first public release of samba4 code
authorAndrew Tridgell <tridge@samba.org>
Wed, 13 Aug 2003 01:53:07 +0000 (01:53 +0000)
committerAndrew Tridgell <tridge@samba.org>
Wed, 13 Aug 2003 01:53:07 +0000 (01:53 +0000)
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)

779 files changed:
source4/.cvsignore [new file with mode: 0644]
source4/.dmallocrc [new file with mode: 0644]
source4/Doxyfile [new file with mode: 0644]
source4/Makefile.in [new file with mode: 0644]
source4/aclocal.m4 [new file with mode: 0644]
source4/auth/auth.c [new file with mode: 0644]
source4/auth/auth_builtin.c [new file with mode: 0644]
source4/auth/auth_compat.c [new file with mode: 0644]
source4/auth/auth_domain.c [new file with mode: 0644]
source4/auth/auth_ntlmssp.c [new file with mode: 0644]
source4/auth/auth_sam.c [new file with mode: 0644]
source4/auth/auth_server.c [new file with mode: 0644]
source4/auth/auth_unix.c [new file with mode: 0644]
source4/auth/auth_util.c [new file with mode: 0644]
source4/auth/auth_winbind.c [new file with mode: 0644]
source4/auth/pampass.c [new file with mode: 0644]
source4/auth/pass_check.c [new file with mode: 0644]
source4/autogen.sh [new file with mode: 0755]
source4/build/tests/README [new file with mode: 0644]
source4/build/tests/crypttest.c [new file with mode: 0644]
source4/build/tests/fcntl_lock.c [new file with mode: 0644]
source4/build/tests/fcntl_lock64.c [new file with mode: 0644]
source4/build/tests/fcntl_lock_thread.c [new file with mode: 0644]
source4/build/tests/ftruncate.c [new file with mode: 0644]
source4/build/tests/getgroups.c [new file with mode: 0644]
source4/build/tests/shared_mmap.c [new file with mode: 0644]
source4/build/tests/shlib.c [new file with mode: 0644]
source4/build/tests/summary.c [new file with mode: 0644]
source4/build/tests/trivial.c [new file with mode: 0644]
source4/build/tests/unixsock.c [new file with mode: 0644]
source4/change-log [new file with mode: 0644]
source4/client/.cvsignore [new file with mode: 0644]
source4/client/client.c [new file with mode: 0644]
source4/client/clitar.c [new file with mode: 0644]
source4/client/mount.cifs.c [new file with mode: 0644]
source4/client/smbmnt.c [new file with mode: 0644]
source4/client/smbmount.c [new file with mode: 0644]
source4/client/smbspool.c [new file with mode: 0644]
source4/client/smbumount.c [new file with mode: 0644]
source4/client/tree.c [new file with mode: 0644]
source4/codepages/.cvsignore [new file with mode: 0644]
source4/codepages/valid.dat [new file with mode: 0644]
source4/config.sub [new file with mode: 0755]
source4/configure.developer [new file with mode: 0755]
source4/configure.in [new file with mode: 0644]
source4/configure.nodebug.developer [new file with mode: 0755]
source4/configure.tridge.opt [new file with mode: 0755]
source4/dynconfig.c [new file with mode: 0644]
source4/groupdb/mapping.c [new file with mode: 0644]
source4/ignore.txt [new file with mode: 0644]
source4/include/.cvsignore [new file with mode: 0644]
source4/include/MacExtensions.h [new file with mode: 0644]
source4/include/ads.h [new file with mode: 0644]
source4/include/adt_tree.h [new file with mode: 0644]
source4/include/asn_1.h [new file with mode: 0644]
source4/include/auth.h [new file with mode: 0644]
source4/include/byteorder.h [new file with mode: 0644]
source4/include/charset.h [new file with mode: 0644]
source4/include/cli_context.h [new file with mode: 0644]
source4/include/client.h [new file with mode: 0644]
source4/include/clitar.h [new file with mode: 0644]
source4/include/context.h [new file with mode: 0644]
source4/include/debug.h [new file with mode: 0644]
source4/include/dlinklist.h [new file with mode: 0644]
source4/include/doserr.h [new file with mode: 0644]
source4/include/dynconfig.h [new file with mode: 0644]
source4/include/enums.h [new file with mode: 0644]
source4/include/events.h [new file with mode: 0644]
source4/include/genparser.h [new file with mode: 0644]
source4/include/genparser_samba.h [new file with mode: 0644]
source4/include/gums.h [new file with mode: 0644]
source4/include/hmacmd5.h [new file with mode: 0644]
source4/include/includes.h [new file with mode: 0644]
source4/include/interfaces.h [new file with mode: 0644]
source4/include/intl.h [new file with mode: 0644]
source4/include/ioctl.h [new file with mode: 0644]
source4/include/libsmb_internal.h [new file with mode: 0644]
source4/include/libsmbclient.h [new file with mode: 0644]
source4/include/local.h [new file with mode: 0644]
source4/include/mangle.h [new file with mode: 0644]
source4/include/mapping.h [new file with mode: 0644]
source4/include/md5.h [new file with mode: 0644]
source4/include/messages.h [new file with mode: 0644]
source4/include/msdfs.h [new file with mode: 0644]
source4/include/mutex.h [new file with mode: 0644]
source4/include/nameserv.h [new file with mode: 0644]
source4/include/nt_printing.h [new file with mode: 0644]
source4/include/nt_status.h [new file with mode: 0644]
source4/include/ntdomain.h [new file with mode: 0644]
source4/include/nterr.h [new file with mode: 0644]
source4/include/ntlmssp.h [new file with mode: 0644]
source4/include/ntvfs.h [new file with mode: 0644]
source4/include/passdb.h [new file with mode: 0644]
source4/include/popt_common.h [new file with mode: 0644]
source4/include/printing.h [new file with mode: 0644]
source4/include/process_model.h [new file with mode: 0644]
source4/include/pstring.h [new file with mode: 0644]
source4/include/rap.h [new file with mode: 0755]
source4/include/rpc_brs.h [new file with mode: 0644]
source4/include/rpc_client.h [new file with mode: 0644]
source4/include/rpc_creds.h [new file with mode: 0644]
source4/include/rpc_dce.h [new file with mode: 0644]
source4/include/rpc_dfs.h [new file with mode: 0644]
source4/include/rpc_ds.h [new file with mode: 0644]
source4/include/rpc_lsa.h [new file with mode: 0644]
source4/include/rpc_misc.h [new file with mode: 0644]
source4/include/rpc_netlogon.h [new file with mode: 0644]
source4/include/rpc_parse.h [new file with mode: 0644]
source4/include/rpc_reg.h [new file with mode: 0644]
source4/include/rpc_samr.h [new file with mode: 0644]
source4/include/rpc_secdes.h [new file with mode: 0644]
source4/include/rpc_spoolss.h [new file with mode: 0755]
source4/include/rpc_srvsvc.h [new file with mode: 0644]
source4/include/rpc_wkssvc.h [new file with mode: 0644]
source4/include/safe_string.h [new file with mode: 0644]
source4/include/sam.h [new file with mode: 0644]
source4/include/secrets.h [new file with mode: 0644]
source4/include/session.h [new file with mode: 0644]
source4/include/smb.h [new file with mode: 0644]
source4/include/smb_acls.h [new file with mode: 0644]
source4/include/smb_interfaces.h [new file with mode: 0644]
source4/include/smb_macros.h [new file with mode: 0644]
source4/include/stamp-h.in [new file with mode: 0644]
source4/include/talloc.h [new file with mode: 0644]
source4/include/tdbsam2.h [new file with mode: 0644]
source4/include/trans2.h [new file with mode: 0644]
source4/include/util_getent.h [new file with mode: 0644]
source4/include/version.h [new file with mode: 0644]
source4/include/vt_mode.h [new file with mode: 0644]
source4/include/xfile.h [new file with mode: 0644]
source4/install-sh [new file with mode: 0644]
source4/intl/.cvsignore [new file with mode: 0644]
source4/intl/lang_tdb.c [new file with mode: 0644]
source4/intl/linux-msg.sed [new file with mode: 0644]
source4/lib/.cvsignore [new file with mode: 0644]
source4/lib/account_pol.c [new file with mode: 0644]
source4/lib/adt_tree.c [new file with mode: 0644]
source4/lib/bitmap.c [new file with mode: 0644]
source4/lib/charcnv.c [new file with mode: 0644]
source4/lib/cmdline/popt_common.c [new file with mode: 0644]
source4/lib/cmdline/readline.c [new file with mode: 0644]
source4/lib/crc32.c [new file with mode: 0644]
source4/lib/crypto/crc32.c [new file with mode: 0644]
source4/lib/crypto/hmacmd5.c [new file with mode: 0644]
source4/lib/crypto/md4.c [new file with mode: 0644]
source4/lib/crypto/md5.c [new file with mode: 0644]
source4/lib/data_blob.c [new file with mode: 0644]
source4/lib/debug.c [new file with mode: 0644]
source4/lib/dmallocmsg.c [new file with mode: 0644]
source4/lib/dprintf.c [new file with mode: 0644]
source4/lib/events.c [new file with mode: 0644]
source4/lib/fault.c [new file with mode: 0644]
source4/lib/fsusage.c [new file with mode: 0644]
source4/lib/gencache.c [new file with mode: 0644]
source4/lib/genparser.c [new file with mode: 0644]
source4/lib/genparser_samba.c [new file with mode: 0644]
source4/lib/genrand.c [new file with mode: 0644]
source4/lib/getsmbpass.c [new file with mode: 0644]
source4/lib/hmacmd5.c [new file with mode: 0644]
source4/lib/iconv.c [new file with mode: 0644]
source4/lib/interface.c [new file with mode: 0644]
source4/lib/interfaces.c [new file with mode: 0644]
source4/lib/ldap_escape.c [new file with mode: 0644]
source4/lib/md4.c [new file with mode: 0644]
source4/lib/md5.c [new file with mode: 0644]
source4/lib/messages.c [new file with mode: 0644]
source4/lib/module.c [new file with mode: 0644]
source4/lib/ms_fnmatch.c [new file with mode: 0644]
source4/lib/mutex.c [new file with mode: 0644]
source4/lib/pam_errors.c [new file with mode: 0644]
source4/lib/pidfile.c [new file with mode: 0644]
source4/lib/popt/CHANGES [new file with mode: 0644]
source4/lib/popt/COPYING [new file with mode: 0644]
source4/lib/popt/README [new file with mode: 0644]
source4/lib/popt/dummy.in [new file with mode: 0644]
source4/lib/popt/findme.c [new file with mode: 0644]
source4/lib/popt/findme.h [new file with mode: 0644]
source4/lib/popt/popt.c [new file with mode: 0644]
source4/lib/popt/popt.h [new file with mode: 0644]
source4/lib/popt/poptconfig.c [new file with mode: 0644]
source4/lib/popt/popthelp.c [new file with mode: 0644]
source4/lib/popt/poptint.h [new file with mode: 0644]
source4/lib/popt/poptparse.c [new file with mode: 0644]
source4/lib/popt/system.h [new file with mode: 0644]
source4/lib/popt_common.c [new file with mode: 0644]
source4/lib/readline.c [new file with mode: 0644]
source4/lib/replace.c [new file with mode: 0644]
source4/lib/select.c [new file with mode: 0644]
source4/lib/sendfile.c [new file with mode: 0644]
source4/lib/server_mutex.c [new file with mode: 0644]
source4/lib/signal.c [new file with mode: 0644]
source4/lib/smbpasswd.c [new file with mode: 0644]
source4/lib/smbrun.c [new file with mode: 0644]
source4/lib/snprintf.c [new file with mode: 0644]
source4/lib/substitute.c [new file with mode: 0644]
source4/lib/sysacls.c [new file with mode: 0644]
source4/lib/system.c [new file with mode: 0644]
source4/lib/system_smbd.c [new file with mode: 0644]
source4/lib/talloc.c [new file with mode: 0644]
source4/lib/tallocmsg.c [new file with mode: 0644]
source4/lib/talloctort.c [new file with mode: 0644]
source4/lib/tdb/README [new file with mode: 0644]
source4/lib/tdb/spinlock.c [new file with mode: 0644]
source4/lib/tdb/spinlock.h [new file with mode: 0644]
source4/lib/tdb/tdb.c [new file with mode: 0644]
source4/lib/tdb/tdb.h [new file with mode: 0644]
source4/lib/tdb/tdb.magic [new file with mode: 0644]
source4/lib/tdb/tdbutil.c [new file with mode: 0644]
source4/lib/tdb/tdbutil.h [new file with mode: 0644]
source4/lib/time.c [new file with mode: 0644]
source4/lib/username.c [new file with mode: 0644]
source4/lib/util.c [new file with mode: 0644]
source4/lib/util_file.c [new file with mode: 0644]
source4/lib/util_getent.c [new file with mode: 0644]
source4/lib/util_pw.c [new file with mode: 0644]
source4/lib/util_seaccess.c [new file with mode: 0644]
source4/lib/util_sid.c [new file with mode: 0644]
source4/lib/util_smbd.c [new file with mode: 0644]
source4/lib/util_sock.c [new file with mode: 0644]
source4/lib/util_str.c [new file with mode: 0644]
source4/lib/util_unistr.c [new file with mode: 0644]
source4/lib/util_uuid.c [new file with mode: 0644]
source4/lib/wins_srv.c [new file with mode: 0644]
source4/lib/xfile.c [new file with mode: 0644]
source4/libads/.cvsignore [new file with mode: 0644]
source4/libads/ads_ldap.c [new file with mode: 0644]
source4/libads/ads_status.c [new file with mode: 0644]
source4/libads/ads_struct.c [new file with mode: 0644]
source4/libads/ads_utils.c [new file with mode: 0644]
source4/libads/disp_sec.c [new file with mode: 0644]
source4/libads/kerberos.c [new file with mode: 0644]
source4/libads/kerberos_verify.c [new file with mode: 0644]
source4/libads/krb5_setpw.c [new file with mode: 0644]
source4/libads/ldap.c [new file with mode: 0644]
source4/libads/ldap_printer.c [new file with mode: 0644]
source4/libads/ldap_user.c [new file with mode: 0644]
source4/libads/ldap_utils.c [new file with mode: 0644]
source4/libads/sasl.c [new file with mode: 0644]
source4/libads/util.c [new file with mode: 0644]
source4/libcli/.cvsignore [new file with mode: 0644]
source4/libcli/cliconnect.c [new file with mode: 0644]
source4/libcli/clideltree.c [new file with mode: 0644]
source4/libcli/clidfs.c [new file with mode: 0644]
source4/libcli/clifile.c [new file with mode: 0644]
source4/libcli/clilist.c [new file with mode: 0644]
source4/libcli/climessage.c [new file with mode: 0644]
source4/libcli/clireadwrite.c [new file with mode: 0644]
source4/libcli/clisecdesc.c [new file with mode: 0644]
source4/libcli/clitrans2.c [new file with mode: 0644]
source4/libcli/namecache.c [new file with mode: 0644]
source4/libcli/namequery.c [new file with mode: 0644]
source4/libcli/namequery_dc.c [new file with mode: 0644]
source4/libcli/nmblib.c [new file with mode: 0644]
source4/libcli/ntlmssp.c [new file with mode: 0644]
source4/libcli/ntlmssp_parse.c [new file with mode: 0644]
source4/libcli/raw/README [new file with mode: 0644]
source4/libcli/raw/clikrb5.c [new file with mode: 0644]
source4/libcli/raw/clioplock.c [new file with mode: 0644]
source4/libcli/raw/clirewrite.c [new file with mode: 0644]
source4/libcli/raw/clisession.c [new file with mode: 0644]
source4/libcli/raw/clisocket.c [new file with mode: 0644]
source4/libcli/raw/clispnego.c [new file with mode: 0644]
source4/libcli/raw/clitransport.c [new file with mode: 0644]
source4/libcli/raw/clitree.c [new file with mode: 0644]
source4/libcli/raw/raweas.c [new file with mode: 0644]
source4/libcli/raw/rawfile.c [new file with mode: 0644]
source4/libcli/raw/rawfileinfo.c [new file with mode: 0644]
source4/libcli/raw/rawfsinfo.c [new file with mode: 0644]
source4/libcli/raw/rawioctl.c [new file with mode: 0644]
source4/libcli/raw/rawnegotiate.c [new file with mode: 0644]
source4/libcli/raw/rawnotify.c [new file with mode: 0644]
source4/libcli/raw/rawreadwrite.c [new file with mode: 0644]
source4/libcli/raw/rawrequest.c [new file with mode: 0644]
source4/libcli/raw/rawsearch.c [new file with mode: 0644]
source4/libcli/raw/rawsetfileinfo.c [new file with mode: 0644]
source4/libcli/raw/rawtrans.c [new file with mode: 0644]
source4/libcli/raw/smb_signing.c [new file with mode: 0644]
source4/libcli/unexpected.c [new file with mode: 0644]
source4/libcli/util/asn1.c [new file with mode: 0644]
source4/libcli/util/clierror.c [new file with mode: 0644]
source4/libcli/util/cliutil.c [new file with mode: 0644]
source4/libcli/util/credentials.c [new file with mode: 0644]
source4/libcli/util/doserr.c [new file with mode: 0644]
source4/libcli/util/errormap.c [new file with mode: 0644]
source4/libcli/util/nterr.c [new file with mode: 0644]
source4/libcli/util/ntlmssp_sign.c [new file with mode: 0644]
source4/libcli/util/pwd_cache.c [new file with mode: 0644]
source4/libcli/util/smbdes.c [new file with mode: 0644]
source4/libcli/util/smbencrypt.c [new file with mode: 0644]
source4/libcli/util/smberr.c [new file with mode: 0644]
source4/locking/brlock.c [new file with mode: 0644]
source4/locking/locking.c [new file with mode: 0644]
source4/locking/posix.c [new file with mode: 0644]
source4/modules/developer.c [new file with mode: 0644]
source4/modules/mysql.c [new file with mode: 0644]
source4/modules/vfs_audit.c [new file with mode: 0644]
source4/modules/vfs_extd_audit.c [new file with mode: 0644]
source4/modules/vfs_fake_perms.c [new file with mode: 0644]
source4/modules/vfs_netatalk.c [new file with mode: 0644]
source4/modules/vfs_recycle.c [new file with mode: 0644]
source4/modules/xml.c [new file with mode: 0644]
source4/msdfs/README [new file with mode: 0644]
source4/msdfs/msdfs.c [new file with mode: 0644]
source4/nmbd/.cvsignore [new file with mode: 0644]
source4/nmbd/asyncdns.c [new file with mode: 0644]
source4/nmbd/nmbd.c [new file with mode: 0644]
source4/nmbd/nmbd_become_dmb.c [new file with mode: 0644]
source4/nmbd/nmbd_become_lmb.c [new file with mode: 0644]
source4/nmbd/nmbd_browserdb.c [new file with mode: 0644]
source4/nmbd/nmbd_browsesync.c [new file with mode: 0644]
source4/nmbd/nmbd_elections.c [new file with mode: 0644]
source4/nmbd/nmbd_incomingdgrams.c [new file with mode: 0644]
source4/nmbd/nmbd_incomingrequests.c [new file with mode: 0644]
source4/nmbd/nmbd_lmhosts.c [new file with mode: 0644]
source4/nmbd/nmbd_logonnames.c [new file with mode: 0644]
source4/nmbd/nmbd_mynames.c [new file with mode: 0644]
source4/nmbd/nmbd_namelistdb.c [new file with mode: 0644]
source4/nmbd/nmbd_namequery.c [new file with mode: 0644]
source4/nmbd/nmbd_nameregister.c [new file with mode: 0644]
source4/nmbd/nmbd_namerelease.c [new file with mode: 0644]
source4/nmbd/nmbd_nodestatus.c [new file with mode: 0644]
source4/nmbd/nmbd_packets.c [new file with mode: 0644]
source4/nmbd/nmbd_processlogon.c [new file with mode: 0644]
source4/nmbd/nmbd_responserecordsdb.c [new file with mode: 0644]
source4/nmbd/nmbd_sendannounce.c [new file with mode: 0644]
source4/nmbd/nmbd_serverlistdb.c [new file with mode: 0644]
source4/nmbd/nmbd_subnetdb.c [new file with mode: 0644]
source4/nmbd/nmbd_synclists.c [new file with mode: 0644]
source4/nmbd/nmbd_winsproxy.c [new file with mode: 0644]
source4/nmbd/nmbd_winsserver.c [new file with mode: 0644]
source4/nmbd/nmbd_workgroupdb.c [new file with mode: 0644]
source4/nsswitch/.cvsignore [new file with mode: 0644]
source4/nsswitch/README [new file with mode: 0644]
source4/nsswitch/hp_nss_common.h [new file with mode: 0644]
source4/nsswitch/hp_nss_dbdefs.h [new file with mode: 0644]
source4/nsswitch/nss.h [new file with mode: 0644]
source4/nsswitch/pam_winbind.c [new file with mode: 0644]
source4/nsswitch/pam_winbind.h [new file with mode: 0644]
source4/nsswitch/wb_client.c [new file with mode: 0644]
source4/nsswitch/wb_common.c [new file with mode: 0644]
source4/nsswitch/wbinfo.c [new file with mode: 0644]
source4/nsswitch/winbind_client.h [new file with mode: 0644]
source4/nsswitch/winbind_nss.c [new file with mode: 0644]
source4/nsswitch/winbind_nss_config.h [new file with mode: 0644]
source4/nsswitch/winbind_nss_solaris.c [new file with mode: 0644]
source4/nsswitch/winbindd.c [new file with mode: 0644]
source4/nsswitch/winbindd.h [new file with mode: 0644]
source4/nsswitch/winbindd_ads.c [new file with mode: 0644]
source4/nsswitch/winbindd_cache.c [new file with mode: 0644]
source4/nsswitch/winbindd_cm.c [new file with mode: 0644]
source4/nsswitch/winbindd_dual.c [new file with mode: 0644]
source4/nsswitch/winbindd_group.c [new file with mode: 0644]
source4/nsswitch/winbindd_idmap.c [new file with mode: 0644]
source4/nsswitch/winbindd_idmap_tdb.c [new file with mode: 0644]
source4/nsswitch/winbindd_misc.c [new file with mode: 0644]
source4/nsswitch/winbindd_nss.h [new file with mode: 0644]
source4/nsswitch/winbindd_pam.c [new file with mode: 0644]
source4/nsswitch/winbindd_rpc.c [new file with mode: 0644]
source4/nsswitch/winbindd_sid.c [new file with mode: 0644]
source4/nsswitch/winbindd_user.c [new file with mode: 0644]
source4/nsswitch/winbindd_util.c [new file with mode: 0644]
source4/nsswitch/winbindd_wins.c [new file with mode: 0644]
source4/nsswitch/wins.c [new file with mode: 0644]
source4/ntvfs/README [new file with mode: 0644]
source4/ntvfs/cifs/README [new file with mode: 0644]
source4/ntvfs/cifs/vfs_cifs.c [new file with mode: 0644]
source4/ntvfs/ipc/README [new file with mode: 0644]
source4/ntvfs/ipc/vfs_ipc.c [new file with mode: 0644]
source4/ntvfs/ntvfs_base.c [new file with mode: 0644]
source4/ntvfs/ntvfs_dfs.c [new file with mode: 0644]
source4/ntvfs/ntvfs_generic.c [new file with mode: 0644]
source4/ntvfs/ntvfs_util.c [new file with mode: 0644]
source4/ntvfs/posix/vfs_posix.c [new file with mode: 0644]
source4/ntvfs/print/README [new file with mode: 0644]
source4/ntvfs/print/vfs_print.c [new file with mode: 0644]
source4/ntvfs/reference/ref.h [new file with mode: 0644]
source4/ntvfs/reference/ref_util.c [new file with mode: 0644]
source4/ntvfs/reference/vfs_ref.c [new file with mode: 0644]
source4/ntvfs/simple/svfs.h [new file with mode: 0644]
source4/ntvfs/simple/svfs_util.c [new file with mode: 0644]
source4/ntvfs/simple/vfs_simple.c [new file with mode: 0644]
source4/pam_smbpass/CHANGELOG [new file with mode: 0644]
source4/pam_smbpass/INSTALL [new file with mode: 0644]
source4/pam_smbpass/README [new file with mode: 0644]
source4/pam_smbpass/TODO [new file with mode: 0644]
source4/pam_smbpass/general.h [new file with mode: 0644]
source4/pam_smbpass/pam_smb_acct.c [new file with mode: 0644]
source4/pam_smbpass/pam_smb_auth.c [new file with mode: 0644]
source4/pam_smbpass/pam_smb_passwd.c [new file with mode: 0644]
source4/pam_smbpass/samples/README [new file with mode: 0644]
source4/pam_smbpass/samples/kdc-pdc [new file with mode: 0644]
source4/pam_smbpass/samples/password-mature [new file with mode: 0644]
source4/pam_smbpass/samples/password-migration [new file with mode: 0644]
source4/pam_smbpass/samples/password-sync [new file with mode: 0644]
source4/pam_smbpass/support.c [new file with mode: 0644]
source4/pam_smbpass/support.h [new file with mode: 0644]
source4/param/.cvsignore [new file with mode: 0644]
source4/param/loadparm.c [new file with mode: 0644]
source4/param/params.c [new file with mode: 0644]
source4/passdb/.cvsignore [new file with mode: 0644]
source4/passdb/machine_sid.c [new file with mode: 0644]
source4/passdb/passdb.c [new file with mode: 0644]
source4/passdb/pdb_compat.c [new file with mode: 0644]
source4/passdb/pdb_get_set.c [new file with mode: 0644]
source4/passdb/pdb_guest.c [new file with mode: 0644]
source4/passdb/pdb_interface.c [new file with mode: 0644]
source4/passdb/pdb_ldap.c [new file with mode: 0644]
source4/passdb/pdb_nisplus.c [new file with mode: 0644]
source4/passdb/pdb_smbpasswd.c [new file with mode: 0644]
source4/passdb/pdb_tdb.c [new file with mode: 0644]
source4/passdb/pdb_unix.c [new file with mode: 0644]
source4/passdb/privileges.c [new file with mode: 0644]
source4/passdb/secrets.c [new file with mode: 0644]
source4/passdb/util_sam_sid.c [new file with mode: 0644]
source4/po/de.msg [new file with mode: 0644]
source4/po/en.msg [new file with mode: 0644]
source4/po/fr.msg [new file with mode: 0644]
source4/po/it.msg [new file with mode: 0644]
source4/po/ja.msg [new file with mode: 0644]
source4/po/pl.msg [new file with mode: 0644]
source4/po/tr.msg [new file with mode: 0644]
source4/popt/.cvsignore [new file with mode: 0644]
source4/popt/CHANGES [new file with mode: 0644]
source4/popt/COPYING [new file with mode: 0644]
source4/popt/README [new file with mode: 0644]
source4/popt/dummy.in [new file with mode: 0644]
source4/popt/findme.c [new file with mode: 0644]
source4/popt/findme.h [new file with mode: 0644]
source4/popt/popt.c [new file with mode: 0644]
source4/popt/popt.h [new file with mode: 0644]
source4/popt/poptconfig.c [new file with mode: 0644]
source4/popt/popthelp.c [new file with mode: 0644]
source4/popt/poptint.h [new file with mode: 0644]
source4/popt/poptparse.c [new file with mode: 0644]
source4/popt/system.h [new file with mode: 0644]
source4/printing/.cvsignore [new file with mode: 0644]
source4/printing/load.c [new file with mode: 0644]
source4/printing/lpq_parse.c [new file with mode: 0644]
source4/printing/notify.c [new file with mode: 0644]
source4/printing/nt_printing.c [new file with mode: 0644]
source4/printing/pcap.c [new file with mode: 0644]
source4/printing/print_cups.c [new file with mode: 0644]
source4/printing/print_generic.c [new file with mode: 0644]
source4/printing/print_svid.c [new file with mode: 0644]
source4/printing/printfsp.c [new file with mode: 0644]
source4/printing/printing.c [new file with mode: 0644]
source4/printing/printing_db.c [new file with mode: 0644]
source4/python/.cvsignore [new file with mode: 0644]
source4/python/README [new file with mode: 0644]
source4/python/examples/spoolss/changeid.py [new file with mode: 0755]
source4/python/examples/spoolss/enumprinters.py [new file with mode: 0755]
source4/python/examples/spoolss/psec.py [new file with mode: 0755]
source4/python/examples/tdbpack/.cvsignore [new file with mode: 0644]
source4/python/examples/tdbpack/oldtdbutil.py [new file with mode: 0644]
source4/python/examples/tdbpack/tdbtimetrial.py [new file with mode: 0755]
source4/python/examples/tdbpack/test_tdbpack.py [new file with mode: 0755]
source4/python/gprinterdata [new file with mode: 0755]
source4/python/gtdbtool [new file with mode: 0755]
source4/python/gtkdictbrowser.py [new file with mode: 0755]
source4/python/mkpatch [new file with mode: 0755]
source4/python/py_common.c [new file with mode: 0644]
source4/python/py_common.h [new file with mode: 0644]
source4/python/py_conv.c [new file with mode: 0644]
source4/python/py_conv.h [new file with mode: 0644]
source4/python/py_lsa.c [new file with mode: 0644]
source4/python/py_lsa.h [new file with mode: 0644]
source4/python/py_ntsec.c [new file with mode: 0644]
source4/python/py_samba.c [new file with mode: 0644]
source4/python/py_samr.c [new file with mode: 0644]
source4/python/py_samr.h [new file with mode: 0644]
source4/python/py_samr_conv.c [new file with mode: 0644]
source4/python/py_smb.c [new file with mode: 0644]
source4/python/py_smb.h [new file with mode: 0644]
source4/python/py_spoolss.c [new file with mode: 0644]
source4/python/py_spoolss.h [new file with mode: 0644]
source4/python/py_spoolss_common.c [new file with mode: 0644]
source4/python/py_spoolss_drivers.c [new file with mode: 0644]
source4/python/py_spoolss_drivers_conv.c [new file with mode: 0644]
source4/python/py_spoolss_forms.c [new file with mode: 0644]
source4/python/py_spoolss_forms_conv.c [new file with mode: 0644]
source4/python/py_spoolss_jobs.c [new file with mode: 0644]
source4/python/py_spoolss_jobs_conv.c [new file with mode: 0644]
source4/python/py_spoolss_ports.c [new file with mode: 0644]
source4/python/py_spoolss_ports_conv.c [new file with mode: 0644]
source4/python/py_spoolss_printerdata.c [new file with mode: 0644]
source4/python/py_spoolss_printers.c [new file with mode: 0644]
source4/python/py_spoolss_printers_conv.c [new file with mode: 0644]
source4/python/py_srvsvc.c [new file with mode: 0644]
source4/python/py_srvsvc.h [new file with mode: 0644]
source4/python/py_srvsvc_conv.c [new file with mode: 0644]
source4/python/py_tdb.c [new file with mode: 0644]
source4/python/py_tdb.h [new file with mode: 0644]
source4/python/py_tdbpack.c [new file with mode: 0644]
source4/python/py_winbind.c [new file with mode: 0644]
source4/python/py_winbind.h [new file with mode: 0644]
source4/python/py_winbind_conv.c [new file with mode: 0644]
source4/python/py_winreg.c [new file with mode: 0644]
source4/python/py_winreg.h [new file with mode: 0644]
source4/python/samba/.cvsignore [new file with mode: 0644]
source4/python/samba/__init__.py [new file with mode: 0644]
source4/python/samba/printerdata.py [new file with mode: 0644]
source4/python/setup.py [new file with mode: 0755]
source4/registry/reg_cachehook.c [new file with mode: 0644]
source4/registry/reg_db.c [new file with mode: 0644]
source4/registry/reg_frontend.c [new file with mode: 0644]
source4/registry/reg_objects.c [new file with mode: 0644]
source4/registry/reg_printing.c [new file with mode: 0644]
source4/rpc_client/cli_dfs.c [new file with mode: 0644]
source4/rpc_client/cli_ds.c [new file with mode: 0644]
source4/rpc_client/cli_lsarpc.c [new file with mode: 0644]
source4/rpc_client/cli_netlogon.c [new file with mode: 0644]
source4/rpc_client/cli_pipe.c [new file with mode: 0644]
source4/rpc_client/cli_reg.c [new file with mode: 0644]
source4/rpc_client/cli_samr.c [new file with mode: 0644]
source4/rpc_client/cli_spoolss.c [new file with mode: 0644]
source4/rpc_client/cli_spoolss_notify.c [new file with mode: 0644]
source4/rpc_client/cli_srvsvc.c [new file with mode: 0644]
source4/rpc_client/cli_wkssvc.c [new file with mode: 0644]
source4/rpc_parse/.cvsignore [new file with mode: 0644]
source4/rpc_parse/parse_dfs.c [new file with mode: 0644]
source4/rpc_parse/parse_ds.c [new file with mode: 0644]
source4/rpc_parse/parse_lsa.c [new file with mode: 0644]
source4/rpc_parse/parse_misc.c [new file with mode: 0644]
source4/rpc_parse/parse_net.c [new file with mode: 0644]
source4/rpc_parse/parse_prs.c [new file with mode: 0644]
source4/rpc_parse/parse_reg.c [new file with mode: 0644]
source4/rpc_parse/parse_rpc.c [new file with mode: 0644]
source4/rpc_parse/parse_samr.c [new file with mode: 0644]
source4/rpc_parse/parse_sec.c [new file with mode: 0644]
source4/rpc_parse/parse_spoolss.c [new file with mode: 0644]
source4/rpc_parse/parse_srv.c [new file with mode: 0644]
source4/rpc_parse/parse_wks.c [new file with mode: 0644]
source4/rpc_server/.cvsignore [new file with mode: 0644]
source4/rpc_server/srv_dfs.c [new file with mode: 0644]
source4/rpc_server/srv_dfs_nt.c [new file with mode: 0644]
source4/rpc_server/srv_lsa.c [new file with mode: 0644]
source4/rpc_server/srv_lsa_hnd.c [new file with mode: 0644]
source4/rpc_server/srv_lsa_nt.c [new file with mode: 0644]
source4/rpc_server/srv_netlog.c [new file with mode: 0644]
source4/rpc_server/srv_netlog_nt.c [new file with mode: 0644]
source4/rpc_server/srv_pipe.c [new file with mode: 0644]
source4/rpc_server/srv_pipe_hnd.c [new file with mode: 0644]
source4/rpc_server/srv_reg.c [new file with mode: 0644]
source4/rpc_server/srv_reg_nt.c [new file with mode: 0644]
source4/rpc_server/srv_samr.c [new file with mode: 0644]
source4/rpc_server/srv_samr_nt.c [new file with mode: 0644]
source4/rpc_server/srv_samr_util.c [new file with mode: 0644]
source4/rpc_server/srv_spoolss.c [new file with mode: 0755]
source4/rpc_server/srv_spoolss_nt.c [new file with mode: 0644]
source4/rpc_server/srv_srvsvc.c [new file with mode: 0644]
source4/rpc_server/srv_srvsvc_nt.c [new file with mode: 0644]
source4/rpc_server/srv_util.c [new file with mode: 0644]
source4/rpc_server/srv_wkssvc.c [new file with mode: 0644]
source4/rpc_server/srv_wkssvc_nt.c [new file with mode: 0644]
source4/rpcclient/cmd_dfs.c [new file with mode: 0644]
source4/rpcclient/cmd_ds.c [new file with mode: 0644]
source4/rpcclient/cmd_lsarpc.c [new file with mode: 0644]
source4/rpcclient/cmd_netlogon.c [new file with mode: 0644]
source4/rpcclient/cmd_reg.c [new file with mode: 0644]
source4/rpcclient/cmd_samr.c [new file with mode: 0644]
source4/rpcclient/cmd_spoolss.c [new file with mode: 0644]
source4/rpcclient/cmd_srvsvc.c [new file with mode: 0644]
source4/rpcclient/cmd_wkssvc.c [new file with mode: 0644]
source4/rpcclient/display_sec.c [new file with mode: 0644]
source4/rpcclient/rpcclient.c [new file with mode: 0644]
source4/rpcclient/rpcclient.h [new file with mode: 0644]
source4/sam/SAM-interface_handles.txt [new file with mode: 0644]
source4/sam/account.c [new file with mode: 0644]
source4/sam/get_set_account.c [new file with mode: 0644]
source4/sam/get_set_domain.c [new file with mode: 0644]
source4/sam/get_set_group.c [new file with mode: 0644]
source4/sam/group.c [new file with mode: 0644]
source4/sam/gumm_tdb.c [new file with mode: 0644]
source4/sam/gums.c [new file with mode: 0644]
source4/sam/gums_api.c [new file with mode: 0644]
source4/sam/gums_helper.c [new file with mode: 0644]
source4/sam/interface.c [new file with mode: 0644]
source4/sam/sam_ads.c [new file with mode: 0755]
source4/sam/sam_plugin.c [new file with mode: 0644]
source4/sam/sam_skel.c [new file with mode: 0644]
source4/script/.cvsignore [new file with mode: 0644]
source4/script/addtosmbpass [new file with mode: 0644]
source4/script/build_env.sh [new file with mode: 0644]
source4/script/convert_smbpasswd [new file with mode: 0755]
source4/script/creategroup [new file with mode: 0755]
source4/script/extract_allparms.sh [new file with mode: 0755]
source4/script/find_missing_doc.pl [new file with mode: 0755]
source4/script/findsmb.in [new file with mode: 0755]
source4/script/findstatic.pl [new file with mode: 0755]
source4/script/genstruct.pl [new file with mode: 0755]
source4/script/installbin.sh [new file with mode: 0644]
source4/script/installdat.sh [new file with mode: 0755]
source4/script/installdirs.sh [new file with mode: 0755]
source4/script/installman.sh [new file with mode: 0644]
source4/script/installmodules.sh [new file with mode: 0644]
source4/script/installscripts.sh [new file with mode: 0644]
source4/script/installswat.sh [new file with mode: 0644]
source4/script/makeunicodecasemap.awk [new file with mode: 0644]
source4/script/mkinstalldirs [new file with mode: 0755]
source4/script/mknissmbpasswd.sh [new file with mode: 0755]
source4/script/mknissmbpwdtbl.sh [new file with mode: 0755]
source4/script/mkproto.awk [new file with mode: 0644]
source4/script/mkproto.sh [new file with mode: 0644]
source4/script/mksmbpasswd.sh [new file with mode: 0644]
source4/script/revert.sh [new file with mode: 0644]
source4/script/scancvslog.pl [new file with mode: 0755]
source4/script/smbtar [new file with mode: 0644]
source4/script/uninstallbin.sh [new file with mode: 0644]
source4/script/uninstallman.sh [new file with mode: 0644]
source4/script/uninstallmodules.sh [new file with mode: 0644]
source4/script/uninstallscripts.sh [new file with mode: 0644]
source4/script/updatesmbpasswd.sh [new file with mode: 0644]
source4/smbadduser [new file with mode: 0755]
source4/smbd/.cvsignore [new file with mode: 0644]
source4/smbd/build_options.c [new file with mode: 0644]
source4/smbd/conn.c [new file with mode: 0644]
source4/smbd/connection.c [new file with mode: 0644]
source4/smbd/negprot.c [new file with mode: 0644]
source4/smbd/password.c [new file with mode: 0644]
source4/smbd/process.c [new file with mode: 0644]
source4/smbd/process_model.c [new file with mode: 0644]
source4/smbd/process_single.c [new file with mode: 0644]
source4/smbd/process_standard.c [new file with mode: 0644]
source4/smbd/process_thread.c [new file with mode: 0644]
source4/smbd/reply.c [new file with mode: 0644]
source4/smbd/request.c [new file with mode: 0644]
source4/smbd/rewrite.c [new file with mode: 0644]
source4/smbd/server.c [new file with mode: 0644]
source4/smbd/service.c [new file with mode: 0644]
source4/smbd/session.c [new file with mode: 0644]
source4/smbd/sesssetup.c [new file with mode: 0644]
source4/smbd/tcon.c [new file with mode: 0644]
source4/smbd/trans2.c [new file with mode: 0644]
source4/smbwrapper/.cvsignore [new file with mode: 0644]
source4/smbwrapper/PORTING [new file with mode: 0644]
source4/smbwrapper/README [new file with mode: 0644]
source4/smbwrapper/realcalls.c [new file with mode: 0644]
source4/smbwrapper/realcalls.h [new file with mode: 0644]
source4/smbwrapper/shared.c [new file with mode: 0644]
source4/smbwrapper/smbsh.c [new file with mode: 0644]
source4/smbwrapper/smbsh.in [new file with mode: 0644]
source4/smbwrapper/smbw.c [new file with mode: 0644]
source4/smbwrapper/smbw.h [new file with mode: 0644]
source4/smbwrapper/smbw_cache.c [new file with mode: 0644]
source4/smbwrapper/smbw_dir.c [new file with mode: 0644]
source4/smbwrapper/smbw_stat.c [new file with mode: 0644]
source4/smbwrapper/wrapped.c [new file with mode: 0644]
source4/tdb/.cvsignore [new file with mode: 0644]
source4/tdb/Makefile [new file with mode: 0644]
source4/tdb/README [new file with mode: 0644]
source4/tdb/spinlock.c [new file with mode: 0644]
source4/tdb/spinlock.h [new file with mode: 0644]
source4/tdb/tdb.c [new file with mode: 0644]
source4/tdb/tdb.h [new file with mode: 0644]
source4/tdb/tdb.magic [new file with mode: 0644]
source4/tdb/tdbbackup.c [new file with mode: 0644]
source4/tdb/tdbdump.c [new file with mode: 0644]
source4/tdb/tdbtest.c [new file with mode: 0644]
source4/tdb/tdbtool.c [new file with mode: 0644]
source4/tdb/tdbtorture.c [new file with mode: 0644]
source4/tdb/tdbutil.c [new file with mode: 0644]
source4/tdb/tdbutil.h [new file with mode: 0644]
source4/tests/.cvsignore [new file with mode: 0644]
source4/tests/README [new file with mode: 0644]
source4/tests/crypttest.c [new file with mode: 0644]
source4/tests/fcntl_lock.c [new file with mode: 0644]
source4/tests/fcntl_lock64.c [new file with mode: 0644]
source4/tests/fcntl_lock_thread.c [new file with mode: 0644]
source4/tests/ftruncate.c [new file with mode: 0644]
source4/tests/getgroups.c [new file with mode: 0644]
source4/tests/shared_mmap.c [new file with mode: 0644]
source4/tests/shlib.c [new file with mode: 0644]
source4/tests/summary.c [new file with mode: 0644]
source4/tests/trivial.c [new file with mode: 0644]
source4/tests/unixsock.c [new file with mode: 0644]
source4/torture/.cvsignore [new file with mode: 0644]
source4/torture/aliases.c [new file with mode: 0644]
source4/torture/cmd_sam.c [new file with mode: 0644]
source4/torture/cmd_vfs.c [new file with mode: 0644]
source4/torture/denytest.c [new file with mode: 0644]
source4/torture/dfstest.c [new file with mode: 0644]
source4/torture/genbit.c [new file with mode: 0644]
source4/torture/gendefs.h [new file with mode: 0644]
source4/torture/genparm.c [new file with mode: 0644]
source4/torture/gentest.c [new file with mode: 0644]
source4/torture/locktest.c [new file with mode: 0644]
source4/torture/locktest2.c [new file with mode: 0644]
source4/torture/mangle_test.c [new file with mode: 0644]
source4/torture/masktest.c [new file with mode: 0644]
source4/torture/msgtest.c [new file with mode: 0644]
source4/torture/nbio.c [new file with mode: 0644]
source4/torture/nsstest.c [new file with mode: 0644]
source4/torture/qfileinfo.c [new file with mode: 0644]
source4/torture/qfsinfo.c [new file with mode: 0644]
source4/torture/raw/chkpath.c [new file with mode: 0644]
source4/torture/raw/close.c [new file with mode: 0644]
source4/torture/raw/context.c [new file with mode: 0644]
source4/torture/raw/ioctl.c [new file with mode: 0644]
source4/torture/raw/lock.c [new file with mode: 0644]
source4/torture/raw/missing.txt [new file with mode: 0644]
source4/torture/raw/mkdir.c [new file with mode: 0644]
source4/torture/raw/mux.c [new file with mode: 0644]
source4/torture/raw/notify.c [new file with mode: 0644]
source4/torture/raw/open.c [new file with mode: 0644]
source4/torture/raw/oplock.c [new file with mode: 0644]
source4/torture/raw/qfileinfo.c [new file with mode: 0644]
source4/torture/raw/qfsinfo.c [new file with mode: 0644]
source4/torture/raw/read.c [new file with mode: 0644]
source4/torture/raw/rename.c [new file with mode: 0644]
source4/torture/raw/search.c [new file with mode: 0644]
source4/torture/raw/seek.c [new file with mode: 0644]
source4/torture/raw/setfileinfo.c [new file with mode: 0644]
source4/torture/raw/unlink.c [new file with mode: 0644]
source4/torture/raw/write.c [new file with mode: 0644]
source4/torture/rpctorture.c [new file with mode: 0644]
source4/torture/samtest.c [new file with mode: 0644]
source4/torture/samtest.h [new file with mode: 0644]
source4/torture/scanner.c [new file with mode: 0644]
source4/torture/search.c [new file with mode: 0644]
source4/torture/setfileinfo.c [new file with mode: 0644]
source4/torture/t_strcmp.c [new file with mode: 0644]
source4/torture/torture.c [new file with mode: 0644]
source4/torture/torture_util.c [new file with mode: 0644]
source4/torture/utable.c [new file with mode: 0644]
source4/torture/vfstest.c [new file with mode: 0644]
source4/torture/vfstest.h [new file with mode: 0644]
source4/ubiqx/.cvsignore [new file with mode: 0644]
source4/utils/.cvsignore [new file with mode: 0644]
source4/utils/debug2html.c [new file with mode: 0644]
source4/utils/editreg.c [new file with mode: 0644]
source4/utils/net.c [new file with mode: 0644]
source4/utils/net.h [new file with mode: 0644]
source4/utils/net_ads.c [new file with mode: 0644]
source4/utils/net_ads_cldap.c [new file with mode: 0644]
source4/utils/net_cache.c [new file with mode: 0644]
source4/utils/net_help.c [new file with mode: 0644]
source4/utils/net_lookup.c [new file with mode: 0644]
source4/utils/net_rap.c [new file with mode: 0644]
source4/utils/net_rpc.c [new file with mode: 0644]
source4/utils/net_rpc_join.c [new file with mode: 0644]
source4/utils/net_rpc_samsync.c [new file with mode: 0644]
source4/utils/net_time.c [new file with mode: 0644]
source4/utils/nmblookup.c [new file with mode: 0644]
source4/utils/ntlm_auth.c [new file with mode: 0644]
source4/utils/pdbedit.c [new file with mode: 0644]
source4/utils/profiles.c [new file with mode: 0644]
source4/utils/rewrite.c [new file with mode: 0644]
source4/utils/rpccheck.c [new file with mode: 0644]
source4/utils/smbcacls.c [new file with mode: 0644]
source4/utils/smbcontrol.c [new file with mode: 0644]
source4/utils/smbfilter.c [new file with mode: 0644]
source4/utils/smbgroupedit.c [new file with mode: 0644]
source4/utils/smbpasswd.c [new file with mode: 0644]
source4/utils/smbtree.c [new file with mode: 0644]
source4/utils/smbw_sample.c [new file with mode: 0644]
source4/utils/status.c [new file with mode: 0644]
source4/utils/tdb/Makefile [new file with mode: 0644]
source4/utils/tdb/README [new file with mode: 0644]
source4/utils/tdb/tdb.magic [new file with mode: 0644]
source4/utils/tdb/tdbbackup.c [new file with mode: 0644]
source4/utils/tdb/tdbdump.c [new file with mode: 0644]
source4/utils/tdb/tdbtest.c [new file with mode: 0644]
source4/utils/tdb/tdbtool.c [new file with mode: 0644]
source4/utils/tdb/tdbtorture.c [new file with mode: 0644]
source4/utils/testparm.c [new file with mode: 0644]
source4/utils/testprns.c [new file with mode: 0644]
source4/web/.cvsignore [new file with mode: 0644]
source4/web/cgi.c [new file with mode: 0644]
source4/web/diagnose.c [new file with mode: 0644]
source4/web/neg_lang.c [new file with mode: 0644]
source4/web/startstop.c [new file with mode: 0644]
source4/web/statuspage.c [new file with mode: 0644]
source4/web/swat.c [new file with mode: 0644]
source4/wrepld/parser.c [new file with mode: 0644]
source4/wrepld/partners.c [new file with mode: 0644]
source4/wrepld/process.c [new file with mode: 0644]
source4/wrepld/server.c [new file with mode: 0644]
source4/wrepld/socket.c [new file with mode: 0644]
source4/wrepld/wins_repl.h [new file with mode: 0644]

diff --git a/source4/.cvsignore b/source4/.cvsignore
new file mode 100644 (file)
index 0000000..d1c7873
--- /dev/null
@@ -0,0 +1,34 @@
+libsmb
+*.po
+*.po32
+.headers.stamp
+.inslog2
+.ix*
+.proto.check
+.proto.stamp
+autom4te.cache
+ID
+ID
+Makefile
+bin
+build
+config.cache
+config.log
+config.status
+configure.tridge
+cvs.log
+diffs
+dmalloc.log
+dmallog.log
+dox
+libtool
+so_locations
+tca.log
+testdir
+testtmp
+trace.out
+typescript*
+configure
+config.jjm
+config.stfs
+*.dat
diff --git a/source4/.dmallocrc b/source4/.dmallocrc
new file mode 100644 (file)
index 0000000..5e5c45e
--- /dev/null
@@ -0,0 +1,2 @@
+samba allow-free-null, log-stats, log-non-free, log-trans, \
+        check-fence, check-heap, check-lists, error-abort
\ No newline at end of file
diff --git a/source4/Doxyfile b/source4/Doxyfile
new file mode 100644 (file)
index 0000000..c104078
--- /dev/null
@@ -0,0 +1,176 @@
+# Doxyfile 0.1
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = Samba
+PROJECT_NUMBER         = HEAD
+
+# NOTE: By default, Doxygen writes into the dox/ subdirectory of the
+# invocation directory.  If you want to put it somewhere else, for
+# example, to write straight into a webserver directory, then override
+# this variable in a configuration concatenated to this one: Doxygen
+# doesn't mind variables being redefined.
+
+OUTPUT_DIRECTORY       = dox
+
+OUTPUT_LANGUAGE        = English
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ALWAYS_DETAILED_SEC    = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = $(PWD)/
+INTERNAL_DOCS          = YES
+CLASS_DIAGRAMS         = YES
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = YES
+STRIP_CODE_COMMENTS    = NO
+CASE_SENSE_NAMES       = YES
+SHORT_NAMES            = NO
+HIDE_SCOPE_NAMES       = YES
+VERBATIM_HEADERS       = YES
+SHOW_INCLUDE_FILES     = YES
+JAVADOC_AUTOBRIEF      = YES
+INHERIT_DOCS           = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = NO
+DISTRIBUTE_GROUP_DOC   = NO
+TAB_SIZE               = 8
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+ALIASES                = 
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+OPTIMIZE_OUTPUT_FOR_C  = YES
+SHOW_USED_FILES        = YES
+REFERENCED_BY_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = YES
+WARNINGS               = NO
+WARN_IF_UNDOCUMENTED   = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = . 
+FILE_PATTERNS          = *.c \
+                         *.h \
+                         *.idl
+RECURSIVE              = YES
+EXCLUDE                = include/includes.h \
+                         include/proto.h
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = 
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 1
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = .
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 3
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+#---------------------------------------------------------------------------
+# configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = NO
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+TEMPLATE_RELATIONS     = YES
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+GRAPHICAL_HIERARCHY    = YES
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
+CGI_NAME               = search.cgi
+CGI_URL                = 
+DOC_URL                = 
+DOC_ABSPATH            = 
+BIN_ABSPATH            = /usr/local/bin/
+EXT_DOC_PATHS          = 
diff --git a/source4/Makefile.in b/source4/Makefile.in
new file mode 100644 (file)
index 0000000..d6541a7
--- /dev/null
@@ -0,0 +1,1338 @@
+#########################################################################
+# Makefile.in for Samba - rewritten for autoconf support
+# Copyright Andrew Tridgell 1992-1998
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+# Copyright Andrew Barteltt 2002
+# Copyright (C) 2003 Anthony Liguori <aliguor@us.ibm.com>
+# Copyright (C) 2003 James Myers <myersjj@us.ibm.com>
+###########################################################################
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+mandir=@mandir@
+
+LIBS=@LIBS@ 
+CC=@CC@
+SHLD=@SHLD@
+CFLAGS=@CFLAGS@
+CPPFLAGS=@CPPFLAGS@
+EXEEXT=@EXEEXT@
+LDFLAGS=@LDFLAGS@
+LDSHFLAGS=@LDSHFLAGS@ @LDFLAGS@ @CFLAGS@
+AWK=@AWK@
+DYNEXP=@DYNEXP@
+PYTHON=@PYTHON@
+
+TERMLDFLAGS=@TERMLDFLAGS@
+TERMLIBS=@TERMLIBS@
+PRINTLIBS=@PRINTLIBS@
+AUTHLIBS=@AUTHLIBS@
+ACLLIBS=@ACLLIBS@
+
+LINK=$(CC) $(FLAGS) $(LDFLAGS)
+
+INSTALLCMD=@INSTALL@
+INSTALLCLIENTCMD_SH=@INSTALLCLIENTCMD_SH@
+INSTALLCLIENTCMD_A=@INSTALLCLIENTCMD_A@
+
+VPATH=@srcdir@
+srcdir=@srcdir@
+builddir=@builddir@
+SHELL=/bin/sh
+
+BASEDIR= @prefix@
+BINDIR = @bindir@
+# sbindir is mapped to bindir when compiling SAMBA in 2.0.x compatibility mode.
+SBINDIR = @sbindir@
+LIBDIR = @libdir@
+VFSLIBDIR = $(LIBDIR)/vfs
+PDBLIBDIR = $(LIBDIR)/pdb
+RPCLIBDIR = $(LIBDIR)/rpc
+CONFIGDIR = @configdir@
+VARDIR = @localstatedir@
+MANDIR = @mandir@
+
+# The permissions to give the executables
+INSTALLPERMS = 0755
+
+# set these to where to find various files
+# These can be overridden by command line switches (see smbd(8))
+# or in smb.conf (see smb.conf(5))
+LOGFILEBASE = @logfilebase@
+CONFIGFILE = $(CONFIGDIR)/smb.conf
+LMHOSTSFILE = $(CONFIGDIR)/lmhosts
+
+# This is where smbpasswd et al go
+PRIVATEDIR = @privatedir@
+
+SMB_PASSWD_FILE = $(PRIVATEDIR)/smbpasswd
+PRIVATE_DIR = $(PRIVATEDIR)
+
+# This is where SWAT images and help files go
+SWATDIR = @swatdir@
+
+# the directory where lock files go
+LOCKDIR = @lockdir@
+
+# the directory where pid files go
+PIDDIR = @piddir@
+# man pages language(s)
+man_langs = "@manlangs@"
+
+LIBSMBCLIENT_MAJOR=0
+LIBSMBCLIENT_MINOR=1
+
+
+FLAGS1 = $(CFLAGS) @FLAGS1@ -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I. $(CPPFLAGS) -I$(srcdir)
+FLAGS2 = -I/usr/src/newport/csm/include/linuxusp -I/usr/src/newport/csm/include/common -I/usr/src/newport/stp/include
+FLAGS3 = 
+FLAGS4 = 
+FLAGS5 = $(FLAGS1) $(FLAGS2) $(FLAGS3) $(FLAGS4)
+FLAGS  = $(ISA) $(FLAGS5) 
+FLAGS32  = $(ISA32) $(FLAGS5)
+
+PASSWD_FLAGS = -DSMB_PASSWD_FILE=\"$(SMB_PASSWD_FILE)\" -DPRIVATE_DIR=\"$(PRIVATE_DIR)\"
+PATH_FLAGS1 = -DCONFIGFILE=\"$(CONFIGFILE)\"  -DSBINDIR=\"$(SBINDIR)\"
+PATH_FLAGS2 = $(PATH_FLAGS1) -DBINDIR=\"$(BINDIR)\" -DDRIVERFILE=\"$(DRIVERFILE)\" 
+PATH_FLAGS3 = $(PATH_FLAGS2) -DLMHOSTSFILE=\"$(LMHOSTSFILE)\" 
+PATH_FLAGS4 = $(PATH_FLAGS3) -DSWATDIR=\"$(SWATDIR)\"  -DLOCKDIR=\"$(LOCKDIR)\" -DPIDDIR=\"$(PIDDIR)\"
+PATH_FLAGS5 = $(PATH_FLAGS4) -DLIBDIR=\"$(LIBDIR)\" \
+             -DLOGFILEBASE=\"$(LOGFILEBASE)\" -DSHLIBEXT=\"@SHLIBEXT@\"
+PATH_FLAGS6 = $(PATH_FLAGS5) -DCONFIGDIR=\"$(CONFIGDIR)\"
+PATH_FLAGS = $(PATH_FLAGS6) $(PASSWD_FLAGS)
+
+# Note that all executable programs now provide for an optional executable suffix.
+
+SBIN_PROGS = bin/smbd@EXEEXT@ bin/nmbd@EXEEXT@ bin/swat@EXEEXT@ \
+       bin/wrepld@EXEEXT@ @EXTRA_SBIN_PROGS@ 
+
+BIN_PROGS1 = bin/smbclient@EXEEXT@ bin/net@EXEEXT@ bin/smbspool@EXEEXT@ \
+       bin/testparm@EXEEXT@ bin/testprns@EXEEXT@ bin/smbstatus@EXEEXT@ 
+BIN_PROGS2 = bin/smbcontrol@EXEEXT@ bin/smbtree@EXEEXT@ bin/tdbbackup@EXEEXT@ \
+       bin/nmblookup@EXEEXT@ bin/pdbedit@EXEEXT@
+BIN_PROGS3 = bin/smbpasswd@EXEEXT@ bin/rpcclient@EXEEXT@ bin/smbcacls@EXEEXT@ \
+       bin/profiles@EXEEXT@ bin/smbgroupedit@EXEEXT@ bin/ntlm_auth@EXEEXT@ \
+       bin/editreg@EXEEXT@
+
+TORTURE_PROGS = bin/smbtorture@EXEEXT@ bin/gentest@EXEEXT@
+#bin/msgtest@EXEEXT@ \
+#      bin/masktest@EXEEXT@ bin/locktest@EXEEXT@ \
+#      bin/locktest2@EXEEXT@ bin/nsstest@EXEEXT@ bin/vfstest@EXEEXT@ \
+
+BIN_PROGS = $(BIN_PROGS1) $(BIN_PROGS2) $(BIN_PROGS3) @EXTRA_BIN_PROGS@
+
+SHLIBS = @SHLIB_PROGS@ @LIBSMBCLIENT@
+
+SCRIPTS = $(srcdir)/script/smbtar $(srcdir)/script/addtosmbpass $(srcdir)/script/convert_smbpasswd \
+         $(builddir)/script/findsmb
+
+# QUOTAOBJS=@QUOTAOBJS@
+
+VFS_MODULES = bin/vfs_audit.@SHLIBEXT@ bin/vfs_extd_audit.@SHLIBEXT@ bin/vfs_recycle.@SHLIBEXT@ \
+       bin/vfs_netatalk.@SHLIBEXT@ bin/vfs_fake_perms.@SHLIBEXT@
+PDB_MODULES = @MODULE_MYSQL@ @MODULE_XML@
+MODULES = bin/developer.@SHLIBEXT@ 
+
+######################################################################
+# object file lists
+######################################################################
+
+TDBBASE_OBJ = tdb/tdb.o tdb/spinlock.o
+TDB_OBJ = $(TDBBASE_OBJ) tdb/tdbutil.o 
+
+LIB_OBJ = lib/charcnv.o lib/debug.o lib/fault.o \
+          lib/getsmbpass.o lib/interface.o lib/md4.o \
+          lib/interfaces.o lib/pidfile.o lib/replace.o \
+          lib/signal.o lib/system.o lib/sendfile.o lib/time.o \
+         lib/genrand.o lib/username.o \
+         lib/util_getent.o lib/util_pw.o lib/smbrun.o \
+         lib/bitmap.o lib/crc32.o lib/snprintf.o lib/dprintf.o \
+         lib/xfile.o lib/wins_srv.o \
+         lib/util_str.o lib/util_sid.o lib/util_uuid.o \
+         lib/util_unistr.o lib/util_file.o lib/data_blob.o \
+         lib/util.o lib/util_sock.o \
+         lib/talloc.o lib/substitute.o lib/fsusage.o \
+         lib/ms_fnmatch.o lib/select.o lib/messages.o \
+         lib/tallocmsg.o lib/dmallocmsg.o \
+         lib/md5.o lib/hmacmd5.o lib/iconv.o lib/smbpasswd.o \
+         nsswitch/wb_client.o nsswitch/wb_common.o \
+         lib/pam_errors.o intl/lang_tdb.o lib/account_pol.o \
+         lib/gencache.o $(TDB_OBJ) \
+         lib/module.o lib/genparser.o \
+         lib/ldap_escape.o lib/events.o lib/mutex.o
+
+LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o $(LIB_OBJ)
+
+READLINE_OBJ = lib/readline.o
+
+POPT_LIB_OBJ = lib/popt_common.o 
+
+PARAM_OBJ = param/loadparm.o param/params.o dynconfig.o
+
+KRBCLIENT_OBJ = libads/kerberos.o
+
+#LIBADS_OBJ = libads/ldap.o libads/ldap_printer.o libads/sasl.o \
+#           libads/krb5_setpw.o libads/ldap_user.o \
+#           libads/ads_struct.o libads/ads_status.o \
+#             libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \
+#           libads/ads_ldap.o
+
+#LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o
+
+SECRETS_OBJ = passdb/secrets.o
+
+LIBNMB_OBJ = libcli/unexpected.o libcli/namecache.o libcli/nmblib.o \
+            libcli/namequery.o 
+
+LIBNTLMSSP_OBJ = libcli/ntlmssp.o libcli/ntlmssp_parse.o libcli/util/ntlmssp_sign.o
+
+LIBSAMBA_OBJ = libcli/util/nterr.o libcli/util/smbdes.o libcli/util/smbencrypt.o
+
+LIBCLIUTIL_OBJ = libcli/util/asn1.o \
+            libcli/util/smberr.o libcli/util/credentials.o \
+            libcli/util/doserr.o libcli/util/errormap.o \
+            libcli/util/pwd_cache.o libcli/util/clierror.o libcli/util/cliutil.o
+
+LIBRAW_OBJ = libcli/raw/rawfile.o libcli/raw/smb_signing.o  \
+            libcli/raw/clisocket.o libcli/raw/clitransport.o \
+            libcli/raw/clisession.o libcli/raw/clitree.o \
+            libcli/raw/clikrb5.o libcli/raw/clispnego.o libcli/raw/rawrequest.o \
+            libcli/raw/rawreadwrite.o \
+            libcli/raw/rawsearch.o libcli/raw/rawsetfileinfo.o libcli/raw/raweas.o \
+            libcli/raw/rawtrans.o libcli/raw/clioplock.o \
+            libcli/raw/rawnegotiate.o libcli/raw/rawfsinfo.o \
+            libcli/raw/rawfileinfo.o libcli/raw/rawnotify.o \
+            libcli/raw/rawioctl.o \
+            $(LIBSAMBA_OBJ) $(LIBCLIUTIL_OBJ) \
+            $(RPC_PARSE_OBJ1) $(LIBNTLMSSP_OBJ) $(LIBNMB_OBJ) $(KRBCLIENT_OBJ)
+
+LIBSMB_OBJ = libcli/clireadwrite.o libcli/cliconnect.o \
+            libcli/clifile.o libcli/clilist.o libcli/clitrans2.o  \
+            libcli/clisecdesc.o libcli/climessage.o \
+            libcli/clideltree.o \
+            $(LIBRAW_OBJ)
+
+# LIBDFS_OBJ = libcli/clidfs.o
+
+LIBMSRPC_OBJ = rpc_client/cli_lsarpc.o rpc_client/cli_samr.o \
+              rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o \
+              rpc_client/cli_wkssvc.o rpc_client/cli_dfs.o \
+              rpc_client/cli_reg.o rpc_client/cli_pipe.o \
+              rpc_client/cli_spoolss.o rpc_client/cli_spoolss_notify.o  \
+              rpc_client/cli_ds.o libcli/namequery_dc.o
+
+#LIBMSRPC_SERVER_OBJ = libcli/trust_passwd.o
+
+#REGOBJS_OBJ = registry/reg_objects.o
+#REGISTRY_OBJ = registry/reg_frontend.o registry/reg_cachehook.o registry/reg_printing.o \
+#               registry/reg_db.o 
+
+#RPC_LSA_OBJ = rpc_server/srv_lsa.o rpc_server/srv_lsa_nt.o
+
+#RPC_NETLOG_OBJ = rpc_server/srv_netlog.o rpc_server/srv_netlog_nt.o
+
+#RPC_SAMR_OBJ = rpc_server/srv_samr.o rpc_server/srv_samr_nt.o \
+#               rpc_server/srv_samr_util.o
+
+#RPC_REG_OBJ =  rpc_server/srv_reg.o rpc_server/srv_reg_nt.o
+
+#RPC_SVC_OBJ = rpc_server/srv_srvsvc.o rpc_server/srv_srvsvc_nt.o
+
+#RPC_WKS_OBJ =  rpc_server/srv_wkssvc.o rpc_server/srv_wkssvc_nt.o
+
+#RPC_DFS_OBJ =  rpc_server/srv_dfs.o rpc_server/srv_dfs_nt.o
+#RPC_SPOOLSS_OBJ = rpc_server/srv_spoolss.o rpc_server/srv_spoolss_nt.o 
+
+RPC_PIPE_OBJ = rpc_server/srv_pipe_hnd.o rpc_server/srv_util.o \
+               rpc_server/srv_pipe.o rpc_server/srv_lsa_hnd.o
+
+# These are like they are to avoid a dependency on GNU MAKE
+@LSA_DYNAMIC_YES@RPC_MODULES1 = bin/librpc_lsarpc.@SHLIBEXT@
+@NETLOG_DYNAMIC_YES@RPC_MODULES2 = bin/librpc_NETLOGON.@SHLIBEXT@
+@SAMR_DYNAMIC_YES@RPC_MODULES3 = bin/librpc_samr.@SHLIBEXT@
+@SVC_DYNAMIC_YES@RPC_MODULES4 = bin/librpc_srvsvc.@SHLIBEXT@
+@WKS_DYNAMIC_YES@RPC_MODULES5 = bin/librpc_wkssvc.@SHLIBEXT@
+@REG_DYNAMIC_YES@RPC_MODULES6 = bin/librpc_winreg.@SHLIBEXT@
+@SPOOLSS_DYNAMIC_YES@RPC_MODULES7 = bin/librpc_spoolss.@SHLIBEXT@
+@DFS_DYNAMIC_YES@RPC_MODULES8 = bin/librpc_netdfs.@SHLIBEXT@
+RPC_MODULES = $(RPC_MODULES1) $(RPC_MODULES2) $(RPC_MODULES3) $(RPC_MODULES4) \
+       $(RPC_MODULES5) $(RPC_MODULES6) $(RPC_MODULES7) $(RPC_MODULES8)
+
+@LSA_DYNAMIC_NO@RPC_PIPE_OBJ1 = $(RPC_LSA_OBJ)
+@NETLOG_DYNAMIC_NO@RPC_PIPE_OBJ2 = $(RPC_NETLOG_OBJ)
+@SAMR_DYNAMIC_NO@RPC_PIPE_OBJ3 = $(RPC_SAMR_OBJ)
+@SVC_DYNAMIC_NO@RPC_PIPE_OBJ4 = $(RPC_SVC_OBJ)
+@WKS_DYNAMIC_NO@RPC_PIPE_OBJ5 = $(RPC_WKS_OBJ)
+@REG_DYNAMIC_NO@RPC_PIPE_OBJ6 = $(RPC_REG_OBJ)
+@SPOOLSS_DYNAMIC_NO@RPC_PIPE_OBJ7 = $(RPC_SPOOLSS_OBJ)
+@DFS_DYNAMIC_NO@RPC_PIPE_OBJ8 =        $(RPC_DFS_OBJ)
+RPC_SERVER_OBJ = $(RPC_PIPE_OBJ1) $(RPC_PIPE_OBJ2) $(RPC_PIPE_OBJ3) \
+       $(RPC_PIPE_OBJ4) $(RPC_PIPE_OBJ5) $(RPC_PIPE_OBJ6) $(RPC_PIPE_OBJ7) \
+       $(RPC_PIPE_OBJ8) $(RPC_PIPE_OBJ)
+
+# this includes only the low level parse code, not stuff
+# that requires knowledge of security contexts
+RPC_PARSE_OBJ1 = rpc_parse/parse_prs.o rpc_parse/parse_sec.o \
+                rpc_parse/parse_misc.o
+
+RPC_PARSE_OBJ = rpc_parse/parse_lsa.o rpc_parse/parse_net.o \
+                rpc_parse/parse_reg.o rpc_parse/parse_rpc.o \
+                rpc_parse/parse_samr.o rpc_parse/parse_srv.o \
+                rpc_parse/parse_wks.o rpc_parse/parse_ds.o \
+               rpc_parse/parse_spoolss.o rpc_parse/parse_dfs.o \
+               $(REGOBJS_OBJ)
+
+
+RPC_CLIENT_OBJ = rpc_client/cli_pipe.o 
+
+#LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o
+
+PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o
+
+PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \
+               passdb/machine_sid.o passdb/pdb_smbpasswd.o \
+               passdb/pdb_tdb.o passdb/pdb_ldap.o \
+               passdb/pdb_unix.o passdb/pdb_guest.o passdb/util_sam_sid.o \
+               passdb/pdb_compat.o passdb/pdb_nisplus.o \
+               passdb/privileges.o 
+
+XML_OBJ = modules/xml.o
+MYSQL_OBJ = modules/mysql.o
+DEVEL_HELP_OBJ = modules/developer.o
+
+SAM_STATIC_MODULES = sam/sam_plugin.o sam/sam_skel.o sam/sam_ads.o
+
+SAM_OBJ = sam/account.o sam/get_set_account.o sam/get_set_group.o \
+               sam/get_set_domain.o sam/interface.o $(SAM_STATIC_MODULES)
+
+SAMTEST_OBJ = torture/samtest.o torture/cmd_sam.o $(SAM_OBJ) $(LIB_OBJ) $(PARAM_OBJ) $(LIBSMB_OBJ) $(READLINE_OBJ) lib/util_seaccess.o $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(GROUPDB_OBJ)
+
+GROUPDB_OBJ = groupdb/mapping.o
+
+# passdb/smbpass.o passdb/ldap.o passdb/nispass.o 
+
+#PROFILE_OBJ = profile/profile.o
+
+# OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
+
+# NOTIFY_OBJ = smbd/notify.o smbd/notify_hash.o smbd/notify_kernel.o
+
+PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o
+
+# UNIGRP_OBJ = libcli/netlogon_unigrp.o 
+
+AUTH_OBJ = auth/auth.o auth/auth_sam.o \
+          auth/auth_unix.o auth/auth_util.o    \
+          auth/auth_builtin.o auth/auth_compat.o \
+          $(PLAINTEXT_AUTH_OBJ) $(UNIGRP_OBJ)
+
+# auth/auth_server.o auth/auth_domain.o auth/auth_winbind.o auth/auth_ntlmssp.o 
+
+MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
+
+SMBD_OBJ_MAIN = smbd/server.o
+
+CSM_NTVFS_MAIN = ntvfs/tank/vfs_tank.o
+#we don't want these in main proto.h
+CSM_NTVFS_OBJ = ntvfs/tank/csm_init.o ntvfs/tank/csm_unlink.o \
+               ntvfs/tank/csm_util.o ntvfs/tank/csm_error.o ntvfs/tank/csm_lookup.o \
+               ntvfs/tank/csm_blockmap.o ntvfs/tank/csm_dir.o \
+               ntvfs/tank/csm_fcntl.o ntvfs/tank/csm_io.o ntvfs/tank/csm_mkdir.o \
+               ntvfs/tank/csm_open.o ntvfs/tank/csm_rename.o \
+               ntvfs/tank/csm_attr.o ntvfs/tank/csm_truncate.o \
+               ntvfs/tank/csm_fd.o
+
+@STFS_ENABLED@STFS_MAIN = $(CSM_NTVFS_MAIN)
+@STFS_ENABLED@STFS_OBJS = $(CSM_NTVFS_OBJ)
+@STFS_ENABLED@STFS_LIBS = -L/usr/lib -L/usr/src/newport/csm/lib -Wl,"-(,-lcsm,-lcsmlinuxusp,-)"
+
+SMBD_NTVFS_OBJ = ntvfs/ntvfs_base.o ntvfs/ntvfs_util.o ntvfs/ntvfs_generic.o \
+       ntvfs/simple/vfs_simple.o ntvfs/simple/svfs_util.o \
+       ntvfs/ipc/vfs_ipc.o ntvfs/cifs/vfs_cifs.o \
+       ntvfs/print/vfs_print.o
+
+SMBD_OBJ_SRV = smbd/connection.o \
+              smbd/session.o \
+           smbd/password.o smbd/conn.o \
+           smbd/negprot.o smbd/request.o \
+           smbd/reply.o smbd/sesssetup.o \
+          smbd/trans2.o \
+           lib/sysacls.o lib/server_mutex.o \
+           smbd/build_options.o smbd/service.o \
+          smbd/rewrite.o \
+              $(SMBD_NTVFS_OBJ) $(STFS_MAIN) @SMBD_EXTRA_OBJS@ 
+
+PROCESS_MODEL_OBJ = smbd/process.o smbd/process_model.o smbd/process_standard.o \
+                       smbd/process_single.o
+
+# lib/util_seaccess.o 
+
+# printing/printfsp.o
+
+SMBD_OBJ_BASE = $(PROCESS_MODEL_OBJ) $(SMBD_OBJ_SRV) $(STFS_OBJS) \
+               $(MSDFS_OBJ) $(PARAM_OBJ) \
+               $(SECRETS_OBJ) \
+               $(PASSDB_OBJ)  \
+               $(AUTH_OBJ) $(GROUPDB_OBJ) \
+               $(LIB_SMBD_OBJ) $(POPT_LIB_OBJ) $(LIBSMB_OBJ)           
+
+#  $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ)  $(LOCKING_OBJ) $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(LIBADS_SERVER_OBJ) 
+# $(PRINTING_OBJ) $(PROFILE_OBJ) $(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ) $(NOTIFY_OBJ) $(REGISTRY_OBJ) 
+
+#PRINTING_OBJ = printing/pcap.o printing/print_svid.o \
+#                              printing/print_cups.o printing/print_generic.o \
+#                              printing/lpq_parse.o printing/load.o
+
+#PRINTBACKEND_OBJ = printing/printing.o printing/nt_printing.o printing/notify.o \
+#              printing/printing_db.o
+
+# MSDFS_OBJ = msdfs/msdfs.o 
+
+SMBD_OBJ = $(SMBD_OBJ_MAIN) $(SMBD_OBJ_BASE)
+
+NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \
+            nmbd/nmbd_become_lmb.o nmbd/nmbd_browserdb.o \
+            nmbd/nmbd_browsesync.o nmbd/nmbd_elections.o \
+            nmbd/nmbd_incomingdgrams.o nmbd/nmbd_incomingrequests.o \
+            nmbd/nmbd_lmhosts.o nmbd/nmbd_logonnames.o nmbd/nmbd_mynames.o \
+            nmbd/nmbd_namelistdb.o nmbd/nmbd_namequery.o \
+            nmbd/nmbd_nameregister.o nmbd/nmbd_namerelease.o \
+            nmbd/nmbd_nodestatus.o nmbd/nmbd_packets.o \
+            nmbd/nmbd_processlogon.o nmbd/nmbd_responserecordsdb.o \
+            nmbd/nmbd_sendannounce.o nmbd/nmbd_serverlistdb.o \
+            nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \
+            nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o
+
+NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+           $(PROFILE_OBJ) $(LIB_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+WREPL_OBJ1 = wrepld/server.o wrepld/process.o wrepld/parser.o wrepld/socket.o \
+             wrepld/partners.o
+
+WREPL_OBJ = $(WREPL_OBJ1)  $(PARAM_OBJ) \
+           $(PROFILE_OBJ) $(LIB_OBJ)
+
+SWAT_OBJ1 = web/cgi.o web/diagnose.o web/startstop.o web/statuspage.o \
+           web/swat.o web/neg_lang.o 
+
+SWAT_OBJ = $(SWAT_OBJ1) $(PRINTING_OBJ) $(LIBSMB_OBJ) $(LOCKING_OBJ) \
+           $(PARAM_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(KRBCLIENT_OBJ) \
+          $(LIB_OBJ) $(GROUPDB_OBJ) $(PLAINTEXT_AUTH_OBJ)
+
+SMBSH_OBJ = smbwrapper/smbsh.o smbwrapper/shared.o \
+            $(PARAM_OBJ) $(LIB_OBJ)
+
+STATUS_OBJ = utils/status.o utils/rewrite.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+             $(PROFILE_OBJ) $(LIB_OBJ) $(POPT_LIB_OBJ)
+
+SMBCONTROL_OBJ = utils/smbcontrol.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+       $(PROFILE_OBJ) $(LIB_OBJ) utils/rewrite.o 
+#      printing/notify.o printing/printing_db.o
+
+SMBTREE_OBJ = utils/smbtree.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+             $(PROFILE_OBJ) $(LIB_OBJ) $(LIBSMB_OBJ) \
+            $(KRBCLIENT_OBJ)
+
+TESTPARM_OBJ = utils/testparm.o \
+               $(PARAM_OBJ) $(LIB_OBJ) $(POPT_LIB_OBJ)
+
+TESTPRNS_OBJ = utils/testprns.o $(PARAM_OBJ) $(PRINTING_OBJ) $(LIB_OBJ)
+
+SMBPASSWD_OBJ = utils/smbpasswd.o $(PARAM_OBJ) $(SECRETS_OBJ) \
+               $(LIBSMB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ)\
+                $(LIB_OBJ) $(KRBCLIENT_OBJ)
+
+PDBEDIT_OBJ = utils/pdbedit.o $(PARAM_OBJ) $(PASSDB_OBJ) $(LIBSAMBA_OBJ) \
+               $(LIB_OBJ) $(GROUPDB_OBJ) $(SECRETS_OBJ) \
+               $(POPT_LIB_OBJ)
+
+SMBGROUPEDIT_OBJ = utils/smbgroupedit.o $(GROUPDB_OBJ) $(PARAM_OBJ) \
+               $(LIBSAMBA_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(LIB_OBJ)
+
+RPCCLIENT_OBJ1 = rpcclient/rpcclient.o rpcclient/cmd_lsarpc.o \
+                rpcclient/cmd_samr.o rpcclient/cmd_spoolss.o \
+                rpcclient/cmd_netlogon.o rpcclient/cmd_srvsvc.o \
+                rpcclient/cmd_dfs.o rpcclient/cmd_reg.o \
+                rpcclient/display_sec.o rpcclient/cmd_ds.o
+
+RPCCLIENT_OBJ = $(RPCCLIENT_OBJ1) \
+             $(PARAM_OBJ) $(LIBSMB_OBJ) $(LIB_OBJ) \
+             $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(LIBMSRPC_OBJ) \
+             $(READLINE_OBJ) $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) \
+            $(LIBADS_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+PAM_WINBIND_OBJ = nsswitch/pam_winbind.po nsswitch/wb_common.po lib/snprintf.po
+
+#SMBW_OBJ1 = smbwrapper/smbw.o \
+#              smbwrapper/smbw_dir.o smbwrapper/smbw_stat.o \
+#              smbwrapper/realcalls.o smbwrapper/shared.o \
+#              smbwrapper/smbw_cache.o
+
+SMBW_OBJ = $(SMBW_OBJ1) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+                $(LIB_OBJ)
+
+SMBWRAPPER_OBJ1 = smbwrapper/wrapped.o
+
+SMBWRAPPER_OBJ = $(SMBW_OBJ) $(SMBWRAPPER_OBJ1)
+
+LIBSMBCLIENT_OBJ = libcli/libcliclient.o libcli/libcli_compat.o \
+                  libcli/libcli_cache.o $(LIB_OBJ) \
+                  $(LIBSMB_OBJ) $(PARAM_OBJ)
+
+# This shared library is intended for linking with unit test programs
+# to test Samba internals.  It's called libbigballofmud.so to
+# discourage casual usage.
+
+LIBBIGBALLOFMUD_MAJOR = 0
+
+LIBBIGBALLOFMUD_OBJ = $(LIB_OBJ) $(PARAM_OBJ) $(SECRETS_OBJ) \
+       $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \
+       $(GROUPDB_OBJ) $(KRBCLIENT_OBJ)
+
+LIBBIGBALLOFMUD_PICOBJS = $(LIBBIGBALLOFMUD_OBJ:.o=.po)
+
+CLIENT_OBJ1 = client/client.o client/clitar.o libcli/raw/clirewrite.o
+
+CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) \
+            $(LIB_OBJ) \
+             $(READLINE_OBJ) $(POPT_LIB_OBJ) 
+
+NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
+          utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
+          utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
+          utils/net_cache.o
+
+NET_OBJ = $(NET_OBJ1) $(SECRETS_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+         $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+         $(PARAM_OBJ) $(LIB_OBJ) \
+         $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_OBJ) \
+         $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ)
+
+CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+         $(LIB_OBJ) $(KRBCLIENT_OBJ)
+
+MOUNT_OBJ = client/smbmount.o \
+             $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(LIB_OBJ) 
+
+MNT_OBJ = client/smbmnt.o              
+
+UMOUNT_OBJ = client/smbumount.o
+
+NMBLOOKUP_OBJ = utils/nmblookup.o $(PARAM_OBJ) $(LIBNMB_OBJ) \
+               $(LIB_OBJ)
+
+
+SMBTORTURE_RAW_OBJ = torture/raw/qfsinfo.o torture/raw/qfileinfo.o torture/raw/setfileinfo.o \
+               torture/raw/search.o torture/raw/close.o torture/raw/open.o torture/raw/mkdir.o \
+               torture/raw/oplock.o torture/raw/notify.o torture/raw/mux.o torture/raw/ioctl.o \
+               torture/raw/chkpath.o torture/raw/unlink.o torture/raw/read.o torture/raw/context.o \
+               torture/raw/write.o torture/raw/lock.o torture/raw/rename.o torture/raw/seek.o 
+
+SMBTORTURE_OBJ1 = torture/torture.o torture/torture_util.o torture/nbio.o torture/scanner.o \
+               torture/utable.o torture/denytest.o torture/mangle_test.o \
+               torture/aliases.o libcli/raw/clirewrite.o $(SMBTORTURE_RAW_OBJ)
+
+SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) \
+       $(LIBSMB_OBJ) $(LIBDFS_OBJ) $(PARAM_OBJ) $(LIB_OBJ)
+
+
+MASKTEST_OBJ = torture/masktest.o $(LIBSMB_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ) libcli/raw/clirewrite.o
+
+MSGTEST_OBJ = torture/msgtest.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ)
+
+LOCKTEST_OBJ = torture/locktest.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ) libcli/raw/clirewrite.o
+
+GENTEST_OBJ = torture/gentest.o torture/torture_util.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ) libcli/raw/clirewrite.o
+
+NSSTEST_OBJ = torture/nsstest.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ)
+
+VFSTEST_OBJ = torture/cmd_vfs.o torture/vfstest.o $(READLINE_OBJ)
+
+VFS_AUDIT_OBJ = modules/vfs_audit.o
+VFS_EXTD_AUDIT_OBJ = modules/vfs_extd_audit.o
+VFS_RECYCLE_OBJ = modules/vfs_recycle.o
+VFS_NETATALK_OBJ = modules/vfs_netatalk.o
+VFS_FAKE_PERMS_OBJ = modules/vfs_fake_perms.o
+
+LOCKTEST2_OBJ = torture/locktest2.o $(LOCKING_OBJ) $(LIBSMB_OBJ) \
+               $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ)
+
+SMBCACLS_OBJ = utils/smbcacls.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+               $(PARAM_OBJ) \
+                 $(LIB_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ) \
+                $(LIBMSRPC_OBJ) $(SECRETS_OBJ)
+
+TALLOCTORT_OBJ = lib/talloctort.o  $(LIB_OBJ) $(PARAM_OBJ)
+
+RPCTORTURE_OBJ = torture/rpctorture.o \
+             rpcclient/display.o \
+             rpcclient/cmd_lsarpc.o \
+             rpcclient/cmd_wkssvc.o \
+             rpcclient/cmd_samr.o \
+             rpcclient/cmd_srvsvc.o \
+             rpcclient/cmd_netlogon.o \
+             $(PARAM_OBJ) $(LIBSMB_OBJ) $(LIB_OBJ) $(KRBCLIENT_OBJ) \
+             $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ)
+
+DEBUG2HTML_OBJ = utils/debug2html.o ubiqx/debugparse.o
+
+SMBFILTER_OBJ = utils/smbfilter.o $(LIBSMB_OBJ) $(PARAM_OBJ) \
+                 $(LIB_OBJ) $(KRBCLIENT_OBJ) 
+
+PROTO_OBJ = $(SMBD_OBJ_SRV) \
+           $(SMBD_OBJ_MAIN) $(PROCESS_MODEL_OBJ) \
+           $(NMBD_OBJ1) $(SWAT_OBJ1) $(LIBSMB_OBJ) \
+           $(LIBRAW_OBJ) $(LIBDFS_OBJ) $(LIBCLIUTIL) $(LIBNTLMSSP_OBJ) \
+           $(SMBW_OBJ1) $(SMBWRAPPER_OBJ1) $(SMBTORTURE_OBJ1) $(RPCCLIENT_OBJ1) \
+           $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_OBJ) $(RPC_CLIENT_OBJ) \
+           $(RPC_PIPE_OBJ) $(RPC_PARSE_OBJ) $(KRBCLIENT_OBJ) \
+           $(AUTH_OBJ) $(PARAM_OBJ) $(LOCKING_OBJ) $(SECRETS_OBJ) \
+           $(PRINTING_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) $(NOTIFY_OBJ) \
+           $(QUOTAOBJS) $(PASSDB_OBJ) $(GROUPDB_OBJ) $(MSDFS_OBJ) \
+           $(READLINE_OBJ) $(PROFILE_OBJ) $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) \
+           $(LIB_SMBD_OBJ) $(SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \
+           $(RPC_LSA_OBJ) $(RPC_NETLOG_OBJ) $(RPC_SAMR_OBJ) $(RPC_REG_OBJ) \
+           $(RPC_SVC_OBJ) $(RPC_WKS_OBJ) $(RPC_DFS_OBJ) $(RPC_SPOOLSS_OBJ)
+
+NSS_OBJ_0 = nsswitch/wins.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+           $(LIB_OBJ) $(NSSWINS_OBJ)
+
+NSS_OBJ = $(NSS_OBJ_0:.o=.po)
+
+PICOBJS = $(SMBWRAPPER_OBJ:.o=.po)
+PICOBJS32 = $(SMBWRAPPER_OBJ:.o=.po32)
+LIBSMBCLIENT_PICOBJS = $(LIBSMBCLIENT_OBJ:.o=.po)
+
+PAM_SMBPASS_OBJ_0 = pam_smbpass/pam_smb_auth.o pam_smbpass/pam_smb_passwd.o \
+               pam_smbpass/pam_smb_acct.o pam_smbpass/support.o \
+               libcli/smbencrypt.o libcli/smbdes.o libcli/nterr.o \
+               $(PARAM_OBJ) $(LIB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+               $(SECRETS_OBJ)
+
+PAM_SMBPASS_PICOOBJ = $(PAM_SMBPASS_OBJ_0:.o=.po)
+
+WINBINDD_OBJ1 = \
+               nsswitch/winbindd.o       \
+               nsswitch/winbindd_user.o  \
+               nsswitch/winbindd_group.o \
+               nsswitch/winbindd_idmap.o \
+               nsswitch/winbindd_idmap_tdb.o \
+               nsswitch/winbindd_util.o  \
+               nsswitch/winbindd_cache.o \
+               nsswitch/winbindd_pam.o   \
+               nsswitch/winbindd_sid.o   \
+               nsswitch/winbindd_misc.o  \
+               nsswitch/winbindd_cm.o \
+               nsswitch/winbindd_wins.o \
+               nsswitch/winbindd_rpc.o \
+               nsswitch/winbindd_ads.o \
+               nsswitch/winbindd_dual.o
+
+WINBINDD_OBJ = \
+               $(WINBINDD_OBJ1) $(PASSDB_GET_SET_OBJ) \
+               $(PARAM_OBJ) $(LIB_OBJ) \
+               $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) \
+               $(PROFILE_OBJ) $(UNIGRP_OBJ) \
+               $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ)
+
+WBINFO_OBJ = nsswitch/wbinfo.o libcli/smbencrypt.o libcli/smbdes.o $(POPT_LIB_OBJ)
+
+WINBIND_NSS_OBJ = nsswitch/winbind_nss.o nsswitch/wb_common.o @WINBIND_NSS_EXTRA_OBJS@
+
+WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.po)
+
+POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+          popt/popthelp.o popt/poptparse.o
+
+TDBBACKUP_OBJ = tdb/tdbbackup.o $(TDBBASE_OBJ)
+
+NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBNTLMSSP_OBJ) $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ)
+
+######################################################################
+# now the rules...
+######################################################################
+all: bin/smbd bin/smbclient bin/smbtorture bin/locktest bin/masktest bin/gentest
+
+#SHOWFLAGS proto_exists $(SBIN_PROGS) $(BIN_PROGS) $(SHLIBS) \
+#      $(TORTURE_PROGS) $(RPC_MODULES) @EXTRA_ALL_TARGETS@
+
+pam_smbpass : SHOWFLAGS bin/pam_smbpass.@SHLIBEXT@
+
+smbwrapper : SHOWFLAGS @SMBWRAPPER@
+
+torture : SHOWFLAGS $(TORTURE_PROGS)
+
+smbtorture : SHOWFLAGS bin/smbtorture@EXEEXT@
+
+gentest: SHOWFLAGS bin/gentest@EXEEXT@
+
+masktest : SHOWFLAGS bin/masktest@EXEEXT@
+
+msgtest : SHOWFLAGS bin/msgtest@EXEEXT@
+
+locktest : SHOWFLAGS bin/locktest@EXEEXT@
+
+smbcacls : SHOWFLAGS bin/smbcacls@EXEEXT@
+
+locktest2 : SHOWFLAGS bin/locktest2@EXEEXT@
+
+rpctorture : SHOWFLAGS bin/rpctorture@EXEEXT@
+
+debug2html : SHOWFLAGS bin/debug2html@EXEEXT@
+
+smbfilter : SHOWFLAGS bin/smbfilter@EXEEXT@
+
+talloctort : SHOWFLAGS bin/talloctort@EXEEXT@
+
+nsswitch : SHOWFLAGS bin/winbindd@EXEEXT@ bin/wbinfo@EXEEXT@ nsswitch/libnss_winbind.@SHLIBEXT@ nsswitch/pam_winbind.@SHLIBEXT@
+
+wins : SHOWFLAGS nsswitch/libnss_wins.@SHLIBEXT@
+
+modules: SHOWFLAGS proto_exists $(VFS_MODULES) $(PDB_MODULES) $(MODULES)
+
+everything: all libsmbclient debug2html smbfilter talloctort
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .po32 .lo
+
+SHOWFLAGS:
+       @echo "Using FLAGS = $(FLAGS)"
+       @echo "      FLAGS32 = $(FLAGS32)"
+       @echo "      LIBS = $(LIBS)"
+       @echo "      LDSHFLAGS = $(LDSHFLAGS)"
+       @echo "      LDFLAGS = $(LDFLAGS)"
+
+MAKEDIR = || exec false; \
+         if test -d "$$dir"; then :; else \
+         echo mkdir "$$dir"; \
+         mkdir -p "$$dir" >/dev/null 2>&1 || \
+         test -d "$$dir" || \
+         mkdir "$$dir" || \
+         exec false; fi || exec false
+
+.c.o:
+       @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+        dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+       @echo Compiling $*.c
+       @$(CC) -I. -I$(srcdir) $(FLAGS) -c $< \
+         -o $@ 
+@BROKEN_CC@    -mv `echo $@ | sed 's%^.*/%%g'` $@
+
+# 'make pch' is extremely useful for fast compiles if you have gcc-3.4
+pch:
+       $(CC) -I. -I$(srcdir) $(FLAGS) -c $(srcdir)/include/includes.h -o $(srcdir)/include/includes.h.gch
+
+
+# These dependencies are only approximately correct: we want to make
+# sure Samba's paths are updated if ./configure is re-run.  Really it
+# would be nice if "make prefix=/opt/samba all" also rebuilt things,
+# but since we also require "make install prefix=/opt/samba" *not* to
+# rebuild it's a bit hard.
+
+dynconfig.o: dynconfig.c Makefile
+       @echo Compiling $*.c
+       @$(CC) $(FLAGS) $(PATH_FLAGS) -c $< -o $@ 
+
+dynconfig.po: dynconfig.c Makefile
+       @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+         dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+       @echo Compiling $*.c with @PICFLAG@
+       @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAG@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@    -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po$$%.o%'` $@
+@POBAD_CC@     @mv $*.po.o $@
+
+.c.po: 
+       @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+         dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+       @echo Compiling $*.c with @PICFLAG@
+       @$(CC) -I. -I$(srcdir) $(FLAGS) @PICFLAG@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@    -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po$$%.o%'` $@
+@POBAD_CC@     @mv $*.po.o $@
+
+# this is for IRIX
+.c.po32: 
+       @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+         dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+       @echo Compiling $*.c with @PICFLAG@ and -32
+       @$(CC) -32 -I. -I$(srcdir) $(FLAGS32) $(PATH_FLAGS) @PICFLAG@ -c $< \
+         -o $*.po32.o 
+@BROKEN_CC@    -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po32$$%.o%'` $@.o
+       @mv $*.po32.o $@
+
+bin/.dummy:
+       @if (: >> $@ || : > $@) >/dev/null 2>&1; then :; else \
+         dir=bin $(MAKEDIR); fi
+       @: >> $@ || : > $@ # what a fancy emoticon!
+
+bin/smbd@EXEEXT@: $(SMBD_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBD_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINTLIBS) \
+         $(AUTHLIBS) $(ACLLIBS) $(LIBS) $(PTHREAD_LIB) @SMBD_EXTRA_LIBS@ $(STFS_LIBS) @BUILD_POPT@
+
+bin/nmbd@EXEEXT@: $(NMBD_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(NMBD_OBJ) $(LDFLAGS) $(LIBS) @BUILD_POPT@
+
+bin/wrepld@EXEEXT@: $(WREPL_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(WREPL_OBJ) $(LDFLAGS) $(LIBS) 
+
+bin/swat@EXEEXT@: $(SWAT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SWAT_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINTLIBS) \
+         $(AUTHLIBS) $(LIBS) 
+
+bin/rpcclient@EXEEXT@: $(RPCCLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(RPCCLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @BUILD_POPT@
+
+bin/smbclient@EXEEXT@: $(CLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @BUILD_POPT@
+
+bin/net@EXEEXT@: $(NET_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(NET_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @BUILD_POPT@
+
+bin/profiles@EXEEXT@: utils/profiles.o bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ utils/profiles.o $(LDFLAGS) $(LIBS)
+
+bin/editreg@EXEEXT@: utils/editreg.o bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ utils/editreg.o $(LDFLAGS) $(LIBS)
+
+bin/smbspool@EXEEXT@: $(CUPS_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(CUPS_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbmount@EXEEXT@: $(MOUNT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(MOUNT_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbmnt@EXEEXT@: $(MNT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(MNT_OBJ) $(LDFLAGS) 
+
+bin/smbumount@EXEEXT@: $(UMOUNT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(UMOUNT_OBJ) $(LDFLAGS)
+
+bin/testparm@EXEEXT@: $(TESTPARM_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(TESTPARM_OBJ) $(LDFLAGS) $(LIBS) @BUILD_POPT@
+
+bin/testprns@EXEEXT@: $(TESTPRNS_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(TESTPRNS_OBJ) $(LDFLAGS) $(PRINTLIBS) $(LIBS)
+
+bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(STATUS_OBJ) $(LDFLAGS) $(LIBS) @BUILD_POPT@
+
+bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) -DUSING_SMBCONTROL $(FLAGS) -o $@ $(SMBCONTROL_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBTREE_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbpasswd@EXEEXT@: $(SMBPASSWD_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBPASSWD_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS)
+
+bin/pdbedit@EXEEXT@: $(PDBEDIT_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @BUILD_POPT@
+
+bin/samtest@EXEEXT@: $(SAMTEST_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SAMTEST_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @BUILD_POPT@
+
+bin/smbgroupedit@EXEEXT@: $(SMBGROUPEDIT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBGROUPEDIT_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/nmblookup@EXEEXT@: $(NMBLOOKUP_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(NMBLOOKUP_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbtorture@EXEEXT@: $(SMBTORTURE_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBTORTURE_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/gentest@EXEEXT@: $(GENTEST_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(GENTEST_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/talloctort@EXEEXT@: $(TALLOCTORT_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(TALLOCTORT_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/masktest@EXEEXT@: $(MASKTEST_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(MASKTEST_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/msgtest@EXEEXT@: $(MSGTEST_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(MSGTEST_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbcacls@EXEEXT@: $(SMBCACLS_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBCACLS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS)
+
+bin/locktest@EXEEXT@: $(LOCKTEST_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(LOCKTEST_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/nsstest@EXEEXT@: $(NSSTEST_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(NSSTEST_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/vfstest@EXEEXT@: $(VFSTEST_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(VFSTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(PRINTLIBS) $(AUTHLIBS) $(ACLLIBS) $(LIBS) @BUILD_POPT@
+
+bin/locktest2@EXEEXT@: $(LOCKTEST2_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(LOCKTEST2_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/rpctorture@EXEEXT@: $(RPCTORTURE_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(RPCTORTURE_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS)
+
+bin/debug2html@EXEEXT@: $(DEBUG2HTML_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(DEBUG2HTML_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbfilter@EXEEXT@: $(SMBFILTER_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBFILTER_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbw_sample@EXEEXT@: $(SMBW_OBJ) utils/smbw_sample.o bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBW_OBJ) utils/smbw_sample.o $(LDFLAGS) $(LIBS)
+
+bin/smbsh@EXEEXT@: $(SMBSH_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(SMBSH_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbwrapper.@SHLIBEXT@: $(PICOBJS) bin/.dummy
+       @echo Linking shared library $@
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(PICOBJS) $(LIBS) \
+               @SONAMEFLAG@`basename $@`
+
+bin/smbwrapper.32.@SHLIBEXT@: $(PICOBJS32)
+       @echo Linking shared library $@
+       @$(SHLD) -32 $(LDSHFLAGS) -o $@ $(PICOBJS32) $(LIBS) \
+               @SONAMEFLAG@`basename $@`
+
+bin/libsmbclient.@SHLIBEXT@: $(LIBSMBCLIENT_PICOBJS)
+       @echo Linking libsmbclient shared library $@
+       $(SHLD) $(LDSHFLAGS) -o $@ $(LIBSMBCLIENT_PICOBJS) $(LDFLAGS) $(LIBS) \
+               @SONAMEFLAG@`basename $@`.$(LIBSMBCLIENT_MAJOR)
+
+bin/libsmbclient.a: $(LIBSMBCLIENT_PICOBJS)
+       @echo Linking libsmbclient non-shared library $@
+       -$(AR) -rc $@ $(LIBSMBCLIENT_PICOBJS) 
+
+bin/libbigballofmud.@SHLIBEXT@: $(LIBBIGBALLOFMUD_PICOBJS)
+       @echo Linking bigballofmud shared library $@
+       $(SHLD) $(LDSHFLAGS) -o $@ $(LIBBIGBALLOFMUD_PICOBJS) $(LIBS) \
+               @SONAMEFLAG@`basename $@`.$(LIBBIGBALLOFMUD_MAJOR)
+
+libsmbclient: bin/libsmbclient.a @LIBSMBCLIENT_SHARED@
+
+bin/librpc_lsarpc.@SHLIBEXT@: $(RPC_LSA_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_LSA_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_samr.@SHLIBEXT@: $(RPC_SAMR_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SAMR_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_srvsvc.@SHLIBEXT@: $(RPC_SVC_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SVC_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_wkssvc.@SHLIBEXT@: $(RPC_WKS_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_WKS_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_NETLOGON.@SHLIBEXT@: $(RPC_NETLOG_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_NETLOG_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_winreg.@SHLIBEXT@: $(RPC_REG_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_REG_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_spoolss.@SHLIBEXT@: $(RPC_SPOOLSS_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SPOOLSS_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/librpc_netdfs.@SHLIBEXT@: $(RPC_DFS_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_DFS_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+nsswitch/libnss_wins.@SHLIBEXT@: $(NSS_OBJ)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(NSS_OBJ) -lc \
+               @SONAMEFLAG@`basename $@`
+
+bin/winbindd@EXEEXT@: $(WINBINDD_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(LINK) -o $@ $(WINBINDD_OBJ) $(DYNEXP) $(LIBS)
+
+nsswitch/libns_winbind.@SHLIBEXT@: $(WINBIND_NSS_PICOBJS)
+       @echo "Linking $@"
+       @$(SHLD) @LDSHFLAGS@ -o $@ $(WINBIND_NSS_PICOBJS) @WINBIND_NSS_EXTRA_LIBS@ \
+               @SONAMEFLAG@`basename $@`
+
+nsswitch/libnss_winbind.@SHLIBEXT@: $(WINBIND_NSS_PICOBJS)
+       @echo "Linking $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(WINBIND_NSS_PICOBJS) @WINBIND_NSS_EXTRA_LIBS@ \
+               @SONAMEFLAG@`basename $@`
+
+nsswitch/pam_winbind.@SHLIBEXT@: $(PAM_WINBIND_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_WINBIND_OBJ) \
+               @SONAMEFLAG@`basename $@` -lpam
+
+bin/mysql.@SHLIBEXT@: $(MYSQL_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(MYSQL_OBJ) @MYSQL_LIBS@ \
+               @SONAMEFLAG@`basename $@`
+
+bin/developer.@SHLIBEXT@: $(DEVEL_HELP_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(DEVEL_HELP_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/xml.@SHLIBEXT@: $(XML_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(XML_OBJ) @XML_LIBS@ \
+               @SONAMEFLAG@`basename $@`
+
+bin/vfs_audit.@SHLIBEXT@: $(VFS_AUDIT_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/vfs_extd_audit.@SHLIBEXT@: $(VFS_EXTD_AUDIT_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/vfs_recycle.@SHLIBEXT@: $(VFS_RECYCLE_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_RECYCLE_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/vfs_netatalk.@SHLIBEXT@: $(VFS_NETATALK_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_NETATALK_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/vfs_fake_perms.@SHLIBEXT@: $(VFS_FAKE_PERMS_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_FAKE_PERMS_OBJ) \
+               @SONAMEFLAG@`basename $@`
+
+bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
+               $(SECRETS_OBJ) @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(LINK) -o $@ $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
+               $(SECRETS_OBJ) $(LIBS) @BUILD_POPT@
+
+bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
+               @BUILD_POPT@ bin/.dummy
+       @echo Linking $@
+       @$(LINK) -o $@ $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
+               $(LIBS) @BUILD_POPT@
+
+bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ)
+       @echo "Linking shared library $@"
+       $(SHLD) $(LDSHFLAGS) -o $@ $(PAM_SMBPASS_PICOOBJ) -lpam $(DYNEXP) $(LIBS) -lc
+
+bin/libmsrpc.a: $(LIBMSRPC_PICOBJ)
+       -$(AR) -rc $@ $(LIBMSRPC_PICOBJ) 
+
+bin/tdbbackup@EXEEXT@: $(TDBBACKUP_OBJ) bin/.dummy
+       @echo Linking $@
+       @$(CC) $(FLAGS) -o $@ $(TDBBACKUP_OBJ)
+
+install: installbin installman installscripts installdat installswat 
+
+# DESTDIR is used here to prevent packagers wasting their time
+# duplicating the Makefile. Remove it and you will have the privelege
+# of package each samba release for muliple versions of multiple
+# distributions and operating systems, or at least supplying patches
+# to all the packaging files required for this, prior to committing
+# the removal of DESTDIR. Do not remove it even though you think it
+# is not used
+
+installdirs:
+       @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(PRIVATEDIR) $(DESTDIR)$(VFSLIBDIR) $(DESTDIR)$(PDBLIBDIR) $(DESTDIR)$(PIDDIR) $(DESTDIR)$(LOCKDIR)
+
+installservers: all installdirs
+       @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+
+installbin: all installdirs
+       @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+       @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(BIN_PROGS)
+
+       @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(RPC_MODULES)
+
+installmodules: all installdirs
+       @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(VFS_MODULES)
+       @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(PDB_MODULES)
+
+installscripts: installdirs
+       @$(SHELL) $(srcdir)/script/installscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+installdat: installdirs
+       @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(LIBDIR) $(srcdir)
+
+installswat: installdirs
+       @$(SHELL) $(srcdir)/script/installswat.sh $(DESTDIR)$(SWATDIR) $(srcdir)
+
+installclientlib:
+       -$(INSTALLCLIENTCMD_SH) bin/libsmbclient.@SHLIBEXT@ $(DESTDIR)${prefix}/lib
+       -$(INSTALLCLIENTCMD_A) bin/libsmbclient.a $(DESTDIR)${prefix}/lib
+       -$(INSTALLCMD) -d $(DESTDIR)${prefix}/include
+       -$(INSTALLCMD) include/libsmbclient.h $(DESTDIR)${prefix}/include
+
+# Python extensions
+
+PYTHON_OBJS = $(LIB_OBJ) $(LIBSMB_OBJ) $(RPC_PARSE_OBJ) \
+       $(PARAM_OBJ) $(LIBMSRPC_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+       $(SECRETS_OBJ) $(KRBCLIENT_OBJ)
+
+python_ext: $(PYTHON_OBJS)
+       @if test -z "$(PYTHON)"; then \
+               echo Use the option --with-python to configure python; \
+               exit 1; fi
+       PYTHON_OBJS="$(PYTHON_OBJS)" PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS) $(FLAGS)" \
+       LIBS="$(LIBS)" \
+               $(PYTHON) python/setup.py build
+
+python_install: $(PYTHON_OBJS)
+       @if test -z "$(PYTHON)"; then \
+               echo Use the option --with-python to configure python; \
+               exit 1; fi
+       PYTHON_OBJS="$(PYTHON_OBJS)" PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS)" \
+       LIBS="$(LIBS)" \
+               $(PYTHON) python/setup.py install
+
+python_clean:
+       @-if test -n "$(PYTHON)"; then $(PYTHON) python/setup.py clean; fi
+
+# revert to the previously installed version
+revert:
+       @$(SHELL) $(srcdir)/script/revert.sh $(SBINDIR) $(SBIN_PROGS) 
+       @$(SHELL) $(srcdir)/script/revert.sh $(BINDIR) $(BIN_PROGS) $(SCRIPTS)
+
+installman:
+       @$(SHELL) $(srcdir)/script/installman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs) "@ROFF@"
+
+.PHONY: showlayout
+
+showlayout: 
+       @echo "Samba will be installed into:"
+       @echo "  basedir: $(BASEDIR)"
+       @echo "  bindir:  $(BINDIR)"
+       @echo "  sbindir: $(SBINDIR)"
+       @echo "  libdir:  $(LIBDIR)"
+       @echo "  vardir:  $(VARDIR)"
+       @echo "  mandir:  $(MANDIR)"
+
+
+uninstall: uninstallman uninstallbin uninstallscripts
+
+uninstallman:
+       @$(SHELL) $(srcdir)/script/uninstallman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs)
+
+uninstallbin:
+       @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(SBIN_PROGS)
+       @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(BIN_PROGS)
+       @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(DESTDIR)$(RPC_MODULES)
+
+uninstallmodules:
+       @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(DESTDIR)$(VFS_MODULES)
+       @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(DESTDIR)$(PDB_MODULES)
+       @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(MODULES)
+
+uninstallscripts:
+       @$(SHELL) $(srcdir)/script/uninstallscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+# Toplevel clean files
+TOPFILES=dynconfig.o dynconfig.po
+
+clean: delheaders python_clean
+       -rm -f core */*~ *~ */*.o */*/*.o */*/*.po */*/*.po32 */*.po */*.po32 */*.@SHLIBEXT@ \
+               $(TOPFILES) $(BIN_PROGS) $(SBIN_PROGS) $(MODULES) $(TORTURE_PROGS) .headers.stamp
+
+# Making this target will just make sure that the prototype files
+# exist, not necessarily that they are up to date.  Since they're
+# removed by "make clean" this will always be run when you do anything
+# afterwards.
+proto_exists: include/proto.h include/wrepld_proto.h include/build_env.h \
+               nsswitch/winbindd_proto.h web/swat_proto.h \
+@STFS_ENABLED@ ntvfs/tank/vfs_tank_proto.h \
+               client/client_proto.h utils/net_proto.h \
+               include/tdbsam2_parse_info.h
+
+delheaders:
+       @echo Removing prototype headers
+       @/bin/rm -f $(srcdir)/include/proto.h $(srcdir)/include/build_env.h 
+       @/bin/rm -f $(srcdir)/include/wrepld_proto.h $(srcdir)/nsswitch/winbindd_proto.h 
+       @/bin/rm -f $(srcdir)/web/swat_proto.h
+       @/bin/rm -f $(srcdir)/client/client_proto.h $(srcdir)/utils/net_proto.h
+       @/bin/rm -f $(srcdir)/include/tdbsam2_parse_info.h
+@STFS_ENABLED@ @/bin/rm -f $(srcdir)/ntvfs/tank/vfs_tank_proto.h
+
+include/proto.h:
+       @echo Building include/proto.h
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _PROTO_H_ $(builddir)/include/proto.h \
+         $(PROTO_OBJ)
+
+include/build_env.h:
+       @echo Building include/build_env.h
+       @cd $(srcdir) && $(SHELL) script/build_env.sh $(srcdir) $(builddir) $(CC) > $(builddir)/include/build_env.h
+
+include/wrepld_proto.h:
+       @echo Building include/wrepld_proto.h
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _WREPLD_PROTO_H_ $(builddir)/include/wrepld_proto.h \
+         $(WREPL_OBJ1)
+
+nsswitch/winbindd_proto.h: 
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _WINBINDD_PROTO_H_ nsswitch/winbindd_proto.h \
+         $(WINBINDD_OBJ1)
+
+ntvfs/tank/vfs_tank_proto.h: 
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _VFS_TANK_PROTO_H_ ntvfs/tank/vfs_tank_proto.h \
+         $(STFS_OBJS)
+
+web/swat_proto.h: 
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _SWAT_PROTO_H_ web/swat_proto.h \
+         $(SWAT_OBJ1)
+
+client/client_proto.h: 
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _CLIENT_PROTO_H_ client/client_proto.h \
+         $(CLIENT_OBJ1)
+
+utils/net_proto.h: 
+       @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \
+         -h _CLIENT_PROTO_H_ utils/net_proto.h \
+         $(NET_OBJ1)
+
+include/tdbsam2_parse_info.h:
+       @cd $(srcdir) && @PERL@ -w script/genstruct.pl \
+       -o include/tdbsam2_parse_info.h $(CC) -E -g \
+       include/tdbsam2.h
+
+# "make headers" or "make proto" calls a subshell because we need to
+# make sure these commands are executed in sequence even for a
+# parallel make.
+headers: 
+       $(MAKE) delheaders; \
+       $(MAKE) include/proto.h; \
+       $(MAKE) include/build_env.h; \
+       $(MAKE) include/wrepld_proto.h; \
+       $(MAKE) nsswitch/winbindd_proto.h; \
+       $(MAKE) web/swat_proto.h; \
+       $(MAKE) client/client_proto.h; \
+       $(MAKE) utils/net_proto.h; \
+       $(MAKE) include/tdbsam2_parse_info.h; \
+@STFS_ENABLED@ $(MAKE) ntvfs/tank/vfs_tank_proto.h
+
+proto: headers 
+
+.PHONY: headers proto
+
+etags:
+       etags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+ctags:
+       ctags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+realclean: clean delheaders
+       -rm -f config.log $(BIN_PROGS) $(MODULES) $(SBIN_PROGS) bin/.dummy script/findsmb
+
+distclean: realclean
+       -rm -f include/stamp-h
+       -rm -f include/config.h Makefile
+       -rm -f config.status config.cache so_locations
+       -rm -rf .deps
+
+# this target is really just for my use. It only works on a limited
+# range of machines and is used to produce a list of potentially
+# dead (ie. unused) functions in the code. (tridge)
+finddead:
+       nm */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
+       nm */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
+       comm -13 nmused.txt nmfns.txt 
+
+
+# when configure.in is updated, reconfigure
+$(srcdir)/configure: $(srcdir)/configure.in
+       @echo "WARNING: you need to rerun autoconf"
+
+config.status: $(srcdir)/configure
+       @echo "WARNING: you need to run configure"
+
+Makefile: $(srcdir)/Makefile.in config.status
+       @echo "WARNING: you need to run ./config.status"
+
+test_prefix=/tmp/test-samba
+# Run regression suite using the external "satyr" framework
+check:
+       @echo "** Sorry, samba self-test without installation does not work "
+       @echo "** yet.  Please try specifying a scratch directory to"
+       @echo "**       ./configure --prefix DIR"
+       @echo "** then run \"make install installcheck\""
+       exit 1
+
+#      -rm -rf $(test_prefix)/lib
+#      mkdir $(test_prefix)/lib -p ./testdir 
+#      PATH=$(builddir)/bin:$(PATH) \
+#      SATYR_SUITEDIR=../testsuite/build_farm/ prefix=$(test_prefix) \
+#      testdir=./testdir $(SHELL) satyr
+
+# Run regression suite on the installed version.
+
+# `installcheck'
+#      Perform installation tests (if any).  The user must build and
+#      install the program before running the tests.  You should not
+#      assume that `$(bindir)' is in the search path.
+
+dangerous-installcheck:
+       mkdir -p $(BASEDIR)/lib
+       mkdir -p $(BASEDIR)/var
+       PATH=$(BINDIR):$(SBINDIR):$(PATH) \
+       SATYR_DISCOURAGE=1 \
+       SATYR_SUITEDIR=../testsuite/satyr/ prefix=$(BASEDIR) \
+       LIBSMB_PROG=$(SBINDIR)/smbd \
+       testdir=./testdir $(SHELL) satyr
diff --git a/source4/aclocal.m4 b/source4/aclocal.m4
new file mode 100644 (file)
index 0000000..7bec88d
--- /dev/null
@@ -0,0 +1,643 @@
+dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
+dnl if the cache file is inconsistent with the current host,
+dnl target and build system types, execute CMD or print a default
+dnl error message.
+AC_DEFUN(AC_VALIDATE_CACHE_SYSTEM_TYPE, [
+    AC_REQUIRE([AC_CANONICAL_SYSTEM])
+    AC_MSG_CHECKING([config.cache system type])
+    if { test x"${ac_cv_host_system_type+set}" = x"set" &&
+         test x"$ac_cv_host_system_type" != x"$host"; } ||
+       { test x"${ac_cv_build_system_type+set}" = x"set" &&
+         test x"$ac_cv_build_system_type" != x"$build"; } ||
+       { test x"${ac_cv_target_system_type+set}" = x"set" &&
+         test x"$ac_cv_target_system_type" != x"$target"; }; then
+       AC_MSG_RESULT([different])
+       ifelse($#, 1, [$1],
+               [AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
+    else
+       AC_MSG_RESULT([same])
+    fi
+    ac_cv_host_system_type="$host"
+    ac_cv_build_system_type="$build"
+    ac_cv_target_system_type="$target"
+])
+
+
+dnl test whether dirent has a d_off member
+AC_DEFUN(AC_DIRENT_D_OFF,
+[AC_CACHE_CHECK([for d_off in dirent], ac_cv_dirent_d_off,
+[AC_TRY_COMPILE([
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>], [struct dirent d; d.d_off;],
+ac_cv_dirent_d_off=yes, ac_cv_dirent_d_off=no)])
+if test $ac_cv_dirent_d_off = yes; then
+  AC_DEFINE(HAVE_DIRENT_D_OFF,1,[Whether dirent has a d_off member])
+fi
+])
+
+
+dnl AC_PROG_CC_FLAG(flag)
+AC_DEFUN(AC_PROG_CC_FLAG,
+[AC_CACHE_CHECK(whether ${CC-cc} accepts -$1, ac_cv_prog_cc_$1,
+[echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -$1 -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_$1=yes
+else
+  ac_cv_prog_cc_$1=no
+fi
+rm -f conftest*
+])])
+
+dnl see if a declaration exists for a function or variable
+dnl defines HAVE_function_DECL if it exists
+dnl AC_HAVE_DECL(var, includes)
+AC_DEFUN(AC_HAVE_DECL,
+[
+ AC_CACHE_CHECK([for $1 declaration],ac_cv_have_$1_decl,[
+    AC_TRY_COMPILE([$2],[int i = (int)$1],
+        ac_cv_have_$1_decl=yes,ac_cv_have_$1_decl=no)])
+ if test x"$ac_cv_have_$1_decl" = x"yes"; then
+    AC_DEFINE([HAVE_]translit([$1], [a-z], [A-Z])[_DECL],1,[Whether $1() is available])
+ fi
+])
+
+
+dnl check for a function in a library, but don't
+dnl keep adding the same library to the LIBS variable.
+dnl AC_LIBTESTFUNC(lib,func)
+AC_DEFUN(AC_LIBTESTFUNC,
+[case "$LIBS" in
+  *-l$1*) AC_CHECK_FUNCS($2) ;;
+  *) AC_CHECK_LIB($1, $2) 
+     AC_CHECK_FUNCS($2)
+  ;;
+  esac
+])
+
+dnl Define an AC_DEFINE with ifndef guard.
+dnl AC_N_DEFINE(VARIABLE [, VALUE])
+define(AC_N_DEFINE,
+[cat >> confdefs.h <<\EOF
+[#ifndef] $1
+[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1)
+[#endif]
+EOF
+])
+
+dnl Add an #include
+dnl AC_ADD_INCLUDE(VARIABLE)
+define(AC_ADD_INCLUDE,
+[cat >> confdefs.h <<\EOF
+[#include] $1
+EOF
+])
+
+dnl Copied from libtool.m4
+AC_DEFUN(AC_PROG_LD_GNU,
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  ac_cv_prog_gnu_ld=yes
+else
+  ac_cv_prog_gnu_ld=no
+fi])
+])
+
+# Configure paths for LIBXML2
+# Toshio Kuratomi 2001-04-21
+# Adapted from:
+# Configure paths for GLIB
+# Owen Taylor     97-11-3
+
+dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for XML, and define XML_CFLAGS and XML_LIBS
+dnl
+AC_DEFUN(AM_PATH_XML2,[ 
+AC_ARG_WITH(xml-prefix,
+            [  --with-xml-prefix=PFX   Prefix where libxml is installed (optional)],
+            xml_config_prefix="$withval", xml_config_prefix="")
+AC_ARG_WITH(xml-exec-prefix,
+            [  --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
+            xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
+AC_ARG_ENABLE(xmltest,
+              [  --disable-xmltest       Do not try to compile and run a test LIBXML program],,
+              enable_xmltest=yes)
+
+  if test x$xml_config_exec_prefix != x ; then
+     xml_config_args="$xml_config_args --exec-prefix=$xml_config_exec_prefix"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
+     fi
+  fi
+  if test x$xml_config_prefix != x ; then
+     xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
+     if test x${XML2_CONFIG+set} != xset ; then
+        XML2_CONFIG=$xml_config_prefix/bin/xml2-config
+     fi
+  fi
+
+  AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
+  min_xml_version=ifelse([$1], ,2.0.0,[$1])
+  AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
+  no_xml=""
+  if test "$XML2_CONFIG" = "no" ; then
+    no_xml=yes
+  else
+    XML_CFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
+    XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
+    xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_xmltest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $XML_CFLAGS"
+      LIBS="$XML_LIBS $LIBS"
+dnl
+dnl Now check if the installed libxml is sufficiently new.
+dnl (Also sanity checks the results of xml2-config to some extent)
+dnl
+      rm -f conf.xmltest
+      AC_TRY_RUN([
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlversion.h>
+
+int 
+main()
+{
+  int xml_major_version, xml_minor_version, xml_micro_version;
+  int major, minor, micro;
+  char *tmp_version;
+
+  system("touch conf.xmltest");
+
+  /* Capture xml2-config output via autoconf/configure variables */
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = (char *)strdup("$min_xml_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string from xml2-config\n", "$min_xml_version");
+     exit(1);
+   }
+   free(tmp_version);
+
+   /* Capture the version information from the header files */
+   tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
+   if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
+     printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
+     exit(1);
+   }
+   free(tmp_version);
+
+ /* Compare xml2-config output to the libxml headers */
+  if ((xml_major_version != $xml_config_major_version) ||
+      (xml_minor_version != $xml_config_minor_version) ||
+      (xml_micro_version != $xml_config_micro_version))
+    {
+      printf("*** libxml header files (version %d.%d.%d) do not match\n",
+         xml_major_version, xml_minor_version, xml_micro_version);
+      printf("*** xml2-config (version %d.%d.%d)\n",
+         $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
+      return 1;
+    } 
+/* Compare the headers to the library to make sure we match */
+  /* Less than ideal -- doesn't provide us with return value feedback, 
+   * only exits if there's a serious mismatch between header and library.
+   */
+    LIBXML_TEST_VERSION;
+
+    /* Test that the library is greater than our minimum version */
+    if ((xml_major_version > major) ||
+        ((xml_major_version == major) && (xml_minor_version > minor)) ||
+        ((xml_major_version == major) && (xml_minor_version == minor) &&
+        (xml_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
+               xml_major_version, xml_minor_version, xml_micro_version);
+        printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
+           major, minor, micro);
+        printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
+        printf("*** correct copy of xml2-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+    }
+  return 1;
+}
+],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+
+  if test "x$no_xml" = x ; then
+     AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$XML2_CONFIG" = "no" ; then
+       echo "*** The xml2-config script installed by LIBXML could not be found"
+       echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the XML2_CONFIG environment variable to the"
+       echo "*** full path to xml2-config."
+     else
+       if test -f conf.xmltest ; then
+        :
+       else
+          echo "*** Could not run libxml test program, checking why..."
+          CFLAGS="$CFLAGS $XML_CFLAGS"
+          LIBS="$LIBS $XML_LIBS"
+          AC_TRY_LINK([
+#include <libxml/xmlversion.h>
+#include <stdio.h>
+],      [ LIBXML_TEST_VERSION; return 0;],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
+          echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+          echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
+          echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
+          echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+
+     XML_CFLAGS=""
+     XML_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(XML_CFLAGS)
+  AC_SUBST(XML_LIBS)
+  rm -f conf.xmltest
+])
+
+# =========================================================================
+# AM_PATH_MYSQL : MySQL library
+
+dnl AM_PATH_MYSQL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for MYSQL, and define MYSQL_CFLAGS and MYSQL_LIBS
+dnl
+AC_DEFUN(AM_PATH_MYSQL,
+[dnl
+dnl Get the cflags and libraries from the mysql_config script
+dnl
+AC_ARG_WITH(mysql-prefix,[  --with-mysql-prefix=PFX   Prefix where MYSQL is installed (optional)],
+            mysql_prefix="$withval", mysql_prefix="")
+AC_ARG_WITH(mysql-exec-prefix,[  --with-mysql-exec-prefix=PFX Exec prefix where MYSQL is installed (optional)],
+            mysql_exec_prefix="$withval", mysql_exec_prefix="")
+AC_ARG_ENABLE(mysqltest, [  --disable-mysqltest       Do not try to compile and run a test MYSQL program],
+         , enable_mysqltest=yes)
+
+  if test x$mysql_exec_prefix != x ; then
+     mysql_args="$mysql_args --exec-prefix=$mysql_exec_prefix"
+     if test x${MYSQL_CONFIG+set} != xset ; then
+        MYSQL_CONFIG=$mysql_exec_prefix/bin/mysql_config
+     fi
+  fi
+  if test x$mysql_prefix != x ; then
+     mysql_args="$mysql_args --prefix=$mysql_prefix"
+     if test x${MYSQL_CONFIG+set} != xset ; then
+        MYSQL_CONFIG=$mysql_prefix/bin/mysql_config
+     fi
+  fi
+
+  AC_REQUIRE([AC_CANONICAL_TARGET])
+  AC_PATH_PROG(MYSQL_CONFIG, mysql_config, no)
+  min_mysql_version=ifelse([$1], ,0.11.0,$1)
+  AC_MSG_CHECKING(for MYSQL - version >= $min_mysql_version)
+  no_mysql=""
+  if test "$MYSQL_CONFIG" = "no" ; then
+    no_mysql=yes
+  else
+    MYSQL_CFLAGS=`$MYSQL_CONFIG $mysqlconf_args --cflags | sed -e "s/'//g"`
+    MYSQL_LIBS=`$MYSQL_CONFIG $mysqlconf_args --libs | sed -e "s/'//g"`
+
+    mysql_major_version=`$MYSQL_CONFIG $mysql_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    mysql_minor_version=`$MYSQL_CONFIG $mysql_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    mysql_micro_version=`$MYSQL_CONFIG $mysql_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_mysqltest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $MYSQL_CFLAGS"
+      LIBS="$LIBS $MYSQL_LIBS"
+dnl
+dnl Now check if the installed MYSQL is sufficiently new. (Also sanity
+dnl checks the results of mysql_config to some extent
+dnl
+      rm -f conf.mysqltest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mysql.h>
+
+char*
+my_strdup (char *str)
+{
+  char *new_str;
+
+  if (str)
+    {
+      new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
+      strcpy (new_str, str);
+    }
+  else
+    new_str = NULL;
+
+  return new_str;
+}
+
+int main (int argc, char *argv[])
+{
+int major, minor, micro;
+  char *tmp_version;
+
+  /* This hangs on some systems (?)
+  system ("touch conf.mysqltest");
+  */
+  { FILE *fp = fopen("conf.mysqltest", "a"); if ( fp ) fclose(fp); }
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = my_strdup("$min_mysql_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_mysql_version");
+     exit(1);
+   }
+
+   if (($mysql_major_version > major) ||
+      (($mysql_major_version == major) && ($mysql_minor_version > minor)) ||
+      (($mysql_major_version == major) && ($mysql_minor_version == minor) && ($mysql_micro_version >= micro)))
+    {
+      return 0;
+    }
+  else
+    {
+      printf("\n*** 'mysql_config --version' returned %d.%d.%d, but the minimum version\n", $mysql_major_version, $mysql_minor_version, $mysql_micro_version);
+      printf("*** of MYSQL required is %d.%d.%d. If mysql_config is correct, then it is\n", major, minor, micro);
+      printf("*** best to upgrade to the required version.\n");
+      printf("*** If mysql_config was wrong, set the environment variable MYSQL_CONFIG\n");
+      printf("*** to point to the correct copy of mysql_config, and remove the file\n");
+      printf("*** config.cache before re-running configure\n");
+      return 1;
+    }
+}
+
+],, no_mysql=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_mysql" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])
+  else
+     AC_MSG_RESULT(no)
+     if test "$MYSQL_CONFIG" = "no" ; then
+       echo "*** The mysql_config script installed by MYSQL could not be found"
+       echo "*** If MYSQL was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the MYSQL_CONFIG environment variable to the"
+       echo "*** full path to mysql_config."
+     else
+       if test -f conf.mysqltest ; then
+        :
+       else
+          echo "*** Could not run MYSQL test program, checking why..."
+          CFLAGS="$CFLAGS $MYSQL_CFLAGS"
+          LIBS="$LIBS $MYSQL_LIBS"
+          AC_TRY_LINK([
+#include <stdio.h>
+#include <mysql.h>
+
+int main(int argc, char *argv[])
+{ return 0; }
+#undef  main
+#define main K_and_R_C_main
+],      [ return 0; ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding MYSQL or finding the wrong"
+          echo "*** version of MYSQL. If it is not finding MYSQL, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+    echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means MYSQL was incorrectly installed"
+          echo "*** or that you have moved MYSQL since it was installed. In the latter case, you"
+          echo "*** may want to edit the mysql_config script: $MYSQL_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     MYSQL_CFLAGS=""
+     MYSQL_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(MYSQL_CFLAGS)
+  AC_SUBST(MYSQL_LIBS)
+  rm -f conf.mysqltest
+])
+
+dnl Removes -I/usr/include/? from given variable
+AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
+  ac_new_flags=""
+  for i in [$]$1; do
+    case [$]i in
+    -I/usr/include|-I/usr/include/) ;;
+    *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+    esac
+  done
+  $1=[$]ac_new_flags
+])
+    
+dnl Removes -L/usr/lib/? from given variable
+AC_DEFUN(LIB_REMOVE_USR_LIB,[
+  ac_new_flags=""
+  for i in [$]$1; do
+    case [$]i in
+    -L/usr/lib|-L/usr/lib/) ;;
+    *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+    esac
+  done
+  $1=[$]ac_new_flags
+])
+
+dnl From Bruno Haible.
+
+AC_DEFUN(jm_ICONV,
+[
+  dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+  dnl those with the standalone portable libiconv installed).
+  AC_MSG_CHECKING(for iconv in $1)
+    jm_cv_func_iconv="no"
+    jm_cv_lib_iconv=no
+    jm_cv_giconv=no
+    AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+      [iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);],
+      jm_cv_func_iconv=yes
+      jm_cv_giconv=yes)
+
+    if test "$jm_cv_func_iconv" != yes; then
+      AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+        [iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);],
+        jm_cv_func_iconv=yes)
+
+        if test "$jm_cv_lib_iconv" != yes; then
+          jm_save_LIBS="$LIBS"
+          LIBS="$LIBS -lgiconv"
+          AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+            [iconv_t cd = iconv_open("","");
+             iconv(cd,NULL,NULL,NULL,NULL);
+             iconv_close(cd);],
+            jm_cv_lib_iconv=yes
+            jm_cv_func_iconv=yes
+            jm_cv_giconv=yes)
+          LIBS="$jm_save_LIBS"
+
+      if test "$jm_cv_func_iconv" != yes; then
+        jm_save_LIBS="$LIBS"
+        LIBS="$LIBS -liconv"
+        AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+          [iconv_t cd = iconv_open("","");
+           iconv(cd,NULL,NULL,NULL,NULL);
+           iconv_close(cd);],
+          jm_cv_lib_iconv=yes
+          jm_cv_func_iconv=yes)
+        LIBS="$jm_save_LIBS"
+        fi
+      fi
+    fi
+
+  if test "$jm_cv_func_iconv" = yes; then
+    if test "$jm_cv_giconv" = yes; then
+      AC_DEFINE(HAVE_GICONV, 1, [What header to include for iconv() function: giconv.h])
+      AC_MSG_RESULT(yes)
+      ICONV_FOUND=yes
+    else
+      AC_DEFINE(HAVE_ICONV, 1, [What header to include for iconv() function: iconv.h])
+      AC_MSG_RESULT(yes)
+      ICONV_FOUND=yes
+    fi
+  else
+    AC_MSG_RESULT(no)
+  fi
+  if test "$jm_cv_lib_iconv" = yes; then
+    if test "$jm_cv_giconv" = yes; then
+      LIBS="$LIBS -lgiconv"
+    else
+      LIBS="$LIBS -liconv"
+    fi
+  fi
+])
+
+dnl CFLAGS_ADD_DIR(CFLAGS, $INCDIR)
+dnl This function doesn't add -I/usr/include into CFLAGS
+AC_DEFUN(CFLAGS_ADD_DIR,[
+if test "$2" != "/usr/include" ; then
+    $1="$$1 -I$2"
+fi
+])
+
+dnl LIB_ADD_DIR(LDFLAGS, $LIBDIR)
+dnl This function doesn't add -L/usr/lib into LDFLAGS
+AC_DEFUN(LIB_ADD_DIR,[
+if test "$2" != "/usr/lib" ; then
+    $1="$$1 -L$2"
+fi
+])
+
+dnl AC_ENABLE_SHARED - implement the --enable-shared flag
+dnl Usage: AC_ENABLE_SHARED[(DEFAULT)]
+dnl   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+dnl   `yes'.
+AC_DEFUN([AC_ENABLE_SHARED],
+[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(shared,
+changequote(<<, >>)dnl
+<<  --enable-shared[=PKGS]    build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+  enable_shared=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_shared=yes
+    fi
+
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl
+])
+
+dnl AC_ENABLE_STATIC - implement the --enable-static flag
+dnl Usage: AC_ENABLE_STATIC[(DEFAULT)]
+dnl   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+dnl   `yes'.
+AC_DEFUN([AC_ENABLE_STATIC],
+[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(static,
+changequote(<<, >>)dnl
+<<  --enable-static[=PKGS]    build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+  enable_static=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_static=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_static=AC_ENABLE_STATIC_DEFAULT)dnl
+])
+
+dnl AC_DISABLE_STATIC - set the default static flag to --disable-static
+AC_DEFUN([AC_DISABLE_STATIC],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)])
diff --git a/source4/auth/auth.c b/source4/auth/auth.c
new file mode 100644 (file)
index 0000000..74c60f6
--- /dev/null
@@ -0,0 +1,456 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Bartlett         2001-2002
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/** List of various built-in authentication modules */
+
+static const struct auth_init_function_entry builtin_auth_init_functions[] = {
+       { "guest", auth_init_guest },
+//     { "rhosts", auth_init_rhosts },
+//     { "hostsequiv", auth_init_hostsequiv },
+       { "sam", auth_init_sam },       
+       { "samstrict", auth_init_samstrict },
+       { "samstrict_dc", auth_init_samstrict_dc },
+       { "unix", auth_init_unix },
+//     { "smbserver", auth_init_smbserver },
+//     { "ntdomain", auth_init_ntdomain },
+//     { "trustdomain", auth_init_trustdomain },
+//     { "winbind", auth_init_winbind },
+#ifdef DEVELOPER
+       { "name_to_ntstatus", auth_init_name_to_ntstatus },
+       { "fixed_challenge", auth_init_fixed_challenge },
+#endif
+       { "plugin", auth_init_plugin },
+       { NULL, NULL}
+};
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+
+static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) 
+{
+       DATA_BLOB challenge = data_blob(NULL, 0);
+       const char *challenge_set_by = NULL;
+       auth_methods *auth_method;
+       TALLOC_CTX *mem_ctx;
+
+       if (auth_context->challenge.length) {
+               DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n", 
+                         auth_context->challenge_set_by));
+               return auth_context->challenge.data;
+       }
+
+       for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) {
+               if (auth_method->get_chal == NULL) {
+                       DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
+                       continue;
+               }
+
+               DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
+               if (challenge_set_by != NULL) {
+                       DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authentication method %s has already specified a challenge.  Challenge by %s ignored.\n", 
+                                 challenge_set_by, auth_method->name));
+                       continue;
+               }
+
+               mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name);
+               if (!mem_ctx) {
+                       smb_panic("talloc_init() failed!");
+               }
+               
+               challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
+               if (!challenge.length) {
+                       DEBUG(3, ("auth_get_challenge: getting challenge from authentication method %s FAILED.\n", 
+                                 auth_method->name));
+               } else {
+                       DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
+                       auth_context->challenge = challenge;
+                       challenge_set_by = auth_method->name;
+                       auth_context->challenge_set_method = auth_method;
+               }
+               talloc_destroy(mem_ctx);
+       }
+       
+       if (!challenge_set_by) {
+               uchar chal[8];
+               
+               generate_random_buffer(chal, sizeof(chal), False);
+               auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, 
+                                                          chal, sizeof(chal));
+               
+               challenge_set_by = "random";
+       } 
+       
+       DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
+       DEBUG(5, ("challenge is: \n"));
+       dump_data(5, auth_context->challenge.data, auth_context->challenge.length);
+       
+       SMB_ASSERT(auth_context->challenge.length == 8);
+
+       auth_context->challenge_set_by=challenge_set_by;
+
+       return auth_context->challenge.data;
+}
+
+
+/**
+ * Check user is in correct domain (if required)
+ *
+ * @param user Only used to fill in the debug message
+ * 
+ * @param domain The domain to be verified
+ *
+ * @return True if the user can connect with that domain, 
+ *         False otherwise.
+**/
+
+static BOOL check_domain_match(const char *user, const char *domain) 
+{
+       /*
+        * If we aren't serving to trusted domains, we must make sure that
+        * the validation request comes from an account in the same domain
+        * as the Samba server
+        */
+
+       if (!lp_allow_trusted_domains() &&
+           !(strequal("", domain) || 
+             strequal(lp_workgroup(), domain) || 
+             is_myname(domain))) {
+               DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
+               return False;
+       } else {
+               return True;
+       }
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * This function does NOT need to be in a become_root()/unbecome_root() pair
+ * as it makes the calls itself when needed.
+ *
+ * The return value takes precedence over the contents of the server_info 
+ * struct.  When the return is other than NT_STATUS_OK the contents 
+ * of that structure is undefined.
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ *                  Must be created with make_user_info() or one of its wrappers.
+ *
+ * @param auth_context Supplies the challenges and some other data. 
+ *                  Must be created with make_auth_context(), and the challenges should be 
+ *                  filled in, either at creation or by calling the challenge geneation 
+ *                  function auth_get_challenge().  
+ *
+ * @param server_info If successful, contains information about the authentication, 
+ *                    including a SAM_ACCOUNT struct describing the user.
+ *
+ * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
+ *
+ **/
+
+static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
+                                   const struct auth_usersupplied_info *user_info, 
+                                   struct auth_serversupplied_info **server_info)
+{
+       
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+       const char *pdb_username;
+       auth_methods *auth_method;
+       TALLOC_CTX *mem_ctx;
+
+       if (!user_info || !auth_context || !server_info)
+               return NT_STATUS_LOGON_FAILURE;
+
+       DEBUG(3, ("check_ntlm_password:  Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", 
+                 user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
+
+       DEBUG(3, ("check_ntlm_password:  mapped user is: [%s]\\[%s]@[%s]\n", 
+                 user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
+
+       if (auth_context->challenge.length != 8) {
+               DEBUG(0, ("check_ntlm_password:  Invalid challenge stored for this auth context - cannot continue\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       if (auth_context->challenge_set_by)
+               DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
+                                       auth_context->challenge_set_by));
+
+       DEBUG(10, ("challenge is: \n"));
+       dump_data(5, auth_context->challenge.data, auth_context->challenge.length);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("user_info has passwords of length %d and %d\n", 
+                   user_info->lm_resp.length, user_info->nt_resp.length));
+       DEBUG(100, ("lm:\n"));
+       dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
+       DEBUG(100, ("nt:\n"));
+       dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
+#endif
+
+       /* This needs to be sorted:  If it doesn't match, what should we do? */
+       if (!check_domain_match(user_info->smb_name.str, user_info->domain.str))
+               return NT_STATUS_LOGON_FAILURE;
+
+       for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) {
+               mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name, 
+                                           user_info->domain.str, user_info->smb_name.str);
+
+               nt_status = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] suceeded\n", 
+                                 auth_method->name, user_info->smb_name.str));
+               } else {
+                       DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n", 
+                                 auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
+               }
+
+               talloc_destroy(mem_ctx);
+
+               if (NT_STATUS_IS_OK(nt_status))
+                       break;
+       }
+
+       /* This is one of the few places the *relies* (rather than just sets defaults
+          on the value of lp_security().  This needs to change.  A new paramater 
+          perhaps? */
+       if (lp_security() >= SEC_SERVER)
+               smb_user_control(user_info, *server_info, nt_status);
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+               pdb_username = pdb_get_username((*server_info)->sam_account);
+               if (!(*server_info)->guest) {
+                       /* We might not be root if we are an RPC call */
+                       become_root();
+                       nt_status = smb_pam_accountcheck(pdb_username);
+                       unbecome_root();
+                       
+                       if (NT_STATUS_IS_OK(nt_status)) {
+                               DEBUG(5, ("check_ntlm_password:  PAM Account for user [%s] suceeded\n", 
+                                         pdb_username));
+                       } else {
+                               DEBUG(3, ("check_ntlm_password:  PAM Account for user [%s] FAILED with error %s\n", 
+                                         pdb_username, nt_errstr(nt_status)));
+                       } 
+               }
+               
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG((*server_info)->guest ? 5 : 2, 
+                             ("check_ntlm_password:  %sauthentication for user [%s] -> [%s] -> [%s] suceeded\n", 
+                              (*server_info)->guest ? "guest " : "", 
+                              user_info->smb_name.str, 
+                              user_info->internal_username.str, 
+                              pdb_username));
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(2, ("check_ntlm_password:  Authentication for user [%s] -> [%s] FAILED with error %s\n", 
+                         user_info->smb_name.str, user_info->internal_username.str, 
+                         nt_errstr(nt_status)));
+               ZERO_STRUCTP(server_info);
+       }
+       return nt_status;
+}
+
+/***************************************************************************
+ Clear out a auth_context, and destroy the attached TALLOC_CTX
+***************************************************************************/
+
+static void free_auth_context(struct auth_context **auth_context)
+{
+       if (*auth_context != NULL)
+               talloc_destroy((*auth_context)->mem_ctx);
+       *auth_context = NULL;
+}
+
+/***************************************************************************
+ Make a auth_info struct
+***************************************************************************/
+
+static NTSTATUS make_auth_context(struct auth_context **auth_context) 
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("authentication context");
+       
+       *auth_context = talloc(mem_ctx, sizeof(**auth_context));
+       if (!*auth_context) {
+               DEBUG(0,("make_auth_context: talloc failed!\n"));
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ZERO_STRUCTP(*auth_context);
+
+       (*auth_context)->mem_ctx = mem_ctx;
+       (*auth_context)->check_ntlm_password = check_ntlm_password;
+       (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
+       (*auth_context)->free = free_auth_context;
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+***************************************************************************/
+
+static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list) 
+{
+       auth_methods *list = NULL;
+       auth_methods *t = NULL;
+       auth_methods *tmp;
+       int i;
+       NTSTATUS nt_status;
+
+       if (!text_list) {
+               DEBUG(2,("make_auth_context_text_list: No auth method list!?\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context)))
+               return nt_status;
+       
+       for (;*text_list; text_list++) { 
+               DEBUG(5,("make_auth_context_text_list: Attempting to find an auth method to match %s\n",
+                                       *text_list));
+               for (i = 0; builtin_auth_init_functions[i].name; i++) {
+                       char *module_name = smb_xstrdup(*text_list);
+                       char *module_params = NULL;
+                       char *p;
+
+                       p = strchr(module_name, ':');
+                       if (p) {
+                               *p = 0;
+                               module_params = p+1;
+                               trim_string(module_params, " ", " ");
+                       }
+
+                       trim_string(module_name, " ", " ");
+
+                       if (strequal(builtin_auth_init_functions[i].name, module_name)) {
+                               DEBUG(5,("make_auth_context_text_list: Found auth method %s (at pos %d)\n", *text_list, i));
+                               if (NT_STATUS_IS_OK(builtin_auth_init_functions[i].init(*auth_context, module_params, &t))) {
+                                       DEBUG(5,("make_auth_context_text_list: auth method %s has a valid init\n",
+                                                               *text_list));
+                                       DLIST_ADD_END(list, t, tmp);
+                               } else {
+                                       DEBUG(0,("make_auth_context_text_list: auth method %s did not correctly init\n",
+                                                               *text_list));
+                               }
+                               break;
+                       }
+                       SAFE_FREE(module_name);
+               }
+       }
+       
+       (*auth_context)->auth_method_list = list;
+       
+       return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_context struct for the auth subsystem
+***************************************************************************/
+
+NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context) 
+{
+       char **auth_method_list = NULL; 
+       NTSTATUS nt_status;
+
+       if (lp_auth_methods() && !str_list_copy(&auth_method_list, lp_auth_methods())) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (auth_method_list == NULL) {
+               switch (lp_security()) 
+               {
+               case SEC_DOMAIN:
+                       DEBUG(5,("Making default auth method list for security=domain\n"));
+                       auth_method_list = str_list_make("guest sam winbind ntdomain", NULL);
+                       break;
+               case SEC_SERVER:
+                       DEBUG(5,("Making default auth method list for security=server\n"));
+                       auth_method_list = str_list_make("guest sam smbserver", NULL);
+                       break;
+               case SEC_USER:
+                       if (lp_encrypted_passwords()) { 
+                               DEBUG(5,("Making default auth method list for security=user, encrypt passwords = yes\n"));
+                               auth_method_list = str_list_make("guest sam", NULL);
+                       } else {
+                               DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n"));
+                               auth_method_list = str_list_make("guest unix", NULL);
+                       }
+                       break;
+               case SEC_SHARE:
+                       if (lp_encrypted_passwords()) {
+                               DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n"));
+                               auth_method_list = str_list_make("guest sam", NULL);
+                       } else {
+                               DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n"));
+                               auth_method_list = str_list_make("guest unix", NULL);
+                       }
+                       break;
+               case SEC_ADS:
+                       DEBUG(5,("Making default auth method list for security=ADS\n"));
+                       auth_method_list = str_list_make("guest sam ads winbind ntdomain", NULL);
+                       break;
+               default:
+                       DEBUG(5,("Unknown auth method!\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       } else {
+               DEBUG(5,("Using specified auth order\n"));
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) {
+               str_list_free(&auth_method_list);
+               return nt_status;
+       }
+       
+       str_list_free(&auth_method_list);
+       return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_info struct with a fixed challenge
+***************************************************************************/
+
+NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8]) 
+{
+       NTSTATUS nt_status;
+       if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
+               return nt_status;
+       }
+       
+       (*auth_context)->challenge = data_blob(chal, 8);
+       (*auth_context)->challenge_set_by = "fixed";
+       return nt_status;
+}
+
+
diff --git a/source4/auth/auth_builtin.c b/source4/auth/auth_builtin.c
new file mode 100644 (file)
index 0000000..32f3931
--- /dev/null
@@ -0,0 +1,210 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Generic authenticaion types
+   Copyright (C) Andrew Bartlett         2001-2002
+   Copyright (C) Jelmer Vernooij              2002
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * Return a guest logon for guest users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * guest logons to be dealt with in one place.  Non-guest logons 'fail'
+ * and pass onto the next module.
+ **/
+
+static NTSTATUS check_guest_security(const struct auth_context *auth_context,
+                                    void *my_private_data, 
+                                    TALLOC_CTX *mem_ctx,
+                                    const auth_usersupplied_info *user_info, 
+                                    auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+
+       if (!(user_info->internal_username.str 
+             && *user_info->internal_username.str)) {
+               nt_status = make_server_info_guest(server_info);
+       }
+
+       return nt_status;
+}
+
+/* Guest modules initialisation */
+
+NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *options, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method))
+               return NT_STATUS_NO_MEMORY;
+
+       (*auth_method)->auth = check_guest_security;
+       (*auth_method)->name = "guest";
+       return NT_STATUS_OK;
+}
+
+/** 
+ * Return an error based on username
+ *
+ * This function allows the testing of obsure errors, as well as the generation
+ * of NT_STATUS -> DOS error mapping tables.
+ *
+ * This module is of no value to end-users.
+ *
+ * The password is ignored.
+ *
+ * @return An NTSTATUS value based on the username
+ **/
+
+static NTSTATUS check_name_to_ntstatus_security(const struct auth_context *auth_context,
+                                               void *my_private_data, 
+                                               TALLOC_CTX *mem_ctx,
+                                               const auth_usersupplied_info *user_info, 
+                                               auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status;
+       fstring user;
+       long error_num;
+       fstrcpy(user, user_info->smb_name.str);
+       
+       if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
+               strupper(user);
+               return nt_status_string_to_code(user);
+       }
+
+       strlower(user);
+       error_num = strtoul(user, NULL, 16);
+       
+       DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num));
+
+       nt_status = NT_STATUS(error_num);
+       
+       return nt_status;
+}
+
+/** Module initailisation function */
+
+NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method))
+               return NT_STATUS_NO_MEMORY;
+
+       (*auth_method)->auth = check_name_to_ntstatus_security;
+       (*auth_method)->name = "name_to_ntstatus";
+       return NT_STATUS_OK;
+}
+
+/** 
+ * Return a 'fixed' challenge instead of a varaible one.
+ *
+ * The idea of this function is to make packet snifs consistant
+ * with a fixed challenge, so as to aid debugging.
+ *
+ * This module is of no value to end-users.
+ *
+ * This module does not actually authenticate the user, but
+ * just pretenteds to need a specified challenge.  
+ * This module removes *all* security from the challenge-response system
+ *
+ * @return NT_STATUS_UNSUCCESSFUL
+ **/
+
+static NTSTATUS check_fixed_challenge_security(const struct auth_context *auth_context,
+                                              void *my_private_data, 
+                                              TALLOC_CTX *mem_ctx,
+                                              const auth_usersupplied_info *user_info, 
+                                              auth_serversupplied_info **server_info)
+{
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_fixed_challenge(const struct auth_context *auth_context,
+                                         void **my_private_data, 
+                                         TALLOC_CTX *mem_ctx)
+{
+       const char *challenge = "I am a teapot";   
+       return data_blob(challenge, 8);
+}
+
+
+/** Module initailisation function */
+
+NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method))
+               return NT_STATUS_NO_MEMORY;
+
+       (*auth_method)->auth = check_fixed_challenge_security;
+       (*auth_method)->get_chal = auth_get_fixed_challenge;
+       (*auth_method)->name = "fixed_challenge";
+       return NT_STATUS_OK;
+}
+
+/**
+ * Outsorce an auth module to an external loadable .so
+ *
+ * Only works on systems with dlopen() etc.
+ **/
+
+/* Plugin modules initialisation */
+
+NTSTATUS auth_init_plugin(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       void * dl_handle;
+       char *plugin_param, *plugin_name, *p;
+       auth_init_function plugin_init;
+
+       if (param == NULL) {
+               DEBUG(0, ("auth_init_plugin: The plugin module needs an argument!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       plugin_name = smb_xstrdup(param);
+       p = strchr(plugin_name, ':');
+       if (p) {
+               *p = 0;
+               plugin_param = p+1;
+               trim_string(plugin_param, " ", " ");
+       } else plugin_param = NULL;
+
+       trim_string(plugin_name, " ", " ");
+
+       DEBUG(5, ("auth_init_plugin: Trying to load auth plugin %s\n", plugin_name));
+       dl_handle = sys_dlopen(plugin_name, RTLD_NOW );
+       if (!dl_handle) {
+               DEBUG(0, ("auth_init_plugin: Failed to load auth plugin %s using sys_dlopen (%s)\n",
+                                       plugin_name, sys_dlerror()));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+    
+       plugin_init = sys_dlsym(dl_handle, "auth_init");
+       if (!plugin_init){
+               DEBUG(0, ("Failed to find function 'auth_init' using sys_dlsym in sam plugin %s (%s)\n",
+                                       plugin_name, sys_dlerror()));       
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       DEBUG(5, ("Starting sam plugin %s with paramater %s\n", plugin_name, plugin_param?plugin_param:"(null)"));
+       return plugin_init(auth_context, plugin_param, auth_method);
+}
diff --git a/source4/auth/auth_compat.c b/source4/auth/auth_compat.c
new file mode 100644 (file)
index 0000000..49cd2e8
--- /dev/null
@@ -0,0 +1,122 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Bartlett         2001-2002
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ COMPATIBILITY INTERFACES:
+ ***************************************************************************/
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash
+return True if the password is correct, False otherwise
+****************************************************************************/
+
+NTSTATUS check_plaintext_password(const char *smb_name, DATA_BLOB plaintext_password, auth_serversupplied_info **server_info)
+{
+       struct auth_context *plaintext_auth_context = NULL;
+       auth_usersupplied_info *user_info = NULL;
+       const uint8 *chal;
+       NTSTATUS nt_status;
+       if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+               return nt_status;
+       }
+       
+       chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+       
+       if (!make_user_info_for_reply(&user_info, 
+                                     smb_name, lp_workgroup(), chal,
+                                     plaintext_password)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context, 
+                                                               user_info, server_info); 
+       
+       (plaintext_auth_context->free)(&plaintext_auth_context);
+       free_user_info(&user_info);
+       return nt_status;
+}
+
+static NTSTATUS pass_check_smb(struct server_context *smb,
+                              const char *smb_name,
+                              const char *domain, 
+                              DATA_BLOB lm_pwd,
+                              DATA_BLOB nt_pwd,
+                              DATA_BLOB plaintext_password,
+                              BOOL encrypted)
+
+{
+       NTSTATUS nt_status;
+       auth_serversupplied_info *server_info = NULL;
+       if (encrypted) {                
+               auth_usersupplied_info *user_info = NULL;
+               make_user_info_for_reply_enc(&user_info, smb_name, 
+                                            domain,
+                                            lm_pwd, 
+                                            nt_pwd);
+               nt_status = smb->negotiate.auth_context->check_ntlm_password(smb->negotiate.auth_context, 
+                                                                            user_info, &server_info);
+               free_user_info(&user_info);
+       } else {
+               nt_status = check_plaintext_password(smb_name, plaintext_password, &server_info);
+       }               
+       free_server_info(&server_info);
+       return nt_status;
+}
+
+/****************************************************************************
+check if a username/password pair is ok via the auth subsystem.
+return True if the password is correct, False otherwise
+****************************************************************************/
+BOOL password_ok(struct server_context *smb, const char *smb_name, DATA_BLOB password_blob)
+{
+
+       DATA_BLOB null_password = data_blob(NULL, 0);
+       BOOL encrypted = (smb->negotiate.encrypted_passwords && password_blob.length == 24);
+       NTSTATUS status;
+
+       if (encrypted) {
+               /* 
+                * The password could be either NTLM or plain LM.  Try NTLM first, 
+                * but fall-through as required.
+                * NTLMv2 makes no sense here.
+                */
+               status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, 
+                                       password_blob, null_password, encrypted);
+               if (NT_STATUS_IS_OK(status)) {
+                       return True;
+               }
+               
+               status = pass_check_smb(smb, smb_name, lp_workgroup(), password_blob, 
+                                       null_password, null_password, encrypted);
+       } else {
+               status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, 
+                                       null_password, password_blob, encrypted);
+       }
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
diff --git a/source4/auth/auth_domain.c b/source4/auth/auth_domain.c
new file mode 100644 (file)
index 0000000..ff75953
--- /dev/null
@@ -0,0 +1,554 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Authenticate against a remote domain
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Andrew Bartlett 2001
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+BOOL global_machine_password_needs_changing = False;
+
+extern userdom_struct current_user_info;
+
+
+/*
+  resolve the name of a DC in ways appropriate for an ADS domain mode
+  an ADS domain may not have Netbios enabled at all, so this is 
+  quite different from the RPC case
+  Note that we ignore the 'server' parameter here. That has the effect of using
+  the 'ADS server' smb.conf parameter, which is what we really want anyway
+ */
+static NTSTATUS ads_resolve_dc(fstring remote_machine, 
+                              struct in_addr *dest_ip)
+{
+       ADS_STRUCT *ads;
+       ads = ads_init_simple();
+       if (!ads) {
+               return NT_STATUS_NO_LOGON_SERVERS;              
+       }
+
+       DEBUG(4,("ads_resolve_dc: realm=%s\n", ads->config.realm));
+
+       ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+#ifdef HAVE_ADS
+       /* a full ads_connect() is actually overkill, as we don't srictly need
+          to do the SASL auth in order to get the info we need, but libads
+          doesn't offer a better way right now */
+       ads_connect(ads);
+#endif
+
+       fstrcpy(remote_machine, ads->config.ldap_server_name);
+       strupper(remote_machine);
+       *dest_ip = ads->ldap_ip;
+       ads_destroy(&ads);
+       
+       if (!*remote_machine || is_zero_ip(*dest_ip)) {
+               return NT_STATUS_NO_LOGON_SERVERS;              
+       }
+
+       DEBUG(4,("ads_resolve_dc: using server='%s' IP=%s\n",
+                remote_machine, inet_ntoa(*dest_ip)));
+       
+       return NT_STATUS_OK;
+}
+
+/*
+  resolve the name of a DC in ways appropriate for RPC domain mode
+  this relies on the server supporting netbios and port 137 not being
+  firewalled
+ */
+static NTSTATUS rpc_resolve_dc(const char *server, 
+                              fstring remote_machine, 
+                              struct in_addr *dest_ip)
+{
+       if (is_ipaddress(server)) {
+               struct in_addr to_ip = *interpret_addr2(server);
+
+               /* we need to know the machines netbios name - this is a lousy
+                  way to find it, but until we have a RPC call that does this
+                  it will have to do */
+               if (!name_status_find("*", 0x20, 0x20, to_ip, remote_machine)) {
+                       DEBUG(2, ("rpc_resolve_dc: Can't resolve name for IP %s\n", server));
+                       return NT_STATUS_NO_LOGON_SERVERS;
+               }
+
+               *dest_ip = to_ip;
+               return NT_STATUS_OK;
+       } 
+
+       fstrcpy(remote_machine, server);
+       strupper(remote_machine);
+       if (!resolve_name(remote_machine, dest_ip, 0x20)) {
+               DEBUG(1,("rpc_resolve_dc: Can't resolve address for %s\n", 
+                        remote_machine));
+               return NT_STATUS_NO_LOGON_SERVERS;
+       }
+
+       DEBUG(4,("rpc_resolve_dc: using server='%s' IP=%s\n",
+                remote_machine, inet_ntoa(*dest_ip)));
+
+       return NT_STATUS_OK;
+}
+
+/**
+ * Connect to a remote server for domain security authenticaion.
+ *
+ * @param cli the cli to return containing the active connection
+ * @param server either a machine name or text IP address to
+ *               connect to.
+ * @param trust_passwd the trust password to establish the
+ *                       credentials with.
+ *
+ **/
+
+static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, 
+                                                 const char *server, 
+                                                 const char *setup_creds_as,
+                                                 uint16 sec_chan,
+                                                 const unsigned char *trust_passwd,
+                                                 BOOL *retry)
+{
+       struct in_addr dest_ip;
+       fstring remote_machine;
+        NTSTATUS result;
+       uint32 neg_flags = 0x000001ff;
+
+       *retry = False;
+
+       if (lp_security() == SEC_ADS)
+               result = ads_resolve_dc(remote_machine, &dest_ip);
+       else
+               result = rpc_resolve_dc(server, remote_machine, &dest_ip);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(2,("connect_to_domain_password_server: unable to resolve DC: %s\n", 
+                        nt_errstr(result)));
+               return result;
+       }
+
+       if (ismyip(dest_ip)) {
+               DEBUG(1,("connect_to_domain_password_server: Password server loop - not using password server %s\n",
+                        remote_machine));
+               return NT_STATUS_NO_LOGON_SERVERS;
+       }
+  
+       /* TODO: Send a SAMLOGON request to determine whether this is a valid
+          logonserver.  We can avoid a 30-second timeout if the DC is down
+          if the SAMLOGON request fails as it is only over UDP. */
+
+       /* we use a mutex to prevent two connections at once - when a 
+          Win2k PDC get two connections where one hasn't completed a 
+          session setup yet it will send a TCP reset to the first 
+          connection (tridge) */
+
+       /*
+        * With NT4.x DC's *all* authentication must be serialized to avoid
+        * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
+        */
+
+       *retry = True;
+
+       if (!grab_server_mutex(server))
+               return NT_STATUS_NO_LOGON_SERVERS;
+       
+       /* Attempt connection */
+       result = cli_full_connection(cli, lp_netbios_name(), remote_machine,
+                                    &dest_ip, 0, "IPC$", "IPC", "", "", "",0, retry);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               release_server_mutex();
+               return result;
+       }
+
+       /*
+        * We now have an anonymous connection to IPC$ on the domain password server.
+        */
+
+       /*
+        * Even if the connect succeeds we need to setup the netlogon
+        * pipe here. We do this as we may just have changed the domain
+        * account password on the PDC and yet we may be talking to
+        * a BDC that doesn't have this replicated yet. In this case
+        * a successful connect to a DC needs to take the netlogon connect
+        * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
+        */
+
+       if(cli_nt_session_open(*cli, PI_NETLOGON) == False) {
+               DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
+machine %s. Error was : %s.\n", remote_machine, cli_errstr(*cli)));
+               cli_nt_session_close(*cli);
+               cli_ulogoff(*cli);
+               cli_shutdown(*cli);
+               release_server_mutex();
+               return NT_STATUS_NO_LOGON_SERVERS;
+       }
+
+       snprintf((*cli)->mach_acct, sizeof((*cli)->mach_acct) - 1, "%s$", setup_creds_as);
+
+       if (!(*cli)->mach_acct) {
+               release_server_mutex();
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       result = cli_nt_setup_creds(*cli, sec_chan, trust_passwd, &neg_flags, 2);
+
+        if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
+%s. Error was : %s.\n", remote_machine, nt_errstr(result)));
+               cli_nt_session_close(*cli);
+               cli_ulogoff(*cli);
+               cli_shutdown(*cli);
+               release_server_mutex();
+               return result;
+       }
+
+       /* We exit here with the mutex *locked*. JRA */
+
+       return NT_STATUS_OK;
+}
+
+/***********************************************************************
+ Utility function to attempt a connection to an IP address of a DC.
+************************************************************************/
+
+static NTSTATUS attempt_connect_to_dc(struct cli_state **cli, 
+                                     const char *domain, 
+                                     struct in_addr *ip, 
+                                     const char *setup_creds_as, 
+                                     uint16 sec_chan,
+                                     const unsigned char *trust_passwd)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       BOOL retry = True;
+       fstring dc_name;
+       int i;
+
+       /*
+        * Ignore addresses we have already tried.
+        */
+
+       if (is_zero_ip(*ip))
+               return NT_STATUS_NO_LOGON_SERVERS;
+
+       if (!lookup_dc_name(lp_netbios_name(), domain, ip, dc_name))
+               return NT_STATUS_NO_LOGON_SERVERS;
+
+       for (i = 0; (!NT_STATUS_IS_OK(ret)) && retry && (i < 3); i++)
+               ret = connect_to_domain_password_server(cli, dc_name, setup_creds_as,
+                               sec_chan, trust_passwd, &retry);
+       return ret;
+}
+
+/***********************************************************************
+ We have been asked to dynamically determine the IP addresses of
+ the PDC and BDC's for DOMAIN, and query them in turn.
+************************************************************************/
+static NTSTATUS find_connect_dc(struct cli_state **cli, 
+                                const char *domain,
+                                const char *setup_creds_as,
+                                uint16 sec_chan,
+                                unsigned char *trust_passwd, 
+                                time_t last_change_time)
+{
+       struct in_addr dc_ip;
+       fstring srv_name;
+
+       if ( !rpc_find_dc(lp_workgroup(), srv_name, &dc_ip) ) {
+               DEBUG(0,("find_connect_dc: Failed to find an DCs for %s\n", lp_workgroup()));
+               return NT_STATUS_NO_LOGON_SERVERS;
+       }
+       
+       return attempt_connect_to_dc( cli, domain, &dc_ip, setup_creds_as, 
+                       sec_chan, trust_passwd );
+}
+
+/***********************************************************************
+ Do the same as security=server, but using NT Domain calls and a session
+ key from the machine password.  If the server parameter is specified
+ use it, otherwise figure out a server from the 'password server' param.
+************************************************************************/
+
+static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
+                                      const auth_usersupplied_info *user_info, 
+                                      const char *domain,
+                                      uchar chal[8],
+                                      auth_serversupplied_info **server_info, 
+                                      const char *server, const char *setup_creds_as,
+                                      uint16 sec_chan,
+                                      unsigned char trust_passwd[16],
+                                      time_t last_change_time)
+{
+       fstring remote_machine;
+       NET_USER_INFO_3 info3;
+       struct cli_state *cli = NULL;
+       NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
+
+       /*
+        * At this point, smb_apasswd points to the lanman response to
+        * the challenge in local_challenge, and smb_ntpasswd points to
+        * the NT response to the challenge in local_challenge. Ship
+        * these over the secure channel to a domain controller and
+        * see if they were valid.
+        */
+
+       while (!NT_STATUS_IS_OK(nt_status) &&
+              next_token(&server,remote_machine,LIST_SEP,sizeof(remote_machine))) {
+               if(lp_security() != SEC_ADS && strequal(remote_machine, "*")) {
+                       nt_status = find_connect_dc(&cli, domain, setup_creds_as, sec_chan, trust_passwd, last_change_time);
+               } else {
+                       int i;
+                       BOOL retry = True;
+                       for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++)
+                               nt_status = connect_to_domain_password_server(&cli, remote_machine, setup_creds_as,
+                                               sec_chan, trust_passwd, &retry);
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
+               return nt_status;
+       }
+
+       ZERO_STRUCT(info3);
+
+        /*
+         * If this call succeeds, we now have lots of info about the user
+         * in the info3 structure.  
+         */
+
+       nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx,
+                                                  user_info->smb_name.str, user_info->domain.str, 
+                                                  user_info->wksta_name.str, chal, 
+                                                  user_info->lm_resp, user_info->nt_resp, 
+                                                  &info3);
+        
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0,("domain_client_validate: unable to validate password "
+                         "for user %s in domain %s to Domain controller %s. "
+                         "Error was %s.\n", user_info->smb_name.str,
+                         user_info->domain.str, cli->srv_name_slash, 
+                         nt_errstr(nt_status)));
+       } else {
+               nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str, 
+                                                  user_info->smb_name.str, domain, server_info, &info3);
+#if 0 
+               /* The stuff doesn't work right yet */
+               SMB_ASSERT(sizeof((*server_info)->session_key) == sizeof(info3.user_sess_key)); 
+               memcpy((*server_info)->session_key, info3.user_sess_key, sizeof((*server_info)->session_key)/* 16 */);
+               SamOEMhash((*server_info)->session_key, trust_passwd, sizeof((*server_info)->session_key));
+#endif         
+
+               uni_group_cache_store_netlogon(mem_ctx, &info3);
+       }
+
+#if 0
+       /* 
+        * We don't actually need to do this - plus it fails currently with
+        * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
+        * send here. JRA.
+        */
+
+       if (NT_STATUS_IS_OK(status)) {
+               if(cli_nt_logoff(&cli, &ctr) == False) {
+                       DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
+%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli)));        
+                       nt_status = NT_STATUS_LOGON_FAILURE;
+               }
+       }
+#endif /* 0 */
+
+       /* Note - once the cli stream is shutdown the mem_ctx used
+          to allocate the other_sids and gids structures has been deleted - so
+          these pointers are no longer valid..... */
+
+       cli_nt_session_close(cli);
+       cli_ulogoff(cli);
+       cli_shutdown(cli);
+       release_server_mutex();
+       return nt_status;
+}
+
+/****************************************************************************
+ Check for a valid username and password in security=domain mode.
+****************************************************************************/
+
+static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
+                                       void *my_private_data, 
+                                       TALLOC_CTX *mem_ctx,
+                                       const auth_usersupplied_info *user_info, 
+                                       auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+       char *password_server;
+       unsigned char trust_passwd[16];
+       time_t last_change_time;
+       const char *domain = lp_workgroup();
+
+       if (!user_info || !server_info || !auth_context) {
+               DEBUG(1,("check_ntdomain_security: Critical variables not present.  Failing.\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* 
+        * Check that the requested domain is not our own machine name.
+        * If it is, we should never check the PDC here, we use our own local
+        * password file.
+        */
+
+       if(is_myname(user_info->domain.str)) {
+               DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /*
+        * Get the machine account password for our primary domain
+        * No need to become_root() as secrets_init() is done at startup.
+        */
+
+       if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time))
+       {
+               DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       /* Test if machine password has expired and needs to be changed */
+       if (lp_machine_password_timeout()) {
+               if (last_change_time > 0 && 
+                   time(NULL) > (last_change_time + 
+                                 lp_machine_password_timeout())) {
+                       global_machine_password_needs_changing = True;
+               }
+       }
+
+       /*
+        * Treat each name in the 'password server =' line as a potential
+        * PDC/BDC. Contact each in turn and try and authenticate.
+        */
+
+       password_server = lp_passwordserver();
+
+       nt_status = domain_client_validate(mem_ctx, user_info, domain,
+                                          (uchar *)auth_context->challenge.data, 
+                                          server_info, 
+                                          password_server, lp_netbios_name(), SEC_CHAN_WKSTA, trust_passwd, last_change_time);
+       return nt_status;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->name = "ntdomain";
+       (*auth_method)->auth = check_ntdomain_security;
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in a trusted domain
+****************************************************************************/
+
+static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
+                                          void *my_private_data, 
+                                          TALLOC_CTX *mem_ctx,
+                                          const auth_usersupplied_info *user_info, 
+                                          auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+       unsigned char trust_md4_password[16];
+       char *trust_password;
+       time_t last_change_time;
+       DOM_SID sid;
+
+       if (!user_info || !server_info || !auth_context) {
+               DEBUG(1,("check_trustdomain_security: Critical variables not present.  Failing.\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* 
+        * Check that the requested domain is not our own machine name.
+        * If it is, we should never check the PDC here, we use our own local
+        * password file.
+        */
+
+       if(is_myname(user_info->domain.str)) {
+               DEBUG(3,("check_trustdomain_security: Requested domain was for this machine.\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* 
+        * Check that the requested domain is not our own domain,
+        * If it is, we should use our own local password file.
+        */
+
+       if(strequal(lp_workgroup(), (user_info->domain.str))) {
+               DEBUG(3,("check_trustdomain_security: Requested domain was for this domain.\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /*
+        * Get the trusted account password for the trusted domain
+        * No need to become_root() as secrets_init() is done at startup.
+        */
+
+       if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
+       {
+               DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
+#endif
+       E_md4hash(trust_password, trust_md4_password);
+       SAFE_FREE(trust_password);
+
+#if 0
+       /* Test if machine password is expired and need to be changed */
+       if (time(NULL) > last_change_time + lp_machine_password_timeout())
+       {
+               global_machine_password_needs_changing = True;
+       }
+#endif
+
+       nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
+                                          (uchar *)auth_context->challenge.data, 
+                                          server_info, "*" /* Do a lookup */, 
+                                          lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
+       
+       return nt_status;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->name = "trustdomain";
+       (*auth_method)->auth = check_trustdomain_security;
+       return NT_STATUS_OK;
+}
diff --git a/source4/auth/auth_ntlmssp.c b/source4/auth/auth_ntlmssp.c
new file mode 100644 (file)
index 0000000..b3dff8d
--- /dev/null
@@ -0,0 +1,138 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   handle NLTMSSP, server side
+
+   Copyright (C) Andrew Tridgell      2001
+   Copyright (C) Andrew Bartlett 2001-2003
+
+   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"
+
+static const uint8 *auth_ntlmssp_get_challenge(struct ntlmssp_state *ntlmssp_state)
+{
+       AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+       return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context);
+}
+
+static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) 
+{
+       AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+       uint32 auth_flags = AUTH_FLAG_NONE;
+       auth_usersupplied_info *user_info = NULL;
+       DATA_BLOB plaintext_password = data_blob(NULL, 0);
+       NTSTATUS nt_status;
+
+       if (auth_ntlmssp_state->ntlmssp_state->lm_resp.length) {
+               auth_flags |= AUTH_FLAG_LM_RESP;
+       }
+
+       if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length == 24) {
+               auth_flags |= AUTH_FLAG_NTLM_RESP;
+       } else  if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) {
+               auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+       };
+
+       /* the client has given us its machine name (which we otherwise would not get on port 445).
+          we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
+
+       sub_set_remote_machine(auth_ntlmssp_state->ntlmssp_state->workstation);
+
+       /* setup the string used by %U */
+       /* sub_set_smb_name checks for weird internally */
+       sub_set_user_name(auth_ntlmssp_state->ntlmssp_state->user);
+
+       reload_services(True);
+
+       nt_status = make_user_info_map(&user_info, 
+                                      auth_ntlmssp_state->ntlmssp_state->user, 
+                                      auth_ntlmssp_state->ntlmssp_state->domain, 
+                                      auth_ntlmssp_state->ntlmssp_state->workstation, 
+                                      auth_ntlmssp_state->ntlmssp_state->lm_resp, 
+                                      auth_ntlmssp_state->ntlmssp_state->nt_resp, 
+                                      plaintext_password, 
+                                      auth_flags, True);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, user_info, &auth_ntlmssp_state->server_info); 
+                       
+       free_user_info(&user_info);
+
+       return nt_status;
+}
+
+NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+       NTSTATUS nt_status;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("AUTH NTLMSSP context");
+       
+       *auth_ntlmssp_state = talloc_zero(mem_ctx, sizeof(**auth_ntlmssp_state));
+       if (!*auth_ntlmssp_state) {
+               DEBUG(0,("auth_ntlmssp_start: talloc failed!\n"));
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*auth_ntlmssp_state);
+
+       (*auth_ntlmssp_state)->mem_ctx = mem_ctx;
+
+       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) {
+               return nt_status;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) {
+               return nt_status;
+       }
+
+       (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state);
+       (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+       (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password;
+       (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role();
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+       TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx;
+
+       if ((*auth_ntlmssp_state)->ntlmssp_state) {
+               ntlmssp_server_end(&(*auth_ntlmssp_state)->ntlmssp_state);
+       }
+       if ((*auth_ntlmssp_state)->auth_context) {
+               ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context);
+       }
+       if ((*auth_ntlmssp_state)->server_info) {
+               free_server_info(&(*auth_ntlmssp_state)->server_info);
+       }
+       talloc_destroy(mem_ctx);
+       *auth_ntlmssp_state = NULL;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, 
+                            const DATA_BLOB request, DATA_BLOB *reply) 
+{
+       return ntlmssp_server_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
+}
+
diff --git a/source4/auth/auth_sam.c b/source4/auth/auth_sam.c
new file mode 100644 (file)
index 0000000..1d097c9
--- /dev/null
@@ -0,0 +1,564 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Andrew Bartlett              2001
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+core of smb password checking routine.
+****************************************************************************/
+static BOOL smb_pwd_check_ntlmv1(DATA_BLOB nt_response,
+                                const uchar *part_passwd,
+                                DATA_BLOB sec_blob,
+                                uint8 user_sess_key[16])
+{
+       /* Finish the encryption of part_passwd. */
+       uchar p24[24];
+       
+       if (part_passwd == NULL) {
+               DEBUG(10,("No password set - DISALLOWING access\n"));
+               /* No password set - always false ! */
+               return False;
+       }
+       
+       if (sec_blob.length != 8) {
+               DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%d)\n", sec_blob.length));
+               return False;
+       }
+       
+       if (nt_response.length != 24) {
+               DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%d)\n", nt_response.length));
+               return False;
+       }
+
+       SMBOWFencrypt(part_passwd, sec_blob.data, p24);
+       if (user_sess_key != NULL)
+       {
+               SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key);
+       }
+       
+       
+       
+#if DEBUG_PASSWORD
+       DEBUG(100,("Part password (P16) was |"));
+       dump_data(100, part_passwd, 16);
+       DEBUG(100,("Password from client was |"));
+       dump_data(100, nt_response.data, nt_response.length);
+       DEBUG(100,("Given challenge was |"));
+       dump_data(100, sec_blob.data, sec_blob.length);
+       DEBUG(100,("Value from encryption was |"));
+       dump_data(100, p24, 24);
+#endif
+  return (memcmp(p24, nt_response.data, 24) == 0);
+}
+
+
+/****************************************************************************
+core of smb password checking routine. (NTLMv2, LMv2)
+
+Note:  The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB ntv2_response,
+                                const uchar *part_passwd,
+                                const DATA_BLOB sec_blob,
+                                const char *user, const char *domain,
+                                uint8 user_sess_key[16])
+{
+       /* Finish the encryption of part_passwd. */
+       uchar kr[16];
+       uchar value_from_encryption[16];
+       uchar client_response[16];
+       DATA_BLOB client_key_data;
+
+       if (part_passwd == NULL)
+       {
+               DEBUG(10,("No password set - DISALLOWING access\n"));
+               /* No password set - always False */
+               return False;
+       }
+
+       if (ntv2_response.length < 16) {
+               /* We MUST have more than 16 bytes, or the stuff below will go
+                  crazy... */
+               DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%d)\n", 
+                         ntv2_response.length));
+               return False;
+       }
+
+       client_key_data = data_blob(ntv2_response.data+16, ntv2_response.length-16);
+       /* 
+          todo:  should we be checking this for anything?  We can't for LMv2, 
+          but for NTLMv2 it is meant to contain the current time etc.
+       */
+
+       memcpy(client_response, ntv2_response.data, sizeof(client_response));
+
+       if (!ntv2_owf_gen(part_passwd, user, domain, kr)) {
+               return False;
+       }
+
+       SMBOWFencrypt_ntv2(kr, sec_blob, client_key_data, value_from_encryption);
+       if (user_sess_key != NULL)
+       {
+               SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key);
+       }
+
+#if DEBUG_PASSWORD
+       DEBUG(100,("Part password (P16) was |"));
+       dump_data(100, part_passwd, 16);
+       DEBUG(100,("Password from client was |"));
+       dump_data(100, ntv2_response.data, ntv2_response.length);
+       DEBUG(100,("Variable data from client was |"));
+       dump_data(100, client_key_data.data, client_key_data.length);
+       DEBUG(100,("Given challenge was |"));
+       dump_data(100, sec_blob.data, sec_blob.length);
+       DEBUG(100,("Value from encryption was |"));
+       dump_data(100, value_from_encryption, 16);
+#endif
+       data_blob_clear_free(&client_key_data);
+       return (memcmp(value_from_encryption, client_response, 16) == 0);
+}
+
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+static NTSTATUS sam_password_ok(const struct auth_context *auth_context,
+                               TALLOC_CTX *mem_ctx,
+                               SAM_ACCOUNT *sampass, 
+                               const auth_usersupplied_info *user_info, 
+                               uint8 user_sess_key[16])
+{
+       uint16 acct_ctrl;
+       const uint8 *nt_pw, *lm_pw;
+       uint32 auth_flags;
+
+       acct_ctrl = pdb_get_acct_ctrl(sampass);
+       if (acct_ctrl & ACB_PWNOTREQ) 
+       {
+               if (lp_null_passwords()) 
+               {
+                       DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", pdb_get_username(sampass)));
+                       return(NT_STATUS_OK);
+               } 
+               else 
+               {
+                       DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", pdb_get_username(sampass)));
+                       return(NT_STATUS_LOGON_FAILURE);
+               }               
+       }
+
+       auth_flags = user_info->auth_flags;
+
+       if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) {
+               DEBUG(3,("sam_password_ok: NO NT password stored for user %s.\n", 
+                        pdb_get_username(sampass)));
+               /* No return, we want to check the LM hash below in this case */
+               auth_flags &= (~(AUTH_FLAG_NTLMv2_RESP |  AUTH_FLAG_NTLM_RESP));
+       }
+       
+       if (auth_flags & AUTH_FLAG_NTLMv2_RESP) {
+               nt_pw = pdb_get_nt_passwd(sampass);
+               /* We have the NT MD4 hash challenge available - see if we can
+                  use it (ie. does it exist in the smbpasswd file).
+               */
+               DEBUG(4,("sam_password_ok: Checking NTLMv2 password with domain [%s]\n", user_info->client_domain.str));
+               if (smb_pwd_check_ntlmv2( user_info->nt_resp, 
+                                         nt_pw, auth_context->challenge, 
+                                         user_info->smb_name.str, 
+                                         user_info->client_domain.str,
+                                         user_sess_key))
+               {
+                       return NT_STATUS_OK;
+               }
+
+               DEBUG(4,("sam_password_ok: Checking NTLMv2 password without a domain\n"));
+               if (smb_pwd_check_ntlmv2( user_info->nt_resp, 
+                                         nt_pw, auth_context->challenge, 
+                                         user_info->smb_name.str, 
+                                         "",
+                                         user_sess_key))
+               {
+                       return NT_STATUS_OK;
+               } else {
+                       DEBUG(3,("sam_password_ok: NTLMv2 password check failed\n"));
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
+       } else if (auth_flags & AUTH_FLAG_NTLM_RESP) {
+               if (lp_ntlm_auth()) {           
+                       nt_pw = pdb_get_nt_passwd(sampass);
+                       /* We have the NT MD4 hash challenge available - see if we can
+                          use it (ie. does it exist in the smbpasswd file).
+                       */
+                       DEBUG(4,("sam_password_ok: Checking NT MD4 password\n"));
+                       if (smb_pwd_check_ntlmv1(user_info->nt_resp, 
+                                                nt_pw, auth_context->challenge,
+                                                user_sess_key)) 
+                       {
+                               return NT_STATUS_OK;
+                       } else {
+                               DEBUG(3,("sam_password_ok: NT MD4 password check failed for user %s\n",pdb_get_username(sampass)));
+                               return NT_STATUS_WRONG_PASSWORD;
+                       }
+               } else {
+                       DEBUG(2,("sam_password_ok: NTLMv1 passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass)));                   
+                       /* no return, becouse we might pick up LMv2 in the LM feild */
+               }
+       }
+       
+       if (auth_flags & AUTH_FLAG_LM_RESP) {
+               if (user_info->lm_resp.length != 24) {
+                       DEBUG(2,("sam_password_ok: invalid LanMan password length (%d) for user %s\n", 
+                                user_info->nt_resp.length, pdb_get_username(sampass)));                
+               }
+               
+               if (!lp_lanman_auth()) {
+                       DEBUG(3,("sam_password_ok: Lanman passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass)));
+               } else if (IS_SAM_DEFAULT(sampass, PDB_LMPASSWD)) {
+                       DEBUG(3,("sam_password_ok: NO LanMan password set for user %s (and no NT password supplied)\n",pdb_get_username(sampass)));
+               } else {
+                       lm_pw = pdb_get_lanman_passwd(sampass);
+                       
+                       DEBUG(4,("sam_password_ok: Checking LM password\n"));
+                       if (smb_pwd_check_ntlmv1(user_info->lm_resp, 
+                                                lm_pw, auth_context->challenge,
+                                                user_sess_key)) 
+                       {
+                               return NT_STATUS_OK;
+                       }
+               }
+
+               if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) {
+                       DEBUG(4,("sam_password_ok: LM password check failed for user, no NT password %s\n",pdb_get_username(sampass)));
+                       return NT_STATUS_WRONG_PASSWORD;
+               } 
+               
+               nt_pw = pdb_get_nt_passwd(sampass);
+
+               /* This is for 'LMv2' authentication.  almost NTLMv2 but limited to 24 bytes.
+                  - related to Win9X, legacy NAS pass-though authentication
+               */
+               DEBUG(4,("sam_password_ok: Checking LMv2 password with domain %s\n", user_info->client_domain.str));
+               if (smb_pwd_check_ntlmv2( user_info->lm_resp, 
+                                         nt_pw, auth_context->challenge, 
+                                         user_info->smb_name.str, 
+                                         user_info->client_domain.str,
+                                         user_sess_key))
+               {
+                       return NT_STATUS_OK;
+               }
+
+               DEBUG(4,("sam_password_ok: Checking LMv2 password without a domain\n"));
+               if (smb_pwd_check_ntlmv2( user_info->lm_resp, 
+                                         nt_pw, auth_context->challenge, 
+                                         user_info->smb_name.str, 
+                                         "",
+                                         user_sess_key))
+               {
+                       return NT_STATUS_OK;
+               }
+
+               /* Apparently NT accepts NT responses in the LM field
+                  - I think this is related to Win9X pass-though authentication
+               */
+               DEBUG(4,("sam_password_ok: Checking NT MD4 password in LM field\n"));
+               if (lp_ntlm_auth()) 
+               {
+                       if (smb_pwd_check_ntlmv1(user_info->lm_resp, 
+                                                nt_pw, auth_context->challenge,
+                                                user_sess_key)) 
+                       {
+                               return NT_STATUS_OK;
+                       }
+                       DEBUG(3,("sam_password_ok: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",pdb_get_username(sampass)));
+                       return NT_STATUS_WRONG_PASSWORD;
+               } else {
+                       DEBUG(3,("sam_password_ok: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",pdb_get_username(sampass)));
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
+                       
+       }
+               
+       /* Should not be reached, but if they send nothing... */
+       DEBUG(3,("sam_password_ok: NEITHER LanMan nor NT password supplied for user %s\n",pdb_get_username(sampass)));
+       return NT_STATUS_WRONG_PASSWORD;
+}
+
+/****************************************************************************
+ Do a specific test for a SAM_ACCOUNT being vaild for this connection 
+ (ie not disabled, expired and the like).
+****************************************************************************/
+static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx,
+                              SAM_ACCOUNT *sampass, 
+                              const auth_usersupplied_info *user_info)
+{
+       uint16  acct_ctrl = pdb_get_acct_ctrl(sampass);
+       char *workstation_list;
+       time_t kickoff_time;
+       
+       DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass)));
+
+       /* Quit if the account was disabled. */
+       if (acct_ctrl & ACB_DISABLED) {
+               DEBUG(1,("Account for user '%s' was disabled.\n", pdb_get_username(sampass)));
+               return NT_STATUS_ACCOUNT_DISABLED;
+       }
+
+       /* Test account expire time */
+       
+       kickoff_time = pdb_get_kickoff_time(sampass);
+       if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+               DEBUG(1,("Account for user '%s' has expried.\n", pdb_get_username(sampass)));
+               DEBUG(3,("Account expired at '%ld' unix time.\n", (long)kickoff_time));
+               return NT_STATUS_ACCOUNT_EXPIRED;
+       }
+
+       if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP)) {
+               time_t must_change_time = pdb_get_pass_must_change_time(sampass);
+               time_t last_set_time = pdb_get_pass_last_set_time(sampass);
+
+               /* check for immediate expiry "must change at next logon" */
+               if (must_change_time == 0 && last_set_time != 0) {
+                       DEBUG(1,("Account for user '%s' password must change!.\n", pdb_get_username(sampass)));
+                       return NT_STATUS_PASSWORD_MUST_CHANGE;
+               }
+
+               /* check for expired password */
+               if (must_change_time < time(NULL) && must_change_time != 0) {
+                       DEBUG(1,("Account for user '%s' password expired!.\n", pdb_get_username(sampass)));
+                       DEBUG(1,("Password expired at '%s' (%ld) unix time.\n", http_timestring(mem_ctx, must_change_time), (long)must_change_time));
+                       return NT_STATUS_PASSWORD_EXPIRED;
+               }
+       }
+
+       /* Test workstation. Workstation list is comma separated. */
+
+       workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass));
+
+       if (!workstation_list) return NT_STATUS_NO_MEMORY;
+
+       if (*workstation_list) {
+               BOOL invalid_ws = True;
+               const char *s = workstation_list;
+                       
+               fstring tok;
+                       
+               while (next_token(&s, tok, ",", sizeof(tok))) {
+                       DEBUG(10,("checking for workstation match %s and %s (len=%d)\n",
+                                 tok, user_info->wksta_name.str, user_info->wksta_name.len));
+                       if(strequal(tok, user_info->wksta_name.str)) {
+                               invalid_ws = False;
+                               break;
+                       }
+               }
+               
+               if (invalid_ws) 
+                       return NT_STATUS_INVALID_WORKSTATION;
+       }
+
+       if (acct_ctrl & ACB_DOMTRUST) {
+               DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass)));
+               return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+       }
+       
+       if (acct_ctrl & ACB_SVRTRUST) {
+               DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass)));
+               return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+       }
+       
+       if (acct_ctrl & ACB_WSTRUST) {
+               DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass)));
+               return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash supplied in the user_info structure
+return an NT_STATUS constant.
+****************************************************************************/
+
+static NTSTATUS check_sam_security(const struct auth_context *auth_context,
+                                  void *my_private_data, 
+                                  TALLOC_CTX *mem_ctx,
+                                  const auth_usersupplied_info *user_info, 
+                                  auth_serversupplied_info **server_info)
+{
+       SAM_ACCOUNT *sampass=NULL;
+       BOOL ret;
+       NTSTATUS nt_status;
+       uint8 user_sess_key[16];
+       const uint8* lm_hash;
+
+       if (!user_info || !auth_context) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Can't use the talloc version here, becouse the returned struct gets
+          kept on the server_info */
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+               return nt_status;
+       }
+
+       /* get the account information */
+
+       become_root();
+       ret = pdb_getsampwnam(sampass, user_info->internal_username.str);
+       unbecome_root();
+
+       if (ret == False)
+       {
+               DEBUG(3,("Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str));
+               pdb_free_sam(&sampass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       nt_status = sam_account_ok(mem_ctx, sampass, user_info);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               pdb_free_sam(&sampass);
+               return nt_status;
+       }
+
+       nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, user_sess_key);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               pdb_free_sam(&sampass);
+               return nt_status;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_server_info_sam(server_info, sampass))) {         
+               DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
+               return nt_status;
+       }
+
+       lm_hash = pdb_get_lanman_passwd((*server_info)->sam_account);
+       if (lm_hash) {
+               memcpy((*server_info)->first_8_lm_hash, lm_hash, 8);
+       }
+       
+       memcpy((*server_info)->session_key, user_sess_key, sizeof(user_sess_key));
+
+       return nt_status;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->auth = check_sam_security;      
+       (*auth_method)->name = "sam";
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+
+static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
+                                        void *my_private_data, 
+                                        TALLOC_CTX *mem_ctx,
+                                        const auth_usersupplied_info *user_info, 
+                                        auth_serversupplied_info **server_info)
+{
+
+       if (!user_info || !auth_context) {
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* If we are a domain member, we must not 
+          attempt to check the password locally,
+          unless it is one of our aliases. */
+       
+       if (!is_myname(user_info->domain.str)) {
+               DEBUG(7,("The requested user domain is not the local server name. [%s]\\[%s]\n",
+                       user_info->domain.str,user_info->internal_username.str));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
+}
+
+/* module initialisation */
+NTSTATUS auth_init_samstrict(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->auth = check_samstrict_security;
+       (*auth_method)->name = "samstrict";
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks if we're a DC.
+****************************************************************************/
+
+static NTSTATUS check_samstrict_dc_security(const struct auth_context *auth_context,
+                                        void *my_private_data, 
+                                        TALLOC_CTX *mem_ctx,
+                                        const auth_usersupplied_info *user_info, 
+                                        auth_serversupplied_info **server_info)
+{
+
+       if (!user_info || !auth_context) {
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* If we are a domain member, we must not 
+          attempt to check the password locally,
+          unless it is one of our aliases, empty
+          or our domain if we are a logon server.*/
+       
+
+       if ((strcasecmp(lp_workgroup(), user_info->domain.str) != 0) &&
+           (!is_myname(user_info->domain.str))) {
+               DEBUG(7,("The requested user domain is not the local server name or our domain. [%s]\\[%s]\n",
+                       user_info->domain.str,user_info->internal_username.str));
+               return NT_STATUS_NO_SUCH_USER;
+       }               
+
+       return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
+}
+
+/* module initialisation */
+NTSTATUS auth_init_samstrict_dc(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->auth = check_samstrict_dc_security;
+       (*auth_method)->name = "samstrict_dc";
+       return NT_STATUS_OK;
+}
diff --git a/source4/auth/auth_server.c b/source4/auth/auth_server.c
new file mode 100644 (file)
index 0000000..620d9a3
--- /dev/null
@@ -0,0 +1,402 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Authenticate to a remote server
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Andrew Bartlett 2001
+
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern userdom_struct current_user_info;
+
+/****************************************************************************
+ Support for server level security.
+****************************************************************************/
+
+static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
+{
+       struct cli_state *cli = NULL;
+       fstring desthost;
+       struct in_addr dest_ip;
+       const char *p;
+       char *pserver;
+       BOOL connected_ok = False;
+
+       if (!(cli = cli_initialise(cli)))
+               return NULL;
+
+       /* security = server just can't function with spnego */
+       cli->use_spnego = False;
+
+        pserver = talloc_strdup(mem_ctx, lp_passwordserver());
+       p = pserver;
+
+        while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
+               standard_sub_basic(current_user_info.smb_name, desthost, sizeof(desthost));
+               strupper(desthost);
+
+               if(!resolve_name( desthost, &dest_ip, 0x20)) {
+                       DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
+                       continue;
+               }
+
+               if (ismyip(dest_ip)) {
+                       DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
+                       continue;
+               }
+
+               /* we use a mutex to prevent two connections at once - when a 
+                  Win2k PDC get two connections where one hasn't completed a 
+                  session setup yet it will send a TCP reset to the first 
+                  connection (tridge) */
+
+               if (!grab_server_mutex(desthost)) {
+                       return NULL;
+               }
+
+               if (cli_connect(cli, desthost, &dest_ip)) {
+                       DEBUG(3,("connected to password server %s\n",desthost));
+                       connected_ok = True;
+                       break;
+               }
+       }
+
+       if (!connected_ok) {
+               release_server_mutex();
+               DEBUG(0,("password server not available\n"));
+               cli_shutdown(cli);
+               return NULL;
+       }
+       
+       if (!attempt_netbios_session_request(cli, lp_netbios_name(), 
+                                            desthost, &dest_ip)) {
+               release_server_mutex();
+               DEBUG(1,("password server fails session request\n"));
+               cli_shutdown(cli);
+               return NULL;
+       }
+       
+       if (strequal(desthost,myhostname(mem_ctx))) {
+               exit_server("Password server loop!");
+       }
+       
+       DEBUG(3,("got session\n"));
+
+       if (!cli_negprot(cli)) {
+               DEBUG(1,("%s rejected the negprot\n",desthost));
+               release_server_mutex();
+               cli_shutdown(cli);
+               return NULL;
+       }
+
+       if (cli->protocol < PROTOCOL_LANMAN2 ||
+           !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+               DEBUG(1,("%s isn't in user level security mode\n",desthost));
+               release_server_mutex();
+               cli_shutdown(cli);
+               return NULL;
+       }
+
+       /* Get the first session setup done quickly, to avoid silly 
+          Win2k bugs.  (The next connection to the server will kill
+          this one... 
+       */
+
+       if (!cli_session_setup(cli, "", "", 0, "", 0,
+                              "")) {
+               DEBUG(0,("%s rejected the initial session setup (%s)\n",
+                        desthost, cli_errstr(cli)));
+               release_server_mutex();
+               cli_shutdown(cli);
+               return NULL;
+       }
+       
+       release_server_mutex();
+       
+       DEBUG(3,("password server OK\n"));
+       
+       return cli;
+}
+
+/****************************************************************************
+ Clean up our allocated cli.
+****************************************************************************/
+
+static void free_server_private_data(void **private_data_pointer) 
+{
+       struct cli_state **cli = (struct cli_state **)private_data_pointer;
+       if (*cli && (*cli)->initialised) {
+               cli_shutdown(*cli);
+       }
+}
+
+/****************************************************************************
+ Send a 'keepalive' packet down the cli pipe.
+****************************************************************************/
+
+static void send_server_keepalive(void **private_data_pointer) 
+{
+       struct cli_state **cli = (struct cli_state **)private_data_pointer;
+       
+       /* also send a keepalive to the password server if its still
+          connected */
+       if (cli && *cli && (*cli)->initialised) {
+               if (!send_nbt_keepalive((*cli)->fd)) {
+                       DEBUG( 2, ( "password server keepalive failed.\n"));
+                       cli_shutdown(*cli);
+               }
+       }
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
+                                          void **my_private_data, 
+                                          TALLOC_CTX *mem_ctx)
+{
+       struct cli_state *cli = server_cryptkey(mem_ctx);
+       
+       if (cli) {
+               DEBUG(3,("using password server validation\n"));
+
+               if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+                       /* We can't work with unencrypted password servers
+                          unless 'encrypt passwords = no' */
+                       DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
+                       
+                       /* However, it is still a perfectly fine connection
+                          to pass that unencrypted password over */
+                       *my_private_data = (void *)cli;
+                       return data_blob(NULL, 0);
+                       
+               } else if (cli->secblob.length < 8) {
+                       /* We can't do much if we don't get a full challenge */
+                       DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
+                       cli_shutdown(cli);
+                       return data_blob(NULL, 0);
+               }
+
+               *my_private_data = (void *)cli;
+
+               /* The return must be allocated on the caller's mem_ctx, as our own will be
+                  destoyed just after the call. */
+               return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
+       } else {
+               return data_blob(NULL, 0);
+       }
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in security=server mode.
+  - Validate a password with the password server.
+****************************************************************************/
+
+static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
+                                        void *my_private_data, 
+                                        TALLOC_CTX *mem_ctx,
+                                        const auth_usersupplied_info *user_info, 
+                                        auth_serversupplied_info **server_info)
+{
+       struct cli_state *cli;
+       static unsigned char badpass[24];
+       static fstring baduser; 
+       static BOOL tested_password_server = False;
+       static BOOL bad_password_server = False;
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+       BOOL locally_made_cli = False;
+
+       /* 
+        * Check that the requested domain is not our own machine name.
+        * If it is, we should never check the PDC here, we use our own local
+        * password file.
+        */
+
+       if(is_myname(user_info->domain.str)) {
+               DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       cli = my_private_data;
+       
+       if (cli) {
+       } else {
+               cli = server_cryptkey(mem_ctx);
+               locally_made_cli = True;
+       }
+
+       if (!cli || !cli->initialised) {
+               DEBUG(1,("password server is not connected (cli not initilised)\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }  
+       
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+               if (user_info->encrypted) {
+                       DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
+                       return NT_STATUS_LOGON_FAILURE;         
+               }
+       } else {
+               if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
+                       DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
+                       return NT_STATUS_LOGON_FAILURE;         
+               }
+       }
+
+       if(badpass[0] == 0)
+               memset(badpass, 0x1f, sizeof(badpass));
+
+       if((user_info->nt_resp.length == sizeof(badpass)) && 
+          !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
+               /* 
+                * Very unlikely, our random bad password is the same as the users
+                * password.
+                */
+               memset(badpass, badpass[0]+1, sizeof(badpass));
+       }
+
+       if(baduser[0] == 0) {
+               fstrcpy(baduser, INVALID_USER_PREFIX);
+               fstrcat(baduser, lp_netbios_name());
+       }
+
+       /*
+        * Attempt a session setup with a totally incorrect password.
+        * If this succeeds with the guest bit *NOT* set then the password
+        * server is broken and is not correctly setting the guest bit. We
+        * need to detect this as some versions of NT4.x are broken. JRA.
+        */
+
+       /* I sure as hell hope that there aren't servers out there that take 
+        * NTLMv2 and have this bug, as we don't test for that... 
+        *  - abartlet@samba.org
+        */
+
+       if ((!tested_password_server) && (lp_paranoid_server_security())) {
+               if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass), 
+                                       (char *)badpass, sizeof(badpass), user_info->domain.str)) {
+
+                       /*
+                        * We connected to the password server so we
+                        * can say we've tested it.
+                        */
+                       tested_password_server = True;
+
+                       if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
+                               DEBUG(0,("server_validate: password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+                               DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+                               cli_ulogoff(cli);
+
+                               /*
+                                * Password server has the bug.
+                                */
+                               bad_password_server = True;
+                               return NT_STATUS_LOGON_FAILURE;
+                       }
+                       cli_ulogoff(cli);
+               }
+       } else {
+
+               /*
+                * We have already tested the password server.
+                * Fail immediately if it has the bug.
+                */
+
+               if(bad_password_server) {
+                       DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+                       DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+                       return NT_STATUS_LOGON_FAILURE;
+               }
+       }
+
+       /*
+        * Now we know the password server will correctly set the guest bit, or is
+        * not guest enabled, we can try with the real password.
+        */
+
+       if (!user_info->encrypted) {
+               /* Plaintext available */
+               if (!cli_session_setup(cli, user_info->smb_name.str, 
+                                      (char *)user_info->plaintext_password.data, 
+                                      user_info->plaintext_password.length, 
+                                      NULL, 0,
+                                      user_info->domain.str)) {
+                       DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+                       /* Make this cli_nt_error() when the conversion is in */
+                       nt_status = cli_nt_error(cli);
+               } else {
+                       nt_status = NT_STATUS_OK;
+               }
+       } else {
+               if (!cli_session_setup(cli, user_info->smb_name.str, 
+                                      (char *)user_info->lm_resp.data, 
+                                      user_info->lm_resp.length, 
+                                      (char *)user_info->nt_resp.data, 
+                                      user_info->nt_resp.length, 
+                                      user_info->domain.str)) {
+                       DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+                       /* Make this cli_nt_error() when the conversion is in */
+                       nt_status = cli_nt_error(cli);
+               } else {
+                       nt_status = NT_STATUS_OK;
+               }
+       }
+
+       /* if logged in as guest then reject */
+       if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
+               DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
+               nt_status = NT_STATUS_LOGON_FAILURE;
+       }
+
+       cli_ulogoff(cli);
+
+       if NT_STATUS_IS_OK(nt_status) {
+               struct passwd *pass = Get_Pwnam(user_info->internal_username.str);
+               if (pass) {
+                       nt_status = make_server_info_pw(server_info, pass);
+               } else {
+                       nt_status = NT_STATUS_NO_SUCH_USER;
+               }
+       }
+
+       if (locally_made_cli) {
+               cli_shutdown(cli);
+       }
+
+       return(nt_status);
+}
+
+NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       (*auth_method)->name = "smbserver";
+       (*auth_method)->auth = check_smbserver_security;
+       (*auth_method)->get_chal = auth_get_challenge_server;
+       (*auth_method)->send_keepalive = send_server_keepalive;
+       (*auth_method)->free_private_data = free_server_private_data;
+       return NT_STATUS_OK;
+}
diff --git a/source4/auth/auth_unix.c b/source4/auth/auth_unix.c
new file mode 100644 (file)
index 0000000..4f44767
--- /dev/null
@@ -0,0 +1,132 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Bartlett              2001
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * update the encrypted smbpasswd file from the plaintext username and password
+ *  
+ *  this ugly hack needs to die, but not quite yet, I think people still use it...
+ **/
+static BOOL update_smbpassword_file(const char *user, const char *password)
+{
+       SAM_ACCOUNT     *sampass = NULL;
+       BOOL            ret;
+       
+       pdb_init_sam(&sampass);
+       
+       become_root();
+       ret = pdb_getsampwnam(sampass, user);
+       unbecome_root();
+
+       if(ret == False) {
+               DEBUG(0,("pdb_getsampwnam returned NULL\n"));
+               pdb_free_sam(&sampass);
+               return False;
+       }
+
+       /*
+        * Remove the account disabled flag - we are updating the
+        * users password from a login.
+        */
+       if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED, PDB_CHANGED)) {
+               pdb_free_sam(&sampass);
+               return False;
+       }
+
+       if (!pdb_set_plaintext_passwd (sampass, password)) {
+               pdb_free_sam(&sampass);
+               return False;
+       }
+
+       /* Now write it into the file. */
+       become_root();
+
+       ret = pdb_update_sam_account (sampass);
+
+       unbecome_root();
+
+       if (ret) {
+               DEBUG(3,("pdb_update_sam_account returned %d\n",ret));
+       }
+
+       pdb_free_sam(&sampass);
+       return ret;
+}
+
+
+/** Check a plaintext username/password
+ *
+ * Cannot deal with an encrupted password in any manner whatsoever,
+ * unless the account has a null password.
+ **/
+
+static NTSTATUS check_unix_security(const struct auth_context *auth_context,
+                            void *my_private_data, 
+                            TALLOC_CTX *mem_ctx,
+                            const auth_usersupplied_info *user_info, 
+                            auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status;
+       struct passwd *pass = NULL;
+
+       become_root();
+       pass = Get_Pwnam(user_info->internal_username.str);
+
+       
+       /** @todo This call assumes a ASCII password, no charset transformation is 
+           done.  We may need to revisit this **/
+       nt_status = pass_check(pass,
+                               pass ? pass->pw_name : user_info->internal_username.str, 
+                               (char *)user_info->plaintext_password.data,
+                               user_info->plaintext_password.length-1,
+                               lp_update_encrypted() ? 
+                               update_smbpassword_file : NULL,
+                               True);
+       
+       unbecome_root();
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+               if (pass) {
+                       make_server_info_pw(server_info, pass);
+               } else {
+                       /* we need to do somthing more useful here */
+                       nt_status = NT_STATUS_NO_SUCH_USER;
+               }
+       }
+
+       return nt_status;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*auth_method)->name = "unix";
+       (*auth_method)->auth = check_unix_security;
+       return NT_STATUS_OK;
+}
+
diff --git a/source4/auth/auth_util.c b/source4/auth/auth_util.c
new file mode 100644 (file)
index 0000000..7096361
--- /dev/null
@@ -0,0 +1,1222 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Authentication utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Andrew Bartlett 2001
+   Copyright (C) Jeremy Allison 2000-2001
+   Copyright (C) Rafal Szczesniak 2002
+
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Network;
+extern DOM_SID global_sid_Builtin_Guests;
+extern DOM_SID global_sid_Authenticated_Users;
+
+
+/****************************************************************************
+ Create a UNIX user on demand.
+****************************************************************************/
+static int smb_create_user(const char *unix_user, const char *homedir)
+{
+       pstring add_script;
+       int ret;
+
+       pstrcpy(add_script, lp_adduser_script());
+       if (! *add_script)
+               return -1;
+       all_string_sub(add_script, "%u", unix_user, sizeof(pstring));
+       if (homedir)
+               all_string_sub(add_script, "%H", homedir, sizeof(pstring));
+       ret = smbrun(add_script,NULL);
+       DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret));
+       return ret;
+}
+
+/****************************************************************************
+ Add and Delete UNIX users on demand, based on NTSTATUS codes.
+****************************************************************************/
+
+void smb_user_control(const auth_usersupplied_info *user_info, auth_serversupplied_info *server_info, NTSTATUS nt_status)
+{
+       struct passwd *pwd=NULL;
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+
+               if (!(server_info->sam_fill_level & SAM_FILL_UNIX)) {
+                       
+                       /*
+                        * User validated ok against Domain controller.
+                        * If the admin wants us to try and create a UNIX
+                        * user on the fly, do so.
+                        */
+                       
+                       if(lp_adduser_script() && !(pwd = Get_Pwnam(user_info->internal_username.str))) {
+                               smb_create_user(user_info->internal_username.str, NULL);
+                       }
+               }
+       }
+}
+
+/****************************************************************************
+ Create a SAM_ACCOUNT - either by looking in the pdb, or by faking it up from
+ unix info.
+****************************************************************************/
+
+NTSTATUS auth_get_sam_account(const char *user, SAM_ACCOUNT **account) 
+{
+       BOOL pdb_ret;
+       NTSTATUS nt_status;
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(account))) {
+               return nt_status;
+       }
+       
+       become_root();
+       pdb_ret = pdb_getsampwnam(*account, user);
+       unbecome_root();
+
+       if (!pdb_ret) {
+               
+               struct passwd *pass = Get_Pwnam(user);
+               if (!pass) 
+                       return NT_STATUS_NO_SUCH_USER;
+
+               if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*account, pass))) {
+                       return nt_status;
+               }
+       }
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+static NTSTATUS make_user_info(auth_usersupplied_info **user_info, 
+                               const char *smb_name, 
+                               const char *internal_username,
+                               const char *client_domain, 
+                               const char *domain,
+                               const char *wksta_name, 
+                               DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+                               DATA_BLOB plaintext, 
+                               uint32 auth_flags, BOOL encrypted)
+{
+
+       DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name));
+
+       *user_info = malloc(sizeof(**user_info));
+       if (!user_info) {
+               DEBUG(0,("malloc failed for user_info (size %d)\n", sizeof(*user_info)));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*user_info);
+
+       DEBUG(5,("making strings for %s's user_info struct\n", internal_username));
+
+       (*user_info)->smb_name.str = strdup(smb_name);
+       if ((*user_info)->smb_name.str) { 
+               (*user_info)->smb_name.len = strlen(smb_name);
+       } else {
+               free_user_info(user_info);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       (*user_info)->internal_username.str = strdup(internal_username);
+       if ((*user_info)->internal_username.str) { 
+               (*user_info)->internal_username.len = strlen(internal_username);
+       } else {
+               free_user_info(user_info);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*user_info)->domain.str = strdup(domain);
+       if ((*user_info)->domain.str) { 
+               (*user_info)->domain.len = strlen(domain);
+       } else {
+               free_user_info(user_info);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*user_info)->client_domain.str = strdup(client_domain);
+       if ((*user_info)->client_domain.str) { 
+               (*user_info)->client_domain.len = strlen(client_domain);
+       } else {
+               free_user_info(user_info);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*user_info)->wksta_name.str = strdup(wksta_name);
+       if ((*user_info)->wksta_name.str) { 
+               (*user_info)->wksta_name.len = strlen(wksta_name);
+       } else {
+               free_user_info(user_info);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       DEBUG(5,("making blobs for %s's user_info struct\n", internal_username));
+
+       (*user_info)->lm_resp = data_blob(lm_pwd.data, lm_pwd.length);
+       (*user_info)->nt_resp = data_blob(nt_pwd.data, nt_pwd.length);
+       (*user_info)->plaintext_password = data_blob(plaintext.data, plaintext.length);
+
+       (*user_info)->encrypted = encrypted;
+       (*user_info)->auth_flags = auth_flags;
+
+       DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name));
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS make_user_info_map(auth_usersupplied_info **user_info, 
+                           const char *smb_name, 
+                           const char *client_domain, 
+                           const char *wksta_name, 
+                           DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+                           DATA_BLOB plaintext, 
+                           uint32 ntlmssp_flags, BOOL encrypted)
+{
+       const char *domain;
+       fstring internal_username;
+       fstrcpy(internal_username, smb_name);
+       
+       DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n",
+             client_domain, smb_name, wksta_name));
+       
+       if (lp_allow_trusted_domains() && *client_domain) {
+
+               /* the client could have given us a workstation name
+                  or other crap for the workgroup - we really need a
+                  way of telling if this domain name is one of our
+                  trusted domain names 
+
+                  Also don't allow "" as a domain, fixes a Win9X bug 
+                  where it doens't supply a domain for logon script
+                  'net use' commands.
+
+                  The way I do it here is by checking if the fully
+                  qualified username exists. This is rather reliant
+                  on winbind, but until we have a better method this
+                  will have to do 
+               */
+
+               domain = client_domain;
+
+               if ((smb_name) && (*smb_name)) { /* Don't do this for guests */
+                       char *user = NULL;
+                       if (asprintf(&user, "%s%s%s", 
+                                client_domain, lp_winbind_separator(), 
+                                smb_name) < 0) {
+                               DEBUG(0, ("make_user_info_map: asprintf() failed!\n"));
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       DEBUG(5, ("make_user_info_map: testing for user %s\n", user));
+                       
+                       if (Get_Pwnam(user) == NULL) {
+                               DEBUG(5, ("make_user_info_map: test for user %s failed\n", user));
+                               domain = lp_workgroup();
+                               DEBUG(5, ("make_user_info_map: trusted domain %s doesn't appear to exist, using %s\n", 
+                                         client_domain, domain));
+                       } else {
+                               DEBUG(5, ("make_user_info_map: using trusted domain %s\n", domain));
+                       }
+                       SAFE_FREE(user);
+               }
+       } else {
+               domain = lp_workgroup();
+       }
+       
+       return make_user_info(user_info, 
+                             smb_name, internal_username,
+                             client_domain, domain,
+                             wksta_name, 
+                             lm_pwd, nt_pwd,
+                             plaintext, 
+                             ntlmssp_flags, encrypted);
+       
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here. 
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info, 
+                                    const char *smb_name, 
+                                    const char *client_domain, 
+                                    const char *wksta_name, 
+                                    const uchar *lm_network_pwd, int lm_pwd_len,
+                                    const uchar *nt_network_pwd, int nt_pwd_len)
+{
+       BOOL ret;
+       NTSTATUS nt_status;
+       DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len);
+       DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len);
+       DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+       uint32 auth_flags = AUTH_FLAG_NONE;
+
+       if (lm_pwd_len)
+               auth_flags |= AUTH_FLAG_LM_RESP;
+       if (nt_pwd_len == 24) {
+               auth_flags |= AUTH_FLAG_NTLM_RESP; 
+       } else if (nt_pwd_len != 0) {
+               auth_flags |= AUTH_FLAG_NTLMv2_RESP; 
+       }
+
+       nt_status = make_user_info_map(user_info,
+                                     smb_name, client_domain, 
+                                  wksta_name, 
+                                     lm_blob, nt_blob,
+                                     plaintext_blob, 
+                                     auth_flags, True);
+       
+       ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+               
+       data_blob_free(&lm_blob);
+       data_blob_free(&nt_blob);
+       return ret;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here. 
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info, 
+                                        const char *smb_name, 
+                                        const char *client_domain, 
+                                        const char *wksta_name, 
+                                        const uchar chal[8], 
+                                        const uchar lm_interactive_pwd[16], 
+                                        const uchar nt_interactive_pwd[16], 
+                                        const uchar *dc_sess_key)
+{
+       char lm_pwd[16];
+       char nt_pwd[16];
+       unsigned char local_lm_response[24];
+       unsigned char local_nt_response[24];
+       unsigned char key[16];
+       uint32 auth_flags = AUTH_FLAG_NONE;
+       
+       ZERO_STRUCT(key);
+       memcpy(key, dc_sess_key, 8);
+       
+       if (lm_interactive_pwd) memcpy(lm_pwd, lm_interactive_pwd, sizeof(lm_pwd));
+       if (nt_interactive_pwd) memcpy(nt_pwd, nt_interactive_pwd, sizeof(nt_pwd));
+       
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("key:"));
+       dump_data(100, (char *)key, sizeof(key));
+       
+       DEBUG(100,("lm owf password:"));
+       dump_data(100, lm_pwd, sizeof(lm_pwd));
+       
+       DEBUG(100,("nt owf password:"));
+       dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+       
+       SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd));
+       SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd));
+       
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("decrypt of lm owf password:"));
+       dump_data(100, lm_pwd, sizeof(lm_pwd));
+       
+       DEBUG(100,("decrypt of nt owf password:"));
+       dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+       
+       SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response);
+       SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response);
+       
+       /* Password info paranoia */
+       ZERO_STRUCT(lm_pwd);
+       ZERO_STRUCT(nt_pwd);
+       ZERO_STRUCT(key);
+
+       {
+               BOOL ret;
+               NTSTATUS nt_status;
+               DATA_BLOB local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response));
+               DATA_BLOB local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response));
+               DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+
+               if (lm_interactive_pwd)
+                       auth_flags |= AUTH_FLAG_LM_RESP;
+               if (nt_interactive_pwd)
+                       auth_flags |= AUTH_FLAG_NTLM_RESP; 
+
+               nt_status = make_user_info_map(user_info, 
+                                              smb_name, client_domain, 
+                                              wksta_name, 
+                                              local_lm_blob,
+                                              local_nt_blob,
+                                              plaintext_blob, 
+                                              auth_flags, True);
+               
+               ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+               data_blob_free(&local_lm_blob);
+               data_blob_free(&local_nt_blob);
+               return ret;
+       }
+}
+
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+BOOL make_user_info_for_reply(auth_usersupplied_info **user_info, 
+                             const char *smb_name, 
+                             const char *client_domain,
+                             const uint8 chal[8],
+                             DATA_BLOB plaintext_password)
+{
+
+       DATA_BLOB local_lm_blob;
+       DATA_BLOB local_nt_blob;
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       uint32 auth_flags = AUTH_FLAG_NONE;
+                       
+       /*
+        * Not encrypted - do so.
+        */
+       
+       DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted format.\n"));
+       
+       if (plaintext_password.data) {
+               unsigned char local_lm_response[24];
+               
+#ifdef DEBUG_PASSWORD
+               DEBUG(10,("Unencrypted password (len %d):\n",plaintext_password.length));
+               dump_data(100, plaintext_password.data, plaintext_password.length);
+#endif
+
+               SMBencrypt( (const uchar *)plaintext_password.data, (const uchar*)chal, local_lm_response);
+               local_lm_blob = data_blob(local_lm_response, 24);
+               
+               /* We can't do an NT hash here, as the password needs to be
+                  case insensitive */
+               local_nt_blob = data_blob(NULL, 0); 
+               
+               auth_flags = (AUTH_FLAG_PLAINTEXT | AUTH_FLAG_LM_RESP);
+       } else {
+               local_lm_blob = data_blob(NULL, 0); 
+               local_nt_blob = data_blob(NULL, 0); 
+       }
+       
+       ret = make_user_info_map(user_info, smb_name,
+                                client_domain, 
+                                sub_get_remote_machine(),
+                                local_lm_blob,
+                                local_nt_blob,
+                                plaintext_password, 
+                                auth_flags, False);
+       
+       data_blob_free(&local_lm_blob);
+       return NT_STATUS_IS_OK(ret) ? True : False;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info, 
+                                      const char *smb_name,
+                                      const char *client_domain, 
+                                      DATA_BLOB lm_resp, DATA_BLOB nt_resp)
+{
+       uint32 auth_flags = AUTH_FLAG_NONE;
+
+       DATA_BLOB no_plaintext_blob = data_blob(NULL, 0); 
+       
+       if (lm_resp.length == 24) {
+               auth_flags |= AUTH_FLAG_LM_RESP;
+       }
+       if (nt_resp.length == 0) {
+       } else if (nt_resp.length == 24) {
+               auth_flags |= AUTH_FLAG_NTLM_RESP;
+       } else {
+               auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+       }
+
+       return make_user_info_map(user_info, smb_name, 
+                                 client_domain, 
+                                 sub_get_remote_machine(), 
+                                 lm_resp, 
+                                 nt_resp, 
+                                 no_plaintext_blob, 
+                                 auth_flags, True);
+}
+
+/****************************************************************************
+ Create a guest user_info blob, for anonymous authenticaion.
+****************************************************************************/
+
+BOOL make_user_info_guest(auth_usersupplied_info **user_info) 
+{
+       DATA_BLOB lm_blob = data_blob(NULL, 0);
+       DATA_BLOB nt_blob = data_blob(NULL, 0);
+       DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+       uint32 auth_flags = AUTH_FLAG_NONE;
+       NTSTATUS nt_status;
+
+       nt_status = make_user_info(user_info, 
+                             "","", 
+                             "","", 
+                             "", 
+                             nt_blob, lm_blob,
+                             plaintext_blob, 
+                             auth_flags, True);
+                             
+       return NT_STATUS_IS_OK(nt_status) ? True : False;
+}
+
+/****************************************************************************
+ prints a NT_USER_TOKEN to debug output.
+****************************************************************************/
+
+void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token)
+{
+       fstring sid_str;
+       size_t     i;
+       
+       if (!token) {
+               DEBUGC(dbg_class, dbg_lev, ("NT user token: (NULL)\n"));
+               return;
+       }
+       
+       DEBUGC(dbg_class, dbg_lev, ("NT user token of user %s\n",
+                                   sid_to_string(sid_str, &token->user_sids[0]) ));
+       DEBUGADDC(dbg_class, dbg_lev, ("contains %i SIDs\n", token->num_sids));
+       for (i = 0; i < token->num_sids; i++)
+               DEBUGADDC(dbg_class, dbg_lev, ("SID[%3i]: %s\n", i, 
+                                              sid_to_string(sid_str, &token->user_sids[i])));
+}
+
+/****************************************************************************
+ prints a UNIX 'token' to debug output.
+****************************************************************************/
+
+void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, int n_groups, gid_t *groups)
+{
+       int     i;
+       DEBUGC(dbg_class, dbg_lev, ("UNIX token of user %ld\n", (long int)uid));
+
+       DEBUGADDC(dbg_class, dbg_lev, ("Primary group is %ld and contains %i supplementary groups\n", (long int)gid, n_groups));
+       for (i = 0; i < n_groups; i++)
+               DEBUGADDC(dbg_class, dbg_lev, ("Group[%3i]: %ld\n", i, 
+                       (long int)groups[i]));
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+static NTSTATUS create_nt_user_token(const DOM_SID *user_sid, const DOM_SID *group_sid, 
+                                    int n_groupSIDs, DOM_SID *groupSIDs, 
+                                    BOOL is_guest, NT_USER_TOKEN **token)
+{
+       NTSTATUS       nt_status = NT_STATUS_OK;
+       NT_USER_TOKEN *ptoken;
+       int i;
+       int sid_ndx;
+       
+       if ((ptoken = malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) {
+               DEBUG(0, ("create_nt_token: Out of memory allocating token\n"));
+               nt_status = NT_STATUS_NO_MEMORY;
+               return nt_status;
+       }
+
+       ZERO_STRUCTP(ptoken);
+
+       ptoken->num_sids = n_groupSIDs + 5;
+
+       if ((ptoken->user_sids = (DOM_SID *)malloc( sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+               DEBUG(0, ("create_nt_token: Out of memory allocating SIDs\n"));
+               nt_status = NT_STATUS_NO_MEMORY;
+               return nt_status;
+       }
+       
+       memset((char*)ptoken->user_sids,0,sizeof(DOM_SID) * ptoken->num_sids);
+       
+       /*
+        * Note - user SID *MUST* be first in token !
+        * se_access_check depends on this.
+        *
+        * Primary group SID is second in token. Convention.
+        */
+
+       sid_copy(&ptoken->user_sids[PRIMARY_USER_SID_INDEX], user_sid);
+       if (group_sid)
+               sid_copy(&ptoken->user_sids[PRIMARY_GROUP_SID_INDEX], group_sid);
+
+       /*
+        * Finally add the "standard" SIDs.
+        * The only difference between guest and "anonymous" (which we
+        * don't really support) is the addition of Authenticated_Users.
+        */
+
+       sid_copy(&ptoken->user_sids[2], &global_sid_World);
+       sid_copy(&ptoken->user_sids[3], &global_sid_Network);
+
+       if (is_guest)
+               sid_copy(&ptoken->user_sids[4], &global_sid_Builtin_Guests);
+       else
+               sid_copy(&ptoken->user_sids[4], &global_sid_Authenticated_Users);
+       
+       sid_ndx = 5; /* next available spot */
+
+       for (i = 0; i < n_groupSIDs; i++) {
+               size_t check_sid_idx;
+               for (check_sid_idx = 1; check_sid_idx < ptoken->num_sids; check_sid_idx++) {
+                       if (sid_equal(&ptoken->user_sids[check_sid_idx], 
+                                     &groupSIDs[i])) {
+                               break;
+                       }
+               }
+               
+               if (check_sid_idx >= ptoken->num_sids) /* Not found already */ {
+                       sid_copy(&ptoken->user_sids[sid_ndx++], &groupSIDs[i]);
+               } else {
+                       ptoken->num_sids--;
+               }
+       }
+       
+       debug_nt_user_token(DBGC_AUTH, 10, ptoken);
+       
+       *token = ptoken;
+
+       return nt_status;
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest)
+{
+       DOM_SID user_sid;
+       DOM_SID group_sid;
+       DOM_SID *group_sids;
+       NT_USER_TOKEN *token;
+       int i;
+
+       if (!uid_to_sid(&user_sid, uid)) {
+               return NULL;
+       }
+       if (!gid_to_sid(&group_sid, gid)) {
+               return NULL;
+       }
+
+       group_sids   = malloc(sizeof(DOM_SID) * ngroups);
+       if (!group_sids) {
+               DEBUG(0, ("create_nt_token: malloc() failed for DOM_SID list!\n"));
+               return NULL;
+       }
+
+       for (i = 0; i < ngroups; i++) {
+               if (!gid_to_sid(&(group_sids)[i], (groups)[i])) {
+                       DEBUG(1, ("create_nt_token: failed to convert gid %ld to a sid!\n", (long int)groups[i]));
+                       SAFE_FREE(group_sids);
+                       return NULL;
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(create_nt_user_token(&user_sid, &group_sid, 
+                                                 ngroups, group_sids, is_guest, &token))) {
+               SAFE_FREE(group_sids);
+               return NULL;
+       }
+
+       SAFE_FREE(group_sids);
+
+       return token;
+}
+
+/******************************************************************************
+ * this function returns the groups (SIDs) of the local SAM the user is in.
+ * If this samba server is a DC of the domain the user belongs to, it returns 
+ * both domain groups and local / builtin groups. If the user is in a trusted
+ * domain, or samba is a member server of a domain, then this function returns
+ * local and builtin groups the user is a member of. 
+ *
+ * currently this is a hack, as there is no sam implementation that is capable
+ * of groups.
+ ******************************************************************************/
+
+static NTSTATUS get_user_groups_from_local_sam(SAM_ACCOUNT *sampass,
+                                              int *n_groups, DOM_SID **groups, gid_t **unix_groups)
+{
+       uid_t             uid;
+       gid_t             gid;
+       int               n_unix_groups;
+       int               i;
+       struct passwd    *usr;  
+
+       *n_groups = 0;
+       *groups   = NULL;
+
+       if (!IS_SAM_UNIX_USER(sampass)) {
+               DEBUG(1, ("user %s does not have a unix identity!\n", pdb_get_username(sampass)));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       uid = pdb_get_uid(sampass);
+       gid = pdb_get_gid(sampass);
+       
+       n_unix_groups = groups_max();
+       if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) {
+               DEBUG(0, ("get_user_groups_from_local_sam: Out of memory allocating unix group list\n"));
+               passwd_free(&usr);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       if (sys_getgrouplist(pdb_get_username(sampass), gid, *unix_groups, &n_unix_groups) == -1) {
+               gid_t *groups_tmp;
+               groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups);
+               if (!groups_tmp) {
+                       SAFE_FREE(*unix_groups);
+                       passwd_free(&usr);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               *unix_groups = groups_tmp;
+
+               if (sys_getgrouplist(pdb_get_username(sampass), gid, *unix_groups, &n_unix_groups) == -1) {
+                       DEBUG(0, ("get_user_groups_from_local_sam: failed to get the unix group list\n"));
+                       SAFE_FREE(*unix_groups);
+                       passwd_free(&usr);
+                       return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+               }
+       }
+
+       debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups);
+       
+       if (n_unix_groups > 0) {
+               *groups   = malloc(sizeof(DOM_SID) * n_unix_groups);
+               if (!*groups) {
+                       DEBUG(0, ("get_user_group_from_local_sam: malloc() failed for DOM_SID list!\n"));
+                       SAFE_FREE(*unix_groups);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       *n_groups = n_unix_groups;
+
+       for (i = 0; i < *n_groups; i++) {
+               if (!gid_to_sid(&(*groups)[i], (*unix_groups)[i])) {
+                       DEBUG(1, ("get_user_groups_from_local_sam: failed to convert gid %ld to a sid!\n", (long int)(*unix_groups)[i+1]));
+                       SAFE_FREE(*groups);
+                       SAFE_FREE(*unix_groups);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+       }
+                    
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make a user_info struct
+***************************************************************************/
+
+static NTSTATUS make_server_info(auth_serversupplied_info **server_info, SAM_ACCOUNT *sampass)
+{
+       *server_info = malloc(sizeof(**server_info));
+       if (!*server_info) {
+               DEBUG(0,("make_server_info: malloc failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+       ZERO_STRUCTP(*server_info);
+
+       (*server_info)->sam_fill_level = SAM_FILL_ALL;
+       (*server_info)->sam_account    = sampass;
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info, 
+                             SAM_ACCOUNT *sampass)
+{
+       NTSTATUS nt_status = NT_STATUS_OK;
+       const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+       const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+       int       n_groupSIDs = 0;
+       DOM_SID  *groupSIDs   = NULL;
+       gid_t    *unix_groups = NULL;
+       NT_USER_TOKEN *token;
+       BOOL is_guest;
+       uint32 rid;
+
+       if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info, sampass))) {
+               return nt_status;
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status 
+                            = get_user_groups_from_local_sam(sampass, 
+               &n_groupSIDs, &groupSIDs, &unix_groups)))
+       {
+               DEBUG(4,("get_user_groups_from_local_sam failed\n"));
+               free_server_info(server_info);
+               return nt_status;
+       }
+       
+       is_guest = (sid_peek_rid(user_sid, &rid) && rid == DOMAIN_USER_RID_GUEST);
+
+       if (!NT_STATUS_IS_OK(nt_status = create_nt_user_token(user_sid, group_sid,
+                                                             n_groupSIDs, groupSIDs, is_guest, 
+                                                             &token)))
+       {
+               DEBUG(4,("create_nt_user_token failed\n"));
+               SAFE_FREE(groupSIDs);
+               SAFE_FREE(unix_groups);
+               free_server_info(server_info);
+               return nt_status;
+       }
+       
+       SAFE_FREE(groupSIDs);
+
+       (*server_info)->n_groups = n_groupSIDs;
+       (*server_info)->groups = unix_groups;
+
+       (*server_info)->ptok = token;
+       
+       DEBUG(5,("make_server_info_sam: made server info for user %s\n",
+                pdb_get_username((*server_info)->sam_account)));
+
+       return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a 'struct passwd' by conversion 
+ to a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info, const struct passwd *pwd)
+{
+       NTSTATUS nt_status;
+       SAM_ACCOUNT *sampass = NULL;
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sampass, pwd))) {             
+               return nt_status;
+       }
+       return make_server_info_sam(server_info, sampass);
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct for a guest login.
+***************************************************************************/
+
+NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info)
+{
+       NTSTATUS nt_status;
+       SAM_ACCOUNT *sampass = NULL;
+       DOM_SID guest_sid;
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+               return nt_status;
+       }
+
+       sid_copy(&guest_sid, get_global_sam_sid());
+       sid_append_rid(&guest_sid, DOMAIN_USER_RID_GUEST);
+
+       become_root();
+       if (!pdb_getsampwsid(sampass, &guest_sid)) {
+               unbecome_root();
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       unbecome_root();
+
+       nt_status = make_server_info_sam(server_info, sampass);
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+               (*server_info)->guest = True;
+       }
+
+       return nt_status;
+}
+
+/***************************************************************************
+ Make a server_info struct from the info3 returned by a domain logon 
+***************************************************************************/
+
+NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, 
+                               const char *internal_username,
+                               const char *sent_nt_username,
+                               const char *domain,
+                               auth_serversupplied_info **server_info, 
+                               NET_USER_INFO_3 *info3) 
+{
+       NTSTATUS nt_status = NT_STATUS_OK;
+
+       const char *nt_domain;
+       const char *nt_username;
+
+       SAM_ACCOUNT *sam_account = NULL;
+       DOM_SID user_sid;
+       DOM_SID group_sid;
+
+       struct passwd *passwd;
+
+       uid_t uid;
+       gid_t gid;
+
+       int n_lgroupSIDs;
+       DOM_SID *lgroupSIDs   = NULL;
+
+       gid_t *unix_groups = NULL;
+       NT_USER_TOKEN *token;
+
+       DOM_SID *all_group_SIDs;
+       size_t i;
+
+       /* 
+          Here is where we should check the list of
+          trusted domains, and verify that the SID 
+          matches.
+       */
+
+       sid_copy(&user_sid, &info3->dom_sid.sid);
+       if (!sid_append_rid(&user_sid, info3->user_rid)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       sid_copy(&group_sid, &info3->dom_sid.sid);
+       if (!sid_append_rid(&group_sid, info3->group_rid)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!(nt_username = unistr2_tdup(mem_ctx, &(info3->uni_user_name)))) {
+               /* If the server didn't give us one, just use the one we sent them */
+               nt_username = sent_nt_username;
+       }
+
+       if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3->uni_logon_dom)))) {
+               /* If the server didn't give us one, just use the one we sent them */
+               domain = domain;
+       }
+
+       if (winbind_sid_to_uid(&uid, &user_sid) 
+           && winbind_sid_to_gid(&gid, &group_sid) 
+           && ((passwd = getpwuid_alloc(uid)))) {
+               nt_status = pdb_init_sam_pw(&sam_account, passwd);
+               passwd_free(&passwd);
+       } else {
+               char *dom_user;
+               dom_user = talloc_asprintf(mem_ctx, "%s%s%s", 
+                                          nt_domain,
+                                          lp_winbind_separator(),
+                                          internal_username);
+               
+               if (!dom_user) {
+                       DEBUG(0, ("talloc_asprintf failed!\n"));
+                       return NT_STATUS_NO_MEMORY;
+               } else { 
+               
+                       if (!(passwd = Get_Pwnam(dom_user))
+                               /* Only lookup local for the local
+                                  domain, we don't want this for
+                                  trusted domains */
+                           && strequal(nt_domain, lp_workgroup())) {
+                               passwd = Get_Pwnam(internal_username);
+                       }
+                           
+                       if (!passwd) {
+                               return NT_STATUS_NO_SUCH_USER;
+                       } else {
+                               nt_status = pdb_init_sam_pw(&sam_account, passwd);
+                       }
+               }
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("make_server_info_info3: pdb_init_sam failed!\n"));
+               return nt_status;
+       }
+               
+       if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+               
+       if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_fullname(sam_account, unistr2_static(mem_ctx, &(info3->uni_full_name)), PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_logon_script(sam_account, unistr2_static(mem_ctx, &(info3->uni_logon_script)), PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_profile_path(sam_account, unistr2_static(mem_ctx, &(info3->uni_profile_path)), PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_homedir(sam_account, unistr2_static(mem_ctx, &(info3->uni_home_dir)), PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_dir_drive(sam_account, unistr2_static(mem_ctx, &(info3->uni_dir_drive)), PDB_CHANGED)) {
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info, sam_account))) {
+               DEBUG(4, ("make_server_info failed!\n"));
+               pdb_free_sam(&sam_account);
+               return nt_status;
+       }
+
+       /* Store the user group information in the server_info 
+          returned to the caller. */
+       
+       if (!NT_STATUS_IS_OK(nt_status 
+                            = get_user_groups_from_local_sam(sam_account, 
+                                                             &n_lgroupSIDs, 
+                                                             &lgroupSIDs, 
+                                                             &unix_groups)))
+       {
+               DEBUG(4,("get_user_groups_from_local_sam failed\n"));
+               return nt_status;
+       }
+
+       (*server_info)->groups = unix_groups;
+       (*server_info)->n_groups = n_lgroupSIDs;
+       
+       /* Create a 'combined' list of all SIDs we might want in the SD */
+       all_group_SIDs   = malloc(sizeof(DOM_SID) * 
+                                 (n_lgroupSIDs + info3->num_groups2 +
+                                  info3->num_other_sids));
+       if (!all_group_SIDs) {
+               DEBUG(0, ("create_nt_token_info3: malloc() failed for DOM_SID list!\n"));
+               SAFE_FREE(lgroupSIDs);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Copy the 'local' sids */
+       memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs);
+       SAFE_FREE(lgroupSIDs);
+
+       /* and create (by appending rids) the 'domain' sids */
+       for (i = 0; i < info3->num_groups2; i++) {
+               sid_copy(&all_group_SIDs[i+n_lgroupSIDs], &(info3->dom_sid.sid));
+               if (!sid_append_rid(&all_group_SIDs[i+n_lgroupSIDs], info3->gids[i].g_rid)) {
+                       nt_status = NT_STATUS_INVALID_PARAMETER;
+                       DEBUG(3,("create_nt_token_info3: could not append additional group rid 0x%x\n",
+                               info3->gids[i].g_rid));                 
+                       SAFE_FREE(lgroupSIDs);
+                       return nt_status;
+               }
+       }
+
+       /* Copy 'other' sids.  We need to do sid filtering here to
+          prevent possible elevation of privileges.  See:
+
+           http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
+         */
+
+       for (i = 0; i < info3->num_other_sids; i++) 
+               sid_copy(&all_group_SIDs[
+                                n_lgroupSIDs + info3->num_groups2 + i],
+                        &info3->other_sids[i].sid);
+       
+       /* Where are the 'global' sids... */
+
+       /* can the user be guest? if yes, where is it stored? */
+       if (!NT_STATUS_IS_OK(
+                   nt_status = create_nt_user_token(
+                           &user_sid, &group_sid,
+                           n_lgroupSIDs + info3->num_groups2 + info3->num_other_sids, 
+                           all_group_SIDs, False, &token))) {
+               DEBUG(4,("create_nt_user_token failed\n"));
+               SAFE_FREE(all_group_SIDs);
+               return nt_status;
+       }
+
+       (*server_info)->ptok = token; 
+
+       SAFE_FREE(all_group_SIDs);
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Free a user_info struct
+***************************************************************************/
+
+void free_user_info(auth_usersupplied_info **user_info)
+{
+       DEBUG(5,("attempting to free (and zero) a user_info structure\n"));
+       if (*user_info != NULL) {
+               if ((*user_info)->smb_name.str) {
+                       DEBUG(10,("structure was created for %s\n", (*user_info)->smb_name.str));
+               }
+               SAFE_FREE((*user_info)->smb_name.str);
+               SAFE_FREE((*user_info)->internal_username.str);
+               SAFE_FREE((*user_info)->client_domain.str);
+               SAFE_FREE((*user_info)->domain.str);
+               SAFE_FREE((*user_info)->wksta_name.str);
+               data_blob_free(&(*user_info)->lm_resp);
+               data_blob_free(&(*user_info)->nt_resp);
+               SAFE_FREE((*user_info)->interactive_password);
+               data_blob_clear_free(&(*user_info)->plaintext_password);
+               ZERO_STRUCT(**user_info);
+       }
+       SAFE_FREE(*user_info);
+}
+
+/***************************************************************************
+ Clear out a server_info struct that has been allocated
+***************************************************************************/
+
+void free_server_info(auth_serversupplied_info **server_info)
+{
+       DEBUG(5,("attempting to free (and zero) a server_info structure\n"));
+       if (*server_info != NULL) {
+               pdb_free_sam(&(*server_info)->sam_account);
+
+               /* call pam_end here, unless we know we are keeping it */
+               delete_nt_token( &(*server_info)->ptok );
+               SAFE_FREE((*server_info)->groups);
+               ZERO_STRUCT(**server_info);
+       }
+       SAFE_FREE(*server_info);
+}
+
+/***************************************************************************
+ Make an auth_methods struct
+***************************************************************************/
+
+BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_method) 
+{
+       if (!auth_context) {
+               smb_panic("no auth_context supplied to make_auth_methods()!\n");
+       }
+
+       if (!auth_method) {
+               smb_panic("make_auth_methods: pointer to auth_method pointer is NULL!\n");
+       }
+
+       *auth_method = talloc(auth_context->mem_ctx, sizeof(**auth_method));
+       if (!*auth_method) {
+               DEBUG(0,("make_auth_method: malloc failed!\n"));
+               return False;
+       }
+       ZERO_STRUCTP(*auth_method);
+       
+       return True;
+}
+
+/****************************************************************************
+ Delete a SID token.
+****************************************************************************/
+
+void delete_nt_token(NT_USER_TOKEN **pptoken)
+{
+    if (*pptoken) {
+           NT_USER_TOKEN *ptoken = *pptoken;
+           SAFE_FREE( ptoken->user_sids );
+           ZERO_STRUCTP(ptoken);
+    }
+    SAFE_FREE(*pptoken);
+}
+
+/****************************************************************************
+ Duplicate a SID token.
+****************************************************************************/
+
+NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
+{
+       NT_USER_TOKEN *token;
+
+       if (!ptoken)
+               return NULL;
+
+    if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
+        return NULL;
+
+    ZERO_STRUCTP(token);
+
+    if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+        SAFE_FREE(token);
+        return NULL;
+    }
+
+    token->num_sids = ptoken->num_sids;
+
+       return token;
+}
+
+/**
+ * Squash an NT_STATUS in line with security requirements.
+ * In an attempt to avoid giving the whole game away when users
+ * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and 
+ * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations 
+ * (session setups in particular).
+ *
+ * @param nt_status NTSTATUS input for squashing.
+ * @return the 'squashed' nt_status
+ **/
+
+NTSTATUS nt_status_squash(NTSTATUS nt_status)
+{
+       if NT_STATUS_IS_OK(nt_status) {
+               return nt_status;               
+       } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
+               /* Match WinXP and don't give the game away */
+               return NT_STATUS_LOGON_FAILURE;
+               
+       } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
+               /* Match WinXP and don't give the game away */
+               return NT_STATUS_LOGON_FAILURE;
+       } else {
+               return nt_status;
+       }  
+}
+
+
+
diff --git a/source4/auth/auth_winbind.c b/source4/auth/auth_winbind.c
new file mode 100644 (file)
index 0000000..5e1567d
--- /dev/null
@@ -0,0 +1,136 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind authentication mechnism
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Andrew Bartlett 2001 - 2002
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
+{
+       uint8 *info3_ndr;
+       size_t len = response->length - sizeof(response);
+       prs_struct ps;
+       if (len > 0) {
+               info3_ndr = response->extra_data;
+               if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               prs_copy_data_in(&ps, info3_ndr, len);
+               prs_set_offset(&ps,0);
+               if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+                       DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               prs_mem_free(&ps);
+
+               return NT_STATUS_OK;
+       } else {
+               DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+}
+
+/* Authenticate a user with a challenge/response */
+
+static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
+                                      void *my_private_data, 
+                                      TALLOC_CTX *mem_ctx,
+                                      const auth_usersupplied_info *user_info, 
+                                      auth_serversupplied_info **server_info)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+       NTSTATUS nt_status;
+        NET_USER_INFO_3 info3;
+
+       if (!user_info) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!auth_context) {
+               DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n", 
+                        user_info->internal_username.str));            
+               return NT_STATUS_UNSUCCESSFUL;
+       }               
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.auth_crap.flags = WINBIND_PAM_INFO3_NDR;
+
+       push_utf8_fstring(request.data.auth_crap.user, 
+                         user_info->smb_name.str);
+       push_utf8_fstring(request.data.auth_crap.domain, 
+                         user_info->domain.str);
+       push_utf8_fstring(request.data.auth_crap.workstation, 
+                         user_info->wksta_name.str);
+
+       memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
+       
+       request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length, 
+                                                sizeof(request.data.auth_crap.lm_resp));
+       request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length, 
+                                                sizeof(request.data.auth_crap.nt_resp));
+       
+       memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data, 
+              request.data.auth_crap.lm_resp_len);
+       memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, 
+              request.data.auth_crap.nt_resp_len);
+       
+       result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+       nt_status = NT_STATUS(response.data.auth.nt_status);
+
+       if (result == NSS_STATUS_SUCCESS && response.extra_data) {
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3))) { 
+                               nt_status = 
+                                       make_server_info_info3(mem_ctx, 
+                                                              user_info->internal_username.str, 
+                                                              user_info->smb_name.str, 
+                                                              user_info->domain.str, 
+                                                              server_info, 
+                                                              &info3); 
+                       }
+               }
+       } else if (NT_STATUS_IS_OK(nt_status)) {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+       }
+
+        return nt_status;
+}
+
+/* module initialisation */
+NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method) 
+{
+       if (!make_auth_methods(auth_context, auth_method))
+               return NT_STATUS_NO_MEMORY;
+
+       (*auth_method)->name = "winbind";
+       (*auth_method)->auth = check_winbind_security;
+       return NT_STATUS_OK;
+}
diff --git a/source4/auth/pampass.c b/source4/auth/pampass.c
new file mode 100644 (file)
index 0000000..045ceb7
--- /dev/null
@@ -0,0 +1,875 @@
+/* 
+   Unix SMB/CIFS implementation.
+   PAM Password checking
+   Copyright (C) Andrew Tridgell 1992-2001
+   Copyright (C) John H Terpsta 1999-2001
+   Copyright (C) Andrew Bartlett 2001
+   Copyright (C) Jeremy Allison 2001
+   
+   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.
+*/
+
+/*
+ * This module provides PAM based functions for validation of
+ * username/password pairs, account managment, session and access control.
+ * Note: SMB password checking is done in smbpass.c
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#ifdef WITH_PAM
+
+/*******************************************************************
+ * Handle PAM authentication 
+ *     - Access, Authentication, Session, Password
+ *   Note: See PAM Documentation and refer to local system PAM implementation
+ *   which determines what actions/limitations/allowances become affected.
+ *********************************************************************/
+
+#include <security/pam_appl.h>
+
+/*
+ * Structure used to communicate between the conversation function
+ * and the server_login/change password functions.
+ */
+
+struct smb_pam_userdata {
+       const char *PAM_username;
+       const char *PAM_password;
+       const char *PAM_newpassword;
+};
+
+typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
+
+/*
+ *  Macros to help make life easy
+ */
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*******************************************************************
+ PAM error handler.
+ *********************************************************************/
+
+static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const char *msg, int dbglvl)
+{
+
+       if( pam_error != PAM_SUCCESS) {
+               DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
+                               msg, pam_strerror(pamh, pam_error)));
+               
+               return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ This function is a sanity check, to make sure that we NEVER report
+ failure as sucess.
+*********************************************************************/
+
+static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
+                                           const char *msg, int dbglvl, 
+                                           NTSTATUS *nt_status)
+{
+       *nt_status = pam_to_nt_status(pam_error);
+
+       if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
+               return True;
+
+       if (NT_STATUS_IS_OK(*nt_status)) {
+               /* Complain LOUDLY */
+               DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
+error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE"));
+               *nt_status = NT_STATUS_LOGON_FAILURE;
+       }
+       return False;
+}
+
+/*
+ * PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int smb_pam_conv(int num_msg,
+                   const struct pam_message **msg,
+                   struct pam_response **resp,
+                   void *appdata_ptr)
+{
+       int replies = 0;
+       struct pam_response *reply = NULL;
+       struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+
+       *resp = NULL;
+
+       if (num_msg <= 0)
+               return PAM_CONV_ERR;
+
+       /*
+        * Apparantly HPUX has a buggy PAM that doesn't support the
+        * appdata_ptr. Fail if this is the case. JRA.
+        */
+
+       if (udp == NULL) {
+               DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+               return PAM_CONV_ERR;
+       }
+
+       reply = malloc(sizeof(struct pam_response) * num_msg);
+       if (!reply)
+               return PAM_CONV_ERR;
+
+       memset(reply, '\0', sizeof(struct pam_response) * num_msg);
+
+       for (replies = 0; replies < num_msg; replies++) {
+               switch (msg[replies]->msg_style) {
+                       case PAM_PROMPT_ECHO_ON:
+                               reply[replies].resp_retcode = PAM_SUCCESS;
+                               reply[replies].resp = COPY_STRING(udp->PAM_username);
+                               /* PAM frees resp */
+                               break;
+
+                       case PAM_PROMPT_ECHO_OFF:
+                               reply[replies].resp_retcode = PAM_SUCCESS;
+                               reply[replies].resp = COPY_STRING(udp->PAM_password);
+                               /* PAM frees resp */
+                               break;
+
+                       case PAM_TEXT_INFO:
+                               /* fall through */
+
+                       case PAM_ERROR_MSG:
+                               /* ignore it... */
+                               reply[replies].resp_retcode = PAM_SUCCESS;
+                               reply[replies].resp = NULL;
+                               break;
+
+                       default:
+                               /* Must be an error of some sort... */
+                               SAFE_FREE(reply);
+                               return PAM_CONV_ERR;
+               }
+       }
+       if (reply)
+               *resp = reply;
+       return PAM_SUCCESS;
+}
+
+/*
+ * PAM password change conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static void special_char_sub(char *buf)
+{
+       all_string_sub(buf, "\\n", "", 0);
+       all_string_sub(buf, "\\r", "", 0);
+       all_string_sub(buf, "\\s", " ", 0);
+       all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass)
+{
+       fstring_sub(buf, "%u", username);
+       all_string_sub(buf, "%o", oldpass, sizeof(fstring));
+       all_string_sub(buf, "%n", newpass, sizeof(fstring));
+}
+
+
+struct chat_struct {
+       struct chat_struct *next, *prev;
+       fstring prompt;
+       fstring reply;
+};
+
+/**************************************************************
+ Create a linked list containing chat data.
+***************************************************************/
+
+static struct chat_struct *make_pw_chat(char *p) 
+{
+       fstring prompt;
+       fstring reply;
+       struct chat_struct *list = NULL;
+       struct chat_struct *t;
+       struct chat_struct *tmp;
+
+       while (1) {
+               t = (struct chat_struct *)malloc(sizeof(*t));
+               if (!t) {
+                       DEBUG(0,("make_pw_chat: malloc failed!\n"));
+                       return NULL;
+               }
+
+               ZERO_STRUCTP(t);
+
+               DLIST_ADD_END(list, t, tmp);
+
+               if (!next_token(&p, prompt, NULL, sizeof(fstring)))
+                       break;
+
+               if (strequal(prompt,"."))
+                       fstrcpy(prompt,"*");
+
+               special_char_sub(prompt);
+               fstrcpy(t->prompt, prompt);
+               strlower(t->prompt);
+               trim_string(t->prompt, " ", " ");
+
+               if (!next_token(&p, reply, NULL, sizeof(fstring)))
+                       break;
+
+               if (strequal(reply,"."))
+                               fstrcpy(reply,"");
+
+               special_char_sub(reply);
+               fstrcpy(t->reply, reply);
+               strlower(t->reply);
+               trim_string(t->reply, " ", " ");
+
+       }
+       return list;
+}
+
+static void free_pw_chat(struct chat_struct *list)
+{
+    while (list) {
+        struct chat_struct *old_head = list;
+        DLIST_REMOVE(list, list);
+        SAFE_FREE(old_head);
+    }
+}
+
+static int smb_pam_passchange_conv(int num_msg,
+                               const struct pam_message **msg,
+                               struct pam_response **resp,
+                               void *appdata_ptr)
+{
+       int replies = 0;
+       struct pam_response *reply = NULL;
+       fstring current_prompt;
+       fstring current_reply;
+       struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+       struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat());
+       struct chat_struct *t;
+       BOOL found; 
+       *resp = NULL;
+       
+       DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg));
+
+       if (num_msg <= 0)
+               return PAM_CONV_ERR;
+
+       if (pw_chat == NULL)
+               return PAM_CONV_ERR;
+
+       /*
+        * Apparantly HPUX has a buggy PAM that doesn't support the
+        * appdata_ptr. Fail if this is the case. JRA.
+        */
+
+       if (udp == NULL) {
+               DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+               free_pw_chat(pw_chat);
+               return PAM_CONV_ERR;
+       }
+
+       reply = malloc(sizeof(struct pam_response) * num_msg);
+       if (!reply) {
+               DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
+               free_pw_chat(pw_chat);
+               return PAM_CONV_ERR;
+       }
+
+       for (replies = 0; replies < num_msg; replies++) {
+               found = False;
+               DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
+               switch (msg[replies]->msg_style) {
+               case PAM_PROMPT_ECHO_ON:
+                       DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
+                       fstrcpy(current_prompt, msg[replies]->msg);
+                       trim_string(current_prompt, " ", " ");
+                       for (t=pw_chat; t; t=t->next) {
+
+                               DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n",
+                                               t->prompt, current_prompt ));
+
+                               if (unix_wild_match(t->prompt, current_prompt) == 0) {
+                                       fstrcpy(current_reply, t->reply);
+                                       DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
+                                       pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+#ifdef DEBUG_PASSWORD
+                                       DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply));
+#endif
+                                       reply[replies].resp_retcode = PAM_SUCCESS;
+                                       reply[replies].resp = COPY_STRING(current_reply);
+                                       found = True;
+                                       break;
+                               }
+                       }
+                       /* PAM frees resp */
+                       if (!found) {
+                               DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+                               free_pw_chat(pw_chat);
+                               SAFE_FREE(reply);
+                               return PAM_CONV_ERR;
+                       }
+                       break;
+
+               case PAM_PROMPT_ECHO_OFF:
+                       DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
+                       fstrcpy(current_prompt, msg[replies]->msg);
+                       trim_string(current_prompt, " ", " ");
+                       for (t=pw_chat; t; t=t->next) {
+
+                               DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n",
+                                               t->prompt, current_prompt ));
+
+                               if (unix_wild_match(t->prompt, current_prompt) == 0) {
+                                       fstrcpy(current_reply, t->reply);
+                                       DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
+                                       pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+                                       reply[replies].resp_retcode = PAM_SUCCESS;
+                                       reply[replies].resp = COPY_STRING(current_reply);
+#ifdef DEBUG_PASSWORD
+                                       DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply));
+#endif
+                                       found = True;
+                                       break;
+                               }
+                       }
+                       /* PAM frees resp */
+                       
+                       if (!found) {
+                               DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+                               free_pw_chat(pw_chat);
+                               SAFE_FREE(reply);
+                               return PAM_CONV_ERR;
+                       }
+                       break;
+
+               case PAM_TEXT_INFO:
+                       /* fall through */
+
+               case PAM_ERROR_MSG:
+                       /* ignore it... */
+                       reply[replies].resp_retcode = PAM_SUCCESS;
+                       reply[replies].resp = NULL;
+                       break;
+                       
+               default:
+                       /* Must be an error of some sort... */
+                       free_pw_chat(pw_chat);
+                       SAFE_FREE(reply);
+                       return PAM_CONV_ERR;
+               }
+       }
+               
+       free_pw_chat(pw_chat);
+       if (reply)
+               *resp = reply;
+       return PAM_SUCCESS;
+}
+
+/***************************************************************************
+ Free up a malloced pam_conv struct.
+****************************************************************************/
+
+static void smb_free_pam_conv(struct pam_conv *pconv)
+{
+       if (pconv)
+               SAFE_FREE(pconv->appdata_ptr);
+
+       SAFE_FREE(pconv);
+}
+
+/***************************************************************************
+ Allocate a pam_conv struct.
+****************************************************************************/
+
+static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user,
+                                       const char *passwd, const char *newpass)
+{
+       struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv));
+       struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata));
+
+       if (pconv == NULL || udp == NULL) {
+               SAFE_FREE(pconv);
+               SAFE_FREE(udp);
+               return NULL;
+       }
+
+       udp->PAM_username = user;
+       udp->PAM_password = passwd;
+       udp->PAM_newpassword = newpass;
+
+       pconv->conv = smb_pam_conv_fnptr;
+       pconv->appdata_ptr = (void *)udp;
+       return pconv;
+}
+
+/* 
+ * PAM Closing out cleanup handler
+ */
+
+static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
+{
+       int pam_error;
+
+       smb_free_pam_conv(smb_pam_conv_ptr);
+       
+       if( pamh != NULL ) {
+               pam_error = pam_end(pamh, 0);
+               if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
+                       DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n"));
+                       return True;
+               }
+       }
+       DEBUG(2,("smb_pam_end: PAM: not initialised"));
+       return False;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv)
+{
+       int pam_error;
+       const char *our_rhost;
+
+       *pamh = (pam_handle_t *)NULL;
+
+       DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
+
+       pam_error = pam_start("samba", user, pconv, pamh);
+       if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
+               *pamh = (pam_handle_t *)NULL;
+               return False;
+       }
+
+       if (rhost == NULL) {
+               our_rhost = client_name();
+               if (strequal(rhost,"UNKNOWN"))
+                       our_rhost = client_addr();
+       } else {
+               our_rhost = rhost;
+       }
+
+#ifdef PAM_RHOST
+       DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost));
+       pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost);
+       if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
+               smb_pam_end(*pamh, pconv);
+               *pamh = (pam_handle_t *)NULL;
+               return False;
+       }
+#endif
+#ifdef PAM_TTY
+       DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+       pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+       if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
+               smb_pam_end(*pamh, pconv);
+               *pamh = (pam_handle_t *)NULL;
+               return False;
+       }
+#endif
+       DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
+       return True;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
+{
+       int pam_error;
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+
+       /*
+        * To enable debugging set in /etc/pam.d/samba:
+        *      auth required /lib/security/pam_pwdb.so nullok shadow audit
+        */
+       
+       DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
+       pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
+       switch( pam_error ){
+               case PAM_AUTH_ERR:
+                       DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user));
+                       break;
+               case PAM_CRED_INSUFFICIENT:
+                       DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
+                       break;
+               case PAM_AUTHINFO_UNAVAIL:
+                       DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
+                       break;
+               case PAM_USER_UNKNOWN:
+                       DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
+                       break;
+               case PAM_MAXTRIES:
+                       DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
+                       break;
+               case PAM_ABORT:
+                       DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
+                       break;
+               case PAM_SUCCESS:
+                       DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
+                       break;
+               default:
+                       DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
+                       break;
+       }
+
+       smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
+       return nt_status;
+}
+
+/* 
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+       int pam_error;
+       NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+
+       DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
+       pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
+       switch( pam_error ) {
+               case PAM_AUTHTOK_EXPIRED:
+                       DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
+                       break;
+               case PAM_ACCT_EXPIRED:
+                       DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
+                       break;
+               case PAM_AUTH_ERR:
+                       DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
+                       break;
+               case PAM_PERM_DENIED:
+                       DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
+                       break;
+               case PAM_USER_UNKNOWN:
+                       DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
+                       break;
+               case PAM_SUCCESS:
+                       DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
+                       break;
+               default:
+                       DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
+                       break;
+       }
+
+       smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
+       return nt_status;
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+       int pam_error;
+       NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
+
+       /*
+        * This will allow samba to aquire a kerberos token. And, when
+        * exporting an AFS cell, be able to /write/ to this cell.
+        */
+
+       DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
+       pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); 
+       switch( pam_error ) {
+               case PAM_CRED_UNAVAIL:
+                       DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
+                       break;
+               case PAM_CRED_EXPIRED:
+                       DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
+                       break;
+               case PAM_USER_UNKNOWN:
+                       DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
+                       break;
+               case PAM_CRED_ERR:
+                       DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
+                       break;
+               case PAM_SUCCESS:
+                       DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
+                       break;
+               default:
+                       DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
+                       break;
+       }
+
+       smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
+       return nt_status;
+}
+
+/*
+ * PAM Internal Session Handler
+ */
+static BOOL smb_internal_pam_session(pam_handle_t *pamh, const char *user, const char *tty, BOOL flag)
+{
+       int pam_error;
+
+#ifdef PAM_TTY
+       DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
+       pam_error = pam_set_item(pamh, PAM_TTY, tty);
+       if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
+               return False;
+#endif
+
+       if (flag) {
+               pam_error = pam_open_session(pamh, PAM_SILENT);
+               if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
+                       return False;
+       } else {
+               pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
+               pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
+               if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
+                       return False;
+       }
+       return (True);
+}
+
+/*
+ * Internal PAM Password Changer.
+ */
+
+static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user)
+{
+       int pam_error;
+
+       DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
+
+       pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
+
+       switch( pam_error ) {
+       case PAM_AUTHTOK_ERR:
+               DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
+               break;
+
+       /* This doesn't seem to be defined on Solaris. JRA */
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+       case PAM_AUTHTOK_RECOVER_ERR:
+               DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
+               break;
+#endif
+
+       case PAM_AUTHTOK_LOCK_BUSY:
+               DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
+               break;
+       case PAM_AUTHTOK_DISABLE_AGING:
+               DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
+               break;
+       case PAM_PERM_DENIED:
+               DEBUG(0, ("PAM: Permission denied.\n"));
+               break;
+       case PAM_TRY_AGAIN:
+               DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
+               break;
+       case PAM_USER_UNKNOWN:
+               DEBUG(0, ("PAM: User not known to PAM\n"));
+               break;
+       case PAM_SUCCESS:
+               DEBUG(4, ("PAM: Account OK for User: %s\n", user));
+               break;
+       default:
+               DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
+       }
+       if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
+               return False;
+       }
+
+       /* If this point is reached, the password has changed. */
+       return True;
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+       pam_handle_t *pamh = NULL;
+       struct pam_conv *pconv = NULL;
+
+       /* Ignore PAM if told to. */
+
+       if (!lp_obey_pam_restrictions())
+               return True;
+
+       if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+               return False;
+
+       if (!smb_pam_start(&pamh, user, rhost, pconv))
+               return False;
+
+       if (!smb_internal_pam_session(pamh, user, tty, True)) {
+               smb_pam_end(pamh, pconv);
+               return False;
+       }
+
+       return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_close_session(char *user, char *tty, char *rhost)
+{
+       pam_handle_t *pamh = NULL;
+       struct pam_conv *pconv = NULL;
+
+       /* Ignore PAM if told to. */
+
+       if (!lp_obey_pam_restrictions())
+               return True;
+
+       if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+               return False;
+
+       if (!smb_pam_start(&pamh, user, rhost, pconv))
+               return False;
+
+       if (!smb_internal_pam_session(pamh, user, tty, False)) {
+               smb_pam_end(pamh, pconv);
+               return False;
+       }
+
+       return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Account handler
+ */
+
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+       NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+       pam_handle_t *pamh = NULL;
+       struct pam_conv *pconv = NULL;
+
+       /* Ignore PAM if told to. */
+
+       if (!lp_obey_pam_restrictions())
+               return NT_STATUS_OK;
+
+       if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if (!smb_pam_start(&pamh, user, NULL, pconv))
+               return NT_STATUS_ACCOUNT_DISABLED;
+
+       if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
+               DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
+
+       smb_pam_end(pamh, pconv);
+       return nt_status;
+}
+
+/*
+ * PAM Password Validation Suite
+ */
+
+NTSTATUS smb_pam_passcheck(const char * user, const char * password)
+{
+       pam_handle_t *pamh = NULL;
+       NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+       struct pam_conv *pconv = NULL;
+
+       /*
+        * Note we can't ignore PAM here as this is the only
+        * way of doing auths on plaintext passwords when
+        * compiled --with-pam.
+        */
+
+       if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
+               return NT_STATUS_LOGON_FAILURE;
+
+       if (!smb_pam_start(&pamh, user, NULL, pconv))
+               return NT_STATUS_LOGON_FAILURE;
+
+       if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
+               DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
+               smb_pam_end(pamh, pconv);
+               return nt_status;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
+               DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
+               smb_pam_end(pamh, pconv);
+               return nt_status;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
+               DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
+               smb_pam_end(pamh, pconv);
+               return nt_status;
+       }
+
+       smb_pam_end(pamh, pconv);
+       return nt_status;
+}
+
+/*
+ * PAM Password Change Suite
+ */
+
+BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword)
+{
+       /* Appropriate quantities of root should be obtained BEFORE calling this function */
+       struct pam_conv *pconv = NULL;
+       pam_handle_t *pamh = NULL;
+
+       if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
+               return False;
+
+       if(!smb_pam_start(&pamh, user, NULL, pconv))
+               return False;
+
+       if (!smb_pam_chauthtok(pamh, user)) {
+               DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
+               smb_pam_end(pamh, pconv);
+               return False;
+       }
+
+       return smb_pam_end(pamh, pconv);
+}
+
+#else
+
+/* If PAM not used, no PAM restrictions on accounts. */
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+       return NT_STATUS_OK;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+       return True;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost)
+{
+       return True;
+}
+#endif /* WITH_PAM */
diff --git a/source4/auth/pass_check.c b/source4/auth/pass_check.c
new file mode 100644 (file)
index 0000000..88b82e3
--- /dev/null
@@ -0,0 +1,784 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password checking
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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.
+*/
+
+/* this module is for checking a username/password against a system
+   password database. The SMB encrypted password support is elsewhere */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/* these are kept here to keep the string_combinations function simple */
+static fstring this_user;
+#if !defined(WITH_PAM) 
+static fstring this_salt;
+static fstring this_crypted;
+#endif
+
+#ifdef WITH_AFS
+
+#include <afs/stds.h>
+#include <afs/kautils.h>
+
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *user, char *password)
+{
+       long password_expires = 0;
+       char *reason;
+
+       /* For versions of AFS prior to 3.3, this routine has few arguments, */
+       /* but since I can't find the old documentation... :-)               */
+       setpag();
+       if (ka_UserAuthenticateGeneral
+           (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0,       /* instance */
+            (char *)0,         /* cell */
+            password, 0,       /* lifetime, default */
+            &password_expires, /*days 'til it expires */
+            0,                 /* spare 2 */
+            &reason) == 0)
+       {
+               return (True);
+       }
+       DEBUG(1,
+             ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
+       return (False);
+}
+#endif
+
+
+#ifdef WITH_DFS
+
+#include <dce/dce_error.h>
+#include <dce/sec_login.h>
+
+/*****************************************************************
+ This new version of the DFS_AUTH code was donated by Karsten Muuss
+ <muuss@or.uni-bonn.de>. It fixes the following problems with the
+ old code :
+
+  - Server credentials may expire
+  - Client credential cache files have wrong owner
+  - purge_context() function is called with invalid argument
+
+ This new code was modified to ensure that on exit the uid/gid is
+ still root, and the original directory is restored. JRA.
+******************************************************************/
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *user, char *password)
+{
+       error_status_t err;
+       int err2;
+       int prterr;
+       signed32 expire_time, current_time;
+       boolean32 password_reset;
+       struct passwd *pw;
+       sec_passwd_rec_t passwd_rec;
+       sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+       unsigned char dce_errstr[dce_c_error_string_len];
+       gid_t egid;
+
+       if (dcelogin_atmost_once)
+               return (False);
+
+#ifdef HAVE_CRYPT
+       /*
+        * We only go for a DCE login context if the given password
+        * matches that stored in the local password file.. 
+        * Assumes local passwd file is kept in sync w/ DCE RGY!
+        */
+
+       if (strcmp((char *)crypt(password, this_salt), this_crypted))
+       {
+               return (False);
+       }
+#endif
+
+       sec_login_get_current_context(&my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+               return (False);
+       }
+
+       sec_login_certify_identity(my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+               return (False);
+       }
+
+       sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+
+               return (False);
+       }
+
+       time(&current_time);
+
+       if (expire_time < (current_time + 60))
+       {
+               struct passwd *pw;
+               sec_passwd_rec_t *key;
+
+               sec_login_get_pwent(my_dce_sec_context,
+                                   (sec_login_passwd_t *) & pw, &err);
+               if (err != error_status_ok)
+               {
+                       dce_error_inq_text(err, dce_errstr, &err2);
+                       DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+                       return (False);
+               }
+
+               sec_login_refresh_identity(my_dce_sec_context, &err);
+               if (err != error_status_ok)
+               {
+                       dce_error_inq_text(err, dce_errstr, &err2);
+                       DEBUG(0, ("DCE can't refresh identity. %s\n",
+                                 dce_errstr));
+
+                       return (False);
+               }
+
+               sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
+                                    (unsigned char *)pw->pw_name,
+                                    sec_c_key_version_none,
+                                    (void **)&key, &err);
+               if (err != error_status_ok)
+               {
+                       dce_error_inq_text(err, dce_errstr, &err2);
+                       DEBUG(0, ("DCE can't get key for %s. %s\n",
+                                 pw->pw_name, dce_errstr));
+
+                       return (False);
+               }
+
+               sec_login_valid_and_cert_ident(my_dce_sec_context, key,
+                                              &password_reset, &auth_src,
+                                              &err);
+               if (err != error_status_ok)
+               {
+                       dce_error_inq_text(err, dce_errstr, &err2);
+                       DEBUG(0,
+                             ("DCE can't validate and certify identity for %s. %s\n",
+                              pw->pw_name, dce_errstr));
+               }
+
+               sec_key_mgmt_free_key(key, &err);
+               if (err != error_status_ok)
+               {
+                       dce_error_inq_text(err, dce_errstr, &err2);
+                       DEBUG(0, ("DCE can't free key.\n", dce_errstr));
+               }
+       }
+
+       if (sec_login_setup_identity((unsigned char *)user,
+                                    sec_login_no_flags,
+                                    &my_dce_sec_context, &err) == 0)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+                         user, dce_errstr));
+               return (False);
+       }
+
+       sec_login_get_pwent(my_dce_sec_context,
+                           (sec_login_passwd_t *) & pw, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+               return (False);
+       }
+
+       sec_login_purge_context(&my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
+
+               return (False);
+       }
+
+       /*
+        * NB. I'd like to change these to call something like change_to_user()
+        * instead but currently we don't have a connection
+        * context to become the correct user. This is already
+        * fairly platform specific code however, so I think
+        * this should be ok. I have added code to go
+        * back to being root on error though. JRA.
+        */
+
+       egid = getegid();
+
+       set_effective_gid(pw->pw_gid);
+       set_effective_uid(pw->pw_uid);
+
+       if (sec_login_setup_identity((unsigned char *)user,
+                                    sec_login_no_flags,
+                                    &my_dce_sec_context, &err) == 0)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+                         user, dce_errstr));
+               goto err;
+       }
+
+       sec_login_get_pwent(my_dce_sec_context,
+                           (sec_login_passwd_t *) & pw, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+               goto err;
+       }
+
+       passwd_rec.version_number = sec_passwd_c_version_none;
+       passwd_rec.pepper = NULL;
+       passwd_rec.key.key_type = sec_passwd_plain;
+       passwd_rec.key.tagged_union.plain = (idl_char *) password;
+
+       sec_login_validate_identity(my_dce_sec_context,
+                                   &passwd_rec, &password_reset,
+                                   &auth_src, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0,
+                     ("DCE Identity Validation failed for principal %s: %s\n",
+                      user, dce_errstr));
+               goto err;
+       }
+
+       sec_login_certify_identity(my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
+               goto err;
+       }
+
+       if (auth_src != sec_login_auth_src_network)
+       {
+               DEBUG(0, ("DCE context has no network credentials.\n"));
+       }
+
+       sec_login_set_context(my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0,
+                     ("DCE login failed for principal %s, cant set context: %s\n",
+                      user, dce_errstr));
+
+               sec_login_purge_context(&my_dce_sec_context, &err);
+               goto err;
+       }
+
+       sec_login_get_pwent(my_dce_sec_context,
+                           (sec_login_passwd_t *) & pw, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+               goto err;
+       }
+
+       DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
+                 user, sys_getpid()));
+
+       DEBUG(3, ("DCE principal: %s\n"
+                 "          uid: %d\n"
+                 "          gid: %d\n",
+                 pw->pw_name, pw->pw_uid, pw->pw_gid));
+       DEBUG(3, ("         info: %s\n"
+                 "          dir: %s\n"
+                 "        shell: %s\n",
+                 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
+
+       sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+               goto err;
+       }
+
+       set_effective_uid(0);
+       set_effective_gid(0);
+
+       DEBUG(0,
+             ("DCE context expires: %s", asctime(localtime(&expire_time))));
+
+       dcelogin_atmost_once = 1;
+       return (True);
+
+      err:
+
+       /* Go back to root, JRA. */
+       set_effective_uid(0);
+       set_effective_gid(egid);
+       return (False);
+}
+
+void dfs_unlogin(void)
+{
+       error_status_t err;
+       int err2;
+       unsigned char dce_errstr[dce_c_error_string_len];
+
+       sec_login_purge_context(&my_dce_sec_context, &err);
+       if (err != error_status_ok)
+       {
+               dce_error_inq_text(err, dce_errstr, &err2);
+               DEBUG(0,
+                     ("DCE purge login context failed for server instance %d: %s\n",
+                      sys_getpid(), dce_errstr));
+       }
+}
+#endif
+
+#ifdef LINUX_BIGCRYPT
+/****************************************************************************
+an enhanced crypt for Linux to handle password longer than 8 characters
+****************************************************************************/
+static int linux_bigcrypt(char *password, char *salt1, char *crypted)
+{
+#define LINUX_PASSWORD_SEG_CHARS 8
+       char salt[3];
+       int i;
+
+       StrnCpy(salt, salt1, 2);
+       crypted += 2;
+
+       for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
+               char *p = crypt(password, salt) + 2;
+               if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
+                       return (0);
+               password += LINUX_PASSWORD_SEG_CHARS;
+               crypted += strlen(p);
+       }
+
+       return (1);
+}
+#endif
+
+#ifdef OSF1_ENH_SEC
+/****************************************************************************
+an enhanced crypt for OSF1
+****************************************************************************/
+static char *osf1_bigcrypt(char *password, char *salt1)
+{
+       static char result[AUTH_MAX_PASSWD_LENGTH] = "";
+       char *p1;
+       char *p2 = password;
+       char salt[3];
+       int i;
+       int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
+       if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
+               parts++;
+
+       StrnCpy(salt, salt1, 2);
+       StrnCpy(result, salt1, 2);
+       result[2] = '\0';
+
+       for (i = 0; i < parts; i++) {
+               p1 = crypt(p2, salt);
+               strncat(result, p1 + 2,
+                       AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
+               StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
+               p2 += AUTH_CLEARTEXT_SEG_CHARS;
+       }
+
+       return (result);
+}
+#endif
+
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
+                                int N)
+{
+       int len = strlen(s);
+       int i;
+       NTSTATUS nt_status;
+
+#ifdef PASSWORD_LENGTH
+       len = MIN(len, PASSWORD_LENGTH);
+#endif
+
+       if (N <= 0 || offset >= len)
+               return (fn(s));
+
+       for (i = offset; i < (len - (N - 1)); i++) {
+               char c = s[i];
+               if (!islower(c))
+                       continue;
+               s[i] = toupper(c);
+               if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
+                       return (nt_status);
+               }
+               s[i] = c;
+       }
+       return (NT_STATUS_WRONG_PASSWORD);
+}
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with up to N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
+{
+       int n;
+       NTSTATUS nt_status;
+       for (n = 1; n <= N; n++)
+               if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
+                       return nt_status;
+       return NT_STATUS_WRONG_PASSWORD;
+}
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *password)
+{
+#ifdef WITH_PAM
+       return smb_pam_passcheck(this_user, password);
+#else
+
+       BOOL ret;
+
+#ifdef WITH_AFS
+       if (afs_auth(this_user, password))
+               return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+       if (dfs_auth(this_user, password))
+               return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+       
+       ret = (strcmp(osf1_bigcrypt(password, this_salt),
+                     this_crypted) == 0);
+       if (!ret) {
+               DEBUG(2,
+                     ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+               ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+       }
+       if (ret) {
+               return NT_STATUS_OK;
+       } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+       
+#endif /* OSF1_ENH_SEC */
+       
+#ifdef ULTRIX_AUTH
+       ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
+       if (ret) {
+               return NT_STATUS_OK;
+        } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+       
+#endif /* ULTRIX_AUTH */
+       
+#ifdef LINUX_BIGCRYPT
+       ret = (linux_bigcrypt(password, this_salt, this_crypted));
+        if (ret) {
+               return NT_STATUS_OK;
+       } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+#endif /* LINUX_BIGCRYPT */
+       
+#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
+       
+       /*
+        * Some systems have bigcrypt in the C library but might not
+        * actually use it for the password hashes (HPUX 10.20) is
+        * a noteable example. So we try bigcrypt first, followed
+        * by crypt.
+        */
+
+       if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
+               return NT_STATUS_OK;
+       else
+               ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+       if (ret) {
+               return NT_STATUS_OK;
+       } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+       
+#ifdef HAVE_BIGCRYPT
+       ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
+        if (ret) {
+               return NT_STATUS_OK;
+       } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+#endif /* HAVE_BIGCRYPT */
+       
+#ifndef HAVE_CRYPT
+       DEBUG(1, ("Warning - no crypt available\n"));
+       return NT_STATUS_LOGON_FAILURE;
+#else /* HAVE_CRYPT */
+       ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+        if (ret) {
+               return NT_STATUS_OK;
+       } else {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+#endif /* HAVE_CRYPT */
+#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+#endif /* WITH_PAM */
+}
+
+
+
+/****************************************************************************
+CHECK if a username/password is OK
+the function pointer fn() points to a function to call when a successful
+match is found and is used to update the encrypted password file 
+return NT_STATUS_OK on correct match, appropriate error otherwise
+****************************************************************************/
+
+NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password, 
+                   int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
+{
+       pstring pass2;
+       int level = lp_passwordlevel();
+
+       NTSTATUS nt_status;
+
+#if DEBUG_PASSWORD
+       DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
+#endif
+
+       if (!password)
+               return NT_STATUS_LOGON_FAILURE;
+
+       if (((!*password) || (!pwlen)) && !lp_null_passwords())
+               return NT_STATUS_LOGON_FAILURE;
+
+#if defined(WITH_PAM) 
+
+       /*
+        * If we're using PAM we want to short-circuit all the 
+        * checks below and dive straight into the PAM code.
+        */
+
+       fstrcpy(this_user, user);
+
+       DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
+
+#else /* Not using PAM */
+
+       DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
+
+       if (!pass) {
+               DEBUG(3, ("Couldn't find user %s\n", user));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+
+       /* Copy into global for the convenience of looping code */
+       /* Also the place to keep the 'password' no matter what
+          crazy struct it started in... */
+       fstrcpy(this_crypted, pass->pw_passwd);
+       fstrcpy(this_salt, pass->pw_passwd);
+
+#ifdef HAVE_GETSPNAM
+       {
+               struct spwd *spass;
+
+               /* many shadow systems require you to be root to get
+                  the password, in most cases this should already be
+                  the case when this function is called, except
+                  perhaps for IPC password changing requests */
+
+               spass = getspnam(pass->pw_name);
+               if (spass && spass->sp_pwdp) {
+                       fstrcpy(this_crypted, spass->sp_pwdp);
+                       fstrcpy(this_salt, spass->sp_pwdp);
+               }
+       }
+#elif defined(IA_UINFO)
+       {
+               /* Need to get password with SVR4.2's ia_ functions
+                  instead of get{sp,pw}ent functions. Required by
+                  UnixWare 2.x, tested on version
+                  2.1. (tangent@cyberport.com) */
+               uinfo_t uinfo;
+               if (ia_openinfo(pass->pw_name, &uinfo) != -1)
+                       ia_get_logpwd(uinfo, &(pass->pw_passwd));
+       }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+       {
+               struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+               if (pr_pw && pr_pw->ufld.fd_encrypt)
+                       fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
+       }
+#endif
+
+#ifdef HAVE_GETPWANAM
+       {
+               struct passwd_adjunct *pwret;
+               pwret = getpwanam(s);
+               if (pwret && pwret->pwa_passwd)
+                       fstrcpy(this_crypted, pwret->pwa_passwd);
+       }
+#endif
+
+#ifdef OSF1_ENH_SEC
+       {
+               struct pr_passwd *mypasswd;
+               DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
+                         user));
+               mypasswd = getprpwnam(user);
+               if (mypasswd) {
+                       fstrcpy(this_user, mypasswd->ufld.fd_name);
+                       fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
+               } else {
+                       DEBUG(5,
+                             ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
+                              user));
+               }
+       }
+#endif
+
+#ifdef ULTRIX_AUTH
+       {
+               AUTHORIZATION *ap = getauthuid(pass->pw_uid);
+               if (ap) {
+                       fstrcpy(this_crypted, ap->a_password);
+                       endauthent();
+               }
+       }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+       /* crypt on some platforms (HPUX in particular)
+          won't work with more than 2 salt characters. */
+       this_salt[2] = 0;
+#endif
+
+       if (!*this_crypted) {
+               if (!lp_null_passwords()) {
+                       DEBUG(2, ("Disallowing %s with null password\n",
+                                 this_user));
+                       return NT_STATUS_LOGON_FAILURE;
+               }
+               if (!*password) {
+                       DEBUG(3,
+                             ("Allowing access to %s with null password\n",
+                              this_user));
+                       return NT_STATUS_OK;
+               }
+       }
+
+#endif /* defined(WITH_PAM) */
+
+       /* try it as it came to us */
+       nt_status = password_check(password);
+        if NT_STATUS_IS_OK(nt_status) {
+                if (fn) {
+                        fn(user, password);
+               }
+               return (nt_status);
+       } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+                /* No point continuing if its not the password thats to blame (ie PAM disabled). */
+                return (nt_status);
+        }
+
+       if (!run_cracker) {
+               return (nt_status);
+       }
+
+       /* if the password was given to us with mixed case then we don't
+        * need to proceed as we know it hasn't been case modified by the
+        * client */
+       if (strhasupper(password) && strhaslower(password)) {
+               return nt_status;
+       }
+
+       /* make a copy of it */
+       pstrcpy(pass2, password);
+
+       /* try all lowercase if it's currently all uppercase */
+       if (strhasupper(pass2)) {
+               strlower(pass2);
+               if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
+                       if (fn)
+                               fn(user, pass2);
+                       return (nt_status);
+               }
+       }
+
+       /* give up? */
+       if (level < 1) {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+
+       /* last chance - all combinations of up to level chars upper! */
+       strlower(pass2);
+
+        if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
+                if (fn)
+                       fn(user, pass2);
+               return nt_status;
+       }
+        
+       return NT_STATUS_WRONG_PASSWORD;
+}
diff --git a/source4/autogen.sh b/source4/autogen.sh
new file mode 100755 (executable)
index 0000000..a2228a6
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/sh
+
+# Run this script to build samba from CVS.
+
+## first try the default names
+AUTOHEADER="autoheader"
+AUTOCONF="autoconf"
+
+if which $AUTOCONF > /dev/null
+then
+    :
+else
+    echo "$0: need autoconf 2.53 or later to build samba from CVS" >&2
+    exit 1
+fi
+
+##
+## what version do we need?
+##
+if [ `$AUTOCONF --version | head -1 | cut -d.  -f 2` -lt 53 ]; then
+
+       ## maybe it's installed under a different name (e.g. RedHat 7.3)
+
+       AUTOCONF="autoconf-2.53"
+       AUTOHEADER="autoheader-2.53"
+
+fi
+
+echo "$0: running $AUTOHEADER"
+$AUTOHEADER || exit 1
+
+echo "$0: running $AUTOCONF"
+$AUTOCONF || exit 1
+
+echo "Now run ./configure and then make."
+exit 0
diff --git a/source4/build/tests/README b/source4/build/tests/README
new file mode 100644 (file)
index 0000000..cf1be8b
--- /dev/null
@@ -0,0 +1,10 @@
+This directory contains autoconf test programs that are too large to
+comfortably fit in configure.in.
+
+These programs should test one feature of the OS and exit(0) if it
+works or exit(1) if it doesn't work (do _not_ use return)
+
+The programs should be kept simple and to the point. Beautiful/fast
+code is not necessary
+
+
diff --git a/source4/build/tests/crypttest.c b/source4/build/tests/crypttest.c
new file mode 100644 (file)
index 0000000..efee2e5
--- /dev/null
@@ -0,0 +1,852 @@
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined(HAVE_CRYPT)
+
+/*
+   This bit of code was derived from the UFC-crypt package which
+   carries the following copyright
+
+   Modified for use by Samba by Andrew Tridgell, October 1994
+
+   Note that this routine is only faster on some machines. Under Linux 1.1.51 
+   libc 4.5.26 I actually found this routine to be slightly slower.
+
+   Under SunOS I found a huge speedup by using these routines 
+   (a factor of 20 or so)
+
+   Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+   that this crypt routine may sometimes get the wrong answer. Only
+   use UFC_CRYT if you really need it.
+
+*/
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c    2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#if (SIZEOF_INT == 4)
+#define long32 int
+#elif (SIZEOF_LONG == 4)
+#define long32 long
+#elif (SIZEOF_SHORT == 4)
+#define long32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define long32 int
+#endif
+#endif
+
+#ifndef long64
+#ifdef HAVE_LONGLONG
+#define long64 long long long
+#endif
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/* 
+ * Permutation done once on the 56 bit 
+ *  key derived from the original 8 byte ASCII key.
+ */
+static int pc1[56] = { 
+  57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
+  10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
+  63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
+  14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ *  56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = { 
+  1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 
+};
+
+/* 
+ * Permutation giving the key 
+ * of the i' DES round 
+ */
+static int pc2[48] = { 
+  14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
+  23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
+  41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+  44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[48] = { 
+  32,  1,  2,  3,  4,  5,  4,  5,  6,  7,  8,  9,
+   8,  9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
+  16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
+  24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32,  1
+};
+static int e_inverse[64];
+
+/* 
+ * Permutation done on the 
+ * result of sbox lookups 
+ */
+static int perm32[32] = {
+  16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+  2,   8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+/* 
+ * The sboxes
+ */
+static int sbox[8][4][16]= {
+        { { 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 },
+          {  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 },
+          {  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0 },
+          { 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 }
+        },
+
+        { { 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 },
+          {  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 },
+          {  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15 },
+          { 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 }
+        },
+
+        { { 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 },
+          { 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 },
+          { 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7 },
+          {  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 }
+        },
+
+        { {  7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 },
+          { 13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 },
+          { 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4 },
+          {  3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 }
+        },
+
+        { {  2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 },
+          { 14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 },
+          {  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14 },
+          { 11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 }
+        },
+
+        { { 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 },
+          { 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 },
+          {  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6 },
+          {  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 }
+        },
+
+        { {  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 },
+          { 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 },
+          {  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2 },
+          {  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 }
+        },
+
+        { { 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 },
+          {  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 },
+          {  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8 },
+          {  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 }
+        }
+};
+
+/* 
+ * This is the final 
+ * permutation matrix
+ */
+static int final_perm[64] = {
+  40,  8, 48, 16, 56, 24, 64, 32, 39,  7, 47, 15, 55, 23, 63, 31,
+  38,  6, 46, 14, 54, 22, 62, 30, 37,  5, 45, 13, 53, 21, 61, 29,
+  36,  4, 44, 12, 52, 20, 60, 28, 35,  3, 43, 11, 51, 19, 59, 27,
+  34,  2, 42, 10, 50, 18, 58, 26, 33,  1, 41,  9, 49, 17, 57, 25
+};
+
+/* 
+ * The 16 DES keys in BITMASK format 
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this  value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+/* 
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ *  -  second  -   is the value of this byte
+ *  -  third   -   selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/* 
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first   index is the byte number in the 8 byte ASCII key
+ *  -  second    -      -    the two 28 bits halfs of the result
+ *  -  third     -   selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first   index is the septet number in the two 28 bit intermediate values
+ *  -  second    -    -  -  septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ *      permutation giving the DES result.
+ * 
+ *      Invoked 6 bit a time on two 48 bit values
+ *      giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8]  = {
+  0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+  0x80000000, 0x40000000, 0x20000000, 0x10000000,
+  0x08000000, 0x04000000, 0x02000000, 0x01000000,
+  0x00800000, 0x00400000, 0x00200000, 0x00100000,
+  0x00080000, 0x00040000, 0x00020000, 0x00010000,
+  0x00008000, 0x00004000, 0x00002000, 0x00001000,
+  0x00000800, 0x00000400, 0x00000200, 0x00000100,
+  0x00000080, 0x00000040, 0x00000020, 0x00000010,
+  0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+  { while(cnt--)
+      *start++ = '\0';
+  }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+  { int comes_from_bit;
+    int bit, sg;
+    ufc_long j;
+    ufc_long mask1, mask2;
+
+    /*
+     * Create the do_pc1 table used
+     * to affect pc1 permutation
+     * when generating keys
+     */
+    for(bit = 0; bit < 56; bit++) {
+      comes_from_bit  = pc1[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 8 + 1];
+      mask2 = longmask[bit % 28 + 4];
+      for(j = 0; j < 128; j++) {
+       if(j & mask1) 
+         do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+      }
+    }
+
+    /*
+     * Create the do_pc2 table used
+     * to affect pc2 permutation when
+     * generating keys
+     */
+    for(bit = 0; bit < 48; bit++) {
+      comes_from_bit  = pc2[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 7 + 1];
+      mask2 = BITMASK(bit % 24);
+      for(j = 0; j < 128; j++) {
+       if(j & mask1)
+         do_pc2[comes_from_bit / 7][j] |= mask2;
+      }
+    }
+
+    /* 
+     * Now generate the table used to do combined
+     * 32 bit permutation and e expansion
+     *
+     * We use it because we have to permute 16384 32 bit
+     * longs into 48 bit in order to initialize sb.
+     *
+     * Looping 48 rounds per permutation becomes 
+     * just too slow...
+     *
+     */
+
+    clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+    for(bit = 0; bit < 48; bit++) {
+      ufc_long inner_mask1,comes_from;
+       
+      comes_from = perm32[esel[bit]-1]-1;
+      inner_mask1      = bytemask[comes_from % 8];
+       
+      for(j = 256; j--;) {
+       if(j & inner_mask1)
+         eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+      }
+    }
+    
+    /* 
+     * Create the sb tables:
+     *
+     * For each 12 bit segment of an 48 bit intermediate
+     * result, the sb table precomputes the two 4 bit
+     * values of the sbox lookups done with the two 6
+     * bit halves, shifts them to their proper place,
+     * sends them through perm32 and finally E expands
+     * them so that they are ready for the next
+     * DES round.
+     *
+     */
+    for(sg = 0; sg < 4; sg++) {
+      int j1, j2;
+      int s1, s2;
+    
+      for(j1 = 0; j1 < 64; j1++) {
+       s1 = s_lookup(2 * sg, j1);
+       for(j2 = 0; j2 < 64; j2++) {
+         ufc_long to_permute, inx;
+    
+         s2         = s_lookup(2 * sg + 1, j2);
+         to_permute = ((s1 << 4)  | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+         inx = ((j1 << 6)  | j2) << 1;
+         sb[sg][inx  ]  = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+         sb[sg][inx+1]  = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[2][(to_permute >>  8) & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[2][(to_permute >>  8) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[3][(to_permute)       & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+#ifdef _UFC_64_
+         inx = ((j1 << 6)  | j2);
+         sb[sg][inx]  = 
+           ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+            (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+         sb[sg][inx] |=
+           ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+            (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+         sb[sg][inx] |= 
+           ((long64)eperm32tab[2][(to_permute >>  8) & 0xff][0] << 32) |
+            (long64)eperm32tab[2][(to_permute >>  8) & 0xff][1];
+         sb[sg][inx] |=
+           ((long64)eperm32tab[3][(to_permute)       & 0xff][0] << 32) |
+            (long64)eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+       }
+      }
+    }  
+
+    /* 
+     * Create an inverse matrix for esel telling
+     * where to plug out bits if undoing it
+     */
+    for(bit=48; bit--;) {
+      e_inverse[esel[bit] - 1     ] = bit;
+      e_inverse[esel[bit] - 1 + 32] = bit + 48;
+    }
+
+    /* 
+     * create efp: the matrix used to
+     * undo the E expansion and effect final permutation
+     */
+    clearmem((char*)efp, sizeof efp);
+    for(bit = 0; bit < 64; bit++) {
+      int o_bit, o_long;
+      ufc_long word_value, inner_mask1, inner_mask2;
+      int comes_from_f_bit, comes_from_e_bit;
+      int comes_from_word, bit_within_word;
+
+      /* See where bit i belongs in the two 32 bit long's */
+      o_long = bit / 32; /* 0..1  */
+      o_bit  = bit % 32; /* 0..31 */
+
+      /* 
+       * And find a bit in the e permutated value setting this bit.
+       *
+       * Note: the e selection may have selected the same bit several
+       * times. By the initialization of e_inverse, we only look
+       * for one specific instance.
+       */
+      comes_from_f_bit = final_perm[bit] - 1;         /* 0..63 */
+      comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+      comes_from_word  = comes_from_e_bit / 6;        /* 0..15 */
+      bit_within_word  = comes_from_e_bit % 6;        /* 0..5  */
+
+      inner_mask1 = longmask[bit_within_word + 26];
+      inner_mask2 = longmask[o_bit];
+
+      for(word_value = 64; word_value--;) {
+       if(word_value & inner_mask1)
+         efp[comes_from_word][word_value][o_long] |= inner_mask2;
+      }
+    }
+    initialized++;
+  }
+
+/* 
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+  { ufc_long j;
+    long32 x;
+    for(j=4096; j--;) {
+      x = (k[0] ^ k[1]) & (long32)saltbits;
+      *k++ ^= x;
+      *k++ ^= x;
+    }
+  }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+  { ufc_long j;
+    long64 x;
+    for(j=4096; j--;) {
+      x = ((*k >> 32) ^ *k) & (long64)saltbits;
+      *k++ ^= (x << 32) | x;
+    }
+  }
+#endif
+
+/* 
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+  { ufc_long i, j, saltbits;
+    const unsigned char *s2 = (const unsigned char *)s1;
+
+    if(!initialized)
+      ufc_init_des();
+
+    if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+      return;
+    current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+    /* 
+     * This is the only crypt change to DES:
+     * entries are swapped in the expansion table
+     * according to the bits set in the salt.
+     */
+    saltbits = 0;
+    for(i = 0; i < 2; i++) {
+      long c=ascii_to_bin(s2[i]);
+      if(c < 0 || c > 63)
+       c = 0;
+      for(j = 0; j < 6; j++) {
+       if((c >> j) & 0x1)
+         saltbits |= BITMASK(6 * i + j);
+      }
+    }
+
+    /*
+     * Permute the sb table values
+     * to reflect the changed e
+     * selection table
+     */
+    shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits); 
+    shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+    current_saltbits = saltbits;
+  }
+
+static void ufc_mk_keytab(char *key)
+  { ufc_long v1, v2, *k1;
+    int i;
+#ifdef _UFC_32_
+    long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+    long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+    v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+    for(i = 8; i--;) {
+      v1 |= k1[*key   & 0x7f]; k1 += 128;
+      v2 |= k1[*key++ & 0x7f]; k1 += 128;
+    }
+
+    for(i = 0; i < 16; i++) {
+      k1 = &do_pc2[0][0];
+
+      v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+      v  = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v1      ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+      *k2++ = v;
+      v = 0;
+#endif
+#ifdef _UFC_64_
+      v <<= 32;
+#endif
+
+      v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+      v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v2      ) & 0x7f];
+
+      *k2++ = v;
+    }
+
+    direction = 0;
+  }
+
+/* 
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+  { ufc_long v1, v2, x;
+    static ufc_long ary[2];
+
+    x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+    x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+    v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+    v1 |= efp[15][ r2         & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+    v1 |= efp[14][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+    v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+    v1 |= efp[12][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+    v1 |= efp[11][ r1         & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+    v1 |= efp[10][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+    v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+    v1 |= efp[ 8][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+    v1 |= efp[ 7][ l2         & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+    v1 |= efp[ 6][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+    v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+    v1 |= efp[ 4][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+    v1 |= efp[ 3][ l1         & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+    v1 |= efp[ 2][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+    v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+    v1 |= efp[ 0][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+    ary[0] = v1; ary[1] = v2;
+    return ary;
+  }
+
+/* 
+ * crypt only: convert from 64 bit to 11 bit ASCII 
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+  { static char outbuf[14];
+    int i, s;
+
+    outbuf[0] = salt[0];
+    outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+    for(i = 0; i < 5; i++)
+      outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+    s  = (v2 & 0xf) << 2;
+    v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+    for(i = 5; i < 10; i++)
+      outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+    outbuf[12] = bin_to_ascii(s);
+    outbuf[13] = 0;
+
+    return outbuf;
+  }
+
+/* 
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+   
+char *ufc_crypt(const char *key,const char *salt)
+  { ufc_long *s;
+    char ktab[9];
+
+    /*
+     * Hack DES tables according to salt
+     */
+    setup_salt(salt);
+
+    /*
+     * Setup key schedule
+     */
+    clearmem(ktab, sizeof ktab);
+    strncpy(ktab, key, 8);
+    ufc_mk_keytab(ktab);
+
+    /*
+     * Go for the 25 DES encryptions
+     */
+    s = _ufc_doit((ufc_long)0, (ufc_long)0, 
+                 (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+    /*
+     * And convert back to 6 bit ASCII
+     */
+    return output_conversion(s[0], s[1], salt);
+  }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+  { int i;
+    long32 s, *k;
+
+    while(itr--) {
+      k = &_ufc_keytab[0][0];
+      for(i=8; i--; ) {
+       s = *k++ ^ r1;
+       l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);  
+        l1 ^= SBA(_ufc_sb0, s >>= 16);   l2 ^= SBA(_ufc_sb0, (s)         +4); 
+        s = *k++ ^ r2; 
+        l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+        l1 ^= SBA(_ufc_sb2, s >>= 16);   l2 ^= SBA(_ufc_sb2, (s)         +4);
+
+        s = *k++ ^ l1; 
+        r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);  
+        r1 ^= SBA(_ufc_sb0, s >>= 16);   r2 ^= SBA(_ufc_sb0, (s)         +4); 
+        s = *k++ ^ l2; 
+        r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);  
+        r1 ^= SBA(_ufc_sb2, s >>= 16);   r2 ^= SBA(_ufc_sb2, (s)         +4);
+      } 
+      s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+    }
+    return _ufc_dofinalperm(l1, l2, r1, r2);
+  }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+  { int i;
+    long64 l, r, s, *k;
+
+    l = (((long64)l1) << 32) | ((long64)l2);
+    r = (((long64)r1) << 32) | ((long64)r2);
+
+    while(itr--) {
+      k = &_ufc_keytab[0];
+      for(i=8; i--; ) {
+       s = *k++ ^ r;
+       l ^= SBA(_ufc_sb3, (s >>  0) & 0xffff);
+        l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+        l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+        l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+       s = *k++ ^ l;
+       r ^= SBA(_ufc_sb3, (s >>  0) & 0xffff);
+        r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+        r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+        r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+      } 
+      s=l; l=r; r=s;
+    }
+
+    l1 = l >> 32; l2 = l & 0xffffffff;
+    r1 = r >> 32; r2 = r & 0xffffffff;
+    return _ufc_dofinalperm(l1, l2, r1, r2);
+  }
+
+#endif
+
+#define crypt ufc_crypt
+#endif
+
+main()
+{
+       char passwd[9];
+       char salt[9];
+       char c_out1[256];
+       char c_out2[256];
+
+       char expected_out[14];
+
+       strcpy(expected_out, "12yJ.Of/NQ.Pk");
+       strcpy(passwd, "12345678");
+       strcpy(salt, "12345678");
+       
+       strcpy(c_out1, crypt(passwd, salt));
+       salt[2] = '\0';
+       strcpy(c_out2, crypt(passwd, salt));
+
+       /*
+        * If the non-trucated salt fails but the
+        * truncated salt succeeds then exit 1.
+        */
+
+       if((strcmp(c_out1, expected_out) != 0) && 
+               (strcmp(c_out2, expected_out) == 0))
+               exit(1);
+
+#ifdef HAVE_BIGCRYPT
+       /*
+        * Try the same with bigcrypt...
+        */
+
+       {
+               char big_passwd[17];
+               char big_salt[17];
+               char big_c_out1[256];
+               char big_c_out2[256];
+               char big_expected_out[27];
+
+               strcpy(big_passwd, "1234567812345678");
+               strcpy(big_salt, "1234567812345678");
+               strcpy(big_expected_out, "12yJ.Of/NQ.PklfyCuHi/rwM");
+
+               strcpy(big_c_out1, bigcrypt(big_passwd, big_salt));
+               big_salt[2] = '\0';
+               strcpy(big_c_out2, bigcrypt(big_passwd, big_salt));
+
+               /*
+                * If the non-trucated salt fails but the
+                * truncated salt succeeds then exit 1.
+                */
+
+               if((strcmp(big_c_out1, big_expected_out) != 0) && 
+                       (strcmp(big_c_out2, big_expected_out) == 0))
+                       exit(1);
+
+       }
+#endif
+
+       exit(0);
+}
diff --git a/source4/build/tests/fcntl_lock.c b/source4/build/tests/fcntl_lock.c
new file mode 100644 (file)
index 0000000..3dc12a3
--- /dev/null
@@ -0,0 +1,121 @@
+/* test whether fcntl locking works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+  return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+  return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock lock;
+       int fd, ret, status=1;
+       pid_t pid;
+       char *testdir = NULL;
+
+       testdir = getenv("TESTDIR");
+       if (testdir) chdir(testdir);
+
+       alarm(10);
+
+       if (!(pid=fork())) {
+               sleep(2);
+               fd = open(DATA, O_RDONLY);
+
+               if (fd == -1) {
+                       fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                               DATA, (int)errno);
+                       exit(1);
+               }
+
+               lock.l_type = F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = 0;
+               lock.l_len = 4;
+               lock.l_pid = getpid();
+               
+               lock.l_type = F_WRLCK;
+               
+               /* check if a lock applies */
+               ret = fcntl(fd,F_GETLK,&lock);
+
+               if ((ret == -1) ||
+                   (lock.l_type == F_UNLCK)) {
+                       fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+                       exit(1);
+               } else {
+                       exit(0);
+               }
+       }
+
+       unlink(DATA);
+       fd = open(DATA, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               exit(1);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK,&lock);
+
+       sys_waitpid(pid, &status, 0);
+
+       unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if(WIFEXITED(status)) {
+        status = WEXITSTATUS(status);
+    } else {
+        status = 1;
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       if (status) {
+               fprintf(stderr,"ERROR: lock test failed with status=%d\n", 
+                       status);
+       }
+
+       exit(status);
+}
diff --git a/source4/build/tests/fcntl_lock64.c b/source4/build/tests/fcntl_lock64.c
new file mode 100644 (file)
index 0000000..e5ecd88
--- /dev/null
@@ -0,0 +1,96 @@
+/* test whether 64 bit fcntl locking really works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+  return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+  return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl64"
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock64 lock;
+       int fd, ret, status=1;
+       pid_t pid;
+
+       if (!(pid=fork())) {
+               sleep(2);
+               fd = open64(DATA, O_RDONLY);
+
+               if (fd == -1) exit(1);
+
+               lock.l_type = F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = 0;
+               lock.l_len = 4;
+               lock.l_pid = getpid();
+               
+               lock.l_type = F_WRLCK;
+               
+               /* check if a lock applies */
+               ret = fcntl(fd,F_GETLK64,&lock);
+
+               if ((ret == -1) ||
+                   (lock.l_type == F_UNLCK)) {
+/*            printf("No lock conflict\n"); */
+                       exit(1);
+               } else {
+/*            printf("lock conflict\n"); */
+                       exit(0);
+               }
+       }
+
+       fd = open64(DATA, O_RDWR|O_CREAT|O_TRUNC, 0600);
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+#if defined(COMPILER_SUPPORTS_LL)
+       lock.l_start = 0x100000000LL;
+#else
+       lock.l_start = 0x100000000;
+#endif
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK64,&lock);
+
+       sys_waitpid(pid, &status, 0);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+       if(WIFEXITED(status)) {
+               status = WEXITSTATUS(status);
+       } else {
+               status = 1;
+       }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       unlink(DATA);
+
+       exit(status);
+}
diff --git a/source4/build/tests/fcntl_lock_thread.c b/source4/build/tests/fcntl_lock_thread.c
new file mode 100644 (file)
index 0000000..f311056
--- /dev/null
@@ -0,0 +1,122 @@
+/* test whether fcntl locking works between threads on this Linux system */
+
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+
+#include <sys/fcntl.h>
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <pthread.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+  return waitpid(pid,status,options);
+}
+
+#define DATA "conftest.fcntl"
+
+#define SEEK_SET 0
+
+static void *test_thread(void *thread_parm)
+{
+       int *status = thread_parm;
+       int fd, ret;
+       struct flock lock;
+       
+       sleep(2);
+       fd = open(DATA, O_RDWR);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               pthread_exit(thread_parm);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = 0;
+               
+       /* check if a lock applies */
+       ret = fcntl(fd,F_SETLK,&lock);
+       if ((ret != -1)) {
+               fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+       } else {
+               *status = 0;  /* SUCCESS! */
+       }
+       pthread_exit(thread_parm);
+}
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock lock;
+       int fd, ret, status=1, rc;
+       pid_t pid;
+       char *testdir = NULL;
+       pthread_t thread_id;
+       pthread_attr_t thread_attr;
+
+       testdir = getenv("TESTDIR");
+       if (testdir) chdir(testdir);
+
+       alarm(10);
+
+       pthread_attr_init(&thread_attr);
+       pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+       rc = pthread_create(&thread_id, &thread_attr, &test_thread, &status);
+       pthread_attr_destroy(&thread_attr);
+       if (rc == 0) {
+               fprintf(stderr,"created thread_id=%lu\n", 
+                       (unsigned long int)thread_id);
+       } else {
+               fprintf(stderr,"ERROR: thread create failed, rc=%d\n", rc);
+       }
+
+       unlink(DATA);
+       fd = open(DATA, O_RDWR|O_CREAT|O_RDWR, 0600);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               exit(1);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK,&lock);
+
+       sleep(4);  /* allow thread to try getting lock */
+
+       unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if(WIFEXITED(status)) {
+        status = WEXITSTATUS(status);
+    } else {
+        status = 1;
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       if (status) {
+               fprintf(stderr,"ERROR: lock test failed with status=%d\n", 
+                       status);
+       }
+
+       exit(status);
+}
diff --git a/source4/build/tests/ftruncate.c b/source4/build/tests/ftruncate.c
new file mode 100644 (file)
index 0000000..9328278
--- /dev/null
@@ -0,0 +1,27 @@
+/* test whether ftruncte() can extend a file */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.trunc"
+#define LEN 7663
+
+main()
+{
+       int *buf;
+       int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+
+       ftruncate(fd, LEN);
+
+       unlink(DATA);
+
+       if (lseek(fd, 0, SEEK_END) == LEN) {
+               exit(0);
+       }
+       exit(1);
+}
diff --git a/source4/build/tests/getgroups.c b/source4/build/tests/getgroups.c
new file mode 100644 (file)
index 0000000..343fd5a
--- /dev/null
@@ -0,0 +1,66 @@
+/* this tests whether getgroups actually returns lists of integers
+   rather than gid_t. The test only works if the user running
+   the test is in at least 1 group 
+
+   The test is designed to check for those broken OSes that define
+   getgroups() as returning an array of gid_t but actually return a
+   array of ints! Ultrix is one culprit
+  */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <grp.h>
+
+main()
+{
+       int i;
+       int *igroups;
+       char *cgroups;
+       int grp = 0;
+       int  ngroups = getgroups(0,&grp);
+
+       if (sizeof(gid_t) == sizeof(int)) {
+               fprintf(stderr,"gid_t and int are the same size\n");
+               exit(1);
+       }
+
+       if (ngroups <= 0)
+               ngroups = 32;
+
+       igroups = (int *)malloc(sizeof(int)*ngroups);
+
+       for (i=0;i<ngroups;i++)
+               igroups[i] = 0x42424242;
+
+       ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+       if (igroups[0] == 0x42424242)
+               ngroups = 0;
+
+       if (ngroups == 0) {
+               printf("WARNING: can't determine getgroups return type\n");
+               exit(1);
+       }
+       
+       cgroups = (char *)igroups;
+
+       if (ngroups == 1 && 
+           cgroups[2] == 0x42 && cgroups[3] == 0x42) {
+               fprintf(stderr,"getgroups returns gid_t\n");
+               exit(1);
+       }
+         
+       for (i=0;i<ngroups;i++) {
+               if (igroups[i] == 0x42424242) {
+                       fprintf(stderr,"getgroups returns gid_t\n");
+                       exit(1);
+               }
+       }
+
+       exit(0);
+}
diff --git a/source4/build/tests/shared_mmap.c b/source4/build/tests/shared_mmap.c
new file mode 100644 (file)
index 0000000..fcef75d
--- /dev/null
@@ -0,0 +1,68 @@
+/* this tests whether we can use a shared writeable mmap on a file -
+   as needed for the mmap varient of FAST_SHARE_MODES */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.mmap"
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+main()
+{
+       int *buf;
+       int i; 
+       int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+       int count=7;
+
+       if (fd == -1) exit(1);
+
+       for (i=0;i<10000;i++) {
+               write(fd,&i,sizeof(i));
+       }
+
+       close(fd);
+
+       if (fork() == 0) {
+               fd = open(DATA,O_RDWR);
+               if (fd == -1) exit(1);
+
+               buf = (int *)mmap(NULL, 10000*sizeof(int), 
+                                  (PROT_READ | PROT_WRITE), 
+                                  MAP_FILE | MAP_SHARED, 
+                                  fd, 0);
+
+               while (count-- && buf[9124] != 55732) sleep(1);
+
+               if (count <= 0) exit(1);
+
+               buf[1763] = 7268;
+               exit(0);
+       }
+
+       fd = open(DATA,O_RDWR);
+       if (fd == -1) exit(1);
+
+       buf = (int *)mmap(NULL, 10000*sizeof(int), 
+                          (PROT_READ | PROT_WRITE), 
+                          MAP_FILE | MAP_SHARED, 
+                          fd, 0);
+
+       if (buf == (int *)-1) exit(1);
+
+       buf[9124] = 55732;
+
+       while (count-- && buf[1763] != 7268) sleep(1);
+
+       unlink(DATA);
+               
+       if (count > 0) exit(0);
+       exit(1);
+}
diff --git a/source4/build/tests/shlib.c b/source4/build/tests/shlib.c
new file mode 100644 (file)
index 0000000..761d9fd
--- /dev/null
@@ -0,0 +1,6 @@
+/* a trivial function used to test building shared libraries */
+
+int foo(void)
+{
+       return 1;
+}
diff --git a/source4/build/tests/summary.c b/source4/build/tests/summary.c
new file mode 100644 (file)
index 0000000..d3708c2
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main()
+{
+#if !(defined(HAVE_FCNTL_LOCK) || defined(HAVE_STRUCT_FLOCK64))
+       printf("ERROR: No locking available. Running Samba would be unsafe\n");
+       exit(1);
+#endif
+
+#if !(defined(HAVE_IFACE_IFCONF) || defined(HAVE_IFACE_IFREQ) || defined(HAVE_IFACE_AIX))
+       printf("WARNING: No automated network interface determination\n");
+#endif
+
+#if !(defined(USE_SETEUID) || defined(USE_SETREUID) || defined(USE_SETRESUID) || defined(USE_SETUIDX))
+       printf("ERROR: no seteuid method available\n");
+       /* REWRITE: exit(1); */
+#endif
+
+#if !(defined(STAT_STATVFS) || defined(STAT_STATVFS64) || defined(STAT_STATFS3_OSF1) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS4) || defined(STAT_STATFS2_FSIZE) || defined(STAT_STATFS2_FS_DATA))
+       printf("ERROR: No disk free routine!\n");
+       exit(1);
+#endif
+
+#if !((defined(HAVE_RANDOM) || defined(HAVE_RAND)) && (defined(HAVE_SRANDOM) || defined(HAVE_SRAND)))
+    printf("ERROR: No random or srandom routine!\n");
+    exit(1);
+#endif
+
+       exit(0);
+}
diff --git a/source4/build/tests/trivial.c b/source4/build/tests/trivial.c
new file mode 100644 (file)
index 0000000..2723637
--- /dev/null
@@ -0,0 +1,4 @@
+main()
+{
+       exit(0);
+}
diff --git a/source4/build/tests/unixsock.c b/source4/build/tests/unixsock.c
new file mode 100644 (file)
index 0000000..f2765d6
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*- c-file-style: "linux" -*-
+ *
+ * Try creating a Unix-domain socket, opening it, and reading from it.
+ * The POSIX name for these is AF_LOCAL/PF_LOCAL.
+ *
+ * This is used by the Samba autoconf scripts to detect systems which
+ * don't have Unix-domain sockets, such as (probably) VMS, or systems
+ * on which they are broken under some conditions, such as RedHat 7.0
+ * (unpatched).  We can't build WinBind there at the moment.
+ *
+ * Coding standard says to always use exit() for this, not return, so
+ * we do.
+ *
+ * Martin Pool <mbp@samba.org>, June 2000. */
+
+/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#  include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_ERRNO_DECL
+# include <errno.h>
+#else
+extern int errno;
+#endif
+
+static int bind_socket(char const *filename)
+{
+       int sock_fd;
+       struct sockaddr_un name;
+       size_t size;
+       
+       /* Create the socket. */
+       if ((sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+               perror ("socket(PF_LOCAL, SOCK_STREAM)");
+               exit(1);
+       }
+     
+       /* Bind a name to the socket. */
+       name.sun_family = AF_LOCAL;
+       strncpy(name.sun_path, filename, sizeof (name.sun_path));
+     
+       /* The size of the address is
+          the offset of the start of the filename,
+          plus its length,
+          plus one for the terminating null byte.
+          Alternatively you can just do:
+          size = SUN_LEN (&name);
+      */
+       size = SUN_LEN(&name);
+       /* XXX: This probably won't work on unfriendly libcs */
+     
+       if (bind(sock_fd, (struct sockaddr *) &name, size) < 0) {
+               perror ("bind");
+               exit(1);
+       }
+
+       return sock_fd;
+}
+
+
+int main(void)
+{
+       int sock_fd;
+       int kid;
+       char const *filename = "conftest.unixsock.sock";
+
+       /* abolish hanging */
+       alarm(15);              /* secs */
+
+       if ((sock_fd = bind_socket(filename)) < 0)
+               exit(1);
+
+       /* the socket will be deleted when autoconf cleans up these
+           files. */
+
+       exit(0);
+}
diff --git a/source4/change-log b/source4/change-log
new file mode 100644 (file)
index 0000000..71f5012
--- /dev/null
@@ -0,0 +1,1878 @@
+SUPERCEDED Change Log for Samba
+^^^^^^^^^^
+
+Unless otherwise attributed, all changes were made by 
+Andrew.Tridgell@anu.edu.au.
+
+NOTE: THIS LOG IS IN CHRONOLOGICAL ORDER
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+1.5.00 announced to mailing list
+
+1.5.01 1/12/93
+       - configuration through makefile only
+       - fixed silly bug that made the client not accept dir's from 
+       the server
+       - tested and updated include files for ultrix, aix and solaris
+       - several things fixed thanks to pierson@ketje.enet.dec.com
+       who provided invaluable help and advice.
+
+1.5.02 1/12/93
+       - added username option to services file so connection
+       as non guest from lanmanager is possible
+       - made server abort when it can't read/write on a socket
+       - added logging to client
+
+1.5.03 2/12/93
+       - printing now works
+       - fixed a minor bug to do with hidden and system attributes
+       
+1.5.04 2/12/93
+       - added reduce_name() call to fill in security hole.
+       - cleanup up debug stuff a little
+
+1.5.05 2/12/93
+       - fixed bug in reduce_name that affects services with base paths
+       that have a soft link in them.
+
+1.5.06 3/12/93
+       - used the reserved server field in the search status to hold the 
+       directory pointer. This allows lots of directories to be open
+       at once by clients without stuffing things up.
+       - preserved all the client reserved bytes in the search status
+       in case they actually use them. Hopefully this will fix the annoying
+       empty directory dir bug. (it does)
+       
+1.5.07 3/12/93
+       - fixed silly bug that caused volume ids to appear twice
+       - fixed a wrote-too-few bug in smb_send()
+
+1.5.08 3/12/93
+       - did the SMBsearch properly. It can now handle recursive searches.
+       In order to keep the required dir info I encode the dirptr and
+       the current dir offset (from telldir) into 5 bytes by using a table
+       on the last 7 bits of the first byte. The first bit is always on
+       as this byte must by != 0
+       This is all put in the "server reserved" search field.
+
+1.5.09 5/12/93
+       - added a prototype nameserver. It's broken but can at least interpret
+       incoming packets.
+       - minor fixes to the server and client
+
+
+1.5.10 5/12/93
+       - fixed silly unsigned/signed char bug that made dosshell noot see all files
+       - added nmbd to Makefile
+
+1.5.11 6/12/93
+       - made the volume label appear as the service name, rather than "Remote"
+       - made the nmbd actually work (a little) for lanman for dos
+
+1.5.12 7/12/93
+       - fixed broadcasting in the nameserver
+       - the smbd now correctly sets the pid and uid
+       - nmbd now seems to work enough to satisfy the MS client.
+
+
+1.5.13 7/12/93
+       - fixed a silly bug that truncated filenames
+       - added -B option to nameserver to specify bcast address
+       - added -R option to nameserver to prevent name registering
+       - fixed minor read() bug. Does this fix the "cmp" bug?
+
+1.5.14 8/12/93
+       - fixed a bug in send_login() in the client. Thanks to 
+       tim.hudson@gslmail.mincom.oz.au for pointing this out.
+       - changed name_mangle() to pad to minimum of 32 bytes with spaces
+       - changed the returned buffer size in reply_connect() to not
+       count the 4 byte length field. This fixes the "can execute" bug
+       and the "comp" bug
+       - once again re-wrote the directory pointer handling code.
+       now "tree" works correctly
+
+1.5.15 9/12/93
+       - fixed name mangle bug introduced in 1.5.14 which stopped
+       nameserver from working
+
+1.5.16 9/12/93
+       - arrgh. another silly bug in name_mangle() causes the client to die.
+
+
+1.5.17 13/12/93
+       - some cosmetic cleanups to the code
+       - changed make_connection not to lower case the password (thanks
+       to bryan@alex.com)
+       - fixed accept() bug not initialising in_addrlen (thanks to
+       bogstad@cs.jhu.edu)
+       - fixed cd bug in client.c (thanks to joergs@toppoint.de)
+       - lots of fixes to the nameserver to read_socket and
+       associated routines. It should now correctly reply to the originating
+       address and use the correct broadcast. 
+       (thanks to troyer@saifr00.ateng.az.honeywell.com)
+       - SVR4 patches from mark@scot1.ucsalf.ac.uk
+       - changed the default BUFFER_SIZE to 0xFFFF
+
+1.5.18 15/12/93
+       - minor fix to reply_printqueue() to zero data buffer array.
+       - added print command to client.
+       - fixed minor bug in cmd_put() in client where a handle could
+       be closed without being previously opened.
+       - minor cleanups to the client
+       - minor solaris fixes from lonnie@itg.ti.com
+       - SYSV, shadow password  and dfree() fixes from mark@scot1.ucsalf.ac.uk
+       - fixed reply_delete() to not delete read-only files
+       - fixed infinite loop in reply_delete on "del ." 
+       Thanks to mark@scot1.ucsalf.ac.uk for pointing this out.
+       - posix mode definitions and changes from mark@scot1.ucsalf.ac.uk
+
+
+1.5.19 18/12/93
+       - another very minor fix to dfree().
+       - minor change to SVR4 makefile entry from rossw@march.co.uk
+       - changed reply_open not to open directories, this fixes the 
+       "copy .." bug pointed out by mark@scot1.ucsalf.ac.uk
+       - changed dos_mode() so it doesn't return hidden and system info
+       on directories.
+       - changed get_dir_entry() not to descend into proc/self under linux
+       control this with the DONT_DESCEND define in includes.h
+       - changed smb_setlen() to add in the SMB id. (thanks 
+       to troyer@saifr00.ateng.az.honeywell.com)
+       - fixed minor bug in reply_dir() so it won't return a ACCESS_DENIED
+       when searching a directory that is unreadable
+       - removed second stat() from get_dir_entry() (speed up)
+       - made null searches close the dirptr (fixes big filesystem problem)
+       - fixed clean_name for cd .. (from magnus@axiom.se)
+
+       
+1.5.20 28/12/93
+       - added debug statement in case of SMBcreate with volid set (leefi@microsoft.com)
+       - fixed a bug in dptr_close() so it sets the next_key to a better 
+       value, this fixes a annoying dir bug
+       - LOTS of changes from jeremy@netcom.com (Jeremy Allison). This
+       makes it possible to at least connect to a NT server with the client
+       and also fixes up much of the socket/process code. This also includes
+       stuff for compiling on a sun386
+       - got the client working with the Syntax server (a commercial
+       smb-based server). This required a few minor changes so the xmit 
+       sizes were negotiated properly.
+       - added support for OSF1, tested on a DEC3000/400 alpha.
+       - fixed the ifconf support under ultrix
+
+1.5.21 31/12/93
+       - minor cosmetic change to reduce_name()
+       - changes for HPUX from ppk@atk.tpo.fi (Pasi Kaara)
+       - minor fix to nameserver
+       - revamped configuration file format. It now takes a Windows-style
+          (.INI style) configuration file. See the file services for
+          full details of the format. New files: loadparm.c, loadparm.h,
+          params.c, params.h, testparm.c. Several changes to smb.h, local.h,
+          server.c, Makefile. The services structure is no longer visible 
+          to the rest of the system. (Karl Auer)
+        - added ability to specify a print command on a per service basis
+          and globally via the configuration file. Also allows guest account
+          to be specified in the configuration file. Made appropriate changes
+          to server.c so that these data items are obtained from the config
+          module rather than from hardcoded strings (though the hardcoded
+          strings are still the source of the defaults). (Karl Auer)
+        - renamed old-style configuration file to services.old (Karl Auer)
+        - changed README to reflect new configuration details. (Karl Auer)
+        - removed an item from the bugs wishlist (now supplied!) (Karl Auer)
+        - protected smb.h against multiple compilation. (Karl Auer)
+        - protected local.h against multiple compilation. (Karl Auer)
+       - made config stuff do dynamic allocation
+       - added "homes" capability
+       - added create_mask to each service in config
+
+1.5.22 3/1/94
+       - added "root dir" option for extra security
+       - added -n option to client (useful for OS/2)
+       - changed operation of -n to nameserver to be more useful
+       - patches from Jeremy Allison (jeremy@netcom.com)
+       fixing bug in set_message(), fixing up wait3() for SYSV,
+       making cd check the path in the client, allowing fetching to stdin
+       in client, and enhancing prompt in client to include directory.
+       - made the -D become_daemon() actually detach from the tty. This
+       may need tuning for different flavors of unix.
+       - added "dont descend" option to each service to prevent infinite 
+       loops on recursive filesystems.
+       - updated README to add "running as a daemon" and a simple
+       smb.conf file.
+       - HP/UX fixes from ppk@atk.tpo.fi
+       - made lock calls only if opened with write enabled, as pointed out
+       by gadams@ddrive.demon.co.uk
+
+1.5.23 4/1/94
+       - minor fix to logging of data in receive_smb(). It used to
+       miss the last 4 bytes of packets.
+       - added the pid,uid and mid fields to the negotiation phase of
+       the client.
+       - made client able to print from stdin
+       - added password on command line for client
+       - created a sample printcap input filter "smbprint"
+       - several fixes to client to work with OS/2
+       - added mput, mget, prompt and lcd to client
+
+1.5.24 5/1/94
+       - a resend of 1.5.23 as I managed to not include the new
+       prompt, mput and mget code.
+
+1.5.25 7/1/94
+       - change -B on nameserver so it can override the broadcast address
+       - minor changes to printing in client so OS/2 server can handle it.
+       - fixed reply_access() where OK was not being initialised
+       - added "max xmit" to global parameters.
+       - changed create to open with O_RDWR instead of O_WRONLY
+       - added printmode command to client
+       - made help return extra help on a specified command in client
+       - fixed return code in chkpath
+       - added "recurse" and "lowercase" options to client
+       - fixed some error codes from server
+       - added -I option to client
+       - fix for become_daemon() for HPUX from ppk@atk.tpo.fi
+       - added "hosts allow" and "hosts deny" to server
+       - added keepalives to server
+       - added "access" feature to testparam
+       - NetBSD patches from sreiz@aie.nl
+
+1.5.26 8/1/94
+       - changed semantics of hosts access code to do more sensible defaults
+       when either of "hosts allow" or "hosts deny" is blank
+       - added the SO_KEEPALIVE option to configurations of sockets in the
+       server
+       - made some of the SVAL fns into macros to keep fussy compilers from
+       complaining
+       - fixed several null pointer bugs in check_access(). These bugs
+       made 1.5.25 unuseable for many people.
+       - fixed null pointer reference of lp_dontdescend()
+       - reload services file after each new connection. 
+
+1.5.27 11/1/94
+       - fixed opening mode for reply_open() in server
+       - patches from Jeremy Allison (jeremy@netcom.com) to support the 
+       "core+" protocol. The patches also inclued some other features, such
+       as a new read_with_timeout() call (used by SMBreadbraw), and auto 
+       detection of the need to create a socket.
+       - changed the default KEEPALIVE value to 0, as it caused
+       problems with Lanmanager.
+       - added tar capability to client when getting files
+       - altered unix_mode() to return x bits for directories
+       - fixed bug in trim_string()
+
+1.5.28 12/1/94
+       - cleaned up the debug levels a little so debug level 1 is a practical
+       level for general use
+       - fixed a bug in add_a_service() where a freed pointer was referenced. Thanks
+       to bryan@alex.com for finding the bug.
+       - fixed bug in time structure handling in server and client. Thanks to 
+       bryan@alex.com for pointing out the bug.
+
+
+1.5.29 15/1/94
+       - fixed a silly bug in reply_open(). Thanks to
+       jeremy@netcom.com for pointing this out.
+       - fixed debug levels in client to be more sensible
+       - added raw read to client
+       - added -B option to client
+       - fixed several bugs in the client, mostly to do with the tar option
+       - added -E option to client
+
+1.5.30 16/1/94
+       - added lots of prototypes so compilers don't complain
+       - fixed minor bug in reply_rename() (thanks to ppk@atk.tpo.fi)
+       - added more support for LANMAN1.0 protocol.
+       - added SESSION SETUP AND X call
+       - added READ AND X call
+       - added TREE CONNECT AND X call
+       - added support for setbuffer for HPUX (thanks to ppk@atk.tpo.fi)
+
+1.5.31 29/1/94
+        - added support for user level security in smbclient eg:
+          smbclient "\\SERVER\SHARE" -U USERNAME%PASSWORD
+        - added error message decode as per SMB File Sharing
+          protocol extensions. (thanks to merik@blackadder.dsh.oz.au)
+        - added selection masks to smbclient that recurse down directory
+          tree. eg: mget *.* with recurse and mask *.c on will retrieve all
+          *.c files in the tree.
+       - patches for FreeBSD from kuku@acds.physik.rwth-aachen.de
+       - changed reduce_name() to trim ./ from front of strings and / from 
+         back
+       - fixed a nasty bug in trim_string().
+       - numerous small changes to lots of stuff that I didn't
+       document while I was doing them. Sorry :-(
+       - slightly updated sockspy
+
+       - The following was done by Karl Auer (Karl.Auer@anu.edu.au)
+       - added processing in configuration file of a [printers] section. Allows
+         connection to any printer specified in /etc/printcap (or the file
+         specified in the global parameter 'printcap name').
+       - added full processing of 'available' flag to configuration file. A
+         service can now be 'turned off' by specifying 'available = no'. Of
+         dubious utility.
+       - added 'printcap =' parameter to [global] section in the configuration
+         file. This allows the normal /etc/printcap to be bypassed when
+         checking printer names for dynamic printer connections via [printers].
+       - added 'printer name =' parameters to both the [global] section and
+         services sections of the configuration file. This allows the printer
+         name only to be set, without having to specify an entire print
+         command.
+       - added some synonyms: 'writable' and 'write ok' have the opposite sense
+         to 'read only'. 'public' may be used instead of 'guest ok'. 'printer'
+         may be used instead of 'printer name'. 'printable' is the same as 
+         'print ok'. 'root' may be used instead of 'root dir' or 'root 
+         directory'.
+       - added lots more detail to the sample configuration file to take
+         account of the above.
+       - many minor fixes to internal documentation in the configuration
+         sources.
+       - also - Man pages!
+
+
+1.5.32 3/2/94
+       - addition of smbd, smbclient and testparm man pages 
+         from Karl Auer
+       - zombie process fix from lendecke@namu01.gwdg.de
+       - added capability to nmbd to serve names available 
+       via gethostbyname().
+
+1.5.33 3/2/94
+       - fixed up getting of netmask so it works on more unix variants
+       - added -N option to nmbd
+       - changed GMT diff calculation. need to check it's right for
+       lots of OSes
+       - fixed a bug in read_and_X() and chain_reply() chaining now
+       seems to work correctly
+
+1.5.34 4/2/94
+       - fixed bug in client that meant it couldn't get/put files from WfWg
+       - fixed a bug in the server that caused lpr to return -1 under sunos
+       - fixed a few errors in the hosts allow section of the
+       smb.conf.5 manual page and added examples
+
+1.5.35  6/2/1994
+       - minor bugfix in reduce_name().
+       - changed width of "size" in client during a dir
+       - patches for NEXT (among other things) from lendecke@namu01.gwdg.de
+       - added -a switch to server, and made default action to append
+       to log file
+       - added deadtime options to [global] section for timing out
+       dead connections to the smbd.
+       - HPUX changes from Pasi.Kaara@atk.tpo.fi
+       - made use of unsigned char more consistent
+       - changed the way of getting the default username and host in the
+        client
+       - made LANMAN1 default to on in the client, off in server.
+       Use -DLANMAN1=1 to make it on in both.
+       - lots of casts and cleanups for various operating systems
+       - changes to the Makefile from Karl to auto-instal the man pages
+       - added a short history of the project to the distribution
+
+1.5.36 15/2/94
+       - fixed minor bug in Debug() (thanks to Pasi.Kaara@atk.tpo.fi)
+       - fixed bug in server.c so -a wasn't accepted.
+       - minor fixes to the client
+       - added hosts file to name server (-H option)
+       - added -G option for groups to nameserver
+       - cleanups and additions from Jeremy Allison, taking us
+       closer to LANMAN1.0. In particular the locking code was cleaned up
+       considerably.
+
+1.5.37 16/2/94
+       - fixed bug introduced in 1.5.36 which disabled SMBcreate
+
+1.5.38 18/2/94
+       - fixed get_broadcast() for ultrix (fix from iversen@dsfys1.fi.uib.no)
+       - added automatic group registration
+       - fixed bug in registration code
+       - made nmbd work better with WfWg, and probably others
+       - updated the man pages to include the new nmbd options.
+       - minor updates to the README
+       - fixed double log_out() in send_packet().
+       - fixed bug in smbclient so that "dir" didn't work correctly
+       with pathworks
+       - possibly fixed bug in server that led to "abort retry ignore" from
+       pathworks client when doing a "dir".
+       - changed behaviour of smbclient login slightly, to try a
+       blank password in SMBtcon if the right password fails, and a
+       session setup has succeeded. Some clients seem to use a blank
+       one if a session setup has succeeded.
+       - ISC patches from imb@asstdc.scgt.oz.au
+       - the client now tries to do name registration using a unicast.
+       Let me know if this helps anyone.
+       - tried to add a "contributed" line to each OS in the Makefile.
+
+1.5.39 18/2/94
+       - fixed silly C code that only worked with some compilers
+       - fixed another silly bug in nameserv.c that caused it to seg fault
+
+1.5.40 21/2/94
+       - removed the from (IP) message so people don't worry about 0.0.0.0,
+       it's redundant anyway.
+       - changed the client so the crypt key isn't printed
+       - changed the structure of switch_message() to use a list of functions.
+       This improves the debug info.
+       - made SMBopen ignore supplied attribute as per X/Open spec
+       - made SMBopen fail if file doesn't exist in all cases. Let me know
+       if this breaks something. It is implied in the X/Open spec. This
+       fixes the pkzip bug.
+       - added dptr_demote() to replace dptr_close() to try and fix 
+       pathworks dir bug. This has the potential disadvantage of
+       leaving lots of open file descriptors.
+       - changed mask_match to disallow two .s in a name
+
+1.5.41 2/3/94
+       - added "dfree command" global option to smbd to support an
+       external "disk free" executable (typically a script). This gets 
+       around the problem of getting disk free info reliably on lots
+       of systems.
+       - added ffirst and fclose to client
+       - simple SYSVR4 patch from mark@scot1.ucsalf.ac.uk
+       - added better uid/gid reporting for debugging purposes
+       - several changes to the logon procedure for the client, so hopefully
+       it will connect correctly to a wider range of servers.
+       - server should no longer crash if it can't open the debug
+       file (thanks to MGK@newton.npl.co.uk)
+       - added the THANKS file.
+
+1.5.42 6/3/94
+       - lots of changes from Jeremy Allison, implementing more of
+       the LANMAN1.0 protocol, and fixing a few bugs.
+       - fixed delete bug, so hopefully wildcards are correct now
+       - pcap changes from Martin Kiff so non-aliased printers in
+       /etc/printcap are recognised
+       - wrote announce file ready for 1.6
+       - re-wrote browse code in client (still doesn't work)
+       - updates to man-pages from Karl Auer
+       - made raw packet dumps mode 0600 and only if -dA is given
+       - changed socket code to use utility functions in util.c
+
+1.6.00 17/3/94
+       - made server always return to original directory (rather than /)
+       - fixed bug in params.c that caused a seg fault if no parms in a 
+         section
+       - minor clean ups for clean compile under solaris
+       - solaris fix for running from inetd from Karl Auer
+       - fixes for dfree() under solaris
+       - minor changes that might help BSDI
+       - changes to the Makefile, manual-pages and sample config file from 
+       Karl Auer
+       - fixed dfree for Ultrix
+
+1.6.01 19/3/94
+       - fixed setatr bug that allowed directories to be unusable 
+
+1.6.02 27/3/94
+       - added timestamps to connection message in log
+       - added idle timeout of 10 minutes to name server
+       - made HAVE_SYSCONF==0 the default in includes.h
+       - made the client not register by default
+       - ISC patches from imb@asstdc.scgt.oz.au
+       - GetWd() cache code from Martin Kiff
+       - rewrote the locking code in terms of fcntl() calls.
+       - fixed "can't delete directory" bug
+       - added code to close old dirptrs for duplicate searches
+       - removed exchange_uids() and the access() call and replaced them.
+
+1.6.03 28/3/94
+       - tried to clean up the time handling a little (local vs gmt time)
+       - added debug level global to server config
+       - added protocol level global to server config
+       - added SMBecho command to server
+       - included Karl Auers SMBGuide in the distribution.
+
+1.6.04 31/3/94
+       - fixed time zeroing bug in smb_close and smb_setatr
+       - re-wrote the username/password handling to be more flexible
+       - added "guest only" service setting to smb.conf
+       - updated man pages for new username/password handling
+       - fixed parse bug in reply_tconX
+       - improved error return code from tcon
+       - several changes to fix printing from WfWg
+
+1.6.05 2/4/94
+       - changed the name of the whole package to Samba
+       - removed SMBexit call from client to stop exiting error message
+       - added interpret_addr() call to replace inet_addr() so
+       a hostname can be used whenever a IP is required
+
+1.6.06 8/4/94
+       - added random tid choice to reduce problem of clients not
+       detecting a server disconnection.
+       - made client not report spurious time from CORE or COREPLUS server.
+       - minor HPUX fix from gunjkoa@dep.sa.gov.au
+       - turned off GETWD_CACHE until we track down a minor bug in it
+
+1.6.07: 10/4/94
+       - added helpful error messages to connection failure in client.
+       - fixed problem with mput in client
+       - changed server to allow guest-only sesssetup messages with any
+       password. Control this with GUEST_SESSION_SETUP in local.h.
+       - minor change to session setup handling in make_connection()
+       - added check for right number of \s in the client.
+       - made the server not exit on last close if the deadtime is != 0
+       - added malloc and realloc wrappers. enable them with -DWRAP_MALLOC=1
+       - if smbd is started with a debug level of 10 or greater it creates
+       a log file ending in the process number
+
+1.6.08: 18/4/94
+       - updated the THANKS file
+       - changes from marcel@fanout.et.tudelft.nl (Marcel Mol) for AMPM
+       times and error report on connect().
+       - made the get_myname() routine discard any part after the first '.'
+       - added a wrapper for free from Martin Kiff
+       - added simpleminded code to handle trapdoor uid systems (untested)
+       - added Martin Kiffs "paranoid" getwd code.
+       - added default MAXPATHLEN if undefined of 1024
+       - made get_broadcast() continue to get netmask if it can't get
+       broadcast (suggestion from Hannu Martikk)
+       - replaced fchmod() calls with chmod() to satisfy some unixes
+
+
+
+1.6.09: 4/5/94
+       - changed perror() calls to strerror() in server.c
+       - fix for dfree on OSF1 from 
+       Maximilian Errath (errath@balu.kfunigraz.ac.at)
+       - fixed server time reporting for protocol >= LANMAN1
+       - fixed TimeDiff() for machines without TIMEZONE or TIMELOCAL
+       (thanks to Vesa S{rkel{ <vesku@rankki.kcl.fi>)
+       - added SYSV defs to AIX and HPUX to fix "memory" problem
+       (actually a signal problem).
+       - added version to client banner in log file
+       - Ultrix patches from Vesa S{rkel{ <vesku@rankki.kcl.fi>
+       - added ! command to client for executing shell commands
+       - fixed ERRnofids bug in server
+       - fixed name_equal bug 
+       (thanks to cjkiick@flinx.b11.ingr.com (Chris Kiick))
+       - wrapped gethostbyname() with Get_Hostbyname() to prevent
+       case sensitive problems on name lookups
+       - limit printer tmp filename to 14 chars 
+       (from Paul Thomas Mahoney <ptm@xact1.xact.com>)
+       - added ability to understand 64 bit file times 
+       (thanks to davidb@ndl.co.uk (David Boreham))
+       - added Gwt_Pwnam() wrapper to cover server case-sensitivity
+       problems (suggestion from J.M.OConnor@massey.ac.nz (John O'Connor))
+       - changed the setuid() calls to try and work for more systems
+       without breaking the ones it currently works for
+       - added version number to usage() 
+       (suggestion from peter@prospect.anprod.csiro.au)
+       - added "security=" option for share or user level security
+       - allowed multiple usernames in "user=" field
+       - changed display method for recursive dorectory listings
+       - switched client to use long filenames where supported
+       - added speed reporting to client transfers
+       - several NT fixes to server from jra@vantive.com (Jeremy Allison)
+       - ISC fixes from ptm@xact.demon.co.uk (Paul Mahoney)
+       - fix to README from grif@cs.ucr.edu (Michael A. Griffith)
+       - default netmask and broadcast from  Ian A Young <iay@threel.co.uk>   
+       - changed default of is_locked() on fcntl() error.
+       - fixed bug in read_with_timeout() that could cause a runaway 
+       smbd process.
+       - fixed findnext bug for long filenames in client
+       - changed default protocol level to LANMAN1
+       - change default reported security level to SHARE.
+       - changed password_ok() so that if pwdauth() fails it tries
+       with standard crypt.
+       - added "translate" command to the client to do CR/LF translation
+       for printing, and add a form feed at the end. 
+       (thanks to mh2620@sarek.sbc.com (Mark A. Horstman ) )
+       - added "locking=yes/no" toggle for each service
+       - SCO unix patches from Heinz Mauelshagen (mauelsha@ez.da.telekom.de)
+
+1.6.10: 7/5/94
+       - fixed important bug in readbraw/writebraw
+       - added -A option to client
+       - fixed delete bug on long filenames (untested). Thanks to
+       Stefan Wessels <SWESSELS@dos-lan.cs.up.ac.za>
+       - neatened up the byte swapping code
+
+1.6.11: 3/6/94
+       - fixed bug in client in receive_trans2_response() that caused
+       some strange behaviour with LANMAN2.
+       - fixed some offset/alignment problems with lockingX (thanks to 
+       Jeremy Allison)
+       - allow locking on O_RDONLY files. Thanks to Martin N Dey <mnd@netmgrs.co.uk>
+       - fixed del bug in client thanks to paulzn@olivetti.nl (Paul van der Zwan)
+       - fixed multiple user= bug thanks to MDGrosen@spectron.COM (Mark Grosen)
+       - added translate ability for all files. Thanks to mh2620@sarek.sbc.com (Mark A. Horstman )
+       - mask out negative lock offsets. Thanks to bgm@atml.co.uk (Barry G Merrick)
+       - more attempts to get the structure alignment better for some machines
+       - cleaned up the machine dependencies a little
+       - ISC fixes from Paul Thomas Mahoney <ptm@xact1.xact.com>
+       - enabled printing with a SMBclose and SMBwrite for NT 
+       thanks to jkf@frisky.Franz.COM (Sean Foderaro)
+       - SGI changes from Michael Chua <lpc@solomon.technet.sg>
+       - CLIX patches from cjkiick@ingr.com
+       - NEXT2 and NEXT3_0 patches from Brad Greer (brad@cac.washington.edu)
+       - BSDI changes from tomh@metrics.com (Tom Haapanen)
+       - SCO patches from John Owens (john@micros.com)
+       - fix psz bug in pcap.c (thanks to Karl Auer)
+       - added widelinks option (global and per service). Suggestion from
+       Karl Auer. Defaults to True.
+       - made locking able to be global or local (default is give by global)
+       - added check_name() to dir listings
+       - added "packet size" option to globals. default to 32767. This
+       "fixes" a WfWg bug (thanks to Karl Auer)
+       - fixes for getattrE and setattrE and minor fix in util.c from Jeremy Allison.
+       - Karl updated the man pages o be current
+       - disabled writebraw and readbraw until a possible bug can be investigated further
+
+1.7.00: 14/7/94
+       - added session_users list, to overcome problem of missing usernames in SMBTconX.
+       - added term support to the client
+       - added "default service"
+       - fork for print so user is not root
+       - added name mangling to 8.3 (rudimentary)
+       - fixed bug in in_group()
+       - changed to use gid in place of egid
+       - fixed client connection to OS/2 (1.3 + lanman2.2) and long filenames
+       - added patches from mcochran@wellfeet.com (Marc Cochran)
+         these implement scope ids and fix some udp bugs. It means
+         the -L option to nmbd now works.
+       - made nmbd respond to incoming port rather than only 137
+       - made wide links refuse .. components
+       - fixed "dir foo." bug to stop it showing "foo.???"
+       - improved name mangling (added stack)
+       - added valid FNUM check to most calls
+       - fixed important do_put bug in the client
+       - added magic scripts to the server
+       - re-enabled getwd_cache code
+       - added optional agressive password checking
+       - removed dptr_closepath from SMBsearch to try and stop "dos for loop"
+         bug
+       - DGUX patches from ross@augie.insci.com (ross andrus)
+       - updated the README and THANKS file.
+       - added node status request to -L option of nmbd
+       - stripped trailing spaces in mask_match() (thanks to mike hench hench@cae.uwm.edu)
+       - added COREPLUS style print queue reporting and "lpq command"
+         in globals.
+       - cleaned up date handling and fixed byte order dependancy on dates
+       in SMBgetattrE.
+       - cleaned up the password handling and added "password level" with
+       the possability of checking all case combinations up to N upper
+       case chars.
+       - changed to use recvfrom only on udp ports (fixed read raw!)
+       - added TCB password support for SCO (thanks to lance@fox.com)
+       - updated README, THANKS and announce files.
+       - fixed timezone reporting to be signed (thanks to noses@oink.rhein.de)
+       - disabled max packet as it could cause problems with WfWg (no longer 
+       needed now readraw is "fixed")
+       - changed from creat() to open() in mktemp and mknew.
+       - changed umask handling
+       - sped up nmbd by making it cache names
+       - changed idle timeout on nmbd to 2 mins
+       - Netbsd changes from noses@oink.rhein.de
+       - released alpha2
+       - added name timeout to nmbd
+       - changed bind port retry in nmbd
+       - added Limitations sections to README
+       - fixed two . in is_83()
+       - fixed compilations warnings in util.c (thanks to njw@cpsg.com.au)
+       - made [homes] honour multiple user list 
+       - fixed mask match bug introduced in alpha1
+       - added "mangled stack" option for stack size
+       - added mangled stack promotion
+       - released alpha3
+       - netbsd-1.0 fix for statfs().
+       - added null_string to util.c to reduce memory usage
+       - changed the way directory structures are put together
+       - added smbrun for system() requests
+       - changed maxmux to 0 in hope of avoiding mpx commands problem
+       - fixed zero response length for session keepalives
+       - removed called name from session users list
+       - added F_RDLCK support to try and handle locks on readonly files
+       - made directory creation honour the lowercase flag in client (thanks 
+       to charlie@edina.demon.co.uk)
+       - made checksum for mangling independant of extension if extension is 
+       lowercase
+       - added ability to rename files with different extension, preserving 
+       root name
+       - released alpha4
+       - better command line error checking in client
+       - changed all debug statements to new format
+       - fixed delete error code reporting
+       - released alpha5
+       - added mangled name support to wildcard delete in server
+       - fixed mask bug in SMBsearch
+       - cleaned up prototypes
+       - released alpha6
+       - fixed important bug in session_setup which made WfWg freeze 
+       (maxmux was 0 - this bug was introduced in alpha4)
+       - released alpha7
+       - two printing bug fixes thanks to bgm@atml.co.uk (Barry G Merrick)
+       - uid fix to smbrun (thanks to larry@witch.mitra.com)
+       - man page updates from Karl Auer
+       - FAQ file from Karl Auer
+       - released alpha8
+       - fixed read-only flag in dos_mode() for non writeable services
+       - fixed error code reporting in open() and openX().
+       - minor secureware fix from (thanks to lance@fox.com)
+       - released alpha9
+       - casting cleanups for memcpy().
+       - cleaned up error code names to be more consistant
+
+1.7.01: 17/7/94
+       - minor man page fix from baeder@cadence.com (Scott Baeder)
+       - changed usage() error message in client
+       - made nmbd not exit if can't register own name
+       - made nmbd only register if running as a daemon
+       - fixed stdout problem in smbrun by closing stdin/stdout/stderr
+       - minor fix to lmhosts parsing
+
+
+1.7.02: 20/7/94
+       - made nmbd not call get_broadcast if both -B and -N are used (thanks 
+       to Chris Woodrow <Chris.Woodrow@actrix.gen.nz>)
+       - disabled GETWD_CACHE again
+       - fixed INCLUDES list in Makefile to add version.h (thanks to 
+       jimw@PE-Nelson.COM (Jim Watt))
+       - made checkname do a become user if it hasn't already done so.
+       - added consistancy check to become_user().
+       - removed mask extension expansion from SMBsearch
+       - small change to chkpth
+       - fix to snum select for lpq status (thanks to Rafi Sadowsky 
+       rafi@tavor.openu.ac.il)
+       - changed daemon to is_daemon for NetBSD (thanks to noses@oink.rhein.de)
+       - removed STAFS3 stuff for NETBSD_1_0
+
+
+1.7.03: 29/7/94
+       - updated docs for new distribution structure
+       - made getatr return 0 size for directories (thanks to Bernd Esser 
+       esser@pib1.physik.uni-bonn.de)
+       - added valid dos filename checks from Stefan Wessels 
+       (swessels@cs.up.ac.za)
+       - added trimming of . in hostnames to -S mode of nmbd
+       - removed become_user() and OPEN_CNUM calls. Now make them 
+       in switch_message instead which simplifies a lot of code.
+       - added GETFNUM macro to make chain_fnum more consistant and
+       reliable.
+       - added flags to protocol structures to simplify CAN_WRITE and AS_USER
+       checking
+       - added getwd cache boolean option to globals
+       - added fclose() to lpq status routine thanks to 
+       dgb900@durras.anu.edu.au (David Baldwin)
+       - added "only user" option, to limit connection usernames to those 
+       in the user= line
+       - changed to badpath from badfile in chkpath despite specs (following 
+       what WfWg does). This fixes "file not found" error in copy command.
+       Thanks to rwa@aber.ac.uk for pointing out the bug
+       - changes for apollo from Stephen C. Steel <steve@qv3donald.LeidenUniv.nl>
+       - more changes for Apollo from jmi@csd.cri.dk (John Mills)
+       - released alpha release
+       - added FTRUNCATE_CAN_EXTEND=0 as default to fix problem with word6.
+       Possibly not needed on many OSes? Thanks to Charlie Hussey 
+       charlie@edina.demon.co.uk
+       - started adding max connections code
+       - much improved group handling contributed by 
+       Ian Heath (ih@ecs.soton.ac.uk)
+
+1.7.04: 29/7/94
+       - fixed one line bug in SMBopenX that got error code wrong.
+
+1.7.05: 2/8/94
+       - added UNIXERROR() macro to get error code from unix errno.
+       - fixed lpq status for MSTCPB3
+       - added @ option for user= line to lookup groups in group file
+       - added become_user optimisation and process timeout (thanks to
+       Jeanette Pauline Middelink (middelin@calvin.iaf.nl)
+       - added malloc optimisation in readbraw
+       - released alpha
+       - patches for OSF1 enhanced security from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+       - made level 2 a more useful debug level (less guff)
+       - added "max connections" and "lock dir" options to allow
+       limiting of the number of connections to a service at one time.
+       - released alpha2
+       - updated man pages
+       - released alpha3
+       - added read prediction code for better read performance
+       - released alpha4
+       - minor tuning to receive_smb()
+       - changed the order of mangled stack checking
+       - bug fix in read_predict().
+       - released alpha5
+       - minor search optimisation
+       - fixed keep alive bug in writebraw and in readbraw in the client
+       - released alpha6
+       - disabled writeraw by default pending a bug fix
+       - added profiling code (off by default)
+       - minor delete tuning
+       
+
+1.7.06: 4/8/94
+       - OSF1 crypt fix thanks to Udo Linauer <ul@eacpc4.tuwien.ac.at>
+       - ifdef'd EDQUOT in case you don't have it (thanks to Paul Blackman <ictinus@Lake.canberra.edu.au>)
+       - tidied up UNIXERROR stuff to work on more systems.
+       - made Makefile more sophisticated and added "make revert"
+
+1.7.07: 4/8/94
+       - fixed one line fatal bug in receive_smb. Thanks to bruce@pixar.com
+
+1.7.08: 2/9/94
+       - initgroups call for SCO from lance@fox.com
+       - code cleanups from cap@isac.hces.com (Simon Casady)
+       - use full pathname in print command construction
+       - ISC includes fix from Martin Tomes <mt00@ecl.etherm.co.uk>
+       - added GID_TYPE define to cope with ultrix. Thanks to
+       brad@cac.washington.edu
+       - added umask call to main in server
+       - fixed several minor problems with the max connections
+       code. Thanks to lehmann@klizix.mpi-stuttgart.mpg.de (Arno Lehmann).
+       - fixed filetime in writeclose. Thanks to Andreas Bahrdt
+       <100321.2431@compuserve.com>
+       - df fix for large disks from Andreas Bahrdt
+       - getpwanam support from horn@mickey.jsc.nasa.gov
+       - clean name change from Bernd Esser
+       <be@syli30.physik.uni-bonn.de>
+       - released alpha1
+       - more locking changes to fix Excel problem
+       - released alpha3
+       - another minor locking change
+       - smarter masking in the locking code. Excel now apparently works.
+       - minor FAQ updates
+       - changed max connections refusal error to access denied.
+       - added queue command to client to show the print queue
+       - changed some print queue reporting stuff
+
+1.8.0: 14/10/94
+        - added international chars to valid_dos_char(). Thanks 
+       to Daniel.Grandjean@dgr.epfl.ch
+       - volume label fix
+       - released alpha1
+       - important off by 4 fix in the server
+       - readbraw size adaption in the client
+       - released alpha2       
+       - wait3 cast for NeXt fixed. Thanks to dbrandon@politics.tamu.edu.
+       - man page fix for max xmit. Thanks to mmoore@wexford (Mike Moore)
+       - is_8_3() fixes from Jochen Roderburg <Roderburg@rrz.Uni-Koeln.DE>
+       - list_match() fix from jkf@soton.ac.uk
+       - statfs3 fix for BSDI from dan@supra.com
+       - changed file open/close/read in server in preparation for mmap()
+       based IO.
+       - added mmap() support for reading files in the server. Optional
+       at compile time. Thanks to suggestion from Roger Binns <rogerb@x.co.uk>
+       - mmap bug fixes
+       - added __SAMBA__ name in nmbd
+       - major changes for support of lanman2 and long filenames from
+       Jeremy Allison (jeremy@netcom.com)
+       - lseek optimisation. Thanks to Linus Torvalds.
+       - released alpha4
+       - date patches for lanman2 from Jeremy Allison
+       - added protocol aliases to handle WfWg (untested)
+       - allow for zero params or data in reply_trans2
+       - small lanman2 patches from jeremy
+       - more prototype additions for clean compilation
+       - postscript patches from tim@fsg.com
+       - more lanman2 patches from Jeremy
+       - added null ioctl support 
+       - kanji patches from fujita@ainix.isac.co.jp
+       - released alpha6
+       - disallowed null password access (thanks to Birger Kraegelin krg@iitb.fhg.de)
+       - Makefile fix for ultrix from andrew@d2bsys.demon.co.uk (Andrew Stirling)
+       - added per-service mangled names
+       - totally re-vamped loadparm.c  
+       - added "mangling char" parameter
+       - released alpha7
+       - added "default case = lower|upper" service option
+       - change mangling char to a service parameter
+       - ultrix enhanced security patch from steven@gopher.dosli.govt.nz
+       - more changes to loadparm.c
+       - printer name always set in [printers]
+       - string_free() fix thanks to jef_iwaniw@pts.mot.com
+       - changed group handling to be faster and work for large numbers 
+         of groups
+       - added dynamic gid_t type determination
+       - released alpha8
+       - fixed become_user() problem for services with invalid
+       directories
+       - added "invalid users" list on per service basis
+       - fixed pointer problems in alpha8 (thanks to murnaghant@a1uproar.yuppy.rdgmts.MTS.dec.com)
+       - fixed some date setting problems
+       - trans2 fixes from jeremy to stop infinite directory listings of 
+       long filenames
+       - "standard input" lpq patch from root@tlspu.demon.co.uk (Adrian Hungate)
+       - changed password checking to check session list and validated ids 
+       before user list
+       - split off password functions into password.c
+       - added hosts equiv and rhosts code (thanks to Tim Murnaghan <murnaghant@a1uproar.yuppy.hhl.MTS.dec.com>)
+       - released alpha11
+       - added "newer" command to the client
+       - attempt at aix trapdoor uid workaround
+       - released alpha12
+       - minor trans2 bugfix
+       - added ufc crypt (fast crypt) support. Thanks to suggestion from
+       forrest d whitcher <fw@world.std.com>
+       - socket() fix for getting bcast and netmask thanks to
+       Brian.Onn@Canada.Sun.COM
+       - added beginnings of IPC and named pipe support in the server
+       - changed file structure a bit, creating reply.c
+       - finished print queue support for lanman1
+       - changed default protocol to LANMAN2
+       - released alpha13
+       - logged IPC connects at a higher debug level
+       - added netgroup support to hosts equiv search
+       - disallowed root access though hosts.equiv (thanks to Colin.Dean@Smallworld.co.uk)
+       - kanji and password handling fixes from fujita@ainix.isac.co.jp
+       - several bug fixes for lanman and other things from
+       esser@pib1.physik.uni-bonn.de (Bernd Esser)
+       - updated man pages, README and announce files.
+       - released 1.8.00alpha1
+       - reply_close() time change fix from Andreas Bahrdt <100321.2431@compuserve.com>
+       - added valid users list to compliment invalid users list.
+       - aix fixes from tomc@osi.curtin.edu.au (Tom Crawley)
+       - changed testparm output format
+       - support for getting time from the server (nearly untested)
+       - fixed device type error for wild device ???? 
+       - fixed groups problem when in 0 groups
+       - more IPC fixups
+       - added support for "net view \\server" command to list
+       available services (like browsing)
+       - released 1.8.00alpha2
+       - changed port choice for nmbd -L
+       - added -L option to client to view share list on a host
+       - bug fixes for NetShareEnum code
+       - added "server string" option
+       - changed default print file name to include remote machine name.
+       - added hooks for browsing in nmbd
+       - added browsing to nmbd
+       - freebsd fixed from Steve Sims SimsS@Infi.Net
+       - got rid of tell()
+       - added subnet browsing with the S option in lmhosts
+       - made smbd prime nmbd with a 1 byte dgram
+       - added REUSADDR to open_socket_in() thanks to peter@ifm.liu.se
+
+
+1.8.01: 18/10/94
+
+       - auto add group "LANGROUP" if no group specified in nmbd
+       - made nmbd more responsive at startup
+       - lots of cleanups and consistancy checks
+       - added -C option to nmbd to set "machine comment".
+       - fixed postscript option
+       - force print_file in print_open()
+       - restructured the browsing a little
+       - casesignames fix for lanman-dos
+       - auto-load home directory from session setup
+       - changed to StrnCpy() for safety
+       - fixed "out of file descriptors" bug in the client (a WfWg bug?)
+
+
+1.8.02: 22/10/94
+       - fixed uppercase username problem
+       - added "hide dot files" option
+       - changed auto debug log in nmbd
+       - added LMHOSTS to Makefile
+       - added M flag in lmhosts to specify own netbios name
+       - added "load printers" option to auto-load all printers
+       - substitution of %p in lpq command
+       - substitution of %h and %v in server string and -C option of
+       nmbd
+       - string substitions substitute all occurances of a pattern
+       - added casesignames global option
+       - fix for man pages thanks to David Gardiner <dgardine@cssip.edu.au>
+       - changed debug options a bit
+       - added default for lpq command and lpr command
+       - changed default shell path to /bin/sh
+       - forced lpq under api to run as root - should speed things up
+       - added "group" option to force group of a connection
+       - added "read list" and "write list" options
+       - added max mux option - seems to fix NT browsing?
+       - added "mangled map" option thanks to
+       Martin.Tomes@uk.co.eurotherm.controls
+       - separated mangling functions into mangle.c
+       - allowed all dos chars in mangled names
+       - apollo changes from Helmut Buchsbaum <buc@eze22.siemens.co.at>
+       - password changing code from Bob Nance <Bob.Nance@niehs.nih.gov>
+       it doesn't quite work yet, but it's a start (disabled by default)
+
+
+1.8.03: 25/10/94
+       - made auto loaded services browsable as per default service
+       so you can hide homes but keep home directories.
+       - changed check_name() to handle "direct to network" printing
+       - auto 3 minute deadtime if all connections are closed. This 
+       prevents restart when polling the print queue.
+       - fix for newer command in client from Rich-Hoesly@uai.com
+       - changed connection recording method
+       - added the program smbstatus
+       - changed timeout mechanism
+       - "null passwords" option from Pim Zandbergen <pim@cti-software.nl>
+       - made new files with casesignames=False set their case to the default
+       case.
+       - fixed problem of uppercasing first letter of printers in printcap
+       - debug level fixes in trans2 from jimw@PE-Nelson.COM (Jim Watt)
+       - made null printer default to lp
+       
+1.8.04: 27/10/94
+       - added OS2.txt from riiber@oslonett.no
+       - another "auto services" fix. A silly strtok() bug :-(
+       - fixed the status locking and max connections (broken in 1.8.03)
+       - released alpha1
+       - added gets_slash so lines can be continued in smb.conf and
+       lmhosts
+       - browse list bugfix
+       - default to "load printers=yes"
+       - rewrote pcap.c
+       - intergraph bugfix from tarjeij@ulrik.uio.no
+       - changed properties flags in nmbd (to fix NT print browsing)
+       - allowed very long lines in printcap parsing.
+
+1.8.05: 28/10/94
+       - lanman2 fix from Jeremy
+
+1.9.00: 22/1/95
+       - only add home if not already there.
+       - added ulogoffX support
+       - PTR_DIFF() cleanups
+       - fixed a bug that caused STATUS..LCK to grow very large
+       - changed mangling to handle names ending in . a little better
+       - added "strip dot" option
+       - SGI and setgroups() fix from bill@sg25.npt.nuwc.navy.mil
+       - fixed password preservation in password_ok() (again?)
+       - unink fix from emer@vssad.enet.dec.com (Joel S. Emer)
+       - changed username part of spool filename to max 10 chars (from 6)
+       - magic script fix from beverly@datacube.com (Beverly Brown)
+       - reply_special() fix from Peter Brouwer <pb@apd.dec.com>
+       - stopped nmbd from listening on 138. It didn't seem to help much.
+       - clix fixes from ttj@sknsws61.sjo.statkart.no
+       - fixed select behaviour under Linux
+       - man page fix from Robin Cutshaw <robin@intercore.com>
+       - ISC block size fix from ralf@rbsoft.sdata.de (Ralf Beck)
+       - ISC fixes from Martin.Tomes@controls.eurotherm.co.uk
+       - attrib bit fix in smbclient (pointed out by Rich-Hoesly@uai.com)
+       - japanese extensions from fujita@ainix.isac.co.jp (Takashi
+       Fujita) and ouki@gssm.otuska.tsukuba.ac.jp.
+       - SCO patches from Stephen.Rothwell@pd.necisa.oz.au
+       - changed the system commands to redirect stderr
+       - changed default printername to service name for all print ops
+       - added ability to delete print queue entries
+       - added warning if you try to print without -P in smbclient
+       - INTERACTIVE patches from cardinal@settimo.italtel.it
+       - patch to handle spaces in group names from GJC@vax1.village.com 
+       (GEORGE J. CARRETTE)
+       - lockingX fix from stefank@esi.COM.AU (Stefan Kjellberg)
+       - some fairly radical changes to filename handling. We can now
+       handle mixed case filenames properly
+       - released alpha2
+       - added sysv printing support and improved bsd support
+       - changed the user that does print queues and lprm jobs
+       - return code support in the client from doylen@nbslib.isc-br.com (Doyle Nickless)
+       - added "strict locking" option. Defaults to no.
+
+       - added -I switch to nmbd
+       - fixed DEV bug thanks to Dirk.DeWachter@rug.ac.be
+       - use pw_encrypt() for shadow passords in Linux (from begemot@begemot.iko.kharkov.ua (Dmitry Gorodchanin))
+       - disabled read prediction by default
+       - added varient handling code to ipc.c for printQ and printDel.
+       - released alpha5
+       - AUX patches from root@dolphin.csudh.edu
+       - struct timeval fix from gkb1@york.ac.uk
+       - patches to merge ISC and INTERACTIVE from pim@cti-software.nl
+       - changed to "printing ="
+       - fixed problem with long print queues.
+       - fixed node status request in nmbd to go to non bcast
+       - made default path in services /tmp if not specified
+       - added %u in passwd program
+       - fixed up the password changing code for Linux
+       - no guest sess setup when user level security
+       - changed timeouts to kill dirptrs so cdroms can be unmounted
+       - added auto-reload of smb.conf if changed
+       - added SIGHUP to reload the config files
+       - added -M option to nmbd to search for a master browser
+       - added support for continue bit in trans2findnext      
+       - changed to dynamic strings in some more structures
+       - changed default deadtime to 30 minutes
+       - cleaned up the memory swapping code a bit
+       - updated the man pages somewhat
+       - added %m and %u in the "path=" of services
+       - released alpha6
+       - simple testing and fixups for solaris, sunos, aix, ultrix and
+       osf/1 (this is all I have access to).
+       - fixed chdir bug 
+       - added hashing to cnum selection
+       - released alpha7
+       - fixed printing bug
+       - reduced chance of "hung" smbd with dead client
+       - fixed do_match() bug (recently introduced)
+       - released alpha8
+       - nameserver fix from W.J.M.vGeest@et.tudelft.nl (W.J.M. van Geest)
+       - rewrote readbraw to try and overlap reads with writes
+       - client optimisations
+       - rewrote getwd cache and enabled it by default
+       - added partial smb packet reads (hopefully faster writes)
+       - added log file and log level options (with subs)
+       - added "read size" option
+       - tried setting some more socket options
+       - can use subs in "config file=" and will auto-reload
+       - added "include" options, with some subs
+       - finally got print manager working with NT
+       - auto-respond in nmbd to non-broadcast (auto WINS server, no -A
+       needed)
+       - released alpha10
+       - auto-delet unused services when reloading
+       - fixed auto-deletion
+       - fixed long names in printing
+       - fixed double loading of services file
+       - added printer file name support
+       - reformatted man pages for better www conversion
+       - renamed to 1.9.00.
+       - added support for RNetServerGetInfo and NetWkstaGetInfo API's
+       - updated the docs a bit
+       - released alpha1
+       - added -M -
+       - changed nmbd announce interval to 10 mins in outgoing packets
+       - hopefully fixed idle timeout reconnects
+       - strupper all command lines in nmbd
+       - added %a substitution for "remote architecture"
+       - added "Samba" protocol (same as lanman2)
+       - added "security = SERVER"
+       - released alpha2
+       - lowercase password fix
+       - fixed connect path length bug (thanks to JOHN YTSENG 
+       <jtseng@cory.EECS.Berkeley.EDU>)
+       - added subs on "password server".
+       - fixed printing filename bug from smbclient
+       - disk quotas and hpux printing support from Dirk.DeWachter@rug.ac.be
+       - Makefile patches from pappinm@ayr_srv2.nth.dpi.qld.gov.au
+       - AFS patches from Mike Allard (mgrmja@nextwork.rose-hulman.edu)
+       - fixed grp name = server name problem
+       - man page updates from Charlie Brady (charlieb@budge.apana.org.au)
+       - fixed file search bug by adding "finished" flag 
+       - added "max log size". Suggestion from Mark Hastings <mark.hastings@gain.com>
+       - released alpha3
+       - changed the read/write routines to handle partial read/writes
+       - released alpha4
+       - changed "guest account" to per-service
+       - changed so "guest ok" allows access to the guest account, 
+       not the "user=" line
+       - changed default readsize to 2048
+       - try bind to 137 in nmbd if possible
+       - added server lookup to -L option in smbclient (gets list of servers)
+       - added -M switch to smbclient for sending winpopup messages
+       - released alpha5
+       - FAQ updates from Paul Blackman ictinus@lake.canberra.edu.au
+
+1.9.01: 23/1/95
+       - changed comment in print Q info to service rather than server comment
+       - fixed smbclient -L to NT when in user level security mode
+       - hopefully finally fixed NT print manager problems
+       - added informative messages during smbclient -M 
+       - added node status replies to nmbd
+       - changed the lock offset fixup calculation to be more friendly
+       to dumb lockd daemons.
+       - added sigbus and sigsegv handlers to catch any silly errors and
+       print a message
+       - added message receipt to smbd and "message command =" option
+       
+1.9.02: 25/1/95
+        - added argv/argc mangling for people who start the server the
+       wrong way.
+       - some man page updates
+       - added "revalidate" option
+       - added hosts allow/deny access check to messaging access
+       - added timeouts in the client
+       - added check for existance of smbrun binary
+       - man page updates from Colin.Dean@Smallworld.co.uk
+       - freebsd patches from dfr@render.com
+       - added mask sanity check in SMBsearch
+       - added more useful substitutions (%S, %P, %I and %T)
+       - added "exec =" option to execute commands on each connection
+
+1.9.03: 13/3/95
+        - added "socket options" option
+       - close base fd's (0,1 and 2)
+       - use dup(0) for inetd operation
+       - better detection of is_daemon
+       - hopefully finally fixed silly put bug that gave the wrong
+       date on files.
+       - fixed segv in readbraw bug
+       - added improved checing for invalid (or null) print file name
+       - several patches from ad@papyrus.hamburg.com (Andreas Degert)
+       - fixed slow logout bug in smbclient
+       - fixed automounter problems
+       - added subs on lock dir
+       - BSDI patch from John.Terpstra@Aquasoft.com.au
+       - added separate nmb and smb logfile entries in the Makefile
+       - fixed return code error in open calls
+       - added simple status display of printer in lpq parsing
+       - rewrote the directory handling to avoid seekdir (added dir.c)
+       - added uid=65535 check (thanks to grant@gear.torque.net)
+       - enhanced transfer_file() to add header (used in readbraw)
+       - reply_special bugfix from ferret@pc8871.seqeb.gov.au
+       - added HAVE_PATHCONF
+       - RiscIX patches from Jim Barry <jim@ilp.com> and 
+       Charles Gay-Jones <charlie@ilp.com> 
+       - CLIX patches from ttj@sknsws61.sjo.statkart.no
+       - fixed aix lpq parser from kvintus@acd.com
+       - added substitutions to "include="
+       - M88K_S3 patches from tonyb@plaza.ds.adp.com (Tony D. Birnseth)
+       - fixed mangled stack problem
+       - added code to handle broken readdir() setups on solaris
+       - initgroups() fix from jarit@to.icl.fi
+       - dgux dfree fix from listwork@cloud9.net
+       - dnix support from Peter Olsson <pol@leissner.se>
+       - getgrgid() patch from tpg@bailey.com (Tom Gall)
+       - Makefile patch from obrien@Sea.Legent.com (David O'Brien)
+       - password changing fixes from Dirk.DeWachter@rug.ac.be
+       - minor man page updates
+       - tried to enhance the read prediction code a little bit
+
+1.9.04: 16/3/95
+        - a bit better handling of global include lists
+       - fixed GSTRING bug in loadparm.c (affected "socket options =")
+       - fixed broken lpq parsing code (recent bug). 
+       Thanks to Dirk.DeWachter@rug.ac.be
+
+1.9.05: 20/3/95
+        - improved mget in client to take multiple arguments and default
+       to *.*
+       - socket option fixes for both nmbd and smbd
+       - changed the byteorder handling scheme to be more portable (and
+       faster)
+       - lint cleanups from kast@kcs.planet.net (Robert Kast)
+       - added crude segv, sigbus and sighup recovery to nmbd
+       - rewrote lanman2_match to be closer to NT and WfWg behaviour
+       - Cray support from velo@sesun3.epfl.ch (Martin Ouwehand)
+       - "admin users" patch from Tim Leamy <tcleamy@ucdavis.edu>
+       - released alpha1
+       - added samba.7 man page
+       - no chdir when doing non AS_USER protocols
+       - become_guest() returns true in trapdoor uid system
+       - added more sophisticated segv/sigbus reporting (Linux only)
+       - released alpha2
+       - minor code cleanups (output of -Wall)
+       - smbprint fix from James Dryfoos <dryfoos@ll.mit.edu>
+       - improved testparm a little
+       - updated INSTALL.txt a little
+
+
+1.9.06: 21/3/95
+        - added %S substitution to users, valid users and invalid
+       users. This is useful for [homes].
+       - split off printing routines into printing.c and more dir
+       commands into dir.c
+       - postexec patch from jpm@gin.Mens.DE (Jan-Piet Mens)
+       - smbstatus updates from jpm@gin.Mens.DE (Jan-Piet Mens)
+       - reload sighup after use       
+       - fixed name ptr offset bug
+       - added %f in print commands 
+       - fixed byte ordering in nmbd which caused browsing to fail in
+       1.9.05
+
+1.9.07: 22/3/95
+       - important directory listing fix
+       - allowed path= in [homes] section
+       - printer status patches from Dirk.DeWachter@rug.ac.be
+
+1.9.08: 24/3/95
+        - fixed . and .. in root dir for lanman2
+       - better default comment in [homes]
+       - added time stamping to directory entries
+       - check directory access at connection time     
+       - rlimit code from loebach@homer.atria.com (Thomas M. Loebach)
+       - fixed home dir default comment
+       - totally rewrote dptr handling to overcome a persistant bug
+       - added [globals] as well as [global]
+
+1.9.09: 30/3/95
+        - fixed static string bug in nmbd
+       - better null password handling
+       - split CFLAGS in Makefile
+       - fixed typo in smbclient messaging
+       - made home dir not inherit path from [global]
+       - standard input printing patch from xiao@ic.ac.uk
+       - added O_CREAT to all print opens (bug in Win95)
+       - use /proc for process_exists under Linux and solaris
+       - fixed another segv problem in readbraw
+       - fixed volume label problem
+       - lots of changes to try and support the NT1 protocol
+       - released alpha1
+       - fixed session setup bug with NT in NT1 protocol
+       - released alpha2
+       - fixed "get" bug in smbclient that affected NT3.5
+       - added SO_KEEPALIVE as a default socket option in smbd
+       - changed some error codes to match those that NT 3.5 produces
+       - updated trans2 with some new calls for Win95 and WinNT (better
+       long file support)
+       - released alpha3
+       - fixed "nmbd -D -b" timeouts
+       - added IS_LONG_NAME flag to getattr in NT1
+       - added the NT qfileinfo trans2 commands
+       - merged qpathinfo with qfileinfo
+       - changed idling technique to try and be more friendly to
+       clients
+       - merged setfileinfo with setpathinfo and updated them with the NT fns
+       - improved read prediction a lot
+       - added read prediction to readbraw
+       - improved fault reporting (last packet dump)
+
+1.9.10: 30/3/95
+        - fixed read prediction+readbraw bug for read/write files
+
+1.9.11: 9/4/95
+        - fixed trans2 qpathinfo bug
+       - fixed bug with % in service name when doing print queue requests
+       - default readsize now 16K
+       - minor read prediction changes
+       - fixed status initialisation in print queue reporting
+       - fixed const compile problem for hpux
+       - minor SMBread fix from Volker Lendecke <lendecke@namu01.gwdg.de>
+       - removed space after -P in print commands (for fussy systems)
+       - disabled level2 of setfilepathinfo
+       - changed to a single read dir model, saving all dir names in
+       the Dir structure
+       - disabled NT protocols in the client due to reported problems
+       - fixed QUERY_FS_VOLUME_INFO which caused Win95 to hang on drive
+       properties
+       - minor lseek bug fix
+       - fixed up keepalives
+       - new timezone handling code (hopefully better!) 
+       from steve@qv3pluto.LeidenUniv.nl
+       - BSDI interface patch from jrb@csi.compuserve.com
+       - gettimeofday changes from Roger Binns <rogerb@x.co.uk>
+       - added smbrun option
+       - added "root preexec" and "root postexec" options
+
+1.9.12: 12/4/95
+        - hopefully fixed some recently introduced NT problems
+       - fixed a unlink error code problem
+       - minor testparm fix
+       - fixed silly error messages about comments in config files
+       - added "valid chars" option for other languages
+
+1.9.13: 28/4/95
+        - patches from David O'Brien (obrien@Sea.Legent.com) improving the
+       netgroup suport, and adding the "map archive" option, as well as
+       other minor cleanups.
+       - tried to add info level 3 and 4 support for OS/2
+       - default deadtime set to 0 as in docs
+       - cleaned up the trans2 code a little
+       - cleaned up the Makefile a little
+       - added charset.c and charset.h 
+       - expanded "valid chars" option to handle case mapping
+       - lots of changes to try and get timezones right
+       - released alpha1
+       - win95 fixups
+       - released alpha2
+       - added %H substitution (gives home directory)
+       - nameserv.c cleanups and minor bug fixes
+       - redid the browse hook logic 
+       - fixed daylight saving time offset for logfile messages
+       - added name cacheing to nmbd
+       - added send counts to node status in nmbd
+       - added STRICT_TIMEZONES compile time option (very computationally
+       expensive)
+       - removed the partial read code
+       - cleaned up the permission checking a lot
+       - added share modes (DENY_READ, DENY_WRITE, DENY_ALL, DENY_NONE,
+       DENY_FCB and DENY_DOS)
+       - added "share modes" option
+       - cleaned up the file open calls
+       - released alpha4
+       - fixed important one line bug in open_file()
+       - trans2 client fix from lendecke@namu01.gwdg.de
+       - netgroup patche from David O'Brien (obrien@Sea.Legent.com)
+       - case sensitive fix from lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis)
+       - got long filenames working from Win95 dos prompt
+       - added "workgroup=" option
+       - added "username map" option including multiple maps, group maps etc
+       - fixed password server for NT1 protocol and made it more robust
+       - changed unix_mode() to add IWUSR to read-only directories. This
+       is much closer to what clients expect.
+       - added preservation of unused permission bits when a chmod() is
+       called from a client.
+       - made static those fns that could be
+       - fixed typo in access.c (thanks to  Andrew J Cole
+       <A.J.Cole@cbl.leeds.ac.uk>)
+       - added %d substitution for process id 
+       (thanks to lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis))
+       - changed share error code to ERRbadshare
+       - added locked files list to smbstatus if share modes is enabled
+       - changed DENY_DOS to allow read by other tasks
+       - added shared_pending checks to server
+       - preserverd all possible permission bits during a chmod, and
+       fixed a trans2 chmod bug
+       - open /dev/null to use up first 3 fds, in an attempt to stop rogue
+       library routines from causing havoc
+       - fixed NT username problem when in server security
+       - added "force user" and "force group" options
+       - cleaned up some of the IPC calls a bit
+       - added writeraw to the client and cleaned up write raw in the server
+       - osf1 big-crypt bugfix from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+       - hopefully better disk-full checking
+       - next uid bugfix from patrick@graphics.cornell.edu
+       - changed share modes so lock directory doesn't need to be world 
+       writeable
+       - enabled write-raw by default
+       - added server_info() in client
+       - added level checks in some ipc calls
+       - added defines for the important timeouts in local.h
+       - added print queue deletion to smbclient (untested)
+       - removed the sysconf() calls
+       - optimised writebraw a bit
+       - fixed some file deletion problems
+       - added total_data check for extended attribs in trans2 (for OS/2)
+       - fixed broadcast reply bug in nmbd
+       - added careful core dumping code
+       - added faster password level searches (suggestion 
+       by lydick@cvpsun104.csc.ti.com (Dan Lydick))
+
+
+1.9.14: 22/9/95
+        - fixed up level 3 and 4 trans2 requests for OS/2
+       - minor optimisations in a few places
+       - cleaned up the closing of low fds a bit
+       - added SO_REUSEADDR to socket as a daemon
+       - override aDIR bit for directories in dos_chmod()
+       - SGI5 fixes from ymd@biosym.com (Yuri Diomin)
+       - bsize sanity check and removed sunos force to 1k
+       - force the create mode to be at least 0700
+       - SCO and freebsd include changes from Peter Olsson
+       <pol@leissner.se>
+       - check with FQDN in access.c (thanks to Arne Ansper <arne@ioc.ee>)
+       - default broadcast for dnix from Peter Olsson <pol@leissner.se>
+       - solaris patches from Ronald Guilmette <rfg@segfault.us.com>
+       - added EXDEV handling
+       - small AFS Makefile patch from mgrlhc@nextwork.rose-hulman.edu
+       - hopefully fixed the Win95 dates to work in other than my
+       timezone
+       - attempted alignment fixups (to speed up memcpy)
+       - added some DCE/DFS support (thanks to Jim Doyle <doyle@oec.com>)
+       - added fix so that root doesn't have special privilages to open
+       readonly files for writing (but admin users do). This fixes the MS
+       office install problem.
+       - fixed trans2 response bug in client
+       - got dual names working for NT
+       - enabled lock_and_read in NT protocol
+       - added %L macro for "local machine"
+       - changed dfree reporting to use "sectors per unit"
+       - fixed "not enough memory" bug in MS print manger by limiting
+       share name length in share enum.
+       - "short preserve case" option from Rabin Ezra (rabin@acm.org)
+       - added archive option to client
+       - changed openX in client to be able to open hidden and system files
+       - added "sync always" option
+       - rewrote writebmpx and readbmpx
+       - added auto string_sub_basic to all loadparm strings
+       - lots of nmbd fixups (add registration, refresh etc)
+       - released alpha1
+       - added smbtar patches from Ricky Poulten (poultenr@logica.co.uk)
+       - added a lpq cache and the "lpq cache time" option
+       - released alpha 2
+       - sun includes fix from Kimmo Suominen <kim@deshaw.com>
+       - change nmbd -L lookup type to workstation from server
+       - added min print space option
+       - added user and group names to smbstatus (thanks to 
+       davide.migliavacca@inferentia.it)
+       - fixed %f in print command bug (thanks to huver@amgraf.com)
+       - added wildcard support to SMBmv
+       - misc patches from David Elm (delm@hookup.net)
+       - changed default of "share modes" to yes
+       - changed default of "status" to yes
+       - aix qconfig parsing from Jean-Pierre.Boulard@univ-rennes1.fr
+       - more long_date fixups
+       - added wildcards to nmbd
+       - extensive changes to ipc.c and miscellaneous other changes 
+       from ad@papyrus.hamburg.com (Andreas Degert). Should especially
+       help OS/2 users
+       - added name release to nmbd
+       - relesed alpha4
+       - fixed "SOLARIS" to SUNOS5 in Makefile
+       - several minor fixups to get it to compile on aix, osf1, ultrix,
+       solaris and sunos
+       - released alpha5       
+       - minor bug fixes and cleanups in ipc.c
+       - fixed "only user" bug
+       - changed lpq to report guest queue entries as sesssetup_user to
+       allow for deletion by windows
+       - released alpha6
+       - added __SAMBA__ as type 0 in nmbd (was type 20)
+       - fixed null print job bug
+       - added 8 char warnings to testparm and smbclient
+       - changed to 8 char limit for names in pcap.c
+       - added linked list of config files to detect all date changes
+       that require a reload
+       - simplified pcap guessing heuristics
+       - added space trimming to the name mapping
+       - updated Get_Pwnam to add allow_change field for username mapping
+       - fixed MemMove bug (thanks to mass@tanner.com (Massimo
+       Sivilotti))
+       - released alpha7
+       - rewrote MemMove to be a little more efficient
+       - ipc va_arg bug fix from djg@tas.com (Dave Gesswein)
+       - added check for illegal chars in long filenames
+       - fixed name cache init bug in nmbd
+       - Convex patches from Victor Balashov <balashov@cv.jinr.dubna.su>
+       - timestring() bugfix from staale@spacetec.no
+       - changed %H to give path of forced user if one is set
+       - added quoting to smbclient to allow spaces in filenames
+       - convex and other patches from Ulrich Hahn
+       <ulrich.hahn@zdv.uni-tuebingen.de>
+       - released alpha8
+       - fixed rename directory bug
+       - nmbd wins fix from Maximilian Errath <errath@balu.kfunigraz.ac.at>
+       - client and AFS changes + password.c reorganisation + "more" and
+       "pwd" commands in client from Todd j. Derr (tjd@smi.med.pitt.edu)
+       - fixed several nmbd bugs
+       - released alpha9
+       - fixed another "cd" bug in smbclient
+       - password encryption from Jeremy Allison
+       - added "passwd chat" option and chat interpretation code
+       - added "smb passwd file" option
+       - released alpha10
+       - cleaned up chgpasswd.c a little
+       - portability changes to the encryption handling code
+       - added password encryption to smbclient
+       - fixed a share level security encryption bug
+       - added "ENCRYPTION.txt" document
+       - released alpha11
+       - added code to detect a password server loop
+       - fixed typo in chkpath in client.c that broken cd (again)
+       - LINUX_BIGCRYPT from marsj@ida.liu.se
+       - AFS password fixup from jbushey@primenet.com (Jeffrey G. Bushey)
+       - iso/8859-1 charcnv patches from Dan.Oscarsson@malmo.trab.se
+       - strtok/user_in_list fix from roderich@nodebonn.muc.bmw.de
+       - NETGROUP patches from J.W.Schilperoort@research.ptt.nl
+       - trim_string patch from J.W.Schilperoort@research.ptt.nl
+       - fixed problem with files with no extension getting mixed up
+       - ipc bugfix for print job deletion from Rainer Leberle <rleberle@auspex.de>
+       - released alpha12
+       - pwlen fix in NETGROUP from Andrew J Cole <A.J.Cole@cbl.leeds.ac.uk>
+       - lots of uid and encryption changes from Jeremy Allison. WinDD
+       should now work.
+       - released alpha13
+       - fixed max_xmit bug in client
+       - select fix in server (fixed critical drive errors under ISC)
+       - released alpha14
+       - wildcard fix from Jeremy
+       - changes to make IPC code more robust
+       - small select loop change to reduce cleaning of share files
+       - vtp, altos and mktime patches from Christian A. Lademann
+       <cal@zls.com>
+       - EEXIST bugfix in server.c
+       - changed mangled map to apply in all cases
+       - released alpha15
+       - fixed fcb open permissions (should mean apps know when a file is
+       read only)
+       - released alpha16
+       - client help formatting fix and docs fix from Peter Jones
+       <thanatos@drealm.org>
+       - added a directory cache
+       - use /proc whenever possible for pid detection
+       - TCSANOW fix in getsmbpasswd from roderich@nodebonn.muc.bmw.de
+       - fixed default printing mode for sysv systems
+       - make client always expand mask
+       - more minor IPC fixups
+       - pyramid makefile entry from jeffrey@itm.org
+       - client fixups for passlen, maxvcs and session redirect from
+       Charles Hoch <hoch@hplcgh.hpl.hp.com>
+       - finally fixed important IPC bug (varargs bug with int16)
+       - quota patches from Dirk.DeWachter@rug.ac.be
+       - print queue cache changes (per service) and print queue priority
+       additions from Dirk.DeWachter@rug.ac.be
+       - new japanese patches (incomplete) from 
+       fujita@ainix.isac.co.jp (Takashi Fujita)
+       - moved a lot more functions into system.c via wrappers
+       - changed a lot of the connection refused error codes to be more
+       informative (or at least different)
+       - released alpha17
+       - changed error return code from cannor chdir() in make_connection
+       - fixed realloc() bug in printing.c
+       - fixed invalid username bug in sesssetupX
+       - released alpha18
+       - made default service change name to asked for service (idea
+       from Ian McEwan <ijm@doc.ic.ac.uk>)
+       - fixed "guest only" bug
+       - sambatar patches from Ricky
+       - printing.c patches from Dirk.DeWachter@rug.ac.be
+       - rewrote become_user()
+       - sunos5 patch from Niels.Baggesen@uni-c.dk
+       - more japanese extensions patches from fujita@ainix.isac.co.jp 
+       - released alpha20
+       - added force_user to conn struct
+
+
+1.9.15: 14/11/95
+        - removed bcast override from workgroup announce in nmbd
+       - aix patch, added NO_SYSMOUNTH, from 
+       lionel leston <102624.346@compuserve.com>       
+       - quick fix in lp_string() to try and stop some core dumps
+       - added uid cache in connections structure 
+       to make user level security faster
+       - changed dos_mode() to show read-only on read-only shares only if
+       user w bit not set
+       - added check to stop exit_server() looping
+       - core dump fix in string_sub()
+       - fix client bug for long dirs in NT1 mode. 
+       Thanks to Erwin Authried (erwin@ws1.atv.tuwien.ac.at)
+       - switched to a safer (but probably slower) readbraw implementation
+       - released p1
+       - readbraw fix from Stefaan.Eeckels@eunet.lu
+       - fixed groups bug when user is in 1 group
+       - fixed NT1 dir bug
+       - changed default protocol in client to NT1
+       - changed trans2 to not return both names in long listing if long
+       name is 8.3
+       - made stat of "" return RONLY if not writeable drive
+       - wrapped strcpy() to stop nulls propogating (hack)
+       - made rename and unlink look at share locks on file
+       - clitar memory leak fix from jjm@jjm.com
+       - added -p option to smbstatus to list smbd processes
+       - added rename to the client
+       - released p2
+       - fixed SMBmv for case where the destination exists
+       - man page patch from michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+       - once again redid the time handling, but finally explained what
+       is going on, this is written up in TIME.txt. The "kludge-GMT" used
+       by NT is a bastard and led to a lot of the confusion
+       - kanji patch from fujita@ainix.isac.co.jp (Takashi Fujita)
+       - is08859-1 patches from eauth@mail.cso.co.at
+       - starting rewriting nmbd, new nmbd is nmbd2, old one still around
+       for time being
+       - released p3
+       - rewrote more of nmbd2 to use new structures
+       - CLIX patches from Jason.J.Faultless@bechtel.btx400.co.uk
+       - DirCacheFlush() bugfix from Michael Joosten
+       <joost@ori.cadlab.de>. This bug explains a lot of the crashes.
+       - fixed a bug in ChDir() that caused reversion to / in some
+       situations
+       - ipc fix from Magnus Hyllander <mhy@os.se>
+       - released p4
+       - smbpasswd fix from Jeremy
+       - compilation fixes from Magnus Hyllander <mhy@os.se>
+       - added NetServerEnum to ipc.c (needed for master browser stuff)
+       - Makefile fix from Gunther Mayer <gmayer@physik.uni-kl.de>
+       - cleanups for clean compile on several OSes
+       - added browse mastering code
+       - started integration with smb.conf for nmbd2
+       - released p5
+       - fixed death_time (should be t+ttl*3)
+       - fixed non-removal of dead servers
+       - added smbstatus -u patch from oskarh@spornet.is (Oskar Hannesson)
+       - NETGROUP fix from J.W.Schilperoort@research.kpn.com
+       - select and NO_SETGROUPS patches from lennylim@netcom.com (Lenny
+       Lim)
+       - added LINKS_READ_ONLY define in dos_mode() for LM/X
+       compatability
+       - "dir a.c" bug fixed thanks to roderich@nodebonn.muc.bmw.de 
+       (Roderich Schupp)
+       - job cancel fix in client from peo@mtek.chalmers.se
+       - changed nmbd2 to nmbd 
+       - fixed "dir a*" under trans2 lookups
+       - added StrnCaseCmp()
+       - updated docs a bit for new browsing stuff
+       - updated INSTALL.txt
+       - hopefully fixed server level security with WfWg
+
+1.9.15 (patches):
+        - major/minor fix for solaris from Jeroen Schipper 
+       <Jeroen.Schipper@let.ruu.nl>
+       - fixed critical bug in directory listings
+       - released p1
+       - fixed one of the causes of "out of memory" while browsing
+       - fixed manpage install script (Paul Blackman)
+       - added DNS failures to name cache
+       - fixed writebmpx bug (affects OS/2)
+       - misc OS/2 fixes, mostly for EA handling
+       - added SMBcopy
+       - added "max ttl" option
+       - arch detection patch from Bas Laarhoven <bas@vimec.nl>
+       - released p2
+       - another OS/2 fix - the level 4 getpathinfo for EAs
+       - added "alternate permissions" option
+       - changed client to parse destination names into name + domain
+       - fixed problem with PrimaryGroup and lmhosts loading
+       - added domain master ability to nmbd
+       - added "domain master" option
+       - added "domain controller" option and code
+       - pwd fix to client from Erik Devriendt (de@te6.siemens.be)
+       - fixed problem in smbmv that led to ar not working in mks
+       - added transs2
+       - released p3
+       - updated email addresses
+       - fix for innetgr from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+       - client translate fix from bandc@dircon.co.uk
+       - netbsd bcast fix from from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+       - syslog code from Alex Nash <alex@fa.tca.com>
+       - strip dot fix from Arne Ansper <arne@ioc.ee>
+       - added addtosmbpass + man page from 
+       michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+       - pcap fix for AIX from Jon Christiansen <jchristi@sctcorp.com>
+       - fixed servertype bug in remote announcements
+       - fixed up illegal name checks (should also be faster)
+       - kanji patches from fujita@ainix.isac.co.jp (Takashi Fujita)
+       - fixed bug handling non-encrypted passwords
+       - released p4
+       - fixed makefile for addtosmbpass
+       - DCE/DFS fixes from John Brezak (brezak@ch.hp.com)
+       - client patch for partial command matching from Andrew Wiseman 
+       <bandc@dircon.co.uk>
+       - made is_8_3() handle full paths
+       - rewrote open_file_shared() with help from Charles Hoch 
+       <hoch@hplcgh.hpl.hp.com>
+       - changed syslog to handle interactive programs
+       - fixed syslog problem with full path in argv[0]
+       - illegal name fixup for kanji from fujita@ainix.isac.co.jp
+       - fixed server level security to allow fallback to encryption
+       - changed reply_read() and reply_lockread() to ignore clients 
+       smb_bufsize in order to handle broken lanman clients
+       - fixed NT wildcard problem with old style programs
+       - man page patches from "John M. Sellens" 
+       <jmsellen@watdragon.uwaterloo.ca>
+       - partially documented the "character set" option
+       - changed default for MAXDIR to 64
+       - changed default DPTR idle time to 120
+       - released p5
+       - QNX patches from eldo@invisa.satlink.net (Eldo Loguzzo)       
+       - made nmbd use the "max log size" option and changed log handling
+       code a bit
+       - sunos patches, remote protocol (%R) addition and arch detection
+       changes to stop compiler warning from Timothy Hunt <tim@fsg.com>
+       - fixed become_user() bug that led to incorrect permissions in
+       some situations.
+       - released p6   
+       - is_8_3() fix from Charles Hoch <hoch@hplcgh.hpl.hp.com>
+       - nmblib bugfix from gmk@mhcnet.att.com (George Kull)
+       - aix pcap fix from Jon Christiansen <jchristi@sctcorp.com>
+       - added explicit sig_pipe() in server.c
+       - added domain logins option (not fully implemented)
+       - added HAVE_GMTOFF code
+       - got rid of PM_MAXLINE 
+       - minor client fix from goggi@eflir (Garðar Georg Nielsen)
+       - added SIGCLD_IGNORE for HPUX (from Tor Lillqvist 
+       <tml@hemuli.tte.vtt.fi>)        
+       - OSF/1 lpq patch from scooter@GENE.COM (Scooter Morris)
+       - NeXT patches from pmarcos@next.com (Paul Marcos)
+       - dstdiff patch to stop infinite loop from Erwin Authried (eauth@cso.co.at)
+       - password server option can now take a list of password servers
+       - patches to let samba run on OS/2 from Jason Rumney <jasonr@pec.co.nz>
+       - added domain logon and logon script suport
+       - SCO openserver 5 patches from Scott Michel <scottm@intime.intime.com>
+       - Makefile changes from Marty Leisner <leisner@sdsp.mc.xerox.com>
+       - chgpasswd changes from Roman Dumych <roman@nyxis.unibase.com>
+       for SVR4
+       - GUEST_SESSSETUP change from David.Chappell@mail.cc.trincoll.edu
+       - released p7
+       - moved SO_REUSEADDR before bind() (thanks to Thomas Bellman 
+       <tbe@ivab.se>)
+       - added more flexible GUEST_SESSSETUP to local.h and restored
+       pre-p7 behaviour as default
+       - released p8
+
+1.9.16: 
+       - Makefile fix from Marty Leisner <leisner@sdsp.mc.xerox.com>
+       - added %g and %G substitutions
+       - changed IDLE_CLOSED_TIMEOUT to 60 
+       - fixed the "admin user" status in domain logons
+       - hpux 10 "trusted security" patches from David-Michael Lincke
+       (dlincke@sgcl1.unisg.ch)
+       - added nmb lookups to client from Adrian Hill <Adrian.Hill@softimage.co.uk>
+       - svr4 pause/resume printing patch from Brendan O'Dea (bod@tyndall.com.au)
+       - fixed master announcement thanks to Luke Leighton <rah14@dial.pipex.com>
+       - changed srcdir usage in Makefile to be friendly to more systems
+       - NT4 alignment patches from Jeremy Allison (jra@vantive.com)
+       - updated share mode code for new spec
+       - minor client bugfix (for smbclient '\\\')
+       - fix for level 260 when magling disabled. From Martin Tomes
+       <Martin.Tomes@ecl.etherm.co.uk>
+       - SMBtranss2 fix for OS/2 from Jeremy Allison
+       - profiles fixup from Timm Wetzel <twetzel@cage.mpibpc.gwdg.de>
+       - man page updates from Dirk.DeWachter@rug.ac.be
+       - nmbsync fix from Andy Whitcroft <andy@soi.city.ac.uk>
+       - Lynx patches from Manfred Woelfel <woelfel@hpesco1.fzk.de>
+       - new smbtar stuff from Ricky
+       - changed to share mode DENY_NONE for tar
+       - fixed -D option of smbclient when in tar mode
+       - added aARCH to open modes
+       - added code to cope with select/read errors
+       - fixed blank browse entries after smb.conf reread
+       - integrated new browse stuff from Luke into ipc.c
+       - added workgroup list to smbclient -L
+       - improved archive attribute handling in close_file() and
+       write_file()
+       - smbtar fixes from Martin.Kraemer@mch.sni.de
+       - Linux quota patch from xeno@mix.hsv.no
+       - try to work around NT passlen2 problem in session setup
+       - released alpha1
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+       
+       
+==========
+todo:
+
+
+64 bit longs and IP addresses may give problems with unsigned longs?
+       
+set archive bit whenever file is modified??
+       
+fix man page dates
+
+reply only to own workgroup in server enum
+       
+patch to compile with g++ and possibly solaris c++
+       
+nmbd needs to keep browse list uptodate by talking to the master if it loses
+an election as others may still think its a valid backup and use it to get
+lists. 
+       
+leftover lock files can end up belonging to non-smbd processes after a reboot.
+       
+hosts allow in nmbd
+
+hosts allow cache
+
+add password command in smbclient
+
+drag long filename to samba under os/2 gives short name
+
+document max ttl option
+
+dup/close 0 for getopt?
+
+implement SMBmove  ??
+
+add option to print more info about locked files (full path, share name
+etc)
+
+very slow listing CD, perhaps because of order of stat and readdir or add 
+masking to opendir?
+
+protocol drop back in client to avoid openX etc.
+
+handle exported fat drives to a long filename capable client
+
+add check for existance of lpq commands etc (use stat?)
+
+get rid of the silly +4 and -4 by removing NBT stuff
+
+write-only shares
+
+document cnvchar stuff
+
+allow smbd to serve user and group lists to win95
+
+document homes behaviour with WinDD
+
+add "hide file = *.o" "hide dir = .Foo*" "show file = xx*" type options.
+
+ALLOW_PASSWORD_CHANGE only compiles/works on some systems
+
+weird foooooooo/open.exe bug on NT
+
+%a detection can't detect Win95 versus WinNT
+
+reverse mangled maps, so (*.html *.htm) works for new files.
+
+install problems with w95. could be some sort of race?
+
+more efficient Files[] structure to handle thousands of open files
+       
+lpd stuff:
+       Tony Aiuto (tony@ics.com)
+
+make max disk size local
+       
diff --git a/source4/client/.cvsignore b/source4/client/.cvsignore
new file mode 100644 (file)
index 0000000..49a52f7
--- /dev/null
@@ -0,0 +1 @@
+client_proto.h
\ No newline at end of file
diff --git a/source4/client/client.c b/source4/client/client.c
new file mode 100644 (file)
index 0000000..5c72da1
--- /dev/null
@@ -0,0 +1,3025 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Simo Sorce 2001-2002
+   Copyright (C) Jelmer Vernooij 2003
+   Copyright (C) James J Myers   2003 <myersjj@samba.org>
+   
+   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 "../client/client_proto.h"
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+struct cli_state *cli;
+extern BOOL in_client;
+static int port = 0;
+pstring cur_dir = "\\";
+static pstring cd_path = "";
+static pstring service;
+static pstring desthost;
+static pstring username;
+static pstring password;
+static BOOL use_kerberos;
+static BOOL got_pass;
+static char *cmdstr = NULL;
+
+static int io_bufsize = 64512;
+
+static int name_type = 0x20;
+
+static int process_tok(fstring tok);
+static int cmd_help(void);
+
+/* 30 second timeout on most commands */
+#define CLIENT_TIMEOUT (30*1000)
+#define SHORT_TIMEOUT (5*1000)
+
+/* value for unused fid field in trans2 secondary request */
+#define FID_UNUSED (0xFFFF)
+
+time_t newer_than = 0;
+static int archive_level = 0;
+
+static BOOL translation = False;
+
+static BOOL have_ip;
+
+/* clitar bits insert */
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+/* clitar bits end */
+
+static BOOL prompt = True;
+
+static int printmode = 1;
+
+static BOOL recurse = False;
+BOOL lowercase = False;
+
+static struct in_addr dest_ip;
+
+#define SEPARATORS " \t\n\r"
+
+static BOOL abort_mget = True;
+
+static pstring fileselection = "";
+
+extern file_info def_finfo;
+
+/* timing globals */
+SMB_BIG_UINT get_total_size = 0;
+unsigned int get_total_time_ms = 0;
+static SMB_BIG_UINT put_total_size = 0;
+static unsigned int put_total_time_ms = 0;
+
+/* totals globals */
+static double dir_total;
+
+#define USENMB
+
+/* some forward declarations */
+static struct cli_state *do_connect(const char *server, const char *share);
+
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+void dos_clean_name(char *s)
+{
+       char *p=NULL;
+
+       DEBUG(3,("dos_clean_name [%s]\n",s));
+
+       /* remove any double slashes */
+       all_string_sub(s, "\\\\", "\\", 0);
+
+       while ((p = strstr(s,"\\..\\")) != NULL) {
+               pstring s1;
+
+               *p = 0;
+               pstrcpy(s1,p+3);
+
+               if ((p=strrchr_m(s,'\\')) != NULL)
+                       *p = 0;
+               else
+                       *s = 0;
+               pstrcat(s,s1);
+       }  
+
+       trim_string(s,NULL,"\\..");
+
+       all_string_sub(s, "\\.\\", "\\", 0);
+}
+
+/****************************************************************************
+write to a local file with CR/LF->LF translation if appropriate. return the 
+number taken from the buffer. This may not equal the number written.
+****************************************************************************/
+static int writefile(int f, char *b, int n)
+{
+       int i;
+
+       if (!translation) {
+               return write(f,b,n);
+       }
+
+       i = 0;
+       while (i < n) {
+               if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') {
+                       b++;i++;
+               }
+               if (write(f, b, 1) != 1) {
+                       break;
+               }
+               b++;
+               i++;
+       }
+  
+       return(i);
+}
+
+/****************************************************************************
+  read from a file with LF->CR/LF translation if appropriate. return the 
+  number read. read approx n bytes.
+****************************************************************************/
+static int readfile(char *b, int n, XFILE *f)
+{
+       int i;
+       int c;
+
+       if (!translation)
+               return x_fread(b,1,n,f);
+  
+       i = 0;
+       while (i < (n - 1) && (i < CLI_BUFFER_SIZE)) {
+               if ((c = x_getc(f)) == EOF) {
+                       break;
+               }
+      
+               if (c == '\n') { /* change all LFs to CR/LF */
+                       b[i++] = '\r';
+               }
+      
+               b[i++] = c;
+       }
+  
+       return(i);
+}
+
+/****************************************************************************
+send a message
+****************************************************************************/
+static void send_message(void)
+{
+       int total_len = 0;
+       int grp_id;
+
+       if (!cli_message_start(cli, desthost, username, &grp_id)) {
+               d_printf("message start: %s\n", cli_errstr(cli));
+               return;
+       }
+
+
+       d_printf("Connected. Type your message, ending it with a Control-D\n");
+
+       while (!feof(stdin) && total_len < 1600) {
+               int maxlen = MIN(1600 - total_len,127);
+               pstring msg;
+               int l=0;
+               int c;
+
+               ZERO_ARRAY(msg);
+
+               for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
+                       if (c == '\n')
+                               msg[l++] = '\r';
+                       msg[l] = c;   
+               }
+
+               if (!cli_message_text(cli, msg, l, grp_id)) {
+                       d_printf("SMBsendtxt failed (%s)\n",cli_errstr(cli));
+                       return;
+               }      
+               
+               total_len += l;
+       }
+
+       if (total_len >= 1600)
+               d_printf("the message was truncated to 1600 bytes\n");
+       else
+               d_printf("sent %d bytes\n",total_len);
+
+       if (!cli_message_end(cli, grp_id)) {
+               d_printf("SMBsendend failed (%s)\n",cli_errstr(cli));
+               return;
+       }      
+}
+
+
+
+/****************************************************************************
+check the space on a device
+****************************************************************************/
+static int do_dskattr(void)
+{
+       int total, bsize, avail;
+
+       if (!cli_dskattr(cli, &bsize, &total, &avail)) {
+               d_printf("Error in dskattr: %s\n",cli_errstr(cli)); 
+               return 1;
+       }
+
+       d_printf("\n\t\t%d blocks of size %d. %d blocks available\n",
+                total, bsize, avail);
+
+       return 0;
+}
+
+/****************************************************************************
+show cd/pwd
+****************************************************************************/
+static int cmd_pwd(void)
+{
+       d_printf("Current directory is %s",service);
+       d_printf("%s\n",cur_dir);
+       return 0;
+}
+
+
+/****************************************************************************
+change directory - inner section
+****************************************************************************/
+static int do_cd(char *newdir)
+{
+       char *p = newdir;
+       pstring saved_dir;
+       pstring dname;
+      
+       dos_format(newdir);
+
+       /* Save the current directory in case the
+          new directory is invalid */
+       pstrcpy(saved_dir, cur_dir);
+       if (*p == '\\')
+               pstrcpy(cur_dir,p);
+       else
+               pstrcat(cur_dir,p);
+       if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
+               pstrcat(cur_dir, "\\");
+       }
+       dos_clean_name(cur_dir);
+       pstrcpy(dname,cur_dir);
+       pstrcat(cur_dir,"\\");
+       dos_clean_name(cur_dir);
+       
+       if (!strequal(cur_dir,"\\")) {
+               if (!cli_chkpath(cli, dname)) {
+                       d_printf("cd %s: %s\n", dname, cli_errstr(cli));
+                       pstrcpy(cur_dir,saved_dir);
+               }
+       }
+       
+       pstrcpy(cd_path,cur_dir);
+
+       return 0;
+}
+
+/****************************************************************************
+change directory
+****************************************************************************/
+static int cmd_cd(void)
+{
+       fstring buf;
+       int rc = 0;
+
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+               rc = do_cd(buf);
+       else
+               d_printf("Current directory is %s\n",cur_dir);
+
+       return rc;
+}
+
+
+/*******************************************************************
+  decide if a file should be operated on
+  ********************************************************************/
+static BOOL do_this_one(file_info *finfo)
+{
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) return(True);
+
+       if (*fileselection && 
+           !mask_match(cli, finfo->name,fileselection,False)) {
+               DEBUG(3,("mask_match %s failed\n", finfo->name));
+               return False;
+       }
+
+       if (newer_than && finfo->mtime < newer_than) {
+               DEBUG(3,("newer_than %s failed\n", finfo->name));
+               return(False);
+       }
+
+       if ((archive_level==1 || archive_level==2) && !(finfo->mode & FILE_ATTRIBUTE_ARCHIVE)) {
+               DEBUG(3,("archive %s failed\n", finfo->name));
+               return(False);
+       }
+       
+       return(True);
+}
+
+/*******************************************************************
+ Return a string representing an attribute for a file.
+********************************************************************/
+static const char *attrib_string(uint16 mode)
+{
+       static fstring attrstr;
+       int i, len;
+       const struct {
+               char c;
+               uint16 attr;
+       } attr_strs[] = {
+               {'V', FILE_ATTRIBUTE_VOLUME},
+               {'D', FILE_ATTRIBUTE_DIRECTORY},
+               {'A', FILE_ATTRIBUTE_ARCHIVE},
+               {'H', FILE_ATTRIBUTE_HIDDEN},
+               {'S', FILE_ATTRIBUTE_SYSTEM},
+               {'R', FILE_ATTRIBUTE_READONLY},
+               {'d', FILE_ATTRIBUTE_DEVICE},
+               {'t', FILE_ATTRIBUTE_TEMPORARY},
+               {'s', FILE_ATTRIBUTE_SPARSE},
+               {'r', FILE_ATTRIBUTE_REPARSE_POINT},
+               {'c', FILE_ATTRIBUTE_COMPRESSED},
+               {'o', FILE_ATTRIBUTE_OFFLINE},
+               {'n', FILE_ATTRIBUTE_NONINDEXED},
+               {'e', FILE_ATTRIBUTE_ENCRYPTED}
+       };
+
+       for (len=i=0; i<ARRAY_SIZE(attr_strs); i++) {
+               if (mode & attr_strs[i].attr) {
+                       attrstr[len++] = attr_strs[i].c;
+               }
+       }
+
+       attrstr[len] = 0;
+
+       return(attrstr);
+}
+
+/****************************************************************************
+  display info about a file
+  ****************************************************************************/
+static void display_finfo(file_info *finfo)
+{
+       if (do_this_one(finfo)) {
+               time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+               d_printf("  %-30s%7.7s %8.0f  %s",
+                        finfo->name,
+                        attrib_string(finfo->mode),
+                        (double)finfo->size,
+                        asctime(LocalTime(&t)));
+               dir_total += finfo->size;
+       }
+}
+
+
+/****************************************************************************
+   accumulate size of a file
+  ****************************************************************************/
+static void do_du(file_info *finfo)
+{
+       if (do_this_one(finfo)) {
+               dir_total += finfo->size;
+       }
+}
+
+static BOOL do_list_recurse;
+static BOOL do_list_dirs;
+static char *do_list_queue = 0;
+static long do_list_queue_size = 0;
+static long do_list_queue_start = 0;
+static long do_list_queue_end = 0;
+static void (*do_list_fn)(file_info *);
+
+/****************************************************************************
+functions for do_list_queue
+  ****************************************************************************/
+
+/*
+ * The do_list_queue is a NUL-separated list of strings stored in a
+ * char*.  Since this is a FIFO, we keep track of the beginning and
+ * ending locations of the data in the queue.  When we overflow, we
+ * double the size of the char*.  When the start of the data passes
+ * the midpoint, we move everything back.  This is logically more
+ * complex than a linked list, but easier from a memory management
+ * angle.  In any memory error condition, do_list_queue is reset.
+ * Functions check to ensure that do_list_queue is non-NULL before
+ * accessing it.
+ */
+static void reset_do_list_queue(void)
+{
+       SAFE_FREE(do_list_queue);
+       do_list_queue_size = 0;
+       do_list_queue_start = 0;
+       do_list_queue_end = 0;
+}
+
+static void init_do_list_queue(void)
+{
+       reset_do_list_queue();
+       do_list_queue_size = 1024;
+       do_list_queue = malloc(do_list_queue_size);
+       if (do_list_queue == 0) { 
+               d_printf("malloc fail for size %d\n",
+                        (int)do_list_queue_size);
+               reset_do_list_queue();
+       } else {
+               memset(do_list_queue, 0, do_list_queue_size);
+       }
+}
+
+static void adjust_do_list_queue(void)
+{
+       /*
+        * If the starting point of the queue is more than half way through,
+        * move everything toward the beginning.
+        */
+       if (do_list_queue && (do_list_queue_start == do_list_queue_end))
+       {
+               DEBUG(4,("do_list_queue is empty\n"));
+               do_list_queue_start = do_list_queue_end = 0;
+               *do_list_queue = '\0';
+       }
+       else if (do_list_queue_start > (do_list_queue_size / 2))
+       {
+               DEBUG(4,("sliding do_list_queue backward\n"));
+               memmove(do_list_queue,
+                       do_list_queue + do_list_queue_start,
+                       do_list_queue_end - do_list_queue_start);
+               do_list_queue_end -= do_list_queue_start;
+               do_list_queue_start = 0;
+       }
+          
+}
+
+static void add_to_do_list_queue(const char* entry)
+{
+       char *dlq;
+       long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
+       while (new_end > do_list_queue_size)
+       {
+               do_list_queue_size *= 2;
+               DEBUG(4,("enlarging do_list_queue to %d\n",
+                        (int)do_list_queue_size));
+               dlq = Realloc(do_list_queue, do_list_queue_size);
+               if (! dlq) {
+                       d_printf("failure enlarging do_list_queue to %d bytes\n",
+                                (int)do_list_queue_size);
+                       reset_do_list_queue();
+               }
+               else
+               {
+                       do_list_queue = dlq;
+                       memset(do_list_queue + do_list_queue_size / 2,
+                              0, do_list_queue_size / 2);
+               }
+       }
+       if (do_list_queue)
+       {
+               safe_strcpy(do_list_queue + do_list_queue_end, entry, 
+                           do_list_queue_size - do_list_queue_end - 1);
+               do_list_queue_end = new_end;
+               DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n",
+                        entry, (int)do_list_queue_start, (int)do_list_queue_end));
+       }
+}
+
+static char *do_list_queue_head(void)
+{
+       return do_list_queue + do_list_queue_start;
+}
+
+static void remove_do_list_queue_head(void)
+{
+       if (do_list_queue_end > do_list_queue_start)
+       {
+               do_list_queue_start += strlen(do_list_queue_head()) + 1;
+               adjust_do_list_queue();
+               DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n",
+                        (int)do_list_queue_start, (int)do_list_queue_end));
+       }
+}
+
+static int do_list_queue_empty(void)
+{
+       return (! (do_list_queue && *do_list_queue));
+}
+
+/****************************************************************************
+a helper for do_list
+  ****************************************************************************/
+static void do_list_helper(file_info *f, const char *mask, void *state)
+{
+       if (f->mode & FILE_ATTRIBUTE_DIRECTORY) {
+               if (do_list_dirs && do_this_one(f)) {
+                       do_list_fn(f);
+               }
+               if (do_list_recurse && 
+                   !strequal(f->name,".") && 
+                   !strequal(f->name,"..")) {
+                       pstring mask2;
+                       char *p;
+
+                       pstrcpy(mask2, mask);
+                       p = strrchr_m(mask2,'\\');
+                       if (!p) return;
+                       p[1] = 0;
+                       pstrcat(mask2, f->name);
+                       pstrcat(mask2,"\\*");
+                       add_to_do_list_queue(mask2);
+               }
+               return;
+       }
+
+       if (do_this_one(f)) {
+               do_list_fn(f);
+       }
+}
+
+
+/****************************************************************************
+a wrapper around cli_list that adds recursion
+  ****************************************************************************/
+void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, BOOL dirs)
+{
+       static int in_do_list = 0;
+
+       if (in_do_list && rec)
+       {
+               fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
+               exit(1);
+       }
+
+       in_do_list = 1;
+
+       do_list_recurse = rec;
+       do_list_dirs = dirs;
+       do_list_fn = fn;
+
+       if (rec)
+       {
+               init_do_list_queue();
+               add_to_do_list_queue(mask);
+               
+               while (! do_list_queue_empty())
+               {
+                       /*
+                        * Need to copy head so that it doesn't become
+                        * invalid inside the call to cli_list.  This
+                        * would happen if the list were expanded
+                        * during the call.
+                        * Fix from E. Jay Berkenbilt (ejb@ql.org)
+                        */
+                       pstring head;
+                       pstrcpy(head, do_list_queue_head());
+                       cli_list(cli, head, attribute, do_list_helper, NULL);
+                       remove_do_list_queue_head();
+                       if ((! do_list_queue_empty()) && (fn == display_finfo))
+                       {
+                               char* next_file = do_list_queue_head();
+                               char* save_ch = 0;
+                               if ((strlen(next_file) >= 2) &&
+                                   (next_file[strlen(next_file) - 1] == '*') &&
+                                   (next_file[strlen(next_file) - 2] == '\\'))
+                               {
+                                       save_ch = next_file +
+                                               strlen(next_file) - 2;
+                                       *save_ch = '\0';
+                               }
+                               d_printf("\n%s\n",next_file);
+                               if (save_ch)
+                               {
+                                       *save_ch = '\\';
+                               }
+                       }
+               }
+       }
+       else
+       {
+               if (cli_list(cli, mask, attribute, do_list_helper, NULL) == -1)
+               {
+                       d_printf("%s listing %s\n", cli_errstr(cli), mask);
+               }
+       }
+
+       in_do_list = 0;
+       reset_do_list_queue();
+}
+
+/****************************************************************************
+  get a directory listing
+  ****************************************************************************/
+static int cmd_dir(void)
+{
+       uint16 attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+       pstring mask;
+       fstring buf;
+       char *p=buf;
+       int rc;
+       
+       dir_total = 0;
+       pstrcpy(mask,cur_dir);
+       if(mask[strlen(mask)-1]!='\\')
+               pstrcat(mask,"\\");
+       
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               dos_format(p);
+               if (*p == '\\')
+                       pstrcpy(mask,p);
+               else
+                       pstrcat(mask,p);
+       }
+       else {
+               pstrcat(mask,"*");
+       }
+
+       do_list(mask, attribute, display_finfo, recurse, True);
+
+       rc = do_dskattr();
+
+       DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
+
+       return rc;
+}
+
+
+/****************************************************************************
+  get a directory listing
+  ****************************************************************************/
+static int cmd_du(void)
+{
+       uint16 attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+       pstring mask;
+       fstring buf;
+       char *p=buf;
+       int rc;
+       
+       dir_total = 0;
+       pstrcpy(mask,cur_dir);
+       if(mask[strlen(mask)-1]!='\\')
+               pstrcat(mask,"\\");
+       
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               dos_format(p);
+               if (*p == '\\')
+                       pstrcpy(mask,p);
+               else
+                       pstrcat(mask,p);
+       } else {
+               pstrcat(mask,"*");
+       }
+
+       do_list(mask, attribute, do_du, recurse, True);
+
+       rc = do_dskattr();
+
+       d_printf("Total number of bytes: %.0f\n", dir_total);
+
+       return rc;
+}
+
+
+/****************************************************************************
+  get a file from rname to lname
+  ****************************************************************************/
+static int do_get(char *rname, const char *lname, BOOL reget)
+{  
+       int handle = 0, fnum;
+       BOOL newhandle = False;
+       char *data;
+       struct timeval tp_start;
+       int read_size = io_bufsize;
+       uint16 attr;
+       size_t size;
+       off_t start = 0;
+       off_t nread = 0;
+       int rc = 0;
+
+       GetTimeOfDay(&tp_start);
+
+       if (lowercase) {
+               strlower(lname);
+       }
+
+       fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+       if (fnum == -1) {
+               d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+               return 1;
+       }
+
+       if(!strcmp(lname,"-")) {
+               handle = fileno(stdout);
+       } else {
+               if (reget) {
+                       handle = sys_open(lname, O_WRONLY|O_CREAT, 0644);
+                       if (handle >= 0) {
+                               start = sys_lseek(handle, 0, SEEK_END);
+                               if (start == -1) {
+                                       d_printf("Error seeking local file\n");
+                                       return 1;
+                               }
+                       }
+               } else {
+                       handle = sys_open(lname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+               }
+               newhandle = True;
+       }
+       if (handle < 0) {
+               d_printf("Error opening local file %s\n",lname);
+               return 1;
+       }
+
+
+       if (!cli_qfileinfo(cli, fnum, 
+                          &attr, &size, NULL, NULL, NULL, NULL, NULL) &&
+           !cli_getattrE(cli, fnum, 
+                         &attr, &size, NULL, NULL, NULL)) {
+               d_printf("getattrib: %s\n",cli_errstr(cli));
+               return 1;
+       }
+
+       DEBUG(2,("getting file %s of size %.0f as %s ", 
+                rname, (double)size, lname));
+
+       if(!(data = (char *)malloc(read_size))) { 
+               d_printf("malloc fail for size %d\n", read_size);
+               cli_close(cli, fnum);
+               return 1;
+       }
+
+       while (1) {
+               int n = cli_read(cli, fnum, data, nread + start, read_size);
+
+               if (n <= 0) break;
+               if (writefile(handle,data, n) != n) {
+                       d_printf("Error writing local file\n");
+                       rc = 1;
+                       break;
+               }
+      
+               nread += n;
+       }
+
+       if (nread + start < size) {
+               DEBUG (0, ("Short read when getting file %s. Only got %ld bytes.\n",
+                           rname, (long)nread));
+
+               rc = 1;
+       }
+
+       SAFE_FREE(data);
+       
+       if (!cli_close(cli, fnum)) {
+               d_printf("Error %s closing remote file\n",cli_errstr(cli));
+               rc = 1;
+       }
+
+       if (newhandle) {
+               close(handle);
+       }
+
+       if (archive_level >= 2 && (attr & FILE_ATTRIBUTE_ARCHIVE)) {
+               cli_setatr(cli, rname, attr & ~(uint16)FILE_ATTRIBUTE_ARCHIVE, 0);
+       }
+
+       {
+               struct timeval tp_end;
+               int this_time;
+               
+               GetTimeOfDay(&tp_end);
+               this_time = 
+                       (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+                       (tp_end.tv_usec - tp_start.tv_usec)/1000;
+               get_total_time_ms += this_time;
+               get_total_size += nread;
+               
+               DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+                        nread / (1.024*this_time + 1.0e-4),
+                        get_total_size / (1.024*get_total_time_ms)));
+       }
+       
+       return rc;
+}
+
+
+/****************************************************************************
+  get a file
+  ****************************************************************************/
+static int cmd_get(void)
+{
+       pstring lname;
+       pstring rname;
+       char *p;
+
+       pstrcpy(rname,cur_dir);
+       pstrcat(rname,"\\");
+       
+       p = rname + strlen(rname);
+       
+       if (!next_token_nr(NULL,p,NULL,sizeof(rname)-strlen(rname))) {
+               d_printf("get <filename>\n");
+               return 1;
+       }
+       pstrcpy(lname,p);
+       dos_clean_name(rname);
+       
+       next_token_nr(NULL,lname,NULL,sizeof(lname));
+       
+       return do_get(rname, lname, False);
+}
+
+
+/****************************************************************************
+  do a mget operation on one file
+  ****************************************************************************/
+static void do_mget(file_info *finfo)
+{
+       pstring rname;
+       pstring quest;
+       pstring saved_curdir;
+       pstring mget_mask;
+
+       if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+               return;
+
+       if (abort_mget) {
+               d_printf("mget aborted\n");
+               return;
+       }
+
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY)
+               slprintf(quest,sizeof(pstring)-1,
+                        "Get directory %s? ",finfo->name);
+       else
+               slprintf(quest,sizeof(pstring)-1,
+                        "Get file %s? ",finfo->name);
+
+       if (prompt && !yesno(quest)) return;
+
+       if (!(finfo->mode & FILE_ATTRIBUTE_DIRECTORY)) {
+               pstrcpy(rname,cur_dir);
+               pstrcat(rname,finfo->name);
+               do_get(rname, finfo->name, False);
+               return;
+       }
+
+       /* handle directories */
+       pstrcpy(saved_curdir,cur_dir);
+
+       pstrcat(cur_dir,finfo->name);
+       pstrcat(cur_dir,"\\");
+
+       unix_format(finfo->name);
+       if (lowercase)
+               strlower(finfo->name);
+       
+       if (!directory_exist(finfo->name,NULL) && 
+           mkdir(finfo->name,0777) != 0) {
+               d_printf("failed to create directory %s\n",finfo->name);
+               pstrcpy(cur_dir,saved_curdir);
+               return;
+       }
+       
+       if (chdir(finfo->name) != 0) {
+               d_printf("failed to chdir to directory %s\n",finfo->name);
+               pstrcpy(cur_dir,saved_curdir);
+               return;
+       }
+
+       pstrcpy(mget_mask,cur_dir);
+       pstrcat(mget_mask,"*");
+       
+       do_list(mget_mask, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY,do_mget,False, True);
+       chdir("..");
+       pstrcpy(cur_dir,saved_curdir);
+}
+
+
+/****************************************************************************
+view the file using the pager
+****************************************************************************/
+static int cmd_more(void)
+{
+       fstring rname,lname,pager_cmd;
+       char *pager;
+       int fd;
+       int rc = 0;
+
+       fstrcpy(rname,cur_dir);
+       fstrcat(rname,"\\");
+       
+       slprintf(lname,sizeof(lname)-1, "%s/smbmore.XXXXXX",tmpdir());
+       fd = smb_mkstemp(lname);
+       if (fd == -1) {
+               d_printf("failed to create temporary file for more\n");
+               return 1;
+       }
+       close(fd);
+
+       if (!next_token_nr(NULL,rname+strlen(rname),NULL,sizeof(rname)-strlen(rname))) {
+               d_printf("more <filename>\n");
+               unlink(lname);
+               return 1;
+       }
+       dos_clean_name(rname);
+
+       rc = do_get(rname, lname, False);
+
+       pager=getenv("PAGER");
+
+       slprintf(pager_cmd,sizeof(pager_cmd)-1,
+                "%s %s",(pager? pager:PAGER), lname);
+       system(pager_cmd);
+       unlink(lname);
+       
+       return rc;
+}
+
+
+
+/****************************************************************************
+do a mget command
+****************************************************************************/
+static int cmd_mget(void)
+{
+       uint16 attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+       pstring mget_mask;
+       fstring buf;
+       char *p=buf;
+
+       *mget_mask = 0;
+
+       if (recurse)
+               attribute |= FILE_ATTRIBUTE_DIRECTORY;
+       
+       abort_mget = False;
+
+       while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+               pstrcpy(mget_mask,cur_dir);
+               if(mget_mask[strlen(mget_mask)-1]!='\\')
+                       pstrcat(mget_mask,"\\");
+               
+               if (*p == '\\')
+                       pstrcpy(mget_mask,p);
+               else
+                       pstrcat(mget_mask,p);
+               do_list(mget_mask, attribute,do_mget,False,True);
+       }
+
+       if (!*mget_mask) {
+               pstrcpy(mget_mask,cur_dir);
+               if(mget_mask[strlen(mget_mask)-1]!='\\')
+                       pstrcat(mget_mask,"\\");
+               pstrcat(mget_mask,"*");
+               do_list(mget_mask, attribute,do_mget,False,True);
+       }
+       
+       return 0;
+}
+
+
+/****************************************************************************
+make a directory of name "name"
+****************************************************************************/
+static BOOL do_mkdir(char *name)
+{
+       if (!cli_mkdir(cli, name)) {
+               d_printf("%s making remote directory %s\n",
+                        cli_errstr(cli),name);
+               return(False);
+       }
+
+       return(True);
+}
+
+/****************************************************************************
+show 8.3 name of a file
+****************************************************************************/
+static BOOL do_altname(char *name)
+{
+       const char *altname;
+       if (!NT_STATUS_IS_OK(cli_qpathinfo_alt_name(cli, name, &altname))) {
+               d_printf("%s getting alt name for %s\n",
+                        cli_errstr(cli),name);
+               return(False);
+       }
+       d_printf("%s\n", altname);
+
+       return(True);
+}
+
+
+/****************************************************************************
+ Exit client.
+****************************************************************************/
+static int cmd_quit(void)
+{
+       cli_shutdown(cli);
+       exit(0);
+       /* NOTREACHED */
+       return 0;
+}
+
+
+/****************************************************************************
+  make a directory
+  ****************************************************************************/
+static int cmd_mkdir(void)
+{
+       pstring mask;
+       fstring buf;
+       char *p=buf;
+  
+       pstrcpy(mask,cur_dir);
+
+       if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+               if (!recurse)
+                       d_printf("mkdir <dirname>\n");
+               return 1;
+       }
+       pstrcat(mask,p);
+
+       if (recurse) {
+               pstring ddir;
+               pstring ddir2;
+               *ddir2 = 0;
+               
+               pstrcpy(ddir,mask);
+               trim_string(ddir,".",NULL);
+               p = strtok(ddir,"/\\");
+               while (p) {
+                       pstrcat(ddir2,p);
+                       if (!cli_chkpath(cli, ddir2)) { 
+                               do_mkdir(ddir2);
+                       }
+                       pstrcat(ddir2,"\\");
+                       p = strtok(NULL,"/\\");
+               }        
+       } else {
+               do_mkdir(mask);
+       }
+       
+       return 0;
+}
+
+
+/****************************************************************************
+  show alt name
+  ****************************************************************************/
+static int cmd_altname(void)
+{
+       pstring name;
+       fstring buf;
+       char *p=buf;
+  
+       pstrcpy(name,cur_dir);
+
+       if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+               d_printf("altname <file>\n");
+               return 1;
+       }
+       pstrcat(name,p);
+
+       do_altname(name);
+
+       return 0;
+}
+
+
+/****************************************************************************
+  put a single file
+  ****************************************************************************/
+static int do_put(char *rname, char *lname, BOOL reput)
+{
+       int fnum;
+       XFILE *f;
+       size_t start = 0;
+       off_t nread = 0;
+       char *buf = NULL;
+       int maxwrite = io_bufsize;
+       int rc = 0;
+       
+       struct timeval tp_start;
+       GetTimeOfDay(&tp_start);
+
+       if (reput) {
+               fnum = cli_open(cli, rname, O_RDWR|O_CREAT, DENY_NONE);
+               if (fnum >= 0) {
+                       if (!cli_qfileinfo(cli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
+                           !cli_getattrE(cli, fnum, NULL, &start, NULL, NULL, NULL)) {
+                               d_printf("getattrib: %s\n",cli_errstr(cli));
+                               return 1;
+                       }
+               }
+       } else {
+               fnum = cli_open(cli, rname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+       }
+  
+       if (fnum == -1) {
+               d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+               return 1;
+       }
+
+       /* allow files to be piped into smbclient
+          jdblair 24.jun.98
+
+          Note that in this case this function will exit(0) rather
+          than returning. */
+       if (!strcmp(lname, "-")) {
+               f = x_stdin;
+               /* size of file is not known */
+       } else {
+               f = x_fopen(lname,O_RDONLY, 0);
+               if (f && reput) {
+                       if (x_tseek(f, start, SEEK_SET) == -1) {
+                               d_printf("Error seeking local file\n");
+                               return 1;
+                       }
+               }
+       }
+
+       if (!f) {
+               d_printf("Error opening local file %s\n",lname);
+               return 1;
+       }
+
+  
+       DEBUG(1,("putting file %s as %s ",lname,
+                rname));
+  
+       buf = (char *)malloc(maxwrite);
+       if (!buf) {
+               d_printf("ERROR: Not enough memory!\n");
+               return 1;
+       }
+       while (!x_feof(f)) {
+               int n = maxwrite;
+               int ret;
+
+               if ((n = readfile(buf,n,f)) < 1) {
+                       if((n == 0) && x_feof(f))
+                               break; /* Empty local file. */
+
+                       d_printf("Error reading local file: %s\n", strerror(errno));
+                       rc = 1;
+                       break;
+               }
+
+               ret = cli_write(cli, fnum, 0, buf, nread + start, n);
+
+               if (n != ret) {
+                       d_printf("Error writing file: %s\n", cli_errstr(cli));
+                       rc = 1;
+                       break;
+               } 
+
+               nread += n;
+       }
+
+       if (!cli_close(cli, fnum)) {
+               d_printf("%s closing remote file %s\n",cli_errstr(cli),rname);
+               x_fclose(f);
+               SAFE_FREE(buf);
+               return 1;
+       }
+
+       
+       if (f != x_stdin) {
+               x_fclose(f);
+       }
+
+       SAFE_FREE(buf);
+
+       {
+               struct timeval tp_end;
+               int this_time;
+               
+               GetTimeOfDay(&tp_end);
+               this_time = 
+                       (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+                       (tp_end.tv_usec - tp_start.tv_usec)/1000;
+               put_total_time_ms += this_time;
+               put_total_size += nread;
+               
+               DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+                        nread / (1.024*this_time + 1.0e-4),
+                        put_total_size / (1.024*put_total_time_ms)));
+       }
+
+       if (f == x_stdin) {
+               cli_shutdown(cli);
+               exit(0);
+       }
+       
+       return rc;
+}
+
+
+/****************************************************************************
+  put a file
+  ****************************************************************************/
+static int cmd_put(void)
+{
+       pstring lname;
+       pstring rname;
+       fstring buf;
+       char *p=buf;
+       
+       pstrcpy(rname,cur_dir);
+       pstrcat(rname,"\\");
+  
+       if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+               d_printf("put <filename>\n");
+               return 1;
+       }
+       pstrcpy(lname,p);
+  
+       if (next_token_nr(NULL,p,NULL,sizeof(buf)))
+               pstrcat(rname,p);      
+       else
+               pstrcat(rname,lname);
+       
+       dos_clean_name(rname);
+
+       {
+               SMB_STRUCT_STAT st;
+               /* allow '-' to represent stdin
+                  jdblair, 24.jun.98 */
+               if (!file_exist(lname,&st) &&
+                   (strcmp(lname,"-"))) {
+                       d_printf("%s does not exist\n",lname);
+                       return 1;
+               }
+       }
+
+       return do_put(rname, lname, False);
+}
+
+/*************************************
+  File list structure
+*************************************/
+
+static struct file_list {
+       struct file_list *prev, *next;
+       char *file_path;
+       BOOL isdir;
+} *file_list;
+
+/****************************************************************************
+  Free a file_list structure
+****************************************************************************/
+
+static void free_file_list (struct file_list * list)
+{
+       struct file_list *tmp;
+       
+       while (list)
+       {
+               tmp = list;
+               DLIST_REMOVE(list, list);
+               SAFE_FREE(tmp->file_path);
+               SAFE_FREE(tmp);
+       }
+}
+
+/****************************************************************************
+  seek in a directory/file list until you get something that doesn't start with
+  the specified name
+  ****************************************************************************/
+static BOOL seek_list(struct file_list *list, char *name)
+{
+       while (list) {
+               trim_string(list->file_path,"./","\n");
+               if (strncmp(list->file_path, name, strlen(name)) != 0) {
+                       return(True);
+               }
+               list = list->next;
+       }
+      
+       return(False);
+}
+
+/****************************************************************************
+  set the file selection mask
+  ****************************************************************************/
+static int cmd_select(void)
+{
+       pstrcpy(fileselection,"");
+       next_token_nr(NULL,fileselection,NULL,sizeof(fileselection));
+
+       return 0;
+}
+
+/****************************************************************************
+  Recursive file matching function act as find
+  match must be always set to True when calling this function
+****************************************************************************/
+static int file_find(struct file_list **list, const char *directory, 
+                     const char *expression, BOOL match)
+{
+       DIR *dir;
+       struct file_list *entry;
+        struct stat statbuf;
+        int ret;
+        char *path;
+       BOOL isdir;
+       const char *dname;
+
+        dir = opendir(directory);
+       if (!dir) return -1;
+       
+        while ((dname = readdirname(dir))) {
+               if (!strcmp("..", dname)) continue;
+               if (!strcmp(".", dname)) continue;
+               
+               if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+                       continue;
+               }
+
+               isdir = False;
+               if (!match || !gen_fnmatch(expression, dname)) {
+                       if (recurse) {
+                               ret = stat(path, &statbuf);
+                               if (ret == 0) {
+                                       if (S_ISDIR(statbuf.st_mode)) {
+                                               isdir = True;
+                                               ret = file_find(list, path, expression, False);
+                                       }
+                               } else {
+                                       d_printf("file_find: cannot stat file %s\n", path);
+                               }
+                               
+                               if (ret == -1) {
+                                       SAFE_FREE(path);
+                                       closedir(dir);
+                                       return -1;
+                               }
+                       }
+                       entry = (struct file_list *) malloc(sizeof (struct file_list));
+                       if (!entry) {
+                               d_printf("Out of memory in file_find\n");
+                               closedir(dir);
+                               return -1;
+                       }
+                       entry->file_path = path;
+                       entry->isdir = isdir;
+                        DLIST_ADD(*list, entry);
+               } else {
+                       SAFE_FREE(path);
+               }
+        }
+
+       closedir(dir);
+       return 0;
+}
+
+/****************************************************************************
+  mput some files
+  ****************************************************************************/
+static int cmd_mput(void)
+{
+       fstring buf;
+       char *p=buf;
+       
+       while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+               int ret;
+               struct file_list *temp_list;
+               char *quest, *lname, *rname;
+       
+               file_list = NULL;
+
+               ret = file_find(&file_list, ".", p, True);
+               if (ret) {
+                       free_file_list(file_list);
+                       continue;
+               }
+               
+               quest = NULL;
+               lname = NULL;
+               rname = NULL;
+                               
+               for (temp_list = file_list; temp_list; 
+                    temp_list = temp_list->next) {
+
+                       SAFE_FREE(lname);
+                       if (asprintf(&lname, "%s/", temp_list->file_path) <= 0)
+                               continue;
+                       trim_string(lname, "./", "/");
+                       
+                       /* check if it's a directory */
+                       if (temp_list->isdir) {
+                               /* if (!recurse) continue; */
+                               
+                               SAFE_FREE(quest);
+                               if (asprintf(&quest, "Put directory %s? ", lname) < 0) break;
+                               if (prompt && !yesno(quest)) { /* No */
+                                       /* Skip the directory */
+                                       lname[strlen(lname)-1] = '/';
+                                       if (!seek_list(temp_list, lname))
+                                               break;              
+                               } else { /* Yes */
+                                       SAFE_FREE(rname);
+                                       if(asprintf(&rname, "%s%s", cur_dir, lname) < 0) break;
+                                       dos_format(rname);
+                                       if (!cli_chkpath(cli, rname) && 
+                                           !do_mkdir(rname)) {
+                                               DEBUG (0, ("Unable to make dir, skipping..."));
+                                               /* Skip the directory */
+                                               lname[strlen(lname)-1] = '/';
+                                               if (!seek_list(temp_list, lname))
+                                                       break;
+                                       }
+                               }
+                               continue;
+                       } else {
+                               SAFE_FREE(quest);
+                               if (asprintf(&quest,"Put file %s? ", lname) < 0) break;
+                               if (prompt && !yesno(quest)) /* No */
+                                       continue;
+                               
+                               /* Yes */
+                               SAFE_FREE(rname);
+                               if (asprintf(&rname, "%s%s", cur_dir, lname) < 0) break;
+                       }
+
+                       dos_format(rname);
+
+                       do_put(rname, lname, False);
+               }
+               free_file_list(file_list);
+               SAFE_FREE(quest);
+               SAFE_FREE(lname);
+               SAFE_FREE(rname);
+       }
+
+       return 0;
+}
+
+
+/****************************************************************************
+  cancel a print job
+  ****************************************************************************/
+static int do_cancel(int job)
+{
+       d_printf("REWRITE: print job cancel not implemented\n");
+       return 1;
+}
+
+
+/****************************************************************************
+  cancel a print job
+  ****************************************************************************/
+static int cmd_cancel(void)
+{
+       fstring buf;
+       int job; 
+
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("cancel <jobid> ...\n");
+               return 1;
+       }
+       do {
+               job = atoi(buf);
+               do_cancel(job);
+       } while (next_token_nr(NULL,buf,NULL,sizeof(buf)));
+       
+       return 0;
+}
+
+
+/****************************************************************************
+  print a file
+  ****************************************************************************/
+static int cmd_print(void)
+{
+       pstring lname;
+       pstring rname;
+       char *p;
+
+       if (!next_token_nr(NULL,lname,NULL, sizeof(lname))) {
+               d_printf("print <filename>\n");
+               return 1;
+       }
+
+       pstrcpy(rname,lname);
+       p = strrchr_m(rname,'/');
+       if (p) {
+               slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)getpid());
+       }
+
+       if (strequal(lname,"-")) {
+               slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)getpid());
+       }
+
+       return do_put(rname, lname, False);
+}
+
+
+/****************************************************************************
+ show a print queue
+****************************************************************************/
+static int cmd_queue(void)
+{
+       d_printf("REWRITE: print job queue not implemented\n");
+       
+       return 0;
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static void do_del(file_info *finfo)
+{
+       pstring mask;
+
+       pstrcpy(mask,cur_dir);
+       pstrcat(mask,finfo->name);
+
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) 
+               return;
+
+       if (!cli_unlink(cli, mask)) {
+               d_printf("%s deleting remote file %s\n",cli_errstr(cli),mask);
+       }
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static int cmd_del(void)
+{
+       pstring mask;
+       fstring buf;
+       uint16 attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+
+       if (recurse)
+               attribute |= FILE_ATTRIBUTE_DIRECTORY;
+       
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("del <filename>\n");
+               return 1;
+       }
+       pstrcat(mask,buf);
+
+       do_list(mask, attribute,do_del,False,False);
+       
+       return 0;
+}
+
+
+/****************************************************************************
+delete a whole directory tree
+****************************************************************************/
+static int cmd_deltree(void)
+{
+       pstring dname;
+       fstring buf;
+       int ret;
+
+       pstrcpy(dname,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("deltree <dirname>\n");
+               return 1;
+       }
+       pstrcat(dname,buf);
+
+       ret = cli_deltree(cli, dname);
+
+       if (ret == -1) {
+               printf("Failed to delete tree %s - %s\n", dname, cli_errstr(cli));
+               return -1;
+       }
+
+       printf("Deleted %d files in %s\n", ret, dname);
+       
+       return 0;
+}
+
+
+/****************************************************************************
+show as much information as possible about a file
+****************************************************************************/
+static int cmd_allinfo(void)
+{
+       pstring fname;
+       fstring buf;
+       int ret = 0;
+       TALLOC_CTX *mem_ctx;
+       union smb_fileinfo finfo;
+       NTSTATUS status;
+
+       pstrcpy(fname,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("deltree <dirname>\n");
+               return 1;
+       }
+       pstrcat(fname,buf);
+
+       mem_ctx = talloc_init(fname);
+
+       /* first a ALL_INFO QPATHINFO */
+       finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+       finfo.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("%s - %s\n", fname, nt_errstr(status));
+               ret = 1;
+               goto done;
+       }
+
+       d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.create_time));
+       d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.access_time));
+       d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.write_time));
+       d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.change_time));
+       d_printf("\tattrib:         0x%x\n", finfo.all_info.out.attrib);
+       d_printf("\talloc_size:     %lu\n", (unsigned long)finfo.all_info.out.alloc_size);
+       d_printf("\tsize:           %lu\n", (unsigned long)finfo.all_info.out.size);
+       d_printf("\tnlink:          %u\n", finfo.all_info.out.nlink);
+       d_printf("\tdelete_pending: %u\n", finfo.all_info.out.delete_pending);
+       d_printf("\tdirectory:      %u\n", finfo.all_info.out.directory);
+       d_printf("\tea_size:        %u\n", finfo.all_info.out.ea_size);
+       d_printf("\tfname:          '%s'\n", finfo.all_info.out.fname.s);
+
+       /* 8.3 name if any */
+       finfo.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (NT_STATUS_IS_OK(status)) {
+               d_printf("\talt_name:       %s\n", finfo.alt_name_info.out.fname.s);
+       }
+
+       /* dev/inode if available */
+       finfo.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (NT_STATUS_IS_OK(status)) {
+               d_printf("\tdevice          0x%x\n", finfo.internal_information.out.device);
+               d_printf("\tinode           0x%x\n", finfo.internal_information.out.inode);
+       }
+
+       /* the EAs, if any */
+       finfo.generic.level = RAW_FILEINFO_ALL_EAS;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (NT_STATUS_IS_OK(status)) {
+               int i;
+               for (i=0;i<finfo.all_eas.out.num_eas;i++) {
+                       d_printf("\tEA[%d] flags=%d %s=%*.*s\n", i,
+                                finfo.all_eas.out.eas[i].flags,
+                                finfo.all_eas.out.eas[i].name.s,
+                                finfo.all_eas.out.eas[i].value.length,
+                                finfo.all_eas.out.eas[i].value.length,
+                                finfo.all_eas.out.eas[i].value.data);
+               }
+       }
+
+       /* streams, if available */
+       finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (NT_STATUS_IS_OK(status)) {
+               int i;
+               for (i=0;i<finfo.stream_info.out.num_streams;i++) {
+                       d_printf("\tstream %d:\n", i);
+                       d_printf("\t\tsize       %ld\n", 
+                                (long)finfo.stream_info.out.streams[i].size);
+                       d_printf("\t\talloc size %ld\n", 
+                                (long)finfo.stream_info.out.streams[i].alloc_size);
+                       d_printf("\t\tname       %s\n", finfo.stream_info.out.streams[i].stream_name.s);
+               }
+       }       
+
+       /* dev/inode if available */
+       finfo.generic.level = RAW_FILEINFO_COMPRESSION_INFORMATION;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       if (NT_STATUS_IS_OK(status)) {
+               d_printf("\tcompressed size %ld\n", (long)finfo.compression_info.out.compressed_size);
+               d_printf("\tformat          %ld\n", (long)finfo.compression_info.out.format);
+               d_printf("\tunit_shift      %ld\n", (long)finfo.compression_info.out.unit_shift);
+               d_printf("\tchunk_shift     %ld\n", (long)finfo.compression_info.out.chunk_shift);
+               d_printf("\tcluster_shift   %ld\n", (long)finfo.compression_info.out.cluster_shift);
+       }
+
+       talloc_destroy(mem_ctx);
+
+done:
+       return ret;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static int cmd_open(void)
+{
+       pstring mask;
+       fstring buf;
+       
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("open <filename>\n");
+               return 1;
+       }
+       pstrcat(mask,buf);
+
+       cli_open(cli, mask, O_RDWR, DENY_ALL);
+
+       return 0;
+}
+
+
+/****************************************************************************
+remove a directory
+****************************************************************************/
+static int cmd_rmdir(void)
+{
+       pstring mask;
+       fstring buf;
+  
+       pstrcpy(mask,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               d_printf("rmdir <dirname>\n");
+               return 1;
+       }
+       pstrcat(mask,buf);
+
+       if (!cli_rmdir(cli, mask)) {
+               d_printf("%s removing remote directory file %s\n",
+                        cli_errstr(cli),mask);
+       }
+       
+       return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+static int cmd_link(void)
+{
+       pstring src,dest;
+       fstring buf,buf2;
+  
+       if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+  
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("link <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if (!cli_unix_hardlink(cli, src, dest)) {
+               d_printf("%s linking files (%s -> %s)\n", cli_errstr(cli), src, dest);
+               return 1;
+       }  
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(void)
+{
+       pstring src,dest;
+       fstring buf,buf2;
+  
+       if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("symlink <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if (!cli_unix_symlink(cli, src, dest)) {
+               d_printf("%s symlinking files (%s -> %s)\n",
+                       cli_errstr(cli), src, dest);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(void)
+{
+       pstring src;
+       mode_t mode;
+       fstring buf, buf2;
+  
+       if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("chmod mode file\n");
+               return 1;
+       }
+
+       mode = (mode_t)strtol(buf, NULL, 8);
+       pstrcat(src,buf2);
+
+       if (!cli_unix_chmod(cli, src, mode)) {
+               d_printf("%s chmod file %s 0%o\n",
+                       cli_errstr(cli), src, (unsigned int)mode);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(void)
+{
+       pstring src;
+       uid_t uid;
+       gid_t gid;
+       fstring buf, buf2, buf3;
+  
+       if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2)) ||
+           !next_token(NULL,buf3,NULL, sizeof(buf3))) {
+               d_printf("chown uid gid file\n");
+               return 1;
+       }
+
+       uid = (uid_t)atoi(buf);
+       gid = (gid_t)atoi(buf2);
+       pstrcat(src,buf3);
+
+       if (!cli_unix_chown(cli, src, uid, gid)) {
+               d_printf("%s chown file %s uid=%d, gid=%d\n",
+                       cli_errstr(cli), src, (int)uid, (int)gid);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+rename some files
+****************************************************************************/
+static int cmd_rename(void)
+{
+       pstring src,dest;
+       fstring buf,buf2;
+  
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+       
+       if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("rename <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if (!cli_rename(cli, src, dest)) {
+               d_printf("%s renaming files\n",cli_errstr(cli));
+               return 1;
+       }
+       
+       return 0;
+}
+
+
+/****************************************************************************
+toggle the prompt flag
+****************************************************************************/
+static int cmd_prompt(void)
+{
+       prompt = !prompt;
+       DEBUG(2,("prompting is now %s\n",prompt?"on":"off"));
+       
+       return 1;
+}
+
+
+/****************************************************************************
+set the newer than time
+****************************************************************************/
+static int cmd_newer(void)
+{
+       fstring buf;
+       BOOL ok;
+       SMB_STRUCT_STAT sbuf;
+
+       ok = next_token_nr(NULL,buf,NULL,sizeof(buf));
+       if (ok && (sys_stat(buf,&sbuf) == 0)) {
+               newer_than = sbuf.st_mtime;
+               DEBUG(1,("Getting files newer than %s",
+                        asctime(LocalTime(&newer_than))));
+       } else {
+               newer_than = 0;
+       }
+
+       if (ok && newer_than == 0) {
+               d_printf("Error setting newer-than time\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+/****************************************************************************
+set the archive level
+****************************************************************************/
+static int cmd_archive(void)
+{
+       fstring buf;
+
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               archive_level = atoi(buf);
+       } else
+               d_printf("Archive level is %d\n",archive_level);
+
+       return 0;
+}
+
+/****************************************************************************
+toggle the lowercaseflag
+****************************************************************************/
+static int cmd_lowercase(void)
+{
+       lowercase = !lowercase;
+       DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off"));
+
+       return 0;
+}
+
+
+
+
+/****************************************************************************
+toggle the recurse flag
+****************************************************************************/
+static int cmd_recurse(void)
+{
+       recurse = !recurse;
+       DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+
+       return 0;
+}
+
+/****************************************************************************
+toggle the translate flag
+****************************************************************************/
+static int cmd_translate(void)
+{
+       translation = !translation;
+       DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+                translation?"on":"off"));
+
+       return 0;
+}
+
+
+/****************************************************************************
+do a printmode command
+****************************************************************************/
+static int cmd_printmode(void)
+{
+       fstring buf;
+       fstring mode;
+
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               if (strequal(buf,"text")) {
+                       printmode = 0;      
+               } else {
+                       if (strequal(buf,"graphics"))
+                               printmode = 1;
+                       else
+                               printmode = atoi(buf);
+               }
+       }
+
+       switch(printmode)
+               {
+               case 0: 
+                       fstrcpy(mode,"text");
+                       break;
+               case 1: 
+                       fstrcpy(mode,"graphics");
+                       break;
+               default: 
+                       slprintf(mode,sizeof(mode)-1,"%d",printmode);
+                       break;
+               }
+       
+       DEBUG(2,("the printmode is now %s\n",mode));
+
+       return 0;
+}
+
+/****************************************************************************
+ do the lcd command
+ ****************************************************************************/
+static int cmd_lcd(void)
+{
+       fstring buf;
+       pstring d;
+       
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+               chdir(buf);
+       DEBUG(2,("the local directory is now %s\n",sys_getwd(d)));
+
+       return 0;
+}
+
+/****************************************************************************
+ get a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reget(void)
+{
+       pstring local_name;
+       pstring remote_name;
+       char *p;
+
+       pstrcpy(remote_name, cur_dir);
+       pstrcat(remote_name, "\\");
+       
+       p = remote_name + strlen(remote_name);
+       
+       if (!next_token_nr(NULL, p, NULL, sizeof(remote_name) - strlen(remote_name))) {
+               d_printf("reget <filename>\n");
+               return 1;
+       }
+       pstrcpy(local_name, p);
+       dos_clean_name(remote_name);
+       
+       next_token_nr(NULL, local_name, NULL, sizeof(local_name));
+       
+       return do_get(remote_name, local_name, True);
+}
+
+/****************************************************************************
+ put a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reput(void)
+{
+       pstring local_name;
+       pstring remote_name;
+       fstring buf;
+       char *p = buf;
+       SMB_STRUCT_STAT st;
+       
+       pstrcpy(remote_name, cur_dir);
+       pstrcat(remote_name, "\\");
+  
+       if (!next_token_nr(NULL, p, NULL, sizeof(buf))) {
+               d_printf("reput <filename>\n");
+               return 1;
+       }
+       pstrcpy(local_name, p);
+  
+       if (!file_exist(local_name, &st)) {
+               d_printf("%s does not exist\n", local_name);
+               return 1;
+       }
+
+       if (next_token_nr(NULL, p, NULL, sizeof(buf)))
+               pstrcat(remote_name, p);
+       else
+               pstrcat(remote_name, local_name);
+       
+       dos_clean_name(remote_name);
+
+       return do_put(remote_name, local_name, True);
+}
+
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL browse_host(BOOL sort)
+{
+       d_printf("REWRITE: host browsing not implemented\n");
+
+       return False;
+}
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL list_servers(char *wk_grp)
+{
+       d_printf("REWRITE: list servers not implemented\n");
+       return False;
+}
+
+/* Some constants for completing filename arguments */
+
+#define COMPL_NONE        0          /* No completions */
+#define COMPL_REMOTE      1          /* Complete remote filename */
+#define COMPL_LOCAL       2          /* Complete local filename */
+
+/* This defines the commands supported by this client.
+ * NOTE: The "!" must be the last one in the list because it's fn pointer
+ *       field is NULL, and NULL in that field is used in process_tok()
+ *       (below) to indicate the end of the list.  crh
+ */
+static struct
+{
+  const char *name;
+  int (*fn)(void);
+  const char *description;
+  char compl_args[2];      /* Completion argument info */
+} commands[] = 
+{
+  {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+  {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
+  {"allinfo",cmd_allinfo,"<file> show all possible info about a file",{COMPL_NONE,COMPL_NONE}},
+  {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
+  {"blocksize",cmd_block,"blocksize <number> (default 20)",{COMPL_NONE,COMPL_NONE}},
+  {"cancel",cmd_cancel,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
+  {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+  {"deltree",cmd_deltree,"<dir> delete a whole directory tree",{COMPL_REMOTE,COMPL_NONE}},
+  {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+  {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+  {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+  {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
+  {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
+  {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},  
+  {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
+  {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+  {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
+  {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+  {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},  
+  {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
+  {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
+  {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
+  {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
+  {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
+  {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},  
+  {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
+  {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
+  {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+  {"queue",cmd_queue,"show the print queue",{COMPL_NONE,COMPL_NONE}},
+  {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+  {"rd",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+  {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}},  
+  {"reget",cmd_reget,"<remote name> [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}},
+  {"rename",cmd_rename,"<src> <dest> rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
+  {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+  {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+  {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
+  {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"tar",cmd_tar,"tar <c|x>[IXFqbgNan] current directory to/from <file name>",{COMPL_NONE,COMPL_NONE}},
+  {"tarmode",cmd_tarmode,"<full|inc|reset|noreset> tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}},
+  {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+  
+  /* Yes, this must be here, see crh's comment above. */
+  {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
+  {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
+};
+
+
+/*******************************************************************
+  lookup a command string in the list of commands, including 
+  abbreviations
+  ******************************************************************/
+static int process_tok(fstring tok)
+{
+       int i = 0, matches = 0;
+       int cmd=0;
+       int tok_len = strlen(tok);
+       
+       while (commands[i].fn != NULL) {
+               if (strequal(commands[i].name,tok)) {
+                       matches = 1;
+                       cmd = i;
+                       break;
+               } else if (strnequal(commands[i].name, tok, tok_len)) {
+                       matches++;
+                       cmd = i;
+               }
+               i++;
+       }
+  
+       if (matches == 0)
+               return(-1);
+       else if (matches == 1)
+               return(cmd);
+       else
+               return(-2);
+}
+
+/****************************************************************************
+help
+****************************************************************************/
+static int cmd_help(void)
+{
+       int i=0,j;
+       fstring buf;
+       
+       if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+               if ((i = process_tok(buf)) >= 0)
+                       d_printf("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description);
+       } else {
+               while (commands[i].description) {
+                       for (j=0; commands[i].description && (j<5); j++) {
+                               d_printf("%-15s",commands[i].name);
+                               i++;
+                       }
+                       d_printf("\n");
+               }
+       }
+       return 0;
+}
+
+/****************************************************************************
+process a -c command string
+****************************************************************************/
+static int process_command_string(char *cmd)
+{
+       pstring line;
+       const char *ptr;
+       int rc = 0;
+
+       /* establish the connection if not already */
+       
+       if (!cli) {
+               cli = do_connect(desthost, service);
+               if (!cli)
+                       return 0;
+       }
+       
+       while (cmd[0] != '\0')    {
+               char *p;
+               fstring tok;
+               int i;
+               
+               if ((p = strchr_m(cmd, ';')) == 0) {
+                       strncpy(line, cmd, 999);
+                       line[1000] = '\0';
+                       cmd += strlen(cmd);
+               } else {
+                       if (p - cmd > 999) p = cmd + 999;
+                       strncpy(line, cmd, p - cmd);
+                       line[p - cmd] = '\0';
+                       cmd = p + 1;
+               }
+               
+               /* and get the first part of the command */
+               ptr = line;
+               if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+               
+               if ((i = process_tok(tok)) >= 0) {
+                       rc = commands[i].fn();
+               } else if (i == -2) {
+                       d_printf("%s: command abbreviation ambiguous\n",tok);
+               } else {
+                       d_printf("%s: command not found\n",tok);
+               }
+       }
+       
+       return rc;
+}      
+
+#define MAX_COMPLETIONS 100
+
+typedef struct {
+       pstring dirmask;
+       char **matches;
+       int count, samelen;
+       const char *text;
+       int len;
+} completion_remote_t;
+
+static void completion_remote_filter(file_info *f, const char *mask, void *state)
+{
+       completion_remote_t *info = (completion_remote_t *)state;
+
+       if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (strcmp(f->name, ".") != 0) && (strcmp(f->name, "..") != 0)) {
+               if ((info->dirmask[0] == 0) && !(f->mode & FILE_ATTRIBUTE_DIRECTORY))
+                       info->matches[info->count] = strdup(f->name);
+               else {
+                       pstring tmp;
+
+                       if (info->dirmask[0] != 0)
+                               pstrcpy(tmp, info->dirmask);
+                       else
+                               tmp[0] = 0;
+                       pstrcat(tmp, f->name);
+                       if (f->mode & FILE_ATTRIBUTE_DIRECTORY)
+                               pstrcat(tmp, "/");
+                       info->matches[info->count] = strdup(tmp);
+               }
+               if (info->matches[info->count] == NULL)
+                       return;
+               if (f->mode & FILE_ATTRIBUTE_DIRECTORY)
+                       smb_readline_ca_char(0);
+
+               if (info->count == 1)
+                       info->samelen = strlen(info->matches[info->count]);
+               else
+                       while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0)
+                               info->samelen--;
+               info->count++;
+       }
+}
+
+static char **remote_completion(const char *text, int len)
+{
+       pstring dirmask;
+       int i;
+       completion_remote_t info = { "", NULL, 1, len, text, len };
+
+       if (len >= PATH_MAX)
+               return(NULL);
+
+       info.matches = (char **)malloc(sizeof(info.matches[0])*MAX_COMPLETIONS);
+       if (!info.matches) return NULL;
+       info.matches[0] = NULL;
+
+       for (i = len-1; i >= 0; i--)
+               if ((text[i] == '/') || (text[i] == '\\'))
+                       break;
+       info.text = text+i+1;
+       info.samelen = info.len = len-i-1;
+
+       if (i > 0) {
+               strncpy(info.dirmask, text, i+1);
+               info.dirmask[i+1] = 0;
+               snprintf(dirmask, sizeof(dirmask), "%s%*s*", cur_dir, i-1, text);
+       } else
+               snprintf(dirmask, sizeof(dirmask), "%s*", cur_dir);
+
+       if (cli_list(cli, dirmask, 
+                    FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 
+                    completion_remote_filter, &info) < 0)
+               goto cleanup;
+
+       if (info.count == 2)
+               info.matches[0] = strdup(info.matches[1]);
+       else {
+               info.matches[0] = malloc(info.samelen+1);
+               if (!info.matches[0])
+                       goto cleanup;
+               strncpy(info.matches[0], info.matches[1], info.samelen);
+               info.matches[0][info.samelen] = 0;
+       }
+       info.matches[info.count] = NULL;
+       return info.matches;
+
+cleanup:
+       for (i = 0; i < info.count; i++)
+               free(info.matches[i]);
+       free(info.matches);
+       return NULL;
+}
+
+static char **completion_fn(const char *text, int start, int end)
+{
+       smb_readline_ca_char(' ');
+
+       if (start) {
+               const char *buf, *sp;
+               int i;
+               char compl_type;
+
+               buf = smb_readline_get_line_buffer();
+               if (buf == NULL)
+                       return NULL;
+               
+               sp = strchr(buf, ' ');
+               if (sp == NULL)
+                       return NULL;
+               
+               for (i = 0; commands[i].name; i++)
+                       if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
+                               break;
+               if (commands[i].name == NULL)
+                       return NULL;
+
+               while (*sp == ' ')
+                       sp++;
+
+               if (sp == (buf + start))
+                       compl_type = commands[i].compl_args[0];
+               else
+                       compl_type = commands[i].compl_args[1];
+
+               if (compl_type == COMPL_REMOTE)
+                       return remote_completion(text, end - start);
+               else /* fall back to local filename completion */
+                       return NULL;
+       } else {
+               char **matches;
+               int i, len, samelen, count=1;
+
+               matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+               if (!matches) return NULL;
+               matches[0] = NULL;
+
+               len = strlen(text);
+               for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
+                       if (strncmp(text, commands[i].name, len) == 0) {
+                               matches[count] = strdup(commands[i].name);
+                               if (!matches[count])
+                                       goto cleanup;
+                               if (count == 1)
+                                       samelen = strlen(matches[count]);
+                               else
+                                       while (strncmp(matches[count], matches[count-1], samelen) != 0)
+                                               samelen--;
+                               count++;
+                       }
+               }
+
+               switch (count) {
+               case 0: /* should never happen */
+               case 1:
+                       goto cleanup;
+               case 2:
+                       matches[0] = strdup(matches[1]);
+                       break;
+               default:
+                       matches[0] = malloc(samelen+1);
+                       if (!matches[0])
+                               goto cleanup;
+                       strncpy(matches[0], matches[1], samelen);
+                       matches[0][samelen] = 0;
+               }
+               matches[count] = NULL;
+               return matches;
+
+cleanup:
+               while (i >= 0) {
+                       free(matches[i]);
+                       i--;
+               }
+               free(matches);
+               return NULL;
+       }
+}
+
+/****************************************************************************
+make sure we swallow keepalives during idle time
+****************************************************************************/
+static void readline_callback(void)
+{
+       fd_set fds;
+       struct timeval timeout;
+       static time_t last_t;
+       time_t t;
+
+       t = time(NULL);
+
+       if (t - last_t < 5) return;
+
+       last_t = t;
+
+ again:
+       if (cli->transport->socket->fd == -1)
+               return;
+
+       FD_ZERO(&fds);
+       FD_SET(cli->transport->socket->fd, &fds);
+
+       timeout.tv_sec = 0;
+       timeout.tv_usec = 0;
+       sys_select_intr(cli->transport->socket->fd+1,&fds,NULL,NULL,&timeout);
+               
+       /* We deliberately use cli_swallow_keepalives instead of
+          client_receive_smb as we want to receive
+          session keepalives and then drop them here.
+       */
+       if (FD_ISSET(cli->transport->socket->fd, &fds)) {
+               if (!cli_request_receive_next(cli->transport)) {
+                       d_printf("Lost connection to server\n");
+                       exit(1);
+               }
+               goto again;
+       }
+
+       if (cli->tree) {
+               cli_chkpath(cli, "\\");
+       }
+}
+
+
+/****************************************************************************
+process commands on stdin
+****************************************************************************/
+static void process_stdin(void)
+{
+       const char *ptr;
+
+       while (1) {
+               fstring tok;
+               fstring the_prompt;
+               char *cline;
+               pstring line;
+               int i;
+               
+               /* display a prompt */
+               slprintf(the_prompt, sizeof(the_prompt)-1, "smb: %s> ", cur_dir);
+               cline = smb_readline(the_prompt, readline_callback, completion_fn);
+                       
+               if (!cline) break;
+               
+               pstrcpy(line, cline);
+
+               /* special case - first char is ! */
+               if (*line == '!') {
+                       system(line + 1);
+                       continue;
+               }
+      
+               /* and get the first part of the command */
+               ptr = line;
+               if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+
+               if ((i = process_tok(tok)) >= 0) {
+                       commands[i].fn();
+               } else if (i == -2) {
+                       d_printf("%s: command abbreviation ambiguous\n",tok);
+               } else {
+                       d_printf("%s: command not found\n",tok);
+               }
+       }
+}
+
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *do_connect(const char *server, const char *share)
+{
+       struct cli_state *c;
+       struct nmb_name called, calling;
+       const char *server_n;
+       struct in_addr ip;
+       fstring servicename;
+       char *sharename;
+       
+       /* make a copy so we don't modify the global string 'service' */
+       fstrcpy(servicename, share);
+       sharename = servicename;
+       if (*sharename == '\\') {
+               server = sharename+2;
+               sharename = strchr_m(server,'\\');
+               if (!sharename) return NULL;
+               *sharename = 0;
+               sharename++;
+       }
+
+       server_n = server;
+       
+       zero_ip(&ip);
+
+       make_nmb_name(&calling, lp_netbios_name(), 0x0);
+       make_nmb_name(&called , server, name_type);
+
+ again:
+       zero_ip(&ip);
+       if (have_ip) ip = dest_ip;
+
+       /* have to open a new connection */
+       if (!(c=cli_state_init()) || !cli_socket_connect(c, server_n, &ip)) {
+               d_printf("Connection to %s failed\n", server_n);
+               return NULL;
+       }
+
+       if (!cli_transport_establish(c, &calling, &called)) {
+               char *p;
+               d_printf("session request to %s failed (%s)\n", 
+                        called.name, cli_errstr(c));
+               cli_shutdown(c);
+               if ((p=strchr_m(called.name, '.'))) {
+                       *p = 0;
+                       goto again;
+               }
+               if (strcmp(called.name, "*SMBSERVER")) {
+                       make_nmb_name(&called , "*SMBSERVER", 0x20);
+                       goto again;
+               }
+               return NULL;
+       }
+
+       DEBUG(4,(" session request ok\n"));
+
+       if (!cli_negprot(c)) {
+               d_printf("protocol negotiation failed\n");
+               cli_shutdown(c);
+               return NULL;
+       }
+
+       if (!got_pass) {
+               const char *pass = getpass("Password: ");
+               if (pass) {
+                       pstrcpy(password, pass);
+               }
+       }
+
+       if (!cli_session_setup(c, username, password, 
+                              lp_workgroup())) {
+               /* if a password was not supplied then try again with a null username */
+               if (password[0] || !username[0] || use_kerberos ||
+                   !cli_session_setup(c, "", "", lp_workgroup())) { 
+                       d_printf("session setup failed: %s\n", cli_errstr(c));
+                       cli_shutdown(c);
+                       return NULL;
+               }
+               d_printf("Anonymous login successful\n");
+       }
+
+       DEBUG(4,(" session setup ok\n"));
+
+       if (!cli_send_tconX(c, sharename, "?????", password)) {
+               d_printf("tree connect failed: %s\n", cli_errstr(c));
+               cli_shutdown(c);
+               return NULL;
+       }
+
+       DEBUG(4,(" tconx ok\n"));
+
+       return c;
+}
+
+
+/****************************************************************************
+  process commands from the client
+****************************************************************************/
+static int process(char *base_directory)
+{
+       int rc = 0;
+
+       cli = do_connect(desthost, service);
+       if (!cli) {
+               return 1;
+       }
+
+       if (*base_directory) do_cd(base_directory);
+       
+       if (cmdstr) {
+               rc = process_command_string(cmdstr);
+       } else {
+               process_stdin();
+       }
+  
+       cli_shutdown(cli);
+       return rc;
+}
+
+/****************************************************************************
+handle a -L query
+****************************************************************************/
+static int do_host_query(char *query_host)
+{
+       cli = do_connect(query_host, "IPC$");
+       if (!cli)
+               return 1;
+
+       browse_host(True);
+       list_servers(lp_workgroup());
+
+       cli_shutdown(cli);
+       
+       return(0);
+}
+
+
+/****************************************************************************
+handle a tar operation
+****************************************************************************/
+static int do_tar_op(char *base_directory)
+{
+       int ret;
+
+       /* do we already have a connection? */
+       if (!cli) {
+               cli = do_connect(desthost, service);    
+               if (!cli)
+                       return 1;
+       }
+
+       recurse=True;
+
+       if (*base_directory) do_cd(base_directory);
+       
+       ret=process_tar();
+
+       cli_shutdown(cli);
+
+       return(ret);
+}
+
+/****************************************************************************
+handle a message operation
+****************************************************************************/
+static int do_message_op(void)
+{
+       struct in_addr ip;
+       struct nmb_name called, calling;
+       fstring server_name;
+       char name_type_hex[10];
+
+       make_nmb_name(&calling, lp_netbios_name(), 0x0);
+       make_nmb_name(&called , desthost, name_type);
+
+       fstrcpy(server_name, desthost);
+       snprintf(name_type_hex, sizeof(name_type_hex), "#%X", name_type);
+       fstrcat(server_name, name_type_hex);
+
+       zero_ip(&ip);
+       if (have_ip) ip = dest_ip;
+
+       if (!(cli=cli_state_init()) || !cli_socket_connect(cli, server_name, &ip)) {
+               d_printf("Connection to %s failed\n", desthost);
+               return 1;
+       }
+
+       if (!cli_transport_establish(cli, &calling, &called)) {
+               d_printf("session request failed\n");
+               cli_shutdown(cli);
+               return 1;
+       }
+
+       send_message();
+       cli_shutdown(cli);
+
+       return 0;
+}
+
+
+/**
+ * Process "-L hostname" option.
+ *
+ * We don't actually do anything yet -- we just stash the name in a
+ * global variable and do the query when all options have been read.
+ **/
+static void remember_query_host(const char *arg,
+                               pstring query_host)
+{
+       char *slash;
+       
+       while (*arg == '\\' || *arg == '/')
+               arg++;
+       pstrcpy(query_host, arg);
+       if ((slash = strchr(query_host, '/'))
+           || (slash = strchr(query_host, '\\'))) {
+               *slash = 0;
+       }
+}
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       fstring base_directory;
+       int opt;
+       pstring query_host;
+       BOOL message = False;
+       extern char tar_type;
+       pstring term_code;
+       poptContext pc;
+       char *p;
+       int rc = 0;
+       TALLOC_CTX *mem_ctx;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+
+               { "name-resolve", 'R', POPT_ARG_STRING, NULL, 'R', "Use these name resolution services only", "NAME-RESOLVE-ORDER" },
+               { "message", 'M', POPT_ARG_STRING, NULL, 'M', "Send message", "HOST" },
+               { "ip-address", 'I', POPT_ARG_STRING, NULL, 'I', "Use this IP to connect to", "IP" },
+               { "stderr", 'E', POPT_ARG_NONE, NULL, 'E', "Write messages to stderr instead of stdout" },
+               { "list", 'L', POPT_ARG_STRING, NULL, 'L', "Get a list of shares available on a host", "HOST" },
+               { "terminal", 't', POPT_ARG_STRING, NULL, 't', "Terminal I/O code {sjis|euc|jis7|jis8|junet|hex}", "CODE" },
+               { "max-protocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set the max protocol level", "LEVEL" },
+               { "tar", 'T', POPT_ARG_STRING, NULL, 'T', "Command line tar", "<c|x>IXFqgbNan" },
+               { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" },
+               { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" }, 
+               { "send-buffer", 'b', POPT_ARG_INT, NULL, 'b', "Changes the transmit/send buffer", "BYTES" },
+               { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
+               POPT_COMMON_SAMBA
+               POPT_COMMON_CONNECTION
+               POPT_COMMON_CREDENTIALS
+               POPT_TABLEEND
+       };
+       
+
+#ifdef KANJI
+       pstrcpy(term_code, KANJI);
+#else /* KANJI */
+       *term_code = 0;
+#endif /* KANJI */
+
+       *query_host = 0;
+       *base_directory = 0;
+
+       setup_logging(argv[0],True);
+       mem_ctx = talloc_init("client.c/main");
+       if (!mem_ctx) {
+               d_printf("\nclient.c: Not enough memory\n");
+               exit(1);
+       }
+
+       if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+               fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
+                       argv[0], dyn_CONFIGFILE);
+       }
+       
+       pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 
+                               POPT_CONTEXT_KEEP_FIRST);
+       poptSetOtherOptionHelp(pc, "service <password>");
+
+       in_client = True;   /* Make sure that we tell lp_load we are */
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'M':
+                       /* Messages are sent to NetBIOS name type 0x3
+                        * (Messenger Service).  Make sure we default
+                        * to port 139 instead of port 445. srl,crh
+                        */
+                       name_type = 0x03; 
+                       pstrcpy(desthost,poptGetOptArg(pc));
+                       if( 0 == port ) port = 139;
+                       message = True;
+                       break;
+               case 'I':
+                       {
+                               dest_ip = *interpret_addr2(mem_ctx, poptGetOptArg(pc));
+                               if (is_zero_ip(dest_ip))
+                                       exit(1);
+                               have_ip = True;
+                       }
+                       break;
+               case 'E':
+                       setup_logging("client", DEBUG_STDERR);
+                       break;
+
+               case 'L':
+                       remember_query_host(poptGetOptArg(pc), query_host);
+                       break;
+               case 't':
+                       pstrcpy(term_code, poptGetOptArg(pc));
+                       break;
+               case 'm':
+                       lp_set_cmdline("max protocol", poptGetOptArg(pc));
+                       break;
+               case 'R':
+                       lp_set_cmdline("name resolve order", poptGetOptArg(pc));
+                       break;
+               case 'T':
+                       if (!tar_parseargs(argc, argv, poptGetOptArg(pc), optind)) {
+                               poptPrintUsage(pc, stderr, 0);
+                               exit(1);
+                       }
+                       break;
+               case 'D':
+                       fstrcpy(base_directory,poptGetOptArg(pc));
+                       break;
+               case 'b':
+                       io_bufsize = MAX(1, atoi(poptGetOptArg(pc)));
+                       break;
+               }
+       }
+
+       poptGetArg(pc);
+       
+       load_interfaces();
+
+       if(poptPeekArg(pc)) {
+               pstrcpy(service,poptGetArg(pc));  
+               /* Convert any '/' characters in the service name to '\' characters */
+               string_replace(service, '/','\\');
+
+               if (count_chars(service,'\\') < 3) {
+                       d_printf("\n%s: Not enough '\\' characters in service\n",service);
+                       poptPrintUsage(pc, stderr, 0);
+                       exit(1);
+               }
+       }
+
+       if (poptPeekArg(pc)) { 
+               cmdline_auth_info.got_pass = True;
+               pstrcpy(cmdline_auth_info.password,poptGetArg(pc));  
+       }
+
+       //init_names();
+
+       if (!tar_type && !*query_host && !*service && !message) {
+               poptPrintUsage(pc, stderr, 0);
+               exit(1);
+       }
+
+       poptFreeContext(pc);
+
+       pstrcpy(username, cmdline_auth_info.username);
+       pstrcpy(password, cmdline_auth_info.password);
+       use_kerberos = cmdline_auth_info.use_kerberos;
+       got_pass = cmdline_auth_info.got_pass;
+
+       DEBUG( 3, ( "Client started (version %s).\n", SAMBA_VERSION ) );
+
+       talloc_destroy(mem_ctx);
+       if (tar_type) {
+               if (cmdstr)
+                       process_command_string(cmdstr);
+               return do_tar_op(base_directory);
+       }
+
+       if ((p=strchr_m(query_host,'#'))) {
+               *p = 0;
+               p++;
+               sscanf(p, "%x", &name_type);
+       }
+  
+       if (*query_host) {
+               return do_host_query(query_host);
+       }
+
+       if (message) {
+               return do_message_op();
+       }
+       
+       if (process(base_directory)) {
+               return 1;
+       }
+
+       return rc;
+}
diff --git a/source4/client/clitar.c b/source4/client/clitar.c
new file mode 100644 (file)
index 0000000..78f44da
--- /dev/null
@@ -0,0 +1,1856 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Tar Extensions
+   Copyright (C) Ricky Poulten 1995-1998
+   Copyright (C) Richard Sharpe 1998
+   
+   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.
+*/
+/* The following changes developed by Richard Sharpe for Canon Information
+   Systems Research Australia (CISRA)
+
+   1. Restore can now restore files with long file names
+   2. Save now saves directory information so that we can restore 
+      directory creation times
+   3. tar now accepts both UNIX path names and DOS path names. I prefer
+      those lovely /'s to those UGLY \'s :-)
+   4. the files to exclude can be specified as a regular expression by adding
+      an r flag to the other tar flags. Eg:
+
+         -TcrX file.tar "*.(obj|exe)"
+
+      will skip all .obj and .exe files
+*/
+
+
+#include "includes.h"
+#include "clitar.h"
+#include "../client/client_proto.h"
+
+static int clipfind(char **aret, int ret, char *tok);
+void dos_clean_name(char *s);
+
+typedef struct file_info_struct file_info2;
+
+struct file_info_struct
+{
+  SMB_BIG_UINT size;
+  uint16 mode;
+  uid_t uid;
+  gid_t gid;
+  /* These times are normally kept in GMT */
+  time_t mtime;
+  time_t atime;
+  time_t ctime;
+  char *name;     /* This is dynamically allocate */
+
+  file_info2 *next, *prev;  /* Used in the stack ... */
+
+};
+
+typedef struct
+{
+  file_info2 *top;
+  int items;
+
+} stack;
+
+#define SEPARATORS " \t\n\r"
+extern struct cli_state *cli;
+
+/* These defines are for the do_setrattr routine, to indicate
+ * setting and reseting of file attributes in the function call */
+#define ATTRSET 1
+#define ATTRRESET 0
+
+static uint16 attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+
+#ifndef CLIENT_TIMEOUT
+#define CLIENT_TIMEOUT (30*1000)
+#endif
+
+static char *tarbuf, *buffer_p;
+static int tp, ntarf, tbufsiz;
+static double ttarf;
+/* Incremental mode */
+static BOOL tar_inc=False;
+/* Reset archive bit */
+static BOOL tar_reset=False;
+/* Include / exclude mode (true=include, false=exclude) */
+static BOOL tar_excl=True;
+/* use regular expressions for search on file names */
+static BOOL tar_re_search=False;
+#ifdef HAVE_REGEX_H
+regex_t *preg;
+#endif
+/* Do not dump anything, just calculate sizes */
+static BOOL dry_run=False;
+/* Dump files with System attribute */
+static BOOL tar_system=True;
+/* Dump files with Hidden attribute */
+static BOOL tar_hidden=True;
+/* Be noisy - make a catalogue */
+static BOOL tar_noisy=True;
+static BOOL tar_real_noisy=False;  /* Don't want to be really noisy by default */
+
+char tar_type='\0';
+static char **cliplist=NULL;
+static int clipn=0;
+static BOOL must_free_cliplist = False;
+
+extern file_info def_finfo;
+extern BOOL lowercase;
+extern uint16 cnum;
+extern BOOL readbraw_supported;
+extern int max_xmit;
+extern pstring cur_dir;
+extern int get_total_time_ms;
+extern int get_total_size;
+
+static int blocksize=20;
+static int tarhandle;
+
+static void writetarheader(int f,  const char *aname, SMB_BIG_UINT size, time_t mtime,
+                          const char *amode, unsigned char ftype);
+static void do_atar(char *rname,char *lname,file_info *finfo1);
+static void do_tar(file_info *finfo);
+static void oct_it(SMB_BIG_UINT value, int ndgs, char *p);
+static void fixtarname(char *tptr, const char *fp, int l);
+static int dotarbuf(int f, char *b, int n);
+static void dozerobuf(int f, int n);
+static void dotareof(int f);
+static void initarbuf(void);
+
+/* restore functions */
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix);
+static long unoct(char *p, int ndgs);
+static void do_tarput(void);
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first);
+
+/*
+ * tar specific utitlities
+ */
+
+/*******************************************************************
+Create  a string of size size+1 (for the null)
+*******************************************************************/
+static char *string_create_s(int size)
+{
+  char *tmp;
+
+  tmp = (char *)malloc(size+1);
+
+  if (tmp == NULL) {
+
+    DEBUG(0, ("Out of memory in string_create_s\n"));
+
+  }
+
+  return(tmp);
+
+}
+
+/****************************************************************************
+Write a tar header to buffer
+****************************************************************************/
+static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
+                          const char *amode, unsigned char ftype)
+{
+  union hblock hb;
+  int i, chk, l;
+  char *jp;
+
+  DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
+
+  memset(hb.dummy, 0, sizeof(hb.dummy));
+  
+  l=strlen(aname);
+  if (l >= NAMSIZ - 1) {
+         /* write a GNU tar style long header */
+         char *b;
+         b = (char *)malloc(l+TBLOCK+100);
+         if (!b) {
+                 DEBUG(0,("out of memory\n"));
+                 exit(1);
+         }
+         writetarheader(f, "/./@LongLink", l+2, 0, "     0 \0", 'L');
+         memset(b, 0, l+TBLOCK+100);
+         fixtarname(b, aname, l);
+         i = strlen(b)+1;
+         DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b)));
+         dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
+         SAFE_FREE(b);
+  }
+
+  /* use l + 1 to do the null too */
+  fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1);
+
+  if (lowercase)
+    strlower(hb.dbuf.name);
+
+  /* write out a "standard" tar format header */
+
+  hb.dbuf.name[NAMSIZ-1]='\0';
+  safe_strcpy(hb.dbuf.mode, amode, strlen(amode));
+  oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.uid);
+  oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.gid);
+  oct_it((SMB_BIG_UINT) size, 13, hb.dbuf.size);
+  oct_it((SMB_BIG_UINT) mtime, 13, hb.dbuf.mtime);
+  memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
+  memset(hb.dbuf.linkname, 0, NAMSIZ);
+  hb.dbuf.linkflag=ftype;
+  
+  for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
+
+  oct_it((SMB_BIG_UINT) chk, 8, hb.dbuf.chksum);
+  hb.dbuf.chksum[6] = '\0';
+
+  (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
+}
+
+/****************************************************************************
+Read a tar header into a hblock structure, and validate
+***************************************************************************/
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
+{
+  long chk, fchk;
+  int i;
+  char *jp;
+
+  /*
+   * read in a "standard" tar format header - we're not that interested
+   * in that many fields, though
+   */
+
+  /* check the checksum */
+  for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) chk+=(0xFF & *jp++);
+
+  if (chk == 0)
+    return chk;
+
+  /* compensate for blanks in chksum header */
+  for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
+    chk-=(0xFF & *jp++);
+
+  chk += ' ' * sizeof(hb->dbuf.chksum);
+
+  fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
+
+  DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
+           chk, fchk, hb->dbuf.chksum));
+
+  if (fchk != chk)
+    {
+      DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
+      dump_data(5, (char *)hb - TBLOCK, TBLOCK *3);
+      return -1;
+    }
+
+  if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
+
+    DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
+    return(-1);
+
+  }
+
+  safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
+
+  /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
+  unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
+              strlen(hb->dbuf.name) + 1, True);
+
+  /* can't handle some links at present */
+  if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
+    if (hb->dbuf.linkflag == 0) {
+      DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
+               finfo->name));
+    } else { 
+      if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
+         /* Do nothing here at the moment. do_tarput will handle this
+            as long as the longlink gets back to it, as it has to advance 
+            the buffer pointer, etc */
+
+      } else {
+        DEBUG(0, ("this tar file appears to contain some kind of link other than a GNUtar Longlink - ignoring\n"));
+        return -2;
+      }
+    }
+  }
+    
+  if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR)
+    || (*(finfo->name+strlen(finfo->name)-1) == '\\'))
+    {
+      finfo->mode=FILE_ATTRIBUTE_DIRECTORY;
+    }
+  else
+    finfo->mode=0; /* we don't care about mode at the moment, we'll
+                   * just make it a regular file */
+  /*
+   * Bug fix by richard@sj.co.uk
+   *
+   * REC: restore times correctly (as does tar)
+   * We only get the modification time of the file; set the creation time
+   * from the mod. time, and the access time to current time
+   */
+  finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
+  finfo->atime = time(NULL);
+  finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
+
+  return True;
+}
+
+/****************************************************************************
+Write out the tar buffer to tape or wherever
+****************************************************************************/
+static int dotarbuf(int f, char *b, int n)
+{
+  int fail=1, writ=n;
+
+  if (dry_run) {
+    return writ;
+  }
+  /* This routine and the next one should be the only ones that do write()s */
+  if (tp + n >= tbufsiz)
+    {
+      int diff;
+
+      diff=tbufsiz-tp;
+      memcpy(tarbuf + tp, b, diff);
+      fail=fail && (1+write(f, tarbuf, tbufsiz));
+      n-=diff;
+      b+=diff;
+      tp=0;
+
+      while (n >= tbufsiz)
+       {
+         fail=fail && (1 + write(f, b, tbufsiz));
+         n-=tbufsiz;
+         b+=tbufsiz;
+       }
+    }
+  if (n>0) {
+    memcpy(tarbuf+tp, b, n);
+    tp+=n;
+  }
+
+  return(fail ? writ : 0);
+}
+
+/****************************************************************************
+Write zeros to buffer / tape
+****************************************************************************/
+static void dozerobuf(int f, int n)
+{
+  /* short routine just to write out n zeros to buffer -
+   * used to round files to nearest block
+   * and to do tar EOFs */
+
+  if (dry_run)
+    return;
+  
+  if (n+tp >= tbufsiz)
+    {
+      memset(tarbuf+tp, 0, tbufsiz-tp);
+
+      write(f, tarbuf, tbufsiz);
+      memset(tarbuf, 0, (tp+=n-tbufsiz));
+    }
+  else
+    {
+      memset(tarbuf+tp, 0, n);
+      tp+=n;
+    }
+}
+
+/****************************************************************************
+Malloc tape buffer
+****************************************************************************/
+static void initarbuf(void)
+{
+  /* initialize tar buffer */
+  tbufsiz=blocksize*TBLOCK;
+  tarbuf=malloc(tbufsiz);      /* FIXME: We might not get the buffer */
+
+  /* reset tar buffer pointer and tar file counter and total dumped */
+  tp=0; ntarf=0; ttarf=0;
+}
+
+/****************************************************************************
+Write two zero blocks at end of file
+****************************************************************************/
+static void dotareof(int f)
+{
+  SMB_STRUCT_STAT stbuf;
+  /* Two zero blocks at end of file, write out full buffer */
+
+  if (dry_run)
+    return;
+
+  (void) dozerobuf(f, TBLOCK);
+  (void) dozerobuf(f, TBLOCK);
+
+  if (sys_fstat(f, &stbuf) == -1)
+    {
+      DEBUG(0, ("Couldn't stat file handle\n"));
+      return;
+    }
+
+  /* Could be a pipe, in which case S_ISREG should fail,
+   * and we should write out at full size */
+  if (tp > 0) write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
+}
+
+/****************************************************************************
+(Un)mangle DOS pathname, make nonabsolute
+****************************************************************************/
+static void fixtarname(char *tptr, const char *fp, int l)
+{
+       /* add a '.' to start of file name, convert from ugly dos \'s in path
+        * to lovely unix /'s :-} */
+       *tptr++='.';
+
+       safe_strcpy(tptr, fp, l);
+       string_replace(tptr, '\\', '/');
+}
+
+/****************************************************************************
+Convert from decimal to octal string
+****************************************************************************/
+static void oct_it (SMB_BIG_UINT value, int ndgs, char *p)
+{
+  /* Converts long to octal string, pads with leading zeros */
+
+  /* skip final null, but do final space */
+  --ndgs;
+  p[--ndgs] = ' ';
+  /* Loop does at least one digit */
+  do {
+      p[--ndgs] = '0' + (char) (value & 7);
+      value >>= 3;
+    }
+  while (ndgs > 0 && value != 0);
+  /* Do leading zeros */
+  while (ndgs > 0)
+    p[--ndgs] = '0';
+}
+
+/****************************************************************************
+Convert from octal string to long
+***************************************************************************/
+static long unoct(char *p, int ndgs)
+{
+  long value=0;
+  /* Converts octal string to long, ignoring any non-digit */
+
+  while (--ndgs)
+    {
+      if (isdigit((int)*p))
+        value = (value << 3) | (long) (*p - '0');
+
+      p++;
+    }
+
+  return value;
+}
+
+/****************************************************************************
+Compare two strings in a slash insensitive way, allowing s1 to match s2 
+if s1 is an "initial" string (up to directory marker).  Thus, if s2 is 
+a file in any subdirectory of s1, declare a match.
+***************************************************************************/
+static int strslashcmp(char *s1, char *s2)
+{
+  char *s1_0=s1;
+
+  while(*s1 && *s2 &&
+       (*s1 == *s2
+        || tolower(*s1) == tolower(*s2)
+        || (*s1 == '\\' && *s2=='/')
+        || (*s1 == '/' && *s2=='\\'))) {
+         s1++; s2++;
+  }
+
+  /* if s1 has a trailing slash, it compared equal, so s1 is an "initial" 
+     string of s2.
+   */
+  if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\')) return 0;
+
+  /* ignore trailing slash on s1 */
+  if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1)) return 0;
+
+  /* check for s1 is an "initial" string of s2 */
+  if ((*s2 == '/' || *s2 == '\\') && !*s1) return 0;
+
+  return *s1-*s2;
+}
+
+
+/****************************************************************************
+Ensure a remote path exists (make if necessary)
+***************************************************************************/
+static BOOL ensurepath(char *fname)
+{
+  /* *must* be called with buffer ready malloc'ed */
+  /* ensures path exists */
+
+  char *partpath, *ffname;
+  char *p=fname, *basehack;
+
+  DEBUG(5, ( "Ensurepath called with: %s\n", fname));
+
+  partpath = string_create_s(strlen(fname));
+  ffname = string_create_s(strlen(fname));
+
+  if ((partpath == NULL) || (ffname == NULL)){
+
+    DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
+    return(False);
+
+  }
+
+  *partpath = 0;
+
+  /* fname copied to ffname so can strtok */
+
+  safe_strcpy(ffname, fname, strlen(fname));
+
+  /* do a `basename' on ffname, so don't try and make file name directory */
+  if ((basehack=strrchr_m(ffname, '\\')) == NULL)
+    return True;
+  else
+    *basehack='\0';
+
+  p=strtok(ffname, "\\");
+
+  while (p)
+    {
+      safe_strcat(partpath, p, strlen(fname) + 1);
+
+      if (!cli_chkpath(cli, partpath)) {
+       if (!cli_mkdir(cli, partpath))
+         {
+           DEBUG(0, ("Error mkdirhiering\n"));
+           return False;
+         }
+       else
+         DEBUG(3, ("mkdirhiering %s\n", partpath));
+
+      }
+
+      safe_strcat(partpath, "\\", strlen(fname) + 1);
+      p = strtok(NULL,"/\\");
+    }
+
+    return True;
+}
+
+static int padit(char *buf, int bufsize, int padsize)
+{
+       int berr= 0;
+       int bytestowrite;
+  
+       DEBUG(5, ("Padding with %d zeros\n", padsize));
+       memset(buf, 0, bufsize);
+       while( !berr && padsize > 0 ) {
+               bytestowrite= MIN(bufsize, padsize);
+               berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
+               padsize -= bytestowrite;
+       }
+  
+       return berr;
+}
+
+
+static void do_setrattr(char *name, uint16 attr, int set)
+{
+       uint16 oldattr;
+
+       if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
+
+       if (set == ATTRSET) {
+               attr |= oldattr;
+       } else {
+               attr = oldattr & ~attr;
+       }
+
+       if (!cli_setatr(cli, name, attr, 0)) {
+               DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
+       }
+}
+
+
+/****************************************************************************
+append one remote file to the tar file
+***************************************************************************/
+static void do_atar(char *rname,char *lname,file_info *finfo1)
+{
+  int fnum;
+  SMB_BIG_UINT nread=0;
+  char ftype;
+  file_info2 finfo;
+  BOOL close_done = False;
+  BOOL shallitime=True;
+  char data[65520];
+  int read_size = 65520;
+  int datalen=0;
+
+  struct timeval tp_start;
+  GetTimeOfDay(&tp_start);
+
+  ftype = '0'; /* An ordinary file ... */
+
+  if (finfo1) {
+    finfo.size  = finfo1 -> size;
+    finfo.mode  = finfo1 -> mode;
+    finfo.uid   = finfo1 -> uid;
+    finfo.gid   = finfo1 -> gid;
+    finfo.mtime = finfo1 -> mtime;
+    finfo.atime = finfo1 -> atime;
+    finfo.ctime = finfo1 -> ctime;
+    finfo.name  = finfo1 -> name;
+  }
+  else {
+    finfo.size  = def_finfo.size;
+    finfo.mode  = def_finfo.mode;
+    finfo.uid   = def_finfo.uid;
+    finfo.gid   = def_finfo.gid;
+    finfo.mtime = def_finfo.mtime;
+    finfo.atime = def_finfo.atime;
+    finfo.ctime = def_finfo.ctime;
+    finfo.name  = def_finfo.name;
+  }
+
+  if (dry_run)
+    {
+      DEBUG(3,("skipping file %s of size %12.0f bytes\n",
+              finfo.name,
+              (double)finfo.size));
+      shallitime=0;
+      ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+      ntarf++;
+      return;
+    }
+
+  fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+  dos_clean_name(rname);
+
+  if (fnum == -1) {
+         DEBUG(0,("%s opening remote file %s (%s)\n",
+                  cli_errstr(cli),rname, cur_dir));
+         return;
+  }
+
+  finfo.name = string_create_s(strlen(rname));
+  if (finfo.name == NULL) {
+         DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
+         return;
+  }
+
+  safe_strcpy(finfo.name,rname, strlen(rname));
+  if (!finfo1) {
+         size_t size;
+         if (!cli_getattrE(cli, fnum, &finfo.mode, &size, NULL, &finfo.atime, &finfo.mtime)) {
+                 DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
+                 return;
+         }
+         finfo.size = size;
+         finfo.ctime = finfo.mtime;
+  }
+
+  DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
+
+  if (tar_inc && !(finfo.mode & FILE_ATTRIBUTE_ARCHIVE))
+    {
+      DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
+      shallitime=0;
+    }
+  else if (!tar_system && (finfo.mode & FILE_ATTRIBUTE_SYSTEM))
+    {
+      DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
+      shallitime=0;
+    }
+  else if (!tar_hidden && (finfo.mode & FILE_ATTRIBUTE_HIDDEN))
+    {
+      DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
+      shallitime=0;
+    }
+  else
+    {
+      DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
+              finfo.name,
+              (double)finfo.size,
+              lname));
+      
+      /* write a tar header, don't bother with mode - just set to 100644 */
+      writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
+
+      while (nread < finfo.size && !close_done)        {
+             
+             DEBUG(3,("nread=%.0f\n",(double)nread));
+             
+             datalen = cli_read(cli, fnum, data, nread, read_size);
+             
+             if (datalen == -1) {
+                     DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
+                     break;
+             }
+             
+                 nread += datalen;
+
+                 /* if file size has increased since we made file size query, truncate
+                       read so tar header for this file will be correct.
+                  */
+
+                 if (nread > finfo.size) {
+                       datalen -= nread - finfo.size;
+                       DEBUG(0,("File size change - truncating %s to %.0f bytes\n", finfo.name, (double)finfo.size));
+                 }
+
+             /* add received bits of file to buffer - dotarbuf will
+              * write out in 512 byte intervals */
+             if (dotarbuf(tarhandle,data,datalen) != datalen) {
+                     DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
+                     break;
+             }
+             
+             if (datalen == 0) {
+                     DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
+                     break;
+             }
+
+             datalen=0;
+      }
+
+      /* pad tar file with zero's if we couldn't get entire file */
+      if (nread < finfo.size) {
+             DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n", (double)finfo.size, (int)nread));
+             if (padit(data, sizeof(data), finfo.size - nread))
+                     DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
+      }
+
+      /* round tar file to nearest block */
+      if (finfo.size % TBLOCK)
+       dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
+      
+      ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+      ntarf++;
+    }
+  
+  cli_close(cli, fnum);
+
+  if (shallitime)
+    {
+      struct timeval tp_end;
+      int this_time;
+
+      /* if shallitime is true then we didn't skip */
+      if (tar_reset && !dry_run)
+       (void) do_setrattr(finfo.name, FILE_ATTRIBUTE_ARCHIVE, ATTRRESET);
+      
+      GetTimeOfDay(&tp_end);
+      this_time = 
+       (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+         (tp_end.tv_usec - tp_start.tv_usec)/1000;
+      get_total_time_ms += this_time;
+      get_total_size += finfo.size;
+
+      if (tar_noisy)
+       {
+         DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
+              (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
+               finfo.name));
+       }
+
+      /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
+      DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
+              finfo.size / MAX(0.001, (1.024*this_time)),
+              get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
+    }
+}
+
+/****************************************************************************
+Append single file to tar file (or not)
+***************************************************************************/
+static void do_tar(file_info *finfo)
+{
+  pstring rname;
+
+  if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
+    return;
+
+  /* Is it on the exclude list ? */
+  if (!tar_excl && clipn) {
+    pstring exclaim;
+
+    DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
+
+    safe_strcpy(exclaim, cur_dir, sizeof(pstring));
+    *(exclaim+strlen(exclaim)-1)='\0';
+
+    safe_strcat(exclaim, "\\", sizeof(pstring));
+    safe_strcat(exclaim, finfo->name, sizeof(exclaim));
+
+    DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
+
+    if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
+#ifdef HAVE_REGEX_H
+       (tar_re_search && !regexec(preg, exclaim, 0, NULL, 0))) {
+#else
+        (tar_re_search && mask_match(cli, exclaim, cliplist[0], True))) {
+#endif
+      DEBUG(3,("Skipping file %s\n", exclaim));
+      return;
+    }
+  }
+
+  if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY)
+    {
+      pstring saved_curdir;
+      pstring mtar_mask;
+
+      safe_strcpy(saved_curdir, cur_dir, sizeof(saved_curdir));
+
+      DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n", (int)sizeof(cur_dir), (int)strlen(cur_dir), (int)strlen(finfo->name), finfo->name, cur_dir));
+
+      safe_strcat(cur_dir,finfo->name, sizeof(cur_dir));
+      safe_strcat(cur_dir,"\\", sizeof(cur_dir));
+
+      DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
+
+      /* write a tar directory, don't bother with mode - just set it to
+       * 40755 */
+      writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
+      if (tar_noisy) {
+          DEBUG(0,("                directory %s\n", cur_dir));
+      }
+      ntarf++;  /* Make sure we have a file on there */
+      safe_strcpy(mtar_mask,cur_dir, sizeof(pstring));
+      safe_strcat(mtar_mask,"*", sizeof(pstring));
+      DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
+      do_list(mtar_mask, attribute, do_tar, False, True);
+      safe_strcpy(cur_dir,saved_curdir, sizeof(pstring));
+    }
+  else
+    {
+      safe_strcpy(rname,cur_dir, sizeof(pstring));
+      safe_strcat(rname,finfo->name, sizeof(pstring));
+      do_atar(rname,finfo->name,finfo);
+    }
+}
+
+/****************************************************************************
+Convert from UNIX to DOS file names
+***************************************************************************/
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first)
+{
+       /* remove '.' from start of file name, convert from unix /'s to
+        * dos \'s in path. Kill any absolute path names. But only if first!
+        */
+
+       DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
+
+       if (first) {
+               if (*fp == '.') {
+                       fp++;
+                       l--;
+               }
+               if (*fp == '\\' || *fp == '/') {
+                       fp++;
+                       l--;
+               }
+       }
+
+       safe_strcpy(tptr, fp, l);
+       string_replace(tptr, '/', '\\');
+}
+
+
+/****************************************************************************
+Move to the next block in the buffer, which may mean read in another set of
+blocks. FIXME, we should allow more than one block to be skipped.
+****************************************************************************/
+static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
+{
+  int bufread, total = 0;
+
+  DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
+  *bufferp += TBLOCK;
+  total = TBLOCK;
+
+  if (*bufferp >= (ltarbuf + bufsiz)) {
+
+    DEBUG(5, ("Reading more data into ltarbuf ...\n"));
+
+    /*
+     * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
+     * Fixes bug where read can return short if coming from
+     * a pipe.
+     */
+
+    bufread = read(tarhandle, ltarbuf, bufsiz);
+    total = bufread;
+
+    while (total < bufsiz) {
+      if (bufread < 0) { /* An error, return false */
+        return (total > 0 ? -2 : bufread);
+      }
+      if (bufread == 0) {
+        if (total <= 0) {
+            return -2;
+        }
+        break;
+      }
+      bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
+      total += bufread;
+    }
+
+    DEBUG(5, ("Total bytes read ... %i\n", total));
+
+    *bufferp = ltarbuf;
+
+  }
+
+  return(total);
+
+}
+
+/* Skip a file, even if it includes a long file name? */
+static int skip_file(int skipsize)
+{
+  int dsize = skipsize;
+
+  DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
+
+  /* FIXME, we should skip more than one block at a time */
+
+  while (dsize > 0) {
+
+    if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+
+       DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+       return(False);
+
+    }
+
+    dsize -= TBLOCK;
+
+  }
+
+  return(True);
+}
+
+/*************************************************************
+ Get a file from the tar file and store it.
+ When this is called, tarbuf already contains the first
+ file block. This is a bit broken & needs fixing.
+**************************************************************/
+
+static int get_file(file_info2 finfo)
+{
+  int fnum = -1, pos = 0, dsize = 0, rsize = 0, bpos = 0;
+
+  DEBUG(5, ("get_file: file: %s, size %i\n", finfo.name, (int)finfo.size));
+
+  if (ensurepath(finfo.name) && 
+      (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
+      DEBUG(0, ("abandoning restore\n"));
+      return(False);
+  }
+
+  /* read the blocks from the tar file and write to the remote file */
+
+  rsize = finfo.size;  /* This is how much to write */
+
+  while (rsize > 0) {
+
+    /* We can only write up to the end of the buffer */
+
+    dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
+    dsize = MIN(dsize, rsize);  /* Should be only what is left */
+    DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
+
+    if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
+           DEBUG(0, ("Error writing remote file\n"));
+           return 0;
+    }
+
+    rsize -= dsize;
+    pos += dsize;
+
+    /* Now figure out how much to move in the buffer */
+
+    /* FIXME, we should skip more than one block at a time */
+
+    /* First, skip any initial part of the part written that is left over */
+    /* from the end of the first TBLOCK                                   */
+
+    if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
+
+      dsize -= (TBLOCK - bpos);  /* Get rid of the end of the first block */
+      bpos = 0;
+
+      if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {  /* and skip the block */
+       DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+       return False;
+
+      }
+
+    }
+
+    /*
+     * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
+     * If the file being extracted is an exact multiple of
+     * TBLOCK bytes then we don't want to extract the next
+     * block from the tarfile here, as it will be done in
+     * the caller of get_file().
+     */
+
+    while (((rsize != 0) && (dsize >= TBLOCK)) ||
+         ((rsize == 0) && (dsize > TBLOCK))) {
+
+      if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+       DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+       return False;
+      }
+
+      dsize -= TBLOCK;
+    }
+
+    bpos = dsize;
+
+  }
+
+  /* Now close the file ... */
+
+  if (!cli_close(cli, fnum)) {
+         DEBUG(0, ("Error closing remote file\n"));
+         return(False);
+  }
+
+  /* Now we update the creation date ... */
+
+  DEBUG(5, ("Updating creation date on %s\n", finfo.name));
+
+  if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime)) {
+         if (tar_real_noisy) {
+                 DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
+                 /*return(False); */ /* Ignore, as Win95 does not allow changes */
+         }
+  }
+
+  ntarf++;
+
+  DEBUG(0, ("restore tar file %s of size %d bytes\n", finfo.name, (int)finfo.size));
+  
+  return(True);
+}
+
+/* Create a directory.  We just ensure that the path exists and return as there
+   is no file associated with a directory 
+*/
+static int get_dir(file_info2 finfo)
+{
+
+  DEBUG(0, ("restore directory %s\n", finfo.name));
+
+  if (!ensurepath(finfo.name)) {
+
+    DEBUG(0, ("Problems creating directory\n"));
+    return(False);
+
+  }
+
+  ntarf++;
+  return(True);
+
+}
+/* Get a file with a long file name ... first file has file name, next file 
+   has the data. We only want the long file name, as the loop in do_tarput
+   will deal with the rest.
+*/
+static char * get_longfilename(file_info2 finfo)
+{
+  int namesize = finfo.size + strlen(cur_dir) + 2;
+  char *longname = malloc(namesize);
+  int offset = 0, left = finfo.size;
+  BOOL first = True;
+
+  DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
+  DEBUG(5, ("Len = %d\n", (int)finfo.size));
+
+  if (longname == NULL) {
+
+    DEBUG(0, ("could not allocate buffer of size %d for longname\n", 
+             (int)(finfo.size + strlen(cur_dir) + 2)));
+    return(NULL);
+  }
+
+  /* First, add cur_dir to the long file name */
+
+  if (strlen(cur_dir) > 0) {
+    strncpy(longname, cur_dir, namesize);
+    offset = strlen(cur_dir);
+  }
+
+  /* Loop through the blocks picking up the name */
+
+  while (left > 0) {
+
+    if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+
+      DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+      return(NULL);
+
+    }
+
+    unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
+    DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
+
+    offset += TBLOCK;
+    left -= TBLOCK;
+
+  }
+
+  return(longname);
+
+}
+
+static void do_tarput(void)
+{
+  file_info2 finfo;
+  struct timeval tp_start;
+  char *longfilename = NULL, linkflag;
+  int skip = False;
+
+  GetTimeOfDay(&tp_start);
+
+  DEBUG(5, ("RJS do_tarput called ...\n"));
+
+  buffer_p = tarbuf + tbufsiz;  /* init this to force first read */
+
+  /* Now read through those files ... */
+
+  while (True) {
+
+    /* Get us to the next block, or the first block first time around */
+
+    if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+
+      DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+
+      return;
+
+    }
+
+    DEBUG(5, ("Reading the next header ...\n"));
+
+    switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
+
+    case -2:    /* Hmm, not good, but not fatal */
+      DEBUG(0, ("Skipping %s...\n", finfo.name));
+      if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) &&
+          !skip_file(finfo.size)) {
+
+       DEBUG(0, ("Short file, bailing out...\n"));
+       return;
+
+      }
+
+      break;
+
+    case -1:
+      DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
+      return;
+
+    case 0: /* chksum is zero - looks like an EOF */
+      DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
+      return;        /* Hmmm, bad here ... */
+
+    default: 
+      /* No action */
+
+      break;
+
+    }
+
+    /* Now, do we have a long file name? */
+
+    if (longfilename != NULL) {
+
+      SAFE_FREE(finfo.name);   /* Free the space already allocated */
+      finfo.name = longfilename;
+      longfilename = NULL;
+
+    }
+
+    /* Well, now we have a header, process the file ...            */
+
+    /* Should we skip the file? We have the long name as well here */
+
+    skip = clipn &&
+      ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl)
+#ifdef HAVE_REGEX_H
+      || (tar_re_search && !regexec(preg, finfo.name, 0, NULL, 0)));
+#else
+      || (tar_re_search && mask_match(cli, finfo.name, cliplist[0], True)));
+#endif
+
+  DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
+
+  if (skip) {
+
+    skip_file(finfo.size);
+    continue;
+
+  }
+
+    /* We only get this far if we should process the file */
+  linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
+
+    switch (linkflag) {
+
+    case '0':  /* Should use symbolic names--FIXME */
+
+      /* 
+       * Skip to the next block first, so we can get the file, FIXME, should
+       * be in get_file ...
+       * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
+       * Fixes bug where file size in tarfile is zero.
+       */
+
+      if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+       DEBUG(0, ("Short file, bailing out...\n"));
+       return;
+      }
+      if (!get_file(finfo)) {
+       DEBUG(0, ("Abandoning restore\n"));
+       return;
+
+      }
+      break;
+
+    case '5':
+      if (!get_dir(finfo)) {
+       DEBUG(0, ("Abandoning restore \n"));
+       return;
+      }
+      break;
+
+    case 'L':
+      longfilename = get_longfilename(finfo);
+      if (!longfilename) {
+       DEBUG(0, ("abandoning restore\n"));
+       return;
+
+      }
+      DEBUG(5, ("Long file name: %s\n", longfilename));
+      break;
+
+    default:
+      skip_file(finfo.size);  /* Don't handle these yet */
+      break;
+
+    }
+
+  }
+
+
+}
+
+
+/*
+ * samba interactive commands
+ */
+
+/****************************************************************************
+Blocksize command
+***************************************************************************/
+int cmd_block(void)
+{
+  fstring buf;
+  int block;
+
+  if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
+    {
+      DEBUG(0, ("blocksize <n>\n"));
+      return 1;
+    }
+
+  block=atoi(buf);
+  if (block < 0 || block > 65535)
+    {
+      DEBUG(0, ("blocksize out of range"));
+      return 1;
+    }
+
+  blocksize=block;
+  DEBUG(2,("blocksize is now %d\n", blocksize));
+
+  return 0;
+}
+
+/****************************************************************************
+command to set incremental / reset mode
+***************************************************************************/
+int cmd_tarmode(void)
+{
+  fstring buf;
+
+  while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+    if (strequal(buf, "full"))
+      tar_inc=False;
+    else if (strequal(buf, "inc"))
+      tar_inc=True;
+    else if (strequal(buf, "reset"))
+      tar_reset=True;
+    else if (strequal(buf, "noreset"))
+      tar_reset=False;
+    else if (strequal(buf, "system"))
+      tar_system=True;
+    else if (strequal(buf, "nosystem"))
+      tar_system=False;
+    else if (strequal(buf, "hidden"))
+      tar_hidden=True;
+    else if (strequal(buf, "nohidden"))
+      tar_hidden=False;
+    else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
+      tar_noisy=True;
+    else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
+      tar_noisy=False;
+    else DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
+  }
+
+  DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
+           tar_inc ? "incremental" : "full",
+           tar_system ? "system" : "nosystem",
+           tar_hidden ? "hidden" : "nohidden",
+           tar_reset ? "reset" : "noreset",
+           tar_noisy ? "verbose" : "quiet"));
+
+  return 0;
+}
+
+/****************************************************************************
+Feeble attrib command
+***************************************************************************/
+int cmd_setmode(void)
+{
+  char *q;
+  fstring buf;
+  pstring fname;
+  uint16 attra[2];
+  int direct=1;
+
+  attra[0] = attra[1] = 0;
+
+  if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
+    {
+      DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+      return 1;
+    }
+
+  safe_strcpy(fname, cur_dir, sizeof(pstring));
+  safe_strcat(fname, buf, sizeof(pstring));
+
+  while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+    q=buf;
+
+    while(*q)
+      switch (*q++) {
+      case '+': direct=1;
+       break;
+      case '-': direct=0;
+       break;
+      case 'r': attra[direct]|=FILE_ATTRIBUTE_READONLY;
+       break;
+      case 'h': attra[direct]|=FILE_ATTRIBUTE_HIDDEN;
+       break;
+      case 's': attra[direct]|=FILE_ATTRIBUTE_SYSTEM;
+       break;
+      case 'a': attra[direct]|=FILE_ATTRIBUTE_ARCHIVE;
+       break;
+      default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+       return 1;
+      }
+  }
+
+  if (attra[ATTRSET]==0 && attra[ATTRRESET]==0)
+    {
+      DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+      return 1;
+    }
+
+  DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+  do_setrattr(fname, attra[ATTRSET], ATTRSET);
+  do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
+
+  return 0;
+}
+
+/****************************************************************************
+Principal command for creating / extracting
+***************************************************************************/
+int cmd_tar(void)
+{
+  fstring buf;
+  char **argl;
+  int argcl;
+
+  if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
+    {
+      DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
+      return 1;
+    }
+
+  argl=toktocliplist(&argcl, NULL);
+  if (!tar_parseargs(argcl, argl, buf, 0))
+    return 1;
+
+  process_tar();
+
+  SAFE_FREE(argl);
+
+  return 0;
+}
+
+/****************************************************************************
+Command line (option) version
+***************************************************************************/
+int process_tar(void)
+{
+  initarbuf();
+  switch(tar_type) {
+  case 'x':
+
+#if 0
+    do_tarput2();
+#else
+    do_tarput();
+#endif
+    SAFE_FREE(tarbuf);
+    close(tarhandle);
+    break;
+  case 'r':
+  case 'c':
+    if (clipn && tar_excl) {
+      int i;
+      pstring tarmac;
+
+      for (i=0; i<clipn; i++) {
+       DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
+
+       if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
+         *(cliplist[i]+strlen(cliplist[i])-1)='\0';
+       }
+       
+       if (strrchr_m(cliplist[i], '\\')) {
+         pstring saved_dir;
+         
+         safe_strcpy(saved_dir, cur_dir, sizeof(pstring));
+         
+         if (*cliplist[i]=='\\') {
+           safe_strcpy(tarmac, cliplist[i], sizeof(pstring));
+         } else {
+           safe_strcpy(tarmac, cur_dir, sizeof(pstring));
+           safe_strcat(tarmac, cliplist[i], sizeof(pstring));
+         }
+         safe_strcpy(cur_dir, tarmac, sizeof(pstring));
+         *(strrchr_m(cur_dir, '\\')+1)='\0';
+
+         DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+         do_list(tarmac,attribute,do_tar, False, True);
+         safe_strcpy(cur_dir,saved_dir, sizeof(pstring));
+       } else {
+         safe_strcpy(tarmac, cur_dir, sizeof(pstring));
+         safe_strcat(tarmac, cliplist[i], sizeof(pstring));
+         DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+         do_list(tarmac,attribute,do_tar, False, True);
+       }
+      }
+    } else {
+      pstring mask;
+      safe_strcpy(mask,cur_dir, sizeof(pstring));
+      DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
+      safe_strcat(mask,"\\*", sizeof(pstring));
+      do_list(mask,attribute,do_tar,False, True);
+    }
+    
+    if (ntarf) dotareof(tarhandle);
+    close(tarhandle);
+    SAFE_FREE(tarbuf);
+    
+    DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
+    DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
+    break;
+  }
+
+  if (must_free_cliplist) {
+    int i;
+    for (i = 0; i < clipn; ++i) {
+      SAFE_FREE(cliplist[i]);
+    }
+    SAFE_FREE(cliplist);
+    cliplist = NULL;
+    clipn = 0;
+    must_free_cliplist = False;
+  }
+
+  return(0);
+}
+
+/****************************************************************************
+Find a token (filename) in a clip list
+***************************************************************************/
+static int clipfind(char **aret, int ret, char *tok)
+{
+  if (aret==NULL) return 0;
+
+  /* ignore leading slashes or dots in token */
+  while(strchr_m("/\\.", *tok)) tok++;
+
+  while(ret--) {
+    char *pkey=*aret++;
+
+    /* ignore leading slashes or dots in list */
+    while(strchr_m("/\\.", *pkey)) pkey++;
+
+    if (!strslashcmp(pkey, tok)) return 1;
+  }
+
+  return 0;
+}
+
+/****************************************************************************
+Read list of files to include from the file and initialize cliplist
+accordingly.
+***************************************************************************/
+static int read_inclusion_file(char *filename)
+{
+  XFILE *inclusion = NULL;
+  char buf[MAXPATHLEN + 1];
+  char *inclusion_buffer = NULL;
+  int inclusion_buffer_size = 0;
+  int inclusion_buffer_sofar = 0;
+  char *p;
+  char *tmpstr;
+  int i;
+  int error = 0;
+
+  clipn = 0;
+  buf[MAXPATHLEN] = '\0'; /* guarantee null-termination */
+  if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
+    /* XXX It would be better to include a reason for failure, but without
+     * autoconf, it's hard to use strerror, sys_errlist, etc.
+     */
+    DEBUG(0,("Unable to open inclusion file %s\n", filename));
+    return 0;
+  }
+
+  while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
+    if (inclusion_buffer == NULL) {
+      inclusion_buffer_size = 1024;
+      if ((inclusion_buffer = malloc(inclusion_buffer_size)) == NULL) {
+       DEBUG(0,("failure allocating buffer to read inclusion file\n"));
+       error = 1;
+       break;
+      }
+    }
+    
+    if (buf[strlen(buf)-1] == '\n') {
+      buf[strlen(buf)-1] = '\0';
+    }
+    
+    if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
+      char *ib;
+      inclusion_buffer_size *= 2;
+      ib = Realloc(inclusion_buffer,inclusion_buffer_size);
+      if (! ib) {
+       DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
+                inclusion_buffer_size));
+       error = 1;
+       break;
+      }
+      else inclusion_buffer = ib;
+    }
+    
+    safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
+    inclusion_buffer_sofar += strlen(buf) + 1;
+    clipn++;
+  }
+  x_fclose(inclusion);
+
+  if (! error) {
+    /* Allocate an array of clipn + 1 char*'s for cliplist */
+    cliplist = malloc((clipn + 1) * sizeof(char *));
+    if (cliplist == NULL) {
+      DEBUG(0,("failure allocating memory for cliplist\n"));
+      error = 1;
+    } else {
+      cliplist[clipn] = NULL;
+      p = inclusion_buffer;
+      for (i = 0; (! error) && (i < clipn); i++) {
+       /* set current item to NULL so array will be null-terminated even if
+        * malloc fails below. */
+       cliplist[i] = NULL;
+       if ((tmpstr = (char *)malloc(strlen(p)+1)) == NULL) {
+         DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
+         error = 1;
+       } else {
+         unfixtarname(tmpstr, p, strlen(p) + 1, True);
+         cliplist[i] = tmpstr;
+         if ((p = strchr_m(p, '\000')) == NULL) {
+           DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
+           abort();
+         }
+       }
+       ++p;
+      }
+      must_free_cliplist = True;
+    }
+  }
+
+  SAFE_FREE(inclusion_buffer);
+  if (error) {
+    if (cliplist) {
+      char **pp;
+      /* We know cliplist is always null-terminated */
+      for (pp = cliplist; *pp; ++pp) {
+        SAFE_FREE(*pp);
+      }
+      SAFE_FREE(cliplist);
+      cliplist = NULL;
+      must_free_cliplist = False;
+    }
+    return 0;
+  }
+  
+  /* cliplist and its elements are freed at the end of process_tar. */
+  return 1;
+}
+
+/****************************************************************************
+Parse tar arguments. Sets tar_type, tar_excl, etc.
+***************************************************************************/
+int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
+{
+  char tar_clipfl='\0';
+
+  /* Reset back to defaults - could be from interactive version 
+   * reset mode and archive mode left as they are though
+   */
+  tar_type='\0';
+  tar_excl=True;
+  dry_run=False;
+
+  while (*Optarg) 
+    switch(*Optarg++) {
+    case 'c':
+      tar_type='c';
+      break;
+    case 'x':
+      if (tar_type=='c') {
+       printf("Tar must be followed by only one of c or x.\n");
+       return 0;
+      }
+      tar_type='x';
+      break;
+    case 'b':
+      if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
+       DEBUG(0,("Option b must be followed by valid blocksize\n"));
+       return 0;
+      } else {
+       Optind++;
+      }
+      break;
+    case 'g':
+      tar_inc=True;
+      break;
+    case 'N':
+      if (Optind>=argc) {
+       DEBUG(0,("Option N must be followed by valid file name\n"));
+       return 0;
+      } else {
+       SMB_STRUCT_STAT stbuf;
+       extern time_t newer_than;
+       
+       if (sys_stat(argv[Optind], &stbuf) == 0) {
+         newer_than = stbuf.st_mtime;
+         DEBUG(1,("Getting files newer than %s",
+                  asctime(LocalTime(&newer_than))));
+         Optind++;
+       } else {
+         DEBUG(0,("Error setting newer-than time\n"));
+         return 0;
+       }
+      }
+      break;
+    case 'a':
+      tar_reset=True;
+      break;
+    case 'q':
+      tar_noisy=False;
+      break;
+    case 'I':
+      if (tar_clipfl) {
+       DEBUG(0,("Only one of I,X,F must be specified\n"));
+       return 0;
+      }
+      tar_clipfl='I';
+      break;
+    case 'X':
+      if (tar_clipfl) {
+       DEBUG(0,("Only one of I,X,F must be specified\n"));
+       return 0;
+      }
+      tar_clipfl='X';
+      break;
+    case 'F':
+      if (tar_clipfl) {
+       DEBUG(0,("Only one of I,X,F must be specified\n"));
+       return 0;
+      }
+      tar_clipfl='F';
+      break;
+    case 'r':
+      DEBUG(0, ("tar_re_search set\n"));
+      tar_re_search = True;
+      break;
+    case 'n':
+      if (tar_type == 'c') {
+       DEBUG(0, ("dry_run set\n"));
+       dry_run = True;
+      } else {
+       DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
+       return 0;
+      }
+      break;
+    default:
+      DEBUG(0,("Unknown tar option\n"));
+      return 0;
+    }
+
+  if (!tar_type) {
+    printf("Option T must be followed by one of c or x.\n");
+    return 0;
+  }
+
+  /* tar_excl is true if cliplist lists files to be included.
+   * Both 'I' and 'F' mean include. */
+  tar_excl=tar_clipfl!='X';
+
+  if (tar_clipfl=='F') {
+    if (argc-Optind-1 != 1) {
+      DEBUG(0,("Option F must be followed by exactly one filename.\n"));
+      return 0;
+    }
+    if (! read_inclusion_file(argv[Optind+1])) {
+      return 0;
+    }
+  } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
+    char *tmpstr;
+    char **tmplist;
+    int clipcount;
+
+    cliplist=argv+Optind+1;
+    clipn=argc-Optind-1;
+    clipcount = clipn;
+
+    if ((tmplist=malloc(clipn*sizeof(char *))) == NULL) {
+      DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", 
+               clipn)
+           );
+      return 0;
+    }
+
+    for (clipcount = 0; clipcount < clipn; clipcount++) {
+
+      DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
+
+      if ((tmpstr = (char *)malloc(strlen(cliplist[clipcount])+1)) == NULL) {
+        DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n",
+                 clipcount)
+             );
+        return 0;
+      }
+      unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
+      tmplist[clipcount] = tmpstr;
+      DEBUG(5, ("Processed an item, %s\n", tmpstr));
+
+      DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
+    }
+    cliplist = tmplist;
+    must_free_cliplist = True;
+  }
+
+  if (Optind+1<argc && tar_re_search) {  /* Doing regular expression seaches */
+#ifdef HAVE_REGEX_H
+    int errcode;
+
+    if ((preg = (regex_t *)malloc(65536)) == NULL) {
+
+      DEBUG(0, ("Could not allocate buffer for regular expression search\n"));
+      return;
+
+    }
+
+    if (errcode = regcomp(preg, argv[Optind + 1], REG_EXTENDED)) {
+      char errstr[1024];
+      size_t errlen;
+
+      errlen = regerror(errcode, preg, errstr, sizeof(errstr) - 1);
+      
+      DEBUG(0, ("Could not compile pattern buffer for re search: %s\n%s\n", argv[Optind + 1], errstr));
+      return;
+
+    }
+#endif
+
+    clipn=argc-Optind-1;
+    cliplist=argv+Optind+1;
+
+  }
+
+  if (Optind>=argc || !strcmp(argv[Optind], "-")) {
+    /* Sets tar handle to either 0 or 1, as appropriate */
+    tarhandle=(tar_type=='c');
+    /*
+     * Make sure that dbf points to stderr if we are using stdout for 
+     * tar output
+    */
+    if (tarhandle == 1) 
+           setup_logging("clitar", DEBUG_STDERR);
+  } else {
+    if (tar_type=='c' && (dry_run || strcmp(argv[Optind], "/dev/null")==0))
+      {
+       if (!dry_run) {
+         DEBUG(0,("Output is /dev/null, assuming dry_run\n"));
+         dry_run = True;
+       }
+       tarhandle=-1;
+      } else
+    if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
+       || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0))
+      {
+       DEBUG(0,("Error opening local file %s - %s\n",
+                argv[Optind], strerror(errno)));
+       return(0);
+      }
+  }
+
+  return 1;
+}
diff --git a/source4/client/mount.cifs.c b/source4/client/mount.cifs.c
new file mode 100644 (file)
index 0000000..7167859
--- /dev/null
@@ -0,0 +1,557 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <mntent.h>
+
+#define MOUNT_CIFS_VERSION "1"
+
+extern char *getusername(void);
+
+char * thisprogram;
+int verboseflag = 0;
+static int got_password = 0;
+static int got_user = 0;
+static int got_domain = 0;
+static int got_ip = 0;
+static int got_unc = 0;
+static int got_uid = 0;
+static int got_gid = 0;
+static char * user_name = NULL;
+char * mountpassword = NULL;
+
+
+void mount_cifs_usage()
+{
+       printf("\nUsage:  %s remotetarget dir\n", thisprogram);
+       printf("\nMount the remotetarget, specified as either a UNC name or ");
+       printf(" CIFS URL, to the local directory, dir.\n");
+
+       exit(1);
+}
+
+/* caller frees username if necessary */
+char * getusername() {
+       char *username = NULL;
+       struct passwd *password = getpwuid(getuid());
+
+       if (password) {
+               username = password->pw_name;
+       }
+       return username;
+}
+
+char * parse_cifs_url(unc_name)
+{
+       printf("\ncifs url %s\n",unc_name);
+}
+
+int parse_options(char * options)
+{
+       char * data;
+       char * value = 0;
+
+       if (!options)
+               return 1;
+
+       while ((data = strsep(&options, ",")) != NULL) {
+               if (!*data)
+                       continue;
+               if ((value = strchr(data, '=')) != NULL) {
+                       *value++ = '\0';
+               }
+               if (strncmp(data, "user", 4) == 0) {
+                       if (!value || !*value) {
+                               printf("invalid or missing username\n");
+                               return 1;       /* needs_arg; */
+                       }
+                       if (strnlen(value, 260) < 260) {
+                               got_user=1;
+                               /* BB add check for format user%pass */
+                               /* if(strchr(username%passw) got_password = 1) */
+                       } else {
+                               printf("username too long\n");
+                               return 1;
+                       }
+       } else if (strncmp(data, "pass", 4) == 0) {
+               if (!value || !*value) {
+                       if(got_password) {
+                               printf("password specified twice, ignoring second\n");
+                       } else
+                               got_password = 1;
+               } else if (strnlen(value, 17) < 17) {
+                       got_password = 1;
+               } else {
+                       printf("password too long\n");
+                       return 1;
+               }
+       } else if (strncmp(data, "ip", 2) == 0) {
+               if (!value || !*value) {
+                       printf("target ip address argument missing");
+               } else if (strnlen(value, 35) < 35) {
+                       got_ip = 1;
+               } else {
+                       printf("ip address too long\n");
+                       return 1;
+               }
+       } else if ((strncmp(data, "unc", 3) == 0)
+                  || (strncmp(data, "target", 6) == 0)
+                  || (strncmp(data, "path", 4) == 0)) {
+               if (!value || !*value) {
+                       printf("invalid path to network resource\n");
+                       return 1;  /* needs_arg; */
+               } else if(strnlen(value,5) < 5) {
+                       printf("UNC name too short");
+               }
+
+               if (strnlen(value, 300) < 300) {
+                       got_unc = 1;
+                       if (strncmp(value, "//", 2) == 0) {
+                               if(got_unc)
+                                       printf("unc name specified twice, ignoring second\n");
+                               else
+                                       got_unc = 1;
+                       } else if (strncmp(value, "\\\\", 2) != 0) {                       
+                               printf("UNC Path does not begin with // or \\\\ \n");
+                               return 1;
+                       } else {
+                               if(got_unc)
+                                       printf("unc name specified twice, ignoring second\n");
+                               else
+                                       got_unc = 1;
+                       }
+               } else {
+                       printf("CIFS: UNC name too long\n");
+                       return 1;
+               }
+       } else if ((strncmp(data, "domain", 3) == 0)
+                  || (strncmp(data, "workgroup", 5) == 0)) {
+               if (!value || !*value) {
+                       printf("CIFS: invalid domain name\n");
+                       return 1;       /* needs_arg; */
+               }
+               if (strnlen(value, 65) < 65) {
+                       got_domain = 1;
+               } else {
+                       printf("domain name too long\n");
+                       return 1;
+               }
+       } else if (strncmp(data, "uid", 3) == 0) {
+               if (value && *value) {
+                       got_uid = 1;
+               }
+       } else if (strncmp(data, "gid", 3) == 0) {
+               if (value && *value) {
+                       got_gid = 1;
+               }
+       } /* else if (strnicmp(data, "file_mode", 4) == 0) {
+               if (value && *value) {
+                       vol->file_mode =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "dir_mode", 3) == 0) {
+               if (value && *value) {
+                       vol->dir_mode =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "port", 4) == 0) {
+               if (value && *value) {
+                       vol->port =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "rsize", 5) == 0) {
+               if (value && *value) {
+                       vol->rsize =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "wsize", 5) == 0) {
+               if (value && *value) {
+                       vol->wsize =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "version", 3) == 0) {
+               
+       } else if (strnicmp(data, "rw", 2) == 0) {
+               
+       } else
+               printf("CIFS: Unknown mount option %s\n",data); */
+       }
+       return 0;
+}
+
+/* Note that caller frees the returned buffer if necessary */
+char * parse_server(char * unc_name)
+{
+       int length = strnlen(unc_name,1024);
+       char * share;
+       char * ipaddress_string = NULL;
+       struct hostent * host_entry;
+       struct in_addr server_ipaddr;
+       int rc,j;
+       char temp[64];
+
+       if(length > 1023) {
+               printf("mount error: UNC name too long");
+               return 0;
+       }
+       if (strncasecmp("cifs://",unc_name,7) == 0)
+               return parse_cifs_url(unc_name+7);
+       if (strncasecmp("smb://",unc_name,6) == 0) {
+               return parse_cifs_url(unc_name+6);
+       }
+
+       if(length < 3) {
+               /* BB add code to find DFS root here */
+               printf("\nMounting the DFS root for domain not implemented yet");
+               return 0;
+       } else {
+               /* BB add support for \\\\ not just // */
+               if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
+                       printf("mount error: improperly formatted UNC name.");
+                       printf(" %s does not begin with \\\\ or //\n",unc_name);
+                       return 0;
+               } else {
+                       unc_name[0] = '\\';
+                       unc_name[1] = '\\';
+                       unc_name += 2;
+                       if ((share = strchr(unc_name, '/')) || 
+                               (share = strchr(unc_name,'\\'))) {
+                               *share = 0;  /* temporarily terminate the string */
+                               share += 1;
+                               host_entry = gethostbyname(unc_name);
+                               *(share - 1) = '\\'; /* put the slash back */
+/*                             rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
+                               if(host_entry == NULL) {
+                                       printf("mount error: could not find target server. TCP name %s not found ", unc_name);
+                                       printf(" rc = %d\n",rc);
+                                       return 0;
+                               }
+                               else {
+                                       /* BB should we pass an alternate version of the share name as Unicode */
+                                       /* BB what about ipv6? BB */
+                                       /* BB add retries with alternate servers in list */
+
+                                       memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
+
+                                       ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
+                                       if(ipaddress_string == NULL) {
+                                               printf("mount error: could not get valid ip address for target server\n");
+                                               return 0;
+                                       }
+                                       return ipaddress_string; 
+                               }
+                       } else {
+                               /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
+                               printf("Mounting the DFS root for a particular server not implemented yet\n");
+                               return 0;
+                       }
+               }
+       }
+}
+
+static struct option longopts[] = {
+       { "all", 0, 0, 'a' },
+       { "help", 0, 0, 'h' },
+       { "read-only", 0, 0, 'r' },
+       { "ro", 0, 0, 'r' },
+       { "verbose", 0, 0, 'v' },
+       { "version", 0, 0, 'V' },
+       { "read-write", 0, 0, 'w' },
+       { "rw", 0, 0, 'w' },
+       { "options", 1, 0, 'o' },
+       { "types", 1, 0, 't' },
+       { "replace", 0, 0, 129 },
+       { "after", 0, 0, 130 },
+       { "before", 0, 0, 131 },
+       { "over", 0, 0, 132 },
+       { "move", 0, 0, 133 },
+       { "rsize",1, 0, 136 },
+       { "wsize",1, 0, 137 },
+       { "uid", 1, 0, 138},
+       { "gid", 1, 0, 139},
+       { "uuid",1,0,'U' },
+       { "user",1,0,140},
+       { "username",1,0,140},
+       { "dom",1,0,141},
+       { "domain",1,0,141},
+       { "password",1,0,142},
+       { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char ** argv)
+{
+       int c;
+       int flags = MS_MANDLOCK | MS_MGC_VAL;
+       char * orgoptions = NULL;
+       char * share_name = NULL;
+       char * domain_name = NULL;
+       char * ipaddr = NULL;
+       char * uuid = NULL;
+       char * mountpoint;
+       char * options;
+       int rc,i;
+       int rsize = 0;
+       int wsize = 0;
+       int nomtab = 0;
+       int uid = 0;
+       int gid = 0;
+       int optlen = 0;
+       struct stat statbuf;
+       struct utsname sysinfo;
+       struct mntent mountent;
+       FILE * pmntfile;
+
+       /* setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE); */
+
+       if(argc && argv) {
+               thisprogram = argv[0];
+       }
+       if(thisprogram == NULL)
+               thisprogram = "mount.cifs";
+
+       uname(&sysinfo);
+       /* BB add workstation name and domain and pass down */
+/*#ifdef _GNU_SOURCE
+       printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
+#endif*/
+       if(argc < 3)
+               mount_cifs_usage();
+       share_name = argv[1];
+       mountpoint = argv[2];
+       /* add sharename in opts string as unc= parm */
+
+       while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
+                        longopts, NULL)) != -1) {
+               switch (c) {
+/*     case 'a':              
+               ++mount_all;
+               break;
+       case 'f':              
+               ++fake;
+               break;
+       case 'F':
+               ++optfork;
+               break; */
+               case 'h':        /* help */
+                       mount_cifs_usage ();
+                       break;
+/*     case 'i':
+               external_allowed = 0;
+               break;
+       case 'l':
+               list_with_volumelabel = 1;
+               break;
+       case 'L':
+               volumelabel = optarg;
+               break; */
+       case 'n':
+               ++nomtab;
+               break;
+       case 'o':
+               if (orgoptions) {
+                       orgoptions = strcat(orgoptions, ",");
+                       orgoptions = strcat(orgoptions,optarg);
+               } else
+                       orgoptions = strdup(optarg);
+               break;
+
+/*     case 'O':
+               if (test_opts)
+                       test_opts = xstrconcat3(test_opts, ",", optarg);
+               else
+                       test_opts = xstrdup(optarg);
+               break;*/
+               case 'r':  /* mount readonly */
+                       flags |= MS_RDONLY;;
+                       break;
+               case 'U':
+                       uuid = optarg;
+                       break;
+               case 'v':
+                       ++verboseflag;
+                       break;
+/*     case 'V':          
+               printf ("mount: %s\n", version);
+               exit (0);*/
+               case 'w':
+                       flags &= ~MS_RDONLY;;
+                       break;
+/*     case 0:
+               break;
+
+       case 128: 
+               mounttype = MS_BIND;
+               break;
+       case 129: 
+               mounttype = MS_REPLACE;
+               break;
+       case 130: 
+               mounttype = MS_AFTER;
+               break;
+       case 131: 
+               mounttype = MS_BEFORE;
+               break;
+       case 132: 
+               mounttype = MS_OVER;
+               break;
+       case 133: 
+               mounttype = MS_MOVE;
+               break;
+       case 135:
+               mounttype = (MS_BIND | MS_REC);
+               break; */
+               case 136:
+                       rsize = atoi(optarg) ;
+                       break;
+               case 137:
+                       wsize = atoi(optarg);
+                       break;
+               case 138:
+                       uid = atoi(optarg);
+                       break;
+               case 139:
+                       gid = atoi(optarg);
+                       break;
+               case 140:
+                       got_user = 1;
+                       user_name = optarg;
+                       break;
+               case 141:
+                       domain_name = optarg;
+                       break;
+               case 142:
+                       got_password = 1;
+                        mountpassword = optarg;
+                       break;
+               case '?':
+               default:
+                       mount_cifs_usage ();
+               }
+       }
+
+       /* canonicalize the path in argv[1]? */
+
+       if(stat (mountpoint, &statbuf)) {
+               printf("mount error: mount point %s does not exist\n",mountpoint);
+               return -1;
+       }
+       if (S_ISDIR(statbuf.st_mode) == 0) {
+               printf("mount error: mount point %s is not a directory\n",mountpoint);
+               return -1;
+       }
+
+       if(geteuid()) {
+               printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n"); 
+               return -1;
+       }
+
+       ipaddr = parse_server(share_name);
+/*     if(share_name == NULL)
+               return 1; */
+       if (parse_options(strdup(orgoptions)))
+               return 1;
+
+       if(got_user == 0)
+               user_name = getusername();
+       
+/*     check username for user%password format */
+
+       if(got_password == 0) {
+               if (getenv("PASSWD")) {
+                       mountpassword = malloc(33);
+                       if(mountpassword) {
+                               strncpy(mountpassword,getenv("PASSWD"),32);
+                               got_password = 1;
+                       }
+/*             } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+                       get_password_file();
+                       got_password = 1;*/ /* BB add missing function */
+               } else {
+                       mountpassword = getpass("Password: "); /* BB obsolete */
+                       got_password = 1;
+               }
+       }
+       /* FIXME launch daemon (handles dfs name resolution and credential change) 
+          remember to clear parms and overwrite password field before launching */
+       if(orgoptions) {
+               optlen = strlen(orgoptions);
+       } else
+               optlen = 0;
+       if(share_name)
+               optlen += strlen(share_name) + 4;
+       if(user_name)
+               optlen += strlen(user_name) + 6;
+       if(ipaddr)
+               optlen += strlen(ipaddr) + 4;
+       if(mountpassword)
+               optlen += strlen(mountpassword) + 6;
+       options = malloc(optlen + 10);
+
+    options[0] = 0;
+       strncat(options,"unc=",4);
+       strcat(options,share_name);
+       if(ipaddr) {
+               strncat(options,",ip=",4);
+               strcat(options,ipaddr);
+       } 
+       if(user_name) {
+               strncat(options,",user=",6);
+               strcat(options,user_name);
+       } 
+       if(mountpassword) {
+               strncat(options,",pass=",6);
+               strcat(options,mountpassword);
+       }
+       strncat(options,",ver=",5);
+       strcat(options,MOUNT_CIFS_VERSION);
+
+       if(orgoptions) {
+               strcat(options,",");
+               strcat(options,orgoptions);
+       }
+       /* printf("\noptions %s \n",options);*/
+       if(mount(share_name, mountpoint, "cifs", flags, options)) {
+       /* remember to kill daemon on error */
+               switch (errno) {
+               case 0:
+                       printf("mount failed but no error number set\n");
+                       return 0;
+               case ENODEV:
+                       printf("mount error: cifs filesystem not supported by the system\n");
+                       break;
+               default:
+                       printf("mount error %d = %s",errno,strerror(errno));
+               }
+               printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+               return -1;
+       } else {
+               pmntfile = setmntent(MOUNTED, "a+");
+               if(pmntfile) {
+                       mountent.mnt_fsname = share_name;
+                       mountent.mnt_dir = mountpoint; 
+                       mountent.mnt_type = "cifs"; 
+                       mountent.mnt_opts = "";
+                       mountent.mnt_freq = 0;
+                       mountent.mnt_passno = 0;
+                       rc = addmntent(pmntfile,&mountent);
+                       endmntent(pmntfile);
+               } else {
+                   printf("could not update mount table\n");
+               }
+       }
+       return 0;
+}
+
diff --git a/source4/client/smbmnt.c b/source4/client/smbmnt.c
new file mode 100644 (file)
index 0000000..ce40617
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ *  smbmnt.c
+ *
+ *  Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
+ *  extensively modified by Tridge
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <sys/utsname.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <asm/unistd.h>
+
+#ifndef        MS_MGC_VAL
+/* This may look strange but MS_MGC_VAL is what we are looking for and
+       is what we need from <linux/fs.h> under libc systems and is
+       provided in standard includes on glibc systems.  So...  We
+       switch on what we need...  */
+#include <linux/fs.h>
+#endif
+
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static unsigned mount_fmask;
+static unsigned mount_dmask;
+static int user_mount;
+static char *options;
+
+static void
+help(void)
+{
+        printf("\n");
+        printf("Usage: smbmnt mount-point [options]\n");
+       printf("Version %s\n\n",VERSION);
+        printf("-s share       share name on server\n"
+               "-r             mount read-only\n"
+               "-u uid         mount as uid\n"
+               "-g gid         mount as gid\n"
+               "-f mask        permission mask for files\n"
+               "-d mask        permission mask for directories\n"
+               "-o options     name=value, list of options\n"
+               "-h             print this help text\n");
+}
+
+static int
+parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
+{
+        int opt;
+
+        while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
+       {
+                switch (opt)
+               {
+                case 's':
+                        *share = optarg;
+                        break;
+                case 'u':
+                       if (!user_mount) {
+                               mount_uid = strtol(optarg, NULL, 0);
+                       }
+                        break;
+                case 'g':
+                       if (!user_mount) {
+                               mount_gid = strtol(optarg, NULL, 0);
+                       }
+                        break;
+                case 'r':
+                        mount_ro = 1;
+                        break;
+                case 'f':
+                        mount_fmask = strtol(optarg, NULL, 8);
+                        break;
+                case 'd':
+                        mount_dmask = strtol(optarg, NULL, 8);
+                        break;
+               case 'o':
+                       options = optarg;
+                       break;
+                default:
+                        return -1;
+                }
+        }
+        return 0;
+        
+}
+
+static char *
+fullpath(const char *p)
+{
+        char path[MAXPATHLEN];
+
+       if (strlen(p) > MAXPATHLEN-1) {
+               return NULL;
+       }
+
+        if (realpath(p, path) == NULL) {
+               fprintf(stderr,"Failed to find real path for mount point\n");
+               exit(1);
+       }
+       return strdup(path);
+}
+
+/* Check whether user is allowed to mount on the specified mount point. If it's
+   OK then we change into that directory - this prevents race conditions */
+static int mount_ok(char *mount_point)
+{
+       struct stat st;
+
+       if (chdir(mount_point) != 0) {
+               return -1;
+       }
+
+        if (stat(".", &st) != 0) {
+               return -1;
+        }
+
+        if (!S_ISDIR(st.st_mode)) {
+                errno = ENOTDIR;
+                return -1;
+        }
+
+        if ((getuid() != 0) && 
+           ((getuid() != st.st_uid) || 
+            ((st.st_mode & S_IRWXU) != S_IRWXU))) {
+                errno = EPERM;
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Tries to mount using the appropriate format. For 2.2 the struct,
+   for 2.4 the ascii version. */
+static int
+do_mount(char *share_name, unsigned int flags, struct smb_mount_data *data)
+{
+       pstring opts;
+       struct utsname uts;
+       char *release, *major, *minor;
+       char *data1, *data2;
+
+       uname(&uts);
+       release = uts.release;
+       major = strtok(release, ".");
+       minor = strtok(NULL, ".");
+       if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
+               /* < 2.4, assume struct */
+               data1 = (char *) data;
+               data2 = opts;
+       } else {
+               /* >= 2.4, assume ascii but fall back on struct */
+               data1 = opts;
+               data2 = (char *) data;
+       }
+
+       slprintf(opts, sizeof(opts)-1,
+                "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
+                data->uid, data->gid, data->file_mode, data->dir_mode,options);
+       if (mount(share_name, ".", "smbfs", flags, data1) == 0)
+               return 0;
+       return mount(share_name, ".", "smbfs", flags, data2);
+}
+
+ int main(int argc, char *argv[])
+{
+       char *mount_point, *share_name = NULL;
+       FILE *mtab;
+       int fd;
+       unsigned int flags;
+       struct smb_mount_data data;
+       struct mntent ment;
+
+       memset(&data, 0, sizeof(struct smb_mount_data));
+
+       if (argc < 2) {
+               help();
+               exit(1);
+       }
+
+       if (argv[1][0] == '-') {
+               help();
+               exit(1);
+       }
+
+       if (getuid() != 0) {
+               user_mount = 1;
+       }
+
+        if (geteuid() != 0) {
+                fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
+                exit(1);
+        }
+
+       mount_uid = getuid();
+       mount_gid = getgid();
+       mount_fmask = umask(0);
+        umask(mount_fmask);
+       mount_fmask = ~mount_fmask;
+
+        mount_point = fullpath(argv[1]);
+
+        argv += 1;
+        argc -= 1;
+
+        if (mount_ok(mount_point) != 0) {
+                fprintf(stderr, "cannot mount on %s: %s\n",
+                        mount_point, strerror(errno));
+                exit(1);
+        }
+
+       data.version = SMB_MOUNT_VERSION;
+
+        /* getuid() gives us the real uid, who may umount the fs */
+        data.mounted_uid = getuid();
+
+        if (parse_args(argc, argv, &data, &share_name) != 0) {
+                help();
+                return -1;
+        }
+
+        data.uid = mount_uid;
+        data.gid = mount_gid;
+        data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
+        data.dir_mode  = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
+
+        if (mount_dmask == 0) {
+                data.dir_mode = data.file_mode;
+                if ((data.dir_mode & S_IRUSR) != 0)
+                        data.dir_mode |= S_IXUSR;
+                if ((data.dir_mode & S_IRGRP) != 0)
+                        data.dir_mode |= S_IXGRP;
+                if ((data.dir_mode & S_IROTH) != 0)
+                        data.dir_mode |= S_IXOTH;
+        }
+
+       flags = MS_MGC_VAL;
+
+       if (mount_ro) flags |= MS_RDONLY;
+
+       if (do_mount(share_name, flags, &data) < 0) {
+               switch (errno) {
+               case ENODEV:
+                       fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
+                       break;
+               default:
+                       perror("mount error");
+               }
+               fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
+               return -1;
+       }
+
+        ment.mnt_fsname = share_name ? share_name : "none";
+        ment.mnt_dir = mount_point;
+        ment.mnt_type = "smbfs";
+        ment.mnt_opts = "";
+        ment.mnt_freq = 0;
+        ment.mnt_passno= 0;
+
+        mount_point = ment.mnt_dir;
+
+       if (mount_point == NULL)
+       {
+               fprintf(stderr, "Mount point too long\n");
+               return -1;
+       }
+       
+        if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+        {
+                fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+                return 1;
+        }
+        close(fd);
+       
+        if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
+        {
+                fprintf(stderr, "Can't open " MOUNTED);
+                return 1;
+        }
+
+        if (addmntent(mtab, &ment) == 1)
+        {
+                fprintf(stderr, "Can't write mount entry");
+                return 1;
+        }
+        if (fchmod(fileno(mtab), 0644) == -1)
+        {
+                fprintf(stderr, "Can't set perms on "MOUNTED);
+                return 1;
+        }
+        endmntent(mtab);
+
+        if (unlink(MOUNTED"~") == -1)
+        {
+                fprintf(stderr, "Can't remove "MOUNTED"~");
+                return 1;
+        }
+
+       return 0;
+}      
diff --git a/source4/client/smbmount.c b/source4/client/smbmount.c
new file mode 100644 (file)
index 0000000..da34014
--- /dev/null
@@ -0,0 +1,930 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMBFS mount program
+   Copyright (C) Andrew Tridgell 1999
+   
+   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 <mntent.h>
+#include <asm/types.h>
+#include <linux/smb_fs.h>
+
+extern BOOL in_client;
+
+static pstring credentials;
+static pstring my_netbios_name;
+static pstring password;
+static pstring username;
+static pstring workgroup;
+static pstring mpoint;
+static pstring service;
+static pstring options;
+
+static struct in_addr dest_ip;
+static BOOL have_ip;
+static int smb_port = 0;
+static BOOL got_user;
+static BOOL got_pass;
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static unsigned mount_fmask;
+static unsigned mount_dmask;
+static BOOL use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static BOOL status32_smbfs = False;
+
+static void usage(void);
+
+static void exit_parent(int sig)
+{
+       /* parent simply exits when child says go... */
+       exit(0);
+}
+
+static void daemonize(void)
+{
+       int j, status;
+       pid_t child_pid;
+
+       signal( SIGTERM, exit_parent );
+
+       if ((child_pid = sys_fork()) < 0) {
+               DEBUG(0,("could not fork\n"));
+       }
+
+       if (child_pid > 0) {
+               while( 1 ) {
+                       j = waitpid( child_pid, &status, 0 );
+                       if( j < 0 ) {
+                               if( EINTR == errno ) {
+                                       continue;
+                               }
+                               status = errno;
+                       }
+                       break;
+               }
+
+               /* If we get here - the child exited with some error status */
+               if (WIFSIGNALED(status))
+                       exit(128 + WTERMSIG(status));
+               else
+                       exit(WEXITSTATUS(status));
+       }
+
+       signal( SIGTERM, SIG_DFL );
+       chdir("/");
+}
+
+static void close_our_files(int client_fd)
+{
+       int i;
+       struct rlimit limits;
+
+       getrlimit(RLIMIT_NOFILE,&limits);
+       for (i = 0; i< limits.rlim_max; i++) {
+               if (i == client_fd)
+                       continue;
+               close(i);
+       }
+}
+
+static void usr1_handler(int x)
+{
+       return;
+}
+
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *do_connection(char *the_service)
+{
+       struct cli_state *c;
+       struct nmb_name called, calling;
+       char *server_n;
+       struct in_addr ip;
+       pstring server;
+       char *share;
+
+       if (the_service[0] != '\\' || the_service[1] != '\\') {
+               usage();
+               exit(1);
+       }
+
+       pstrcpy(server, the_service+2);
+       share = strchr_m(server,'\\');
+       if (!share) {
+               usage();
+               exit(1);
+       }
+       *share = 0;
+       share++;
+
+       server_n = server;
+
+       make_nmb_name(&calling, my_netbios_name, 0x0);
+       make_nmb_name(&called , server, 0x20);
+
+ again:
+        zero_ip(&ip);
+       if (have_ip) ip = dest_ip;
+
+       /* have to open a new connection */
+       if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
+           !cli_connect(c, server_n, &ip)) {
+               DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
+               if (c) {
+                       cli_shutdown(c);
+               }
+               return NULL;
+       }
+
+       /* SPNEGO doesn't work till we get NTSTATUS error support */
+       /* But it is REQUIRED for kerberos authentication */
+       if(!use_kerberos) c->use_spnego = False;
+
+       /* The kernel doesn't yet know how to sign it's packets */
+       c->sign_info.allow_smb_signing = False;
+
+       /* Use kerberos authentication if specified */
+       c->use_kerberos = use_kerberos;
+
+       if (!cli_session_request(c, &calling, &called)) {
+               char *p;
+               DEBUG(0,("%d: session request to %s failed (%s)\n", 
+                        sys_getpid(), called.name, cli_errstr(c)));
+               cli_shutdown(c);
+               if ((p=strchr_m(called.name, '.'))) {
+                       *p = 0;
+                       goto again;
+               }
+               if (strcmp(called.name, "*SMBSERVER")) {
+                       make_nmb_name(&called , "*SMBSERVER", 0x20);
+                       goto again;
+               }
+               return NULL;
+       }
+
+       DEBUG(4,("%d: session request ok\n", sys_getpid()));
+
+       if (!cli_negprot(c)) {
+               DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
+               cli_shutdown(c);
+               return NULL;
+       }
+
+       if (!got_pass) {
+               char *pass = getpass("Password: ");
+               if (pass) {
+                       pstrcpy(password, pass);
+               }
+       }
+
+       /* This should be right for current smbfs. Future versions will support
+         large files as well as unicode and oplocks. */
+       if (status32_smbfs) {
+           c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+                                 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+       }
+       else {
+           c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+                                CAP_NT_FIND | CAP_STATUS32 |
+                                CAP_LEVEL_II_OPLOCKS);
+           c->force_dos_errors = True;
+       }
+
+       if (!cli_session_setup(c, username, 
+                              password, strlen(password),
+                              password, strlen(password),
+                              workgroup)) {
+               /* if a password was not supplied then try again with a
+                       null username */
+               if (password[0] || !username[0] ||
+                               !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
+                       DEBUG(0,("%d: session setup failed: %s\n",
+                               sys_getpid(), cli_errstr(c)));
+                       cli_shutdown(c);
+                       return NULL;
+               }
+               DEBUG(0,("Anonymous login successful\n"));
+       }
+
+       DEBUG(4,("%d: session setup ok\n", sys_getpid()));
+
+       if (!cli_send_tconX(c, share, "?????",
+                           password, strlen(password)+1)) {
+               DEBUG(0,("%d: tree connect failed: %s\n",
+                        sys_getpid(), cli_errstr(c)));
+               cli_shutdown(c);
+               return NULL;
+       }
+
+       DEBUG(4,("%d: tconx ok\n", sys_getpid()));
+
+       got_pass = True;
+
+       return c;
+}
+
+
+/****************************************************************************
+unmount smbfs  (this is a bailout routine to clean up if a reconnect fails)
+       Code blatently stolen from smbumount.c
+               -mhw-
+****************************************************************************/
+static void smb_umount(char *mount_point)
+{
+       int fd;
+        struct mntent *mnt;
+        FILE* mtab;
+        FILE* new_mtab;
+
+       /* Programmers Note:
+               This routine only gets called to the scene of a disaster
+               to shoot the survivors...  A connection that was working
+               has now apparently failed.  We have an active mount point
+               (presumably) that we need to dump.  If we get errors along
+               the way - make some noise, but we are already turning out
+               the lights to exit anyways...
+       */
+        if (umount(mount_point) != 0) {
+                DEBUG(0,("%d: Could not umount %s: %s\n",
+                        sys_getpid(), mount_point, strerror(errno)));
+                return;
+        }
+
+        if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
+                DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
+                return;
+        }
+
+        close(fd);
+       
+        if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+                DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
+                        sys_getpid(), strerror(errno)));
+                return;
+        }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+        if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+                DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
+                        sys_getpid(), strerror(errno)));
+                endmntent(mtab);
+                return;
+        }
+
+        while ((mnt = getmntent(mtab)) != NULL) {
+                if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+                        addmntent(new_mtab, mnt);
+                }
+        }
+
+        endmntent(mtab);
+
+        if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+                DEBUG(0,("%d: Error changing mode of %s: %s\n",
+                        sys_getpid(), MOUNTED_TMP, strerror(errno)));
+                return;
+        }
+
+        endmntent(new_mtab);
+
+        if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+                DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
+                        sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
+                return;
+        }
+
+        if (unlink(MOUNTED"~") == -1) {
+                DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
+                return;
+        }
+}
+
+
+/*
+ * Call the smbfs ioctl to install a connection socket,
+ * then wait for a signal to reconnect. Note that we do
+ * not exit after open_sockets() or send_login() errors,
+ * as the smbfs mount would then have no way to recover.
+ */
+static void send_fs_socket(char *the_service, char *mount_point, struct cli_state *c)
+{
+       int fd, closed = 0, res = 1;
+       pid_t parentpid = getppid();
+       struct smb_conn_opt conn_options;
+
+       memset(&conn_options, 0, sizeof(conn_options));
+
+       while (1) {
+               if ((fd = open(mount_point, O_RDONLY)) < 0) {
+                       DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
+                                sys_getpid(), mount_point));
+                       break;
+               }
+
+               conn_options.fd = c->fd;
+               conn_options.protocol = c->protocol;
+               conn_options.case_handling = SMB_CASE_DEFAULT;
+               conn_options.max_xmit = c->max_xmit;
+               conn_options.server_uid = c->vuid;
+               conn_options.tid = c->cnum;
+               conn_options.secmode = c->sec_mode;
+               conn_options.rawmode = 0;
+               conn_options.sesskey = c->sesskey;
+               conn_options.maxraw = 0;
+               conn_options.capabilities = c->capabilities;
+               conn_options.serverzone = c->serverzone/60;
+
+               res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
+               if (res != 0) {
+                       DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
+                                sys_getpid(), res));
+                       close(fd);
+                       break;
+               }
+
+               if (parentpid) {
+                       /* Ok...  We are going to kill the parent.  Now
+                               is the time to break the process group... */
+                       setsid();
+                       /* Send a signal to the parent to terminate */
+                       kill(parentpid, SIGTERM);
+                       parentpid = 0;
+               }
+
+               close(fd);
+
+               /* This looks wierd but we are only closing the userspace
+                  side, the connection has already been passed to smbfs and 
+                  it has increased the usage count on the socket.
+
+                  If we don't do this we will "leak" sockets and memory on
+                  each reconnection we have to make. */
+               cli_shutdown(c);
+               c = NULL;
+
+               if (!closed) {
+                       /* redirect stdout & stderr since we can't know that
+                          the library functions we use are using DEBUG. */
+                       if ( (fd = open("/dev/null", O_WRONLY)) < 0)
+                               DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
+                       close_our_files(fd);
+                       if (fd >= 0) {
+                               dup2(fd, STDOUT_FILENO);
+                               dup2(fd, STDERR_FILENO);
+                               close(fd);
+                       }
+
+                       /* here we are no longer interactive */
+                       set_remote_machine_name("smbmount");    /* sneaky ... */
+                       setup_logging("mount.smbfs", False);
+                       reopen_logs();
+                       DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
+
+                       closed = 1;
+               }
+
+               /* Wait for a signal from smbfs ... but don't continue
+                   until we actually get a new connection. */
+               while (!c) {
+                       CatchSignal(SIGUSR1, &usr1_handler);
+                       pause();
+                       DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
+                       c = do_connection(the_service);
+               }
+       }
+
+       smb_umount(mount_point);
+       DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
+       exit(1);
+}
+
+
+/**
+ * Mount a smbfs
+ **/
+static void init_mount(void)
+{
+       char mount_point[MAXPATHLEN+1];
+       pstring tmp;
+       pstring svc2;
+       struct cli_state *c;
+       char *args[20];
+       int i, status;
+
+       if (realpath(mpoint, mount_point) == NULL) {
+               fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
+               return;
+       }
+
+
+       c = do_connection(service);
+       if (!c) {
+               fprintf(stderr,"SMB connection failed\n");
+               exit(1);
+       }
+
+       /*
+               Set up to return as a daemon child and wait in the parent
+               until the child say it's ready...
+       */
+       daemonize();
+
+       pstrcpy(svc2, service);
+       string_replace(svc2, '\\','/');
+       string_replace(svc2, ' ','_');
+
+       memset(args, 0, sizeof(args[0])*20);
+
+       i=0;
+       args[i++] = "smbmnt";
+
+       args[i++] = mount_point;
+       args[i++] = "-s";
+       args[i++] = svc2;
+
+       if (mount_ro) {
+               args[i++] = "-r";
+       }
+       if (mount_uid) {
+               slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
+               args[i++] = "-u";
+               args[i++] = smb_xstrdup(tmp);
+       }
+       if (mount_gid) {
+               slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
+               args[i++] = "-g";
+               args[i++] = smb_xstrdup(tmp);
+       }
+       if (mount_fmask) {
+               slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
+               args[i++] = "-f";
+               args[i++] = smb_xstrdup(tmp);
+       }
+       if (mount_dmask) {
+               slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
+               args[i++] = "-d";
+               args[i++] = smb_xstrdup(tmp);
+       }
+       if (options) {
+               args[i++] = "-o";
+               args[i++] = options;
+       }
+
+       if (sys_fork() == 0) {
+               char *smbmnt_path;
+
+               asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
+               
+               if (file_exist(smbmnt_path, NULL)) {
+                       execv(smbmnt_path, args);
+                       fprintf(stderr,
+                               "smbfs/init_mount: execv of %s failed. Error was %s.",
+                               smbmnt_path, strerror(errno));
+               } else {
+                       execvp("smbmnt", args);
+                       fprintf(stderr,
+                               "smbfs/init_mount: execv of %s failed. Error was %s.",
+                               "smbmnt", strerror(errno));
+               }
+               free(smbmnt_path);
+               exit(1);
+       }
+
+       if (waitpid(-1, &status, 0) == -1) {
+               fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
+               /* FIXME: do some proper error handling */
+               exit(1);
+       }
+
+       if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+               fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
+               /* FIXME: do some proper error handling */
+               exit(1);
+       } else if (WIFSIGNALED(status)) {
+               fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
+               exit(1);
+       }
+
+       /* Ok...  This is the rubicon for that mount point...  At any point
+          after this, if the connections fail and can not be reconstructed
+          for any reason, we will have to unmount the mount point.  There
+          is no exit from the next call...
+       */
+       send_fs_socket(service, mount_point, c);
+}
+
+
+/****************************************************************************
+get a password from a a file or file descriptor
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void get_password_file(void)
+{
+       int fd = -1;
+       char *p;
+       BOOL close_it = False;
+       pstring spec;
+       char pass[128];
+
+       if ((p = getenv("PASSWD_FD")) != NULL) {
+               pstrcpy(spec, "descriptor ");
+               pstrcat(spec, p);
+               sscanf(p, "%d", &fd);
+               close_it = False;
+       } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+               fd = sys_open(p, O_RDONLY, 0);
+               pstrcpy(spec, p);
+               if (fd < 0) {
+                       fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+                               spec, strerror(errno));
+                       exit(1);
+               }
+               close_it = True;
+       }
+
+       for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+           p && p - pass < sizeof(pass);) {
+               switch (read(fd, p, 1)) {
+               case 1:
+                       if (*p != '\n' && *p != '\0') {
+                               *++p = '\0'; /* advance p, and null-terminate pass */
+                               break;
+                       }
+               case 0:
+                       if (p - pass) {
+                               *p = '\0'; /* null-terminate it, just in case... */
+                               p = NULL; /* then force the loop condition to become false */
+                               break;
+                       } else {
+                               fprintf(stderr, "Error reading password from file %s: %s\n",
+                                       spec, "empty password\n");
+                               exit(1);
+                       }
+
+               default:
+                       fprintf(stderr, "Error reading password from file %s: %s\n",
+                               spec, strerror(errno));
+                       exit(1);
+               }
+       }
+       pstrcpy(password, pass);
+       if (close_it)
+               close(fd);
+}
+
+/****************************************************************************
+get username and password from a credentials file
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void read_credentials_file(char *filename)
+{
+       FILE *auth;
+       fstring buf;
+       uint16 len = 0;
+       char *ptr, *val, *param;
+
+       if ((auth=sys_fopen(filename, "r")) == NULL)
+       {
+               /* fail if we can't open the credentials file */
+               DEBUG(0,("ERROR: Unable to open credentials file!\n"));
+               exit (-1);
+       }
+
+       while (!feof(auth))
+       {
+               /* get a line from the file */
+               if (!fgets (buf, sizeof(buf), auth))
+                       continue;
+               len = strlen(buf);
+
+               if ((len) && (buf[len-1]=='\n'))
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }
+               if (len == 0)
+                       continue;
+
+               /* break up the line into parameter & value.
+                  will need to eat a little whitespace possibly */
+               param = buf;
+               if (!(ptr = strchr (buf, '=')))
+                       continue;
+               val = ptr+1;
+               *ptr = '\0';
+
+               /* eat leading white space */
+               while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+                       val++;
+
+               if (strwicmp("password", param) == 0)
+               {
+                       pstrcpy(password, val);
+                       got_pass = True;
+               }
+               else if (strwicmp("username", param) == 0) {
+                       pstrcpy(username, val);
+               }
+
+               memset(buf, 0, sizeof(buf));
+       }
+       fclose(auth);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+       printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
+
+       printf("Version %s\n\n",VERSION);
+
+       printf(
+"Options:\n\
+      username=<arg>                  SMB username\n\
+      password=<arg>                  SMB password\n\
+      credentials=<filename>          file with username/password\n\
+      krb                             use kerberos (active directory)\n\
+      netbiosname=<arg>               source NetBIOS name\n\
+      uid=<arg>                       mount uid or username\n\
+      gid=<arg>                       mount gid or groupname\n\
+      port=<arg>                      remote SMB port number\n\
+      fmask=<arg>                     file umask\n\
+      dmask=<arg>                     directory umask\n\
+      debug=<arg>                     debug level\n\
+      ip=<arg>                        destination host or IP address\n\
+      workgroup=<arg>                 workgroup on destination\n\
+      sockopt=<arg>                   TCP socket options\n\
+      scope=<arg>                     NetBIOS scope\n\
+      iocharset=<arg>                 Linux charset (iso8859-1, utf8)\n\
+      codepage=<arg>                  server codepage (cp850)\n\
+      ttl=<arg>                       dircache time to live\n\
+      guest                           don't prompt for a password\n\
+      ro                              mount read-only\n\
+      rw                              mount read-write\n\
+\n\
+This command is designed to be run from within /bin/mount by giving\n\
+the option '-t smbfs'. For example:\n\
+  mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
+");
+}
+
+
+/****************************************************************************
+  Argument parsing for mount.smbfs interface
+  mount will call us like this:
+    mount.smbfs device mountpoint -o <options>
+  
+  <options> is never empty, containing at least rw or ro
+ ****************************************************************************/
+static void parse_mount_smb(int argc, char **argv)
+{
+       int opt;
+       char *opts;
+       char *opteq;
+       extern char *optarg;
+       int val;
+       char *p;
+
+       /* FIXME: This function can silently fail if the arguments are
+        * not in the expected order.
+
+       > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
+       > requires that one gives "-o" before further options like username=...
+       > . Without -o, the username=.. setting is *silently* ignored. I've
+       > spent about an hour trying to find out why I couldn't log in now..
+
+       */
+
+
+       if (argc < 2 || argv[1][0] == '-') {
+               usage();
+               exit(1);
+       }
+       
+       pstrcpy(service, argv[1]);
+       pstrcpy(mpoint, argv[2]);
+
+       /* Convert any '/' characters in the service name to
+          '\' characters */
+       string_replace(service, '/','\\');
+       argc -= 2;
+       argv += 2;
+
+       opt = getopt(argc, argv, "o:");
+       if(opt != 'o') {
+               return;
+       }
+
+       options[0] = 0;
+       p = options;
+
+       /*
+        * option parsing from nfsmount.c (util-linux-2.9u)
+        */
+        for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
+               DEBUG(3, ("opts: %s\n", opts));
+                if ((opteq = strchr_m(opts, '='))) {
+                        val = atoi(opteq + 1);
+                        *opteq = '\0';
+
+                        if (!strcmp(opts, "username") || 
+                           !strcmp(opts, "logon")) {
+                               char *lp;
+                               got_user = True;
+                               pstrcpy(username,opteq+1);
+                               if ((lp=strchr_m(username,'%'))) {
+                                       *lp = 0;
+                                       pstrcpy(password,lp+1);
+                                       got_pass = True;
+                                       memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
+                               }
+                               if ((lp=strchr_m(username,'/'))) {
+                                       *lp = 0;
+                                       pstrcpy(workgroup,lp+1);
+                               }
+                       } else if(!strcmp(opts, "passwd") ||
+                                 !strcmp(opts, "password")) {
+                               pstrcpy(password,opteq+1);
+                               got_pass = True;
+                               memset(opteq+1,'X',strlen(password));
+                       } else if(!strcmp(opts, "credentials")) {
+                               pstrcpy(credentials,opteq+1);
+                       } else if(!strcmp(opts, "netbiosname")) {
+                               pstrcpy(my_netbios_name,opteq+1);
+                       } else if(!strcmp(opts, "uid")) {
+                               mount_uid = nametouid(opteq+1);
+                       } else if(!strcmp(opts, "gid")) {
+                               mount_gid = nametogid(opteq+1);
+                       } else if(!strcmp(opts, "port")) {
+                               smb_port = val;
+                       } else if(!strcmp(opts, "fmask")) {
+                               mount_fmask = strtol(opteq+1, NULL, 8);
+                       } else if(!strcmp(opts, "dmask")) {
+                               mount_dmask = strtol(opteq+1, NULL, 8);
+                       } else if(!strcmp(opts, "debug")) {
+                               DEBUGLEVEL = val;
+                       } else if(!strcmp(opts, "ip")) {
+                               dest_ip = *interpret_addr2(opteq+1);
+                               if (is_zero_ip(dest_ip)) {
+                                       fprintf(stderr,"Can't resolve address %s\n", opteq+1);
+                                       exit(1);
+                               }
+                               have_ip = True;
+                       } else if(!strcmp(opts, "workgroup")) {
+                               pstrcpy(workgroup,opteq+1);
+                       } else if(!strcmp(opts, "sockopt")) {
+                               lp_set_cmdline("socket options", opteq+1);
+                       } else if(!strcmp(opts, "scope")) {
+                               lp_set_cmdline("netbios scope", opteq+1);
+                       } else {
+                               slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
+                               p += strlen(p);
+                       }
+               } else {
+                       val = 1;
+                       if(!strcmp(opts, "nocaps")) {
+                               fprintf(stderr, "Unhandled option: %s\n", opteq+1);
+                               exit(1);
+                       } else if(!strcmp(opts, "guest")) {
+                               *password = '\0';
+                               got_pass = True;
+                       } else if(!strcmp(opts, "krb")) {
+#ifdef HAVE_KRB5
+
+                               use_kerberos = True;
+                               if(!status32_smbfs)
+                                       fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
+#else
+                               fprintf(stderr,"No kerberos support compiled in\n");
+                               exit(1);
+#endif
+                       } else if(!strcmp(opts, "rw")) {
+                               mount_ro = 0;
+                       } else if(!strcmp(opts, "ro")) {
+                               mount_ro = 1;
+                       } else {
+                               strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
+                               p += strlen(opts);
+                               *p++ = ',';
+                               *p = 0;
+                       }
+               }
+       }
+
+       if (!*service) {
+               usage();
+               exit(1);
+       }
+
+       if (p != options) {
+               *(p-1) = 0;     /* remove trailing , */
+               DEBUG(3,("passthrough options '%s'\n", options));
+       }
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       char *p;
+
+       DEBUGLEVEL = 1;
+
+       /* here we are interactive, even if run from autofs */
+       setup_logging("mount.smbfs",True);
+
+#if 0 /* JRA - Urban says not needed ? */
+       /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
+          is to not announce any unicode capabilities as current smbfs does
+          not support it. */
+       p = getenv("CLI_FORCE_ASCII");
+       if (p && !strcmp(p, "false"))
+               unsetenv("CLI_FORCE_ASCII");
+       else
+               setenv("CLI_FORCE_ASCII", "true", 1);
+#endif
+
+       in_client = True;   /* Make sure that we tell lp_load we are */
+
+       if (getenv("USER")) {
+               pstrcpy(username,getenv("USER"));
+
+               if ((p=strchr_m(username,'%'))) {
+                       *p = 0;
+                       pstrcpy(password,p+1);
+                       got_pass = True;
+                       memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
+               }
+               strupper(username);
+       }
+
+       if (getenv("PASSWD")) {
+               pstrcpy(password,getenv("PASSWD"));
+               got_pass = True;
+       }
+
+       if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+               get_password_file();
+               got_pass = True;
+       }
+
+       if (*username == 0 && getenv("LOGNAME")) {
+               pstrcpy(username,getenv("LOGNAME"));
+       }
+
+       if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
+                       dyn_CONFIGFILE);
+       }
+
+       parse_mount_smb(argc, argv);
+
+       if (use_kerberos && !got_user) {
+               got_pass = True;
+       }
+
+       if (*credentials != 0) {
+               read_credentials_file(credentials);
+       }
+
+       DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
+
+       if (*workgroup == 0) {
+               pstrcpy(workgroup,lp_workgroup());
+       }
+
+       load_interfaces();
+       if (!*my_netbios_name) {
+               pstrcpy(my_netbios_name, myhostname());
+       }
+       strupper(my_netbios_name);
+
+       init_mount();
+       return 0;
+}
diff --git a/source4/client/smbspool.c b/source4/client/smbspool.c
new file mode 100644 (file)
index 0000000..43046bb
--- /dev/null
@@ -0,0 +1,362 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB backend for the Common UNIX Printing System ("CUPS")
+   Copyright 1999 by Easy Software Products
+   Copyright Andrew Tridgell 1994-1998
+   Copyright Andrew Bartlett 2002
+   
+   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"
+
+/*
+ * Globals...
+ */
+
+extern BOOL            in_client;      /* Boolean for client library */
+
+
+/*
+ * Local functions...
+ */
+
+static void            list_devices(void);
+static struct cli_state        *smb_connect(const char *, const char *, const char *, const char *, const char *);
+static int             smb_print(struct cli_state *, char *, FILE *);
+
+
+/*
+ * 'main()' - Main entry for SMB backend.
+ */
+
+ int                           /* O - Exit status */
+ main(int  argc,                       /* I - Number of command-line arguments */
+     char *argv[])             /* I - Command-line arguments */
+{
+  int          i;              /* Looping var */
+  int          copies;         /* Number of copies */
+  char         uri[1024],      /* URI */
+               *sep,           /* Pointer to separator */
+               *password;      /* Password */
+  const char   *username,      /* Username */
+               *server,        /* Server name */
+               *printer;       /* Printer name */
+  const char   *workgroup;     /* Workgroup */
+  FILE         *fp;            /* File to print */
+  int          status=0;               /* Status of LPD job */
+  struct cli_state *cli;       /* SMB interface */
+
+  /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
+  if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
+         argv++;
+         argc--;
+  }
+
+  if (argc == 1)
+  {
+   /*
+    * NEW!  In CUPS 1.1 the backends are run with no arguments to list the
+    *       available devices.  These can be devices served by this backend
+    *       or any other backends (i.e. you can have an SNMP backend that
+    *       is only used to enumerate the available network printers... :)
+    */
+
+    list_devices();
+    return (0);
+  }
+
+  if (argc < 6 || argc > 7)
+  {
+    fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
+            argv[0]);
+    fputs("       The DEVICE_URI environment variable can also contain the\n", stderr);
+    fputs("       destination printer:\n", stderr);
+    fputs("\n", stderr);
+    fputs("           smb://[username:password@][workgroup/]server/printer\n", stderr);
+    return (1);
+  }
+
+ /*
+  * If we have 7 arguments, print the file named on the command-line.
+  * Otherwise, print data from stdin...
+  */
+
+  if (argc == 6)
+  {
+   /*
+    * Print from Copy stdin to a temporary file...
+    */
+
+    fp     = stdin;
+    copies = 1;
+  }
+  else if ((fp = fopen(argv[6], "rb")) == NULL)
+  {
+    perror("ERROR: Unable to open print file");
+    return (1);
+  }
+  else
+    copies = atoi(argv[4]);
+
+ /*
+  * Find the URI...
+  */
+
+  if (strncmp(argv[0], "smb://", 6) == 0)
+    strncpy(uri, argv[0], sizeof(uri) - 1);
+  else if (getenv("DEVICE_URI") != NULL)
+    strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1);
+  else
+  {
+    fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr);
+    return (1);
+  }
+
+  uri[sizeof(uri) - 1] = '\0';
+
+ /*
+  * Extract the destination from the URI...
+  */
+
+  if ((sep = strrchr_m(uri, '@')) != NULL)
+  {
+    username = uri + 6;
+    *sep++ = '\0';
+
+    server = sep;
+
+   /*
+    * Extract password as needed...
+    */
+
+    if ((password = strchr_m(username, ':')) != NULL)
+      *password++ = '\0';
+    else
+      password = "";
+  }
+  else
+  {
+    username = "";
+    password = "";
+    server   = uri + 6;
+  }
+
+  if ((sep = strchr_m(server, '/')) == NULL)
+  {
+    fputs("ERROR: Bad URI - need printer name!\n", stderr);
+    return (1);
+  }
+
+  *sep++ = '\0';
+  printer = sep;
+
+  if ((sep = strchr_m(printer, '/')) != NULL)
+  {
+   /*
+    * Convert to smb://[username:password@]workgroup/server/printer...
+    */
+
+    *sep++ = '\0';
+
+    workgroup = server;
+    server    = printer;
+    printer   = sep;
+  }
+  else
+    workgroup = NULL;
+
+ /*
+  * Setup the SAMBA server state...
+  */
+
+  setup_logging("smbspool", True);
+
+  in_client = True;   /* Make sure that we tell lp_load we are */
+
+  if (!lp_load(dyn_CONFIGFILE, True, False, False))
+  {
+    fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+    return (1);
+  }
+
+  if (workgroup == NULL)
+    workgroup = lp_workgroup();
+
+  load_interfaces();
+
+  do
+  {
+    if ((cli = smb_connect(workgroup, server, printer, username, password)) == NULL)
+    {
+      if (getenv("CLASS") == NULL)
+      {
+        fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds...");
+        sleep (60);
+      }
+      else
+      {
+        fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer...");
+        return (1);
+      }
+    }
+  }
+  while (cli == NULL);
+
+ /*
+  * Now that we are connected to the server, ignore SIGTERM so that we
+  * can finish out any page data the driver sends (e.g. to eject the
+  * current page...  Only ignore SIGTERM if we are printing data from
+  * stdin (otherwise you can't cancel raw jobs...)
+  */
+
+  if (argc < 7)
+    CatchSignal(SIGTERM, SIG_IGN);
+
+ /*
+  * Queue the job...
+  */
+
+  for (i = 0; i < copies; i ++)
+    if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
+      break;
+
+  cli_shutdown(cli);
+
+ /*
+  * Return the queue status...
+  */
+
+  return (status);
+}
+
+
+/*
+ * 'list_devices()' - List the available printers seen on the network...
+ */
+
+static void
+list_devices(void)
+{
+ /*
+  * Eventually, search the local workgroup for available hosts and printers.
+  */
+
+  puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
+}
+
+
+/*
+ * 'smb_connect()' - Return a connection to a server.
+ */
+
+static struct cli_state *              /* O - SMB connection */
+smb_connect(const char *workgroup,             /* I - Workgroup */
+            const char *server,                /* I - Server */
+            const char *share,         /* I - Printer */
+            const char *username,              /* I - Username */
+            const char *password)              /* I - Password */
+{
+  struct cli_state     *c;             /* New connection */
+  char *myname;                /* Client name */
+  NTSTATUS nt_status;
+
+ /*
+  * Get the names and addresses of the client and server...
+  */
+
+  myname = get_myname();  
+       
+  nt_status = cli_full_connection(&c, myname, server, NULL, 0, share, "?????", 
+                                 username, workgroup, password, 0, NULL);
+  
+  free(myname);
+  if (!NT_STATUS_IS_OK(nt_status)) {
+         fprintf(stderr, "ERROR:  Connection failed with error %s\n", nt_errstr(nt_status));
+         return NULL;
+  }
+
+  /*
+   * Return the new connection...
+   */
+  
+  return (c);
+}
+
+
+/*
+ * 'smb_print()' - Queue a job for printing using the SMB protocol.
+ */
+
+static int                             /* O - 0 = success, non-0 = failure */
+smb_print(struct cli_state *cli,       /* I - SMB connection */
+          char             *title,     /* I - Title/job name */
+          FILE             *fp)                /* I - File to print */
+{
+  int  fnum;           /* File number */
+  int  nbytes,         /* Number of bytes read */
+       tbytes;         /* Total bytes read */
+  char buffer[8192],   /* Buffer for copy */
+       *ptr;           /* Pointer into tile */
+
+
+ /*
+  * Sanitize the title...
+  */
+
+  for (ptr = title; *ptr; ptr ++)
+    if (!isalnum((int)*ptr) && !isspace((int)*ptr))
+      *ptr = '_';
+
+ /*
+  * Open the printer device...
+  */
+
+  if ((fnum = cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
+  {
+    fprintf(stderr, "ERROR: %s opening remote file %s\n",
+            cli_errstr(cli), title);
+    return (1);
+  }
+
+ /*
+  * Copy the file to the printer...
+  */
+
+  if (fp != stdin)
+    rewind(fp);
+
+  tbytes = 0;
+
+  while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+  {
+    if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
+    {
+      fprintf(stderr, "ERROR: Error writing file: %s\n", cli_errstr(cli));
+      break;
+    }
+
+    tbytes += nbytes;
+  } 
+
+  if (!cli_close(cli, fnum))
+  {
+    fprintf(stderr, "ERROR: %s closing remote file %s\n",
+            cli_errstr(cli), title);
+    return (1);
+  }
+  else
+    return (0);
+}
diff --git a/source4/client/smbumount.c b/source4/client/smbumount.c
new file mode 100644 (file)
index 0000000..9ea3083
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *  smbumount.c
+ *
+ *  Copyright (C) 1995-1998 by Volker Lendecke
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <linux/smb_fs.h>
+
+/* This is a (hopefully) temporary hack due to the fact that
+       sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc.
+       This may change in the future and smb.h may get fixed in the
+       future.  In the mean time, it's ugly hack time - get over it.
+*/
+#undef SMB_IOC_GETMOUNTUID
+#define        SMB_IOC_GETMOUNTUID             _IOR('u', 1, __kernel_uid_t)
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW     0400000
+#endif
+
+static void
+usage(void)
+{
+        printf("usage: smbumount mountpoint\n");
+}
+
+static int
+umount_ok(const char *mount_point)
+{
+       /* we set O_NOFOLLOW to prevent users playing games with symlinks to
+          umount filesystems they don't own */
+        int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0);
+        __kernel_uid_t mount_uid;
+       
+        if (fid == -1) {
+                fprintf(stderr, "Could not open %s: %s\n",
+                        mount_point, strerror(errno));
+                return -1;
+        }
+        
+        if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) {
+                fprintf(stderr, "%s probably not smb-filesystem\n",
+                        mount_point);
+                return -1;
+        }
+
+        if ((getuid() != 0)
+            && (mount_uid != getuid())) {
+                fprintf(stderr, "You are not allowed to umount %s\n",
+                        mount_point);
+                return -1;
+        }
+
+        close(fid);
+        return 0;
+}
+
+/* Make a canonical pathname from PATH.  Returns a freshly malloced string.
+   It is up the *caller* to ensure that the PATH is sensible.  i.e.
+   canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+   is not a legal pathname for ``/dev/fd0''  Anything we cannot parse
+   we return unmodified.   */
+static char *
+canonicalize (char *path)
+{
+       char *canonical = malloc (PATH_MAX + 1);
+
+       if (!canonical) {
+               fprintf(stderr, "Error! Not enough memory!\n");
+               return NULL;
+       }
+
+       if (strlen(path) > PATH_MAX) {
+               fprintf(stderr, "Mount point string too long\n");
+               return NULL;
+       }
+
+       if (path == NULL)
+               return NULL;
+  
+       if (realpath (path, canonical))
+               return canonical;
+
+       strncpy (canonical, path, PATH_MAX);
+       canonical[PATH_MAX] = '\0';
+       return canonical;
+}
+
+
+int 
+main(int argc, char *argv[])
+{
+        int fd;
+        char* mount_point;
+        struct mntent *mnt;
+        FILE* mtab;
+        FILE* new_mtab;
+
+        if (argc != 2) {
+                usage();
+                exit(1);
+        }
+
+        if (geteuid() != 0) {
+                fprintf(stderr, "smbumount must be installed suid root\n");
+                exit(1);
+        }
+
+        mount_point = canonicalize(argv[1]);
+
+       if (mount_point == NULL)
+       {
+               exit(1);
+       }
+
+        if (umount_ok(mount_point) != 0) {
+                exit(1);
+        }
+
+        if (umount(mount_point) != 0) {
+                fprintf(stderr, "Could not umount %s: %s\n",
+                        mount_point, strerror(errno));
+                exit(1);
+        }
+
+        if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+        {
+                fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+                return 1;
+        }
+        close(fd);
+       
+        if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+                fprintf(stderr, "Can't open " MOUNTED ": %s\n",
+                        strerror(errno));
+                return 1;
+        }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+        if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+                fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n",
+                        strerror(errno));
+                endmntent(mtab);
+                return 1;
+        }
+
+        while ((mnt = getmntent(mtab)) != NULL) {
+                if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+                        addmntent(new_mtab, mnt);
+                }
+        }
+
+        endmntent(mtab);
+
+        if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+                fprintf(stderr, "Error changing mode of %s: %s\n",
+                        MOUNTED_TMP, strerror(errno));
+                exit(1);
+        }
+
+        endmntent(new_mtab);
+
+        if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+                fprintf(stderr, "Cannot rename %s to %s: %s\n",
+                        MOUNTED, MOUNTED_TMP, strerror(errno));
+                exit(1);
+        }
+
+        if (unlink(MOUNTED"~") == -1)
+        {
+                fprintf(stderr, "Can't remove "MOUNTED"~");
+                return 1;
+        }
+
+       return 0;
+}      
diff --git a/source4/client/tree.c b/source4/client/tree.c
new file mode 100644 (file)
index 0000000..94fd93c
--- /dev/null
@@ -0,0 +1,811 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client GTK+ tree-based application
+   Copyright (C) Andrew Tridgell 1998
+   Copyright (C) Richard Sharpe 2001
+   Copyright (C) John Terpstra 2001
+   
+   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.
+*/
+
+/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
+
+#include <stdio.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "libsmbclient.h"
+
+static GtkWidget *clist;
+
+struct tree_data {
+
+  guint32 type;    /* Type of tree item, an SMBC_TYPE */
+  char name[256];  /* May need to change this later   */
+
+};
+
+void error_message(gchar *message) {
+
+  GtkWidget *dialog, *label, *okay_button;
+     
+  /* Create the widgets */
+     
+  dialog = gtk_dialog_new();
+  gtk_window_set_modal(GTK_WINDOW(dialog), True);
+  label = gtk_label_new (message);
+  okay_button = gtk_button_new_with_label("Okay");
+     
+  /* Ensure that the dialog box is destroyed when the user clicks ok. */
+     
+  gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
+                            GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
+                    okay_button);
+
+  /* Add the label, and show everything we've added to the dialog. */
+
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+                    label);
+  gtk_widget_show_all (dialog);
+}
+
+/*
+ * We are given a widget, and we want to retrieve its URL so we 
+ * can do a directory listing.
+ *
+ * We walk back up the tree, picking up pieces until we hit a server or
+ * workgroup type and return a path from there
+ */
+
+static char path_string[1024];
+
+char *get_path(GtkWidget *item)
+{
+  GtkWidget *p = item;
+  struct tree_data *pd;
+  char *comps[1024];  /* We keep pointers to the components here */
+  int i = 0, j, level,type;
+
+  /* Walk back up the tree, getting the private data */
+
+  level = GTK_TREE(item->parent)->level;
+
+  /* Pick up this item's component info */
+
+  pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item));
+
+  comps[i++] = pd->name;
+  type = pd->type;
+
+  while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) {
+
+    /* Find the parent and extract the data etc ... */
+
+    p = GTK_WIDGET(p->parent);    
+    p = GTK_WIDGET(GTK_TREE(p)->tree_owner);
+
+    pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p));
+
+    level = GTK_TREE(item->parent)->level;
+
+    comps[i++] = pd->name;
+    type = pd->type;
+
+  }
+
+  /* 
+   * Got a list of comps now, should check that we did not hit a workgroup
+   * when we got other things as well ... Later
+   *
+   * Now, build the path
+   */
+
+  snprintf(path_string, sizeof(path_string), "smb:/");
+
+  for (j = i - 1; j >= 0; j--) {
+
+    strncat(path_string, "/", sizeof(path_string) - strlen(path_string));
+    strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string));
+
+  }
+  
+  fprintf(stdout, "Path string = %s\n", path_string);
+
+  return path_string;
+
+}
+
+struct tree_data *make_tree_data(guint32 type, const char *name)
+{
+  struct tree_data *p = (struct tree_data *)malloc(sizeof(struct tree_data));
+
+  if (p) {
+
+    p->type = type;
+    strncpy(p->name, name, sizeof(p->name));
+
+  }
+
+  return p;
+
+}
+
+/* Note that this is called every time the user clicks on an item,
+   whether it is already selected or not. */
+static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
+                            GtkWidget *subtree)
+{
+  gint dh, err, dirlen;
+  char dirbuf[512];
+  struct smbc_dirent *dirp;
+  struct stat st1;
+  char path[1024], path1[1024];
+
+  g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+          root_tree, subtree, child);
+
+  /* Now, figure out what it is, and display it in the clist ... */
+
+  gtk_clist_clear(GTK_CLIST(clist));  /* Clear the CLIST */
+
+  /* Now, get the private data for the subtree */
+
+  strncpy(path, get_path(child), 1024);
+
+  if ((dh = smbc_opendir(path)) < 0) { /* Handle error */
+
+    g_print("cb_select_child: Could not open dir %s, %s\n", path,
+           strerror(errno));
+
+    gtk_main_quit();
+
+    return;
+
+  }
+
+  while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+                             sizeof(dirbuf))) != 0) {
+
+    if (err < 0) {
+
+      g_print("cb_select_child: Could not read dir %s, %s\n", path,
+             strerror(errno));
+
+      gtk_main_quit();
+
+      return;
+
+    }
+
+    dirp = (struct smbc_dirent *)dirbuf;
+
+    while (err > 0) {
+      gchar col1[128], col2[128], col3[128], col4[128];
+      gchar *rowdata[4] = {col1, col2, col3, col4};
+
+      dirlen = dirp->dirlen;
+
+      /* Format each of the items ... */
+
+      strncpy(col1, dirp->name, 128);
+
+      col2[0] = col3[0] = col4[0] = (char)0;
+
+      switch (dirp->smbc_type) {
+
+      case SMBC_WORKGROUP:
+
+       break;
+
+      case SMBC_SERVER:
+
+       strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+       break;
+
+      case SMBC_FILE_SHARE:
+
+       strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+       break;
+
+      case SMBC_PRINTER_SHARE:
+
+       strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+       break;
+
+      case SMBC_COMMS_SHARE:
+
+       break;
+
+      case SMBC_IPC_SHARE:
+
+       break;
+
+      case SMBC_DIR:
+      case SMBC_FILE:
+
+       /* Get stats on the file/dir and see what we have */
+
+       if ((strcmp(dirp->name, ".") != 0) &&
+           (strcmp(dirp->name, "..") != 0)) {
+
+         strncpy(path1, path, sizeof(path1));
+         strncat(path1, "/", sizeof(path) - strlen(path));
+         strncat(path1, dirp->name, sizeof(path) - strlen(path));
+
+         if (smbc_stat(path1, &st1) < 0) {
+           
+           if (errno != EBUSY) {
+             
+             g_print("cb_select_child: Could not stat file %s, %s\n", path1, 
+                     strerror(errno));
+           
+             gtk_main_quit();
+
+             return;
+
+           }
+           else {
+
+             strncpy(col2, "Device or resource busy", sizeof(col2));
+
+           }
+         }
+         else {
+           /* Now format each of the relevant things ... */
+
+           snprintf(col2, sizeof(col2), "%c%c%c%c%c%c%c%c%c(%0X)",
+                    (st1.st_mode&S_IRUSR?'r':'-'),
+                    (st1.st_mode&S_IWUSR?'w':'-'),
+                    (st1.st_mode&S_IXUSR?'x':'-'),
+                    (st1.st_mode&S_IRGRP?'r':'-'),
+                    (st1.st_mode&S_IWGRP?'w':'-'),
+                    (st1.st_mode&S_IXGRP?'x':'-'),
+                    (st1.st_mode&S_IROTH?'r':'-'),
+                    (st1.st_mode&S_IWOTH?'w':'-'),
+                    (st1.st_mode&S_IXOTH?'x':'-'),
+                    st1.st_mode); 
+           snprintf(col3, sizeof(col3), "%u", st1.st_size);
+           snprintf(col4, sizeof(col4), "%s", ctime(&st1.st_mtime));
+         }
+       }
+
+       break;
+
+      default:
+
+       break;
+      }
+
+      gtk_clist_append(GTK_CLIST(clist), rowdata);
+
+      (char *)dirp += dirlen;
+      err -= dirlen;
+
+    }
+
+  }
+
+}
+
+/* Note that this is never called */
+static void cb_unselect_child( GtkWidget *root_tree,
+                               GtkWidget *child,
+                               GtkWidget *subtree )
+{
+  g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+          root_tree, subtree, child);
+}
+
+/* for all the GtkItem:: and GtkTreeItem:: signals */
+static void cb_itemsignal( GtkWidget *item,
+                           gchar     *signame )
+{
+  GtkWidget *real_tree, *aitem, *subtree;
+  gchar *name;
+  GtkLabel *label;
+  gint dh, err, dirlen, level;
+  char dirbuf[512];
+  struct smbc_dirent *dirp;
+  
+  label = GTK_LABEL (GTK_BIN (item)->child);
+  /* Get the text of the label */
+  gtk_label_get (label, &name);
+
+  level = GTK_TREE(item->parent)->level;
+
+  /* Get the level of the tree which the item is in */
+  g_print ("%s called for item %s->%p, level %d\n", signame, name,
+          item, GTK_TREE (item->parent)->level);
+
+  real_tree = GTK_TREE_ITEM_SUBTREE(item);  /* Get the subtree */
+
+  if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+    char server[128];
+
+    if ((dh = smbc_opendir(get_path(item))) < 0) { /* Handle error */
+      gchar errmsg[256];
+
+      g_print("cb_itemsignal: Could not open dir %s, %s\n", get_path(item), 
+             strerror(errno));
+
+      slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not open dir %s, %s\n", get_path(item), strerror(errno));
+
+      error_message(errmsg);
+
+      /*      gtk_main_quit();*/
+
+      return;
+
+    }
+
+    while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf, 
+                               sizeof(dirbuf))) != 0) {
+
+      if (err < 0) { /* An error, report it */
+       gchar errmsg[256];
+
+       g_print("cb_itemsignal: Could not read dir smbc://, %s\n",
+               strerror(errno));
+
+       slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno));
+
+       error_message(errmsg);
+
+       /*      gtk_main_quit();*/
+
+       return;
+
+      }
+
+      dirp = (struct smbc_dirent *)dirbuf;
+
+      while (err > 0) {
+       struct tree_data *my_data;
+
+       dirlen = dirp->dirlen;
+
+       my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+       if (!my_data) {
+
+         g_print("Could not allocate space for tree_data: %s\n",
+                 dirp->name);
+
+         gtk_main_quit();
+         return;
+
+       }
+
+       aitem = gtk_tree_item_new_with_label(dirp->name);
+
+       /* Connect all GtkItem:: and GtkTreeItem:: signals */
+       gtk_signal_connect (GTK_OBJECT(aitem), "select",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+       gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+       gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+       gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+       gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+       /* Add it to the parent tree */
+       gtk_tree_append (GTK_TREE(real_tree), aitem);
+
+       gtk_widget_show (aitem);
+
+       gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+       fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+       if (dirp->smbc_type != SMBC_FILE &&
+           dirp->smbc_type != SMBC_IPC_SHARE &&
+           (strcmp(dirp->name, ".") != 0) && 
+           (strcmp(dirp->name, "..") !=0)){
+         
+         subtree = gtk_tree_new();
+         gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+         gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+                            GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+         gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+                            GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+       }
+
+       (char *)dirp += dirlen;
+       err -= dirlen;
+
+      }
+
+    }
+
+    smbc_closedir(dh);   
+
+  }
+  else if (strncmp(signame, "collapse", 8) == 0) {
+    GtkWidget *subtree = gtk_tree_new();
+
+    gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+    gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+    gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+                       GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+    gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+                       GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+  }
+
+}
+
+static void cb_selection_changed( GtkWidget *tree )
+{
+  GList *i;
+  
+  g_print ("selection_change called for tree %p\n", tree);
+  g_print ("selected objects are:\n");
+
+  i = GTK_TREE_SELECTION(tree);
+  while (i){
+    gchar *name;
+    GtkLabel *label;
+    GtkWidget *item;
+
+    /* Get a GtkWidget pointer from the list node */
+    item = GTK_WIDGET (i->data);
+    label = GTK_LABEL (GTK_BIN (item)->child);
+    gtk_label_get (label, &name);
+    g_print ("\t%s on level %d\n", name, GTK_TREE
+            (item->parent)->level);
+    i = i->next;
+  }
+}
+
+/*
+ * Expand or collapse the whole network ...
+ */
+static void cb_wholenet(GtkWidget *item, gchar *signame)
+{
+  GtkWidget *real_tree, *aitem, *subtree;
+  gchar *name;
+  GtkLabel *label;
+  gint dh, err, dirlen;
+  char dirbuf[512];
+  struct smbc_dirent *dirp;
+  
+  label = GTK_LABEL (GTK_BIN (item)->child);
+  gtk_label_get (label, &name);
+  g_print ("%s called for item %s->%p, level %d\n", signame, name,
+          item, GTK_TREE (item->parent)->level);
+
+  real_tree = GTK_TREE_ITEM_SUBTREE(item);  /* Get the subtree */
+
+  if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+
+    if ((dh = smbc_opendir("smb://")) < 0) { /* Handle error */
+
+      g_print("cb_wholenet: Could not open dir smbc://, %s\n",
+             strerror(errno));
+
+      gtk_main_quit();
+
+      return;
+
+    }
+
+    while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf, 
+                               sizeof(dirbuf))) != 0) {
+
+      if (err < 0) { /* An error, report it */
+
+       g_print("cb_wholenet: Could not read dir smbc://, %s\n",
+               strerror(errno));
+
+       gtk_main_quit();
+
+       return;
+
+      }
+
+      dirp = (struct smbc_dirent *)dirbuf;
+
+      while (err > 0) {
+       struct tree_data *my_data;
+
+       dirlen = dirp->dirlen;
+
+       my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+       aitem = gtk_tree_item_new_with_label(dirp->name);
+
+       /* Connect all GtkItem:: and GtkTreeItem:: signals */
+       gtk_signal_connect (GTK_OBJECT(aitem), "select",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+       gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+       gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+       gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+       gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+                           GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+
+       gtk_tree_append (GTK_TREE(real_tree), aitem);
+       /* Show it - this can be done at any time */
+       gtk_widget_show (aitem);
+
+       gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+       fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+       subtree = gtk_tree_new();
+
+       gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+       gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+                          GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+       gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+                          GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+       (char *)dirp += dirlen;
+       err -= dirlen;
+
+      }
+
+    }
+
+    smbc_closedir(dh);   
+
+  }
+  else { /* Must be collapse ... FIXME ... */
+    GtkWidget *subtree = gtk_tree_new();
+
+    gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+    gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+    gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+                       GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+    gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+                       GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+
+  }
+
+}
+
+/* Should put up a dialog box to ask the user for username and password */
+
+static void 
+auth_fn(const char *server, const char *share,
+       char *workgroup, int wgmaxlen, char *username, int unmaxlen,
+       char *password, int pwmaxlen)
+{
+
+   strncpy(username, "test", unmaxlen);
+   strncpy(password, "test", pwmaxlen);
+
+}
+
+static char *col_titles[] = {
+  "Name", "Attributes", "Size", "Modification Date",
+};
+
+int main( int   argc,
+          char *argv[] )
+{
+  GtkWidget *window, *scrolled_win, *scrolled_win2, *tree;
+  GtkWidget *subtree, *item, *main_hbox, *r_pane, *l_pane;
+  gint err, dh;
+  gint i;
+  char dirbuf[512];
+  struct smbc_dirent *dirp;
+
+  gtk_init (&argc, &argv);
+
+  /* Init the smbclient library */
+
+  err = smbc_init(auth_fn, 10);
+
+  /* Print an error response ... */
+
+  if (err < 0) {
+
+    fprintf(stderr, "smbc_init returned %s (%i)\nDo you have a ~/.smb/smb.conf file?\n", strerror(errno), errno);
+    exit(1);
+
+  }
+
+  /* a generic toplevel window */
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_name(window, "main browser window");
+  gtk_signal_connect (GTK_OBJECT(window), "delete_event",
+                     GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+  gtk_window_set_title(GTK_WINDOW(window), "The Linux Windows Network Browser");
+  gtk_widget_set_usize(GTK_WIDGET(window), 750, -1);
+  gtk_container_set_border_width (GTK_CONTAINER(window), 5);
+
+  gtk_widget_show (window);
+
+  /* A container for the two panes ... */
+
+  main_hbox = gtk_hbox_new(FALSE, 1);
+  gtk_container_border_width(GTK_CONTAINER(main_hbox), 1);
+  gtk_container_add(GTK_CONTAINER(window), main_hbox);
+
+  gtk_widget_show(main_hbox);
+
+  l_pane = gtk_hpaned_new();
+  gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
+  r_pane = gtk_hpaned_new();
+  gtk_paned_gutter_size(GTK_PANED(r_pane), (GTK_PANED(r_pane))->handle_size);
+  gtk_container_add(GTK_CONTAINER(main_hbox), l_pane);
+  gtk_widget_show(l_pane);
+
+  /* A generic scrolled window */
+  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+  gtk_widget_set_usize (scrolled_win, 150, 200);
+  gtk_container_add (GTK_CONTAINER(l_pane), scrolled_win);
+  gtk_widget_show (scrolled_win);
+
+  /* Another generic scrolled window */
+  scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2),
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+  gtk_widget_set_usize (scrolled_win2, 150, 200);
+  gtk_paned_add2 (GTK_PANED(l_pane), scrolled_win2);
+  gtk_widget_show (scrolled_win2);
+  
+  /* Create the root tree */
+  tree = gtk_tree_new();
+  g_print ("root tree is %p\n", tree);
+  /* connect all GtkTree:: signals */
+  gtk_signal_connect (GTK_OBJECT(tree), "select_child",
+                     GTK_SIGNAL_FUNC(cb_select_child), tree);
+  gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
+                     GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+  gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
+                     GTK_SIGNAL_FUNC(cb_selection_changed), tree);
+  /* Add it to the scrolled window */
+  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_win),
+                                         tree);
+  /* Set the selection mode */
+  gtk_tree_set_selection_mode (GTK_TREE(tree),
+                              GTK_SELECTION_MULTIPLE);
+  /* Show it */
+  gtk_widget_show (tree);
+
+  /* Now, create a clist and attach it to the second pane */
+
+  clist = gtk_clist_new_with_titles(4, col_titles);
+
+  gtk_container_add (GTK_CONTAINER(scrolled_win2), clist);
+
+  gtk_widget_show(clist);
+
+  /* Now, build the top level display ... */
+
+  if ((dh = smbc_opendir("smb:///")) < 0) {
+
+    fprintf(stderr, "Could not list default workgroup: smb:///: %s\n",
+           strerror(errno));
+
+    exit(1);
+
+  }
+
+  /* Create a tree item for Whole Network */
+
+  item = gtk_tree_item_new_with_label ("Whole Network");
+  /* Connect all GtkItem:: and GtkTreeItem:: signals */
+  gtk_signal_connect (GTK_OBJECT(item), "select",
+                     GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+  gtk_signal_connect (GTK_OBJECT(item), "deselect",
+                     GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+  gtk_signal_connect (GTK_OBJECT(item), "toggle",
+                     GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+  gtk_signal_connect (GTK_OBJECT(item), "expand",
+                     GTK_SIGNAL_FUNC(cb_wholenet), "expand");
+  gtk_signal_connect (GTK_OBJECT(item), "collapse",
+                     GTK_SIGNAL_FUNC(cb_wholenet), "collapse");
+  /* Add it to the parent tree */
+  gtk_tree_append (GTK_TREE(tree), item);
+  /* Show it - this can be done at any time */
+  gtk_widget_show (item);
+
+  subtree = gtk_tree_new();  /* A subtree for Whole Network */
+
+  gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+  gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+                     GTK_SIGNAL_FUNC(cb_select_child), tree);
+  gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+                     GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+  /* Now, get the items in smb:/// and add them to the tree */
+
+  dirp = (struct smbc_dirent *)dirbuf;
+
+  while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf, 
+                             sizeof(dirbuf))) != 0) {
+
+    if (err < 0) { /* Handle the error */
+
+      fprintf(stderr, "Could not read directory for smbc:///: %s\n",
+             strerror(errno));
+
+      exit(1);
+
+    }
+
+    fprintf(stdout, "Dir len: %u\n", err);
+
+    while (err > 0) { /* Extract each entry and make a sub-tree */
+      struct tree_data *my_data;
+      int dirlen = dirp->dirlen;
+
+      my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+      item = gtk_tree_item_new_with_label(dirp->name);
+      /* Connect all GtkItem:: and GtkTreeItem:: signals */
+      gtk_signal_connect (GTK_OBJECT(item), "select",
+                         GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+      gtk_signal_connect (GTK_OBJECT(item), "deselect",
+                         GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+      gtk_signal_connect (GTK_OBJECT(item), "toggle",
+                         GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+      gtk_signal_connect (GTK_OBJECT(item), "expand",
+                         GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+      gtk_signal_connect (GTK_OBJECT(item), "collapse",
+                         GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+      /* Add it to the parent tree */
+      gtk_tree_append (GTK_TREE(tree), item);
+      /* Show it - this can be done at any time */
+      gtk_widget_show (item);
+
+      gtk_object_set_user_data(GTK_OBJECT(item), (gpointer)my_data);
+
+      fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+      subtree = gtk_tree_new();
+
+      gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+      gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+                         GTK_SIGNAL_FUNC(cb_select_child), tree);
+      gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+                         GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+      (char *)dirp += dirlen;
+      err -= dirlen;
+
+    }
+
+  }
+
+  smbc_closedir(dh); /* FIXME, check for error :-) */
+
+  /* Show the window and loop endlessly */
+  gtk_main();
+  return 0;
+}
+/* example-end */
diff --git a/source4/codepages/.cvsignore b/source4/codepages/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/codepages/valid.dat b/source4/codepages/valid.dat
new file mode 100644 (file)
index 0000000..78c14b3
Binary files /dev/null and b/source4/codepages/valid.dat differ
diff --git a/source4/config.sub b/source4/config.sub
new file mode 100755 (executable)
index 0000000..04baf3d
--- /dev/null
@@ -0,0 +1,1473 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-01-03'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k \
+       | m32r | m68000 | m68k | m88k | mcore \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | msp430 \
+       | ns16k | ns32k \
+       | openrisc | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | sh | sh[1234] | sh3e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic80 | tron \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xscale | xstormy16 | xtensa \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* \
+       | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* \
+       | clipper-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* \
+       | m32r-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | mcore-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | msp430-* \
+       | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | sh-* | sh[1234]-* | sh3e-* | sh[34]eb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+       | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* | tic30-* | tic4x-* | tic54x-* | tic80-* | tron-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+       | xtensa-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       mmix*)
+               basic_machine=mmix-knuth
+               os=-mmixware
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nv1)
+               basic_machine=nv1-cray
+               os=-unicosmp
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       or32 | or32-*)
+               basic_machine=or32-unknown
+               os=-coff
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2)
+               basic_machine=i686-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+        tic4x | c4x*)
+               basic_machine=tic4x-unknown
+               os=-coff
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh3 | sh4 | sh3eb | sh4eb | sh[1234]le | sh3ele)
+               basic_machine=sh-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparc | sparcv9 | sparcv9b)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -microbsd*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source4/configure.developer b/source4/configure.developer
new file mode 100755 (executable)
index 0000000..0409a75
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+`dirname $0`/configure --enable-developer $*
diff --git a/source4/configure.in b/source4/configure.in
new file mode 100644 (file)
index 0000000..16b5bb5
--- /dev/null
@@ -0,0 +1,3455 @@
+dnl -*- mode: m4-mode -*-
+dnl Process this file with autoconf to produce a configure script.
+
+dnl We must use autotools 2.53 or above
+AC_PREREQ(2.53)
+AC_INIT(include/includes.h)
+AC_CONFIG_HEADER(include/config.h)
+
+AC_DISABLE_STATIC
+AC_ENABLE_SHARED
+
+#################################################
+# Directory handling stuff to support both the
+# legacy SAMBA directories and FHS compliant
+# ones...
+AC_PREFIX_DEFAULT(/usr/local/samba)
+
+AC_ARG_WITH(fhs, 
+[  --with-fhs              Use FHS-compliant paths (default=no)],
+    configdir="${sysconfdir}/samba"
+    lockdir="\${VARDIR}/cache/samba"
+    piddir="\${VARDIR}/run/samba"
+    logfilebase="\${VARDIR}/log/samba"
+    privatedir="\${CONFIGDIR}/private"
+    libdir="\${prefix}/lib/samba"
+    swatdir="\${DATADIR}/samba/swat",
+    configdir="\${LIBDIR}"
+    logfilebase="\${VARDIR}"
+    lockdir="\${VARDIR}/locks"
+    piddir="\${VARDIR}/locks"
+    privatedir="\${prefix}/private"
+    swatdir="\${prefix}/swat")
+
+#################################################
+# set private directory location
+AC_ARG_WITH(privatedir,
+[  --with-privatedir=DIR   Where to put smbpasswd ($ac_default_prefix/private)],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody calls it without argument
+  #
+    AC_MSG_WARN([--with-privatedir called without argument - will use default])
+  ;;
+  * )
+    privatedir="$withval"
+    ;;
+  esac])
+
+#################################################
+# set lock directory location
+AC_ARG_WITH(lockdir,
+[  --with-lockdir=DIR      Where to put lock files ($ac_default_prefix/var/locks)],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody calls it without argument
+  #
+    AC_MSG_WARN([--with-lockdir called without argument - will use default])
+  ;;
+  * )
+    lockdir="$withval"
+    ;;
+  esac])
+
+#################################################
+# set pid directory location
+AC_ARG_WITH(piddir,
+[  --with-piddir=DIR       Where to put pid files ($ac_default_prefix/var/locks)],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody calls it without argument
+  #
+    AC_MSG_WARN([--with-piddir called without argument - will use default])
+  ;;
+  * )
+    piddir="$withval"
+    ;;
+  esac])
+
+#################################################
+# set SWAT directory location
+AC_ARG_WITH(swatdir,
+[  --with-swatdir=DIR      Where to put SWAT files ($ac_default_prefix/swat)],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody does it
+  #
+    AC_MSG_WARN([--with-swatdir called without argument - will use default])
+  ;;
+  * )
+    swatdir="$withval"
+    ;;
+  esac])
+
+#################################################
+# set configuration directory location
+AC_ARG_WITH(configdir,
+[  --with-configdir=DIR    Where to put configuration files (\$libdir)],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody does it
+  #
+    AC_MSG_WARN([--with-configdir called without argument - will use default])
+  ;;
+  * )
+    configdir="$withval"
+    ;;
+  esac])
+
+#################################################
+# set log directory location
+AC_ARG_WITH(logfilebase,
+[  --with-logfilebase=DIR  Where to put log files (\$(VARDIR))],
+[ case "$withval" in
+  yes|no)
+  #
+  # Just in case anybody does it
+  #
+    AC_MSG_WARN([--with-logfilebase called without argument - will use default])
+  ;;
+  * )
+    logfilebase="$withval"
+    ;;
+  esac])
+
+AC_SUBST(configdir)
+AC_SUBST(lockdir)
+AC_SUBST(piddir)
+AC_SUBST(logfilebase)
+AC_SUBST(privatedir)
+AC_SUBST(swatdir)
+AC_SUBST(bindir)
+AC_SUBST(sbindir)
+
+dnl Unique-to-Samba variables we'll be playing with.
+AC_SUBST(SHELL)
+AC_SUBST(LDSHFLAGS)
+AC_SUBST(SONAMEFLAG)
+AC_SUBST(SHLD)
+AC_SUBST(HOST_OS)
+AC_SUBST(PICFLAG)
+AC_SUBST(PICSUFFIX)
+AC_SUBST(POBAD_CC)
+AC_SUBST(SHLIBEXT)
+AC_SUBST(INSTALLCLIENTCMD_SH)
+AC_SUBST(INSTALLCLIENTCMD_A)
+AC_SUBST(LIBSMBCLIENT_SHARED)
+AC_SUBST(LIBSMBCLIENT)
+AC_SUBST(PRINTLIBS)
+AC_SUBST(AUTHLIBS)
+AC_SUBST(ACLLIBS)
+AC_SUBST(SHLIB_PROGS)
+AC_SUBST(SMBWRAPPER)
+AC_SUBST(EXTRA_BIN_PROGS)
+AC_SUBST(EXTRA_SBIN_PROGS)
+AC_SUBST(EXTRA_ALL_TARGETS)
+dnl For the DYNAMIC RPC stuff
+dnl The complicated _YES and _NO stuff allows us to avoid a dependency
+dnl on GNU Make.
+AC_SUBST(LSA_DYNAMIC_YES)
+AC_SUBST(LSA_DYNAMIC_NO)
+LSA_DYNAMIC_YES="#"
+LSA_DYNAMIC_NO=
+AC_SUBST(NETLOG_DYNAMIC_YES)
+AC_SUBST(NETLOG_DYNAMIC_NO)
+NETLOG_DYNAMIC_YES="#"
+NETLOG_DYNAMIC_NO=
+AC_SUBST(SAMR_DYNAMIC_YES)
+AC_SUBST(SAMR_DYNAMIC_NO)
+SAMR_DYNAMIC_YES="#"
+SAMR_DYNAMIC_NO=
+AC_SUBST(SVC_DYNAMIC_YES)
+AC_SUBST(SVC_DYNAMIC_NO)
+SVC_DYNAMIC_YES="#"
+SVC_DYNAMIC_NO=
+AC_SUBST(WKS_DYNAMIC_YES)
+AC_SUBST(WKS_DYNAMIC_NO)
+WKS_DYNAMIC_YES="#"
+WKS_DYNAMIC_NO=
+AC_SUBST(REG_DYNAMIC_YES)
+AC_SUBST(REG_DYNAMIC_NO)
+REG_DYNAMIC_YES="#"
+REG_DYNAMIC_NO=
+AC_SUBST(SPOOLSS_DYNAMIC_YES)
+AC_SUBST(SPOOLSS_DYNAMIC_NO)
+SPOOLSS_DYNAMIC_YES="#"
+SPOOLSS_DYNAMIC_NO=
+AC_SUBST(DFS_DYNAMIC_YES)
+AC_SUBST(DFS_DYNAMIC_NO)
+DFS_DYNAMIC_YES="#"
+DFS_DYNAMIC_NO=
+
+AC_ARG_ENABLE(debug, 
+[  --enable-debug          Turn on compiler debugging information (default=no)],
+    [if eval "test x$enable_debug = xyes"; then
+       CFLAGS="${CFLAGS} -gstabs"
+    fi])
+
+AC_ARG_ENABLE(developer, [  --enable-developer      Turn on developer warnings and debugging (default=no)],
+    [if eval "test x$enable_developer = xyes"; then
+       CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+    fi])
+
+AC_ARG_ENABLE(krb5developer, [  --enable-krb5developer  Turn on developer warnings and debugging, except -Wstrict-prototypes (default=no)],
+    [if eval "test x$enable_krb5developer = xyes"; then
+       CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+    fi])
+
+# compile with optimization and without debugging by default
+if test "x$CFLAGS" = x; then
+   CFLAGS = "-O";
+fi
+
+AC_ARG_ENABLE(dmalloc, [  --enable-dmalloc        Enable heap debugging [default=no]])
+
+if test "x$enable_dmalloc" = xyes
+then
+       AC_DEFINE(ENABLE_DMALLOC, 1, [Define to turn on dmalloc debugging])
+       AC_DEFINE(DMALLOC_FUNC_CHECK, 1, 
+                  [Define to check invariants around some common functions])
+       LIBS="$LIBS -ldmalloc"  
+fi
+
+AC_ARG_ENABLE(dynrpc,  [  --enable-dynrpc         Enable dynamic RPC modules [default=no]])
+
+if test x$enable_dynrpc = xyes
+then
+       enable_dynrpc=lsa,samr,reg,wks,netlog,dfs
+fi
+
+if test x$enable_dynrpc != xno
+then
+       for i in `echo $enable_dynrpc | sed -e's/,/ /g'` 
+         do case $i in lsa)
+         LSA_DYNAMIC_YES=
+         LSA_DYNAMIC_NO="#"
+          AC_DEFINE(RPC_LSA_DYNAMIC, 1,
+                    [Define to make the LSA pipe dynamic])
+        ;; samr)
+          SAMR_DYNAMIC_YES=
+          SAMR_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_SAMR_DYNAMIC, 1, 
+                   [Define to make the SAMR pipe dynamic])
+        ;; svc)
+          SVC_DYNAMIC_YES=
+          SVC_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_SVC_DYNAMIC, 1, 
+                   [Define to make the SRVSVC pipe dynamic])
+        ;; wks)
+          WKS_DYNAMIC_YES=
+          WKS_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_WKS_DYNAMIC, 1, 
+                   [Define to make the WKSSVC pipe dynamic])
+        ;; netlog)
+          NETLOG_DYNAMIC_YES=
+          NETLOG_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_NETLOG_DYNAMIC, 1, 
+                   [Define to make the NETLOGON pipe dynamic])
+        ;; reg)
+          REG_DYNAMIC_YES=
+          REG_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_REG_DYNAMIC, 1, 
+                   [Define to make the WINREG pipe dynamic])
+        ;; spoolss)
+          SPOOLSS_DYNAMIC_YES=
+          SPOOLSS_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_SPOOLSS_DYNAMIC, 1, 
+                   [Define to make the SPOOLSS pipe dynamic])
+        ;; dfs)
+          DFS_DYNAMIC_YES=
+          DFS_DYNAMIC_NO="#"
+         AC_DEFINE(RPC_DFS_DYNAMIC, 1, 
+                   [Define to make the NETDFS pipe dynamic])
+        ;; esac
+         done
+fi
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_AWK
+AC_PATH_PROG(PERL, perl)
+
+dnl Check if we use GNU ld
+LD=ld
+AC_PROG_LD_GNU
+
+dnl needed before AC_TRY_COMPILE
+AC_ISC_POSIX
+
+dnl look for executable suffix
+AC_EXEEXT
+
+dnl Check if C compiler understands -c and -o at the same time
+AC_PROG_CC_C_O
+if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = no"; then
+      BROKEN_CC=
+else
+      BROKEN_CC=#
+fi
+AC_SUBST(BROKEN_CC)
+
+dnl Check if the C compiler understands volatile (it should, being ANSI).
+AC_CACHE_CHECK([that the C compiler understands volatile],samba_cv_volatile, [
+    AC_TRY_COMPILE([#include <sys/types.h>],[volatile int i = 0],
+       samba_cv_volatile=yes,samba_cv_volatile=no)])
+if test x"$samba_cv_volatile" = x"yes"; then
+   AC_DEFINE(HAVE_VOLATILE, 1, [Whether the C compiler understands volatile])
+fi
+
+
+AC_CANONICAL_SYSTEM
+
+dnl Add #include for broken IRIX header files
+  case "$host_os" in
+       *irix6*) AC_ADD_INCLUDE(<standards.h>)
+       ;;
+esac
+
+AC_VALIDATE_CACHE_SYSTEM_TYPE
+
+DYNEXP=
+
+#
+# Config CPPFLAG settings for strange OS's that must be set
+# before other tests.
+#
+case "$host_os" in
+# Try to work out if this is the native HPUX compiler that uses the -Ae flag.
+    *hpux*)
+    
+      AC_PROG_CC_FLAG(Ae)
+      # mmap on HPUX is completely broken...
+      AC_DEFINE(MMAP_BLACKLIST, 1, [Whether MMAP is broken])
+      if test $ac_cv_prog_cc_Ae = yes; then
+        CPPFLAGS="$CPPFLAGS -Ae"
+      fi
+#
+# Defines needed for HPUX support.
+# HPUX has bigcrypt but (sometimes?) doesn't use it for
+# password hashing - hence the USE_BOTH_CRYPT_CALLS define.
+#
+      case `uname -r` in
+                       *9*|*10*)
+                               CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+                               AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+                               AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+                               AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+                               AC_DEFINE(_ALIGNMENT_REQUIRED,1,[Required alignment])
+                               AC_DEFINE(_MAX_ALIGNMENT,4,[Maximum alignment])
+                               ;;
+                       *11*)
+                               CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_LARGEFILE64_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+                               AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+                               AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+                               AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+                               AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to use large file support])
+                               AC_DEFINE(_ALIGNMENT_REQUIRED, 1, [Required alignment])
+                               AC_DEFINE(_MAX_ALIGNMENT, 4, [Maximum alignment])
+                               ;;
+      esac
+      DYNEXP="-Wl,-E"
+      ;;
+
+#
+# CRAY Unicos has broken const handling
+       *unicos*)
+         AC_MSG_RESULT([disabling const])
+         CPPFLAGS="$CPPFLAGS -Dconst="
+         ;;
+       
+#
+# AIX4.x doesn't even admit to having large
+# files *at all* unless the -D_LARGE_FILE or -D_LARGE_FILE_API flags are set.
+#
+    *aix4*)
+         AC_MSG_RESULT([enabling large file support])
+      CPPFLAGS="$CPPFLAGS -D_LARGE_FILES"
+         AC_DEFINE(_LARGE_FILES, 1, [Whether to enable large file support])
+      ;;    
+#
+# Defines needed for Solaris 2.6/2.7 aka 7.0 to make it admit
+# to the existance of large files..
+# Note that -D_LARGEFILE64_SOURCE is different from the Sun
+# recommendations on large file support, however it makes the
+# compile work using gcc 2.7 and 2.8, whereas using the Sun
+# recommendation makes the compile fail on gcc2.7. JRA.
+#
+       *solaris*)
+               case `uname -r` in
+                       5.0*|5.1*|5.2*|5.3*|5.5*)
+                               AC_MSG_RESULT([no large file support])
+                               ;;
+                       5.*)
+                       AC_MSG_RESULT([enabling large file support])
+                       if test "$ac_cv_prog_gcc" = yes; then
+                               ${CC-cc} -v >conftest.c 2>&1
+                               ac_cv_gcc_compiler_version_number=`grep 'gcc version' conftest.c`
+                               rm -fr conftest.c
+                               case "$ac_cv_gcc_compiler_version_number" in
+                                       *"gcc version 2.6"*|*"gcc version 2.7"*)
+                                               CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE"
+                                               AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+                                               ;;
+                                       *)
+                                               CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+                                               AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+                                               AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+                                               ;;
+                               esac
+                       else
+                               CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+                               AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+                               AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+                       fi
+                       ;;
+               esac
+               ;;
+#
+# VOS may need to have POSIX support and System V compatibility enabled.
+#
+    *vos*)
+    case "$CPPFLAGS" in
+         *-D_POSIX_C_SOURCE*)
+               ;;
+         *)
+               CPPFLAGS="$CPPFLAGS -D_POSIX_C_SOURCE=199506L"
+               AC_DEFINE(_POSIX_C_SOURCE, 199506L, [Whether to enable POSIX support])
+               ;;
+    esac
+    case "$CPPFLAGS" in
+         *-D_SYSV*|*-D_SVID_SOURCE*)
+               ;;
+         *)
+               CPPFLAGS="$CPPFLAGS -D_SYSV"
+               AC_DEFINE(_SYSV, 1, [Whether to enable System V compatibility])
+    esac
+    ;;
+#
+# Tests needed for SINIX large file support.
+#
+    *sysv4*)
+      if test $host = mips-sni-sysv4 ; then
+        AC_MSG_CHECKING([for LFS support])
+        old_CPPFLAGS="$CPPFLAGS"
+        CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+        AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [SINIX_LFS_SUPPORT=yes], [SINIX_LFS_SUPPORT=no], [SINIX_LFS_SUPPORT=cross])
+        CPPFLAGS="$old_CPPFLAGS"
+        if test x$SINIX_LFS_SUPPORT = xyes ; then
+          CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+                 AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+          CFLAGS="`getconf LFS64_CFLAGS` $CFLAGS"
+          LDFLAGS="`getconf LFS64_LDFLAGS` $LDFLAGS"
+          LIBS="`getconf LFS64_LIBS` $LIBS"
+        fi
+      AC_MSG_RESULT([$SINIX_LFS_SUPPORT])
+      fi
+    ;;
+
+# Tests for linux LFS support. Need kernel 2.4 and glibc2.2 or greater support.
+#
+    *linux*)
+        AC_MSG_CHECKING([for LFS support])
+        old_CPPFLAGS="$CPPFLAGS"
+        CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+       AC_TRY_RUN([
+#include <unistd.h>
+#include <sys/utsname.h>
+main() {
+#if _LFS64_LARGEFILE == 1
+       struct utsname uts;
+       char *release;
+       int major, minor;
+
+       /* Ensure this is glibc 2.2 or higher */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+       int libc_major = __GLIBC__;
+       int libc_minor = __GLIBC_MINOR__;
+
+       if (libc_major < 2)
+              exit(1);
+       if (libc_minor < 2)
+              exit(1);
+#endif
+
+       /* Ensure this is kernel 2.4 or higher */
+
+       uname(&uts);
+       release = uts.release;
+       major = atoi(strsep(&release, "."));
+       minor = atoi(strsep(&release, "."));
+
+       if (major > 2 || (major == 2 && minor > 3))
+               exit(0);
+       exit(1);
+#else
+       exit(1);
+#endif
+}
+], [LINUX_LFS_SUPPORT=yes], [LINUX_LFS_SUPPORT=no], [LINUX_LFS_SUPPORT=cross])
+        CPPFLAGS="$old_CPPFLAGS"
+        if test x$LINUX_LFS_SUPPORT = xyes ; then
+          CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+                 AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+          AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+          AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+        fi
+       AC_MSG_RESULT([$LINUX_LFS_SUPPORT])
+               ;;
+
+    *hurd*)
+        AC_MSG_CHECKING([for LFS support])
+        old_CPPFLAGS="$CPPFLAGS"
+        CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+        AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [GLIBC_LFS_SUPPORT=yes], [GLIBC_LFS_SUPPORT=no], [GLIBC_LFS_SUPPORT=cross])
+        CPPFLAGS="$old_CPPFLAGS"
+        if test x$GLIBC_LFS_SUPPORT = xyes ; then
+          CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+                 AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+          AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+        fi
+      AC_MSG_RESULT([$GLIBC_LFS_SUPPORT])
+    ;;
+
+esac
+
+AC_INLINE
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_HEADER_TIME
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(arpa/inet.h sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h)
+AC_CHECK_HEADERS(unistd.h utime.h grp.h sys/id.h limits.h memory.h net/if.h)
+AC_CHECK_HEADERS(compat.h rpc/rpc.h rpcsvc/nis.h rpcsvc/yp_prot.h rpcsvc/ypclnt.h)
+AC_CHECK_HEADERS(sys/param.h ctype.h sys/wait.h sys/resource.h sys/ioctl.h sys/ipc.h sys/mode.h)
+AC_CHECK_HEADERS(sys/mman.h sys/filio.h sys/priv.h sys/shm.h string.h strings.h stdlib.h sys/socket.h)
+AC_CHECK_HEADERS(sys/mount.h sys/vfs.h sys/fs/s5param.h sys/filsys.h termios.h termio.h)
+AC_CHECK_HEADERS(sys/termio.h sys/statfs.h sys/dustat.h sys/statvfs.h stdarg.h sys/sockio.h)
+AC_CHECK_HEADERS(security/pam_modules.h security/_pam_macros.h ldap.h lber.h dlfcn.h)
+AC_CHECK_HEADERS(sys/syslog.h syslog.h)
+
+#
+# HPUX has a bug in that including shadow.h causes a re-definition of MAXINT.
+# This causes configure to fail to detect it. Check for shadow separately on HPUX.
+#
+case "$host_os" in
+    *hpux*)
+               AC_TRY_COMPILE([#include <shadow.h>],[struct spwd testme],
+                       ac_cv_header_shadow_h=yes,ac_cv_header_shadow_h=no)
+               if test x"$ac_cv_header_shadow_h" = x"yes"; then
+                  AC_DEFINE(HAVE_SHADOW_H,1,[Whether we have shadow.h])
+               fi
+       ;;
+esac
+AC_CHECK_HEADERS(shadow.h netinet/ip.h netinet/tcp.h netinet/in_systm.h netinet/in_ip.h)
+AC_CHECK_HEADERS(nss.h nss_common.h ns_api.h sys/security.h security/pam_appl.h security/pam_modules.h)
+AC_CHECK_HEADERS(stropts.h poll.h)
+AC_CHECK_HEADERS(sys/capability.h syscall.h sys/syscall.h)
+AC_CHECK_HEADERS(sys/acl.h sys/cdefs.h glob.h)
+
+# For experimental utmp support (lastlog on some BSD-like systems)
+AC_CHECK_HEADERS(utmp.h utmpx.h lastlog.h)
+# For quotas on Veritas VxFS filesystems
+AC_CHECK_HEADERS(sys/fs/vx_quota.h)
+
+# For quotas on Linux XFS filesystems
+AC_CHECK_HEADERS(linux/xqm.h)
+AC_CHECK_HEADERS(xfs/xqm.h)
+
+AC_CHECK_SIZEOF(int,cross)
+AC_CHECK_SIZEOF(long,cross)
+AC_CHECK_SIZEOF(short,cross)
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_BIGENDIAN
+AC_C_CHAR_UNSIGNED
+
+AC_TYPE_SIGNAL
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_PID_T
+AC_STRUCT_ST_RDEV
+AC_DIRENT_D_OFF
+AC_CHECK_TYPE(ino_t,unsigned)
+AC_CHECK_TYPE(loff_t,off_t)
+AC_CHECK_TYPE(offset_t,loff_t)
+AC_CHECK_TYPE(ssize_t, int)
+AC_CHECK_TYPE(wchar_t, unsigned short)
+
+############################################
+# for cups support we need libcups, and a handful of header files
+
+AC_ARG_ENABLE(cups,
+[  --enable-cups           Turn on CUPS support (default=auto)])
+
+if test x$enable_cups != xno; then
+       AC_PATH_PROG(CUPS_CONFIG, cups-config)
+
+        if test "x$CUPS_CONFIG" != x; then
+                        AC_DEFINE(HAVE_CUPS,1,[Whether we have CUPS])
+               CFLAGS="$CFLAGS `$CUPS_CONFIG --cflags`"
+               LDFLAGS="$LDFLAGS `$CUPS_CONFIG --ldflags`"
+               PRINTLIBS="$PRINTLIBS `$CUPS_CONFIG --libs`"
+        fi
+fi
+
+############################################
+# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
+AC_SEARCH_LIBS(dlopen, [dl])
+# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
+
+############################################
+# check if the compiler can do immediate structures
+AC_CACHE_CHECK([for immediate structures],samba_cv_immediate_structures, [
+    AC_TRY_COMPILE([
+#include <stdio.h>],
+[
+   typedef struct {unsigned x;} FOOBAR;
+   #define X_FOOBAR(x) ((FOOBAR) { x })
+   #define FOO_ONE X_FOOBAR(1)
+   FOOBAR f = FOO_ONE;   
+   static struct {
+       FOOBAR y; 
+       } f2[] = {
+               {FOO_ONE}
+       };   
+],
+       samba_cv_immediate_structures=yes,samba_cv_immediate_structures=no)])
+if test x"$samba_cv_immediate_structures" = x"yes"; then
+   AC_DEFINE(HAVE_IMMEDIATE_STRUCTURES,1,[Whether the compiler supports immediate structures])
+fi
+
+############################################
+# check for unix domain sockets
+AC_CACHE_CHECK([for unix domain sockets],samba_cv_unixsocket, [
+    AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>],
+[
+  struct sockaddr_un sunaddr; 
+  sunaddr.sun_family = AF_UNIX;
+],
+       samba_cv_unixsocket=yes,samba_cv_unixsocket=no)])
+if test x"$samba_cv_unixsocket" = x"yes"; then
+   AC_DEFINE(HAVE_UNIXSOCKET,1,[If we need to build with unixscoket support])
+fi
+
+
+AC_CACHE_CHECK([for socklen_t type],samba_cv_socklen_t, [
+    AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <sys/socket.h>],[socklen_t i = 0],
+       samba_cv_socklen_t=yes,samba_cv_socklen_t=no)])
+if test x"$samba_cv_socklen_t" = x"yes"; then
+   AC_DEFINE(HAVE_SOCKLEN_T_TYPE,1,[Whether we have the variable type socklen_t])
+fi
+
+AC_CACHE_CHECK([for sig_atomic_t type],samba_cv_sig_atomic_t, [
+    AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <signal.h>],[sig_atomic_t i = 0],
+       samba_cv_sig_atomic_t=yes,samba_cv_sig_atomic_t=no)])
+if test x"$samba_cv_sig_atomic_t" = x"yes"; then
+   AC_DEFINE(HAVE_SIG_ATOMIC_T_TYPE,1,[Whether we have the atomic_t variable type])
+fi
+
+# stupid headers have the functions but no declaration. grrrr.
+AC_HAVE_DECL(errno, [#include <errno.h>])
+AC_HAVE_DECL(setresuid, [#include <unistd.h>])
+AC_HAVE_DECL(setresgid, [#include <unistd.h>])
+AC_HAVE_DECL(asprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vasprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vsnprintf, [#include <stdio.h>])
+AC_HAVE_DECL(snprintf, [#include <stdio.h>])
+
+# and glibc has setresuid under linux but the function does
+# nothing until kernel 2.1.44! very dumb.
+AC_CACHE_CHECK([for real setresuid],samba_cv_have_setresuid,[
+    AC_TRY_RUN([#include <errno.h>
+main() { setresuid(1,1,1); setresuid(2,2,2); exit(errno==EPERM?0:1);}],
+       samba_cv_have_setresuid=yes,samba_cv_have_setresuid=no,samba_cv_have_setresuid=cross)])
+if test x"$samba_cv_have_setresuid" = x"yes"; then
+    AC_DEFINE(HAVE_SETRESUID,1,[Whether the system has setresuid])
+fi
+
+# Do the same check for setresguid...
+#
+AC_CACHE_CHECK([for real setresgid],samba_cv_have_setresgid,[
+    AC_TRY_RUN([#include <unistd.h>
+#include <errno.h>
+main() { errno = 0; setresgid(1,1,1); exit(errno != 0 ? (errno==EPERM ? 0 : 1) : 0);}],
+       samba_cv_have_setresgid=yes,samba_cv_have_setresgid=no,samba_cv_have_setresgid=cross)])
+if test x"$samba_cv_have_setresgid" = x"yes"; then
+    AC_DEFINE(HAVE_SETRESGID,1,[Whether the system has setresgid])
+fi
+
+AC_FUNC_MEMCMP
+
+###############################################
+# Readline included by default unless explicitly asked not to
+test "${with_readline+set}" != "set" && with_readline=yes
+
+# test for where we get readline() from
+AC_MSG_CHECKING(whether to use readline)
+AC_ARG_WITH(readline,
+[  --with-readline[=DIR]   Look for readline include/libs in DIR (default=auto) ],
+[  case "$with_readline" in
+  yes)
+    AC_MSG_RESULT(yes)
+
+    AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+    AC_CHECK_HEADERS(readline/history.h)
+
+    AC_CHECK_HEADERS(readline.h readline/readline.h,[
+      for termlib in ncurses curses termcap terminfo termlib tinfo; do
+       AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+      done
+      AC_CHECK_LIB(readline, rl_callback_handler_install,
+       [TERMLIBS="-lreadline $TERMLIBS"
+       AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+       break], [TERMLIBS=], $TERMLIBS)])
+    ;;
+  no)
+    AC_MSG_RESULT(no)
+    ;;
+  *)
+    AC_MSG_RESULT(yes)
+
+    # Needed for AC_CHECK_HEADERS and AC_CHECK_LIB to look at
+    # alternate readline path
+    _ldflags=${LDFLAGS}
+    _cppflags=${CPPFLAGS}
+
+    # Add additional search path
+    LDFLAGS="-L$with_readline/lib $LDFLAGS"
+    CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+
+    AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+    AC_CHECK_HEADERS(readline/history.h)
+
+    AC_CHECK_HEADERS(readline.h readline/readline.h,[
+      for termlib in ncurses curses termcap terminfo termlib; do
+       AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+      done
+      AC_CHECK_LIB(readline, rl_callback_handler_install,
+       [TERMLDFLAGS="-L$with_readline/lib"
+       TERMCPPFLAGS="-I$with_readline/include"
+       CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+       TERMLIBS="-lreadline $TERMLIBS"
+       AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+       break], [TERMLIBS= CPPFLAGS=$_cppflags], $TERMLIBS)])
+
+    LDFLAGS=$_ldflags
+    ;;
+  esac],
+  AC_MSG_RESULT(no)
+)
+AC_SUBST(TERMLIBS)
+AC_SUBST(TERMLDFLAGS)
+
+# The readline API changed slightly from readline3 to readline4, so
+# code will generate warnings on one of them unless we have a few
+# special cases.
+AC_CHECK_LIB(readline, rl_completion_matches,
+            [AC_DEFINE(HAVE_NEW_LIBREADLINE, 1, 
+                       [Do we have rl_completion_matches?])],
+            [],
+            [$TERMLIBS])
+
+# The following test taken from the cvs sources
+# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
+# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
+# libsocket.so which has a bad implementation of gethostbyname (it
+# only looks in /etc/hosts), so we only look for -lsocket if we need
+# it.
+AC_CHECK_FUNCS(connect)
+if test x"$ac_cv_func_connect" = x"no"; then
+    case "$LIBS" in
+    *-lnsl*) ;;
+    *) AC_CHECK_LIB(nsl_s, printf) ;;
+    esac
+    case "$LIBS" in
+    *-lnsl*) ;;
+    *) AC_CHECK_LIB(nsl, printf) ;;
+    esac
+    case "$LIBS" in
+    *-lsocket*) ;;
+    *) AC_CHECK_LIB(socket, connect) ;;
+    esac
+    case "$LIBS" in
+    *-linet*) ;;
+    *) AC_CHECK_LIB(inet, connect) ;;
+    esac
+    dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
+    dnl has been cached.
+    if test x"$ac_cv_lib_socket_connect" = x"yes" || 
+       test x"$ac_cv_lib_inet_connect" = x"yes"; then
+        # ac_cv_func_connect=yes
+        # don't!  it would cause AC_CHECK_FUNC to succeed next time configure is run
+        AC_DEFINE(HAVE_CONNECT,1,[Whether the system has connect()])
+    fi
+fi
+
+###############################################
+# test for where we get yp_get_default_domain() from
+AC_SEARCH_LIBS(yp_get_default_domain, [nsl])
+AC_CHECK_FUNCS(yp_get_default_domain)
+
+# Check if we have execl, if not we need to compile smbrun.
+AC_CHECK_FUNCS(execl)
+if test x"$ac_cv_func_execl" = x"no"; then
+    EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbrun\$(EXEEXT)"
+fi
+
+AC_CHECK_FUNCS(dlopen dlclose dlsym dlerror waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
+AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset strlcpy strlcat setpgid)
+AC_CHECK_FUNCS(memmove vsnprintf snprintf asprintf vasprintf setsid glob strpbrk pipe crypt16 getauthuid)
+AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction sigset innetgr setnetgrent getnetgrent endnetgrent)
+AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf realpath)
+AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
+AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
+AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam setlinebuf)
+AC_CHECK_FUNCS(srandom random srand rand setenv usleep strcasecmp fcvt fcvtl symlink readlink)
+AC_CHECK_FUNCS(syslog vsyslog getgrouplist timegm)
+# setbuffer, shmget, shm_open are needed for smbtorture
+AC_CHECK_FUNCS(setbuffer shmget shm_open)
+
+# syscall() is needed for smbwrapper.
+AC_CHECK_FUNCS(syscall)
+
+AC_CHECK_FUNCS(_dup _dup2 _opendir _readdir _seekdir _telldir _closedir)
+AC_CHECK_FUNCS(__dup __dup2 __opendir __readdir __seekdir __telldir __closedir)
+AC_CHECK_FUNCS(__getcwd _getcwd)
+AC_CHECK_FUNCS(__xstat __fxstat __lxstat)
+AC_CHECK_FUNCS(_stat _lstat _fstat __stat __lstat __fstat)
+AC_CHECK_FUNCS(_acl __acl _facl __facl _open __open _chdir __chdir)
+AC_CHECK_FUNCS(_close __close _fchdir __fchdir _fcntl __fcntl)
+AC_CHECK_FUNCS(getdents _getdents __getdents _lseek __lseek _read __read)
+AC_CHECK_FUNCS(getdirentries _write __write _fork __fork)
+AC_CHECK_FUNCS(_stat64 __stat64 _fstat64 __fstat64 _lstat64 __lstat64)
+AC_CHECK_FUNCS(__sys_llseek llseek _llseek __llseek readdir64 _readdir64 __readdir64)
+AC_CHECK_FUNCS(pread _pread __pread pread64 _pread64 __pread64)
+AC_CHECK_FUNCS(pwrite _pwrite __pwrite pwrite64 _pwrite64 __pwrite64)
+AC_CHECK_FUNCS(open64 _open64 __open64 creat64)
+
+#
+# stat64 family may need <sys/stat.h> on some systems, notably ReliantUNIX
+#
+
+if test x$ac_cv_func_stat64 = xno ; then
+  AC_MSG_CHECKING([for stat64 in <sys/stat.h>])
+  AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(stat64(".",&st64));], [ac_cv_func_stat64=yes])
+  AC_MSG_RESULT([$ac_cv_func_stat64])
+  if test x$ac_cv_func_stat64 = xyes ; then
+    AC_DEFINE(HAVE_STAT64,1,[Whether stat64() is available])
+  fi
+fi
+
+if test x$ac_cv_func_lstat64 = xno ; then
+  AC_MSG_CHECKING([for lstat64 in <sys/stat.h>])
+  AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(lstat64(".",&st64));], [ac_cv_func_lstat64=yes])
+  AC_MSG_RESULT([$ac_cv_func_lstat64])
+  if test x$ac_cv_func_lstat64 = xyes ; then
+    AC_DEFINE(HAVE_LSTAT64,[Whether lstat64() is available])
+  fi
+fi
+
+if test x$ac_cv_func_fstat64 = xno ; then
+  AC_MSG_CHECKING([for fstat64 in <sys/stat.h>])
+  AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(fstat64(0,&st64));], [ac_cv_func_fstat64=yes])
+  AC_MSG_RESULT([$ac_cv_func_fstat64])
+  if test x$ac_cv_func_fstat64 = xyes ; then
+    AC_DEFINE(HAVE_FSTAT64,1,[Whether fstat64() is available])
+  fi
+fi
+
+#####################################
+# we might need the resolv library on some systems
+AC_CHECK_LIB(resolv, dn_expand)
+
+#
+# Check for the functions putprpwnam, set_auth_parameters,
+# getspnam, bigcrypt and getprpwnam in -lsec and -lsecurity
+# Needed for OSF1 and HPUX.
+#
+
+AC_LIBTESTFUNC(security, putprpwnam)
+AC_LIBTESTFUNC(sec, putprpwnam)
+
+AC_LIBTESTFUNC(security, set_auth_parameters)
+AC_LIBTESTFUNC(sec, set_auth_parameters)
+
+# UnixWare 7.x has its getspnam in -lgen
+AC_LIBTESTFUNC(gen, getspnam)
+
+AC_LIBTESTFUNC(security, getspnam)
+AC_LIBTESTFUNC(sec, getspnam)
+
+AC_LIBTESTFUNC(security, bigcrypt)
+AC_LIBTESTFUNC(sec, bigcrypt)
+
+AC_LIBTESTFUNC(security, getprpwnam)
+AC_LIBTESTFUNC(sec, getprpwnam)
+
+# Assume non-shared by default and override below
+BLDSHARED="false"
+
+# these are the defaults, good for lots of systems
+HOST_OS="$host_os"
+LDSHFLAGS="-shared"
+SONAMEFLAG="#"
+SHLD="\${CC}"
+PICFLAG=""
+PICSUFFIX="po"
+POBAD_CC="#"
+SHLIBEXT="so"
+
+if test "$enable_shared" = "yes"; then
+  # this bit needs to be modified for each OS that is suported by
+  # smbwrapper. You need to specify how to created a shared library and
+  # how to compile C code to produce PIC object files
+
+  AC_MSG_CHECKING([ability to build shared libraries])
+
+  # and these are for particular systems
+  case "$host_os" in
+               *linux*)   AC_DEFINE(LINUX,1,[Whether the host os is linux])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-shared" 
+                       DYNEXP="-Wl,--export-dynamic"
+                       PICFLAG="-fPIC"
+                       SONAMEFLAG="-Wl,-soname="
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *solaris*) AC_DEFINE(SUNOS5,1,[Whether the host os is solaris])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-G"
+                       SONAMEFLAG="-h "
+                       if test "${GCC}" = "yes"; then
+                               PICFLAG="-fPIC"
+                               if test "${ac_cv_prog_gnu_ld}" = "yes"; then
+                                       DYNEXP="-Wl,-E"
+                               fi
+                       else
+                               PICFLAG="-KPIC"
+                               ## ${CFLAGS} added for building 64-bit shared 
+                               ## libs using Sun's Compiler
+                               LDSHFLAGS="-G \${CFLAGS}"
+                               POBAD_CC=""
+                               PICSUFFIX="po.o"
+                       fi
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+                       ;;
+               *sunos*) AC_DEFINE(SUNOS4,1,[Whether the host os is sunos4])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-G"
+                       SONAMEFLAG="-Wl,-h,"
+                       PICFLAG="-KPIC"   # Is this correct for SunOS
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *netbsd* | *freebsd*)  BLDSHARED="true"
+                       LDSHFLAGS="-shared"
+                       DYNEXP="-Wl,--export-dynamic"
+                       SONAMEFLAG="-Wl,-soname,"
+                       PICFLAG="-fPIC -DPIC"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+                       ;;
+               *openbsd*)  BLDSHARED="true"
+                       LDSHFLAGS="-shared"
+                       DYNEXP="-Wl,-Bdynamic"
+                       SONAMEFLAG="-Wl,-soname,"
+                       PICFLAG="-fPIC"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+                       ;;
+               *irix*) AC_DEFINE(IRIX,1,[Whether the host os is irix])
+                       case "$host_os" in
+                       *irix6*) AC_DEFINE(IRIX6,1,[Whether the host os is irix6])
+                       ;;
+                       esac
+                       ATTEMPT_WRAP32_BUILD=yes
+                       BLDSHARED="true"
+                       LDSHFLAGS="-set_version sgi1.0 -shared"
+                       SONAMEFLAG="-soname "
+                       SHLD="\${LD}"
+                       if test "${GCC}" = "yes"; then
+                               PICFLAG="-fPIC"
+                       else 
+                               PICFLAG="-KPIC"
+                       fi
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+                       ;;
+               *aix*) AC_DEFINE(AIX,1,[Whether the host os is aix])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-bnoentry"
+                       DYNEXP="-Wl,-brtl,-bexpall"
+                       PICFLAG="-O2"
+                       if test "${GCC}" != "yes"; then
+                               ## for funky AIX compiler using strncpy()
+                               CFLAGS="$CFLAGS -D_LINUX_SOURCE_COMPAT -qmaxmem=32000"
+                       fi
+
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,DEV_BSIZE,[The size of a block])
+                       ;;
+               *hpux*) AC_DEFINE(HPUX,1,[Whether the host os is HPUX])
+                       SHLIBEXT="sl"
+                       # Use special PIC flags for the native HP-UX compiler.
+                       if test $ac_cv_prog_cc_Ae = yes; then
+                               BLDSHARED="true"
+                               SHLD="/usr/bin/ld"
+                               LDSHFLAGS="-B symbolic -b -z"
+                               SONAMEFLAG="+h "
+                               PICFLAG="+z"
+                       fi
+                       DYNEXP="-Wl,-E"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,8192,[The size of a block])
+                       ;;
+               *qnx*) AC_DEFINE(QNX,1,[Whether the host os is qnx])
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *osf*) AC_DEFINE(OSF1,1,[Whether the host os is osf1])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-shared"
+                       SONAMEFLAG="-Wl,-soname,"
+                       PICFLAG="-fPIC"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *sco*) AC_DEFINE(SCO,1,[Whether the host os is sco unix])
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *unixware*) AC_DEFINE(UNIXWARE,1,[Whether the host os is unixware])
+                       BLDSHARED="true"
+                       LDSHFLAGS="-shared"
+                       SONAMEFLAG="-Wl,-soname,"
+                       PICFLAG="-KPIC"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *next2*) AC_DEFINE(NEXT2,1,[Whether the host os is NeXT v2])
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *dgux*) AC_CHECK_PROG( ROFF, groff, [groff -etpsR -Tascii -man])
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *sysv4*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+                       case "$host" in
+                               *-univel-*)     if [ test "$GCC" != yes ]; then
+                                               AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+                                       fi
+                                       LDSHFLAGS="-G"
+                                       DYNEXP="-Bexport"
+                               ;;
+                               *mips-sni-sysv4*) AC_DEFINE(RELIANTUNIX,1,[Whether the host os is reliantunix]);;
+                       esac
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+
+               *sysv5*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+                       if [ test "$GCC" != yes ]; then
+                               AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+                       fi
+                       LDSHFLAGS="-G"
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+               *vos*) AC_DEFINE(STAT_ST_BLOCKSIZE,4096)
+                       BLDSHARED="false"
+                       LDSHFLAGS=""
+                       ;;
+               *)
+                       AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+                       ;;
+  esac
+  AC_SUBST(DYNEXP)
+  AC_MSG_RESULT($BLDSHARED)
+  AC_MSG_CHECKING([linker flags for shared libraries])
+  AC_MSG_RESULT([$LDSHFLAGS])
+  AC_MSG_CHECKING([compiler flags for position-independent code])
+  AC_MSG_RESULT([$PICFLAGS])
+fi
+
+#######################################################
+# test whether building a shared library actually works
+if test $BLDSHARED = true; then
+AC_CACHE_CHECK([whether building shared libraries actually works], 
+               [ac_cv_shlib_works],[
+   ac_cv_shlib_works=no
+   # try building a trivial shared library
+   if test "$PICSUFFIX" = "po"; then
+     $CC $CPPFLAGS $CFLAGS $PICFLAG -c -o shlib.po ${srcdir-.}/tests/shlib.c &&
+       $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o shlib.so shlib.po &&
+       ac_cv_shlib_works=yes
+   else
+     $CC $CPPFLAGS $CFLAGS $PICFLAG -c -o shlib.$PICSUFFIX ${srcdir-.}/tests/shlib.c &&
+       mv shlib.$PICSUFFIX shlib.po &&
+       $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o shlib.so shlib.po &&
+       ac_cv_shlib_works=yes
+   fi
+   rm -f shlib.so shlib.po
+])
+if test $ac_cv_shlib_works = no; then
+   BLDSHARED=false
+fi
+fi
+
+################
+
+AC_CACHE_CHECK([for long long],samba_cv_have_longlong,[
+AC_TRY_RUN([#include <stdio.h>
+main() { long long x = 1000000; x *= x; exit(((x/1000000) == 1000000)? 0: 1); }],
+samba_cv_have_longlong=yes,samba_cv_have_longlong=no,samba_cv_have_longlong=cross)])
+if test x"$samba_cv_have_longlong" = x"yes"; then
+    AC_DEFINE(HAVE_LONGLONG,1,[Whether the host supports long long's])
+fi
+
+#
+# Check if the compiler supports the LL prefix on long long integers.
+# AIX needs this.
+
+AC_CACHE_CHECK([for LL suffix on long long integers],samba_cv_compiler_supports_ll, [
+    AC_TRY_COMPILE([#include <stdio.h>],[long long i = 0x8000000000LL],
+       samba_cv_compiler_supports_ll=yes,samba_cv_compiler_supports_ll=no)])
+if test x"$samba_cv_compiler_supports_ll" = x"yes"; then
+   AC_DEFINE(COMPILER_SUPPORTS_LL,1,[Whether the compiler supports the LL prefix on long long integers])
+fi
+
+  
+AC_CACHE_CHECK([for 64 bit off_t],samba_cv_SIZEOF_OFF_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(off_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_OFF_T=yes,samba_cv_SIZEOF_OFF_T=no,samba_cv_SIZEOF_OFF_T=cross)])
+if test x"$samba_cv_SIZEOF_OFF_T" = x"yes"; then
+    AC_DEFINE(SIZEOF_OFF_T,8,[The size of the 'off_t' type])
+fi
+
+AC_CACHE_CHECK([for off64_t],samba_cv_HAVE_OFF64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; off64_t s; if (sizeof(off_t) == sizeof(off64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_OFF64_T=yes,samba_cv_HAVE_OFF64_T=no,samba_cv_HAVE_OFF64_T=cross)])
+if test x"$samba_cv_HAVE_OFF64_T" = x"yes"; then
+    AC_DEFINE(HAVE_OFF64_T,1,[Whether off64_t is available])
+fi
+
+AC_CACHE_CHECK([for 64 bit ino_t],samba_cv_SIZEOF_INO_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(ino_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_INO_T=yes,samba_cv_SIZEOF_INO_T=no,samba_cv_SIZEOF_INO_T=cross)])
+if test x"$samba_cv_SIZEOF_INO_T" = x"yes"; then
+    AC_DEFINE(SIZEOF_INO_T,8,[The size of the 'ino_t' type])
+fi
+
+AC_CACHE_CHECK([for ino64_t],samba_cv_HAVE_INO64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; ino64_t s; if (sizeof(ino_t) == sizeof(ino64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_INO64_T=yes,samba_cv_HAVE_INO64_T=no,samba_cv_HAVE_INO64_T=cross)])
+if test x"$samba_cv_HAVE_INO64_T" = x"yes"; then
+    AC_DEFINE(HAVE_INO64_T,1,[Whether the 'ino64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for dev64_t],samba_cv_HAVE_DEV64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; dev64_t s; if (sizeof(dev_t) == sizeof(dev64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_DEV64_T=yes,samba_cv_HAVE_DEV64_T=no,samba_cv_HAVE_DEV64_T=cross)])
+if test x"$samba_cv_HAVE_DEV64_T" = x"yes"; then
+    AC_DEFINE(HAVE_DEV64_T,1,[Whether the 'dev64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for struct dirent64],samba_cv_HAVE_STRUCT_DIRENT64,[
+AC_TRY_COMPILE([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <dirent.h>],
+[struct dirent64 de;],
+samba_cv_HAVE_STRUCT_DIRENT64=yes,samba_cv_HAVE_STRUCT_DIRENT64=no)])
+if test x"$samba_cv_HAVE_STRUCT_DIRENT64" = x"yes" && test x"$ac_cv_func_readdir64" = x"yes"; then
+    AC_DEFINE(HAVE_STRUCT_DIRENT64,1,[Whether the 'dirent64' struct is available])
+fi
+
+AC_CACHE_CHECK([for major macro],samba_cv_HAVE_DEVICE_MAJOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = major(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MAJOR_FN=yes,samba_cv_HAVE_DEVICE_MAJOR_FN=no,samba_cv_HAVE_DEVICE_MAJOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MAJOR_FN" = x"yes"; then
+    AC_DEFINE(HAVE_DEVICE_MAJOR_FN,1,[Whether the major macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for minor macro],samba_cv_HAVE_DEVICE_MINOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = minor(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MINOR_FN=yes,samba_cv_HAVE_DEVICE_MINOR_FN=no,samba_cv_HAVE_DEVICE_MINOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MINOR_FN" = x"yes"; then
+    AC_DEFINE(HAVE_DEVICE_MINOR_FN,1,[Whether the minor macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for unsigned char],samba_cv_HAVE_UNSIGNED_CHAR,[
+AC_TRY_RUN([#include <stdio.h>
+main() { char c; c=250; exit((c > 0)?0:1); }],
+samba_cv_HAVE_UNSIGNED_CHAR=yes,samba_cv_HAVE_UNSIGNED_CHAR=no,samba_cv_HAVE_UNSIGNED_CHAR=cross)])
+if test x"$samba_cv_HAVE_UNSIGNED_CHAR" = x"yes"; then
+    AC_DEFINE(HAVE_UNSIGNED_CHAR,1,[Whether the 'unsigned char' type is available])
+fi
+
+AC_CACHE_CHECK([for sin_len in sock],samba_cv_HAVE_SOCK_SIN_LEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>],
+[struct sockaddr_in sock; sock.sin_len = sizeof(sock);],
+samba_cv_HAVE_SOCK_SIN_LEN=yes,samba_cv_HAVE_SOCK_SIN_LEN=no)])
+if test x"$samba_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
+    AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
+fi
+
+AC_CACHE_CHECK([whether seekdir returns void],samba_cv_SEEKDIR_RETURNS_VOID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <dirent.h>
+void seekdir(DIR *d, long loc) { return; }],[return 0;],
+samba_cv_SEEKDIR_RETURNS_VOID=yes,samba_cv_SEEKDIR_RETURNS_VOID=no)])
+if test x"$samba_cv_SEEKDIR_RETURNS_VOID" = x"yes"; then
+    AC_DEFINE(SEEKDIR_RETURNS_VOID,1,[Whether seekdir returns void])
+fi
+
+AC_CACHE_CHECK([for __FUNCTION__ macro],samba_cv_HAVE_FUNCTION_MACRO,[
+AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
+samba_cv_HAVE_FUNCTION_MACRO=yes,samba_cv_HAVE_FUNCTION_MACRO=no)])
+if test x"$samba_cv_HAVE_FUNCTION_MACRO" = x"yes"; then
+    AC_DEFINE(HAVE_FUNCTION_MACRO,1,[Whether there is a __FUNCTION__ macro])
+fi
+
+AC_CACHE_CHECK([if gettimeofday takes tz argument],samba_cv_HAVE_GETTIMEOFDAY_TZ,[
+AC_TRY_RUN([
+#include <sys/time.h>
+#include <unistd.h>
+main() { struct timeval tv; exit(gettimeofday(&tv, NULL));}],
+           samba_cv_HAVE_GETTIMEOFDAY_TZ=yes,samba_cv_HAVE_GETTIMEOFDAY_TZ=no,samba_cv_HAVE_GETTIMEOFDAY_TZ=cross)])
+if test x"$samba_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then
+    AC_DEFINE(HAVE_GETTIMEOFDAY_TZ,1,[Whether gettimeofday() is available])
+fi
+
+AC_CACHE_CHECK([for __va_copy],samba_cv_HAVE_VA_COPY,[
+AC_TRY_LINK([#include <stdarg.h>
+va_list ap1,ap2;], [__va_copy(ap1,ap2);],
+samba_cv_HAVE_VA_COPY=yes,samba_cv_HAVE_VA_COPY=no)])
+if test x"$samba_cv_HAVE_VA_COPY" = x"yes"; then
+    AC_DEFINE(HAVE_VA_COPY,1,[Whether __va_copy() is available])
+fi
+
+AC_CACHE_CHECK([for C99 vsnprintf],samba_cv_HAVE_C99_VSNPRINTF,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <stdarg.h>
+void foo(const char *format, ...) { 
+       va_list ap;
+       int len;
+       char buf[5];
+
+       va_start(ap, format);
+       len = vsnprintf(buf, 0, format, ap);
+       va_end(ap);
+       if (len != 5) exit(1);
+
+       va_start(ap, format);
+       len = vsnprintf(0, 0, format, ap);
+       va_end(ap);
+       if (len != 5) exit(1);
+
+       if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
+
+       exit(0);
+}
+main() { foo("hello"); }
+],
+samba_cv_HAVE_C99_VSNPRINTF=yes,samba_cv_HAVE_C99_VSNPRINTF=no,samba_cv_HAVE_C99_VSNPRINTF=cross)])
+if test x"$samba_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
+    AC_DEFINE(HAVE_C99_VSNPRINTF,1,[Whether there is a C99 compliant vsnprintf])
+fi
+
+AC_CACHE_CHECK([for broken readdir],samba_cv_HAVE_BROKEN_READDIR,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <dirent.h>
+main() { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
+if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
+di->d_name[0] == 0) exit(0); exit(1);} ],
+samba_cv_HAVE_BROKEN_READDIR=yes,samba_cv_HAVE_BROKEN_READDIR=no,samba_cv_HAVE_BROKEN_READDIR=cross)])
+if test x"$samba_cv_HAVE_BROKEN_READDIR" = x"yes"; then
+    AC_DEFINE(HAVE_BROKEN_READDIR,1,[Whether readdir() is broken])
+fi
+
+AC_CACHE_CHECK([for utimbuf],samba_cv_HAVE_UTIMBUF,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utime.h>],
+[struct utimbuf tbuf;  tbuf.actime = 0; tbuf.modtime = 1; exit(utime("foo.c",&tbuf));],
+samba_cv_HAVE_UTIMBUF=yes,samba_cv_HAVE_UTIMBUF=no,samba_cv_HAVE_UTIMBUF=cross)])
+if test x"$samba_cv_HAVE_UTIMBUF" = x"yes"; then
+    AC_DEFINE(HAVE_UTIMBUF,1,[Whether struct utimbuf is available])
+fi
+
+dnl  utmp and utmpx come in many flavours
+dnl  We need to check for many of them
+dnl  But we don't need to do each and every one, because our code uses
+dnl  mostly just the utmp (not utmpx) fields.
+
+AC_CHECK_FUNCS(pututline pututxline updwtmp updwtmpx getutmpx)
+
+AC_CACHE_CHECK([for ut_name in utmp],samba_cv_HAVE_UT_UT_NAME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_name[0] = 'a';],
+samba_cv_HAVE_UT_UT_NAME=yes,samba_cv_HAVE_UT_UT_NAME=no,samba_cv_HAVE_UT_UT_NAME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_NAME" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_NAME,1,[Whether the utmp struct has a property ut_name])
+fi 
+
+AC_CACHE_CHECK([for ut_user in utmp],samba_cv_HAVE_UT_UT_USER,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_user[0] = 'a';],
+samba_cv_HAVE_UT_UT_USER=yes,samba_cv_HAVE_UT_UT_USER=no,samba_cv_HAVE_UT_UT_USER=cross)])
+if test x"$samba_cv_HAVE_UT_UT_USER" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_USER,1,[Whether the utmp struct has a property ut_user])
+fi 
+
+AC_CACHE_CHECK([for ut_id in utmp],samba_cv_HAVE_UT_UT_ID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_id[0] = 'a';],
+samba_cv_HAVE_UT_UT_ID=yes,samba_cv_HAVE_UT_UT_ID=no,samba_cv_HAVE_UT_UT_ID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ID" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_ID,1,[Whether the utmp struct has a property ut_id])
+fi 
+
+AC_CACHE_CHECK([for ut_host in utmp],samba_cv_HAVE_UT_UT_HOST,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_host[0] = 'a';],
+samba_cv_HAVE_UT_UT_HOST=yes,samba_cv_HAVE_UT_UT_HOST=no,samba_cv_HAVE_UT_UT_HOST=cross)])
+if test x"$samba_cv_HAVE_UT_UT_HOST" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_HOST,1,[Whether the utmp struct has a property ut_host])
+fi 
+
+AC_CACHE_CHECK([for ut_time in utmp],samba_cv_HAVE_UT_UT_TIME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  time_t t; ut.ut_time = t;],
+samba_cv_HAVE_UT_UT_TIME=yes,samba_cv_HAVE_UT_UT_TIME=no,samba_cv_HAVE_UT_UT_TIME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TIME" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_TIME,1,[Whether the utmp struct has a property ut_time])
+fi 
+
+AC_CACHE_CHECK([for ut_tv in utmp],samba_cv_HAVE_UT_UT_TV,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  struct timeval tv; ut.ut_tv = tv;],
+samba_cv_HAVE_UT_UT_TV=yes,samba_cv_HAVE_UT_UT_TV=no,samba_cv_HAVE_UT_UT_TV=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TV" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_TV,1,[Whether the utmp struct has a property ut_tv])
+fi 
+
+AC_CACHE_CHECK([for ut_type in utmp],samba_cv_HAVE_UT_UT_TYPE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_type = 0;],
+samba_cv_HAVE_UT_UT_TYPE=yes,samba_cv_HAVE_UT_UT_TYPE=no,samba_cv_HAVE_UT_UT_TYPE=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TYPE" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_TYPE,1,[Whether the utmp struct has a property ut_type])
+fi 
+
+AC_CACHE_CHECK([for ut_pid in utmp],samba_cv_HAVE_UT_UT_PID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_pid = 0;],
+samba_cv_HAVE_UT_UT_PID=yes,samba_cv_HAVE_UT_UT_PID=no,samba_cv_HAVE_UT_UT_PID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_PID" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_PID,1,[Whether the utmp struct has a property ut_pid])
+fi 
+
+AC_CACHE_CHECK([for ut_exit in utmp],samba_cv_HAVE_UT_UT_EXIT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_exit.e_exit = 0;],
+samba_cv_HAVE_UT_UT_EXIT=yes,samba_cv_HAVE_UT_UT_EXIT=no,samba_cv_HAVE_UT_UT_EXIT=cross)])
+if test x"$samba_cv_HAVE_UT_UT_EXIT" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_EXIT,1,[Whether the utmp struct has a property ut_exit])
+fi 
+
+AC_CACHE_CHECK([for ut_addr in utmp],samba_cv_HAVE_UT_UT_ADDR,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut;  ut.ut_addr = 0;],
+samba_cv_HAVE_UT_UT_ADDR=yes,samba_cv_HAVE_UT_UT_ADDR=no,samba_cv_HAVE_UT_UT_ADDR=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ADDR" = x"yes"; then
+    AC_DEFINE(HAVE_UT_UT_ADDR,1,[Whether the utmp struct has a property ut_addr])
+fi 
+
+if test x$ac_cv_func_pututline = xyes ; then
+  AC_CACHE_CHECK([whether pututline returns pointer],samba_cv_PUTUTLINE_RETURNS_UTMP,[
+  AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+  [struct utmp utarg; struct utmp *utreturn; utreturn = pututline(&utarg);],
+  samba_cv_PUTUTLINE_RETURNS_UTMP=yes,samba_cv_PUTUTLINE_RETURNS_UTMP=no)])
+  if test x"$samba_cv_PUTUTLINE_RETURNS_UTMP" = x"yes"; then
+      AC_DEFINE(PUTUTLINE_RETURNS_UTMP,1,[Whether pututline returns pointer])
+  fi
+fi
+
+AC_CACHE_CHECK([for ut_syslen in utmpx],samba_cv_HAVE_UX_UT_SYSLEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmpx.h>],
+[struct utmpx ux;  ux.ut_syslen = 0;],
+samba_cv_HAVE_UX_UT_SYSLEN=yes,samba_cv_HAVE_UX_UT_SYSLEN=no,samba_cv_HAVE_UX_UT_SYSLEN=cross)])
+if test x"$samba_cv_HAVE_UX_UT_SYSLEN" = x"yes"; then
+    AC_DEFINE(HAVE_UX_UT_SYSLEN,1,[Whether the utmpx struct has a property ut_syslen])
+fi 
+
+
+ICONV_LOCATION=standard
+LOOK_DIRS="/usr /usr/local /sw"
+AC_ARG_WITH(libiconv,
+[  --with-libiconv=BASEDIR Use libiconv in BASEDIR/lib and BASEDIR/include (default=auto) ],
+[
+  if test "$withval" = "no" ; then
+    AC_MSG_ERROR(I won't take no for an answer)
+  else
+     if test "$withval" != "yes" ; then
+        LOOK_DIRS="$withval $LOOK_DIRS"
+     fi
+  fi
+])
+
+ICONV_FOUND="no"
+for i in $LOOK_DIRS ; do
+    save_LIBS=$LIBS
+    save_LDFLAGS=$LDFLAGS
+    save_CPPFLAGS=$CPPFLAGS
+    CPPFLAGS="-I$i/include"
+    LDFLAGS="-L$i/lib"
+    LIBS=
+    export LDFLAGS LIBS CPPFLAGS
+dnl Try to find iconv(3)
+    jm_ICONV($i)
+
+    CPPFLAGS=$save_CPPFLAGS
+    if test -n "$ICONV_FOUND" ; then
+        LDFLAGS=$save_LDFLAGS
+        LIB_ADD_DIR(LDFLAGS, "$i/lib")
+        CFLAGS_ADD_DIR(CPPFLAGS, "$i/include")
+        LIBS="$save_LIBS $LIBS"
+        ICONV_LOCATION=$i
+        export LDFLAGS LIBS CPPFLAGS
+        break
+    else
+       LDFLAGS=$save_LDFLAGS
+        LIBS=$save_LIBS
+        export LDFLAGS LIBS CPPFLAGS
+    fi
+done
+
+############
+# check for iconv in libc
+AC_CACHE_CHECK([for working iconv],samba_cv_HAVE_NATIVE_ICONV,[
+AC_TRY_RUN([
+#include <iconv.h>
+main() {
+       iconv_t cd = iconv_open("ASCII", "UCS-2LE");
+       if (cd == 0 || cd == (iconv_t)-1) return -1;
+       return 0;
+}
+],
+samba_cv_HAVE_NATIVE_ICONV=yes,samba_cv_HAVE_NATIVE_ICONV=no,samba_cv_HAVE_NATIVE_ICONV=cross)])
+if test x"$samba_cv_HAVE_NATIVE_ICONV" = x"yes"; then
+    AC_DEFINE(HAVE_NATIVE_ICONV,1,[Whether to use native iconv])
+fi
+
+if test x"$ICONV_FOUND" = x"no" -o x"$samba_cv_HAVE_NATIVE_ICONV" != x"yes" ; then
+    AC_MSG_WARN([Sufficient support for iconv function was not found. 
+    Install libiconv from http://freshmeat.net/projects/libiconv/ for better charset compatibility!])
+fi
+
+
+AC_CACHE_CHECK([for Linux kernel oplocks],samba_cv_HAVE_KERNEL_OPLOCKS_LINUX,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#ifndef F_GETLEASE
+#define F_GETLEASE     1025
+#endif
+main() {
+       int fd = open("/dev/null", O_RDONLY);
+       return fcntl(fd, F_GETLEASE, 0) == -1;
+}
+],
+samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=no,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=cross)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_LINUX" = x"yes"; then
+    AC_DEFINE(HAVE_KERNEL_OPLOCKS_LINUX,1,[Whether to use linux kernel oplocks])
+fi
+
+AC_CACHE_CHECK([for kernel change notify support],samba_cv_HAVE_KERNEL_CHANGE_NOTIFY,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+main() {
+               exit(fcntl(open("/tmp", O_RDONLY), F_NOTIFY, 0) == -1 ?  1 : 0);
+}
+],
+samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=yes,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=no,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=cross)])
+if test x"$samba_cv_HAVE_KERNEL_CHANGE_NOTIFY" = x"yes"; then
+    AC_DEFINE(HAVE_KERNEL_CHANGE_NOTIFY,1,[Whether kernel notifies changes])
+fi
+
+AC_CACHE_CHECK([for kernel share modes],samba_cv_HAVE_KERNEL_SHARE_MODES,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/file.h>
+#ifndef LOCK_MAND
+#define LOCK_MAND      32
+#define LOCK_READ      64
+#endif
+main() {
+               exit(flock(open("/dev/null", O_RDWR), LOCK_MAND|LOCK_READ) != 0);
+}
+],
+samba_cv_HAVE_KERNEL_SHARE_MODES=yes,samba_cv_HAVE_KERNEL_SHARE_MODES=no,samba_cv_HAVE_KERNEL_SHARE_MODES=cross)])
+if test x"$samba_cv_HAVE_KERNEL_SHARE_MODES" = x"yes"; then
+    AC_DEFINE(HAVE_KERNEL_SHARE_MODES,1,[Whether the kernel supports share modes])
+fi
+
+
+
+
+AC_CACHE_CHECK([for IRIX kernel oplock type definitions],samba_cv_HAVE_KERNEL_OPLOCKS_IRIX,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <fcntl.h>],
+[oplock_stat_t t; t.os_state = OP_REVOKE; t.os_dev = 1; t.os_ino = 1;],
+samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=no)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_IRIX" = x"yes"; then
+    AC_DEFINE(HAVE_KERNEL_OPLOCKS_IRIX,1,[Whether IRIX kernel oplock type definitions are available])
+fi
+
+AC_CACHE_CHECK([for irix specific capabilities],samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <sys/capability.h>
+main() {
+ cap_t cap;
+ if ((cap = cap_get_proc()) == NULL)
+   exit(1);
+ cap->cap_effective |= CAP_NETWORK_MGT;
+ cap->cap_inheritable |= CAP_NETWORK_MGT;
+ cap_set_proc(cap);
+ exit(0);
+}
+],
+samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=yes,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=no,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=cross)])
+if test x"$samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES" = x"yes"; then
+    AC_DEFINE(HAVE_IRIX_SPECIFIC_CAPABILITIES,1,[Whether IRIX specific capabilities are available])
+fi
+
+#
+# Check for int16, uint16, int32 and uint32 in rpc/types.h included from rpc/rpc.h
+# This is *really* broken but some systems (DEC OSF1) do this.... JRA.
+#
+
+AC_CACHE_CHECK([for int16 typedef included by rpc/rpc.h],samba_cv_HAVE_INT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int16 testvar;],
+samba_cv_HAVE_INT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT16_FROM_RPC_RPC_H" = x"yes"; then
+    AC_DEFINE(HAVE_INT16_FROM_RPC_RPC_H,1,[Whether int16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint16 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint16 testvar;],
+samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT16_FROM_RPC_RPC_H" = x"yes"; then
+    AC_DEFINE(HAVE_UINT16_FROM_RPC_RPC_H,1,[Whether uint16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for int32 typedef included by rpc/rpc.h],samba_cv_HAVE_INT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int32 testvar;],
+samba_cv_HAVE_INT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT32_FROM_RPC_RPC_H" = x"yes"; then
+    AC_DEFINE(HAVE_INT32_FROM_RPC_RPC_H,1,[Whether int32 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint32 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint32 testvar;],
+samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT32_FROM_RPC_RPC_H" = x"yes"; then
+    AC_DEFINE(HAVE_UINT32_FROM_RPC_RPC_H,1,[Whether uint32 typedef is included by rpc/rpc.h])
+fi
+
+dnl
+dnl Some systems (SCO) have a problem including
+dnl <prot.h> and <rpc/rpc.h> due to AUTH_ERROR being defined
+dnl as a #define in <prot.h> and as part of an enum
+dnl in <rpc/rpc.h>.
+dnl
+
+AC_CACHE_CHECK([for conflicting AUTH_ERROR define in rpc/rpc.h],samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#endif  /* HAVE_SYS_SECURITY_H */
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int testvar;],
+samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=no,samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=yes)])
+if test x"$samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT" = x"yes"; then
+    AC_DEFINE(HAVE_RPC_AUTH_ERROR_CONFLICT,1,[Whether there is a conflicting AUTH_ERROR define in rpc/rpc.h])
+fi
+
+AC_MSG_CHECKING([for test routines])
+AC_TRY_RUN([#include "${srcdir-.}/tests/trivial.c"],
+           AC_MSG_RESULT(yes),
+          AC_MSG_ERROR([cant find test code. Aborting config]),
+          AC_MSG_WARN([cannot run when cross-compiling]))
+
+AC_CACHE_CHECK([for ftruncate extend],samba_cv_HAVE_FTRUNCATE_EXTEND,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncate.c"],
+           samba_cv_HAVE_FTRUNCATE_EXTEND=yes,samba_cv_HAVE_FTRUNCATE_EXTEND=no,samba_cv_HAVE_FTRUNCATE_EXTEND=cross)])
+if test x"$samba_cv_HAVE_FTRUNCATE_EXTEND" = x"yes"; then
+    AC_DEFINE(HAVE_FTRUNCATE_EXTEND,1,[Truncate extend])
+fi
+
+AC_CACHE_CHECK([for AF_LOCAL socket support], samba_cv_HAVE_WORKING_AF_LOCAL, [
+AC_TRY_RUN([#include "${srcdir-.}/tests/unixsock.c"],
+          samba_cv_HAVE_WORKING_AF_LOCAL=yes,
+          samba_cv_HAVE_WORKING_AF_LOCAL=no,
+          samba_cv_HAVE_WORKING_AF_LOCAL=cross)])
+if test x"$samba_cv_HAVE_WORKING_AF_LOCAL" != xno
+then
+    AC_DEFINE(HAVE_WORKING_AF_LOCAL, 1, [Define if you have working AF_LOCAL sockets])
+fi
+
+AC_CACHE_CHECK([for broken getgroups],samba_cv_HAVE_BROKEN_GETGROUPS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/getgroups.c"],
+           samba_cv_HAVE_BROKEN_GETGROUPS=yes,samba_cv_HAVE_BROKEN_GETGROUPS=no,samba_cv_HAVE_BROKEN_GETGROUPS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_GETGROUPS" = x"yes"; then
+    AC_DEFINE(HAVE_BROKEN_GETGROUPS,1,[Whether getgroups is broken])
+fi
+
+AC_CACHE_CHECK([whether getpass should be replaced],samba_cv_REPLACE_GETPASS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt  -I${srcdir-.}/smbwrapper"
+AC_TRY_COMPILE([
+#define REPLACE_GETPASS 1
+#define NO_CONFIG_H 1
+#define main dont_declare_main
+#include "${srcdir-.}/lib/getsmbpass.c"
+#undef main
+],[],samba_cv_REPLACE_GETPASS=yes,samba_cv_REPLACE_GETPASS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_REPLACE_GETPASS" = x"yes"; then
+       AC_DEFINE(REPLACE_GETPASS,1,[Whether getpass should be replaced])
+fi
+
+AC_CACHE_CHECK([for broken inet_ntoa],samba_cv_REPLACE_INET_NTOA,[
+AC_TRY_RUN([
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+main() { struct in_addr ip; ip.s_addr = 0x12345678;
+if (strcmp(inet_ntoa(ip),"18.52.86.120") &&
+    strcmp(inet_ntoa(ip),"120.86.52.18")) { exit(0); } 
+exit(1);}],
+           samba_cv_REPLACE_INET_NTOA=yes,samba_cv_REPLACE_INET_NTOA=no,samba_cv_REPLACE_INET_NTOA=cross)])
+if test x"$samba_cv_REPLACE_INET_NTOA" = x"yes"; then
+    AC_DEFINE(REPLACE_INET_NTOA,1,[Whether inet_ntoa should be replaced])
+fi
+
+AC_CACHE_CHECK([for secure mkstemp],samba_cv_HAVE_SECURE_MKSTEMP,[
+AC_TRY_RUN([#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+main() { 
+  struct stat st;
+  char tpl[20]="/tmp/test.XXXXXX"; 
+  int fd = mkstemp(tpl); 
+  if (fd == -1) exit(1);
+  unlink(tpl);
+  if (fstat(fd, &st) != 0) exit(1);
+  if ((st.st_mode & 0777) != 0600) exit(1);
+  exit(0);
+}],
+samba_cv_HAVE_SECURE_MKSTEMP=yes,
+samba_cv_HAVE_SECURE_MKSTEMP=no,
+samba_cv_HAVE_SECURE_MKSTEMP=cross)])
+if test x"$samba_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
+    AC_DEFINE(HAVE_SECURE_MKSTEMP,1,[Whether mkstemp is secure])
+fi
+
+AC_CACHE_CHECK([for sysconf(_SC_NGROUPS_MAX)],samba_cv_SYSCONF_SC_NGROUPS_MAX,[
+AC_TRY_RUN([#include <unistd.h>
+main() { exit(sysconf(_SC_NGROUPS_MAX) == -1 ? 1 : 0); }],
+samba_cv_SYSCONF_SC_NGROUPS_MAX=yes,samba_cv_SYSCONF_SC_NGROUPS_MAX=no,samba_cv_SYSCONF_SC_NGROUPS_MAX=cross)])
+if test x"$samba_cv_SYSCONF_SC_NGROUPS_MAX" = x"yes"; then
+    AC_DEFINE(SYSCONF_SC_NGROUPS_MAX,1,[Whether sysconf(_SC_NGROUPS_MAX) is available])
+fi
+
+AC_CACHE_CHECK([for root],samba_cv_HAVE_ROOT,[
+AC_TRY_RUN([main() { exit(getuid() != 0); }],
+           samba_cv_HAVE_ROOT=yes,samba_cv_HAVE_ROOT=no,samba_cv_HAVE_ROOT=cross)])
+if test x"$samba_cv_HAVE_ROOT" = x"yes"; then
+    AC_DEFINE(HAVE_ROOT,1,[Whether current user is root])
+else
+    AC_MSG_WARN(running as non-root will disable some tests)
+fi
+
+##################
+# look for a method of finding the list of network interfaces
+iface=no;
+AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[
+AC_TRY_RUN([
+#define HAVE_IFACE_AIX 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+           samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)])
+if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available])
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFCONF 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+           samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available])
+fi
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFREQ 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+           samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available])
+fi
+fi
+
+
+################################################
+# look for a method of setting the effective uid
+seteuid=no;
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setresuid],samba_cv_USE_SETRESUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETRESUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+           samba_cv_USE_SETRESUID=yes,samba_cv_USE_SETRESUID=no,samba_cv_USE_SETRESUID=cross)])
+if test x"$samba_cv_USE_SETRESUID" = x"yes"; then
+    seteuid=yes;AC_DEFINE(USE_SETRESUID,1,[Whether setresuid() is available])
+fi
+fi
+
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setreuid],samba_cv_USE_SETREUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETREUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+           samba_cv_USE_SETREUID=yes,samba_cv_USE_SETREUID=no,samba_cv_USE_SETREUID=cross)])
+if test x"$samba_cv_USE_SETREUID" = x"yes"; then
+    seteuid=yes;AC_DEFINE(USE_SETREUID,1,[Whether setreuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for seteuid],samba_cv_USE_SETEUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETEUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+           samba_cv_USE_SETEUID=yes,samba_cv_USE_SETEUID=no,samba_cv_USE_SETEUID=cross)])
+if test x"$samba_cv_USE_SETEUID" = x"yes"; then
+    seteuid=yes;AC_DEFINE(USE_SETEUID,1,[Whether seteuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setuidx],samba_cv_USE_SETUIDX,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETUIDX 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+           samba_cv_USE_SETUIDX=yes,samba_cv_USE_SETUIDX=no,samba_cv_USE_SETUIDX=cross)])
+if test x"$samba_cv_USE_SETUIDX" = x"yes"; then
+    seteuid=yes;AC_DEFINE(USE_SETUIDX,1,[Whether setuidx() is available])
+fi
+fi
+
+
+AC_CACHE_CHECK([for working mmap],samba_cv_HAVE_MMAP,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/shared_mmap.c"],
+           samba_cv_HAVE_MMAP=yes,samba_cv_HAVE_MMAP=no,samba_cv_HAVE_MMAP=cross)])
+if test x"$samba_cv_HAVE_MMAP" = x"yes"; then
+    AC_DEFINE(HAVE_MMAP,1,[Whether mmap works])
+fi
+
+AC_CACHE_CHECK([for ftruncate needs root],samba_cv_FTRUNCATE_NEEDS_ROOT,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncroot.c"],
+           samba_cv_FTRUNCATE_NEEDS_ROOT=yes,samba_cv_FTRUNCATE_NEEDS_ROOT=no,samba_cv_FTRUNCATE_NEEDS_ROOT=cross)])
+if test x"$samba_cv_FTRUNCATE_NEEDS_ROOT" = x"yes"; then
+    AC_DEFINE(FTRUNCATE_NEEDS_ROOT,1,[Whether ftruncate() needs root])
+fi
+
+AC_CACHE_CHECK([for fcntl locking],samba_cv_HAVE_FCNTL_LOCK,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock.c"],
+           samba_cv_HAVE_FCNTL_LOCK=yes,samba_cv_HAVE_FCNTL_LOCK=no,samba_cv_HAVE_FCNTL_LOCK=cross)])
+if test x"$samba_cv_HAVE_FCNTL_LOCK" = x"yes"; then
+    AC_DEFINE(HAVE_FCNTL_LOCK,1,[Whether fcntl locking is available])
+fi
+
+AC_CACHE_CHECK([for broken (glibc2.1/x86) 64 bit fcntl locking],samba_cv_HAVE_BROKEN_FCNTL64_LOCKS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock64.c"],
+           samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=yes,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=no,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_FCNTL64_LOCKS" = x"yes"; then
+    AC_DEFINE(HAVE_BROKEN_FCNTL64_LOCKS,1,[Whether fcntl64 locks are broken])
+
+else
+
+dnl
+dnl Don't check for 64 bit fcntl locking if we know that the
+dnl glibc2.1 broken check has succeeded.
+dnl 
+
+  AC_CACHE_CHECK([for 64 bit fcntl locking],samba_cv_HAVE_STRUCT_FLOCK64,[
+  AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+main() { struct flock64 fl64;
+#if defined(F_SETLKW64) && defined(F_SETLK64) && defined(F_GETLK64)
+exit(0);
+#else
+exit(1);
+#endif
+}],
+       samba_cv_HAVE_STRUCT_FLOCK64=yes,samba_cv_HAVE_STRUCT_FLOCK64=no,samba_cv_HAVE_STRUCT_FLOCK64=cross)])
+
+  if test x"$samba_cv_HAVE_STRUCT_FLOCK64" = x"yes"; then
+      AC_DEFINE(HAVE_STRUCT_FLOCK64,1,[Whether the flock64 struct is available])
+  fi
+fi
+
+AC_CACHE_CHECK([for st_blocks in struct stat],samba_cv_HAVE_STAT_ST_BLOCKS,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st;  st.st_blocks = 0;],
+samba_cv_HAVE_STAT_ST_BLOCKS=yes,samba_cv_HAVE_STAT_ST_BLOCKS=no,samba_cv_HAVE_STAT_ST_BLOCKS=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLOCKS" = x"yes"; then
+    AC_DEFINE(HAVE_STAT_ST_BLOCKS,1,[Whether the stat struct has a st_block property])
+fi 
+
+AC_CACHE_CHECK([for st_blksize in struct stat],samba_cv_HAVE_STAT_ST_BLKSIZE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st;  st.st_blksize = 0;],
+samba_cv_HAVE_STAT_ST_BLKSIZE=yes,samba_cv_HAVE_STAT_ST_BLKSIZE=no,samba_cv_HAVE_STAT_ST_BLKSIZE=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLKSIZE" = x"yes"; then
+    AC_DEFINE(HAVE_STAT_ST_BLKSIZE,1,[Whether the stat struct has a st_blksize property])
+fi
+
+case "$host_os" in
+*linux*)
+AC_CACHE_CHECK([for broken RedHat 7.2 system header files],samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS,[
+AC_TRY_COMPILE([
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+],[int i;],
+   samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=no,samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=yes)])
+if test x"$samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS" = x"yes"; then
+   AC_DEFINE(BROKEN_REDHAT_7_SYSTEM_HEADERS,1,[Broken RedHat 7.2 system header files])
+fi
+;;
+esac
+
+AC_CACHE_CHECK([for broken nisplus include files],samba_cv_BROKEN_NISPLUS_INCLUDE_FILES,[
+AC_TRY_COMPILE([#include <sys/acl.h>
+#if defined(HAVE_RPCSVC_NIS_H)
+#include <rpcsvc/nis.h>
+#endif],
+[int i;],
+samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=no,samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=yes)])
+if test x"$samba_cv_BROKEN_NISPLUS_INCLUDE_FILES" = x"yes"; then
+       AC_DEFINE(BROKEN_NISPLUS_INCLUDE_FILES,1,[Whether the nisplus include files are broken])
+fi
+
+
+#################################################
+# check for smbwrapper support
+AC_MSG_CHECKING(whether to use smbwrapper)
+AC_ARG_WITH(smbwrapper,
+[  --with-smbwrapper       Include SMB wrapper support (default=no) ],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_SMBWRAPPER,1,[Whether to include smbwrapper support])
+       WRAPPROG="bin/smbsh\$(EXEEXT)"
+       WRAP="bin/smbwrapper.$SHLIBEXT"
+
+       if test x$ATTEMPT_WRAP32_BUILD = x; then
+               WRAP32=""
+       else
+                       WRAP32=bin/smbwrapper.32.$SHLIBEXT
+       fi
+
+# Conditions under which smbwrapper should not be built.
+
+       if test x$PICFLAG = x; then
+          echo No support for PIC code - disabling smbwrapper and smbsh
+          WRAPPROG=""
+          WRAP=""
+          WRAP32=""
+       elif test x$ac_cv_func_syscall = xno; then
+          AC_MSG_RESULT([No syscall() -- disabling smbwrapper and smbsh])
+          WRAPPROG=""
+          WRAP=""
+          WRAP32=""
+       fi
+       EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS $WRAPPROG $WRAP $WRAP32"
+       SMBWRAPPER="$WRAPPROG $WRAP $WRAP32"
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for AFS clear-text auth support
+AC_MSG_CHECKING(whether to use AFS clear-text auth)
+AC_ARG_WITH(afs,
+[  --with-afs              Include AFS clear-text auth support (default=no) ],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_AFS,1,[Whether to include AFS clear-text auth support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for pthread support
+AC_MSG_CHECKING(whether to use pthreads)
+AC_ARG_WITH(pthreads,
+[  --with-pthreads              Include pthreads (default=no) ],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_PTHREADS,1,[Whether to use pthreads])
+    SMBD_EXTRA_OBJS="smbd/process_thread.o"
+    SMBD_EXTRA_LIBS="-lpthread"
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+AC_SUBST(SMBD_EXTRA_OBJS)
+AC_SUBST(SMBD_EXTRA_LIBS)
+
+
+#################################################
+# check for STFS NTVFS backend
+STFS_ENABLED=#
+AC_MSG_CHECKING(whether to use STFS NTVFS backend)
+AC_ARG_WITH(stfs,
+[  --with-stfs              Include STFS NTVFS backend (default=no) ],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_NTVFS_STFS,1,[Whether to include STFS NTVFS backend])
+    STFS_ENABLED=
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+AC_SUBST(STFS_ENABLED)
+
+
+#################################################
+# check for the DFS clear-text auth system
+AC_MSG_CHECKING(whether to use DFS clear-text auth)
+AC_ARG_WITH(dfs,
+[  --with-dce-dfs          Include DCE/DFS clear-text auth support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_DFS,1,[Whether to include DFS support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# active directory support
+
+with_ads_support=yes
+AC_MSG_CHECKING([whether to use Active Directory])
+
+AC_ARG_WITH(ads,
+[   --with-ads  Active Directory support (default yes)],
+[ case "$withval" in
+    no)
+       with_ads_support=no
+       ;;
+  esac ])
+
+if test x"$with_ads_support" = x"yes"; then
+   AC_DEFINE(WITH_ADS,1,[Whether to include Active Directory support])
+fi
+
+AC_MSG_RESULT($with_ads_support)
+
+FOUND_KRB5=no
+if test x"$with_ads_support" = x"yes"; then
+
+  #################################################
+  # check for krb5-config from recent MIT and Heimdal kerberos 5
+  AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+  AC_MSG_CHECKING(for working krb5-config)
+  if test -x "$KRB5_CONFIG"; then
+    LIBS="$LIBS `$KRB5_CONFIG --libs`"
+    CFLAGS="$CFLAGS `$KRB5_CONFIG --cflags`" 
+    CPPFLAGS="$CPPFLAGS `$KRB5_CONFIG --cflags`"
+    FOUND_KRB5=yes
+    AC_MSG_RESULT(yes)
+  else
+    AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
+  fi
+  
+  if test x$FOUND_KRB5 = x"no"; then
+  #################################################
+  # check for location of Kerberos 5 install
+  AC_MSG_CHECKING(for kerberos 5 install path)
+  AC_ARG_WITH(krb5,
+  [  --with-krb5=base-dir    Locate Kerberos 5 support (default=/usr)],
+  [ case "$withval" in
+    no)
+      AC_MSG_RESULT(no)
+      ;;
+    *)
+      AC_MSG_RESULT(yes)
+      LIBS="$LIBS -lkrb5"
+      CFLAGS="$CFLAGS -I$withval/include"
+      CPPFLAGS="$CPPFLAGS -I$withval/include"
+      LDFLAGS="$LDFLAGS -L$withval/lib"
+      FOUND_KRB5=yes
+      ;;
+    esac ],
+    AC_MSG_RESULT(no)
+  )
+  fi
+
+if test x$FOUND_KRB5 = x"no"; then
+#################################################
+# see if this box has the SuSE location for the heimdal kerberos implementation
+AC_MSG_CHECKING(for /usr/include/heimdal)
+if test -d /usr/include/heimdal; then
+    if test -f /usr/lib/heimdal/lib/libkrb5.a; then
+        LIBS="$LIBS -lkrb5"
+        CFLAGS="$CFLAGS -I/usr/include/heimdal"
+        CPPFLAGS="$CPPFLAGS -I/usr/include/heimdal"
+        LDFLAGS="$LDFLAGS -L/usr/lib/heimdal/lib"
+        AC_MSG_RESULT(yes)
+    else
+        LIBS="$LIBS -lkrb5"
+        CFLAGS="$CFLAGS -I/usr/include/heimdal"
+        CPPFLAGS="$CPPFLAGS -I/usr/include/heimdal"
+        AC_MSG_RESULT(yes)
+    fi
+else
+    AC_MSG_RESULT(no)
+fi
+fi
+
+
+if test x$FOUND_KRB5 = x"no"; then
+#################################################
+# see if this box has the RedHat location for kerberos
+AC_MSG_CHECKING(for /usr/kerberos)
+if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
+    LIBS="$LIBS -lkrb5"
+    LDFLAGS="$LDFLAGS -L/usr/kerberos/lib"
+    CFLAGS="$CFLAGS -I/usr/kerberos/include"
+    CPPFLAGS="$CPPFLAGS -I/usr/kerberos/include"
+    AC_MSG_RESULT(yes)
+else
+    AC_MSG_RESULT(no)
+fi
+fi
+
+  # now check for krb5.h. Some systems have the libraries without the headers!
+  # note that this check is done here to allow for different kerberos
+  # include paths
+  AC_CHECK_HEADERS(krb5.h)
+
+  # now check for gssapi headers.  This is also done here to allow for
+  # different kerberos include paths
+  AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h)
+
+  ##################################################################
+  # we might need the k5crypto and com_err libraries on some systems
+  AC_CHECK_LIB(com_err, _et_list, [LIBS="$LIBS -lcom_err"])
+  AC_CHECK_LIB(k5crypto, krb5_encrypt_data, [LIBS="$LIBS -lk5crypto"])
+  # Heimdal checks.
+  AC_CHECK_LIB(crypto, des_set_key, [LIBS="$LIBS -lcrypto"])
+  AC_CHECK_LIB(asn1, copy_Authenticator, [LIBS="$LIBS -lasn1 -lroken"])
+  # Heimdal checks. On static Heimdal gssapi must be linked before krb5.
+  AC_CHECK_LIB(gssapi, gss_display_status, [LIBS="$LIBS -lgssapi -lkrb5 -lasn1";
+        AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])])
+
+  AC_CHECK_LIB(krb5, krb5_set_real_time, [AC_DEFINE(HAVE_KRB5_SET_REAL_TIME,1,[Whether krb5_set_real_time is available])])
+  AC_CHECK_LIB(krb5, krb5_set_default_in_tkt_etypes, [AC_DEFINE(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES,1,[Whether krb5_set_default_in_tkt_etypes, is available])])
+  AC_CHECK_LIB(krb5, krb5_set_default_tgs_ktypes, [AC_DEFINE(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES,1,[Whether krb5_set_default_tgs_ktypes is available])])
+
+  AC_CHECK_LIB(krb5, krb5_principal2salt, [AC_DEFINE(HAVE_KRB5_PRINCIPAL2SALT,1,[Whether krb5_principal2salt is available])])
+  AC_CHECK_LIB(krb5, krb5_use_enctype, [AC_DEFINE(HAVE_KRB5_USE_ENCTYPE,1,[Whether krb5_use_enctype is available])])
+  AC_CHECK_LIB(krb5, krb5_string_to_key, [AC_DEFINE(HAVE_KRB5_STRING_TO_KEY,1,[Whether krb5_string_to_key is available])])
+  AC_CHECK_LIB(krb5, krb5_get_pw_salt, [AC_DEFINE(HAVE_KRB5_GET_PW_SALT,1,[Whether krb5_get_pw_salt is available])])
+  AC_CHECK_LIB(krb5, krb5_string_to_key_salt, [AC_DEFINE(HAVE_KRB5_STRING_TO_KEY_SALT,1,[Whether krb5_string_to_key_salt is available])])
+  AC_CHECK_LIB(krb5, krb5_auth_con_setkey, [AC_DEFINE(HAVE_KRB5_AUTH_CON_SETKEY,1,[Whether krb5_auth_con_setkey is available])])
+  AC_CHECK_LIB(krb5, krb5_auth_con_setuseruserkey, [AC_DEFINE(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY,1,[Whether krb5_auth_con_setuseruserkey is available])])
+  AC_CHECK_LIB(krb5, krb5_locate_kdc, [AC_DEFINE(HAVE_KRB5_LOCATE_KDC,1,[Whether krb5_locate_kdc is available])])
+  AC_CHECK_LIB(krb5, krb5_get_permitted_enctypes, [AC_DEFINE(HAVE_KRB5_GET_PERMITTED_ENCTYPES,1,[Whether krb5_get_permitted_enctypes is available])])
+  AC_CHECK_LIB(krb5, krb5_get_default_in_tkt_etypes, [AC_DEFINE(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES,1,[Whether krb5_get_default_in_tkt_etypes is available])])
+  AC_CHECK_LIB(krb5, krb5_free_ktypes, [AC_DEFINE(HAVE_KRB5_FREE_KTYPES,1,[Whether krb5_free_ktypes is available])])
+
+AC_CACHE_CHECK([for addrtype in krb5_address],samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
+samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
+if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
+    AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,[Whether the krb5_address struct has a addrtype property])
+fi
+
+AC_CACHE_CHECK([for addr_type in krb5_address],samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
+samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
+if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
+    AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,[Whether the krb5_address struct has a addr_type property])
+fi
+
+AC_CACHE_CHECK([for enc_part2 in krb5_ticket],samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
+samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
+if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
+    AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,[Whether the krb5_ticket struct has a enc_part2 property])
+fi
+
+AC_CACHE_CHECK([for keyvalue in krb5_keyblock],samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_keyblock key; key.keyvalue.data = NULL;],
+samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
+if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
+    AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,[Whether the krb5_keyblock struct has a keyvalue property])
+fi
+
+AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
+AC_TRY_COMPILE([#include <krb5.h>],
+[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
+samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
+if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes"; then
+    AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
+fi
+
+  ########################################################
+  # now see if we can find the krb5 libs in standard paths
+  # or as specified above
+  AC_CHECK_LIB(krb5, krb5_mk_req_extended, [LIBS="$LIBS -lkrb5";
+        AC_DEFINE(HAVE_KRB5,1,[Whether KRB5 is available])])
+
+  ########################################################
+  # now see if we can find the gssapi libs in standard paths
+  AC_CHECK_LIB(gssapi_krb5, gss_display_status, [LIBS="$LIBS -lgssapi_krb5";
+        AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])])
+
+fi
+
+########################################################
+# Compile with LDAP support?
+
+with_ldap_support=yes
+AC_MSG_CHECKING([whether to use LDAP])
+
+AC_ARG_WITH(ldap,
+[   --with-ldap  LDAP support (default yes)],
+[ case "$withval" in
+    no)
+       with_ldap_support=no
+       ;;
+  esac ])
+
+AC_MSG_RESULT($with_ldap_support)
+
+if test x"$with_ldap_support" = x"yes"; then
+
+  ##################################################################
+  # we might need the lber lib on some systems. To avoid link errors
+  # this test must be before the libldap test
+  AC_CHECK_LIB(lber, ber_scanf, [LIBS="$LIBS -llber"])
+
+  ########################################################
+  # now see if we can find the ldap libs in standard paths
+  if test x$have_ldap != xyes; then
+  AC_CHECK_LIB(ldap, ldap_domain2hostlist, [LIBS="$LIBS -lldap";
+       AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])])
+
+       ########################################################
+       # If we have LDAP, does it's rebind procedure take 2 or 3 arguments?
+       # Check found in pam_ldap 145.
+       AC_CHECK_FUNCS(ldap_set_rebind_proc)
+       AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, pam_ldap_cv_ldap_set_rebind_proc, [
+       AC_TRY_COMPILE([
+       #include <lber.h>
+       #include <ldap.h>], [ldap_set_rebind_proc(0, 0, 0);], [pam_ldap_cv_ldap_set_rebind_proc=3], [pam_ldap_cv_ldap_set_rebind_proc=2]) ])
+       AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $pam_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc])
+  fi
+fi
+
+########################################################
+# Compile with MySQL support?
+AM_PATH_MYSQL([0.11.0],[MODULE_MYSQL=bin/mysql.so],[MODULE_MYSQL=])
+CFLAGS="$CFLAGS $MYSQL_CFLAGS"
+AC_SUBST(MODULE_MYSQL)
+
+########################################################
+# Compile with XML support?
+AM_PATH_XML2([2.0.0],[MODULE_XML=bin/xml.so],[MODULE_XML=])
+CFLAGS="$CFLAGS $XML_CFLAGS"
+AC_SUBST(MODULE_XML)
+
+#################################################
+# check for automount support
+AC_MSG_CHECKING(whether to use automount)
+AC_ARG_WITH(automount,
+[  --with-automount        Include automount support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_AUTOMOUNT,1,[Whether to include automount support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for smbmount support
+AC_MSG_CHECKING(whether to use smbmount)
+AC_ARG_WITH(smbmount,
+[  --with-smbmount         Include smbmount (Linux only) support (default=no)],
+[ case "$withval" in
+  yes)
+       case "$host_os" in
+       *linux*)
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(WITH_SMBMOUNT,1,[Whether to build smbmount])
+               EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbmount bin/smbmnt bin/smbumount"
+               ;;
+       *)
+               AC_MSG_ERROR(not on a linux system!)
+               ;;
+       esac
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for a PAM clear-text auth, accounts, password and session support
+with_pam_for_crypt=no
+AC_MSG_CHECKING(whether to use PAM)
+AC_ARG_WITH(pam,
+[  --with-pam              Include PAM support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_PAM,1,[Whether to include PAM support])
+    AUTHLIBS="$AUTHLIBS -lpam"
+    with_pam_for_crypt=yes
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+# we can't build a pam module if we don't have pam.
+AC_CHECK_LIB(pam, pam_get_data, [AC_DEFINE(HAVE_LIBPAM,1,[Whether libpam is available])])
+
+#################################################
+# check for pam_smbpass support
+AC_MSG_CHECKING(whether to use pam_smbpass)
+AC_ARG_WITH(pam_smbpass,
+[  --with-pam_smbpass      Build a PAM module to allow other applications to use our smbpasswd file (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+
+# Conditions under which pam_smbpass should not be built.
+
+       if test x$PICFLAG = x; then
+          AC_MSG_RESULT([No support for PIC code - disabling pam_smbpass])
+       elif test x$ac_cv_lib_pam_pam_get_data = xno; then
+          AC_MSG_RESULT([No libpam found -- disabling pam_smbpass])
+       else
+          SHLIB_PROGS="$SHLIB_PROGS bin/pam_smbpass.so"
+       fi
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+
+###############################################
+# test for where we get crypt() from
+AC_SEARCH_LIBS(crypt, [crypt],
+  [test "$ac_cv_search_crypt" = "none required" || AUTHLIBS="-lcrypt $AUTHLIBS"
+  AC_DEFINE(HAVE_CRYPT,1,[Whether the system has the crypt() function])])
+
+##
+## moved after the check for -lcrypt in order to
+## ensure that the necessary libraries are included
+## check checking for truncated salt.  Wrapped by the
+## $with_pam_for_crypt variable as above   --jerry
+##
+if test $with_pam_for_crypt = no; then
+AC_CACHE_CHECK([for a crypt that needs truncated salt],samba_cv_HAVE_TRUNCATED_SALT,[
+crypt_LIBS="$LIBS"
+LIBS="$AUTHLIBS $LIBS"
+AC_TRY_RUN([#include "${srcdir-.}/tests/crypttest.c"],
+       samba_cv_HAVE_TRUNCATED_SALT=no,samba_cv_HAVE_TRUNCATED_SALT=yes,samba_cv_HAVE_TRUNCATED_SALT=cross)
+LIBS="$crypt_LIBS"])
+if test x"$samba_cv_HAVE_TRUNCATED_SALT" = x"yes"; then
+       AC_DEFINE(HAVE_TRUNCATED_SALT,1,[Whether crypt needs truncated salt])
+fi
+fi
+
+# New experimental SAM system
+
+AC_MSG_CHECKING([whether to build the new (experimental) SAM database])
+AC_ARG_WITH(sam,
+[  --with-sam              Build new (experimental) SAM database (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_SAM,1,[Whether to build the new (experimental) SAM database])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+
+########################################################################################
+##
+## TESTS FOR SAM BACKENDS.  KEEP THESE GROUPED TOGETHER
+##
+########################################################################################
+
+#################################################
+# check for a LDAP password database configuration backwards compatibility
+AC_MSG_CHECKING(whether to use LDAP SAM 2.2 compatible configuration)
+AC_ARG_WITH(ldapsam,
+[  --with-ldapsam           Include LDAP SAM 2.2 compatible configuration (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_LDAP_SAMCONFIG,1,[Whether to include 2.2 compatibel LDAP SAM configuration])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for a TDB password database
+AC_MSG_CHECKING(whether to use TDB SAM database)
+AC_ARG_WITH(tdbsam,
+[  --with-tdbsam           Include experimental TDB SAM support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_TDB_SAM,1,[Whether to include experimental TDB SAM support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for a NISPLUS password database
+AC_MSG_CHECKING(whether to use NISPLUS SAM database)
+AC_ARG_WITH(nisplussam,
+[  --with-nisplussam       Include NISPLUS SAM support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_NISPLUS_SAM,1,[Whether to include nisplus SAM support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+########################################################################################
+##
+## END OF TESTS FOR SAM BACKENDS.  
+##
+########################################################################################
+
+#################################################
+# check for a NISPLUS_HOME support 
+AC_MSG_CHECKING(whether to use NISPLUS_HOME)
+AC_ARG_WITH(nisplus-home,
+[  --with-nisplus-home     Include NISPLUS_HOME support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_NISPLUS_HOME,1,[Whether to include nisplus_home support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for syslog logging
+AC_MSG_CHECKING(whether to use syslog logging)
+AC_ARG_WITH(syslog,
+[  --with-syslog           Include experimental SYSLOG support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_SYSLOG,1,[Whether to include experimental syslog support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for experimental disk-quotas support
+QUOTAOBJS=smbd/noquotas.o
+
+AC_MSG_CHECKING(whether to support disk-quotas)
+AC_ARG_WITH(quotas,
+[  --with-quotas           Include experimental disk-quota support (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    case "$host_os" in
+      *linux*)
+        # Check for kernel 2.4.x quota braindamage...
+        AC_CACHE_CHECK([for linux 2.4.x quota braindamage..],samba_cv_linux_2_4_quota_braindamage, [
+        AC_TRY_COMPILE([#include <stdio.h>
+#include <sys/types.h>
+#include <asm/types.h>
+#include <linux/quota.h>
+#include <mntent.h>
+#include <linux/unistd.h>],[struct mem_dqblk D;],
+      samba_cv_linux_2_4_quota_braindamage=yes,samba_cv_linux_2_4_quota_braindamage=no)])
+if test x"$samba_cv_linux_2_4_quota_braindamage" = x"yes"; then
+        AC_DEFINE(LINUX_QUOTAS_2,1,[linux 2.4.x quota braindamage])
+else
+        AC_DEFINE(LINUX_QUOTAS_1,1,[linux quotas])
+fi
+        ;;
+      *)
+        ;;
+    esac
+    QUOTAOBJS=smbd/quotas.o
+    AC_DEFINE(WITH_QUOTAS,1,[Whether to include experimental quota support])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+AC_SUBST(QUOTAOBJS)
+
+#################################################
+# check for experimental utmp accounting
+
+AC_MSG_CHECKING(whether to support utmp accounting)
+AC_ARG_WITH(utmp,
+[  --with-utmp             Include experimental utmp accounting (default=no)],
+[ case "$withval" in
+  yes)
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(WITH_UTMP,1,[Whether to include experimental utmp accounting])
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# choose native language(s) of man pages
+AC_MSG_CHECKING(chosen man pages' language(s))
+AC_ARG_WITH(manpages-langs,
+[  --with-manpages-langs={en,ja,pl}  Choose man pages' language(s). (en)],
+[ case "$withval" in
+  yes|no)
+    AC_MSG_WARN(--with-manpages-langs called without argument - will use default)
+    manlangs="en"
+  ;;
+  *)
+    manlangs="$withval"
+  ;;
+  esac
+
+  AC_MSG_RESULT($manlangs)
+  manlangs=`echo $manlangs | sed "s/,/ /g"`   # replacing commas with spaces to produce a list
+  AC_SUBST(manlangs)],
+
+  [manlangs="en"
+  AC_MSG_RESULT($manlangs)
+  AC_SUBST(manlangs)]
+)
+
+#################################################
+# should we build libsmbclient?
+
+INSTALLCLIENTCMD_SH=:
+INSTALLCLIENTCMD_A=:
+LIBSMBCLIENT_SHARED=
+LIBSMBCLIENT=
+AC_MSG_CHECKING(whether to build the libsmbclient shared library)
+AC_ARG_WITH(libsmbclient,
+[  --with-libsmbclient     Build the libsmbclient shared library (default=yes if shared libs supported)],
+[ case "$withval" in
+  no) 
+     AC_MSG_RESULT(no)
+     ;;
+  *)
+     if test $BLDSHARED = true; then
+        INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+        LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+        LIBSMBCLIENT=libsmbclient
+        AC_MSG_RESULT(yes)
+     else
+       enable_static=yes
+        AC_MSG_RESULT(no shared library support -- will supply static library)
+     fi
+     if test $enable_static = yes; then
+        INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+        LIBSMBCLIENT=libsmbclient
+     fi
+     ;;
+  esac ],
+[
+# if unspecified, default is to built it iff possible.
+  if test $BLDSHARED = true; then
+     INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+     LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+     LIBSMBCLIENT=libsmbclient
+     AC_MSG_RESULT(yes)
+   else
+     enable_static=yes
+     AC_MSG_RESULT(no shared library support -- will supply static library)
+   fi
+   if test $enable_static = yes; then
+     INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+     LIBSMBCLIENT=libsmbclient
+  fi]
+)
+
+
+#################################################
+# these tests are taken from the GNU fileutils package
+AC_CHECKING(how to get filesystem space usage)
+space=no
+
+# Test for statvfs64.
+if test $space = no; then
+  # SVR4
+  AC_CACHE_CHECK([statvfs64 function (SVR4)], fu_cv_sys_stat_statvfs64,
+  [AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/statvfs.h>
+  main ()
+  {
+    struct statvfs64 fsd;
+    exit (statvfs64 (".", &fsd));
+  }],
+  fu_cv_sys_stat_statvfs64=yes,
+  fu_cv_sys_stat_statvfs64=no,
+  fu_cv_sys_stat_statvfs64=cross)])
+  if test $fu_cv_sys_stat_statvfs64 = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATVFS64,1,[Whether statvfs64() is available])
+  fi
+fi
+
+# Perform only the link test since it seems there are no variants of the
+# statvfs function.  This check is more than just AC_CHECK_FUNCS(statvfs)
+# because that got a false positive on SCO OSR5.  Adding the declaration
+# of a `struct statvfs' causes this test to fail (as it should) on such
+# systems.  That system is reported to work fine with STAT_STATFS4 which
+# is what it gets when this test fails.
+if test $space = no; then
+  # SVR4
+  AC_CACHE_CHECK([statvfs function (SVR4)], fu_cv_sys_stat_statvfs,
+                [AC_TRY_LINK([#include <sys/types.h>
+#include <sys/statvfs.h>],
+                             [struct statvfs fsd; statvfs (0, &fsd);],
+                             fu_cv_sys_stat_statvfs=yes,
+                             fu_cv_sys_stat_statvfs=no)])
+  if test $fu_cv_sys_stat_statvfs = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATVFS,1,[Whether statvfs() is available])
+  fi
+fi
+
+if test $space = no; then
+  # DEC Alpha running OSF/1
+  AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)])
+  AC_CACHE_VAL(fu_cv_sys_stat_statfs3_osf1,
+  [AC_TRY_RUN([
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+  main ()
+  {
+    struct statfs fsd;
+    fsd.f_fsize = 0;
+    exit (statfs (".", &fsd, sizeof (struct statfs)));
+  }],
+  fu_cv_sys_stat_statfs3_osf1=yes,
+  fu_cv_sys_stat_statfs3_osf1=no,
+  fu_cv_sys_stat_statfs3_osf1=no)])
+  AC_MSG_RESULT($fu_cv_sys_stat_statfs3_osf1)
+  if test $fu_cv_sys_stat_statfs3_osf1 = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATFS3_OSF1,1,[Whether statfs requires 3 arguments])
+  fi
+fi
+
+if test $space = no; then
+# AIX
+  AC_MSG_CHECKING([for two-argument statfs with statfs.bsize dnl
+member (AIX, 4.3BSD)])
+  AC_CACHE_VAL(fu_cv_sys_stat_statfs2_bsize,
+  [AC_TRY_RUN([
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+  main ()
+  {
+  struct statfs fsd;
+  fsd.f_bsize = 0;
+  exit (statfs (".", &fsd));
+  }],
+  fu_cv_sys_stat_statfs2_bsize=yes,
+  fu_cv_sys_stat_statfs2_bsize=no,
+  fu_cv_sys_stat_statfs2_bsize=no)])
+  AC_MSG_RESULT($fu_cv_sys_stat_statfs2_bsize)
+  if test $fu_cv_sys_stat_statfs2_bsize = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATFS2_BSIZE,1,[Whether statfs requires two arguments and struct statfs has bsize property])
+  fi
+fi
+
+if test $space = no; then
+# SVR3
+  AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)])
+  AC_CACHE_VAL(fu_cv_sys_stat_statfs4,
+  [AC_TRY_RUN([#include <sys/types.h>
+#include <sys/statfs.h>
+  main ()
+  {
+  struct statfs fsd;
+  exit (statfs (".", &fsd, sizeof fsd, 0));
+  }],
+    fu_cv_sys_stat_statfs4=yes,
+    fu_cv_sys_stat_statfs4=no,
+    fu_cv_sys_stat_statfs4=no)])
+  AC_MSG_RESULT($fu_cv_sys_stat_statfs4)
+  if test $fu_cv_sys_stat_statfs4 = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATFS4,1,[Whether statfs requires 4 arguments])
+  fi
+fi
+
+if test $space = no; then
+# 4.4BSD and NetBSD
+  AC_MSG_CHECKING([for two-argument statfs with statfs.fsize dnl
+member (4.4BSD and NetBSD)])
+  AC_CACHE_VAL(fu_cv_sys_stat_statfs2_fsize,
+  [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+  main ()
+  {
+  struct statfs fsd;
+  fsd.f_fsize = 0;
+  exit (statfs (".", &fsd));
+  }],
+  fu_cv_sys_stat_statfs2_fsize=yes,
+  fu_cv_sys_stat_statfs2_fsize=no,
+  fu_cv_sys_stat_statfs2_fsize=no)])
+  AC_MSG_RESULT($fu_cv_sys_stat_statfs2_fsize)
+  if test $fu_cv_sys_stat_statfs2_fsize = yes; then
+    space=yes
+       AC_DEFINE(STAT_STATFS2_FSIZE,1,[Whether statfs requires 2 arguments and struct statfs has fsize])
+  fi
+fi
+
+if test $space = no; then
+  # Ultrix
+  AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)])
+  AC_CACHE_VAL(fu_cv_sys_stat_fs_data,
+  [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+  main ()
+  {
+  struct fs_data fsd;
+  /* Ultrix's statfs returns 1 for success,
+     0 for not mounted, -1 for failure.  */
+  exit (statfs (".", &fsd) != 1);
+  }],
+  fu_cv_sys_stat_fs_data=yes,
+  fu_cv_sys_stat_fs_data=no,
+  fu_cv_sys_stat_fs_data=no)])
+  AC_MSG_RESULT($fu_cv_sys_stat_fs_data)
+  if test $fu_cv_sys_stat_fs_data = yes; then
+    space=yes
+    AC_DEFINE(STAT_STATFS2_FS_DATA,1,[Whether statfs requires 2 arguments and struct fs_data is available])
+  fi
+fi
+
+#
+# As a gating factor for large file support, in order to
+# use <4GB files we must have the following minimal support
+# available.
+# long long, and a 64 bit off_t or off64_t.
+# If we don't have all of these then disable large
+# file support.
+#
+AC_MSG_CHECKING([if large file support can be enabled])
+AC_TRY_COMPILE([
+#if defined(HAVE_LONGLONG) && (defined(HAVE_OFF64_T) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8)))
+#include <sys/types.h>
+#else
+__COMPILE_ERROR_
+#endif
+],
+[int i],
+samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=yes,samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=no)
+if test x"$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT" = x"yes"; then
+       AC_DEFINE(HAVE_EXPLICIT_LARGEFILE_SUPPORT,1,[Whether large file support can be enabled])
+fi
+AC_MSG_RESULT([$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT])
+
+#################################################
+# check for ACL support
+
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acl-support,
+[  --with-acl-support      Include ACL support (default=no)],
+[ case "$withval" in
+  yes)
+
+       case "$host_os" in
+       *sysv5*)
+               AC_MSG_RESULT(Using UnixWare ACLs)
+               AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+               ;;
+       *solaris*)
+               AC_MSG_RESULT(Using solaris ACLs)
+               AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+               ;;
+       *hpux*)
+               AC_MSG_RESULT(Using HPUX ACLs)
+               AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+               ;;
+       *irix*)
+               AC_MSG_RESULT(Using IRIX ACLs)
+               AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+               ;;
+       *aix*)
+               AC_MSG_RESULT(Using AIX ACLs)
+               AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+               ;;
+       *osf*)
+               AC_MSG_RESULT(Using Tru64 ACLs)
+               AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+               ACLLIBS="$ACLLIBS -lpacl"
+               ;;
+        *)
+               AC_CHECK_LIB(acl,acl_get_file,[ACLLIBS="$ACLLIBS -lacl"])
+               AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+               acl_LIBS=$LIBS
+               LIBS="$LIBS -lacl"
+               AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)
+               LIBS=$acl_LIBS])
+                       if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+                               AC_MSG_RESULT(Using posix ACLs)
+                               AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+                               AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+                               acl_LIBS=$LIBS
+                               LIBS="$LIBS -lacl"
+                               AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)
+                               LIBS=$acl_LIBS])
+                               if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+                                       AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+                               fi
+                       fi
+            ;;
+        esac
+        ;;
+  *)
+    AC_MSG_RESULT(no)
+    AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+    ;;
+  esac ],
+  AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+  AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for sendfile support
+
+with_sendfile_support=yes
+AC_MSG_CHECKING(whether to check to support sendfile)
+AC_ARG_WITH(sendfile-support,
+[  --with-sendfile-support      Check for sendfile support (default=yes)],
+[ case "$withval" in
+  yes)
+
+       AC_MSG_RESULT(yes);
+
+       case "$host_os" in
+       *linux*)
+               AC_CACHE_CHECK([for linux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+               AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off64_t offset;
+size_t total;
+ssize_t nwritten = sendfile64(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+
+               AC_CACHE_CHECK([for linux sendfile support],samba_cv_HAVE_SENDFILE,[
+               AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+# Try and cope with broken Linux sendfile....
+               AC_CACHE_CHECK([for broken linux sendfile support],samba_cv_HAVE_BROKEN_LINUX_SENDFILE,[
+               AC_TRY_LINK([\
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#undef _FILE_OFFSET_BITS
+#endif
+#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_BROKEN_LINUX_SENDFILE=yes,samba_cv_HAVE_BROKEN_LINUX_SENDFILE=no)])
+
+       if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILE64,1,[Whether 64-bit sendfile() is available])
+               AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+       elif test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+               AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+       elif test x"$samba_cv_HAVE_BROKEN_LINUX_SENDFILE" = x"yes"; then
+               AC_DEFINE(LINUX_BROKEN_SENDFILE_API,1,[Whether (linux) sendfile() is broken])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile should be used])
+       else
+               AC_MSG_RESULT(no);
+       fi
+
+       ;;
+       *freebsd*)
+               AC_CACHE_CHECK([for freebsd sendfile support],samba_cv_HAVE_SENDFILE,[
+               AC_TRY_LINK([\
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+       int fromfd, tofd, ret, total=0;
+       off_t offset, nwritten;
+       struct sf_hdtr hdr;
+       struct iovec hdtrl;
+       hdr.headers = &hdtrl;
+       hdr.hdr_cnt = 1;
+       hdr.trailers = NULL;
+       hdr.trl_cnt = 0;
+       hdtrl.iov_base = NULL;
+       hdtrl.iov_len = 0;
+       ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+       if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() support is available])
+               AC_DEFINE(FREEBSD_SENDFILE_API,1,[Whether the FreeBSD sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+       else
+               AC_MSG_RESULT(no);
+       fi
+       ;;
+
+       *hpux*)
+               AC_CACHE_CHECK([for hpux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+               AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+       int fromfd, tofd;
+       size_t total=0;
+       struct iovec hdtrl[2];
+       ssize_t nwritten;
+       off64_t offset;
+
+       hdtrl[0].iov_base = 0;
+       hdtrl[0].iov_len = 0;
+
+       nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+       if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILE64,1,[Whether sendfile64() is available])
+               AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+       else
+               AC_MSG_RESULT(no);
+       fi
+
+               AC_CACHE_CHECK([for hpux sendfile support],samba_cv_HAVE_SENDFILE,[
+               AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+       int fromfd, tofd;
+       size_t total=0;
+       struct iovec hdtrl[2];
+       ssize_t nwritten;
+       off_t offset;
+
+       hdtrl[0].iov_base = 0;
+       hdtrl[0].iov_len = 0;
+
+       nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+       if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+               AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+       else
+               AC_MSG_RESULT(no);
+       fi
+       ;;
+
+       *solaris*)
+               AC_CHECK_LIB(sendfile,sendfilev)
+               AC_CACHE_CHECK([for solaris sendfilev64 support],samba_cv_HAVE_SENDFILEV64,[
+               AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+        int sfvcnt;
+        size_t xferred;
+        struct sendfilevec vec[2];
+       ssize_t nwritten;
+       int tofd;
+
+       sfvcnt = 2;
+
+       vec[0].sfv_fd = SFV_FD_SELF;
+       vec[0].sfv_flag = 0;
+       vec[0].sfv_off = 0;
+       vec[0].sfv_len = 0;
+
+       vec[1].sfv_fd = 0;
+       vec[1].sfv_flag = 0;
+       vec[1].sfv_off = 0;
+       vec[1].sfv_len = 0;
+       nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV64=yes,samba_cv_HAVE_SENDFILEV64=no)])
+
+       if test x"$samba_cv_HAVE_SENDFILEV64" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILEV64,1,[Whether sendfilev64() is available])
+               AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the soloris sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+       else
+               AC_MSG_RESULT(no);
+       fi
+
+               AC_CACHE_CHECK([for solaris sendfilev support],samba_cv_HAVE_SENDFILEV,[
+               AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+        int sfvcnt;
+        size_t xferred;
+        struct sendfilevec vec[2];
+       ssize_t nwritten;
+       int tofd;
+
+       sfvcnt = 2;
+
+       vec[0].sfv_fd = SFV_FD_SELF;
+       vec[0].sfv_flag = 0;
+       vec[0].sfv_off = 0;
+       vec[0].sfv_len = 0;
+
+       vec[1].sfv_fd = 0;
+       vec[1].sfv_flag = 0;
+       vec[1].sfv_off = 0;
+       vec[1].sfv_len = 0;
+       nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV=yes,samba_cv_HAVE_SENDFILEV=no)])
+
+       if test x"$samba_cv_HAVE_SENDFILEV" = x"yes"; then
+               AC_DEFINE(HAVE_SENDFILEV,1,[Whether sendfilev() is available])
+               AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the solaris sendfile() API is available])
+               AC_DEFINE(WITH_SENDFILE,1,[Whether to include sendfile() support])
+       else
+               AC_MSG_RESULT(no);
+       fi
+       ;;
+
+       *)
+       ;;
+        esac
+        ;;
+  *)
+    AC_MSG_RESULT(no)
+    ;;
+  esac ],
+  AC_MSG_RESULT(yes)
+)
+
+
+#################################################
+# Check whether winbind is supported on this platform.  If so we need to
+# build and install client programs, sbin programs and shared libraries
+
+AC_MSG_CHECKING(whether to build winbind)
+
+# Initially, the value of $host_os decides whether winbind is supported
+
+case "$host_os" in
+       *linux*|*irix*)
+               HAVE_WINBIND=yes
+               ;;
+       *solaris*)
+               HAVE_WINBIND=yes
+               WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o"
+               WINBIND_NSS_EXTRA_LIBS="-lsocket"
+               ;;
+       *hpux11*)
+               HAVE_WINBIND=yes
+               WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o"
+               ;;
+       *)
+               HAVE_WINBIND=no
+               winbind_no_reason=", unsupported on $host_os"
+               ;;
+esac
+
+AC_SUBST(WINBIND_NSS_EXTRA_OBJS)
+AC_SUBST(WINBIND_NSS_EXTRA_LIBS)
+
+# Check the setting of --with-winbindd
+
+AC_ARG_WITH(winbind,
+[  --with-winbind          Build winbind (default, if supported by OS)],
+[ 
+  case "$withval" in
+       yes)
+               HAVE_WINBIND=yes
+               ;;
+        no)
+               HAVE_WINBIND=no
+                winbind_reason=""
+                ;;
+  esac ],
+)
+
+# We need unix domain sockets for winbind
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+       if test x"$samba_cv_unixsocket" = x"no"; then
+               winbind_no_reason=", no unix domain socket support on $host_os"
+               HAVE_WINBIND=no
+       fi
+fi
+
+# Display test results
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+        AC_MSG_RESULT(yes)
+       AC_DEFINE(WITH_WINBIND,1,[Whether to build winbind])
+
+       EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/wbinfo\$(EXEEXT)"
+       EXTRA_SBIN_PROGS="$EXTRA_SBIN_PROGS bin/winbindd\$(EXEEXT)"
+        if test x"$BLDSHARED" = x"true"; then
+               case "$host_os" in
+               *irix*)
+                       SHLIB_PROGS="$SHLIB_PROGS nsswitch/libns_winbind.so"
+                       ;;
+               *)
+                       SHLIB_PROGS="$SHLIB_PROGS nsswitch/libnss_winbind.so"
+                       ;;
+               esac
+               if test x"$with_pam" = x"yes"; then
+                       SHLIB_PROGS="$SHLIB_PROGS nsswitch/pam_winbind.so"
+               fi
+       fi
+else
+        AC_MSG_RESULT(no$winbind_no_reason)
+fi
+
+# Solaris has some extra fields in struct passwd that need to be
+# initialised otherwise nscd crashes.  Unfortunately autoconf < 2.50
+# doesn't have the AC_CHECK_MEMBER macro which would be handy for checking
+# this. 
+
+#AC_CHECK_MEMBER(struct passwd.pw_comment,
+#              AC_DEFINE(HAVE_PASSWD_PW_COMMENT, 1, [Defined if struct passwd has pw_comment field]),
+#              [#include <pwd.h>])
+
+AC_CACHE_CHECK([whether struct passwd has pw_comment],samba_cv_passwd_pw_comment, [
+    AC_TRY_COMPILE([#include <pwd.h>],[struct passwd p; p.pw_comment;],
+       samba_cv_passwd_pw_comment=yes,samba_cv_passwd_pw_comment=no)])
+if test x"$samba_cv_passwd_pw_comment" = x"yes"; then
+   AC_DEFINE(HAVE_PASSWD_PW_COMMENT,1,[Whether struct passwd has pw_comment])
+fi
+
+#AC_CHECK_MEMBER(struct passwd.pw_age,
+#              AC_DEFINE(HAVE_PASSWD_PW_AGE, 1, [Defined if struct passwd has pw_age field]),
+#              [#include <pwd.h>])
+
+AC_CACHE_CHECK([whether struct passwd has pw_age],samba_cv_passwd_pw_age, [
+    AC_TRY_COMPILE([#include <pwd.h>],[struct passwd p; p.pw_age;],
+       samba_cv_passwd_pw_age=yes,samba_cv_passwd_pw_age=no)])
+if test x"$samba_cv_passwd_pw_age" = x"yes"; then
+   AC_DEFINE(HAVE_PASSWD_PW_AGE,1,[Whether struct passwd has pw_age])
+fi
+
+#################################################
+# Check to see if we should use the included popt 
+
+AC_ARG_WITH(included-popt,
+[  --with-included-popt    use bundled popt library, not from system],
+[ 
+  case "$withval" in
+       yes)
+               INCLUDED_POPT=yes
+               ;;
+        no)
+               INCLUDED_POPT=no
+                ;;
+  esac ],
+)
+if test x"$INCLUDED_POPT" != x"yes"; then
+    AC_CHECK_LIB(popt, poptGetContext,
+                INCLUDED_POPT=no, INCLUDED_POPT=yes)
+fi
+
+AC_MSG_CHECKING(whether to use included popt)
+if test x"$INCLUDED_POPT" = x"yes"; then
+    AC_MSG_RESULT(yes)
+    BUILD_POPT='$(POPT_OBJS)'
+    FLAGS1="-I$srcdir/popt"
+else
+    AC_MSG_RESULT(no)
+    LIBS="$LIBS -lpopt"
+fi
+AC_SUBST(BUILD_POPT)
+AC_SUBST(FLAGS1)
+
+#################################################
+# Check if the user wants Python
+
+# At the moment, you can use this to set which Python binary to link
+# against.  (Libraries built for Python2.2 can't be used by 2.1,
+# though they can coexist in different directories.)  In the future
+# this might make the Python stuff be built by default.
+
+# Defaulting python breaks the clean target if python isn't installed
+
+PYTHON=
+
+AC_ARG_WITH(python,
+[  --with-python=PYTHONNAME  build Python libraries],
+[ case "${withval-python}" in
+  yes)
+       PYTHON=python
+       EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS python_ext"
+       ;;
+  no)
+       PYTHON=
+       ;;
+  *)
+       PYTHON=${withval-python}
+       ;;
+  esac ])
+AC_SUBST(PYTHON)
+
+#################################################
+# do extra things if we are running insure
+
+if test "${ac_cv_prog_CC}" = "insure"; then
+       CPPFLAGS="$CPPFLAGS -D__INSURE__"
+fi
+
+#################################################
+# final configure stuff
+
+AC_MSG_CHECKING([configure summary])
+AC_TRY_RUN([#include "${srcdir-.}/tests/summary.c"],
+           AC_MSG_RESULT(yes),
+          AC_MSG_ERROR([summary failure. Aborting config]); exit 1;,
+          AC_MSG_WARN([cannot run when cross-compiling]))
+
+builddir=`pwd`
+AC_SUBST(builddir)
+
+dnl Remove -L/usr/lib/? from LDFLAGS and LIBS
+LIB_REMOVE_USR_LIB(LDFLAGS)
+LIB_REMOVE_USR_LIB(LIBS)
+
+dnl Remove -I/usr/include/? from CFLAGS and CPPFLAGS
+CFLAGS_REMOVE_USR_INCLUDE(CFLAGS)
+CFLAGS_REMOVE_USR_INCLUDE(CPPFLAGS)
+
+AC_OUTPUT(include/stamp-h Makefile script/findsmb)
+
+#################################################
+# Print very concise instructions on building/use
+if test "x$enable_dmalloc" = xyes
+then
+       AC_MSG_RESULT([Note: The dmalloc debug library will be included.  To turn it on use])
+       AC_MSG_RESULT([      \$ eval \`dmalloc samba\`.])
+fi
diff --git a/source4/configure.nodebug.developer b/source4/configure.nodebug.developer
new file mode 100755 (executable)
index 0000000..65e21b4
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+CFLAGS="-Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -DDEBUG_PASSWORD"; export CFLAGS
+./configure $*
diff --git a/source4/configure.tridge.opt b/source4/configure.tridge.opt
new file mode 100755 (executable)
index 0000000..5ed55b3
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+export CFLAGS="-O2 -Wall"
+`dirname $0`/configure $* --prefix=/home/tridge/samba/samba4/prefix 
diff --git a/source4/dynconfig.c b/source4/dynconfig.c
new file mode 100644 (file)
index 0000000..42e8dff
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+   Copyright (C) 2003 by Anthony Liguori <aliguor@us.ibm.com>
+   
+   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"
+
+/**
+ * @file dynconfig.c
+ *
+ * @brief Global configurations, initialized to configured defaults.
+ *
+ * This file should be the only file that depends on path
+ * configuration (--prefix, etc), so that if ./configure is re-run,
+ * all programs will be appropriately updated.  Everything else in
+ * Samba should import extern variables from here, rather than relying
+ * on preprocessor macros.
+ *
+ * Eventually some of these may become even more variable, so that
+ * they can for example consistently be set across the whole of Samba
+ * by command-line parameters, config file entries, or environment
+ * variables.
+ *
+ * @todo Perhaps eventually these should be merged into the parameter
+ * table?  There's kind of a chicken-and-egg situation there...
+ **/
+
+char const *dyn_SBINDIR = SBINDIR,
+       *dyn_BINDIR = BINDIR,
+       *dyn_SWATDIR = SWATDIR;
+
+pstring dyn_CONFIGFILE = CONFIGFILE; /**< Location of smb.conf file. **/
+
+/** Log file directory. **/
+const char *dyn_LOGFILEBASE = LOGFILEBASE;
+
+/** Statically configured LanMan hosts. **/
+pstring dyn_LMHOSTSFILE = LMHOSTSFILE;
+
+/**
+ * @brief Samba library directory.
+ *
+ * @sa lib_path() to get the path to a file inside the LIBDIR.
+ **/
+pstring dyn_LIBDIR = LIBDIR;
+const fstring dyn_SHLIBEXT = SHLIBEXT;
+
+/**
+ * @brief Directory holding lock files.
+ *
+ * Not writable, but used to set a default in the parameter table.
+ **/
+const pstring dyn_LOCKDIR = LOCKDIR;
+const pstring dyn_PIDDIR  = PIDDIR;
+
+const pstring dyn_SMB_PASSWD_FILE = SMB_PASSWD_FILE;
+const pstring dyn_PRIVATE_DIR = PRIVATE_DIR;
diff --git a/source4/groupdb/mapping.c b/source4/groupdb/mapping.c
new file mode 100644 (file)
index 0000000..958d6de
--- /dev/null
@@ -0,0 +1,1340 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2001.
+ *  
+ *  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"
+
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION_V1 1 /* native byte format. */
+#define DATABASE_VERSION_V2 2 /* le format. */
+
+#define GROUP_PREFIX "UNIXGROUP/"
+
+PRIVS privs[] = {
+       {SE_PRIV_NONE,           "no_privs",                  "No privilege"                    }, /* this one MUST be first */
+       {SE_PRIV_ADD_MACHINES,   "SeMachineAccountPrivilege", "Add workstations to the domain"  },
+       {SE_PRIV_SEC_PRIV,       "SeSecurityPrivilege",       "Manage the audit logs"           },
+       {SE_PRIV_TAKE_OWNER,     "SeTakeOwnershipPrivilege",  "Take ownership of file"          },
+       {SE_PRIV_ADD_USERS,      "SaAddUsers",                "Add users to the domain - Samba" },
+       {SE_PRIV_PRINT_OPERATOR, "SaPrintOp",                 "Add or remove printers - Samba"  },
+       {SE_PRIV_ALL,            "SaAllPrivs",                "all privileges"                  }
+};
+/*
+PRIVS privs[] = {
+       {  2, "SeCreateTokenPrivilege" },
+       {  3, "SeAssignPrimaryTokenPrivilege" },
+       {  4, "SeLockMemoryPrivilege" },
+       {  5, "SeIncreaseQuotaPrivilege" },
+       {  6, "SeMachineAccountPrivilege" },
+       {  7, "SeTcbPrivilege" },
+       {  8, "SeSecurityPrivilege" },
+       {  9, "SeTakeOwnershipPrivilege" },
+       { 10, "SeLoadDriverPrivilege" },
+       { 11, "SeSystemProfilePrivilege" },
+       { 12, "SeSystemtimePrivilege" },
+       { 13, "SeProfileSingleProcessPrivilege" },
+       { 14, "SeIncreaseBasePriorityPrivilege" },
+       { 15, "SeCreatePagefilePrivilege" },
+       { 16, "SeCreatePermanentPrivilege" },
+       { 17, "SeBackupPrivilege" },
+       { 18, "SeRestorePrivilege" },
+       { 19, "SeShutdownPrivilege" },
+       { 20, "SeDebugPrivilege" },
+       { 21, "SeAuditPrivilege" },
+       { 22, "SeSystemEnvironmentPrivilege" },
+       { 23, "SeChangeNotifyPrivilege" },
+       { 24, "SeRemoteShutdownPrivilege" },
+       { 25, "SeUndockPrivilege" },
+       { 26, "SeSyncAgentPrivilege" },
+       { 27, "SeEnableDelegationPrivilege" },
+};
+*/
+
+       /*
+        * Those are not really privileges like the other ones.
+        * They are handled in a special case and called
+        * system privileges.
+        *
+        * SeNetworkLogonRight
+        * SeUnsolicitedInputPrivilege
+        * SeBatchLogonRight
+        * SeServiceLogonRight
+        * SeInteractiveLogonRight
+        * SeDenyInteractiveLogonRight
+        * SeDenyNetworkLogonRight
+        * SeDenyBatchLogonRight
+        * SeDenyBatchLogonRight
+        */
+
+#if 0
+/****************************************************************************
+check if the user has the required privilege.
+****************************************************************************/
+static BOOL se_priv_access_check(NT_USER_TOKEN *token, uint32 privilege)
+{
+       /* no token, no privilege */
+       if (token==NULL)
+               return False;
+       
+       if ((token->privilege & privilege)==privilege)
+               return True;
+       
+       return False;
+}
+#endif
+
+/****************************************************************************
+dump the mapping group mapping to a text file
+****************************************************************************/
+char *decode_sid_name_use(fstring group, enum SID_NAME_USE name_use)
+{      
+       static fstring group_type;
+
+       switch(name_use) {
+               case SID_NAME_USER:
+                       fstrcpy(group_type,"User");
+                       break;
+               case SID_NAME_DOM_GRP:
+                       fstrcpy(group_type,"Domain group");
+                       break;
+               case SID_NAME_DOMAIN:
+                       fstrcpy(group_type,"Domain");
+                       break;
+               case SID_NAME_ALIAS:
+                       fstrcpy(group_type,"Local group");
+                       break;
+               case SID_NAME_WKN_GRP:
+                       fstrcpy(group_type,"Builtin group");
+                       break;
+               case SID_NAME_DELETED:
+                       fstrcpy(group_type,"Deleted");
+                       break;
+               case SID_NAME_INVALID:
+                       fstrcpy(group_type,"Invalid");
+                       break;
+               case SID_NAME_UNKNOWN:
+               default:
+                       fstrcpy(group_type,"Unknown type");
+                       break;
+       }
+       
+       fstrcpy(group, group_type);
+       return group_type;
+}
+
+/****************************************************************************
+initialise first time the mapping list - called from init_group_mapping()
+****************************************************************************/
+static BOOL default_group_mapping(void)
+{
+       DOM_SID sid_admins;
+       DOM_SID sid_users;
+       DOM_SID sid_guests;
+       fstring str_admins;
+       fstring str_users;
+       fstring str_guests;
+       LUID_ATTR set;
+
+       PRIVILEGE_SET privilege_none;
+       PRIVILEGE_SET privilege_all;
+       PRIVILEGE_SET privilege_print_op;
+
+       init_privilege(&privilege_none);
+       init_privilege(&privilege_all);
+       init_privilege(&privilege_print_op);
+
+       set.attr=0;
+       set.luid.high=0;
+       set.luid.low=SE_PRIV_PRINT_OPERATOR;
+       add_privilege(&privilege_print_op, set);
+
+       add_all_privilege(&privilege_all);
+
+       /* Add the Wellknown groups */
+
+       add_initial_entry(-1, "S-1-5-32-544", SID_NAME_ALIAS, "Administrators", "", privilege_all, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+       add_initial_entry(-1, "S-1-5-32-545", SID_NAME_ALIAS, "Users", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+       add_initial_entry(-1, "S-1-5-32-546", SID_NAME_ALIAS, "Guests", "", privilege_none, PR_ACCESS_FROM_NETWORK);
+       add_initial_entry(-1, "S-1-5-32-547", SID_NAME_ALIAS, "Power Users", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+
+       add_initial_entry(-1, "S-1-5-32-548", SID_NAME_ALIAS, "Account Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+       add_initial_entry(-1, "S-1-5-32-549", SID_NAME_ALIAS, "System Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+       add_initial_entry(-1, "S-1-5-32-550", SID_NAME_ALIAS, "Print Operators", "", privilege_print_op, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+       add_initial_entry(-1, "S-1-5-32-551", SID_NAME_ALIAS, "Backup Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+
+       add_initial_entry(-1, "S-1-5-32-552", SID_NAME_ALIAS, "Replicators", "", privilege_none, PR_ACCESS_FROM_NETWORK);
+
+       /* Add the defaults domain groups */
+
+       sid_copy(&sid_admins, get_global_sam_sid());
+       sid_append_rid(&sid_admins, DOMAIN_GROUP_RID_ADMINS);
+       sid_to_string(str_admins, &sid_admins);
+       add_initial_entry(-1, str_admins, SID_NAME_DOM_GRP, "Domain Admins", "", privilege_all, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+
+       sid_copy(&sid_users,  get_global_sam_sid());
+       sid_append_rid(&sid_users,  DOMAIN_GROUP_RID_USERS);
+       sid_to_string(str_users, &sid_users);
+       add_initial_entry(-1, str_users,  SID_NAME_DOM_GRP, "Domain Users",  "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY);
+
+       sid_copy(&sid_guests, get_global_sam_sid());
+       sid_append_rid(&sid_guests, DOMAIN_GROUP_RID_GUESTS);
+       sid_to_string(str_guests, &sid_guests);
+       add_initial_entry(-1, str_guests, SID_NAME_DOM_GRP, "Domain Guests", "", privilege_none, PR_ACCESS_FROM_NETWORK);
+
+       return True;
+}
+
+/****************************************************************************
+ Open the group mapping tdb.
+****************************************************************************/
+
+static BOOL init_group_mapping(void)
+{
+       static pid_t local_pid;
+       const char *vstring = "INFO/version";
+       int32 vers_id;
+       TALLOC_CTX *mem_ctx;
+       
+       if (tdb && local_pid == getpid())
+               return True;
+       mem_ctx = talloc_init("init_group_mapping");
+       if (!mem_ctx) {
+               DEBUG(0,("No memory to open group mapping database\n"));
+               return False;
+       }
+       tdb = tdb_open_log(lock_path(mem_ctx, "group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       talloc_destroy(mem_ctx);
+       if (!tdb) {
+               DEBUG(0,("Failed to open group mapping database\n"));
+               return False;
+       }
+
+       local_pid = getpid();
+
+       /* handle a Samba upgrade */
+       tdb_lock_bystring(tdb, vstring, 0);
+
+       /* Cope with byte-reversed older versions of the db. */
+       vers_id = tdb_fetch_int32(tdb, vstring);
+       if ((vers_id == DATABASE_VERSION_V1) || (IREV(vers_id) == DATABASE_VERSION_V1)) {
+               /* Written on a bigendian machine with old fetch_int code. Save as le. */
+               tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+               vers_id = DATABASE_VERSION_V2;
+       }
+
+       if (vers_id != DATABASE_VERSION_V2) {
+               tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+               tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+       }
+
+       tdb_unlock_bystring(tdb, vstring);
+
+       /* write a list of default groups */
+       if(!default_group_mapping())
+               return False;
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+BOOL add_mapping_entry(GROUP_MAP *map, int flag)
+{
+       TDB_DATA kbuf, dbuf;
+       pstring key, buf;
+       fstring string_sid="";
+       int len;
+       int i;
+       PRIVILEGE_SET *set;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+       
+       sid_to_string(string_sid, &map->sid);
+
+       len = tdb_pack(buf, sizeof(buf), "ddffd",
+                       map->gid, map->sid_name_use, map->nt_name, map->comment, map->systemaccount);
+
+       /* write the privilege list in the TDB database */
+
+       set=&map->priv_set;
+       len += tdb_pack(buf+len, sizeof(buf)-len, "d", set->count);
+       for (i=0; i<set->count; i++)
+               len += tdb_pack(buf+len, sizeof(buf)-len, "ddd", 
+                               set->set[i].luid.low, set->set[i].luid.high, set->set[i].attr);
+
+       if (len > sizeof(buf))
+               return False;
+
+       slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+       kbuf.dsize = strlen(key)+1;
+       kbuf.dptr = key;
+       dbuf.dsize = len;
+       dbuf.dptr = buf;
+       if (tdb_store(tdb, kbuf, dbuf, flag) != 0) return False;
+
+       return True;
+}
+
+/****************************************************************************
+initialise first time the mapping list
+****************************************************************************/
+BOOL add_initial_entry(gid_t gid, const char *sid, enum SID_NAME_USE sid_name_use,
+                      const char *nt_name, const char *comment, PRIVILEGE_SET priv_set, uint32 systemaccount)
+{
+       GROUP_MAP map;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+       
+       map.gid=gid;
+       if (!string_to_sid(&map.sid, sid)) {
+               DEBUG(0, ("string_to_sid failed: %s", sid));
+               return False;
+       }
+       
+       map.sid_name_use=sid_name_use;
+       fstrcpy(map.nt_name, nt_name);
+       fstrcpy(map.comment, comment);
+       map.systemaccount=systemaccount;
+
+       map.priv_set.count=priv_set.count;
+       map.priv_set.set=priv_set.set;
+
+       pdb_add_group_mapping_entry(&map);
+
+       return True;
+}
+
+/****************************************************************************
+initialise a privilege list
+****************************************************************************/
+void init_privilege(PRIVILEGE_SET *priv_set)
+{
+       priv_set->count=0;
+       priv_set->control=0;
+       priv_set->set=NULL;
+}
+
+/****************************************************************************
+free a privilege list
+****************************************************************************/
+BOOL free_privilege(PRIVILEGE_SET *priv_set)
+{
+       if (priv_set->count==0) {
+               DEBUG(100,("free_privilege: count=0, nothing to clear ?\n"));
+               return False;
+       }
+
+       if (priv_set->set==NULL) {
+               DEBUG(0,("free_privilege: list ptr is NULL, very strange !\n"));
+               return False;
+       }
+
+       safe_free(priv_set->set);
+       priv_set->count=0;
+       priv_set->control=0;
+       priv_set->set=NULL;
+
+       return True;
+}
+
+/****************************************************************************
+add a privilege to a privilege array
+****************************************************************************/
+BOOL add_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+       LUID_ATTR *new_set;
+
+       /* check if the privilege is not already in the list */
+       if (check_priv_in_privilege(priv_set, set))
+               return False;
+
+       /* we can allocate memory to add the new privilege */
+
+       new_set=(LUID_ATTR *)Realloc(priv_set->set, (priv_set->count+1)*(sizeof(LUID_ATTR)));
+       if (new_set==NULL) {
+               DEBUG(0,("add_privilege: could not Realloc memory to add a new privilege\n"));
+               return False;
+       }
+
+       new_set[priv_set->count].luid.high=set.luid.high;
+       new_set[priv_set->count].luid.low=set.luid.low;
+       new_set[priv_set->count].attr=set.attr;
+       
+       priv_set->count++;
+       priv_set->set=new_set;
+       
+       return True;    
+}
+
+/****************************************************************************
+add all the privileges to a privilege array
+****************************************************************************/
+BOOL add_all_privilege(PRIVILEGE_SET *priv_set)
+{
+       LUID_ATTR set;
+
+       set.attr=0;
+       set.luid.high=0;
+       
+       set.luid.low=SE_PRIV_ADD_USERS;
+       add_privilege(priv_set, set);
+
+       set.luid.low=SE_PRIV_ADD_MACHINES;
+       add_privilege(priv_set, set);
+
+       set.luid.low=SE_PRIV_PRINT_OPERATOR;
+       add_privilege(priv_set, set);
+       
+       return True;
+}
+
+/****************************************************************************
+check if the privilege list is empty
+****************************************************************************/
+BOOL check_empty_privilege(PRIVILEGE_SET *priv_set)
+{
+       return (priv_set->count == 0);
+}
+
+/****************************************************************************
+check if the privilege is in the privilege list
+****************************************************************************/
+BOOL check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+       int i;
+
+       /* if the list is empty, obviously we can't have it */
+       if (check_empty_privilege(priv_set))
+               return False;
+
+       for (i=0; i<priv_set->count; i++) {
+               LUID_ATTR *cur_set;
+
+               cur_set=&priv_set->set[i];
+               /* check only the low and high part. Checking the attr field has no meaning */
+               if( (cur_set->luid.low==set.luid.low) && (cur_set->luid.high==set.luid.high) )
+                       return True;
+       }
+
+       return False;
+}
+
+/****************************************************************************
+remove a privilege from a privilege array
+****************************************************************************/
+BOOL remove_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+       LUID_ATTR *new_set;
+       LUID_ATTR *old_set;
+       int i,j;
+
+       /* check if the privilege is in the list */
+       if (!check_priv_in_privilege(priv_set, set))
+               return False;
+
+       /* special case if it's the only privilege in the list */
+       if (priv_set->count==1) {
+               free_privilege(priv_set);
+               init_privilege(priv_set);       
+       
+               return True;
+       }
+
+       /* 
+        * the privilege is there, create a new list,
+        * and copy the other privileges
+        */
+
+       old_set=priv_set->set;
+
+       new_set=(LUID_ATTR *)malloc((priv_set->count-1)*(sizeof(LUID_ATTR)));
+       if (new_set==NULL) {
+               DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n"));
+               return False;
+       }
+
+       for (i=0, j=0; i<priv_set->count; i++) {
+               if ((old_set[i].luid.low==set.luid.low) && 
+                   (old_set[i].luid.high==set.luid.high)) {
+                       continue;
+               }
+               
+               new_set[j].luid.low=old_set[i].luid.low;
+               new_set[j].luid.high=old_set[i].luid.high;
+               new_set[j].attr=old_set[i].attr;
+
+               j++;
+       }
+       
+       if (j!=priv_set->count-1) {
+               DEBUG(0,("remove_privilege: mismatch ! difference is not -1\n"));
+               DEBUGADD(0,("old count:%d, new count:%d\n", priv_set->count, j));
+               safe_free(new_set);
+               return False;
+       }
+               
+       /* ok everything is fine */
+       
+       priv_set->count--;
+       priv_set->set=new_set;
+       
+       safe_free(old_set);
+       
+       return True;    
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+BOOL get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv)
+{
+       TDB_DATA kbuf, dbuf;
+       pstring key;
+       fstring string_sid;
+       int ret;
+       int i;
+       PRIVILEGE_SET *set;
+       
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       /* the key is the SID, retrieving is direct */
+
+       sid_to_string(string_sid, &sid);
+       slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+               
+       dbuf = tdb_fetch(tdb, kbuf);
+       if (!dbuf.dptr)
+               return False;
+
+       ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddffd",
+                               &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount);
+
+       set=&map->priv_set;
+       init_privilege(set);
+       ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count);
+
+       DEBUG(10,("get_group_map_from_sid: %d privileges\n", map->priv_set.count));
+
+       set->set = NULL;
+       if (set->count) {
+               set->set=(LUID_ATTR *)smb_xmalloc(set->count*sizeof(LUID_ATTR));
+       }
+
+       for (i=0; i<set->count; i++)
+               ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", 
+                               &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr));
+
+       SAFE_FREE(dbuf.dptr);
+       if (ret != dbuf.dsize) {
+               DEBUG(0,("get_group_map_from_sid: group mapping TDB corrupted ?\n"));
+               free_privilege(set);
+               return False;
+       }
+
+       /* we don't want the privileges */
+       if (with_priv==MAPPING_WITHOUT_PRIV)
+               free_privilege(set);
+
+       sid_copy(&map->sid, &sid);
+       
+       return True;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+BOOL get_group_map_from_gid(gid_t gid, GROUP_MAP *map, BOOL with_priv)
+{
+       TDB_DATA kbuf, dbuf, newkey;
+       fstring string_sid;
+       int ret;
+       int i;
+       PRIVILEGE_SET *set;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       /* we need to enumerate the TDB to find the GID */
+
+       for (kbuf = tdb_firstkey(tdb); 
+            kbuf.dptr; 
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr)
+                       continue;
+
+               fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+               string_to_sid(&map->sid, string_sid);
+               
+               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddffd",
+                                &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount);
+
+               set=&map->priv_set;
+               ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count);
+               set->set = NULL;
+               if (set->count) {
+                       set->set=(LUID_ATTR *)smb_xmalloc(set->count*sizeof(LUID_ATTR));
+               }
+
+               for (i=0; i<set->count; i++)
+                       ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", 
+                                       &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr));
+
+               SAFE_FREE(dbuf.dptr);
+               if (ret != dbuf.dsize){
+                       free_privilege(set);
+                       continue;
+               }
+
+               if (gid==map->gid) {
+                       if (!with_priv)
+                               free_privilege(&map->priv_set);
+                       return True;
+               }
+               
+               free_privilege(set);
+       }
+
+       return False;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+BOOL get_group_map_from_ntname(char *name, GROUP_MAP *map, BOOL with_priv)
+{
+       TDB_DATA kbuf, dbuf, newkey;
+       fstring string_sid;
+       int ret;
+       int i;
+       PRIVILEGE_SET *set;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("get_group_map_from_ntname:failed to initialize group mapping"));
+               return(False);
+       }
+
+       /* we need to enumerate the TDB to find the name */
+
+       for (kbuf = tdb_firstkey(tdb); 
+            kbuf.dptr; 
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr)
+                       continue;
+
+               fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+               string_to_sid(&map->sid, string_sid);
+               
+               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddffd",
+                                &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount);
+
+               set=&map->priv_set;
+               ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count);
+       
+               set->set=(LUID_ATTR *)malloc(set->count*sizeof(LUID_ATTR));
+               if (set->set==NULL) {
+                       DEBUG(0,("get_group_map_from_ntname: could not allocate memory for privileges\n"));
+                       return False;
+               }
+
+               for (i=0; i<set->count; i++)
+                       ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", 
+                                       &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr));
+
+               SAFE_FREE(dbuf.dptr);
+               if (ret != dbuf.dsize) {
+                       free_privilege(set);
+                       continue;
+               }
+
+               if (StrCaseCmp(name, map->nt_name)==0) {
+                       if (!with_priv)
+                               free_privilege(&map->priv_set);
+                       return True;
+               }
+
+               free_privilege(set);
+       }
+
+       return False;
+}
+
+/****************************************************************************
+ Remove a group mapping entry.
+****************************************************************************/
+
+BOOL group_map_remove(DOM_SID sid)
+{
+       TDB_DATA kbuf, dbuf;
+       pstring key;
+       fstring string_sid;
+       
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       /* the key is the SID, retrieving is direct */
+
+       sid_to_string(string_sid, &sid);
+       slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+               
+       dbuf = tdb_fetch(tdb, kbuf);
+       if (!dbuf.dptr)
+               return False;
+       
+       SAFE_FREE(dbuf.dptr);
+
+       if(tdb_delete(tdb, kbuf) != TDB_SUCCESS)
+               return False;
+
+       return True;
+}
+
+/****************************************************************************
+ Enumerate the group mapping.
+****************************************************************************/
+
+BOOL enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+                       int *num_entries, BOOL unix_only, BOOL with_priv)
+{
+       TDB_DATA kbuf, dbuf, newkey;
+       fstring string_sid;
+       fstring group_type;
+       GROUP_MAP map;
+       GROUP_MAP *mapt;
+       int ret;
+       int entries=0;
+       int i;
+       PRIVILEGE_SET *set;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       *num_entries=0;
+       *rmap=NULL;
+
+       for (kbuf = tdb_firstkey(tdb); 
+            kbuf.dptr; 
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0)
+                       continue;
+               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr)
+                       continue;
+
+               fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+                               
+               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddffd",
+                                &map.gid, &map.sid_name_use, &map.nt_name, &map.comment, &map.systemaccount);
+
+               set=&map.priv_set;
+               init_privilege(set);
+               
+               ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count);
+       
+               if (set->count!=0) {
+                       set->set=(LUID_ATTR *)malloc(set->count*sizeof(LUID_ATTR));
+                       if (set->set==NULL) {
+                               DEBUG(0,("enum_group_mapping: could not allocate memory for privileges\n"));
+                               return False;
+                       }
+               }
+
+               for (i=0; i<set->count; i++)
+                       ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", 
+                                       &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr));
+
+               SAFE_FREE(dbuf.dptr);
+               if (ret != dbuf.dsize) {
+                       DEBUG(11,("enum_group_mapping: error in memory size\n"));
+                       free_privilege(set);
+                       continue;
+               }
+
+               /* list only the type or everything if UNKNOWN */
+               if (sid_name_use!=SID_NAME_UNKNOWN  && sid_name_use!=map.sid_name_use) {
+                       DEBUG(11,("enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
+                       free_privilege(set);
+                       continue;
+               }
+               
+               if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+                       DEBUG(11,("enum_group_mapping: group %s is non mapped\n", map.nt_name));
+                       free_privilege(set);
+                       continue;
+               }
+
+               string_to_sid(&map.sid, string_sid);
+               
+               decode_sid_name_use(group_type, map.sid_name_use);
+               DEBUG(11,("enum_group_mapping: returning group %s of type %s\n", map.nt_name ,group_type));
+
+               mapt=(GROUP_MAP *)Realloc((*rmap), (entries+1)*sizeof(GROUP_MAP));
+               if (!mapt) {
+                       DEBUG(0,("enum_group_mapping: Unable to enlarge group map!\n"));
+                       SAFE_FREE(*rmap);
+                       free_privilege(set);
+                       return False;
+               }
+               else
+                       (*rmap) = mapt;
+
+               mapt[entries].gid = map.gid;
+               sid_copy( &mapt[entries].sid, &map.sid);
+               mapt[entries].sid_name_use = map.sid_name_use;
+               fstrcpy(mapt[entries].nt_name, map.nt_name);
+               fstrcpy(mapt[entries].comment, map.comment);
+               mapt[entries].systemaccount=map.systemaccount;
+               mapt[entries].priv_set.count=set->count;
+               mapt[entries].priv_set.control=set->control;
+               mapt[entries].priv_set.set=set->set;
+               if (!with_priv)
+                       free_privilege(&(mapt[entries].priv_set));
+
+               entries++;
+       }
+
+       *num_entries=entries;
+
+       return True;
+}
+
+
+/****************************************************************************
+convert a privilege string to a privilege array
+****************************************************************************/
+void convert_priv_from_text(PRIVILEGE_SET *se_priv, char *privilege)
+{
+       pstring tok;
+       const char *p = privilege;
+       int i;
+       LUID_ATTR set;
+
+       /* By default no privilege */
+       init_privilege(se_priv);
+       
+       if (privilege==NULL)
+               return;
+
+       while(next_token(&p, tok, " ", sizeof(tok)) ) {
+               for (i=0; i<=PRIV_ALL_INDEX; i++) {
+                       if (StrCaseCmp(privs[i].priv, tok)==0) {
+                               set.attr=0;
+                               set.luid.high=0;
+                               set.luid.low=privs[i].se_priv;
+                               add_privilege(se_priv, set);
+                       }
+               }               
+       }
+}
+
+/****************************************************************************
+convert a privilege array to a privilege string
+****************************************************************************/
+void convert_priv_to_text(PRIVILEGE_SET *se_priv, char *privilege)
+{
+       int i,j;
+
+       if (privilege==NULL)
+               return;
+
+       ZERO_STRUCTP(privilege);
+
+       if (check_empty_privilege(se_priv)) {
+               fstrcat(privilege, "No privilege");
+               return;
+       }
+
+       for(i=0; i<se_priv->count; i++) {
+               j=1;
+               while (privs[j].se_priv!=se_priv->set[i].luid.low && j<=PRIV_ALL_INDEX) {
+                       j++;
+               }
+
+               fstrcat(privilege, privs[j].priv);
+               fstrcat(privilege, " ");
+       }
+}
+
+
+/*
+ *
+ * High level functions
+ * better to use them than the lower ones.
+ *
+ * we are checking if the group is in the mapping file
+ * and if the group is an existing unix group
+ *
+ */
+
+/* get a domain group from it's SID */
+
+BOOL get_domain_group_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv)
+{
+       struct group *grp;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       DEBUG(10, ("get_domain_group_from_sid\n"));
+
+       /* if the group is NOT in the database, it CAN NOT be a domain group */
+       if(!pdb_getgrsid(map, sid, with_priv))
+               return False;
+
+       DEBUG(10, ("get_domain_group_from_sid: SID found in the TDB\n"));
+
+       /* if it's not a domain group, continue */
+       if (map->sid_name_use!=SID_NAME_DOM_GRP) {
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       DEBUG(10, ("get_domain_group_from_sid: SID is a domain group\n"));
+       
+       if (map->gid==-1) {
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       DEBUG(10, ("get_domain_group_from_sid: SID is mapped to gid:%d\n",map->gid));
+
+       if ( (grp=getgrgid(map->gid)) == NULL) {
+               DEBUG(10, ("get_domain_group_from_sid: gid DOESN'T exist in UNIX security\n"));
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       DEBUG(10, ("get_domain_group_from_sid: gid exists in UNIX security\n"));
+
+       return True;
+}
+
+
+/* get a local (alias) group from it's SID */
+
+BOOL get_local_group_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv)
+{
+       struct group *grp;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       /* The group is in the mapping table */
+       if(pdb_getgrsid(map, sid, with_priv)) {
+               if (map->sid_name_use!=SID_NAME_ALIAS) {
+                       if (with_priv)
+                               free_privilege(&map->priv_set);
+                       return False;
+               }
+               
+               if (map->gid==-1) {
+                       if (with_priv)
+                               free_privilege(&map->priv_set);
+                       return False;
+               }
+
+               if ( (grp=getgrgid(map->gid)) == NULL) {
+                       if (with_priv)
+                               free_privilege(&map->priv_set);
+                       return False;
+               }
+       } else {
+               /* the group isn't in the mapping table.
+                * make one based on the unix information */
+               uint32 alias_rid;
+
+               sid_peek_rid(&sid, &alias_rid);
+               map->gid=pdb_group_rid_to_gid(alias_rid);
+
+               if ((grp=getgrgid(map->gid)) == NULL)
+                       return False;
+
+               map->sid_name_use=SID_NAME_ALIAS;
+               map->systemaccount=PR_ACCESS_FROM_NETWORK;
+
+               fstrcpy(map->nt_name, grp->gr_name);
+               fstrcpy(map->comment, "Local Unix Group");
+
+               init_privilege(&map->priv_set);
+
+               sid_copy(&map->sid, &sid);
+       }
+
+       return True;
+}
+
+/* get a builtin group from it's SID */
+
+BOOL get_builtin_group_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv)
+{
+       struct group *grp;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       if(!pdb_getgrsid(map, sid, with_priv))
+               return False;
+
+       if (map->sid_name_use!=SID_NAME_WKN_GRP) {
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       if (map->gid==-1) {
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       if ( (grp=getgrgid(map->gid)) == NULL) {
+               if (with_priv)
+                       free_privilege(&map->priv_set);
+               return False;
+       }
+
+       return True;
+}
+
+
+
+/****************************************************************************
+Returns a GROUP_MAP struct based on the gid.
+****************************************************************************/
+BOOL get_group_from_gid(gid_t gid, GROUP_MAP *map, BOOL with_priv)
+{
+       struct group *grp;
+
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       if ( (grp=getgrgid(gid)) == NULL)
+               return False;
+
+       /*
+        * make a group map from scratch if doesn't exist.
+        */
+       if (!pdb_getgrgid(map, gid, with_priv)) {
+               map->gid=gid;
+               map->sid_name_use=SID_NAME_ALIAS;
+               map->systemaccount=PR_ACCESS_FROM_NETWORK;
+               init_privilege(&map->priv_set);
+
+               /* interim solution until we have a last RID allocated */
+
+               sid_copy(&map->sid, get_global_sam_sid());
+               sid_append_rid(&map->sid, pdb_gid_to_group_rid(gid));
+
+               fstrcpy(map->nt_name, grp->gr_name);
+               fstrcpy(map->comment, "Local Unix Group");
+       }
+       
+       return True;
+}
+
+
+
+
+/****************************************************************************
+ Get the member users of a group and
+ all the users who have that group as primary.
+            
+ give back an array of uid
+ return the grand number of users
+
+
+ TODO: sort the list and remove duplicate. JFM.
+
+****************************************************************************/
+        
+BOOL get_uid_list_of_group(gid_t gid, uid_t **uid, int *num_uids)
+{
+       struct group *grp;
+       struct passwd *pwd;
+       int i=0;
+       char *gr;
+       uid_t *u;
+       if(!init_group_mapping()) {
+               DEBUG(0,("failed to initialize group mapping"));
+               return(False);
+       }
+
+       *num_uids = 0;
+       *uid=NULL;
+       
+       if ( (grp=getgrgid(gid)) == NULL)
+               return False;
+
+       gr = grp->gr_mem[0];
+       DEBUG(10, ("getting members\n"));
+        
+       while (gr && (*gr != (char)'\0')) {
+               u = Realloc((*uid), sizeof(uid_t)*(*num_uids+1));
+               if (!u) {
+                       DEBUG(0,("get_uid_list_of_group: unable to enlarge uid list!\n"));
+                       return False;
+               }
+               else (*uid) = u;
+
+               if( (pwd=getpwnam_alloc(gr)) !=NULL) {
+                       (*uid)[*num_uids]=pwd->pw_uid;
+                       (*num_uids)++;
+               }
+               passwd_free(&pwd);
+               gr = grp->gr_mem[++i];
+       }
+       DEBUG(10, ("got [%d] members\n", *num_uids));
+
+       setpwent();
+       while ((pwd=getpwent()) != NULL) {
+               if (pwd->pw_gid==gid) {
+                       u = Realloc((*uid), sizeof(uid_t)*(*num_uids+1));
+                       if (!u) {
+                               DEBUG(0,("get_uid_list_of_group: unable to enlarge uid list!\n"));
+                               return False;
+                       }
+                       else (*uid) = u;
+                       (*uid)[*num_uids]=pwd->pw_uid;
+
+                       (*num_uids)++;
+               }
+       }
+       endpwent();
+       DEBUG(10, ("got primary groups, members: [%d]\n", *num_uids));
+
+        return True;
+}
+
+/****************************************************************************
+ Create a UNIX group on demand.
+****************************************************************************/
+
+int smb_create_group(char *unix_group, gid_t *new_gid)
+{
+       pstring add_script;
+       int ret;
+       int fd = 0;
+
+       pstrcpy(add_script, lp_addgroup_script());
+       if (! *add_script) return -1;
+       pstring_sub(add_script, "%g", unix_group);
+       ret = smbrun(add_script, (new_gid!=NULL) ? &fd : NULL);
+       DEBUG(3,("smb_create_group: Running the command `%s' gave %d\n",add_script,ret));
+       if (ret != 0)
+               return ret;
+
+       if (fd != 0) {
+               fstring output;
+
+               *new_gid = 0;
+               if (read(fd, output, sizeof(output)) > 0) {
+                       *new_gid = (gid_t)strtoul(output, NULL, 10);
+               }
+               close(fd);
+
+               if (*new_gid == 0) {
+                       /* The output was garbage. We assume nobody
+                           will create group 0 via smbd. Now we try to
+                           get the group via getgrnam. */
+
+                       struct group *grp = getgrnam(unix_group);
+                       if (grp != NULL)
+                               *new_gid = grp->gr_gid;
+                       else
+                               return 1;
+               }
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ Delete a UNIX group on demand.
+****************************************************************************/
+
+int smb_delete_group(char *unix_group)
+{
+       pstring del_script;
+       int ret;
+
+       pstrcpy(del_script, lp_delgroup_script());
+       if (! *del_script) return -1;
+       pstring_sub(del_script, "%g", unix_group);
+       ret = smbrun(del_script,NULL);
+       DEBUG(3,("smb_delete_group: Running the command `%s' gave %d\n",del_script,ret));
+       return ret;
+}
+
+/****************************************************************************
+ Set a user's primary UNIX group.
+****************************************************************************/
+int smb_set_primary_group(const char *unix_group, const char* unix_user)
+{
+       pstring add_script;
+       int ret;
+
+       pstrcpy(add_script, lp_setprimarygroup_script());
+       if (! *add_script) return -1;
+       all_string_sub(add_script, "%g", unix_group, sizeof(add_script));
+       all_string_sub(add_script, "%u", unix_user, sizeof(add_script));
+       ret = smbrun(add_script,NULL);
+       DEBUG(3,("smb_set_primary_group: "
+                "Running the command `%s' gave %d\n",add_script,ret));
+       return ret;
+}
+
+/****************************************************************************
+ Add a user to a UNIX group.
+****************************************************************************/
+
+int smb_add_user_group(char *unix_group, char *unix_user)
+{
+       pstring add_script;
+       int ret;
+
+       pstrcpy(add_script, lp_addusertogroup_script());
+       if (! *add_script) return -1;
+       pstring_sub(add_script, "%g", unix_group);
+       pstring_sub(add_script, "%u", unix_user);
+       ret = smbrun(add_script,NULL);
+       DEBUG(3,("smb_add_user_group: Running the command `%s' gave %d\n",add_script,ret));
+       return ret;
+}
+
+/****************************************************************************
+ Delete a user from a UNIX group
+****************************************************************************/
+
+int smb_delete_user_group(const char *unix_group, const char *unix_user)
+{
+       pstring del_script;
+       int ret;
+
+       pstrcpy(del_script, lp_deluserfromgroup_script());
+       if (! *del_script) return -1;
+       pstring_sub(del_script, "%g", unix_group);
+       pstring_sub(del_script, "%u", unix_user);
+       ret = smbrun(del_script,NULL);
+       DEBUG(3,("smb_delete_user_group: Running the command `%s' gave %d\n",del_script,ret));
+       return ret;
+}
+
+
+NTSTATUS pdb_default_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+                                DOM_SID sid, BOOL with_priv)
+{
+       return get_group_map_from_sid(sid, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+                                gid_t gid, BOOL with_priv)
+{
+       return get_group_map_from_gid(gid, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+                                char *name, BOOL with_priv)
+{
+       return get_group_map_from_ntname(name, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_add_group_mapping_entry(struct pdb_methods *methods,
+                                               GROUP_MAP *map)
+{
+       return add_mapping_entry(map, TDB_INSERT) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_update_group_mapping_entry(struct pdb_methods *methods,
+                                                  GROUP_MAP *map)
+{
+       return add_mapping_entry(map, TDB_REPLACE) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_delete_group_mapping_entry(struct pdb_methods *methods,
+                                                  DOM_SID sid)
+{
+       return group_map_remove(sid) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_enum_group_mapping(struct pdb_methods *methods,
+                                          enum SID_NAME_USE sid_name_use,
+                                          GROUP_MAP **rmap, int *num_entries,
+                                          BOOL unix_only, BOOL with_priv)
+{
+       return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only,
+                                 with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
diff --git a/source4/ignore.txt b/source4/ignore.txt
new file mode 100644 (file)
index 0000000..420095d
--- /dev/null
@@ -0,0 +1,4 @@
+all_info.out.fname
+compression_info.out.*_shift
+internal_information.out.*
+stream_info.out.streams[i].size
diff --git a/source4/include/.cvsignore b/source4/include/.cvsignore
new file mode 100644 (file)
index 0000000..f486cca
--- /dev/null
@@ -0,0 +1,8 @@
+build_env.h
+config.h
+config.h.in
+includes.h.gch
+proto.h
+stamp-h
+tdbsam2_parse_info.h
+wrepld_proto.h
diff --git a/source4/include/MacExtensions.h b/source4/include/MacExtensions.h
new file mode 100644 (file)
index 0000000..d09370e
--- /dev/null
@@ -0,0 +1,246 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) John H Terpstra 1996-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+   Copyright (C) Paul Ashton 1998
+    
+   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.
+*/
+#ifndef _MAC_EXTENSIONS_H
+#define _MAC_EXTENSIONS_H
+
+/* Folder that holds the stream info */
+#define STREAM_FOLDER                                  ".streams"
+#define STREAM_FOLDER_SLASH                    ".streams/"
+
+/* Common Streams Names*/
+#define DefaultStreamTestLen   6
+#define DefaultStreamTest              ":$DATA"
+#define AFPDATA_STREAM                         "::$DATA"
+#define AFPINFO_STREAM                         ":AFP_AfpInfo:$DATA"
+#define AFPRESOURCE_STREAM             ":AFP_Resource:$DATA"
+#define AFPCOMMENTS_STREAM             ":Comments:$DATA"
+#define AFPDESKTOP_STREAM              ":AFP_DeskTop:$DATA"
+#define AFPIDINDEX_STREAM              ":AFP_IdIndex:$DATA"
+
+/*
+** NT's AFP_AfpInfo stream structure
+*/
+#define APF_INFO_SIZE          0x3c            
+#define AFP_Signature          0x41465000 
+#define AFP_Version                    0x00000100
+#define AFP_BackupTime         0x00000080
+#define AFP_FinderSize         32
+/*
+** Orginal AFP_AfpInfo stream used by NT 
+** We needed a way to store the create date so SAMBA
+** AFP_AfpInfo adds for bytes to this structrure
+** and call's it _SambaAfpInfo
+*/
+typedef struct _AfpInfo
+{
+        uint32         afpi_Signature;                 /* Must be *(PDWORD)"AFP" */
+        uint32         afpi_Version;                   /* Must be 0x00010000 */
+        uint32         afpi_Reserved1;
+        uint32         afpi_BackupTime;                /* Backup time for the file/dir */
+        unsigned char  afpi_FinderInfo[AFP_FinderSize];        /* Finder Info (32 bytes) */
+        unsigned char  afpi_ProDosInfo[6];     /* ProDos Info (6 bytes) # */
+        unsigned char  afpi_Reserved2[6];
+} AfpInfo;
+
+typedef struct _SambaAfpInfo
+{
+        AfpInfo        afp; 
+        unsigned long  createtime;
+} SambaAfpInfo;
+
+/*
+** On SAMBA this structrue is followed by 4 bytes that store the create
+** date of the file or folder asociated with it.
+*/
+
+/*
+** These extentions are only supported with the NT LM 0.12 Dialect. These extentions
+** will be process on a share by share bases.
+*/
+
+/*
+** Trans2_Query_FS_Information Call is used by the MacCIFS extentions for three reasons.
+** First to see if the remote server share supports the basic Macintosh CIFS extentions.
+** Second to return some basic need information about the share to the Macintosh.
+** Third to see if this share support any other Macintosh extentions.
+**
+** We will be using infromation levels that are betwwen 0x300 and 0x399 for all Macintosh
+** extentions calls. The first of these will be the SMB_MAC_QUERY_FS_INFO level which
+** will allow the server to return the MacQueryFSInfo structure. All fields are Little
+** Endian unless other wise specified.
+*/
+#define SMB_MAC_QUERY_FS_INFO 0x301
+
+
+
+/* 
+** The server will return folder access control in the Trans2_Find_First2 
+** and Trans2_Find_Next2 message described later in this document. 
+*/
+#define SUPPORT_MAC_ACCESS_CNTRL       0x0010
+/*
+** The server supports setting/getting comments using the mechanism in this 
+** document instead of using the NTFS format described in the Introduction. 
+*/
+#define SUPPORT_MAC_GETSETCOMMENTS     0x0020
+/*
+** The Server supports setting and getting Macintosh desktop database information
+** using the mechanism in this document. 
+*/
+#define SUPPORT_MAC_DESKTOPDB_CALLS    0x0040
+/*
+** The server will return a unique id for files and directories in the 
+** Trans2_Find_First2 and Trans2_Find_Next2 message described later in this document. 
+*/
+#define SUPPORT_MAC_UNIQUE_IDS         0x0080
+/*
+** The server will return this flag telling the client that the server does
+** not support streams or the Macintosh extensions. The rest of this message
+** will be ignored by the client. 
+*/
+#define NO_STREAMS_OR_MAC_SUPPORT      0x0100
+
+/*
+** We will be adding a new info level to the Trans2_Find_First2 and Trans2_Find_Next2.
+** This info level will be SMB_MAC_FIND_BOTH_HFS_INFO and will support the server
+** return additional information need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_FIND_BOTH_HFS_INFO       0x302
+
+enum {
+       ownerRead       = 0x0400,
+       ownerWrite      = 0x0200,
+       ownerSearch     = 0x0100,
+       groupRead       = 0x0040,
+       groupWrite      = 0x0020,
+       groupSearch     = 0x0010,
+       otherRead       = 0x0004,
+       otherWrite      = 0x0002,
+       otherSearch     = 0x0001,
+       Owner           = 0x0800
+};
+       
+
+/*
+** We will be adding a new info level to the Trans2_Set_Path_Information.
+** This info level will be SMB_MAC_SET_FINDER_INFO and will support the client
+** setting information on the server need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_SET_FINDER_INFO          0x303
+
+enum {
+       SetCreateDate   = 0x01,                 /* If this is set then set the create date of the file/folder */
+       SetModDate              = 0x02,                 /* If this is set then set the modify date of the file/folder */
+       SetFLAttrib             = 0x04,                 /* If this is set then set the Macintosh lock bit of the file/folder */
+       FndrInfo1               = 0x08,                 /* If this is set then set the first 16 bytes of finder info */
+       FndrInfo2               = 0x10,                 /* If this is set then set the second 16 bytes of finder info */
+       SetHidden               = 0x20                  /* We are either setting or unsetting the hidden bit */
+};
+
+
+/*
+** We will be adding some new info level to the Trans2_Set_Path_Information and Trans2_Query_Path_Information.
+** These info levels will allow the client to add, get, and remove desktop inforamtion from the
+** server. How the server stores this information is up to them.
+*/
+
+/*
+** We need to be able to store an application name and its creator in a database. We send a 
+** Trans2_Set_Path_Information call with the full path of the application in the path field. 
+** We will send an info level that represents adding an application name and creator to the database.
+** We will pass the File Creator in the data message. 
+**
+** The server should just respond  with no error or an error.
+*/
+#define SMB_MAC_DT_ADD_APPL      0x304
+
+/*
+** We need to be able to remove an application name and its creator from a database. We send a 
+** Trans2_Set_Path_Information call with the full path of the application in the path field. 
+** We will send an info level that represents removing an application name and creator from the database.
+** We will pass the File Creator in the data message. 
+**
+** The server should just respond  with no error or an error.
+*/
+#define SMB_MAC_DT_REMOVE_APPL   0x305
+
+
+/*
+** We need to be able to get an application name and its creator from a database. We send a 
+** Trans2_Query_Path_Information call in which the name field is just ignore. 
+** We will send an info level that represents getting an application name with a structure that 
+** contains the File Creator and index. Were index has the following meaning.
+**             Index = 0; Get the application path from the database with the most current date.
+**             Index > 0; Use the index to find the application path from the database.
+**                             e.g.    index of 5 means get the fifth entry of this application name in the database.
+**                                             if not entry return an error.
+**
+** The server returns with a structure that contains the full path to the appication and
+** its creator's date.
+*/
+#define SMB_MAC_DT_GET_APPL      0x306
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in 
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the Requested size of the icon, the Icon type, File Creator, and File Type.
+**
+** The server returns with a structure that contains the actual size of the icon 
+** (must be less than requested length) and the icon bit map. 
+*/
+#define SMB_MAC_DT_GET_ICON      0x307
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in 
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the index and File Creator. The index allows the client to make repeated calls to the server
+** gathering all icon stored by this file creator.
+**             
+**
+** The server returns with a structure that contains the actual size of the icon 
+** (must be less than requested length) and the icon bit map, File Type, and Icon Type. 
+*/
+#define SMB_MAC_DT_GET_ICON_INFO         0x308
+
+
+
+/*
+** We need to be able to add an icon to a database. We send a Trans2_Set_Path_Information call in 
+** which the path name is ignore. We will send an info level that represents setting an icon with a structure
+** that contains the icon data, icon size, icon type, the file type, and file creator.
+**             
+**
+** The server returns only that the call was succesfull or not. 
+*/
+#define SMB_MAC_DT_ADD_ICON      0x309
+
+#endif /* _MAC_EXTENSIONS_H */
+
+/* _MAC_EXTENSIONS_H */
+
diff --git a/source4/include/ads.h b/source4/include/ads.h
new file mode 100644 (file)
index 0000000..f90983e
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+  header for ads (active directory) library routines
+
+  basically this is a wrapper around ldap
+*/
+
+typedef struct {
+       void *ld; /* the active ldap structure */
+       struct in_addr ldap_ip; /* the ip of the active connection, if any */
+       time_t last_attempt; /* last attempt to reconnect */
+       int ldap_port;
+       
+       /* info needed to find the server */
+       struct {
+               char *realm;
+               char *workgroup;
+               char *ldap_server;
+               char *ldap_uri;
+               int foreign; /* set to 1 if connecting to a foreign realm */
+       } server;
+
+       /* info needed to authenticate */
+       struct {
+               char *realm;
+               char *password;
+               char *user_name;
+               char *kdc_server;
+               unsigned flags;
+               int time_offset;
+       } auth;
+
+       /* info derived from the servers config */
+       struct {
+               char *realm;
+               char *bind_path;
+               char *ldap_server_name;
+               time_t current_time;
+       } config;
+} ADS_STRUCT;
+
+/* there are 4 possible types of errors the ads subsystem can produce */
+enum ads_error_type {ADS_ERROR_KRB5, ADS_ERROR_GSS, 
+                    ADS_ERROR_LDAP, ADS_ERROR_SYSTEM, ADS_ERROR_NT};
+
+typedef struct {
+       enum ads_error_type error_type;
+       union err_state{                
+               int rc;
+               NTSTATUS nt_status;
+       } err;
+       /* For error_type = ADS_ERROR_GSS minor_status describe GSS API error */
+       /* Where rc represents major_status of GSS API error */
+       int minor_status;
+} ADS_STATUS;
+
+#ifdef HAVE_ADS
+typedef LDAPMod **ADS_MODLIST;
+#else
+typedef void **ADS_MODLIST;
+#endif
+
+/* macros to simplify error returning */
+#define ADS_ERROR(rc) ADS_ERROR_LDAP(rc)
+#define ADS_ERROR_LDAP(rc) ads_build_error(ADS_ERROR_LDAP, rc, 0)
+#define ADS_ERROR_SYSTEM(rc) ads_build_error(ADS_ERROR_SYSTEM, rc?rc:EINVAL, 0)
+#define ADS_ERROR_KRB5(rc) ads_build_error(ADS_ERROR_KRB5, rc, 0)
+#define ADS_ERROR_GSS(rc, minor) ads_build_error(ADS_ERROR_GSS, rc, minor)
+#define ADS_ERROR_NT(rc) ads_build_nt_error(ADS_ERROR_NT,rc)
+
+#define ADS_ERR_OK(status) ((status.error_type == ADS_ERROR_NT) ? NT_STATUS_IS_OK(status.err.nt_status):(status.err.rc == 0))
+#define ADS_SUCCESS ADS_ERROR(0)
+
+/* time between reconnect attempts */
+#define ADS_RECONNECT_TIME 5
+
+/* timeout on searches */
+#define ADS_SEARCH_TIMEOUT 10
+
+/* ldap control oids */
+#define ADS_PAGE_CTL_OID "1.2.840.113556.1.4.319"
+#define ADS_NO_REFERRALS_OID "1.2.840.113556.1.4.1339"
+#define ADS_SERVER_SORT_OID "1.2.840.113556.1.4.473"
+#define ADS_PERMIT_MODIFY_OID "1.2.840.113556.1.4.1413"
+
+/* UserFlags for userAccountControl */
+#define UF_SCRIPT                              0x00000001
+#define UF_ACCOUNTDISABLE                      0x00000002
+#define UF_UNUSED_1                            0x00000004
+#define UF_HOMEDIR_REQUIRED                    0x00000008
+
+#define UF_LOCKOUT                             0x00000010
+#define UF_PASSWD_NOTREQD                      0x00000020
+#define UF_PASSWD_CANT_CHANGE                  0x00000040
+#define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED     0x00000080
+
+#define UF_TEMP_DUPLICATE_ACCOUNT              0x00000100
+#define UF_NORMAL_ACCOUNT                      0x00000200
+#define UF_UNUSED_2                            0x00000400
+#define UF_INTERDOMAIN_TRUST_ACCOUNT           0x00000800
+
+#define UF_WORKSTATION_TRUST_ACCOUNT           0x00001000
+#define UF_SERVER_TRUST_ACCOUNT                0x00002000
+#define UF_UNUSED_3                            0x00004000
+#define UF_UNUSED_4                            0x00008000
+
+#define UF_DONT_EXPIRE_PASSWD                  0x00010000
+#define UF_MNS_LOGON_ACCOUNT                   0x00020000
+#define UF_SMARTCARD_REQUIRED                  0x00040000
+#define UF_TRUSTED_FOR_DELEGATION              0x00080000
+
+#define UF_NOT_DELEGATED                       0x00100000
+#define UF_USE_DES_KEY_ONLY                    0x00200000
+#define UF_DONT_REQUIRE_PREAUTH                        0x00400000
+#define UF_UNUSED_5                            0x00800000
+
+#define UF_UNUSED_6                            0x01000000
+#define UF_UNUSED_7                            0x02000000
+#define UF_UNUSED_8                            0x04000000
+#define UF_UNUSED_9                            0x08000000
+
+#define UF_UNUSED_10                           0x10000000
+#define UF_UNUSED_11                           0x20000000
+#define UF_UNUSED_12                           0x40000000
+#define UF_UNUSED_13                           0x80000000
+
+#define UF_MACHINE_ACCOUNT_MASK (\
+               UF_INTERDOMAIN_TRUST_ACCOUNT |\
+               UF_WORKSTATION_TRUST_ACCOUNT |\
+               UF_SERVER_TRUST_ACCOUNT \
+               )
+
+#define UF_ACCOUNT_TYPE_MASK (\
+               UF_TEMP_DUPLICATE_ACCOUNT |\
+               UF_NORMAL_ACCOUNT |\
+               UF_INTERDOMAIN_TRUST_ACCOUNT |\
+               UF_WORKSTATION_TRUST_ACCOUNT |\
+               UF_SERVER_TRUST_ACCOUNT \
+                )
+
+#define UF_SETTABLE_BITS (\
+               UF_SCRIPT |\
+               UF_ACCOUNTDISABLE |\
+               UF_HOMEDIR_REQUIRED  |\
+               UF_LOCKOUT |\
+               UF_PASSWD_NOTREQD |\
+               UF_PASSWD_CANT_CHANGE |\
+               UF_ACCOUNT_TYPE_MASK | \
+               UF_DONT_EXPIRE_PASSWD | \
+               UF_MNS_LOGON_ACCOUNT |\
+               UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED |\
+               UF_SMARTCARD_REQUIRED |\
+               UF_TRUSTED_FOR_DELEGATION |\
+               UF_NOT_DELEGATED |\
+               UF_USE_DES_KEY_ONLY  |\
+               UF_DONT_REQUIRE_PREAUTH \
+               )
+
+/* sAMAccountType */
+#define ATYPE_NORMAL_ACCOUNT                   0x30000000 /* 805306368 */
+#define ATYPE_WORKSTATION_TRUST                        0x30000001 /* 805306369 */
+#define ATYPE_INTERDOMAIN_TRUST                        0x30000002 /* 805306370 */ 
+#define ATYPE_SECURITY_GLOBAL_GROUP            0x10000000 /* 268435456 */
+#define ATYPE_DISTRIBUTION_GLOBAL_GROUP                0x10000001 /* 268435457 */
+#define ATYPE_DISTRIBUTION_UNIVERSAL_GROUP     ATYPE_DISTRIBUTION_GLOBAL_GROUP
+#define ATYPE_SECURITY_LOCAL_GROUP             0x20000000 /* 536870912 */
+#define ATYPE_DISTRIBUTION_LOCAL_GROUP         0x20000001 /* 536870913 */
+
+#define ATYPE_ACCOUNT          ATYPE_NORMAL_ACCOUNT            /* 0x30000000 805306368 */
+#define ATYPE_GLOBAL_GROUP     ATYPE_SECURITY_GLOBAL_GROUP     /* 0x10000000 268435456 */
+#define ATYPE_LOCAL_GROUP      ATYPE_SECURITY_LOCAL_GROUP      /* 0x20000000 536870912 */
+
+/* groupType */
+#define GTYPE_SECURITY_BUILTIN_LOCAL_GROUP     0x80000005      /* -2147483643 */
+#define GTYPE_SECURITY_DOMAIN_LOCAL_GROUP      0x80000004      /* -2147483644 */
+#define GTYPE_SECURITY_GLOBAL_GROUP            0x80000002      /* -2147483646 */
+#define GTYPE_DISTRIBUTION_GLOBAL_GROUP                0x00000002      /* 2 */
+#define GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP  0x00000004      /* 4 */
+#define GTYPE_DISTRIBUTION_UNIVERSAL_GROUP     0x00000008      /* 8 */
+
+/* Mailslot or cldap getdcname response flags */
+#define ADS_PDC            0x00000001  /* DC is PDC */
+#define ADS_GC             0x00000004  /* DC is a GC of forest */
+#define ADS_LDAP           0x00000008  /* DC is an LDAP server */
+#define ADS_DS             0x00000010  /* DC supports DS */
+#define ADS_KDC            0x00000020  /* DC is running KDC */
+#define ADS_TIMESERV       0x00000040  /* DC is running time services */
+#define ADS_CLOSEST        0x00000080  /* DC is closest to client */
+#define ADS_WRITABLE       0x00000100  /* DC has writable DS */
+#define ADS_GOOD_TIMESERV  0x00000200  /* DC has hardware clock
+                                        (and running time) */
+#define ADS_NDNC           0x00000400  /* DomainName is non-domain NC serviced
+                                        by LDAP server */
+#define ADS_PINGS          0x0000FFFF  /* Ping response */
+#define ADS_DNS_CONTROLLER 0x20000000  /* DomainControllerName is a DNS name*/
+#define ADS_DNS_DOMAIN     0x40000000  /* DomainName is a DNS name */
+#define ADS_DNS_FOREST     0x80000000  /* DnsForestName is a DNS name */
+
+/* DomainCntrollerAddressType */
+#define ADS_INET_ADDRESS      0x00000001
+#define ADS_NETBIOS_ADDRESS   0x00000002
+
+
+/* ads auth control flags */
+#define ADS_AUTH_DISABLE_KERBEROS 0x01
+#define ADS_AUTH_NO_BIND          0x02
+#define ADS_AUTH_ANON_BIND        0x04
+#define ADS_AUTH_SIMPLE_BIND      0x08
+
+/* Kerberos environment variable names */
+#define KRB5_ENV_CCNAME "KRB5CCNAME"
+
+/* Heimdal uses a slightly different name */
+#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+#define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
+#endif
diff --git a/source4/include/adt_tree.h b/source4/include/adt_tree.h
new file mode 100644 (file)
index 0000000..b1bf7ad
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  Generic Abstract Data Types
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+#ifndef ADT_TREE_H
+#define ADT_TREE_H
+
+typedef struct _tree_node {
+       struct _tree_node       *parent;
+       struct _tree_node       **children;
+       int                     num_children;
+       char                    *key;
+       void                    *data_p;
+} TREE_NODE;
+
+typedef struct _tree_root {
+       TREE_NODE       *root;
+       int             (*compare)(void* x, void *y);
+       void            (*free)(void *p);
+} SORTED_TREE;
+
+#endif
diff --git a/source4/include/asn_1.h b/source4/include/asn_1.h
new file mode 100644 (file)
index 0000000..7d4da0d
--- /dev/null
@@ -0,0 +1,69 @@
+/* 
+   Unix SMB/CIFS implementation.   
+   simple ASN1 code
+   Copyright (C) Andrew Tridgell 2001
+   
+   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.
+*/
+
+#ifndef _ASN_1_H
+#define _ASN_1_H
+
+struct nesting {
+       off_t start;
+       size_t taglen; /* for parsing */
+       struct nesting *next;
+};
+
+typedef struct {
+       uint8 *data;
+       size_t length;
+       off_t ofs;
+       struct nesting *nesting;
+       BOOL has_error;
+} ASN1_DATA;
+
+
+#define ASN1_APPLICATION(x) ((x)+0x60)
+#define ASN1_SEQUENCE(x) ((x)+0x30)
+#define ASN1_CONTEXT(x) ((x)+0xa0)
+#define ASN1_GENERAL_STRING 0x1b
+#define ASN1_OCTET_STRING 0x4
+#define ASN1_OID 0x6
+#define ASN1_BOOLEAN 0x1
+#define ASN1_INTEGER 0x2
+#define ASN1_ENUMERATED 0xa
+#define ASN1_SET 0x31
+
+#define ASN1_MAX_OIDS 20
+
+/* some well known object IDs */
+#define OID_SPNEGO "1 3 6 1 5 5 2"
+#define OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
+#define OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
+#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
+
+#define SPNEGO_NEG_RESULT_ACCEPT 0
+#define SPNEGO_NEG_RESULT_INCOMPLETE 1
+#define SPNEGO_NEG_RESULT_REJECT 2
+
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ      "\x01\x00"
+#define TOK_ID_KRB_AP_REP      "\x02\x00"
+#define TOK_ID_KRB_ERROR       "\x03\x00"
+#define TOK_ID_GSS_GETMIC      "\x01\x01"
+#define TOK_ID_GSS_WRAP                "\x02\x01"
+
+#endif /* _ASN_1_H */
diff --git a/source4/include/auth.h b/source4/include/auth.h
new file mode 100644 (file)
index 0000000..e37f181
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef _SMBAUTH_H_
+#define _SMBAUTH_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Standardised Authentication types
+   Copyright (C) Andrew Bartlett 2001
+   
+   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.
+*/
+
+/* AUTH_STR - string */
+typedef struct normal_string
+{
+       int len;
+       char *str;
+} AUTH_STR;
+
+/* AUTH_UNISTR - unicode string or buffer */
+typedef struct unicode_string
+{
+       int len;
+       uchar *unistr;
+} AUTH_UNISTR;
+
+typedef struct interactive_password
+{
+       OWF_INFO          lm_owf;              /* LM OWF Password */
+       OWF_INFO          nt_owf;              /* NT OWF Password */
+} auth_interactive_password;
+
+#define AUTH_FLAG_NONE        0x000000
+#define AUTH_FLAG_PLAINTEXT   0x000001
+#define AUTH_FLAG_LM_RESP     0x000002
+#define AUTH_FLAG_NTLM_RESP   0x000004
+#define AUTH_FLAG_NTLMv2_RESP 0x000008
+
+typedef struct auth_usersupplied_info
+{
+       
+       DATA_BLOB lm_resp;
+       DATA_BLOB nt_resp;
+       auth_interactive_password * interactive_password;
+       DATA_BLOB plaintext_password;
+       
+       BOOL encrypted;
+       
+       uint32 auth_flags;
+
+       AUTH_STR           client_domain;          /* domain name string */
+       AUTH_STR           domain;               /* domain name after mapping */
+       AUTH_STR           internal_username;    /* username after mapping */
+       AUTH_STR           smb_name;        /* username before mapping */
+       AUTH_STR           wksta_name;           /* workstation name (netbios calling name) unicode string */
+       
+} auth_usersupplied_info;
+
+#define SAM_FILL_NAME  0x01
+#define SAM_FILL_INFO3 0x02
+#define SAM_FILL_SAM   0x04
+#define SAM_FILL_UNIX  0x08
+#define SAM_FILL_ALL (SAM_FILL_NAME | SAM_FILL_INFO3 | SAM_FILL_SAM | SAM_FILL_UNIX)
+
+typedef struct auth_serversupplied_info 
+{
+       BOOL guest;
+       
+       /* This groups info is needed for when we become_user() for this uid */
+       int n_groups;
+       gid_t *groups;
+       
+       /* NT group information taken from the info3 structure */
+       
+       NT_USER_TOKEN *ptok;
+       
+       uint8 session_key[16];
+       
+       uint8 first_8_lm_hash[8];
+
+       uint32 sam_fill_level;  /* How far is this structure filled? */
+       
+       SAM_ACCOUNT *sam_account;
+       
+       void *pam_handle;
+       
+} auth_serversupplied_info;
+
+struct auth_context {
+       DATA_BLOB challenge; 
+
+       /* Who set this up in the first place? */ 
+       const char *challenge_set_by; 
+
+       struct auth_methods *challenge_set_method; 
+       /* What order are the various methods in?   Try to stop it changing under us */ 
+       struct auth_methods *auth_method_list;  
+
+       TALLOC_CTX *mem_ctx;
+       const uint8 *(*get_ntlm_challenge)(struct auth_context *auth_context);
+       NTSTATUS (*check_ntlm_password)(const struct auth_context *auth_context,
+                                       const struct auth_usersupplied_info *user_info, 
+                                       struct auth_serversupplied_info **server_info);
+       NTSTATUS (*nt_status_squash)(NTSTATUS nt_status);
+       void (*free)(struct auth_context **auth_context);
+};
+
+typedef struct auth_methods
+{
+       struct auth_methods *prev, *next;
+       const char *name; /* What name got this module */
+
+       NTSTATUS (*auth)(const struct auth_context *auth_context,
+                        void *my_private_data, 
+                        TALLOC_CTX *mem_ctx,
+                        const struct auth_usersupplied_info *user_info, 
+                        auth_serversupplied_info **server_info);
+
+       DATA_BLOB (*get_chal)(const struct auth_context *auth_context,
+                             void **my_private_data, 
+                             TALLOC_CTX *mem_ctx);
+       
+       /* Used to keep tabs on things like the cli for SMB server authentication */
+       void *private_data;
+       
+       /* Function to clean up the above arbitary structure */
+       void (*free_private_data)(void **private_data);
+
+       /* Function to send a keepalive message on the above structure */
+       void (*send_keepalive)(void **private_data);
+
+} auth_methods;
+
+typedef NTSTATUS (*auth_init_function)(struct auth_context *, const char *, struct auth_methods **);
+
+struct auth_init_function_entry {
+       const char *name;
+       /* Function to create a member of the authmethods list */
+
+       auth_init_function init;
+};
+
+typedef struct auth_ntlmssp_state
+{
+       TALLOC_CTX *mem_ctx;
+       struct auth_context *auth_context;
+       struct auth_serversupplied_info *server_info;
+       struct ntlmssp_state *ntlmssp_state;
+} AUTH_NTLMSSP_STATE;
+
+#endif /* _SMBAUTH_H_ */
diff --git a/source4/include/byteorder.h b/source4/include/byteorder.h
new file mode 100644 (file)
index 0000000..ed0eaba
--- /dev/null
@@ -0,0 +1,175 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB Byte handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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.
+*/
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) ((unsigned)(((unsigned char *)(buf))[pos])) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+
+/* macros for accessing SMB protocol elements */
+#define VWV(vwv) ((vwv)*2)
+
+#endif /* _BYTEORDER_H */
diff --git a/source4/include/charset.h b/source4/include/charset.h
new file mode 100644 (file)
index 0000000..3b3e613
--- /dev/null
@@ -0,0 +1,40 @@
+/* 
+   Unix SMB/CIFS implementation.
+   charset defines
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Jelmer Vernooij 2002
+   
+   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.
+*/
+
+/* this defines the charset types used in samba */
+typedef enum {CH_UCS2=0, CH_UNIX=1, CH_DISPLAY=2, CH_DOS=3, CH_UTF8=4} charset_t;
+
+#define NUM_CHARSETS 5
+
+/*
+ *   for each charset we have a function that pulls from that charset to
+ *     a ucs2 buffer, and a function that pushes to a ucs2 buffer
+ *     */
+
+struct charset_functions {
+       const char *name;
+       size_t (*pull)(void *, const char **inbuf, size_t *inbytesleft,
+                                  char **outbuf, size_t *outbytesleft);
+       size_t (*push)(void *, const char **inbuf, size_t *inbytesleft,
+                                  char **outbuf, size_t *outbytesleft);
+       struct charset_functions *prev, *next;
+};
+
diff --git a/source4/include/cli_context.h b/source4/include/cli_context.h
new file mode 100644 (file)
index 0000000..184327e
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+   Copyright (C) Jeremy Allison 1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+   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.
+*/
+
+#ifndef _CLI_CONTEXT_H
+#define _CLI_CONTEXT_H
+
+struct cli_tree;  /* forward declare */
+struct cli_request;  /* forward declare */
+struct cli_session;  /* forward declare */
+struct cli_transport;  /* forward declare */
+
+typedef struct smb_sign_info {
+       void (*sign_outgoing_message)(struct cli_request *req);
+       BOOL (*check_incoming_message)(struct cli_request *req);
+       void (*free_signing_context)(struct cli_transport *transport);
+       void *signing_context;
+
+       BOOL doing_signing;
+} smb_sign_info;
+
+/* context that will be and has been negotiated between the client and server */
+struct cli_negotiate {
+       /* 
+        * negotiated maximum transmit size - this is given to us by the server
+        */
+       unsigned max_xmit;
+
+       /* maximum number of requests that can be multiplexed */
+       uint16 max_mux;
+
+       /* the negotiatiated protocol */
+       enum protocol_types protocol;
+
+       int sec_mode;           /* security mode returned by negprot */
+       DATA_BLOB secblob;      /* cryptkey or negTokenInit blob */
+       uint32 sesskey;
+       
+       smb_sign_info sign_info;
+
+       /* capabilities that the server reported */
+       uint32 capabilities;
+       
+       int server_zone;
+       time_t server_time;
+       int readbraw_supported:1;
+       int writebraw_supported:1;
+
+       const char *server_domain;
+};
+       
+/* this is the context for a SMB socket associated with the socket itself */
+struct cli_socket {
+       TALLOC_CTX *mem_ctx;    /* life of socket pool */
+
+       /* when the reference count reaches zero then the socket is destroyed */
+       int reference_count;
+
+       struct in_addr dest_ip;
+
+       /* the port used */
+       int port;
+       
+       /* the open file descriptor */
+       int fd;
+
+       /* a count of the number of packets we have received. We
+        * actually only care about zero/non-zero at this stage */
+       unsigned pkt_count;
+
+       /* the network address of the client */
+       char *client_addr;
+       
+       /* timeout for socket operations in milliseconds. */
+       int timeout;
+};
+
+/*
+  this structure allows applications to control the behaviour of the
+  client library
+*/
+struct cli_options {
+       int use_oplocks:1;
+       int use_level2_oplocks:1;
+       int use_spnego:1;
+};
+
+/* this is the context for the client transport layer */
+struct cli_transport {
+       TALLOC_CTX *mem_ctx;
+
+       /* when the reference count reaches zero then the transport is destroyed */
+       int reference_count;
+
+       /* socket level info */
+       struct cli_socket *socket;
+
+       /* the next mid to be allocated - needed for signing and
+          request matching */
+       uint16 next_mid;
+       
+       /* negotiated protocol information */
+       struct cli_negotiate negotiate;
+
+       /* options to control the behaviour of the client code */
+       struct cli_options options;
+
+       /* is a readbraw pending? we need to handle that case
+          specially on receiving packets */
+       int readbraw_pending:1;
+       
+       /* an idle function - if this is defined then it will be
+          called once every period milliseconds while we are waiting
+          for a packet */
+       struct {
+               void (*func)(struct cli_transport *, void *);
+               void *private;
+               uint_t period;
+       } idle;
+
+       /* the error fields from the last message */
+       struct {
+               enum {ETYPE_NONE, ETYPE_DOS, ETYPE_NT, ETYPE_SOCKET, ETYPE_NBT} etype;
+               union {
+                       struct {
+                               uint8 eclass;
+                               uint16 ecode;
+                       } dos;
+                       NTSTATUS nt_status;
+                       enum socket_error socket_error;
+                       unsigned nbt_error;
+               } e;
+       } error;
+
+       struct {
+               /* a oplock break request handler */
+               BOOL (*handler)(struct cli_transport *transport, 
+                               uint16 tid, uint16 fnum, uint8 level, void *private);
+               /* private data passed to the oplock handler */
+               void *private;
+       } oplock;
+
+       /* a list of async requests that are pending on this connection */
+       struct cli_request *pending_requests;
+};
+
+/* this is the context for the user */
+
+/* this is the context for the session layer */
+struct cli_session {   
+       TALLOC_CTX *mem_ctx;    /* life of session */
+
+       /* when the reference count reaches zero then the session is destroyed */
+       int reference_count;    
+       
+       /* transport layer info */
+       struct cli_transport *transport;
+       
+       /* after a session setup the server provides us with
+          a vuid identifying the security context */
+       uint16 vuid;
+
+       /* default pid for this session */
+       uint16 pid;
+};
+
+/* 
+   cli_tree context: internal state for a tree connection. 
+ */
+struct cli_tree {
+       /* life of tree tree */
+       TALLOC_CTX *mem_ctx;
+
+       /* when the reference count reaches zero then the tree is destroyed */
+       int reference_count;    
+
+       /* session layer info */
+       struct cli_session *session;
+
+       uint16 tid;                     /* tree id, aka cnum */
+       char *device;
+       char *fs_type;
+};
+
+/* the context for a single SMB request. This is passed to any request-context 
+ * functions (similar to context.h, the server version).
+ * This will allow requests to be multi-threaded. */
+struct cli_request {
+       /* allow a request to be part of a list of requests */
+       struct cli_request *next, *prev;
+
+       /* a talloc context for the lifetime of this request */
+       TALLOC_CTX *mem_ctx;
+       
+       /* a request always has a transport context, nearly always has
+          a session context and usually has a tree context */
+       struct cli_transport *transport;
+       struct cli_session *session;
+       struct cli_tree *tree;
+
+       /* the flags2 from the SMB request, in raw form (host byte
+          order). Used to parse strings */
+       uint16 flags2;
+
+       /* the NT status for this request. Set by packet receive code
+          or code detecting error. */
+       NTSTATUS status;
+       
+       /* the sequence number of this packet - used for signing */
+       unsigned seq_num;
+
+       /* set if this is a one-way request, meaning we are not
+          expecting a reply from the server. */
+       int one_way_request:1;
+
+       /* the mid of this packet - used to match replies */
+       uint16 mid;
+
+       struct {
+               /* the raw SMB buffer, including the 4 byte length header */
+               char *buffer;
+               
+               /* the size of the raw buffer, including 4 byte header */
+               unsigned size;
+
+               /* how much has been allocated - on reply the buffer is over-allocated to 
+                  prevent too many realloc() calls 
+               */
+               unsigned allocated;
+
+               /* the start of the SMB header - this is always buffer+4 */
+               char *hdr;
+
+               /* the command words and command word count. vwv points
+                  into the raw buffer */
+               char *vwv;
+               unsigned wct;
+
+               /* the data buffer and size. data points into the raw buffer */
+               char *data;
+               unsigned data_size;
+
+               /* ptr is used as a moving pointer into the data area
+                * of the packet. The reason its here and not a local
+                * variable in each function is that when a realloc of
+                * a send packet is done we need to move this
+                * pointer */
+               char *ptr;
+       } in, out;
+
+       /* information on what to do with a reply when it is received
+          asyncronously. If this is not setup when a reply is received then
+          the reply is discarded
+
+          The private pointer is private to the caller of the client
+          library (the application), not private to the library
+       */
+       struct {
+               void (*fn)(struct cli_request *);
+               void *private;
+       } async;
+};
+
+/* 
+   cli_state: internal state used in libcli library for single-threaded callers, 
+   i.e. a single session on a single socket. 
+ */
+struct cli_state {
+       TALLOC_CTX *mem_ctx;    /* life of client pool */
+       struct cli_transport *transport;
+       struct cli_session *session;
+       struct cli_tree *tree;
+       struct substitute_context substitute;
+};
+
+/* useful way of catching wct errors with file and line number */
+#define CLI_CHECK_MIN_WCT(req, wcount) if ((req)->in.wct < (wcount)) { \
+      DEBUG(1,("Unexpected WCT %d at %s(%d) - expected min %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+      req->status = NT_STATUS_INVALID_PARAMETER; \
+      goto failed; \
+}
+
+#define CLI_CHECK_WCT(req, wcount) if ((req)->in.wct != (wcount)) { \
+      DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+      req->status = NT_STATUS_INVALID_PARAMETER; \
+      goto failed; \
+}
+
+#endif /* _CLI_CONTEXT_H */
diff --git a/source4/include/client.h b/source4/include/client.h
new file mode 100644 (file)
index 0000000..015c8fb
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+   Copyright (C) Jeremy Allison 1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+   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.
+*/
+
+#ifndef _CLIENT_H
+#define _CLIENT_H
+
+/* the client asks for a smaller buffer to save ram and also to get more
+   overlap on the wire. This size gives us a nice read/write size, which
+   will be a multiple of the page size on almost any system */
+#define CLI_BUFFER_SIZE (0xFFFF)
+#define CLI_DFS_MAX_REFERRAL_LEVEL 3
+
+#define SAFETY_MARGIN 1024
+#define LARGE_WRITEX_HDR_SIZE 65
+
+
+/*
+ * These definitions depend on smb.h
+ */
+
+typedef struct file_info
+{
+       SMB_BIG_UINT size;
+       uint16 mode;
+       uid_t uid;
+       gid_t gid;
+       /* these times are normally kept in GMT */
+       time_t mtime;
+       time_t atime;
+       time_t ctime;
+       const char *name;
+       char short_name[13*3]; /* the *3 is to cope with multi-byte */
+} file_info;
+
+struct print_job_info
+{
+       uint16 id;
+       uint16 priority;
+       size_t size;
+       fstring user;
+       fstring name;
+       time_t t;
+};
+
+typedef struct referral_info
+{
+       int server_type;
+       int referral_flags;
+       int proximity;
+       int ttl;
+       int pathOffset;
+       int altPathOffset;
+       int nodeOffset;
+       char *path;
+       char *altPath;
+       char *node;
+       char *host;
+       char *share;
+} referral_info;
+
+typedef struct dfs_info
+{
+       int path_consumed;
+       int referral_flags;
+       int selected_referral;
+       int number_referrals;
+       referral_info referrals[10];
+} dfs_info;
+
+/* Internal client error codes for cli_request_context.internal_error_code */
+#define CLI_ERR_INVALID_TRANS_RESPONSE         100
+
+#define DFS_MAX_CLUSTER_SIZE 8
+/* client_context: used by cliraw callers to maintain Dfs
+ * state across multiple Dfs servers
+ */
+struct cli_client
+{
+       const char* sockops;
+       char* username;
+       char* password;
+       char* workgroup;
+       TALLOC_CTX *mem_ctx;
+       int number_members;
+       BOOL use_dfs;                           /* True if client should support Dfs */
+       int connection_flags;           /* see CLI_FULL_CONN.. below */
+       uint16 max_xmit_frag;
+       uint16 max_recv_frag;
+       struct cli_state *cli[DFS_MAX_CLUSTER_SIZE];
+};
+
+#define CLI_FULL_CONNECTION_DONT_SPNEGO 0x0001
+#define CLI_FULL_CONNECTION_USE_KERBEROS 0x0002
+#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004
+#define CLI_FULL_CONNECTION_USE_DFS 0x0008
+
+#include "cli_context.h"
+#endif /* _CLIENT_H */
diff --git a/source4/include/clitar.h b/source4/include/clitar.h
new file mode 100644 (file)
index 0000000..b773117
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * clitar file format
+ * Copyright (C) Andrew Tridgell              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.  
+ */
+
+#ifndef _CLITAR_H
+#define _CLITAR_H
+
+#define TBLOCK 512
+#define NAMSIZ 100
+union hblock {
+  char dummy[TBLOCK];
+  struct header {
+    char name[NAMSIZ];
+    char mode[8];
+    char uid[8];
+    char gid[8];
+    char size[12];
+    char mtime[12];
+    char chksum[8];
+    char linkflag;
+    char linkname[NAMSIZ];
+  } dbuf;
+};
+
+#endif /* _CLITAR_H */
diff --git a/source4/include/context.h b/source4/include/context.h
new file mode 100644 (file)
index 0000000..102403d
--- /dev/null
@@ -0,0 +1,346 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Andrew Tridgell              2003
+   Copyright (C) James J Myers                       2003 <myersjj@samba.org>
+   
+   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.
+*/
+
+/*
+  this header declares the core context structures associated with smb
+  sockets, tree connects, requests etc
+
+  the idea is that we will eventually get rid of all our global
+  variables and instead store our stang from structures hanging off
+  these basic elements
+*/
+
+/* the current user context for a request */
+struct user_context {
+       /* the vuid is used to specify the security context for this
+          request. Note that this may not be the same vuid as we
+          received on the wire (for example, for share mode or guest
+          access) */
+       uint16 vuid;
+
+       /* the domain name, user name etc - mostly used in % substitutions */
+       struct userdom_struct *user;
+
+       struct user_struct *vuser;
+};
+
+/* the context for a single SMB request. This is passed to any request-context 
+   functions */
+struct request_context {
+       /* the server_context contains all context specific to this SMB socket */
+       struct server_context *smb;
+
+       /* conn is only set for operations that have a valid TID */
+       struct tcon_context *conn;
+
+       /* the user context is derived from the vuid plus smb.conf options */
+       struct user_context *user_ctx;
+
+       /* a talloc context for the lifetime of this request */
+       TALLOC_CTX *mem_ctx;
+
+       /* a set of flags to control usage of the request. See REQ_CONTROL_* */
+       unsigned control_flags;
+
+       /* the smb pid is needed for locking contexts */
+       uint16 smbpid;
+
+       /* the flags from the SMB request, in raw form (host byte order) */
+       uint16 flags, flags2;
+
+       /* the system time when the request arrived */
+       struct timeval request_time;
+
+       /* this can contain a fnum from an earlier part of a chained
+        * message (such as an SMBOpenX), or -1 */
+       int chained_fnum;
+
+       /* how far through the chain of SMB commands have we gone? */
+       unsigned chain_count;
+
+       /* the async structure allows backend functions to delay
+          replying to requests. To use this, the front end must set
+          async.send_fn to a function to be called by the backend
+          when the reply is finally ready to be sent. The backend
+          must set async.status to the status it wants in the
+          reply. The backend must set the REQ_CONTROL_ASYNC
+          control_flag on the request to indicate that it wishes to
+          delay the reply
+
+          If async.send_fn is NULL then the backend cannot ask for a
+          delayed reply for this request
+
+          note that the async.private pointer is private to the front
+          end not the backend. The backend must not change it.
+       */
+       struct {
+               void (*send_fn)(struct request_context *);
+               void *private;
+               NTSTATUS status;
+       } async;
+
+       struct {
+               /* the raw SMB buffer, including the 4 byte length header */
+               char *buffer;
+               
+               /* the size of the raw buffer, including 4 byte header */
+               unsigned size;
+
+               /* how much has been allocated - on reply the buffer is over-allocated to 
+                  prevent too many realloc() calls 
+               */
+               unsigned allocated;
+
+               /* the start of the SMB header - this is always buffer+4 */
+               char *hdr;
+
+               /* the command words and command word count. vwv points
+                  into the raw buffer */
+               char *vwv;
+               unsigned wct;
+
+               /* the data buffer and size. data points into the raw buffer */
+               char *data;
+               unsigned data_size;
+
+               /* ptr is used as a moving pointer into the data area
+                * of the packet. The reason its here and not a local
+                * variable in each function is that when a realloc of
+                * a reply packet is done we need to move this
+                * pointer */
+               char *ptr;
+       } in, out;
+};
+
+
+
+/* the context associated with open files on an smb socket */
+struct files_context {
+       struct files_struct *files; /* open files */
+       struct bitmap *file_bmap; /* bitmap used to allocate file handles */
+
+       /* a fsp to use when chaining */
+       struct files_struct *chain_fsp;
+
+       /* a fsp to use to save when breaking an oplock. */
+       struct files_struct *oplock_save_chain_fsp;
+
+       /* how many files are open */
+       int files_used;
+
+       /* limit for maximum open files */
+       int real_max_open_files;
+};
+
+
+/* the context associated with open tree connects on a smb socket */
+struct tree_context {
+       struct tcon_context *connections;
+
+       /* number of open connections */
+       struct bitmap *bmap;
+       int num_open;
+};
+
+/* context associated with currently valid session setups */
+struct users_context {
+       /* users from session setup */
+       char *session_users; /* was a pstring */
+
+       /* this holds info on user ids that are already validated for this VC */
+       struct user_struct *validated_users;
+       int next_vuid; /* initialise to VUID_OFFSET */
+       int num_validated_vuids;
+};
+
+
+/* this contains variables that should be used in % substitutions for
+ * smb.conf parameters */
+struct substitute_context {
+       char *remote_arch;
+
+       /* our local netbios name, as give to us by the client */
+       char *local_machine;
+
+       /* the remote netbios name, as give to us by the client */
+       char *remote_machine;
+
+       /* the select remote protocol */
+       char *remote_proto;     
+
+       /* the name of the client as should be displayed in
+        * smbstatus. Can be an IP or a netbios name */
+       char *client_name; 
+
+       /* the username for %U */
+       char *user_name;
+};
+
+/* context that has been negotiated between the client and server */
+struct negotiate_context {
+       /* have we already done the NBT session establishment? */
+       BOOL done_nbt_session;
+
+       /* only one negprot per connection is allowed */
+       BOOL done_negprot;
+
+       /* multiple session setups are allowed, but some parameters are
+          ignored in any but the first */
+       BOOL done_sesssetup;
+       
+       /* 
+        * Size of data we can send to client. Set
+        *  by the client for all protocols above CORE.
+        *  Set by us for CORE protocol.
+        */
+       unsigned max_send; /* init to BUFFER_SIZE */
+
+       /*
+        * Size of the data we can receive. Set by us.
+        * Can be modified by the max xmit parameter.
+        */
+       unsigned max_recv; /* init to BUFFER_SIZE */
+
+       /* a guess at the remote architecture. Try not to rely on this - in almost
+          all cases using these values is the wrong thing to do */
+       enum remote_arch_types ra_type;
+
+       /* the negotiatiated protocol */
+       enum protocol_types protocol;
+
+       /* authentication context for multi-part negprot */
+       struct auth_context *auth_context;
+
+       /* state of NTLMSSP auth */
+       struct auth_ntlmssp_state *ntlmssp_state;
+
+       /* did we tell the client we support encrypted passwords? */
+       BOOL encrypted_passwords;
+
+       /* did we send an extended security negprot reply? */
+       BOOL spnego_negotiated;
+
+       /* client capabilities */
+       uint32 client_caps;
+};
+       
+/* this is the context for a SMB socket associated with the socket itself */
+struct socket_context {
+       /* the open file descriptor */
+       int fd; 
+
+       /* the last read error on the socket, if any (replaces smb_read_error global) */
+       int read_error;
+
+       /* a count of the number of packets we have received. We
+        * actually only care about zero/non-zero at this stage */
+       unsigned pkt_count;
+
+       /* the network address of the client */
+       char *client_addr;
+};
+
+
+/* this holds long term state specific to the printing subsystem */
+struct printing_context {
+       struct notify_queue *notify_queue_head;
+};
+
+
+/* the server_context holds a linked list of pending requests,
+ * this is used for blocking locks and requests blocked due to oplock
+ * break requests */
+struct pending_request {
+       struct pending_request *next, *prev;
+
+       /* the request itself - needs to be freed */
+       struct request_context *request;
+};
+
+/* the timers context contains info on when we last did various
+ * functions */
+struct timers_context {
+       /* when did we last do timeout processing? */
+       time_t last_timeout_processing;
+
+       /* when did we last sent a keepalive */
+       time_t last_keepalive_sent;
+       
+       /* when we last checked the smb.conf for auto-reload */
+       time_t last_smb_conf_reload;
+};
+
+
+/* the process model operations structure - contains function pointers to 
+   the model-specific implementations of each operation */
+struct model_ops {
+       /* called at startup when the model is selected */
+       void (*model_startup)(void);
+
+       /* function to accept new connection */
+       void (*accept_connection)(struct event_context *, struct fd_event *, time_t, uint16);
+                               
+       /* function to terminate a connection */
+       void (*terminate_connection)(struct server_context *smb, const char *reason);
+       
+       /* function to exit server */
+       void (*exit_server)(struct server_context *smb, const char *reason);
+       
+       /* returns process or thread id */
+       int (*get_id)(struct request_context *req);
+};
+
+
+/* smb context structure. This should contain all the state
+ * information associated with a SMB server */
+struct server_context {
+       /* a talloc context for all data in this structure */
+       TALLOC_CTX *mem_ctx;
+
+       struct negotiate_context negotiate;
+
+       struct substitute_context substitute;
+
+       struct socket_context socket;
+
+       struct files_context file;
+
+       struct tree_context tree;
+
+       struct users_context users;
+
+       struct printing_context print;
+
+       struct timers_context timers;
+
+       /* the pid of the process handling this session */
+       pid_t pid;
+       
+       /* pointer make to smbd daemon context */
+       struct smbd_context *smbd;
+       
+       /* pointer to list of events that we are waiting on */
+       struct event_context *events;
+
+       /* process model specific operations */
+       struct model_ops *model_ops;
+};
+
diff --git a/source4/include/debug.h b/source4/include/debug.h
new file mode 100644 (file)
index 0000000..814a79a
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba debug defines
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+
+/* the debug operations structure - contains function pointers to 
+   various debug implementations of each operation */
+struct debug_ops {
+       /* function to log (using DEBUG) suspicious usage of data structure */
+       void (*log_suspicious_usage)(const char* from, const char* info);
+                               
+       /* function to log (using printf) suspicious usage of data structure.
+        * To be used in circumstances when using DEBUG would cause loop. */
+       void (*print_suspicious_usage)(const char* from, const char* info);
+       
+       /* function to return process/thread id */
+       uint32 (*get_task_id)(void);
+};
+
+void do_debug(const char *, ...) PRINTF_ATTRIBUTE(1,2);
+
+extern int DEBUGLEVEL;
+
+#define DEBUGLVL(level) ((level) <= DEBUGLEVEL)
+#define DEBUG(level, body) do { if (DEBUGLVL(level)) do_debug body; } while (0)
+#define DEBUGADD(level, body) DEBUG(level, body)
+#define DEBUGC(class, level, body) DEBUG(level, body)
+#define DEBUGADDC(class, level, body) DEBUG(level, body)
+#define DEBUGTAB(n) do_debug_tab(n)
+
+enum debug_logtype {DEBUG_FILE, DEBUG_STDOUT, DEBUG_STDERR};
+
+/* keep some debug class defines for now to avoid changing old code too much */
+#define DBGC_AUTH 0
diff --git a/source4/include/dlinklist.h b/source4/include/dlinklist.h
new file mode 100644 (file)
index 0000000..f1ceb8a
--- /dev/null
@@ -0,0 +1,78 @@
+/* 
+   Unix SMB/CIFS implementation.
+   some simple double linked list macros
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+/* To use these macros you must have a structure containing a next and
+   prev pointer */
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+               (list) = (p); \
+               (p)->next = (p)->prev = NULL; \
+       } else { \
+               (list)->prev = (p); \
+               (p)->next = (list); \
+               (p)->prev = NULL; \
+               (list) = (p); \
+       }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define DLIST_REMOVE(list, p) \
+do { \
+       if ((p) == (list)) { \
+               (list) = (p)->next; \
+               if (list) (list)->prev = NULL; \
+       } else { \
+               if ((p)->prev) (p)->prev->next = (p)->next; \
+               if ((p)->next) (p)->next->prev = (p)->prev; \
+       } \
+       if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, tmp) \
+do { \
+               if (!(list)) { \
+                       (list) = (p); \
+                       (p)->next = (p)->prev = NULL; \
+               } else { \
+                       for ((tmp) = (list); (tmp)->next; (tmp) = (tmp)->next) ; \
+                       (tmp)->next = (p); \
+                       (p)->next = NULL; \
+                       (p)->prev = (tmp); \
+               } \
+} while (0)
+
+/* demote an element to the end of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+do { \
+               DLIST_REMOVE(list, p); \
+               DLIST_ADD_END(list, p, tmp); \
+} while (0)
diff --git a/source4/include/doserr.h b/source4/include/doserr.h
new file mode 100644 (file)
index 0000000..576aeda
--- /dev/null
@@ -0,0 +1,233 @@
+/* 
+   Unix SMB/CIFS implementation.
+   DOS error code constants
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) John H Terpstra              1996-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Paul Ashton                  1998-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.
+*/
+
+#ifndef _DOSERR_H
+#define _DOSERR_H
+
+/* Error classes */
+
+#define ERRDOS 0x01 /*  Error is from the core DOS operating system set. */
+#define ERRSRV 0x02  /* Error is generated by the server network file manager.*/
+#define ERRHRD 0x03  /* Error is an hardware error. */
+#define ERRCMD 0xFF  /* Command was not in the "SMB" format. */
+
+/* SMB X/Open error codes for the ERRDOS error class */
+#define ERRsuccess 0 /* No error */
+#define ERRbadfunc 1 /* Invalid function (or system call) */
+#define ERRbadfile 2 /* File not found (pathname error) */
+#define ERRbadpath 3 /* Directory not found */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid fid */
+#define ERRbadmcb 7 /* Memory control blocks destroyed. */
+#define ERRnomem 8 /* Out of memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data (only from ioctl call) */
+#define ERRres 14 /* reserved */
+#define ERRbaddrive 15 /* Invalid drive */
+#define ERRremcd 16 /* Attempt to delete current directory */
+#define ERRdiffdevice 17 /* rename/move across different filesystems */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRgeneral 31 /* General failure */
+#define ERRbadshare 32 /* Share mode on file conflict with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRunsup 50 /* Request unsupported, returned by Win 95, RJS 20Jun98 */
+#define ERRnetnamedel 64 /* Network name deleted or not available */
+#define ERRnosuchshare 67 /* You specified an invalid share name */
+#define ERRfilexists 80 /* File in operation already exists */
+#define ERRinvalidparam 87
+#define ERRcannotopen 110 /* Cannot open the file specified */
+#define ERRinsufficientbuffer 122
+#define ERRinvalidname 123 /* Invalid name */
+#define ERRunknownlevel 124
+#define ERRnotlocked 158 /* This region is not locked by this locking context. */
+#define ERRrename 183
+#define ERRbadpipe 230 /* Named pipe invalid */
+#define ERRpipebusy 231 /* All instances of pipe are busy */
+#define ERRpipeclosing 232 /* named pipe close in progress */
+#define ERRnotconnected 233 /* No process on other end of named pipe */
+#define ERRmoredata 234 /* More data to be returned */
+#define ERRnomoreitems 259
+#define ERRbaddirectory 267 /* Invalid directory name in a path. */
+#define ERReasnotsupported 282 /* Extended attributes */
+#define ERRlogonfailure 1326 /* Unknown username or bad password */
+#define ERRbuftoosmall 2123
+#define ERRunknownipc 2142
+#define ERRnosuchprintjob 2151
+#define ERRinvgroup 2455
+
+/* here's a special one from observing NT */
+#define ERRnoipc 66 /* don't support ipc */
+
+/* These errors seem to be only returned by the NT printer driver system */
+#define ERRdriveralreadyinstalled 1795 /* ERROR_PRINTER_DRIVER_ALREADY_INSTALLED */
+#define ERRunknownprinterport 1796 /* ERROR_UNKNOWN_PORT */
+#define ERRunknownprinterdriver 1797 /* ERROR_UNKNOWN_PRINTER_DRIVER */
+#define ERRunknownprintprocessor 1798 /* ERROR_UNKNOWN_PRINTPROCESSOR */
+#define ERRinvalidseparatorfile 1799 /* ERROR_INVALID_SEPARATOR_FILE */
+#define ERRinvalidjobpriority 1800 /* ERROR_INVALID_PRIORITY */
+#define ERRinvalidprintername 1801 /* ERROR_INVALID_PRINTER_NAME */
+#define ERRprinteralreadyexists 1802 /* ERROR_PRINTER_ALREADY_EXISTS */
+#define ERRinvalidprintercommand 1803 /* ERROR_INVALID_PRINTER_COMMAND */
+#define ERRinvaliddatatype 1804 /* ERROR_INVALID_DATATYPE */
+#define ERRinvalidenvironment 1805 /* ERROR_INVALID_ENVIRONMENT */
+
+#define ERRunknownprintmonitor 3000 /* ERROR_UNKNOWN_PRINT_MONITOR */
+#define ERRprinterdriverinuse 3001 /* ERROR_PRINTER_DRIVER_IN_USE */
+#define ERRspoolfilenotfound 3002 /* ERROR_SPOOL_FILE_NOT_FOUND */
+#define ERRnostartdoc 3003 /* ERROR_SPL_NO_STARTDOC */
+#define ERRnoaddjob 3004 /* ERROR_SPL_NO_ADDJOB */
+#define ERRprintprocessoralreadyinstalled 3005 /* ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED */
+#define ERRprintmonitoralreadyinstalled 3006 /* ERROR_PRINT_MONITOR_ALREADY_INSTALLED */
+#define ERRinvalidprintmonitor 3007 /* ERROR_INVALID_PRINT_MONITOR */
+#define ERRprintmonitorinuse 3008 /* ERROR_PRINT_MONITOR_IN_USE */
+#define ERRprinterhasjobsqueued 3009 /* ERROR_PRINTER_HAS_JOBS_QUEUED */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1 /* Non specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* No permissions to do the requested operation */
+#define ERRinvnid 5 /* tid invalid */
+#define ERRinvnetname 6 /* Invalid servername */
+#define ERRinvdevice 7 /* Invalid device */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Queued item too big */
+#define ERRinvpfid 52 /* Invalid print file in smb_fid */
+#define ERRsmbcmd 64 /* Unrecognised command */
+#define ERRsrverror 65 /* smb server internal error */
+#define ERRfilespecs 67 /* fid and pathname invalid combination */
+#define ERRbadlink 68 /* reserved */
+#define ERRbadpermits 69 /* Access specified for a file is not valid */
+#define ERRbadpid 70 /* reserved */
+#define ERRsetattrmode 71 /* attribute mode invalid */
+#define ERRpaused 81 /* Message server paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room for message */
+#define ERRrmuns 87 /* too many remote usernames */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource  89 /* No resources currently available for request. */
+#define ERRtoomanyuids 90 /* too many userids */
+#define ERRbaduid 91 /* bad userid */
+#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251 /* temporarily unable to use raw mode, use standard mode */
+#define ERRcontMPX 252 /* resume MPX mode */
+#define ERRbadPW /* reserved */
+#define ERRnosupport 0xFFFF
+#define ERRunknownsmb 22 /* from NT 3.5 response */
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19 /* read only media */
+#define ERRbadunit 20 /* Unknown device */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data (CRC) error */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29 /* write fault */
+#define ERRread 30 /* read fault */
+#define ERRgeneral 31 /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36 /* share buffer exceeded */
+#define ERRdiskfull 39
+
+
+/* these are win32 error codes. There are only a few places where
+   these matter for Samba, primarily in the NT printing code */
+#define WERR_OK W_ERROR(0)
+#define WERR_BADFUNC W_ERROR(1)
+#define WERR_BADFILE W_ERROR(2)
+#define WERR_ACCESS_DENIED W_ERROR(5)
+#define WERR_BADFID W_ERROR(6)
+#define WERR_NOMEM W_ERROR(8)
+#define WERR_GENERAL_FAILURE W_ERROR(31)
+#define WERR_NOT_SUPPORTED W_ERROR(50)
+#define WERR_PRINTQ_FULL W_ERROR(61)
+#define WERR_NO_SPOOL_SPACE W_ERROR(62)
+#define WERR_NO_SUCH_SHARE W_ERROR(67)
+#define WERR_ALREADY_EXISTS W_ERROR(80)
+#define WERR_BAD_PASSWORD W_ERROR(86)
+#define WERR_INVALID_PARAM W_ERROR(87)
+#define WERR_INSUFFICIENT_BUFFER W_ERROR(122)
+#define WERR_INVALID_NAME W_ERROR(123)
+#define WERR_UNKNOWN_LEVEL W_ERROR(124)
+#define WERR_OBJECT_PATH_INVALID W_ERROR(161)
+#define WERR_NO_MORE_ITEMS W_ERROR(259)
+#define WERR_MORE_DATA W_ERROR(234)
+#define WERR_INVALID_OWNER W_ERROR(1307)
+#define WERR_CAN_NOT_COMPLETE W_ERROR(1003)
+#define WERR_INVALID_SECURITY_DESCRIPTOR W_ERROR(1338)
+#define WERR_SERVER_UNAVAILABLE W_ERROR(1722)
+#define WERR_INVALID_FORM_NAME W_ERROR(1902)
+#define WERR_INVALID_FORM_SIZE W_ERROR(1903)
+#define WERR_BUF_TOO_SMALL W_ERROR(2123)
+#define WERR_JOB_NOT_FOUND W_ERROR(2151)
+#define WERR_DEST_NOT_FOUND W_ERROR(2152)
+#define WERR_NOT_LOCAL_DOMAIN W_ERROR(2320)
+#define WERR_STATUS_MORE_ENTRIES   W_ERROR(0x0105)
+
+#define WERR_PRINTER_DRIVER_ALREADY_INSTALLED W_ERROR(ERRdriveralreadyinstalled)
+#define WERR_UNKNOWN_PORT W_ERROR(ERRunknownprinterport)
+#define WERR_UNKNOWN_PRINTER_DRIVER W_ERROR(ERRunknownprinterdriver)
+#define WERR_UNKNOWN_PRINTPROCESSOR W_ERROR(ERRunknownprintprocessor)
+#define WERR_INVALID_SEPARATOR_FILE W_ERROR(ERRinvalidseparatorfile)
+#define WERR_INVALID_PRIORITY W_ERROR(ERRinvalidjobpriority)
+#define WERR_INVALID_PRINTER_NAME W_ERROR(ERRinvalidprintername)
+#define WERR_PRINTER_ALREADY_EXISTS W_ERROR(ERRprinteralreadyexists)
+#define WERR_INVALID_PRINTER_COMMAND W_ERROR(ERRinvalidprintercommand)
+#define WERR_INVALID_DATATYPE W_ERROR(ERRinvaliddatatype)
+#define WERR_INVALID_ENVIRONMENT W_ERROR(ERRinvalidenvironment)
+
+#define WERR_UNKNOWN_PRINT_MONITOR W_ERROR(ERRunknownprintmonitor)
+#define WERR_PRINTER_DRIVER_IN_USE W_ERROR(ERRprinterdriverinuse)
+#define WERR_SPOOL_FILE_NOT_FOUND W_ERROR(ERRspoolfilenotfound)
+#define WERR_SPL_NO_STARTDOC W_ERROR(ERRnostartdoc)
+#define WERR_SPL_NO_ADDJOB W_ERROR(ERRnoaddjob)
+#define WERR_PRINT_PROCESSOR_ALREADY_INSTALLED W_ERROR(ERRprintprocessoralreadyinstalled)
+#define WERR_PRINT_MONITOR_ALREADY_INSTALLED W_ERROR(ERRprintmonitoralreadyinstalled)
+#define WERR_INVALID_PRINT_MONITOR W_ERROR(ERRinvalidprintmonitor)
+#define WERR_PRINT_MONITOR_IN_USE W_ERROR(ERRprintmonitorinuse)
+#define WERR_PRINTER_HAS_JOBS_QUEUED W_ERROR(ERRprinterhasjobsqueued)
+
+
+/* DFS errors */
+
+#ifndef NERR_BASE
+#define NERR_BASE (2100)
+#endif
+
+#define WERR_DFS_NO_SUCH_VOL            W_ERROR(NERR_BASE+562)
+#define WERR_DFS_NO_SUCH_SHARE          W_ERROR(NERR_BASE+565)
+#define WERR_DFS_NO_SUCH_SERVER         W_ERROR(NERR_BASE+573)
+#define WERR_DFS_INTERNAL_ERROR         W_ERROR(NERR_BASE+590)
+#define WERR_DFS_CANT_CREATE_JUNCT      W_ERROR(NERR_BASE+569)
+
+#endif /* _DOSERR_H */
diff --git a/source4/include/dynconfig.h b/source4/include/dynconfig.h
new file mode 100644 (file)
index 0000000..93a182e
--- /dev/null
@@ -0,0 +1,39 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+   Copyright (C) 2003 by Anthony Liguori <aliguor@us.ibm.com>
+   
+   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.
+*/
+
+/**
+ * @file dynconfig.h
+ *
+ * @brief Exported global configurations.
+ **/
+
+extern char const *dyn_SBINDIR,
+       *dyn_BINDIR,
+       *dyn_SWATDIR;
+
+extern pstring dyn_CONFIGFILE;
+extern const char *dyn_LOGFILEBASE;
+extern pstring dyn_LMHOSTSFILE;
+extern pstring dyn_LIBDIR;
+extern const fstring dyn_SHLIBEXT;
+extern const pstring dyn_LOCKDIR; 
+extern const pstring dyn_PIDDIR;
+extern const pstring dyn_SMB_PASSWD_FILE;
+extern const pstring dyn_PRIVATE_DIR;
diff --git a/source4/include/enums.h b/source4/include/enums.h
new file mode 100644 (file)
index 0000000..5be1588
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Andrew Tridgell              2003
+   
+   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.
+*/
+
+/*
+  this header declares basic enumerated types
+*/
+
+/* protocol types. It assumes that higher protocols include lower protocols
+   as subsets */
+enum protocol_types {PROTOCOL_NONE,PROTOCOL_CORE,PROTOCOL_COREPLUS,PROTOCOL_LANMAN1,PROTOCOL_LANMAN2,PROTOCOL_NT1};
+
+/* security levels */
+enum security_types {SEC_SHARE,SEC_USER,SEC_SERVER,SEC_DOMAIN,SEC_ADS};
+
+/* server roles */
+enum server_types
+{
+       ROLE_STANDALONE,
+       ROLE_DOMAIN_MEMBER,
+       ROLE_DOMAIN_BDC,
+       ROLE_DOMAIN_PDC
+};
+
+/* printing types */
+enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX,
+                    PRINT_QNX,PRINT_PLP,PRINT_LPRNG,PRINT_SOFTQ,
+                    PRINT_CUPS,PRINT_LPRNT,PRINT_LPROS2
+#ifdef DEVELOPER
+,PRINT_TEST,PRINT_VLP
+#endif /* DEVELOPER */
+};
+
+/* LDAP schema types */
+enum schema_types {SCHEMA_COMPAT, SCHEMA_AD, SCHEMA_SAMBA};
+
+/* LDAP SSL options */
+enum ldap_ssl_types {LDAP_SSL_ON, LDAP_SSL_OFF, LDAP_SSL_START_TLS};
+
+/* LDAP PASSWD SYNC methods */
+enum ldap_passwd_sync_types {LDAP_PASSWD_SYNC_ON, LDAP_PASSWD_SYNC_OFF, LDAP_PASSWD_SYNC_ONLY};
+
+/* Remote architectures we know about. */
+enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT, RA_WIN2K, RA_WINXP, RA_SAMBA};
+
+/* case handling */
+enum case_handling {CASE_LOWER,CASE_UPPER};
+
diff --git a/source4/include/events.h b/source4/include/events.h
new file mode 100644 (file)
index 0000000..7d04a38
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+   Unix SMB/CIFS implementation.
+   main select loop and event handling
+   Copyright (C) Andrew Tridgell 2003
+   
+   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.
+*/
+
+/*
+  please read the comments in events.c before modifying
+*/
+
+struct event_context { 
+       /* list of filedescriptor events */
+       struct fd_event {
+               struct fd_event *next, *prev;
+               int fd;
+               uint16 flags; /* see EVENT_FD_* flags */
+               void (*handler)(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags);
+               void *private;
+               int ref_count;
+       } *fd_events;
+
+       /* list of timed events */
+       struct timed_event {
+               struct timed_event *next, *prev;
+               time_t next_event;
+               void (*handler)(struct event_context *ev, struct timed_event *te, time_t t);
+               void *private;
+               int ref_count;
+       } *timed_events;
+
+       /* list of loop events - called on each select() */
+       struct loop_event {
+               struct loop_event *next, *prev;
+               void (*handler)(struct event_context *ev, struct loop_event *le, time_t t);
+               void *private;
+               int ref_count;
+       } *loop_events;
+
+       /* list of signal events */
+       struct signal_event {
+               struct signal_event *next, *prev;
+               int signum;
+               void (*handler)(struct event_context *ev, struct signal_event *se, int signum, void *sigarg);
+               void *private;
+               int ref_count;
+       } *signal_events;
+
+       /* the maximum file descriptor number in fd_events */
+       int maxfd;
+
+       /* information for exiting from the event loop */
+       struct {
+               BOOL exit_now;
+               int code;
+       } exit;
+};
+
+
+/* bits for fd_event.flags */
+#define EVENT_FD_READ 1
+#define EVENT_FD_WRITE 2
diff --git a/source4/include/genparser.h b/source4/include/genparser.h
new file mode 100644 (file)
index 0000000..f28cd78
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+   Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+   
+   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.
+*/
+
+#ifndef _GENPARSER_H
+#define _GENPARSER_H
+
+/* these macros are needed for genstruct auto-parsers */
+#ifndef GENSTRUCT
+#define GENSTRUCT
+#define _LEN(x)
+#define _NULLTERM
+#endif
+
+/*
+  automatic marshalling/unmarshalling system for C structures
+*/
+
+/* flag to mark a fixed size array as actually being null terminated */
+#define FLAG_NULLTERM 1
+#define FLAG_ALWAYS 2
+
+struct enum_struct {
+       const char *name;
+       unsigned value;
+};
+
+/* intermediate dumps are stored in one of these */
+struct parse_string {
+       unsigned allocated;
+       unsigned length;
+       char *s;
+};
+
+typedef int (*gen_dump_fn)(TALLOC_CTX *, struct parse_string *, const char *ptr, unsigned indent);
+typedef int (*gen_parse_fn)(TALLOC_CTX *, char *ptr, const char *str);
+
+/* genstruct.pl generates arrays of these */
+struct parse_struct {
+       const char *name;
+       unsigned ptr_count;
+       unsigned size;
+       unsigned offset;
+       unsigned array_len;
+       const char *dynamic_len;
+       unsigned flags;
+       gen_dump_fn dump_fn;
+       gen_parse_fn parse_fn;
+};
+
+#define DUMP_PARSE_DECL(type) \
+  int gen_dump_ ## type(TALLOC_CTX *, struct parse_string *, const char *, unsigned); \
+  int gen_parse_ ## type(TALLOC_CTX *, char *, const char *);
+
+DUMP_PARSE_DECL(char)
+DUMP_PARSE_DECL(int)
+DUMP_PARSE_DECL(unsigned)
+DUMP_PARSE_DECL(double)
+DUMP_PARSE_DECL(float)
+
+#define gen_dump_unsigned_char gen_dump_char
+#define gen_parse_unsigned_char gen_parse_char
+
+#endif /* _GENPARSER_H */
diff --git a/source4/include/genparser_samba.h b/source4/include/genparser_samba.h
new file mode 100644 (file)
index 0000000..172ff23
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+   Copyright (C) Simo Sorce <idra@samba.org> 2002
+   
+   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.
+*/
+
+#ifndef _GENPARSER_SAMBA_H
+#define _GENPARSER_SAMBA_H
+
+const struct parse_struct pinfo_security_ace_info[] = {
+{"type", 0, sizeof(uint8), offsetof(struct security_ace_info, type), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"flags", 0, sizeof(uint8), offsetof(struct security_ace_info, flags), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"size", 0, sizeof(uint16), offsetof(struct security_ace_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"info", 0, sizeof(char), offsetof(struct security_ace_info, info), 0, NULL, 0, gen_dump_SEC_ACCESS, gen_parse_SEC_ACCESS},
+{"obj_flags", 0, sizeof(uint32), offsetof(struct security_ace_info, obj_flags), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"obj_guid", 0, sizeof(char), offsetof(struct security_ace_info, obj_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"inh_guid", 0, sizeof(char), offsetof(struct security_ace_info, inh_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"trustee", 0, sizeof(char), offsetof(struct security_ace_info, trustee), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_acl_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_acl_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"size", 0, sizeof(uint16), offsetof(struct security_acl_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"num_aces", 0, sizeof(uint32), offsetof(struct security_acl_info, num_aces), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"ace", 1, sizeof(struct security_ace_info), offsetof(struct security_acl_info, ace), 0, "size", 0, gen_dump_SEC_ACE, gen_parse_SEC_ACE},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_descriptor_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_descriptor_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"type", 0, sizeof(uint16), offsetof(struct security_descriptor_info, type), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"off_owner_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_owner_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_grp_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_grp_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_sacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_sacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_dacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_dacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"dacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, dacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"sacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, sacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"owner_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, owner_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{"grp_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, grp_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_luid_attr_info[] = {
+{"attr", 0, sizeof(uint32), offsetof(struct LUID_ATTR, attr), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"luid", 1, sizeof(LUID), offsetof(struct LUID_ATTR, luid), 0, NULL, 0, gen_dump_LUID, gen_parse_LUID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+#endif /* _GENPARSER_SAMBA_H */
diff --git a/source4/include/gums.h b/source4/include/gums.h
new file mode 100644 (file)
index 0000000..ca124d7
--- /dev/null
@@ -0,0 +1,230 @@
+/* 
+   Unix SMB/CIFS implementation.
+   GUMS structures
+   Copyright (C) Simo Sorce 2002
+   
+   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.
+*/
+
+#ifndef _GUMS_H
+#define _GUMS_H
+
+#define GUMS_VERSION_MAJOR 0
+#define GUMS_VERSION_MINOR 1
+#define GUMS_OBJECT_VERSION    1
+
+#define GUMS_OBJ_DOMAIN                        1
+#define GUMS_OBJ_NORMAL_USER           2
+#define GUMS_OBJ_GROUP                 3
+#define GUMS_OBJ_ALIAS                 4
+#define GUMS_OBJ_WORKSTATION_TRUST     5
+#define GUMS_OBJ_SERVER_TRUST          6
+#define GUMS_OBJ_DOMAIN_TRUST          7
+
+typedef struct gums_user
+{
+       DOM_SID *group_sid;             /* Primary Group SID */
+
+       NTTIME logon_time;              /* logon time */
+       NTTIME logoff_time;             /* logoff time */
+       NTTIME kickoff_time;            /* kickoff time */
+       NTTIME pass_last_set_time;      /* password last set time */
+       NTTIME pass_can_change_time;    /* password can change time */
+       NTTIME pass_must_change_time;   /* password must change time */
+
+       char *full_name;                /* user's full name string */
+       char *home_dir;                 /* home directory string */
+       char *dir_drive;                /* home directory drive string */
+       char *logon_script;             /* logon script string */
+       char *profile_path;             /* profile path string */
+       char *workstations;             /* login from workstations string */
+       char *unknown_str;              /* don't know what this is, yet. */
+       char *munged_dial;              /* munged path name and dial-back tel number */
+               
+       DATA_BLOB lm_pw;                /* .data is Null if no password */
+       DATA_BLOB nt_pw;                /* .data is Null if no password */
+               
+       uint32 unknown_3;               /* 0x00ff ffff */
+               
+       uint16 logon_divs;              /* 168 - number of hours in a week */
+       uint32 hours_len;               /* normally 21 bytes */
+       uint8 *hours;
+               
+       uint32 unknown_5;               /* 0x0002 0000 */
+       uint32 unknown_6;               /* 0x0000 04ec */
+
+} GUMS_USER;
+
+typedef struct gums_group
+{
+       uint32 count;                   /* Number of SIDs */
+       DOM_SID **members;              /* SID array */
+
+} GUMS_GROUP;
+
+union gums_obj_p {
+       gums_user *user;
+       gums_group *group;
+}
+
+typedef struct gums_object
+{
+       TALLOC_CTX *mem_ctx;
+
+       uint32 type;                    /* Object Type */
+       uint32 version;                 /* Object Version */
+       uint32 seq_num;                 /* Object Sequence Number */
+
+       SEC_DESC *sec_desc;             /* Security Descriptor */
+
+       DOM_SID *sid;                   /* Object Sid */
+       char *name;                     /* Object Name */
+       char *description;              /* Object Description */
+
+       union gums_obj_p data;          /* Object Specific data */
+
+} GUMS_OBJECT;
+
+typedef struct gums_data_set
+{
+       int type; /* GUMS_SET_xxx */
+       void *data;
+
+} GUMS_DATA_SET;
+
+typedef struct gums_commit_set
+{
+       TALLOC_CTX *mem_ctx;
+
+       uint32 type;                    /* Object type */
+       DOM_SID sid;                    /* Object Sid */
+       uint32 count;                   /* number of changes */
+       GUMS_DATA_SET **data;
+} GUMS_COMMIT_SET;
+
+typedef struct gums_privilege
+{
+       TALLOC_CTX *mem_ctx;
+
+       uint32 type;                    /* Object Type */
+       uint32 version;                 /* Object Version */
+       uint32 seq_num;                 /* Object Sequence Number */
+
+       LUID_ATTR *privilege;           /* Privilege Type */
+       char *name;                     /* Object Name */
+       char *description;              /* Object Description */
+
+       uint32 count;
+       DOM_SID **members;
+
+} GUMS_PRIVILEGE;
+
+
+typedef struct gums_functions
+{
+       /* Generic object functions */
+
+       NTSTATUS (*get_domain_sid) (DOM_SID **sid, const char* name);
+       NTSTATUS (*set_domain_sid) (const DOM_SID *sid);
+
+       NTSTATUS (*get_sequence_number) (void);
+
+       NTSTATUS (*new_object) (DOM_SID **sid, const char *name, const int obj_type);
+       NTSTATUS (*delete_object) (const DOM_SID *sid);
+
+       NTSTATUS (*get_object_from_sid) (GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type);
+       NTSTATUS (*get_sid_from_name) (GUMS_OBJECT **object, const char *name);
+       /* This function is used to get the list of all objects changed since b_time, it is
+          used to support PDC<->BDC synchronization */
+       NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+       NTSTATUS (*enumerate_objects_start) (void *handle, const DOM_SID *sid, const int obj_type);
+       NTSTATUS (*enumerate_objects_get_next) (GUMS_OBJECT **object, void *handle);
+       NTSTATUS (*enumerate_objects_stop) (void *handle);
+
+       /* This function MUST be used ONLY by PDC<->BDC replication code or recovery tools.
+          Never use this function to update an object in the database, use set_object_values() */
+       NTSTATUS (*set_object) (const GUMS_OBJECT *object);
+
+       /* set object values function */
+       NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+       /* Group related functions */
+       NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
+       NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+       NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+       NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+       NTSTATUS (*lock_sid) (const DOM_SID *sid);
+       NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+       /* privileges related functions */
+
+       NTSTATUS (*add_members_to_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
+       NTSTATUS (*delete_members_from_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
+       NTSTATUS (*enumerate_privilege_members) (DOM_SID **members, const LUID_ATTR *priv);
+       NTSTATUS (*get_sid_privileges) (DOM_SID **privs, const DOM_SID *sid);
+       /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+       NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+
+} GUMS_FUNCTIONS;
+
+/* define value types */
+
+#define GUMS_SET_PRIMARY_GROUP         1
+#define GUMS_SET_SEC_DESC              2
+
+/* user specific type values */
+#define GUMS_SET_LOGON_TIME            10  /* keep NTTIME consecutive */
+#define GUMS_SET_LOGOFF_TIME           11 /* too ease checking */
+#define GUMS_SET_KICKOFF_TIME          13
+#define GUMS_SET_PASS_LAST_SET_TIME    14
+#define GUMS_SET_PASS_CAN_CHANGE_TIME  15
+#define GUMS_SET_PASS_MUST_CHANGE_TIME 16 /* NTTIME end */
+
+#define GUMS_SET_NAME                  20 /* keep strings consecutive */
+#define GUMS_SET_DESCRIPTION           21 /* too ease checking */
+#define GUMS_SET_FULL_NAME             22
+#define GUMS_SET_HOME_DIRECTORY                23
+#define GUMS_SET_DRIVE                 24
+#define GUMS_SET_LOGON_SCRIPT          25
+#define GUMS_SET_PROFILE_PATH          26
+#define GUMS_SET_WORKSTATIONS          27
+#define GUMS_SET_UNKNOWN_STRING                28
+#define GUMS_SET_MUNGED_DIAL           29 /* strings end */
+
+#define GUMS_SET_LM_PASSWORD           40
+#define GUMS_SET_NT_PASSWORD           41
+#define GUMS_SET_PLAINTEXT_PASSWORD    42
+#define GUMS_SET_UNKNOWN_3             43
+#define GUMS_SET_LOGON_DIVS            44
+#define GUMS_SET_HOURS_LEN             45
+#define GUMS_SET_HOURS                 46
+#define GUMS_SET_UNKNOWN_5             47
+#define GUMS_SET_UNKNOWN_6             48
+
+#define GUMS_SET_MUST_CHANGE_PASS      50
+#define GUMS_SET_CANNOT_CHANGE_PASS    51
+#define GUMS_SET_PASS_NEVER_EXPIRE     52
+#define GUMS_SET_ACCOUNT_DISABLED      53
+#define GUMS_SET_ACCOUNT_LOCKOUT       54
+
+/*group specific type values */
+#define GUMS_ADD_SID_LIST              60
+#define GUMS_DEL_SID_LIST              61
+#define GUMS_SET_SID_LIST              62
+
+#endif /* _GUMS_H */
diff --git a/source4/include/hmacmd5.h b/source4/include/hmacmd5.h
new file mode 100644 (file)
index 0000000..6b53a6f
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Interface header: Scheduler service
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+   Copyright (C) Andrew Tridgell 1992-1999
+   
+   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.
+*/
+
+#ifndef _HMAC_MD5_H
+
+typedef struct 
+{
+        struct MD5Context ctx;
+        uchar k_ipad[65];    
+        uchar k_opad[65];
+
+} HMACMD5Context;
+
+#endif /* _HMAC_MD5_H */
diff --git a/source4/include/includes.h b/source4/include/includes.h
new file mode 100644 (file)
index 0000000..f369367
--- /dev/null
@@ -0,0 +1,1237 @@
+#ifndef _INCLUDES_H
+#define _INCLUDES_H
+/* 
+   Unix SMB/CIFS implementation.
+   Machine customisation and include handling
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+   
+   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.
+*/
+
+#ifndef NO_CONFIG_H /* for some tests */
+#include "config.h"
+#endif
+
+#include "local.h"
+
+#ifdef AIX
+#define DEFAULT_PRINTING PRINT_AIX
+#define PRINTCAP_NAME "/etc/qconfig"
+#endif
+
+#ifdef HPUX
+#define DEFAULT_PRINTING PRINT_HPUX
+#endif
+
+#ifdef QNX
+#define DEFAULT_PRINTING PRINT_QNX
+#endif
+
+#ifdef SUNOS4
+/* on SUNOS4 termios.h conflicts with sys/ioctl.h */
+#undef HAVE_TERMIOS_H
+#endif
+
+#ifdef LINUX
+#ifndef DEFAULT_PRINTING
+#define DEFAULT_PRINTING PRINT_BSD
+#endif
+#ifndef PRINTCAP_NAME
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+#endif
+
+#ifdef __GNUC__
+/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument.  **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+
+#ifdef __GNUC__
+/** gcc attribute used on function parameters so that it does not emit
+ * warnings about them being unused. **/
+#  define UNUSED(param) param __attribute__ ((unused))
+#else
+#  define UNUSED(param) param
+/** Feel free to add definitions for other compilers here. */
+#endif
+
+#ifdef RELIANTUNIX
+/*
+ * <unistd.h> has to be included before any other to get
+ * large file support on Reliant UNIX. Yes, it's broken :-).
+ */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#endif /* RELIANTUNIX */
+
+#include <sys/types.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_SYS_PRIV_H
+#include <sys/priv.h>
+#endif
+#ifdef HAVE_SYS_ID_H
+#include <sys/id.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_MODE_H
+/* apparently AIX needs this for S_ISLNK */
+#ifndef S_ISLNK
+#include <sys/mode.h>
+#endif
+#endif
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#include <pwd.h>
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#else
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#endif
+
+#include <sys/file.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/*
+ * The next three defines are needed to access the IPTOS_* options
+ * on some systems.
+ */
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_IP_H
+#include <netinet/in_ip.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#if defined(HAVE_TERMIOS_H)
+/* POSIX terminal handling. */
+#include <termios.h>
+#elif defined(HAVE_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <termio.h>
+#elif defined(HAVE_SYS_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <sys/termio.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
+#ifdef HAVE_SYS_FS_S5PARAM_H 
+#include <sys/fs/s5param.h>
+#endif
+
+#if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY)
+#include <sys/filsys.h> 
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_DUSTAT_H              
+#include <sys/dustat.h>
+#endif
+
+#ifdef HAVE_SYS_STATVFS_H          
+#include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_GETPWANAM
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#define PASSWORD_LENGTH 16
+#endif  /* HAVE_SYS_SECURITY_H */
+
+#ifdef HAVE_COMPAT_H
+#include <compat.h>
+#endif
+
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+
+#if defined(BROKEN_REDHAT_7_SYSTEM_HEADERS) && !defined(_I386_STATFS_H)
+#define _I386_STATFS_H
+#define BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#include <sys/capability.h>
+
+#ifdef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#undef _I386_STATFS_H
+#undef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#endif
+
+#if defined(HAVE_RPC_RPC_H)
+/*
+ * Check for AUTH_ERROR define conflict with rpc/rpc.h in prot.h.
+ */
+#if defined(HAVE_SYS_SECURITY_H) && defined(HAVE_RPC_AUTH_ERROR_CONFLICT)
+#undef AUTH_ERROR
+#endif
+#include <rpc/rpc.h>
+#endif
+
+#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined(HAVE_SETNETGRENT) && defined(HAVE_ENDNETGRENT) && defined(HAVE_GETNETGRENT)
+#define HAVE_NETGROUP 1
+#endif
+
+#if defined (HAVE_NETGROUP)
+#if defined(HAVE_RPCSVC_YP_PROT_H)
+#include <rpcsvc/yp_prot.h>
+#endif
+#if defined(HAVE_RPCSVC_YPCLNT_H)
+#include <rpcsvc/ypclnt.h>
+#endif
+#endif /* HAVE_NETGROUP */
+
+#if defined(HAVE_SYS_IPC_H)
+#include <sys/ipc.h>
+#endif /* HAVE_SYS_IPC_H */
+
+#if defined(HAVE_SYS_SHM_H)
+#include <sys/shm.h>
+#endif /* HAVE_SYS_SHM_H */
+
+#ifdef HAVE_NATIVE_ICONV
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+#ifdef HAVE_GICONV
+#include <giconv.h>
+#endif
+#endif
+
+#if HAVE_KRB5_H
+#include <krb5.h>
+#else
+#undef HAVE_KRB5
+#endif
+
+#if HAVE_LBER_H
+#include <lber.h>
+#endif
+
+#if HAVE_LDAP_H
+#include <ldap.h>
+#else
+#undef HAVE_LDAP
+#endif
+
+#if HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+
+#if HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+/* we support ADS if we want it and have krb5 and ldap libs */
+#if defined(WITH_ADS) && defined(HAVE_KRB5) && defined(HAVE_LDAP)
+#define HAVE_ADS
+#endif
+
+/*
+ * Define VOLATILE if needed.
+ */
+
+#if defined(HAVE_VOLATILE)
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+/*
+ * Define additional missing types
+ */
+#if defined(HAVE_SIG_ATOMIC_T_TYPE) && defined(AIX)
+typedef sig_atomic_t SIG_ATOMIC_T;
+#elif defined(HAVE_SIG_ATOMIC_T_TYPE) && !defined(AIX)
+typedef sig_atomic_t VOLATILE SIG_ATOMIC_T;
+#else
+typedef int VOLATILE SIG_ATOMIC_T;
+#endif
+
+#ifndef HAVE_SOCKLEN_T_TYPE
+typedef int socklen_t;
+#endif
+
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifdef HAVE_UNSIGNED_CHAR
+#define schar signed char
+#else
+#define schar char
+#endif
+
+/*
+   Samba needs type definitions for int16, int32, uint16 and uint32.
+
+   Normally these are signed and unsigned 16 and 32 bit integers, but
+   they actually only need to be at least 16 and 32 bits
+   respectively. Thus if your word size is 8 bytes just defining them
+   as signed and unsigned int will work.
+*/
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#if !defined(int16) && !defined(HAVE_INT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define int16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define int16 short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned 
+ * case as int16 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint16) && !defined(HAVE_UINT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#if !defined(int32) && !defined(HAVE_INT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define int32 int
+#elif (SIZEOF_LONG == 4)
+#define int32 long
+#elif (SIZEOF_SHORT == 4)
+#define int32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define int32 int
+#endif
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned 
+ * case as int32 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint32) && !defined(HAVE_UINT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define uint32 unsigned
+#endif
+#endif
+
+/*
+ * Types for devices, inodes and offsets.
+ */
+
+#ifndef SMB_DEV_T
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_DEV64_T)
+#    define SMB_DEV_T dev64_t
+#  else
+#    define SMB_DEV_T dev_t
+#  endif
+#endif
+
+/*
+ * Setup the correctly sized inode type.
+ */
+
+#ifndef SMB_INO_T
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)
+#    define SMB_INO_T ino64_t
+#  else
+#    define SMB_INO_T ino_t
+#  endif
+#endif
+
+#ifndef LARGE_SMB_INO_T
+#  if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)) || (defined(SIZEOF_INO_T) && (SIZEOF_INO_T == 8))
+#    define LARGE_SMB_INO_T 1
+#  endif
+#endif
+
+#ifdef LARGE_SMB_INO_T
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#else 
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#endif
+
+#ifndef SMB_OFF_T
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)
+#    define SMB_OFF_T off64_t
+#  else
+#    define SMB_OFF_T off_t
+#  endif
+#endif
+
+/* this should really be a 64 bit type if possible */
+#define br_off SMB_BIG_UINT
+
+#define SMB_OFF_T_BITS (sizeof(SMB_OFF_T)*8)
+
+/*
+ * Set the define that tells us if we can do 64 bit
+ * NT SMB calls.
+ */
+
+#ifndef LARGE_SMB_OFF_T
+#  if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8))
+#    define LARGE_SMB_OFF_T 1
+#  endif
+#endif
+
+#ifdef LARGE_SMB_OFF_T
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,(v)&0xFFFFFFFF), SIVAL(p,ofs,(v)>>32))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF) )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+               (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#else 
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,v),SIVAL(p,ofs,0))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((uint32)(IVAL((buf),(off)))) & 0xFFFFFFFF )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+                               (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#endif
+
+/*
+ * Type for stat structure.
+ */
+
+#ifndef SMB_STRUCT_STAT
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STAT64) && defined(HAVE_OFF64_T)
+#    define SMB_STRUCT_STAT struct stat64
+#  else
+#    define SMB_STRUCT_STAT struct stat
+#  endif
+#endif
+
+/*
+ * Type for dirent structure.
+ */
+
+#ifndef SMB_STRUCT_DIRENT
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_DIRENT64)
+#    define SMB_STRUCT_DIRENT struct dirent64
+#  else
+#    define SMB_STRUCT_DIRENT struct dirent
+#  endif
+#endif
+
+/*
+ * Defines for 64 bit fcntl locks.
+ */
+
+#ifndef SMB_STRUCT_FLOCK
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+#    define SMB_STRUCT_FLOCK struct flock64
+#  else
+#    define SMB_STRUCT_FLOCK struct flock
+#  endif
+#endif
+
+#ifndef SMB_F_SETLKW
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+#    define SMB_F_SETLKW F_SETLKW64
+#  else
+#    define SMB_F_SETLKW F_SETLKW
+#  endif
+#endif
+
+#ifndef SMB_F_SETLK
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+#    define SMB_F_SETLK F_SETLK64
+#  else
+#    define SMB_F_SETLK F_SETLK
+#  endif
+#endif
+
+#ifndef SMB_F_GETLK
+#  if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+#    define SMB_F_GETLK F_GETLK64
+#  else
+#    define SMB_F_GETLK F_GETLK
+#  endif
+#endif
+
+#if defined(HAVE_LONGLONG)
+#define SMB_BIG_UINT unsigned long long
+#define SMB_BIG_INT long long
+#define SBVAL(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#define BVAL(p, ofs) (IVAL(p,ofs) | (((SMB_BIG_UINT)IVAL(p,(ofs)+4)) << 32))
+#else
+#define SMB_BIG_UINT unsigned long
+#define SMB_BIG_INT long
+#define SBVAL(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#define BVAL(p, ofs) IVAL(p,ofs)
+#endif
+
+#define SMB_BIG_UINT_BITS (sizeof(SMB_BIG_UINT)*8)
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+
+#ifndef HAVE_ERRNO_DECL
+extern int errno;
+#endif
+
+#ifdef HAVE_BROKEN_GETGROUPS
+#define GID_T int
+#else
+#define GID_T gid_t
+#endif
+
+#ifndef NGROUPS_MAX
+#define NGROUPS_MAX 32 /* Guess... */
+#endif
+
+/* Our own pstrings and fstrings */
+#include "pstring.h"
+
+/* Lists, trees, caching, database... */
+#include "xfile.h"
+#include "intl.h"
+#include "dlinklist.h"
+#include "../tdb/tdb.h"
+#include "../tdb/spinlock.h"
+#include "../tdb/tdbutil.h"
+#include "talloc.h"
+#include "nt_status.h"
+#include "ads.h"
+#include "interfaces.h"
+#include "trans2.h"
+#include "ioctl.h"
+#include "nterr.h"
+#include "messages.h"
+#include "charset.h"
+#include "dynconfig.h"
+#include "adt_tree.h"
+
+#include "util_getent.h"
+
+#include "version.h"
+#include "smb.h"
+#include "nameserv.h"
+#include "secrets.h"
+
+#include "byteorder.h"
+
+#include "ntdomain.h"
+
+#include "msdfs.h"
+
+#include "mapping.h"
+
+#include "rap.h"
+
+#include "md5.h"
+#include "hmacmd5.h"
+
+#include "ntlmssp.h"
+
+#include "auth.h"
+
+#include "passdb.h"
+
+#include "sam.h"
+
+#include "session.h"
+
+#include "asn_1.h"
+
+#include "popt.h"
+
+#include "mangle.h"
+
+#include "nsswitch/winbind_client.h"
+
+#include "genparser.h"
+
+#include "mutex.h"
+
+/*
+ * Type for wide character dirent structure.
+ * Only d_name is defined by POSIX.
+ */
+
+typedef struct smb_wdirent {
+       wpstring        d_name;
+} SMB_STRUCT_WDIRENT;
+
+/*
+ * Type for wide character passwd structure.
+ */
+
+typedef struct smb_wpasswd {
+       wfstring       pw_name;
+       char           *pw_passwd;
+       uid_t          pw_uid;
+       gid_t          pw_gid;
+       wpstring       pw_gecos;
+       wpstring       pw_dir;
+       wpstring       pw_shell;
+} SMB_STRUCT_WPASSWD;
+
+/* used in net.c */
+struct functable {
+       const char *funcname;
+       int (*fn)(int argc, const char **argv);
+};
+
+
+/* Defines for wisXXX functions. */
+#define UNI_UPPER    0x1
+#define UNI_LOWER    0x2
+#define UNI_DIGIT    0x4
+#define UNI_XDIGIT   0x8
+#define UNI_SPACE    0x10
+
+#include "nsswitch/nss.h"
+
+/* forward declaration from printing.h to get around 
+   header file dependencies */
+
+struct printjob;
+
+/***** automatically generated prototypes *****/
+#include "proto.h"
+
+/* String routines */
+
+#include "safe_string.h"
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#ifndef DEFAULT_PRINTING
+#ifdef HAVE_CUPS
+#define DEFAULT_PRINTING PRINT_CUPS
+#define PRINTCAP_NAME "cups"
+#elif defined(SYSV)
+#define DEFAULT_PRINTING PRINT_SYSV
+#define PRINTCAP_NAME "lpstat"
+#else
+#define DEFAULT_PRINTING PRINT_BSD
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+#endif
+
+#ifndef PRINTCAP_NAME
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#if (!defined(WITH_NISPLUS) && !defined(WITH_LDAP) && !defined(WITH_TDB_SAM))
+#define USE_SMBPASS_DB 1
+#endif
+
+#if defined(HAVE_PUTPRPWNAM) && defined(AUTH_CLEARTEXT_SEG_CHARS)
+#define OSF1_ENH_SEC 1
+#endif
+
+#ifndef ALLOW_CHANGE_PASSWORD
+#if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
+#define ALLOW_CHANGE_PASSWORD 1
+#endif
+#endif
+
+/* what is the longest significant password available on your system? 
+ Knowing this speeds up password searches a lot */
+#ifndef PASSWORD_LENGTH
+#define PASSWORD_LENGTH 8
+#endif
+
+#ifdef REPLACE_INET_NTOA
+#define inet_ntoa rep_inet_ntoa
+#endif
+
+#ifndef HAVE_PIPE
+#define SYNC_DNS 1
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 256
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef HAVE_CRYPT
+#define crypt ufc_crypt
+#endif
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#if defined(HAVE_CRYPT16) && defined(HAVE_GETAUTHUID)
+#define ULTRIX_AUTH 1
+#endif
+
+#ifndef HAVE_STRDUP
+char *strdup(const char *s);
+#endif
+
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest,const void *src,int size);
+#endif
+
+#ifndef HAVE_INITGROUPS
+int initgroups(char *name,gid_t id);
+#endif
+
+#ifndef HAVE_RENAME
+int rename(const char *zfrom, const char *zto);
+#endif
+
+#ifndef HAVE_MKTIME
+time_t mktime(struct tm *t);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_FTRUNCATE
+int ftruncate(int f,long l);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRTOUL
+unsigned long strtoul(const char *nptr, char **endptr, int base);
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite); 
+#endif
+
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESUID_DECL))
+/* stupid glibc */
+int setresuid(uid_t ruid, uid_t euid, uid_t suid);
+#endif
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESGID_DECL))
+int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+#endif
+#ifndef HAVE_VASPRINTF_DECL
+int vasprintf(char **ptr, const char *format, va_list ap);
+#endif
+
+#if !defined(HAVE_BZERO) && defined(HAVE_MEMSET)
+#define bzero(a,b) memset((a),'\0',(b))
+#endif
+
+#ifdef REPLACE_GETPASS
+#define getpass(prompt) getsmbpass((prompt))
+#endif
+
+/*
+ * Some older systems seem not to have MAXHOSTNAMELEN
+ * defined.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 254
+#endif
+
+/* yuck, I'd like a better way of doing this */
+#define DIRP_SIZE (256 + 32)
+
+/*
+ * glibc on linux doesn't seem to have MSG_WAITALL
+ * defined. I think the kernel has it though..
+ */
+
+#ifndef MSG_WAITALL
+#define MSG_WAITALL 0
+#endif
+
+/* default socket options. Dave Miller thinks we should default to TCP_NODELAY
+   given the socket IO pattern that Samba uses */
+#ifdef TCP_NODELAY
+#define DEFAULT_SOCKET_OPTIONS "TCP_NODELAY"
+#else
+#define DEFAULT_SOCKET_OPTIONS ""
+#endif
+
+/* Load header file for dynamic linking stuff */
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+/* dmalloc -- free heap debugger (dmalloc.org).  This should be near
+ * the *bottom* of include files so as not to conflict. */
+#ifdef ENABLE_DMALLOC
+#  include <dmalloc.h>
+#endif
+
+
+/* Some POSIX definitions for those without */
+#ifndef S_IFDIR
+#define S_IFDIR         0x4000
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode)   ((mode & 0xF000) == S_IFDIR)
+#endif
+#ifndef S_IRWXU
+#define S_IRWXU 00700           /* read, write, execute: owner */
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR 00400           /* read permission: owner */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200           /* write permission: owner */
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 00100           /* execute permission: owner */
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG 00070           /* read, write, execute: group */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040           /* read permission: group */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020           /* write permission: group */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 00010           /* execute permission: group */
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO 00007           /* read, write, execute: other */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004           /* read permission: other */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002           /* write permission: other */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 00001           /* execute permission: other */
+#endif
+
+/* For sys_adminlog(). */
+#ifndef LOG_EMERG
+#define LOG_EMERG       0       /* system is unusable */
+#endif
+
+#ifndef LOG_ALERT
+#define LOG_ALERT       1       /* action must be taken immediately */
+#endif
+
+#ifndef LOG_CRIT
+#define LOG_CRIT        2       /* critical conditions */
+#endif
+
+#ifndef LOG_ERR
+#define LOG_ERR         3       /* error conditions */
+#endif
+
+#ifndef LOG_WARNING
+#define LOG_WARNING     4       /* warning conditions */
+#endif
+
+#ifndef LOG_NOTICE
+#define LOG_NOTICE      5       /* normal but significant condition */
+#endif
+
+#ifndef LOG_INFO
+#define LOG_INFO        6       /* informational */
+#endif
+
+#ifndef LOG_DEBUG
+#define LOG_DEBUG       7       /* debug-level messages */
+#endif
+
+/* NetBSD doesn't have these */
+#ifndef SHM_R
+#define SHM_R 0400
+#endif
+
+#ifndef SHM_W
+#define SHM_W 0200
+#endif
+
+#if HAVE_KERNEL_SHARE_MODES
+#ifndef LOCK_MAND 
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+#endif
+#endif
+
+extern int DEBUGLEVEL;
+
+#define MAX_SEC_CTX_DEPTH 8    /* Maximum number of security contexts */
+
+
+#ifdef GLIBC_HACK_FCNTL64
+/* this is a gross hack. 64 bit locking is completely screwed up on
+   i386 Linux in glibc 2.1.95 (which ships with RedHat 7.0). This hack
+   "fixes" the problem with the current 2.4.0test kernels 
+*/
+#define fcntl fcntl64
+#undef F_SETLKW 
+#undef F_SETLK 
+#define F_SETLK 13
+#define F_SETLKW 14
+#endif
+
+
+/* Needed for sys_dlopen/sys_dlsym/sys_dlclose */
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+/* needed for some systems without iconv. Doesn't really matter
+   what error code we use */
+#ifndef EILSEQ
+#define EILSEQ EIO
+#endif
+
+/* add varargs prototypes with printf checking */
+int fdprintf(int , const char *, ...) PRINTF_ATTRIBUTE(2,3);
+int d_printf(const char *, ...) PRINTF_ATTRIBUTE(1,2);
+int d_fprintf(FILE *f, const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#ifndef HAVE_SNPRINTF_DECL
+int snprintf(char *,size_t ,const char *, ...) PRINTF_ATTRIBUTE(3,4);
+#endif
+#ifndef HAVE_ASPRINTF_DECL
+int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#endif
+
+void sys_adminlog(int priority, const char *format_str, ...) PRINTF_ATTRIBUTE(2,3);
+
+int pstr_sprintf(pstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+int fstr_sprintf(fstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+int d_vfprintf(FILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+int smb_xvasprintf(char **ptr, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/* we used to use these fns, but now we have good replacements
+   for snprintf and vsnprintf */
+#define slprintf snprintf
+#define vslprintf vsnprintf
+
+
+/* we need to use __va_copy() on some platforms */
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+
+#ifndef HAVE_TIMEGM
+time_t timegm(struct tm *tm);
+#endif
+
+#if defined(VALGRIND)
+#define strlen(x) valgrind_strlen(x)
+#endif
+
+/*
+ * Veritas File System.  Often in addition to native.
+ * Quotas different.
+ */
+#if defined(HAVE_SYS_FS_VX_QUOTA_H)
+#define VXFS_QUOTA
+#endif
+
+#if defined(HAVE_KRB5)
+
+#ifndef KRB5_SET_REAL_TIME
+krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
+#endif
+
+#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
+krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
+#endif
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
+#endif
+
+/* Samba wrapper function for krb5 functionality. */
+void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
+int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt);
+krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
+krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
+void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
+BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16]);
+#endif /* HAVE_KRB5 */
+
+#endif /* _INCLUDES_H */
+
diff --git a/source4/include/interfaces.h b/source4/include/interfaces.h
new file mode 100644 (file)
index 0000000..3b786f1
--- /dev/null
@@ -0,0 +1,12 @@
+/* 
+   This structure is used by lib/interfaces.c to return the list of network
+   interfaces on the machine
+*/
+
+#define MAX_INTERFACES 128
+
+struct iface_struct {
+       char name[16];
+       struct in_addr ip;
+       struct in_addr netmask;
+};
diff --git a/source4/include/intl.h b/source4/include/intl.h
new file mode 100644 (file)
index 0000000..5b56d9a
--- /dev/null
@@ -0,0 +1,24 @@
+/* 
+   Unix SMB/CIFS implementation.
+   internationalisation headers
+   Copyright (C) Andrew Tridgell              2001
+   
+   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.
+*/
+
+
+/* ideally we would have a static mapping, but that precludes
+   dynamic loading. This is a reasonable compromise */
+#define _(x) lang_msg_rotate(x)
diff --git a/source4/include/ioctl.h b/source4/include/ioctl.h
new file mode 100644 (file)
index 0000000..272004d
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ioctl and fsctl definitions
+   
+   Copyright (C) Andrew Tridgell              2003
+   
+   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.
+*/
+
+
+/* ioctl codes */
+#define IOCTL_QUERY_JOB_INFO      0x530060
+
+
+/* filesystem control codes */
+#define FSCTL_FILESYSTEM 0x90000
+#define FSCTL_SET_SPARSE (FSCTL_FILESYSTEM | 0xc4)
+
diff --git a/source4/include/libsmb_internal.h b/source4/include/libsmb_internal.h
new file mode 100644 (file)
index 0000000..21fe47d
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef _LIBSMB_INTERNAL_H_
+#define _LIBSMB_INTERNAL_H_
+
+#define SMBC_MAX_NAME  1023
+#define SMBC_FILE_MODE (S_IFREG | 0444)
+#define SMBC_DIR_MODE  (S_IFDIR | 0555)
+
+
+#include "../include/libsmbclient.h"
+
+
+struct _SMBCSRV {
+       struct cli_state cli;
+       dev_t dev;
+       BOOL no_pathinfo2;
+       int server_fd;
+
+       SMBCSRV *next, *prev;
+       
+};
+
+/* 
+ * Keep directory entries in a list 
+ */
+struct smbc_dir_list {
+       struct smbc_dir_list *next;
+       struct smbc_dirent *dirent;
+};
+
+
+/*
+ * Structure for open file management
+ */ 
+struct _SMBCFILE {
+       int cli_fd; 
+       char *fname;
+       off_t offset;
+       struct _SMBCSRV *srv;
+       BOOL file;
+       struct smbc_dir_list *dir_list, *dir_end, *dir_next;
+       int dir_type, dir_error;
+
+       SMBCFILE *next, *prev;
+};
+
+
+struct smbc_internal_data {
+
+       /** INTERNAL: is this handle initialized ? 
+        */
+       int     _initialized;
+
+       /** INTERNAL: dirent pointer location 
+        */
+       char    _dirent[512];  
+
+       /** INTERNAL: server connection list
+        */
+       SMBCSRV * _servers;
+       
+       /** INTERNAL: open file/dir list
+        */
+       SMBCFILE * _files;
+};     
+
+
+#endif
diff --git a/source4/include/libsmbclient.h b/source4/include/libsmbclient.h
new file mode 100644 (file)
index 0000000..0c905ed
--- /dev/null
@@ -0,0 +1,1073 @@
+/*=====================================================================
+  Unix SMB/Netbios implementation.
+  SMB client library API definitions
+  Copyright (C) Andrew Tridgell 1998
+  Copyright (C) Richard Sharpe 2000
+  Copyright (C) John Terpsra 2000
+  Copyright (C) Tom Jansen (Ninja ISD) 2002 
+
+   
+  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.
+  =====================================================================*/
+
+#ifndef SMBCLIENT_H_INCLUDED
+#define SMBCLIENT_H_INCLUDED
+
+/*-------------------------------------------------------------------*/
+/* The following are special comments to instruct DOXYGEN (automated 
+ * documentation tool:
+*/
+/** \defgroup libsmbclient
+*/
+/** \defgroup structure Data Structures Type and Constants
+*   \ingroup libsmbclient
+*   Data structures, types, and constants
+*/
+/** \defgroup callback Callback function types
+*   \ingroup libsmbclient
+*   Callback functions
+*/
+/** \defgroup file File Functions
+*   \ingroup libsmbclient
+*   Functions used to access individual file contents
+*/
+/** \defgroup directory Directory Functions
+*   \ingroup libsmbclient
+*   Functions used to access directory entries
+*/
+/** \defgroup attribute Attributes Functions
+*   \ingroup libsmbclient
+*   Functions used to view or change file and directory attributes
+*/
+/** \defgroup print Print Functions
+*   \ingroup libsmbclient
+*   Functions used to access printing functionality
+*/
+/** \defgroup misc Miscellaneous Functions
+*   \ingroup libsmbclient
+*   Functions that don't fit in to other categories
+*/
+/*-------------------------------------------------------------------*/   
+
+/* Make sure we have the following includes for now ... */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define SMBC_WORKGROUP      1
+#define SMBC_SERVER         2
+#define SMBC_FILE_SHARE     3
+#define SMBC_PRINTER_SHARE  4
+#define SMBC_COMMS_SHARE    5
+#define SMBC_IPC_SHARE      6
+#define SMBC_DIR            7
+#define SMBC_FILE           8
+#define SMBC_LINK           9
+
+/**@ingroup structure
+ * Structure that represents a directory entry.
+ *
+ */
+struct smbc_dirent 
+{
+       /** Type of entity.
+           SMBC_WORKGROUP=1,
+           SMBC_SERVER=2, 
+           SMBC_FILE_SHARE=3,
+           SMBC_PRINTER_SHARE=4,
+           SMBC_COMMS_SHARE=5,
+           SMBC_IPC_SHARE=6,
+           SMBC_DIR=7,
+           SMBC_FILE=8,
+           SMBC_LINK=9,*/ 
+       unsigned int smbc_type; 
+
+       /** Length of this smbc_dirent in bytes
+        */
+       unsigned int dirlen;
+       /** The length of the comment string in bytes (includes null 
+        *  terminator)
+        */
+       unsigned int commentlen;
+       /** Points to the null terminated comment string 
+        */
+       char *comment;
+       /** The length of the name string in bytes (includes null 
+        *  terminator)
+        */
+       unsigned int namelen;
+       /** Points to the null terminated name string 
+        */
+       char name[1];
+};
+
+
+/**@ingroup structure
+ * Structure that represents a print job.
+ *
+ */
+#ifndef _CLIENT_H
+struct print_job_info 
+{
+       /** numeric ID of the print job
+        */
+       unsigned short id;
+    
+       /** represents print job priority (lower numbers mean higher priority)
+        */
+       unsigned short priority;
+    
+       /** Size of the print job
+        */
+       size_t size;
+    
+       /** Name of the user that owns the print job
+        */
+       char user[128];
+  
+       /** Name of the print job. This will have no name if an anonymous print
+        *  file was opened. Ie smb://server/printer
+        */
+       char name[128];
+
+       /** Time the print job was spooled
+        */
+       time_t t;
+};
+#endif /* _CLIENT_H */
+
+
+/**@ingroup structure
+ * Server handle 
+ */
+typedef struct _SMBCSRV  SMBCSRV;
+
+/**@ingroup structure
+ * File or directory handle 
+ */
+typedef struct _SMBCFILE SMBCFILE;
+
+/**@ingroup structure
+ * File or directory handle 
+ */
+typedef struct _SMBCCTX SMBCCTX;
+
+
+
+
+
+/**@ingroup callback
+ * Authentication callback function type.
+ * 
+ * Type for the the authentication function called by the library to
+ * obtain authentication credentals
+ *
+ * @param srv       Server being authenticated to
+ *
+ * @param shr       Share being authenticated to
+ *
+ * @param wg        Pointer to buffer containing a "hint" for the
+ *                  workgroup to be authenticated.  Should be filled in
+ *                  with the correct workgroup if the hint is wrong.
+ * 
+ * @param wglen     The size of the workgroup buffer in bytes
+ *
+ * @param un        Pointer to buffer containing a "hint" for the
+ *                  user name to be use for authentication. Should be
+ *                  filled in with the correct workgroup if the hint is
+ *                  wrong.
+ * 
+ * @param unlen     The size of the username buffer in bytes
+ *
+ * @param pw        Pointer to buffer containing to which password 
+ *                  copied
+ * 
+ * @param pwlen     The size of the password buffer in bytes
+ *           
+ */
+typedef void (*smbc_get_auth_data_fn)(const char *srv, 
+                                      const char *shr,
+                                      char *wg, int wglen, 
+                                      char *un, int unlen,
+                                      char *pw, int pwlen);
+
+
+/**@ingroup callback
+ * Print job info callback function type.
+ *
+ * @param i         pointer to print job information structure
+ *
+ */ 
+typedef void (*smbc_list_print_job_fn)(struct print_job_info *i);
+               
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c         pointer to smb context
+ *
+ * @param srv       pointer to server to check
+ *
+ * @return          0 when connection is good. 1 on error.
+ *
+ */ 
+typedef int (*smbc_check_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+/**@ingroup callback
+ * Remove a server if unused
+ *
+ * @param c         pointer to smb context
+ *
+ * @param srv       pointer to server to remove
+ *
+ * @return          0 on success. 1 on failure.
+ *
+ */ 
+typedef int (*smbc_remove_unused_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Add a server to the cache system
+ *
+ * @param c         pointer to smb context
+ *
+ * @param srv       pointer to server to add
+ *
+ * @param server    server name 
+ *
+ * @param share     share name
+ *
+ * @param workgroup workgroup used to connect
+ *
+ * @param username  username used to connect
+ *
+ * @return          0 on success. 1 on failure.
+ *
+ */ 
+typedef int (*smbc_add_cached_srv_fn)   (SMBCCTX * c, SMBCSRV *srv, 
+                                   char * server, char * share, 
+                                   char * workgroup, char * username);
+
+
+/**@ingroup callback
+ * Look up a server in the cache system
+ *
+ * @param c         pointer to smb context
+ *
+ * @param server    server name to match
+ *
+ * @param share     share name to match
+ *
+ * @param workgroup workgroup to match
+ *
+ * @param username  username to match
+ *
+ * @return          pointer to SMBCSRV on success. NULL on failure.
+ *
+ */ 
+typedef SMBCSRV * (*smbc_get_cached_srv_fn)   (SMBCCTX * c, char * server, 
+                                              char * share, char * workgroup, char * username);
+
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c         pointer to smb context
+ *
+ * @param srv       pointer to server to remove
+ *
+ * @return          0 when found and removed. 1 on failure.
+ *
+ */ 
+typedef int (*smbc_remove_cached_srv_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Try to remove all servers from the cache system and disconnect
+ *
+ * @param c         pointer to smb context
+ *
+ * @return          0 when found and removed. 1 on failure.
+ *
+ */ 
+typedef int (*smbc_purge_cached_fn)     (SMBCCTX * c);
+
+
+
+
+/**@ingroup structure
+ * Structure that contains a client context information 
+ * This structure is know as SMBCCTX
+ */
+struct _SMBCCTX {
+       /** debug level 
+        */
+       int     debug;
+       
+       /** netbios name used for making connections
+        */
+       char * netbios_name;
+
+       /** workgroup name used for making connections 
+        */
+       char * workgroup;
+
+       /** username used for making connections 
+        */
+       char * user;
+
+       /** timeout used for waiting on connections / response data (in milliseconds)
+        */
+       int timeout;
+
+       /** callable functions for files:
+        * For usage and return values see the smbc_* functions
+        */ 
+       SMBCFILE * (*open)    (SMBCCTX *c, const char *fname, int flags, mode_t mode);
+       SMBCFILE * (*creat)   (SMBCCTX *c, const char *path, mode_t mode);
+       ssize_t    (*read)    (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+       ssize_t    (*write)   (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+       int        (*unlink)  (SMBCCTX *c, const char *fname);
+       int        (*rename)  (SMBCCTX *ocontext, const char *oname, 
+                              SMBCCTX *ncontext, const char *nname);
+       off_t      (*lseek)   (SMBCCTX *c, SMBCFILE * file, off_t offset, int whence);
+       int        (*stat)    (SMBCCTX *c, const char *fname, struct stat *st);
+       int        (*fstat)   (SMBCCTX *c, SMBCFILE *file, struct stat *st);
+       int        (*close)   (SMBCCTX *c, SMBCFILE *file);
+
+       /** callable functions for dirs
+        */ 
+       SMBCFILE * (*opendir) (SMBCCTX *c, const char *fname);
+       int        (*closedir)(SMBCCTX *c, SMBCFILE *dir);
+       struct smbc_dirent * (*readdir)(SMBCCTX *c, SMBCFILE *dir);
+       int        (*getdents)(SMBCCTX *c, SMBCFILE *dir, 
+                              struct smbc_dirent *dirp, int count);
+       int        (*mkdir)   (SMBCCTX *c, const char *fname, mode_t mode);
+       int        (*rmdir)   (SMBCCTX *c, const char *fname);
+       off_t      (*telldir) (SMBCCTX *c, SMBCFILE *dir);
+       int        (*lseekdir)(SMBCCTX *c, SMBCFILE *dir, off_t offset);
+       int        (*fstatdir)(SMBCCTX *c, SMBCFILE *dir, struct stat *st);
+
+       /** callable functions for printing
+        */ 
+       int        (*print_file)(SMBCCTX *c_file, const char *fname, 
+                                SMBCCTX *c_print, const char *printq);
+       SMBCFILE * (*open_print_job)(SMBCCTX *c, const char *fname);
+       int        (*list_print_jobs)(SMBCCTX *c, const char *fname, smbc_list_print_job_fn fn);
+       int        (*unlink_print_job)(SMBCCTX *c, const char *fname, int id);
+
+
+       /** Callbacks
+        * These callbacks _always_ have to be initialized because they will not be checked
+        * at dereference for increased speed.
+        */
+       struct _smbc_callbacks {
+               /** authentication function callback: called upon auth requests
+                */
+               smbc_get_auth_data_fn auth_fn;
+               
+               /** check if a server is still good
+                */
+               smbc_check_server_fn check_server_fn;
+
+               /** remove a server if unused
+                */
+               smbc_remove_unused_server_fn remove_unused_server_fn;
+
+               /** Cache subsystem
+                * For an example cache system see samba/source/libsmb/libsmb_cache.c
+                * Cache subsystem functions follow.
+                */
+
+               /** server cache addition 
+                */
+               smbc_add_cached_srv_fn add_cached_srv_fn;
+
+               /** server cache lookup 
+                */
+               smbc_get_cached_srv_fn get_cached_srv_fn;
+
+               /** server cache removal
+                */
+               smbc_remove_cached_srv_fn remove_cached_srv_fn;
+               
+               /** server cache purging, try to remove all cached servers (disconnect)
+                */
+               smbc_purge_cached_fn purge_cached_fn;
+       } callbacks;
+
+
+       /** Space to store private data of the server cache.
+        */
+       struct smbc_server_cache * server_cache;
+
+       /** INTERNAL DATA
+        * do _NOT_ touch this from your program !
+        */
+       struct smbc_internal_data * internal;
+       
+};
+
+
+/**@ingroup misc
+ * Create a new SBMCCTX (a context).
+ *
+ * Must be called before the context is passed to smbc_context_init()
+ *
+ * @return          The given SMBCCTX pointer on success, NULL on error with errno set:
+ *                  - ENOMEM Out of memory
+ *
+ * @see             smbc_free_context(), smbc_init_context()
+ *
+ * @note            Do not forget to smbc_init_context() the returned SMBCCTX pointer !
+ */
+SMBCCTX * smbc_new_context(void);
+
+/**@ingroup misc
+ * Delete a SBMCCTX (a context) acquired from smbc_new_context().
+ *
+ * The context will be deleted if possible.
+ *
+ * @param context   A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @param shutdown_ctx   If 1, all connections and files will be closed even if they are busy.
+ *
+ *
+ * @return          Returns 0 on succes. Returns 1 on failure with errno set:
+ *                  - EBUSY Server connections are still used, Files are open or cache 
+ *                          could not be purged
+ *                  - EBADF context == NULL
+ *
+ * @see             smbc_new_context()
+ *
+ * @note            It is advised to clean up all the contexts with shutdown_ctx set to 1
+ *                  just before exit()'ing. When shutdown_ctx is 0, this function can be
+ *                  use in periodical cleanup functions for example.
+ */
+int smbc_free_context(SMBCCTX * context, int shutdown_ctx);
+
+
+/**@ingroup misc
+ * Initialize a SBMCCTX (a context).
+ *
+ * Must be called before using any SMBCCTX API function
+ *
+ * @param context   A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @return          A pointer to the given SMBCCTX on success, NULL on error with errno set:
+ *                  - EBADF  NULL context given
+ *                  - ENOMEM Out of memory
+ *                  - ENOENT The smb.conf file would not load
+ *
+ * @see             smbc_new_context()
+ *
+ * @note            my_context = smbc_init_context(smbc_new_context()) is perfectly safe, 
+ *                  but it might leak memory on smbc_context_init() failure. Avoid this.
+ *                  You'll have to call smbc_free_context() yourself on failure.  
+ */
+
+SMBCCTX * smbc_init_context(SMBCCTX * context);
+
+/**@ingroup misc
+ * Initialize the samba client library.
+ *
+ * Must be called before using any of the smbclient API function
+ *  
+ * @param fn        The function that will be called to obtaion 
+ *                  authentication credentials.
+ *
+ * @param debug     Allows caller to set the debug level. Can be
+ *                  changed in smb.conf file. Allows caller to set
+ *                  debugging if no smb.conf.
+ *   
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - ENOMEM Out of memory
+ *                  - ENOENT The smb.conf file would not load
+ *
+ */
+
+int smbc_init(smbc_get_auth_data_fn fn, int debug);
+
+/**@ingroup file
+ * Open a file on an SMB server.
+ *
+ * @param furl      The smb url of the file to be opened. 
+ *
+ * @param flags     Is one of O_RDONLY, O_WRONLY or O_RDWR which 
+ *                  request opening  the  file  read-only,write-only
+ *                  or read/write. flags may also be bitwise-or'd with
+ *                  one or  more of  the following: 
+ *                  O_CREAT - If the file does not exist it will be 
+ *                  created.
+ *                  O_EXCL - When  used with O_CREAT, if the file 
+ *                  already exists it is an error and the open will 
+ *                  fail. 
+ *                  O_TRUNC - If the file already exists it will be
+ *                  truncated.
+ *                  O_APPEND The  file  is  opened  in  append mode 
+ *
+ * @param mode      mode specifies the permissions to use if a new 
+ *                  file is created.  It  is  modified  by  the 
+ *                  process's umask in the usual way: the permissions
+ *                  of the created file are (mode & ~umask) 
+ *
+ *                  Not currently use, but there for future use.
+ *                  We will map this to SYSTEM, HIDDEN, etc bits
+ *                  that reverses the mapping that smbc_fstat does.
+ *
+ * @return          Valid file handle, < 0 on error with errno set:
+ *                  - ENOMEM  Out of memory
+ *                  - EINVAL if an invalid parameter passed, like no 
+ *                  file, or smbc_init not called.
+ *                  - EEXIST  pathname already exists and O_CREAT and 
+ *                  O_EXCL were used.
+ *                  - EISDIR  pathname  refers  to  a  directory  and  
+ *                  the access requested involved writing.
+ *                  - EACCES  The requested access to the file is not 
+ *                  allowed 
+ *                  - ENODEV The requested share does not exist
+ *                  - ENOTDIR A file on the path is not a directory
+ *                  - ENOENT  A directory component in pathname does 
+ *                  not exist.
+ *
+ * @see             smbc_creat()
+ *
+ * @note            This call uses an underlying routine that may create
+ *                  a new connection to the server specified in the URL.
+ *                  If the credentials supplied in the URL, or via the
+ *                  auth_fn in the smbc_init call, fail, this call will
+ *                  try again with an empty username and password. This 
+ *                  often gets mapped to the guest account on some machines.
+ */
+
+int smbc_open(const char *furl, int flags, mode_t mode);
+
+/**@ingroup file
+ * Create a file on an SMB server.
+ *
+ * Same as calling smbc_open() with flags = O_CREAT|O_WRONLY|O_TRUNC 
+ *   
+ * @param furl      The smb url of the file to be created
+ *  
+ * @param mode      mode specifies the permissions to use if  a  new  
+ *                  file is created.  It  is  modified  by  the 
+ *                  process's umask in the usual way: the permissions
+ *                  of the created file are (mode & ~umask)
+ *
+ *                  NOTE, the above is not true. We are dealing with 
+ *                  an SMB server, which has no concept of a umask!
+ *      
+ * @return          Valid file handle, < 0 on error with errno set:
+ *                  - ENOMEM  Out of memory
+ *                  - EINVAL if an invalid parameter passed, like no 
+ *                  file, or smbc_init not called.
+ *                  - EEXIST  pathname already exists and O_CREAT and
+ *                  O_EXCL were used.
+ *                  - EISDIR  pathname  refers  to  a  directory  and
+ *                  the access requested involved writing.
+ *                  - EACCES  The requested access to the file is not
+ *                  allowed 
+ *                  - ENOENT  A directory component in pathname does 
+ *                  not exist.
+ *                  - ENODEV The requested share does not exist.
+ * @see             smbc_open()
+ *
+ */
+
+int smbc_creat(const char *furl, mode_t mode);
+
+/**@ingroup file
+ * Read from a file using an opened file handle.
+ *
+ * @param fd        Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf       Pointer to buffer to recieve read data
+ *
+ * @param bufsize   Size of buf in bytes
+ *
+ * @return          Number of bytes read, < 0 on error with errno set:
+ *                  - EISDIR fd refers to a directory
+ *                  - EBADF  fd  is  not  a valid file descriptor or 
+ *                  is not open for reading.
+ *                  - EINVAL fd is attached to an object which is 
+ *                  unsuitable for reading, or no buffer passed or
+ *                 smbc_init not called.
+ *
+ * @see             smbc_open(), smbc_write()
+ *
+ */
+ssize_t smbc_read(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Write to a file using an opened file handle.
+ *
+ * @param fd        Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf       Pointer to buffer to recieve read data
+ *
+ * @param bufsize   Size of buf in bytes
+ *
+ * @return          Number of bytes written, < 0 on error with errno set:
+ *                  - EISDIR fd refers to a directory.
+ *                  - EBADF  fd  is  not  a valid file descriptor or 
+ *                  is not open for reading.
+ *                  - EINVAL fd is attached to an object which is 
+ *                  unsuitable for reading, or no buffer passed or
+ *                 smbc_init not called.
+ *
+ * @see             smbc_open(), smbc_read()
+ *
+ */
+ssize_t smbc_write(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Seek to a specific location in a file.
+ *
+ * @param fd        Open file handle from smbc_open() or smbc_creat()
+ * 
+ * @param offset    Offset in bytes from whence
+ * 
+ * @param whence    A location in the file:
+ *                  - SEEK_SET The offset is set to offset bytes from
+ *                  the beginning of the file
+ *                  - SEEK_CUR The offset is set to current location 
+ *                  plus offset bytes.
+ *                  - SEEK_END The offset is set to the size of the 
+ *                  file plus offset bytes.
+ *
+ * @return          Upon successful completion, lseek returns the 
+ *                  resulting offset location as measured in bytes 
+ *                  from the beginning  of the file. Otherwise, a value
+ *                  of (off_t)-1 is returned and errno is set to 
+ *                  indicate the error:
+ *                  - EBADF  Fildes is not an open file descriptor.
+ *                  - EINVAL Whence is not a proper value or smbc_init
+ *                   not called.
+ *
+ * @todo Are all the whence values really supported?
+ * 
+ * @todo Are errno values complete and correct?
+ */
+off_t smbc_lseek(int fd, off_t offset, int whence);
+
+
+/**@ingroup file
+ * Close an open file handle.
+ *
+ * @param fd        The file handle to close
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EBADF  fd isn't a valid open file descriptor
+ *                  - EINVAL smbc_init() failed or has not been called
+ *
+ * @see             smbc_open(), smbc_creat()
+ */
+int smbc_close(int fd);
+
+
+/**@ingroup directory
+ * Unlink (delete) a file or directory.
+ *
+ * @param furl      The smb url of the file to delete
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EACCES or EPERM Write  access  to the directory 
+ *                  containing pathname is not allowed or one  
+ *                  of  the  directories in pathname did not allow
+ *                  search (execute) permission
+ *                  - ENOENT A directory component in pathname does
+ *                  not exist
+ *                  - EINVAL NULL was passed in the file param or
+ *                   smbc_init not called.
+ *                  - EACCES You do not have access to the file
+ *                  - ENOMEM Insufficient kernel memory was available
+ *
+ * @see             smbc_rmdir()s
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_unlink(const char *furl);
+
+
+/**@ingroup directory
+ * Rename or move a file or directory.
+ * 
+ * @param ourl      The original smb url (source url) of file or 
+ *                  directory to be moved
+ * 
+ * @param nurl      The new smb url (destination url) of the file
+ *                  or directory after the move.  Currently nurl must
+ *                  be on the same share as ourl.
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EISDIR nurl is an existing directory, but ourl is
+ *                  not a directory.
+ *                  - EEXIST nurl is  a  non-empty directory, 
+ *                  i.e., contains entries other than "." and ".."
+ *                  - EINVAL The  new  url  contained  a path prefix 
+ *                  of the old, or, more generally, an  attempt was
+ *                  made  to make a directory a subdirectory of itself
+ *                 or smbc_init not called.
+ *                  - ENOTDIR A component used as a directory in ourl 
+ *                  or nurl path is not, in fact, a directory.  Or, 
+ *                  ourl  is a directory, and newpath exists but is not
+ *                  a directory.
+ *                  - EACCES or EPERM Write access to the directory 
+ *                  containing ourl or nurl is not allowed for the 
+ *                  process's effective uid,  or  one of the 
+ *                  directories in ourl or nurl did not allow search
+ *                  (execute) permission,  or ourl  was  a  directory
+ *                  and did not allow write permission.
+ *                  - ENOENT A  directory component in ourl or nurl 
+ *                  does not exist.
+ *                  - EXDEV Rename across shares not supported.
+ *                  - ENOMEM Insufficient kernel memory was available.
+ *                  - EEXIST The target file, nurl, already exists.
+ *
+ *
+ * @todo Are we going to support copying when urls are not on the same
+ *       share?  I say no... NOTE. I agree for the moment.
+ *
+ */
+int smbc_rename(const char *ourl, const char *nurl);
+
+
+/**@ingroup directory
+ * Open a directory used to obtain directory entries.
+ *
+ * @param durl      The smb url of the directory to open
+ *
+ * @return          Valid directory handle. < 0 on error with errno set:
+ *                  - EACCES Permission denied.
+ *                  - EINVAL A NULL file/URL was passed, or the URL would
+ *                  not parse, or was of incorrect form or smbc_init not
+ *                  called.
+ *                  - ENOENT durl does not exist, or name is an 
+ *                  - ENOMEM Insufficient memory to complete the 
+ *                  operation.                              
+ *                  - ENOTDIR name is not a directory.
+ *                  - EPERM the workgroup could not be found.
+ *                  - ENODEV the workgroup or server could not be found.
+ *
+ * @see             smbc_getdents(), smbc_readdir(), smbc_closedir()
+ *
+ */
+int smbc_opendir(const char *durl);
+
+
+/**@ingroup directory
+ * Close a directory handle opened by smbc_opendir().
+ *
+ * @param dh        Directory handle to close
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EBADF dh is an invalid directory handle
+ *
+ * @see             smbc_opendir()
+ */
+int smbc_closedir(int dh);
+
+
+/**@ingroup directory
+ * Get multiple directory entries.
+ *
+ * smbc_getdents() reads as many dirent structures from the an open 
+ * directory handle into a specified memory area as will fit.
+ *
+ * @param dh        Valid directory as returned by smbc_opendir()
+ *
+ * @param dirp      pointer to buffer that will receive the directory
+ *                  entries.
+ * 
+ * @param count     The size of the dirp buffer in bytes
+ *
+ * @returns         If any dirents returned, return will indicate the
+ *                  total size. If there were no more dirents available,
+ *                  0 is returned. < 0 indicates an error.
+ *                  - EBADF  Invalid directory handle
+ *                  - EINVAL Result buffer is too small or smbc_init
+ *                 not called.
+ *                  - ENOENT No such directory.
+ * @see             , smbc_dirent, smbc_readdir(), smbc_open()
+ *
+ * @todo Are errno values complete and correct?
+ *
+ * @todo Add example code so people know how to parse buffers.
+ */
+int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count);
+
+
+/**@ingroup directory
+ * Get a single directory entry.
+ *
+ * @param dh        Valid directory as returned by smbc_opendir()
+ *
+ * @return          A pointer to a smbc_dirent structure, or NULL if an
+ *                  error occurs or end-of-directory is reached:
+ *                  - EBADF Invalid directory handle
+ *                  - EINVAL smbc_init() failed or has not been called
+ *
+ * @see             smbc_dirent, smbc_getdents(), smbc_open()
+ */
+struct smbc_dirent* smbc_readdir(unsigned int dh);
+
+
+/**@ingroup directory
+ * Get the current directory offset.
+ *
+ * smbc_telldir() may be used in conjunction with smbc_readdir() and
+ * smbc_lseekdir().
+ *
+ * @param dh        Valid directory as returned by smbc_opendir()
+ *
+ * @return          The current location in the directory stream or -1
+ *                  if an error occur.  The current location is not
+ *                  an offset. Becuase of the implementation, it is a 
+ *                  handle that allows the library to find the entry
+ *                  later.
+ *                  - EBADF dh is not a valid directory handle
+ *                  - EINVAL smbc_init() failed or has not been called
+ *                  - ENOTDIR if dh is not a directory
+ *
+ * @see             smbc_readdir()
+ *
+ */
+off_t smbc_telldir(int dh);
+
+
+/**@ingroup directory
+ * lseek on directories.
+ *
+ * smbc_lseekdir() may be used in conjunction with smbc_readdir() and
+ * smbc_telldir(). (rewind by smbc_lseekdir(fd, NULL))
+ *
+ * @param fd        Valid directory as returned by smbc_opendir()
+ * 
+ * @param offset    The offset (as returned by smbc_telldir). Can be
+ *                  NULL, in which case we will rewind
+ *
+ * @return          0 on success, -1 on failure
+ *                  - EBADF dh is not a valid directory handle
+ *                  - ENOTDIR if dh is not a directory
+ *                  - EINVAL offset did not refer to a valid dirent or
+ *                   smbc_init not called.
+ *
+ * @see             smbc_telldir()
+ *
+ *
+ * @todo In what does the reture and errno values mean?
+ */
+int smbc_lseekdir(int fd, off_t offset);
+
+/**@ingroup directory
+ * Create a directory.
+ *
+ * @param durl      The url of the directory to create
+ *
+ * @param mode      Specifies  the  permissions to use. It is modified
+ *                  by the process's umask in the usual way: the 
+ *                  permissions of the created file are (mode & ~umask).
+ * 
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EEXIST directory url already exists
+ *                  - EACCES The parent directory does not allow write
+ *                  permission to the process, or one of the directories
+ *                  - ENOENT A directory component in pathname does not
+ *                  exist.
+ *                  - EINVAL NULL durl passed or smbc_init not called.
+ *                  - ENOMEM Insufficient memory was available.
+ *
+ * @see             smbc_rmdir()
+ *
+ */
+int smbc_mkdir(const char *durl, mode_t mode);
+
+
+/**@ingroup directory
+ * Remove a directory.
+ * 
+ * @param durl      The smb url of the directory to remove
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EACCES or EPERM Write access to the directory
+ *                  containing pathname was not allowed.
+ *                  - EINVAL durl is NULL or smbc_init not called.
+ *                  - ENOENT A directory component in pathname does not
+ *                  exist.
+ *                  - ENOTEMPTY directory contains entries.
+ *                  - ENOMEM Insufficient kernel memory was available.
+ *
+ * @see             smbc_mkdir(), smbc_unlink() 
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_rmdir(const char *durl);
+
+
+/**@ingroup attribute
+ * Get information about a file or directory.
+ *
+ * @param url       The smb url to get information for
+ *
+ * @param st        pointer to a buffer that will be filled with 
+ *                  standard Unix struct stat information.
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - ENOENT A component of the path file_name does not
+ *                  exist.
+ *                  - EINVAL a NULL url was passed or smbc_init not called.
+ *                  - EACCES Permission denied.
+ *                  - ENOMEM Out of memory
+ *                  - ENOTDIR The target dir, url, is not a directory.
+ *
+ * @see             Unix stat()
+ *
+ */
+int smbc_stat(const char *url, struct stat *st);
+
+
+/**@ingroup attribute
+ * Get file information via an file descriptor.
+ * 
+ * @param fd        Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param st        pointer to a buffer that will be filled with 
+ *                  standard Unix struct stat information.
+ * 
+ * @return          EBADF  filedes is bad.
+ *                  - EACCES Permission denied.
+ *                  - EBADF fd is not a valid file descriptor
+ *                  - EINVAL Problems occurred in the underlying routines
+ *                   or smbc_init not called.
+ *                  - ENOMEM Out of memory
+ *
+ * @see             smbc_stat(), Unix stat()
+ *
+ */
+int smbc_fstat(int fd, struct stat *st);
+
+
+/**@ingroup attribue
+ * Change the ownership of a file or directory.
+ *
+ * @param url       The smb url of the file or directory to change 
+ *                  ownership of.
+ *
+ * @param owner     I have no idea?
+ *
+ * @param group     I have not idea?
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EPERM  The effective UID does not match the owner
+ *                  of the file, and is not zero; or the owner or group
+ *                  were specified incorrectly.
+ *                  - ENOENT The file does not exist.
+ *                  - ENOMEM Insufficient was available.
+ *                  - ENOENT file or directory does not exist
+ *
+ * @todo Are we actually going to be able to implement this function
+ *
+ * @todo How do we abstract owner and group uid and gid?
+ *
+ */
+int smbc_chown(const char *url, uid_t owner, gid_t group);
+
+
+/**@ingroup attribute
+ * Change the permissions of a file.
+ *
+ * @param url       The smb url of the file or directory to change
+ *                  permissions of
+ * 
+ * @param mode      The permissions to set:
+ *                  - Put good explaination of permissions here!
+ *
+ * @return          0 on success, < 0 on error with errno set:
+ *                  - EPERM  The effective UID does not match the owner
+ *                  of the file, and is not zero
+ *                  - ENOENT The file does not exist.
+ *                  - ENOMEM Insufficient was available.
+ *                  - ENOENT file or directory does not exist
+ *
+ * @todo Actually implement this fuction?
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_chmod(const char *url, mode_t mode);
+
+
+/**@ingroup print
+ * Print a file given the name in fname. It would be a URL ...
+ * 
+ * @param fname     The URL of a file on a remote SMB server that the
+ *                  caller wants printed
+ *
+ * @param printq    The URL of the print share to print the file to.
+ *
+ * @return          0 on success, < 0 on error with errno set:         
+ *
+ *                  - EINVAL fname or printq was NULL or smbc_init not
+ *                   not called.
+ *                  and errors returned by smbc_open
+ *
+ */                                     
+int smbc_print_file(const char *fname, const char *printq);
+
+/**@ingroup print
+ * Open a print file that can be written to by other calls. This simply
+ * does an smbc_open call after checking if there is a file name on the
+ * URI. If not, a temporary name is added ...
+ *
+ * @param fname     The URL of the print share to print to?
+ *
+ * @returns         A file handle for the print file if successful.
+ *                  Returns -1 if an error ocurred and errno has the values
+ *                  - EINVAL fname was NULL or smbc_init not called.
+ *                  - all errors returned by smbc_open
+ *
+ */
+int smbc_open_print_job(const char *fname);
+
+/**@ingroup print
+ * List the print jobs on a print share, for the moment, pass a callback 
+ *
+ * @param purl      The url of the print share to list the jobs of
+ * 
+ * @param fn        Callback function the receives printjob info
+ * 
+ * @return          0 on success, < 0 on error with errno set: 
+ *                  - EINVAL fname was NULL or smbc_init not called
+ *                  - EACCES ???
+ */
+int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn);
+
+/**@ingroup print
+ * Delete a print job 
+ *
+ * @param purl      Url of the print share
+ *
+ * @param id        The id of the job to delete
+ *
+ * @return          0 on success, < 0 on error with errno set: 
+ *                  - EINVAL fname was NULL or smbc_init not called
+ *
+ * @todo    what errno values are possible here?
+ */
+int smbc_unlink_print_job(const char *purl, int id);
+
+
+#endif /* SMBCLIENT_H_INCLUDED */
diff --git a/source4/include/local.h b/source4/include/local.h
new file mode 100644 (file)
index 0000000..4515bd8
--- /dev/null
@@ -0,0 +1,226 @@
+/* Copyright (C) 1995-1998 Samba-Team */
+/* Copyright (C) 1998 John H Terpstra <jht@aquasoft.com.au> */
+
+/* local definitions for file server */
+#ifndef _LOCAL_H
+#define _LOCAL_H
+
+/* The default workgroup - usually overridden in smb.conf */
+#ifndef DEFAULT_WORKGROUP
+#define DEFAULT_WORKGROUP "WORKGROUP"
+#endif
+
+/* the maximum debug level to compile into the code. This assumes a good 
+   optimising compiler that can remove unused code 
+   for embedded or low-memory systems set this to a value like 2 to get
+   only important messages. This gives *much* smaller binaries
+*/
+#ifndef MAX_DEBUG_LEVEL
+#define MAX_DEBUG_LEVEL 1000
+#endif
+
+/* This defines the section name in the configuration file that will contain */
+/* global parameters - that is, parameters relating to the whole server, not */
+/* just services. This name is then reserved, and may not be used as a       */
+/* a service name. It will default to "global" if not defined here.          */
+#define GLOBAL_NAME "global"
+#define GLOBAL_NAME2 "globals"
+
+/* This defines the section name in the configuration file that will
+   refer to the special "homes" service */
+#define HOMES_NAME "homes"
+
+/* This defines the section name in the configuration file that will
+   refer to the special "printers" service */
+#define PRINTERS_NAME "printers"
+
+/* Yves Gaige <yvesg@hptnodur.grenoble.hp.com> requested this set this              */
+/* to a maximum of 8 if old smb clients break because of long printer names. */
+#define MAXPRINTERLEN 15
+
+/* max number of directories open at once */
+/* note that with the new directory code this no longer requires a
+   file handle per directory, but large numbers do use more memory */
+#define MAX_OPEN_DIRECTORIES 256
+
+/* max number of directory handles */
+/* As this now uses the bitmap code this can be
+   quite large. */
+#define MAX_DIRECTORY_HANDLES 2048
+
+/* maximum number of file caches per smbd */
+#define MAX_WRITE_CACHES 10
+
+/* define what facility to use for syslog */
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_DAEMON
+#endif
+
+/* 
+ * Default number of maximum open files per smbd. This is
+ * also limited by the maximum available file descriptors
+ * per process and can also be set in smb.conf as "max open files"
+ * in the [global] section.
+ */
+
+#ifndef MAX_OPEN_FILES
+#define MAX_OPEN_FILES 10000
+#endif
+#define WORDMAX 0xFFFF
+
+/* the maximum password length before we declare a likely attack */
+#define MAX_PASS_LEN 200
+
+/* separators for lists */
+#define LIST_SEP " \t,;\n\r"
+
+/* wchar separators for lists */
+#define LIST_SEP_W wchar_list_sep
+
+/* this is where browse lists are kept in the lock dir */
+#define SERVER_LIST "browse.dat"
+
+/* shall filenames with illegal chars in them get mangled in long
+   filename listings? */
+#define MANGLE_LONG_FILENAMES 
+
+/* define this if you want to stop spoofing with .. and soft links
+   NOTE: This also slows down the server considerably */
+#define REDUCE_PATHS
+
+/* the size of the directory cache */
+#define DIRCACHESIZE 20
+
+/* what default type of filesystem do we want this to show up as in a
+   NT file manager window? */
+#define FSTYPE_STRING "NTFS"
+
+/* the default guest account - normally set in the Makefile or smb.conf */
+#ifndef GUEST_ACCOUNT
+#define GUEST_ACCOUNT "nobody"
+#endif
+
+/* user to test password server with as invalid in security=server mode. */
+#ifndef INVALID_USER_PREFIX
+#define INVALID_USER_PREFIX "sambatest"
+#endif
+
+/* the default pager to use for the client "more" command. Users can
+   override this with the PAGER environment variable */
+#ifndef PAGER
+#define PAGER "more"
+#endif
+
+/* the size of the uid cache used to reduce valid user checks */
+#define VUID_CACHE_SIZE 32
+
+/* the following control timings of various actions. Don't change 
+   them unless you know what you are doing. These are all in seconds */
+#define DEFAULT_SMBD_TIMEOUT (60*60*24*7)
+#define SMBD_RELOAD_CHECK (180)
+#define IDLE_CLOSED_TIMEOUT (60)
+#define DPTR_IDLE_TIMEOUT (120)
+#define SMBD_SELECT_TIMEOUT (60)
+#define NMBD_SELECT_LOOP (10)
+#define BROWSE_INTERVAL (60)
+#define REGISTRATION_INTERVAL (10*60)
+#define NMBD_INETD_TIMEOUT (120)
+#define NMBD_MAX_TTL (24*60*60)
+#define LPQ_LOCK_TIMEOUT (5)
+#define NMBD_INTERFACES_RELOAD (120)
+#define NMBD_UNEXPECTED_TIMEOUT (15)
+
+/* the following are in milliseconds */
+#define LOCK_RETRY_TIMEOUT (100)
+
+/* do you want to dump core (carefully!) when an internal error is
+   encountered? Samba will be careful to make the core file only
+   accessible to root */
+#define DUMP_CORE 1
+
+/* shall we support browse requests via a FIFO to nmbd? */
+#define ENABLE_FIFO 1
+
+/* how long (in miliseconds) to wait for a socket connect to happen */
+#define LONG_CONNECT_TIMEOUT 30000
+#define SHORT_CONNECT_TIMEOUT 5000
+
+/* the default netbios keepalive timeout */
+#define DEFAULT_KEEPALIVE 300
+
+/* the directory to sit in when idle */
+/* #define IDLE_DIR "/" */
+
+/* Timout (in seconds) to wait for an oplock break
+   message to return from the client. */
+
+#define OPLOCK_BREAK_TIMEOUT 30
+
+/* Timout (in seconds) to add to the oplock break timeout
+   to wait for the smbd to smbd message to return. */
+
+#define OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR 2
+
+/* the read preciction code has been disabled until some problems with
+   it are worked out */
+#define USE_READ_PREDICTION 0
+
+/*
+ * Default passwd chat script.
+ */
+
+#define DEFAULT_PASSWD_CHAT "*new*password* %n\\n *new*password* %n\\n *changed*"
+
+/* Minimum length of allowed password when changing UNIX password. */
+#define MINPASSWDLENGTH 5
+
+/* maximum ID number used for session control. This cannot be larger
+   than 62*62 for the current code */
+#define MAX_SESSION_ID 3000
+
+/* For the benifit of PAM and the 'session exec' scripts, we fake up a terminal
+   name. This can be in one of two forms:  The first for systems not using
+   utmp (and therefore not constrained as to length or the need for a number
+   < 3000 or so) and the second for systems with this 'well behaved terminal
+   like name' constraint.
+*/
+
+#ifndef SESSION_TEMPLATE
+/* Paramaters are 'pid' and 'vuid' */
+#define SESSION_TEMPLATE "smb/%lu/%d"
+#endif
+
+#ifndef SESSION_UTMP_TEMPLATE
+#define SESSION_UTMP_TEMPLATE "smb/%d"
+#endif
+
+/* the maximum age in seconds of a password. Should be a lp_ parameter */
+#define MAX_PASSWORD_AGE (21*24*60*60)
+
+/* Allocation roundup. */
+#define SMB_ROUNDUP_ALLOCATION_SIZE 0x100000
+
+/* shall we deny oplocks to clients that get timeouts? */
+#define FASCIST_OPLOCK_BACKOFF 1
+
+/* this enables the "rabbit pellet" fix for SMBwritebraw */
+#define RABBIT_PELLET_FIX 1
+
+/* Max number of jobs per print queue. */
+#define PRINT_MAX_JOBID 10000
+
+/* Max number of open RPC pipes. */
+#define MAX_OPEN_PIPES 2048
+
+/* Tuning for server auth mutex. */
+#define CLI_AUTH_TIMEOUT 5000 /* In milli-seconds. */
+#define NUM_CLI_AUTH_CONNECT_RETRIES 3
+/* Number in seconds to wait for the mutex. This must be less than 30 seconds. */
+#define SERVER_MUTEX_WAIT_TIME ( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)
+/* Number in seconds for winbindd to wait for the mutex. Make this 2 * smbd wait time. */
+#define WINBIND_SERVER_MUTEX_WAIT_TIME (( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)*2)
+
+/* Max number of simultaneous winbindd socket connections. */
+#define WINBINDD_MAX_SIMULTANEOUS_CLIENTS 200
+#endif
diff --git a/source4/include/mangle.h b/source4/include/mangle.h
new file mode 100644 (file)
index 0000000..769278d
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _MANGLE_H_
+#define _MANGLE_H_
+/*
+  header for 8.3 name mangling interface 
+*/
+
+struct mangle_fns {
+       BOOL (*is_mangled)(const char *s);
+       BOOL (*is_8_3)(const char *fname, BOOL check_case, BOOL allow_wildcards);
+       void (*reset)(void);
+       BOOL (*check_cache)(char *s);
+       void (*name_map)(char *OutName, BOOL need83, BOOL cache83);
+};
+#endif /* _MANGLE_H_ */
diff --git a/source4/include/mapping.h b/source4/include/mapping.h
new file mode 100644 (file)
index 0000000..d4f2d28
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2001.
+ *  
+ *  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.
+ */
+
+#define PRIV_ALL_INDEX         5
+
+#define SE_PRIV_NONE           0x0000
+#define SE_PRIV_ADD_MACHINES   0x0006
+#define SE_PRIV_SEC_PRIV       0x0008
+#define SE_PRIV_TAKE_OWNER     0x0009
+#define SE_PRIV_ADD_USERS      0xff01
+#define SE_PRIV_PRINT_OPERATOR 0xff03
+#define SE_PRIV_ALL            0xffff
+
+#define ENUM_ONLY_MAPPED True
+#define ENUM_ALL_MAPPED False
+
+#define MAPPING_WITH_PRIV True
+#define MAPPING_WITHOUT_PRIV False
+
+#define PR_NONE                0x0000
+#define PR_LOG_ON_LOCALLY      0x0001
+#define PR_ACCESS_FROM_NETWORK 0x0002
+#define PR_LOG_ON_BATCH_JOB    0x0004
+#define PR_LOG_ON_SERVICE      0x0010
+
+
+typedef struct _GROUP_MAP {
+       struct pdb_methods *methods;
+       gid_t gid;
+       DOM_SID sid;
+       enum SID_NAME_USE sid_name_use;
+       fstring nt_name;
+       fstring comment;
+       uint32 systemaccount;
+       PRIVILEGE_SET priv_set;
+} GROUP_MAP;
+
+typedef struct _PRIVS {
+       uint32 se_priv;
+       const char *priv;
+       const char *description;
+} PRIVS;
+
diff --git a/source4/include/md5.h b/source4/include/md5.h
new file mode 100644 (file)
index 0000000..6665171
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef MD5_H
+#define MD5_H
+#ifndef HEADER_MD5_H
+/* Try to avoid clashes with OpenSSL */
+#define HEADER_MD5_H 
+#endif
+
+struct MD5Context {
+       uint32 buf[4];
+       uint32 bits[2];
+       unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/source4/include/messages.h b/source4/include/messages.h
new file mode 100644 (file)
index 0000000..ce167a7
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+   Unix SMB/CIFS implementation.
+   messages.c header
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) 2001, 2002 by Martin Pool
+   
+   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.
+*/
+
+#ifndef _MESSAGES_H_
+#define _MESSAGES_H_
+
+/* general messages */
+#define MSG_DEBUG              1
+#define MSG_PING               2
+#define MSG_PONG               3
+#define MSG_PROFILE            4
+#define MSG_REQ_DEBUGLEVEL     5
+#define MSG_DEBUGLEVEL         6
+#define MSG_REQ_PROFILELEVEL   7
+#define MSG_PROFILELEVEL       8
+#define MSG_REQ_POOL_USAGE     9
+#define MSG_POOL_USAGE         10
+
+/* If dmalloc is included, set a steady-state mark */
+#define MSG_REQ_DMALLOC_MARK   11
+
+/* If dmalloc is included, dump to the dmalloc log a description of
+ * what has changed since the last MARK */
+#define MSG_REQ_DMALLOC_LOG_CHANGED    12
+
+#define MSG_SHUTDOWN           13
+
+/* Dump out the talloc useage. */
+#define MSG_REQ_TALLOC_USAGE      14
+#define MSG_TALLOC_USAGE          15
+
+/* nmbd messages */
+#define MSG_FORCE_ELECTION 1001
+#define MSG_WINS_NEW_ENTRY 1002
+
+/* printing messages */
+/* #define MSG_PRINTER_NOTIFY  2001*/ /* Obsolete */
+#define MSG_PRINTER_DRVUPGRADE 2002
+#define MSG_PRINTER_NOTIFY2     2003
+#define MSG_PRINTERDATA_INIT_RESET     2004
+
+/* smbd messages */
+#define MSG_SMB_CONF_UPDATED 3001
+#define MSG_SMB_FORCE_TDIS   3002
+#define MSG_SMB_SAM_SYNC     3003
+#define MSG_SMB_SAM_REPL     3004
+#define MSG_SMB_UNLOCK       3005
+
+/* Flags to classify messages - used in message_send_all() */
+/* Sender will filter by flag. */
+
+#define FLAG_MSG_GENERAL       0x0001
+#define FLAG_MSG_SMBD          0x0002
+#define FLAG_MSG_NMBD          0x0004
+#define FLAG_MSG_PRINTING      0x0008
+
+#endif
diff --git a/source4/include/msdfs.h b/source4/include/msdfs.h
new file mode 100644 (file)
index 0000000..1bfff9a
--- /dev/null
@@ -0,0 +1,77 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   MSDfs services for Samba
+   Copyright (C) Shirish Kalele 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.
+   
+*/
+
+#ifndef _MSDFS_H
+#define _MSDFS_H
+
+#define REFERRAL_TTL 600
+
+/* Flags used in trans2 Get Referral reply */
+#define DFSREF_REFERRAL_SERVER 0x1
+#define DFSREF_STORAGE_SERVER  0x2
+
+/* Referral sizes */
+#define VERSION2_REFERRAL_SIZE 0x16
+#define VERSION3_REFERRAL_SIZE 0x22
+#define REFERRAL_HEADER_SIZE 0x08
+
+/* Maximum number of referrals for each Dfs volume */
+#define MAX_REFERRAL_COUNT   256
+
+struct referral
+{
+       pstring alternate_path; /* contains the path referred */
+       uint32 proximity;
+       uint32 ttl; /* how long should client cache referral */
+};
+
+struct junction_map
+{
+  pstring service_name;
+  pstring volume_name;
+  int referral_count;
+  struct referral* referral_list;
+};
+
+struct dfs_path
+{
+       pstring hostname;
+       pstring servicename;
+       pstring reqpath;
+};
+
+#define RESOLVE_DFSPATH(name, conn, inbuf, outbuf)             \
+{ if ((SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) &&         \
+      lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&          \
+      dfs_redirect(name,conn,False))                           \
+             return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED,     \
+                              ERRSRV, ERRbadpath);; }  
+                               
+#define RESOLVE_FINDFIRST_DFSPATH(name, conn, inbuf, outbuf)           \
+{ if ( (SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) ||                \
+       ((get_remote_arch() == RA_WIN95) && lp_msdfs_root(SNUM(conn))) )        \
+        if (lp_host_msdfs() && dfs_redirect(name,conn,True))           \
+                return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED,          \
+                                  ERRSRV, ERRbadpath);; }          
+
+#endif /* _MSDFS_H */
diff --git a/source4/include/mutex.h b/source4/include/mutex.h
new file mode 100644 (file)
index 0000000..c3e146d
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef _MUTEX_H_
+#define _MUTEX_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Samba mutex functions
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers 2003
+   
+   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.
+*/
+
+/* To add a new mutex, add it to enum mutex_id
+ */
+enum mutex_id { MUTEX_SMBD,            /* global smbd lock */
+               MUTEX_TALLOC,           /* global talloc.c lock */
+               MUTEX_DEBUG,            /* global debug.c lock */
+               MUTEX_TANK,             /* vfs_tank lock */
+
+               MUTEX_MAX /* this MUST be kept last */
+};
+
+/* To add a new read/write lock, add it to enum rwlock_id
+ */
+enum rwlock_id { RWLOCK_SMBD,          /* global smbd lock */
+
+               RWLOCK_MAX /* this MUST be kept last */
+};
+
+#define MUTEX_LOCK_BY_ID(mutex_index) mutex_lock_by_id(mutex_index, #mutex_index)
+#define MUTEX_UNLOCK_BY_ID(mutex_index) mutex_unlock_by_id(mutex_index, #mutex_index)
+#define MUTEX_INIT(mutex, name) mutex_init(mutex, #name)
+#define MUTEX_DESTROY(mutex, name) mutex_destroy(mutex, #name)
+#define MUTEX_LOCK(mutex, name) mutex_lock(mutex, #name)
+#define MUTEX_UNLOCK(mutex, name) mutex_unlock(mutex, #name)
+
+#define RWLOCK_INIT(rwlock, name) rwlock_init(rwlock, #name)
+#define RWLOCK_DESTROY(rwlock, name) rwlock_destroy(rwlock, #name)
+#define RWLOCK_LOCK_WRITE(rwlock, name) rwlock_lock_write(rwlock, #name)
+#define RWLOCK_LOCK_READ(rwlock, name) rwlock_lock_read(rwlock, #name)
+#define RWLOCK_UNLOCK(rwlock, name) rwlock_unlock(rwlock, #name)
+
+
+
+/* this null typedef ensures we get the types right and avoids the
+   pitfalls of void* */
+typedef struct {
+       void *mutex;
+} mutex_t;
+typedef struct {
+       void *rwlock;
+} rwlock_t;
+
+/* the mutex model operations structure - contains function pointers to 
+   the model-specific implementations of each operation */
+struct mutex_ops {
+       int (*mutex_init)(mutex_t *mutex, const char *name);
+       int (*mutex_lock)(mutex_t *mutex, const char *name);
+       int (*mutex_unlock)(mutex_t *mutex, const char *name);
+       int (*mutex_destroy)(mutex_t *mutex, const char *name);
+       int (*rwlock_init)(rwlock_t *rwlock, const char *name);
+       int (*rwlock_lock_write)(rwlock_t *rwlock, const char *name);
+       int (*rwlock_lock_read)(rwlock_t *rwlock, const char *name);
+       int (*rwlock_unlock)(rwlock_t *rwlock, const char *name);
+       int (*rwlock_destroy)(rwlock_t *rwlock, const char *name);
+};
+
+#endif /* ndef _MUTEX_H_ */
diff --git a/source4/include/nameserv.h b/source4/include/nameserv.h
new file mode 100644 (file)
index 0000000..7611fdf
--- /dev/null
@@ -0,0 +1,644 @@
+#ifndef _NAMESERV_H_
+#define _NAMESERV_H_
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios header - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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.
+   
+*/
+
+#define INFO_VERSION   "INFO/version"
+#define INFO_COUNT     "INFO/num_entries"
+#define INFO_ID_HIGH   "INFO/id_high"
+#define INFO_ID_LOW    "INFO/id_low"
+#define ENTRY_PREFIX   "ENTRY/"
+
+#define PERMANENT_TTL 0
+
+/* NTAS uses 2, NT uses 1, WfWg uses 0 */
+#define MAINTAIN_LIST    2
+#define ELECTION_VERSION 1
+
+#define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */
+#define MIN_DGRAM_SIZE 12
+
+/*********************************************************
+ Types of reply packet.
+**********************************************************/
+
+enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH,
+                               NMB_REL, NMB_WAIT_ACK, NMB_MULTIHOMED_REG,
+                               WINS_REG, WINS_QUERY };
+
+/* From rfc1002, 4.2.1.2 */
+/* Question types. */
+#define QUESTION_TYPE_NB_QUERY  0x20
+#define QUESTION_TYPE_NB_STATUS 0x21
+
+/* Question class */
+#define QUESTION_CLASS_IN  0x1
+
+/* Opcode definitions */
+#define NMB_NAME_QUERY_OPCODE       0x0
+#define NMB_NAME_REG_OPCODE         0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */
+#define NMB_NAME_RELEASE_OPCODE     0x06 /* see rfc1002.txt 4.2.9,10,11 */
+#define NMB_WACK_OPCODE             0x07 /* see rfc1002.txt 4.2.16 */
+/* Ambiguity in rfc1002 about which of these is correct. */
+/* WinNT uses 8 by default but can be made to use 9. */
+#define NMB_NAME_REFRESH_OPCODE_8   0x08 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_REFRESH_OPCODE_9   0x09 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_MULTIHOMED_REG_OPCODE 0x0F /* Invented by Microsoft. */
+
+/* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */
+
+/* Resource record types. rfc1002 4.2.1.3 */
+#define RR_TYPE_A                  0x1
+#define RR_TYPE_NS                 0x2
+#define RR_TYPE_NULL               0xA
+#define RR_TYPE_NB                0x20
+#define RR_TYPE_NBSTAT            0x21
+
+/* Resource record class. */
+#define RR_CLASS_IN                0x1
+
+/* NetBIOS flags */
+#define NB_GROUP  0x80
+#define NB_PERM   0x02
+#define NB_ACTIVE 0x04
+#define NB_CONFL  0x08
+#define NB_DEREG  0x10
+#define NB_BFLAG  0x00 /* Broadcast node type. */
+#define NB_PFLAG  0x20 /* Point-to-point node type. */
+#define NB_MFLAG  0x40 /* Mixed bcast & p-p node type. */
+#define NB_HFLAG  0x60 /* Microsoft 'hybrid' node type. */
+#define NB_NODETYPEMASK 0x60
+/* Mask applied to outgoing NetBIOS flags. */
+#define NB_FLGMSK 0xE0
+
+/* The wins flags. Looks like the nbflags ! */
+#define WINS_UNIQUE    0x00 /* Unique record */
+#define WINS_NGROUP    0x01 /* Normal Group eg: 1B */
+#define WINS_SGROUP    0x02 /* Special Group eg: 1C */
+#define WINS_MHOMED    0x03 /* MultiHomed */
+
+#define WINS_ACTIVE    0x00 /* active record */
+#define WINS_RELEASED  0x04 /* released record */
+#define WINS_TOMBSTONED 0x08 /* tombstoned record */
+#define WINS_DELETED   0x0C /* deleted record */
+
+#define WINS_STATE_MASK        0x0C
+
+#define WINS_LOCAL     0x00 /* local record */
+#define WINS_REMOTE    0x10 /* remote record */
+
+#define WINS_BNODE     0x00 /* Broadcast node */
+#define WINS_PNODE     0x20 /* PtP node */
+#define WINS_MNODE     0x40 /* Mixed node */
+#define WINS_HNODE     0x60 /* Hybrid node */
+
+#define WINS_NONSTATIC 0x00 /* dynamic record */
+#define WINS_STATIC    0x80 /* static record */
+
+#define WINS_STATE_ACTIVE(p) (((p)->data.wins_flags & WINS_STATE_MASK) == WINS_ACTIVE)
+
+
+/* NetBIOS flag identifier. */
+#define NAME_GROUP(p)  ((p)->data.nb_flags & NB_GROUP)
+#define NAME_BFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_BFLAG)
+#define NAME_PFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_PFLAG)
+#define NAME_MFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_MFLAG)
+#define NAME_HFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_HFLAG)
+
+/* Samba name state for a name in a namelist. */
+#define NAME_IS_ACTIVE(p)        ((p)->data.nb_flags & NB_ACTIVE)
+#define NAME_IN_CONFLICT(p)      ((p)->data.nb_flags & NB_CONFL)
+#define NAME_IS_DEREGISTERING(p) ((p)->data.nb_flags & NB_DEREG)
+
+/* Error codes for NetBIOS requests. */
+#define FMT_ERR   0x1       /* Packet format error. */
+#define SRV_ERR   0x2       /* Internal server error. */
+#define NAM_ERR   0x3       /* Name does not exist. */
+#define IMP_ERR   0x4       /* Request not implemented. */
+#define RFS_ERR   0x5       /* Request refused. */
+#define ACT_ERR   0x6       /* Active error - name owned by another host. */
+#define CFT_ERR   0x7       /* Name in conflict error. */
+
+#define REFRESH_TIME (15*60)
+#define NAME_POLL_REFRESH_TIME (5*60)
+#define NAME_POLL_INTERVAL 15
+
+/* Workgroup state identifiers. */
+#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL)
+#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER)
+#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST)
+#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV)
+
+/* Microsoft browser NetBIOS name. */
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+
+/* Mail slots. */
+#define BROWSE_MAILSLOT    "\\MAILSLOT\\BROWSE"
+#define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON"
+#define NT_LOGON_MAILSLOT  "\\MAILSLOT\\NET\\NTLOGON"
+#define LANMAN_MAILSLOT    "\\MAILSLOT\\LANMAN"
+
+/* Samba definitions for find_name_on_subnet(). */
+#define FIND_ANY_NAME   0
+#define FIND_SELF_NAME  1
+
+/*
+ * The different name types that can be in namelists.
+ *
+ * SELF_NAME should only be on the broadcast and unicast subnets.
+ * LMHOSTS_NAME should only be in the remote_broadcast_subnet.
+ * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet.
+ * WINS_PROXY_NAME should only be on the broadcast subnets.
+ * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet.
+ *
+ */
+
+enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME, 
+                  DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME};
+enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3};
+enum packet_type {NMB_PACKET, DGRAM_PACKET};
+
+enum master_state
+{
+  MST_NONE,
+  MST_POTENTIAL,
+  MST_BACKUP,
+  MST_MSB,
+  MST_BROWSER,
+  MST_UNBECOMING_MASTER
+};
+
+enum domain_state
+{
+  DOMAIN_NONE,
+  DOMAIN_WAIT,
+  DOMAIN_MST
+};
+
+enum logon_state
+{
+  LOGON_NONE,
+  LOGON_WAIT,
+  LOGON_SRV
+};
+
+struct subnet_record;
+
+struct nmb_data
+{
+  uint16 nb_flags;         /* Netbios flags. */
+  int num_ips;             /* Number of ip entries. */
+  struct in_addr *ip;      /* The ip list for this name. */
+
+  enum name_source source; /* Where the name came from. */
+
+  time_t death_time; /* The time the record must be removed (do not remove if 0). */
+  time_t refresh_time; /* The time the record should be refreshed. */
+  
+  SMB_BIG_UINT id;             /* unique id */
+  struct in_addr wins_ip;      /* the adress of the wins server this record comes from */
+
+  int wins_flags;              /* similar to the netbios flags but different ! */
+};
+
+/* This structure represents an entry in a local netbios name list. */
+struct name_record
+  {
+#if 0
+  ubi_trNode            node[1];
+#endif
+  struct subnet_record *subnet;
+  struct nmb_name       name;    /* The netbios name. */
+  struct nmb_data       data;    /* The netbios data. */
+  };
+
+/* Browser cache for synchronising browse lists. */
+struct browse_cache_record
+  {
+#if 0
+  ubi_dlNode     node[1];
+#endif
+  pstring        lmb_name;
+  pstring        work_group;
+  struct in_addr ip;
+  time_t         sync_time;
+  time_t         death_time; /* The time the record must be removed. */
+  };
+
+/* This is used to hold the list of servers in my domain, and is
+   contained within lists of domains. */
+
+struct server_record
+{
+  struct server_record *next;
+  struct server_record *prev;
+
+  struct subnet_record *subnet;
+
+  struct server_info_struct serv;
+  time_t death_time;  
+};
+
+/* A workgroup structure. It contains a list of servers. */
+struct work_record
+{
+  struct work_record *next;
+  struct work_record *prev;
+
+  struct subnet_record *subnet;
+
+  struct server_record *serverlist;
+
+  /* Stage of development from non-local-master up to local-master browser. */
+  enum master_state mst_state;
+
+  /* Stage of development from non-domain-master to domain-master browser. */
+  enum domain_state dom_state;
+
+  /* Stage of development from non-logon-server to logon server. */
+  enum logon_state log_state;
+
+  /* Work group info. */
+  fstring work_group;
+  int     token;        /* Used when communicating with backup browsers. */
+  fstring local_master_browser_name;      /* Current local master browser. */
+
+  /* Announce info. */
+  time_t lastannounce_time;
+  int announce_interval;
+  BOOL    needannounce;
+
+  /* Timeout time for this workgroup. 0 means permanent. */
+  time_t death_time;  
+
+  /* Election info */
+  BOOL    RunningElection;
+  BOOL    needelection;
+  int     ElectionCount;
+  uint32  ElectionCriterion;
+
+  /* Domain master browser info. Used for efficient syncs. */
+  struct nmb_name dmb_name;
+  struct in_addr dmb_addr;
+};
+
+/* typedefs needed to define copy & free functions for userdata. */
+struct userdata_struct;
+
+typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *);
+typedef void (*userdata_free_fn)(struct userdata_struct *);
+
+/* Structure to define any userdata passed around. */
+
+struct userdata_struct {
+  userdata_copy_fn copy_fn;
+  userdata_free_fn free_fn;
+  unsigned int userdata_len;
+  char data[16]; /* 16 is to ensure alignment/padding on all systems */
+};
+
+struct response_record;
+struct packet_struct;
+struct res_rec;
+
+/* typedef to define the function called when this response packet comes in. */
+typedef void (*response_function)(struct subnet_record *, struct response_record *,
+                                  struct packet_struct *);
+
+/* typedef to define the function called when this response record times out. */
+typedef void (*timeout_response_function)(struct subnet_record *,
+                                          struct response_record *);
+
+/* typedef to define the function called when the request that caused this
+   response record to be created is successful. */
+typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...);
+
+/* typedef to define the function called when the request that caused this
+   response record to be created is unsuccessful. */
+typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...);
+
+/* List of typedefs for success and fail functions of the different query
+   types. Used to catch any compile time prototype errors. */
+
+typedef void (*register_name_success_function)( struct subnet_record *,
+                                                struct userdata_struct *,
+                                                struct nmb_name *,
+                                                uint16,
+                                                int,
+                                                struct in_addr);
+typedef void (*register_name_fail_function)( struct subnet_record *,
+                                             struct response_record *,
+                                             struct nmb_name *);
+
+typedef void (*release_name_success_function)( struct subnet_record *,
+                                               struct userdata_struct *, 
+                                               struct nmb_name *,
+                                               struct in_addr);
+typedef void (*release_name_fail_function)( struct subnet_record *,
+                                            struct response_record *, 
+                                            struct nmb_name *);
+
+typedef void (*refresh_name_success_function)( struct subnet_record *,
+                                               struct userdata_struct *, 
+                                               struct nmb_name *,
+                                               uint16,
+                                               int,
+                                               struct in_addr);
+typedef void (*refresh_name_fail_function)( struct subnet_record *,
+                                            struct response_record *,
+                                            struct nmb_name *);
+
+typedef void (*query_name_success_function)( struct subnet_record *,
+                                             struct userdata_struct *,
+                                             struct nmb_name *,
+                                             struct in_addr,
+                                             struct res_rec *answers);
+
+typedef void (*query_name_fail_function)( struct subnet_record *,
+                                          struct response_record *,    
+                                          struct nmb_name *,
+                                          int);  
+
+typedef void (*node_status_success_function)( struct subnet_record *,
+                                              struct userdata_struct *,
+                                              struct res_rec *,
+                                              struct in_addr);
+typedef void (*node_status_fail_function)( struct subnet_record *,
+                                           struct response_record *);
+
+/* Initiated name queries are recorded in this list to track any responses. */
+
+struct response_record
+{
+  struct response_record *next;
+  struct response_record *prev;
+
+  uint16 response_id;
+
+  /* Callbacks for packets received or not. */ 
+  response_function resp_fn;
+  timeout_response_function timeout_fn;
+
+  /* Callbacks for the request succeeding or not. */
+  success_function success_fn;
+  fail_function fail_fn;
+  struct packet_struct *packet;
+
+  struct userdata_struct *userdata;
+
+  int num_msgs;
+
+  time_t repeat_time;
+  time_t repeat_interval;
+  int    repeat_count;
+
+  /* Recursion protection. */
+  BOOL in_expiration_processing;
+};
+
+/* A subnet structure. It contains a list of workgroups and netbios names. */
+
+/*
+   B nodes will have their own, totally separate subnet record, with their
+   own netbios name set. These do NOT interact with other subnet records'
+   netbios names.
+*/
+
+enum subnet_type {
+  NORMAL_SUBNET              = 0,  /* Subnet listed in interfaces list. */
+  UNICAST_SUBNET             = 1,  /* Subnet for unicast packets. */
+  REMOTE_BROADCAST_SUBNET    = 2,  /* Subnet for remote broadcasts. */
+  WINS_SERVER_SUBNET         = 3   /* Only created if we are a WINS server. */
+};
+
+struct subnet_record
+{
+  struct subnet_record *next;
+  struct subnet_record *prev;
+
+  char  *subnet_name;      /* For Debug identification. */
+  enum subnet_type type;   /* To catagorize the subnet. */
+
+  struct work_record     *workgrouplist; /* List of workgroups. */
+#if 0
+  ubi_trRoot              namelist[1];   /* List of netbios names. */
+#endif
+  struct response_record *responselist;  /* List of responses expected. */
+
+  BOOL namelist_changed;
+  BOOL work_changed;
+
+  struct in_addr bcast_ip;
+  struct in_addr mask_ip;
+  struct in_addr myip;
+  int nmb_sock;               /* socket to listen for unicast 137. */
+  int dgram_sock;             /* socket to listen for unicast 138. */
+};
+
+/* A resource record. */
+struct res_rec {
+  struct nmb_name rr_name;
+  int rr_type;
+  int rr_class;
+  int ttl;
+  int rdlength;
+  char rdata[MAX_DGRAM_SIZE];
+};
+
+/* Define these so we can pass info back to caller of name_query */
+#define NM_FLAGS_RS 0x80 /* Response. Cheat     */
+#define NM_FLAGS_AA 0x40 /* Authoritative       */
+#define NM_FLAGS_TC 0x20 /* Truncated           */
+#define NM_FLAGS_RD 0x10 /* Recursion Desired   */
+#define NM_FLAGS_RA 0x08 /* Recursion Available */
+#define NM_FLAGS_B  0x01 /* Broadcast           */
+
+/* An nmb packet. */
+struct nmb_packet
+{
+  struct {
+    int name_trn_id;
+    int opcode;
+    BOOL response;
+    struct {
+      BOOL bcast;
+      BOOL recursion_available;
+      BOOL recursion_desired;
+      BOOL trunc;
+      BOOL authoritative;
+    } nm_flags;
+    int rcode;
+    int qdcount;
+    int ancount;
+    int nscount;
+    int arcount;
+  } header;
+
+  struct {
+    struct nmb_name question_name;
+    int question_type;
+    int question_class;
+  } question;
+
+  struct res_rec *answers;
+  struct res_rec *nsrecs;
+  struct res_rec *additional;
+};
+
+/* msg_type field options - from rfc1002. */
+
+#define DGRAM_UNIQUE 0x10
+#define DGRAM_GROUP 0x11
+#define DGRAM_BROADCAST 0x12
+#define DGRAM_ERROR 0x13
+#define DGRAM_QUERY_REQUEST 0x14
+#define DGRAM_POSITIVE_QUERY_RESPONSE 0x15
+#define DGRAM_NEGATIVE_QUERT_RESPONSE 0x16
+
+/* A datagram - this normally contains SMB data in the data[] array. */
+
+struct dgram_packet {
+  struct {
+    int msg_type;
+    struct {
+      enum node_type node_type;
+      BOOL first;
+      BOOL more;
+    } flags;
+    int dgm_id;
+    struct in_addr source_ip;
+    int source_port;
+    int dgm_length;
+    int packet_offset;
+  } header;
+  struct nmb_name source_name;
+  struct nmb_name dest_name;
+  int datasize;
+  char data[MAX_DGRAM_SIZE];
+};
+
+/* Define a structure used to queue packets. This will be a linked
+ list of nmb packets. */
+
+struct packet_struct
+{
+  struct packet_struct *next;
+  struct packet_struct *prev;
+  BOOL locked;
+  struct in_addr ip;
+  int port;
+  int fd;
+  time_t timestamp;
+  enum packet_type packet_type;
+  union {
+    struct nmb_packet nmb;
+    struct dgram_packet dgram;
+  } packet;
+};
+
+/* NETLOGON opcodes */
+
+#define QUERYFORPDC     7 /* Query for PDC. */
+#define SAM_UAS_CHANGE  10 /* Announce change to UAS or SAM. */
+#define QUERYFORPDC_R  12 /* Response to Query for PDC. */
+#define SAMLOGON       18
+#define SAMLOGON_R     19
+#define SAMLOGON_UNK_R 21
+#define SAMLOGON_AD_UNK_R 23
+#define SAMLOGON_AD_R   25
+
+/* Ids for netbios packet types. */
+
+#define ANN_HostAnnouncement         1
+#define ANN_AnnouncementRequest      2
+#define ANN_Election                 8
+#define ANN_GetBackupListReq         9
+#define ANN_GetBackupListResp       10
+#define ANN_BecomeBackup            11
+#define ANN_DomainAnnouncement      12
+#define ANN_MasterAnnouncement      13
+#define ANN_ResetBrowserState       14
+#define ANN_LocalMasterAnnouncement 15
+
+
+/* Broadcast packet announcement intervals, in minutes. */
+
+/* Attempt to add domain logon and domain master names. */
+#define CHECK_TIME_ADD_DOM_NAMES 5 
+
+/* Search for master browsers of workgroups samba knows about, 
+   except default. */
+#define CHECK_TIME_MST_BROWSE       5 
+
+/* Request backup browser announcements from other servers. */
+#define CHECK_TIME_ANNOUNCE_BACKUP 15
+
+/* Request host announcements from other servers: min and max of interval. */
+#define CHECK_TIME_MIN_HOST_ANNCE   3
+#define CHECK_TIME_MAX_HOST_ANNCE  12
+
+/* Announce as master to WINS server and any Primary Domain Controllers. */
+#define CHECK_TIME_MST_ANNOUNCE    15
+
+/* Time between syncs from domain master browser to local master browsers. */
+#define CHECK_TIME_DMB_TO_LMB_SYNC    15
+
+/* Do all remote announcements this often. */
+#define REMOTE_ANNOUNCE_INTERVAL 180
+
+/* what is the maximum period between name refreshes. Note that this only
+   affects non-permanent self names (in seconds) */
+#define MAX_REFRESH_TIME (60*20)
+
+/* The Extinction interval: 4 days, time a node will stay in released state  */
+#define EXTINCTION_INTERVAL (4*24*60*60)
+
+/* The Extinction time-out: 1 day, time a node will stay in deleted state */
+#define EXTINCTION_TIMEOUT (24*60*60)
+
+/* Macro's to enumerate subnets either with or without
+   the UNICAST subnet. */
+
+extern struct subnet_record *subnetlist;
+extern struct subnet_record *unicast_subnet;
+extern struct subnet_record *wins_server_subnet;
+extern struct subnet_record *remote_broadcast_subnet;
+
+#define FIRST_SUBNET subnetlist
+#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next)
+#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x)))
+
+/* wins replication record used between nmbd and wrepld */
+typedef struct _WINS_RECORD {
+       char name[17];
+       char type;
+       int nb_flags;
+       int wins_flags;
+       SMB_BIG_UINT id;
+       int num_ips;
+       struct in_addr ip[25];
+       struct in_addr wins_ip;
+} WINS_RECORD;
+
+/* To be removed. */
+enum state_type { TEST };
+#endif /* _NAMESERV_H_ */
diff --git a/source4/include/nt_printing.h b/source4/include/nt_printing.h
new file mode 100644 (file)
index 0000000..ca65a40
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell              1992-2000,
+   Copyright (C) Jean Francois Micouleau      1998-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.
+*/
+
+#ifndef NT_PRINTING_H_
+#define NT_PRINTING_H_
+
+#define ORIENTATION      0x00000001L
+#define PAPERSIZE        0x00000002L
+#define PAPERLENGTH      0x00000004L
+#define PAPERWIDTH       0x00000008L
+#define SCALE            0x00000010L
+#define COPIES           0x00000100L
+#define DEFAULTSOURCE    0x00000200L
+#define PRINTQUALITY     0x00000400L
+#define COLOR            0x00000800L
+#define DUPLEX           0x00001000L
+#define YRESOLUTION      0x00002000L
+#define TTOPTION         0x00004000L
+#define COLLATE          0x00008000L
+#define FORMNAME         0x00010000L
+#define LOGPIXELS        0x00020000L
+#define BITSPERPEL       0x00040000L
+#define PELSWIDTH        0x00080000L
+#define PELSHEIGHT       0x00100000L
+#define DISPLAYFLAGS     0x00200000L
+#define DISPLAYFREQUENCY 0x00400000L
+#define PANNINGWIDTH     0x00800000L
+#define PANNINGHEIGHT    0x01000000L
+
+#define ORIENT_PORTRAIT   1
+#define ORIENT_LANDSCAPE  2
+
+#define PAPER_FIRST                PAPER_LETTER
+#define PAPER_LETTER               1  /* Letter 8 1/2 x 11 in               */
+#define PAPER_LETTERSMALL          2  /* Letter Small 8 1/2 x 11 in         */
+#define PAPER_TABLOID              3  /* Tabloid 11 x 17 in                 */
+#define PAPER_LEDGER               4  /* Ledger 17 x 11 in                  */
+#define PAPER_LEGAL                5  /* Legal 8 1/2 x 14 in                */
+#define PAPER_STATEMENT            6  /* Statement 5 1/2 x 8 1/2 in         */
+#define PAPER_EXECUTIVE            7  /* Executive 7 1/4 x 10 1/2 in        */
+#define PAPER_A3                   8  /* A3 297 x 420 mm                    */
+#define PAPER_A4                   9  /* A4 210 x 297 mm                    */
+#define PAPER_A4SMALL             10  /* A4 Small 210 x 297 mm              */
+#define PAPER_A5                  11  /* A5 148 x 210 mm                    */
+#define PAPER_B4                  12  /* B4 (JIS) 250 x 354                 */
+#define PAPER_B5                  13  /* B5 (JIS) 182 x 257 mm              */
+#define PAPER_FOLIO               14  /* Folio 8 1/2 x 13 in                */
+#define PAPER_QUARTO              15  /* Quarto 215 x 275 mm                */
+#define PAPER_10X14               16  /* 10x14 in                           */
+#define PAPER_11X17               17  /* 11x17 in                           */
+#define PAPER_NOTE                18  /* Note 8 1/2 x 11 in                 */
+#define PAPER_ENV_9               19  /* Envelope #9 3 7/8 x 8 7/8          */
+#define PAPER_ENV_10              20  /* Envelope #10 4 1/8 x 9 1/2         */
+#define PAPER_ENV_11              21  /* Envelope #11 4 1/2 x 10 3/8        */
+#define PAPER_ENV_12              22  /* Envelope #12 4 \276 x 11           */
+#define PAPER_ENV_14              23  /* Envelope #14 5 x 11 1/2            */
+#define PAPER_CSHEET              24  /* C size sheet                       */
+#define PAPER_DSHEET              25  /* D size sheet                       */
+#define PAPER_ESHEET              26  /* E size sheet                       */
+#define PAPER_ENV_DL              27  /* Envelope DL 110 x 220mm            */
+#define PAPER_ENV_C5              28  /* Envelope C5 162 x 229 mm           */
+#define PAPER_ENV_C3              29  /* Envelope C3  324 x 458 mm          */
+#define PAPER_ENV_C4              30  /* Envelope C4  229 x 324 mm          */
+#define PAPER_ENV_C6              31  /* Envelope C6  114 x 162 mm          */
+#define PAPER_ENV_C65             32  /* Envelope C65 114 x 229 mm          */
+#define PAPER_ENV_B4              33  /* Envelope B4  250 x 353 mm          */
+#define PAPER_ENV_B5              34  /* Envelope B5  176 x 250 mm          */
+#define PAPER_ENV_B6              35  /* Envelope B6  176 x 125 mm          */
+#define PAPER_ENV_ITALY           36  /* Envelope 110 x 230 mm              */
+#define PAPER_ENV_MONARCH         37  /* Envelope Monarch 3.875 x 7.5 in    */
+#define PAPER_ENV_PERSONAL        38  /* 6 3/4 Envelope 3 5/8 x 6 1/2 in    */
+#define PAPER_FANFOLD_US          39  /* US Std Fanfold 14 7/8 x 11 in      */
+#define PAPER_FANFOLD_STD_GERMAN  40  /* German Std Fanfold 8 1/2 x 12 in   */
+#define PAPER_FANFOLD_LGL_GERMAN  41  /* German Legal Fanfold 8 1/2 x 13 in */
+
+#define PAPER_LAST                PAPER_FANFOLD_LGL_GERMAN
+#define PAPER_USER                256
+
+#define BIN_FIRST         BIN_UPPER
+#define BIN_UPPER         1
+#define BIN_ONLYONE       1
+#define BIN_LOWER         2
+#define BIN_MIDDLE        3
+#define BIN_MANUAL        4
+#define BIN_ENVELOPE      5
+#define BIN_ENVMANUAL     6
+#define BIN_AUTO          7
+#define BIN_TRACTOR       8
+#define BIN_SMALLFMT      9
+#define BIN_LARGEFMT      10
+#define BIN_LARGECAPACITY 11
+#define BIN_CASSETTE      14
+#define BIN_FORMSOURCE    15
+#define BIN_LAST          BIN_FORMSOURCE
+
+#define BIN_USER          256     /* device specific bins start here */
+
+#define RES_DRAFT         (-1)
+#define RES_LOW           (-2)
+#define RES_MEDIUM        (-3)
+#define RES_HIGH          (-4)
+
+#define COLOR_MONOCHROME  1
+#define COLOR_COLOR       2
+
+#define DUP_SIMPLEX    1
+#define DUP_VERTICAL   2
+#define DUP_HORIZONTAL 3
+
+#define TT_BITMAP     1       /* print TT fonts as graphics */
+#define TT_DOWNLOAD   2       /* download TT fonts as soft fonts */
+#define TT_SUBDEV     3       /* substitute device fonts for TT fonts */
+
+#define COLLATE_FALSE  0
+#define COLLATE_TRUE   1
+
+typedef struct nt_printer_driver_info_level_3
+{
+       uint32 cversion;
+
+       fstring name;
+       fstring environment;
+       fstring driverpath;
+       fstring datafile;
+       fstring configfile;
+       fstring helpfile;
+       fstring monitorname;
+       fstring defaultdatatype;
+       fstring *dependentfiles;
+} NT_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+       uint32  version;
+       fstring name;
+       fstring environment;
+       fstring driverpath;
+       fstring datafile;
+       fstring configfile;
+       fstring helpfile;
+       fstring monitorname;
+       fstring defaultdatatype;
+       fstring mfgname;
+       fstring oemurl;
+       fstring hardwareid;
+       fstring provider;
+       fstring *dependentfiles;
+       fstring *previousnames;
+} NT_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct nt_printer_driver_info_level
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+       NT_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+} NT_PRINTER_DRIVER_INFO_LEVEL;
+
+/* predefined registry key names for printer data */
+
+#define SPOOL_PRINTERDATA_KEY          "PrinterDriverData"
+#define SPOOL_DSSPOOLER_KEY            "DsSpooler"
+#define SPOOL_DSDRIVER_KEY             "DsDriver"
+#define SPOOL_DSUSER_KEY               "DsUser"
+#define SPOOL_PNPDATA_KEY              "PnPData"
+#define SPOOL_OID_KEY                  "OID"
+
+/* predefined value names for printer data */
+#define SPOOL_REG_ASSETNUMBER          "assetNumber"
+#define SPOOL_REG_BYTESPERMINUTE       "bytesPerMinute"
+#define SPOOL_REG_DEFAULTPRIORITY      "defaultPriority"
+#define SPOOL_REG_DESCRIPTION          "description"
+#define SPOOL_REG_DRIVERNAME           "driverName"
+#define SPOOL_REG_DRIVERVERSION                "driverVersion"
+#define SPOOL_REG_FLAGS                        "flags"
+#define SPOOL_REG_LOCATION             "location"
+#define SPOOL_REG_OPERATINGSYSTEM      "operatingSystem"
+#define SPOOL_REG_OPERATINGSYSTEMHOTFIX        "operatingSystemHotfix"
+#define SPOOL_REG_OPERATINGSYSTEMSERVICEPACK "operatingSystemServicePack"
+#define SPOOL_REG_OPERATINGSYSTEMVERSION "operatingSystemVersion"
+#define SPOOL_REG_PORTNAME             "portName"
+#define SPOOL_REG_PRINTATTRIBUTES      "printAttributes"
+#define SPOOL_REG_PRINTBINNAMES                "printBinNames"
+#define SPOOL_REG_PRINTCOLLATE         "printCollate"
+#define SPOOL_REG_PRINTCOLOR           "printColor"
+#define SPOOL_REG_PRINTDUPLEXSUPPORTED "printDuplexSupported"
+#define SPOOL_REG_PRINTENDTIME         "printEndTime"
+#define SPOOL_REG_PRINTERNAME          "printerName"
+#define SPOOL_REG_PRINTFORMNAME                "printFormName"
+#define SPOOL_REG_PRINTKEEPPRINTEDJOBS "printKeepPrintedJobs"
+#define SPOOL_REG_PRINTLANGUAGE                "printLanguage"
+#define SPOOL_REG_PRINTMACADDRESS      "printMACAddress"
+#define SPOOL_REG_PRINTMAXCOPIES       "printMaxCopies"
+#define SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED "printMaxResolutionSupported"
+#define SPOOL_REG_PRINTMAXXEXTENT      "printMaxXExtent"
+#define SPOOL_REG_PRINTMAXYEXTENT      "printMaxYExtent"
+#define SPOOL_REG_PRINTMEDIAREADY      "printMediaReady"
+#define SPOOL_REG_PRINTMEDIASUPPORTED  "printMediaSupported"
+#define SPOOL_REG_PRINTMEMORY          "printMemory"
+#define SPOOL_REG_PRINTMINXEXTENT      "printMinXExtent"
+#define SPOOL_REG_PRINTMINYEXTENT      "printMinYExtent"
+#define SPOOL_REG_PRINTNETWORKADDRESS  "printNetworkAddress"
+#define SPOOL_REG_PRINTNOTIFY          "printNotify"
+#define SPOOL_REG_PRINTNUMBERUP                "printNumberUp"
+#define SPOOL_REG_PRINTORIENTATIONSSUPPORTED "printOrientationsSupported"
+#define SPOOL_REG_PRINTOWNER           "printOwner"
+#define SPOOL_REG_PRINTPAGESPERMINUTE  "printPagesPerMinute"
+#define SPOOL_REG_PRINTRATE            "printRate"
+#define SPOOL_REG_PRINTRATEUNIT                "printRateUnit"
+#define SPOOL_REG_PRINTSEPARATORFILE   "printSeparatorFile"
+#define SPOOL_REG_PRINTSHARENAME       "printShareName"
+#define SPOOL_REG_PRINTSPOOLING                "printSpooling"
+#define SPOOL_REGVAL_PRINTWHILESPOOLING        "PrintWhileSpooling"
+#define SPOOL_REGVAL_PRINTAFTERSPOOLED "PrintAfterSpooled"
+#define SPOOL_REGVAL_PRINTDIRECT       "PrintDirect"
+#define SPOOL_REG_PRINTSTAPLINGSUPPORTED "printStaplingSupported"
+#define SPOOL_REG_PRINTSTARTTIME       "printStartTime"
+#define SPOOL_REG_PRINTSTATUS          "printStatus"
+#define SPOOL_REG_PRIORITY             "priority"
+#define SPOOL_REG_SERVERNAME           "serverName"
+#define SPOOL_REG_SHORTSERVERNAME      "shortServerName"
+#define SPOOL_REG_UNCNAME              "uNCName"
+#define SPOOL_REG_URL                  "url"
+#define SPOOL_REG_VERSIONNUMBER                "versionNumber"
+
+/* container for a single registry key */
+
+typedef struct {
+       char            *name;
+       REGVAL_CTR      values;
+} NT_PRINTER_KEY;
+
+/* container for all printer data */
+
+typedef struct {
+       int             num_keys;
+       NT_PRINTER_KEY  *keys;
+} NT_PRINTER_DATA;
+
+typedef struct ntdevicemode
+{
+       fstring devicename;
+       fstring formname;
+
+       uint16  specversion;
+       uint16  driverversion;
+       uint16  size;
+       uint16  driverextra;
+       uint16  orientation;
+       uint16  papersize;
+       uint16  paperlength;
+       uint16  paperwidth;
+       uint16  scale;
+       uint16  copies;
+       uint16  defaultsource;
+       uint16  printquality;
+       uint16  color;
+       uint16  duplex;
+       uint16  yresolution;
+       uint16  ttoption;
+       uint16  collate;
+       uint16  logpixels;
+
+       uint32  fields;
+       uint32  bitsperpel;
+       uint32  pelswidth;
+       uint32  pelsheight;
+       uint32  displayflags;
+       uint32  displayfrequency;
+       uint32  icmmethod;
+       uint32  icmintent;
+       uint32  mediatype;
+       uint32  dithertype;
+       uint32  reserved1;
+       uint32  reserved2;
+       uint32  panningwidth;
+       uint32  panningheight;
+       uint8   *private;
+} NT_DEVICEMODE;
+
+typedef struct nt_printer_info_level_2
+{
+       uint32 attributes;
+       uint32 priority;
+       uint32 default_priority;
+       uint32 starttime;
+       uint32 untiltime;
+       uint32 status;
+       uint32 cjobs;
+       uint32 averageppm;
+       fstring servername;
+       fstring printername;
+       fstring sharename;
+       fstring portname;
+       fstring drivername;
+       pstring comment;
+       fstring location;
+       NT_DEVICEMODE *devmode;
+       fstring sepfile;
+       fstring printprocessor;
+       fstring datatype;
+       fstring parameters;
+       NT_PRINTER_DATA data;
+       SEC_DESC_BUF *secdesc_buf;
+       uint32 changeid;
+       uint32 c_setprinter;
+       uint32 setuptime;       
+} NT_PRINTER_INFO_LEVEL_2;
+
+typedef struct nt_printer_info_level
+{
+       NT_PRINTER_INFO_LEVEL_2 *info_2;
+} NT_PRINTER_INFO_LEVEL;
+
+typedef struct
+{
+       fstring name;
+       uint32 flag;
+       uint32 width;
+       uint32 length;
+       uint32 left;
+       uint32 top;
+       uint32 right;
+       uint32 bottom;
+} nt_forms_struct;
+
+/*
+typedef struct _form
+{
+       uint32 flags;
+       uint32 name_ptr;
+       uint32 size_x;
+       uint32 size_y;
+       uint32 left;
+       uint32 top;
+       uint32 right;
+       uint32 bottom;
+       UNISTR2 name;
+} FORM;
+*/
+
+#ifndef SAMBA_PRINTER_PORT_NAME
+#define SAMBA_PRINTER_PORT_NAME "Samba Printer Port"
+#endif
+
+/* DOS header format */
+#define DOS_HEADER_SIZE                 64
+#define DOS_HEADER_MAGIC_OFFSET         0
+#define DOS_HEADER_MAGIC                0x5A4D
+#define DOS_HEADER_LFANEW_OFFSET        60
+
+/* New Executable format (Win or OS/2 1.x segmented) */
+#define NE_HEADER_SIZE                  64
+#define NE_HEADER_SIGNATURE_OFFSET      0
+#define NE_HEADER_SIGNATURE             0x454E
+#define NE_HEADER_TARGET_OS_OFFSET      54
+#define NE_HEADER_TARGOS_WIN            0x02
+#define NE_HEADER_MINOR_VER_OFFSET      62
+#define NE_HEADER_MAJOR_VER_OFFSET      63
+
+/* Portable Executable format */
+#define PE_HEADER_SIZE                  248
+#define PE_HEADER_SIGNATURE_OFFSET      0
+#define PE_HEADER_SIGNATURE             0x00004550
+#define PE_HEADER_MACHINE_OFFSET        4
+#define PE_HEADER_MACHINE_I386          0x14c
+#define PE_HEADER_NUMBER_OF_SECTIONS    6
+#define PE_HEADER_MAJOR_OS_VER_OFFSET   64
+#define PE_HEADER_MINOR_OS_VER_OFFSET   66
+#define PE_HEADER_MAJOR_IMG_VER_OFFSET  68
+#define PE_HEADER_MINOR_IMG_VER_OFFSET  70
+#define PE_HEADER_MAJOR_SS_VER_OFFSET   72
+#define PE_HEADER_MINOR_SS_VER_OFFSET   74
+#define PE_HEADER_SECT_HEADER_SIZE      40
+#define PE_HEADER_SECT_NAME_OFFSET      0
+#define PE_HEADER_SECT_SIZE_DATA_OFFSET 16
+#define PE_HEADER_SECT_PTR_DATA_OFFSET  20
+
+/* Microsoft file version format */
+#define VS_SIGNATURE                    "VS_VERSION_INFO"
+#define VS_MAGIC_VALUE                  0xfeef04bd
+#define VS_MAJOR_OFFSET                                        8
+#define VS_MINOR_OFFSET                                        12
+#define VS_VERSION_INFO_UNICODE_SIZE    (sizeof(VS_SIGNATURE)*2+4+VS_MINOR_OFFSET+4) /* not true size! */
+#define VS_VERSION_INFO_SIZE            (sizeof(VS_SIGNATURE)+4+VS_MINOR_OFFSET+4)   /* not true size! */
+#define VS_NE_BUF_SIZE                  4096  /* Must be > 2*VS_VERSION_INFO_SIZE */
+
+/* Notify spoolss clients that something has changed.  The
+   notification data is either stored in two uint32 values or a
+   variable length array. */
+
+#define SPOOLSS_NOTIFY_MSG_UNIX_JOBID 0x0001    /* Job id is unix  */
+
+typedef struct spoolss_notify_msg {
+       fstring printer;        /* Name of printer notified */
+       uint32 type;            /* Printer or job notify */
+       uint32 field;           /* Notify field changed */
+       uint32 id;              /* Job id */
+       uint32 len;             /* Length of data, 0 for two uint32 value */
+       uint32 flags;
+       union {
+               uint32 value[2];
+               char *data;
+       } notify;
+} SPOOLSS_NOTIFY_MSG;
+
+typedef struct {
+       fstring                 printername;
+       uint32                  num_msgs;
+       SPOOLSS_NOTIFY_MSG      *msgs;
+} SPOOLSS_NOTIFY_MSG_GROUP;
+
+typedef struct {
+       TALLOC_CTX                      *ctx;
+       uint32                          num_groups;
+       SPOOLSS_NOTIFY_MSG_GROUP        *msg_groups;
+} SPOOLSS_NOTIFY_MSG_CTR;
+
+#define PRINTER_HANDLE_IS_PRINTER      0
+#define PRINTER_HANDLE_IS_PRINTSERVER  1
+
+/* structure to store the printer handles */
+/* and a reference to what it's pointing to */
+/* and the notify info asked about */
+/* that's the central struct */
+typedef struct _Printer{
+       struct _Printer *prev, *next;
+       BOOL document_started;
+       BOOL page_started;
+       uint32 jobid; /* jobid in printing backend */
+       BOOL printer_type;
+       TALLOC_CTX *ctx;
+       union {
+               fstring handlename;
+               fstring printerservername;
+       } dev;
+       uint32 type;
+       uint32 access_granted;
+       struct {
+               uint32 flags;
+               uint32 options;
+               fstring localmachine;
+               uint32 printerlocal;
+               SPOOL_NOTIFY_OPTION *option;
+               POLICY_HND client_hnd;
+               BOOL client_connected;
+               uint32 change;
+               /* are we in a FindNextPrinterChangeNotify() call? */
+               BOOL fnpcn;
+       } notify;
+       struct {
+               fstring machine;
+               fstring user;
+       } client;
+       
+       /* devmode sent in the OpenPrinter() call */
+       NT_DEVICEMODE   *nt_devmode;
+       
+       /* cache the printer info */
+       NT_PRINTER_INFO_LEVEL *printer_info;
+       
+} Printer_entry;
+
+#endif /* NT_PRINTING_H_ */
diff --git a/source4/include/nt_status.h b/source4/include/nt_status.h
new file mode 100644 (file)
index 0000000..9747f73
--- /dev/null
@@ -0,0 +1,63 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup, plus a whole lot more.
+   
+   Copyright (C) Andrew Tridgell              2001
+   
+   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.
+*/
+
+#ifndef _NT_STATUS_H
+#define _NT_STATUS_H
+
+/* The Splint code analysis tool doesn't like immediate structures. */
+
+#ifdef _SPLINT_                      /* http://www.splint.org */
+#undef HAVE_IMMEDIATE_STRUCTURES
+#endif
+
+/* the following rather strange looking definitions of NTSTATUS and WERROR
+   and there in order to catch common coding errors where different error types
+   are mixed up. This is especially important as we slowly convert Samba
+   from using BOOL for internal functions 
+*/
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} NTSTATUS;
+#define NT_STATUS(x) ((NTSTATUS) { x })
+#define NT_STATUS_V(x) ((x).v)
+#else
+typedef uint32 NTSTATUS;
+#define NT_STATUS(x) (x)
+#define NT_STATUS_V(x) (x)
+#endif
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} WERROR;
+#define W_ERROR(x) ((WERROR) { x })
+#define W_ERROR_V(x) ((x).v)
+#else
+typedef uint32 WERROR;
+#define W_ERROR(x) (x)
+#define W_ERROR_V(x) (x)
+#endif
+
+#define NT_STATUS_IS_OK(x) (NT_STATUS_V(x) == 0)
+#define NT_STATUS_IS_ERR(x) ((NT_STATUS_V(x) & 0xc0000000) == 0xc0000000)
+#define NT_STATUS_EQUAL(x,y) (NT_STATUS_V(x) == NT_STATUS_V(y))
+#define W_ERROR_IS_OK(x) (W_ERROR_V(x) == 0)
+#define W_ERROR_EQUAL(x,y) (W_ERROR_V(x) == W_ERROR_V(y))
+
+#endif
diff --git a/source4/include/ntdomain.h b/source4/include/ntdomain.h
new file mode 100644 (file)
index 0000000..62608b2
--- /dev/null
@@ -0,0 +1,379 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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.
+*/
+
+#ifndef _NT_DOMAIN_H /* _NT_DOMAIN_H */
+#define _NT_DOMAIN_H 
+
+/* dce/rpc support */
+#include "rpc_dce.h"
+
+/* miscellaneous structures / defines */
+#include "rpc_misc.h"
+
+#include "rpc_creds.h"
+
+#include "talloc.h"
+
+/*
+ * A bunch of stuff that was put into smb.h
+ * in the NTDOM branch - it didn't belong there.
+ */
+typedef struct _prs_struct 
+{
+       BOOL io; /* parsing in or out of data stream */
+       /* 
+        * If the (incoming) data is big-endian. On output we are
+        * always little-endian.
+        */ 
+       BOOL bigendian_data;
+       uint8 align; /* data alignment */
+       BOOL is_dynamic; /* Do we own this memory or not ? */
+       uint32 data_offset; /* Current working offset into data. */
+       uint32 buffer_size; /* Current allocated size of the buffer. */
+       uint32 grow_size; /* size requested via prs_grow() calls */
+       char *data_p; /* The buffer itself. */
+       TALLOC_CTX *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+/*
+ * Defines for io member of prs_struct.
+ */
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+#define RPC_BIG_ENDIAN                 1
+#define RPC_LITTLE_ENDIAN      0
+
+#define RPC_PARSE_ALIGN 4
+
+typedef struct _output_data {
+       /*
+        * Raw RPC output data. This does not include RPC headers or footers.
+        */
+       prs_struct rdata;
+
+       /* The amount of data sent from the current rdata struct. */
+       uint32 data_sent_length;
+
+       /*
+        * The current PDU being returned. This inclues
+        * headers, data and authentication footer.
+        */
+       unsigned char current_pdu[MAX_PDU_FRAG_LEN];
+
+       /* The amount of data in the current_pdu buffer. */
+       uint32 current_pdu_len;
+
+       /* The amount of data sent from the current PDU. */
+       uint32 current_pdu_sent;
+} output_data;
+
+typedef struct _input_data {
+    /*
+     * This is the current incoming pdu. The data here
+     * is collected via multiple writes until a complete
+     * pdu is seen, then the data is copied into the in_data
+     * structure. The maximum size of this is 0x1630 (MAX_PDU_FRAG_LEN).
+     */
+    unsigned char current_in_pdu[MAX_PDU_FRAG_LEN];
+
+    /*
+     * The amount of data needed to complete the in_pdu.
+     * If this is zero, then we are at the start of a new
+     * pdu.
+     */
+    uint32 pdu_needed_len;
+
+    /*
+     * The amount of data received so far in the in_pdu.
+     * If this is zero, then we are at the start of a new
+     * pdu.
+     */
+    uint32 pdu_received_len;
+
+    /*
+     * This is the collection of input data with all
+     * the rpc headers and auth footers removed.
+     * The maximum length of this (1Mb) is strictly enforced.
+     */
+    prs_struct data;
+} input_data;
+
+/*
+ * Handle database - stored per pipe.
+ */
+
+struct policy
+{
+    struct policy *next, *prev;
+
+    POLICY_HND pol_hnd;
+
+    void *data_ptr;
+    void (*free_fn)(void *);
+
+};
+
+struct handle_list {
+       struct policy *Policy;  /* List of policies. */
+       size_t count;                   /* Current number of handles. */
+       size_t pipe_ref_count;  /* Number of pipe handles referring to this list. */
+};
+
+/* Domain controller authentication protocol info */
+struct dcinfo
+{
+       DOM_CHAL clnt_chal; /* Initial challenge received from client */
+       DOM_CHAL srv_chal;  /* Initial server challenge */
+       DOM_CRED clnt_cred; /* Last client credential */
+       DOM_CRED srv_cred;  /* Last server credential */
+       uchar  sess_key[8]; /* Session key */
+       uchar  md4pw[16];   /* md4(machine password) */
+
+       fstring mach_acct;  /* Machine name we've authenticated. */
+
+       fstring remote_machine;  /* Machine name we've authenticated. */
+
+       BOOL challenge_sent;
+       BOOL got_session_key;
+       BOOL authenticated;
+
+};
+
+/*
+ * DCE/RPC-specific samba-internal-specific handling of data on
+ * NamedPipes.
+ *
+ */
+
+typedef struct pipes_struct
+{
+       struct pipes_struct *next, *prev;
+
+       struct tcon_context *conn;
+       uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+
+       fstring name;
+       fstring pipe_srv_name;
+
+       RPC_HDR hdr; /* Incoming RPC header. */
+       RPC_HDR_REQ hdr_req; /* Incoming request header. */
+
+       uint32 ntlmssp_chal_flags; /* Client challenge flags. */
+       BOOL ntlmssp_auth_requested; /* If the client wanted authenticated rpc. */
+       BOOL ntlmssp_auth_validated; /* If the client *got* authenticated rpc. */
+       unsigned char challenge[8];
+       unsigned char ntlmssp_hash[258];
+       uint32 ntlmssp_seq_num;
+       struct dcinfo dc; /* Keeps the creds data. */
+
+       /*
+        * Windows user info.
+        */
+       fstring user_name;
+       fstring domain;
+       fstring wks;
+
+       /*
+        * Unix user name and credentials.
+        */
+
+       fstring pipe_user_name;
+       struct current_user pipe_user;
+
+       uint8 session_key[16];
+
+       /*
+        * Set to true when an RPC bind has been done on this pipe.
+        */
+       
+       BOOL pipe_bound;
+       
+       /*
+        * Set to true when we should return fault PDU's for everything.
+        */
+       
+       BOOL fault_state;
+
+       /*
+        * Set to true when we should return fault PDU's for a bad handle.
+        */
+
+       BOOL bad_handle_fault_state;
+       
+       /*
+        * Set to RPC_BIG_ENDIAN when dealing with big-endian PDU's
+        */
+       
+       BOOL endian;
+       
+       /*
+        * Struct to deal with multiple pdu inputs.
+        */
+
+       input_data in_data;
+
+       /*
+        * Struct to deal with multiple pdu outputs.
+        */
+
+       output_data out_data;
+
+       /* talloc context to use when allocating memory on this pipe. */
+       TALLOC_CTX *mem_ctx;
+
+       /* handle database to use on this pipe. */
+       struct handle_list *pipe_handles;
+
+} pipes_struct;
+
+typedef struct smb_np_struct
+{
+       struct smb_np_struct *next, *prev;
+       int pnum;
+       struct tcon_context *conn;
+       uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+       BOOL open; /* open connection */
+       uint16 device_state;
+       uint16 priority;
+       fstring name;
+
+       /* When replying to an SMBtrans, this is the maximum amount of
+           data that can be sent in the initial reply. */
+       int max_trans_reply;
+
+       /*
+        * NamedPipe state information.
+        *
+        * (e.g. typecast a np_struct, above).
+        */
+       void *np_state;
+
+       /*
+        * NamedPipe functions, to be called to perform
+        * Named Pipe transactions on request from an
+        * SMB client.
+        */
+
+       /* call to create a named pipe connection.
+        * returns: state information representing the connection.
+        *          is stored in np_state, above.
+        */
+       void *   (*namedpipe_create)(char *pipe_name, 
+                                         struct tcon_context *conn, uint16 vuid);
+
+       /* call to perform a write / read namedpipe transaction.
+        * TransactNamedPipe is weird: it returns whether there
+        * is more data outstanding to be read, and the
+        * caller is expected to take note and follow up with
+        * read requests.
+        */
+       ssize_t  (*namedpipe_transact)(void *np_state,
+                                      char *data, int len,
+                                      char *rdata, int rlen,
+                                      BOOL *pipe_outstanding);
+
+       /* call to perform a write namedpipe operation
+        */
+       ssize_t  (*namedpipe_write)(void * np_state,
+                                   char *data, size_t n);
+
+       /* call to perform a read namedpipe operation.
+        *
+        * NOTE: the only reason that the pipe_outstanding
+        * argument is here is because samba does not use
+        * the namedpipe_transact function yet: instead,
+        * it performs the same as what namedpipe_transact
+        * does - a write, followed by a read.
+        *
+        * when samba is modified to use namedpipe_transact,
+        * the pipe_outstanding argument may be removed.
+        */
+       ssize_t  (*namedpipe_read)(void * np_state,
+                                  char *data, size_t max_len,
+                                  BOOL *pipe_outstanding);
+
+       /* call to close a namedpipe.
+        * function is expected to perform all cleanups
+        * necessary, free all memory etc.
+        *
+        * returns True if cleanup was successful (not that
+        * we particularly care).
+        */
+       BOOL     (*namedpipe_close)(void * np_state);
+
+} smb_np_struct;
+
+struct api_struct
+{  
+  const char *name;
+  uint8 opnum;
+  BOOL (*fn) (pipes_struct *);
+};
+
+typedef struct
+{  
+       uint32 rid;
+       const char *name;
+
+} rid_name;
+
+struct acct_info
+{
+    fstring acct_name; /* account name */
+    fstring acct_desc; /* account name */
+    uint32 rid; /* domain-relative RID */
+};
+
+/*
+ * higher order functions for use with msrpc client code
+ */
+
+#define PRINT_INFO_FN(fn)\
+        void (*fn)(const char*, uint32, uint32, void  *const *const)
+#define JOB_INFO_FN(fn)\
+        void (*fn)(const char*, const char*, uint32, uint32, void *const *const)
+
+/* end higher order functions */
+
+
+/* security descriptor structures */
+#include "rpc_secdes.h"
+
+/* different dce/rpc pipes */
+#include "rpc_lsa.h"
+#include "rpc_netlogon.h"
+#include "rpc_reg.h"
+#include "rpc_samr.h"
+#include "rpc_srvsvc.h"
+#include "rpc_wkssvc.h"
+#include "rpc_spoolss.h"
+#include "rpc_dfs.h"
+#include "rpc_ds.h"
+
+#endif /* _NT_DOMAIN_H */
diff --git a/source4/include/nterr.h b/source4/include/nterr.h
new file mode 100644 (file)
index 0000000..1c052eb
--- /dev/null
@@ -0,0 +1,570 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT error code constants
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) John H Terpstra              1996-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Paul Ashton                  1998-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.
+*/
+
+#ifndef _NTERR_H
+#define _NTERR_H
+
+/* Win32 Status codes. */
+
+#define STATUS_BUFFER_OVERFLOW            NT_STATUS(0x80000005)
+#define NT_STATUS_NO_MORE_ENTRIES         NT_STATUS(0x8000001a)
+
+#define STATUS_MORE_ENTRIES            NT_STATUS(0x0105)
+#define STATUS_SOME_UNMAPPED              NT_STATUS(0x0107)
+#define ERROR_INVALID_PARAMETER                  NT_STATUS(0x0057)
+#define ERROR_INSUFFICIENT_BUFFER        NT_STATUS(0x007a)
+#define STATUS_NOTIFY_ENUM_DIR            NT_STATUS(0x010c)
+#define ERROR_INVALID_DATATYPE           NT_STATUS(0x070c)
+
+/* Win32 Error codes extracted using a loop in smbclient then printing a
+   netmon sniff to a file. */
+
+/*
+                       --------------
+                      /              \
+                     /      REST      \
+                    /        IN        \
+                   /       PEACE        \
+                  /                      \
+                  | NT_STATUS_NOPROBLEMO |
+                  |                      |
+                  |                      |
+                  |      4 September     |
+                  |                      |
+                  |         2001         |
+                 *|     *  *  *          | *
+        _________)/\\_//(\/(/\)/\//\/\///|_)_______
+*/
+
+#define NT_STATUS_OK NT_STATUS(0x0000)
+#define NT_STATUS_UNSUCCESSFUL NT_STATUS(0xC0000000 | 0x0001)
+#define NT_STATUS_NOT_IMPLEMENTED NT_STATUS(0xC0000000 | 0x0002)
+#define NT_STATUS_INVALID_INFO_CLASS NT_STATUS(0xC0000000 | 0x0003)
+#define NT_STATUS_INFO_LENGTH_MISMATCH NT_STATUS(0xC0000000 | 0x0004)
+#define NT_STATUS_ACCESS_VIOLATION NT_STATUS(0xC0000000 | 0x0005)
+#define NT_STATUS_IN_PAGE_ERROR NT_STATUS(0xC0000000 | 0x0006)
+#define NT_STATUS_PAGEFILE_QUOTA NT_STATUS(0xC0000000 | 0x0007)
+#define NT_STATUS_INVALID_HANDLE NT_STATUS(0xC0000000 | 0x0008)
+#define NT_STATUS_BAD_INITIAL_STACK NT_STATUS(0xC0000000 | 0x0009)
+#define NT_STATUS_BAD_INITIAL_PC NT_STATUS(0xC0000000 | 0x000a)
+#define NT_STATUS_INVALID_CID NT_STATUS(0xC0000000 | 0x000b)
+#define NT_STATUS_TIMER_NOT_CANCELED NT_STATUS(0xC0000000 | 0x000c)
+#define NT_STATUS_INVALID_PARAMETER NT_STATUS(0xC0000000 | 0x000d)
+#define NT_STATUS_NO_SUCH_DEVICE NT_STATUS(0xC0000000 | 0x000e)
+#define NT_STATUS_NO_SUCH_FILE NT_STATUS(0xC0000000 | 0x000f)
+#define NT_STATUS_INVALID_DEVICE_REQUEST NT_STATUS(0xC0000000 | 0x0010)
+#define NT_STATUS_END_OF_FILE NT_STATUS(0xC0000000 | 0x0011)
+#define NT_STATUS_WRONG_VOLUME NT_STATUS(0xC0000000 | 0x0012)
+#define NT_STATUS_NO_MEDIA_IN_DEVICE NT_STATUS(0xC0000000 | 0x0013)
+#define NT_STATUS_UNRECOGNIZED_MEDIA NT_STATUS(0xC0000000 | 0x0014)
+#define NT_STATUS_NONEXISTENT_SECTOR NT_STATUS(0xC0000000 | 0x0015)
+#define NT_STATUS_MORE_PROCESSING_REQUIRED NT_STATUS(0xC0000000 | 0x0016)
+#define NT_STATUS_NO_MEMORY NT_STATUS(0xC0000000 | 0x0017)
+#define NT_STATUS_CONFLICTING_ADDRESSES NT_STATUS(0xC0000000 | 0x0018)
+#define NT_STATUS_NOT_MAPPED_VIEW NT_STATUS(0xC0000000 | 0x0019)
+#define NT_STATUS_UNABLE_TO_FREE_VM NT_STATUS(0xC0000000 | 0x001a)
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION NT_STATUS(0xC0000000 | 0x001b)
+#define NT_STATUS_INVALID_SYSTEM_SERVICE NT_STATUS(0xC0000000 | 0x001c)
+#define NT_STATUS_ILLEGAL_INSTRUCTION NT_STATUS(0xC0000000 | 0x001d)
+#define NT_STATUS_INVALID_LOCK_SEQUENCE NT_STATUS(0xC0000000 | 0x001e)
+#define NT_STATUS_INVALID_VIEW_SIZE NT_STATUS(0xC0000000 | 0x001f)
+#define NT_STATUS_INVALID_FILE_FOR_SECTION NT_STATUS(0xC0000000 | 0x0020)
+#define NT_STATUS_ALREADY_COMMITTED NT_STATUS(0xC0000000 | 0x0021)
+#define NT_STATUS_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x0022)
+#define NT_STATUS_BUFFER_TOO_SMALL NT_STATUS(0xC0000000 | 0x0023)
+#define NT_STATUS_OBJECT_TYPE_MISMATCH NT_STATUS(0xC0000000 | 0x0024)
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION NT_STATUS(0xC0000000 | 0x0025)
+#define NT_STATUS_INVALID_DISPOSITION NT_STATUS(0xC0000000 | 0x0026)
+#define NT_STATUS_UNWIND NT_STATUS(0xC0000000 | 0x0027)
+#define NT_STATUS_BAD_STACK NT_STATUS(0xC0000000 | 0x0028)
+#define NT_STATUS_INVALID_UNWIND_TARGET NT_STATUS(0xC0000000 | 0x0029)
+#define NT_STATUS_NOT_LOCKED NT_STATUS(0xC0000000 | 0x002a)
+#define NT_STATUS_PARITY_ERROR NT_STATUS(0xC0000000 | 0x002b)
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM NT_STATUS(0xC0000000 | 0x002c)
+#define NT_STATUS_NOT_COMMITTED NT_STATUS(0xC0000000 | 0x002d)
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES NT_STATUS(0xC0000000 | 0x002e)
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG NT_STATUS(0xC0000000 | 0x002f)
+#define NT_STATUS_INVALID_PARAMETER_MIX NT_STATUS(0xC0000000 | 0x0030)
+#define NT_STATUS_INVALID_QUOTA_LOWER NT_STATUS(0xC0000000 | 0x0031)
+#define NT_STATUS_DISK_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0032)
+#define NT_STATUS_OBJECT_NAME_INVALID NT_STATUS(0xC0000000 | 0x0033)
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x0034)
+#define NT_STATUS_OBJECT_NAME_COLLISION NT_STATUS(0xC0000000 | 0x0035)
+#define NT_STATUS_HANDLE_NOT_WAITABLE NT_STATUS(0xC0000000 | 0x0036)
+#define NT_STATUS_PORT_DISCONNECTED NT_STATUS(0xC0000000 | 0x0037)
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED NT_STATUS(0xC0000000 | 0x0038)
+#define NT_STATUS_OBJECT_PATH_INVALID NT_STATUS(0xC0000000 | 0x0039)
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND NT_STATUS(0xC0000000 | 0x003a)
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD NT_STATUS(0xC0000000 | 0x003b)
+#define NT_STATUS_DATA_OVERRUN NT_STATUS(0xC0000000 | 0x003c)
+#define NT_STATUS_DATA_LATE_ERROR NT_STATUS(0xC0000000 | 0x003d)
+#define NT_STATUS_DATA_ERROR NT_STATUS(0xC0000000 | 0x003e)
+#define NT_STATUS_CRC_ERROR NT_STATUS(0xC0000000 | 0x003f)
+#define NT_STATUS_SECTION_TOO_BIG NT_STATUS(0xC0000000 | 0x0040)
+#define NT_STATUS_PORT_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0041)
+#define NT_STATUS_INVALID_PORT_HANDLE NT_STATUS(0xC0000000 | 0x0042)
+#define NT_STATUS_SHARING_VIOLATION NT_STATUS(0xC0000000 | 0x0043)
+#define NT_STATUS_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0044)
+#define NT_STATUS_INVALID_PAGE_PROTECTION NT_STATUS(0xC0000000 | 0x0045)
+#define NT_STATUS_MUTANT_NOT_OWNED NT_STATUS(0xC0000000 | 0x0046)
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0047)
+#define NT_STATUS_PORT_ALREADY_SET NT_STATUS(0xC0000000 | 0x0048)
+#define NT_STATUS_SECTION_NOT_IMAGE NT_STATUS(0xC0000000 | 0x0049)
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED NT_STATUS(0xC0000000 | 0x004a)
+#define NT_STATUS_THREAD_IS_TERMINATING NT_STATUS(0xC0000000 | 0x004b)
+#define NT_STATUS_BAD_WORKING_SET_LIMIT NT_STATUS(0xC0000000 | 0x004c)
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP NT_STATUS(0xC0000000 | 0x004d)
+#define NT_STATUS_SECTION_PROTECTION NT_STATUS(0xC0000000 | 0x004e)
+#define NT_STATUS_EAS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x004f)
+#define NT_STATUS_EA_TOO_LARGE NT_STATUS(0xC0000000 | 0x0050)
+#define NT_STATUS_NONEXISTENT_EA_ENTRY NT_STATUS(0xC0000000 | 0x0051)
+#define NT_STATUS_NO_EAS_ON_FILE NT_STATUS(0xC0000000 | 0x0052)
+#define NT_STATUS_EA_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0053)
+#define NT_STATUS_FILE_LOCK_CONFLICT NT_STATUS(0xC0000000 | 0x0054)
+#define NT_STATUS_LOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0055)
+#define NT_STATUS_DELETE_PENDING NT_STATUS(0xC0000000 | 0x0056)
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x0057)
+#define NT_STATUS_UNKNOWN_REVISION NT_STATUS(0xC0000000 | 0x0058)
+#define NT_STATUS_REVISION_MISMATCH NT_STATUS(0xC0000000 | 0x0059)
+#define NT_STATUS_INVALID_OWNER NT_STATUS(0xC0000000 | 0x005a)
+#define NT_STATUS_INVALID_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x005b)
+#define NT_STATUS_NO_IMPERSONATION_TOKEN NT_STATUS(0xC0000000 | 0x005c)
+#define NT_STATUS_CANT_DISABLE_MANDATORY NT_STATUS(0xC0000000 | 0x005d)
+#define NT_STATUS_NO_LOGON_SERVERS NT_STATUS(0xC0000000 | 0x005e)
+#define NT_STATUS_NO_SUCH_LOGON_SESSION NT_STATUS(0xC0000000 | 0x005f)
+#define NT_STATUS_NO_SUCH_PRIVILEGE NT_STATUS(0xC0000000 | 0x0060)
+#define NT_STATUS_PRIVILEGE_NOT_HELD NT_STATUS(0xC0000000 | 0x0061)
+#define NT_STATUS_INVALID_ACCOUNT_NAME NT_STATUS(0xC0000000 | 0x0062)
+#define NT_STATUS_USER_EXISTS NT_STATUS(0xC0000000 | 0x0063)
+#define NT_STATUS_NO_SUCH_USER NT_STATUS(0xC0000000 | 0x0064)
+#define NT_STATUS_GROUP_EXISTS NT_STATUS(0xC0000000 | 0x0065)
+#define NT_STATUS_NO_SUCH_GROUP NT_STATUS(0xC0000000 | 0x0066)
+#define NT_STATUS_MEMBER_IN_GROUP NT_STATUS(0xC0000000 | 0x0067)
+#define NT_STATUS_MEMBER_NOT_IN_GROUP NT_STATUS(0xC0000000 | 0x0068)
+#define NT_STATUS_LAST_ADMIN NT_STATUS(0xC0000000 | 0x0069)
+#define NT_STATUS_WRONG_PASSWORD NT_STATUS(0xC0000000 | 0x006a)
+#define NT_STATUS_ILL_FORMED_PASSWORD NT_STATUS(0xC0000000 | 0x006b)
+#define NT_STATUS_PASSWORD_RESTRICTION NT_STATUS(0xC0000000 | 0x006c)
+#define NT_STATUS_LOGON_FAILURE NT_STATUS(0xC0000000 | 0x006d)
+#define NT_STATUS_ACCOUNT_RESTRICTION NT_STATUS(0xC0000000 | 0x006e)
+#define NT_STATUS_INVALID_LOGON_HOURS NT_STATUS(0xC0000000 | 0x006f)
+#define NT_STATUS_INVALID_WORKSTATION NT_STATUS(0xC0000000 | 0x0070)
+#define NT_STATUS_PASSWORD_EXPIRED NT_STATUS(0xC0000000 | 0x0071)
+#define NT_STATUS_ACCOUNT_DISABLED NT_STATUS(0xC0000000 | 0x0072)
+#define NT_STATUS_NONE_MAPPED NT_STATUS(0xC0000000 | 0x0073)
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0074)
+#define NT_STATUS_LUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0075)
+#define NT_STATUS_INVALID_SUB_AUTHORITY NT_STATUS(0xC0000000 | 0x0076)
+#define NT_STATUS_INVALID_ACL NT_STATUS(0xC0000000 | 0x0077)
+#define NT_STATUS_INVALID_SID NT_STATUS(0xC0000000 | 0x0078)
+#define NT_STATUS_INVALID_SECURITY_DESCR NT_STATUS(0xC0000000 | 0x0079)
+#define NT_STATUS_PROCEDURE_NOT_FOUND NT_STATUS(0xC0000000 | 0x007a)
+#define NT_STATUS_INVALID_IMAGE_FORMAT NT_STATUS(0xC0000000 | 0x007b)
+#define NT_STATUS_NO_TOKEN NT_STATUS(0xC0000000 | 0x007c)
+#define NT_STATUS_BAD_INHERITANCE_ACL NT_STATUS(0xC0000000 | 0x007d)
+#define NT_STATUS_RANGE_NOT_LOCKED NT_STATUS(0xC0000000 | 0x007e)
+#define NT_STATUS_DISK_FULL NT_STATUS(0xC0000000 | 0x007f)
+#define NT_STATUS_SERVER_DISABLED NT_STATUS(0xC0000000 | 0x0080)
+#define NT_STATUS_SERVER_NOT_DISABLED NT_STATUS(0xC0000000 | 0x0081)
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0082)
+#define NT_STATUS_GUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0083)
+#define NT_STATUS_INVALID_ID_AUTHORITY NT_STATUS(0xC0000000 | 0x0084)
+#define NT_STATUS_AGENTS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0085)
+#define NT_STATUS_INVALID_VOLUME_LABEL NT_STATUS(0xC0000000 | 0x0086)
+#define NT_STATUS_SECTION_NOT_EXTENDED NT_STATUS(0xC0000000 | 0x0087)
+#define NT_STATUS_NOT_MAPPED_DATA NT_STATUS(0xC0000000 | 0x0088)
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND NT_STATUS(0xC0000000 | 0x0089)
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND NT_STATUS(0xC0000000 | 0x008a)
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x008b)
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED NT_STATUS(0xC0000000 | 0x008c)
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND NT_STATUS(0xC0000000 | 0x008d)
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x008e)
+#define NT_STATUS_FLOAT_INEXACT_RESULT NT_STATUS(0xC0000000 | 0x008f)
+#define NT_STATUS_FLOAT_INVALID_OPERATION NT_STATUS(0xC0000000 | 0x0090)
+#define NT_STATUS_FLOAT_OVERFLOW NT_STATUS(0xC0000000 | 0x0091)
+#define NT_STATUS_FLOAT_STACK_CHECK NT_STATUS(0xC0000000 | 0x0092)
+#define NT_STATUS_FLOAT_UNDERFLOW NT_STATUS(0xC0000000 | 0x0093)
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x0094)
+#define NT_STATUS_INTEGER_OVERFLOW NT_STATUS(0xC0000000 | 0x0095)
+#define NT_STATUS_PRIVILEGED_INSTRUCTION NT_STATUS(0xC0000000 | 0x0096)
+#define NT_STATUS_TOO_MANY_PAGING_FILES NT_STATUS(0xC0000000 | 0x0097)
+#define NT_STATUS_FILE_INVALID NT_STATUS(0xC0000000 | 0x0098)
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED NT_STATUS(0xC0000000 | 0x0099)
+#define NT_STATUS_INSUFFICIENT_RESOURCES NT_STATUS(0xC0000000 | 0x009a)
+#define NT_STATUS_DFS_EXIT_PATH_FOUND NT_STATUS(0xC0000000 | 0x009b)
+#define NT_STATUS_DEVICE_DATA_ERROR NT_STATUS(0xC0000000 | 0x009c)
+#define NT_STATUS_DEVICE_NOT_CONNECTED NT_STATUS(0xC0000000 | 0x009d)
+#define NT_STATUS_DEVICE_POWER_FAILURE NT_STATUS(0xC0000000 | 0x009e)
+#define NT_STATUS_FREE_VM_NOT_AT_BASE NT_STATUS(0xC0000000 | 0x009f)
+#define NT_STATUS_MEMORY_NOT_ALLOCATED NT_STATUS(0xC0000000 | 0x00a0)
+#define NT_STATUS_WORKING_SET_QUOTA NT_STATUS(0xC0000000 | 0x00a1)
+#define NT_STATUS_MEDIA_WRITE_PROTECTED NT_STATUS(0xC0000000 | 0x00a2)
+#define NT_STATUS_DEVICE_NOT_READY NT_STATUS(0xC0000000 | 0x00a3)
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES NT_STATUS(0xC0000000 | 0x00a4)
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL NT_STATUS(0xC0000000 | 0x00a5)
+#define NT_STATUS_CANT_OPEN_ANONYMOUS NT_STATUS(0xC0000000 | 0x00a6)
+#define NT_STATUS_BAD_VALIDATION_CLASS NT_STATUS(0xC0000000 | 0x00a7)
+#define NT_STATUS_BAD_TOKEN_TYPE NT_STATUS(0xC0000000 | 0x00a8)
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD NT_STATUS(0xC0000000 | 0x00a9)
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT NT_STATUS(0xC0000000 | 0x00aa)
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ab)
+#define NT_STATUS_PIPE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ac)
+#define NT_STATUS_INVALID_PIPE_STATE NT_STATUS(0xC0000000 | 0x00ad)
+#define NT_STATUS_PIPE_BUSY NT_STATUS(0xC0000000 | 0x00ae)
+#define NT_STATUS_ILLEGAL_FUNCTION NT_STATUS(0xC0000000 | 0x00af)
+#define NT_STATUS_PIPE_DISCONNECTED NT_STATUS(0xC0000000 | 0x00b0)
+#define NT_STATUS_PIPE_CLOSING NT_STATUS(0xC0000000 | 0x00b1)
+#define NT_STATUS_PIPE_CONNECTED NT_STATUS(0xC0000000 | 0x00b2)
+#define NT_STATUS_PIPE_LISTENING NT_STATUS(0xC0000000 | 0x00b3)
+#define NT_STATUS_INVALID_READ_MODE NT_STATUS(0xC0000000 | 0x00b4)
+#define NT_STATUS_IO_TIMEOUT NT_STATUS(0xC0000000 | 0x00b5)
+#define NT_STATUS_FILE_FORCED_CLOSED NT_STATUS(0xC0000000 | 0x00b6)
+#define NT_STATUS_PROFILING_NOT_STARTED NT_STATUS(0xC0000000 | 0x00b7)
+#define NT_STATUS_PROFILING_NOT_STOPPED NT_STATUS(0xC0000000 | 0x00b8)
+#define NT_STATUS_COULD_NOT_INTERPRET NT_STATUS(0xC0000000 | 0x00b9)
+#define NT_STATUS_FILE_IS_A_DIRECTORY NT_STATUS(0xC0000000 | 0x00ba)
+#define NT_STATUS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x00bb)
+#define NT_STATUS_REMOTE_NOT_LISTENING NT_STATUS(0xC0000000 | 0x00bc)
+#define NT_STATUS_DUPLICATE_NAME NT_STATUS(0xC0000000 | 0x00bd)
+#define NT_STATUS_BAD_NETWORK_PATH NT_STATUS(0xC0000000 | 0x00be)
+#define NT_STATUS_NETWORK_BUSY NT_STATUS(0xC0000000 | 0x00bf)
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST NT_STATUS(0xC0000000 | 0x00c0)
+#define NT_STATUS_TOO_MANY_COMMANDS NT_STATUS(0xC0000000 | 0x00c1)
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR NT_STATUS(0xC0000000 | 0x00c2)
+#define NT_STATUS_INVALID_NETWORK_RESPONSE NT_STATUS(0xC0000000 | 0x00c3)
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR NT_STATUS(0xC0000000 | 0x00c4)
+#define NT_STATUS_BAD_REMOTE_ADAPTER NT_STATUS(0xC0000000 | 0x00c5)
+#define NT_STATUS_PRINT_QUEUE_FULL NT_STATUS(0xC0000000 | 0x00c6)
+#define NT_STATUS_NO_SPOOL_SPACE NT_STATUS(0xC0000000 | 0x00c7)
+#define NT_STATUS_PRINT_CANCELLED NT_STATUS(0xC0000000 | 0x00c8)
+#define NT_STATUS_NETWORK_NAME_DELETED NT_STATUS(0xC0000000 | 0x00c9)
+#define NT_STATUS_NETWORK_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x00ca)
+#define NT_STATUS_BAD_DEVICE_TYPE NT_STATUS(0xC0000000 | 0x00cb)
+#define NT_STATUS_BAD_NETWORK_NAME NT_STATUS(0xC0000000 | 0x00cc)
+#define NT_STATUS_TOO_MANY_NAMES NT_STATUS(0xC0000000 | 0x00cd)
+#define NT_STATUS_TOO_MANY_SESSIONS NT_STATUS(0xC0000000 | 0x00ce)
+#define NT_STATUS_SHARING_PAUSED NT_STATUS(0xC0000000 | 0x00cf)
+#define NT_STATUS_REQUEST_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x00d0)
+#define NT_STATUS_REDIRECTOR_PAUSED NT_STATUS(0xC0000000 | 0x00d1)
+#define NT_STATUS_NET_WRITE_FAULT NT_STATUS(0xC0000000 | 0x00d2)
+#define NT_STATUS_PROFILING_AT_LIMIT NT_STATUS(0xC0000000 | 0x00d3)
+#define NT_STATUS_NOT_SAME_DEVICE NT_STATUS(0xC0000000 | 0x00d4)
+#define NT_STATUS_FILE_RENAMED NT_STATUS(0xC0000000 | 0x00d5)
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED NT_STATUS(0xC0000000 | 0x00d6)
+#define NT_STATUS_NO_SECURITY_ON_OBJECT NT_STATUS(0xC0000000 | 0x00d7)
+#define NT_STATUS_CANT_WAIT NT_STATUS(0xC0000000 | 0x00d8)
+#define NT_STATUS_PIPE_EMPTY NT_STATUS(0xC0000000 | 0x00d9)
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO NT_STATUS(0xC0000000 | 0x00da)
+#define NT_STATUS_CANT_TERMINATE_SELF NT_STATUS(0xC0000000 | 0x00db)
+#define NT_STATUS_INVALID_SERVER_STATE NT_STATUS(0xC0000000 | 0x00dc)
+#define NT_STATUS_INVALID_DOMAIN_STATE NT_STATUS(0xC0000000 | 0x00dd)
+#define NT_STATUS_INVALID_DOMAIN_ROLE NT_STATUS(0xC0000000 | 0x00de)
+#define NT_STATUS_NO_SUCH_DOMAIN NT_STATUS(0xC0000000 | 0x00df)
+#define NT_STATUS_DOMAIN_EXISTS NT_STATUS(0xC0000000 | 0x00e0)
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x00e1)
+#define NT_STATUS_OPLOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x00e2)
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL NT_STATUS(0xC0000000 | 0x00e3)
+#define NT_STATUS_INTERNAL_DB_CORRUPTION NT_STATUS(0xC0000000 | 0x00e4)
+#define NT_STATUS_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x00e5)
+#define NT_STATUS_GENERIC_NOT_MAPPED NT_STATUS(0xC0000000 | 0x00e6)
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT NT_STATUS(0xC0000000 | 0x00e7)
+#define NT_STATUS_INVALID_USER_BUFFER NT_STATUS(0xC0000000 | 0x00e8)
+#define NT_STATUS_UNEXPECTED_IO_ERROR NT_STATUS(0xC0000000 | 0x00e9)
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR NT_STATUS(0xC0000000 | 0x00ea)
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR NT_STATUS(0xC0000000 | 0x00eb)
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR NT_STATUS(0xC0000000 | 0x00ec)
+#define NT_STATUS_NOT_LOGON_PROCESS NT_STATUS(0xC0000000 | 0x00ed)
+#define NT_STATUS_LOGON_SESSION_EXISTS NT_STATUS(0xC0000000 | 0x00ee)
+#define NT_STATUS_INVALID_PARAMETER_1 NT_STATUS(0xC0000000 | 0x00ef)
+#define NT_STATUS_INVALID_PARAMETER_2 NT_STATUS(0xC0000000 | 0x00f0)
+#define NT_STATUS_INVALID_PARAMETER_3 NT_STATUS(0xC0000000 | 0x00f1)
+#define NT_STATUS_INVALID_PARAMETER_4 NT_STATUS(0xC0000000 | 0x00f2)
+#define NT_STATUS_INVALID_PARAMETER_5 NT_STATUS(0xC0000000 | 0x00f3)
+#define NT_STATUS_INVALID_PARAMETER_6 NT_STATUS(0xC0000000 | 0x00f4)
+#define NT_STATUS_INVALID_PARAMETER_7 NT_STATUS(0xC0000000 | 0x00f5)
+#define NT_STATUS_INVALID_PARAMETER_8 NT_STATUS(0xC0000000 | 0x00f6)
+#define NT_STATUS_INVALID_PARAMETER_9 NT_STATUS(0xC0000000 | 0x00f7)
+#define NT_STATUS_INVALID_PARAMETER_10 NT_STATUS(0xC0000000 | 0x00f8)
+#define NT_STATUS_INVALID_PARAMETER_11 NT_STATUS(0xC0000000 | 0x00f9)
+#define NT_STATUS_INVALID_PARAMETER_12 NT_STATUS(0xC0000000 | 0x00fa)
+#define NT_STATUS_REDIRECTOR_NOT_STARTED NT_STATUS(0xC0000000 | 0x00fb)
+#define NT_STATUS_REDIRECTOR_STARTED NT_STATUS(0xC0000000 | 0x00fc)
+#define NT_STATUS_STACK_OVERFLOW NT_STATUS(0xC0000000 | 0x00fd)
+#define NT_STATUS_NO_SUCH_PACKAGE NT_STATUS(0xC0000000 | 0x00fe)
+#define NT_STATUS_BAD_FUNCTION_TABLE NT_STATUS(0xC0000000 | 0x00ff)
+#define NT_STATUS_DIRECTORY_NOT_EMPTY NT_STATUS(0xC0000000 | 0x0101)
+#define NT_STATUS_FILE_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0102)
+#define NT_STATUS_NOT_A_DIRECTORY NT_STATUS(0xC0000000 | 0x0103)
+#define NT_STATUS_BAD_LOGON_SESSION_STATE NT_STATUS(0xC0000000 | 0x0104)
+#define NT_STATUS_LOGON_SESSION_COLLISION NT_STATUS(0xC0000000 | 0x0105)
+#define NT_STATUS_NAME_TOO_LONG NT_STATUS(0xC0000000 | 0x0106)
+#define NT_STATUS_FILES_OPEN NT_STATUS(0xC0000000 | 0x0107)
+#define NT_STATUS_CONNECTION_IN_USE NT_STATUS(0xC0000000 | 0x0108)
+#define NT_STATUS_MESSAGE_NOT_FOUND NT_STATUS(0xC0000000 | 0x0109)
+#define NT_STATUS_PROCESS_IS_TERMINATING NT_STATUS(0xC0000000 | 0x010a)
+#define NT_STATUS_INVALID_LOGON_TYPE NT_STATUS(0xC0000000 | 0x010b)
+#define NT_STATUS_NO_GUID_TRANSLATION NT_STATUS(0xC0000000 | 0x010c)
+#define NT_STATUS_CANNOT_IMPERSONATE NT_STATUS(0xC0000000 | 0x010d)
+#define NT_STATUS_IMAGE_ALREADY_LOADED NT_STATUS(0xC0000000 | 0x010e)
+#define NT_STATUS_ABIOS_NOT_PRESENT NT_STATUS(0xC0000000 | 0x010f)
+#define NT_STATUS_ABIOS_LID_NOT_EXIST NT_STATUS(0xC0000000 | 0x0110)
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED NT_STATUS(0xC0000000 | 0x0111)
+#define NT_STATUS_ABIOS_NOT_LID_OWNER NT_STATUS(0xC0000000 | 0x0112)
+#define NT_STATUS_ABIOS_INVALID_COMMAND NT_STATUS(0xC0000000 | 0x0113)
+#define NT_STATUS_ABIOS_INVALID_LID NT_STATUS(0xC0000000 | 0x0114)
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x0115)
+#define NT_STATUS_ABIOS_INVALID_SELECTOR NT_STATUS(0xC0000000 | 0x0116)
+#define NT_STATUS_NO_LDT NT_STATUS(0xC0000000 | 0x0117)
+#define NT_STATUS_INVALID_LDT_SIZE NT_STATUS(0xC0000000 | 0x0118)
+#define NT_STATUS_INVALID_LDT_OFFSET NT_STATUS(0xC0000000 | 0x0119)
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR NT_STATUS(0xC0000000 | 0x011a)
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT NT_STATUS(0xC0000000 | 0x011b)
+#define NT_STATUS_RXACT_INVALID_STATE NT_STATUS(0xC0000000 | 0x011c)
+#define NT_STATUS_RXACT_COMMIT_FAILURE NT_STATUS(0xC0000000 | 0x011d)
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO NT_STATUS(0xC0000000 | 0x011e)
+#define NT_STATUS_TOO_MANY_OPENED_FILES NT_STATUS(0xC0000000 | 0x011f)
+#define NT_STATUS_CANCELLED NT_STATUS(0xC0000000 | 0x0120)
+#define NT_STATUS_CANNOT_DELETE NT_STATUS(0xC0000000 | 0x0121)
+#define NT_STATUS_INVALID_COMPUTER_NAME NT_STATUS(0xC0000000 | 0x0122)
+#define NT_STATUS_FILE_DELETED NT_STATUS(0xC0000000 | 0x0123)
+#define NT_STATUS_SPECIAL_ACCOUNT NT_STATUS(0xC0000000 | 0x0124)
+#define NT_STATUS_SPECIAL_GROUP NT_STATUS(0xC0000000 | 0x0125)
+#define NT_STATUS_SPECIAL_USER NT_STATUS(0xC0000000 | 0x0126)
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x0127)
+#define NT_STATUS_FILE_CLOSED NT_STATUS(0xC0000000 | 0x0128)
+#define NT_STATUS_TOO_MANY_THREADS NT_STATUS(0xC0000000 | 0x0129)
+#define NT_STATUS_THREAD_NOT_IN_PROCESS NT_STATUS(0xC0000000 | 0x012a)
+#define NT_STATUS_TOKEN_ALREADY_IN_USE NT_STATUS(0xC0000000 | 0x012b)
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x012c)
+#define NT_STATUS_COMMITMENT_LIMIT NT_STATUS(0xC0000000 | 0x012d)
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT NT_STATUS(0xC0000000 | 0x012e)
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ NT_STATUS(0xC0000000 | 0x012f)
+#define NT_STATUS_INVALID_IMAGE_PROTECT NT_STATUS(0xC0000000 | 0x0130)
+#define NT_STATUS_INVALID_IMAGE_WIN_16 NT_STATUS(0xC0000000 | 0x0131)
+#define NT_STATUS_LOGON_SERVER_CONFLICT NT_STATUS(0xC0000000 | 0x0132)
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC NT_STATUS(0xC0000000 | 0x0133)
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED NT_STATUS(0xC0000000 | 0x0134)
+#define NT_STATUS_DLL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0135)
+#define NT_STATUS_OPEN_FAILED NT_STATUS(0xC0000000 | 0x0136)
+#define NT_STATUS_IO_PRIVILEGE_FAILED NT_STATUS(0xC0000000 | 0x0137)
+#define NT_STATUS_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0138)
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0139)
+#define NT_STATUS_CONTROL_C_EXIT NT_STATUS(0xC0000000 | 0x013a)
+#define NT_STATUS_LOCAL_DISCONNECT NT_STATUS(0xC0000000 | 0x013b)
+#define NT_STATUS_REMOTE_DISCONNECT NT_STATUS(0xC0000000 | 0x013c)
+#define NT_STATUS_REMOTE_RESOURCES NT_STATUS(0xC0000000 | 0x013d)
+#define NT_STATUS_LINK_FAILED NT_STATUS(0xC0000000 | 0x013e)
+#define NT_STATUS_LINK_TIMEOUT NT_STATUS(0xC0000000 | 0x013f)
+#define NT_STATUS_INVALID_CONNECTION NT_STATUS(0xC0000000 | 0x0140)
+#define NT_STATUS_INVALID_ADDRESS NT_STATUS(0xC0000000 | 0x0141)
+#define NT_STATUS_DLL_INIT_FAILED NT_STATUS(0xC0000000 | 0x0142)
+#define NT_STATUS_MISSING_SYSTEMFILE NT_STATUS(0xC0000000 | 0x0143)
+#define NT_STATUS_UNHANDLED_EXCEPTION NT_STATUS(0xC0000000 | 0x0144)
+#define NT_STATUS_APP_INIT_FAILURE NT_STATUS(0xC0000000 | 0x0145)
+#define NT_STATUS_PAGEFILE_CREATE_FAILED NT_STATUS(0xC0000000 | 0x0146)
+#define NT_STATUS_NO_PAGEFILE NT_STATUS(0xC0000000 | 0x0147)
+#define NT_STATUS_INVALID_LEVEL NT_STATUS(0xC0000000 | 0x0148)
+#define NT_STATUS_WRONG_PASSWORD_CORE NT_STATUS(0xC0000000 | 0x0149)
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT NT_STATUS(0xC0000000 | 0x014a)
+#define NT_STATUS_PIPE_BROKEN NT_STATUS(0xC0000000 | 0x014b)
+#define NT_STATUS_REGISTRY_CORRUPT NT_STATUS(0xC0000000 | 0x014c)
+#define NT_STATUS_REGISTRY_IO_FAILED NT_STATUS(0xC0000000 | 0x014d)
+#define NT_STATUS_NO_EVENT_PAIR NT_STATUS(0xC0000000 | 0x014e)
+#define NT_STATUS_UNRECOGNIZED_VOLUME NT_STATUS(0xC0000000 | 0x014f)
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED NT_STATUS(0xC0000000 | 0x0150)
+#define NT_STATUS_NO_SUCH_ALIAS NT_STATUS(0xC0000000 | 0x0151)
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS NT_STATUS(0xC0000000 | 0x0152)
+#define NT_STATUS_MEMBER_IN_ALIAS NT_STATUS(0xC0000000 | 0x0153)
+#define NT_STATUS_ALIAS_EXISTS NT_STATUS(0xC0000000 | 0x0154)
+#define NT_STATUS_LOGON_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0155)
+#define NT_STATUS_TOO_MANY_SECRETS NT_STATUS(0xC0000000 | 0x0156)
+#define NT_STATUS_SECRET_TOO_LONG NT_STATUS(0xC0000000 | 0x0157)
+#define NT_STATUS_INTERNAL_DB_ERROR NT_STATUS(0xC0000000 | 0x0158)
+#define NT_STATUS_FULLSCREEN_MODE NT_STATUS(0xC0000000 | 0x0159)
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS NT_STATUS(0xC0000000 | 0x015a)
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED NT_STATUS(0xC0000000 | 0x015b)
+#define NT_STATUS_NOT_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x015c)
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x015d)
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR NT_STATUS(0xC0000000 | 0x015e)
+#define NT_STATUS_FT_MISSING_MEMBER NT_STATUS(0xC0000000 | 0x015f)
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY NT_STATUS(0xC0000000 | 0x0160)
+#define NT_STATUS_ILLEGAL_CHARACTER NT_STATUS(0xC0000000 | 0x0161)
+#define NT_STATUS_UNMAPPABLE_CHARACTER NT_STATUS(0xC0000000 | 0x0162)
+#define NT_STATUS_UNDEFINED_CHARACTER NT_STATUS(0xC0000000 | 0x0163)
+#define NT_STATUS_FLOPPY_VOLUME NT_STATUS(0xC0000000 | 0x0164)
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND NT_STATUS(0xC0000000 | 0x0165)
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER NT_STATUS(0xC0000000 | 0x0166)
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR NT_STATUS(0xC0000000 | 0x0167)
+#define NT_STATUS_FLOPPY_BAD_REGISTERS NT_STATUS(0xC0000000 | 0x0168)
+#define NT_STATUS_DISK_RECALIBRATE_FAILED NT_STATUS(0xC0000000 | 0x0169)
+#define NT_STATUS_DISK_OPERATION_FAILED NT_STATUS(0xC0000000 | 0x016a)
+#define NT_STATUS_DISK_RESET_FAILED NT_STATUS(0xC0000000 | 0x016b)
+#define NT_STATUS_SHARED_IRQ_BUSY NT_STATUS(0xC0000000 | 0x016c)
+#define NT_STATUS_FT_ORPHANING NT_STATUS(0xC0000000 | 0x016d)
+#define NT_STATUS_PARTITION_FAILURE NT_STATUS(0xC0000000 | 0x0172)
+#define NT_STATUS_INVALID_BLOCK_LENGTH NT_STATUS(0xC0000000 | 0x0173)
+#define NT_STATUS_DEVICE_NOT_PARTITIONED NT_STATUS(0xC0000000 | 0x0174)
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA NT_STATUS(0xC0000000 | 0x0175)
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA NT_STATUS(0xC0000000 | 0x0176)
+#define NT_STATUS_EOM_OVERFLOW NT_STATUS(0xC0000000 | 0x0177)
+#define NT_STATUS_NO_MEDIA NT_STATUS(0xC0000000 | 0x0178)
+#define NT_STATUS_NO_SUCH_MEMBER NT_STATUS(0xC0000000 | 0x017a)
+#define NT_STATUS_INVALID_MEMBER NT_STATUS(0xC0000000 | 0x017b)
+#define NT_STATUS_KEY_DELETED NT_STATUS(0xC0000000 | 0x017c)
+#define NT_STATUS_NO_LOG_SPACE NT_STATUS(0xC0000000 | 0x017d)
+#define NT_STATUS_TOO_MANY_SIDS NT_STATUS(0xC0000000 | 0x017e)
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x017f)
+#define NT_STATUS_KEY_HAS_CHILDREN NT_STATUS(0xC0000000 | 0x0180)
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE NT_STATUS(0xC0000000 | 0x0181)
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR NT_STATUS(0xC0000000 | 0x0182)
+#define NT_STATUS_DRIVER_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x0183)
+#define NT_STATUS_INVALID_DEVICE_STATE NT_STATUS(0xC0000000 | 0x0184)
+#define NT_STATUS_IO_DEVICE_ERROR NT_STATUS(0xC0000000 | 0x0185)
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR NT_STATUS(0xC0000000 | 0x0186)
+#define NT_STATUS_BACKUP_CONTROLLER NT_STATUS(0xC0000000 | 0x0187)
+#define NT_STATUS_LOG_FILE_FULL NT_STATUS(0xC0000000 | 0x0188)
+#define NT_STATUS_TOO_LATE NT_STATUS(0xC0000000 | 0x0189)
+#define NT_STATUS_NO_TRUST_LSA_SECRET NT_STATUS(0xC0000000 | 0x018a)
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT NT_STATUS(0xC0000000 | 0x018b)
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE NT_STATUS(0xC0000000 | 0x018c)
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE NT_STATUS(0xC0000000 | 0x018d)
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT NT_STATUS(0xC0000000 | 0x018e)
+#define NT_STATUS_EVENTLOG_CANT_START NT_STATUS(0xC0000000 | 0x018f)
+#define NT_STATUS_TRUST_FAILURE NT_STATUS(0xC0000000 | 0x0190)
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0191)
+#define NT_STATUS_NETLOGON_NOT_STARTED NT_STATUS(0xC0000000 | 0x0192)
+#define NT_STATUS_ACCOUNT_EXPIRED NT_STATUS(0xC0000000 | 0x0193)
+#define NT_STATUS_POSSIBLE_DEADLOCK NT_STATUS(0xC0000000 | 0x0194)
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT NT_STATUS(0xC0000000 | 0x0195)
+#define NT_STATUS_REMOTE_SESSION_LIMIT NT_STATUS(0xC0000000 | 0x0196)
+#define NT_STATUS_EVENTLOG_FILE_CHANGED NT_STATUS(0xC0000000 | 0x0197)
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0198)
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0199)
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x019a)
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT NT_STATUS(0xC0000000 | 0x019b)
+#define NT_STATUS_FS_DRIVER_REQUIRED NT_STATUS(0xC0000000 | 0x019c)
+#define NT_STATUS_NO_USER_SESSION_KEY NT_STATUS(0xC0000000 | 0x0202)
+#define NT_STATUS_USER_SESSION_DELETED NT_STATUS(0xC0000000 | 0x0203)
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND NT_STATUS(0xC0000000 | 0x0204)
+#define NT_STATUS_INSUFF_SERVER_RESOURCES NT_STATUS(0xC0000000 | 0x0205)
+#define NT_STATUS_INVALID_BUFFER_SIZE NT_STATUS(0xC0000000 | 0x0206)
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT NT_STATUS(0xC0000000 | 0x0207)
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD NT_STATUS(0xC0000000 | 0x0208)
+#define NT_STATUS_TOO_MANY_ADDRESSES NT_STATUS(0xC0000000 | 0x0209)
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS NT_STATUS(0xC0000000 | 0x020a)
+#define NT_STATUS_ADDRESS_CLOSED NT_STATUS(0xC0000000 | 0x020b)
+#define NT_STATUS_CONNECTION_DISCONNECTED NT_STATUS(0xC0000000 | 0x020c)
+#define NT_STATUS_CONNECTION_RESET NT_STATUS(0xC0000000 | 0x020d)
+#define NT_STATUS_TOO_MANY_NODES NT_STATUS(0xC0000000 | 0x020e)
+#define NT_STATUS_TRANSACTION_ABORTED NT_STATUS(0xC0000000 | 0x020f)
+#define NT_STATUS_TRANSACTION_TIMED_OUT NT_STATUS(0xC0000000 | 0x0210)
+#define NT_STATUS_TRANSACTION_NO_RELEASE NT_STATUS(0xC0000000 | 0x0211)
+#define NT_STATUS_TRANSACTION_NO_MATCH NT_STATUS(0xC0000000 | 0x0212)
+#define NT_STATUS_TRANSACTION_RESPONDED NT_STATUS(0xC0000000 | 0x0213)
+#define NT_STATUS_TRANSACTION_INVALID_ID NT_STATUS(0xC0000000 | 0x0214)
+#define NT_STATUS_TRANSACTION_INVALID_TYPE NT_STATUS(0xC0000000 | 0x0215)
+#define NT_STATUS_NOT_SERVER_SESSION NT_STATUS(0xC0000000 | 0x0216)
+#define NT_STATUS_NOT_CLIENT_SESSION NT_STATUS(0xC0000000 | 0x0217)
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x0218)
+#define NT_STATUS_DEBUG_ATTACH_FAILED NT_STATUS(0xC0000000 | 0x0219)
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED NT_STATUS(0xC0000000 | 0x021a)
+#define NT_STATUS_DATA_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x021b)
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND NT_STATUS(0xC0000000 | 0x021c)
+#define NT_STATUS_VDM_HARD_ERROR NT_STATUS(0xC0000000 | 0x021d)
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT NT_STATUS(0xC0000000 | 0x021e)
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH NT_STATUS(0xC0000000 | 0x021f)
+#define NT_STATUS_MAPPED_ALIGNMENT NT_STATUS(0xC0000000 | 0x0220)
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH NT_STATUS(0xC0000000 | 0x0221)
+#define NT_STATUS_LOST_WRITEBEHIND_DATA NT_STATUS(0xC0000000 | 0x0222)
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID NT_STATUS(0xC0000000 | 0x0223)
+#define NT_STATUS_PASSWORD_MUST_CHANGE NT_STATUS(0xC0000000 | 0x0224)
+#define NT_STATUS_NOT_FOUND NT_STATUS(0xC0000000 | 0x0225)
+#define NT_STATUS_NOT_TINY_STREAM NT_STATUS(0xC0000000 | 0x0226)
+#define NT_STATUS_RECOVERY_FAILURE NT_STATUS(0xC0000000 | 0x0227)
+#define NT_STATUS_STACK_OVERFLOW_READ NT_STATUS(0xC0000000 | 0x0228)
+#define NT_STATUS_FAIL_CHECK NT_STATUS(0xC0000000 | 0x0229)
+#define NT_STATUS_DUPLICATE_OBJECTID NT_STATUS(0xC0000000 | 0x022a)
+#define NT_STATUS_OBJECTID_EXISTS NT_STATUS(0xC0000000 | 0x022b)
+#define NT_STATUS_CONVERT_TO_LARGE NT_STATUS(0xC0000000 | 0x022c)
+#define NT_STATUS_RETRY NT_STATUS(0xC0000000 | 0x022d)
+#define NT_STATUS_FOUND_OUT_OF_SCOPE NT_STATUS(0xC0000000 | 0x022e)
+#define NT_STATUS_ALLOCATE_BUCKET NT_STATUS(0xC0000000 | 0x022f)
+#define NT_STATUS_PROPSET_NOT_FOUND NT_STATUS(0xC0000000 | 0x0230)
+#define NT_STATUS_MARSHALL_OVERFLOW NT_STATUS(0xC0000000 | 0x0231)
+#define NT_STATUS_INVALID_VARIANT NT_STATUS(0xC0000000 | 0x0232)
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND NT_STATUS(0xC0000000 | 0x0233)
+#define NT_STATUS_ACCOUNT_LOCKED_OUT NT_STATUS(0xC0000000 | 0x0234)
+#define NT_STATUS_HANDLE_NOT_CLOSABLE NT_STATUS(0xC0000000 | 0x0235)
+#define NT_STATUS_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0236)
+#define NT_STATUS_GRACEFUL_DISCONNECT NT_STATUS(0xC0000000 | 0x0237)
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED NT_STATUS(0xC0000000 | 0x0238)
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED NT_STATUS(0xC0000000 | 0x0239)
+#define NT_STATUS_CONNECTION_INVALID NT_STATUS(0xC0000000 | 0x023a)
+#define NT_STATUS_CONNECTION_ACTIVE NT_STATUS(0xC0000000 | 0x023b)
+#define NT_STATUS_NETWORK_UNREACHABLE NT_STATUS(0xC0000000 | 0x023c)
+#define NT_STATUS_HOST_UNREACHABLE NT_STATUS(0xC0000000 | 0x023d)
+#define NT_STATUS_PROTOCOL_UNREACHABLE NT_STATUS(0xC0000000 | 0x023e)
+#define NT_STATUS_PORT_UNREACHABLE NT_STATUS(0xC0000000 | 0x023f)
+#define NT_STATUS_REQUEST_ABORTED NT_STATUS(0xC0000000 | 0x0240)
+#define NT_STATUS_CONNECTION_ABORTED NT_STATUS(0xC0000000 | 0x0241)
+#define NT_STATUS_BAD_COMPRESSION_BUFFER NT_STATUS(0xC0000000 | 0x0242)
+#define NT_STATUS_USER_MAPPED_FILE NT_STATUS(0xC0000000 | 0x0243)
+#define NT_STATUS_AUDIT_FAILED NT_STATUS(0xC0000000 | 0x0244)
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET NT_STATUS(0xC0000000 | 0x0245)
+#define NT_STATUS_CONNECTION_COUNT_LIMIT NT_STATUS(0xC0000000 | 0x0246)
+#define NT_STATUS_LOGIN_TIME_RESTRICTION NT_STATUS(0xC0000000 | 0x0247)
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION NT_STATUS(0xC0000000 | 0x0248)
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH NT_STATUS(0xC0000000 | 0x0249)
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO NT_STATUS(0xC0000000 | 0x0250)
+#define NT_STATUS_BAD_DLL_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0251)
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0252)
+#define NT_STATUS_LPC_REPLY_LOST NT_STATUS(0xC0000000 | 0x0253)
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 NT_STATUS(0xC0000000 | 0x0254)
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 NT_STATUS(0xC0000000 | 0x0255)
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT NT_STATUS(0xC0000000 | 0x0256)
+#define NT_STATUS_PATH_NOT_COVERED NT_STATUS(0xC0000000 | 0x0257)
+#define NT_STATUS_NO_CALLBACK_ACTIVE NT_STATUS(0xC0000000 | 0x0258)
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0259)
+#define NT_STATUS_PWD_TOO_SHORT NT_STATUS(0xC0000000 | 0x025a)
+#define NT_STATUS_PWD_TOO_RECENT NT_STATUS(0xC0000000 | 0x025b)
+#define NT_STATUS_PWD_HISTORY_CONFLICT NT_STATUS(0xC0000000 | 0x025c)
+#define NT_STATUS_PLUGPLAY_NO_DEVICE NT_STATUS(0xC0000000 | 0x025e)
+#define NT_STATUS_UNSUPPORTED_COMPRESSION NT_STATUS(0xC0000000 | 0x025f)
+#define NT_STATUS_INVALID_HW_PROFILE NT_STATUS(0xC0000000 | 0x0260)
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH NT_STATUS(0xC0000000 | 0x0261)
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0262)
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0263)
+#define NT_STATUS_RESOURCE_NOT_OWNED NT_STATUS(0xC0000000 | 0x0264)
+#define NT_STATUS_TOO_MANY_LINKS NT_STATUS(0xC0000000 | 0x0265)
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT NT_STATUS(0xC0000000 | 0x0266)
+#define NT_STATUS_FILE_IS_OFFLINE NT_STATUS(0xC0000000 | 0x0267)
+#define NT_STATUS_NO_SUCH_JOB NT_STATUS(0xC0000000 | 0xEDE) /* scheduler */
+
+
+/* I use NT_STATUS_FOOBAR when I have no idea what error code to use -
+ * this means we need a torture test */
+#define NT_STATUS_FOOBAR NT_STATUS_UNSUCCESSFUL
+
+#endif /* _NTERR_H */
+
+
diff --git a/source4/include/ntlmssp.h b/source4/include/ntlmssp.h
new file mode 100644 (file)
index 0000000..f0278ff
--- /dev/null
@@ -0,0 +1,133 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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.
+*/
+
+/* NTLMSSP mode */
+enum NTLMSSP_ROLE
+{
+       NTLMSSP_SERVER,
+       NTLMSSP_CLIENT
+};
+
+/* NTLMSSP message types */
+enum NTLM_MESSAGE_TYPE
+{
+       NTLMSSP_NEGOTIATE = 1,
+       NTLMSSP_CHALLENGE = 2,
+       NTLMSSP_AUTH      = 3,
+       NTLMSSP_UNKNOWN   = 4
+};
+
+/* NTLMSSP negotiation flags */
+#define NTLMSSP_NEGOTIATE_UNICODE          0x00000001
+#define NTLMSSP_NEGOTIATE_OEM              0x00000002
+#define NTLMSSP_REQUEST_TARGET             0x00000004
+#define NTLMSSP_NEGOTIATE_SIGN             0x00000010 /* Message integrity */
+#define NTLMSSP_NEGOTIATE_SEAL             0x00000020 /* Message confidentiality */
+#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE   0x00000040
+#define NTLMSSP_NEGOTIATE_LM_KEY           0x00000080
+#define NTLMSSP_NEGOTIATE_NETWARE          0x00000100
+#define NTLMSSP_NEGOTIATE_NTLM             0x00000200
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED  0x00001000
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
+#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL  0x00004000
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN      0x00008000
+#define NTLMSSP_TARGET_TYPE_DOMAIN            0x10000
+#define NTLMSSP_TARGET_TYPE_SERVER            0x20000
+#define NTLMSSP_CHAL_INIT_RESPONSE         0x00010000
+
+#define NTLMSSP_CHAL_ACCEPT_RESPONSE       0x00020000
+#define NTLMSSP_CHAL_NON_NT_SESSION_KEY    0x00040000
+#define NTLMSSP_NEGOTIATE_NTLM2            0x00080000
+#define NTLMSSP_CHAL_TARGET_INFO           0x00800000
+#define NTLMSSP_NEGOTIATE_128              0x20000000 /* 128-bit encryption */
+#define NTLMSSP_NEGOTIATE_KEY_EXCH         0x40000000
+#define NTLMSSP_NEGOTIATE_080000000        0x80000000
+
+#define NTLMSSP_NAME_TYPE_DOMAIN      0x01
+#define NTLMSSP_NAME_TYPE_SERVER      0x02
+#define NTLMSSP_NAME_TYPE_DOMAIN_DNS  0x03
+#define NTLMSSP_NAME_TYPE_SERVER_DNS  0x04
+
+typedef struct ntlmssp_state 
+{
+       TALLOC_CTX *mem_ctx;
+       enum NTLMSSP_ROLE role;
+       BOOL unicode;
+       char *user;
+       char *domain;
+       char *workstation;
+       DATA_BLOB lm_resp;
+       DATA_BLOB nt_resp;
+       DATA_BLOB chal;
+       void *auth_context;
+       const uint8 *(*get_challenge)(struct ntlmssp_state *ntlmssp_state);
+       NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state);
+
+       const char *(*get_global_myname)(void);
+       const char *(*get_domain)(void);
+
+       int server_role;
+       uint32 expected_state;
+} NTLMSSP_STATE;
+
+typedef struct ntlmssp_client_state 
+{
+       TALLOC_CTX *mem_ctx;
+       unsigned int ref_count;
+
+       BOOL unicode;
+       BOOL use_ntlmv2;
+       char *user;
+       char *domain;
+       char *workstation;
+       char *password;
+
+       const char *(*get_global_myname)(void);
+       const char *(*get_domain)(void);
+
+       DATA_BLOB chal;
+       DATA_BLOB lm_resp;
+       DATA_BLOB nt_resp;
+       DATA_BLOB session_key;
+       
+       uint32 neg_flags;
+       
+       /* SMB Signing */
+       
+       uint32 ntlmssp_seq_num;
+
+       /* ntlmv2 */
+       char cli_sign_const[16];
+       char cli_seal_const[16];
+       char srv_sign_const[16];
+       char srv_seal_const[16];
+
+       unsigned char cli_sign_hash[258];
+       unsigned char cli_seal_hash[258];
+       unsigned char srv_sign_hash[258];
+       unsigned char srv_seal_hash[258];
+
+       /* ntlmv1 */
+       unsigned char ntlmssp_hash[258];
+
+} NTLMSSP_CLIENT_STATE;
+
diff --git a/source4/include/ntvfs.h b/source4/include/ntvfs.h
new file mode 100644 (file)
index 0000000..edec2a7
--- /dev/null
@@ -0,0 +1,86 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NTVFS structures and defines
+   Copyright (C) Andrew Tridgell                       2003
+   
+   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.
+*/
+
+/* modules can use the following to determine if the interface has changed */
+#define NTVFS_INTERFACE_VERSION 1
+
+
+
+/* each backend has to be one one of the following 3 basic types. In
+ * earlier versions of Samba backends needed to handle all types, now
+ * we implement them separately. */
+enum ntvfs_type {NTVFS_DISK, NTVFS_PRINT, NTVFS_IPC};
+
+
+/* the ntvfs operations structure - contains function pointers to 
+   the backend implementations of each operation */
+struct ntvfs_ops {
+       /* initial setup */
+       NTSTATUS (*connect)(struct request_context *req, const char *sharename);
+       NTSTATUS (*disconnect)(struct tcon_context *conn);
+
+       /* path operations */
+       NTSTATUS (*unlink)(struct request_context *req, struct smb_unlink *unl);
+       NTSTATUS (*chkpath)(struct request_context *req, struct smb_chkpath *cp);
+       NTSTATUS (*qpathinfo)(struct request_context *req, union smb_fileinfo *st);
+       NTSTATUS (*setpathinfo)(struct request_context *req, union smb_setfileinfo *st);
+       NTSTATUS (*open)(struct request_context *req, union smb_open *oi);
+       NTSTATUS (*mkdir)(struct request_context *req, union smb_mkdir *md);
+       NTSTATUS (*rmdir)(struct request_context *req, struct smb_rmdir *rd);
+       NTSTATUS (*rename)(struct request_context *req, struct smb_rename *ren);
+       NTSTATUS (*copy)(struct request_context *req, struct smb_copy *cp);
+
+       /* directory search */
+       NTSTATUS (*search_first)(struct request_context *req, union smb_search_first *io, void *private,
+                                BOOL (*callback)(void *private, union smb_search_data *file));
+       NTSTATUS (*search_next)(struct request_context *req, union smb_search_next *io, void *private,
+                                BOOL (*callback)(void *private, union smb_search_data *file));
+       NTSTATUS (*search_close)(struct request_context *req, union smb_search_close *io);
+
+       /* operations on open files */
+       NTSTATUS (*ioctl)(struct request_context *req, struct smb_ioctl *io);
+       NTSTATUS (*read)(struct request_context *req, union smb_read *io);
+       NTSTATUS (*write)(struct request_context *req, union smb_write *io);
+       NTSTATUS (*seek)(struct request_context *req, struct smb_seek *io);
+       NTSTATUS (*flush)(struct request_context *req, struct smb_flush *flush);
+       NTSTATUS (*close)(struct request_context *req, union smb_close *io);
+       NTSTATUS (*exit)(struct request_context *req);
+       NTSTATUS (*lock)(struct request_context *req, union smb_lock *lck);
+       NTSTATUS (*setfileinfo)(struct request_context *req, union smb_setfileinfo *info);
+       NTSTATUS (*qfileinfo)(struct request_context *req, union smb_fileinfo *info);
+
+       /* filesystem operations */
+       NTSTATUS (*fsinfo)(struct request_context *req, union smb_fsinfo *fs);
+
+       /* printing specific operations */
+       NTSTATUS (*lpq)(struct request_context *req, union smb_lpq *lpq);
+
+       /* trans interfaces - only used by CIFS backend to prover complete passthru for testing */
+       NTSTATUS (*trans2)(struct request_context *req, struct smb_trans2 *trans2);
+};
+
+
+/* this structure is used by backends to determine the size of some critical types */
+struct ntvfs_critical_sizes {
+       int sizeof_ntvfs_ops;
+       int sizeof_SMB_OFF_T;
+       int sizeof_tcon_context;
+       int sizeof_request_context;
+};
diff --git a/source4/include/passdb.h b/source4/include/passdb.h
new file mode 100644 (file)
index 0000000..06409aa
--- /dev/null
@@ -0,0 +1,155 @@
+/* 
+   Unix SMB/CIFS implementation.
+   passdb structures and parameters
+   Copyright (C) Gerald Carter 2001
+   Copyright (C) Luke Kenneth Casson Leighton 1998 - 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.
+*/
+
+#ifndef _PASSDB_H
+#define _PASSDB_H
+
+
+/*****************************************************************
+ Functions to be implemented by the new (v2) passdb API 
+****************************************************************/
+
+/*
+ * This next constant specifies the version number of the PASSDB interface
+ * this SAMBA will load. Increment this if *ANY* changes are made to the interface. 
+ */
+
+#define PASSDB_INTERFACE_VERSION 4
+
+typedef struct pdb_context 
+{
+       struct pdb_methods *pdb_methods;
+       struct pdb_methods *pwent_methods;
+       
+       /* These functions are wrappers for the functions listed above.
+          They may do extra things like re-reading a SAM_ACCOUNT on update */
+
+       NTSTATUS (*pdb_setsampwent)(struct pdb_context *, BOOL update);
+       
+       void (*pdb_endsampwent)(struct pdb_context *);
+       
+       NTSTATUS (*pdb_getsampwent)(struct pdb_context *, SAM_ACCOUNT *user);
+       
+       NTSTATUS (*pdb_getsampwnam)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const char *username);
+       
+       NTSTATUS (*pdb_getsampwsid)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const DOM_SID *sid);
+       
+       NTSTATUS (*pdb_add_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+       
+       NTSTATUS (*pdb_update_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+       
+       NTSTATUS (*pdb_delete_sam_account)(struct pdb_context *, SAM_ACCOUNT *username);
+
+       NTSTATUS (*pdb_getgrsid)(struct pdb_context *context, GROUP_MAP *map,
+                                DOM_SID sid, BOOL with_priv);
+       
+       NTSTATUS (*pdb_getgrgid)(struct pdb_context *context, GROUP_MAP *map,
+                                gid_t gid, BOOL with_priv);
+       
+       NTSTATUS (*pdb_getgrnam)(struct pdb_context *context, GROUP_MAP *map,
+                                char *name, BOOL with_priv);
+       
+       NTSTATUS (*pdb_add_group_mapping_entry)(struct pdb_context *context,
+                                               GROUP_MAP *map);
+       
+       NTSTATUS (*pdb_update_group_mapping_entry)(struct pdb_context *context,
+                                                  GROUP_MAP *map);
+       
+       NTSTATUS (*pdb_delete_group_mapping_entry)(struct pdb_context *context,
+                                                  DOM_SID sid);
+       
+       NTSTATUS (*pdb_enum_group_mapping)(struct pdb_context *context,
+                                          enum SID_NAME_USE sid_name_use,
+                                          GROUP_MAP **rmap, int *num_entries,
+                                          BOOL unix_only, BOOL with_priv);
+
+       void (*free_fn)(struct pdb_context **);
+       
+       TALLOC_CTX *mem_ctx;
+       
+} PDB_CONTEXT;
+
+typedef struct pdb_methods 
+{
+       const char *name; /* What name got this module */
+       struct pdb_context *parent;
+
+       /* Use macros from dlinklist.h on these two */
+       struct pdb_methods *next;
+       struct pdb_methods *prev;
+
+       NTSTATUS (*setsampwent)(struct pdb_methods *, BOOL update);
+       
+       void (*endsampwent)(struct pdb_methods *);
+       
+       NTSTATUS (*getsampwent)(struct pdb_methods *, SAM_ACCOUNT *user);
+       
+       NTSTATUS (*getsampwnam)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const char *username);
+       
+       NTSTATUS (*getsampwsid)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const DOM_SID *Sid);
+       
+       NTSTATUS (*add_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+       
+       NTSTATUS (*update_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+       
+       NTSTATUS (*delete_sam_account)(struct pdb_methods *, SAM_ACCOUNT *username);
+       
+       NTSTATUS (*getgrsid)(struct pdb_methods *methods, GROUP_MAP *map,
+                            DOM_SID sid, BOOL with_priv);
+
+       NTSTATUS (*getgrgid)(struct pdb_methods *methods, GROUP_MAP *map,
+                            gid_t gid, BOOL with_priv);
+
+       NTSTATUS (*getgrnam)(struct pdb_methods *methods, GROUP_MAP *map,
+                            char *name, BOOL with_priv);
+
+       NTSTATUS (*add_group_mapping_entry)(struct pdb_methods *methods,
+                                           GROUP_MAP *map);
+
+       NTSTATUS (*update_group_mapping_entry)(struct pdb_methods *methods,
+                                              GROUP_MAP *map);
+
+       NTSTATUS (*delete_group_mapping_entry)(struct pdb_methods *methods,
+                                              DOM_SID sid);
+
+       NTSTATUS (*enum_group_mapping)(struct pdb_methods *methods,
+                                      enum SID_NAME_USE sid_name_use,
+                                      GROUP_MAP **rmap, int *num_entries,
+                                      BOOL unix_only, BOOL with_priv);
+
+       void *private_data;  /* Private data of some kind */
+       
+       void (*free_private_data)(void **);
+
+} PDB_METHODS;
+
+typedef NTSTATUS (*pdb_init_function)(struct pdb_context *, 
+                        struct pdb_methods **, 
+                        const char *);
+
+struct pdb_init_function_entry {
+       const char *name;
+       /* Function to create a member of the pdb_methods list */
+       pdb_init_function init;
+       struct pdb_init_function_entry *prev, *next;
+};
+
+#endif /* _PASSDB_H */
diff --git a/source4/include/popt_common.h b/source4/include/popt_common.h
new file mode 100644 (file)
index 0000000..57850bf
--- /dev/null
@@ -0,0 +1,48 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Common popt arguments
+   Copyright (C) Jelmer Vernooij       2003
+   
+   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.
+*/
+
+#ifndef _POPT_COMMON_H
+#define _POPT_COMMON_H
+
+/* Common popt structures */
+extern struct poptOption popt_common_samba[];
+extern struct poptOption popt_common_connection[];
+extern struct poptOption popt_common_version[];
+extern struct poptOption popt_common_credentials[];
+
+#ifndef POPT_TABLEEND
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+#endif
+
+#define POPT_COMMON_SAMBA { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_samba, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CONNECTION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_connection, 0, "Connection options:", NULL },
+#define POPT_COMMON_VERSION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CREDENTIALS { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_credentials, 0, "Authentication options:", NULL },
+
+struct user_auth_info {
+       pstring username;
+       pstring password;
+       BOOL got_pass;
+       BOOL use_kerberos;
+};
+
+extern struct user_auth_info cmdline_auth_info;
+
+#endif /* _POPT_COMMON_H */
diff --git a/source4/include/printing.h b/source4/include/printing.h
new file mode 100644 (file)
index 0000000..229b2e6
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef PRINTING_H_
+#define PRINTING_H_
+
+/* 
+   Unix SMB/CIFS implementation.
+   printing definitions
+   Copyright (C) Andrew Tridgell 1992-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"
+
+/*
+   This file defines the low-level printing system interfaces used by the
+   SAMBA printing subsystem.
+*/
+
+/* Information for print jobs */
+struct printjob {
+       pid_t pid; /* which process launched the job */
+       int sysjob; /* the system (lp) job number */
+       int fd; /* file descriptor of open file if open */
+       time_t starttime; /* when the job started spooling */
+       int status; /* the status of this job */
+       size_t size; /* the size of the job so far */
+       int page_count; /* then number of pages so far */
+       BOOL spooled; /* has it been sent to the spooler yet? */
+       BOOL smbjob; /* set if the job is a SMB job */
+       fstring filename; /* the filename used to spool the file */
+       fstring jobname; /* the job name given to us by the client */
+       fstring user; /* the user who started the job */
+       fstring queuename; /* service number of printer for this job */
+       NT_DEVICEMODE *nt_devmode;
+};
+
+/* Information for print interfaces */
+struct printif
+{
+  int (*queue_get)(int snum, print_queue_struct **q,
+                   print_status_struct *status);
+  int (*queue_pause)(int snum);
+  int (*queue_resume)(int snum);
+  int (*job_delete)(int snum, struct printjob *pjob);
+  int (*job_pause)(int snum, struct printjob *pjob);
+  int (*job_resume)(int snum, struct printjob *pjob);
+  int (*job_submit)(int snum, struct printjob *pjob);
+};
+
+extern struct printif  generic_printif;
+
+#ifdef HAVE_CUPS
+extern struct printif  cups_printif;
+#endif /* HAVE_CUPS */
+
+/* PRINT_MAX_JOBID is now defined in local.h */
+#define UNIX_JOB_START PRINT_MAX_JOBID
+#define NEXT_JOBID(j) ((j+1) % PRINT_MAX_JOBID > 0 ? (j+1) % PRINT_MAX_JOBID : 1)
+
+#define MAX_CACHE_VALID_TIME 3600
+
+#define PRINT_SPOOL_PREFIX "smbprn."
+#define PRINT_DATABASE_VERSION 5
+
+/* There can be this many printing tdb's open, plus any locked ones. */
+#define MAX_PRINT_DBS_OPEN 1
+
+struct tdb_print_db {
+       struct tdb_print_db *next, *prev;
+       TDB_CONTEXT *tdb;
+       int ref_count;
+       fstring printer_name;
+};
+
+/* 
+ * Used for print notify
+ */
+
+#define NOTIFY_PID_LIST_KEY "NOTIFY_PID_LIST"
+
+
+struct notify_queue {
+       struct notify_queue *next, *prev;
+       struct spoolss_notify_msg *msg;
+       char *buf;
+       size_t buflen;
+};
+
+
+#endif /* PRINTING_H_ */
diff --git a/source4/include/process_model.h b/source4/include/process_model.h
new file mode 100644 (file)
index 0000000..0b8acfc
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process model structures and defines
+   Copyright (C) Andrew Tridgell                       2003
+   Copyright (C) James J Myers                         2003  <myersjj@samba.org>
+   
+   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.
+*/
+
+/* modules can use the following to determine if the interface has changed */
+#define MODEL_INTERFACE_VERSION 1
+
+/* the process model operations structure - contains function pointers to 
+   the model-specific implementations of each operation */
+struct model_ops {
+       /* setup handler functions for select */
+       void (*setup_handlers)(struct smbd_context *smbd, struct socket_select *socket_sel);
+       
+       /* function to reload services if necessary */
+       void (*check_sighup)(struct smbd_context *smbd);
+       
+       /* function to accept new connection */
+       BOOL (*accept_connection)(struct smbd_context *smbd, void **private, 
+               int fd, enum socket_state *state);
+                               
+       /* function to terminate a connection */
+       void (*terminate_connection)( struct server_context *smb, const char *reason);
+       
+       /* function to exit server */
+       void (*exit_server)(struct server_context *smb, const char *reason);
+
+       /* synchronization operations */
+       int (*mutex_init) (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutex_attr);
+       int (*mutex_lock) (pthread_mutex_t *mutex);
+       int (*mutex_unlock) (pthread_mutex_t *mutex);
+       int (*mutex_destroy) (pthread_mutex_t *mutex);
+};
diff --git a/source4/include/pstring.h b/source4/include/pstring.h
new file mode 100644 (file)
index 0000000..92870e4
--- /dev/null
@@ -0,0 +1,36 @@
+/* 
+   samba -- Unix SMB/CIFS implementation.
+   Safe standardized string types
+   
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) John H Terpstra              1996-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Paul Ashton                  1998-2000
+   Copyright (C) Martin Pool                 2002
+   
+   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.
+*/
+
+#ifndef _PSTRING
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+#define _PSTRING
+
+#endif /* ndef _PSTRING */
diff --git a/source4/include/rap.h b/source4/include/rap.h
new file mode 100755 (executable)
index 0000000..993dfa7
--- /dev/null
@@ -0,0 +1,507 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   RAP (SMB Remote Procedure Calls) defines and structures
+   Copyright (C) Steve French 2001  (sfrench@us.ibm.com)
+   Copyright (C) Jim McDonough 2001 (jmcd@us.ibm.com)
+   
+   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.
+*/
+
+#ifndef _RAP_H_
+#define _RAP_H_
+
+/*****************************************************/
+/*                                                   */
+/*   Additional RAP functionality                    */
+/*                                                   */
+/*   RAP is the original SMB RPC, documented         */
+/*   by Microsoft and X/Open in the 1990s and        */
+/*   supported by most SMB/CIFS servers although     */
+/*   it is unlikely that any one implementation      */
+/*   supports all RAP command codes since some       */
+/*   are quite obsolete and a few are specific       */
+/*   to a particular network operating system        */
+/*                                                   */ 
+/*   Although it has largely been replaced           */ 
+/*   for complex remote admistration and management  */
+/*   (of servers) by the relatively newer            */
+/*   DCE/RPC based remote API (which better handles  */
+/*   large >64K data structures), there are many     */
+/*   important administrative and resource location  */
+/*   tasks and user tasks (e.g. password change)     */
+/*   that are performed via RAP.                     */
+/*                                                   */
+/*   Although a few of the RAP calls are implemented */
+/*   in the Samba client library already (clirap.c)  */
+/*   the new ones are in clirap2.c for easy patching */
+/*   and integration and a corresponding header      */
+/*   file, rap.h, has been created.                  */
+/*                                                   */
+/*   This is based on data from the CIFS spec        */
+/*   and the LAN Server and LAN Manager              */
+/*   Programming Reference books and published       */
+/*   RAP document and CIFS forum postings and        */
+/*   lots of trial and error.  Additional            */
+/*   background information is available from the    */
+/*   X/Open reference book in their PC Interworking  */
+/*   series "IPC for SMB" and also from the          */
+/*   interoperability documentation in               */
+/*   ftp://ftp.microsoft.com/developr/drg/cifs       */
+/*                                                   */
+/*   Function names changed from API_ (as they are   */
+/*   in the CIFS specification to RAP_ in order      */
+/*   to avoid confusion with other API calls         */
+/*   sent via DCE RPC                                */
+/*                                                   */
+/*****************************************************/
+
+/*****************************************************/
+/*                                                   */
+/* Although without pound defines (of this header)   */
+/* cifsrap.c already includes support for:           */
+/*                                                   */
+/* WshareEnum (API number 0, level 1)                */
+/* NetServerEnum2 (API num 104, level 1)             */
+/* WWkstaUserLogon (132)                             */
+/* SamOEMchgPasswordUser2_P (214)                    */
+/*                                                   */
+/* and cifsprint.c already includes support for:     */
+/*                                                   */
+/* WPrintJobEnum (API num 76, level 2)               */
+/* WPrintJobDel  (API num 81)                        */
+/*                                                   */
+/*****************************************************/ 
+
+#define RAP_WshareEnum                         0
+#define RAP_WshareGetInfo                      1
+#define RAP_WshareSetInfo                      2
+#define RAP_WshareAdd                          3
+#define RAP_WshareDel                          4
+#define RAP_NetShareCheck                      5
+#define RAP_WsessionEnum                       6
+#define RAP_WsessionGetInfo                    7
+#define RAP_WsessionDel                                8
+#define RAP_WconnectionEnum                    9
+#define RAP_WfileEnum                          10
+#define RAP_WfileGetInfo                       11
+#define RAP_WfileClose                         12
+#define RAP_WserverGetInfo                     13
+#define RAP_WserverSetInfo                     14
+#define RAP_WserverDiskEnum                    15
+#define RAP_WserverAdminCommand                        16
+#define RAP_NetAuditOpen                       17
+#define RAP_WauditClear                                18
+#define RAP_NetErrorLogOpen                    19
+#define RAP_WerrorLogClear                     20
+#define RAP_NetCharDevEnum                     21
+#define RAP_NetCharDevGetInfo                  22
+#define RAP_WCharDevControl                    23
+#define RAP_NetCharDevQEnum                    24
+#define RAP_NetCharDevQGetInfo                 25
+#define RAP_WCharDevQSetInfo                   26
+#define RAP_WCharDevQPurge                     27
+#define RAP_WCharDevQPurgeSelf                 28
+#define RAP_WMessageNameEnum                   29
+#define RAP_WMessageNameGetInfo                30
+#define RAP_WMessageNameAdd                    31
+#define RAP_WMessageNameDel                    32
+#define RAP_WMessageNameFwd                    33
+#define RAP_WMessageNameUnFwd                  34
+#define RAP_WMessageBufferSend                 35
+#define RAP_WMessageFileSend                   36
+#define RAP_WMessageLogFileSet                 37
+#define RAP_WMessageLogFileGet                 38
+#define RAP_WServiceEnum                       39
+#define RAP_WServiceInstall                    40
+#define RAP_WServiceControl                    41
+#define RAP_WAccessEnum                                42
+#define RAP_WAccessGetInfo                     43
+#define RAP_WAccessSetInfo                     44
+#define RAP_WAccessAdd                         45
+#define RAP_WAccessDel                         46
+#define RAP_WGroupEnum                         47
+#define RAP_WGroupAdd                          48
+#define RAP_WGroupDel                          49
+#define RAP_WGroupAddUser                      50
+#define RAP_WGroupDelUser                      51
+#define RAP_WGroupGetUsers                     52
+#define RAP_WUserEnum                          53
+#define RAP_WUserAdd                           54
+#define RAP_WUserDel                           55
+#define RAP_WUserGetInfo                       56
+#define RAP_WUserSetInfo                       57
+#define RAP_WUserPasswordSet                   58
+#define RAP_WUserGetGroups                     59
+#define RAP_WWkstaSetUID                       62
+#define RAP_WWkstaGetInfo                      63
+#define RAP_WWkstaSetInfo                      64
+#define RAP_WUseEnum                           65
+#define RAP_WUseAdd                            66
+#define RAP_WUseDel                            67
+#define RAP_WUseGetInfo                                68
+#define RAP_WPrintQEnum                                69
+#define RAP_WPrintQGetInfo                     70
+#define RAP_WPrintQSetInfo                     71
+#define RAP_WPrintQAdd                         72
+#define RAP_WPrintQDel                         73
+#define RAP_WPrintQPause                       74
+#define RAP_WPrintQContinue                    75
+#define RAP_WPrintJobEnum                      76
+#define RAP_WPrintJobGetInfo                   77
+#define RAP_WPrintJobSetInfo_OLD               78
+#define RAP_WPrintJobDel                       81
+#define RAP_WPrintJobPause                     82
+#define RAP_WPrintJobContinue                  83
+#define RAP_WPrintDestEnum                     84
+#define RAP_WPrintDestGetInfo                  85
+#define RAP_WPrintDestControl                  86
+#define RAP_WProfileSave                       87
+#define RAP_WProfileLoad                       88
+#define RAP_WStatisticsGet                     89
+#define RAP_WStatisticsClear                   90
+#define RAP_NetRemoteTOD                       91
+#define RAP_WNetBiosEnum                       92
+#define RAP_WNetBiosGetInfo                    93
+#define RAP_NetServerEnum                      94
+#define RAP_I_NetServerEnum                    95
+#define RAP_WServiceGetInfo                    96
+#define RAP_WPrintQPurge                       103
+#define RAP_NetServerEnum2                     104
+#define RAP_WAccessGetUserPerms                        105
+#define RAP_WGroupGetInfo                      106
+#define RAP_WGroupSetInfo                      107
+#define RAP_WGroupSetUsers                     108
+#define RAP_WUserSetGroups                     109
+#define RAP_WUserModalsGet                     110
+#define RAP_WUserModalsSet                     111
+#define RAP_WFileEnum2                         112
+#define RAP_WUserAdd2                          113
+#define RAP_WUserSetInfo2                      114
+#define RAP_WUserPasswordSet2                  115
+#define RAP_I_NetServerEnum2                   116
+#define RAP_WConfigGet2                                117
+#define RAP_WConfigGetAll2                     118
+#define RAP_WGetDCName                         119
+#define RAP_NetHandleGetInfo                   120
+#define RAP_NetHandleSetInfo                   121
+#define RAP_WStatisticsGet2                    122
+#define RAP_WBuildGetInfo                      123
+#define RAP_WFileGetInfo2                      124
+#define RAP_WFileClose2                                125
+#define RAP_WNetServerReqChallenge             126
+#define RAP_WNetServerAuthenticate             127
+#define RAP_WNetServerPasswordSet              128
+#define RAP_WNetAccountDeltas                  129
+#define RAP_WNetAccountSync                    130
+#define RAP_WUserEnum2                         131
+#define RAP_WWkstaUserLogon                    132
+#define RAP_WWkstaUserLogoff                   133
+#define RAP_WLogonEnum                         134
+#define RAP_WErrorLogRead                      135
+#define RAP_NetPathType                                136
+#define RAP_NetPathCanonicalize                        137
+#define RAP_NetPathCompare                     138
+#define RAP_NetNameValidate                    139
+#define RAP_NetNameCanonicalize                        140
+#define RAP_NetNameCompare                     141
+#define RAP_WAuditRead                         142
+#define RAP_WPrintDestAdd                      143
+#define RAP_WPrintDestSetInfo                  144
+#define RAP_WPrintDestDel                      145
+#define RAP_WUserValidate2                     146
+#define RAP_WPrintJobSetInfo                   147
+#define RAP_TI_NetServerDiskEnum               148
+#define RAP_TI_NetServerDiskGetInfo            149
+#define RAP_TI_FTVerifyMirror                  150
+#define RAP_TI_FTAbortVerify                   151
+#define RAP_TI_FTGetInfo                       152
+#define RAP_TI_FTSetInfo                       153
+#define RAP_TI_FTLockDisk                      154
+#define RAP_TI_FTFixError                      155
+#define RAP_TI_FTAbortFix                      156
+#define RAP_TI_FTDiagnoseError                 157
+#define RAP_TI_FTGetDriveStats                 158
+#define RAP_TI_FTErrorGetInfo                  160
+#define RAP_NetAccessCheck                     163
+#define RAP_NetAlertRaise                      164
+#define RAP_NetAlertStart                      165
+#define RAP_NetAlertStop                       166
+#define RAP_NetAuditWrite                      167
+#define RAP_NetIRemoteAPI                      168
+#define RAP_NetServiceStatus                   169
+#define RAP_NetServerRegister                  170
+#define RAP_NetServerDeregister                        171
+#define RAP_NetSessionEntryMake                        172
+#define RAP_NetSessionEntryClear               173
+#define RAP_NetSessionEntryGetInfo             174
+#define RAP_NetSessionEntrySetInfo             175
+#define RAP_NetConnectionEntryMake             176
+#define RAP_NetConnectionEntryClear            177
+#define RAP_NetConnectionEntrySetInfo          178
+#define RAP_NetConnectionEntryGetInfo          179
+#define RAP_NetFileEntryMake                   180
+#define RAP_NetFileEntryClear                  181
+#define RAP_NetFileEntrySetInfo                        182
+#define RAP_NetFileEntryGetInfo                        183
+#define RAP_AltSrvMessageBufferSend            184
+#define RAP_AltSrvMessageFileSend              185
+#define RAP_wI_NetRplWkstaEnum                 186
+#define RAP_wI_NetRplWkstaGetInfo              187
+#define RAP_wI_NetRplWkstaSetInfo              188
+#define RAP_wI_NetRplWkstaAdd                  189
+#define RAP_wI_NetRplWkstaDel                  190
+#define RAP_wI_NetRplProfileEnum               191
+#define RAP_wI_NetRplProfileGetInfo            192
+#define RAP_wI_NetRplProfileSetInfo            193
+#define RAP_wI_NetRplProfileAdd                        194
+#define RAP_wI_NetRplProfileDel                        195
+#define RAP_wI_NetRplProfileClone              196
+#define RAP_wI_NetRplBaseProfileEnum           197
+#define RAP_WIServerSetInfo                    201
+#define RAP_WPrintDriverEnum                   205
+#define RAP_WPrintQProcessorEnum               206
+#define RAP_WPrintPortEnum                     207
+#define RAP_WNetWriteUpdateLog                 208
+#define RAP_WNetAccountUpdate                  209
+#define RAP_WNetAccountConfirmUpdate           210
+#define RAP_WConfigSet                         211
+#define RAP_WAccountsReplicate                 212                      
+#define RAP_SamOEMChgPasswordUser2_P           214
+#define RAP_NetServerEnum3                     215
+#define RAP_WprintDriverGetInfo                        250
+#define RAP_WprintDriverSetInfo                        251
+#define RAP_WaliasAdd                          252
+#define RAP_WaliasDel                          253
+#define RAP_WaliasGetInfo                      254
+#define RAP_WaliasSetInfo                      255
+#define RAP_WaliasEnum                         256
+#define RAP_WuserGetLogonAsn                   257
+#define RAP_WuserSetLogonAsn                   258
+#define RAP_WuserGetAppSel                     259
+#define RAP_WuserSetAppSel                     260
+#define RAP_WappAdd                            261
+#define RAP_WappDel                            262
+#define RAP_WappGetInfo                                263
+#define RAP_WappSetInfo                                264
+#define RAP_WappEnum                           265
+#define RAP_WUserDCDBInit                      266
+#define RAP_WDASDAdd                           267
+#define RAP_WDASDDel                           268
+#define RAP_WDASDGetInfo                       269
+#define RAP_WDASDSetInfo                       270
+#define RAP_WDASDEnum                          271
+#define RAP_WDASDCheck                         272
+#define RAP_WDASDCtl                           273
+#define RAP_WuserRemoteLogonCheck              274
+#define RAP_WUserPasswordSet3                  275
+#define RAP_WCreateRIPLMachine                 276
+#define RAP_WDeleteRIPLMachine                 277
+#define RAP_WGetRIPLMachineInfo                        278
+#define RAP_WSetRIPLMachineInfo                        279
+#define RAP_WEnumRIPLMachine                   280
+#define RAP_I_ShareAdd                         281
+#define RAP_AliasEnum                          282
+#define RAP_WaccessApply                       283
+#define RAP_WPrt16Query                                284
+#define RAP_WPrt16Set                          285
+#define RAP_WUserDel100                                286
+#define RAP_WUserRemoteLogonCheck2             287
+#define RAP_WRemoteTODSet                      294
+#define RAP_WprintJobMoveAll                   295
+#define RAP_W16AppParmAdd                      296
+#define RAP_W16AppParmDel                      297
+#define RAP_W16AppParmGet                      298
+#define RAP_W16AppParmSet                      299
+#define RAP_W16RIPLMachineCreate               300
+#define RAP_W16RIPLMachineGetInfo              301
+#define RAP_W16RIPLMachineSetInfo              302
+#define RAP_W16RIPLMachineEnum                 303
+#define RAP_W16RIPLMachineListParmEnum         304
+#define RAP_W16RIPLMachClassGetInfo            305
+#define RAP_W16RIPLMachClassEnum               306
+#define RAP_W16RIPLMachClassCreate             307
+#define RAP_W16RIPLMachClassSetInfo            308
+#define RAP_W16RIPLMachClassDelete             309
+#define RAP_W16RIPLMachClassLPEnum             310
+#define RAP_W16RIPLMachineDelete               311
+#define RAP_W16WSLevelGetInfo                  312
+#define RAP_WserverNameAdd                     313
+#define RAP_WserverNameDel                     314
+#define RAP_WserverNameEnum                    315
+#define RAP_I_WDASDEnum                                316
+#define RAP_WDASDEnumTerminate                 317
+#define RAP_WDASDSetInfo2                      318
+#define MAX_API                                        318
+
+
+/* Parameter description strings for RAP calls   */
+/* Names are defined name for RAP call with _REQ */
+/* appended to end.                              */
+
+#define RAP_WFileEnum2_REQ     "zzWrLehb8g8"
+#define RAP_WFileGetInfo2_REQ  "DWrLh"
+#define RAP_WFileClose2_REQ     "D"    
+
+#define RAP_NetGroupEnum_REQ    "WrLeh"
+#define RAP_NetGroupAdd_REQ     "WsT"
+#define RAP_NetGroupDel_REQ     "z"
+#define RAP_NetGroupAddUser_REQ "zz"
+#define RAP_NetGroupDelUser_REQ "zz"
+#define RAP_NetGroupGetUsers_REQ "zWrLeh"
+#define RAP_NetGroupSetUsers_REQ "zWsTW"
+
+#define RAP_NetUserAdd2_REQ       "WsTWW"
+#define RAP_NetUserEnum_REQ       "WrLeh"
+#define RAP_NetUserEnum2_REQ      "WrLDieh"
+#define RAP_NetUserGetGroups_REQ  "zWrLeh"
+#define RAP_NetUserSetGroups_REQ  "zWsTW"
+#define RAP_NetUserPasswordSet_REQ "zb16b16w"
+#define RAP_NetUserPasswordSet2_REQ "zb16b16WW"
+#define RAP_SAMOEMChgPasswordUser2_REQ "B516B16"
+#define RAP_NetUserValidate2_REQ    "Wb62WWrLhWW"
+
+#define RAP_NetServerEnum2_REQ  "WrLehDz"
+#define RAP_WserverGetInfo_REQ  "WrLh"
+#define RAP_NetWkstatGetInfo    "WrLh"
+
+#define RAP_WShareAdd_REQ       "WsT"
+#define RAP_WShareEnum_REQ      "WrLeh"
+#define RAP_WShareDel_REQ       "zW"
+#define RAP_WWkstaGetInfo_REQ   "WrLh"
+
+#define RAP_NetPrintQEnum_REQ   "WrLeh"
+#define RAP_NetPrintQGetInfo_REQ "zWrLh"
+
+#define RAP_NetServerAdminCommand_REQ "zhrLeh"
+#define RAP_NetServiceEnum_REQ  "WrLeh"
+#define RAP_NetServiceControl_REQ "zWWrL"
+#define RAP_NetServiceInstall_REQ "zF88sg88T"
+#define RAP_NetServiceGetInfo_REQ "zWrLh"
+#define RAP_NetSessionEnum_REQ  "WrLeh"
+#define RAP_NetSessionGetInfo_REQ "zWrLh"
+#define RAP_NetSessionDel_REQ   "zW"
+
+#define RAP_NetConnectionEnum_REQ "zWrLeh"
+
+#define RAP_NetWkstaUserLogoff_REQ "zzWb38WrLh"
+
+/* Description strings for returned data in RAP calls */
+/* I use all caps here in part to avoid accidental    */
+/* name collisions */
+
+#define RAP_FILE_INFO_L2        "D"
+#define RAP_FILE_INFO_L3        "DWWzz"
+
+#define RAP_GROUP_INFO_L0       "B21"
+#define RAP_GROUP_INFO_L1       "B21Bz"
+#define RAP_GROUP_USERS_INFO_0  "B21"
+#define RAP_GROUP_USERS_INFO_1  "B21BN"
+
+#define RAP_USER_INFO_L0        "B21"
+#define RAP_USER_INFO_L1        "B21BB16DWzzWz"
+
+#define RAP_SERVER_INFO_L0      "B16"
+#define RAP_SERVER_INFO_L1      "B16BBDz"
+#define RAP_SERVER_INFO_L2 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz"
+#define RAP_SERVER_INFO_L3 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWzDWz"
+#define RAP_SERVICE_INFO_L0     "B16"
+#define RAP_SERVICE_INFO_L2     "B16WDWB64"
+#define RAP_SHARE_INFO_L0       "B13"
+#define RAP_SHARE_INFO_L1      "B13BWz"
+#define RAP_SHARE_INFO_L2      "B13BWzWWWzB9B"
+
+#define RAP_PRINTQ_INFO_L2      "B13BWWWzzzzzWN"
+#define RAP_SMB_PRINT_JOB_L1    "WB21BB16B10zWWzDDz"
+
+#define RAP_SESSION_INFO_L2      "zzWWWDDDz"
+#define RAP_CONNECTION_INFO_L1   "WWWWDzz"
+
+#define RAP_USER_LOGOFF_INFO_L1     "WDW"
+
+#define RAP_WKSTA_INFO_L1       "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW"
+#define RAP_WKSTA_INFO_L10      "zzzBBzz"
+
+/* BB explicit packing would help in structs below */
+
+/* sizes of fixed-length fields, including null terminator */
+#define RAP_GROUPNAME_LEN 21
+#define RAP_USERNAME_LEN 21
+#define RAP_SHARENAME_LEN 13
+#define RAP_UPASSWD_LEN 16 /* user password */
+#define RAP_SPASSWD_LEN 9 /* share password */
+#define RAP_MACHNAME_LEN 16
+#define RAP_SRVCNAME_LEN 16
+#define RAP_SRVCCMNT_LEN 64
+#define RAP_DATATYPE_LEN 10
+
+
+typedef struct rap_group_info_1
+{
+    char   group_name[RAP_GROUPNAME_LEN];
+    char   reserved1;
+    char * comment;
+} RAP_GROUP_INFO_1;
+
+typedef struct rap_user_info_1
+{
+    char   user_name[RAP_USERNAME_LEN];
+    char   reserved1;
+    char   passwrd[RAP_UPASSWD_LEN];
+    uint32 pwage;
+    uint16 priv;
+    char * home_dir;
+    char * comment;
+    uint16 userflags;
+    char * logon_script;
+} RAP_USER_INFO_1;
+
+typedef struct rap_service_info_2
+{
+    char   service_name[RAP_SRVCNAME_LEN];
+    uint16 status;
+    uint32 installcode;
+    uint16 process_num;
+    char * comment;
+} RAP_SERVICE_INFO_2;
+
+
+typedef struct rap_share_info_0
+{
+    char   share_name[RAP_SHARENAME_LEN];
+} RAP_SHARE_INFO_0;
+
+typedef struct rap_share_info_1
+{
+    char   share_name[RAP_SHARENAME_LEN];
+    char   reserved1;
+    uint16 share_type;
+    char * comment;
+} RAP_SHARE_INFO_1;
+
+typedef struct rap_share_info_2
+{
+    char   share_name[RAP_SHARENAME_LEN];
+    char   reserved1;
+    uint16 share_type;
+    char * comment;
+    uint16 perms;
+    uint16 maximum_users;
+    uint16 active_users;
+    char * path;
+    char   password[RAP_SPASSWD_LEN];
+    char   reserved2;
+} RAP_SHARE_INFO_2;
+
+#endif /* _RAP_H_ */
diff --git a/source4/include/rpc_brs.h b/source4/include/rpc_brs.h
new file mode 100644 (file)
index 0000000..cd0928d
--- /dev/null
@@ -0,0 +1,80 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+   
+   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.
+*/
+
+#ifndef _RPC_BRS_H /* _RPC_BRS_H */
+#define _RPC_BRS_H 
+
+
+/* brssvc pipe */
+#define BRS_QUERY_INFO    0x02
+
+
+/* BRS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_brs_query_info_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* unicode server name starting with '\\' */
+
+       uint16 switch_value1;            /* info level 100 (0x64) */
+       /* align */
+       uint16 switch_value2;            /* info level 100 (0x64) */
+
+       uint32 ptr;
+       uint32 pad1;
+       uint32 pad2;
+
+} BRS_Q_QUERY_INFO;
+
+
+/* BRS_INFO_100 - level 100 info */
+typedef struct brs_info_100_info
+{
+       uint32 pad1;
+       uint32 ptr2;
+       uint32 pad2;
+       uint32 pad3;
+
+} BRS_INFO_100;
+
+
+/* BRS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_brs_query_info_info
+{
+       uint16 switch_value1;          /* 100 (0x64) - switch value */
+       /* align */
+       uint16 switch_value2;            /* info level 100 (0x64) */
+
+       /* for now, only level 100 is supported.  this should be an enum container */
+       uint32 ptr_1;              /* pointer 1 */
+
+       union
+       {
+               BRS_INFO_100 *brs100;      /* browser info level 100 */
+               void *id;
+
+       } info;
+
+       NTSTATUS status;             /* return status */
+
+} BRS_R_QUERY_INFO;
+
+#endif /* _RPC_BRS_H */
+
diff --git a/source4/include/rpc_client.h b/source4/include/rpc_client.h
new file mode 100644 (file)
index 0000000..bce9ec7
--- /dev/null
@@ -0,0 +1,28 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Elrond                            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.
+*/
+
+#ifndef _RPC_CLIENT_H
+#define _RPC_CLIENT_H
+
+#if 0  /* JERRY */
+#include "rpc_client_proto.h"
+#endif 
+
+#endif /* _RPC_CLIENT_H */
diff --git a/source4/include/rpc_creds.h b/source4/include/rpc_creds.h
new file mode 100644 (file)
index 0000000..3022b17
--- /dev/null
@@ -0,0 +1,96 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+   
+   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.
+*/
+
+
+#ifndef _RPC_CREDS_H /* _RPC_CREDS_H */
+#define _RPC_CREDS_H 
+
+typedef struct ntuser_creds
+{
+       fstring user_name;
+       fstring domain;
+       struct pwd_info pwd;
+
+       uint32 ntlmssp_flags;
+
+} CREDS_NT;
+
+typedef struct unixuser_creds
+{
+       fstring user_name;
+       fstring requested_name;
+       fstring real_name;
+       BOOL guest;
+
+} CREDS_UNIX;
+
+typedef struct unixsec_creds
+{
+       uint32 uid;
+       uint32 gid;
+       int num_grps;
+       uint32 *grps;
+
+} CREDS_UNIX_SEC;
+
+typedef struct ntsec_creds
+{
+       DOM_SID sid;
+       uint32 num_grps;
+       uint32 *grp_rids;
+
+} CREDS_NT_SEC;
+
+typedef struct user_creds
+{
+       BOOL reuse;
+
+       uint32 ptr_ntc;
+       uint32 ptr_uxc;
+       uint32 ptr_nts;
+       uint32 ptr_uxs;
+       uint32 ptr_ssk;
+
+       CREDS_NT   ntc;
+       CREDS_UNIX uxc;
+
+       CREDS_NT_SEC   nts;
+       CREDS_UNIX_SEC uxs;
+
+       uchar usr_sess_key[16];
+
+} CREDS_HYBRID;
+
+typedef struct cred_command
+{
+       uint16 version;
+       uint16 command;
+       uint32 pid; /* unique process id */
+
+       fstring name;
+
+       uint32 ptr_creds;
+       CREDS_HYBRID *cred;
+
+} CREDS_CMD;
+
+#endif /* _RPC_CREDS_H */
+
diff --git a/source4/include/rpc_dce.h b/source4/include/rpc_dce.h
new file mode 100644 (file)
index 0000000..6a8c650
--- /dev/null
@@ -0,0 +1,320 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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.
+*/
+
+#ifndef _DCE_RPC_H /* _DCE_RPC_H */
+#define _DCE_RPC_H 
+
+#include "rpc_misc.h"  /* this only pulls in STRHDR */
+
+
+/* DCE/RPC packet types */
+
+enum RPC_PKT_TYPE
+{
+       RPC_REQUEST = 0x00,
+       RPC_RESPONSE = 0x02,
+       RPC_FAULT    = 0x03,
+       RPC_BIND     = 0x0B,
+       RPC_BINDACK  = 0x0C,
+       RPC_BINDNACK = 0x0D,
+       RPC_ALTCONT  = 0x0E,
+       RPC_ALTCONTRESP = 0x0F,
+       RPC_BINDRESP = 0x10 /* not the real name!  this is undocumented! */
+};
+
+/* DCE/RPC flags */
+#define RPC_FLG_FIRST 0x01
+#define RPC_FLG_LAST  0x02
+#define RPC_FLG_NOCALL 0x20
+
+#define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1 /* ALWAYS_SIGN|NEG_NTLM|NEG_LM|NEG_SEAL|NEG_SIGN|NEG_UNICODE */
+
+/* NTLMSSP signature version */
+#define NTLMSSP_SIGN_VERSION 0x01
+
+/* NTLMSSP auth type and level. */
+#define NTLMSSP_AUTH_TYPE 0xa
+#define NTLMSSP_AUTH_LEVEL 0x6
+
+/* Maximum PDU fragment size. */
+#define MAX_PDU_FRAG_LEN 0x1630
+/* #define MAX_PDU_FRAG_LEN 0x10b8             this is what w2k sets */
+
+/*
+ * Actual structure of a DCE UUID
+ */
+
+typedef struct rpc_uuid
+{
+  uint32 time_low;
+  uint16 time_mid;
+  uint16 time_hi_and_version;
+  uint8 remaining[8];
+} RPC_UUID;
+
+#define RPC_UUID_LEN 16
+
+/* RPC_IFACE */
+typedef struct rpc_iface_info
+{
+  RPC_UUID uuid;    /* 16 bytes of rpc interface identification */
+  uint32 version;    /* the interface version number */
+
+} RPC_IFACE;
+
+#define RPC_IFACE_LEN (RPC_UUID_LEN + 4)
+
+struct pipe_id_info
+{
+       /* the names appear not to matter: the syntaxes _do_ matter */
+
+       const char *client_pipe;
+       RPC_IFACE abstr_syntax; /* this one is the abstract syntax id */
+
+       const char *server_pipe;  /* this one is the secondary syntax name */
+       RPC_IFACE trans_syntax; /* this one is the primary syntax id */
+};
+
+/* RPC_HDR - dce rpc header */
+typedef struct rpc_hdr_info
+{
+  uint8  major; /* 5 - RPC major version */
+  uint8  minor; /* 0 - RPC minor version */
+  uint8  pkt_type; /* RPC_PKT_TYPE - RPC response packet */
+  uint8  flags; /* DCE/RPC flags */
+  uint8  pack_type[4]; /* 0x1000 0000 - little-endian packed data representation */
+  uint16 frag_len; /* fragment length - data size (bytes) inc header and tail. */
+  uint16 auth_len; /* 0 - authentication length  */
+  uint32 call_id; /* call identifier.  matches 12th uint32 of incoming RPC data. */
+
+} RPC_HDR;
+
+#define RPC_HEADER_LEN 16
+
+/* RPC_HDR_REQ - ms request rpc header */
+typedef struct rpc_hdr_req_info
+{
+  uint32 alloc_hint;   /* allocation hint - data size (bytes) minus header and tail. */
+  uint16 context_id;   /* 0 - presentation context identifier */
+  uint16  opnum;        /* opnum */
+
+} RPC_HDR_REQ;
+
+#define RPC_HDR_REQ_LEN 8
+
+/* RPC_HDR_RESP - ms response rpc header */
+typedef struct rpc_hdr_resp_info
+{
+  uint32 alloc_hint;   /* allocation hint - data size (bytes) minus header and tail. */
+  uint16 context_id;   /* 0 - presentation context identifier */
+  uint8  cancel_count; /* 0 - cancel count */
+  uint8  reserved;     /* 0 - reserved. */
+
+} RPC_HDR_RESP;
+
+#define RPC_HDR_RESP_LEN 8
+
+/* RPC_HDR_FAULT - fault rpc header */
+typedef struct rpc_hdr_fault_info
+{
+  NTSTATUS status;
+  uint32 reserved; /* 0x0000 0000 */
+} RPC_HDR_FAULT;
+
+#define RPC_HDR_FAULT_LEN 8
+
+/* this seems to be the same string name depending on the name of the pipe,
+ * but is more likely to be linked to the interface name
+ * "srvsvc", "\\PIPE\\ntsvcs"
+ * "samr", "\\PIPE\\lsass"
+ * "wkssvc", "\\PIPE\\wksvcs"
+ * "NETLOGON", "\\PIPE\\NETLOGON"
+ */
+/* RPC_ADDR_STR */
+typedef struct rpc_addr_info
+{
+  uint16 len;   /* length of the string including null terminator */
+  fstring str; /* the string above in single byte, null terminated form */
+
+} RPC_ADDR_STR;
+
+/* RPC_HDR_BBA */
+typedef struct rpc_hdr_bba_info
+{
+  uint16 max_tsize;       /* maximum transmission fragment size (0x1630) */
+  uint16 max_rsize;       /* max receive fragment size (0x1630) */
+  uint32 assoc_gid;       /* associated group id (0x0) */
+
+} RPC_HDR_BBA;
+
+#define RPC_HDR_BBA_LEN 8
+
+/* RPC_HDR_AUTHA */
+typedef struct rpc_hdr_autha_info
+{
+       uint16 max_tsize;       /* maximum transmission fragment size (0x1630) */
+       uint16 max_rsize;       /* max receive fragment size (0x1630) */
+
+       uint8 auth_type; /* 0x0a */
+       uint8 auth_level; /* 0x06 */
+       uint8 stub_type_len; /* don't know */
+       uint8 padding; /* padding */
+
+       uint32 unknown; /* 0x0014a0c0 */
+
+} RPC_HDR_AUTHA;
+
+#define RPC_HDR_AUTHA_LEN 12
+
+/* RPC_HDR_AUTH */
+typedef struct rpc_hdr_auth_info
+{
+       uint8 auth_type; /* 0x0a */
+       uint8 auth_level; /* 0x06 */
+       uint8 stub_type_len; /* don't know */
+       uint8 padding; /* padding */
+
+       uint32 unknown; /* pointer */
+
+} RPC_HDR_AUTH;
+
+#define RPC_HDR_AUTH_LEN 8
+
+/* RPC_BIND_REQ - ms req bind */
+typedef struct rpc_bind_req_info
+{
+  RPC_HDR_BBA bba;
+
+  uint32 num_elements;    /* the number of elements (0x1) */
+  uint16 context_id;      /* presentation context identifier (0x0) */
+  uint8 num_syntaxes;     /* the number of syntaxes (has always been 1?)(0x1) */
+
+  RPC_IFACE abstract;     /* num and vers. of interface client is using */
+  RPC_IFACE transfer;     /* num and vers. of interface to use for replies */
+  
+} RPC_HDR_RB;
+
+/* 
+ * The following length is 8 bytes RPC_HDR_BBA_LEN, 8 bytes internals 
+ * (with 3 bytes padding), + 2 x RPC_IFACE_LEN bytes for RPC_IFACE structs.
+ */
+
+#define RPC_HDR_RB_LEN (RPC_HDR_BBA_LEN + 8 + (2*RPC_IFACE_LEN))
+
+/* RPC_RESULTS - can only cope with one reason, right now... */
+typedef struct rpc_results_info
+{
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+  uint8 num_results; /* the number of results (0x01) */
+
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+  uint16 result; /* result (0x00 = accept) */
+  uint16 reason; /* reason (0x00 = no reason specified) */
+
+} RPC_RESULTS;
+
+/* RPC_HDR_BA */
+typedef struct rpc_hdr_ba_info
+{
+  RPC_HDR_BBA bba;
+
+  RPC_ADDR_STR addr    ;  /* the secondary address string, as described earlier */
+  RPC_RESULTS  res     ; /* results and reasons */
+  RPC_IFACE    transfer; /* the transfer syntax from the request */
+
+} RPC_HDR_BA;
+
+/* RPC_AUTH_VERIFIER */
+typedef struct rpc_auth_verif_info
+{
+       fstring signature; /* "NTLMSSP" */
+       uint32  msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) */
+
+} RPC_AUTH_VERIFIER;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NTLMSSP_NEG */
+typedef struct rpc_auth_ntlmssp_neg_info
+{
+       uint32  neg_flgs; /* 0x0000 b2b3 */
+
+       STRHDR hdr_myname; /* offset is against START of this structure */
+       STRHDR hdr_domain; /* offset is against START of this structure */
+
+       fstring myname; /* calling workstation's name */
+       fstring domain; /* calling workstations's domain */
+
+} RPC_AUTH_NTLMSSP_NEG;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind acknowledgement */
+/* RPC_AUTH_NTLMSSP_CHAL */
+typedef struct rpc_auth_ntlmssp_chal_info
+{
+       uint32 unknown_1; /* 0x0000 0000 */
+       uint32 unknown_2; /* 0x0000 0028 */
+       uint32 neg_flags; /* 0x0000 82b1 */
+
+       uint8 challenge[8]; /* ntlm challenge */
+       uint8 reserved [8]; /* zeros */
+
+} RPC_AUTH_NTLMSSP_CHAL;
+
+
+/* RPC_AUTH_NTLMSSP_RESP */
+typedef struct rpc_auth_ntlmssp_resp_info
+{
+       STRHDR hdr_lm_resp; /* 24 byte response */
+       STRHDR hdr_nt_resp; /* 24 byte response */
+       STRHDR hdr_domain;
+       STRHDR hdr_usr;
+       STRHDR hdr_wks;
+       STRHDR hdr_sess_key; /* NULL unless negotiated */
+       uint32 neg_flags; /* 0x0000 82b1 */
+
+       fstring sess_key;
+       fstring wks;
+       fstring user;
+       fstring domain;
+       fstring nt_resp;
+       fstring lm_resp;
+
+} RPC_AUTH_NTLMSSP_RESP;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NTLMSSP_CHK */
+typedef struct rpc_auth_ntlmssp_chk_info
+{
+       uint32 ver; /* 0x0000 0001 */
+       uint32 reserved;
+       uint32 crc32; /* checksum using 0xEDB8 8320 as a polynomial */
+       uint32 seq_num;
+
+} RPC_AUTH_NTLMSSP_CHK;
+
+#define RPC_AUTH_NTLMSSP_CHK_LEN 16
+
+
+#endif /* _DCE_RPC_H */
diff --git a/source4/include/rpc_dfs.h b/source4/include/rpc_dfs.h
new file mode 100644 (file)
index 0000000..39316a5
--- /dev/null
@@ -0,0 +1,197 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba parameters and setup
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996 - 2000
+   Copyright (C) Shirish Kalele 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.
+*/
+
+#ifndef _RPC_DFS_H
+#define _RPC_DFS_H
+
+/* NETDFS pipe: calls */
+#define DFS_EXIST                0x00
+#define DFS_ADD                  0x01
+#define DFS_REMOVE               0x02
+#define DFS_GET_INFO             0x04
+#define DFS_ENUM                 0x05
+
+/* dfsadd flags */
+#define DFSFLAG_ADD_VOLUME           0x00000001
+#define DFSFLAG_RESTORE_VOLUME       0x00000002
+
+typedef struct dfs_q_dfs_exist
+{
+  uint32 dummy;
+}
+DFS_Q_DFS_EXIST;
+
+/* status == 1 if dfs exists. */
+typedef struct dfs_r_dfs_exist
+{
+       uint32 status;          /* Not a WERROR or NTSTATUS code */
+}
+DFS_R_DFS_EXIST;
+
+typedef struct dfs_q_dfs_add
+{
+  uint32 ptr_DfsEntryPath;
+  UNISTR2 DfsEntryPath;
+  uint32 ptr_ServerName;
+  UNISTR2 ServerName;
+  uint32 ptr_ShareName;
+  UNISTR2 ShareName;
+  uint32 ptr_Comment;
+  UNISTR2 Comment;
+  uint32 Flags;
+}
+DFS_Q_DFS_ADD;
+
+typedef struct dfs_r_dfs_add
+{
+  WERROR status;
+}
+DFS_R_DFS_ADD;
+
+/********************************************/
+typedef struct dfs_q_dfs_remove
+{
+  UNISTR2 DfsEntryPath;
+  uint32 ptr_ServerName;
+  UNISTR2 ServerName;
+  uint32 ptr_ShareName;
+  UNISTR2 ShareName;
+}
+DFS_Q_DFS_REMOVE;
+
+typedef struct dfs_r_dfs_remove
+{
+  WERROR status;
+}
+DFS_R_DFS_REMOVE;
+
+/********************************************/
+typedef struct dfs_info_1
+{
+  uint32 ptr_entrypath;
+  UNISTR2 entrypath;
+}
+DFS_INFO_1;
+
+typedef struct dfs_info_2
+{
+  uint32 ptr_entrypath;
+  UNISTR2 entrypath;
+  uint32 ptr_comment;
+  UNISTR2 comment;
+  uint32 state;
+  uint32 num_storages;
+}
+DFS_INFO_2;
+
+typedef struct dfs_storage_info
+{
+  uint32 state;
+  uint32 ptr_servername;
+  UNISTR2 servername;
+  uint32 ptr_sharename;
+  UNISTR2 sharename;
+}
+DFS_STORAGE_INFO;
+
+typedef struct dfs_info_3
+{
+  uint32 ptr_entrypath;
+  UNISTR2 entrypath;
+  uint32 ptr_comment;
+  UNISTR2 comment;
+  uint32 state;
+  uint32 num_storages;
+  uint32 ptr_storages;
+  uint32 num_storage_infos;
+  DFS_STORAGE_INFO* storages;
+}
+DFS_INFO_3;
+
+typedef struct dfs_info_ctr
+{
+  
+  uint32 switch_value;
+  uint32 num_entries;
+  uint32 ptr_dfs_ctr; /* pointer to dfs info union */
+  union
+  {
+    DFS_INFO_1 *info1;
+    DFS_INFO_2 *info2;
+    DFS_INFO_3 *info3;
+  } dfs;
+}
+DFS_INFO_CTR;
+
+typedef struct dfs_q_dfs_get_info
+{
+  UNISTR2 uni_path;
+  
+  uint32 ptr_server;
+  UNISTR2 uni_server;
+
+  uint32 ptr_share;
+  UNISTR2 uni_share;
+  
+  uint32 level;
+}
+DFS_Q_DFS_GET_INFO;
+
+typedef struct dfs_r_dfs_get_info
+{
+  uint32 level;
+  uint32 ptr_ctr;
+  DFS_INFO_CTR ctr;
+  WERROR status;
+}
+DFS_R_DFS_GET_INFO;
+
+typedef struct dfs_q_dfs_enum
+{
+  uint32 level;
+  uint32 maxpreflen;
+  uint32 ptr_buffer;
+  uint32 level2;
+  uint32 ptr_num_entries;
+  uint32 num_entries;
+  uint32 ptr_num_entries2;
+  uint32 num_entries2;
+  ENUM_HND reshnd;
+}
+DFS_Q_DFS_ENUM;
+
+typedef struct dfs_r_dfs_enum
+{
+  DFS_INFO_CTR *ctr;
+  uint32 ptr_buffer;
+  uint32 level;
+  uint32 level2;
+  uint32 ptr_num_entries;
+  uint32 num_entries;
+  uint32 ptr_num_entries2;
+  uint32 num_entries2;
+  ENUM_HND reshnd;
+  WERROR status;
+}
+DFS_R_DFS_ENUM;
+
+#endif  
diff --git a/source4/include/rpc_ds.h b/source4/include/rpc_ds.h
new file mode 100644 (file)
index 0000000..c01d105
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Gerald Carter                 2002
+      
+   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.
+*/
+
+#ifndef _RPC_DS_H /* _RPC_LSA_H */
+#define _RPC_DS_H 
+
+#include "rpc_misc.h"
+
+
+/* Opcodes available on PIPE_LSARPC_DS */
+
+#define DS_GETPRIMDOMINFO      0x00
+
+
+/* macros for RPC's */
+
+#define DSROLE_PRIMARY_DS_RUNNING           0x00000001
+#define DSROLE_PRIMARY_DS_MIXED_MODE        0x00000002
+#define DSROLE_UPGRADE_IN_PROGRESS          0x00000004
+#define DSROLE_PRIMARY_DOMAIN_GUID_PRESENT  0x01000000
+
+typedef struct
+{
+       uint16          machine_role;
+       uint16          unknown;                /* 0x6173 -- maybe just alignment? */
+       
+       uint32          flags;
+       
+       uint32          netbios_ptr;
+       uint32          dnsname_ptr;
+       uint32          forestname_ptr;
+       
+       GUID            domain_guid;
+       
+       UNISTR2 netbios_domain;
+       /* these 2 might be reversed in order.  I can't tell from 
+          my tests as both values are the same --jerry */
+       UNISTR2 dns_domain;
+       UNISTR2 forest_domain;
+} DSROLE_PRIMARY_DOMAIN_INFO_BASIC;
+
+typedef struct
+{
+       DSROLE_PRIMARY_DOMAIN_INFO_BASIC        *basic;
+} DS_DOMINFO_CTR;
+
+/* info levels for ds_getprimdominfo() */
+
+#define DsRolePrimaryDomainInfoBasic           1
+
+
+/* DS_Q_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() request */
+typedef struct 
+{
+       uint16  level;
+} DS_Q_GETPRIMDOMINFO;
+
+/* DS_R_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() response */
+typedef struct 
+{
+       uint32          ptr;
+               
+       uint16          level;
+       uint16          unknown0;       /* 0x455c -- maybe just alignment? */
+
+       DS_DOMINFO_CTR  info;
+       
+       NTSTATUS status;
+} DS_R_GETPRIMDOMINFO;
+
+
+
+
+#endif /* _RPC_DS_H */
diff --git a/source4/include/rpc_lsa.h b/source4/include/rpc_lsa.h
new file mode 100644 (file)
index 0000000..c091e73
--- /dev/null
@@ -0,0 +1,760 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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.
+*/
+
+#ifndef _RPC_LSA_H /* _RPC_LSA_H */
+#define _RPC_LSA_H 
+
+#include "rpc_misc.h"
+
+/* Opcodes available on PIPE_LSARPC */
+
+#define LSA_CLOSE              0x00
+#define LSA_DELETE             0x01
+#define LSA_ENUM_PRIVS         0x02
+#define LSA_QUERYSECOBJ        0x03
+#define LSA_SETSECOBJ          0x04
+#define LSA_CHANGEPASSWORD     0x05
+#define LSA_OPENPOLICY         0x06
+#define LSA_QUERYINFOPOLICY    0x07
+#define LSA_SETINFOPOLICY      0x08
+#define LSA_CLEARAUDITLOG      0x09
+#define LSA_CREATEACCOUNT      0x0a
+#define LSA_ENUM_ACCOUNTS      0x0b
+#define LSA_CREATETRUSTDOM     0x0c
+#define LSA_ENUMTRUSTDOM       0x0d
+#define LSA_LOOKUPNAMES        0x0e
+#define LSA_LOOKUPSIDS         0x0f
+#define LSA_CREATESECRET       0x10
+#define LSA_OPENACCOUNT               0x11
+#define LSA_ENUMPRIVSACCOUNT   0x12
+#define LSA_ADDPRIVS           0x13
+#define LSA_REMOVEPRIVS        0x14
+#define LSA_GETQUOTAS          0x15
+#define LSA_SETQUOTAS          0x16
+#define LSA_GETSYSTEMACCOUNT   0x17
+#define LSA_SETSYSTEMACCOUNT   0x18
+#define LSA_OPENTRUSTDOM       0x19
+#define LSA_QUERYTRUSTDOM      0x1a
+#define LSA_SETINFOTRUSTDOM    0x1b
+#define LSA_OPENSECRET         0x1c
+#define LSA_SETSECRET          0x1d
+#define LSA_QUERYSECRET        0x1e
+#define LSA_LOOKUPPRIVVALUE    0x1f
+#define LSA_LOOKUPPRIVNAME     0x20
+#define LSA_PRIV_GET_DISPNAME  0x21
+#define LSA_DELETEOBJECT       0x22
+#define LSA_ENUMACCTWITHRIGHT  0x23
+#define LSA_ENUMACCTRIGHTS     0x24
+#define LSA_ADDACCTRIGHTS      0x25
+#define LSA_REMOVEACCTRIGHTS   0x26
+#define LSA_QUERYTRUSTDOMINFO  0x27
+#define LSA_SETTRUSTDOMINFO    0x28
+#define LSA_DELETETRUSTDOM     0x29
+#define LSA_STOREPRIVDATA      0x2a
+#define LSA_RETRPRIVDATA       0x2b
+#define LSA_OPENPOLICY2        0x2c
+#define LSA_UNK_GET_CONNUSER   0x2d /* LsaGetConnectedCredentials ? */
+#define LSA_QUERYINFO2         0x2e
+
+/* XXXX these are here to get a compile! */
+#define LSA_LOOKUPRIDS      0xFD
+
+/* DOM_QUERY - info class 3 and 5 LSA Query response */
+typedef struct dom_query_info
+{
+  uint16 uni_dom_max_len; /* domain name string length * 2 */
+  uint16 uni_dom_str_len; /* domain name string length * 2 */
+  uint32 buffer_dom_name; /* undocumented domain name string buffer pointer */
+  uint32 buffer_dom_sid; /* undocumented domain SID string buffer pointer */
+  UNISTR2 uni_domain_name; /* domain name (unicode string) */
+  DOM_SID2 dom_sid; /* domain SID */
+
+} DOM_QUERY;
+
+/* level 5 is same as level 3. */
+typedef DOM_QUERY DOM_QUERY_3;
+typedef DOM_QUERY DOM_QUERY_5;
+
+/* level 2 is auditing settings */
+typedef struct dom_query_2
+{
+       uint32 auditing_enabled;
+       uint32 count1; /* usualy 7, at least on nt4sp4 */
+       uint32 count2; /* the same */
+       uint32 *auditsettings;
+} DOM_QUERY_2;
+
+/* level 6 is server role information */
+typedef struct dom_query_6
+{
+       uint16 server_role; /* 2=backup, 3=primary */
+} DOM_QUERY_6;
+
+typedef struct seq_qos_info
+{
+       uint32 len; /* 12 */
+       uint16 sec_imp_level; /* 0x02 - impersonation level */
+       uint8  sec_ctxt_mode; /* 0x01 - context tracking mode */
+       uint8  effective_only; /* 0x00 - effective only */
+
+} LSA_SEC_QOS;
+
+typedef struct obj_attr_info
+{
+       uint32 len;          /* 0x18 - length (in bytes) inc. the length field. */
+       uint32 ptr_root_dir; /* 0 - root directory (pointer) */
+       uint32 ptr_obj_name; /* 0 - object name (pointer) */
+       uint32 attributes;   /* 0 - attributes (undocumented) */
+       uint32 ptr_sec_desc; /* 0 - security descriptior (pointer) */
+       uint32 ptr_sec_qos;  /* security quality of service */
+       LSA_SEC_QOS *sec_qos;
+
+} LSA_OBJ_ATTR;
+
+/* LSA_Q_OPEN_POL - LSA Query Open Policy */
+typedef struct lsa_q_open_pol_info
+{
+       uint32 ptr;         /* undocumented buffer pointer */
+       uint16 system_name; /* 0x5c - system name */
+       LSA_OBJ_ATTR attr ; /* object attributes */
+
+       uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL;
+
+/* LSA_R_OPEN_POL - response to LSA Open Policy */
+typedef struct lsa_r_open_pol_info
+{
+       POLICY_HND pol; /* policy handle */
+       NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL;
+
+/* LSA_Q_OPEN_POL2 - LSA Query Open Policy */
+typedef struct lsa_q_open_pol2_info
+{
+       uint32       ptr;             /* undocumented buffer pointer */
+       UNISTR2      uni_server_name; /* server name, starting with two '\'s */
+       LSA_OBJ_ATTR attr           ; /* object attributes */
+
+       uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL2;
+
+/* LSA_R_OPEN_POL2 - response to LSA Open Policy */
+typedef struct lsa_r_open_pol2_info
+{
+       POLICY_HND pol; /* policy handle */
+       NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL2;
+
+
+#define POLICY_VIEW_LOCAL_INFORMATION    0x00000001
+#define POLICY_VIEW_AUDIT_INFORMATION    0x00000002
+#define POLICY_GET_PRIVATE_INFORMATION   0x00000004
+#define POLICY_TRUST_ADMIN               0x00000008
+#define POLICY_CREATE_ACCOUNT            0x00000010
+#define POLICY_CREATE_SECRET             0x00000020
+#define POLICY_CREATE_PRIVILEGE          0x00000040
+#define POLICY_SET_DEFAULT_QUOTA_LIMITS  0x00000080
+#define POLICY_SET_AUDIT_REQUIREMENTS    0x00000100
+#define POLICY_AUDIT_LOG_ADMIN           0x00000200
+#define POLICY_SERVER_ADMIN              0x00000400
+#define POLICY_LOOKUP_NAMES              0x00000800
+
+#define POLICY_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED_ACCESS  |\
+                            POLICY_VIEW_LOCAL_INFORMATION    |\
+                            POLICY_VIEW_AUDIT_INFORMATION    |\
+                            POLICY_GET_PRIVATE_INFORMATION   |\
+                            POLICY_TRUST_ADMIN               |\
+                            POLICY_CREATE_ACCOUNT            |\
+                            POLICY_CREATE_SECRET             |\
+                            POLICY_CREATE_PRIVILEGE          |\
+                            POLICY_SET_DEFAULT_QUOTA_LIMITS  |\
+                            POLICY_SET_AUDIT_REQUIREMENTS    |\
+                            POLICY_AUDIT_LOG_ADMIN           |\
+                            POLICY_SERVER_ADMIN              |\
+                            POLICY_LOOKUP_NAMES )
+
+
+#define POLICY_READ       ( STANDARD_RIGHTS_READ_ACCESS      |\
+                            POLICY_VIEW_AUDIT_INFORMATION    |\
+                            POLICY_GET_PRIVATE_INFORMATION)
+
+#define POLICY_WRITE      ( STANDARD_RIGHTS_WRITE_ACCESS     |\
+                            POLICY_TRUST_ADMIN               |\
+                            POLICY_CREATE_ACCOUNT            |\
+                            POLICY_CREATE_SECRET             |\
+                            POLICY_CREATE_PRIVILEGE          |\
+                            POLICY_SET_DEFAULT_QUOTA_LIMITS  |\
+                            POLICY_SET_AUDIT_REQUIREMENTS    |\
+                            POLICY_AUDIT_LOG_ADMIN           |\
+                            POLICY_SERVER_ADMIN)
+
+#define POLICY_EXECUTE    ( STANDARD_RIGHTS_EXECUTE_ACCESS   |\
+                            POLICY_VIEW_LOCAL_INFORMATION    |\
+                            POLICY_LOOKUP_NAMES )
+
+/* LSA_Q_QUERY_SEC_OBJ - LSA query security */
+typedef struct lsa_query_sec_obj_info
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 sec_info;
+
+} LSA_Q_QUERY_SEC_OBJ;
+
+/* LSA_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_lsa_query_sec_obj_info
+{
+       uint32 ptr;
+       SEC_DESC_BUF *buf;
+
+       NTSTATUS status;         /* return status */
+
+} LSA_R_QUERY_SEC_OBJ;
+
+/* LSA_Q_QUERY_INFO - LSA query info policy */
+typedef struct lsa_query_info
+{
+       POLICY_HND pol; /* policy handle */
+    uint16 info_class; /* info class */
+
+} LSA_Q_QUERY_INFO;
+
+/* LSA_INFO_UNION */
+typedef union lsa_info_union
+{
+       DOM_QUERY_2 id2;
+       DOM_QUERY_3 id3;
+       DOM_QUERY_5 id5;
+       DOM_QUERY_6 id6;
+} LSA_INFO_UNION;
+
+/* LSA_R_QUERY_INFO - response to LSA query info policy */
+typedef struct lsa_r_query_info
+{
+    uint32 undoc_buffer; /* undocumented buffer pointer */
+    uint16 info_class; /* info class (same as info class in request) */
+   
+       LSA_INFO_UNION dom; 
+
+       NTSTATUS status; /* return code */
+
+} LSA_R_QUERY_INFO;
+
+/* LSA_DNS_DOM_INFO - DNS domain info - info class 12*/
+typedef struct lsa_dns_dom_info
+{
+       UNIHDR  hdr_nb_dom_name; /* netbios domain name */
+       UNIHDR  hdr_dns_dom_name;
+       UNIHDR  hdr_forest_name;
+
+       GUID       dom_guid; /* domain GUID */
+
+       UNISTR2 uni_nb_dom_name;
+       UNISTR2 uni_dns_dom_name;
+       UNISTR2 uni_forest_name;
+
+       uint32 ptr_dom_sid;
+       DOM_SID2   dom_sid; /* domain SID */
+} LSA_DNS_DOM_INFO;
+
+typedef union lsa_info2_union
+{
+       LSA_DNS_DOM_INFO dns_dom_info;
+} LSA_INFO2_UNION;
+
+/* LSA_Q_QUERY_INFO2 - LSA query info */
+typedef struct lsa_q_query_info2
+{
+       POLICY_HND pol;    /* policy handle */
+       uint16 info_class; /* info class */
+} LSA_Q_QUERY_INFO2;
+
+typedef struct lsa_r_query_info2
+{
+       uint32 ptr;    /* pointer to info struct */
+       uint16 info_class;
+       LSA_INFO2_UNION info; /* so far the only one */
+       NTSTATUS status;
+} LSA_R_QUERY_INFO2;
+
+/* LSA_Q_ENUM_TRUST_DOM - LSA enumerate trusted domains */
+typedef struct lsa_enum_trust_dom_info
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 enum_context; /* enumeration context handle */
+       uint32 preferred_len; /* preferred maximum length */
+
+} LSA_Q_ENUM_TRUST_DOM;
+
+/* LSA_R_ENUM_TRUST_DOM - response to LSA enumerate trusted domains */
+typedef struct lsa_r_enum_trust_dom_info
+{
+       uint32 enum_context; /* enumeration context handle */
+       uint32 num_domains; /* number of domains */
+       uint32 ptr_enum_domains; /* buffer pointer to num domains */
+
+       /* this lot is only added if ptr_enum_domains is non-NULL */
+       uint32 num_domains2; /* number of domains */
+       UNIHDR2 *hdr_domain_name;
+       UNISTR2 *uni_domain_name;
+       DOM_SID2 *domain_sid;
+
+       NTSTATUS status; /* return code */
+
+} LSA_R_ENUM_TRUST_DOM;
+
+/* LSA_Q_CLOSE */
+typedef struct lsa_q_close_info
+{
+       POLICY_HND pol; /* policy handle */
+
+} LSA_Q_CLOSE;
+
+/* LSA_R_CLOSE */
+typedef struct lsa_r_close_info
+{
+       POLICY_HND pol; /* policy handle.  should be all zeros. */
+
+       NTSTATUS status; /* return code */
+
+} LSA_R_CLOSE;
+
+
+#define MAX_REF_DOMAINS 32
+
+/* DOM_TRUST_HDR */
+typedef struct dom_trust_hdr
+{
+       UNIHDR hdr_dom_name; /* referenced domain unicode string headers */
+       uint32 ptr_dom_sid;
+
+} DOM_TRUST_HDR;
+       
+/* DOM_TRUST_INFO */
+typedef struct dom_trust_info
+{
+       UNISTR2  uni_dom_name; /* domain name unicode string */
+       DOM_SID2 ref_dom     ; /* referenced domain SID */
+
+} DOM_TRUST_INFO;
+       
+/* DOM_R_REF */
+typedef struct dom_ref_info
+{
+    uint32 num_ref_doms_1; /* num referenced domains */
+    uint32 ptr_ref_dom; /* pointer to referenced domains */
+    uint32 max_entries; /* 32 - max number of entries */
+    uint32 num_ref_doms_2; /* num referenced domains */
+
+    DOM_TRUST_HDR  hdr_ref_dom[MAX_REF_DOMAINS]; /* referenced domains */
+    DOM_TRUST_INFO ref_dom    [MAX_REF_DOMAINS]; /* referenced domains */
+
+} DOM_R_REF;
+
+/* the domain_idx points to a SID associated with the name */
+
+/* LSA_TRANS_NAME - translated name */
+typedef struct lsa_trans_name_info
+{
+       uint16 sid_name_use; /* value is 5 for a well-known group; 2 for a domain group; 1 for a user... */
+       UNIHDR hdr_name; 
+       uint32 domain_idx; /* index into DOM_R_REF array of SIDs */
+
+} LSA_TRANS_NAME;
+
+/* This number purly arbitary - just to prevent a client from requesting large amounts of memory */
+#define MAX_LOOKUP_SIDS 256
+
+/* LSA_TRANS_NAME_ENUM - LSA Translated Name Enumeration container */
+typedef struct lsa_trans_name_enum_info
+{
+       uint32 num_entries;
+       uint32 ptr_trans_names;
+       uint32 num_entries2;
+       
+       LSA_TRANS_NAME *name; /* translated names  */
+       UNISTR2 *uni_name;
+
+} LSA_TRANS_NAME_ENUM;
+
+/* LSA_SID_ENUM - LSA SID enumeration container */
+typedef struct lsa_sid_enum_info
+{
+       uint32 num_entries;
+       uint32 ptr_sid_enum;
+       uint32 num_entries2;
+       
+       uint32 *ptr_sid; /* domain SID pointers to be looked up. */
+       DOM_SID2 *sid; /* domain SIDs to be looked up. */
+
+} LSA_SID_ENUM;
+
+/* LSA_Q_LOOKUP_SIDS - LSA Lookup SIDs */
+typedef struct lsa_q_lookup_sids
+{
+       POLICY_HND          pol; /* policy handle */
+       LSA_SID_ENUM        sids;
+       LSA_TRANS_NAME_ENUM names;
+       LOOKUP_LEVEL        level;
+       uint32              mapped_count;
+
+} LSA_Q_LOOKUP_SIDS;
+
+/* LSA_R_LOOKUP_SIDS - response to LSA Lookup SIDs */
+typedef struct lsa_r_lookup_sids
+{
+       uint32              ptr_dom_ref;
+       DOM_R_REF           *dom_ref; /* domain reference info */
+
+       LSA_TRANS_NAME_ENUM *names;
+       uint32              mapped_count;
+
+       NTSTATUS            status; /* return code */
+
+} LSA_R_LOOKUP_SIDS;
+
+/* LSA_Q_LOOKUP_NAMES - LSA Lookup NAMEs */
+typedef struct lsa_q_lookup_names
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 num_entries;
+       uint32 num_entries2;
+       UNIHDR  *hdr_name; /* name buffer pointers */
+       UNISTR2 *uni_name; /* names to be looked up */
+
+       uint32 num_trans_entries;
+       uint32 ptr_trans_sids; /* undocumented domain SID buffer pointer */
+       uint32 lookup_level;
+       uint32 mapped_count;
+
+} LSA_Q_LOOKUP_NAMES;
+
+/* LSA_R_LOOKUP_NAMES - response to LSA Lookup NAMEs by name */
+typedef struct lsa_r_lookup_names
+{
+       uint32 ptr_dom_ref;
+       DOM_R_REF *dom_ref; /* domain reference info */
+
+       uint32 num_entries;
+       uint32 ptr_entries;
+       uint32 num_entries2;
+       DOM_RID2 *dom_rid; /* domain RIDs being looked up */
+
+       uint32 mapped_count;
+
+       NTSTATUS status; /* return code */
+} LSA_R_LOOKUP_NAMES;
+
+/* This is probably a policy handle but at the moment we
+   never read it - so use a dummy struct. */
+
+typedef struct lsa_q_open_secret
+{
+       uint32 dummy;
+} LSA_Q_OPEN_SECRET;
+
+/* We always return "not found" at present - so just marshal the minimum. */
+
+typedef struct lsa_r_open_secret
+{
+       uint32 dummy1;
+       uint32 dummy2;
+       uint32 dummy3;
+       uint32 dummy4;
+       NTSTATUS status;
+} LSA_R_OPEN_SECRET;
+
+typedef struct lsa_enum_priv_entry
+{
+       UNIHDR hdr_name;
+       uint32 luid_low;
+       uint32 luid_high;
+       UNISTR2 name;
+       
+} LSA_PRIV_ENTRY;
+
+/* LSA_Q_ENUM_PRIVS - LSA enum privileges */
+typedef struct lsa_q_enum_privs
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 enum_context;
+       uint32 pref_max_length;
+} LSA_Q_ENUM_PRIVS;
+
+typedef struct lsa_r_enum_privs
+{
+       uint32 enum_context;
+       uint32 count;
+       uint32 ptr;
+       uint32 count1;
+
+       LSA_PRIV_ENTRY *privs;
+
+       NTSTATUS status;
+} LSA_R_ENUM_PRIVS;
+
+/* LSA_Q_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+       POLICY_HND pol; /* policy handle */
+       DOM_SID2 sid;
+} LSA_Q_ENUM_ACCT_RIGHTS;
+
+/* LSA_R_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+       uint32 count;
+       UNISTR2_ARRAY rights;
+       NTSTATUS status;
+} LSA_R_ENUM_ACCT_RIGHTS;
+
+
+/* LSA_Q_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+       POLICY_HND pol; /* policy handle */
+       DOM_SID2 sid;
+       UNISTR2_ARRAY rights;
+} LSA_Q_ADD_ACCT_RIGHTS;
+
+/* LSA_R_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+       NTSTATUS status;
+} LSA_R_ADD_ACCT_RIGHTS;
+
+
+/* LSA_Q_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+       POLICY_HND pol; /* policy handle */
+       DOM_SID2 sid;
+       uint32 removeall;
+       UNISTR2_ARRAY rights;
+} LSA_Q_REMOVE_ACCT_RIGHTS;
+
+/* LSA_R_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+       NTSTATUS status;
+} LSA_R_REMOVE_ACCT_RIGHTS;
+
+/* LSA_Q_ENUM_ACCT_WITH_RIGHT - LSA enum accounts with right */
+typedef struct
+{
+       POLICY_HND pol;
+       STRHDR right_hdr;
+       UNISTR2 right; 
+} LSA_Q_ENUM_ACCT_WITH_RIGHT;
+
+/* LSA_R_ENUM_ACCT_WITH_RIGHT - LSA enum accounts with right */
+typedef struct
+{
+       uint32 count;
+       SID_ARRAY sids;
+       NTSTATUS status;
+} LSA_R_ENUM_ACCT_WITH_RIGHT;
+
+
+/* LSA_Q_PRIV_GET_DISPNAME - LSA get privilege display name */
+typedef struct lsa_q_priv_get_dispname
+{
+       POLICY_HND pol; /* policy handle */
+       UNIHDR hdr_name;
+       UNISTR2 name;
+       uint16 lang_id;
+       uint16 lang_id_sys;
+} LSA_Q_PRIV_GET_DISPNAME;
+
+typedef struct lsa_r_priv_get_dispname
+{
+       uint32 ptr_info;
+       UNIHDR hdr_desc;
+       UNISTR2 desc;
+       /* Don't align ! */
+       uint16 lang_id;
+       /* align */
+       NTSTATUS status;
+} LSA_R_PRIV_GET_DISPNAME;
+
+/* LSA_Q_ENUM_ACCOUNTS */
+typedef struct lsa_q_enum_accounts
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 enum_context;
+       uint32 pref_max_length;
+} LSA_Q_ENUM_ACCOUNTS;
+
+/* LSA_R_ENUM_ACCOUNTS */
+typedef struct lsa_r_enum_accounts
+{
+       uint32 enum_context;
+       LSA_SID_ENUM sids;
+       NTSTATUS status;
+} LSA_R_ENUM_ACCOUNTS;
+
+/* LSA_Q_UNK_GET_CONNUSER - gets username\domain of connected user
+                  called when "Take Ownership" is clicked -SK */
+typedef struct lsa_q_unk_get_connuser
+{
+  uint32 ptr_srvname;
+  UNISTR2 uni2_srvname;
+  uint32 unk1; /* 3 unknown uint32's are seen right after uni2_srvname */
+  uint32 unk2; /* unk2 appears to be a ptr, unk1 = unk3 = 0 usually */
+  uint32 unk3; 
+} LSA_Q_UNK_GET_CONNUSER;
+
+/* LSA_R_UNK_GET_CONNUSER */
+typedef struct lsa_r_unk_get_connuser
+{
+  uint32 ptr_user_name;
+  UNIHDR hdr_user_name;
+  UNISTR2 uni2_user_name;
+  
+  uint32 unk1;
+  
+  uint32 ptr_dom_name;
+  UNIHDR hdr_dom_name;
+  UNISTR2 uni2_dom_name;
+
+  NTSTATUS status;
+} LSA_R_UNK_GET_CONNUSER;
+
+
+typedef struct lsa_q_openaccount
+{
+       POLICY_HND pol; /* policy handle */
+       DOM_SID2 sid;
+       uint32 access; /* desired access */
+} LSA_Q_OPENACCOUNT;
+
+typedef struct lsa_r_openaccount
+{
+       POLICY_HND pol; /* policy handle */
+       NTSTATUS status;
+} LSA_R_OPENACCOUNT;
+
+typedef struct lsa_q_enumprivsaccount
+{
+       POLICY_HND pol; /* policy handle */
+} LSA_Q_ENUMPRIVSACCOUNT;
+
+
+typedef struct LUID
+{
+       uint32 low;
+       uint32 high;
+} LUID;
+
+typedef struct LUID_ATTR
+{
+       LUID luid;
+       uint32 attr;
+} LUID_ATTR ;
+
+typedef struct privilege_set
+{
+       uint32 count;
+       uint32 control;
+       LUID_ATTR *set;
+} PRIVILEGE_SET;
+
+typedef struct lsa_r_enumprivsaccount
+{
+       uint32 ptr;
+       uint32 count;
+       PRIVILEGE_SET set;
+       NTSTATUS status;
+} LSA_R_ENUMPRIVSACCOUNT;
+
+typedef struct lsa_q_getsystemaccount
+{
+       POLICY_HND pol; /* policy handle */
+} LSA_Q_GETSYSTEMACCOUNT;
+
+typedef struct lsa_r_getsystemaccount
+{
+       uint32 access;
+       NTSTATUS status;
+} LSA_R_GETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_setsystemaccount
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 access;
+} LSA_Q_SETSYSTEMACCOUNT;
+
+typedef struct lsa_r_setsystemaccount
+{
+       NTSTATUS status;
+} LSA_R_SETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_lookupprivvalue
+{
+       POLICY_HND pol; /* policy handle */
+       UNIHDR hdr_right;
+       UNISTR2 uni2_right;
+} LSA_Q_LOOKUPPRIVVALUE;
+
+typedef struct lsa_r_lookupprivvalue
+{
+       LUID luid;
+       NTSTATUS status;
+} LSA_R_LOOKUPPRIVVALUE;
+
+
+typedef struct lsa_q_addprivs
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 count;
+       PRIVILEGE_SET set;
+} LSA_Q_ADDPRIVS;
+
+typedef struct lsa_r_addprivs
+{
+       NTSTATUS status;
+} LSA_R_ADDPRIVS;
+
+
+typedef struct lsa_q_removeprivs
+{
+       POLICY_HND pol; /* policy handle */
+       uint32 allrights;
+       uint32 ptr;
+       uint32 count;
+       PRIVILEGE_SET set;
+} LSA_Q_REMOVEPRIVS;
+
+typedef struct lsa_r_removeprivs
+{
+       NTSTATUS status;
+} LSA_R_REMOVEPRIVS;
+
+
+#endif /* _RPC_LSA_H */
+
+
diff --git a/source4/include/rpc_misc.h b/source4/include/rpc_misc.h
new file mode 100644 (file)
index 0000000..06ad760
--- /dev/null
@@ -0,0 +1,429 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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 "ntdomain.h"
+#include "rpc_dce.h"
+
+#ifndef _RPC_MISC_H /* _RPC_MISC_H */
+#define _RPC_MISC_H 
+
+
+
+/* well-known RIDs - Relative IDs */
+
+/* RIDs - Well-known users ... */
+#define DOMAIN_USER_RID_ADMIN          (0x000001F4L)
+#define DOMAIN_USER_RID_GUEST          (0x000001F5L)
+#define DOMAIN_USER_RID_KRBTGT         (0x000001F6L)
+
+/* RIDs - well-known groups ... */
+#define DOMAIN_GROUP_RID_ADMINS        (0x00000200L)
+#define DOMAIN_GROUP_RID_USERS         (0x00000201L)
+#define DOMAIN_GROUP_RID_GUESTS        (0x00000202L)
+#define DOMAIN_GROUP_RID_COMPUTERS     (0x00000203L)
+
+#define DOMAIN_GROUP_RID_CONTROLLERS   (0x00000204L)
+#define DOMAIN_GROUP_RID_CERT_ADMINS   (0x00000205L)
+#define DOMAIN_GROUP_RID_SCHEMA_ADMINS (0x00000206L)
+#define DOMAIN_GROUP_RID_ENTERPRISE_ADMINS (0x00000207L)
+
+/* is the following the right number? I bet it is  --simo
+#define DOMAIN_GROUP_RID_POLICY_ADMINS (0x00000208L)
+*/
+
+/* RIDs - well-known aliases ... */
+#define BUILTIN_ALIAS_RID_ADMINS        (0x00000220L)
+#define BUILTIN_ALIAS_RID_USERS         (0x00000221L)
+#define BUILTIN_ALIAS_RID_GUESTS        (0x00000222L)
+#define BUILTIN_ALIAS_RID_POWER_USERS   (0x00000223L)
+
+#define BUILTIN_ALIAS_RID_ACCOUNT_OPS   (0x00000224L)
+#define BUILTIN_ALIAS_RID_SYSTEM_OPS    (0x00000225L)
+#define BUILTIN_ALIAS_RID_PRINT_OPS     (0x00000226L)
+#define BUILTIN_ALIAS_RID_BACKUP_OPS    (0x00000227L)
+
+#define BUILTIN_ALIAS_RID_REPLICATOR    (0x00000228L)
+#define BUILTIN_ALIAS_RID_RAS_SERVERS   (0x00000229L)
+
+/*
+ * Masks for mappings between unix uid and gid types and
+ * NT RIDS.
+ */
+
+
+#define BASE_RID (0x000003E8L)
+
+/* Take the bottom bit. */
+#define RID_TYPE_MASK 1
+#define RID_MULTIPLIER 2
+
+/* The two common types. */
+#define USER_RID_TYPE 0
+#define GROUP_RID_TYPE 1
+
+/* ENUM_HND */
+typedef struct enum_hnd_info
+{
+       uint32 ptr_hnd;          /* pointer to enumeration handle */
+       uint32 handle;           /* enumeration handle */
+
+} ENUM_HND;
+
+/* LOOKUP_LEVEL - switch value */
+typedef struct lookup_level_info
+{
+  uint16 value;
+
+} LOOKUP_LEVEL;
+
+/* DOM_SID2 - security id */
+typedef struct sid_info_2
+{
+       uint32 num_auths; /* length, bytes, including length of len :-) */
+
+       DOM_SID sid;
+
+} DOM_SID2;
+
+/* STRHDR - string header */
+typedef struct header_info
+{
+  uint16 str_str_len;
+  uint16 str_max_len;
+  uint32 buffer; /* non-zero */
+
+} STRHDR;
+
+/* UNIHDR - unicode string header */
+typedef struct unihdr_info
+{
+  uint16 uni_str_len;
+  uint16 uni_max_len;
+  uint32 buffer; /* usually has a value of 4 */
+
+} UNIHDR;
+
+/* UNIHDR2 - unicode string header and undocumented buffer */
+typedef struct unihdr2_info
+{
+  UNIHDR unihdr;
+  uint32 buffer; /* 32 bit buffer pointer */
+
+} UNIHDR2;
+
+/* clueless as to what maximum length should be */
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+/* UNISTR - unicode string size and buffer */
+typedef struct unistr_info
+{
+  /* unicode characters. ***MUST*** be little-endian. ***MUST*** be null-terminated */
+  uint16 *buffer;
+} UNISTR;
+
+/* BUFHDR - buffer header */
+typedef struct bufhdr_info
+{
+  uint32 buf_max_len;
+  uint32 buf_len;
+
+} BUFHDR;
+
+/* BUFFER2 - unicode string, size (in uint8 ascii chars) and buffer */
+/* pathetic.  some stupid team of \PIPE\winreg writers got the concept */
+/* of a unicode string different from the other \PIPE\ writers */
+typedef struct buffer2_info
+{
+  uint32 buf_max_len;
+  uint32 undoc;
+  uint32 buf_len;
+  /* unicode characters. ***MUST*** be little-endian. **NOT** necessarily null-terminated */
+  uint16 *buffer;
+
+} BUFFER2;
+
+/* BUFFER3 */
+typedef struct buffer3_info
+{
+  uint32 buf_max_len;
+  uint8  *buffer; /* Data */
+  uint32 buf_len;
+
+} BUFFER3;
+
+/* BUFFER5 */
+typedef struct buffer5_info
+{
+  uint32 buf_len;
+  uint16 *buffer; /* data */
+} BUFFER5;
+
+/* UNISTR2 - unicode string size (in uint16 unicode chars) and buffer */
+typedef struct unistr2_info
+{
+  uint32 uni_max_len;
+  uint32 undoc;
+  uint32 uni_str_len;
+  /* unicode characters. ***MUST*** be little-endian. 
+     **must** be null-terminated and the uni_str_len should include
+     the NULL character */
+  uint16 *buffer;
+
+} UNISTR2;
+
+/* STRING2 - string size (in uint8 chars) and buffer */
+typedef struct string2_info
+{
+  uint32 str_max_len;
+  uint32 undoc;
+  uint32 str_str_len;
+  uint8  *buffer; /* uint8 characters. **NOT** necessarily null-terminated */
+
+} STRING2;
+
+/* UNISTR3 - XXXX not sure about this structure */
+typedef struct unistr3_info
+{
+       uint32 uni_str_len;
+       UNISTR str;
+
+} UNISTR3;
+
+/* an element in a unicode string array */
+typedef struct
+{
+       uint16 length;
+       uint16 size;
+       uint32 ref_id;
+       UNISTR2 string;
+} UNISTR2_ARRAY_EL;
+
+/* an array of unicode strings */
+typedef struct 
+{
+       uint32 ref_id;
+       uint32 count;
+       UNISTR2_ARRAY_EL *strings;
+} UNISTR2_ARRAY;
+
+
+/* an element in a sid array */
+typedef struct
+{
+       uint32 ref_id;
+       DOM_SID2 sid;
+} SID_ARRAY_EL;
+
+/* an array of sids */
+typedef struct 
+{
+       uint32 ref_id;
+       uint32 count;
+       SID_ARRAY_EL *sids;
+} SID_ARRAY;
+
+/* DOM_RID2 - domain RID structure for ntlsa pipe */
+typedef struct domrid2_info
+{
+       uint8 type; /* value is SID_NAME_USE enum */
+       uint32 rid;
+       uint32 rid_idx; /* referenced domain index */
+
+} DOM_RID2;
+
+/* DOM_RID3 - domain RID structure for samr pipe */
+typedef struct domrid3_info
+{
+       uint32 rid;        /* domain-relative (to a SID) id */
+       uint32 type1;      /* value is 0x1 */
+       uint32 ptr_type;   /* undocumented pointer */
+       uint32 type2;      /* value is 0x1 */
+       uint32 unk; /* value is 0x2 */
+
+} DOM_RID3;
+
+/* DOM_RID4 - rid + user attributes */
+typedef struct domrid4_info
+{
+  uint32 unknown;
+  uint16 attr;
+  uint32 rid;  /* user RID */
+
+} DOM_RID4;
+
+/* DOM_CLNT_SRV - client / server names */
+typedef struct clnt_srv_info
+{
+  uint32  undoc_buffer; /* undocumented 32 bit buffer pointer */
+  UNISTR2 uni_logon_srv; /* logon server name */
+  uint32  undoc_buffer2; /* undocumented 32 bit buffer pointer */
+  UNISTR2 uni_comp_name; /* client machine name */
+
+} DOM_CLNT_SRV;
+
+/* DOM_LOG_INFO - login info */
+typedef struct log_info
+{
+  uint32  undoc_buffer; /* undocumented 32 bit buffer pointer */
+  UNISTR2 uni_logon_srv; /* logon server name */
+  UNISTR2 uni_acct_name; /* account name */
+  uint16  sec_chan;      /* secure channel type */
+  UNISTR2 uni_comp_name; /* client machine name */
+
+} DOM_LOG_INFO;
+
+/* DOM_CHAL - challenge info */
+typedef struct chal_info
+{
+    uchar data[8]; /* credentials */
+} DOM_CHAL;
+/* DOM_CREDs - timestamped client or server credentials */
+typedef struct cred_info
+{
+    DOM_CHAL challenge; /* credentials */
+    UTIME timestamp;    /* credential time-stamp */
+} DOM_CRED;
+
+/* DOM_CLNT_INFO - client info */
+typedef struct clnt_info
+{
+  DOM_LOG_INFO login;
+  DOM_CRED     cred;
+
+} DOM_CLNT_INFO;
+
+/* DOM_CLNT_INFO2 - client info */
+typedef struct clnt_info2
+{
+  DOM_CLNT_SRV login;
+  uint32        ptr_cred;
+  DOM_CRED      cred;
+
+} DOM_CLNT_INFO2;
+
+/* DOM_LOGON_ID - logon id */
+typedef struct logon_info
+{
+  uint32 low;
+  uint32 high;
+
+} DOM_LOGON_ID;
+
+/* OWF INFO */
+typedef struct owf_info
+{
+  uint8 data[16];
+
+} OWF_INFO;
+
+
+/* DOM_GID - group id + user attributes */
+typedef struct gid_info
+{
+  uint32 g_rid;  /* a group RID */
+  uint32 attr;
+
+} DOM_GID;
+
+/* POLICY_HND */
+typedef struct lsa_policy_info
+{
+       uint32 data1;
+       uint32 data2;
+       uint16 data3;
+       uint16 data4;
+       uint8 data5[8];
+
+#ifdef __INSURE__
+
+       /* To prevent the leakage of policy handles mallocate a bit of
+          memory when a policy handle is created and free it when the
+          handle is closed.  This should cause Insure to flag an error
+          when policy handles are overwritten or fall out of scope without
+          being freed. */
+
+       char *marker;
+#endif
+
+} POLICY_HND;
+
+/*
+ * A client connection's state, pipe name, 
+ * user credentials, etc...
+ */
+typedef struct _cli_auth_fns cli_auth_fns;
+struct user_creds;
+struct cli_connection {
+
+        char                    *srv_name;
+        char                    *pipe_name;
+        struct user_creds       usr_creds;
+
+        struct cli_state        *pCli_state;
+
+        cli_auth_fns            *auth;
+
+        void                    *auth_info;
+        void                    *auth_creds;
+};
+
+
+/* 
+ * Associate a POLICY_HND with a cli_connection
+ */
+typedef struct rpc_hnd_node {
+
+       POLICY_HND              hnd;
+       struct cli_connection   *cli;
+
+} RPC_HND_NODE;
+
+typedef struct uint64_s
+{
+       uint32 low;
+       uint32 high;
+} UINT64_S;
+
+/* BUFHDR2 - another buffer header, with info level */
+typedef struct bufhdr2_info
+{
+       uint32 info_level;
+       uint32 length;          /* uint8 chars */
+       uint32 buffer;
+
+}
+BUFHDR2;
+
+/* BUFFER4 - simple length and buffer */
+typedef struct buffer4_info
+{
+       uint32 buf_len;
+       uint8 buffer[MAX_BUFFERLEN];
+
+}
+BUFFER4;
+
+
+#endif /* _RPC_MISC_H */
diff --git a/source4/include/rpc_netlogon.h b/source4/include/rpc_netlogon.h
new file mode 100644 (file)
index 0000000..fb849f8
--- /dev/null
@@ -0,0 +1,910 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   Copyright (C) Jean François Micouleau 2002
+   
+   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.
+*/
+
+#ifndef _RPC_NETLOGON_H /* _RPC_NETLOGON_H */
+#define _RPC_NETLOGON_H 
+
+
+/* NETLOGON pipe */
+#define NET_SAMLOGON           0x02
+#define NET_SAMLOGOFF          0x03
+#define NET_REQCHAL            0x04
+#define NET_AUTH               0x05
+#define NET_SRVPWSET           0x06
+#define NET_SAM_DELTAS         0x07
+#define NET_LOGON_CTRL         0x0c
+#define NET_AUTH2              0x0f
+#define NET_LOGON_CTRL2                0x0e
+#define NET_SAM_SYNC           0x10
+#define NET_TRUST_DOM_LIST     0x13
+#define NET_AUTH3              0x1a
+
+/* Secure Channel types.  used in NetrServerAuthenticate negotiation */
+#define SEC_CHAN_WKSTA   2
+#define SEC_CHAN_DOMAIN  4
+#define SEC_CHAN_BDC     6
+
+/* Returned delta types */
+#define SAM_DELTA_DOMAIN_INFO    0x01
+#define SAM_DELTA_GROUP_INFO     0x02
+#define SAM_DELTA_RENAME_GROUP   0x04
+#define SAM_DELTA_ACCOUNT_INFO   0x05
+#define SAM_DELTA_RENAME_USER    0x07
+#define SAM_DELTA_GROUP_MEM      0x08
+#define SAM_DELTA_ALIAS_INFO     0x09
+#define SAM_DELTA_RENAME_ALIAS   0x0b
+#define SAM_DELTA_ALIAS_MEM      0x0c
+#define SAM_DELTA_POLICY_INFO    0x0d
+#define SAM_DELTA_TRUST_DOMS     0x0e
+#define SAM_DELTA_PRIVS_INFO     0x10 /* DT_DELTA_ACCOUNTS */
+#define SAM_DELTA_SECRET_INFO    0x12
+#define SAM_DELTA_DELETE_GROUP   0x14
+#define SAM_DELTA_DELETE_USER    0x15
+#define SAM_DELTA_MODIFIED_COUNT 0x16
+
+/* SAM database types */
+#define SAM_DATABASE_DOMAIN    0x00 /* Domain users and groups */
+#define SAM_DATABASE_BUILTIN   0x01 /* BUILTIN users and groups */
+#define SAM_DATABASE_PRIVS     0x02 /* Privileges */
+
+#if 0
+/* I think this is correct - it's what gets parsed on the wire. JRA. */
+/* NET_USER_INFO_2 */
+typedef struct net_user_info_2
+{
+       uint32 ptr_user_info;
+
+       NTTIME logon_time;            /* logon time */
+       NTTIME logoff_time;           /* logoff time */
+       NTTIME kickoff_time;          /* kickoff time */
+       NTTIME pass_last_set_time;    /* password last set time */
+       NTTIME pass_can_change_time;  /* password can change time */
+       NTTIME pass_must_change_time; /* password must change time */
+
+       UNIHDR hdr_user_name;    /* username unicode string header */
+       UNIHDR hdr_full_name;    /* user's full name unicode string header */
+       UNIHDR hdr_logon_script; /* logon script unicode string header */
+       UNIHDR hdr_profile_path; /* profile path unicode string header */
+       UNIHDR hdr_home_dir;     /* home directory unicode string header */
+       UNIHDR hdr_dir_drive;    /* home directory drive unicode string header */
+
+       uint16 logon_count;  /* logon count */
+       uint16 bad_pw_count; /* bad password count */
+
+       uint32 user_id;       /* User ID */
+       uint32 group_id;      /* Group ID */
+       uint32 num_groups;    /* num groups */
+       uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+       uint32 user_flgs;     /* user flags */
+
+       uint8 user_sess_key[16]; /* unused user session key */
+
+       UNIHDR hdr_logon_srv; /* logon server unicode string header */
+       UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+       uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+       uint8 padding[40];    /* unused padding bytes.  expansion room */
+
+       UNISTR2 uni_user_name;    /* username unicode string */
+       UNISTR2 uni_full_name;    /* user's full name unicode string */
+       UNISTR2 uni_logon_script; /* logon script unicode string */
+       UNISTR2 uni_profile_path; /* profile path unicode string */
+       UNISTR2 uni_home_dir;     /* home directory unicode string */
+       UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
+
+       uint32 num_groups2;        /* num groups */
+       DOM_GID *gids; /* group info */
+
+       UNISTR2 uni_logon_srv; /* logon server unicode string */
+       UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+       DOM_SID2 dom_sid;           /* domain SID */
+
+       uint32 num_other_groups;        /* other groups */
+       DOM_GID *other_gids; /* group info */
+       DOM_SID2 *other_sids; /* undocumented - domain SIDs */
+
+} NET_USER_INFO_2;
+#endif
+
+/* NET_USER_INFO_3 */
+typedef struct net_user_info_3
+{
+       uint32 ptr_user_info;
+
+       NTTIME logon_time;            /* logon time */
+       NTTIME logoff_time;           /* logoff time */
+       NTTIME kickoff_time;          /* kickoff time */
+       NTTIME pass_last_set_time;    /* password last set time */
+       NTTIME pass_can_change_time;  /* password can change time */
+       NTTIME pass_must_change_time; /* password must change time */
+
+       UNIHDR hdr_user_name;    /* username unicode string header */
+       UNIHDR hdr_full_name;    /* user's full name unicode string header */
+       UNIHDR hdr_logon_script; /* logon script unicode string header */
+       UNIHDR hdr_profile_path; /* profile path unicode string header */
+       UNIHDR hdr_home_dir;     /* home directory unicode string header */
+       UNIHDR hdr_dir_drive;    /* home directory drive unicode string header */
+
+       uint16 logon_count;  /* logon count */
+       uint16 bad_pw_count; /* bad password count */
+
+       uint32 user_rid;       /* User RID */
+       uint32 group_rid;      /* Group RID */
+
+       uint32 num_groups;    /* num groups */
+       uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+       uint32 user_flgs;     /* user flags */
+
+       uint8 user_sess_key[16]; /* unused user session key */
+
+       UNIHDR hdr_logon_srv; /* logon server unicode string header */
+       UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+       uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+       uint8 padding[40];    /* unused padding bytes.  expansion room */
+
+       uint32 num_other_sids; /* number of foreign/trusted domain sids */
+       uint32 buffer_other_sids;
+       
+       UNISTR2 uni_user_name;    /* username unicode string */
+       UNISTR2 uni_full_name;    /* user's full name unicode string */
+       UNISTR2 uni_logon_script; /* logon script unicode string */
+       UNISTR2 uni_profile_path; /* profile path unicode string */
+       UNISTR2 uni_home_dir;     /* home directory unicode string */
+       UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
+
+       uint32 num_groups2;        /* num groups */
+       DOM_GID *gids; /* group info */
+
+       UNISTR2 uni_logon_srv; /* logon server unicode string */
+       UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+       DOM_SID2 dom_sid;           /* domain SID */
+
+       uint32 num_other_groups;        /* other groups */
+       DOM_GID *other_gids; /* group info */
+       DOM_SID2 *other_sids; /* foreign/trusted domain SIDs */
+
+} NET_USER_INFO_3;
+
+
+/* NETLOGON_INFO_1 - pdc status info, i presume */
+typedef struct netlogon_1_info
+{
+       uint32 flags;            /* 0x0 - undocumented */
+       uint32 pdc_status;       /* 0x0 - undocumented */
+
+} NETLOGON_INFO_1;
+
+/* NETLOGON_INFO_2 - pdc status info, plus trusted domain info */
+typedef struct netlogon_2_info
+{
+       uint32  flags;            /* 0x0 - undocumented */
+       uint32  pdc_status;       /* 0x0 - undocumented */
+       uint32  ptr_trusted_dc_name; /* pointer to trusted domain controller name */
+       uint32  tc_status;           /* 0x051f - ERROR_NO_LOGON_SERVERS */
+       UNISTR2 uni_trusted_dc_name; /* unicode string - trusted dc name */
+
+} NETLOGON_INFO_2;
+
+/* NETLOGON_INFO_3 - logon status info, i presume */
+typedef struct netlogon_3_info
+{
+       uint32 flags;            /* 0x0 - undocumented */
+       uint32 logon_attempts;   /* number of logon attempts */
+       uint32 reserved_1;       /* 0x0 - undocumented */
+       uint32 reserved_2;       /* 0x0 - undocumented */
+       uint32 reserved_3;       /* 0x0 - undocumented */
+       uint32 reserved_4;       /* 0x0 - undocumented */
+       uint32 reserved_5;       /* 0x0 - undocumented */
+
+} NETLOGON_INFO_3;
+
+/********************************************************
+ Logon Control Query
+
+ This is generated by a nltest /bdc_query:DOMAIN
+
+ query_level 0x1, function_code 0x1
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_q_logon_ctrl_info
+{
+       uint32 ptr;
+       UNISTR2 uni_server_name;
+       uint32 function_code;
+       uint32 query_level;
+} NET_Q_LOGON_CTRL;
+
+/* NET_R_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_r_logon_ctrl_info
+{
+       uint32 switch_value;
+       uint32 ptr;
+
+       union {
+               NETLOGON_INFO_1 info1;
+       } logon;
+
+       NTSTATUS status;
+} NET_R_LOGON_CTRL;
+
+/********************************************************
+ Logon Control2 Query
+
+ query_level 0x1 - pdc status
+ query_level 0x3 - number of logon attempts.
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL2 - LSA Netr Logon Control 2 */
+typedef struct net_q_logon_ctrl2_info
+{
+       uint32       ptr;             /* undocumented buffer pointer */
+       UNISTR2      uni_server_name; /* server name, starting with two '\'s */
+       
+       uint32       function_code; /* 0x1 */
+       uint32       query_level;   /* 0x1, 0x3 */
+       uint32       switch_value;  /* 0x1 */
+
+} NET_Q_LOGON_CTRL2;
+
+/*******************************************************
+ Logon Control Response
+
+ switch_value is same as query_level in request 
+ *******************************************************/
+
+/* NET_R_LOGON_CTRL2 - response to LSA Logon Control2 */
+typedef struct net_r_logon_ctrl2_info
+{
+       uint32       switch_value;  /* 0x1, 0x3 */
+       uint32       ptr;
+
+       union
+       {
+               NETLOGON_INFO_1 info1;
+               NETLOGON_INFO_2 info2;
+               NETLOGON_INFO_3 info3;
+
+       } logon;
+
+       NTSTATUS status; /* return code */
+
+} NET_R_LOGON_CTRL2;
+
+/* NET_Q_TRUST_DOM_LIST - LSA Query Trusted Domains */
+typedef struct net_q_trust_dom_info
+{
+       uint32       ptr;             /* undocumented buffer pointer */
+       UNISTR2      uni_server_name; /* server name, starting with two '\'s */
+
+} NET_Q_TRUST_DOM_LIST;
+
+#define MAX_TRUST_DOMS 1
+
+/* NET_R_TRUST_DOM_LIST - response to LSA Trusted Domains */
+typedef struct net_r_trust_dom_info
+{
+       UNISTR2 uni_trust_dom_name[MAX_TRUST_DOMS];
+
+       NTSTATUS status; /* return code */
+
+} NET_R_TRUST_DOM_LIST;
+
+
+/* NEG_FLAGS */
+typedef struct neg_flags_info
+{
+    uint32 neg_flags; /* negotiated flags */
+
+} NEG_FLAGS;
+
+
+/* NET_Q_REQ_CHAL */
+typedef struct net_q_req_chal_info
+{
+    uint32  undoc_buffer; /* undocumented buffer pointer */
+    UNISTR2 uni_logon_srv; /* logon server unicode string */
+    UNISTR2 uni_logon_clnt; /* logon client unicode string */
+    DOM_CHAL clnt_chal; /* client challenge */
+
+} NET_Q_REQ_CHAL;
+
+
+/* NET_R_REQ_CHAL */
+typedef struct net_r_req_chal_info
+{
+       DOM_CHAL srv_chal; /* server challenge */
+       NTSTATUS status; /* return code */
+} NET_R_REQ_CHAL;
+
+/* NET_Q_AUTH */
+typedef struct net_q_auth_info
+{
+       DOM_LOG_INFO clnt_id; /* client identification info */
+       DOM_CHAL clnt_chal;     /* client-calculated credentials */
+} NET_Q_AUTH;
+
+/* NET_R_AUTH */
+typedef struct net_r_auth_info
+{
+       DOM_CHAL srv_chal;     /* server-calculated credentials */
+       NTSTATUS status; /* return code */
+} NET_R_AUTH;
+
+/* NET_Q_AUTH_2 */
+typedef struct net_q_auth2_info
+{
+    DOM_LOG_INFO clnt_id; /* client identification info */
+    DOM_CHAL clnt_chal;     /* client-calculated credentials */
+
+    NEG_FLAGS clnt_flgs; /* usually 0x0000 01ff */
+
+} NET_Q_AUTH_2;
+
+
+/* NET_R_AUTH_2 */
+typedef struct net_r_auth2_info
+{
+       DOM_CHAL srv_chal;     /* server-calculated credentials */
+       NEG_FLAGS srv_flgs; /* usually 0x0000 01ff */
+       NTSTATUS status; /* return code */
+} NET_R_AUTH_2;
+
+/* NET_Q_AUTH_3 */
+typedef struct net_q_auth3_info
+{
+    DOM_LOG_INFO clnt_id;      /* client identification info */
+    DOM_CHAL clnt_chal;                /* client-calculated credentials */
+    NEG_FLAGS clnt_flgs;       /* usually 0x6007 ffff */
+} NET_Q_AUTH_3;
+
+/* NET_R_AUTH_3 */
+typedef struct net_r_auth3_info
+{
+       DOM_CHAL srv_chal;      /* server-calculated credentials */
+       NEG_FLAGS srv_flgs;     /* usually 0x6007 ffff */
+       uint32 unknown;         /* 0x0000045b */
+       NTSTATUS status;        /* return code */
+} NET_R_AUTH_3;
+
+
+/* NET_Q_SRV_PWSET */
+typedef struct net_q_srv_pwset_info
+{
+    DOM_CLNT_INFO clnt_id; /* client identification/authentication info */
+    uint8 pwd[16]; /* new password - undocumented. */
+
+} NET_Q_SRV_PWSET;
+    
+/* NET_R_SRV_PWSET */
+typedef struct net_r_srv_pwset_info
+{
+    DOM_CRED srv_cred;     /* server-calculated credentials */
+
+  NTSTATUS status; /* return code */
+
+} NET_R_SRV_PWSET;
+
+/* NET_ID_INFO_2 */
+typedef struct net_network_info_2
+{
+       uint32            ptr_id_info2;        /* pointer to id_info_2 */
+       UNIHDR            hdr_domain_name;     /* domain name unicode header */
+       uint32            param_ctrl;          /* param control (0x2) */
+       DOM_LOGON_ID      logon_id;            /* logon ID */
+       UNIHDR            hdr_user_name;       /* user name unicode header */
+       UNIHDR            hdr_wksta_name;      /* workstation name unicode header */
+       uint8             lm_chal[8];          /* lan manager 8 byte challenge */
+       STRHDR            hdr_nt_chal_resp;    /* nt challenge response */
+       STRHDR            hdr_lm_chal_resp;    /* lm challenge response */
+
+       UNISTR2           uni_domain_name;     /* domain name unicode string */
+       UNISTR2           uni_user_name;       /* user name unicode string */
+       UNISTR2           uni_wksta_name;      /* workgroup name unicode string */
+       STRING2           nt_chal_resp;        /* nt challenge response */
+       STRING2           lm_chal_resp;        /* lm challenge response */
+
+} NET_ID_INFO_2;
+
+/* NET_ID_INFO_1 */
+typedef struct id_info_1
+{
+       uint32            ptr_id_info1;        /* pointer to id_info_1 */
+       UNIHDR            hdr_domain_name;     /* domain name unicode header */
+       uint32            param_ctrl;          /* param control */
+       DOM_LOGON_ID      logon_id;            /* logon ID */
+       UNIHDR            hdr_user_name;       /* user name unicode header */
+       UNIHDR            hdr_wksta_name;      /* workstation name unicode header */
+       OWF_INFO          lm_owf;              /* LM OWF Password */
+       OWF_INFO          nt_owf;              /* NT OWF Password */
+       UNISTR2           uni_domain_name;     /* domain name unicode string */
+       UNISTR2           uni_user_name;       /* user name unicode string */
+       UNISTR2           uni_wksta_name;      /* workgroup name unicode string */
+
+} NET_ID_INFO_1;
+
+#define INTERACTIVE_LOGON_TYPE 1
+#define NET_LOGON_TYPE 2
+
+/* NET_ID_INFO_CTR */
+typedef struct net_id_info_ctr_info
+{
+  uint16         switch_value;
+  
+  union
+  {
+    NET_ID_INFO_1 id1; /* auth-level 1 - interactive user login */
+    NET_ID_INFO_2 id2; /* auth-level 2 - workstation referred login */
+
+  } auth;
+  
+} NET_ID_INFO_CTR;
+
+/* SAM_INFO - sam logon/off id structure */
+typedef struct sam_info
+{
+  DOM_CLNT_INFO2  client;
+  uint32          ptr_rtn_cred; /* pointer to return credentials */
+  DOM_CRED        rtn_cred; /* return credentials */
+  uint16          logon_level;
+  NET_ID_INFO_CTR *ctr;
+
+} DOM_SAM_INFO;
+
+/* NET_Q_SAM_LOGON */
+typedef struct net_q_sam_logon_info
+{
+    DOM_SAM_INFO sam_id;
+       uint16          validation_level;
+
+} NET_Q_SAM_LOGON;
+
+/* NET_R_SAM_LOGON */
+typedef struct net_r_sam_logon_info
+{
+    uint32 buffer_creds; /* undocumented buffer pointer */
+    DOM_CRED srv_creds; /* server credentials.  server time stamp appears to be ignored. */
+    
+       uint16 switch_value; /* 3 - indicates type of USER INFO */
+    NET_USER_INFO_3 *user;
+
+    uint32 auth_resp; /* 1 - Authoritative response; 0 - Non-Auth? */
+
+  NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGON;
+
+
+/* NET_Q_SAM_LOGOFF */
+typedef struct net_q_sam_logoff_info
+{
+    DOM_SAM_INFO sam_id;
+
+} NET_Q_SAM_LOGOFF;
+
+/* NET_R_SAM_LOGOFF */
+typedef struct net_r_sam_logoff_info
+{
+    uint32 buffer_creds; /* undocumented buffer pointer */
+    DOM_CRED srv_creds; /* server credentials.  server time stamp appears to be ignored. */
+    
+  NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGOFF;
+
+/* NET_Q_SAM_SYNC */
+typedef struct net_q_sam_sync_info
+{
+       UNISTR2 uni_srv_name; /* \\PDC */
+       UNISTR2 uni_cli_name; /* BDC */
+       DOM_CRED cli_creds;
+       DOM_CRED ret_creds;
+
+       uint32 database_id;
+       uint32 restart_state;
+       uint32 sync_context;
+
+       uint32 max_size;       /* preferred maximum length */
+
+} NET_Q_SAM_SYNC;
+
+/* SAM_DELTA_HDR */
+typedef struct sam_delta_hdr_info
+{
+       uint16 type;  /* type of structure attached */
+       uint16 type2;
+       uint32 target_rid;
+
+       uint32 type3;
+       uint32 ptr_delta;
+
+} SAM_DELTA_HDR;
+
+/* SAM_DOMAIN_INFO (0x1) */
+typedef struct sam_domain_info_info
+{
+       UNIHDR hdr_dom_name;
+       UNIHDR hdr_oem_info;
+
+       UINT64_S force_logoff;
+       uint16   min_pwd_len;
+       uint16   pwd_history_len;
+       UINT64_S max_pwd_age;
+       UINT64_S min_pwd_age;
+       UINT64_S dom_mod_count;
+       NTTIME   creation_time;
+
+       BUFHDR2 hdr_sec_desc; /* security descriptor */
+       UNIHDR hdr_unknown;
+       uint8 reserved[40];
+
+       UNISTR2 uni_dom_name;
+       UNISTR2 buf_oem_info; /* never seen */
+
+       BUFFER4 buf_sec_desc;
+       UNISTR2 buf_unknown;
+
+} SAM_DOMAIN_INFO;
+
+/* SAM_GROUP_INFO (0x2) */
+typedef struct sam_group_info_info
+{
+       UNIHDR hdr_grp_name;
+       DOM_GID gid;
+       UNIHDR hdr_grp_desc;
+       BUFHDR2 hdr_sec_desc;  /* security descriptor */
+       uint8 reserved[48];
+
+       UNISTR2 uni_grp_name;
+       UNISTR2 uni_grp_desc;
+       BUFFER4 buf_sec_desc;
+
+} SAM_GROUP_INFO;
+
+/* SAM_PWD */
+typedef struct sam_passwd_info
+{
+       /* this structure probably contains password history */
+       /* this is probably a count of lm/nt pairs */
+       uint32 unk_0; /* 0x0000 0002 */
+
+       UNIHDR hdr_lm_pwd;
+       uint8  buf_lm_pwd[16];
+
+       UNIHDR hdr_nt_pwd;
+       uint8  buf_nt_pwd[16];
+
+       UNIHDR hdr_empty_lm;
+       UNIHDR hdr_empty_nt;
+
+} SAM_PWD;
+
+/* SAM_ACCOUNT_INFO (0x5) */
+typedef struct sam_account_info_info
+{
+       UNIHDR hdr_acct_name;
+       UNIHDR hdr_full_name;
+
+       uint32 user_rid;
+       uint32 group_rid;
+
+       UNIHDR hdr_home_dir;
+       UNIHDR hdr_dir_drive;
+       UNIHDR hdr_logon_script;
+       UNIHDR hdr_acct_desc;
+       UNIHDR hdr_workstations;
+
+       NTTIME logon_time;
+       NTTIME logoff_time;
+
+       uint32 logon_divs; /* 0xA8 */
+       uint32 ptr_logon_hrs;
+
+       uint16 bad_pwd_count;
+       uint16 logon_count;
+       NTTIME pwd_last_set_time;
+       NTTIME acct_expiry_time;
+
+       uint32 acb_info;
+       uint8 nt_pwd[16];
+       uint8 lm_pwd[16];
+       uint8 nt_pwd_present;
+       uint8 lm_pwd_present;
+       uint8 pwd_expired;
+
+       UNIHDR hdr_comment;
+       UNIHDR hdr_parameters;
+       uint16 country;
+       uint16 codepage;
+
+       BUFHDR2 hdr_sec_desc;  /* security descriptor */
+
+       UNIHDR  hdr_profile;
+       UNIHDR  hdr_reserved[3];  /* space for more strings */
+       uint32  dw_reserved[4];   /* space for more data - first two seem to
+                                    be an NTTIME */
+
+       UNISTR2 uni_acct_name;
+       UNISTR2 uni_full_name;
+       UNISTR2 uni_home_dir;
+       UNISTR2 uni_dir_drive;
+       UNISTR2 uni_logon_script;
+       UNISTR2 uni_acct_desc;
+       UNISTR2 uni_workstations;
+
+       uint32 unknown1; /* 0x4EC */
+       uint32 unknown2; /* 0 */
+
+       BUFFER4 buf_logon_hrs;
+       UNISTR2 uni_comment;
+       UNISTR2 uni_parameters;
+       SAM_PWD pass;
+       BUFFER4 buf_sec_desc;
+       UNISTR2 uni_profile;
+
+} SAM_ACCOUNT_INFO;
+
+/* SAM_GROUP_MEM_INFO (0x8) */
+typedef struct sam_group_mem_info_info
+{
+       uint32 ptr_rids;
+       uint32 ptr_attribs;
+       uint32 num_members;
+       uint8 unknown[16];
+
+       uint32 num_members2;
+       uint32 *rids;
+
+       uint32 num_members3;
+       uint32 *attribs;
+
+} SAM_GROUP_MEM_INFO;
+
+/* SAM_ALIAS_INFO (0x9) */
+typedef struct sam_alias_info_info
+{
+       UNIHDR hdr_als_name;
+       uint32 als_rid;
+       BUFHDR2 hdr_sec_desc;  /* security descriptor */
+       UNIHDR hdr_als_desc;
+       uint8 reserved[40];
+
+       UNISTR2 uni_als_name;
+       BUFFER4 buf_sec_desc;
+       UNISTR2 uni_als_desc;
+
+} SAM_ALIAS_INFO;
+
+/* SAM_ALIAS_MEM_INFO (0xC) */
+typedef struct sam_alias_mem_info_info
+{
+       uint32 num_members;
+       uint32 ptr_members;
+       uint8 unknown[16];
+
+       uint32 num_sids;
+       uint32 *ptr_sids;
+       DOM_SID2 *sids;
+
+} SAM_ALIAS_MEM_INFO;
+
+
+/* SAM_DELTA_POLICY (0x0D) */
+typedef struct
+{
+       uint32   max_log_size; /* 0x5000 */
+       UINT64_S audit_retention_period; /* 0 */
+       uint32   auditing_mode; /* 0 */
+       uint32   num_events;
+       uint32   ptr_events;
+       UNIHDR   hdr_dom_name;
+       uint32   sid_ptr;
+
+       uint32   paged_pool_limit; /* 0x02000000 */
+       uint32   non_paged_pool_limit; /* 0x00100000 */
+       uint32   min_workset_size; /* 0x00010000 */
+       uint32   max_workset_size; /* 0x0f000000 */
+       uint32   page_file_limit; /* 0 */
+       UINT64_S time_limit; /* 0 */
+       NTTIME   modify_time; /* 0x3c*/
+       NTTIME   create_time; /* a7080110 */
+       BUFHDR2  hdr_sec_desc;
+
+       uint32   num_event_audit_options;
+       uint32   event_audit_option;
+
+       UNISTR2  domain_name;
+       DOM_SID2 domain_sid;
+
+       BUFFER4  buf_sec_desc;
+} SAM_DELTA_POLICY;
+
+/* SAM_DELTA_TRUST_DOMS */
+typedef struct
+{
+       uint32 buf_size;
+       SEC_DESC *sec_desc;
+       DOM_SID2 sid;
+       UNIHDR hdr_domain;
+       
+       uint32 unknown0;
+       uint32 unknown1;
+       uint32 unknown2;
+       
+       uint32 buf_size2;
+       uint32 ptr;
+
+       uint32 unknown3;
+       UNISTR2 domain;
+
+} SAM_DELTA_TRUSTDOMS;
+
+/* SAM_DELTA_PRIVS (0x10) */
+typedef struct
+{
+       DOM_SID2 sid;
+
+       uint32 priv_count;
+       uint32 priv_control;
+
+       uint32 priv_attr_ptr;
+       uint32 priv_name_ptr;
+
+       uint32   paged_pool_limit; /* 0x02000000 */
+       uint32   non_paged_pool_limit; /* 0x00100000 */
+       uint32   min_workset_size; /* 0x00010000 */
+       uint32   max_workset_size; /* 0x0f000000 */
+       uint32   page_file_limit; /* 0 */
+       UINT64_S time_limit; /* 0 */
+       uint32   system_flags; /* 1 */
+       BUFHDR2  hdr_sec_desc;
+       
+       uint32 buf_size2;
+       
+       uint32 attribute_count;
+       uint32 *attributes;
+       
+       uint32 privlist_count;
+       UNIHDR *hdr_privslist;
+       UNISTR2 *uni_privslist;
+
+       BUFFER4 buf_sec_desc;
+} SAM_DELTA_PRIVS;
+
+/* SAM_DELTA_SECRET */
+typedef struct
+{
+       uint32 buf_size;
+       SEC_DESC *sec_desc;
+       UNISTR2 secret;
+
+       uint32 count1;
+       uint32 count2;
+       uint32 ptr;
+       NTTIME time1;
+       uint32 count3;
+       uint32 count4;
+       uint32 ptr2;
+       NTTIME time2;
+       uint32 unknow1;
+
+       uint32 buf_size2;
+       uint32 ptr3;
+       uint32 unknow2; /* 0x0 12 times */
+
+       uint32 chal_len;
+       uint32 reserved1; /* 0 */
+       uint32 chal_len2;
+       uint8 chal[16];
+
+       uint32 key_len;
+       uint32 reserved2; /* 0 */
+       uint32 key_len2;
+       uint8 key[8];
+
+       uint32 buf_size3;
+       SEC_DESC *sec_desc2;
+
+} SAM_DELTA_SECRET;
+
+/* SAM_DELTA_MOD_COUNT (0x16) */
+typedef struct
+{
+        uint32 seqnum;
+        uint32 dom_mod_count_ptr;
+       UINT64_S dom_mod_count;  /* domain mod count at last sync */
+} SAM_DELTA_MOD_COUNT;
+
+typedef union sam_delta_ctr_info
+{
+       SAM_DOMAIN_INFO    domain_info ;
+       SAM_GROUP_INFO     group_info  ;
+       SAM_ACCOUNT_INFO   account_info;
+       SAM_GROUP_MEM_INFO grp_mem_info;
+       SAM_ALIAS_INFO     alias_info  ;
+       SAM_ALIAS_MEM_INFO als_mem_info;
+       SAM_DELTA_POLICY   policy_info;
+       SAM_DELTA_PRIVS    privs_info;
+       SAM_DELTA_MOD_COUNT mod_count;
+       SAM_DELTA_TRUSTDOMS trustdoms_info;
+       SAM_DELTA_SECRET   secret_info;
+} SAM_DELTA_CTR;
+
+/* NET_R_SAM_SYNC */
+typedef struct net_r_sam_sync_info
+{
+       DOM_CRED srv_creds;
+
+       uint32 sync_context;
+
+       uint32 ptr_deltas;
+       uint32 num_deltas;
+       uint32 ptr_deltas2;
+       uint32 num_deltas2;
+
+       SAM_DELTA_HDR *hdr_deltas;
+       SAM_DELTA_CTR *deltas;
+
+       NTSTATUS status;
+} NET_R_SAM_SYNC;
+
+/* NET_Q_SAM_DELTAS */
+typedef struct net_q_sam_deltas_info
+{
+       UNISTR2 uni_srv_name;
+       UNISTR2 uni_cli_name;
+       DOM_CRED cli_creds;
+       DOM_CRED ret_creds;
+
+       uint32 database_id;
+       UINT64_S dom_mod_count;  /* domain mod count at last sync */
+
+       uint32 max_size;       /* preferred maximum length */
+
+} NET_Q_SAM_DELTAS;
+
+/* NET_R_SAM_DELTAS */
+typedef struct net_r_sam_deltas_info
+{
+       DOM_CRED srv_creds;
+
+       UINT64_S dom_mod_count;   /* new domain mod count */
+
+       uint32 ptr_deltas;
+       uint32 num_deltas;
+       uint32 num_deltas2;
+
+       SAM_DELTA_HDR *hdr_deltas;
+       SAM_DELTA_CTR *deltas;
+
+       NTSTATUS status;
+} NET_R_SAM_DELTAS;
+
+#endif /* _RPC_NETLOGON_H */
diff --git a/source4/include/rpc_parse.h b/source4/include/rpc_parse.h
new file mode 100644 (file)
index 0000000..73fbcb2
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Elrond                            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.
+*/
+
+#ifndef _RPC_PARSE_H
+#define _RPC_PARSE_H 
+
+/* different dce/rpc pipes */
+#include "rpc_reg.h"
+#include "rpc_brs.h"
+
+#endif /* _RPC_PARSE_H */
diff --git a/source4/include/rpc_reg.h b/source4/include/rpc_reg.h
new file mode 100644 (file)
index 0000000..46ec882
--- /dev/null
@@ -0,0 +1,644 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell                 1992-1997.
+   Copyright (C) Luke Kenneth Casson Leighton    1996-1997.
+   Copyright (C) Paul Ashton                          1997.
+   Copyright (C) Gerald Carter                        2002.
+   
+   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.
+*/
+
+#ifndef _RPC_REG_H /* _RPC_REG_H */
+#define _RPC_REG_H 
+
+
+/* winreg pipe defines 
+   NOT IMPLEMENTED !!
+#define _REG_UNK_01            0x01
+#define _REG_UNK_03            0x03
+#define REG_CREATE_KEY         0x06
+#define REG_DELETE_KEY         0x07
+#define REG_DELETE_VALUE       0x08
+#define REG_FLUSH_KEY          0x0b
+#define REG_GET_KEY_SEC                0x0c
+#define        _REG_UNK_0D             0x0d
+#define _REG_UNK_0E            0x0e
+#define        _REG_UNK_12             0x12
+#define _REG_UNK_13            0x13
+#define REG_SET_KEY_SEC                0x15
+#define REG_CREATE_VALUE       0x16
+#define        _REG_UNK_17             0x17
+*/
+
+/* Implemented */
+#define REG_OPEN_HKCR          0x00
+#define REG_OPEN_HKLM          0x02
+#define REG_OPEN_HKU           0x04
+#define REG_CLOSE              0x05
+#define REG_ENUM_KEY           0x09
+#define REG_ENUM_VALUE         0x0a
+#define REG_OPEN_ENTRY         0x0f
+#define REG_QUERY_KEY          0x10
+#define REG_INFO               0x11
+#define REG_SHUTDOWN           0x18
+#define REG_ABORT_SHUTDOWN     0x19
+#define        REG_SAVE_KEY            0x14    /* no idea what the real name is */
+#define REG_UNKNOWN_1A         0x1a
+
+
+#define HKEY_CLASSES_ROOT      0x80000000
+#define HKEY_CURRENT_USER      0x80000001
+#define HKEY_LOCAL_MACHINE     0x80000002
+#define HKEY_USERS             0x80000003
+
+#define KEY_HKLM       "HKLM"
+#define KEY_HKU                "HKU"
+#define KEY_HKCR       "HKCR"
+#define KEY_PRINTING   "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print"
+#define KEY_TREE_ROOT  ""
+
+/* Registry data types */
+
+#define REG_NONE                       0
+#define REG_SZ                        1
+#define REG_EXPAND_SZ                  2
+#define REG_BINARY                    3
+#define REG_DWORD                     4
+#define REG_DWORD_LE                  4        /* DWORD, little endian */
+#define REG_DWORD_BE                  5        /* DWORD, big endian */
+#define REG_LINK                       6
+#define REG_MULTI_SZ                  7
+#define REG_RESOURCE_LIST              8
+#define REG_FULL_RESOURCE_DESCRIPTOR   9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+
+/* structure to contain registry values */
+
+typedef struct {
+       fstring         valuename;
+       uint16          type;
+       uint32          size;   /* in bytes */
+       uint8           *data_p;
+} REGISTRY_VALUE;
+
+/* container for regostry values */
+
+typedef struct {
+       TALLOC_CTX      *ctx;
+       uint32          num_values;
+       REGISTRY_VALUE  **values;
+} REGVAL_CTR;
+
+/* container for registry subkey names */
+
+typedef struct {
+       TALLOC_CTX      *ctx;
+       uint32          num_subkeys;
+       char            **subkeys;
+} REGSUBKEY_CTR;
+
+
+/* 
+ * container for function pointers to enumeration routines
+ * for vitural registry view 
+ */ 
+typedef struct {
+       /* functions for enumerating subkeys and values */      
+       int     (*subkey_fn)( char *key, REGSUBKEY_CTR *subkeys);
+       int     (*value_fn) ( char *key, REGVAL_CTR *val );
+       BOOL    (*store_subkeys_fn)( char *key, REGSUBKEY_CTR *subkeys );
+       BOOL    (*store_values_fn)( char *key, REGVAL_CTR *val );
+} REGISTRY_OPS;
+
+typedef struct {
+       const char      *keyname;       /* full path to name of key */
+       REGISTRY_OPS    *ops;           /* registry function hooks */
+} REGISTRY_HOOK;
+
+
+
+/* structure to store the registry handles */
+
+typedef struct _RegistryKey {
+
+       struct _RegistryKey *prev, *next;
+
+       POLICY_HND      hnd;
+       pstring         name;   /* full name of registry key */
+       REGISTRY_HOOK   *hook;
+       
+} REGISTRY_KEY;
+
+
+/* REG_Q_OPEN_HKCR   */
+typedef struct q_reg_open_hkcr_info
+{
+       uint32 ptr;
+       uint16 unknown_0; /* 0x5428      - 16 bit unknown */
+       uint16 unknown_1; /* random.  changes */
+       uint32 level;     /* 0x0000 0002 - 32 bit unknown */
+
+} REG_Q_OPEN_HKCR  ;
+
+/* REG_R_OPEN_HKCR   */
+typedef struct r_reg_open_hkcr_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} REG_R_OPEN_HKCR;
+
+
+/* REG_Q_OPEN_HKLM   */
+typedef struct q_reg_open_hklm_info
+{
+       uint32 ptr;
+       uint16 unknown_0;       /* 0xE084      - 16 bit unknown */
+       uint16 unknown_1;       /* random.  changes */
+       uint32 access_mask;
+
+}
+REG_Q_OPEN_HKLM;
+
+/* REG_R_OPEN_HKLM   */
+typedef struct r_reg_open_hklm_info
+{
+       POLICY_HND pol;         /* policy handle */
+       NTSTATUS status;                /* return status */
+
+}
+REG_R_OPEN_HKLM;
+
+
+/* REG_Q_OPEN_HKU */
+typedef struct q_reg_open_hku_info
+{
+       uint32 ptr;
+       uint16 unknown_0; 
+       uint16 unknown_1; 
+       uint32 access_mask;    
+
+} REG_Q_OPEN_HKU;
+
+/* REG_R_OPEN_HKU */
+typedef struct r_reg_open_hku_info
+{
+       POLICY_HND pol;      /* policy handle */
+       NTSTATUS status;     /* return status */
+
+} REG_R_OPEN_HKU;
+
+
+/* REG_Q_FLUSH_KEY */
+typedef struct q_reg_open_flush_key_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+} REG_Q_FLUSH_KEY;
+
+/* REG_R_FLUSH_KEY */
+typedef struct r_reg_open_flush_key_info
+{
+       NTSTATUS status;         /* return status */
+
+} REG_R_FLUSH_KEY;
+
+
+/* REG_Q_SET_KEY_SEC */
+typedef struct q_reg_set_key_sec_info
+{
+       POLICY_HND pol;         /* policy handle */
+
+       uint32 sec_info;       /* xxxx_SECURITY_INFORMATION */
+
+       uint32 ptr;       /* pointer */
+       BUFHDR hdr_sec;    /* header for security data */
+       SEC_DESC_BUF *data;    /* security data */
+       
+} REG_Q_SET_KEY_SEC;
+
+/* REG_R_SET_KEY_SEC */
+typedef struct r_reg_set_key_sec_info
+{
+       NTSTATUS status;
+       
+} REG_R_SET_KEY_SEC;
+
+
+/* REG_Q_GET_KEY_SEC */
+typedef struct q_reg_get_key_sec_info
+{
+       POLICY_HND pol;         /* policy handle */
+
+       uint32 sec_info;       /* xxxx_SECURITY_INFORMATION */
+
+       uint32 ptr;       /* pointer */
+       BUFHDR hdr_sec;    /* header for security data */
+       SEC_DESC_BUF *data;    /* security data */
+       
+} REG_Q_GET_KEY_SEC;
+
+/* REG_R_GET_KEY_SEC */
+typedef struct r_reg_get_key_sec_info
+{
+       uint32 sec_info;       /* xxxx_SECURITY_INFORMATION */
+
+       uint32 ptr;       /* pointer */
+       BUFHDR hdr_sec;    /* header for security data */
+       SEC_DESC_BUF *data;    /* security data */
+
+       NTSTATUS status;
+       
+} REG_R_GET_KEY_SEC;
+
+/* REG_Q_CREATE_VALUE */
+typedef struct q_reg_create_value_info
+{
+       POLICY_HND pol;    /* policy handle */
+
+       UNIHDR hdr_name;   /* name of value */
+       UNISTR2 uni_name;
+
+       uint32 type;       /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+       BUFFER3 *buf_value; /* value, in byte buffer */
+
+} REG_Q_CREATE_VALUE;
+
+/* REG_R_CREATE_VALUE */
+typedef struct r_reg_create_value_info
+{ 
+       NTSTATUS status;         /* return status */
+
+} REG_R_CREATE_VALUE;
+
+/* REG_Q_ENUM_VALUE */
+typedef struct q_reg_query_value_info
+{
+       POLICY_HND pol;    /* policy handle */
+
+       uint32 val_index;  /* index */
+
+       UNIHDR hdr_name;   /* name of value */
+       UNISTR2 uni_name;
+
+       uint32 ptr_type;   /* pointer */
+       uint32 type;       /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+       uint32 ptr_value;  /* pointer */
+       BUFFER2 buf_value; /* value, in byte buffer */
+
+       uint32 ptr1;       /* pointer */
+       uint32 len_value1; /* */
+
+       uint32 ptr2;       /* pointer */
+       uint32 len_value2; /* */
+
+
+} REG_Q_ENUM_VALUE;
+
+/* REG_R_ENUM_VALUE */
+typedef struct r_reg_enum_value_info
+{ 
+       UNIHDR hdr_name;        /* name of value */
+       UNISTR2 uni_name;
+
+       uint32 ptr_type;            /* pointer */
+       uint32 type;        /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+       uint32 ptr_value;       /* pointer */
+       BUFFER2 buf_value;    /* value, in byte buffer */
+
+       uint32 ptr1;            /* pointer */
+       uint32 len_value1;       /* */
+
+       uint32 ptr2;            /* pointer */
+       uint32 len_value2;       /* */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_ENUM_VALUE;
+
+/* REG_Q_CREATE_KEY */
+typedef struct q_reg_create_key_info
+{
+       POLICY_HND pnt_pol;       /* parent key policy handle */
+
+       UNIHDR hdr_name;
+       UNISTR2 uni_name;
+
+       UNIHDR hdr_class;
+       UNISTR2 uni_class;
+
+       uint32 reserved; /* 0x0000 0000 */
+       SEC_ACCESS sam_access; /* access rights flags, see rpc_secdes.h */
+
+       uint32 ptr1;
+       uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+       uint32 ptr2;       /* pointer */
+       BUFHDR hdr_sec;    /* header for security data */
+       uint32 ptr3;       /* pointer */
+       SEC_DESC_BUF *data;
+
+       uint32 unknown_2; /* 0x0000 0000 */
+
+} REG_Q_CREATE_KEY;
+
+/* REG_R_CREATE_KEY */
+typedef struct r_reg_create_key_info
+{
+       POLICY_HND key_pol;       /* policy handle */
+       uint32 unknown; /* 0x0000 0000 */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_CREATE_KEY;
+
+/* REG_Q_DELETE_KEY */
+typedef struct q_reg_delete_key_info
+{
+       POLICY_HND pnt_pol;       /* parent key policy handle */
+
+       UNIHDR hdr_name;
+       UNISTR2 uni_name;
+} REG_Q_DELETE_KEY;
+
+/* REG_R_DELETE_KEY */
+typedef struct r_reg_delete_key_info
+{
+       POLICY_HND key_pol;       /* policy handle */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_DELETE_KEY;
+
+/* REG_Q_DELETE_VALUE */
+typedef struct q_reg_delete_val_info
+{
+       POLICY_HND pnt_pol;       /* parent key policy handle */
+
+       UNIHDR hdr_name;
+       UNISTR2 uni_name;
+
+} REG_Q_DELETE_VALUE;
+
+/* REG_R_DELETE_VALUE */
+typedef struct r_reg_delete_val_info
+{
+       POLICY_HND key_pol;       /* policy handle */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_DELETE_VALUE;
+
+/* REG_Q_QUERY_KEY */
+typedef struct q_reg_query_info
+{
+       POLICY_HND pol;       /* policy handle */
+       UNIHDR hdr_class;
+       UNISTR2 uni_class;
+
+} REG_Q_QUERY_KEY;
+
+/* REG_R_QUERY_KEY */
+typedef struct r_reg_query_key_info
+{
+       UNIHDR hdr_class;
+       UNISTR2 uni_class;
+
+       uint32 num_subkeys;
+       uint32 max_subkeylen;
+       uint32 reserved; /* 0x0000 0000 - according to MSDN (max_subkeysize?) */
+       uint32 num_values;
+       uint32 max_valnamelen;
+       uint32 max_valbufsize; 
+       uint32 sec_desc; /* 0x0000 0078 */
+       NTTIME mod_time;  /* modified time */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_QUERY_KEY;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unk_1a_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+} REG_Q_UNKNOWN_1A;
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unk_1a_info
+{
+       uint32 unknown;         /* 0x0500 0000 */
+       NTSTATUS status;         /* return status */
+
+} REG_R_UNKNOWN_1A;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unknown_14
+{
+       POLICY_HND pol;       /* policy handle */
+       
+       UNIHDR  hdr_file;       /* unicode product type header */
+       UNISTR2 uni_file;       /* local filename to save key as from regedt32.exe */
+                               /* e.g. "c:\temp\test.dat" */
+       
+       uint32 unknown;         /* 0x0000 0000 */
+
+} REG_Q_SAVE_KEY;
+
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unknown_14
+{
+       NTSTATUS status;         /* return status */
+
+} REG_R_SAVE_KEY;
+
+
+
+/* REG_Q_CLOSE */
+typedef struct reg_q_close_info
+{
+       POLICY_HND pol; /* policy handle */
+
+} REG_Q_CLOSE;
+
+/* REG_R_CLOSE */
+typedef struct reg_r_close_info
+{
+       POLICY_HND pol; /* policy handle.  should be all zeros. */
+
+       NTSTATUS status; /* return code */
+
+} REG_R_CLOSE;
+
+
+/* REG_Q_ENUM_KEY */
+typedef struct q_reg_enum_value_info
+{
+       POLICY_HND pol;         /* policy handle */
+
+       uint32 key_index;       
+
+       uint16 key_name_len;    /* 0x0000 */
+       uint16 unknown_1;       /* 0x0414 */
+
+       uint32 ptr1;            /* pointer */
+       uint32 unknown_2;       /* 0x0000 020A */
+       uint8  pad1[8];         /* padding - zeros */
+
+       uint32 ptr2;            /* pointer */
+       uint8  pad2[8];         /* padding - zeros */
+
+       uint32 ptr3;            /* pointer */
+       NTTIME time;            /* current time? */
+
+} REG_Q_ENUM_KEY;
+
+/* REG_R_ENUM_KEY */
+typedef struct r_reg_enum_key_info
+{ 
+       uint16 key_name_len;    /* number of bytes in key name */
+       uint16 unknown_1;       /* 0x0414 - matches with query unknown_1 */
+
+       uint32 ptr1;            /* pointer */
+       uint32 unknown_2;       /* 0x0000 020A */
+       uint32 unknown_3;       /* 0x0000 0000 */
+
+       UNISTR3 key_name;
+
+       uint32 ptr2;            /* pointer */
+       uint8  pad2[8];         /* padding - zeros */
+
+       uint32 ptr3;            /* pointer */
+       NTTIME time;            /* current time? */
+
+       NTSTATUS status;         /* return status */
+
+} REG_R_ENUM_KEY;
+
+
+/* REG_Q_INFO */
+typedef struct q_reg_info_info
+{
+       POLICY_HND pol;         /* policy handle */
+
+       UNIHDR  hdr_type;       /* unicode product type header */
+       UNISTR2 uni_type;       /* unicode product type - "ProductType" */
+
+       uint32 ptr_reserved;    /* pointer */
+  
+       uint32 ptr_buf;         /* the next three fields follow if ptr_buf != 0 */
+       uint32 ptr_bufsize;
+       uint32 bufsize;
+       uint32 buf_unk;
+
+       uint32 unk1;
+       uint32 ptr_buflen;
+       uint32 buflen;
+  
+       uint32 ptr_buflen2;
+       uint32 buflen2;
+
+} REG_Q_INFO;
+
+/* REG_R_INFO */
+typedef struct r_reg_info_info
+{ 
+       uint32 ptr_type;        /* key type pointer */
+       uint32 type;            /* key datatype  */
+
+       uint32 ptr_uni_val;     /* key value pointer */
+       BUFFER2 uni_val;        /* key value */
+
+       uint32 ptr_max_len;
+       uint32 buf_max_len;
+
+       uint32 ptr_len;
+       uint32 buf_len;
+  
+       NTSTATUS status;        /* return status */
+
+} REG_R_INFO;
+
+
+/* REG_Q_OPEN_ENTRY */
+typedef struct q_reg_open_entry_info
+{
+       POLICY_HND pol;        /* policy handle */
+
+       UNIHDR  hdr_name;       /* unicode registry string header */
+       UNISTR2 uni_name;       /* unicode registry string name */
+
+       uint32 unknown_0;       /* 32 bit unknown - 0x0000 0000 */
+       uint32 access_desired; 
+
+} REG_Q_OPEN_ENTRY;
+
+
+
+/* REG_R_OPEN_ENTRY */
+typedef struct r_reg_open_entry_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} REG_R_OPEN_ENTRY;
+
+/* REG_Q_SHUTDOWN */
+typedef struct q_reg_shutdown_info
+{
+       uint32 ptr_0;
+       uint32 ptr_1;
+       uint32 ptr_2;
+       UNIHDR hdr_msg;         /* shutdown message */
+       UNISTR2 uni_msg;        /* seconds */
+       uint32 timeout;         /* seconds */
+       uint8 force;            /* boolean: force shutdown */
+       uint8 reboot;           /* boolean: reboot on shutdown */
+               
+} REG_Q_SHUTDOWN;
+
+/* REG_R_SHUTDOWN */
+typedef struct r_reg_shutdown_info
+{
+       NTSTATUS status;                /* return status */
+
+} REG_R_SHUTDOWN;
+
+/* REG_Q_ABORT_SHUTDOWN */
+typedef struct q_reg_abort_shutdown_info
+{
+       uint32 ptr_server;
+       uint16 server;
+
+} REG_Q_ABORT_SHUTDOWN;
+
+/* REG_R_ABORT_SHUTDOWN */
+typedef struct r_reg_abort_shutdown_info
+{ 
+       NTSTATUS status; /* return status */
+
+} REG_R_ABORT_SHUTDOWN;
+
+
+#endif /* _RPC_REG_H */
+
diff --git a/source4/include/rpc_samr.h b/source4/include/rpc_samr.h
new file mode 100644 (file)
index 0000000..e1fa9c0
--- /dev/null
@@ -0,0 +1,1867 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Paul Ashton                  1997-2000
+   Copyright (C) Jean François Micouleau      1998-2001
+   Copyright (C) Anthony Liguori              2002
+   Copyright (C) Jim McDonough                2002
+   
+   
+   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.
+*/
+
+#ifndef _RPC_SAMR_H /* _RPC_SAMR_H */
+#define _RPC_SAMR_H 
+
+#include "rpc_misc.h"
+
+/*******************************************************************
+ the following information comes from a QuickView on samsrv.dll,
+ and gives an idea of exactly what is needed:
+x SamrAddMemberToAlias
+x SamrAddMemberToGroup
+SamrAddMultipleMembersToAlias
+x SamrChangePasswordUser
+x SamrCloseHandle
+x SamrConnect
+x SamrCreateAliasInDomain
+x SamrCreateGroupInDomain
+x SamrCreateUserInDomain
+? SamrDeleteAlias
+SamrDeleteGroup
+x SamrDeleteUser
+x SamrEnumerateAliasesInDomain
+SamrEnumerateDomainsInSamServer
+x SamrEnumerateGroupsInDomain
+x SamrEnumerateUsersInDomain
+SamrGetUserDomainPasswordInformation
+SamrLookupDomainInSamServer
+? SamrLookupIdsInDomain
+x SamrLookupNamesInDomain
+x SamrOpenAlias
+x SamrOpenDomain
+x SamrOpenGroup
+x SamrOpenUser
+x SamrQueryDisplayInformation
+x SamrQueryInformationAlias
+SamrQueryInformationDomain
+? SamrQueryInformationUser
+x SamrQuerySecurityObject
+SamrRemoveMemberFromAlias
+SamrRemoveMemberFromForiegnDomain
+SamrRemoveMemberFromGroup
+SamrRemoveMultipleMembersFromAlias
+x SamrSetInformationAlias
+SamrSetInformationDomain
+x SamrSetInformationGroup
+x SamrSetInformationUser
+SamrSetMemberAttributesOfGroup
+SamrSetSecurityObject
+SamrShutdownSamServer
+SamrTestPrivateFunctionsDomain
+SamrTestPrivateFunctionsUser
+
+********************************************************************/
+
+#define SAMR_CONNECT_ANON      0x00
+#define SAMR_CLOSE_HND         0x01
+#define SAMR_SET_SEC_OBJECT    0x02
+#define SAMR_QUERY_SEC_OBJECT  0x03
+
+#define SAMR_UNKNOWN_4         0x04 /* profile info? */
+#define SAMR_LOOKUP_DOMAIN     0x05
+#define SAMR_ENUM_DOMAINS      0x06
+#define SAMR_OPEN_DOMAIN       0x07
+#define SAMR_QUERY_DOMAIN_INFO 0x08
+#define SAMR_SET_DOMAIN_INFO   0x09
+
+#define SAMR_CREATE_DOM_GROUP  0x0a
+#define SAMR_ENUM_DOM_GROUPS   0x0b
+#define SAMR_ENUM_DOM_USERS    0x0d
+#define SAMR_CREATE_DOM_ALIAS  0x0e
+#define SAMR_ENUM_DOM_ALIASES  0x0f
+#define SAMR_QUERY_USERALIASES 0x10
+
+#define SAMR_LOOKUP_NAMES      0x11
+#define SAMR_LOOKUP_RIDS       0x12
+
+#define SAMR_OPEN_GROUP        0x13
+#define SAMR_QUERY_GROUPINFO   0x14
+#define SAMR_SET_GROUPINFO     0x15
+#define SAMR_ADD_GROUPMEM      0x16
+#define SAMR_DELETE_DOM_GROUP  0x17
+#define SAMR_DEL_GROUPMEM      0x18
+#define SAMR_QUERY_GROUPMEM    0x19
+#define SAMR_UNKNOWN_1A        0x1a
+
+#define SAMR_OPEN_ALIAS        0x1b
+#define SAMR_QUERY_ALIASINFO   0x1c
+#define SAMR_SET_ALIASINFO     0x1d
+#define SAMR_DELETE_DOM_ALIAS  0x1e
+#define SAMR_ADD_ALIASMEM      0x1f
+#define SAMR_DEL_ALIASMEM      0x20
+#define SAMR_QUERY_ALIASMEM    0x21
+
+#define SAMR_OPEN_USER         0x22
+#define SAMR_DELETE_DOM_USER   0x23
+#define SAMR_QUERY_USERINFO    0x24
+#define SAMR_SET_USERINFO2     0x25
+#define SAMR_QUERY_USERGROUPS  0x27
+
+#define SAMR_QUERY_DISPINFO    0x28
+#define SAMR_UNKNOWN_29        0x29
+#define SAMR_UNKNOWN_2a        0x2a
+#define SAMR_UNKNOWN_2b        0x2b
+#define SAMR_GET_USRDOM_PWINFO 0x2c
+#define SAMR_UNKNOWN_2D        0x2d
+#define SAMR_UNKNOWN_2E        0x2e /* looks like an alias for SAMR_QUERY_DOMAIN_INFO */
+#define SAMR_UNKNOWN_2f        0x2f
+#define SAMR_QUERY_DISPINFO3   0x30 /* Alias for SAMR_QUERY_DISPINFO
+                                      with info level 3 */
+#define SAMR_UNKNOWN_31        0x31
+#define SAMR_CREATE_USER       0x32
+#define SAMR_QUERY_DISPINFO4   0x33 /* Alias for SAMR_QUERY_DISPINFO
+                                      with info level 4 */
+#define SAMR_ADDMULTI_ALIASMEM 0x34
+
+#define SAMR_UNKNOWN_35        0x35
+#define SAMR_UNKNOWN_36        0x36
+#define SAMR_CHGPASSWD_USER    0x37
+#define SAMR_GET_DOM_PWINFO    0x38
+#define SAMR_CONNECT           0x39
+#define SAMR_SET_USERINFO      0x3A
+#define SAMR_CONNECT4          0x3E
+
+
+typedef struct _DISP_USER_INFO {
+       SAM_ACCOUNT *sam;
+} DISP_USER_INFO;
+
+typedef struct _DISP_GROUP_INFO {
+       DOMAIN_GRP *grp;
+} DISP_GROUP_INFO;
+
+
+typedef struct logon_hours_info
+{
+       uint32 len; /* normally 21 bytes */
+       uint8 hours[32];
+
+} LOGON_HRS;
+
+/* SAM_USER_INFO_23 */
+typedef struct sam_user_info_23
+{
+       /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+       NTTIME logon_time;            /* logon time */
+       NTTIME logoff_time;           /* logoff time */
+       NTTIME kickoff_time;          /* kickoff time */
+       NTTIME pass_last_set_time;    /* password last set time */
+       NTTIME pass_can_change_time;  /* password can change time */
+       NTTIME pass_must_change_time; /* password must change time */
+
+       UNIHDR hdr_user_name;    /* NULL - user name unicode string header */
+       UNIHDR hdr_full_name;    /* user's full name unicode string header */
+       UNIHDR hdr_home_dir;     /* home directory unicode string header */
+       UNIHDR hdr_dir_drive;    /* home drive unicode string header */
+       UNIHDR hdr_logon_script; /* logon script unicode string header */
+       UNIHDR hdr_profile_path; /* profile path unicode string header */
+       UNIHDR hdr_acct_desc  ;  /* user description */
+       UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+       UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+       UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+       uint8 lm_pwd[16];    /* lm user passwords */
+       uint8 nt_pwd[16];    /* nt user passwords */
+
+       uint32 user_rid;      /* Primary User ID */
+       uint32 group_rid;     /* Primary Group ID */
+
+       uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+       uint32 unknown_3; /* 0x09f8 27fa */
+
+       uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+       /* uint8 pad[2] */
+       uint32 ptr_logon_hrs; /* pointer to logon hours */
+
+       uint32 unknown_5;     /* 0x0001 0000 */
+
+       uint8 padding1[6];
+               
+       uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+       uint8 padding2;
+
+       uint8 pass[516];
+
+       UNISTR2 uni_user_name;    /* NULL - username unicode string */
+       UNISTR2 uni_full_name;    /* user's full name unicode string */
+       UNISTR2 uni_home_dir;     /* home directory unicode string */
+       UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
+       UNISTR2 uni_logon_script; /* logon script unicode string */
+       UNISTR2 uni_profile_path; /* profile path unicode string */
+       UNISTR2 uni_acct_desc  ;  /* user description unicode string */
+       UNISTR2 uni_workstations; /* login from workstations unicode string */
+       UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+       UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+
+       uint32 unknown_6; /* 0x0000 04ec */
+       uint32 padding4;
+
+       LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_23;
+
+/* SAM_USER_INFO_24 */
+typedef struct sam_user_info_24
+{
+       uint8 pass[516];
+       uint16 pw_len;
+} SAM_USER_INFO_24;
+
+/*
+ * NB. This structure is *definately* incorrect. It's my best guess
+ * currently for W2K SP2. The password field is encrypted in a different
+ * way than normal... And there are definately other problems. JRA.
+ */
+
+/* SAM_USER_INFO_25 */
+typedef struct sam_user_info_25
+{
+       /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+       NTTIME logon_time;            /* logon time */
+       NTTIME logoff_time;           /* logoff time */
+       NTTIME kickoff_time;          /* kickoff time */
+       NTTIME pass_last_set_time;    /* password last set time */
+       NTTIME pass_can_change_time;  /* password can change time */
+       NTTIME pass_must_change_time; /* password must change time */
+
+       UNIHDR hdr_user_name;    /* NULL - user name unicode string header */
+       UNIHDR hdr_full_name;    /* user's full name unicode string header */
+       UNIHDR hdr_home_dir;     /* home directory unicode string header */
+       UNIHDR hdr_dir_drive;    /* home drive unicode string header */
+       UNIHDR hdr_logon_script; /* logon script unicode string header */
+       UNIHDR hdr_profile_path; /* profile path unicode string header */
+       UNIHDR hdr_acct_desc  ;  /* user description */
+       UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+       UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+       UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+       uint8 lm_pwd[16];    /* lm user passwords */
+       uint8 nt_pwd[16];    /* nt user passwords */
+
+       uint32 user_rid;      /* Primary User ID */
+       uint32 group_rid;     /* Primary Group ID */
+
+       uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+       uint32 unknown_6[6];
+
+       uint8 pass[532];
+
+       UNISTR2 uni_user_name;    /* NULL - username unicode string */
+       UNISTR2 uni_full_name;    /* user's full name unicode string */
+       UNISTR2 uni_home_dir;     /* home directory unicode string */
+       UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
+       UNISTR2 uni_logon_script; /* logon script unicode string */
+       UNISTR2 uni_profile_path; /* profile path unicode string */
+       UNISTR2 uni_acct_desc  ;  /* user description unicode string */
+       UNISTR2 uni_workstations; /* login from workstations unicode string */
+       UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+       UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+} SAM_USER_INFO_25;
+
+
+/* SAM_USER_INFO_21 */
+typedef struct sam_user_info_21
+{
+       NTTIME logon_time;            /* logon time */
+       NTTIME logoff_time;           /* logoff time */
+       NTTIME kickoff_time;          /* kickoff time */
+       NTTIME pass_last_set_time;    /* password last set time */
+       NTTIME pass_can_change_time;  /* password can change time */
+       NTTIME pass_must_change_time; /* password must change time */
+
+       UNIHDR hdr_user_name;    /* username unicode string header */
+       UNIHDR hdr_full_name;    /* user's full name unicode string header */
+       UNIHDR hdr_home_dir;     /* home directory unicode string header */
+       UNIHDR hdr_dir_drive;    /* home drive unicode string header */
+       UNIHDR hdr_logon_script; /* logon script unicode string header */
+       UNIHDR hdr_profile_path; /* profile path unicode string header */
+       UNIHDR hdr_acct_desc  ;  /* user description */
+       UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+       UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+       UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+       uint8 lm_pwd[16];    /* lm user passwords */
+       uint8 nt_pwd[16];    /* nt user passwords */
+
+       uint32 user_rid;      /* Primary User ID */
+       uint32 group_rid;     /* Primary Group ID */
+
+       uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+       uint32 unknown_3; /* 0x00ff ffff */
+
+       uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+       /* uint8 pad[2] */
+       uint32 ptr_logon_hrs; /* unknown pointer */
+
+       uint32 unknown_5;     /* 0x0002 0000 */
+
+       uint8 padding1[6];
+               
+       uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+       uint8 padding2;
+
+       UNISTR2 uni_user_name;    /* username unicode string */
+       UNISTR2 uni_full_name;    /* user's full name unicode string */
+       UNISTR2 uni_home_dir;     /* home directory unicode string */
+       UNISTR2 uni_dir_drive;    /* home directory drive unicode string */
+       UNISTR2 uni_logon_script; /* logon script unicode string */
+       UNISTR2 uni_profile_path; /* profile path unicode string */
+       UNISTR2 uni_acct_desc  ;  /* user description unicode string */
+       UNISTR2 uni_workstations; /* login from workstations unicode string */
+       UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+       UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+       uint32 unknown_6; /* 0x0000 04ec */
+       uint32 padding4;
+
+       LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_21;
+
+#define PASS_MUST_CHANGE_AT_NEXT_LOGON 0x01
+#define PASS_DONT_CHANGE_AT_NEXT_LOGON 0x00
+
+/* SAM_USER_INFO_20 */
+typedef struct sam_user_info_20
+{
+       UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+       UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+} SAM_USER_INFO_20;
+
+/* SAM_USER_INFO_12 */
+typedef struct sam_user_info_12
+{
+       uint8 lm_pwd[16];    /* lm user passwords */
+       uint8 nt_pwd[16];    /* nt user passwords */
+
+       uint8 lm_pwd_active; 
+       uint8 nt_pwd_active; 
+
+} SAM_USER_INFO_12;
+
+/* SAM_USER_INFO_11 */
+typedef struct sam_user_info_11
+{
+       uint8  padding_0[16];  /* 0 - padding 16 bytes */
+       NTTIME expiry;         /* expiry time or something? */
+       uint8  padding_1[24];  /* 0 - padding 24 bytes */
+
+       UNIHDR hdr_mach_acct;  /* unicode header for machine account */
+       uint32 padding_2;      /* 0 - padding 4 bytes */
+
+       uint32 ptr_1;          /* pointer */
+       uint8  padding_3[32];  /* 0 - padding 32 bytes */
+       uint32 padding_4;      /* 0 - padding 4 bytes */
+
+       uint32 ptr_2;          /* pointer */
+       uint32 padding_5;      /* 0 - padding 4 bytes */
+
+       uint32 ptr_3;          /* pointer */
+       uint8  padding_6[32];  /* 0 - padding 32 bytes */
+
+       uint32 rid_user;       /* user RID */
+       uint32 rid_group;      /* group RID */
+
+       uint16 acct_ctrl;      /* 0080 - ACB_XXXX */
+       uint16 unknown_3;      /* 16 bit padding */
+
+       uint16 unknown_4;      /* 0x003f      - 16 bit unknown */
+       uint16 unknown_5;      /* 0x003c      - 16 bit unknown */
+
+       uint8  padding_7[16];  /* 0 - padding 16 bytes */
+       uint32 padding_8;      /* 0 - padding 4 bytes */
+       
+       UNISTR2 uni_mach_acct; /* unicode string for machine account */
+
+       uint8  padding_9[48];  /* 0 - padding 48 bytes */
+
+} SAM_USER_INFO_11;
+
+
+/* SAM_USER_INFO_10 */
+typedef struct sam_user_info_10
+{
+       uint32 acb_info;
+
+} SAM_USER_INFO_10;
+
+
+
+/* SAMR_Q_CLOSE_HND - probably a policy handle close */
+typedef struct q_samr_close_hnd_info
+{
+    POLICY_HND pol;          /* policy handle */
+
+} SAMR_Q_CLOSE_HND;
+
+
+/* SAMR_R_CLOSE_HND - probably a policy handle close */
+typedef struct r_samr_close_hnd_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_CLOSE_HND;
+
+
+/****************************************************************************
+SAMR_Q_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_Q_GET_USRDOM_PWINFO */
+typedef struct q_samr_usrdom_pwinfo_info
+{
+       POLICY_HND user_pol;          /* policy handle */
+
+} SAMR_Q_GET_USRDOM_PWINFO;
+
+
+/****************************************************************************
+SAMR_R_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_R_GET_USRDOM_PWINFO */
+typedef struct r_samr_usrdom_pwinfo_info
+{
+       uint16 unknown_0; /* 0000 */
+       uint16 unknown_1; /* 0x0016 or 0x0015 */
+       uint32 unknown_2; /* 0x0000 0000 */
+       NTSTATUS status; 
+
+} SAMR_R_GET_USRDOM_PWINFO;
+
+/****************************************************************************
+SAMR_Q_SET_SEC_OBJ - info level 4.
+*****************************************************************************/
+
+/* SAMR_Q_SET_SEC_OBJ - */
+typedef struct q_samr_set_sec_obj_info
+{
+       POLICY_HND pol;          /* policy handle */
+       uint32 sec_info;         /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+       SEC_DESC_BUF *buf;
+
+} SAMR_Q_SET_SEC_OBJ;
+
+/* SAMR_R_SET_SEC_OBJ - */
+typedef struct r_samr_set_sec_obj_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_SET_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_SEC_OBJ - info level 4.  returns SIDs.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_SEC_OBJ - probably get domain info... */
+typedef struct q_samr_query_sec_obj_info
+{
+       POLICY_HND user_pol;          /* policy handle */
+       uint32 sec_info;     /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+
+} SAMR_Q_QUERY_SEC_OBJ;
+
+/* SAMR_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_samr_query_sec_obj_info
+{
+       uint32 ptr;
+       SEC_DESC_BUF *buf;
+
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_QUERY_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_DOMAIN_INFO - probably a query on domain group info.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_DOMAIN_INFO - */
+typedef struct q_samr_query_domain_info
+{
+       POLICY_HND domain_pol;   /* policy handle */
+       uint16 switch_value;     /* 0x0002, 0x0001 */
+
+} SAMR_Q_QUERY_DOMAIN_INFO;
+
+typedef struct sam_unknown_info_3_info
+{
+       NTTIME logout;  
+       /* 0x8000 0000 */ /* DON'T forcibly disconnect remote users from server when logon hours expire*/
+
+       /* 0x0000 0000 */ /* forcibly disconnect remote users from server when logon hours expire*/
+
+} SAM_UNK_INFO_3;
+
+typedef struct sam_unknown_info_6_info
+{
+       uint32 unknown_0; /* 0x0000 0000 */
+
+       uint32 ptr_0;     /* pointer to unknown structure */
+       uint8  padding[12]; /* 12 bytes zeros */
+
+} SAM_UNK_INFO_6;
+
+typedef struct sam_unknown_info_7_info
+{
+       uint16 unknown_0; /* 0x0003 */
+
+} SAM_UNK_INFO_7;
+
+typedef struct sam_unknown_info_12_inf
+{
+       NTTIME duration;
+       NTTIME reset_count;
+       uint16 bad_attempt_lockout;
+
+} SAM_UNK_INFO_12;
+
+typedef struct sam_unknown_info_5_inf
+{
+       UNIHDR hdr_server; /* server name unicode header */
+       UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_5;
+
+typedef struct sam_unknown_info_2_inf
+{
+       uint32 unknown_0; /* 0x0000 0000 */
+       uint32 unknown_1; /* 0x8000 0000 */
+       uint32 unknown_2; /* 0x0000 0000 */
+
+       uint32 ptr_0;     /* pointer to unknown structure */
+       UNIHDR hdr_domain; /* domain name unicode header */
+       UNIHDR hdr_server; /* server name unicode header */
+
+       /* put all the data in here, at the moment, including what the above
+          pointer is referring to
+        */
+
+       uint32 seq_num; /* some sort of incrementing sequence number? */
+       uint32 unknown_3; /* 0x0000 0000 */
+       
+       uint32 unknown_4; /* 0x0000 0001 */
+       uint32 unknown_5; /* 0x0000 0003 */
+       uint32 unknown_6; /* 0x0000 0001 */
+       uint32 num_domain_usrs; /* number of users in domain */
+       uint32 num_domain_grps; /* number of domain groups in domain */
+       uint32 num_local_grps; /* number of local groups in domain */
+
+       uint8 padding[12]; /* 12 bytes zeros */
+
+       UNISTR2 uni_domain; /* domain name unicode string */
+       UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_2;
+
+typedef struct sam_unknown_info_1_inf
+{
+       uint16 min_length_password;
+       uint16 password_history;
+       uint32 flag;
+       NTTIME expire;
+       NTTIME min_passwordage;
+
+} SAM_UNK_INFO_1;
+
+
+typedef struct sam_unknown_ctr_info
+{
+       union
+       {
+               SAM_UNK_INFO_1 inf1;
+               SAM_UNK_INFO_2 inf2;
+               SAM_UNK_INFO_3 inf3;
+               SAM_UNK_INFO_5 inf5;
+               SAM_UNK_INFO_6 inf6;
+               SAM_UNK_INFO_7 inf7;
+               SAM_UNK_INFO_12 inf12;
+
+       } info;
+
+} SAM_UNK_CTR;
+
+
+/* SAMR_R_QUERY_DOMAIN_INFO - */
+typedef struct r_samr_query_domain_info
+{
+       uint32 ptr_0;
+       uint16 switch_value; /* same as in query */
+
+       SAM_UNK_CTR *ctr;
+
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_QUERY_DOMAIN_INFO;
+
+
+/* SAMR_Q_LOOKUP_DOMAIN - obtain SID for a local domain */
+typedef struct q_samr_lookup_domain_info
+{
+       POLICY_HND connect_pol;
+
+       UNIHDR  hdr_domain;
+       UNISTR2 uni_domain;
+
+} SAMR_Q_LOOKUP_DOMAIN;
+
+
+/* SAMR_R_LOOKUP_DOMAIN */
+typedef struct r_samr_lookup_domain_info
+{
+       uint32   ptr_sid;
+       DOM_SID2 dom_sid;
+
+       NTSTATUS status;
+
+} SAMR_R_LOOKUP_DOMAIN;
+
+
+/****************************************************************************
+SAMR_Q_OPEN_DOMAIN - unknown_0 values seen associated with SIDs:
+
+0x0000 03f1 and a specific   domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+0x0000 0200 and a specific   domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+*****************************************************************************/
+
+/* SAMR_Q_OPEN_DOMAIN */
+typedef struct q_samr_open_domain_info
+{
+       POLICY_HND pol;   /* policy handle */
+       uint32 flags;               /* 0x2000 0000; 0x0000 0211; 0x0000 0280; 0x0000 0200 - flags? */
+       DOM_SID2 dom_sid;         /* domain SID */
+
+} SAMR_Q_OPEN_DOMAIN;
+
+
+/* SAMR_R_OPEN_DOMAIN - probably an open */
+typedef struct r_samr_open_domain_info
+{
+       POLICY_HND domain_pol; /* policy handle associated with the SID */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_OPEN_DOMAIN;
+
+#define MAX_SAM_ENTRIES_W2K 0x400
+#define MAX_SAM_ENTRIES_W95 50
+/* The following should be the greater of the preceeding two. */
+#define MAX_SAM_ENTRIES MAX_SAM_ENTRIES_W2K
+
+typedef struct samr_entry_info
+{
+       uint32 rid;
+       UNIHDR hdr_name;
+
+} SAM_ENTRY;
+
+
+/* SAMR_Q_ENUM_DOMAINS - SAM rids and names */
+typedef struct q_samr_enum_domains_info
+{
+       POLICY_HND pol;     /* policy handle */
+
+       uint32 start_idx;   /* enumeration handle */
+       uint32 max_size;    /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOMAINS;
+
+/* SAMR_R_ENUM_DOMAINS - SAM rids and Domain names */
+typedef struct r_samr_enum_domains_info
+{
+       uint32 next_idx;     /* next starting index required for enum */
+       uint32 ptr_entries1;  
+
+       uint32 num_entries2;
+       uint32 ptr_entries2;
+
+       uint32 num_entries3;
+
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_dom_name;
+
+       uint32 num_entries4;
+
+       NTSTATUS status;
+
+} SAMR_R_ENUM_DOMAINS;
+
+/* SAMR_Q_ENUM_DOM_USERS - SAM rids and names */
+typedef struct q_samr_enum_dom_users_info
+{
+       POLICY_HND pol;          /* policy handle */
+
+       uint32 start_idx;   /* number of values (0 indicates unlimited?) */
+       uint16 acb_mask;          /* 0x0000 indicates all */
+       uint16 unknown_1;         /* 0x0000 */
+
+       uint32 max_size;              /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_USERS;
+
+
+/* SAMR_R_ENUM_DOM_USERS - SAM rids and names */
+typedef struct r_samr_enum_dom_users_info
+{
+       uint32 next_idx;     /* next starting index required for enum */
+       uint32 ptr_entries1;  
+
+       uint32 num_entries2;
+       uint32 ptr_entries2;
+
+       uint32 num_entries3;
+
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_acct_name;
+
+       uint32 num_entries4;
+
+       NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_USERS;
+
+
+/* SAMR_Q_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct q_samr_enum_dom_groups_info
+{
+       POLICY_HND pol;          /* policy handle */
+
+       /* this is possibly an enumeration context handle... */
+       uint32 start_idx;         /* 0x0000 0000 */
+
+       uint32 max_size;              /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_GROUPS;
+
+
+/* SAMR_R_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct r_samr_enum_dom_groups_info
+{
+       uint32 next_idx;
+       uint32 ptr_entries1;
+
+       uint32 num_entries2;
+       uint32 ptr_entries2;
+
+       uint32 num_entries3;
+
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_grp_name;
+
+       uint32 num_entries4;
+
+       NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_GROUPS;
+
+
+/* SAMR_Q_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct q_samr_enum_dom_aliases_info
+{
+       POLICY_HND pol;          /* policy handle */
+
+       /* this is possibly an enumeration context handle... */
+       uint32 start_idx;         /* 0x0000 0000 */
+
+       uint32 max_size;              /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_ALIASES;
+
+
+/* SAMR_R_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct r_samr_enum_dom_aliases_info
+{
+       uint32 next_idx;
+       uint32 ptr_entries1;
+
+       uint32 num_entries2;
+       uint32 ptr_entries2;
+
+       uint32 num_entries3;
+
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_grp_name;
+
+       uint32 num_entries4;
+
+       NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_ALIASES;
+
+
+/* -- Level 1 Display Info - User Information -- */
+
+typedef struct samr_entry_info1
+{
+       uint32 user_idx;
+
+       uint32 rid_user;
+       uint16 acb_info;
+
+       UNIHDR hdr_acct_name;
+       UNIHDR hdr_user_name;
+       UNIHDR hdr_user_desc;
+
+} SAM_ENTRY1;
+
+typedef struct samr_str_entry_info1
+{
+       UNISTR2 uni_acct_name;
+       UNISTR2 uni_full_name;
+       UNISTR2 uni_acct_desc;
+
+} SAM_STR1;
+
+typedef struct sam_entry_info_1
+{
+       SAM_ENTRY1 *sam;
+       SAM_STR1   *str;
+
+} SAM_DISPINFO_1;
+
+
+/* -- Level 2 Display Info - Trust Account Information -- */
+
+typedef struct samr_entry_info2
+{
+       uint32 user_idx;
+
+       uint32 rid_user;
+       uint16 acb_info;
+
+       UNIHDR hdr_srv_name;
+       UNIHDR hdr_srv_desc;
+
+} SAM_ENTRY2;
+
+typedef struct samr_str_entry_info2
+{
+       UNISTR2 uni_srv_name;
+       UNISTR2 uni_srv_desc;
+
+} SAM_STR2;
+
+typedef struct sam_entry_info_2
+{
+       SAM_ENTRY2 *sam;
+       SAM_STR2   *str;
+
+} SAM_DISPINFO_2;
+
+
+/* -- Level 3 Display Info - Domain Group Information -- */
+
+typedef struct samr_entry_info3
+{
+       uint32 grp_idx;
+
+       uint32 rid_grp;
+       uint32 attr;     /* SE_GROUP_xxx, usually 7 */
+
+       UNIHDR hdr_grp_name;
+       UNIHDR hdr_grp_desc;
+
+} SAM_ENTRY3;
+
+typedef struct samr_str_entry_info3
+{
+       UNISTR2 uni_grp_name;
+       UNISTR2 uni_grp_desc;
+
+} SAM_STR3;
+
+typedef struct sam_entry_info_3
+{
+       SAM_ENTRY3 *sam;
+       SAM_STR3   *str;
+
+} SAM_DISPINFO_3;
+
+
+/* -- Level 4 Display Info - User List (ASCII) -- */
+
+typedef struct samr_entry_info4
+{
+       uint32 user_idx;
+       STRHDR hdr_acct_name;
+
+} SAM_ENTRY4;
+
+typedef struct samr_str_entry_info4
+{
+       STRING2 acct_name;
+
+} SAM_STR4;
+
+typedef struct sam_entry_info_4
+{
+       SAM_ENTRY4 *sam;
+       SAM_STR4   *str;
+
+} SAM_DISPINFO_4;
+
+
+/* -- Level 5 Display Info - Group List (ASCII) -- */
+
+typedef struct samr_entry_info5
+{
+       uint32 grp_idx;
+       STRHDR hdr_grp_name;
+
+} SAM_ENTRY5;
+
+typedef struct samr_str_entry_info5
+{
+       STRING2 grp_name;
+
+} SAM_STR5;
+
+typedef struct sam_entry_info_5
+{
+       SAM_ENTRY5 *sam;
+       SAM_STR5   *str;
+
+} SAM_DISPINFO_5;
+
+
+typedef struct sam_dispinfo_ctr_info
+{
+       union
+       {
+               SAM_DISPINFO_1 *info1; /* users/names/descriptions */
+               SAM_DISPINFO_2 *info2; /* trust accounts */
+               SAM_DISPINFO_3 *info3; /* domain groups/descriptions */
+               SAM_DISPINFO_4 *info4; /* user list (ASCII) - used by Win95 */
+               SAM_DISPINFO_5 *info5; /* group list (ASCII) */
+               void       *info; /* allows assignment without typecasting, */
+
+       } sam;
+
+} SAM_DISPINFO_CTR;
+
+
+/* SAMR_Q_QUERY_DISPINFO - SAM rids, names and descriptions */
+typedef struct q_samr_query_disp_info
+{
+       POLICY_HND domain_pol;
+
+       uint16 switch_level;    /* see SAM_DISPINFO_CTR above */
+       /* align */
+
+       uint32 start_idx;       /* start enumeration index */
+       uint32 max_entries;     /* maximum number of entries to return */
+       uint32 max_size;        /* recommended data size; if exceeded server
+                                  should return STATUS_MORE_ENTRIES */
+
+} SAMR_Q_QUERY_DISPINFO;
+
+
+/* SAMR_R_QUERY_DISPINFO  */
+typedef struct r_samr_query_dispinfo_info
+{
+       uint32 total_size;     /* total data size for all matching entries
+                                 (0 = uncalculated) */
+       uint32 data_size;      /* actual data size returned = size of SAM_ENTRY
+                                 structures + total length of strings */
+
+       uint16 switch_level;   /* see SAM_DISPINFO_CTR above */
+       /* align */
+
+       uint32 num_entries;    /* number of entries returned */
+       uint32 ptr_entries;
+       uint32 num_entries2;
+
+       SAM_DISPINFO_CTR *ctr;
+
+       NTSTATUS status;
+
+} SAMR_R_QUERY_DISPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_GROUP - delete domain group */
+typedef struct q_samr_delete_dom_group_info
+{
+    POLICY_HND group_pol;          /* policy handle */
+
+} SAMR_Q_DELETE_DOM_GROUP;
+
+
+/* SAMR_R_DELETE_DOM_GROUP - delete domain group */
+typedef struct r_samr_delete_dom_group_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;        /* return status */
+
+} SAMR_R_DELETE_DOM_GROUP;
+
+
+/* SAMR_Q_CREATE_DOM_GROUP - SAM create group */
+typedef struct q_samr_create_dom_group_info
+{
+       POLICY_HND pol;        /* policy handle */
+
+       UNIHDR hdr_acct_desc;
+       UNISTR2 uni_acct_desc;
+
+       uint32 access_mask;    
+
+} SAMR_Q_CREATE_DOM_GROUP;
+
+/* SAMR_R_CREATE_DOM_GROUP - SAM create group */
+typedef struct r_samr_create_dom_group_info
+{
+       POLICY_HND pol;        /* policy handle */
+
+       uint32 rid;    
+       NTSTATUS status;    
+
+} SAMR_R_CREATE_DOM_GROUP;
+
+/* SAMR_Q_QUERY_GROUPINFO - SAM Group Info */
+typedef struct q_samr_query_group_info
+{
+       POLICY_HND pol;        /* policy handle */
+
+       uint16 switch_level;    /* 0x0001 seen */
+
+} SAMR_Q_QUERY_GROUPINFO;
+
+typedef struct samr_group_info1
+{
+       UNIHDR hdr_acct_name;
+
+       uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+       uint32 num_members; /* 0x0000 0001 - number of group members? */
+
+       UNIHDR hdr_acct_desc;
+
+       UNISTR2 uni_acct_name;
+       UNISTR2 uni_acct_desc;
+
+} GROUP_INFO1;
+
+typedef struct samr_group_info3
+{
+       uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+
+} GROUP_INFO3;
+
+typedef struct samr_group_info4
+{
+       UNIHDR hdr_acct_desc;
+       UNISTR2 uni_acct_desc;
+
+} GROUP_INFO4;
+
+/* GROUP_INFO_CTR */
+typedef struct group_info_ctr
+{
+       uint16 switch_value1;
+
+       union
+       {
+               GROUP_INFO1 info1;
+               GROUP_INFO3 info3;
+               GROUP_INFO4 info4;
+
+       } group;
+
+} GROUP_INFO_CTR;
+
+/* SAMR_R_QUERY_GROUPINFO - SAM Group Info */
+typedef struct r_samr_query_groupinfo_info
+{
+       uint32 ptr;        
+       GROUP_INFO_CTR *ctr;
+
+       NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPINFO;
+
+
+/* SAMR_Q_SET_GROUPINFO - SAM Group Info */
+typedef struct q_samr_set_group_info
+{
+       POLICY_HND pol;        /* policy handle */
+       GROUP_INFO_CTR *ctr;
+
+} SAMR_Q_SET_GROUPINFO;
+
+/* SAMR_R_SET_GROUPINFO - SAM Group Info */
+typedef struct r_samr_set_group_info
+{
+       NTSTATUS status;
+
+} SAMR_R_SET_GROUPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct q_samr_delete_dom_alias_info
+{
+    POLICY_HND alias_pol;          /* policy handle */
+
+} SAMR_Q_DELETE_DOM_ALIAS;
+
+
+/* SAMR_R_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct r_samr_delete_dom_alias_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;        /* return status */
+
+} SAMR_R_DELETE_DOM_ALIAS;
+
+
+/* SAMR_Q_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct q_samr_create_dom_alias_info
+{
+       POLICY_HND dom_pol;        /* policy handle */
+
+       UNIHDR hdr_acct_desc;
+       UNISTR2 uni_acct_desc;
+
+       uint32 access_mask;    /* 0x001f000f */
+
+} SAMR_Q_CREATE_DOM_ALIAS;
+
+/* SAMR_R_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct r_samr_create_dom_alias_info
+{
+       POLICY_HND alias_pol;        /* policy handle */
+
+       uint32 rid;    
+       NTSTATUS status;    
+
+} SAMR_R_CREATE_DOM_ALIAS;
+
+/* SAMR_Q_QUERY_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_query_alias_info
+{
+       POLICY_HND pol;        /* policy handle */
+
+       uint16 switch_level;    /* 0x0003 seen */
+
+} SAMR_Q_QUERY_ALIASINFO;
+
+typedef struct samr_alias_info1
+{
+       UNIHDR hdr_acct_name;
+       UNIHDR hdr_acct_desc;
+       uint32 num_member;
+       UNISTR2 uni_acct_name;
+       UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO1;
+
+typedef struct samr_alias_info3
+{
+       UNIHDR hdr_acct_desc;
+       UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO3;
+
+/* ALIAS_INFO_CTR */
+typedef struct alias_info_ctr
+{
+       uint16 switch_value1;
+       uint16 switch_value2;
+
+       union
+       {
+               ALIAS_INFO1 info1;
+               ALIAS_INFO3 info3;
+
+       } alias;
+
+} ALIAS_INFO_CTR;
+
+/* SAMR_R_QUERY_ALIASINFO - SAM alias info */
+typedef struct r_samr_query_aliasinfo_info
+{
+       uint32 ptr;        
+       ALIAS_INFO_CTR ctr;
+
+       NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASINFO;
+
+
+/* SAMR_Q_SET_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_set_alias_info
+{
+       POLICY_HND alias_pol;        /* policy handle */
+       ALIAS_INFO_CTR ctr;
+
+} SAMR_Q_SET_ALIASINFO;
+
+/* SAMR_R_SET_ALIASINFO - SAM alias info */
+typedef struct r_samr_set_aliasinfo_info
+{
+       NTSTATUS status;
+
+} SAMR_R_SET_ALIASINFO;
+
+
+/* SAMR_Q_QUERY_USERGROUPS - */
+typedef struct q_samr_query_usergroup_info
+{
+       POLICY_HND pol;          /* policy handle associated with unknown id */
+
+} SAMR_Q_QUERY_USERGROUPS;
+
+/* SAMR_R_QUERY_USERGROUPS - probably a get sam info */
+typedef struct r_samr_query_usergroup_info
+{
+       uint32 ptr_0;            /* pointer */
+       uint32 num_entries;      /* number of RID groups */
+       uint32 ptr_1;            /* pointer */
+       uint32 num_entries2;     /* number of RID groups */
+
+       DOM_GID *gid; /* group info */
+
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_QUERY_USERGROUPS;
+
+/* SAM_USERINFO_CTR - sam user info */
+typedef struct sam_userinfo_ctr_info
+{
+       uint16 switch_value;      
+
+       union
+       {
+               SAM_USER_INFO_10 *id10; /* auth-level 0x10 */
+               SAM_USER_INFO_11 *id11; /* auth-level 0x11 */
+               SAM_USER_INFO_12 *id12; /* auth-level 0x12 */
+               SAM_USER_INFO_20 *id20; /* auth-level 20 */
+               SAM_USER_INFO_21 *id21; /* auth-level 21 */
+               SAM_USER_INFO_23 *id23; /* auth-level 0x17 */
+               SAM_USER_INFO_24 *id24; /* auth-level 0x18 */
+               SAM_USER_INFO_25 *id25; /* auth-level 0x19 */
+               void* id; /* to make typecasting easy */
+
+       } info;
+
+} SAM_USERINFO_CTR;
+
+
+/* SAMR_Q_SET_USERINFO2 - set sam info */
+typedef struct q_samr_set_user_info2
+{
+       POLICY_HND pol;          /* policy handle associated with user */
+       uint16 switch_value;      /* 0x0010 */
+
+       SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO2;
+
+/* SAMR_R_SET_USERINFO2 - set sam info */
+typedef struct r_samr_set_user_info2
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_SET_USERINFO2;
+
+/* SAMR_Q_SET_USERINFO - set sam info */
+typedef struct q_samr_set_user_info
+{
+       POLICY_HND pol;          /* policy handle associated with user */
+       uint16 switch_value;
+       SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO;
+
+/* SAMR_R_SET_USERINFO - set sam info */
+typedef struct r_samr_set_user_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_SET_USERINFO;
+
+
+/* SAMR_Q_QUERY_USERINFO - probably a get sam info */
+typedef struct q_samr_query_user_info
+{
+       POLICY_HND pol;          /* policy handle associated with unknown id */
+       uint16 switch_value;         /* 0x0015, 0x0011 or 0x0010 - 16 bit unknown */
+
+} SAMR_Q_QUERY_USERINFO;
+
+/* SAMR_R_QUERY_USERINFO - probably a get sam info */
+typedef struct r_samr_query_user_info
+{
+       uint32 ptr;            /* pointer */
+       SAM_USERINFO_CTR *ctr;
+
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_QUERY_USERINFO;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_USERALIASES - do a conversion from name to RID.
+
+the policy handle allocated by an "samr open secret" call is associated
+with a SID.  this policy handle is what is queried here, *not* the SID
+itself.  the response to the lookup rids is relative to this SID.
+*****************************************************************************/
+/* SAMR_Q_QUERY_USERALIASES */
+typedef struct q_samr_query_useraliases_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+       uint32 num_sids1;      /* number of rids being looked up */
+       uint32 ptr;            /* buffer pointer */
+       uint32 num_sids2;      /* number of rids being looked up */
+
+       uint32   *ptr_sid; /* pointers to sids to be looked up */
+       DOM_SID2 *sid    ; /* sids to be looked up. */
+
+} SAMR_Q_QUERY_USERALIASES;
+
+
+/* SAMR_R_QUERY_USERALIASES */
+typedef struct r_samr_query_useraliases_info
+{
+       uint32 num_entries;
+       uint32 ptr; /* undocumented buffer pointer */
+
+       uint32 num_entries2; 
+       uint32 *rid; /* domain RIDs being looked up */
+
+       NTSTATUS status; /* return code */
+
+} SAMR_R_QUERY_USERALIASES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_NAMES - do a conversion from Names to RIDs+types.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_NAMES */
+typedef struct q_samr_lookup_names_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+       uint32 num_names1;      /* number of names being looked up */
+       uint32 flags;           /* 0x0000 03e8 - unknown */
+       uint32 ptr;            /* 0x0000 0000 - 32 bit unknown */
+       uint32 num_names2;      /* number of names being looked up */
+
+       UNIHDR  *hdr_name; /* unicode account name header */
+       UNISTR2 *uni_name; /* unicode account name string */
+
+} SAMR_Q_LOOKUP_NAMES;
+
+
+/* SAMR_R_LOOKUP_NAMES */
+typedef struct r_samr_lookup_names_info
+{
+       uint32 num_rids1;      /* number of aliases being looked up */
+       uint32 ptr_rids;       /* pointer to aliases */
+       uint32 num_rids2;      /* number of aliases being looked up */
+
+       uint32 *rids; /* rids */
+
+       uint32 num_types1;      /* number of users in aliases being looked up */
+       uint32 ptr_types;       /* pointer to users in aliases */
+       uint32 num_types2;      /* number of users in aliases being looked up */
+
+       uint32 *types; /* SID_ENUM type */
+
+       NTSTATUS status; /* return code */
+
+} SAMR_R_LOOKUP_NAMES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_RIDS - do a conversion from RID groups to something.
+
+called to resolve domain RID groups.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_RIDS */
+typedef struct q_samr_lookup_rids_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+       uint32 num_rids1;      /* number of rids being looked up */
+       uint32 flags;          /* 0x0000 03e8 - unknown */
+       uint32 ptr;            /* 0x0000 0000 - 32 bit unknown */
+       uint32 num_rids2;      /* number of rids being looked up */
+
+       uint32 *rid; /* domain RIDs being looked up */
+
+} SAMR_Q_LOOKUP_RIDS;
+
+
+/****************************************************************************
+SAMR_R_LOOKUP_RIDS - do a conversion from group RID to names
+
+*****************************************************************************/
+/* SAMR_R_LOOKUP_RIDS */
+typedef struct r_samr_lookup_rids_info
+{
+       uint32 num_names1;      /* number of aliases being looked up */
+       uint32 ptr_names;       /* pointer to aliases */
+       uint32 num_names2;      /* number of aliases being looked up */
+
+       UNIHDR  *hdr_name; /* unicode account name header */
+       UNISTR2 *uni_name; /* unicode account name string */
+
+       uint32 num_types1;      /* number of users in aliases being looked up */
+       uint32 ptr_types;       /* pointer to users in aliases */
+       uint32 num_types2;      /* number of users in aliases being looked up */
+
+       uint32 *type; /* SID_ENUM type */
+
+       NTSTATUS status;
+
+} SAMR_R_LOOKUP_RIDS;
+
+
+/* SAMR_Q_OPEN_USER - probably an open */
+typedef struct q_samr_open_user_info
+{
+       POLICY_HND domain_pol;       /* policy handle */
+       uint32 access_mask;     /* 32 bit unknown - 0x02011b */
+       uint32 user_rid;      /* user RID */
+
+} SAMR_Q_OPEN_USER;
+
+
+/* SAMR_R_OPEN_USER - probably an open */
+typedef struct r_samr_open_user_info
+{
+       POLICY_HND user_pol;       /* policy handle associated with unknown id */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_OPEN_USER;
+
+
+/* SAMR_Q_CREATE_USER - probably a create */
+typedef struct q_samr_create_user_info
+{
+       POLICY_HND domain_pol;       /* policy handle */
+
+       UNIHDR  hdr_name;       /* unicode account name header */
+       UNISTR2 uni_name;       /* unicode account name */
+
+       uint32 acb_info;      /* account control info */
+       uint32 access_mask;     /* 0xe005 00b0 */
+
+} SAMR_Q_CREATE_USER;
+
+
+/* SAMR_R_CREATE_USER - probably a create */
+typedef struct r_samr_create_user_info
+{
+       POLICY_HND user_pol;       /* policy handle associated with user */
+
+       uint32 access_granted;
+       uint32 user_rid;      /* user RID */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_CREATE_USER;
+
+
+/* SAMR_Q_DELETE_DOM_USER - delete domain user */
+typedef struct q_samr_delete_dom_user_info
+{
+    POLICY_HND user_pol;          /* policy handle */
+
+} SAMR_Q_DELETE_DOM_USER;
+
+
+/* SAMR_R_DELETE_DOM_USER - delete domain user */
+typedef struct r_samr_delete_dom_user_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;        /* return status */
+
+} SAMR_R_DELETE_DOM_USER;
+
+
+/* SAMR_Q_QUERY_GROUPMEM - query group members */
+typedef struct q_samr_query_groupmem_info
+{
+       POLICY_HND group_pol;        /* policy handle */
+
+} SAMR_Q_QUERY_GROUPMEM;
+
+
+/* SAMR_R_QUERY_GROUPMEM - query group members */
+typedef struct r_samr_query_groupmem_info
+{
+       uint32 ptr;
+       uint32 num_entries;
+
+       uint32 ptr_rids;
+       uint32 ptr_attrs;
+
+       uint32 num_rids;
+       uint32 *rid;
+
+       uint32 num_attrs;
+       uint32 *attr;
+
+       NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPMEM;
+
+
+/* SAMR_Q_DEL_GROUPMEM - probably an del group member */
+typedef struct q_samr_del_group_mem_info
+{
+       POLICY_HND pol;       /* policy handle */
+       uint32 rid;         /* rid */
+
+} SAMR_Q_DEL_GROUPMEM;
+
+
+/* SAMR_R_DEL_GROUPMEM - probably an del group member */
+typedef struct r_samr_del_group_mem_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_DEL_GROUPMEM;
+
+
+/* SAMR_Q_ADD_GROUPMEM - probably an add group member */
+typedef struct q_samr_add_group_mem_info
+{
+       POLICY_HND pol;       /* policy handle */
+
+       uint32 rid;         /* rid */
+       uint32 unknown;     /* 0x0000 0005 */
+
+} SAMR_Q_ADD_GROUPMEM;
+
+
+/* SAMR_R_ADD_GROUPMEM - probably an add group member */
+typedef struct r_samr_add_group_mem_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_ADD_GROUPMEM;
+
+
+/* SAMR_Q_OPEN_GROUP - probably an open */
+typedef struct q_samr_open_group_info
+{
+       POLICY_HND domain_pol;       /* policy handle */
+       uint32 access_mask;         /* 0x0000 0001, 0x0000 0003, 0x0000 001f */
+       uint32 rid_group;        /* rid */
+
+} SAMR_Q_OPEN_GROUP;
+
+
+/* SAMR_R_OPEN_GROUP - probably an open */
+typedef struct r_samr_open_group_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_OPEN_GROUP;
+
+
+/* SAMR_Q_QUERY_ALIASMEM - query alias members */
+typedef struct q_samr_query_aliasmem_info
+{
+       POLICY_HND alias_pol;        /* policy handle */
+
+} SAMR_Q_QUERY_ALIASMEM;
+
+
+/* SAMR_R_QUERY_ALIASMEM - query alias members */
+typedef struct r_samr_query_aliasmem_info
+{
+       uint32 num_sids;
+       uint32 ptr;
+       uint32 num_sids1;
+
+       DOM_SID2 *sid;
+
+       NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASMEM;
+
+
+/* SAMR_Q_ADD_ALIASMEM - add alias member */
+typedef struct q_samr_add_alias_mem_info
+{
+       POLICY_HND alias_pol;       /* policy handle */
+
+       DOM_SID2 sid; /* member sid to be added to the alias */
+
+} SAMR_Q_ADD_ALIASMEM;
+
+
+/* SAMR_R_ADD_ALIASMEM - add alias member */
+typedef struct r_samr_add_alias_mem_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_ADD_ALIASMEM;
+
+
+/* SAMR_Q_DEL_ALIASMEM - add an add alias member */
+typedef struct q_samr_del_alias_mem_info
+{
+       POLICY_HND alias_pol;       /* policy handle */
+
+       DOM_SID2 sid; /* member sid to be added to alias */
+
+} SAMR_Q_DEL_ALIASMEM;
+
+
+/* SAMR_R_DEL_ALIASMEM - delete alias member */
+typedef struct r_samr_del_alias_mem_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_DEL_ALIASMEM;
+
+
+
+/* SAMR_Q_OPEN_ALIAS - probably an open */
+typedef struct q_samr_open_alias_info
+{
+       POLICY_HND dom_pol;
+
+       uint32 access_mask;         
+       uint32 rid_alias;
+
+} SAMR_Q_OPEN_ALIAS;
+
+
+/* SAMR_R_OPEN_ALIAS - probably an open */
+typedef struct r_samr_open_alias_info
+{
+       POLICY_HND pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_OPEN_ALIAS;
+
+
+/* SAMR_Q_CONNECT_ANON - probably an open */
+typedef struct q_samr_connect_anon_info
+{
+       uint32 ptr;                  /* ptr? */
+       uint16 unknown_0;            /* 0x005c */
+       uint16 unknown_1;            /* 0x0001 */
+       uint32 access_mask;
+
+} SAMR_Q_CONNECT_ANON;
+
+/* SAMR_R_CONNECT_ANON - probably an open */
+typedef struct r_samr_connect_anon_info
+{
+       POLICY_HND connect_pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_CONNECT_ANON;
+
+/* SAMR_Q_CONNECT - probably an open */
+typedef struct q_samr_connect_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* unicode server name starting with '\\' */
+
+       uint32 access_mask;            
+
+} SAMR_Q_CONNECT;
+
+
+/* SAMR_R_CONNECT - probably an open */
+typedef struct r_samr_connect_info
+{
+    POLICY_HND connect_pol;       /* policy handle */
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_CONNECT;
+
+/* SAMR_Q_CONNECT4 */
+typedef struct q_samr_connect4_info
+{
+       uint32 ptr_srv_name; /* pointer to server name */
+       UNISTR2 uni_srv_name;
+
+       uint32 unk_0; /* possible server name type, 1 for IP num, 2 for name */
+       uint32 access_mask;
+} SAMR_Q_CONNECT4;
+
+/* SAMR_R_CONNECT4 - same format as connect */
+typedef struct r_samr_connect_info SAMR_R_CONNECT4;       
+
+/* SAMR_Q_GET_DOM_PWINFO */
+typedef struct q_samr_get_dom_pwinfo
+{
+       uint32 ptr; 
+       UNIHDR  hdr_srv_name;
+       UNISTR2 uni_srv_name;
+
+} SAMR_Q_GET_DOM_PWINFO;
+
+/* SAMR_R_GET_DOM_PWINFO */
+typedef struct r_samr_get_dom_pwinfo
+{
+       /*
+        * Previously this was 3 uint16's.  However, after some tests
+        * it appears that the data len for the signing needs to be 16.
+        * Not sure how 3 unit16's ever worked since the length always
+        * turned out to 12.  3 uint32's + NT_STATUS == 16 bytes.  Tested
+        * using NT and 2k.  --jerry
+        */
+       uint32 unk_0;
+       uint32 unk_1;
+       uint32 unk_2;
+       NTSTATUS status;
+
+} SAMR_R_GET_DOM_PWINFO;
+
+/* SAMR_ENC_PASSWD */
+typedef struct enc_passwd_info
+{
+       uint32 ptr;
+       uint8 pass[516];
+
+} SAMR_ENC_PASSWD;
+
+/* SAMR_ENC_HASH */
+typedef struct enc_hash_info
+{
+       uint32 ptr;
+       uint8 hash[16];
+
+} SAMR_ENC_HASH;
+
+/* SAMR_Q_CHGPASSWD_USER */
+typedef struct q_samr_chgpasswd_user_info
+{
+       uint32 ptr_0;
+
+       UNIHDR hdr_dest_host; /* server name unicode header */
+       UNISTR2 uni_dest_host; /* server name unicode string */
+
+       UNIHDR hdr_user_name;    /* username unicode string header */
+       UNISTR2 uni_user_name;    /* username unicode string */
+
+       SAMR_ENC_PASSWD nt_newpass;
+       SAMR_ENC_HASH nt_oldhash;
+
+       uint32 unknown; /* 0x0000 0001 */
+
+       SAMR_ENC_PASSWD lm_newpass;
+       SAMR_ENC_HASH lm_oldhash;
+
+} SAMR_Q_CHGPASSWD_USER;
+
+/* SAMR_R_CHGPASSWD_USER */
+typedef struct r_samr_chgpasswd_user_info
+{
+       NTSTATUS status; /* 0 == OK, C000006A (NT_STATUS_WRONG_PASSWORD) */
+
+} SAMR_R_CHGPASSWD_USER;
+
+
+/* SAMR_Q_UNKNOWN_2D */
+typedef struct q_samr_unknown_2d_info
+{
+       POLICY_HND dom_pol;   /* policy handle */
+       DOM_SID2 sid;         /* SID */
+
+} SAMR_Q_UNKNOWN_2D;
+
+
+/* SAMR_R_UNKNOWN_2D - probably an open */
+typedef struct r_samr_unknown_2d_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_UNKNOWN_2D;
+
+
+
+/* these are from the old rpc_samr.h - they are needed while the merge
+   is still going on */
+#define MAX_SAM_SIDS 15
+
+/* DOM_SID3 - security id */
+typedef struct sid_info_3
+{
+        uint16 len; /* length, bytes, including length of len :-) */
+        /* uint8  pad[2]; */
+        
+        DOM_SID sid;
+
+} DOM_SID3;
+
+/* SAMR_Q_UNKNOWN_2E */
+typedef struct q_samr_unknown_2e_info
+{
+       POLICY_HND domain_pol;   /* policy handle */
+       uint16 switch_value;
+
+} SAMR_Q_UNKNOWN_2E;
+
+/* SAMR_R_UNKNOWN_2E */
+typedef struct r_samr_unknown_2e_info
+{
+       uint32 ptr_0;
+       uint16 switch_value;
+       SAM_UNK_CTR *ctr;
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_UNKNOWN_2E;
+
+/* SAMR_Q_SET_DOMAIN_INFO */
+typedef struct q_samr_set_domain_info
+{
+       POLICY_HND domain_pol;   /* policy handle */
+       uint16 switch_value0;
+       uint16 switch_value;
+       SAM_UNK_CTR *ctr;
+
+} SAMR_Q_SET_DOMAIN_INFO;
+
+/* SAMR_R_SET_DOMAIN_INFO */
+typedef struct r_samr_set_domain_info
+{
+       NTSTATUS status;         /* return status */
+
+} SAMR_R_SET_DOMAIN_INFO;
+
+#endif /* _RPC_SAMR_H */
diff --git a/source4/include/rpc_secdes.h b/source4/include/rpc_secdes.h
new file mode 100644 (file)
index 0000000..7019190
--- /dev/null
@@ -0,0 +1,462 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-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.
+*/
+
+#ifndef _RPC_SECDES_H /* _RPC_SECDES_H */
+#define _RPC_SECDES_H 
+
+#define SEC_RIGHTS_QUERY_VALUE         0x00000001
+#define SEC_RIGHTS_SET_VALUE           0x00000002
+#define SEC_RIGHTS_CREATE_SUBKEY       0x00000004
+#define SEC_RIGHTS_ENUM_SUBKEYS                0x00000008
+#define SEC_RIGHTS_NOTIFY              0x00000010
+#define SEC_RIGHTS_CREATE_LINK         0x00000020
+#define SEC_RIGHTS_READ                        0x00020019
+#define SEC_RIGHTS_FULL_CONTROL                0x000f003f
+#define SEC_RIGHTS_MAXIMUM_ALLOWED     0x02000000
+
+/* for ADS */
+#define        SEC_RIGHTS_LIST_CONTENTS        0x4
+#define SEC_RIGHTS_LIST_OBJECT         0x80
+#define        SEC_RIGHTS_READ_ALL_PROP        0x10
+#define        SEC_RIGHTS_READ_PERMS           0x20000
+#define SEC_RIGHTS_WRITE_ALL_VALID     0x8
+#define        SEC_RIGHTS_WRITE_ALL_PROP       0x20     
+#define SEC_RIGHTS_MODIFY_OWNER                0x80000
+#define        SEC_RIGHTS_MODIFY_PERMS         0x40000
+#define        SEC_RIGHTS_CREATE_CHILD         0x1
+#define        SEC_RIGHTS_DELETE_CHILD         0x2
+#define SEC_RIGHTS_DELETE_SUBTREE      0x40
+#define SEC_RIGHTS_DELETE               0x10000 /* advanced/special/object/delete */
+#define SEC_RIGHTS_EXTENDED            0x100 /* change/reset password, receive/send as*/
+#define        SEC_RIGHTS_CHANGE_PASSWD        SEC_RIGHTS_EXTENDED
+#define        SEC_RIGHTS_RESET_PASSWD         SEC_RIGHTS_EXTENDED
+#define SEC_RIGHTS_FULL_CTRL           0xf01ff
+
+#define SEC_ACE_OBJECT_PRESENT           0x00000001 /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_FLAG_OBJECT_INHERIT            0x1
+#define SEC_ACE_FLAG_CONTAINER_INHERIT         0x2
+#define SEC_ACE_FLAG_NO_PROPAGATE_INHERIT      0x4
+#define SEC_ACE_FLAG_INHERIT_ONLY              0x8
+#define SEC_ACE_FLAG_INHERITED_ACE             0x10 /* New for Windows 2000 */
+#define SEC_ACE_FLAG_VALID_INHERIT             0xf
+#define SEC_ACE_FLAG_SUCCESSFUL_ACCESS         0x40
+#define SEC_ACE_FLAG_FAILED_ACCESS             0x80
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED            0x0
+#define SEC_ACE_TYPE_ACCESS_DENIED             0x1
+#define SEC_ACE_TYPE_SYSTEM_AUDIT              0x2
+#define SEC_ACE_TYPE_SYSTEM_ALARM              0x3
+#define SEC_ACE_TYPE_ALLOWED_COMPOUND          0x4
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT     0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT      0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT       0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT       0x8
+
+#define SEC_DESC_OWNER_DEFAULTED       0x0001
+#define SEC_DESC_GROUP_DEFAULTED       0x0002
+#define SEC_DESC_DACL_PRESENT          0x0004
+#define SEC_DESC_DACL_DEFAULTED                0x0008
+#define SEC_DESC_SACL_PRESENT          0x0010
+#define SEC_DESC_SACL_DEFAULTED                0x0020
+#define SEC_DESC_SELF_RELATIVE         0x8000
+/*
+ * New Windows 2000 bits.
+ */
+#define SE_DESC_DACL_AUTO_INHERIT_REQ  0x0100
+#define SE_DESC_SACL_AUTO_INHERIT_REQ  0x0200
+#define SE_DESC_DACL_AUTO_INHERITED    0x0400
+#define SE_DESC_SACL_AUTO_INHERITED    0x0800
+#define SE_DESC_DACL_PROTECTED         0x1000
+#define SE_DESC_SACL_PROTECTED         0x2000
+
+/* security information */
+#define OWNER_SECURITY_INFORMATION     0x00000001
+#define GROUP_SECURITY_INFORMATION     0x00000002
+#define DACL_SECURITY_INFORMATION      0x00000004
+#define SACL_SECURITY_INFORMATION      0x00000008
+
+#define ALL_SECURITY_INFORMATION (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|\
+                                                                       DACL_SECURITY_INFORMATION|SACL_SECURITY_INFORMATION)
+
+/* SEC_ACCESS */
+typedef struct security_info_info
+{
+       uint32 mask;
+
+} SEC_ACCESS;
+
+/* SEC_ACE */
+typedef struct security_ace_info
+{
+       uint8 type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+       uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+       uint16 size;
+
+       SEC_ACCESS info;
+
+       /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+       uint32  obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+       GUID    obj_guid;  /* object GUID */
+       GUID    inh_guid;  /* inherited object GUID */          
+        /* eof object stuff */
+
+       DOM_SID trustee;
+
+} SEC_ACE;
+#define  SEC_ACE_HEADER_SIZE (2 * sizeof(uint8) + sizeof(uint16) + sizeof(uint32))
+
+#ifndef ACL_REVISION
+#define ACL_REVISION 0x3
+#endif
+
+#ifndef NT4_ACL_REVISION
+#define NT4_ACL_REVISION 0x2
+#endif
+
+#ifndef _SEC_ACL
+/* SEC_ACL */
+typedef struct security_acl_info
+{
+       uint16 revision; /* 0x0003 */
+       uint16 size; /* size in bytes of the entire ACL structure */
+       uint32 num_aces; /* number of Access Control Entries */
+
+       SEC_ACE *ace;
+
+} SEC_ACL;
+#define  SEC_ACL_HEADER_SIZE (2 * sizeof(uint16) + sizeof(uint32))
+#define _SEC_ACL
+#endif
+
+#ifndef SEC_DESC_REVISION
+#define SEC_DESC_REVISION 0x1
+#endif
+
+#ifndef _SEC_DESC
+/* SEC_DESC */
+typedef struct security_descriptor_info
+{
+       uint16 revision; /* 0x0001 */
+       uint16 type;     /* SEC_DESC_xxxx flags */
+
+       uint32 off_owner_sid; /* offset to owner sid */
+       uint32 off_grp_sid  ; /* offset to group sid */
+       uint32 off_sacl     ; /* offset to system list of permissions */
+       uint32 off_dacl     ; /* offset to list of permissions */
+
+       SEC_ACL *dacl; /* user ACL */
+       SEC_ACL *sacl; /* system ACL */
+       DOM_SID *owner_sid; 
+       DOM_SID *grp_sid;
+
+} SEC_DESC;
+#define  SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+#define _SEC_DESC
+#endif
+
+#ifndef _SEC_DESC_BUF
+/* SEC_DESC_BUF */
+typedef struct sec_desc_buf_info
+{
+       uint32 max_len;
+       uint32 ptr;
+       uint32 len;
+
+       SEC_DESC *sec;
+
+} SEC_DESC_BUF;
+#define _SEC_DESC_BUF
+#endif
+
+/* A type to describe the mapping of generic access rights to object
+   specific access rights. */
+
+typedef struct generic_mapping {
+       uint32 generic_read;
+       uint32 generic_write;
+       uint32 generic_execute;
+       uint32 generic_all;
+} GENERIC_MAPPING;
+
+typedef struct standard_mapping {
+       uint32 std_read;
+       uint32 std_write;
+       uint32 std_execute;
+       uint32 std_all;
+} STANDARD_MAPPING;
+
+
+/* Security Access Masks Rights */
+
+#define SPECIFIC_RIGHTS_MASK   0x0000FFFF
+#define STANDARD_RIGHTS_MASK   0x00FF0000
+#define GENERIC_RIGHTS_MASK    0xF0000000
+
+#define SEC_RIGHT_SYSTEM_SECURITY      0x01000000
+#define SEC_RIGHT_MAXIMUM_ALLOWED      0x02000000
+
+/* Generic access rights */
+
+#define GENERIC_RIGHT_ALL_ACCESS       0x10000000
+#define GENERIC_RIGHT_EXECUTE_ACCESS   0x20000000
+#define GENERIC_RIGHT_WRITE_ACCESS     0x40000000
+#define GENERIC_RIGHT_READ_ACCESS      0x80000000
+
+/* Standard access rights. */
+
+#define STD_RIGHT_DELETE_ACCESS                0x00010000
+#define STD_RIGHT_READ_CONTROL_ACCESS  0x00020000
+#define STD_RIGHT_WRITE_DAC_ACCESS     0x00040000
+#define STD_RIGHT_WRITE_OWNER_ACCESS   0x00080000
+#define STD_RIGHT_SYNCHRONIZE_ACCESS   0x00100000
+
+#define STD_RIGHT_ALL_ACCESS           0x001F0000
+
+/* Combinations of standard masks. */
+#define STANDARD_RIGHTS_ALL_ACCESS     STD_RIGHT_ALL_ACCESS /* 0x001f0000 */
+#define STANDARD_RIGHTS_EXECUTE_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_READ_ACCESS    STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_WRITE_ACCESS   STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_REQUIRED_ACCESS \
+               (STD_RIGHT_DELETE_ACCESS        | \
+               STD_RIGHT_READ_CONTROL_ACCESS   | \
+               STD_RIGHT_WRITE_DAC_ACCESS      | \
+               STD_RIGHT_WRITE_OWNER_ACCESS)   /* 0x000f0000 */
+
+/* File Object specific access rights */
+
+#define SA_RIGHT_FILE_READ_DATA                0x00000001
+#define SA_RIGHT_FILE_WRITE_DATA       0x00000002
+#define SA_RIGHT_FILE_APPEND_DATA      0x00000004
+#define SA_RIGHT_FILE_READ_EA          0x00000008
+#define SA_RIGHT_FILE_WRITE_EA         0x00000010
+#define SA_RIGHT_FILE_EXECUTE          0x00000020
+#define SA_RIGHT_FILE_DELETE_CHILD     0x00000040
+#define SA_RIGHT_FILE_READ_ATTRIBUTES  0x00000080
+#define SA_RIGHT_FILE_WRITE_ATTRIBUTES 0x00000100
+
+#define SA_RIGHT_FILE_ALL_ACCESS       0x000001FF
+
+#define GENERIC_RIGHTS_FILE_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               STD_RIGHT_SYNCHRONIZE_ACCESS    | \
+               SA_RIGHT_FILE_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_FILE_READ       \
+               (STANDARD_RIGHTS_READ_ACCESS    | \
+               STD_RIGHT_SYNCHRONIZE_ACCESS    | \
+               SA_RIGHT_FILE_READ_DATA         | \
+               SA_RIGHT_FILE_READ_ATTRIBUTES   | \
+               SA_RIGHT_FILE_READ_EA)
+
+#define GENERIC_RIGHTS_FILE_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               STD_RIGHT_SYNCHRONIZE_ACCESS    | \
+               SA_RIGHT_FILE_WRITE_DATA        | \
+               SA_RIGHT_FILE_WRITE_ATTRIBUTES  | \
+               SA_RIGHT_FILE_WRITE_EA          | \
+               SA_RIGHT_FILE_APPEND_DATA)
+
+#define GENERIC_RIGHTS_FILE_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_FILE_READ_ATTRIBUTES   | \
+               SA_RIGHT_FILE_EXECUTE)            
+
+
+/* directory specific access rights */
+#define SA_RIGHT_DIR_LIST              0x0001
+#define SA_RIGHT_DIR_ADD_FILE          0x0002
+#define SA_RIGHT_DIR_ADD_SUBDIRECTORY  0x0004
+#define SA_RIGHT_DIR_TRAVERSE          0x0020
+#define SA_RIGHT_DIR_DELETE_CHILD      0x0040
+
+               
+/* SAM Object specific access rights */
+
+#define SA_RIGHT_SAM_UNKNOWN_1         0x00000001
+#define SA_RIGHT_SAM_SHUTDOWN_SERVER   0x00000002
+#define SA_RIGHT_SAM_UNKNOWN_4         0x00000004
+#define SA_RIGHT_SAM_UNKNOWN_8         0x00000008
+#define SA_RIGHT_SAM_ENUM_DOMAINS      0x00000010
+#define SA_RIGHT_SAM_OPEN_DOMAIN       0x00000020
+
+#define SA_RIGHT_SAM_ALL_ACCESS                0x0000003F
+
+#define GENERIC_RIGHTS_SAM_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               SA_RIGHT_SAM_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_SAM_READ        \
+               (STANDARD_RIGHTS_READ_ACCESS    | \
+               SA_RIGHT_SAM_ENUM_DOMAINS)
+
+#define GENERIC_RIGHTS_SAM_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               SA_RIGHT_SAM_UNKNOWN_8          | \
+               SA_RIGHT_SAM_UNKNOWN_4          | \
+               SA_RIGHT_SAM_SHUTDOWN_SERVER)
+
+#define GENERIC_RIGHTS_SAM_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_SAM_OPEN_DOMAIN        | \
+               SA_RIGHT_SAM_UNKNOWN_1)            
+
+
+/* Domain Object specific access rights */
+
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_1          0x00000001
+#define SA_RIGHT_DOMAIN_SET_INFO_1             0x00000002
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_2          0x00000004
+#define SA_RIGHT_DOMAIN_SET_INFO_2             0x00000008
+#define SA_RIGHT_DOMAIN_CREATE_USER            0x00000010
+#define SA_RIGHT_DOMAIN_CREATE_GROUP           0x00000020
+#define SA_RIGHT_DOMAIN_CREATE_ALIAS           0x00000040
+#define SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM    0x00000080
+#define SA_RIGHT_DOMAIN_ENUM_ACCOUNTS          0x00000100
+#define SA_RIGHT_DOMAIN_OPEN_ACCOUNT           0x00000200
+#define SA_RIGHT_DOMAIN_SET_INFO_3             0x00000400
+
+#define SA_RIGHT_DOMAIN_ALL_ACCESS             0x000007FF
+
+#define GENERIC_RIGHTS_DOMAIN_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               SA_RIGHT_DOMAIN_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_DOMAIN_READ \
+               (STANDARD_RIGHTS_READ_ACCESS            | \
+               SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM     | \
+               SA_RIGHT_DOMAIN_LOOKUP_INFO_2)
+
+#define GENERIC_RIGHTS_DOMAIN_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               SA_RIGHT_DOMAIN_SET_INFO_3      | \
+               SA_RIGHT_DOMAIN_CREATE_ALIAS    | \
+               SA_RIGHT_DOMAIN_CREATE_GROUP    | \
+               SA_RIGHT_DOMAIN_CREATE_USER     | \
+               SA_RIGHT_DOMAIN_SET_INFO_2      | \
+               SA_RIGHT_DOMAIN_SET_INFO_1)
+
+#define GENERIC_RIGHTS_DOMAIN_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_DOMAIN_OPEN_ACCOUNT    | \
+               SA_RIGHT_DOMAIN_ENUM_ACCOUNTS   | \
+               SA_RIGHT_DOMAIN_LOOKUP_INFO_1)            
+
+
+/* User Object specific access rights */
+
+#define SA_RIGHT_USER_GET_NAME_ETC     0x00000001
+#define SA_RIGHT_USER_GET_LOCALE       0x00000002
+#define SA_RIGHT_USER_SET_LOC_COM      0x00000004
+#define SA_RIGHT_USER_GET_LOGONINFO    0x00000008
+#define SA_RIGHT_USER_ACCT_FLAGS_EXPIRY        0x00000010
+#define SA_RIGHT_USER_SET_ATTRIBUTES   0x00000020
+#define SA_RIGHT_USER_CHANGE_PASSWORD  0x00000040
+#define SA_RIGHT_USER_SET_PASSWORD     0x00000080
+#define SA_RIGHT_USER_GET_GROUPS       0x00000100
+#define SA_RIGHT_USER_UNKNOWN_200      0x00000200
+#define SA_RIGHT_USER_UNKNOWN_400      0x00000400
+
+#define SA_RIGHT_USER_ALL_ACCESS       0x000007FF
+
+#define GENERIC_RIGHTS_USER_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               SA_RIGHT_USER_ALL_ACCESS)       /* 0x000f07ff */
+
+#define GENERIC_RIGHTS_USER_READ \
+               (STANDARD_RIGHTS_READ_ACCESS    | \
+               SA_RIGHT_USER_UNKNOWN_200       | \
+               SA_RIGHT_USER_GET_GROUPS        | \
+               SA_RIGHT_USER_ACCT_FLAGS_EXPIRY | \
+               SA_RIGHT_USER_GET_LOGONINFO     | \
+               SA_RIGHT_USER_GET_LOCALE)       /* 0x0002031a */
+
+#define GENERIC_RIGHTS_USER_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               SA_RIGHT_USER_CHANGE_PASSWORD   | \
+               SA_RIGHT_USER_SET_LOC_COM)      /* 0x00020044 */
+
+#define GENERIC_RIGHTS_USER_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_USER_CHANGE_PASSWORD   | \
+               SA_RIGHT_USER_GET_NAME_ETC )    /* 0x00020041 */
+
+
+/* Group Object specific access rights */
+
+#define SA_RIGHT_GROUP_LOOKUP_INFO     0x00000001
+#define SA_RIGHT_GROUP_SET_INFO                0x00000002
+#define SA_RIGHT_GROUP_ADD_MEMBER      0x00000004
+#define SA_RIGHT_GROUP_REMOVE_MEMBER   0x00000008
+#define SA_RIGHT_GROUP_GET_MEMBERS     0x00000010
+
+#define SA_RIGHT_GROUP_ALL_ACCESS      0x0000001F
+
+#define GENERIC_RIGHTS_GROUP_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               SA_RIGHT_GROUP_ALL_ACCESS)      /* 0x000f001f */
+
+#define GENERIC_RIGHTS_GROUP_READ \
+               (STANDARD_RIGHTS_READ_ACCESS    | \
+               SA_RIGHT_GROUP_GET_MEMBERS)     /* 0x00020010 */
+
+#define GENERIC_RIGHTS_GROUP_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               SA_RIGHT_GROUP_REMOVE_MEMBER    | \
+               SA_RIGHT_GROUP_ADD_MEMBER       | \
+               SA_RIGHT_GROUP_SET_INFO )       /* 0x0002000e */
+
+#define GENERIC_RIGHTS_GROUP_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_GROUP_LOOKUP_INFO)     /* 0x00020001 */
+
+
+/* Alias Object specific access rights */
+
+#define SA_RIGHT_ALIAS_ADD_MEMBER      0x00000001
+#define SA_RIGHT_ALIAS_REMOVE_MEMBER   0x00000002
+#define SA_RIGHT_ALIAS_GET_MEMBERS     0x00000004
+#define SA_RIGHT_ALIAS_LOOKUP_INFO     0x00000008
+#define SA_RIGHT_ALIAS_SET_INFO                0x00000010
+
+#define SA_RIGHT_ALIAS_ALL_ACCESS      0x0000001F
+
+#define GENERIC_RIGHTS_ALIAS_ALL_ACCESS \
+               (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+               SA_RIGHT_ALIAS_ALL_ACCESS)      /* 0x000f001f */
+
+#define GENERIC_RIGHTS_ALIAS_READ \
+               (STANDARD_RIGHTS_READ_ACCESS    | \
+               SA_RIGHT_ALIAS_GET_MEMBERS )    /* 0x00020004 */
+
+#define GENERIC_RIGHTS_ALIAS_WRITE \
+               (STANDARD_RIGHTS_WRITE_ACCESS   | \
+               SA_RIGHT_ALIAS_REMOVE_MEMBER    | \
+               SA_RIGHT_ALIAS_ADD_MEMBER       | \
+               SA_RIGHT_ALIAS_SET_INFO )       /* 0x00020013 */
+
+#define GENERIC_RIGHTS_ALIAS_EXECUTE \
+               (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+               SA_RIGHT_ALIAS_LOOKUP_INFO )    /* 0x00020008 */
+
+#endif /* _RPC_SECDES_H */
diff --git a/source4/include/rpc_spoolss.h b/source4/include/rpc_spoolss.h
new file mode 100755 (executable)
index 0000000..c2e3d92
--- /dev/null
@@ -0,0 +1,2228 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell              1992-2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+   Copyright (C) Jean Francois Micouleau      1998-2000.
+   Copyright (C) Gerald Carter                2001-2002.
+   
+   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.
+*/
+
+#ifndef _RPC_SPOOLSS_H         /* _RPC_SPOOLSS_H */
+#define _RPC_SPOOLSS_H
+
+/* spoolss pipe: this are the calls which are not implemented ...
+#define SPOOLSS_GETPRINTERDRIVER                       0x0b
+#define SPOOLSS_READPRINTER                            0x16
+#define SPOOLSS_WAITFORPRINTERCHANGE                   0x1c
+#define SPOOLSS_ADDPORT                                        0x25
+#define SPOOLSS_CONFIGUREPORT                          0x26
+#define SPOOLSS_DELETEPORT                             0x27
+#define SPOOLSS_CREATEPRINTERIC                                0x28
+#define SPOOLSS_PLAYGDISCRIPTONPRINTERIC               0x29
+#define SPOOLSS_DELETEPRINTERIC                                0x2a
+#define SPOOLSS_ADDPRINTERCONNECTION                   0x2b
+#define SPOOLSS_DELETEPRINTERCONNECTION                        0x2c
+#define SPOOLSS_PRINTERMESSAGEBOX                      0x2d
+#define SPOOLSS_ADDMONITOR                             0x2e
+#define SPOOLSS_DELETEMONITOR                          0x2f
+#define SPOOLSS_DELETEPRINTPROCESSOR                   0x30
+#define SPOOLSS_ADDPRINTPROVIDOR                       0x31
+#define SPOOLSS_DELETEPRINTPROVIDOR                    0x32
+#define SPOOLSS_FINDFIRSTPRINTERCHANGENOTIFICATION     0x36
+#define SPOOLSS_FINDNEXTPRINTERCHANGENOTIFICATION      0x37
+#define SPOOLSS_ROUTERFINDFIRSTPRINTERNOTIFICATIONOLD  0x39
+#define SPOOLSS_ADDPORTEX                              0x3d
+#define SPOOLSS_REMOTEFINDFIRSTPRINTERCHANGENOTIFICATION0x3e
+#define SPOOLSS_SPOOLERINIT                            0x3f
+#define SPOOLSS_RESETPRINTEREX                         0x40
+*/
+
+/* those are implemented */
+#define SPOOLSS_ENUMPRINTERS                           0x00
+#define SPOOLSS_OPENPRINTER                            0x01
+#define SPOOLSS_SETJOB                                 0x02
+#define SPOOLSS_GETJOB                                 0x03
+#define SPOOLSS_ENUMJOBS                               0x04
+#define SPOOLSS_ADDPRINTER                             0x05
+#define SPOOLSS_DELETEPRINTER                          0x06
+#define SPOOLSS_SETPRINTER                             0x07
+#define SPOOLSS_GETPRINTER                             0x08
+#define SPOOLSS_ADDPRINTERDRIVER                       0x09
+#define SPOOLSS_ENUMPRINTERDRIVERS                     0x0a
+#define SPOOLSS_GETPRINTERDRIVERDIRECTORY              0x0c
+#define SPOOLSS_DELETEPRINTERDRIVER                    0x0d
+#define SPOOLSS_ADDPRINTPROCESSOR                      0x0e
+#define SPOOLSS_ENUMPRINTPROCESSORS                    0x0f
+#define SPOOLSS_GETPRINTPROCESSORDIRECTORY             0x10
+#define SPOOLSS_STARTDOCPRINTER                                0x11
+#define SPOOLSS_STARTPAGEPRINTER                       0x12
+#define SPOOLSS_WRITEPRINTER                           0x13
+#define SPOOLSS_ENDPAGEPRINTER                         0x14
+#define SPOOLSS_ABORTPRINTER                           0x15
+#define SPOOLSS_ENDDOCPRINTER                          0x17
+#define SPOOLSS_ADDJOB                                 0x18
+#define SPOOLSS_SCHEDULEJOB                            0x19
+#define SPOOLSS_GETPRINTERDATA                         0x1a
+#define SPOOLSS_SETPRINTERDATA                         0x1b
+#define SPOOLSS_CLOSEPRINTER                           0x1d
+#define SPOOLSS_ADDFORM                                        0x1e
+#define SPOOLSS_DELETEFORM                             0x1f
+#define SPOOLSS_GETFORM                                        0x20
+#define SPOOLSS_SETFORM                                        0x21
+#define SPOOLSS_ENUMFORMS                              0x22
+#define SPOOLSS_ENUMPORTS                              0x23
+#define SPOOLSS_ENUMMONITORS                           0x24
+#define SPOOLSS_ENUMPRINTPROCDATATYPES                 0x33
+#define SPOOLSS_RESETPRINTER                           0x34
+#define SPOOLSS_GETPRINTERDRIVER2                      0x35
+#define SPOOLSS_FCPN                                   0x38    /* FindClosePrinterNotify */
+#define SPOOLSS_REPLYOPENPRINTER                       0x3a
+#define SPOOLSS_ROUTERREPLYPRINTER                     0x3b
+#define SPOOLSS_REPLYCLOSEPRINTER                      0x3c
+#define SPOOLSS_RFFPCNEX                               0x41    /* RemoteFindFirstPrinterChangeNotifyEx */
+#define SPOOLSS_RRPCN                                  0x42    /* RouteRefreshPrinterChangeNotification */
+#define SPOOLSS_RFNPCNEX                               0x43    /* RemoteFindNextPrinterChangeNotifyEx */
+#define SPOOLSS_OPENPRINTEREX                          0x45
+#define SPOOLSS_ADDPRINTEREX                           0x46
+#define SPOOLSS_ENUMPRINTERDATA                                0x48
+#define SPOOLSS_DELETEPRINTERDATA                      0x49
+#define SPOOLSS_SETPRINTERDATAEX                       0x4d
+#define SPOOLSS_GETPRINTERDATAEX                       0x4e
+#define SPOOLSS_ENUMPRINTERDATAEX                      0x4f
+#define SPOOLSS_ENUMPRINTERKEY                         0x50
+#define SPOOLSS_DELETEPRINTERDATAEX                    0x51
+#define SPOOLSS_DELETEPRINTERKEY                       0x52
+#define SPOOLSS_DELETEPRINTERDRIVEREX                  0x54
+#define SPOOLSS_ADDPRINTERDRIVEREX                     0x59
+
+
+#define PRINTER_CONTROL_UNPAUSE                0x00000000
+#define PRINTER_CONTROL_PAUSE          0x00000001
+#define PRINTER_CONTROL_RESUME         0x00000002
+#define PRINTER_CONTROL_PURGE          0x00000003
+#define PRINTER_CONTROL_SET_STATUS     0x00000004
+
+#define PRINTER_STATUS_OK               0x00000000
+#define PRINTER_STATUS_PAUSED          0x00000001
+#define PRINTER_STATUS_ERROR           0x00000002
+#define PRINTER_STATUS_PENDING_DELETION        0x00000004
+#define PRINTER_STATUS_PAPER_JAM       0x00000008
+
+#define PRINTER_STATUS_PAPER_OUT       0x00000010
+#define PRINTER_STATUS_MANUAL_FEED     0x00000020
+#define PRINTER_STATUS_PAPER_PROBLEM   0x00000040
+#define PRINTER_STATUS_OFFLINE         0x00000080
+
+#define PRINTER_STATUS_IO_ACTIVE       0x00000100
+#define PRINTER_STATUS_BUSY            0x00000200
+#define PRINTER_STATUS_PRINTING                0x00000400
+#define PRINTER_STATUS_OUTPUT_BIN_FULL 0x00000800
+
+#define PRINTER_STATUS_NOT_AVAILABLE   0x00001000
+#define PRINTER_STATUS_WAITING         0x00002000
+#define PRINTER_STATUS_PROCESSING      0x00004000
+#define PRINTER_STATUS_INITIALIZING    0x00008000
+
+#define PRINTER_STATUS_WARMING_UP      0x00010000
+#define PRINTER_STATUS_TONER_LOW       0x00020000
+#define PRINTER_STATUS_NO_TONER                0x00040000
+#define PRINTER_STATUS_PAGE_PUNT       0x00080000
+
+#define PRINTER_STATUS_USER_INTERVENTION       0x00100000
+#define PRINTER_STATUS_OUT_OF_MEMORY   0x00200000
+#define PRINTER_STATUS_DOOR_OPEN       0x00400000
+#define PRINTER_STATUS_SERVER_UNKNOWN  0x00800000
+
+#define PRINTER_STATUS_POWER_SAVE      0x01000000
+
+#define SERVER_ACCESS_ADMINISTER       0x00000001
+#define SERVER_ACCESS_ENUMERATE                0x00000002
+#define PRINTER_ACCESS_ADMINISTER      0x00000004
+#define PRINTER_ACCESS_USE             0x00000008
+#define JOB_ACCESS_ADMINISTER          0x00000010
+
+/* JOB status codes. */
+
+#define JOB_STATUS_QUEUED               0x0000
+#define JOB_STATUS_PAUSED              0x0001
+#define JOB_STATUS_ERROR               0x0002
+#define JOB_STATUS_DELETING            0x0004
+#define JOB_STATUS_SPOOLING            0x0008
+#define JOB_STATUS_PRINTING            0x0010
+#define JOB_STATUS_OFFLINE             0x0020
+#define JOB_STATUS_PAPEROUT            0x0040
+#define JOB_STATUS_PRINTED             0x0080
+#define JOB_STATUS_DELETED             0x0100
+#define JOB_STATUS_BLOCKED             0x0200
+#define JOB_STATUS_USER_INTERVENTION   0x0400
+
+/* ACE masks for the various print permissions */
+
+#define PRINTER_ACE_FULL_CONTROL      GENERIC_ALL_ACCESS
+#define PRINTER_ACE_MANAGE_DOCUMENTS  READ_CONTROL_ACCESS
+#define PRINTER_ACE_PRINT             \
+    (GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS)
+
+/* Access rights for print servers */
+#define SERVER_ALL_ACCESS      STANDARD_RIGHTS_REQUIRED_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_READ            STANDARD_RIGHTS_READ_ACCESS|SERVER_ACCESS_ENUMERATE
+#define SERVER_WRITE           STANDARD_RIGHTS_WRITE_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_EXECUTE         STANDARD_RIGHTS_EXECUTE_ACCESS|SERVER_ACCESS_ENUMERATE
+
+/* Access rights for printers */
+#define PRINTER_ALL_ACCESS     STANDARD_RIGHTS_REQUIRED_ACCESS|PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE
+#define PRINTER_READ          STANDARD_RIGHTS_READ_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_WRITE         STANDARD_RIGHTS_WRITE_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_EXECUTE       STANDARD_RIGHTS_EXECUTE_ACCESS|PRINTER_ACCESS_USE
+
+/* Access rights for jobs */
+#define JOB_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_READ       STANDARD_RIGHTS_READ_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_WRITE      STANDARD_RIGHTS_WRITE_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_EXECUTE    STANDARD_RIGHTS_EXECUTE_ACCESS|JOB_ACCESS_ADMINISTER
+
+/* Notify field types */
+
+#define NOTIFY_ONE_VALUE 1             /* Notify data is stored in value1 */
+#define NOTIFY_TWO_VALUE 2             /* Notify data is stored in value2 */
+#define NOTIFY_POINTER   3             /* Data is a pointer to a buffer */
+#define NOTIFY_STRING    4             /* Data is a pointer to a buffer w/length */
+#define NOTIFY_SECDESC   5             /* Data is a security descriptor */
+
+#define PRINTER_NOTIFY_TYPE 0x00
+#define JOB_NOTIFY_TYPE     0x01
+
+#define MAX_PRINTER_NOTIFY 26
+#define MAX_JOB_NOTIFY 24
+
+#define MAX_NOTIFY_TYPE_FOR_NOW 26
+
+#define PRINTER_NOTIFY_SERVER_NAME             0x00
+#define PRINTER_NOTIFY_PRINTER_NAME            0x01
+#define PRINTER_NOTIFY_SHARE_NAME              0x02
+#define PRINTER_NOTIFY_PORT_NAME               0x03
+#define PRINTER_NOTIFY_DRIVER_NAME             0x04
+#define PRINTER_NOTIFY_COMMENT                 0x05
+#define PRINTER_NOTIFY_LOCATION                        0x06
+#define PRINTER_NOTIFY_DEVMODE                 0x07
+#define PRINTER_NOTIFY_SEPFILE                 0x08
+#define PRINTER_NOTIFY_PRINT_PROCESSOR         0x09
+#define PRINTER_NOTIFY_PARAMETERS              0x0A
+#define PRINTER_NOTIFY_DATATYPE                        0x0B
+#define PRINTER_NOTIFY_SECURITY_DESCRIPTOR     0x0C
+#define PRINTER_NOTIFY_ATTRIBUTES              0x0D
+#define PRINTER_NOTIFY_PRIORITY                        0x0E
+#define PRINTER_NOTIFY_DEFAULT_PRIORITY                0x0F
+#define PRINTER_NOTIFY_START_TIME              0x10
+#define PRINTER_NOTIFY_UNTIL_TIME              0x11
+#define PRINTER_NOTIFY_STATUS                  0x12
+#define PRINTER_NOTIFY_STATUS_STRING           0x13
+#define PRINTER_NOTIFY_CJOBS                   0x14
+#define PRINTER_NOTIFY_AVERAGE_PPM             0x15
+#define PRINTER_NOTIFY_TOTAL_PAGES             0x16
+#define PRINTER_NOTIFY_PAGES_PRINTED           0x17
+#define PRINTER_NOTIFY_TOTAL_BYTES             0x18
+#define PRINTER_NOTIFY_BYTES_PRINTED           0x19
+
+#define JOB_NOTIFY_PRINTER_NAME                        0x00
+#define JOB_NOTIFY_MACHINE_NAME                        0x01
+#define JOB_NOTIFY_PORT_NAME                   0x02
+#define JOB_NOTIFY_USER_NAME                   0x03
+#define JOB_NOTIFY_NOTIFY_NAME                 0x04
+#define JOB_NOTIFY_DATATYPE                    0x05
+#define JOB_NOTIFY_PRINT_PROCESSOR             0x06
+#define JOB_NOTIFY_PARAMETERS                  0x07
+#define JOB_NOTIFY_DRIVER_NAME                 0x08
+#define JOB_NOTIFY_DEVMODE                     0x09
+#define JOB_NOTIFY_STATUS                      0x0A
+#define JOB_NOTIFY_STATUS_STRING               0x0B
+#define JOB_NOTIFY_SECURITY_DESCRIPTOR         0x0C
+#define JOB_NOTIFY_DOCUMENT                    0x0D
+#define JOB_NOTIFY_PRIORITY                    0x0E
+#define JOB_NOTIFY_POSITION                    0x0F
+#define JOB_NOTIFY_SUBMITTED                   0x10
+#define JOB_NOTIFY_START_TIME                  0x11
+#define JOB_NOTIFY_UNTIL_TIME                  0x12
+#define JOB_NOTIFY_TIME                                0x13
+#define JOB_NOTIFY_TOTAL_PAGES                 0x14
+#define JOB_NOTIFY_PAGES_PRINTED               0x15
+#define JOB_NOTIFY_TOTAL_BYTES                 0x16
+#define JOB_NOTIFY_BYTES_PRINTED               0x17
+
+#define PRINTER_NOTIFY_OPTIONS_REFRESH         0x01
+
+#define PRINTER_CHANGE_ADD_PRINTER                     0x00000001
+#define PRINTER_CHANGE_SET_PRINTER                     0x00000002
+#define PRINTER_CHANGE_DELETE_PRINTER                  0x00000004
+#define PRINTER_CHANGE_FAILED_CONNECTION_PRINTER       0x00000008
+#define PRINTER_CHANGE_PRINTER (PRINTER_CHANGE_ADD_PRINTER | \
+                                PRINTER_CHANGE_SET_PRINTER | \
+                                PRINTER_CHANGE_DELETE_PRINTER | \
+                                PRINTER_CHANGE_FAILED_CONNECTION_PRINTER )
+
+#define PRINTER_CHANGE_ADD_JOB                         0x00000100
+#define PRINTER_CHANGE_SET_JOB                         0x00000200
+#define PRINTER_CHANGE_DELETE_JOB                      0x00000400
+#define PRINTER_CHANGE_WRITE_JOB                       0x00000800
+#define PRINTER_CHANGE_JOB     (PRINTER_CHANGE_ADD_JOB | \
+                                PRINTER_CHANGE_SET_JOB | \
+                                PRINTER_CHANGE_DELETE_JOB | \
+                                PRINTER_CHANGE_WRITE_JOB )
+
+#define PRINTER_CHANGE_ADD_FORM                                0x00010000
+#define PRINTER_CHANGE_SET_FORM                                0x00020000
+#define PRINTER_CHANGE_DELETE_FORM                     0x00040000
+#define PRINTER_CHANGE_FORM    (PRINTER_CHANGE_ADD_FORM | \
+                                PRINTER_CHANGE_SET_FORM | \
+                                PRINTER_CHANGE_DELETE_FORM )
+
+#define PRINTER_CHANGE_ADD_PORT                                0x00100000
+#define PRINTER_CHANGE_CONFIGURE_PORT                  0x00200000
+#define PRINTER_CHANGE_DELETE_PORT                     0x00400000
+#define PRINTER_CHANGE_PORT    (PRINTER_CHANGE_ADD_PORT | \
+                                PRINTER_CHANGE_CONFIGURE_PORT | \
+                                PRINTER_CHANGE_DELETE_PORT )
+
+#define PRINTER_CHANGE_ADD_PRINT_PROCESSOR             0x01000000
+#define PRINTER_CHANGE_DELETE_PRINT_PROCESSOR          0x04000000
+#define PRINTER_CHANGE_PRINT_PROCESSOR (PRINTER_CHANGE_ADD_PRINT_PROCESSOR | \
+                                        PRINTER_CHANGE_DELETE_PRINT_PROCESSOR )
+
+#define PRINTER_CHANGE_ADD_PRINTER_DRIVER              0x10000000
+#define PRINTER_CHANGE_SET_PRINTER_DRIVER              0x20000000
+#define PRINTER_CHANGE_DELETE_PRINTER_DRIVER           0x40000000
+#define PRINTER_CHANGE_PRINTER_DRIVER  (PRINTER_CHANGE_ADD_PRINTER_DRIVER | \
+                                        PRINTER_CHANGE_SET_PRINTER_DRIVER | \
+                                        PRINTER_CHANGE_DELETE_PRINTER_DRIVER )
+
+#define PRINTER_CHANGE_TIMEOUT                         0x80000000
+#define PRINTER_CHANGE_ALL     (PRINTER_CHANGE_JOB | \
+                                PRINTER_CHANGE_FORM | \
+                                PRINTER_CHANGE_PORT | \
+                                PRINTER_CHANGE_PRINT_PROCESSOR | \
+                                PRINTER_CHANGE_PRINTER_DRIVER )
+
+#define PRINTER_NOTIFY_INFO_DISCARDED  0x1
+
+/*
+ * Set of macros for flagging what changed in the PRINTER_INFO_2 struct
+ * when sending messages to other smbd's
+ */
+#define PRINTER_MESSAGE_NULL            0x00000000
+#define PRINTER_MESSAGE_DRIVER         0x00000001
+#define PRINTER_MESSAGE_COMMENT                0x00000002
+#define PRINTER_MESSAGE_PRINTERNAME    0x00000004
+#define PRINTER_MESSAGE_LOCATION       0x00000008
+#define PRINTER_MESSAGE_DEVMODE                0x00000010      /* not curently supported */
+#define PRINTER_MESSAGE_SEPFILE                0x00000020
+#define PRINTER_MESSAGE_PRINTPROC      0x00000040
+#define PRINTER_MESSAGE_PARAMS         0x00000080
+#define PRINTER_MESSAGE_DATATYPE       0x00000100
+#define PRINTER_MESSAGE_SECDESC                0x00000200
+#define PRINTER_MESSAGE_CJOBS          0x00000400
+#define PRINTER_MESSAGE_PORT           0x00000800
+#define PRINTER_MESSAGE_SHARENAME      0x00001000
+#define PRINTER_MESSAGE_ATTRIBUTES     0x00002000
+
+typedef struct printer_message_info {
+       uint32 low;             /* PRINTER_CHANGE_XXX */
+       uint32 high;            /* PRINTER_CHANGE_XXX */
+       fstring printer_name;
+       uint32 flags;           /* PRINTER_MESSAGE_XXX */
+}
+PRINTER_MESSAGE_INFO;
+
+/*
+ * The printer attributes.
+ * I #defined all of them (grabbed form MSDN)
+ * I'm only using:
+ * ( SHARED | NETWORK | RAW_ONLY )
+ * RAW_ONLY _MUST_ be present otherwise NT will send an EMF file
+ */
+
+#define PRINTER_ATTRIBUTE_QUEUED               0x00000001
+#define PRINTER_ATTRIBUTE_DIRECT               0x00000002
+#define PRINTER_ATTRIBUTE_DEFAULT              0x00000004
+#define PRINTER_ATTRIBUTE_SHARED               0x00000008
+
+#define PRINTER_ATTRIBUTE_NETWORK              0x00000010
+#define PRINTER_ATTRIBUTE_HIDDEN               0x00000020
+#define PRINTER_ATTRIBUTE_LOCAL                        0x00000040
+#define PRINTER_ATTRIBUTE_ENABLE_DEVQ          0x00000080
+
+#define PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS      0x00000100
+#define PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST    0x00000200
+#define PRINTER_ATTRIBUTE_WORK_OFFLINE         0x00000400
+#define PRINTER_ATTRIBUTE_ENABLE_BIDI          0x00000800
+
+#define PRINTER_ATTRIBUTE_RAW_ONLY             0x00001000
+#define PRINTER_ATTRIBUTE_PUBLISHED            0x00002000
+
+#define PRINTER_ATTRIBUTE_SAMBA                        (PRINTER_ATTRIBUTE_RAW_ONLY|\
+                                                PRINTER_ATTRIBUTE_SHARED|\
+                                                PRINTER_ATTRIBUTE_NETWORK)
+
+#define NO_PRIORITY     0
+#define MAX_PRIORITY   99
+#define MIN_PRIORITY    1
+#define DEF_PRIORITY    1
+
+/* the flags of the query */
+#define PRINTER_ENUM_DEFAULT           0x00000001
+#define PRINTER_ENUM_LOCAL             0x00000002
+#define PRINTER_ENUM_CONNECTIONS       0x00000004
+#define PRINTER_ENUM_FAVORITE          0x00000004
+#define PRINTER_ENUM_NAME              0x00000008
+#define PRINTER_ENUM_REMOTE            0x00000010
+#define PRINTER_ENUM_SHARED            0x00000020
+#define PRINTER_ENUM_NETWORK           0x00000040
+
+/* the flags of each printers */
+#define PRINTER_ENUM_UNKNOWN_8         0x00000008
+#define PRINTER_ENUM_EXPAND            0x00004000
+#define PRINTER_ENUM_CONTAINER         0x00008000
+#define PRINTER_ENUM_ICONMASK          0x00ff0000
+#define PRINTER_ENUM_ICON1             0x00010000
+#define PRINTER_ENUM_ICON2             0x00020000
+#define PRINTER_ENUM_ICON3             0x00040000
+#define PRINTER_ENUM_ICON4             0x00080000
+#define PRINTER_ENUM_ICON5             0x00100000
+#define PRINTER_ENUM_ICON6             0x00200000
+#define PRINTER_ENUM_ICON7             0x00400000
+#define PRINTER_ENUM_ICON8             0x00800000
+
+/* FLAGS for SPOOLSS_DELETEPRINTERDRIVEREX */
+
+#define DPD_DELETE_UNUSED_FILES                0x00000001
+#define DPD_DELETE_SPECIFIC_VERSION    0x00000002
+#define DPD_DELETE_ALL_FILES           0x00000004
+
+#define DRIVER_ANY_VERSION             0xffffffff
+#define DRIVER_MAX_VERSION             4
+
+/* FLAGS for SPOOLSS_ADDPRINTERDRIVEREX */
+
+#define APD_STRICT_UPGRADE             0x00000001
+#define APD_STRICT_DOWNGRADE           0x00000002
+#define APD_COPY_ALL_FILES             0x00000004
+#define APD_COPY_NEW_FILES             0x00000008
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_1
+{
+       uint32 size;            /* length of user_name & client_name + 2? */
+       uint32 client_name_ptr;
+       uint32 user_name_ptr;
+       uint32 build;
+       uint32 major;
+       uint32 minor;
+       uint32 processor;
+       UNISTR2 client_name;
+       UNISTR2 user_name;
+}
+SPOOL_USER_1;
+
+typedef struct spool_user_ctr_info
+{
+       uint32 level;
+       uint32 ptr;
+       SPOOL_USER_1 user1;
+}
+SPOOL_USER_CTR;
+
+/*
+ * various bits in the DEVICEMODE.fields member
+ */
+
+#define DEVMODE_ORIENTATION            0x00000001
+#define DEVMODE_PAPERSIZE              0x00000002
+#define DEVMODE_PAPERLENGTH            0x00000004
+#define DEVMODE_PAPERWIDTH             0x00000008
+#define DEVMODE_SCALE                  0x00000010
+#define DEVMODE_POSITION               0x00000020
+#define DEVMODE_NUP                    0x00000040
+#define DEVMODE_COPIES                 0x00000100
+#define DEVMODE_DEFAULTSOURCE          0x00000200
+#define DEVMODE_PRINTQUALITY           0x00000400
+#define DEVMODE_COLOR                  0x00000800
+#define DEVMODE_DUPLEX                 0x00001000
+#define DEVMODE_YRESOLUTION            0x00002000
+#define DEVMODE_TTOPTION               0x00004000
+#define DEVMODE_COLLATE                        0x00008000
+#define DEVMODE_FORMNAME               0x00010000
+#define DEVMODE_LOGPIXELS              0x00020000
+#define DEVMODE_BITSPERPEL             0x00040000
+#define DEVMODE_PELSWIDTH              0x00080000
+#define DEVMODE_PELSHEIGHT             0x00100000
+#define DEVMODE_DISPLAYFLAGS           0x00200000
+#define DEVMODE_DISPLAYFREQUENCY       0x00400000
+#define DEVMODE_ICMMETHOD              0x00800000
+#define DEVMODE_ICMINTENT              0x01000000
+#define DEVMODE_MEDIATYPE              0x02000000
+#define DEVMODE_DITHERTYPE             0x04000000
+#define DEVMODE_PANNINGWIDTH           0x08000000
+#define DEVMODE_PANNINGHEIGHT          0x10000000
+
+
+/* 
+ * Devicemode structure
+ */
+
+typedef struct devicemode
+{
+       UNISTR devicename;
+       uint16 specversion;
+       uint16 driverversion;
+       uint16 size;
+       uint16 driverextra;
+       uint32 fields;
+       uint16 orientation;
+       uint16 papersize;
+       uint16 paperlength;
+       uint16 paperwidth;
+       uint16 scale;
+       uint16 copies;
+       uint16 defaultsource;
+       uint16 printquality;
+       uint16 color;
+       uint16 duplex;
+       uint16 yresolution;
+       uint16 ttoption;
+       uint16 collate;
+       UNISTR formname;
+       uint16 logpixels;
+       uint32 bitsperpel;
+       uint32 pelswidth;
+       uint32 pelsheight;
+       uint32 displayflags;
+       uint32 displayfrequency;
+       uint32 icmmethod;
+       uint32 icmintent;
+       uint32 mediatype;
+       uint32 dithertype;
+       uint32 reserved1;
+       uint32 reserved2;
+       uint32 panningwidth;
+       uint32 panningheight;
+       uint8 *private;
+}
+DEVICEMODE;
+
+typedef struct _devmode_cont
+{
+       uint32 size;
+       uint32 devmode_ptr;
+       DEVICEMODE *devmode;
+}
+DEVMODE_CTR;
+
+typedef struct _printer_default
+{
+       uint32 datatype_ptr;
+       UNISTR2 datatype;
+       DEVMODE_CTR devmode_cont;
+       uint32 access_required;
+}
+PRINTER_DEFAULT;
+
+/* SPOOL_Q_OPEN_PRINTER request to open a printer */
+typedef struct spool_q_open_printer
+{
+       uint32 printername_ptr;
+       UNISTR2 printername;
+       PRINTER_DEFAULT printer_default;
+}
+SPOOL_Q_OPEN_PRINTER;
+
+/* SPOOL_R_OPEN_PRINTER reply to an open printer */
+typedef struct spool_r_open_printer
+{
+       POLICY_HND handle;      /* handle used along all transactions (20*uint8) */
+       WERROR status;
+}
+SPOOL_R_OPEN_PRINTER;
+
+/* SPOOL_Q_OPEN_PRINTER_EX request to open a printer */
+typedef struct spool_q_open_printer_ex
+{
+       uint32 printername_ptr;
+       UNISTR2 printername;
+       PRINTER_DEFAULT printer_default;
+       uint32 user_switch;
+       SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_OPEN_PRINTER_EX;
+
+/* SPOOL_R_OPEN_PRINTER_EX reply to an open printer */
+typedef struct spool_r_open_printer_ex
+{
+       POLICY_HND handle;      /* handle used along all transactions (20*uint8) */
+       WERROR status;
+}
+SPOOL_R_OPEN_PRINTER_EX;
+
+typedef struct spool_notify_option_type
+{
+       uint16 type;
+       uint16 reserved0;
+       uint32 reserved1;
+       uint32 reserved2;
+       uint32 count;
+       uint32 fields_ptr;
+       uint32 count2;
+       uint16 fields[MAX_NOTIFY_TYPE_FOR_NOW];
+}
+SPOOL_NOTIFY_OPTION_TYPE;
+
+typedef struct spool_notify_option_type_ctr
+{
+       uint32 count;
+       SPOOL_NOTIFY_OPTION_TYPE *type;
+}
+SPOOL_NOTIFY_OPTION_TYPE_CTR;
+
+
+
+typedef struct s_header_type
+{
+       uint32 type;
+       union
+       {
+               uint32 value;
+               UNISTR string;
+       }
+       data;
+}
+HEADER_TYPE;
+
+typedef struct new_buffer
+{
+       uint32 ptr;
+       uint32 size;
+       prs_struct prs;
+       uint32 struct_start;
+       uint32 string_at_end;
+}
+NEW_BUFFER;
+
+typedef struct spool_q_getprinterdata
+{
+       POLICY_HND handle;
+       UNISTR2 valuename;
+       uint32 size;
+}
+SPOOL_Q_GETPRINTERDATA;
+
+typedef struct spool_r_getprinterdata
+{
+       uint32 type;
+       uint32 size;
+       uint8 *data;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETPRINTERDATA;
+
+typedef struct spool_q_deleteprinterdata
+{
+       POLICY_HND handle;
+       UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATA;
+
+typedef struct spool_r_deleteprinterdata
+{
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATA;
+
+typedef struct spool_q_closeprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_CLOSEPRINTER;
+
+typedef struct spool_r_closeprinter
+{
+       POLICY_HND handle;
+       WERROR status;
+}
+SPOOL_R_CLOSEPRINTER;
+
+typedef struct spool_q_startpageprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_STARTPAGEPRINTER;
+
+typedef struct spool_r_startpageprinter
+{
+       WERROR status;
+}
+SPOOL_R_STARTPAGEPRINTER;
+
+typedef struct spool_q_endpageprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_ENDPAGEPRINTER;
+
+typedef struct spool_r_endpageprinter
+{
+       WERROR status;
+}
+SPOOL_R_ENDPAGEPRINTER;
+
+
+typedef struct spool_q_deleteprinterdriver
+{
+       uint32 server_ptr;
+       UNISTR2 server;
+       UNISTR2 arch;
+       UNISTR2 driver;
+}
+SPOOL_Q_DELETEPRINTERDRIVER;
+
+typedef struct spool_r_deleteprinterdriver
+{
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVER;
+
+typedef struct spool_q_deleteprinterdriverex
+{
+       uint32 server_ptr;
+       UNISTR2 server;
+       UNISTR2 arch;
+       UNISTR2 driver;
+       uint32 delete_flags;
+       uint32 version;
+}
+SPOOL_Q_DELETEPRINTERDRIVEREX;
+
+typedef struct spool_r_deleteprinterdriverex
+{
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVEREX;
+
+
+typedef struct spool_doc_info_1
+{
+       uint32 p_docname;
+       uint32 p_outputfile;
+       uint32 p_datatype;
+       UNISTR2 docname;
+       UNISTR2 outputfile;
+       UNISTR2 datatype;
+}
+DOC_INFO_1;
+
+typedef struct spool_doc_info
+{
+       uint32 switch_value;
+       DOC_INFO_1 doc_info_1;
+}
+DOC_INFO;
+
+typedef struct spool_doc_info_container
+{
+       uint32 level;
+       DOC_INFO docinfo;
+}
+DOC_INFO_CONTAINER;
+
+typedef struct spool_q_startdocprinter
+{
+       POLICY_HND handle;
+       DOC_INFO_CONTAINER doc_info_container;
+}
+SPOOL_Q_STARTDOCPRINTER;
+
+typedef struct spool_r_startdocprinter
+{
+       uint32 jobid;
+       WERROR status;
+}
+SPOOL_R_STARTDOCPRINTER;
+
+typedef struct spool_q_enddocprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_ENDDOCPRINTER;
+
+typedef struct spool_r_enddocprinter
+{
+       WERROR status;
+}
+SPOOL_R_ENDDOCPRINTER;
+
+typedef struct spool_q_writeprinter
+{
+       POLICY_HND handle;
+       uint32 buffer_size;
+       uint8 *buffer;
+       uint32 buffer_size2;
+}
+SPOOL_Q_WRITEPRINTER;
+
+typedef struct spool_r_writeprinter
+{
+       uint32 buffer_written;
+       WERROR status;
+}
+SPOOL_R_WRITEPRINTER;
+
+typedef struct spool_notify_option
+{
+       uint32 version;
+       uint32 flags;
+       uint32 count;
+       uint32 option_type_ptr;
+       SPOOL_NOTIFY_OPTION_TYPE_CTR ctr;
+}
+SPOOL_NOTIFY_OPTION;
+
+typedef struct spool_notify_info_data
+{
+       uint16 type;
+       uint16 field;
+       uint32 reserved;
+       uint32 id;
+       union {
+               uint32 value[2];
+               struct {
+                       uint32 length;
+                       uint16 *string;
+               } data;
+               struct {
+                       uint32  size;
+                       SEC_DESC *desc;
+               } sd;
+       }
+       notify_data;
+       uint32 size;
+       BOOL enc_type;
+} SPOOL_NOTIFY_INFO_DATA;
+
+typedef struct spool_notify_info
+{
+       uint32 version;
+       uint32 flags;
+       uint32 count;
+       SPOOL_NOTIFY_INFO_DATA *data;
+}
+SPOOL_NOTIFY_INFO;
+
+/* If the struct name looks obscure, yes it is ! */
+/* RemoteFindFirstPrinterChangeNotificationEx query struct */
+typedef struct spoolss_q_rffpcnex
+{
+       POLICY_HND handle;
+       uint32 flags;
+       uint32 options;
+       uint32 localmachine_ptr;
+       UNISTR2 localmachine;
+       uint32 printerlocal;
+       uint32 option_ptr;
+       SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFFPCNEX;
+
+typedef struct spool_r_rffpcnex
+{
+       WERROR status;
+}
+SPOOL_R_RFFPCNEX;
+
+/* Remote Find Next Printer Change Notify Ex */
+typedef struct spool_q_rfnpcnex
+{
+       POLICY_HND handle;
+       uint32 change;
+       uint32 option_ptr;
+       SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFNPCNEX;
+
+typedef struct spool_r_rfnpcnex
+{
+       uint32 info_ptr;
+       SPOOL_NOTIFY_INFO info;
+       WERROR status;
+}
+SPOOL_R_RFNPCNEX;
+
+/* Find Close Printer Notify */
+typedef struct spool_q_fcpn
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_FCPN;
+
+typedef struct spool_r_fcpn
+{
+       WERROR status;
+}
+SPOOL_R_FCPN;
+
+
+typedef struct printer_info_0
+{
+       UNISTR printername;
+       UNISTR servername;
+       uint32 cjobs;
+       uint32 total_jobs;
+       uint32 total_bytes;
+       
+       uint16 year;
+       uint16 month;
+       uint16 dayofweek;
+       uint16 day;
+       uint16 hour;
+       uint16 minute;
+       uint16 second;
+       uint16 milliseconds;
+
+       uint32 global_counter;
+       uint32 total_pages;
+
+       uint16 major_version;
+       uint16 build_version;
+
+       uint32 unknown7;
+       uint32 unknown8;
+       uint32 unknown9;
+       uint32 session_counter;
+       uint32 unknown11;
+       uint32 printer_errors;
+       uint32 unknown13;
+       uint32 unknown14;
+       uint32 unknown15;
+       uint32 unknown16;
+       uint32 change_id;
+       uint32 unknown18;
+       uint32 status;
+       uint32 unknown20;
+       uint32 c_setprinter;
+
+       uint16 unknown22;
+       uint16 unknown23;
+       uint16 unknown24;
+       uint16 unknown25;
+       uint16 unknown26;
+       uint16 unknown27;
+       uint16 unknown28;
+       uint16 unknown29;
+} PRINTER_INFO_0;
+
+typedef struct printer_info_1
+{
+       uint32 flags;
+       UNISTR description;
+       UNISTR name;
+       UNISTR comment;
+}
+PRINTER_INFO_1;
+
+typedef struct printer_info_2
+{
+       UNISTR servername;
+       UNISTR printername;
+       UNISTR sharename;
+       UNISTR portname;
+       UNISTR drivername;
+       UNISTR comment;
+       UNISTR location;
+       DEVICEMODE *devmode;
+       UNISTR sepfile;
+       UNISTR printprocessor;
+       UNISTR datatype;
+       UNISTR parameters;
+       SEC_DESC *secdesc;
+       uint32 attributes;
+       uint32 priority;
+       uint32 defaultpriority;
+       uint32 starttime;
+       uint32 untiltime;
+       uint32 status;
+       uint32 cjobs;
+       uint32 averageppm;
+}
+PRINTER_INFO_2;
+
+typedef struct printer_info_3
+{
+       uint32 flags;
+       SEC_DESC *secdesc;
+}
+PRINTER_INFO_3;
+
+typedef struct printer_info_4
+{
+       UNISTR printername;
+       UNISTR servername;
+       uint32 attributes;
+}
+PRINTER_INFO_4;
+
+typedef struct printer_info_5
+{
+       UNISTR printername;
+       UNISTR portname;
+       uint32 attributes;
+       uint32 device_not_selected_timeout;
+       uint32 transmission_retry_timeout;
+}
+PRINTER_INFO_5;
+
+#define SPOOL_DS_PUBLISH       1
+#define SPOOL_DS_UPDATE                2
+#define SPOOL_DS_UNPUBLISH     4
+#define SPOOL_DS_PENDING        0x80000000
+
+typedef struct printer_info_7
+{
+       UNISTR guid; /* text form of printer guid */
+       uint32 action;
+}
+PRINTER_INFO_7;
+
+typedef struct spool_q_enumprinters
+{
+       uint32 flags;
+       uint32 servername_ptr;
+       UNISTR2 servername;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERS;
+
+typedef struct printer_info_ctr_info
+{
+       PRINTER_INFO_0 *printers_0;
+       PRINTER_INFO_1 *printers_1;
+       PRINTER_INFO_2 *printers_2;
+       PRINTER_INFO_3 *printers_3;
+       PRINTER_INFO_4 *printers_4;
+       PRINTER_INFO_5 *printers_5;
+}
+PRINTER_INFO_CTR;
+
+typedef struct spool_r_enumprinters
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;          /* bytes needed */
+       uint32 returned;        /* number of printers */
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTERS;
+
+
+typedef struct spool_q_getprinter
+{
+       POLICY_HND handle;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_GETPRINTER;
+
+typedef struct printer_info_info
+{
+       union
+       {
+               PRINTER_INFO_0 *info0;
+               PRINTER_INFO_1 *info1;
+               PRINTER_INFO_2 *info2;
+               void *info;
+       } printer;
+} PRINTER_INFO;
+
+typedef struct spool_r_getprinter
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+} SPOOL_R_GETPRINTER;
+
+typedef struct driver_info_1
+{
+       UNISTR name;
+} DRIVER_INFO_1;
+
+typedef struct driver_info_2
+{
+       uint32 version;
+       UNISTR name;
+       UNISTR architecture;
+       UNISTR driverpath;
+       UNISTR datafile;
+       UNISTR configfile;
+} DRIVER_INFO_2;
+
+typedef struct driver_info_3
+{
+       uint32 version;
+       UNISTR name;
+       UNISTR architecture;
+       UNISTR driverpath;
+       UNISTR datafile;
+       UNISTR configfile;
+       UNISTR helpfile;
+       uint16 *dependentfiles;
+       UNISTR monitorname;
+       UNISTR defaultdatatype;
+}
+DRIVER_INFO_3;
+
+typedef struct driver_info_6
+{
+       uint32 version;
+       UNISTR name;
+       UNISTR architecture;
+       UNISTR driverpath;
+       UNISTR datafile;
+       UNISTR configfile;
+       UNISTR helpfile;
+       uint16 *dependentfiles;
+       UNISTR monitorname;
+       UNISTR defaultdatatype;
+       uint16* previousdrivernames;
+       NTTIME driver_date;
+       uint32 padding;
+       uint32 driver_version_low;
+       uint32 driver_version_high;
+       UNISTR mfgname;
+       UNISTR oem_url;
+       UNISTR hardware_id;
+       UNISTR provider;
+}
+DRIVER_INFO_6;
+
+typedef struct driver_info_info
+{
+       DRIVER_INFO_1 *info1;
+       DRIVER_INFO_2 *info2;
+       DRIVER_INFO_3 *info3;
+       DRIVER_INFO_6 *info6;
+}
+PRINTER_DRIVER_CTR;
+
+typedef struct spool_q_getprinterdriver2
+{
+       POLICY_HND handle;
+       uint32 architecture_ptr;
+       UNISTR2 architecture;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+       uint32 clientmajorversion;
+       uint32 clientminorversion;
+}
+SPOOL_Q_GETPRINTERDRIVER2;
+
+typedef struct spool_r_getprinterdriver2
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 servermajorversion;
+       uint32 serverminorversion;
+       WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVER2;
+
+
+typedef struct add_jobinfo_1
+{
+       UNISTR path;
+       uint32 job_number;
+}
+ADD_JOBINFO_1;
+
+
+typedef struct spool_q_addjob
+{
+       POLICY_HND handle;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ADDJOB;
+
+typedef struct spool_r_addjob
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_ADDJOB;
+
+/*
+ * I'm really wondering how many different time formats
+ * I will have to cope with
+ *
+ * JFM, 09/13/98 In a mad mood ;-(
+*/
+typedef struct systemtime
+{
+       uint16 year;
+       uint16 month;
+       uint16 dayofweek;
+       uint16 day;
+       uint16 hour;
+       uint16 minute;
+       uint16 second;
+       uint16 milliseconds;
+}
+SYSTEMTIME;
+
+typedef struct s_job_info_1
+{
+       uint32 jobid;
+       UNISTR printername;
+       UNISTR machinename;
+       UNISTR username;
+       UNISTR document;
+       UNISTR datatype;
+       UNISTR text_status;
+       uint32 status;
+       uint32 priority;
+       uint32 position;
+       uint32 totalpages;
+       uint32 pagesprinted;
+       SYSTEMTIME submitted;
+}
+JOB_INFO_1;
+
+typedef struct s_job_info_2
+{
+       uint32 jobid;
+       UNISTR printername;
+       UNISTR machinename;
+       UNISTR username;
+       UNISTR document;
+       UNISTR notifyname;
+       UNISTR datatype;
+       UNISTR printprocessor;
+       UNISTR parameters;
+       UNISTR drivername;
+       DEVICEMODE *devmode;
+       UNISTR text_status;
+/*     SEC_DESC sec_desc;*/
+       uint32 status;
+       uint32 priority;
+       uint32 position;
+       uint32 starttime;
+       uint32 untiltime;
+       uint32 totalpages;
+       uint32 size;
+       SYSTEMTIME submitted;
+       uint32 timeelapsed;
+       uint32 pagesprinted;
+}
+JOB_INFO_2;
+
+typedef struct spool_q_enumjobs
+{
+       POLICY_HND handle;
+       uint32 firstjob;
+       uint32 numofjobs;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMJOBS;
+
+typedef struct job_info_ctr_info
+{
+       union
+       {
+               JOB_INFO_1 *job_info_1;
+               JOB_INFO_2 *job_info_2;
+               void *info;
+       } job;
+
+} JOB_INFO_CTR;
+
+typedef struct spool_r_enumjobs
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMJOBS;
+
+typedef struct spool_q_schedulejob
+{
+       POLICY_HND handle;
+       uint32 jobid;
+}
+SPOOL_Q_SCHEDULEJOB;
+
+typedef struct spool_r_schedulejob
+{
+       WERROR status;
+}
+SPOOL_R_SCHEDULEJOB;
+
+typedef struct s_port_info_1
+{
+       UNISTR port_name;
+}
+PORT_INFO_1;
+
+typedef struct s_port_info_2
+{
+       UNISTR port_name;
+       UNISTR monitor_name;
+       UNISTR description;
+       uint32 port_type;
+       uint32 reserved;
+}
+PORT_INFO_2;
+
+typedef struct spool_q_enumports
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPORTS;
+
+typedef struct port_info_ctr_info
+{
+       union
+       {
+               PORT_INFO_1 *info_1;
+               PORT_INFO_2 *info_2;
+       }
+       port;
+
+}
+PORT_INFO_CTR;
+
+typedef struct spool_r_enumports
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;          /* bytes needed */
+       uint32 returned;        /* number of printers */
+       WERROR status;
+}
+SPOOL_R_ENUMPORTS;
+
+#define JOB_CONTROL_PAUSE              1
+#define JOB_CONTROL_RESUME             2
+#define JOB_CONTROL_CANCEL             3
+#define JOB_CONTROL_RESTART            4
+#define JOB_CONTROL_DELETE             5
+
+typedef struct job_info_info
+{
+       union
+       {
+               JOB_INFO_1 job_info_1;
+               JOB_INFO_2 job_info_2;
+       }
+       job;
+
+}
+JOB_INFO;
+
+typedef struct spool_q_setjob
+{
+       POLICY_HND handle;
+       uint32 jobid;
+       uint32 level;
+       JOB_INFO ctr;
+       uint32 command;
+
+}
+SPOOL_Q_SETJOB;
+
+typedef struct spool_r_setjob
+{
+       WERROR status;
+
+}
+SPOOL_R_SETJOB;
+
+typedef struct spool_q_enumprinterdrivers
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 environment_ptr;
+       UNISTR2 environment;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERDRIVERS;
+
+typedef struct spool_r_enumprinterdrivers
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTERDRIVERS;
+
+#define FORM_USER    0
+#define FORM_BUILTIN 1
+#define FORM_PRINTER 2
+
+typedef struct spool_form_1
+{
+       uint32 flag;
+       UNISTR name;
+       uint32 width;
+       uint32 length;
+       uint32 left;
+       uint32 top;
+       uint32 right;
+       uint32 bottom;
+}
+FORM_1;
+
+typedef struct spool_q_enumforms
+{
+       POLICY_HND handle;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMFORMS;
+
+typedef struct spool_r_enumforms
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 numofforms;
+       WERROR status;
+}
+SPOOL_R_ENUMFORMS;
+
+typedef struct spool_q_getform
+{
+       POLICY_HND handle;
+       UNISTR2 formname;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_GETFORM;
+
+typedef struct spool_r_getform
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETFORM;
+
+typedef struct spool_printer_info_level_1
+{
+       uint32 flags;
+       uint32 description_ptr;
+       uint32 name_ptr;
+       uint32 comment_ptr;
+       UNISTR2 description;
+       UNISTR2 name;
+       UNISTR2 comment;        
+} SPOOL_PRINTER_INFO_LEVEL_1;
+
+typedef struct spool_printer_info_level_2
+{
+       uint32 servername_ptr;
+       uint32 printername_ptr;
+       uint32 sharename_ptr;
+       uint32 portname_ptr;
+       uint32 drivername_ptr;
+       uint32 comment_ptr;
+       uint32 location_ptr;
+       uint32 devmode_ptr;
+       uint32 sepfile_ptr;
+       uint32 printprocessor_ptr;
+       uint32 datatype_ptr;
+       uint32 parameters_ptr;
+       uint32 secdesc_ptr;
+       uint32 attributes;
+       uint32 priority;
+       uint32 default_priority;
+       uint32 starttime;
+       uint32 untiltime;
+       uint32 status;
+       uint32 cjobs;
+       uint32 averageppm;
+       UNISTR2 servername;
+       UNISTR2 printername;
+       UNISTR2 sharename;
+       UNISTR2 portname;
+       UNISTR2 drivername;
+       UNISTR2 comment;
+       UNISTR2 location;
+       UNISTR2 sepfile;
+       UNISTR2 printprocessor;
+       UNISTR2 datatype;
+       UNISTR2 parameters;
+}
+SPOOL_PRINTER_INFO_LEVEL_2;
+
+typedef struct spool_printer_info_level_3
+{
+       uint32 secdesc_ptr;
+}
+SPOOL_PRINTER_INFO_LEVEL_3;
+
+typedef struct spool_printer_info_level_7
+{
+       uint32 guid_ptr;
+       uint32 action;
+       UNISTR2 guid;
+}
+SPOOL_PRINTER_INFO_LEVEL_7;
+
+typedef struct spool_printer_info_level
+{
+       uint32 level;
+       uint32 info_ptr;
+       SPOOL_PRINTER_INFO_LEVEL_1 *info_1;
+       SPOOL_PRINTER_INFO_LEVEL_2 *info_2;
+       SPOOL_PRINTER_INFO_LEVEL_3 *info_3;
+       SPOOL_PRINTER_INFO_LEVEL_7 *info_7;
+}
+SPOOL_PRINTER_INFO_LEVEL;
+
+typedef struct spool_printer_driver_info_level_3
+{
+       uint32 cversion;
+       uint32 name_ptr;
+       uint32 environment_ptr;
+       uint32 driverpath_ptr;
+       uint32 datafile_ptr;
+       uint32 configfile_ptr;
+       uint32 helpfile_ptr;
+       uint32 monitorname_ptr;
+       uint32 defaultdatatype_ptr;
+       uint32 dependentfilessize;
+       uint32 dependentfiles_ptr;
+
+       UNISTR2 name;
+       UNISTR2 environment;
+       UNISTR2 driverpath;
+       UNISTR2 datafile;
+       UNISTR2 configfile;
+       UNISTR2 helpfile;
+       UNISTR2 monitorname;
+       UNISTR2 defaultdatatype;
+       BUFFER5 dependentfiles;
+
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+       uint32 version;
+       uint32 name_ptr;
+       uint32 environment_ptr;
+       uint32 driverpath_ptr;
+       uint32 datafile_ptr;
+       uint32 configfile_ptr;
+       uint32 helpfile_ptr;
+       uint32 monitorname_ptr;
+       uint32 defaultdatatype_ptr;
+       uint32 dependentfiles_len;
+       uint32 dependentfiles_ptr;
+       uint32 previousnames_len;
+       uint32 previousnames_ptr;
+       NTTIME  driverdate;
+       UINT64_S        driverversion;
+       uint32  dummy4;
+       uint32 mfgname_ptr;
+       uint32 oemurl_ptr;
+       uint32 hardwareid_ptr;
+       uint32 provider_ptr;
+       UNISTR2 name;
+       UNISTR2 environment;
+       UNISTR2 driverpath;
+       UNISTR2 datafile;
+       UNISTR2 configfile;
+       UNISTR2 helpfile;
+       UNISTR2 monitorname;
+       UNISTR2 defaultdatatype;
+       BUFFER5 dependentfiles;
+       BUFFER5 previousnames;
+       UNISTR2 mfgname;
+       UNISTR2 oemurl;
+       UNISTR2 hardwareid;
+       UNISTR2 provider;
+} SPOOL_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct spool_printer_driver_info_level
+{
+       uint32 level;
+       uint32 ptr;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL;
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_level_1
+{
+       uint32 size;
+       uint32 client_name_ptr;
+       uint32 user_name_ptr;
+       uint32 build;
+       uint32 major;
+       uint32 minor;
+       uint32 processor;
+       UNISTR2 client_name;
+       UNISTR2 user_name;
+}
+SPOOL_USER_LEVEL_1;
+
+typedef struct spool_user_level
+{
+       SPOOL_USER_LEVEL_1 *user_level_1;
+}
+SPOOL_USER_LEVEL;
+
+typedef struct spool_q_setprinter
+{
+       POLICY_HND handle;
+       uint32 level;
+       SPOOL_PRINTER_INFO_LEVEL info;
+       SEC_DESC_BUF *secdesc_ctr;
+       DEVMODE_CTR devmode_ctr;
+
+       uint32 command;
+
+}
+SPOOL_Q_SETPRINTER;
+
+typedef struct spool_r_setprinter
+{
+       WERROR status;
+}
+SPOOL_R_SETPRINTER;
+
+typedef struct spool_q_addprinter
+{
+       UNISTR2 server_name;
+       uint32 level;
+       SPOOL_PRINTER_INFO_LEVEL info;
+       DEVMODE_CTR devmode_ctr;
+       SEC_DESC_BUF *secdesc_ctr;
+       uint32 user_level;
+       SPOOL_USER_LEVEL user;
+}
+SPOOL_Q_ADDPRINTER;
+
+typedef struct spool_r_addprinter
+{
+       WERROR status;
+}
+SPOOL_R_ADDPRINTER;
+
+typedef struct spool_q_deleteprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_DELETEPRINTER;
+
+typedef struct spool_r_deleteprinter
+{
+       POLICY_HND handle;
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTER;
+
+typedef struct spool_q_abortprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_ABORTPRINTER;
+
+typedef struct spool_r_abortprinter
+{
+       WERROR status;
+}
+SPOOL_R_ABORTPRINTER;
+
+
+typedef struct spool_q_addprinterex
+{
+       uint32 server_name_ptr;
+       UNISTR2 server_name;
+       uint32 level;
+       SPOOL_PRINTER_INFO_LEVEL info;
+       DEVMODE_CTR devmode_ctr;
+       SEC_DESC_BUF *secdesc_ctr;
+       uint32 user_switch;
+       SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_ADDPRINTEREX;
+
+typedef struct spool_r_addprinterex
+{
+       POLICY_HND handle;
+       WERROR status;
+}
+SPOOL_R_ADDPRINTEREX;
+
+
+typedef struct spool_q_addprinterdriver
+{
+       uint32 server_name_ptr;
+       UNISTR2 server_name;
+       uint32 level;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+}
+SPOOL_Q_ADDPRINTERDRIVER;
+
+typedef struct spool_r_addprinterdriver
+{
+       WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVER;
+
+typedef struct spool_q_addprinterdriverex
+{
+       uint32 server_name_ptr;
+       UNISTR2 server_name;
+       uint32 level;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+       uint32 copy_flags;
+}
+SPOOL_Q_ADDPRINTERDRIVEREX;
+
+typedef struct spool_r_addprinterdriverex
+{
+       WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVEREX;
+
+
+typedef struct driver_directory_1
+{
+       UNISTR name;
+}
+DRIVER_DIRECTORY_1;
+
+typedef struct driver_info_ctr_info
+{
+       DRIVER_DIRECTORY_1 *info1;
+}
+DRIVER_DIRECTORY_CTR;
+
+typedef struct spool_q_getprinterdriverdirectory
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 environment_ptr;
+       UNISTR2 environment;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_GETPRINTERDRIVERDIR;
+
+typedef struct spool_r_getprinterdriverdirectory
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVERDIR;
+
+typedef struct spool_q_addprintprocessor
+{
+       uint32 server_ptr;
+       UNISTR2 server;
+       UNISTR2 environment;
+       UNISTR2 path;
+       UNISTR2 name;
+}
+SPOOL_Q_ADDPRINTPROCESSOR;
+
+typedef struct spool_r_addprintprocessor
+{
+       WERROR status;
+}
+SPOOL_R_ADDPRINTPROCESSOR;
+
+
+typedef struct spool_q_enumprintprocessors
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 environment_ptr;
+       UNISTR2 environment;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCESSORS;
+
+typedef struct printprocessor_1
+{
+       UNISTR name;
+}
+PRINTPROCESSOR_1;
+
+typedef struct spool_r_enumprintprocessors
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCESSORS;
+
+typedef struct spool_q_enumprintprocdatatypes
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 processor_ptr;
+       UNISTR2 processor;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCDATATYPES;
+
+typedef struct ppdatatype_1
+{
+       UNISTR name;
+}
+PRINTPROCDATATYPE_1;
+
+typedef struct spool_r_enumprintprocdatatypes
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCDATATYPES;
+
+typedef struct printmonitor_1
+{
+       UNISTR name;
+}
+PRINTMONITOR_1;
+
+typedef struct printmonitor_2
+{
+       UNISTR name;
+       UNISTR environment;
+       UNISTR dll_name;
+}
+PRINTMONITOR_2;
+
+typedef struct spool_q_enumprintmonitors
+{
+       uint32 name_ptr;
+       UNISTR2 name;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_ENUMPRINTMONITORS;
+
+typedef struct spool_r_enumprintmonitors
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTMONITORS;
+
+
+typedef struct spool_q_enumprinterdata
+{
+       POLICY_HND handle;
+       uint32 index;
+       uint32 valuesize;
+       uint32 datasize;
+}
+SPOOL_Q_ENUMPRINTERDATA;
+
+typedef struct spool_r_enumprinterdata
+{
+       uint32 valuesize;
+       uint16 *value;
+       uint32 realvaluesize;
+       uint32 type;
+       uint32 datasize;
+       uint8 *data;
+       uint32 realdatasize;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATA;
+
+typedef struct spool_q_setprinterdata
+{
+       POLICY_HND handle;
+       UNISTR2 value;
+       uint32 type;
+       uint32 max_len;
+       uint8 *data;
+       uint32 real_len;
+       uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATA;
+
+typedef struct spool_r_setprinterdata
+{
+       WERROR status;
+}
+SPOOL_R_SETPRINTERDATA;
+
+typedef struct spool_q_resetprinter
+{
+       POLICY_HND handle;
+       uint32 datatype_ptr;
+       UNISTR2 datatype;
+       DEVMODE_CTR devmode_ctr;
+
+} SPOOL_Q_RESETPRINTER;
+
+typedef struct spool_r_resetprinter
+{
+       WERROR status;
+} 
+SPOOL_R_RESETPRINTER;
+
+
+
+typedef struct _form
+{
+       uint32 flags;
+       uint32 name_ptr;
+       uint32 size_x;
+       uint32 size_y;
+       uint32 left;
+       uint32 top;
+       uint32 right;
+       uint32 bottom;
+       UNISTR2 name;
+}
+FORM;
+
+typedef struct spool_q_addform
+{
+       POLICY_HND handle;
+       uint32 level;
+       uint32 level2;          /* This should really be part of the FORM structure */
+       FORM form;
+}
+SPOOL_Q_ADDFORM;
+
+typedef struct spool_r_addform
+{
+       WERROR status;
+}
+SPOOL_R_ADDFORM;
+
+typedef struct spool_q_setform
+{
+       POLICY_HND handle;
+       UNISTR2 name;
+       uint32 level;
+       uint32 level2;
+       FORM form;
+}
+SPOOL_Q_SETFORM;
+
+typedef struct spool_r_setform
+{
+       WERROR status;
+}
+SPOOL_R_SETFORM;
+
+typedef struct spool_q_deleteform
+{
+       POLICY_HND handle;
+       UNISTR2 name;
+}
+SPOOL_Q_DELETEFORM;
+
+typedef struct spool_r_deleteform
+{
+       WERROR status;
+}
+SPOOL_R_DELETEFORM;
+
+typedef struct spool_q_getjob
+{
+       POLICY_HND handle;
+       uint32 jobid;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_GETJOB;
+
+typedef struct pjob_info_info
+{
+       union
+       {
+               JOB_INFO_1 *job_info_1;
+               JOB_INFO_2 *job_info_2;
+               void *info;
+       }
+       job;
+
+}
+PJOB_INFO;
+
+typedef struct spool_r_getjob
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETJOB;
+
+typedef struct spool_q_replyopenprinter
+{
+       UNISTR2 string;
+       uint32 printer;
+       uint32 type;
+       uint32 unknown0;
+       uint32 unknown1;
+}
+SPOOL_Q_REPLYOPENPRINTER;
+
+typedef struct spool_r_replyopenprinter
+{
+       POLICY_HND handle;
+       WERROR status;
+}
+SPOOL_R_REPLYOPENPRINTER;
+
+typedef struct spool_q_routerreplyprinter
+{
+       POLICY_HND handle;
+       uint32 condition;
+       uint32 unknown1;        /* 0x00000001 */
+       uint32 change_id;
+       uint8  unknown2[5];     /* 0x0000000001 */
+}
+SPOOL_Q_ROUTERREPLYPRINTER;
+
+typedef struct spool_r_routerreplyprinter
+{
+       WERROR status;
+}
+SPOOL_R_ROUTERREPLYPRINTER;
+
+typedef struct spool_q_replycloseprinter
+{
+       POLICY_HND handle;
+}
+SPOOL_Q_REPLYCLOSEPRINTER;
+
+typedef struct spool_r_replycloseprinter
+{
+       POLICY_HND handle;
+       WERROR status;
+}
+SPOOL_R_REPLYCLOSEPRINTER;
+
+typedef struct spool_q_rrpcn
+{
+       POLICY_HND handle;
+       uint32 change_low;
+       uint32 change_high;
+       uint32 unknown0;
+       uint32 unknown1;
+       uint32 info_ptr;
+       SPOOL_NOTIFY_INFO info; 
+}
+SPOOL_Q_REPLY_RRPCN;
+
+typedef struct spool_r_rrpcn
+{
+       uint32 unknown0;
+       WERROR status;
+}
+SPOOL_R_REPLY_RRPCN;
+
+typedef struct spool_q_getprinterdataex
+{
+       POLICY_HND handle;
+       UNISTR2 keyname;
+        UNISTR2 valuename;
+       uint32 size;
+}
+SPOOL_Q_GETPRINTERDATAEX;
+
+typedef struct spool_r_getprinterdataex
+{
+       uint32 type;
+       uint32 size;
+       uint8 *data;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETPRINTERDATAEX;
+
+typedef struct spool_q_setprinterdataex
+{
+       POLICY_HND handle;
+       UNISTR2 key;
+       UNISTR2 value;
+       uint32 type;
+       uint32 max_len;
+       uint8 *data;
+       uint32 real_len;
+       uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATAEX;
+
+typedef struct spool_r_setprinterdataex
+{
+       WERROR status;
+}
+SPOOL_R_SETPRINTERDATAEX;
+
+
+typedef struct spool_q_deleteprinterdataex
+{
+       POLICY_HND handle;
+       UNISTR2 keyname;
+       UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATAEX;
+
+typedef struct spool_r_deleteprinterdataex
+{
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATAEX;
+
+
+typedef struct spool_q_enumprinterkey
+{
+       POLICY_HND handle;
+       UNISTR2 key;
+       uint32 size;
+}
+SPOOL_Q_ENUMPRINTERKEY;
+
+typedef struct spool_r_enumprinterkey
+{
+       BUFFER5 keys;
+       uint32 needed;  /* in bytes */
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTERKEY;
+
+typedef struct spool_q_deleteprinterkey
+{
+       POLICY_HND handle;
+       UNISTR2 keyname;
+}
+SPOOL_Q_DELETEPRINTERKEY;
+
+typedef struct spool_r_deleteprinterkey
+{
+       WERROR status;
+}
+SPOOL_R_DELETEPRINTERKEY;
+
+typedef struct printer_enum_values
+{
+       UNISTR valuename;
+       uint32 value_len;
+       uint32 type;
+       uint8  *data;
+       uint32 data_len; 
+       
+}
+PRINTER_ENUM_VALUES;
+
+typedef struct printer_enum_values_ctr
+{
+       uint32 size;
+       uint32 size_of_array;
+       PRINTER_ENUM_VALUES *values;
+}
+PRINTER_ENUM_VALUES_CTR;
+
+typedef struct spool_q_enumprinterdataex
+{
+       POLICY_HND handle;
+       UNISTR2 key;
+       uint32 size;
+}
+SPOOL_Q_ENUMPRINTERDATAEX;
+
+typedef struct spool_r_enumprinterdataex
+{
+       PRINTER_ENUM_VALUES_CTR ctr;
+       uint32 needed;
+       uint32 returned;
+       WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATAEX;
+
+typedef struct printprocessor_directory_1
+{
+       UNISTR name;
+}
+PRINTPROCESSOR_DIRECTORY_1;
+
+typedef struct spool_q_getprintprocessordirectory
+{
+       UNISTR2 name;
+       UNISTR2 environment;
+       uint32 level;
+       NEW_BUFFER *buffer;
+       uint32 offered;
+}
+SPOOL_Q_GETPRINTPROCESSORDIRECTORY;
+
+typedef struct spool_r_getprintprocessordirectory
+{
+       NEW_BUFFER *buffer;
+       uint32 needed;
+       WERROR status;
+}
+SPOOL_R_GETPRINTPROCESSORDIRECTORY;
+
+#define PRINTER_DRIVER_VERSION 2
+#define PRINTER_DRIVER_ARCHITECTURE "Windows NT x86"
+
+#endif /* _RPC_SPOOLSS_H */
+
diff --git a/source4/include/rpc_srvsvc.h b/source4/include/rpc_srvsvc.h
new file mode 100644 (file)
index 0000000..94d23bb
--- /dev/null
@@ -0,0 +1,948 @@
+/*
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   Copyright (C) Nigel Williams 2001
+   
+   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.
+*/
+
+#ifndef _RPC_SRVSVC_H /* _RPC_SRVSVC_H */
+#define _RPC_SRVSVC_H 
+
+/* srvsvc pipe */
+#define SRV_NET_CONN_ENUM          0x08
+#define SRV_NET_FILE_ENUM          0x09
+#define SRV_NET_FILE_CLOSE         0x0b
+#define SRV_NET_SESS_ENUM          0x0c
+#define SRV_NET_SHARE_ADD          0x0e
+#define SRV_NET_SHARE_ENUM_ALL     0x0f
+#define SRV_NET_SHARE_GET_INFO     0x10
+#define SRV_NET_SHARE_SET_INFO     0x11
+#define SRV_NET_SHARE_DEL          0x12
+#define SRV_NET_SHARE_DEL_STICKY   0x13
+#define SRV_NET_SRV_GET_INFO       0x15
+#define SRV_NET_SRV_SET_INFO       0x16
+#define SRV_NET_DISK_ENUM          0x17
+#define SRV_NET_REMOTE_TOD         0x1c
+#define SRV_NET_NAME_VALIDATE      0x21
+#define SRV_NET_SHARE_ENUM         0x24
+#define SRV_NET_FILE_QUERY_SECDESC 0x27
+#define SRV_NET_FILE_SET_SECDESC   0x28
+
+#define MAX_SERVER_DISK_ENTRIES 15
+
+typedef struct disk_info {
+       uint32  unknown;
+       UNISTR3 disk_name;
+} DISK_INFO;
+
+typedef struct disk_enum_container {
+       uint32 level;
+       uint32 entries_read;
+       uint32 unknown;
+       uint32 disk_info_ptr;
+       DISK_INFO *disk_info;
+} DISK_ENUM_CONTAINER;
+
+typedef struct net_srv_disk_enum {
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* server name */
+
+       DISK_ENUM_CONTAINER disk_enum_ctr;
+
+       uint32 preferred_len;        /* preferred maximum length (0xffff ffff) */
+       uint32 total_entries;        /* total number of entries */
+       ENUM_HND enum_hnd;
+       WERROR status;               /* return status */
+} SRV_Q_NET_DISK_ENUM, SRV_R_NET_DISK_ENUM;
+
+typedef struct net_name_validate {
+       uint32 ptr_srv_name;
+       UNISTR2 uni_srv_name;
+       UNISTR2 uni_name; /*name to validate*/
+       uint32 type;
+       uint32 flags;
+       WERROR status;
+} SRV_Q_NET_NAME_VALIDATE, SRV_R_NET_NAME_VALIDATE;
+
+/* SESS_INFO_0 (pointers to level 0 session info strings) */
+typedef struct ptr_sess_info0
+{
+       uint32 ptr_name; /* pointer to name. */
+
+} SESS_INFO_0;
+
+/* SESS_INFO_0_STR (level 0 session info strings) */
+typedef struct str_sess_info0
+{
+       UNISTR2 uni_name; /* unicode string of name */
+
+} SESS_INFO_0_STR;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_SESS_ENTRIES 32
+
+/* SRV_SESS_INFO_0 */
+typedef struct srv_sess_info_0_info
+{
+       uint32 num_entries_read;                     /* EntriesRead */
+       uint32 ptr_sess_info;                       /* Buffer */
+       uint32 num_entries_read2;                    /* EntriesRead */
+
+       SESS_INFO_0     info_0    [MAX_SESS_ENTRIES]; /* session entry pointers */
+       SESS_INFO_0_STR info_0_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_0;
+
+/* SESS_INFO_1 (pointers to level 1 session info strings) */
+typedef struct ptr_sess_info1
+{
+       uint32 ptr_name; /* pointer to name. */
+       uint32 ptr_user; /* pointer to user name. */
+
+       uint32 num_opens;
+       uint32 open_time;
+       uint32 idle_time;
+       uint32 user_flags;
+
+} SESS_INFO_1;
+
+/* SESS_INFO_1_STR (level 1 session info strings) */
+typedef struct str_sess_info1
+{
+       UNISTR2 uni_name; /* unicode string of name */
+       UNISTR2 uni_user; /* unicode string of user */
+
+} SESS_INFO_1_STR;
+
+/* SRV_SESS_INFO_1 */
+typedef struct srv_sess_info_1_info
+{
+       uint32 num_entries_read;                     /* EntriesRead */
+       uint32 ptr_sess_info;                       /* Buffer */
+       uint32 num_entries_read2;                    /* EntriesRead */
+
+       SESS_INFO_1     info_1    [MAX_SESS_ENTRIES]; /* session entry pointers */
+       SESS_INFO_1_STR info_1_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_1;
+
+/* SRV_SESS_INFO_CTR */
+typedef struct srv_sess_info_ctr_info
+{
+       uint32 switch_value;         /* switch value */
+       uint32 ptr_sess_ctr;       /* pointer to sess info union */
+       union
+    {
+               SRV_SESS_INFO_0 info0; /* session info level 0 */
+               SRV_SESS_INFO_1 info1; /* session info level 1 */
+
+    } sess;
+
+} SRV_SESS_INFO_CTR;
+
+
+/* SRV_Q_NET_SESS_ENUM */
+typedef struct q_net_sess_enum_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* server name */
+
+       uint32 ptr_qual_name;         /* pointer (to qualifier name) */
+       UNISTR2 uni_qual_name;        /* qualifier name "\\qualifier" */
+
+       uint32 ptr_user_name;         /* pointer (to user name */
+       UNISTR2 uni_user_name;        /* user name */
+
+       uint32 sess_level;          /* session level */
+
+       SRV_SESS_INFO_CTR *ctr;
+
+       uint32 preferred_len;        /* preferred maximum length (0xffff ffff) */
+       ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SESS_ENUM;
+
+/* SRV_R_NET_SESS_ENUM */
+typedef struct r_net_sess_enum_info
+{
+       uint32 sess_level;          /* share level */
+
+       SRV_SESS_INFO_CTR *ctr;
+
+       uint32 total_entries;                    /* total number of entries */
+       ENUM_HND enum_hnd;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SESS_ENUM;
+
+/* CONN_INFO_0 (pointers to level 0 connection info strings) */
+typedef struct ptr_conn_info0
+{
+       uint32 id; /* connection id. */
+
+} CONN_INFO_0;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_CONN_ENTRIES 32
+
+/* SRV_CONN_INFO_0 */
+typedef struct srv_conn_info_0_info
+{
+       uint32 num_entries_read;                     /* EntriesRead */
+       uint32 ptr_conn_info;                       /* Buffer */
+       uint32 num_entries_read2;                    /* EntriesRead */
+
+       CONN_INFO_0     info_0    [MAX_CONN_ENTRIES]; /* connection entry pointers */
+
+} SRV_CONN_INFO_0;
+
+/* CONN_INFO_1 (pointers to level 1 connection info strings) */
+typedef struct ptr_conn_info1
+{
+       uint32 id;   /* connection id */
+       uint32 type; /* 0x3 */
+       uint32 num_opens;
+       uint32 num_users;
+       uint32 open_time;
+
+       uint32 ptr_usr_name; /* pointer to user name. */
+       uint32 ptr_net_name; /* pointer to network name (e.g IPC$). */
+
+} CONN_INFO_1;
+
+/* CONN_INFO_1_STR (level 1 connection info strings) */
+typedef struct str_conn_info1
+{
+       UNISTR2 uni_usr_name; /* unicode string of user */
+       UNISTR2 uni_net_name; /* unicode string of name */
+
+} CONN_INFO_1_STR;
+
+/* SRV_CONN_INFO_1 */
+typedef struct srv_conn_info_1_info
+{
+       uint32 num_entries_read;                     /* EntriesRead */
+       uint32 ptr_conn_info;                       /* Buffer */
+       uint32 num_entries_read2;                    /* EntriesRead */
+
+       CONN_INFO_1     info_1    [MAX_CONN_ENTRIES]; /* connection entry pointers */
+       CONN_INFO_1_STR info_1_str[MAX_CONN_ENTRIES]; /* connection entry strings */
+
+} SRV_CONN_INFO_1;
+
+/* SRV_CONN_INFO_CTR */
+typedef struct srv_conn_info_ctr_info
+{
+       uint32 switch_value;         /* switch value */
+       uint32 ptr_conn_ctr;       /* pointer to conn info union */
+       union
+    {
+               SRV_CONN_INFO_0 info0; /* connection info level 0 */
+               SRV_CONN_INFO_1 info1; /* connection info level 1 */
+
+    } conn;
+
+} SRV_CONN_INFO_CTR;
+
+
+/* SRV_Q_NET_CONN_ENUM */
+typedef struct q_net_conn_enum_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name) */
+       UNISTR2 uni_srv_name;        /* server name "\\server" */
+
+       uint32 ptr_qual_name;         /* pointer (to qualifier name) */
+       UNISTR2 uni_qual_name;        /* qualifier name "\\qualifier" */
+
+       uint32 conn_level;          /* connection level */
+
+       SRV_CONN_INFO_CTR *ctr;
+
+       uint32 preferred_len;        /* preferred maximum length (0xffff ffff) */
+       ENUM_HND enum_hnd;
+
+} SRV_Q_NET_CONN_ENUM;
+
+/* SRV_R_NET_CONN_ENUM */
+typedef struct r_net_conn_enum_info
+{
+       uint32 conn_level;          /* share level */
+
+       SRV_CONN_INFO_CTR *ctr;
+
+       uint32 total_entries;                    /* total number of entries */
+       ENUM_HND enum_hnd;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_CONN_ENUM;
+
+/* SH_INFO_0 */
+typedef struct ptr_share_info0
+{
+       uint32 ptr_netname; /* pointer to net name. */
+} SH_INFO_0;
+
+/* SH_INFO_0_STR (level 0 share info strings) */
+typedef struct str_share_info0
+{
+        SH_INFO_0 *ptrs;
+
+       UNISTR2 uni_netname; /* unicode string of net name */
+
+} SH_INFO_0_STR;
+
+/* SRV_SHARE_INFO_0 */
+typedef struct share_info_0_info
+{
+       SH_INFO_0 info_0;
+       SH_INFO_0_STR info_0_str;
+
+} SRV_SHARE_INFO_0;
+
+/* SH_INFO_1 (pointers to level 1 share info strings) */
+typedef struct ptr_share_info1
+{
+       uint32 ptr_netname; /* pointer to net name. */
+       uint32 type; /* ipc, print, disk ... */
+       uint32 ptr_remark; /* pointer to comment. */
+
+} SH_INFO_1;
+
+/* SH_INFO_1_STR (level 1 share info strings) */
+typedef struct str_share_info1
+{
+        SH_INFO_1 *ptrs;
+
+       UNISTR2 uni_netname; /* unicode string of net name */
+       UNISTR2 uni_remark; /* unicode string of comment */
+
+} SH_INFO_1_STR;
+
+/* SRV_SHARE_INFO_1 */
+typedef struct share_info_1_info
+{
+       SH_INFO_1 info_1;
+       SH_INFO_1_STR info_1_str;
+
+} SRV_SHARE_INFO_1;
+
+/* SH_INFO_2 (pointers to level 2 share info strings) */
+typedef struct ptr_share_info2
+{
+       uint32 ptr_netname; /* pointer to net name. */
+       uint32 type; /* ipc, print, disk ... */
+       uint32 ptr_remark; /* pointer to comment. */
+       uint32 perms;      /* permissions */
+       uint32 max_uses;   /* maximum uses */
+       uint32 num_uses;   /* current uses */
+       uint32 ptr_path;   /* pointer to path name */
+       uint32 ptr_passwd; /* pointer to password */
+
+} SH_INFO_2;
+
+/* SH_INFO_2_STR (level 2 share info strings) */
+typedef struct str_share_info2
+{
+       SH_INFO_2 *ptrs;
+
+       UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+       UNISTR2 uni_remark;  /* unicode string of comment (e.g "Logon server share") */
+       UNISTR2 uni_path;    /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+       UNISTR2 uni_passwd;  /* unicode string of password - presumably for share level security (e.g NULL) */
+
+} SH_INFO_2_STR;
+
+/* SRV_SHARE_INFO_2 */
+typedef struct share_info_2_info
+{
+       SH_INFO_2 info_2;
+       SH_INFO_2_STR info_2_str;
+
+} SRV_SHARE_INFO_2;
+
+typedef struct ptr_share_info501
+{
+       uint32 ptr_netname; /* pointer to net name */
+       uint32 type;     /* ipc, print, disk */
+       uint32 ptr_remark;  /* pointer to comment */
+       uint32 csc_policy;  /* client-side offline caching policy << 4 */
+} SH_INFO_501;
+
+typedef struct str_share_info501
+{
+       UNISTR2 uni_netname; /* unicode string of net name */
+       UNISTR2 uni_remark;  /* unicode string of comment */
+} SH_INFO_501_STR;
+
+/* SRV_SHARE_INFO_501 */
+typedef struct share_info_501_info
+{
+       SH_INFO_501 info_501;
+       SH_INFO_501_STR info_501_str;
+} SRV_SHARE_INFO_501;
+
+/* SH_INFO_502 (pointers to level 502 share info strings) */
+typedef struct ptr_share_info502
+{
+       uint32 ptr_netname; /* pointer to net name. */
+       uint32 type; /* ipc, print, disk ... */
+       uint32 ptr_remark; /* pointer to comment. */
+       uint32 perms;      /* permissions */
+       uint32 max_uses;   /* maximum uses */
+       uint32 num_uses;   /* current uses */
+       uint32 ptr_path;   /* pointer to path name */
+       uint32 ptr_passwd; /* pointer to password */
+        uint32 reserved;    /* this holds the space taken by the sd in the rpc packet */
+        uint32 reserved_offset;   /* required for _post operation when marshalling */
+       uint32 sd_size;    /* size of security descriptor */
+       uint32 ptr_sd;     /* pointer to security descriptor */
+
+} SH_INFO_502;
+
+/* SH_INFO_502_STR (level 502 share info strings) */
+typedef struct str_share_info502
+{
+       SH_INFO_502 *ptrs;
+
+       UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+       UNISTR2 uni_remark;  /* unicode string of comment (e.g "Logon server share") */
+       UNISTR2 uni_path;    /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+       UNISTR2 uni_passwd;  /* unicode string of password - presumably for share level security (e.g NULL) */
+
+        uint32 reserved;
+       uint32 sd_size;
+       SEC_DESC *sd;
+
+} SH_INFO_502_STR;
+
+/* SRV_SHARE_INFO_502 */
+typedef struct share_info_502_info
+{
+       SH_INFO_502 info_502;
+       SH_INFO_502_STR info_502_str;
+
+} SRV_SHARE_INFO_502;
+
+typedef struct ptr_share_info1004
+{
+       uint32 ptr_remark;
+
+} SH_INFO_1004;
+
+typedef struct str_share_info1004
+{
+       SH_INFO_1004 *ptrs;
+
+       UNISTR2 uni_remark;
+
+} SH_INFO_1004_STR;
+
+typedef struct ptr_info_1004_info
+{
+       SH_INFO_1004     info_1004; 
+       SH_INFO_1004_STR info_1004_str; 
+} SRV_SHARE_INFO_1004;
+
+typedef struct share_info_1005_info
+{
+  uint32 dfs_root_flag; 
+} SRV_SHARE_INFO_1005;
+
+typedef struct share_info_1006_info
+{
+       uint32 max_uses; 
+} SRV_SHARE_INFO_1006;
+
+typedef struct ptr_share_info1007
+{
+       uint32 flags;
+       uint32 ptr_AlternateDirectoryName;
+
+} SH_INFO_1007;
+
+typedef struct str_share_info1007
+{
+       SH_INFO_1007 *ptrs;
+
+       UNISTR2 uni_AlternateDirectoryName;
+
+} SH_INFO_1007_STR;
+
+typedef struct ptr_info_1007_info
+{
+       SH_INFO_1007     info_1007; 
+       SH_INFO_1007_STR info_1007_str; 
+} SRV_SHARE_INFO_1007;
+
+/* SRV_SHARE_INFO_1501 */
+typedef struct share_info_1501_info
+{
+       SEC_DESC_BUF *sdb;
+} SRV_SHARE_INFO_1501;
+
+/* SRV_SHARE_INFO_CTR */
+typedef struct srv_share_info_ctr_info
+{
+       uint32 info_level;
+       uint32 switch_value;
+       uint32 ptr_share_info;
+
+       uint32 num_entries;
+       uint32 ptr_entries;
+       uint32 num_entries2;
+
+       union {
+               SRV_SHARE_INFO_0    *info0;
+               SRV_SHARE_INFO_1    *info1;    /* share info level 1 */
+               SRV_SHARE_INFO_2    *info2;    /* share info level 2 */
+               SRV_SHARE_INFO_501  *info501;  /* share info level 501 */
+               SRV_SHARE_INFO_502  *info502;  /* share info level 502 */
+               SRV_SHARE_INFO_1004 *info1004;
+               SRV_SHARE_INFO_1005 *info1005;
+               SRV_SHARE_INFO_1006 *info1006;
+               SRV_SHARE_INFO_1007 *info1007;
+               SRV_SHARE_INFO_1501 *info1501;
+               void *info;
+
+       } share;
+
+} SRV_SHARE_INFO_CTR;
+
+/* SRV_Q_NET_SHARE_ENUM */
+typedef struct q_net_share_enum_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* server name */
+
+       SRV_SHARE_INFO_CTR ctr;     /* share info container */
+
+       uint32 preferred_len;        /* preferred maximum length (0xffff ffff) */
+
+       ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SHARE_ENUM;
+
+
+/* SRV_R_NET_SHARE_ENUM */
+typedef struct r_net_share_enum_info
+{
+       SRV_SHARE_INFO_CTR ctr;     /* share info container */
+
+       uint32 total_entries;                    /* total number of entries */
+       ENUM_HND enum_hnd;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SHARE_ENUM;
+
+
+/* SRV_Q_NET_SHARE_GET_INFO */
+typedef struct q_net_share_get_info_info
+{
+       uint32 ptr_srv_name;
+       UNISTR2 uni_srv_name;
+
+       UNISTR2 uni_share_name;
+       uint32 info_level;
+
+} SRV_Q_NET_SHARE_GET_INFO;
+
+/* SRV_SHARE_INFO */
+typedef struct srv_share_info {
+       uint32 switch_value;
+       uint32 ptr_share_ctr;
+
+       union {
+               SRV_SHARE_INFO_0    info0;
+               SRV_SHARE_INFO_1 info1;
+               SRV_SHARE_INFO_2 info2;
+               SRV_SHARE_INFO_501 info501;
+               SRV_SHARE_INFO_502 info502;
+               SRV_SHARE_INFO_1004 info1004;
+               SRV_SHARE_INFO_1005 info1005;
+               SRV_SHARE_INFO_1006 info1006;
+               SRV_SHARE_INFO_1007 info1007;
+               SRV_SHARE_INFO_1501 info1501;
+       } share;
+} SRV_SHARE_INFO;
+
+/* SRV_R_NET_SHARE_GET_INFO */
+typedef struct r_net_share_get_info_info
+{
+       SRV_SHARE_INFO info;
+       WERROR status;
+
+} SRV_R_NET_SHARE_GET_INFO;
+
+/* SRV_Q_NET_SHARE_SET_INFO */
+typedef struct q_net_share_set_info_info
+{
+       uint32 ptr_srv_name;
+       UNISTR2 uni_srv_name;
+
+       UNISTR2 uni_share_name;
+       uint32 info_level;
+
+       SRV_SHARE_INFO info;
+
+        uint32 ptr_parm_error;
+        uint32 parm_error;
+
+} SRV_Q_NET_SHARE_SET_INFO;
+
+/* SRV_R_NET_SHARE_SET_INFO */
+typedef struct r_net_share_set_info
+{
+        uint32 ptr_parm_error;
+        uint32 parm_error;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SHARE_SET_INFO;
+
+/* SRV_Q_NET_SHARE_ADD */
+typedef struct q_net_share_add
+{
+       uint32 ptr_srv_name;
+       UNISTR2 uni_srv_name;
+
+       uint32 info_level;
+
+       SRV_SHARE_INFO info;
+
+       uint32 ptr_err_index; /* pointer to error index */
+       uint32 err_index;     /* index in info to field in error */
+
+} SRV_Q_NET_SHARE_ADD;
+
+/* SRV_R_NET_SHARE_ADD */
+typedef struct r_net_share_add
+{
+
+        uint32 ptr_parm_error;
+        uint32 parm_error;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SHARE_ADD;
+
+/* SRV_Q_NET_SHARE_DEL */
+typedef struct q_net_share_del
+{
+       uint32 ptr_srv_name;
+       UNISTR2 uni_srv_name;
+       UNISTR2 uni_share_name;
+       uint32 reserved;
+
+} SRV_Q_NET_SHARE_DEL;
+
+/* SRV_R_NET_SHARE_DEL */
+typedef struct r_net_share_del
+{
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SHARE_DEL;
+
+/* FILE_INFO_3 (level 3 file info strings) */
+typedef struct file_info3_info
+{
+       uint32 id;            /* file index */
+       uint32 perms;         /* file permissions. don't know what format */
+       uint32 num_locks;     /* file locks */
+       uint32 ptr_path_name; /* file name */
+       uint32 ptr_user_name; /* file owner */
+
+} FILE_INFO_3;
+
+/* FILE_INFO_3_STR (level 3 file info strings) */
+typedef struct str_file_info3_info
+{
+       UNISTR2 uni_path_name; /* unicode string of file name */
+       UNISTR2 uni_user_name; /* unicode string of file owner. */
+
+} FILE_INFO_3_STR;
+
+/* SRV_FILE_INFO_3 */
+typedef struct srv_file_info_3
+{
+       uint32 num_entries_read;                     /* EntriesRead */
+       uint32 ptr_file_info;                        /* Buffer */
+
+       uint32 num_entries_read2;                    /* EntriesRead */
+       FILE_INFO_3     info_3;     /* file entry details */
+       FILE_INFO_3_STR info_3_str; /* file entry strings */
+} SRV_FILE_INFO_3;
+
+/* SRV_FILE_INFO_CTR */
+typedef struct srv_file_info_3_info
+{
+       uint32 switch_value;         /* switch value */
+       uint32 ptr_file_info;        /* pointer to file info union */
+
+       uint32 num_entries;
+       uint32 ptr_entries;
+       uint32 num_entries2;
+       union
+       {
+               SRV_FILE_INFO_3 *info3;
+       } file;
+
+} SRV_FILE_INFO_CTR;
+
+
+/* SRV_Q_NET_FILE_ENUM */
+typedef struct q_net_file_enum_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* server name */
+
+       uint32 ptr_qual_name;         /* pointer (to qualifier name) */
+       UNISTR2 uni_qual_name;        /* qualifier name "\\qualifier" */
+
+       uint32 ptr_user_name;         /* pointer (to user name) */
+       UNISTR2 uni_user_name;        /* user name */
+
+       uint32 file_level;          /* file level */
+
+       SRV_FILE_INFO_CTR ctr;
+
+       uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+       ENUM_HND enum_hnd;
+
+} SRV_Q_NET_FILE_ENUM;
+
+
+/* SRV_R_NET_FILE_ENUM */
+typedef struct r_net_file_enum_info
+{
+       uint32 file_level;          /* file level */
+
+       SRV_FILE_INFO_CTR ctr;
+
+       uint32 total_entries;                    /* total number of files */
+       ENUM_HND enum_hnd;
+
+       WERROR status;        /* return status */
+
+} SRV_R_NET_FILE_ENUM;
+
+/* SRV_Q_NET_FILE_CLOSE */
+typedef struct q_net_file_close
+{
+       uint32 ptr_srv_name;         /* pointer to server name */
+       UNISTR2 uni_srv_name;        /* server name */
+       
+       uint32 file_id;
+} SRV_Q_NET_FILE_CLOSE;
+
+/* SRV_R_NET_FILE_CLOSE */
+typedef struct r_net_file_close
+{
+       WERROR status;               /* return status */
+} SRV_R_NET_FILE_CLOSE;
+
+/* SRV_INFO_100 */
+typedef struct srv_info_100_info
+{
+       uint32 platform_id;     /* 0x500 */
+       uint32 ptr_name;        /* pointer to server name */
+
+       UNISTR2 uni_name;       /* server name "server" */
+
+} SRV_INFO_100;
+
+/* SRV_INFO_101 */
+typedef struct srv_info_101_info
+{
+       uint32 platform_id;     /* 0x500 */
+       uint32 ptr_name;        /* pointer to server name */
+       uint32 ver_major;       /* 0x4 */
+       uint32 ver_minor;       /* 0x2 */
+       uint32 srv_type;        /* browse etc type */
+       uint32 ptr_comment;     /* pointer to server comment */
+
+       UNISTR2 uni_name;       /* server name "server" */
+       UNISTR2 uni_comment;    /* server comment "samba x.x.x blah" */
+
+} SRV_INFO_101;
+
+/* SRV_INFO_102  */
+typedef struct srv_info_102_info
+{
+       uint32 platform_id;     /* 0x500 */
+       uint32 ptr_name;        /* pointer to server name */
+       uint32 ver_major;       /* 0x4 */
+       uint32 ver_minor;       /* 0x2 */
+       uint32 srv_type;        /* browse etc type */
+       uint32 ptr_comment;     /* pointer to server comment */
+       uint32 users;           /* 0xffff ffff*/
+       uint32 disc;            /* 0xf */
+       uint32 hidden;          /* 0x0 */
+       uint32 announce;        /* 240 */
+       uint32 ann_delta;       /* 3000 */
+       uint32 licenses;        /* 0 */
+       uint32 ptr_usr_path;    /* pointer to user path */
+
+       UNISTR2 uni_name;       /* server name "server" */
+       UNISTR2 uni_comment;    /* server comment "samba x.x.x blah" */
+       UNISTR2 uni_usr_path;   /* "c:\" (eh?) */
+
+} SRV_INFO_102;
+
+
+/* SRV_INFO_CTR */
+typedef struct srv_info_ctr_info
+{
+       uint32 switch_value;         /* switch value */
+       uint32 ptr_srv_ctr;         /* pointer to server info */
+       union
+    {
+               SRV_INFO_102 sv102; /* server info level 102 */
+               SRV_INFO_101 sv101; /* server info level 101 */
+               SRV_INFO_100 sv100; /* server info level 100 */
+
+    } srv;
+
+} SRV_INFO_CTR;
+
+/* SRV_Q_NET_SRV_GET_INFO */
+typedef struct q_net_srv_get_info
+{
+       uint32  ptr_srv_name;
+       UNISTR2 uni_srv_name; /* "\\server" */
+       uint32  switch_value;
+
+} SRV_Q_NET_SRV_GET_INFO;
+
+/* SRV_R_NET_SRV_GET_INFO */
+typedef struct r_net_srv_get_info
+{
+       SRV_INFO_CTR *ctr;
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SRV_GET_INFO;
+
+/* SRV_Q_NET_SRV_SET_INFO */
+typedef struct q_net_srv_set_info
+{
+       uint32  ptr_srv_name;
+       UNISTR2 uni_srv_name; /* "\\server" */
+       uint32  switch_value;
+
+       SRV_INFO_CTR *ctr;
+
+} SRV_Q_NET_SRV_SET_INFO;
+
+
+/* SRV_R_NET_SRV_SET_INFO */
+typedef struct r_net_srv_set_info
+{
+       uint32 switch_value;         /* switch value */
+
+       WERROR status;               /* return status */
+
+} SRV_R_NET_SRV_SET_INFO;
+
+/* SRV_Q_NET_REMOTE_TOD */
+typedef struct q_net_remote_tod
+{
+       uint32  ptr_srv_name;
+       UNISTR2 uni_srv_name; /* "\\server" */
+
+} SRV_Q_NET_REMOTE_TOD;
+
+/* TIME_OF_DAY_INFO */
+typedef struct time_of_day_info
+{
+       uint32  elapsedt;
+       uint32  msecs;
+       uint32  hours;
+       uint32  mins;
+       uint32  secs;
+       uint32  hunds;
+       uint32  zone;
+       uint32  tintervals;
+       uint32  day;
+       uint32  month;
+       uint32  year;
+       uint32  weekday;
+       
+} TIME_OF_DAY_INFO;
+
+/* SRV_R_NET_REMOTE_TOD */
+typedef struct r_net_remote_tod
+{
+       uint32 ptr_srv_tod;         /* pointer to TOD */
+       TIME_OF_DAY_INFO *tod;
+       
+       WERROR status;               /* return status */
+
+} SRV_R_NET_REMOTE_TOD;
+
+/* SRV_Q_NET_FILE_QUERY_SECDESC */
+typedef struct q_net_file_query_secdesc
+{
+       uint32  ptr_srv_name;
+       UNISTR2 uni_srv_name;
+       uint32  ptr_qual_name;
+       UNISTR2 uni_qual_name;
+       UNISTR2 uni_file_name;
+       uint32  unknown1;
+       uint32  unknown2;
+       uint32  unknown3;
+} SRV_Q_NET_FILE_QUERY_SECDESC;
+
+/* SRV_R_NET_FILE_QUERY_SECDESC */
+typedef struct r_net_file_query_secdesc
+{
+       uint32 ptr_response;
+       uint32 size_response;
+       uint32 ptr_secdesc;
+       uint32 size_secdesc;
+       SEC_DESC *sec_desc;
+       WERROR status;
+} SRV_R_NET_FILE_QUERY_SECDESC;
+
+/* SRV_Q_NET_FILE_SET_SECDESC */
+typedef struct q_net_file_set_secdesc
+{
+       uint32  ptr_srv_name;
+       UNISTR2 uni_srv_name;
+       uint32  ptr_qual_name;
+       UNISTR2 uni_qual_name;
+       UNISTR2 uni_file_name;
+       uint32  sec_info;
+       uint32  size_set;
+       uint32  ptr_secdesc;
+       uint32  size_secdesc;
+       SEC_DESC *sec_desc;
+} SRV_Q_NET_FILE_SET_SECDESC;
+
+/* SRV_R_NET_FILE_SET_SECDESC */
+typedef struct r_net_file_set_secdesc
+{
+       WERROR status;
+} SRV_R_NET_FILE_SET_SECDESC;
+
+#endif /* _RPC_SRVSVC_H */
diff --git a/source4/include/rpc_wkssvc.h b/source4/include/rpc_wkssvc.h
new file mode 100644 (file)
index 0000000..adc37c2
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Paul Ashton 1997
+   
+   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.
+*/
+
+#ifndef _RPC_WKS_H /* _RPC_WKS_H */
+#define _RPC_WKS_H 
+
+
+/* wkssvc pipe */
+#define WKS_QUERY_INFO    0x00
+
+
+/* WKS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_wks_query_info_info
+{
+       uint32 ptr_srv_name;         /* pointer (to server name?) */
+       UNISTR2 uni_srv_name;        /* unicode server name starting with '\\' */
+
+       uint16 switch_value;            /* info level 100 (0x64) */
+
+} WKS_Q_QUERY_INFO;
+
+
+/* WKS_INFO_100 - level 100 info */
+typedef struct wks_info_100_info
+{
+       uint32 platform_id;          /* 0x0000 01f4 - unknown */
+       uint32 ptr_compname;       /* pointer to server name */
+       uint32 ptr_lan_grp ;       /* pointer to domain name */
+       uint32 ver_major;          /* 4 - unknown */
+       uint32 ver_minor;          /* 0 - unknown */
+
+       UNISTR2 uni_compname;      /* unicode server name */
+       UNISTR2 uni_lan_grp ;      /* unicode domain name */
+
+} WKS_INFO_100;
+
+
+/* WKS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_wks_query_info_info
+{
+       uint16 switch_value;          /* 100 (0x64) - switch value */
+
+       /* for now, only level 100 is supported.  this should be an enum container */
+       uint32 ptr_1;              /* pointer 1 */
+       WKS_INFO_100 *wks100;      /* workstation info level 100 */
+
+       NTSTATUS status;             /* return status */
+
+} WKS_R_QUERY_INFO;
+
+
+#endif /* _RPC_WKS_H */
+
diff --git a/source4/include/safe_string.h b/source4/include/safe_string.h
new file mode 100644 (file)
index 0000000..431dc40
--- /dev/null
@@ -0,0 +1,99 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Safe string handling routines.
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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.
+*/
+
+#ifndef _SAFE_STRING_H
+#define _SAFE_STRING_H
+
+#ifndef _SPLINT_ /* http://www.splint.org */
+
+/* Some macros to ensure people don't use buffer overflow vulnerable string
+   functions. */
+
+#ifdef bcopy
+#undef bcopy
+#endif /* bcopy */
+#define bcopy(src,dest,size) __ERROR__XX__NEVER_USE_BCOPY___;
+
+#ifdef strcpy
+#undef strcpy
+#endif /* strcpy */
+#define strcpy(dest,src) __ERROR__XX__NEVER_USE_STRCPY___;
+
+#ifdef strcat
+#undef strcat
+#endif /* strcat */
+#define strcat(dest,src) __ERROR__XX__NEVER_USE_STRCAT___;
+
+#ifdef sprintf
+#undef sprintf
+#endif /* sprintf */
+#define sprintf __ERROR__XX__NEVER_USE_SPRINTF__;
+
+#endif /* !_SPLINT_ */
+
+char * __unsafe_string_function_usage_here__(void);
+
+#if 0 && defined __GNUC__ && __GNUC__ >= 2 && defined __OPTIMIZE__
+
+#define pstrcpy(d,s) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcpy((d), (s),sizeof(pstring)-1))
+#define pstrcat(d,s) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcat((d), (s),sizeof(pstring)-1))
+#define fstrcpy(d,s) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcpy((d),(s),sizeof(fstring)-1))
+#define fstrcat(d,s) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcat((d),(s),sizeof(fstring)-1))
+
+#define fstrterminate(d) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : (((d)[sizeof(fstring)-1]) = '\0'))
+#define pstrterminate(d) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : (((d)[sizeof(pstring)-1]) = '\0'))
+
+#define wpstrcpy(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) ? __unsafe_string_function_usage_here__() : safe_strcpy_w((d),(s),sizeof(wpstring)))
+#define wpstrcat(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) ? __unsafe_string_function_usage_here__() : safe_strcat_w((d),(s),sizeof(wpstring)))
+#define wfstrcpy(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) ? __unsafe_string_function_usage_here__() : safe_strcpy_w((d),(s),sizeof(wfstring)))
+#define wfstrcat(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) ? __unsafe_string_function_usage_here__() : safe_strcat_w((d),(s),sizeof(wfstring)))
+
+#else
+
+#define pstrcpy(d,s) safe_strcpy((d), (s),sizeof(pstring)-1)
+#define pstrcat(d,s) safe_strcat((d), (s),sizeof(pstring)-1)
+#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
+#define fstrcat(d,s) safe_strcat((d),(s),sizeof(fstring)-1)
+
+#define fstrterminate(d) (((d)[sizeof(fstring)-1]) = '\0')
+#define pstrterminate(d) (((d)[sizeof(pstring)-1]) = '\0')
+
+#define wpstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wpstring))
+#define wpstrcat(d,s) safe_strcat_w((d),(s),sizeof(wpstring))
+#define wfstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wfstring))
+#define wfstrcat(d,s) safe_strcat_w((d),(s),sizeof(wfstring))
+
+#endif
+
+/* replace some string functions with multi-byte
+   versions */
+#define strlower(s) strlower_m(s)
+#define strupper(s) strupper_m(s)
+
+/* the addition of the DEVELOPER checks in safe_strcpy means we must
+ * update a lot of code. To make this a little easier here are some
+ * functions that provide the lengths with less pain */
+#define pstrcpy_base(dest, src, pstring_base) \
+    safe_strcpy(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1)
+
+#define push_pstring_base(dest, src, pstring_base) \
+    push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE)
+
+#endif
diff --git a/source4/include/sam.h b/source4/include/sam.h
new file mode 100644 (file)
index 0000000..f46a6e7
--- /dev/null
@@ -0,0 +1,238 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM structures
+   Copyright (C) Kai Krueger 2002
+   Copyright (C) Stefan (metze) Metzmacher 2002
+   Copyright (C) Simo Sorce 2002
+   Copyright (C) Andrew Bartlett 2002
+   Copyright (C) Jelmer Vernooij 2002
+   
+   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.
+*/
+
+#ifndef _SAM_H
+#define _SAM_H
+
+/* We want to track down bugs early */
+#if 1
+#define SAM_ASSERT(x) SMB_ASSERT(x)
+#else
+#define SAM_ASSERT(x) while (0) { \
+       if (!(x)) {
+               DEBUG(0, ("SAM_ASSERT failed!\n"))
+               return NT_STATUS_FAIL_CHECK;\
+       } \
+    }
+#endif
+
+
+/* let it be 0 until we have a stable interface --metze */
+#define SAM_INTERFACE_VERSION 0
+
+/* use this inside a passdb module */
+#define SAM_MODULE_VERSIONING_MAGIC \
+int sam_version(void)\
+{\
+       return SAM_INTERFACE_VERSION;\
+}
+
+/* Backend to use by default when no backend was specified */
+#define SAM_DEFAULT_BACKEND "plugin"
+
+typedef struct sam_domain_handle {
+       TALLOC_CTX *mem_ctx;
+       uint32 access_granted;
+       const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */
+       void (*free_fn)(struct sam_domain_handle **);
+       struct domain_data {
+               DOM_SID sid; /*SID of the domain. Should not be changed */
+               char *name; /* Name of the domain */
+               char *servername; /* */
+               NTTIME max_passwordage; /* time till next password expiration */
+               NTTIME min_passwordage; /* time till password can be changed again */
+               NTTIME lockout_duration; /* time till login is allowed again after lockout*/
+               NTTIME reset_count; /* time till bad login counter is reset */
+               uint16 min_passwordlength; /* minimum number of characters for a password */
+               uint16 password_history; /* number of passwords stored in history */
+               uint16 lockout_count; /* number of bad login attempts before lockout */
+               BOOL force_logoff; /* force logoff after logon hours have expired */
+               BOOL login_pwdchange; /* Users need to logon to change their password */
+               uint32 num_accounts; /* number of accounts in the domain */
+               uint32 num_groups; /* number of global groups */
+               uint32 num_aliases; /* number of local groups */
+               uint32 sam_sequence_number; /* global sequence number */
+       } private;
+} SAM_DOMAIN_HANDLE;
+
+typedef struct sam_account_handle {
+       TALLOC_CTX *mem_ctx;
+       uint32 access_granted;
+       const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */
+       void (*free_fn)(struct sam_account_handle **);
+       struct sam_account_data {
+               uint32 init_flag;
+               NTTIME logon_time; /* logon time */
+               NTTIME logoff_time; /* logoff time */
+               NTTIME kickoff_time; /* kickoff time */
+               NTTIME pass_last_set_time; /* password last set time */
+               NTTIME pass_can_change_time; /* password can change time */
+               NTTIME pass_must_change_time; /* password must change time */
+               char * account_name; /* account_name string */
+               SAM_DOMAIN_HANDLE * domain; /* domain of account */
+               char *full_name; /* account's full name string */
+               char *unix_home_dir; /* UNIX home directory string */
+               char *home_dir; /* home directory string */
+               char *dir_drive; /* home directory drive string */
+               char *logon_script; /* logon script string */
+               char *profile_path; /* profile path string */
+               char *acct_desc; /* account description string */
+               char *workstations; /* login from workstations string */
+               char *unknown_str; /* don't know what this is, yet. */
+               char *munged_dial; /* munged path name and dial-back tel number */
+               DOM_SID account_sid; /* Primary Account SID */
+               DOM_SID group_sid; /* Primary Group SID */
+               DATA_BLOB lm_pw; /* .data is Null if no password */
+               DATA_BLOB nt_pw; /* .data is Null if no password */
+               char *plaintext_pw; /* if Null not available */
+               uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+               uint32 unknown_1; /* 0x00ff ffff */
+               uint16 logon_divs; /* 168 - number of hours in a week */
+               uint32 hours_len; /* normally 21 bytes */
+               uint8 hours[MAX_HOURS_LEN];
+               uint32 unknown_2; /* 0x0002 0000 */
+               uint32 unknown_3; /* 0x0000 04ec */
+       } private;
+} SAM_ACCOUNT_HANDLE;
+
+typedef struct sam_group_handle {
+       TALLOC_CTX *mem_ctx;
+       uint32 access_granted;
+       const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */
+       void (*free_fn)(struct sam_group_handle **);
+       struct sam_group_data {
+               char *group_name;
+               char *group_desc;
+               DOM_SID sid;
+               uint16 group_ctrl; /* specifies if the group is a local group or a global group */
+               uint32 num_members;
+       } private;
+} SAM_GROUP_HANDLE;
+
+
+typedef struct sam_group_member {
+       DOM_SID sid; 
+       BOOL group; /* specifies if it is a group or a account */ 
+} SAM_GROUP_MEMBER;
+
+typedef struct sam_account_enum {
+       DOM_SID sid; 
+       char *account_name; 
+       char *full_name; 
+       char *account_desc; 
+       uint16 acct_ctrl; 
+} SAM_ACCOUNT_ENUM;
+
+typedef struct sam_group_enum {
+       DOM_SID sid;
+       char *group_name;
+       char *group_desc;
+       uint16 group_ctrl;
+} SAM_GROUP_ENUM;
+
+
+/* bits for group_ctrl: to spezify if the group is global group or alias */
+#define GCB_LOCAL_GROUP                0x0001
+#define GCB_ALIAS_GROUP                (GCB_LOCAL_GROUP |GCB_BUILTIN)
+#define GCB_GLOBAL_GROUP       0x0002
+#define GCB_BUILTIN            0x1000
+
+typedef struct sam_context 
+{
+       struct sam_methods *methods;
+       TALLOC_CTX *mem_ctx;
+       
+       void (*free_fn)(struct sam_context **);
+} SAM_CONTEXT;
+
+typedef struct sam_methods 
+{
+       struct sam_context              *parent;
+       struct sam_methods              *next;
+       struct sam_methods              *prev;
+       const char                      *backendname;
+       const char                      *domain_name;
+       DOM_SID                         domain_sid;
+       void                            *private_data;
+       
+       /* General API */
+       
+       NTSTATUS (*sam_get_sec_desc) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd);
+       NTSTATUS (*sam_set_sec_desc) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd);
+       
+       NTSTATUS (*sam_lookup_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type);
+       NTSTATUS (*sam_lookup_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const char *name, DOM_SID *sid, uint32 *type);
+       
+       /* Domain API */
+
+       NTSTATUS (*sam_update_domain) (const struct sam_methods *, const SAM_DOMAIN_HANDLE *domain);
+       NTSTATUS (*sam_get_domain_handle) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, SAM_DOMAIN_HANDLE **domain);
+
+       /* Account API */
+
+       NTSTATUS (*sam_create_account) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account);
+       NTSTATUS (*sam_add_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account);
+       NTSTATUS (*sam_update_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account);
+       NTSTATUS (*sam_delete_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account);
+       NTSTATUS (*sam_enum_accounts) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts);
+
+       NTSTATUS (*sam_get_account_by_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account);
+       NTSTATUS (*sam_get_account_by_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_ACCOUNT_HANDLE **account);
+
+       /* Group API */
+
+       NTSTATUS (*sam_create_group) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group);
+       NTSTATUS (*sam_add_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group);
+       NTSTATUS (*sam_update_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group);
+       NTSTATUS (*sam_delete_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group);
+       NTSTATUS (*sam_enum_groups) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups);
+       NTSTATUS (*sam_get_group_by_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group);
+       NTSTATUS (*sam_get_group_by_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group);
+
+       NTSTATUS (*sam_add_member_to_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member);
+       NTSTATUS (*sam_delete_member_from_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member);
+       NTSTATUS (*sam_enum_groupmembers) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members);
+
+       NTSTATUS (*sam_get_groups_of_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups);
+
+       void (*free_private_data)(void **);
+} SAM_METHODS;
+
+typedef NTSTATUS (*sam_init_function)(SAM_METHODS *, const char *);
+
+struct sam_init_function_entry {
+       char *module_name;
+       /* Function to create a member of the sam_methods list */
+       sam_init_function init;
+};
+
+typedef struct sam_backend_entry {
+       char    *module_name;
+       char    *module_params;
+       char    *domain_name;
+       DOM_SID *domain_sid;
+} SAM_BACKEND_ENTRY;
+
+
+#endif /* _SAM_H */
diff --git a/source4/include/secrets.h b/source4/include/secrets.h
new file mode 100644 (file)
index 0000000..183b29d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * secrets.tdb file format info
+ * Copyright (C) Andrew Tridgell              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.  
+ */
+
+#ifndef _SECRETS_H
+#define _SECRETS_H
+
+/* the first one is for the hashed password (NT4 style) the latter
+   for plaintext (ADS)
+*/
+#define SECRETS_MACHINE_ACCT_PASS "SECRETS/$MACHINE.ACC"
+#define SECRETS_MACHINE_PASSWORD "SECRETS/MACHINE_PASSWORD"
+
+/* this one is for storing trusted domain account password */
+#define SECRETS_DOMTRUST_ACCT_PASS "SECRETS/$DOMTRUST.ACC"
+
+/* The domain sid and our sid are stored here even though they aren't
+   really secret. */
+#define SECRETS_DOMAIN_SID    "SECRETS/SID"
+#define SECRETS_SAM_SID       "SAM/SID"
+
+/* The domain GUID and server GUID (NOT the same) are also not secret */
+#define SECRETS_DOMAIN_GUID   "SECRETS/DOMGUID"
+#define SECRETS_SERVER_GUID   "SECRETS/GUID"
+
+#define SECRETS_LDAP_BIND_PW "SECRETS/LDAP_BIND_PW"
+
+/* Authenticated user info is stored in secrets.tdb under these keys */
+
+#define SECRETS_AUTH_USER      "SECRETS/AUTH_USER"
+#define SECRETS_AUTH_DOMAIN      "SECRETS/AUTH_DOMAIN"
+#define SECRETS_AUTH_PASSWORD  "SECRETS/AUTH_PASSWORD"
+
+/* structure for storing machine account password
+   (ie. when samba server is member of a domain */
+struct machine_acct_pass {
+       uint8 hash[16];
+       time_t mod_time;
+};
+
+/*
+ * storage structure for trusted domain
+ */
+struct trusted_dom_pass {
+       size_t uni_name_len;
+       smb_ucs2_t uni_name[32]; /* unicode domain name */
+       size_t pass_len;
+       fstring pass;           /* trust relationship's password */
+       time_t mod_time;
+       DOM_SID domain_sid;     /* remote domain's sid */
+};
+
+/*
+ * trusted domain entry/entries returned by secrets_get_trusted_domains
+ * (used in _lsa_enum_trust_dom call)
+ */
+typedef struct trustdom {
+       smb_ucs2_t *name;
+       DOM_SID sid;
+} TRUSTDOM;
+
+
+#endif /* _SECRETS_H */
diff --git a/source4/include/session.h b/source4/include/session.h
new file mode 100644 (file)
index 0000000..f613afe
--- /dev/null
@@ -0,0 +1,40 @@
+/* 
+   Unix SMB/CIFS implementation.
+   session handling for recording currently vailid vuids
+   Copyright (C) tridge@samba.org 2001
+   Copyright (C) Andew Bartlett <abartlet@samba.org> 2001
+   
+   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.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+   and is yielded when the corresponding vuid is destroyed.
+
+   sessions are used to populate utmp and PAM session structures
+*/
+
+struct sessionid {
+       uid_t uid;
+       gid_t gid;
+       fstring username;
+       fstring hostname;
+       fstring netbios_name;
+       fstring remote_machine;
+       fstring id_str;
+       uint32  id_num;
+       uint32  pid;
+       fstring ip_addr;
+};
+
diff --git a/source4/include/smb.h b/source4/include/smb.h
new file mode 100644 (file)
index 0000000..682d392
--- /dev/null
@@ -0,0 +1,1363 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup, plus a whole lot more.
+   
+   Copyright (C) Andrew Tridgell              1992-2000
+   Copyright (C) John H Terpstra              1996-2002
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Paul Ashton                  1998-2000
+   Copyright (C) Simo Sorce                   2001-2002
+   Copyright (C) Martin Pool                 2002
+   
+   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.
+*/
+
+#ifndef _SMB_H
+#define _SMB_H
+
+#define NMB_PORT 137
+#define DGRAM_PORT 138
+#define SMB_PORT1 445
+#define SMB_PORT2 139
+#define SMB_PORTS "445 139"
+
+#define False (0)
+#define True (1)
+#define Auto (2)
+
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL       /* So we don't typedef BOOL again in vfs.h */
+#endif
+
+#define SIZEOFWORD 2
+
+#ifndef DEF_CREATE_MASK
+#define DEF_CREATE_MASK (0755)
+#endif
+
+/* string manipulation flags - see clistr.c and srvstr.c */
+#define STR_TERMINATE 1
+#define STR_UPPER 2
+#define STR_ASCII 4
+#define STR_UNICODE 8
+#define STR_NOALIGN 16
+#define STR_NO_RANGE_CHECK 32
+#define STR_LEN8BIT 64
+#define STR_TERMINATE_ASCII 128 /* only terminate if ascii */
+#define STR_LEN_NOTERM 256 /* the length field is the unterminated length */
+
+/* Debugging stuff */
+#include "debug.h"
+
+/* types of socket errors */
+enum socket_error {SOCKET_READ_TIMEOUT,
+                  SOCKET_READ_EOF,
+                  SOCKET_READ_ERROR,
+                  SOCKET_WRITE_ERROR,
+                  SOCKET_READ_BAD_SIG};
+
+/* deny modes */
+#define DENY_DOS 0
+#define DENY_ALL 1
+#define DENY_WRITE 2
+#define DENY_READ 3
+#define DENY_NONE 4
+#define DENY_FCB 7
+
+/* open modes */
+#define DOS_OPEN_RDONLY 0
+#define DOS_OPEN_WRONLY 1
+#define DOS_OPEN_RDWR 2
+#define DOS_OPEN_FCB 0xF
+
+
+/**********************************/
+/* SMBopen field definitions      */
+#define OPEN_FLAGS_DENY_MASK  0x70
+#define OPEN_FLAGS_DENY_DOS   0x00
+#define OPEN_FLAGS_DENY_ALL   0x10
+#define OPEN_FLAGS_DENY_WRITE 0x20
+#define OPEN_FLAGS_DENY_READ  0x30
+#define OPEN_FLAGS_DENY_NONE  0x40
+
+#define OPEN_FLAGS_MODE_MASK  0x0F
+#define OPEN_FLAGS_OPEN_READ     0
+#define OPEN_FLAGS_OPEN_WRITE    1
+#define OPEN_FLAGS_OPEN_RDWR     2
+#define OPEN_FLAGS_FCB        0xFF
+
+
+/**********************************/
+/* SMBopenX field definitions     */
+
+/* OpenX Flags field. */
+#define OPENX_FLAGS_ADDITIONAL_INFO      0x01
+#define OPENX_FLAGS_REQUEST_OPLOCK       0x02
+#define OPENX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define OPENX_FLAGS_EA_LEN               0x08
+#define OPENX_FLAGS_EXTENDED_RETURN      0x10
+
+/* desired access (open_mode), split info 4 4-bit nibbles */
+#define OPENX_MODE_ACCESS_MASK   0x000F
+#define OPENX_MODE_ACCESS_READ   0x0000
+#define OPENX_MODE_ACCESS_WRITE  0x0001
+#define OPENX_MODE_ACCESS_RDWR   0x0002
+#define OPENX_MODE_ACCESS_EXEC   0x0003
+#define OPENX_MODE_ACCESS_FCB    0x000F
+
+#define OPENX_MODE_DENY_SHIFT    4
+#define OPENX_MODE_DENY_MASK     (0xF        << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_DOS      (DENY_DOS   << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_ALL      (DENY_ALL   << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_WRITE    (DENY_WRITE << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_READ     (DENY_READ  << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_NONE     (DENY_NONE  << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_FCB      (0xF        << OPENX_MODE_DENY_SHIFT)
+
+#define OPENX_MODE_LOCALITY_MASK 0x0F00 /* what does this do? */
+
+#define OPENX_MODE_NO_CACHE      0x1000
+#define OPENX_MODE_WRITE_THRU    0x4000
+
+/* open function values */
+#define OPENX_OPEN_FUNC_MASK  0x3
+#define OPENX_OPEN_FUNC_FAIL  0x0
+#define OPENX_OPEN_FUNC_OPEN  0x1
+#define OPENX_OPEN_FUNC_TRUNC 0x2
+
+/* The above can be OR'ed with... */
+#define OPENX_OPEN_FUNC_CREATE 0x10
+
+/* openx action in reply */
+#define OPENX_ACTION_EXISTED    1
+#define OPENX_ACTION_CREATED    2
+#define OPENX_ACTION_TRUNCATED  3
+
+
+/**********************************/
+/* SMBntcreateX field definitions */
+
+/* ntcreatex flags field. */
+#define NTCREATEX_FLAGS_REQUEST_OPLOCK       0x02
+#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define NTCREATEX_FLAGS_OPEN_DIRECTORY       0x08
+#define NTCREATEX_FLAGS_EXTENDED             0x10
+
+/* the ntcreatex access_mask field 
+   this is split into 4 pieces
+   AAAABBBBCCCCCCCCDDDDDDDDDDDDDDDD
+   A -> GENERIC_RIGHT_*
+   B -> SEC_RIGHT_*
+   C -> STD_RIGHT_*
+   D -> SA_RIGHT_*
+   
+   which set of SA_RIGHT_* bits is applicable depends on the type
+   of object.
+*/
+
+
+
+/* ntcreatex share_access field */
+#define NTCREATEX_SHARE_ACCESS_NONE   0
+#define NTCREATEX_SHARE_ACCESS_READ   1
+#define NTCREATEX_SHARE_ACCESS_WRITE  2
+#define NTCREATEX_SHARE_ACCESS_DELETE 4
+
+/* ntcreatex open_disposition field */
+#define NTCREATEX_DISP_SUPERSEDE 0     /* supersede existing file (if it exists) */
+#define NTCREATEX_DISP_OPEN 1          /* if file exists open it, else fail */
+#define NTCREATEX_DISP_CREATE 2        /* if file exists fail, else create it */
+#define NTCREATEX_DISP_OPEN_IF 3       /* if file exists open it, else create it */
+#define NTCREATEX_DISP_OVERWRITE 4     /* if exists overwrite, else fail */
+#define NTCREATEX_DISP_OVERWRITE_IF 5  /* if exists overwrite, else create */
+
+/* ntcreatex create_options field */
+#define NTCREATEX_OPTIONS_DIRECTORY            0x0001
+#define NTCREATEX_OPTIONS_WRITE_THROUGH        0x0002
+#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY      0x0004
+#define NTCREATEX_OPTIONS_SYNC_ALERT          0x0010
+#define NTCREATEX_OPTIONS_ASYNC_ALERT         0x0020
+#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE   0x0040
+#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE      0x0200
+#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400
+#define NTCREATEX_OPTIONS_RANDOM_ACCESS        0x0800
+#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE      0x1000
+#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID      0x2000
+
+/* ntcreatex impersonation field */
+#define NTCREATEX_IMPERSONATION_ANONYMOUS      0
+#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1
+#define NTCREATEX_IMPERSONATION_IMPERSONATION  2
+#define NTCREATEX_IMPERSONATION_DELEGATION     3
+
+/* ntcreatex security flags bit field */
+#define NTCREATEX_SECURITY_DYNAMIC             1
+#define NTCREATEX_SECURITY_ALL                 2
+
+/* ntcreatex create_action in reply */
+#define NTCREATEX_ACTION_EXISTED     1
+#define NTCREATEX_ACTION_CREATED     2
+#define NTCREATEX_ACTION_TRUNCATED   3
+/* the value 5 can also be returned when you try to create a directory with
+   incorrect parameters - what does it mean? maybe created temporary file? */
+#define NTCREATEX_ACTION_UNKNOWN 5
+
+
+
+/* share types */
+#define STYPE_DISKTREE  0      /* Disk drive */
+#define STYPE_PRINTQ    1      /* Spooler queue */
+#define STYPE_DEVICE    2      /* Serial device */
+#define STYPE_IPC       3      /* Interprocess communication (IPC) */
+#define STYPE_HIDDEN    0x80000000 /* share is a hidden one (ends with $) */
+
+#include "doserr.h"
+
+/* this is a trade with jeremy - I agreed to use uint_t instead of
+ * bare unsigned if he agreed to not use non-braced if statements
+ * (13/4/2003 - train to gottenginen) */
+typedef unsigned int uint_t;
+
+/*
+ * SMB UCS2 (16-bit unicode) internal type.
+ */
+
+typedef uint16 smb_ucs2_t;
+
+/* ucs2 string types. */
+typedef smb_ucs2_t wpstring[PSTRING_LEN];
+typedef smb_ucs2_t wfstring[FSTRING_LEN];
+
+/* This error code can go into the client smb_rw_error. */
+#define WRITE_ERROR 4
+
+#ifdef WORDS_BIGENDIAN
+#define UCS2_SHIFT 8
+#else
+#define UCS2_SHIFT 0
+#endif
+
+/* turn a 7 bit character into a ucs2 character */
+#define UCS2_CHAR(c) ((c) << UCS2_SHIFT)
+
+/* pipe string names */
+#define PIPE_LANMAN   "\\PIPE\\LANMAN"
+#define PIPE_SRVSVC   "\\PIPE\\srvsvc"
+#define PIPE_SAMR     "\\PIPE\\samr"
+#define PIPE_WINREG   "\\PIPE\\winreg"
+#define PIPE_WKSSVC   "\\PIPE\\wkssvc"
+#define PIPE_NETLOGON "\\PIPE\\NETLOGON"
+#define PIPE_NTLSA    "\\PIPE\\ntlsa"
+#define PIPE_NTSVCS   "\\PIPE\\ntsvcs"
+#define PIPE_LSASS    "\\PIPE\\lsass"
+#define PIPE_LSARPC   "\\PIPE\\lsarpc"
+#define PIPE_SPOOLSS  "\\PIPE\\spoolss"
+#define PIPE_NETDFS   "\\PIPE\\netdfs"
+
+#define PI_LSARPC              0
+#define PI_LSARPC_DS           1
+#define PI_SAMR                        2
+#define PI_NETLOGON            3
+#define PI_SRVSVC              4
+#define PI_WKSSVC              5
+#define PI_WINREG              6
+#define PI_SPOOLSS             7
+#define PI_NETDFS              8
+#define PI_MAX_PIPES           9
+
+/* Allowable account control bits */
+#define ACB_DISABLED   0x0001  /* 1 = User account disabled */
+#define ACB_HOMDIRREQ  0x0002  /* 1 = Home directory required */
+#define ACB_PWNOTREQ   0x0004  /* 1 = User password not required */
+#define ACB_TEMPDUP    0x0008  /* 1 = Temporary duplicate account */
+#define ACB_NORMAL     0x0010  /* 1 = Normal user account */
+#define ACB_MNS        0x0020  /* 1 = MNS logon user account */
+#define ACB_DOMTRUST   0x0040  /* 1 = Interdomain trust account */
+#define ACB_WSTRUST    0x0080  /* 1 = Workstation trust account */
+#define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
+#define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
+#define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
+#define MAX_HOURS_LEN 32
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15 /* max sub authorities in a SID */
+#endif
+
+/* SID Types */
+enum SID_NAME_USE
+{
+       SID_NAME_USE_NONE = 0,/* NOTUSED */
+       SID_NAME_USER    = 1, /* user */
+       SID_NAME_DOM_GRP = 2, /* domain group */
+       SID_NAME_DOMAIN  = 3, /* domain: don't know what this is */
+       SID_NAME_ALIAS   = 4, /* local group */
+       SID_NAME_WKN_GRP = 5, /* well-known group */
+       SID_NAME_DELETED = 6, /* deleted account: needed for c2 rating */
+       SID_NAME_INVALID = 7, /* invalid account */
+       SID_NAME_UNKNOWN = 8  /* oops. */
+};
+
+/**
+ * @brief Security Identifier
+ *
+ * @sa http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/accctrl_38yn.asp
+ **/
+typedef struct sid_info
+{
+  uint8  sid_rev_num;             /**< SID revision number */
+  uint8  num_auths;               /**< Number of sub-authorities */
+  uint8  id_auth[6];              /**< Identifier Authority */
+  /*
+   *  Pointer to sub-authorities.
+   *
+   * @note The values in these uint32's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  uint32 sub_auths[MAXSUBAUTHS];  
+
+} DOM_SID;
+
+/*
+ * The complete list of SIDS belonging to this user.
+ * Created when a vuid is registered.
+ * The definition of the user_sids array is as follows :
+ *
+ * token->user_sids[0] = primary user SID.
+ * token->user_sids[1] = primary group SID.
+ * token->user_sids[2..num_sids] = supplementary group SIDS.
+ */
+
+#define PRIMARY_USER_SID_INDEX 0
+#define PRIMARY_GROUP_SID_INDEX 1
+
+typedef struct _nt_user_token {
+       size_t num_sids;
+       DOM_SID *user_sids;
+} NT_USER_TOKEN;
+
+/*** query a local group, get a list of these: shows who is in that group ***/
+
+/* local group member info */
+typedef struct local_grp_member_info
+{
+       DOM_SID sid    ; /* matches with name */
+       uint8   sid_use; /* usr=1 grp=2 dom=3 alias=4 wkng=5 del=6 inv=7 unk=8 */
+       fstring name   ; /* matches with sid: must be of the form "DOMAIN\account" */
+
+} LOCAL_GRP_MEMBER;
+
+/* enumerate these to get list of local groups */
+
+/* local group info */
+typedef struct local_grp_info
+{
+       fstring name;
+       fstring comment;
+
+} LOCAL_GRP;
+
+/*** enumerate these to get list of domain groups ***/
+
+/* domain group member info */
+typedef struct domain_grp_info
+{
+       fstring name;
+       fstring comment;
+       uint32  rid; /* group rid */
+       uint8   attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP;
+
+/*** query a domain group, get a list of these: shows who is in that group ***/
+
+/* domain group info */
+typedef struct domain_grp_member_info
+{
+       fstring name;
+       uint8   attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP_MEMBER;
+
+/* 32 bit time (sec) since 01jan1970 - cifs6.txt, section 3.5, page 30 */
+typedef struct time_info
+{
+  uint32 time;
+} UTIME;
+
+/* used to hold an arbitrary blob of data */
+typedef struct data_blob {
+       uint8 *data;
+       size_t length;
+       void (*free)(struct data_blob *data_blob);
+} DATA_BLOB;
+
+/*
+ * Structure used to keep directory state information around.
+ * Used in NT change-notify code.
+ */
+
+typedef struct
+{
+  time_t modify_time;
+  time_t status_time;
+} dir_status_struct;
+
+struct vuid_cache {
+  unsigned int entries;
+  uint16 list[VUID_CACHE_SIZE];
+};
+
+/* Include VFS stuff */
+
+#include "smb_acls.h"
+#include "enums.h"
+#include "events.h"
+#include "context.h"
+#include "smb_interfaces.h"
+#include "ntvfs.h"
+
+typedef struct smb_vfs_handle_struct
+{
+    void *data;
+    /* Handle on dlopen() call */
+    void *handle;
+    struct smb_vfs_handle_struct  *next, *prev;
+    
+} smb_vfs_handle_struct;
+
+struct tcon_context {
+       struct tcon_context *next, *prev;
+
+       /* the server context that this was created on */
+       struct server_context *smb;
+
+       /* a talloc context for all data in this structure */
+       TALLOC_CTX *mem_ctx;
+
+       /* a private structure used by the active NTVFS backend */
+       void *ntvfs_private;
+
+       uint16 cnum; /* an index passed over the wire (the TID) */
+       int service;
+       enum ntvfs_type type;
+       BOOL read_only;
+       BOOL admin_user;
+
+       /* the NTVFS operations - see source/ntvfs/ and include/ntvfs.h for details */
+       struct ntvfs_ops *ntvfs_ops;
+
+       /* the reported filesystem type */
+       char *fs_type;
+
+       /* the reported device type */
+       char *dev_type;
+};
+
+struct current_user
+{
+       struct tcon_context *conn;
+       uint16 vuid;
+       uid_t uid;
+       gid_t gid;
+       int ngroups;
+       gid_t *groups;
+       NT_USER_TOKEN *nt_user_token;
+};
+
+/* Defines for the sent_oplock_break field above. */
+#define NO_BREAK_SENT 0
+#define EXCLUSIVE_BREAK_SENT 1
+#define LEVEL_II_BREAK_SENT 2
+
+typedef struct userdom_struct {
+       fstring smb_name; /* user name from the client */
+       fstring unix_name; /* unix user name of a validated user */
+       fstring full_name; /* to store full name (such as "Joe Bloggs") from gecos field of password file */
+       fstring domain; /* domain that the client specified */
+} userdom_struct;
+
+/* used for server information: client, nameserv and ipc */
+struct server_info_struct
+{
+  fstring name;
+  uint32 type;
+  fstring comment;
+  fstring domain; /* used ONLY in ipc.c NOT namework.c */
+  BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
+};
+
+
+/* used for network interfaces */
+struct interface
+{
+       struct interface *next, *prev;
+       struct in_addr ip;
+       struct in_addr bcast;
+       struct in_addr nmask;
+};
+
+/* struct returned by get_share_modes */
+typedef struct {
+       pid_t pid;
+       uint16 op_port;
+       uint16 op_type;
+       int share_mode;
+       uint32 desired_access;
+       struct timeval time;
+       SMB_DEV_T dev;
+       SMB_INO_T inode;
+       unsigned long share_file_id;
+} share_mode_entry;
+
+
+#define SHAREMODE_FN_CAST() \
+       void (*)(share_mode_entry *, char*)
+
+#define SHAREMODE_FN(fn) \
+       void (*fn)(share_mode_entry *, char*)
+
+#define NT_HASH_LEN 16
+#define LM_HASH_LEN 16
+
+/*
+ * bit flags representing initialized fields in SAM_ACCOUNT
+ */
+enum pdb_elements {
+       PDB_UNINIT,
+       PDB_UID,
+       PDB_GID,
+       PDB_SMBHOME,
+       PDB_PROFILE,
+       PDB_DRIVE,
+       PDB_LOGONSCRIPT,
+       PDB_LOGONTIME,
+       PDB_LOGOFFTIME,
+       PDB_KICKOFFTIME,
+       PDB_CANCHANGETIME,
+       PDB_MUSTCHANGETIME,
+       PDB_PLAINTEXT_PW,
+       PDB_USERNAME,
+       PDB_FULLNAME,
+       PDB_DOMAIN,
+       PDB_NTUSERNAME,
+       PDB_HOURSLEN,
+       PDB_LOGONDIVS,
+       PDB_USERSID,
+       PDB_GROUPSID,
+       PDB_ACCTCTRL,
+       PDB_PASSLASTSET,
+       PDB_UNIXHOMEDIR,
+       PDB_ACCTDESC,
+       PDB_WORKSTATIONS,
+       PDB_UNKNOWNSTR,
+       PDB_MUNGEDDIAL,
+       PDB_HOURS,
+       PDB_UNKNOWN3,
+       PDB_UNKNOWN5,
+       PDB_UNKNOWN6,
+       PDB_LMPASSWD,
+       PDB_NTPASSWD,
+
+       /* this must be the last element */
+       PDB_COUNT,
+};
+
+enum pdb_value_state {
+       PDB_DEFAULT=0,
+       PDB_SET,
+       PDB_CHANGED
+};
+
+#define IS_SAM_UNIX_USER(x) \
+       (( pdb_get_init_flags(x, PDB_UID) != PDB_DEFAULT ) \
+        && ( pdb_get_init_flags(x,PDB_GID) != PDB_DEFAULT ))
+
+#define IS_SAM_SET(x, flag)    (pdb_get_init_flags(x, flag) == PDB_SET)
+#define IS_SAM_CHANGED(x, flag)        (pdb_get_init_flags(x, flag) == PDB_CHANGED)
+#define IS_SAM_DEFAULT(x, flag)        (pdb_get_init_flags(x, flag) == PDB_DEFAULT)
+               
+typedef struct sam_passwd
+{
+       TALLOC_CTX *mem_ctx;
+       
+       void (*free_fn)(struct sam_passwd **);
+
+       struct pdb_methods *methods;
+
+       struct user_data {
+               /* initiailization flags */
+               struct bitmap *change_flags;
+               struct bitmap *set_flags;
+
+               time_t logon_time;            /* logon time */
+               time_t logoff_time;           /* logoff time */
+               time_t kickoff_time;          /* kickoff time */
+               time_t pass_last_set_time;    /* password last set time */
+               time_t pass_can_change_time;  /* password can change time */
+               time_t pass_must_change_time; /* password must change time */
+               
+               const char * username;     /* UNIX username string */
+               const char * domain;       /* Windows Domain name */
+               const char * nt_username;  /* Windows username string */
+               const char * full_name;    /* user's full name string */
+               const char * unix_home_dir;     /* UNIX home directory string */
+               const char * home_dir;     /* home directory string */
+               const char * dir_drive;    /* home directory drive string */
+               const char * logon_script; /* logon script string */
+               const char * profile_path; /* profile path string */
+               const char * acct_desc  ;  /* user description string */
+               const char * workstations; /* login from workstations string */
+               const char * unknown_str ; /* don't know what this is, yet. */
+               const char * munged_dial ; /* munged path name and dial-back tel number */
+               
+               uid_t uid;          /* this is a unix uid_t */
+               gid_t gid;          /* this is a unix gid_t */
+               DOM_SID user_sid;    /* Primary User SID */
+               DOM_SID group_sid;   /* Primary Group SID */
+               
+               DATA_BLOB lm_pw; /* .data is Null if no password */
+               DATA_BLOB nt_pw; /* .data is Null if no password */
+               char* plaintext_pw; /* is Null if not available */
+               
+               uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+               uint32 unknown_3; /* 0x00ff ffff */
+               
+               uint16 logon_divs; /* 168 - number of hours in a week */
+               uint32 hours_len; /* normally 21 bytes */
+               uint8 hours[MAX_HOURS_LEN];
+               
+               uint32 unknown_5; /* 0x0002 0000 */
+               uint32 unknown_6; /* 0x0000 04ec */
+       } private;
+
+       /* Lets see if the remaining code can get the hint that you
+          are meant to use the pdb_...() functions. */
+       
+} SAM_ACCOUNT;
+
+/*
+ * Flags for account policy.
+ */
+#define AP_MIN_PASSWORD_LEN            1
+#define AP_PASSWORD_HISTORY            2
+#define AP_USER_MUST_LOGON_TO_CHG_PASS 3
+#define AP_MAX_PASSWORD_AGE            4
+#define AP_MIN_PASSWORD_AGE            5
+#define AP_LOCK_ACCOUNT_DURATION       6
+#define AP_RESET_COUNT_TIME            7
+#define AP_BAD_ATTEMPT_LOCKOUT         8
+#define AP_TIME_TO_LOGOUT              9
+
+
+/*
+ * Flags for local user manipulation.
+ */
+
+#define LOCAL_ADD_USER 0x1
+#define LOCAL_DELETE_USER 0x2
+#define LOCAL_DISABLE_USER 0x4
+#define LOCAL_ENABLE_USER 0x8
+#define LOCAL_TRUST_ACCOUNT 0x10
+#define LOCAL_SET_NO_PASSWORD 0x20
+#define LOCAL_SET_PASSWORD 0x40
+#define LOCAL_SET_LDAP_ADMIN_PW 0x80
+#define LOCAL_INTERDOM_ACCOUNT 0x100
+#define LOCAL_AM_ROOT 0x200  /* Act as root */
+
+/* key and data in the connections database - used in smbstatus and smbd */
+struct connections_key {
+       pid_t pid;
+       int cnum;
+       fstring name;
+};
+
+struct connections_data {
+       int magic;
+       pid_t pid;
+       int cnum;
+       uid_t uid;
+       gid_t gid;
+       char name[24];
+       char addr[24];
+       char machine[FSTRING_LEN];
+       time_t start;
+       uint32 bcast_msg_flags;
+};
+
+/* the following are used by loadparm for option lists */
+typedef enum
+{
+  P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST,
+  P_STRING,P_USTRING,P_ENUM,P_SEP
+} parm_type;
+
+typedef enum
+{
+  P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
+} parm_class;
+
+struct enum_list {
+       int value;
+       const char *name;
+};
+
+struct parm_struct
+{
+       const char *label;
+       parm_type type;
+       parm_class class;
+       void *ptr;
+       BOOL (*special)(const char *, char **);
+       const struct enum_list *enum_list;
+       unsigned flags;
+       union {
+               BOOL bvalue;
+               int ivalue;
+               char *svalue;
+               char cvalue;
+               char **lvalue;
+       } def;
+};
+
+struct bitmap {
+       uint32 *b;
+       unsigned int n;
+};
+
+#define FLAG_BASIC     0x0001 /* fundamental options */
+#define FLAG_SHARE     0x0002 /* file sharing options */
+#define FLAG_PRINT     0x0004 /* printing options */
+#define FLAG_GLOBAL    0x0008 /* local options that should be globally settable in SWAT */
+#define FLAG_WIZARD    0x0010 /* Parameters that the wizard will operate on */
+#define FLAG_ADVANCED  0x0020 /* Parameters that the wizard will operate on */
+#define FLAG_DEVELOPER         0x0040 /* Parameters that the wizard will operate on */
+#define FLAG_DEPRECATED 0x1000 /* options that should no longer be used */
+#define FLAG_HIDE      0x2000 /* options that should be hidden in SWAT */
+#define FLAG_DOS_STRING 0x4000 /* convert from UNIX to DOS codepage when reading this string. */
+#define FLAG_CMDLINE    0x8000 /* this option was set from the command line */
+
+#ifndef LOCKING_VERSION
+#define LOCKING_VERSION 4
+#endif /* LOCKING_VERSION */
+
+
+/* the basic packet size, assuming no words or bytes. Does not include the NBT header */
+#define MIN_SMB_SIZE 35
+
+/* when using NBT encapsulation every packet has a 4 byte header */
+#define NBT_HDR_SIZE 4
+
+/* offsets into message header for common items - NOTE: These have
+   changed from being offsets from the base of the NBT packet to the base of the SMB packet.
+   this has reduced all these values by 4
+*/
+#define HDR_COM 4
+#define HDR_RCLS 5
+#define HDR_REH 6
+#define HDR_ERR 7
+#define HDR_FLG 9
+#define HDR_FLG2 10
+#define HDR_PIDHIGH 12
+#define HDR_SS_FIELD 14
+#define HDR_TID 24
+#define HDR_PID 26
+#define HDR_UID 28
+#define HDR_MID 30
+#define HDR_WCT 32
+#define HDR_VWV 33
+
+
+/* types of buffers in core SMB protocol */
+#define SMB_DATA_BLOCK 0x1
+#define SMB_ASCII4     0x4
+
+
+/* flag defines. CIFS spec 3.1.1 */
+#define FLAG_SUPPORT_LOCKREAD       0x01
+#define FLAG_CLIENT_BUF_AVAIL       0x02
+#define FLAG_RESERVED               0x04
+#define FLAG_CASELESS_PATHNAMES     0x08
+#define FLAG_CANONICAL_PATHNAMES    0x10
+#define FLAG_REQUEST_OPLOCK         0x20
+#define FLAG_REQUEST_BATCH_OPLOCK   0x40
+#define FLAG_REPLY                  0x80
+
+/* the complete */
+#define SMBmkdir      0x00   /* create directory */
+#define SMBrmdir      0x01   /* delete directory */
+#define SMBopen       0x02   /* open file */
+#define SMBcreate     0x03   /* create file */
+#define SMBclose      0x04   /* close file */
+#define SMBflush      0x05   /* flush file */
+#define SMBunlink     0x06   /* delete file */
+#define SMBmv         0x07   /* rename file */
+#define SMBgetatr     0x08   /* get file attributes */
+#define SMBsetatr     0x09   /* set file attributes */
+#define SMBread       0x0A   /* read from file */
+#define SMBwrite      0x0B   /* write to file */
+#define SMBlock       0x0C   /* lock byte range */
+#define SMBunlock     0x0D   /* unlock byte range */
+#define SMBctemp      0x0E   /* create temporary file */
+#define SMBmknew      0x0F   /* make new file */
+#define SMBchkpth     0x10   /* check directory path */
+#define SMBexit       0x11   /* process exit */
+#define SMBlseek      0x12   /* seek */
+#define SMBtcon       0x70   /* tree connect */
+#define SMBtconX      0x75   /* tree connect and X*/
+#define SMBtdis       0x71   /* tree disconnect */
+#define SMBnegprot    0x72   /* negotiate protocol */
+#define SMBdskattr    0x80   /* get disk attributes */
+#define SMBsearch     0x81   /* search directory */
+#define SMBsplopen    0xC0   /* open print spool file */
+#define SMBsplwr      0xC1   /* write to print spool file */
+#define SMBsplclose   0xC2   /* close print spool file */
+#define SMBsplretq    0xC3   /* return print queue */
+#define SMBsends      0xD0   /* send single block message */
+#define SMBsendb      0xD1   /* send broadcast message */
+#define SMBfwdname    0xD2   /* forward user name */
+#define SMBcancelf    0xD3   /* cancel forward */
+#define SMBgetmac     0xD4   /* get machine name */
+#define SMBsendstrt   0xD5   /* send start of multi-block message */
+#define SMBsendend    0xD6   /* send end of multi-block message */
+#define SMBsendtxt    0xD7   /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread      0x13   /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* write then range then unlock it */
+#define SMBreadbraw   0x1a  /* read a block of data with no smb header */
+#define SMBwritebraw  0x1d  /* write a block of data with no smb header */
+#define SMBwritec     0x20  /* secondary write request */
+#define SMBwriteclose 0x2c  /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw      0x1A   /* read block raw */
+#define SMBreadBmpx      0x1B   /* read block multiplexed */
+#define SMBreadBs        0x1C   /* read block (secondary response) */
+#define SMBwriteBraw     0x1D   /* write block raw */
+#define SMBwriteBmpx     0x1E   /* write block multiplexed */
+#define SMBwriteBs       0x1F   /* write block (secondary request) */
+#define SMBwriteC        0x20   /* write complete response */
+#define SMBsetattrE      0x22   /* set file attributes expanded */
+#define SMBgetattrE      0x23   /* get file attributes expanded */
+#define SMBlockingX      0x24   /* lock/unlock byte ranges and X */
+#define SMBtrans         0x25   /* transaction - name, bytes in/out */
+#define SMBtranss        0x26   /* transaction (secondary request/response) */
+#define SMBioctl         0x27   /* IOCTL */
+#define SMBioctls        0x28   /* IOCTL  (secondary request/response) */
+#define SMBcopy          0x29   /* copy */
+#define SMBmove          0x2A   /* move */
+#define SMBecho          0x2B   /* echo */
+#define SMBopenX         0x2D   /* open and X */
+#define SMBreadX         0x2E   /* read and X */
+#define SMBwriteX        0x2F   /* write and X */
+#define SMBsesssetupX    0x73   /* Session Set Up & X (including User Logon) */
+#define SMBffirst        0x82   /* find first */
+#define SMBfunique       0x83   /* find unique */
+#define SMBfclose        0x84   /* find close */
+#define SMBkeepalive     0x85   /* keepalive */
+#define SMBinvalid       0xFE   /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2        0x32   /* TRANS2 protocol set */
+#define SMBtranss2       0x33   /* TRANS2 protocol set, secondary command */
+#define SMBfindclose     0x34   /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose    0x35   /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX      0x74   /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans       0xA0   /* NT transact */
+#define SMBnttranss      0xA1   /* NT transact secondary */
+#define SMBntcreateX     0xA2   /* NT create and X */
+#define SMBntcancel      0xA4   /* NT cancel */
+
+/* used to indicate end of chain */
+#define SMB_CHAIN_NONE   0xFF
+
+/* These are the trans subcommands */
+#define TRANSACT_SETNAMEDPIPEHANDLESTATE  0x01 
+#define TRANSACT_DCERPCCMD                0x26
+#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53
+
+/* These are the NT transact sub commands. */
+#define NT_TRANSACT_CREATE                1
+#define NT_TRANSACT_IOCTL                 2
+#define NT_TRANSACT_SET_SECURITY_DESC     3
+#define NT_TRANSACT_NOTIFY_CHANGE         4
+#define NT_TRANSACT_RENAME                5
+#define NT_TRANSACT_QUERY_SECURITY_DESC   6
+
+/* this is used on a TConX. I'm not sure the name is very helpful though */
+#define SMB_SUPPORT_SEARCH_BITS        0x0001
+#define SMB_SHARE_IN_DFS               0x0002
+
+/* Named pipe write mode flags. Used in writeX calls. */
+#define PIPE_RAW_MODE 0x4
+#define PIPE_START_MESSAGE 0x8
+
+/* the desired access to use when opening a pipe */
+#define DESIRED_ACCESS_PIPE 0x2019f
+
+/* Mapping of generic access rights for files to specific rights. */
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| NT_ACCESS_SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+                                                       FILE_READ_EA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+                           FILE_WRITE_EA|FILE_APPEND_DATA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+                           FILE_EXECUTE|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+
+/* FileAttributes (search attributes) field */
+#define FILE_ATTRIBUTE_READONLY                0x0001
+#define FILE_ATTRIBUTE_HIDDEN          0x0002
+#define FILE_ATTRIBUTE_SYSTEM          0x0004
+#define FILE_ATTRIBUTE_VOLUME          0x0008
+#define FILE_ATTRIBUTE_DIRECTORY       0x0010
+#define FILE_ATTRIBUTE_ARCHIVE         0x0020
+#define FILE_ATTRIBUTE_DEVICE          0x0040
+#define FILE_ATTRIBUTE_NORMAL          0x0080
+#define FILE_ATTRIBUTE_TEMPORARY       0x0100
+#define FILE_ATTRIBUTE_SPARSE          0x0200
+#define FILE_ATTRIBUTE_REPARSE_POINT   0x0400
+#define FILE_ATTRIBUTE_COMPRESSED      0x0800
+#define FILE_ATTRIBUTE_OFFLINE         0x1000
+#define FILE_ATTRIBUTE_NONINDEXED      0x2000
+#define FILE_ATTRIBUTE_ENCRYPTED       0x4000
+
+/* Flags - combined with attributes. */
+#define FILE_FLAG_WRITE_THROUGH    0x80000000L
+#define FILE_FLAG_NO_BUFFERING     0x20000000L
+#define FILE_FLAG_RANDOM_ACCESS    0x10000000L
+#define FILE_FLAG_SEQUENTIAL_SCAN  0x08000000L
+#define FILE_FLAG_DELETE_ON_CLOSE  0x04000000L
+#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000L
+#define FILE_FLAG_POSIX_SEMANTICS  0x01000000L
+
+/* Responses when opening a file. */
+#define FILE_WAS_SUPERSEDED 0
+#define FILE_WAS_OPENED 1
+#define FILE_WAS_CREATED 2
+#define FILE_WAS_OVERWRITTEN 3
+
+/* File type flags */
+#define FILE_TYPE_DISK  0
+#define FILE_TYPE_BYTE_MODE_PIPE 1
+#define FILE_TYPE_MESSAGE_MODE_PIPE 2
+#define FILE_TYPE_PRINTER 3
+#define FILE_TYPE_COMM_DEVICE 4
+#define FILE_TYPE_UNKNOWN 0xFFFF
+
+/* Flag for NT transact rename call. */
+#define RENAME_REPLACE_IF_EXISTS 1
+
+/* Filesystem Attributes. */
+#define FILE_CASE_SENSITIVE_SEARCH 0x01
+#define FILE_CASE_PRESERVED_NAMES 0x02
+#define FILE_UNICODE_ON_DISK 0x04
+/* According to cifs9f, this is 4, not 8 */
+/* Acconding to testing, this actually sets the security attribute! */
+#define FILE_PERSISTENT_ACLS 0x08
+/* These entries added from cifs9f --tsb */
+#define FILE_FILE_COMPRESSION 0x10
+#define FILE_VOLUME_QUOTAS 0x20
+/* I think this is wrong. JRA #define FILE_DEVICE_IS_MOUNTED 0x20 */
+#define FILE_VOLUME_SPARSE_FILE 0x40
+#define FILE_VOLUME_IS_COMPRESSED 0x8000
+
+/* ChangeNotify flags. */
+#define FILE_NOTIFY_CHANGE_FILE        0x001
+#define FILE_NOTIFY_CHANGE_DIR_NAME    0x002
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES  0x004
+#define FILE_NOTIFY_CHANGE_SIZE        0x008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE  0x010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x020
+#define FILE_NOTIFY_CHANGE_CREATION    0x040
+#define FILE_NOTIFY_CHANGE_EA          0x080
+#define FILE_NOTIFY_CHANGE_SECURITY    0x100
+#define FILE_NOTIFY_CHANGE_FILE_NAME   0x200
+
+/* change notify action results */
+#define NOTIFY_ACTION_ADDED 1
+#define NOTIFY_ACTION_REMOVED 2
+#define NOTIFY_ACTION_MODIFIED 3
+#define NOTIFY_ACTION_OLD_NAME 4
+#define NOTIFY_ACTION_NEW_NAME 5
+#define NOTIFY_ACTION_ADDED_STREAM 6
+#define NOTIFY_ACTION_REMOVED_STREAM 7
+#define NOTIFY_ACTION_MODIFIED_STREAM 8
+
+/* seek modes for smb_seek */
+#define SEEK_MODE_START   0
+#define SEEK_MODE_CURRENT 1
+#define SEEK_MODE_END     2
+
+/* where to find the base of the SMB packet proper */
+/* REWRITE TODO: smb_base needs to be removed */
+#define smb_base(buf) (((char *)(buf))+4)
+
+/* we don't allow server strings to be longer than 48 characters as
+   otherwise NT will not honour the announce packets */
+#define MAX_SERVER_STRING_LENGTH 48
+
+
+#define SMB_SUCCESS 0  /* The request was successful. */
+
+#ifdef WITH_DFS
+void dfs_unlogin(void);
+extern int dcelogin_atmost_once;
+#endif
+
+#ifdef NOSTRDUP
+char *strdup(char *s);
+#endif
+
+#ifndef SIGNAL_CAST
+#define SIGNAL_CAST (RETSIGTYPE (*)(int))
+#endif
+
+#ifndef SELECT_CAST
+#define SELECT_CAST
+#endif
+
+/* these are used in NetServerEnum to choose what to receive */
+#define SV_TYPE_WORKSTATION         0x00000001
+#define SV_TYPE_SERVER              0x00000002
+#define SV_TYPE_SQLSERVER           0x00000004
+#define SV_TYPE_DOMAIN_CTRL         0x00000008
+#define SV_TYPE_DOMAIN_BAKCTRL      0x00000010
+#define SV_TYPE_TIME_SOURCE         0x00000020
+#define SV_TYPE_AFP                 0x00000040
+#define SV_TYPE_NOVELL              0x00000080
+#define SV_TYPE_DOMAIN_MEMBER       0x00000100
+#define SV_TYPE_PRINTQ_SERVER       0x00000200
+#define SV_TYPE_DIALIN_SERVER       0x00000400
+#define SV_TYPE_SERVER_UNIX         0x00000800
+#define SV_TYPE_NT                  0x00001000
+#define SV_TYPE_WFW                 0x00002000
+#define SV_TYPE_SERVER_MFPN         0x00004000
+#define SV_TYPE_SERVER_NT           0x00008000
+#define SV_TYPE_POTENTIAL_BROWSER   0x00010000
+#define SV_TYPE_BACKUP_BROWSER      0x00020000
+#define SV_TYPE_MASTER_BROWSER      0x00040000
+#define SV_TYPE_DOMAIN_MASTER       0x00080000
+#define SV_TYPE_SERVER_OSF          0x00100000
+#define SV_TYPE_SERVER_VMS          0x00200000
+#define SV_TYPE_WIN95_PLUS          0x00400000
+#define SV_TYPE_DFS_SERVER         0x00800000
+#define SV_TYPE_ALTERNATE_XPORT     0x20000000  
+#define SV_TYPE_LOCAL_LIST_ONLY     0x40000000  
+#define SV_TYPE_DOMAIN_ENUM         0x80000000
+#define SV_TYPE_ALL                 0xFFFFFFFF  
+
+/* This was set by JHT in liaison with Jeremy Allison early 1997
+ * History:
+ * Version 4.0 - never made public
+ * Version 4.10 - New to 1.9.16p2, lost in space 1.9.16p3 to 1.9.16p9
+ *              - Reappeared in 1.9.16p11 with fixed smbd services
+ * Version 4.20 - To indicate that nmbd and browsing now works better
+ * Version 4.50 - Set at release of samba-2.2.0 by JHT
+ *
+ *  Note: In the presence of NT4.X do not set above 4.9
+ *        Setting this above 4.9 can have undesired side-effects.
+ *        This may change again in Samba-3.0 after further testing. JHT
+ */
+#define DEFAULT_MAJOR_VERSION 0x04
+#define DEFAULT_MINOR_VERSION 0x09
+
+/* Browser Election Values */
+#define BROWSER_ELECTION_VERSION       0x010f
+#define BROWSER_CONSTANT       0xaa55
+
+/* Sercurity mode bits. */
+#define NEGOTIATE_SECURITY_USER_LEVEL          0x01
+#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE  0x02
+#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED  0x04
+#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08
+
+/* NT Flags2 bits - cifs6.txt section 3.1.2 */
+   
+#define FLAGS2_LONG_PATH_COMPONENTS    0x0001
+#define FLAGS2_EXTENDED_ATTRIBUTES     0x0002
+#define FLAGS2_SMB_SECURITY_SIGNATURES 0x0004
+#define FLAGS2_IS_LONG_NAME            0x0040
+#define FLAGS2_EXTENDED_SECURITY       0x0800 
+#define FLAGS2_DFS_PATHNAMES           0x1000
+#define FLAGS2_READ_PERMIT_NO_EXECUTE  0x2000
+#define FLAGS2_32_BIT_ERROR_CODES      0x4000 
+#define FLAGS2_UNICODE_STRINGS         0x8000
+
+#define FLAGS2_WIN2K_SIGNATURE         0xC852 /* Hack alert ! For now... JRA. */
+
+/* Capabilities.  see ftp.microsoft.com/developr/drg/cifs/cifs/cifs4.txt */
+
+#define CAP_RAW_MODE         0x0001
+#define CAP_MPX_MODE         0x0002
+#define CAP_UNICODE          0x0004
+#define CAP_LARGE_FILES      0x0008
+#define CAP_NT_SMBS          0x0010
+#define CAP_RPC_REMOTE_APIS  0x0020
+#define CAP_STATUS32         0x0040
+#define CAP_LEVEL_II_OPLOCKS 0x0080
+#define CAP_LOCK_AND_READ    0x0100
+#define CAP_NT_FIND          0x0200
+#define CAP_DFS              0x1000
+#define CAP_W2K_SMBS         0x2000
+#define CAP_LARGE_READX      0x4000
+#define CAP_LARGE_WRITEX     0x8000
+#define CAP_UNIX             0x800000 /* Capabilities for UNIX extensions. Created by HP. */
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/*
+ * Global value meaing that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+#define VUID_OFFSET 100 /* Amount to bias returned vuid numbers */
+
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x1
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x2
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x4
+#define LOCKING_ANDX_CANCEL_LOCK 0x8
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/* Oplock levels */
+#define OPLOCKLEVEL_NONE 0
+#define OPLOCKLEVEL_II 1
+
+/*
+ * Bits we test with.
+ */
+
+#define NO_OPLOCK 0
+#define EXCLUSIVE_OPLOCK 1
+#define BATCH_OPLOCK 2
+#define LEVEL_II_OPLOCK 4
+
+#define CORE_OPLOCK_GRANTED (1<<5)
+#define EXTENDED_OPLOCK_GRANTED (1<<15)
+
+/*
+ * Return values for oplock types.
+ */
+
+#define NO_OPLOCK_RETURN 0
+#define EXCLUSIVE_OPLOCK_RETURN 1
+#define BATCH_OPLOCK_RETURN 2
+#define LEVEL_II_OPLOCK_RETURN 3
+
+/*
+ * Loopback command offsets.
+ */
+
+#define OPBRK_CMD_LEN_OFFSET 0
+#define OPBRK_CMD_PORT_OFFSET 4
+#define OPBRK_CMD_HEADER_LEN 6
+
+#define OPBRK_MESSAGE_CMD_OFFSET 0
+
+/* Message types */
+#define OPLOCK_BREAK_CMD 0x1
+#define KERNEL_OPLOCK_BREAK_CMD 0x2
+#define LEVEL_II_OPLOCK_BREAK_CMD 0x3
+#define ASYNC_LEVEL_II_OPLOCK_BREAK_CMD 0x4
+
+/*
+ * Capabilities abstracted for different systems.
+ */
+
+#define KERNEL_OPLOCK_CAPABILITY 0x1
+
+/*
+ * Oplock break command code sent via the kernel interface (if it exists).
+ *
+ * Form of this is :
+ *
+ *  0     2       2+devsize 2+devsize+inodesize
+ *  +----+--------+--------+----------+
+ *  | cmd| dev    |  inode |  fileid  |
+ *  +----+--------+--------+----------+
+ */
+#define KERNEL_OPLOCK_BREAK_DEV_OFFSET 2
+#define KERNEL_OPLOCK_BREAK_INODE_OFFSET (KERNEL_OPLOCK_BREAK_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define KERNEL_OPLOCK_BREAK_FILEID_OFFSET (KERNEL_OPLOCK_BREAK_INODE_OFFSET + sizeof(SMB_INO_T))
+#define KERNEL_OPLOCK_BREAK_MSG_LEN (KERNEL_OPLOCK_BREAK_FILEID_OFFSET + sizeof(unsigned long))
+
+
+#define CMD_REPLY 0x8000
+
+#include "smb_macros.h"
+
+/* A netbios name structure. */
+struct nmb_name {
+       char         name[17];
+       char         scope[64];
+       unsigned int name_type;
+};
+
+
+/* A netbios node status array element. */
+struct node_status {
+       char name[16];
+       unsigned char type;
+       unsigned char flags;
+};
+
+struct pwd_info
+{
+       BOOL null_pwd;
+       BOOL cleartext;
+       BOOL crypted;
+
+       fstring password;
+
+       uchar smb_lm_pwd[16];
+       uchar smb_nt_pwd[16];
+
+       uchar smb_lm_owf[24];
+       uchar smb_nt_owf[128];
+       size_t nt_owf_len;
+
+       uchar lm_cli_chal[8];
+       uchar nt_cli_chal[128];
+       size_t nt_cli_chal_len;
+
+       uchar sess_key[16];
+};
+
+#include "rpc_creds.h"
+#include "rpc_misc.h"
+#include "rpc_secdes.h"
+#include "nt_printing.h"
+
+typedef struct user_struct
+{
+       struct user_struct *next, *prev;
+       uint16 vuid; /* Tag for this entry. */
+       uid_t uid; /* uid of a validated user */
+       gid_t gid; /* gid of a validated user */
+
+       userdom_struct user;
+       char *homedir;
+       char *unix_homedir;
+       char *logon_script;
+       
+       BOOL guest;
+
+       /* following groups stuff added by ih */
+       /* This groups info is needed for when we become_user() for this uid */
+       int n_groups;
+       gid_t *groups;
+
+       NT_USER_TOKEN *nt_user_token;
+
+       uint8 session_key[16];
+
+       char *session_keystr; /* used by utmp and pam session code.  
+                                TDB key string */
+       int homes_snum;
+
+       struct auth_serversupplied_info *server_info;
+
+} user_struct;
+
+
+struct unix_error_map {
+       int unix_error;
+       int dos_class;
+       int dos_code;
+       NTSTATUS nt_error;
+};
+
+#include "ntdomain.h"
+
+#include "client.h"
+
+/*
+ * Size of new password account encoding string.  This is enough space to
+ * hold 11 ACB characters, plus the surrounding [] and a terminating null.
+ * Do not change unless you are adding new ACB bits!
+ */
+
+#define NEW_PW_FORMAT_SPACE_PADDED_LEN 14
+
+/*
+   Do you want session setups at user level security with a invalid
+   password to be rejected or allowed in as guest? WinNT rejects them
+   but it can be a pain as it means "net view" needs to use a password
+
+   You have 3 choices in the setting of map_to_guest:
+
+   "NEVER_MAP_TO_GUEST" means session setups with an invalid password
+   are rejected. This is the default.
+
+   "MAP_TO_GUEST_ON_BAD_USER" means session setups with an invalid password
+   are rejected, unless the username does not exist, in which case it
+   is treated as a guest login
+
+   "MAP_TO_GUEST_ON_BAD_PASSWORD" means session setups with an invalid password
+   are treated as a guest login
+
+   Note that map_to_guest only has an effect in user or server
+   level security.
+*/
+
+#define NEVER_MAP_TO_GUEST 0
+#define MAP_TO_GUEST_ON_BAD_USER 1
+#define MAP_TO_GUEST_ON_BAD_PASSWORD 2
+
+#define SAFE_NETBIOS_CHARS ". -_"
+
+/* generic iconv conversion structure */
+typedef struct {
+       size_t (*direct)(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft);
+       size_t (*pull)(void *cd, const char **inbuf, size_t *inbytesleft,
+                      char **outbuf, size_t *outbytesleft);
+       size_t (*push)(void *cd, const char **inbuf, size_t *inbytesleft,
+                      char **outbuf, size_t *outbytesleft);
+       void *cd_direct, *cd_pull, *cd_push;
+       char *from_name, *to_name;
+} *smb_iconv_t;
+
+/* The maximum length of a trust account password.
+   Used when we randomly create it, 15 char passwords
+   exceed NT4's max password length */
+
+#define DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH 14
+
+/* Module support */
+typedef int (init_module_function) (void);
+
+
+/* a set of flags to control handling of request structures */
+#define REQ_CONTROL_PROTECTED (1<<0) /* don't destroy this request */
+#define REQ_CONTROL_LARGE     (1<<1) /* allow replies larger than max_xmit */
+#define REQ_CONTROL_ASYNC     (1<<2) /* the backend will answer this one later */
+
+/* passed to br lock code */
+enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK};
+
+#include "popt_common.h"
+
+#endif /* _SMB_H */
diff --git a/source4/include/smb_acls.h b/source4/include/smb_acls.h
new file mode 100644 (file)
index 0000000..e7edb62
--- /dev/null
@@ -0,0 +1,275 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Portable SMB ACL interface
+   Copyright (C) Jeremy Allison 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.
+*/
+
+#ifndef _SMB_ACLS_H
+#define _SMB_ACLS_H
+#if defined(HAVE_POSIX_ACLS)
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T               acl_tag_t
+#define SMB_ACL_TYPE_T                         acl_type_t
+#define SMB_ACL_PERMSET_T           acl_permset_t
+#define SMB_ACL_PERM_T                         acl_perm_t
+#define SMB_ACL_READ                ACL_READ
+#define SMB_ACL_WRITE               ACL_WRITE
+#define SMB_ACL_EXECUTE             ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                ACL_USER
+#define SMB_ACL_USER_OBJ            ACL_USER_OBJ
+#define SMB_ACL_GROUP               ACL_GROUP
+#define SMB_ACL_GROUP_OBJ           ACL_GROUP_OBJ
+#define SMB_ACL_OTHER               ACL_OTHER
+#define SMB_ACL_MASK                ACL_MASK
+
+#define SMB_ACL_T                                      acl_t
+
+#define SMB_ACL_ENTRY_T                                acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY         ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY          ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS         ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT        ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_TRU64_ACLS)
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T               acl_tag_t
+#define SMB_ACL_TYPE_T                         acl_type_t
+#define SMB_ACL_PERMSET_T           acl_permset_t
+#define SMB_ACL_PERM_T                         acl_perm_t
+#define SMB_ACL_READ                ACL_READ
+#define SMB_ACL_WRITE               ACL_WRITE
+#define SMB_ACL_EXECUTE             ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                ACL_USER
+#define SMB_ACL_USER_OBJ            ACL_USER_OBJ
+#define SMB_ACL_GROUP               ACL_GROUP
+#define SMB_ACL_GROUP_OBJ           ACL_GROUP_OBJ
+#define SMB_ACL_OTHER               ACL_OTHER
+#define SMB_ACL_MASK                ACL_MASK
+
+#define SMB_ACL_T                                      acl_t
+
+#define SMB_ACL_ENTRY_T                                acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY         0
+#define SMB_ACL_NEXT_ENTRY          1
+
+#define SMB_ACL_TYPE_ACCESS         ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT        ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int                    SMB_ACL_TAG_T;
+typedef int                    SMB_ACL_TYPE_T;
+typedef ushort         *SMB_ACL_PERMSET_T;
+typedef ushort         SMB_ACL_PERM_T;
+#define SMB_ACL_READ                           4
+#define SMB_ACL_WRITE                          2
+#define SMB_ACL_EXECUTE                                1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                           USER
+#define SMB_ACL_USER_OBJ                       USER_OBJ
+#define SMB_ACL_GROUP                          GROUP
+#define SMB_ACL_GROUP_OBJ                      GROUP_OBJ
+#define SMB_ACL_OTHER                          OTHER_OBJ
+#define SMB_ACL_MASK                           CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+       int     size;
+       int     count;
+       int     next;
+       struct acl      acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl                                     *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY                    0
+#define SMB_ACL_NEXT_ENTRY                     1
+
+#define SMB_ACL_TYPE_ACCESS                    0
+#define SMB_ACL_TYPE_DEFAULT           1
+
+#elif defined(HAVE_HPUX_ACLS)
+
+/*
+ * Based on the Solaris & UnixWare code.
+ */
+
+#undef GROUP
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int                    SMB_ACL_TAG_T;
+typedef int                    SMB_ACL_TYPE_T;
+typedef ushort         *SMB_ACL_PERMSET_T;
+typedef ushort         SMB_ACL_PERM_T;
+#define SMB_ACL_READ                           4
+#define SMB_ACL_WRITE                          2
+#define SMB_ACL_EXECUTE                                1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                           USER
+#define SMB_ACL_USER_OBJ                       USER_OBJ
+#define SMB_ACL_GROUP                          GROUP
+#define SMB_ACL_GROUP_OBJ                      GROUP_OBJ
+#define SMB_ACL_OTHER                          OTHER_OBJ
+#define SMB_ACL_MASK                           CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+       int     size;
+       int     count;
+       int     next;
+       struct acl      acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl                                     *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY                    0
+#define SMB_ACL_NEXT_ENTRY                     1
+
+#define SMB_ACL_TYPE_ACCESS                    0
+#define SMB_ACL_TYPE_DEFAULT           1
+
+#elif defined(HAVE_IRIX_ACLS)
+
+#define SMB_ACL_TAG_T               acl_tag_t
+#define SMB_ACL_TYPE_T             acl_type_t
+#define SMB_ACL_PERMSET_T           acl_permset_t
+#define SMB_ACL_PERM_T             acl_perm_t
+#define SMB_ACL_READ                ACL_READ
+#define SMB_ACL_WRITE               ACL_WRITE
+#define SMB_ACL_EXECUTE             ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                ACL_USER
+#define SMB_ACL_USER_OBJ            ACL_USER_OBJ
+#define SMB_ACL_GROUP               ACL_GROUP
+#define SMB_ACL_GROUP_OBJ           ACL_GROUP_OBJ
+#define SMB_ACL_OTHER               ACL_OTHER_OBJ
+#define SMB_ACL_MASK                ACL_MASK
+
+typedef struct SMB_ACL_T {
+   int next;
+   BOOL freeaclp;
+   struct acl  *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T                    acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY         0
+#define SMB_ACL_NEXT_ENTRY          1
+
+#define SMB_ACL_TYPE_ACCESS         ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT        ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include "/usr/include/acl.h"
+
+typedef uint                        *SMB_ACL_PERMSET_T;
+struct acl_entry_link{
+       struct acl_entry_link *prevp;
+       struct new_acl_entry *entryp;
+       struct acl_entry_link *nextp;
+       int count;
+};
+
+struct new_acl_entry{
+       unsigned short  ace_len;
+       unsigned short  ace_type;
+       unsigned int    ace_access;
+       struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T             struct new_acl_entry*
+#define SMB_ACL_T                   struct acl_entry_link*
+#define SMB_ACL_TAG_T               unsigned short
+#define SMB_ACL_TYPE_T              int
+#define SMB_ACL_PERM_T              uint
+#define SMB_ACL_READ                S_IRUSR
+#define SMB_ACL_WRITE               S_IWUSR
+#define SMB_ACL_EXECUTE             S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                ACEID_USER
+#define SMB_ACL_USER_OBJ            3
+#define SMB_ACL_GROUP               ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ           4
+#define SMB_ACL_OTHER               5
+#define SMB_ACL_MASK                6
+
+
+#define SMB_ACL_FIRST_ENTRY         1
+#define SMB_ACL_NEXT_ENTRY          2
+
+#define SMB_ACL_TYPE_ACCESS         0
+#define SMB_ACL_TYPE_DEFAULT        1
+
+#else /* No ACLs. */
+
+/* No ACLS - fake it. */
+#define SMB_ACL_TAG_T                          int
+#define SMB_ACL_TYPE_T              int
+#define SMB_ACL_PERMSET_T                      mode_t
+#define SMB_ACL_PERM_T                         mode_t
+#define SMB_ACL_READ                           S_IRUSR
+#define SMB_ACL_WRITE                          S_IWUSR
+#define SMB_ACL_EXECUTE                                S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER                           0
+#define SMB_ACL_USER_OBJ                       1
+#define SMB_ACL_GROUP                          2
+#define SMB_ACL_GROUP_OBJ                      3
+#define SMB_ACL_OTHER                          4
+#define SMB_ACL_MASK                           5
+
+typedef struct SMB_ACL_T {
+       int dummy;
+} *SMB_ACL_T;
+
+typedef struct SMB_ACL_ENTRY_T {
+       int dummy;
+} *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY                    0
+#define SMB_ACL_NEXT_ENTRY                     1
+
+#define SMB_ACL_TYPE_ACCESS                    0
+#define SMB_ACL_TYPE_DEFAULT           1
+
+#endif /* No ACLs. */
+#endif /* _SMB_ACLS_H */
diff --git a/source4/include/smb_interfaces.h b/source4/include/smb_interfaces.h
new file mode 100644 (file)
index 0000000..70cff11
--- /dev/null
@@ -0,0 +1,1898 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB request interface structures
+   Copyright (C) Andrew Tridgell                       2003
+   
+   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.
+*/
+
+
+typedef SMB_BIG_UINT large_t;
+
+/* Globally Unique ID */
+#define GUID_SIZE 16
+typedef struct guid_info
+{
+       uint8 info[GUID_SIZE];
+} GUID;
+
+/* 64 bit time (100usec) 1601 - cifs6.txt, section 3.5, page 30 */
+typedef struct nttime_info
+{
+       uint32 low;
+       uint32 high;
+} NTTIME;
+
+
+/* this structure is just a wrapper for a string, the only reason we
+   bother with this is that it allows us to check the length provided
+   on the wire in testsuite test code to ensure that we are
+   terminating names in the same way that win2003 is. The *ONLY* time
+   you should ever look at the 'private_length' field in this
+   structure is inside compliance test code, in all cases just use the
+   null terminated char* as the definitive definition of the
+   string
+
+   also note that this structure is only used in packets where there
+   is an explicit length provided on the wire (hence the name). That
+   length is placed in 'private_length'. For packets where the length
+   is always determined by NULL or packet termination a normal char*
+   is used.
+ */
+typedef struct {
+       uint32 private_length;
+       const char *s;
+} WIRE_STRING;
+
+
+/*
+  this header defines the structures and unions used between the SMB
+  parser and the backends.
+*/
+
+/* struct used for SMBlseek call */
+struct smb_seek {
+       struct {
+               uint16 fnum;
+               uint16 mode;
+               int32  offset; /* signed */
+       } in;
+       struct {
+               uint32 offset;
+       } out;
+};
+
+
+/* struct used in unlink() call */
+struct smb_unlink {
+       struct {
+               const char *pattern;
+               uint16 attrib;
+       } in;
+};
+
+
+/* struct used in chkpath() call */
+struct smb_chkpath {
+       struct {
+               const char *path;
+       } in;
+};
+
+enum mkdir_level {RAW_MKDIR_GENERIC, RAW_MKDIR_MKDIR, RAW_MKDIR_T2MKDIR};
+
+/* union used in mkdir() call */
+union smb_mkdir {
+       /* generic level */
+       struct {
+               enum mkdir_level level;
+       } generic;
+
+       struct {
+               enum mkdir_level level;
+               struct {
+                       const char *path;
+               } in;
+       } mkdir;
+
+       struct {
+               enum mkdir_level level;
+               struct {
+                       const char *path;
+                       uint_t num_eas;
+                       struct ea_struct *eas;                  
+               } in;
+       } t2mkdir;
+};
+
+/* struct used in rmdir() call */
+struct smb_rmdir {
+       struct {
+               const char *path;
+       } in;
+};
+
+/* struct used in rename() call */
+struct smb_rename {
+       struct {
+               const char *pattern1;
+               const char *pattern2;
+               uint16 attrib;
+       } in;
+};
+
+enum tcon_level {RAW_TCON_TCON, RAW_TCON_TCONX};
+
+/* union used in tree connect call */
+union smb_tcon {
+       /* generic interface */
+       struct {
+               enum tcon_level level;
+       } generic;
+
+       /* SMBtcon interface */
+       struct {
+               enum tcon_level level;
+
+               struct {
+                       const char *service;
+                       const char *password;
+                       const char *dev;
+               } in;
+               struct {
+                       uint16 max_xmit;
+                       uint16 cnum;
+               } out;
+       } tcon;
+
+       /* SMBtconX interface */
+       struct {
+               enum tcon_level level;
+
+               struct {
+                       uint16 flags;
+                       DATA_BLOB password;
+                       const char *path;
+                       const char *device;
+               } in;
+               struct {
+                       uint16 options;
+                       char *dev_type;
+                       char *fs_type;
+                       uint16 cnum;
+               } out;
+       } tconx;
+};
+
+
+enum sesssetup_level {RAW_SESSSETUP_GENERIC, RAW_SESSSETUP_OLD, RAW_SESSSETUP_NT1, RAW_SESSSETUP_SPNEGO};
+
+/* union used in session_setup call */
+union smb_sesssetup {
+       
+       /* generic interface - used for auto selecting based on negotiated
+          protocol options */
+       struct {
+               enum sesssetup_level level;
+
+               struct {
+                       uint32 sesskey;
+                       uint32 capabilities;
+                       const char *password;
+                       const char *user;
+                       const char *domain;
+               } in;
+               struct {
+                       uint16 vuid;
+                       char *os;
+                       char *lanman;
+                       char *domain;
+               } out;          
+       } generic;
+
+       /* the pre-NT1 interface */
+       struct {
+               enum sesssetup_level level;
+
+               struct {
+                       uint16 bufsize;
+                       uint16 mpx_max;
+                       uint16 vc_num;
+                       uint32 sesskey;
+                       DATA_BLOB password;
+                       const char *user;
+                       const char *domain;
+                       const char *os;
+                       const char *lanman;
+               } in;
+               struct {
+                       uint16 action;
+                       uint16 vuid;
+                       char *os;
+                       char *lanman;
+                       char *domain;
+               } out;
+       } old;
+
+       /* the NT1 interface */
+       struct {
+               enum sesssetup_level level;
+
+               struct {
+                       uint16 bufsize;
+                       uint16 mpx_max;
+                       uint16 vc_num;
+                       uint32 sesskey;
+                       uint32 capabilities;
+                       DATA_BLOB password1;
+                       DATA_BLOB password2;
+                       const char *user;
+                       const char *domain;
+                       const char *os;
+                       const char *lanman;
+               } in;
+               struct {
+                       uint16 action;
+                       uint16 vuid;
+                       char *os;
+                       char *lanman;
+                       char *domain;
+               } out;
+       } nt1;
+
+
+       /* the SPNEGO interface */
+       struct {
+               enum sesssetup_level level;
+
+               struct {
+                       uint16 bufsize;
+                       uint16 mpx_max;
+                       uint16 vc_num;
+                       uint32 sesskey;
+                       uint32 capabilities;
+                       DATA_BLOB secblob;
+                       const char *os;
+                       const char *lanman;
+                       const char *domain;
+               } in;
+               struct {
+                       uint16 action;
+                       DATA_BLOB secblob;
+                       char *os;
+                       char *lanman;
+                       char *domain;
+                       uint16 vuid;
+               } out;
+       } spnego;
+};
+
+/* Note that the specified enum values are identical to the actual info-levels used
+ * on the wire.
+ */
+enum fileinfo_level {RAW_FILEINFO_GENERIC                    = 0xF000, 
+                    RAW_FILEINFO_GETATTR,                   /* SMBgetatr */
+                    RAW_FILEINFO_GETATTRE,                  /* SMBgetattrE */
+                    RAW_FILEINFO_STANDARD                   = SMB_QFILEINFO_STANDARD,
+                    RAW_FILEINFO_EA_SIZE                    = SMB_QFILEINFO_EA_SIZE,
+                    RAW_FILEINFO_ALL_EAS                    = SMB_QFILEINFO_ALL_EAS,
+                    RAW_FILEINFO_IS_NAME_VALID              = SMB_QFILEINFO_IS_NAME_VALID,
+                    RAW_FILEINFO_BASIC_INFO                 = SMB_QFILEINFO_BASIC_INFO, 
+                    RAW_FILEINFO_STANDARD_INFO              = SMB_QFILEINFO_STANDARD_INFO,
+                    RAW_FILEINFO_EA_INFO                    = SMB_QFILEINFO_EA_INFO,
+                    RAW_FILEINFO_NAME_INFO                  = SMB_QFILEINFO_NAME_INFO, 
+                    RAW_FILEINFO_ALL_INFO                   = SMB_QFILEINFO_ALL_INFO,
+                    RAW_FILEINFO_ALT_NAME_INFO              = SMB_QFILEINFO_ALT_NAME_INFO,
+                    RAW_FILEINFO_STREAM_INFO                = SMB_QFILEINFO_STREAM_INFO,
+                    RAW_FILEINFO_COMPRESSION_INFO           = SMB_QFILEINFO_COMPRESSION_INFO,
+                    RAW_FILEINFO_UNIX_BASIC                 = SMB_QFILEINFO_UNIX_BASIC,
+                    RAW_FILEINFO_UNIX_LINK                  = SMB_QFILEINFO_UNIX_LINK,
+                    RAW_FILEINFO_BASIC_INFORMATION          = SMB_QFILEINFO_BASIC_INFORMATION,
+                    RAW_FILEINFO_STANDARD_INFORMATION       = SMB_QFILEINFO_STANDARD_INFORMATION,
+                    RAW_FILEINFO_INTERNAL_INFORMATION       = SMB_QFILEINFO_INTERNAL_INFORMATION,
+                    RAW_FILEINFO_EA_INFORMATION             = SMB_QFILEINFO_EA_INFORMATION,
+                    RAW_FILEINFO_ACCESS_INFORMATION         = SMB_QFILEINFO_ACCESS_INFORMATION,
+                    RAW_FILEINFO_NAME_INFORMATION           = SMB_QFILEINFO_NAME_INFORMATION,
+                    RAW_FILEINFO_POSITION_INFORMATION       = SMB_QFILEINFO_POSITION_INFORMATION,
+                    RAW_FILEINFO_MODE_INFORMATION           = SMB_QFILEINFO_MODE_INFORMATION,
+                    RAW_FILEINFO_ALIGNMENT_INFORMATION      = SMB_QFILEINFO_ALIGNMENT_INFORMATION,
+                    RAW_FILEINFO_ALL_INFORMATION            = SMB_QFILEINFO_ALL_INFORMATION,
+                    RAW_FILEINFO_ALT_NAME_INFORMATION       = SMB_QFILEINFO_ALT_NAME_INFORMATION,
+                    RAW_FILEINFO_STREAM_INFORMATION         = SMB_QFILEINFO_STREAM_INFORMATION,
+                    RAW_FILEINFO_COMPRESSION_INFORMATION    = SMB_QFILEINFO_COMPRESSION_INFORMATION,
+                    RAW_FILEINFO_NETWORK_OPEN_INFORMATION   = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION,
+                    RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION  = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION
+};
+
+
+/* union used in qfileinfo() and qpathinfo() backend calls */
+union smb_fileinfo {
+       /* generic interface:
+        * matches RAW_FILEINFO_GENERIC */
+       struct {
+               enum fileinfo_level level;
+
+               /* each level can be called on either a pathname or a
+                * filename, in either case the return format is
+                * identical */
+               union smb_fileinfo_in {
+                       const char *fname;
+                       uint16 fnum;
+               } in;
+               
+               struct {
+                       uint16 attrib;
+                       uint32 ea_size;
+                       uint_t num_eas;
+                       struct ea_struct {
+                               uint8 flags;
+                               WIRE_STRING name;
+                               DATA_BLOB value;
+                       } *eas;         
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       uint32 ex_attrib;       
+                       large_t alloc_size;
+                       large_t size;
+                       uint32 nlink;
+                       WIRE_STRING fname;      
+                       WIRE_STRING alt_fname;  
+                       uint8 delete_pending;
+                       uint8 directory;
+                       large_t compressed_size;
+                       uint16 format;
+                       uint8 unit_shift;
+                       uint8 chunk_shift;
+                       uint8 cluster_shift;
+                       uint32 device;
+                       uint32 inode;
+                       uint32 access_flags; /* seen 0x001f01ff from w2k3 */
+                       large_t position;
+                       uint32 mode;
+                       uint32 alignment_requirement;
+                       uint32 reparse_tag;
+                       uint_t num_streams;
+                       struct stream_struct {
+                               large_t size;
+                               large_t alloc_size;
+                               WIRE_STRING stream_name;
+                       } *streams;
+               } out;
+       } generic;
+
+
+       /* SMBgetatr interface:
+        * matches RAW_FILEINFO_GETATTR */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint16 attrib;
+                       uint32 size;
+                       time_t write_time;
+               } out;
+       } getattr;
+
+       /* SMBgetattrE interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       time_t create_time;
+                       time_t access_time;
+                       time_t write_time;
+                       uint32 size;
+                       uint32 alloc_size;
+                       uint16 attrib;
+               } out;
+       } getattre;
+
+       /* trans2 RAW_FILEINFO_STANDARD interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       time_t create_time;
+                       time_t access_time;
+                       time_t write_time;
+                       uint32 size;
+                       uint32 alloc_size;
+                       uint16 attrib;
+               } out;
+       } standard;
+
+       /* trans2 RAW_FILEINFO_EA_SIZE interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       time_t create_time;
+                       time_t access_time;
+                       time_t write_time;
+                       uint32 size;
+                       uint32 alloc_size;
+                       uint16 attrib;
+                       uint32 ea_size;
+               } out;
+       } ea_size;
+
+       /* trans2 RAW_FILEINFO_ALL_EAS interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       /* the ea_size is implied by the list */
+                       uint_t num_eas;
+                       struct ea_struct *eas;
+               } out;
+       } all_eas;
+
+       /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface 
+          only valid for a QPATHNAME call - no returned data */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+       } is_name_valid;
+
+       /* RAW_FILEINFO_BASIC_INFO and RAW_FILEINFO_BASIC_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       uint32 attrib;
+               } out;
+       } basic_info;
+               
+
+       /* RAW_FILEINFO_STANDARD_INFO and RAW_FILEINFO_STANDARD_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       large_t alloc_size;
+                       large_t size;
+                       uint32 nlink;
+                       BOOL delete_pending;
+                       BOOL directory;
+               } out;
+       } standard_info;
+       
+       /* RAW_FILEINFO_EA_INFO and RAW_FILEINFO_EA_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint32 ea_size;
+               } out;
+       } ea_info;
+
+       /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       WIRE_STRING fname;
+               } out;
+       } name_info;
+
+       /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       uint32 attrib;
+                       large_t alloc_size;
+                       large_t size;
+                       uint32 nlink;
+                       uint8 delete_pending;
+                       uint8 directory;
+                       uint32 ea_size;
+                       WIRE_STRING fname;
+               } out;
+       } all_info;     
+
+       /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       WIRE_STRING fname;
+               } out;
+       } alt_name_info;
+
+       /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint_t num_streams;
+                       struct stream_struct *streams;
+               } out;
+       } stream_info;
+       
+       /* RAW_FILEINFO_COMPRESSION_INFO and RAW_FILEINFO_COMPRESSION_INFORMATION interfaces */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       large_t compressed_size;
+                       uint16 format;
+                       uint8 unit_shift;
+                       uint8 chunk_shift;
+                       uint8 cluster_shift;
+               } out;
+       } compression_info;
+
+       /* RAW_FILEINFO_UNIX_BASIC interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       large_t end_of_file;
+                       large_t num_bytes;
+                       NTTIME status_change_time;
+                       NTTIME access_time;
+                       NTTIME change_time;
+                       large_t uid;
+                       large_t gid;
+                       uint32 file_type;
+                       large_t dev_major;
+                       large_t dev_minor;
+                       large_t unique_id;
+                       large_t permissions;
+                       large_t nlink;
+               } out;
+       } unix_basic_info;
+
+       /* RAW_FILEINFO_UNIX_LINK interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       WIRE_STRING link_dest;
+               } out;
+       } unix_link_info;
+
+       /* RAW_FILEINFO_INTERNAL_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       /* REWRITE: these are very uncertain - we need
+                        * to look at this interface */
+                       uint32 device;
+                       uint32 inode;
+               } out;
+       } internal_information;
+
+       /* RAW_FILEINFO_ACCESS_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint32 access_flags; /* seen 0x001f01ff from w2k3 */
+               } out;
+       } access_information;
+
+       /* RAW_FILEINFO_POSITION_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       large_t position;
+               } out;
+       } position_information;
+
+       /* RAW_FILEINFO_MODE_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint32 mode;
+               } out;
+       } mode_information;
+
+       /* RAW_FILEINFO_ALIGNMENT_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint32 alignment_requirement;
+               } out;
+       } alignment_information;
+
+       /* RAW_FILEINFO_NETWORK_OPEN_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       large_t alloc_size;
+                       large_t size;
+                       uint32 attrib;
+               } out;
+       } network_open_information;
+
+
+       /* RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION interface */
+       struct {
+               enum fileinfo_level level;
+               union smb_fileinfo_in in;
+
+               struct {
+                       uint32 attrib;
+                       uint32 reparse_tag;
+               } out;
+       } attribute_tag_information;
+};
+
+
+enum setfileinfo_level {
+       RAW_SFILEINFO_GENERIC                 = 0xF000, 
+       RAW_SFILEINFO_SETATTR,                /* SMBsetatr */
+       RAW_SFILEINFO_SETATTRE,               /* SMBsetattrE */
+       RAW_SFILEINFO_STANDARD                = SMB_SFILEINFO_STANDARD,
+       RAW_SFILEINFO_EA_SET                  = SMB_SFILEINFO_EA_SET,
+       RAW_SFILEINFO_BASIC_INFO              = SMB_SFILEINFO_BASIC_INFO,
+       RAW_SFILEINFO_DISPOSITION_INFO        = SMB_SFILEINFO_DISPOSITION_INFO,
+       RAW_SFILEINFO_ALLOCATION_INFO         = SMB_SFILEINFO_ALLOCATION_INFO,
+       RAW_SFILEINFO_END_OF_FILE_INFO        = SMB_SFILEINFO_END_OF_FILE_INFO,
+       RAW_SFILEINFO_UNIX_BASIC              = SMB_SFILEINFO_UNIX_BASIC,
+       RAW_SFILEINFO_UNIX_LINK               = SMB_SFILEINFO_UNIX_LINK,
+       RAW_SFILEINFO_UNIX_HLINK              = SMB_SFILEINFO_UNIX_HLINK,
+       RAW_SFILEINFO_BASIC_INFORMATION       = SMB_SFILEINFO_BASIC_INFORMATION,
+       RAW_SFILEINFO_RENAME_INFORMATION      = SMB_SFILEINFO_RENAME_INFORMATION,
+       RAW_SFILEINFO_DISPOSITION_INFORMATION = SMB_SFILEINFO_DISPOSITION_INFORMATION,
+       RAW_SFILEINFO_POSITION_INFORMATION    = SMB_SFILEINFO_POSITION_INFORMATION,
+       RAW_SFILEINFO_MODE_INFORMATION        = SMB_SFILEINFO_MODE_INFORMATION,
+       RAW_SFILEINFO_ALLOCATION_INFORMATION  = SMB_SFILEINFO_ALLOCATION_INFORMATION,
+       RAW_SFILEINFO_END_OF_FILE_INFORMATION = SMB_SFILEINFO_END_OF_FILE_INFORMATION,
+       RAW_SFILEINFO_1023                    = SMB_SFILEINFO_1023,
+       RAW_SFILEINFO_1025                    = SMB_SFILEINFO_1025,
+       RAW_SFILEINFO_1029                    = SMB_SFILEINFO_1029,
+       RAW_SFILEINFO_1032                    = SMB_SFILEINFO_1032,
+       RAW_SFILEINFO_1039                    = SMB_SFILEINFO_1039,
+       RAW_SFILEINFO_1040                    = SMB_SFILEINFO_1040
+};
+
+/* union used in setfileinfo() and setpathinfo() calls */
+union smb_setfileinfo {
+       /* generic interface */
+       struct {
+               enum setfileinfo_level level;
+
+               /* we are combining setfileinfo and setpathinfo into one 
+                  interface */
+               union setfileinfo_file {
+                       const char *fname;
+                       uint16 fnum;
+               } file;
+       } generic;
+
+       /* RAW_SFILEINFO_SETATTR (SMBsetatr) interface - only via setpathinfo() */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+               struct {
+                       uint16 attrib;
+                       time_t write_time;
+               } in;
+       } setattr;
+
+       /* RAW_SFILEINFO_SETATTRE (SMBsetattrE) interface - only via setfileinfo() */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       time_t create_time;
+                       time_t access_time;
+                       time_t write_time;
+               } in;
+       } setattre;
+
+       
+       /* RAW_SFILEINFO_STANDARD interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+               struct {
+                       time_t create_time;
+                       time_t access_time;
+                       time_t write_time;
+                       /* notice that size, alloc_size and attrib are not settable,
+                          unlike the corresponding qfileinfo level */
+               } in;
+       } standard;
+
+       /* RAW_SFILEINFO_EA_SET interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+               struct {
+                       struct ea_struct ea;
+               } in;
+       } ea_set;
+
+       /* RAW_SFILEINFO_BASIC_INFO and
+          RAW_SFILEINFO_BASIC_INFORMATION interfaces */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       uint32 attrib;
+               } in;
+       } basic_info;
+
+       /* RAW_SFILEINFO_DISPOSITION_INFO and 
+          RAW_SFILEINFO_DISPOSITION_INFORMATION interfaces */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       BOOL delete_on_close;
+               } in;
+       } disposition_info;
+
+       /* RAW_SFILEINFO_ALLOCATION_INFO and 
+          RAW_SFILEINFO_ALLOCATION_INFORMATION interfaces */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       /* w2k3 rounds this up to nearest 4096 */
+                       large_t alloc_size;
+               } in;
+       } allocation_info;
+       
+       /* RAW_SFILEINFO_END_OF_FILE_INFO and 
+          RAW_SFILEINFO_END_OF_FILE_INFORMATION interfaces */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       large_t size;
+               } in;
+       } end_of_file_info;
+
+       /* RAW_SFILEINFO_RENAME_INFORMATION interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       uint8 overwrite;
+                       uint32 root_fid;
+                       const char *new_name;
+               } in;
+       } rename_information;
+
+       /* RAW_SFILEINFO_POSITION_INFORMATION interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       large_t position;
+               } in;
+       } position_information;
+
+       /* RAW_SFILEINFO_MODE_INFORMATION interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+
+               struct {
+                       /* valid values seem to be 0, 2, 4 and 6 */
+                       uint32 mode;
+               } in;
+       } mode_information;
+
+
+
+       /* RAW_SFILEINFO_UNIX_BASIC interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+               struct {
+                       uint32 mode; /* yuck - this field remains to fix compile of libcli/clifile.c */
+                       large_t end_of_file;
+                       large_t num_bytes;
+                       NTTIME status_change_time;
+                       NTTIME access_time;
+                       NTTIME change_time;
+                       large_t uid;
+                       large_t gid;
+                       uint32 file_type;
+                       large_t dev_major;
+                       large_t dev_minor;
+                       large_t unique_id;
+                       large_t permissions;
+                       large_t nlink;
+               } in;
+       } unix_basic;
+       
+       /* RAW_SFILEINFO_UNIX_LINK, RAW_SFILEINFO_UNIX_HLINK interface */
+       struct {
+               enum setfileinfo_level level;
+               union setfileinfo_file file;
+               struct {
+                       const char *link_dest;
+               } in;
+       } unix_link, unix_hlink;
+};
+
+
+enum fsinfo_level {RAW_QFS_GENERIC                        = 0xF000, 
+                  RAW_QFS_DSKATTR,                         /* SMBdskattr */
+                  RAW_QFS_ALLOCATION                     = SMB_QFS_ALLOCATION,
+                  RAW_QFS_VOLUME                         = SMB_QFS_VOLUME,
+                  RAW_QFS_VOLUME_INFO                    = SMB_QFS_VOLUME_INFO,
+                  RAW_QFS_SIZE_INFO                      = SMB_QFS_SIZE_INFO,
+                  RAW_QFS_DEVICE_INFO                    = SMB_QFS_DEVICE_INFO,
+                  RAW_QFS_ATTRIBUTE_INFO                 = SMB_QFS_ATTRIBUTE_INFO,
+                  RAW_QFS_UNIX_INFO                      = SMB_QFS_UNIX_INFO,
+                  RAW_QFS_VOLUME_INFORMATION             = SMB_QFS_VOLUME_INFORMATION,
+                  RAW_QFS_SIZE_INFORMATION               = SMB_QFS_SIZE_INFORMATION,
+                  RAW_QFS_DEVICE_INFORMATION             = SMB_QFS_DEVICE_INFORMATION,
+                  RAW_QFS_ATTRIBUTE_INFORMATION          = SMB_QFS_ATTRIBUTE_INFORMATION,
+                  RAW_QFS_QUOTA_INFORMATION              = SMB_QFS_QUOTA_INFORMATION,
+                  RAW_QFS_FULL_SIZE_INFORMATION          = SMB_QFS_FULL_SIZE_INFORMATION,
+                  RAW_QFS_OBJECTID_INFORMATION           = SMB_QFS_OBJECTID_INFORMATION};
+
+
+/* union for fsinfo() backend call. Note that there are no in
+   structures, as this call only contains out parameters */
+union smb_fsinfo {
+       /* generic interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint32 block_size;
+                       large_t blocks_total;
+                       large_t blocks_free;
+                       uint32 fs_id;
+                       NTTIME create_time;
+                       uint32 serial_number;
+                       uint32 fs_attr;
+                       uint32 max_file_component_length;
+                       uint32 device_type;
+                       uint32 device_characteristics;
+                       large_t quota_soft;
+                       large_t quota_hard;
+                       large_t quota_flags;
+                       GUID guid;
+                       char *volume_name;
+                       char *fs_type;
+               } out;
+       } generic;
+
+       /* SMBdskattr interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint16 units_total;
+                       uint16 blocks_per_unit;
+                       uint16 block_size;
+                       uint16 units_free;
+               } out;
+       } dskattr;
+
+       /* trans2 RAW_QFS_ALLOCATION interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint32 fs_id;
+                       uint32 sectors_per_unit;
+                       uint32 total_alloc_units;
+                       uint32 avail_alloc_units;
+                       uint16 bytes_per_sector;
+               } out;
+       } allocation;
+
+       /* TRANS2 RAW_QFS_VOLUME interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint32 serial_number;
+                       WIRE_STRING volume_name;
+               } out;
+       } volume;
+
+       /* TRANS2 RAW_QFS_VOLUME_INFO and RAW_QFS_VOLUME_INFORMATION interfaces */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       NTTIME create_time;
+                       uint32 serial_number;
+                       WIRE_STRING volume_name;
+               } out;
+       } volume_info;
+
+       /* trans2 RAW_QFS_SIZE_INFO and RAW_QFS_SIZE_INFORMATION interfaces */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       large_t total_alloc_units;
+                       large_t avail_alloc_units; /* maps to call_avail_alloc_units */
+                       uint32 sectors_per_unit;
+                       uint32 bytes_per_sector;
+               } out;
+       } size_info;
+
+       /* TRANS2 RAW_QFS_DEVICE_INFO and RAW_QFS_DEVICE_INFORMATION interfaces */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint32 device_type;
+                       uint32 characteristics;
+               } out;
+       } device_info;
+
+
+       /* TRANS2 RAW_QFS_ATTRIBUTE_INFO and RAW_QFS_ATTRIBUTE_INFORMATION interfaces */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint32 fs_attr;
+                       uint32 max_file_component_length;
+                       WIRE_STRING fs_type;
+               } out;
+       } attribute_info;
+
+
+       /* TRANS2 RAW_QFS_UNIX_INFO interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       uint16 major_version;
+                       uint16 minor_version;
+                       large_t capability;
+               } out;
+       } unix_info;
+
+       /* trans2 RAW_QFS_QUOTA_INFORMATION interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       large_t unknown[3];
+                       large_t quota_soft;
+                       large_t quota_hard;
+                       large_t quota_flags;
+               } out;
+       } quota_information;    
+
+       /* trans2 RAW_QFS_FULL_SIZE_INFORMATION interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       large_t total_alloc_units;
+                       large_t call_avail_alloc_units;
+                       large_t actual_avail_alloc_units;
+                       uint32 sectors_per_unit;
+                       uint32 bytes_per_sector;
+               } out;
+       } full_size_information;
+
+       /* trans2 RAW_QFS_OBJECTID_INFORMATION interface */
+       struct {
+               enum fsinfo_level level;
+
+               struct {
+                       GUID  guid;
+                       large_t unknown[6];
+               } out;
+       } objectid_information; 
+};
+
+
+
+enum open_level {RAW_OPEN_OPEN, RAW_OPEN_OPENX, 
+                RAW_OPEN_MKNEW, RAW_OPEN_CTEMP, RAW_OPEN_SPLOPEN,
+                RAW_OPEN_NTCREATEX, RAW_OPEN_T2OPEN};
+
+/* the generic interface is defined to be equal to the NTCREATEX interface */
+#define RAW_OPEN_GENERIC RAW_OPEN_NTCREATEX
+
+/* union for open() backend call */
+union smb_open {
+       /* SMBNTCreateX interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint32 flags;
+                       uint32 root_fid;
+                       uint32 access_mask;
+                       large_t alloc_size;
+                       uint32 file_attr;
+                       uint32 share_access;
+                       uint32 open_disposition;
+                       uint32 create_options;
+                       uint32 impersonation;
+                       uint8  security_flags;
+                       const char *fname;
+               } in;
+
+               struct {
+                       uint8 oplock_level;
+                       uint16 fnum;
+                       uint32 create_action;
+                       NTTIME create_time;
+                       NTTIME access_time;
+                       NTTIME write_time;
+                       NTTIME change_time;
+                       uint32 attrib;
+                       large_t alloc_size;
+                       large_t size;
+                       uint16 file_type;
+                       uint16 ipc_state;
+                       uint8  is_directory;
+               } out;
+       } ntcreatex, generic;
+
+       /* TRANS2_OPEN interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 flags;
+                       uint16 open_mode;
+                       uint16 file_attrs;
+                       time_t write_time;
+                       uint16 open_func;
+                       uint32 size;
+                       uint32 timeout;
+                       const char *fname;
+                       uint_t num_eas;
+                       struct ea_struct *eas;                  
+               } in;
+
+               struct {
+                       uint16 fnum;
+                       uint16 attrib;
+                       time_t write_time;
+                       uint32 size;
+                       uint16 access;
+                       uint16 ftype;
+                       uint16 devstate;
+                       uint16 action;
+                       uint32 unknown;
+               } out;
+       } t2open;
+
+       /* SMBopen interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 flags;
+                       uint16 search_attrs;
+                       const char *fname;
+               } in;
+               struct {
+                       uint16 fnum;
+                       uint16 attrib;
+                       time_t write_time;
+                       uint32 size;
+                       uint16 rmode;
+               } out;
+       } open;
+
+       /* SMBopenX interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 flags;
+                       uint16 open_mode;
+                       uint16 search_attrs; /* not honoured by win2003 */
+                       uint16 file_attrs;
+                       time_t write_time; /* not honoured by win2003 */
+                       uint16 open_func;
+                       uint32 size; /* note that this sets the
+                                       initial file size, not
+                                       just allocation size */
+                       uint32 timeout; /* not honoured by win2003 */
+                       const char *fname;
+               } in;
+               struct {
+                       uint16 fnum;
+                       uint16 attrib;
+                       time_t write_time;
+                       uint32 size;
+                       uint16 access;
+                       uint16 ftype;
+                       uint16 devstate;
+                       uint16 action;
+                       uint32 unique_fid;
+                       uint32 access_mask;
+                       uint32 unknown;
+               } out;
+       } openx;
+
+       /* SMBmknew interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 attrib;
+                       time_t write_time;
+                       const char *fname;
+               } in;
+               struct {
+                       uint16 fnum;
+               } out;
+       } mknew;
+
+       /* SMBctemp interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 attrib;
+                       time_t write_time;
+                       const char *directory;
+               } in;
+               struct {
+                       uint16 fnum;
+                       /* temp name, relative to directory */
+                       char *name; 
+               } out;
+       } ctemp;
+
+       /* SMBsplopen interface */
+       struct {
+               enum open_level level;
+
+               struct {
+                       uint16 setup_length;
+                       uint16 mode;
+                       const char *ident;
+               } in;
+               struct {
+                       uint16 fnum;
+               } out;
+       } splopen;
+};
+
+
+
+enum read_level {RAW_READ_GENERIC, RAW_READ_READBRAW, RAW_READ_LOCKREAD, RAW_READ_READ, RAW_READ_READX};
+
+/* union for read() backend call 
+
+   note that .infoX.out.data will be allocated before the backend is
+   called. It will be big enough to hold the maximum size asked for
+*/
+union smb_read {
+       /* generic interface */
+       struct {
+               enum read_level level;
+
+               struct {
+                       uint16 fnum;
+                       SMB_BIG_UINT offset;
+                       uint32    size;
+               } in;
+               struct {
+                       char *data;
+                       uint32 nread;
+               } out;
+       } generic;
+
+
+       /* SMBreadbraw interface */
+       struct {
+               enum read_level level;
+
+               struct {
+                       uint16 fnum;
+                       SMB_BIG_UINT offset;
+                       uint16  maxcnt;
+                       uint16  mincnt;
+                       uint32  timeout;
+               } in;
+               struct {
+                       char *data;
+                       uint32 nread;
+               } out;
+       } readbraw;
+
+
+       /* SMBlockandread interface */
+       struct {
+               enum read_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       uint32 offset;
+                       uint16 remaining;
+               } in;
+               struct {
+                       char *data;
+                       uint16 nread;
+               } out;
+       } lockread;
+
+       /* SMBread interface */
+       struct {
+               enum read_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       uint32 offset;
+                       uint16 remaining;
+               } in;
+               struct {
+                       char *data;
+                       uint16 nread;
+               } out;
+       } read;
+
+       /* SMBreadX interface */
+       struct {
+               enum read_level level;
+
+               struct {
+                       uint16 fnum;
+                       SMB_BIG_UINT offset;
+                       uint16 mincnt;
+                       uint16 maxcnt;
+                       uint16 remaining;
+               } in;
+               struct {
+                       char *data;
+                       uint16 remaining;
+                       uint16 compaction_mode;
+                       uint16 nread;
+               } out;
+       } readx;
+};
+
+
+enum write_level {RAW_WRITE_GENERIC, RAW_WRITE_WRITEUNLOCK, RAW_WRITE_WRITE, 
+                 RAW_WRITE_WRITEX, RAW_WRITE_WRITECLOSE, RAW_WRITE_SPLWRITE};
+
+/* union for write() backend call 
+*/
+union smb_write {
+       /* generic interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       SMB_BIG_UINT offset;
+                       uint32    count;
+                       const char *data;
+               } in;
+               struct {
+                       uint32 nwritten;
+               } out;
+       } generic;
+
+
+       /* SMBwriteunlock interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       uint32 offset;
+                       uint16 remaining;
+                       const char *data;
+               } in;
+               struct {
+                       uint32 nwritten;
+               } out;
+       } writeunlock;
+
+       /* SMBwrite interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       uint32 offset;
+                       uint16 remaining;
+                       const char *data;
+               } in;
+               struct {
+                       uint16 nwritten;
+               } out;
+       } write;
+
+       /* SMBwriteX interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       SMB_BIG_UINT offset;
+                       uint16 wmode;
+                       uint16 remaining;
+                       uint32 count;
+                       const char *data;
+               } in;
+               struct {
+                       uint32 nwritten;
+                       uint16 remaining;
+               } out;
+       } writex;
+
+       /* SMBwriteclose interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       uint32 offset;
+                       time_t mtime;
+                       const char *data;
+               } in;
+               struct {
+                       uint16 nwritten;
+               } out;
+       } writeclose;
+
+       /* SMBsplwrite interface */
+       struct {
+               enum write_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 count;
+                       const char *data;
+               } in;
+       } splwrite;
+};
+
+
+enum lock_level {RAW_LOCK_GENERIC, RAW_LOCK_LOCK, RAW_LOCK_UNLOCK, RAW_LOCK_LOCKX};
+
+/* union for lock() backend call 
+*/
+union smb_lock {
+       /* generic interface */
+       struct {
+               enum lock_level level;
+
+               struct {
+
+               } in;
+       } generic;
+
+       /* SMBlock interface */
+       struct {
+               enum lock_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint32 count;
+                       uint32 offset;
+               } in;
+       } lock;
+
+       /* SMBunlock interface */
+       struct {
+               enum lock_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint32 count;
+                       uint32 offset;
+               } in;
+       } unlock;
+
+       /* SMBlockingX interface */
+       struct {
+               enum lock_level level;
+
+               struct {
+                       uint16 fnum;
+                       uint16 mode;
+                       uint32 timeout;
+                       uint16 ulock_cnt;
+                       uint16 lock_cnt;
+                       struct smb_lock_entry {
+                               uint16 pid;
+                               SMB_BIG_UINT offset;
+                               SMB_BIG_UINT count;
+                       } *locks; /* unlocks are first in the arrray */
+               } in;
+       } lockx;
+};
+
+
+enum close_enum {RAW_CLOSE_GENERIC, RAW_CLOSE_CLOSE, RAW_CLOSE_SPLCLOSE};
+
+/*
+  union for close() backend call
+*/
+union smb_close {
+       /* generic interface */
+       struct {
+               enum close_enum level;
+
+               struct {
+                       uint16 fnum;
+               } in;
+       } generic;
+
+       /* SMBclose interface */
+       struct {
+               enum close_enum level;
+
+               struct {
+                       uint16 fnum;
+                       time_t write_time;
+               } in;
+       } close;
+
+       /* SMBsplclose interface - empty! */
+       struct {
+               enum close_enum level;
+
+               struct {
+                       uint16 fnum;
+               } in;
+       } splclose;
+};
+
+
+enum lpq_level {RAW_LPQ_GENERIC, RAW_LPQ_RETQ};
+
+/*
+  union for lpq() backend
+*/
+union smb_lpq {
+       /* generic interface */
+       struct {
+               enum lpq_level level;
+
+       } generic;
+
+
+       /* SMBsplretq interface */
+       struct {
+               enum lpq_level level;
+
+               struct {
+                       uint16 maxcount;
+                       uint16 startidx;
+               } in;
+               struct {
+                       uint16 count;
+                       uint16 restart_idx;
+                       struct {
+                               time_t time;
+                               uint8 status;
+                               uint16 job;
+                               uint32 size;
+                               char *user;
+                       } *queue;
+               } out;
+       } retq;
+};
+
+
+/* struct for SMBioctl */
+struct smb_ioctl {
+       struct {
+               uint16 fnum;
+               uint32 request;
+       } in;
+       struct {
+               DATA_BLOB blob;
+       } out;
+};
+
+/* struct for SMBflush */
+struct smb_flush {
+       struct {
+               uint16 fnum;
+       } in;
+};
+
+
+/* struct for SMBcopy */
+struct smb_copy {
+       struct {
+               uint16 tid2;
+               uint16 ofun;
+               uint16 flags;
+               const char *path1;
+               const char *path2;
+       } in;
+       struct {
+               uint16 count;
+       } out;
+};
+
+
+/* struct for transact2 call */
+struct smb_trans2 {
+       struct {
+               uint16 max_param;
+               uint16 max_data;
+               uint8  max_setup;
+               uint16 flags;
+               uint32 timeout;
+               uint8  setup_count;
+               uint16 *setup;
+               DATA_BLOB params;
+               DATA_BLOB data;
+       } in;
+
+       struct {
+               uint8  setup_count;
+               uint16 *setup;
+               DATA_BLOB params;
+               DATA_BLOB data;
+       } out;
+};
+
+/* struct for nttransact2 call */
+struct smb_nttrans {
+       struct {
+               uint8  max_setup;
+               uint32 max_param;
+               uint32 max_data;
+               uint32 setup_count;
+               uint16 function;
+               uint16 *setup;
+               DATA_BLOB params;
+               DATA_BLOB data;
+       } in;
+
+       struct {
+               uint8  setup_count;
+               uint16 *setup;
+               DATA_BLOB params;
+               DATA_BLOB data;
+       } out;
+};
+
+
+/* struct for nttrans change notify call */
+struct smb_notify {
+       struct {
+               uint32 buffer_size;
+               uint32 completion_filter;
+               uint16 fnum;
+               BOOL recursive;
+       } in;
+
+       struct {
+               uint32 num_changes;
+               struct {
+                       uint32 action;
+                       WIRE_STRING name;
+               } *changes;
+       } out;
+};
+
+/* struct for NT ioctl call */
+struct smb_ntioctl {
+       struct {
+               uint32 function;
+               uint16 fnum;
+               BOOL fsctl;
+               uint8 filter;
+       } in;
+};
+
+
+enum search_level {RAW_SEARCH_GENERIC                 = 0xF000, 
+                  RAW_SEARCH_SEARCH,                 /* SMBsearch */ 
+                  RAW_SEARCH_STANDARD                = SMB_FIND_STANDARD,
+                  RAW_SEARCH_EA_SIZE                 = SMB_FIND_EA_SIZE,
+                  RAW_SEARCH_DIRECTORY_INFO          = SMB_FIND_DIRECTORY_INFO,
+                  RAW_SEARCH_FULL_DIRECTORY_INFO     = SMB_FIND_FULL_DIRECTORY_INFO,
+                  RAW_SEARCH_NAME_INFO               = SMB_FIND_NAME_INFO,
+                  RAW_SEARCH_BOTH_DIRECTORY_INFO     = SMB_FIND_BOTH_DIRECTORY_INFO,
+                  RAW_SEARCH_261                     = SMB_FIND_261,
+                  RAW_SEARCH_262                     = SMB_FIND_262,
+                  RAW_SEARCH_UNIX_INFO               = SMB_FIND_UNIX_INFO};
+
+       
+/* union for file search */
+union smb_search_first {
+       struct {
+               enum search_level level;
+       } generic;
+       
+       /* search (old) findfirst interface */
+       struct {
+               enum search_level level;
+       
+               struct {
+                       uint16 max_count;
+                       uint16 search_attrib;
+                       const char *pattern;
+               } in;
+               struct {
+                       int16 count;
+               } out;
+       } search_first;
+
+       /* trans2 findfirst interface */
+       struct {
+               enum search_level level;
+               
+               struct {
+                       uint16 search_attrib;
+                       uint16 max_count;
+                       uint16 flags;
+                       uint32 storage_type;
+                       const char *pattern;
+               } in;
+               struct {
+                       uint16 handle;
+                       uint16 count;
+                       uint16 end_of_search;
+               } out;
+       } t2ffirst;
+};
+
+/* union for file search continue */
+union smb_search_next {
+       struct {
+               enum search_level level;
+       } generic;
+
+       /* search (old) findnext interface */
+       struct {
+               enum search_level level;
+       
+               struct {
+                       uint16 max_count;
+                       uint16 search_attrib;
+                       DATA_BLOB search_id;
+               } in;
+               struct {
+                       uint16 count;
+               } out;
+       } search_next;
+       
+       /* trans2 findnext interface */
+       struct {
+               enum search_level level;
+               
+               struct {
+                       uint16 handle;
+                       uint16 max_count;
+                       uint32 resume_key;
+                       uint16 flags;
+                       const char *last_name;
+               } in;
+               struct {
+                       uint16 count;
+                       uint16 end_of_search;
+               } out;
+       } t2fnext;
+};
+
+/* union for search reply file data */
+union smb_search_data {
+       /* search (old) findfirst */
+       struct {
+               uint16 attrib;
+               time_t write_time;
+               uint32 size;
+               DATA_BLOB search_id;  /* used to resume search from this point */
+               char *name;
+       } search;
+       
+       /* trans2 findfirst RAW_SEARCH_STANDARD level */
+       struct {
+               uint32 resume_key;
+               time_t create_time;
+               time_t access_time;
+               time_t write_time;
+               uint32 size;
+               uint32 alloc_size;
+               uint16 attrib;
+               WIRE_STRING name;
+       } standard;
+
+       /* trans2 findfirst RAW_SEARCH_EA_SIZE level */
+       struct {
+               uint32 resume_key;
+               time_t create_time;
+               time_t access_time;
+               time_t write_time;
+               uint32 size;
+               uint32 alloc_size;
+               uint16 attrib;
+               uint32 ea_size;
+               WIRE_STRING name;
+       } ea_size;
+
+       /* RAW_SEARCH_DIRECTORY_INFO interface */
+       struct {
+               uint32 file_index;
+               NTTIME create_time;
+               NTTIME access_time;
+               NTTIME write_time;
+               NTTIME change_time;
+               large_t  size;
+               large_t  alloc_size;
+               uint32   attrib;
+               WIRE_STRING name;
+       } directory_info;
+
+       /* RAW_SEARCH_FULL_DIRECTORY_INFO interface */
+       struct {
+               uint32 file_index;
+               NTTIME create_time;
+               NTTIME access_time;
+               NTTIME write_time;
+               NTTIME change_time;
+               large_t  size;
+               large_t  alloc_size;
+               uint32   attrib;
+               uint32   ea_size;
+               WIRE_STRING name;
+       } full_directory_info;
+
+       /* RAW_SEARCH_NAME_INFO interface */
+       struct {
+               uint32 file_index;
+               WIRE_STRING name;
+       } name_info;
+
+       /* RAW_SEARCH_BOTH_DIRECTORY_INFO interface */
+       struct {
+               uint32 file_index;
+               NTTIME create_time;
+               NTTIME access_time;
+               NTTIME write_time;
+               NTTIME change_time;
+               large_t  size;
+               large_t  alloc_size;
+               uint32   attrib;
+               uint32   ea_size;
+               WIRE_STRING short_name;
+               WIRE_STRING name;
+       } both_directory_info;
+
+       /* RAW_SEARCH_261 interface */
+       struct {
+               uint32 file_index;
+               NTTIME create_time;
+               NTTIME access_time;
+               NTTIME write_time;
+               NTTIME change_time;
+               large_t size;
+               large_t alloc_size;
+               uint32 attrib;
+               uint32 ea_size;
+               uint32 unknown[3];
+               WIRE_STRING name;
+       } level_261;
+
+       /* RAW_SEARCH_262 interface */
+       struct {
+               uint32 file_index;
+               NTTIME create_time;
+               NTTIME access_time;
+               NTTIME write_time;
+               NTTIME change_time;
+               large_t size;
+               large_t alloc_size;
+               uint32  attrib;
+               uint32  ea_size;
+               uint32  unknown[2];
+               WIRE_STRING short_name;
+               WIRE_STRING name;
+       } level_262;
+
+       /* RAW_SEARCH_UNIX_INFO interface */
+       struct {
+               large_t end_of_file;
+               large_t num_bytes;
+               NTTIME status_change_time;
+               NTTIME access_time;
+               NTTIME change_time;
+               large_t uid;
+               large_t gid;
+               uint32 file_type;
+               large_t dev_major;
+               large_t dev_minor;
+               large_t unique_id;
+               large_t permissions;
+               large_t nlink;          
+       } unix_info;
+};
+
+
+enum search_close_level {RAW_FINDCLOSE_GENERIC, RAW_FINDCLOSE_CLOSE};
+
+/* union for file search close */
+union smb_search_close {
+       struct {
+               enum search_close_level level;
+       } generic;
+
+       /* SMBfindclose interface */
+       struct {
+               enum search_level level;
+               
+               struct {
+                       uint16 handle;
+               } in;
+       } findclose;
+};
+
diff --git a/source4/include/smb_macros.h b/source4/include/smb_macros.h
new file mode 100644 (file)
index 0000000..f6a9fb0
--- /dev/null
@@ -0,0 +1,290 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1999
+   Copyright (C) John H Terpstra 1996-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+   Copyright (C) Paul Ashton 1998 - 1999
+   
+   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.
+*/
+
+#ifndef _SMB_MACROS_H
+#define _SMB_MACROS_H
+
+/* Misc bit macros */
+#define BOOLSTR(b) ((b) ? "Yes" : "No")
+#define BITSETB(ptr,bit) ((((char *)ptr)[0] & (1<<(bit)))!=0)
+#define BITSETW(ptr,bit) ((SVAL(ptr,0) & (1<<(bit)))!=0)
+
+/* for readability... */
+#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
+#define IS_DOS_DIR(test_mode)      (((test_mode) & aDIR) != 0)
+#define IS_DOS_ARCHIVE(test_mode)  (((test_mode) & aARCH) != 0)
+#define IS_DOS_SYSTEM(test_mode)   (((test_mode) & aSYSTEM) != 0)
+#define IS_DOS_HIDDEN(test_mode)   (((test_mode) & aHIDDEN) != 0)
+
+#ifndef SAFE_FREE /* Oh no this is also defined in tdb.h */
+
+/**
+ * Free memory if the pointer and zero the pointer.
+ *
+ * @note You are explicitly allowed to pass NULL pointers -- they will
+ * always be ignored.
+ **/
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+#endif
+
+/* zero a structure */
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+
+/* zero a structure given a pointer to the structure - no zero check */
+#define ZERO_STRUCTPN(x) memset((char *)(x), 0, sizeof(*(x)))
+
+/* zero an array - note that sizeof(array) must work - ie. it must not be a 
+   pointer */
+#define ZERO_ARRAY(x) memset((char *)(x), 0, sizeof(x))
+
+/* pointer difference macro */
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+
+/* work out how many elements there are in a static array */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/* assert macros */
+#define SMB_ASSERT(b) do { if (!(b)) { \
+       DEBUG(0,("PANIC: assert failed at %s(%d)\n", __FILE__, __LINE__)); \
+       smb_panic("assert failed"); }} while (0)
+
+#define SMB_ASSERT_ARRAY(a,n) SMB_ASSERT((sizeof(a)/sizeof((a)[0])) >= (n))
+
+/* these are useful macros for checking validity of handles */
+#define OPEN_FSP(fsp)    ((fsp) && !(fsp)->is_directory)
+#define OPEN_CONN(conn)    ((conn) && (conn)->open)
+#define IS_IPC(conn)       ((conn) && (conn)->ipc)
+#define IS_PRINT(conn)       ((conn) && (conn)->printer)
+#define FNUM_OK(fsp,c) (OPEN_FSP(fsp) && (c)==(fsp)->conn)
+
+#define CHECK_FSP(fsp,conn) if (!FNUM_OK(fsp,conn)) \
+                               return(ERROR_DOS(ERRDOS,ERRbadfid)); \
+                       else if((fsp)->fd == -1) \
+                               return(ERROR_DOS(ERRDOS,ERRbadaccess))
+
+#define CHECK_READ(fsp) if (!(fsp)->can_read) \
+                               return(ERROR_DOS(ERRDOS,ERRbadaccess))
+#define CHECK_WRITE(fsp) if (!(fsp)->can_write) \
+                               return(ERROR_DOS(ERRDOS,ERRbadaccess))
+
+#define CHECK_ERROR(fsp) if (HAS_CACHED_ERROR(fsp)) \
+                               return(CACHED_ERROR(fsp))
+
+#define ERROR_WAS_LOCK_DENIED(status) (NT_STATUS_EQUAL((status), NT_STATUS_LOCK_NOT_GRANTED) || \
+                               NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT) )
+
+/* translates a connection number into a service number */
+#define SNUM(conn)         ((conn)?(conn)->service:-1)
+
+/* access various service details */
+#define SERVICE(snum)      (lp_servicename(snum))
+#define PRINTERNAME(snum)  (lp_printername(snum))
+#define CAN_WRITE(conn)    (!conn->read_only)
+#define VALID_SNUM(snum)   (lp_snum_ok(snum))
+#define GUEST_OK(snum)     (VALID_SNUM(snum) && lp_guest_ok(snum))
+#define GUEST_ONLY(snum)   (VALID_SNUM(snum) && lp_guest_only(snum))
+#define CAN_SETDIR(snum)   (!lp_no_set_dir(snum))
+#define CAN_PRINT(conn)    ((conn) && lp_print_ok((conn)->service))
+#define MAP_HIDDEN(conn)   ((conn) && lp_map_hidden((conn)->service))
+#define MAP_SYSTEM(conn)   ((conn) && lp_map_system((conn)->service))
+#define MAP_ARCHIVE(conn)   ((conn) && lp_map_archive((conn)->service))
+#define IS_HIDDEN_PATH(conn,path)  ((conn) && is_in_path((path),(conn)->hide_list))
+#define IS_VETO_PATH(conn,path)  ((conn) && is_in_path((path),(conn)->veto_list))
+#define IS_VETO_OPLOCK_PATH(conn,path)  ((conn) && is_in_path((path),(conn)->veto_oplock_list))
+
+/* 
+ * Used by the stat cache code to check if a returned
+ * stat structure is valid.
+ */
+
+#define VALID_STAT(st) ((st).st_nlink != 0)  
+#define VALID_STAT_OF_DIR(st) (VALID_STAT(st) && S_ISDIR((st).st_mode))
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef ABS
+#define ABS(a) ((a)>0?(a):(-(a)))
+#endif
+
+/* Macros to get at offsets within smb_lkrng and smb_unlkrng
+   structures. We cannot define these as actual structures
+   due to possible differences in structure packing
+   on different machines/compilers. */
+
+#define SMB_LPID_OFFSET(indx) (10 * (indx))
+#define SMB_LKOFF_OFFSET(indx) ( 2 + (10 * (indx)))
+#define SMB_LKLEN_OFFSET(indx) ( 6 + (10 * (indx)))
+#define SMB_LARGE_LPID_OFFSET(indx) (20 * (indx))
+#define SMB_LARGE_LKOFF_OFFSET_HIGH(indx) (4 + (20 * (indx)))
+#define SMB_LARGE_LKOFF_OFFSET_LOW(indx) (8 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_HIGH(indx) (12 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_LOW(indx) (16 + (20 * (indx)))
+
+/* Macro to cache an error in a write_bmpx_struct */
+#define CACHE_ERROR(w,c,e) ((w)->wr_errclass = (c), (w)->wr_error = (e), \
+                w->wr_discard = True, -1)
+/* Macro to test if an error has been cached for this fnum */
+#define HAS_CACHED_ERROR(fsp) ((fsp)->wbmpx_ptr && \
+                (fsp)->wbmpx_ptr->wr_discard)
+/* Macro to turn the cached error into an error packet */
+#define CACHED_ERROR(fsp) cached_error_packet(outbuf,fsp,__LINE__,__FILE__)
+
+/* these are the datagram types */
+#define DGRAM_DIRECT_UNIQUE 0x10
+
+#define ERROR_DOS(class,code) error_packet(outbuf,NT_STATUS_OK,class,code,__LINE__,__FILE__)
+#define ERROR_NT(status) error_packet(outbuf,status,0,0,__LINE__,__FILE__)
+#define ERROR_BOTH(status,class,code) error_packet(outbuf,status,class,code,__LINE__,__FILE__)
+
+/* this is how errors are generated */
+#define UNIXERROR(defclass,deferror) unix_error_packet(outbuf,defclass,deferror,__LINE__,__FILE__)
+
+#define SMB_ROUNDUP(x,r) ( ((x)%(r)) ? ( (((x)+(r))/(r))*(r) ) : (x))
+
+/* REWRITE TODO: remove these smb_xxx macros */
+#define smb_buf(buf) (((char *)(buf)) + MIN_SMB_SIZE + CVAL(buf,HDR_WCT+4)*2)
+
+/* the remaining number of bytes in smb buffer 'buf' from pointer 'p'. */
+#define smb_bufrem(buf, p) (smb_buflen(buf)-PTR_DIFF(p, smb_buf(buf)))
+
+
+#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
+#define _smb_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0x10000)>>16; \
+        (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0)
+
+/*******************************************************************
+find the difference in milliseconds between two struct timeval
+values
+********************************************************************/
+
+#define TvalDiff(tvalold,tvalnew) \
+  (((tvalnew)->tv_sec - (tvalold)->tv_sec)*1000 +  \
+        ((int)(tvalnew)->tv_usec - (int)(tvalold)->tv_usec)/1000)
+
+/****************************************************************************
+true if two IP addresses are equal
+****************************************************************************/
+
+#define ip_equal(ip1,ip2) ((ip1).s_addr == (ip2).s_addr)
+
+/*****************************************************************
+ splits out the last subkey of a key
+ *****************************************************************/  
+
+#define reg_get_subkey(full_keyname, key_name, subkey_name) \
+       split_at_last_component(full_keyname, key_name, '\\', subkey_name)
+
+/****************************************************************************
+ Used by dptr_zero.
+****************************************************************************/
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+ Return True if the offset is at zero.
+****************************************************************************/
+
+#define dptr_zero(buf) ((IVAL(buf,1)&~DPTR_MASK) == 0)
+
+/*******************************************************************
+copy an IP address from one buffer to another
+********************************************************************/
+
+#define putip(dest,src) memcpy(dest,src,4)
+
+/*******************************************************************
+ Return True if a server has CIFS UNIX capabilities.
+********************************************************************/
+
+#define SERVER_HAS_UNIX_CIFS(c) (cli_state_has_unix_cifs(c))
+
+/****************************************************************************
+ Make a filename into unix format.
+****************************************************************************/
+
+#define unix_format(fname) string_replace(fname,'\\','/')
+#define unix_format_w(fname) string_replace_w(fname, UCS2_CHAR('\\'), UCS2_CHAR('/'))
+
+/****************************************************************************
+ Make a file into DOS format.
+****************************************************************************/
+
+#define dos_format(fname) string_replace(fname,'/','\\')
+
+/*******************************************************************
+ vfs stat wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_stat(conn, fname, st) ((conn)->vfs_ops.stat((conn), fname,(st)))
+
+/*******************************************************************
+ vfs lstat wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_lstat(conn, fname, st) ((conn)->vfs_ops.lstat((conn), fname,(st)))
+
+/*******************************************************************
+ vfs fstat wrapper
+********************************************************************/
+
+#define vfs_fstat(fsp, fd, st) ((fsp)->conn->vfs_ops.fstat((fsp),(fd),(st)))
+
+/*******************************************************************
+ vfs rmdir wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_rmdir(conn,fname) ((conn)->vfs_ops.rmdir((conn),fname))
+
+/*******************************************************************
+ vfs Unlink wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_unlink(conn, fname) ((conn)->vfs_ops.unlink((conn),fname))
+
+/*******************************************************************
+ vfs chmod wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_chmod(conn,fname,mode) ((conn)->vfs_ops.chmod((conn),fname,(mode)))
+
+/*******************************************************************
+ vfs chown wrapper that calls internal2unix.
+********************************************************************/
+
+#define vfs_chown(conn,fname,uid,gid) ((conn)->vfs_ops.chown((conn),fname,(uid),(gid)))
+
+/*******************************************************************
+ A wrapper for vfs_chdir().
+********************************************************************/
+
+#define vfs_chdir(conn,fname) ((conn)->vfs_ops.chdir((conn),fname))
+
+#endif /* _SMB_MACROS_H */
diff --git a/source4/include/stamp-h.in b/source4/include/stamp-h.in
new file mode 100644 (file)
index 0000000..c9061b3
--- /dev/null
@@ -0,0 +1 @@
+Sun Jul 18 20:32:29 UTC 1999
diff --git a/source4/include/talloc.h b/source4/include/talloc.h
new file mode 100644 (file)
index 0000000..4badddb
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+   
+   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.
+*/
+
+/**
+ * @ingroup talloc
+ * @{
+ * @sa talloc.c
+ */
+
+/**
+ * talloc allocation pool.  All allocated blocks can be freed in one go.
+ **/
+typedef struct talloc_ctx TALLOC_CTX;
+
+TALLOC_CTX *talloc_init(char const *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+       PRINTF_ATTRIBUTE(2, 0);
+
+char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+       PRINTF_ATTRIBUTE(2, 3);
+
+char *talloc_vasprintf_append(TALLOC_CTX *t, char *, const char *, va_list ap)
+       PRINTF_ATTRIBUTE(3, 0);
+
+char *talloc_asprintf_append(TALLOC_CTX *t, char *, const char *, ...)
+       PRINTF_ATTRIBUTE(3, 4);
+
+/** @} */
+
+#endif /* ndef _TALLOC_H_ */
diff --git a/source4/include/tdbsam2.h b/source4/include/tdbsam2.h
new file mode 100644 (file)
index 0000000..0ca9d34
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * tdbsam2 genstruct enabled header file
+ * Copyright (C) Simo Sorce 2002
+ * 
+ * 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.
+ */
+
+
+/* ALL strings assumes UTF8 as encoding */
+
+GENSTRUCT struct tdbsam2_domain_data {
+       uint32 xcounter;                /* counter to be updated at any change */
+
+       SEC_DESC *sec_desc;             /* Security Descriptor */
+       DOM_SID *user_sid;              /* The User SID */
+       char *name; _NULLTERM           /* NT User Name */
+       char *description; _NULLTERM    /* Descritpion (Gecos) */
+};
+
+GENSTRUCT struct tdbsam2_user_data {
+       uint32 xcounter;                /* counter to be updated at any change */
+
+       SEC_DESC *sec_desc;             /* Security Descriptor */
+       DOM_SID *user_sid;              /* The User SID */
+       char *name; _NULLTERM           /* NT User Name */
+       char *description; _NULLTERM    /* Descritpion (Gecos) */
+
+       DOM_SID *group_sid;             /* The Primary Group SID */
+
+       NTTIME *logon_time;
+       NTTIME *logoff_time;
+       NTTIME *kickoff_time;
+       NTTIME *pass_last_set_time;
+       NTTIME *pass_can_change_time;
+       NTTIME *pass_must_change_time;
+       
+       char *full_name; _NULLTERM      /* The Full Name */
+       char *home_dir; _NULLTERM       /* Home Directory */
+       char *dir_drive; _NULLTERM      /* Drive Letter the home should be mapped to */
+       char *logon_script; _NULLTERM   /* Logon script path */
+       char *profile_path; _NULLTERM   /* Profile is stored here */
+       char *workstations; _NULLTERM   /* List of Workstation names the user is allowed to LogIn */
+       char *unknown_str; _NULLTERM    /* Guess ... Unknown */
+       char *munged_dial; _NULLTERM    /* Callback Number */
+
+       /* passwords are 16 byte leght, pointer is null if no password */
+       uint8 *lm_pw_ptr; _LEN(16)      /* Lanman hashed password */
+       uint8 *nt_pw_ptr; _LEN(16)      /* NT hashed password */
+
+       uint16 logon_divs;              /* 168 - num of hours in a week */
+       uint32 hours_len;               /* normally 21 */
+       uint8 *hours; _LEN(hours_len)   /* normally 21 bytes (depends on hours_len) */
+
+       uint32 unknown_3;               /* 0x00ff ffff */
+       uint32 unknown_5;               /* 0x0002 0000 */
+       uint32 unknown_6;               /* 0x0000 04ec */
+};     
+
+GENSTRUCT struct tdbsam2_group_data {
+       uint32 xcounter;                /* counter to be updated at any change */
+
+       SEC_DESC *sec_desc;             /* Security Descriptor */
+       DOM_SID *group_sid;             /* The Group SID */
+       char *name; _NULLTERM           /* NT User Name */
+       char *description; _NULLTERM    /* Descritpion (Gecos) */
+
+       uint32 count;                   /* number of sids */
+       DOM_SID **members; _LEN(count)  /* SID array */
+};
+
+GENSTRUCT struct tdbsam2_privilege_data {
+       uint32 xcounter;                /* counter to be updated at any change */
+
+       LUID_ATTR *privilege;           /* Privilege */
+       char *name; _NULLTERM           /* NT User Name */
+       char *description; _NULLTERM    /* Descritpion (Gecos) */
+
+       uint32 count;                   /* number of sids */
+       DOM_SID **members; _LEN(count)  /* SID array */
+};
+
diff --git a/source4/include/trans2.h b/source4/include/trans2.h
new file mode 100644 (file)
index 0000000..6a629f8
--- /dev/null
@@ -0,0 +1,428 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB transaction2 handling
+   Copyright (C) Jeremy Allison 1994-2002.
+   Copyright (C) Andrew Tridgell 1995-2003.
+
+   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.
+*/
+
+#ifndef _TRANS2_H_
+#define _TRANS2_H_
+
+/* These are the TRANS2 sub commands */
+#define TRANSACT2_OPEN                        0
+#define TRANSACT2_FINDFIRST                   1
+#define TRANSACT2_FINDNEXT                    2
+#define TRANSACT2_QFSINFO                     3
+#define TRANSACT2_SETFSINFO                   4
+#define TRANSACT2_QPATHINFO                   5
+#define TRANSACT2_SETPATHINFO                 6
+#define TRANSACT2_QFILEINFO                   7
+#define TRANSACT2_SETFILEINFO                 8
+#define TRANSACT2_FSCTL                       9
+#define TRANSACT2_IOCTL                     0xA
+#define TRANSACT2_FINDNOTIFYFIRST           0xB
+#define TRANSACT2_FINDNOTIFYNEXT            0xC
+#define TRANSACT2_MKDIR                     0xD
+#define TRANSACT2_SESSION_SETUP             0xE
+#define TRANSACT2_GET_DFS_REFERRAL         0x10
+#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11
+
+
+/* trans2 Query FS info levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for QFSINFO aliases
+        Found level    1 (0x001) of size 18 (0x12)
+        Found level    2 (0x002) of size 12 (0x0c)
+        Found level  258 (0x102) of size 26 (0x1a)
+        Found level  259 (0x103) of size 24 (0x18)
+        Found level  260 (0x104) of size  8 (0x08)
+        Found level  261 (0x105) of size 20 (0x14)
+        Found level 1001 (0x3e9) of size 26 (0x1a)
+        Found level 1003 (0x3eb) of size 24 (0x18)
+        Found level 1004 (0x3ec) of size  8 (0x08)
+        Found level 1005 (0x3ed) of size 20 (0x14)
+        Found level 1006 (0x3ee) of size 48 (0x30)
+        Found level 1007 (0x3ef) of size 32 (0x20)
+        Found level 1008 (0x3f0) of size 64 (0x40)
+Found 13 levels with success status
+        Level 261 (0x105) and level 1005 (0x3ed) are possible aliases
+        Level 260 (0x104) and level 1004 (0x3ec) are possible aliases
+        Level 259 (0x103) and level 1003 (0x3eb) are possible aliases
+        Level 258 (0x102) and level 1001 (0x3e9) are possible aliases
+Found 4 aliased levels
+*/
+#define SMB_QFS_ALLOCATION                                1
+#define SMB_QFS_VOLUME                                    2
+#define SMB_QFS_VOLUME_INFO                            0x102
+#define SMB_QFS_SIZE_INFO                              0x103
+#define SMB_QFS_DEVICE_INFO                            0x104
+#define SMB_QFS_ATTRIBUTE_INFO                         0x105
+#define SMB_QFS_UNIX_INFO                              0x200
+#define SMB_QFS_VOLUME_INFORMATION                     1001
+#define SMB_QFS_SIZE_INFORMATION                       1003
+#define SMB_QFS_DEVICE_INFORMATION                     1004
+#define SMB_QFS_ATTRIBUTE_INFORMATION                  1005
+#define SMB_QFS_QUOTA_INFORMATION                      1006
+#define SMB_QFS_FULL_SIZE_INFORMATION                  1007
+#define SMB_QFS_OBJECTID_INFORMATION                   1008
+
+
+/* trans2 qfileinfo/qpathinfo */
+/* w2k3 TRANS2ALIASES:
+Checking for QPATHINFO aliases
+setting up complex file \qpathinfo_aliases.txt
+        Found level    1 (0x001) of size  22 (0x16)
+        Found level    2 (0x002) of size  26 (0x1a)
+        Found level    4 (0x004) of size  41 (0x29)
+        Found level    6 (0x006) of size   0 (0x00)
+        Found level  257 (0x101) of size  40 (0x28)
+        Found level  258 (0x102) of size  24 (0x18)
+        Found level  259 (0x103) of size   4 (0x04)
+        Found level  260 (0x104) of size  48 (0x30)
+        Found level  263 (0x107) of size 126 (0x7e)
+        Found level  264 (0x108) of size  28 (0x1c)
+        Found level  265 (0x109) of size  38 (0x26)
+        Found level  267 (0x10b) of size  16 (0x10)
+        Found level 1004 (0x3ec) of size  40 (0x28)
+        Found level 1005 (0x3ed) of size  24 (0x18)
+        Found level 1006 (0x3ee) of size   8 (0x08)
+        Found level 1007 (0x3ef) of size   4 (0x04)
+        Found level 1008 (0x3f0) of size   4 (0x04)
+        Found level 1009 (0x3f1) of size  48 (0x30)
+        Found level 1014 (0x3f6) of size   8 (0x08)
+        Found level 1016 (0x3f8) of size   4 (0x04)
+        Found level 1017 (0x3f9) of size   4 (0x04)
+        Found level 1018 (0x3fa) of size 126 (0x7e)
+        Found level 1021 (0x3fd) of size  28 (0x1c)
+        Found level 1022 (0x3fe) of size  38 (0x26)
+        Found level 1028 (0x404) of size  16 (0x10)
+        Found level 1034 (0x40a) of size  56 (0x38)
+        Found level 1035 (0x40b) of size   8 (0x08)
+Found 27 levels with success status
+        Level 267 (0x10b) and level 1028 (0x404) are possible aliases
+        Level 265 (0x109) and level 1022 (0x3fe) are possible aliases
+        Level 264 (0x108) and level 1021 (0x3fd) are possible aliases
+        Level 263 (0x107) and level 1018 (0x3fa) are possible aliases
+        Level 260 (0x104) and level 1009 (0x3f1) are possible aliases
+        Level 259 (0x103) and level 1007 (0x3ef) are possible aliases
+        Level 258 (0x102) and level 1005 (0x3ed) are possible aliases
+        Level 257 (0x101) and level 1004 (0x3ec) are possible aliases
+Found 8 aliased levels
+*/
+#define SMB_QFILEINFO_STANDARD                             1
+#define SMB_QFILEINFO_EA_SIZE                              2
+#define SMB_QFILEINFO_ALL_EAS                              4
+#define SMB_QFILEINFO_IS_NAME_VALID                        6  /* only for QPATHINFO */
+#define SMB_QFILEINFO_BASIC_INFO                      0x101
+#define SMB_QFILEINFO_STANDARD_INFO                   0x102
+#define SMB_QFILEINFO_EA_INFO                         0x103
+#define SMB_QFILEINFO_NAME_INFO                               0x104
+#define SMB_QFILEINFO_ALL_INFO                        0x107
+#define SMB_QFILEINFO_ALT_NAME_INFO                   0x108
+#define SMB_QFILEINFO_STREAM_INFO                     0x109
+#define SMB_QFILEINFO_COMPRESSION_INFO                 0x10b
+#define SMB_QFILEINFO_UNIX_BASIC                       0x200
+#define SMB_QFILEINFO_UNIX_LINK                        0x201
+#define SMB_QFILEINFO_BASIC_INFORMATION                        1004
+#define SMB_QFILEINFO_STANDARD_INFORMATION             1005
+#define SMB_QFILEINFO_INTERNAL_INFORMATION             1006
+#define SMB_QFILEINFO_EA_INFORMATION                   1007
+#define SMB_QFILEINFO_ACCESS_INFORMATION               1008
+#define SMB_QFILEINFO_NAME_INFORMATION                 1009
+#define SMB_QFILEINFO_POSITION_INFORMATION             1014
+#define SMB_QFILEINFO_MODE_INFORMATION                 1016
+#define SMB_QFILEINFO_ALIGNMENT_INFORMATION            1017
+#define SMB_QFILEINFO_ALL_INFORMATION                  1018
+#define SMB_QFILEINFO_ALT_NAME_INFORMATION             1021
+#define SMB_QFILEINFO_STREAM_INFORMATION               1022
+#define SMB_QFILEINFO_COMPRESSION_INFORMATION          1028
+#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION         1034
+#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION                1035
+
+
+
+/* trans2 setfileinfo/setpathinfo levels */
+/*
+w2k3 TRANS2ALIASES
+Checking for SETFILEINFO aliases
+setting up complex file \setfileinfo_aliases.txt
+        Found level    1 (0x001) of size   2 (0x02)
+        Found level    2 (0x002) of size   2 (0x02)
+        Found level  257 (0x101) of size  40 (0x28)
+        Found level  258 (0x102) of size   2 (0x02)
+        Found level  259 (0x103) of size   8 (0x08)
+        Found level  260 (0x104) of size   8 (0x08)
+        Found level 1004 (0x3ec) of size  40 (0x28)
+        Found level 1010 (0x3f2) of size   2 (0x02)
+        Found level 1013 (0x3f5) of size   2 (0x02)
+        Found level 1014 (0x3f6) of size   8 (0x08)
+        Found level 1016 (0x3f8) of size   4 (0x04)
+        Found level 1019 (0x3fb) of size   8 (0x08)
+        Found level 1020 (0x3fc) of size   8 (0x08)
+        Found level 1023 (0x3ff) of size   8 (0x08)
+        Found level 1025 (0x401) of size  16 (0x10)
+        Found level 1029 (0x405) of size  72 (0x48)
+        Found level 1032 (0x408) of size  56 (0x38)
+        Found level 1039 (0x40f) of size   8 (0x08)
+        Found level 1040 (0x410) of size   8 (0x08)
+Found 19 valid levels
+
+Checking for SETPATHINFO aliases
+        Found level 1004 (0x3ec) of size  40 (0x28)
+        Found level 1010 (0x3f2) of size   2 (0x02)
+        Found level 1013 (0x3f5) of size   2 (0x02)
+        Found level 1014 (0x3f6) of size   8 (0x08)
+        Found level 1016 (0x3f8) of size   4 (0x04)
+        Found level 1019 (0x3fb) of size   8 (0x08)
+        Found level 1020 (0x3fc) of size   8 (0x08)
+        Found level 1023 (0x3ff) of size   8 (0x08)
+        Found level 1025 (0x401) of size  16 (0x10)
+        Found level 1029 (0x405) of size  72 (0x48)
+        Found level 1032 (0x408) of size  56 (0x38)
+        Found level 1039 (0x40f) of size   8 (0x08)
+        Found level 1040 (0x410) of size   8 (0x08)
+Found 13 valid levels
+*/
+#define SMB_SFILEINFO_STANDARD                             1
+#define SMB_SFILEINFO_EA_SET                               2
+#define SMB_SFILEINFO_BASIC_INFO                      0x101
+#define SMB_SFILEINFO_DISPOSITION_INFO                0x102
+#define SMB_SFILEINFO_ALLOCATION_INFO                  0x103
+#define SMB_SFILEINFO_END_OF_FILE_INFO                 0x104
+#define SMB_SFILEINFO_UNIX_BASIC                       0x200
+#define SMB_SFILEINFO_UNIX_LINK                        0x201
+#define SMB_SFILEINFO_BASIC_INFORMATION                        1004
+#define SMB_SFILEINFO_RENAME_INFORMATION               1010
+#define SMB_SFILEINFO_DISPOSITION_INFORMATION          1013
+#define SMB_SFILEINFO_POSITION_INFORMATION             1014
+#define SMB_SFILEINFO_MODE_INFORMATION                 1016
+#define SMB_SFILEINFO_ALLOCATION_INFORMATION           1019
+#define SMB_SFILEINFO_END_OF_FILE_INFORMATION          1020
+
+/* filemon shows FilePipeInformation */
+#define SMB_SFILEINFO_1023                             1023
+
+/* filemon shows FilePipeRemoteInformation */
+#define SMB_SFILEINFO_1025                             1025
+
+/* filemon shows CopyOnWriteInformation */
+#define SMB_SFILEINFO_1029                             1029
+
+/* filemon shows OleClassIdInformation */
+#define SMB_SFILEINFO_1032                             1032
+
+/* seems to be the file size - perhaps valid data size? 
+   filemon shows 'InheritContentIndexInfo'
+*/
+#define SMB_SFILEINFO_1039                             1039
+
+/* OLE_INFORMATION? */
+#define SMB_SFILEINFO_1040                             1040
+
+
+/* trans2 findfirst levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for FINDFIRST aliases
+        Found level    1 (0x001) of size  68 (0x44)
+        Found level    2 (0x002) of size  70 (0x46)
+        Found level  257 (0x101) of size 108 (0x6c)
+        Found level  258 (0x102) of size 116 (0x74)
+        Found level  259 (0x103) of size  60 (0x3c)
+        Found level  260 (0x104) of size 140 (0x8c)
+        Found level  261 (0x105) of size 124 (0x7c)
+        Found level  262 (0x106) of size 148 (0x94)
+Found 8 levels with success status
+Found 0 aliased levels
+*/
+#define SMB_FIND_STANDARD                  1
+#define SMB_FIND_EA_SIZE                   2
+#define SMB_FIND_DIRECTORY_INFO                0x101
+#define SMB_FIND_FULL_DIRECTORY_INFO   0x102
+#define SMB_FIND_NAME_INFO             0x103
+#define SMB_FIND_BOTH_DIRECTORY_INFO   0x104
+#define SMB_FIND_261                   0x105
+#define SMB_FIND_262                   0x106
+#define SMB_FIND_UNIX_INFO              0x200
+
+/* flags on trans2 findfirst/findnext that control search */
+#define FLAG_TRANS2_FIND_CLOSE          0x1
+#define FLAG_TRANS2_FIND_CLOSE_IF_END   0x2
+#define FLAG_TRANS2_FIND_REQUIRE_RESUME 0x4
+#define FLAG_TRANS2_FIND_CONTINUE       0x8
+#define FLAG_TRANS2_FIND_BACKUP_INTENT  0x10
+
+/*
+ * DeviceType and Characteristics returned in a
+ * SMB_QFS_DEVICE_INFO call.
+ */
+#define QFS_DEVICETYPE_CD_ROM                  0x2
+#define QFS_DEVICETYPE_CD_ROM_FILE_SYSTEM      0x3
+#define QFS_DEVICETYPE_DISK                    0x7
+#define QFS_DEVICETYPE_DISK_FILE_SYSTEM                0x8
+#define QFS_DEVICETYPE_FILE_SYSTEM             0x9
+
+/* Characteristics. */
+#define QFS_TYPE_REMOVABLE_MEDIA               0x1
+#define QFS_TYPE_READ_ONLY_DEVICE              0x2
+#define QFS_TYPE_FLOPPY                                0x4
+#define QFS_TYPE_WORM                          0x8
+#define QFS_TYPE_REMOTE                                0x10
+#define QFS_TYPE_MOUNTED                       0x20
+#define QFS_TYPE_VIRTUAL                       0x40
+
+
+/*
+ * Thursby MAC extensions....
+ */
+
+/*
+ * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_MAC_INFO_LEVEL                      0x300
+#define MAX_MAC_INFO_LEVEL                      0x3FF
+#define SMB_QFS_MAC_FS_INFO                     0x301
+
+
+
+/* UNIX CIFS Extensions - created by HP */
+/*
+ * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_UNIX_INFO_LEVEL 0x200
+#define MAX_UNIX_INFO_LEVEL 0x2FF
+
+#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL))
+
+#define SMB_QFILEINFO_UNIX_BASIC       0x200   /* UNIX File Info*/
+#define SMB_SFILEINFO_UNIX_BASIC        0x200
+
+#define SMB_MODE_NO_CHANGE                 0xFFFFFFFF     /* file mode value which */
+                                              /* means "don't change it" */
+#define SMB_UID_NO_CHANGE                  0xFFFFFFFF
+#define SMB_GID_NO_CHANGE                  0xFFFFFFFF
+
+#define SMB_SIZE_NO_CHANGE_LO              0xFFFFFFFF
+#define SMB_SIZE_NO_CHANGE_HI              0xFFFFFFFF
+#define SMB_TIME_NO_CHANGE_LO              0xFFFFFFFF
+#define SMB_TIME_NO_CHANGE_HI              0xFFFFFFFF
+
+/*
+Offset Size         Name
+0      LARGE_INTEGER EndOfFile                File size
+8      LARGE_INTEGER Blocks                   Number of bytes used on disk (st_blocks).
+16     LARGE_INTEGER CreationTime             Creation time
+24     LARGE_INTEGER LastAccessTime           Last access time
+32     LARGE_INTEGER LastModificationTime     Last modification time
+40     LARGE_INTEGER Uid                      Numeric user id for the owner
+48     LARGE_INTEGER Gid                      Numeric group id of owner
+56     ULONG Type                             Enumeration specifying the pathname type:
+                                              0 -- File
+                                              1 -- Directory
+                                              2 -- Symbolic link
+                                              3 -- Character device
+                                              4 -- Block device
+                                              5 -- FIFO (named pipe)
+                                              6 -- Unix domain socket
+
+60     LARGE_INTEGER devmajor                 Major device number if type is device
+68     LARGE_INTEGER devminor                 Minor device number if type is device
+76     LARGE_INTEGER uniqueid                 This is a server-assigned unique id for the file. The client
+                                              will typically map this onto an inode number. The scope of
+                                              uniqueness is the share.
+84     LARGE_INTEGER permissions              Standard UNIX file permissions  - see below.
+92     LARGE_INTEGER nlinks                   The number of directory entries that map to this entry
+                                              (number of hard links)
+
+100 - end.
+*/
+
+/* UNIX filetype mappings. */
+
+#define UNIX_TYPE_FILE      0
+#define UNIX_TYPE_DIR       1
+#define UNIX_TYPE_SYMLINK   2
+#define UNIX_TYPE_CHARDEV   3
+#define UNIX_TYPE_BLKDEV    4
+#define UNIX_TYPE_FIFO      5
+#define UNIX_TYPE_SOCKET    6
+#define UNIX_TYPE_UNKNOWN   0xFFFFFFFF
+
+/*
+ * Oh this is fun. "Standard UNIX permissions" has no
+ * meaning in POSIX. We need to define the mapping onto
+ * and off the wire as this was not done in the original HP
+ * spec. JRA.
+ */
+
+#define UNIX_X_OTH                     0000001
+#define UNIX_W_OTH                     0000002
+#define UNIX_R_OTH                     0000004
+#define UNIX_X_GRP                     0000010
+#define UNIX_W_GRP                      0000020
+#define UNIX_R_GRP                      0000040
+#define UNIX_X_USR                      0000100
+#define UNIX_W_USR                      0000200
+#define UNIX_R_USR                      0000400
+#define UNIX_STICKY                     0001000
+#define UNIX_SET_GID                    0002000
+#define UNIX_SET_UID                    0004000
+
+/* Masks for the above */
+#define UNIX_OTH_MASK                   0000007
+#define UNIX_GRP_MASK                   0000070
+#define UNIX_USR_MASK                   0000700
+#define UNIX_PERM_MASK                  0000777
+#define UNIX_EXTRA_MASK                 0007000
+#define UNIX_ALL_MASK                   0007777
+
+#define SMB_QFILEINFO_UNIX_LINK         0x201
+#define SMB_SFILEINFO_UNIX_LINK         0x201
+#define SMB_SFILEINFO_UNIX_HLINK        0x203
+
+#define SMB_FIND_FILE_UNIX              0x202
+
+/*
+ Info level for QVOLINFO - returns version of CIFS UNIX extensions, plus
+ 64-bits worth of capability fun :-).
+*/
+
+#define SMB_QUERY_CIFS_UNIX_INFO      0x200
+
+/* Returns the following.
+
+  UINT16             major version number
+  UINT16             minor version number
+  LARGE_INTEGER      capability bitfield
+
+*/
+
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+#define CIFS_UNIX_FCNTL_LOCKS_CAP           0x1
+#define CIFS_UNIX_POSIX_ACLS_CAP            0x2
+
+/* ... more as we think of them :-). */
+
+#endif
diff --git a/source4/include/util_getent.h b/source4/include/util_getent.h
new file mode 100644 (file)
index 0000000..b67758b
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Simo Sorce 2001
+
+   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.
+*/
+
+#ifndef _UTIL_GETENT_H
+#define _UTIL_GETENT_H
+
+/* Element for a single linked list of group entries */
+/* Replace the use of struct group in some cases */
+/* Used by getgrent_list() */
+
+struct sys_grent {
+       char *gr_name;
+       char *gr_passwd;
+       gid_t gr_gid;
+       char **gr_mem;
+       struct sys_grent *next;
+};
+
+/* Element for a single linked list of passwd entries */
+/* Replace the use of struct passwd in some cases */
+/* Used by getpwent_list() */
+
+struct sys_pwent {
+       char *pw_name;
+       char *pw_passwd;
+       uid_t pw_uid;
+       gid_t pw_gid;
+       char *pw_gecos;
+       char *pw_dir;
+       char *pw_shell;
+       struct sys_pwent *next;
+};
+
+/* Element for a single linked list of user names in a group. */
+/* Used to return group lists that may span multiple lines in 
+   /etc/group file. */
+/* Used by get_users_in_group() */
+
+struct sys_userlist {
+       struct sys_userlist *next, *prev;
+       char *unix_name;
+};
+
+#endif /* _UTIL_GETENT_H */
diff --git a/source4/include/version.h b/source4/include/version.h
new file mode 100644 (file)
index 0000000..72b0b12
--- /dev/null
@@ -0,0 +1 @@
+#define SAMBA_VERSION "4.0-test"
diff --git a/source4/include/vt_mode.h b/source4/include/vt_mode.h
new file mode 100644 (file)
index 0000000..85b4811
--- /dev/null
@@ -0,0 +1,48 @@
+/* vt_mode.h */
+/*
+support vtp-sessions
+
+written by Christian A. Lademann <cal@zls.com>
+*/
+
+/*
+02.05.95:cal:ported to samba-1.9.13
+*/
+
+#ifndef        __vt_mode_h__
+#      define  __vt_mode_h__
+
+#      define  VT_CLOSED       0
+#      define  VT_OPEN         1
+
+#      define  MS_NONE         0
+#      define  MS_PTY          1
+#      define  MS_STREAM       2
+#      define  MS_VTY          3
+
+#      define  VT_MAXREAD      32
+
+
+#      undef   EXTERN
+
+#      ifndef __vt_mode_c__
+#              define  EXTERN  extern
+#              define  DEFAULT(v)
+#      else
+#              define  EXTERN
+#              define  DEFAULT(v)      =(v)
+#      endif
+
+       EXTERN int      VT_Status               DEFAULT(VT_CLOSED),
+                               VT_Fd                   DEFAULT(-1),
+                               VT_ChildPID             DEFAULT(-1);
+
+       EXTERN BOOL     VT_Mode                 DEFAULT(False),
+                               VT_ChildDied    DEFAULT(False);
+
+       EXTERN char     *VT_Line                DEFAULT(NULL);
+
+#      undef   EXTERN
+
+
+#endif /* __vt_mode_h__ */
diff --git a/source4/include/xfile.h b/source4/include/xfile.h
new file mode 100644 (file)
index 0000000..89fa9d1
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+   Unix SMB/CIFS implementation.
+   stdio replacement
+   Copyright (C) Andrew Tridgell 2001
+   
+   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.
+*/
+
+#ifndef _XFILE_H_
+#define _XFILE_H_
+/*
+  see xfile.c for explanations
+*/
+
+typedef struct {
+       int fd;
+       char *buf;
+       char *next;
+       int bufsize;
+       int bufused;
+       int open_flags;
+       int buftype;
+       int flags;
+} XFILE;
+
+extern XFILE *x_stdin, *x_stdout, *x_stderr;
+
+/* buffering type */
+#define X_IOFBF 0
+#define X_IOLBF 1
+#define X_IONBF 2
+
+#define x_getc(f) x_fgetc(f)
+
+int x_vfprintf(XFILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
+int x_fprintf(XFILE *f, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
+#endif /* _XFILE_H_ */
diff --git a/source4/install-sh b/source4/install-sh
new file mode 100644 (file)
index 0000000..5871924
--- /dev/null
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/source4/intl/.cvsignore b/source4/intl/.cvsignore
new file mode 100644 (file)
index 0000000..5f2a5c4
--- /dev/null
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source4/intl/lang_tdb.c b/source4/intl/lang_tdb.c
new file mode 100644 (file)
index 0000000..6879d70
--- /dev/null
@@ -0,0 +1,235 @@
+/* 
+   Unix SMB/CIFS implementation.
+   tdb based replacement for gettext 
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+static TDB_CONTEXT *tdb;
+
+/* the currently selected language */
+static char *current_lang;
+
+
+/* load a msg file into the tdb */
+static BOOL load_msg(const char *msg_file)
+{
+       char **lines;
+       int num_lines, i;
+       char *msgid, *msgstr;
+       TDB_DATA key, data;
+
+       lines = file_lines_load(msg_file, &num_lines);
+
+       if (!lines) {
+               return False;
+       }
+
+       if (tdb_lockall(tdb) != 0) return False;
+
+       /* wipe the db */
+       tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+
+       msgid = NULL;
+       
+       for (i=0;i<num_lines;i++) {
+               if (strncmp(lines[i], "msgid \"", 7) == 0) {
+                       msgid = lines[i] + 7;
+               }
+               if (msgid && strncmp(lines[i], "msgstr \"", 8) == 0) {
+                       msgstr = lines[i] + 8;
+                       trim_string(msgid, NULL, "\"");
+                       trim_string(msgstr, NULL, "\"");
+                       if (*msgstr == 0) {
+                               msgstr = msgid;
+                       }
+                       key.dptr = msgid;
+                       key.dsize = strlen(msgid)+1;
+                       data.dptr = msgstr;
+                       data.dsize = strlen(msgstr)+1;
+                       tdb_store(tdb, key, data, 0);
+                       msgid = NULL;
+               }
+       }
+
+       file_lines_free(lines);
+       tdb_unlockall(tdb);
+
+       return True;
+}
+
+
+/* work out what language to use from locale variables */
+static const char *get_lang(void)
+{
+       const char *vars[] = {"LANGUAGE", "LC_ALL", "LC_LANG", "LANG", NULL};
+       int i;
+       char *p;
+
+       for (i=0; vars[i]; i++) {
+               if ((p = getenv(vars[i]))) {
+                       return p;
+               }
+       }
+
+       return NULL;
+}
+
+/* initialise the message translation subsystem. If the "lang" argument
+   is NULL then get the language from the normal environment variables */
+BOOL lang_tdb_init(const char *lang)
+{
+       char *path = NULL;
+       char *msg_path = NULL;
+       struct stat st;
+       static int initialised;
+       time_t loadtime;
+       TALLOC_CTX *mem_ctx;
+
+       /* we only want to init once per process, unless given
+          an override */
+       if (initialised && !lang) return True;
+
+       if (initialised) {
+               /* we are re-initialising, free up any old init */
+               if (tdb) {
+                       tdb_close(tdb);
+                       tdb = NULL;
+               }
+               SAFE_FREE(current_lang);
+       }
+
+       initialised = 1;
+
+       if (!lang) {
+               /* no lang given, use environment */
+               lang = get_lang();
+       }
+
+       /* if no lang then we don't translate */
+       if (!lang) return True;
+
+       mem_ctx = talloc_init("lang_tdb_init");
+       if (!mem_ctx) {
+               return False;
+       }
+       asprintf(&msg_path, "%s.msg", lib_path(mem_ctx, (const char *)lang));
+       if (stat(msg_path, &st) != 0) {
+               /* the msg file isn't available */
+               free(msg_path);
+               talloc_destroy(mem_ctx);
+               return False;
+       }
+       
+
+       asprintf(&path, "%s%s.tdb", lock_path(mem_ctx, "lang_"), lang);
+
+       tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
+       if (!tdb) {
+               tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDONLY, 0);
+               free(path);
+               free(msg_path);
+               talloc_destroy(mem_ctx);
+               if (!tdb) return False;
+               current_lang = strdup(lang);
+               return True;
+       }
+
+       free(path);
+       talloc_destroy(mem_ctx);
+
+       loadtime = tdb_fetch_int32(tdb, "/LOADTIME/");
+
+       if (loadtime == -1 || loadtime < st.st_mtime) {
+               load_msg(msg_path);
+               tdb_store_int32(tdb, "/LOADTIME/", (int)time(NULL));
+       }
+       free(msg_path);
+
+       current_lang = strdup(lang);
+
+       return True;
+}
+
+/* translate a msgid to a message string in the current language 
+   returns a string that must be freed by calling lang_msg_free()
+*/
+const char *lang_msg(const char *msgid)
+{
+       TDB_DATA key, data;
+
+       lang_tdb_init(NULL);
+
+       if (!tdb) return msgid;
+
+       key.dptr = strdup(msgid);
+       key.dsize = strlen(msgid)+1;
+       
+       data = tdb_fetch(tdb, key);
+
+       free(key.dptr);
+
+       /* if the message isn't found then we still need to return a pointer
+          that can be freed. Pity. */
+       if (!data.dptr)
+               return strdup(msgid);
+
+       return (const char *)data.dptr;
+}
+
+
+/* free up a string from lang_msg() */
+void lang_msg_free(const char *msgstr)
+{
+       if (!tdb) return;
+       free(msgstr);
+}
+
+
+/*
+  when the _() translation macro is used there is no obvious place to free
+  the resulting string and there is no easy way to give a static pointer.
+  All we can do is rotate between some static buffers and hope a single d_printf() 
+  doesn't have more calls to _() than the number of buffers 
+*/
+const char *lang_msg_rotate(const char *msgid)
+{
+#define NUM_LANG_BUFS 4
+       const char *msgstr;
+       static pstring bufs[NUM_LANG_BUFS];
+       static int next;
+
+       msgstr = lang_msg(msgid);
+       if (!msgstr) return msgid;
+
+       pstrcpy(bufs[next], msgstr);
+       msgstr = bufs[next];
+
+       next = (next+1) % NUM_LANG_BUFS;
+       
+       return msgstr;
+}
+
+
+/* 
+   return the current language - needed for language file mappings 
+*/
+char *lang_tdb_current(void)
+{
+       return current_lang;
+}
diff --git a/source4/intl/linux-msg.sed b/source4/intl/linux-msg.sed
new file mode 100644 (file)
index 0000000..5918e72
--- /dev/null
@@ -0,0 +1,100 @@
+# po2msg.sed - Convert Uniforum style .po file to Linux style .msg file
+# Copyright (C) 1995 Free Software Foundation, Inc.
+# Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+#
+# 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, 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.
+#
+#
+# The first directive in the .msg should be the definition of the
+# message set number.  We use always set number 1.
+#
+1 {
+  i\
+$set 1 # Automatically created by po2msg.sed
+  h
+  s/.*/0/
+  x
+}
+#
+# Mitch's old catalog format does not allow comments.
+#
+# We copy the original message as a comment into the .msg file.
+#
+/^msgid/ {
+  s/msgid[     ]*"//
+#
+# This does not work now with the new format.
+# /"$/! {
+#   s/\\$//
+#   s/$/ ... (more lines following)"/
+# }
+  x
+# The following nice solution is by
+# Bruno <Haible@ma2s2.mathematik.uni-karlsruhe.de>
+  td
+# Increment a decimal number in pattern space.
+# First hide trailing `9' digits.
+  :d
+  s/9\(_*\)$/_\1/
+  td
+# Assure at least one digit is available.
+  s/^\(_*\)$/0\1/
+# Increment the last digit.
+  s/8\(_*\)$/9\1/
+  s/7\(_*\)$/8\1/
+  s/6\(_*\)$/7\1/
+  s/5\(_*\)$/6\1/
+  s/4\(_*\)$/5\1/
+  s/3\(_*\)$/4\1/
+  s/2\(_*\)$/3\1/
+  s/1\(_*\)$/2\1/
+  s/0\(_*\)$/1\1/
+# Convert the hidden `9' digits to `0's.
+  s/_/0/g
+  x
+  G
+  s/\(.*\)"\n\([0-9]*\)/$ #\2 Original Message:(\1)/p
+}
+#
+# The .msg file contains, other then the .po file, only the translations
+# but each given a unique ID.  Starting from 1 and incrementing by 1 for
+# each message we assign them to the messages.
+# It is important that the .po file used to generate the cat-id-tbl.c file
+# (with po-to-tbl) is the same as the one used here.  (At least the order
+# of declarations must not be changed.)
+#
+/^msgstr/ {
+  s/msgstr[    ]*"\(.*\)"/# \1/
+# Clear substitution flag.
+  tb
+# Append the next line.
+  :b
+  N
+# Look whether second part is continuation line.
+  s/\(.*\n\)"\(.*\)"/\1\2/
+# Yes, then branch.
+  ta
+  P
+  D
+# Note that D includes a jump to the start!!
+# We found a continuation line.  But before printing insert '\'.
+  :a
+  s/\(.*\)\(\n.*\)/\1\\\2/
+  P
+# We cannot use D here.
+  s/.*\n\(.*\)/\1/
+  tb
+}
+d
diff --git a/source4/lib/.cvsignore b/source4/lib/.cvsignore
new file mode 100644 (file)
index 0000000..07da222
--- /dev/null
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source4/lib/account_pol.c b/source4/lib/account_pol.c
new file mode 100644 (file)
index 0000000..df1479d
--- /dev/null
@@ -0,0 +1,169 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  account policy storage
+ *  Copyright (C) Jean Fran�ois Micouleau      1998-2001.
+ *  Copyright (C) Andrew Bartlett              2002
+ *  
+ *  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"
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION 1
+
+/****************************************************************************
+ Open the account policy tdb.
+****************************************************************************/
+
+BOOL init_account_policy(void)
+{
+       static pid_t local_pid;
+       const char *vstring = "INFO/version";
+       uint32 version;
+       TALLOC_CTX *mem_ctx;
+
+       if (tdb && local_pid == getpid())
+               return True;
+       mem_ctx = talloc_init("init_account_policy");
+       if (!mem_ctx) {
+               DEBUG(0,("No memory to open account policy database\n"));
+               return False;
+       }
+       tdb = tdb_open_log(lock_path(mem_ctx, "account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       talloc_destroy(mem_ctx);
+       if (!tdb) {
+               DEBUG(0,("Failed to open account policy database\n"));
+               return False;
+       }
+
+       local_pid = getpid();
+
+       /* handle a Samba upgrade */
+       tdb_lock_bystring(tdb, vstring,0);
+       if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) {
+               tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+               tdb_store_uint32(tdb, vstring, DATABASE_VERSION);
+               
+               account_policy_set(AP_MIN_PASSWORD_LEN, MINPASSWDLENGTH);   /* 5 chars minimum             */
+               account_policy_set(AP_PASSWORD_HISTORY, 0);                 /* don't keep any old password */
+               account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, 0);      /* don't force user to logon   */
+               account_policy_set(AP_MAX_PASSWORD_AGE, MAX_PASSWORD_AGE);  /* 21 days                     */
+               account_policy_set(AP_MIN_PASSWORD_AGE, 0);                 /* 0 days                      */
+               account_policy_set(AP_LOCK_ACCOUNT_DURATION, 0);            /* lockout for 0 minutes       */
+               account_policy_set(AP_RESET_COUNT_TIME, 0);                 /* reset immediatly            */
+               account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, 0);              /* don't lockout               */
+               account_policy_set(AP_TIME_TO_LOGOUT, -1);                  /* don't force logout          */
+       }
+       tdb_unlock_bystring(tdb, vstring);
+
+       return True;
+}
+
+static const struct {
+       int field;
+       const char *string;
+} account_policy_names[] = {
+       {AP_MIN_PASSWORD_LEN, "min password length"},
+       {AP_PASSWORD_HISTORY, "password history"},
+       {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password"},
+       {AP_MAX_PASSWORD_AGE, "maximum password age"},
+       {AP_MIN_PASSWORD_AGE,"minimum password age"},
+       {AP_LOCK_ACCOUNT_DURATION, "lockout duration"},
+       {AP_RESET_COUNT_TIME, "reset count minutes"},
+       {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt"},
+       {AP_TIME_TO_LOGOUT, "disconnect time"},
+       {0, NULL}
+};
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+static const char *decode_account_policy_name(int field)
+{
+       int i;
+       for (i=0; account_policy_names[i].string; i++) {
+               if (field == account_policy_names[i].field)
+                       return account_policy_names[i].string;
+       }
+       return NULL;
+
+}
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+int account_policy_name_to_fieldnum(const char *name)
+{
+       int i;
+       for (i=0; account_policy_names[i].string; i++) {
+               if (strcmp(name, account_policy_names[i].string) == 0)
+                       return account_policy_names[i].field;
+       }
+       return 0;
+
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_get(int field, uint32 *value)
+{
+       fstring name;
+
+       init_account_policy();
+
+       *value = 0;
+
+       fstrcpy(name, decode_account_policy_name(field));
+       if (!*name) {
+               DEBUG(1, ("account_policy_get: Field %d is not a valid account policy type!  Cannot get, returning 0.\n", field));
+               return False;
+       }
+       if (!tdb_fetch_uint32(tdb, name, value)) {
+               DEBUG(1, ("account_policy_get: tdb_fetch_uint32 failed for efild %d (%s), returning 0", field, name));
+               return False;
+       }
+       DEBUG(10,("account_policy_get: %s:%d\n", name, *value));
+       return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_set(int field, uint32 value)
+{
+       fstring name;
+
+       init_account_policy();
+
+       fstrcpy(name, decode_account_policy_name(field));
+       if (!*name) {
+               DEBUG(1, ("Field %d is not a valid account policy type!  Cannot set.\n", field));
+               return False;
+       }
+
+       if (!tdb_store_uint32(tdb, name, value)) {
+               DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u", field, name, value));
+               return False;
+       }
+
+       DEBUG(10,("account_policy_set: %s:%d\n", name, value));
+       
+       return True;
+}
+
diff --git a/source4/lib/adt_tree.c b/source4/lib/adt_tree.c
new file mode 100644 (file)
index 0000000..0bc224e
--- /dev/null
@@ -0,0 +1,464 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  Generic Abstract Data Types
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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"
+
+
+/**************************************************************************
+ Initialize the tree's root.  The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+static BOOL trim_tree_keypath( char *path, char **base, char **new_path )
+{
+       char *p;
+       
+       *new_path = *base = NULL;
+       
+       if ( !path )
+               return False;
+       
+       *base = path;
+       
+       p = strchr( path, '/' );
+       
+       if ( p ) {
+               *p = '\0';
+               *new_path = p+1;
+       }
+       
+       return True;
+}
+
+/**************************************************************************
+ Initialize the tree's root.  The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+SORTED_TREE* sorted_tree_init( void *data_p,
+                               int (cmp_fn)(void*, void*),
+                               void (free_fn)(void*) )
+{
+       SORTED_TREE *tree = NULL;
+       
+       if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) )
+               return NULL;
+               
+       ZERO_STRUCTP( tree );
+       
+       tree->compare = cmp_fn;
+       tree->free    = free_fn;
+       
+       if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) {
+               SAFE_FREE( tree );
+               return NULL;
+       }
+       
+       ZERO_STRUCTP( tree->root );
+       tree->root->data_p = data_p;
+       
+       return tree;
+}
+
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+static void sorted_tree_destroy_children( TREE_NODE *root )
+{
+       int i;
+       
+       if ( !root )
+               return;
+       
+       for ( i=0; i<root->num_children; i++ )
+       {
+               sorted_tree_destroy_children( root->children[i] );      
+       }
+       
+       SAFE_FREE( root->children );
+       SAFE_FREE( root->key );
+       
+       return;
+}
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+void sorted_tree_destroy( SORTED_TREE *tree )
+{
+       if ( tree->root )
+               sorted_tree_destroy_children( tree->root );     
+       
+       if ( tree->free )
+               tree->free( tree->root );
+       
+       SAFE_FREE( tree );
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key )
+{
+       TREE_NODE *infant = NULL;
+       TREE_NODE **siblings;
+       int i;
+       
+       if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) )
+               return NULL;
+       
+       ZERO_STRUCTP( infant );
+               
+       infant->key = strdup( key );
+       infant->parent = node;
+       
+       siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) );
+       
+       if ( siblings )
+               node->children = siblings;
+       
+       node->num_children++;
+       
+       /* first child */
+       
+       if ( node->num_children == 1 ) {
+               DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n", 
+                       node->key ? node->key : "NULL", infant->key ));
+               node->children[0] = infant;
+       }
+       else 
+       {
+               /* 
+                * multiple siblings .... (at least 2 children)
+                * 
+                * work from the end of the list forward 
+                * The last child is not set at this point 
+                * Insert the new infanct in ascending order 
+                * from left to right
+                */
+       
+               for ( i = node->num_children-1; i>=1; i-- )
+               {
+                       DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
+                               infant->key, node->children[i-1]->key));
+                       
+                       /* the strings should never match assuming that we 
+                          have called sorted_tree_find_child() first */
+               
+                       if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) {
+                               DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n", 
+                                       i));
+                               node->children[i] = infant;
+                               break;
+                       }
+                       
+                       /* bump everything towards the end on slot */
+                       
+                       node->children[i] = node->children[i-1];
+               }
+
+               DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i ));
+               
+               /* if we haven't found the correct slot yet, the child 
+                  will be first in the list */
+                  
+               if ( i == 0 )
+                       node->children[0] = infant;
+       }
+
+       return infant;
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key )
+{
+       TREE_NODE *next = NULL;
+       int i, result;
+       
+       if ( !node ) {
+               DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n"));
+               return NULL;
+       }
+       
+       if ( !key ) {
+               DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n"));
+               return NULL;
+       }
+       
+       for ( i=0; i<node->num_children; i++ )
+       {       
+               DEBUG(11,("sorted_tree_find_child: child key => [%s]\n",
+                       node->children[i]->key));
+                       
+               result = StrCaseCmp( node->children[i]->key, key );
+               
+               if ( result == 0 )
+                       next = node->children[i];
+               
+               /* if result > 0 then we've gone to far because
+                  the list of children is sorted by key name 
+                  If result == 0, then we have a match         */
+                  
+               if ( result > 0 )
+                       break;
+       }
+
+       DEBUG(11,("sorted_tree_find_child: %s [%s]\n",
+               next ? "Found" : "Did not find", key ));        
+       
+       return next;
+}
+
+/**************************************************************************
+ Add a new node into the tree given a key path and a blob of data
+ *************************************************************************/
+
+BOOL sorted_tree_add( SORTED_TREE *tree, const char *path, void *data_p )
+{
+       char *str, *base, *path2;
+       TREE_NODE *current, *next;
+       BOOL ret = True;
+       
+       DEBUG(8,("sorted_tree_add: Enter\n"));
+               
+       if ( !path || *path != '/' ) {
+               DEBUG(0,("sorted_tree_add: Attempt to add a node with a bad path [%s]\n",
+                       path ? path : "NULL" ));
+               return False;
+       }
+       
+       if ( !tree ) {
+               DEBUG(0,("sorted_tree_add: Attempt to add a node to an uninitialized tree!\n"));
+               return False;
+       }
+       
+       /* move past the first '/' */
+       
+       path++; 
+       path2 = strdup( path );
+       if ( !path2 ) {
+               DEBUG(0,("sorted_tree_add: strdup() failed on string [%s]!?!?!\n", path));
+               return False;
+       }
+       
+
+       /* 
+        * this works sort of like a 'mkdir -p' call, possibly 
+        * creating an entire path to the new node at once
+        * The path should be of the form /<key1>/<key2>/...
+        */
+       
+       base = path2;
+       str  = path2;
+       current = tree->root;
+       
+       do {
+               /* break off the remaining part of the path */
+               
+               str = strchr( str, '/' );
+               if ( str )
+                       *str = '\0';
+                       
+               /* iterate to the next child--birth it if necessary */
+               
+               next = sorted_tree_find_child( current, base );
+               if ( !next ) {
+                       next = sorted_tree_birth_child( current, base );
+                       if ( !next ) {
+                               DEBUG(0,("sorted_tree_add: Failed to create new child!\n"));
+                               ret =  False;
+                               goto done;
+                       }
+               }
+               current = next;
+               
+               /* setup the next part of the path */
+               
+               base = str;
+               if ( base ) {
+                       *base = '/';
+                       base++;
+                       str = base;
+               }
+       
+       } while ( base != NULL );
+       
+       current->data_p = data_p;
+       
+       DEBUG(10,("sorted_tree_add: Successfully added node [%s] to tree\n",
+               path ));
+
+       DEBUG(8,("sorted_tree_add: Exit\n"));
+
+done:
+       SAFE_FREE( path2 );
+       return ret;
+}
+
+
+/**************************************************************************
+ Recursive routine to print out all children of a TREE_NODE
+ *************************************************************************/
+
+static void sorted_tree_print_children( TREE_NODE *node, int debug, const char *path )
+{
+       int i;
+       int num_children;
+       pstring path2;
+       
+       if ( !node )
+               return;
+       
+       
+       if ( node->key )
+               DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
+                       node->data_p ? "data" : "NULL" ));
+
+       *path2 = '\0';
+       if ( path )
+               pstrcpy( path2, path );
+       pstrcat( path2, node->key ? node->key : "NULL" );
+       pstrcat( path2, "/" );
+               
+       num_children = node->num_children;
+       for ( i=0; i<num_children; i++ )
+               sorted_tree_print_children( node->children[i], debug, path2 );
+       
+
+}
+
+/**************************************************************************
+ Dump the kys for a tree to the log file
+ *************************************************************************/
+
+void sorted_tree_print_keys( SORTED_TREE *tree, int debug )
+{
+       int i;
+       int num_children = tree->root->num_children;
+       
+       if ( tree->root->key )
+               DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
+                       tree->root->data_p ? "data" : "NULL" ));
+       
+       for ( i=0; i<num_children; i++ ) {
+               sorted_tree_print_children( tree->root->children[i], debug, 
+                       tree->root->key ? tree->root->key : "ROOT/" );
+       }
+       
+}
+
+/**************************************************************************
+ return the data_p for for the node in tree matching the key string
+ The key string is the full path.  We must break it apart and walk 
+ the tree
+ *************************************************************************/
+
+void* sorted_tree_find( SORTED_TREE *tree, char *key )
+{
+       char *keystr, *base, *str, *p;
+       TREE_NODE *current;
+       void *result = NULL;
+       
+       DEBUG(10,("sorted_tree_find: Enter [%s]\n", key ? key : "NULL" ));
+
+       /* sanity checks first */
+       
+       if ( !key ) {
+               DEBUG(0,("sorted_tree_find: Attempt to search tree using NULL search string!\n"));
+               return NULL;
+       }
+       
+       if ( !tree ) {
+               DEBUG(0,("sorted_tree_find: Attempt to search an uninitialized tree using string [%s]!\n",
+                       key ? key : "NULL" ));
+               return NULL;
+       }
+       
+       if ( !tree->root )
+               return NULL;
+       
+       /* make a copy to play with */
+       
+       if ( *key == '/' )
+               keystr = strdup( key+1 );
+       else
+               keystr = strdup( key );
+       
+       if ( !keystr ) {
+               DEBUG(0,("sorted_tree_find: strdup() failed on string [%s]!?!?!\n", key));
+               return NULL;
+       }
+
+       /* start breaking the path apart */
+       
+       p = keystr;
+       current = tree->root;
+       
+       if ( tree->root->data_p )
+               result = tree->root->data_p;
+               
+       do
+       {
+               /* break off the remaining part of the path */
+
+               trim_tree_keypath( p, &base, &str );
+                       
+               DEBUG(11,("sorted_tree_find: [loop] base => [%s], new_path => [%s]\n", 
+                       base, str));
+
+               /* iterate to the next child */
+               
+               current = sorted_tree_find_child( current, base );
+       
+               /* 
+                * the idea is that the data_p for a parent should 
+                * be inherited by all children, but allow it to be 
+                * overridden farther down
+                */
+               
+               if ( current && current->data_p )
+                       result = current->data_p;
+
+               /* reset the path pointer 'p' to the remaining part of the key string */
+
+               p = str;
+          
+       } while ( str && current );
+       
+       /* result should be the data_p from the lowest match node in the tree */
+       if ( result )
+               DEBUG(11,("sorted_tree_find: Found data_p!\n"));
+       
+       SAFE_FREE( keystr );
+       
+       DEBUG(10,("sorted_tree_find: Exit\n"));
+       
+       return result;
+}
+
+
diff --git a/source4/lib/bitmap.c b/source4/lib/bitmap.c
new file mode 100644 (file)
index 0000000..1023dd6
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+   Unix SMB/CIFS implementation.
+   simple bitmap functions
+   Copyright (C) Andrew Tridgell 1992-1998
+
+   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"
+
+/* these functions provide a simple way to allocate integers from a
+   pool without repetition */
+
+/****************************************************************************
+allocate a bitmap of the specified size
+****************************************************************************/
+struct bitmap *bitmap_allocate(int n)
+{
+       struct bitmap *bm;
+
+       bm = (struct bitmap *)malloc(sizeof(*bm));
+
+       if (!bm) return NULL;
+       
+       bm->n = n;
+       bm->b = (uint32 *)malloc(sizeof(bm->b[0])*(n+31)/32);
+       if (!bm->b) {
+               SAFE_FREE(bm);
+               return NULL;
+       }
+
+       memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+       return bm;
+}
+
+/****************************************************************************
+free a bitmap.
+****************************************************************************/
+
+void bitmap_free(struct bitmap *bm)
+{
+       if (!bm)
+               return;
+
+       SAFE_FREE(bm->b);
+       SAFE_FREE(bm);
+}
+
+/****************************************************************************
+talloc a bitmap
+****************************************************************************/
+struct bitmap *bitmap_talloc(TALLOC_CTX *mem_ctx, int n)
+{
+       struct bitmap *bm;
+
+       if (!mem_ctx) return NULL;
+
+       bm = (struct bitmap *)talloc(mem_ctx, sizeof(*bm));
+
+       if (!bm) return NULL;
+       
+       bm->n = n;
+       bm->b = (uint32 *)talloc(mem_ctx, sizeof(bm->b[0])*(n+31)/32);
+       if (!bm->b) {
+               return NULL;
+       }
+
+       memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+       return bm;
+}
+
+/****************************************************************************
+set a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_set(struct bitmap *bm, unsigned i)
+{
+       if (i >= bm->n) {
+               DEBUG(0,("Setting invalid bitmap entry %d (of %d)\n",
+                     i, bm->n));
+               return False;
+       }
+       bm->b[i/32] |= (1<<(i%32));
+       return True;
+}
+
+/****************************************************************************
+clear a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_clear(struct bitmap *bm, unsigned i)
+{
+       if (i >= bm->n) {
+               DEBUG(0,("clearing invalid bitmap entry %d (of %d)\n",
+                     i, bm->n));
+               return False;
+       }
+       bm->b[i/32] &= ~(1<<(i%32));
+       return True;
+}
+
+/****************************************************************************
+query a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_query(struct bitmap *bm, unsigned i)
+{
+       if (i >= bm->n) return False;
+       if (bm->b[i/32] & (1<<(i%32))) {
+               return True;
+       }
+       return False;
+}
+
+/****************************************************************************
+find a zero bit in a bitmap starting at the specified offset, with
+wraparound
+****************************************************************************/
+int bitmap_find(struct bitmap *bm, unsigned ofs)
+{
+       unsigned int i, j;
+
+       if (ofs > bm->n) ofs = 0;
+
+       i = ofs;
+       while (i < bm->n) {
+               if (~(bm->b[i/32])) {
+                       j = i;
+                       do {
+                               if (!bitmap_query(bm, j)) return j;
+                               j++;
+                       } while (j & 31 && j < bm->n);
+               }
+               i += 32;
+               i &= ~31;
+       }
+
+       i = 0;
+       while (i < ofs) {
+               if (~(bm->b[i/32])) {
+                       j = i;
+                       do {
+                               if (!bitmap_query(bm, j)) return j;
+                               j++;
+                       } while (j & 31 && j < bm->n);
+               }
+               i += 32;
+               i &= ~31;
+       }
+
+       return -1;
+}
diff --git a/source4/lib/charcnv.c b/source4/lib/charcnv.c
new file mode 100644 (file)
index 0000000..90ddbb4
--- /dev/null
@@ -0,0 +1,925 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Character set conversion Extensions
+   Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Simo Sorce 2001
+   
+   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"
+
+/**
+ * @file
+ *
+ * @brief Character-set conversion routines built on our iconv.
+ * 
+ * @note Samba's internal character set (at least in the 3.0 series)
+ * is always the same as the one for the Unix filesystem.  It is
+ * <b>not</b> necessarily UTF-8 and may be different on machines that
+ * need i18n filenames to be compatible with Unix software.  It does
+ * have to be a superset of ASCII.  All multibyte sequences must start
+ * with a byte with the high bit set.
+ *
+ * @sa lib/iconv.c
+ */
+
+static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
+
+
+/**
+ * Return the name of a charset to give to iconv().
+ **/
+static const char *charset_name(charset_t ch)
+{
+       const char *ret = NULL;
+
+       if (ch == CH_UCS2) ret = "UCS-2LE";
+       else if (ch == CH_UNIX) ret = lp_unix_charset();
+       else if (ch == CH_DOS) ret = lp_dos_charset();
+       else if (ch == CH_DISPLAY) ret = lp_display_charset();
+       else if (ch == CH_UTF8) ret = "UTF8";
+
+       if (!ret || !*ret) ret = "ASCII";
+       return ret;
+}
+
+static void lazy_initialize_conv(void)
+{
+       static int initialized = False;
+
+       if (!initialized) {
+               initialized = True;
+               load_case_tables();
+               init_iconv();
+               init_valid_table();
+       }
+}
+
+/**
+ Initialize iconv conversion descriptors.
+**/
+
+void init_iconv(void)
+{
+       int c1, c2;
+       BOOL did_reload = False;
+
+       /* so that charset_name() works we need to get the UNIX<->UCS2 going
+          first */
+       if (!conv_handles[CH_UNIX][CH_UCS2])
+               conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII");
+
+       if (!conv_handles[CH_UCS2][CH_UNIX])
+               conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE");
+
+       for (c1=0;c1<NUM_CHARSETS;c1++) {
+               for (c2=0;c2<NUM_CHARSETS;c2++) {
+                       const char *n1 = charset_name((charset_t)c1);
+                       const char *n2 = charset_name((charset_t)c2);
+                       if (conv_handles[c1][c2] &&
+                           strcmp(n1, conv_handles[c1][c2]->from_name) == 0 &&
+                           strcmp(n2, conv_handles[c1][c2]->to_name) == 0)
+                               continue;
+
+                       did_reload = True;
+
+                       if (conv_handles[c1][c2])
+                               smb_iconv_close(conv_handles[c1][c2]);
+
+                       conv_handles[c1][c2] = smb_iconv_open(n2,n1);
+                       if (conv_handles[c1][c2] == (smb_iconv_t)-1) {
+                               DEBUG(0,("Conversion from %s to %s not supported\n",
+                                        charset_name((charset_t)c1), charset_name((charset_t)c2)));
+                               conv_handles[c1][c2] = NULL;
+                       }
+               }
+       }
+
+       if (did_reload) {
+               init_valid_table();
+       }
+}
+
+/**
+ * Convert string from one encoding to another, making error checking etc
+ *
+ * @param src pointer to source string (multibyte or singlebyte)
+ * @param srclen length of the source string in bytes
+ * @param dest pointer to destination string (multibyte or singlebyte)
+ * @param destlen maximal length allowed for string
+ * @returns the number of bytes occupied in the destination
+ **/
+ssize_t convert_string(charset_t from, charset_t to,
+                     void const *src, size_t srclen, 
+                     void *dest, size_t destlen)
+{
+       size_t i_len, o_len;
+       size_t retval;
+       const char* inbuf = (const char*)src;
+       char* outbuf = (char*)dest;
+       smb_iconv_t descriptor;
+
+       if (srclen == (size_t)-1)
+               srclen = strlen(src)+1;
+
+       lazy_initialize_conv();
+
+       descriptor = conv_handles[from][to];
+
+       if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+               /* conversion not supported, use as is */
+               size_t len = MIN(srclen,destlen);
+               memcpy(dest,src,len);
+               return len;
+       }
+
+       i_len=srclen;
+       o_len=destlen;
+       retval = smb_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
+       if(retval==(size_t)-1) {
+               const char *reason="unknown error";
+               switch(errno) {
+                       case EINVAL:
+                               reason="Incomplete multibyte sequence";
+                               break;
+                       case E2BIG:
+                               reason="No more room"; 
+                               DEBUG(0, ("convert_string: Required %d, available %d\n",
+                                       srclen, destlen));
+                               /* we are not sure we need srclen bytes,
+                                 may be more, may be less.
+                                 We only know we need more than destlen
+                                 bytes ---simo */
+                              break;
+                       case EILSEQ:
+                              reason="Illegal multibyte sequence";
+                              break;
+               }
+               /* smb_panic(reason); */
+       }
+       return destlen-o_len;
+}
+
+/**
+ * Convert between character sets, allocating a new buffer for the result.
+ *
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ **/
+
+ssize_t convert_string_allocate(charset_t from, charset_t to,
+                               void const *src, size_t srclen, void **dest)
+{
+       size_t i_len, o_len, destlen;
+       size_t retval;
+       const char *inbuf = (const char *)src;
+       char *outbuf, *ob;
+       smb_iconv_t descriptor;
+
+       *dest = NULL;
+
+       if (src == NULL || srclen == (size_t)-1 || srclen == 0)
+               return (size_t)-1;
+
+       lazy_initialize_conv();
+
+       descriptor = conv_handles[from][to];
+
+       if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+               /* conversion not supported, return -1*/
+               DEBUG(3, ("convert_string_allocate: conversion not supported!\n"));
+               return -1;
+       }
+
+       destlen = MAX(srclen, 512);
+       outbuf = NULL;
+convert:
+       destlen = destlen * 2;
+       ob = (char *)realloc(outbuf, destlen);
+       if (!ob) {
+               DEBUG(0, ("convert_string_allocate: realloc failed!\n"));
+               SAFE_FREE(outbuf);
+               return (size_t)-1;
+       }
+       else
+               outbuf = ob;
+       i_len = srclen;
+       o_len = destlen;
+       retval = smb_iconv(descriptor,
+                          &inbuf, &i_len,
+                          &outbuf, &o_len);
+       if(retval == (size_t)-1)                {
+               const char *reason="unknown error";
+               switch(errno) {
+                       case EINVAL:
+                               reason="Incomplete multibyte sequence";
+                               break;
+                       case E2BIG:
+                               goto convert;           
+                       case EILSEQ:
+                               reason="Illegal multibyte sequence";
+                               break;
+               }
+               DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
+               /* smb_panic(reason); */
+               return (size_t)-1;
+       }
+       
+       destlen = destlen - o_len;
+       *dest = (char *)Realloc(ob,destlen);
+       if (!*dest) {
+               DEBUG(0, ("convert_string_allocate: out of memory!\n"));
+               SAFE_FREE(ob);
+               return (size_t)-1;
+       }
+
+       return destlen;
+}
+
+
+/**
+ * Convert between character sets, allocating a new buffer using talloc for the result.
+ *
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL 
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ **/
+ssize_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to,
+                             void const *src, size_t srclen, const void **dest)
+{
+       void *alloced_string;
+       size_t dest_len;
+       void *dst;
+
+       *dest = NULL;
+       dest_len=convert_string_allocate(from, to, src, srclen, &alloced_string);
+       if (dest_len == (size_t)-1)
+               return (size_t)-1;
+       dst = talloc(ctx, dest_len + 2);
+       /* we want to be absolutely sure that the result is terminated */
+       memcpy(dst, alloced_string, dest_len);
+       SSVAL(dst, dest_len, 0);
+       SAFE_FREE(alloced_string);
+       if (dst == NULL)
+               return -1;
+       *dest = dst;
+       return dest_len;
+}
+
+size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+       size_t size;
+       smb_ucs2_t *buffer;
+       
+       size = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen,
+                                      (void **) &buffer);
+       if (size == -1) {
+               smb_panic("failed to create UCS2 buffer");
+       }
+       if (!strupper_w(buffer) && (dest == src)) {
+               free(buffer);
+               return srclen;
+       }
+       
+       size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
+       free(buffer);
+       return size;
+}
+
+size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+       size_t size;
+       smb_ucs2_t *buffer;
+       
+       size = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen,
+                                      (void **) &buffer);
+       if (size == -1) {
+               smb_panic("failed to create UCS2 buffer");
+       }
+       if (!strlower_w(buffer) && (dest == src)) {
+               free(buffer);
+               return srclen;
+       }
+       size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen);
+       free(buffer);
+       return size;
+}
+
+size_t ucs2_align(const void *base_ptr, const void *p, int flags)
+{
+       if (flags & (STR_NOALIGN|STR_ASCII))
+               return 0;
+       return PTR_DIFF(p, base_ptr) & 1;
+}
+
+
+/**
+ * Copy a string from a char* unix src to a dos codepage string destination.
+ *
+ * @return the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can include
+ * <dl>
+ * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
+ * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
+ * </dl>
+ *
+ * @param dest_len the maximum length in bytes allowed in the
+ * destination.  If @p dest_len is -1 then no maximum is used.
+ **/
+ssize_t push_ascii(void *dest, const char *src, size_t dest_len, int flags)
+{
+       size_t src_len = strlen(src);
+       pstring tmpbuf;
+
+       /* treat a pstring as "unlimited" length */
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (flags & STR_UPPER) {
+               pstrcpy(tmpbuf, src);
+               strupper(tmpbuf);
+               src = tmpbuf;
+       }
+
+       if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
+               src_len++;
+
+       return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len);
+}
+
+ssize_t push_ascii_fstring(void *dest, const char *src)
+{
+       return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+ssize_t push_ascii_pstring(void *dest, const char *src)
+{
+       return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
+}
+
+ssize_t push_pstring(void *dest, const char *src)
+{
+       return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a dos codepage source to a unix char* destination.
+ *
+ * The resulting string in "dest" is always null terminated.
+ *
+ * @param flags can have:
+ * <dl>
+ * <dt>STR_TERMINATE</dt>
+ * <dd>STR_TERMINATE means the string in @p src
+ * is null terminated, and src_len is ignored.</dd>
+ * </dl>
+ *
+ * @param src_len is the length of the source area in bytes.
+ * @returns the number of bytes occupied by the string in @p src.
+ **/
+ssize_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+       size_t ret;
+
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (flags & STR_TERMINATE) {
+               if (src_len == (size_t)-1) {
+                       src_len = strlen(src) + 1;
+               } else {
+                       size_t len = strnlen(src, src_len);
+                       if (len < src_len)
+                               len++;
+                       src_len = len;
+               }
+       }
+
+       ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len);
+
+       if (dest_len)
+               dest[MIN(ret, dest_len-1)] = 0;
+
+       return src_len;
+}
+
+ssize_t pull_ascii_pstring(char *dest, const void *src)
+{
+       return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+ssize_t pull_ascii_fstring(char *dest, const void *src)
+{
+       return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a char* src to a unicode destination.
+ *
+ * @returns the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can have:
+ *
+ * <dl>
+ * <dt>STR_TERMINATE <dd>means include the null termination.
+ * <dt>STR_UPPER     <dd>means uppercase in the destination.
+ * <dt>STR_NOALIGN   <dd>means don't do alignment.
+ * </dl>
+ *
+ * @param dest_len is the maximum length allowed in the
+ * destination. If dest_len is -1 then no maxiumum is used.
+ **/
+ssize_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+       size_t len=0;
+       size_t src_len = strlen(src);
+       pstring tmpbuf;
+
+       /* treat a pstring as "unlimited" length */
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (flags & STR_UPPER) {
+               pstrcpy(tmpbuf, src);
+               strupper(tmpbuf);
+               src = tmpbuf;
+       }
+
+       if (flags & STR_TERMINATE)
+               src_len++;
+
+       if (ucs2_align(base_ptr, dest, flags)) {
+               *(char *)dest = 0;
+               dest = (void *)((char *)dest + 1);
+               if (dest_len) dest_len--;
+               len++;
+       }
+
+       /* ucs2 is always a multiple of 2 bytes */
+       dest_len &= ~1;
+
+       len += convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len);
+       return len;
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination,
+ * allocating a buffer using talloc().
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ *         or -1 in case of error.
+ **/
+ssize_t push_ucs2_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+
+       *dest = NULL;
+       return convert_string_talloc(ctx, CH_UNIX, CH_UCS2, src, src_len, (const void **)dest);
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ *         or -1 in case of error.
+ **/
+
+ssize_t push_ucs2_allocate(smb_ucs2_t **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+
+       *dest = NULL;
+       return convert_string_allocate(CH_UNIX, CH_UCS2, src, src_len, (void **)dest);  
+}
+
+/**
+ Copy a string from a char* src to a UTF-8 destination.
+ Return the number of bytes occupied by the string in the destination
+ Flags can have:
+  STR_TERMINATE means include the null termination
+  STR_UPPER     means uppercase in the destination
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+ssize_t push_utf8(void *dest, const char *src, size_t dest_len, int flags)
+{
+       size_t src_len = strlen(src);
+       pstring tmpbuf;
+
+       /* treat a pstring as "unlimited" length */
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (flags & STR_UPPER) {
+               pstrcpy(tmpbuf, src);
+               strupper(tmpbuf);
+               src = tmpbuf;
+       }
+
+       if (flags & STR_TERMINATE)
+               src_len++;
+
+       return convert_string(CH_UNIX, CH_UTF8, src, src_len, dest, dest_len);
+}
+
+ssize_t push_utf8_fstring(void *dest, const char *src)
+{
+       return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+ssize_t push_utf8_pstring(void *dest, const char *src)
+{
+       return push_utf8(dest, src, sizeof(pstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+
+       *dest = NULL;
+       return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (const void **)dest);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t push_utf8_allocate(char **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+
+       *dest = NULL;
+       return convert_string_allocate(CH_UNIX, CH_UTF8, src, src_len, (void **)dest);  
+}
+
+/**
+ Copy a string from a ucs2 source to a unix char* destination.
+ Flags can have:
+  STR_TERMINATE means the string in src is null terminated.
+  STR_NOALIGN   means don't try to align.
+ if STR_TERMINATE is set then src_len is ignored if it is -1.
+ src_len is the length of the source area in bytes
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+size_t pull_ucs2(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+       size_t ret;
+
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (ucs2_align(base_ptr, src, flags)) {
+               src = (const void *)((const char *)src + 1);
+               if (src_len > 0)
+                       src_len--;
+       }
+
+       if (flags & STR_TERMINATE) {
+               if (src_len == (size_t)-1) {
+                       src_len = strlen_w(src)*2 + 2;
+               } else {
+                       size_t len = strnlen_w(src, src_len/2);
+                       if (len < src_len/2)
+                               len++;
+                       src_len = len*2;
+               }
+       }
+
+       /* ucs2 is always a multiple of 2 bytes */
+       if (src_len != (size_t)-1)
+               src_len &= ~1;
+       
+       ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len);
+       if (dest_len)
+               dest[MIN(ret, dest_len-1)] = 0;
+
+       return src_len;
+}
+
+ssize_t pull_ucs2_pstring(char *dest, const void *src)
+{
+       return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+ssize_t pull_ucs2_fstring(char *dest, const void *src)
+{
+       return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const smb_ucs2_t *src)
+{
+       size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+       *dest = NULL;
+       return convert_string_talloc(ctx, CH_UCS2, CH_UNIX, src, src_len, (const void **)dest);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t pull_ucs2_allocate(void **dest, const smb_ucs2_t *src)
+{
+       size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+       *dest = NULL;
+       return convert_string_allocate(CH_UCS2, CH_UNIX, src, src_len, dest);   
+}
+
+/**
+ Copy a string from a utf-8 source to a unix char* destination.
+ Flags can have:
+  STR_TERMINATE means the string in src is null terminated.
+ if STR_TERMINATE is set then src_len is ignored.
+ src_len is the length of the source area in bytes
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+ssize_t pull_utf8(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+       size_t ret;
+
+       if (dest_len == (size_t)-1)
+               dest_len = sizeof(pstring);
+
+       if (flags & STR_TERMINATE) {
+               if (src_len == (size_t)-1) {
+                       src_len = strlen(src) + 1;
+               } else {
+                       size_t len = strnlen(src, src_len);
+                       if (len < src_len)
+                               len++;
+                       src_len = len;
+               }
+       }
+
+       ret = convert_string(CH_UTF8, CH_UNIX, src, src_len, dest, dest_len);
+       if (dest_len)
+               dest[MIN(ret, dest_len-1)] = 0;
+
+       return src_len;
+}
+
+ssize_t pull_utf8_pstring(char *dest, const void *src)
+{
+       return pull_utf8(dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+ssize_t pull_utf8_fstring(char *dest, const void *src)
+{
+       return pull_utf8(dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+       *dest = NULL;
+       return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (const void **)dest);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL 
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+ssize_t pull_utf8_allocate(void **dest, const char *src)
+{
+       size_t src_len = strlen(src)+1;
+       *dest = NULL;
+       return convert_string_allocate(CH_UTF8, CH_UNIX, src, src_len, dest);   
+}
+/**
+ Copy a string from a char* src to a unicode or ascii
+ dos codepage destination choosing unicode or ascii based on the 
+ flags in the SMB buffer starting at base_ptr.
+ Return the number of bytes occupied by the string in the destination.
+ flags can have:
+  STR_TERMINATE means include the null termination.
+  STR_UPPER     means uppercase in the destination.
+  STR_ASCII     use ascii even with unicode packet.
+  STR_NOALIGN   means don't do alignment.
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+ssize_t push_string(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+       if (!(flags & STR_ASCII) && \
+           ((flags & STR_UNICODE || \
+             (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) {
+               return push_ucs2(base_ptr, dest, src, dest_len, flags);
+       }
+       return push_ascii(dest, src, dest_len, flags);
+}
+
+
+/**
+ Copy a string from a unicode or ascii source (depending on
+ the packet flags) to a char* destination.
+ Flags can have:
+  STR_TERMINATE means the string in src is null terminated.
+  STR_UNICODE   means to force as unicode.
+  STR_ASCII     use ascii even with unicode packet.
+  STR_NOALIGN   means don't do alignment.
+ if STR_TERMINATE is set then src_len is ignored is it is -1
+ src_len is the length of the source area in bytes.
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+ssize_t pull_string(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+       if (!(flags & STR_ASCII) && \
+           ((flags & STR_UNICODE || \
+             (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) {
+               return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags);
+       }
+       return pull_ascii(dest, src, dest_len, src_len, flags);
+}
+
+ssize_t align_string(const void *base_ptr, const char *p, int flags)
+{
+       if (!(flags & STR_ASCII) && \
+           ((flags & STR_UNICODE || \
+             (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) {
+               return ucs2_align(base_ptr, p, flags);
+       }
+       return 0;
+}
+
+/**
+ Copy a string from a unicode or ascii source (depending on
+ the packet flags) to a TALLOC'ed destination.
+ Flags can have:
+  STR_TERMINATE means the string in src is null terminated.
+  STR_UNICODE   means to force as unicode.
+  STR_ASCII     use ascii even with unicode packet.
+  STR_NOALIGN   means don't do alignment.
+ if STR_TERMINATE is set then src_len is ignored is it is -1
+ src_len is the length of the source area in bytes.
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+ssize_t pull_string_talloc(TALLOC_CTX *ctx, char **dest, const void *src, size_t src_len, int flags)
+{
+       if (!(flags & STR_ASCII) && \
+           (flags & STR_UNICODE)) {
+               return pull_ucs2_talloc(ctx, dest, src);
+       }
+       *dest = NULL;
+       if (flags & STR_TERMINATE) {
+               *dest = talloc_strdup(ctx, src);
+               return strlen(*dest);
+       }
+       *dest = talloc_strndup(ctx, src, src_len);
+       return src_len;
+}
+
+/**
+ Convert from ucs2 to unix charset and return the
+ allocated and converted string or NULL if an error occurred.
+ You must provide a zero terminated string.
+ The returning string will be zero terminated.
+**/
+
+char *acnv_u2ux(const smb_ucs2_t *src)
+{
+       size_t slen;
+       size_t dlen;
+       void *dest;
+       
+       slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t);
+       dlen = convert_string_allocate(CH_UCS2, CH_UNIX, src, slen, &dest);
+       if (dlen == (size_t)-1)
+               return NULL;
+       else
+               return dest;
+}
+
+/**
+ Convert from unix to ucs2 charset and return the
+ allocated and converted string or NULL if an error occurred.
+ You must provide a zero terminated string.
+ The returning string will be zero terminated.
+**/
+
+smb_ucs2_t *acnv_uxu2(const char *src)
+{
+       size_t slen;
+       size_t dlen;
+       void *dest;
+       
+       slen = strlen(src) + 1;
+       dlen = convert_string_allocate(CH_UNIX, CH_UCS2, src, slen, &dest);
+       if (dlen == (size_t)-1)
+               return NULL;
+       else
+               return dest;
+}
+
+/**
+ Convert from ucs2 to dos charset and return the
+ allocated and converted string or NULL if an error occurred.
+ You must provide a zero terminated string.
+ The returning string will be zero terminated.
+**/
+
+char *acnv_u2dos(const smb_ucs2_t *src)
+{
+       size_t slen;
+       size_t dlen;
+       void *dest;
+       
+       slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t);
+       dlen = convert_string_allocate(CH_UCS2, CH_DOS, src, slen, &dest);
+       if (dlen == (size_t)-1)
+               return NULL;
+       else
+               return dest;
+}
+
+/**
+ Convert from dos to ucs2 charset and return the
+ allocated and converted string or NULL if an error occurred.
+ You must provide a zero terminated string.
+ The returning string will be zero terminated.
+**/
+
+smb_ucs2_t *acnv_dosu2(const char *src)
+{
+       size_t slen;
+       size_t dlen;
+       void *dest;
+       
+       slen = strlen(src) + 1;
+       dlen = convert_string_allocate(CH_DOS, CH_UCS2, src, slen, &dest);
+       if (dlen == (size_t)-1)
+               return NULL;
+       else
+               return dest;
+}
diff --git a/source4/lib/cmdline/popt_common.c b/source4/lib/cmdline/popt_common.c
new file mode 100644 (file)
index 0000000..ccdecc9
--- /dev/null
@@ -0,0 +1,327 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Common popt routines
+
+   Copyright (C) Tim Potter 2001,2002
+   Copyright (C) Jelmer Vernooij 2002,2003
+
+   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"
+
+/* Handle command line options:
+ *             -d,--debuglevel 
+ *             -s,--configfile 
+ *             -O,--socket-options 
+ *             -V,--version
+ *             -l,--log-base
+ *             -n,--netbios-name
+ *             -W,--workgroup
+ *             -i,--scope
+ */
+
+extern pstring user_socket_options;
+extern BOOL AllowDebugChange;
+
+struct user_auth_info cmdline_auth_info;
+
+static void popt_common_callback(poptContext con, 
+                          enum poptCallbackReason reason,
+                          const struct poptOption *opt,
+                          const char *arg, const void *data)
+{
+       pstring logfile;
+       const char *pname;
+       
+       /* Find out basename of current program */
+       pname = strrchr_m(poptGetInvocationName(con),'/');
+
+       if (!pname)
+               pname = poptGetInvocationName(con);
+       else 
+               pname++;
+
+       if (reason == POPT_CALLBACK_REASON_PRE) {
+               pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname);
+               lp_set_cmdline("log file", logfile);
+               return;
+       }
+
+       switch(opt->val) {
+       case 'd':
+               lp_set_cmdline("log level", arg);
+               break;
+
+       case 'V':
+               printf( "Version %s\n", SAMBA_VERSION );
+               exit(0);
+               break;
+
+       case 's':
+               if (arg) {
+                       pstrcpy(dyn_CONFIGFILE, arg);
+               }
+               break;
+
+       case 'l':
+               if (arg) {
+                       pstr_sprintf(logfile, "%s/log.%s", arg, pname);
+                       lp_set_cmdline("log file", logfile);
+               }
+               break;
+               
+       case 'W':
+               lp_set_cmdline("workgroup", arg);
+               break;
+               
+       case 'n':
+               lp_set_cmdline("netbios name", arg);
+               break;
+               
+       case 'i':
+               lp_set_cmdline("netbios scope", arg);
+               break;
+       }
+}
+
+struct poptOption popt_common_connection[] = {
+       { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+       { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
+         "SOCKETOPTIONS" },
+       { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
+       { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
+       { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
+       POPT_TABLEEND
+};
+
+struct poptOption popt_common_samba[] = {
+       { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback },
+       { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
+       { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
+       { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
+       { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+       POPT_TABLEEND
+};
+
+struct poptOption popt_common_version[] = {
+       { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+       { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+       POPT_TABLEEND
+};
+
+
+
+/****************************************************************************
+ * get a password from a a file or file descriptor
+ * exit on failure
+ * ****************************************************************************/
+static void get_password_file(struct user_auth_info *a)
+{
+       int fd = -1;
+       char *p;
+       BOOL close_it = False;
+       pstring spec;
+       char pass[128];
+
+       if ((p = getenv("PASSWD_FD")) != NULL) {
+               pstrcpy(spec, "descriptor ");
+               pstrcat(spec, p);
+               sscanf(p, "%d", &fd);
+               close_it = False;
+       } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+               fd = sys_open(p, O_RDONLY, 0);
+               pstrcpy(spec, p);
+               if (fd < 0) {
+                       fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+                                       spec, strerror(errno));
+                       exit(1);
+               }
+               close_it = True;
+       }
+
+       for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+               p && p - pass < sizeof(pass);) {
+               switch (read(fd, p, 1)) {
+               case 1:
+                       if (*p != '\n' && *p != '\0') {
+                               *++p = '\0'; /* advance p, and null-terminate pass */
+                               break;
+                       }
+               case 0:
+                       if (p - pass) {
+                               *p = '\0'; /* null-terminate it, just in case... */
+                               p = NULL; /* then force the loop condition to become false */
+                               break;
+                       } else {
+                               fprintf(stderr, "Error reading password from file %s: %s\n",
+                                               spec, "empty password\n");
+                               exit(1);
+                       }
+
+               default:
+                       fprintf(stderr, "Error reading password from file %s: %s\n",
+                                       spec, strerror(errno));
+                       exit(1);
+               }
+       }
+       pstrcpy(a->password, pass);
+       if (close_it)
+               close(fd);
+}
+
+static void get_credentials_file(const char *file, struct user_auth_info *info) 
+{
+       XFILE *auth;
+       fstring buf;
+       uint16 len = 0;
+       char *ptr, *val, *param;
+
+       if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
+       {
+               /* fail if we can't open the credentials file */
+               d_printf("ERROR: Unable to open credentials file!\n");
+               exit(-1);
+       }
+
+       while (!x_feof(auth))
+       {
+               /* get a line from the file */
+               if (!x_fgets(buf, sizeof(buf), auth))
+                       continue;
+               len = strlen(buf);
+
+               if ((len) && (buf[len-1]=='\n'))
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }
+               if (len == 0)
+                       continue;
+
+               /* break up the line into parameter & value.
+                * will need to eat a little whitespace possibly */
+               param = buf;
+               if (!(ptr = strchr_m (buf, '=')))
+                       continue;
+
+               val = ptr+1;
+               *ptr = '\0';
+
+               /* eat leading white space */
+               while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+                       val++;
+
+               if (strwicmp("password", param) == 0)
+               {
+                       pstrcpy(info->password, val);
+                       info->got_pass = True;
+               }
+               else if (strwicmp("username", param) == 0)
+                       pstrcpy(info->username, val);
+               //else if (strwicmp("domain", param) == 0)
+               //      set_global_myworkgroup(val);
+               memset(buf, 0, sizeof(buf));
+       }
+       x_fclose(auth);
+}
+
+/* Handle command line options:
+ *             -U,--user
+ *             -A,--authentication-file
+ *             -k,--use-kerberos
+ *             -N,--no-pass
+ */
+
+
+static void popt_common_credentials_callback(poptContext con, 
+                                                                                        enum poptCallbackReason reason,
+                                                                                        const struct poptOption *opt,
+                                                                                        const char *arg, const void *data)
+{
+       char *p;
+
+       if (reason == POPT_CALLBACK_REASON_PRE) {
+               cmdline_auth_info.use_kerberos = False;
+               cmdline_auth_info.got_pass = False;
+               pstrcpy(cmdline_auth_info.username, "GUEST");   
+
+               if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
+
+               if (getenv("USER")) {
+                       pstrcpy(cmdline_auth_info.username,getenv("USER"));
+
+                       if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
+                               *p = 0;
+                               pstrcpy(cmdline_auth_info.password,p+1);
+                               cmdline_auth_info.got_pass = True;
+                               memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
+                       }
+               }
+
+               if (getenv("PASSWD")) {
+                       pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
+                       cmdline_auth_info.got_pass = True;
+               }
+
+               if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+                       get_password_file(&cmdline_auth_info);
+                       cmdline_auth_info.got_pass = True;
+               }
+
+               return;
+       }
+
+       switch(opt->val) {
+       case 'U':
+               {
+                       char *lp;
+
+                       pstrcpy(cmdline_auth_info.username,arg);
+                       if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
+                               *lp = 0;
+                               pstrcpy(cmdline_auth_info.password,lp+1);
+                               cmdline_auth_info.got_pass = True;
+                               memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
+                       }
+               }
+               break;
+
+       case 'A':
+               get_credentials_file(arg, &cmdline_auth_info);
+               break;
+
+       case 'k':
+#ifndef HAVE_KRB5
+               d_printf("No kerberos support compiled in\n");
+               exit(1);
+#else
+               cmdline_auth_info.use_kerberos = True;
+               cmdline_auth_info.got_pass = True;
+#endif
+               break;
+       }
+}
+
+
+
+struct poptOption popt_common_credentials[] = {
+       { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback },
+       { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
+       { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, True, "Don't ask for a password" },
+       { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" },
+       { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
+       POPT_TABLEEND
+};
diff --git a/source4/lib/cmdline/readline.c b/source4/lib/cmdline/readline.c
new file mode 100644 (file)
index 0000000..c5da88b
--- /dev/null
@@ -0,0 +1,159 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba readline wrapper implementation
+   Copyright (C) Simo Sorce 2001
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+#ifdef HAVE_LIBREADLINE
+#  ifdef HAVE_READLINE_READLINE_H
+#    include <readline/readline.h>
+#    ifdef HAVE_READLINE_HISTORY_H
+#      include <readline/history.h>
+#    endif
+#  else
+#    ifdef HAVE_READLINE_H
+#      include <readline.h>
+#      ifdef HAVE_HISTORY_H
+#        include <history.h>
+#      endif
+#    else
+#      undef HAVE_LIBREADLINE
+#    endif
+#  endif
+#endif
+
+#ifdef HAVE_NEW_LIBREADLINE
+#  define RL_COMPLETION_CAST (rl_completion_func_t *)
+#else
+/* This type is missing from libreadline<4.0  (approximately) */
+#  define RL_COMPLETION_CAST
+#endif /* HAVE_NEW_LIBREADLINE */
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly
+****************************************************************************/
+
+static char *smb_readline_replacement(const char *prompt, void (*callback)(void), 
+                               char **(completion_fn)(const char *text, int start, int end))
+{
+       fd_set fds;
+       static pstring line;
+       struct timeval timeout;
+       int fd = x_fileno(x_stdin);
+       char *ret;
+
+       do_debug("%s", prompt);
+
+       while (1) {
+               timeout.tv_sec = 5;
+               timeout.tv_usec = 0;
+
+               FD_ZERO(&fds);
+               FD_SET(fd,&fds);
+       
+               if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) {
+                       ret = x_fgets(line, sizeof(line), x_stdin);
+                       return ret;
+               }
+               if (callback)
+                       callback();
+       }
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly.
+****************************************************************************/
+
+char *smb_readline(const char *prompt, void (*callback)(void), 
+                  char **(completion_fn)(const char *text, int start, int end))
+{
+#if HAVE_LIBREADLINE
+       if (isatty(x_fileno(x_stdin))) {
+               char *ret;
+
+               /* Aargh!  Readline does bizzare things with the terminal width
+               that mucks up expect(1).  Set CLI_NO_READLINE in the environment
+               to force readline not to be used. */
+
+               if (getenv("CLI_NO_READLINE"))
+                       return smb_readline_replacement(prompt, callback, completion_fn);
+
+               if (completion_fn) {
+                       /* The callback prototype has changed slightly between
+                       different versions of Readline, so the same function
+                       works in all of them to date, but we get compiler
+                       warnings in some.  */
+                       rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn;
+               }
+
+               if (callback)
+                       rl_event_hook = (Function *)callback;
+               ret = readline(prompt);
+               if (ret && *ret)
+                       add_history(ret);
+               return ret;
+       } else
+#endif
+       return smb_readline_replacement(prompt, callback, completion_fn);
+}
+
+/****************************************************************************
+ * return line buffer text
+ ****************************************************************************/
+const char *smb_readline_get_line_buffer(void)
+{
+#if defined(HAVE_LIBREADLINE)
+       return rl_line_buffer;
+#else
+       return NULL;
+#endif
+}
+
+/****************************************************************************
+ * set completion append character
+ ***************************************************************************/
+void smb_readline_ca_char(char c)
+{
+#if defined(HAVE_LIBREADLINE)
+       rl_completion_append_character = c;
+#endif
+}
+
+
+/****************************************************************************
+history
+****************************************************************************/
+int cmd_history(void)
+{
+#if defined(HAVE_LIBREADLINE)
+       HIST_ENTRY **hlist;
+       int i;
+
+       hlist = history_list();
+       
+       for (i = 0; hlist && hlist[i]; i++) {
+               DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+       }
+#else
+       DEBUG(0,("no history without readline support\n"));
+#endif
+
+       return 0;
+}
diff --git a/source4/lib/crc32.c b/source4/lib/crc32.c
new file mode 100644 (file)
index 0000000..86b0bb6
--- /dev/null
@@ -0,0 +1,71 @@
+/* 
+   Unix SMB/CIFS implementation.
+   crc32 implementation
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/* table generated using algorithm from Mark Adler */
+static const uint32 crc_table[] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
+       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
+       0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
+       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
+       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
+       0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
+       0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
+       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
+       0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
+       0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
+       0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
+       0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
+       0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
+       0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
+       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
+       0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
+       0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
+       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
+       0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
+       0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
+       0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
+       0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+/*
+  see PNG specification or ISO-3309 for details
+*/
+uint32 crc32_buffer(const uint8 *buf, int n)
+{
+       int i;
+       uint32 ret;
+       for (ret=~0, i=0;i<n; i++) {
+               ret = crc_table[0xff & (buf[i] ^ ret)] ^ (ret >> 8);
+       }
+       return ~ret;
+}
diff --git a/source4/lib/crypto/crc32.c b/source4/lib/crypto/crc32.c
new file mode 100644 (file)
index 0000000..86b0bb6
--- /dev/null
@@ -0,0 +1,71 @@
+/* 
+   Unix SMB/CIFS implementation.
+   crc32 implementation
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/* table generated using algorithm from Mark Adler */
+static const uint32 crc_table[] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
+       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
+       0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
+       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
+       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
+       0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
+       0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
+       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
+       0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
+       0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
+       0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
+       0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
+       0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
+       0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
+       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
+       0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
+       0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
+       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
+       0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
+       0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
+       0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
+       0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+/*
+  see PNG specification or ISO-3309 for details
+*/
+uint32 crc32_buffer(const uint8 *buf, int n)
+{
+       int i;
+       uint32 ret;
+       for (ret=~0, i=0;i<n; i++) {
+               ret = crc_table[0xff & (buf[i] ^ ret)] ^ (ret >> 8);
+       }
+       return ~ret;
+}
diff --git a/source4/lib/crypto/hmacmd5.c b/source4/lib/crypto/hmacmd5.c
new file mode 100644 (file)
index 0000000..f436fd3
--- /dev/null
@@ -0,0 +1,134 @@
+/* 
+   Unix SMB/CIFS implementation.
+   HMAC MD5 code for use in NTLMv2
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+/* taken direct from rfc2104 implementation and modified for suitable use
+ * for ntlmv2.
+ */
+
+#include "includes.h"
+
+/***********************************************************************
+ the rfc 2104 version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_rfc2104(uchar*  key, int key_len, HMACMD5Context *ctx)
+{
+        int i;
+
+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
+        if (key_len > 64)
+       {
+               uchar tk[16];
+                struct MD5Context tctx;
+
+                MD5Init(&tctx);
+                MD5Update(&tctx, key, key_len);
+                MD5Final(tk, &tctx);
+
+                key = tk;
+                key_len = 16;
+        }
+
+        /* start out by storing key in pads */
+        ZERO_STRUCT(ctx->k_ipad);
+        ZERO_STRUCT(ctx->k_opad);
+        memcpy( ctx->k_ipad, key, key_len);
+        memcpy( ctx->k_opad, key, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++)
+       {
+                ctx->k_ipad[i] ^= 0x36;
+                ctx->k_opad[i] ^= 0x5c;
+        }
+
+        MD5Init(&ctx->ctx);
+        MD5Update(&ctx->ctx, ctx->k_ipad, 64);  
+}
+
+/***********************************************************************
+ the microsoft version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_limK_to_64(const uchar* key, int key_len,
+                       HMACMD5Context *ctx)
+{
+        int i;
+
+        /* if key is longer than 64 bytes truncate it */
+        if (key_len > 64)
+       {
+                key_len = 64;
+        }
+
+        /* start out by storing key in pads */
+        ZERO_STRUCT(ctx->k_ipad);
+        ZERO_STRUCT(ctx->k_opad);
+        memcpy( ctx->k_ipad, key, key_len);
+        memcpy( ctx->k_opad, key, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++) {
+                ctx->k_ipad[i] ^= 0x36;
+                ctx->k_opad[i] ^= 0x5c;
+        }
+
+        MD5Init(&ctx->ctx);
+        MD5Update(&ctx->ctx, ctx->k_ipad, 64);  
+}
+
+/***********************************************************************
+ update hmac_md5 "inner" buffer
+***********************************************************************/
+void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx)
+{
+        MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+}
+
+/***********************************************************************
+ finish off hmac_md5 "inner" buffer and generate outer one.
+***********************************************************************/
+void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
+
+{
+        struct MD5Context ctx_o;
+
+        MD5Final(digest, &ctx->ctx);          
+
+        MD5Init(&ctx_o);
+        MD5Update(&ctx_o, ctx->k_opad, 64);   
+        MD5Update(&ctx_o, digest, 16); 
+        MD5Final(digest, &ctx_o);
+}
+
+/***********************************************************
+ single function to calculate an HMAC MD5 digest from data.
+ use the microsoft hmacmd5 init method because the key is 16 bytes.
+************************************************************/
+void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+{
+       HMACMD5Context ctx;
+       hmac_md5_init_limK_to_64(key, 16, &ctx);
+       if (data_len != 0)
+       {
+               hmac_md5_update(data, data_len, &ctx);
+       }
+       hmac_md5_final(digest, &ctx);
+}
+
diff --git a/source4/lib/crypto/md4.c b/source4/lib/crypto/md4.c
new file mode 100644 (file)
index 0000000..417e87b
--- /dev/null
@@ -0,0 +1,175 @@
+/* 
+   Unix SMB/CIFS implementation.
+   a implementation of MD4 designed for use in the SMB authentication protocol
+   Copyright (C) Andrew Tridgell 1997-1998.
+   
+   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"
+
+/* NOTE: This code makes no attempt to be fast! 
+
+   It assumes that a int is at least 32 bits long
+*/
+
+struct mdfour_state {
+       uint32 A, B, C, D;
+};
+
+static uint32 F(uint32 X, uint32 Y, uint32 Z)
+{
+       return (X&Y) | ((~X)&Z);
+}
+
+static uint32 G(uint32 X, uint32 Y, uint32 Z)
+{
+       return (X&Y) | (X&Z) | (Y&Z); 
+}
+
+static uint32 H(uint32 X, uint32 Y, uint32 Z)
+{
+       return X^Y^Z;
+}
+
+static uint32 lshift(uint32 x, int s)
+{
+       x &= 0xFFFFFFFF;
+       return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(struct mdfour_state *s, uint32 *M)
+{
+       int j;
+       uint32 AA, BB, CC, DD;
+       uint32 X[16];
+
+       for (j=0;j<16;j++)
+               X[j] = M[j];
+
+       AA = s->A; BB = s->B; CC = s->C; DD = s->D;
+
+        ROUND1(s->A,s->B,s->C,s->D,  0,  3);  ROUND1(s->D,s->A,s->B,s->C,  1,  7);  
+       ROUND1(s->C,s->D,s->A,s->B,  2, 11);  ROUND1(s->B,s->C,s->D,s->A,  3, 19);
+        ROUND1(s->A,s->B,s->C,s->D,  4,  3);  ROUND1(s->D,s->A,s->B,s->C,  5,  7);  
+       ROUND1(s->C,s->D,s->A,s->B,  6, 11);  ROUND1(s->B,s->C,s->D,s->A,  7, 19);
+        ROUND1(s->A,s->B,s->C,s->D,  8,  3);  ROUND1(s->D,s->A,s->B,s->C,  9,  7);  
+       ROUND1(s->C,s->D,s->A,s->B, 10, 11);  ROUND1(s->B,s->C,s->D,s->A, 11, 19);
+        ROUND1(s->A,s->B,s->C,s->D, 12,  3);  ROUND1(s->D,s->A,s->B,s->C, 13,  7);  
+       ROUND1(s->C,s->D,s->A,s->B, 14, 11);  ROUND1(s->B,s->C,s->D,s->A, 15, 19);      
+
+        ROUND2(s->A,s->B,s->C,s->D,  0,  3);  ROUND2(s->D,s->A,s->B,s->C,  4,  5);  
+       ROUND2(s->C,s->D,s->A,s->B,  8,  9);  ROUND2(s->B,s->C,s->D,s->A, 12, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  1,  3);  ROUND2(s->D,s->A,s->B,s->C,  5,  5);  
+       ROUND2(s->C,s->D,s->A,s->B,  9,  9);  ROUND2(s->B,s->C,s->D,s->A, 13, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  2,  3);  ROUND2(s->D,s->A,s->B,s->C,  6,  5);  
+       ROUND2(s->C,s->D,s->A,s->B, 10,  9);  ROUND2(s->B,s->C,s->D,s->A, 14, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  3,  3);  ROUND2(s->D,s->A,s->B,s->C,  7,  5);  
+       ROUND2(s->C,s->D,s->A,s->B, 11,  9);  ROUND2(s->B,s->C,s->D,s->A, 15, 13);
+
+       ROUND3(s->A,s->B,s->C,s->D,  0,  3);  ROUND3(s->D,s->A,s->B,s->C,  8,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  4, 11);  ROUND3(s->B,s->C,s->D,s->A, 12, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  2,  3);  ROUND3(s->D,s->A,s->B,s->C, 10,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  6, 11);  ROUND3(s->B,s->C,s->D,s->A, 14, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  1,  3);  ROUND3(s->D,s->A,s->B,s->C,  9,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  5, 11);  ROUND3(s->B,s->C,s->D,s->A, 13, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  3,  3);  ROUND3(s->D,s->A,s->B,s->C, 11,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  7, 11);  ROUND3(s->B,s->C,s->D,s->A, 15, 15);
+
+       s->A += AA; 
+       s->B += BB; 
+       s->C += CC; 
+       s->D += DD;
+       
+       s->A &= 0xFFFFFFFF; 
+       s->B &= 0xFFFFFFFF;
+       s->C &= 0xFFFFFFFF; 
+       s->D &= 0xFFFFFFFF;
+
+       for (j=0;j<16;j++)
+               X[j] = 0;
+}
+
+static void copy64(uint32 *M, const unsigned char *in)
+{
+       int i;
+
+       for (i=0;i<16;i++)
+               M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+                       (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out, uint32 x)
+{
+       out[0] = x&0xFF;
+       out[1] = (x>>8)&0xFF;
+       out[2] = (x>>16)&0xFF;
+       out[3] = (x>>24)&0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void mdfour(unsigned char *out, const unsigned char *in, int n)
+{
+       unsigned char buf[128];
+       uint32 M[16];
+       uint32 b = n * 8;
+       int i;
+       struct mdfour_state state;
+
+       state.A = 0x67452301;
+       state.B = 0xefcdab89;
+       state.C = 0x98badcfe;
+       state.D = 0x10325476;
+
+       while (n > 64) {
+               copy64(M, in);
+               mdfour64(&state, M);
+               in += 64;
+               n -= 64;
+       }
+
+       for (i=0;i<128;i++)
+               buf[i] = 0;
+       memcpy(buf, in, n);
+       buf[n] = 0x80;
+       
+       if (n <= 55) {
+               copy4(buf+56, b);
+               copy64(M, buf);
+               mdfour64(&state, M);
+       } else {
+               copy4(buf+120, b); 
+               copy64(M, buf);
+               mdfour64(&state, M);
+               copy64(M, buf+64);
+               mdfour64(&state, M);
+       }
+
+       for (i=0;i<128;i++)
+               buf[i] = 0;
+       copy64(M, buf);
+
+       copy4(out, state.A);
+       copy4(out+4, state.B);
+       copy4(out+8, state.C);
+       copy4(out+12, state.D);
+}
+
+
diff --git a/source4/lib/crypto/md5.c b/source4/lib/crypto/md5.c
new file mode 100644 (file)
index 0000000..2121b17
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by 
+   abartlet@samba.org Jun 2001 */
+
+#include "includes.h"
+
+#include "md5.h"
+
+static void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32 t;
+    do {
+       t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    register uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memmove(p, buf, len);
+           return;
+       }
+       memmove(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memmove(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned int count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32 *) ctx->in)[14] = ctx->bits[0];
+    ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memmove(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+    register uint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/source4/lib/data_blob.c b/source4/lib/data_blob.c
new file mode 100644 (file)
index 0000000..8e7df52
--- /dev/null
@@ -0,0 +1,141 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Easy management of byte-length data
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Andrew Bartlett 2001
+   
+   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"
+
+/*******************************************************************
+ free() a data blob
+*******************************************************************/
+static void free_data_blob(DATA_BLOB *d)
+{
+       if ((d) && (d->free)) {
+               SAFE_FREE(d->data);
+       }
+}
+
+/*******************************************************************
+ construct a data blob, must be freed with data_blob_free()
+ you can pass NULL for p and get a blank data blob
+*******************************************************************/
+DATA_BLOB data_blob(const void *p, size_t length)
+{
+       DATA_BLOB ret;
+
+       if (!length) {
+               ZERO_STRUCT(ret);
+               return ret;
+       }
+
+       if (p) {
+               ret.data = smb_xmemdup(p, length);
+       } else {
+               ret.data = smb_xmalloc(length);
+       }
+       ret.length = length;
+       ret.free = free_data_blob;
+       return ret;
+}
+
+/*******************************************************************
+ construct a data blob, using supplied TALLOC_CTX
+*******************************************************************/
+DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length)
+{
+       DATA_BLOB ret;
+
+       if (length == 0) {
+               ZERO_STRUCT(ret);
+               return ret;
+       }
+
+       if (p == NULL) {
+               ret.data = talloc(mem_ctx, length);
+               if (ret.data == NULL) {
+                       smb_panic("data_blob_talloc: talloc_memdup failed.\n");
+               }
+               ret.length = length;
+               memset(ret.data, 0, ret.length);
+               return ret;
+       }
+
+       ret.data = talloc_memdup(mem_ctx, p, length);
+       if (ret.data == NULL) {
+               smb_panic("data_blob_talloc: talloc_memdup failed.\n");
+       }
+
+       ret.length = length;
+       ret.free = NULL;
+       return ret;
+}
+
+/*******************************************************************
+free a data blob
+*******************************************************************/
+void data_blob_free(DATA_BLOB *d)
+{
+       if (d) {
+               if (d->free) {
+                       (d->free)(d);
+               }
+               d->length = 0;
+       }
+}
+
+/*******************************************************************
+clear a DATA_BLOB's contents
+*******************************************************************/
+void data_blob_clear(DATA_BLOB *d)
+{
+       if (d->data) {
+               memset(d->data, 0, d->length);
+       }
+}
+
+/*******************************************************************
+free a data blob and clear its contents
+*******************************************************************/
+void data_blob_clear_free(DATA_BLOB *d)
+{
+       data_blob_clear(d);
+       data_blob_free(d);
+}
+
+
+/*******************************************************************
+check if two data blobs are equal
+*******************************************************************/
+BOOL data_blob_equal(DATA_BLOB *d1, DATA_BLOB *d2)
+{
+       if (d1->length != d2->length) {
+               return False;
+       }
+       if (d1->data == d2->data) {
+               return True;
+       }
+       if (d1->data == NULL || d2->data == NULL) {
+               return False;
+       }
+       if (memcmp(d1->data, d2->data, d1->length) == 0) {
+               return True;
+       }
+       return False;
+}
+
diff --git a/source4/lib/debug.c b/source4/lib/debug.c
new file mode 100644 (file)
index 0000000..37f93b9
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba debug functions
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers  2003
+
+   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"
+
+/* this global variable determines what messages are printed */
+int DEBUGLEVEL;
+
+
+/* the registered mutex handlers */
+static struct {
+       const char *name;
+       struct debug_ops ops;
+} debug_handlers;
+
+/* state variables for the debug system */
+static struct {
+       int fd;
+       enum debug_logtype logtype;
+       const char *prog_name;
+} state;
+
+/*
+  the backend for debug messages. Note that the DEBUG() macro has already
+  ensured that the log level has been met before this is called
+*/
+void do_debug(const char *format, ...)
+{
+       va_list ap;
+       char *s = NULL;
+
+       if (state.fd == 0) {
+               reopen_logs();
+       }
+
+       if (state.fd <= 0) return;
+
+       va_start(ap, format);
+       vasprintf(&s, format, ap);
+       va_end(ap);
+
+       write(state.fd, s, strlen(s));
+       free(s);
+}
+
+/*
+  reopen the log file (usually called because the log file name might have changed)
+*/
+void reopen_logs(void)
+{
+       char *logfile = lp_logfile();
+       char *fname = NULL;
+       int old_fd = state.fd;
+
+       state.fd = 0;
+
+       switch (state.logtype) {
+       case DEBUG_STDOUT:
+               state.fd = 1;
+               break;
+
+       case DEBUG_STDERR:
+               state.fd = 2;
+               break;
+
+       case DEBUG_FILE:
+               if ((*logfile) == '/') {
+                       fname = strdup(logfile);
+               } else {
+                       asprintf(&fname, "%s/%s.log", dyn_LOGFILEBASE, logfile);
+               }
+               if (fname) {
+                       state.fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, 0644);
+                       free(fname);
+               }
+               break;
+       }
+
+       if (old_fd > 2) {
+               close(old_fd);
+       }
+}
+
+/*
+  control the name of the logfile and whether logging will be to stdout, stderr
+  or a file
+*/
+void setup_logging(const char *prog_name, enum debug_logtype new_logtype)
+{
+       state.logtype = new_logtype;
+       state.prog_name = prog_name;
+       reopen_logs();
+}
+
+/*
+  return a string constant containing n tabs
+  no more than 10 tabs are returned
+*/
+const char *do_debug_tab(uint_t n)
+{
+       const char *tabs[] = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", 
+                             "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", 
+                             "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"};
+       return tabs[MIN(n, 10)];
+}
+
+
+/*
+  log/print suspicious usage - print comments and backtrace
+*/     
+void log_suspicious_usage(const char *from, const char *info)
+{
+       if (debug_handlers.ops.log_suspicious_usage) {
+               return debug_handlers.ops.log_suspicious_usage(from, info);
+       }
+}
+void print_suspicious_usage(const char* from, const char* info)
+{
+       if (debug_handlers.ops.print_suspicious_usage) {
+               return debug_handlers.ops.print_suspicious_usage(from, info);
+       }
+}
+
+uint32 get_task_id(void)
+{
+       if (debug_handlers.ops.get_task_id) {
+               return debug_handlers.ops.get_task_id();
+       }
+       return getpid();
+}
+
+/*
+  register a set of debug handlers. 
+*/
+void register_debug_handlers(const char *name, struct debug_ops *ops)
+{
+       debug_handlers.name = name;
+       debug_handlers.ops = *ops;
+}
diff --git a/source4/lib/dmallocmsg.c b/source4/lib/dmallocmsg.c
new file mode 100644 (file)
index 0000000..a83ed51
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+   samba -- Unix SMB/CIFS implementation.
+   Copyright (C) 2002 by Martin Pool
+   
+   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"
+
+/**
+ * @file dmallocmsg.c
+ *
+ * Glue code to cause dmalloc info to come out when we receive a prod
+ * over samba messaging.
+ **/
+
+#ifdef ENABLE_DMALLOC
+static unsigned long our_dm_mark = 0;
+#endif /* ENABLE_DMALLOC */
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+static void msg_req_dmalloc_mark(int UNUSED(msg_type), pid_t UNUSED(src_pid),
+                         void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+       our_dm_mark = dmalloc_mark();
+       DEBUG(2,("Got MSG_REQ_DMALLOC_MARK: mark set\n"));
+#else
+       DEBUG(2,("Got MSG_REQ_DMALLOC_MARK but dmalloc not in this process\n"));
+#endif
+}
+
+
+
+static void msg_req_dmalloc_log_changed(int UNUSED(msg_type),
+                                       pid_t UNUSED(src_pid),
+                                       void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+       dmalloc_log_changed(our_dm_mark, True, True, True);
+       DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED: done\n"));
+#else
+       DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED but dmalloc not in this process\n"));
+#endif
+}
+
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_dmalloc_msgs(void)
+{
+       message_register(MSG_REQ_DMALLOC_MARK, msg_req_dmalloc_mark);
+       message_register(MSG_REQ_DMALLOC_LOG_CHANGED, msg_req_dmalloc_log_changed);
+       DEBUG(2, ("Registered MSG_REQ_DMALLOC_MARK and LOG_CHANGED\n"));
+}      
diff --git a/source4/lib/dprintf.c b/source4/lib/dprintf.c
new file mode 100644 (file)
index 0000000..70387bb
--- /dev/null
@@ -0,0 +1,113 @@
+/* 
+   Unix SMB/CIFS implementation.
+   display print functions
+   Copyright (C) Andrew Tridgell 2001
+   
+   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.
+*/
+
+
+/*
+  this module provides functions for printing internal strings in the "display charset"
+  This charset may be quite different from the chosen unix charset
+
+  Eventually these functions will need to take care of column count constraints
+
+  The d_ prefix on print functions in Samba refers to the display character set
+  conversion
+*/
+
+#include "includes.h"
+
+ int d_vfprintf(FILE *f, const char *format, va_list ap)
+{
+       char *p, *p2;
+       int ret, maxlen, clen;
+       const char *msgstr;
+       va_list ap2;
+
+       /* do any message translations */
+       msgstr = lang_msg(format);
+       if (!msgstr) return -1;
+
+       VA_COPY(ap2, ap);
+
+       ret = vasprintf(&p, msgstr, ap2);
+
+       lang_msg_free(msgstr);
+
+       if (ret <= 0) return ret;
+
+       /* now we have the string in unix format, convert it to the display
+          charset, but beware of it growing */
+       maxlen = ret*2;
+again:
+       p2 = malloc(maxlen);
+       if (!p2) {
+               SAFE_FREE(p);
+               return -1;
+       }
+       clen = convert_string(CH_UNIX, CH_DISPLAY, p, ret, p2, maxlen);
+
+       if (clen >= maxlen) {
+               /* it didn't fit - try a larger buffer */
+               maxlen *= 2;
+               SAFE_FREE(p2);
+               goto again;
+       }
+
+       /* good, its converted OK */
+       SAFE_FREE(p);
+       ret = fwrite(p2, 1, clen, f);
+       SAFE_FREE(p2);
+
+       return ret;
+}
+
+
+ int d_fprintf(FILE *f, const char *format, ...)
+{
+       int ret;
+       va_list ap;
+
+       va_start(ap, format);
+       ret = d_vfprintf(f, format, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+static FILE *outfile;
+
+ int d_printf(const char *format, ...)
+{
+       int ret;
+       va_list ap;
+
+       if (!outfile) outfile = stdout;
+       
+       va_start(ap, format);
+       ret = d_vfprintf(outfile, format, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+/* interactive programs need a way of tell d_*() to write to stderr instead
+   of stdout */
+void display_set_stderr(void)
+{
+       outfile = stderr;
+}
diff --git a/source4/lib/events.c b/source4/lib/events.c
new file mode 100644 (file)
index 0000000..f95028b
--- /dev/null
@@ -0,0 +1,372 @@
+/* 
+   Unix SMB/CIFS implementation.
+   main select loop and event handling
+   Copyright (C) Andrew Tridgell 2003
+   
+   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.
+*/
+
+/*
+  PLEASE READ THIS BEFORE MODIFYING!
+
+  This module is a general abstraction for the main select loop and
+  event handling. Do not ever put any localised hacks in here, instead
+  register one of the possible event types and implement that event
+  somewhere else.
+
+  There are 4 types of event handling that are handled in this module:
+
+  1) a file descriptor becoming readable or writeable. This is mostly
+     used for network sockets, but can be used for any type of file
+     descriptor. You may only register one handler for each file
+     descriptor/io combination or you will get unpredictable results
+     (this means that you can have a handler for read events, and a
+     separate handler for write events, but not two handlers that are
+     both handling read events)
+
+  2) a timed event. You can register an event that happens at a
+     specific time.  You can register as many of these as you
+     like. When they are called the handler can choose to set the time
+     for the next event. If next_event is not set then the event is removed.
+
+  3) an event that happens every time through the select loop. These
+     sorts of events should be very fast, as they will occur a
+     lot. Mostly used for things like destroying a talloc context or
+     checking a signal flag.
+
+  4) an event triggered by a signal. These can be one shot or
+     repeated. You can have more than one handler registered for a
+     single signal if you want to.
+
+  To setup a set of events you first need to create a event_context
+  structure using the function event_context_init(); This returns a
+  'struct event_context' that you use in all subsequent calls.
+
+  After that you can add/remove events that you are interested in
+  using event_add_*() and event_remove_*().
+
+  Finally, you call event_loop_wait() to block waiting for one of the
+  events to occor. In normal operation event_loop_wait() will loop
+  forever, unless you call event_loop_exit() from inside one of your
+  handler functions.
+
+*/
+
+#include "includes.h"
+
+/*
+  create a event_context structure. This must be the first events
+  call, and all subsequent calls pass this event_context as the first
+  element. Event handlers also receive this as their first argument.
+*/
+struct event_context *event_context_init(void)
+{
+       struct event_context *ev;
+
+       ev = malloc(sizeof(*ev));
+       if (!ev) return NULL;
+
+       /* start off with no events */
+       ZERO_STRUCTP(ev);
+
+       return ev;
+}
+
+
+
+/*
+  add a fd based event
+  return False on failure (memory allocation error)
+*/
+BOOL event_add_fd(struct event_context *ev, struct fd_event *e) 
+{
+       e = memdup(e, sizeof(*e));
+       if (!e) return False;
+       DLIST_ADD(ev->fd_events, e);
+       e->ref_count = 1;
+       if (e->fd > ev->maxfd) {
+               ev->maxfd = e->fd;
+       }
+       return True;
+}
+
+
+/*
+  recalculate the maxfd
+*/
+static void calc_maxfd(struct event_context *ev)
+{
+       struct fd_event *e;
+       ev->maxfd = 0;
+       for (e=ev->fd_events; e; e=e->next) {
+               if (e->ref_count && 
+                   e->fd > ev->maxfd) {
+                       ev->maxfd = e->fd;
+               }
+       }
+}
+
+/*
+  remove a fd based event
+  the event to remove is matched by looking at the handler
+  function and the file descriptor
+  return False on failure (event not found)
+*/
+BOOL event_remove_fd(struct event_context *ev, struct fd_event *e1)
+{
+       struct fd_event *e;
+       for (e=ev->fd_events; e; e=e->next) {
+               if (e->ref_count &&
+                   e->fd == e1->fd && 
+                   e->handler == e1->handler) {
+                       e->ref_count--;
+                       /* possibly calculate the new maxfd */
+                       calc_maxfd(ev);
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*
+  remove all fd based events that match a specified fd
+*/
+void event_remove_fd_all(struct event_context *ev, int fd)
+{
+       struct fd_event *e;
+       for (e=ev->fd_events; e; e=e->next) {
+               if (e->ref_count && e->fd == fd) {
+                       e->ref_count--;
+               }
+       }
+       calc_maxfd(ev);
+}
+
+/*
+  remove all fd based events that match a specified handler
+*/
+void event_remove_fd_all_handler(struct event_context *ev, void *handler)
+{
+       struct fd_event *e;
+       for (e=ev->fd_events; e; e=e->next) {
+               if (e->ref_count &&
+                   handler == (void *)e->handler) {
+                       e->ref_count--;
+               }
+       }
+       calc_maxfd(ev);
+}
+
+
+/*
+  add a timed event
+  return False on failure (memory allocation error)
+*/
+BOOL event_add_timed(struct event_context *ev, struct timed_event *e) 
+{
+       e = memdup(e, sizeof(*e));
+       if (!e) return False;
+       e->ref_count = 1;
+       DLIST_ADD(ev->timed_events, e);
+       return True;
+}
+
+/*
+  remove a timed event
+  the event to remove is matched only on the handler function
+  return False on failure (event not found)
+*/
+BOOL event_remove_timed(struct event_context *ev, struct timed_event *e1) 
+{
+       struct timed_event *e;
+       for (e=ev->timed_events; e; e=e->next) {
+               if (e->ref_count &&
+                   e->handler == e1->handler) {
+                       e->ref_count--;
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*
+  add a loop event
+  return False on failure (memory allocation error)
+*/
+BOOL event_add_loop(struct event_context *ev, struct loop_event *e) 
+{
+       e = memdup(e, sizeof(*e));
+       if (!e) return False;
+       e->ref_count = 1;
+       DLIST_ADD(ev->loop_events, e);
+       return True;
+}
+
+/*
+  remove a loop event
+  the event to remove is matched only on the handler function
+  return False on failure (memory allocation error)
+*/
+BOOL event_remove_loop(struct event_context *ev, struct loop_event *e1) 
+{
+       struct loop_event *e;
+       for (e=ev->loop_events; e; e=e->next) {
+               if (e->ref_count &&
+                   e->handler == e1->handler) {
+                       e->ref_count--;
+                       return True;
+               }
+       }
+       return False;
+}
+
+
+/*
+  tell the event loop to exit with the specified code
+*/
+void event_loop_exit(struct event_context *ev, int code)
+{
+       ev->exit.exit_now = True;
+       ev->exit.code = code;
+}
+
+/*
+  go into an event loop using the events defined in ev this function
+  will return with the specified code if one of the handlers calls
+  event_loop_exit()
+
+  also return (with code 0) if all fd events are removed
+*/
+int event_loop_wait(struct event_context *ev)
+{
+       time_t t;
+
+       ZERO_STRUCT(ev->exit);
+
+       t = time(NULL);
+
+       while (ev->fd_events && !ev->exit.exit_now) {
+               fd_set r_fds, w_fds;
+               struct fd_event *fe;
+               struct loop_event *le;
+               struct timed_event *te;
+               int selrtn;
+               struct timeval tval;
+
+               /* the loop events are called on each loop. Be careful to allow the 
+                  event to remove itself */
+               for (le=ev->loop_events;le;) {
+                       struct loop_event *next = le->next;
+                       if (le->ref_count == 0) {
+                               DLIST_REMOVE(ev->loop_events, le);
+                               free(le);
+                       } else {
+                               le->ref_count++;
+                               le->handler(ev, le, t);
+                               le->ref_count--;
+                       }
+                       le = next;
+               }
+
+               ZERO_STRUCT(tval);
+               FD_ZERO(&r_fds);
+               FD_ZERO(&w_fds);
+
+               /* setup any fd events */
+               for (fe=ev->fd_events; fe; ) {
+                       struct fd_event *next = fe->next;
+                       if (fe->ref_count == 0) {
+                               DLIST_REMOVE(ev->fd_events, fe);
+                               free(fe);
+                       } else {
+                               if (fe->flags & EVENT_FD_READ) {
+                                       FD_SET(fe->fd, &r_fds);
+                               }
+                               if (fe->flags & EVENT_FD_WRITE) {
+                                       FD_SET(fe->fd, &w_fds);
+                               }
+                       }
+                       fe = next;
+               }
+
+               /* start with a reasonable max timeout */
+               tval.tv_sec = 600;
+               
+               /* work out the right timeout for all timed events */
+               for (te=ev->timed_events;te;te=te->next) {
+                       int timeout = te->next_event - t;
+                       if (timeout < 0) {
+                               timeout = 0;
+                       }
+                       if (te->ref_count &&
+                           timeout < tval.tv_sec) {
+                               tval.tv_sec = timeout;
+                       }
+               }
+
+
+               selrtn = sys_select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval);
+
+               t = time(NULL);
+
+               if (selrtn == -1 && errno == EBADF) {
+                       /* the socket is dead! this should never
+                          happen as the socket should have first been
+                          made readable and that should have removed
+                          the event, so this must be a bug. This is a
+                          fatal error. */
+                       DEBUG(0,("EBADF on event_loop_wait - exiting\n"));
+                       return -1;
+               }
+
+               if (selrtn > 0) {
+                       /* at least one file descriptor is ready - check
+                          which ones and call the handler, being careful to allow
+                          the handler to remove itself when called */
+                       for (fe=ev->fd_events; fe; fe=fe->next) {
+                               uint16 flags = 0;
+                               if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ;
+                               if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE;
+                               if (fe->ref_count && flags) {
+                                       fe->ref_count++;
+                                       fe->handler(ev, fe, t, flags);
+                                       fe->ref_count--;
+                               }
+                       }
+               }
+
+               /* call any timed events that are now due */
+               for (te=ev->timed_events;te;) {
+                       struct timed_event *next = te->next;
+                       if (te->ref_count == 0) {
+                               DLIST_REMOVE(ev->timed_events, te);
+                               free(te);
+                       } else if (te->next_event <= t) {
+                               te->ref_count++;
+                               te->handler(ev, te, t);
+                               te->ref_count--;
+                               if (te->next_event <= t) {
+                                       /* the handler didn't set a time for the 
+                                          next event - remove the event */
+                                       event_remove_timed(ev, te);
+                               }
+                       }
+                       te = next;
+               }               
+
+       }
+
+       return ev->exit.code;
+}
diff --git a/source4/lib/fault.c b/source4/lib/fault.c
new file mode 100644 (file)
index 0000000..5a76ce2
--- /dev/null
@@ -0,0 +1,107 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Critical Fault handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+static void (*cont_fn)(void *);
+
+/* the registered fault handler */
+static struct {
+       const char *name;
+       void (*fault_handler)(int sig);
+} fault_handlers;
+
+
+/*******************************************************************
+report a fault
+********************************************************************/
+static void fault_report(int sig)
+{
+       static int counter;
+       
+       if (counter) _exit(1);
+
+       DEBUG(0,("===============================================================\n"));
+       DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),SAMBA_VERSION));
+       DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n"));
+       DEBUG(0,("===============================================================\n"));
+
+       smb_panic("internal error");
+
+       if (cont_fn) {
+               cont_fn(NULL);
+#ifdef SIGSEGV
+               CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGBUS
+               CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL);
+#endif
+               return; /* this should cause a core dump */
+       }
+       exit(1);
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void sig_fault(int sig)
+{
+       if (fault_handlers.fault_handler) {
+               /* we have a fault handler, call it. It may not return. */
+               fault_handlers.fault_handler(sig);
+       }
+       /* If it returns or doean't exist, use regular reporter */
+       fault_report(sig);
+}
+
+/*******************************************************************
+setup our fault handlers
+********************************************************************/
+void fault_setup(void (*fn)(void *))
+{
+       cont_fn = fn;
+
+#ifdef SIGSEGV
+       CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGBUS
+       CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
+#endif
+}
+
+/*
+  register a fault handler. 
+  Should only be called once in the execution of smbd.
+*/
+BOOL register_fault_handler(const char *name, void (*fault_handler)(int sig))
+{
+       if (fault_handlers.name != NULL) {
+               /* it's already registered! */
+               DEBUG(2,("fault handler '%s' already registered - failed '%s'\n", 
+                        fault_handlers.name, name));
+               return False;
+       }
+
+       fault_handlers.name = name;
+       fault_handlers.fault_handler = fault_handler;
+
+       DEBUG(2,("fault handler '%s' registered\n", name));
+       return True;
+}
diff --git a/source4/lib/fsusage.c b/source4/lib/fsusage.c
new file mode 100644 (file)
index 0000000..bb7cff0
--- /dev/null
@@ -0,0 +1,148 @@
+/* 
+   Unix SMB/CIFS implementation.
+   functions to calculate the free disk space
+   Copyright (C) Andrew Tridgell 1998-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"
+
+
+/* Return the number of TOSIZE-byte blocks used by
+   BLOCKS FROMSIZE-byte blocks, rounding away from zero.
+*/
+static SMB_BIG_UINT adjust_blocks(SMB_BIG_UINT blocks, SMB_BIG_UINT fromsize, SMB_BIG_UINT tosize)
+{
+       if (fromsize == tosize) /* e.g., from 512 to 512 */
+               return blocks;
+       else if (fromsize > tosize)     /* e.g., from 2048 to 512 */
+               return blocks * (fromsize / tosize);
+       else                            /* e.g., from 256 to 512 */
+               return (blocks + 1) / (tosize / fromsize);
+}
+
+/* this does all of the system specific guff to get the free disk space.
+   It is derived from code in the GNU fileutils package, but has been
+   considerably mangled for use here 
+
+   results are returned in *dfree and *dsize, in 512 byte units
+*/
+int sys_fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+#ifdef STAT_STATFS3_OSF1
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+       struct statfs fsd;
+
+       if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
+               return -1;
+#endif /* STAT_STATFS3_OSF1 */
+       
+#ifdef STAT_STATFS2_FS_DATA    /* Ultrix */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)1024, (SMB_BIG_UINT)512)     
+       struct fs_data fsd;
+       
+       if (statfs (path, &fsd) != 1)
+               return -1;
+       
+       (*dsize) = CONVERT_BLOCKS (fsd.fd_req.btot);
+       (*dfree) = CONVERT_BLOCKS (fsd.fd_req.bfreen);
+#endif /* STAT_STATFS2_FS_DATA */
+       
+#ifdef STAT_STATFS2_BSIZE      /* 4.3BSD, SunOS 4, HP-UX, AIX */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+       struct statfs fsd;
+       
+       if (statfs (path, &fsd) < 0)
+               return -1;
+       
+#ifdef STATFS_TRUNCATES_BLOCK_COUNTS
+       /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
+          struct statfs are truncated to 2GB.  These conditions detect that
+          truncation, presumably without botching the 4.1.1 case, in which
+          the values are not truncated.  The correct counts are stored in
+          undocumented spare fields.  */
+       if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0) {
+               fsd.f_blocks = fsd.f_spare[0];
+               fsd.f_bfree = fsd.f_spare[1];
+               fsd.f_bavail = fsd.f_spare[2];
+       }
+#endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
+#endif /* STAT_STATFS2_BSIZE */
+       
+
+#ifdef STAT_STATFS2_FSIZE      /* 4.4BSD */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+       
+       struct statfs fsd;
+       
+       if (statfs (path, &fsd) < 0)
+               return -1;
+#endif /* STAT_STATFS2_FSIZE */
+       
+#ifdef STAT_STATFS4            /* SVR3, Dynix, Irix, AIX */
+# if _AIX || defined(_CRAY)
+#  define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+#  ifdef _CRAY
+#   define f_bavail f_bfree
+#  endif
+# else
+#  define CONVERT_BLOCKS(B) ((SMB_BIG_UINT)B)
+#  ifndef _SEQUENT_            /* _SEQUENT_ is DYNIX/ptx */
+#   ifndef DOLPHIN             /* DOLPHIN 3.8.alfa/7.18 has f_bavail */
+#    define f_bavail f_bfree
+#   endif
+#  endif
+# endif
+       
+       struct statfs fsd;
+
+       if (statfs (path, &fsd, sizeof fsd, 0) < 0)
+               return -1;
+       /* Empirically, the block counts on most SVR3 and SVR3-derived
+          systems seem to always be in terms of 512-byte blocks,
+          no matter what value f_bsize has.  */
+
+#endif /* STAT_STATFS4 */
+
+#if defined(STAT_STATVFS) || defined(STAT_STATVFS64)           /* SVR4 */
+# define CONVERT_BLOCKS(B) \
+       adjust_blocks ((SMB_BIG_UINT)(B), fsd.f_frsize ? (SMB_BIG_UINT)fsd.f_frsize : (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+
+#ifdef STAT_STATVFS64
+       struct statvfs64 fsd;
+       if (statvfs64(path, &fsd) < 0) return -1;
+#else
+       struct statvfs fsd;
+       if (statvfs(path, &fsd) < 0) return -1;
+#endif
+
+       /* f_frsize isn't guaranteed to be supported.  */
+
+#endif /* STAT_STATVFS */
+
+#ifndef CONVERT_BLOCKS
+       /* we don't have any dfree code! */
+       return -1;
+#else
+#if !defined(STAT_STATFS2_FS_DATA)
+       /* !Ultrix */
+       (*dsize) = CONVERT_BLOCKS (fsd.f_blocks);
+       (*dfree) = CONVERT_BLOCKS (fsd.f_bavail);
+#endif /* not STAT_STATFS2_FS_DATA */
+#endif
+
+       return 0;
+}
diff --git a/source4/lib/gencache.c b/source4/lib/gencache.c
new file mode 100644 (file)
index 0000000..f3740e3
--- /dev/null
@@ -0,0 +1,372 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Generic, persistent and shared between processes cache mechanism for use
+   by various parts of the Samba code
+
+   Copyright (C) Rafal Szczesniak    2002
+   
+   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"
+
+#undef  DBGC_CLASS
+#define DBGC_CLASS DBGC_TDB
+
+#define TIMEOUT_LEN 12
+#define CACHE_DATA_FMT "%12u/%s"
+
+static TDB_CONTEXT *cache;
+
+/**
+ * @file gencache.c
+ * @brief Generic, persistent and shared between processes cache mechanism
+ *        for use by various parts of the Samba code
+ *
+ **/
+
+
+/**
+ * Cache initialisation function. Opens cache tdb file or creates
+ * it if does not exist.
+ *
+ * @return true on successful initialisation of the cache or
+ *         false on failure
+ **/
+
+BOOL gencache_init(void)
+{
+       char* cache_fname = NULL;
+       
+       /* skip file open if it's already opened */
+       if (cache) return True;
+
+       asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
+       if (cache_fname)
+               DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+       else {
+               DEBUG(0, ("Filename allocation failed.\n"));
+               return False;
+       }
+
+       cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+                            O_RDWR|O_CREAT, 0644);
+
+       SAFE_FREE(cache_fname);
+       if (!cache) {
+               DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
+               return False;
+       }
+       return True;
+}
+
+
+/**
+ * Cache shutdown function. Closes opened cache tdb file.
+ *
+ * @return true on successful closing the cache or
+ *         false on failure during cache shutdown
+ **/
+BOOL gencache_shutdown(void)
+{
+       /* tdb_close routine returns -1 on error */
+       if (!cache) return False;
+       DEBUG(5, ("Closing cache file\n"));
+       return tdb_close(cache) != -1;
+}
+
+
+/**
+ * Set an entry in the cache file. If there's no such
+ * one, then add it.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param value text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly stored
+ * @retval false on failure
+ **/
+BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
+{
+       int ret;
+       TDB_DATA keybuf, databuf;
+       char* valstr = NULL;
+       
+       /* fail completely if get null pointers passed */
+       SMB_ASSERT(keystr && value);
+
+       if (!gencache_init()) return False;
+       
+       asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
+       if (!valstr)
+               return False;
+
+       keybuf.dptr = strdup(keystr);
+       keybuf.dsize = strlen(keystr)+1;
+       databuf.dptr = strdup(valstr);
+       databuf.dsize = strlen(valstr)+1;
+       DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout \
+                  = %s (%d seconds %s)\n", keybuf.dptr, value, ctime(&timeout),
+                  (int)(timeout - time(NULL)), timeout > time(NULL) ? "ahead" : "in the past"));
+               
+       ret = tdb_store(cache, keybuf, databuf, 0);
+       SAFE_FREE(valstr);
+       SAFE_FREE(keybuf.dptr);
+       SAFE_FREE(databuf.dptr);
+       
+       return ret == 0;
+}
+
+
+/**
+ * Set existing entry to the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly set
+ * @retval false on failure
+ **/
+
+BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
+{
+       int ret = -1;
+       TDB_DATA keybuf, databuf;
+       char *old_valstr, *datastr;
+       time_t old_timeout;
+       
+       /* fail completely if get null pointers passed */
+       SMB_ASSERT(keystr && valstr);
+
+       if (!gencache_init()) return False;
+                       
+       /* 
+        * Check whether entry exists in the cache
+        * Don't verify gencache_get exit code, since the entry may be expired
+        */     
+       gencache_get(keystr, &old_valstr, &old_timeout);
+       
+       if (!(old_valstr && old_timeout)) return False;
+               
+       DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
+                  = %s\n", keystr, old_valstr, ctime(&old_timeout)));
+
+       asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
+       keybuf.dptr = strdup(keystr);
+       keybuf.dsize = strlen(keystr)+1;
+       databuf.dptr = strdup(datastr);
+       databuf.dsize = strlen(datastr)+1;
+       DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
+                     ctime(&timeout), (int)(timeout - time(NULL)),
+                     timeout > time(NULL) ? "ahead" : "in the past"));
+
+               
+       ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE);
+
+       SAFE_FREE(datastr);
+       SAFE_FREE(old_valstr);
+       SAFE_FREE(keybuf.dptr);
+       SAFE_FREE(databuf.dptr);
+       
+       return ret == 0;
+}
+
+/**
+ * Delete one entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ *
+ * @retval true upon successful deletion
+ * @retval false in case of failure
+ **/
+
+BOOL gencache_del(const char *keystr)
+{
+       int ret;
+       TDB_DATA keybuf;
+       
+       /* fail completely if get null pointers passed */
+       SMB_ASSERT(keystr);
+
+       if (!gencache_init()) return False;     
+       
+       keybuf.dptr = strdup(keystr);
+       keybuf.dsize = strlen(keystr)+1;
+       DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
+       ret = tdb_delete(cache, keybuf);
+       
+       SAFE_FREE(keybuf.dptr);
+       return ret == 0;
+}
+
+
+/**
+ * Get existing entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr buffer that is allocated and filled with the entry value
+ *        buffer's disposing must be done outside
+ * @param timeout pointer to a time_t that is filled with entry's
+ *        timeout
+ *
+ * @retval true when entry is successfuly fetched
+ * @retval False for failure
+ **/
+
+BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
+{
+       TDB_DATA keybuf, databuf;
+
+       /* fail completely if get null pointers passed */
+       SMB_ASSERT(keystr);
+
+       if (!gencache_init())
+               return False;
+       
+       keybuf.dptr = strdup(keystr);
+       keybuf.dsize = strlen(keystr)+1;
+       databuf = tdb_fetch(cache, keybuf);
+       SAFE_FREE(keybuf.dptr);
+       
+       if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
+               char* entry_buf = strndup(databuf.dptr, databuf.dsize);
+               char *v;
+               time_t t;
+
+               v = (char*)malloc(sizeof(char) * 
+                                 (databuf.dsize - TIMEOUT_LEN));
+                               
+               SAFE_FREE(databuf.dptr);
+               sscanf(entry_buf, CACHE_DATA_FMT, (int*)&t, v);
+               SAFE_FREE(entry_buf);
+
+               DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
+                          "timeout = %s\n", t > time(NULL) ? "valid" :
+                          "expired", keystr, v, ctime(&t)));
+
+               if (valstr)
+                       *valstr = v;
+               else
+                       SAFE_FREE(v);
+
+               if (timeout)
+                       *timeout = t;
+
+               return t > time(NULL);
+
+       } else {
+               SAFE_FREE(databuf.dptr);
+
+               if (valstr)
+                       *valstr = NULL;
+
+               if (timeout)
+                       timeout = NULL;
+
+               DEBUG(10, ("Cache entry with key = %s couldn't be found\n", 
+                          keystr));
+
+               return False;
+       }
+}
+
+
+/**
+ * Iterate through all entries which key matches to specified pattern
+ *
+ * @param fn pointer to the function that will be supplied with each single
+ *        matching cache entry (key, value and timeout) as an arguments
+ * @param data void pointer to an arbitrary data that is passed directly to the fn
+ *        function on each call
+ * @param keystr_pattern pattern the existing entries' keys are matched to
+ *
+ **/
+
+void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
+                      void* data, const char* keystr_pattern)
+{
+       TDB_LIST_NODE *node, *first_node;
+       TDB_DATA databuf;
+       char *keystr = NULL, *valstr = NULL, *entry = NULL;
+       time_t timeout = 0;
+
+       /* fail completely if get null pointers passed */
+       SMB_ASSERT(fn && keystr_pattern);
+
+       if (!gencache_init()) return;
+
+       DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
+       node = tdb_search_keys(cache, keystr_pattern);
+       first_node = node;
+       
+       while (node) {
+               /* ensure null termination of the key string */
+               keystr = strndup(node->node_key.dptr, node->node_key.dsize);
+               
+               /* 
+                * We don't use gencache_get function, because we need to iterate through
+                * all of the entries. Validity verification is up to fn routine.
+                */
+               databuf = tdb_fetch(cache, node->node_key);
+               if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
+                       SAFE_FREE(databuf.dptr);
+                       SAFE_FREE(keystr);
+                       node = node->next;
+                       continue;
+               }
+               entry = strndup(databuf.dptr, databuf.dsize);
+               SAFE_FREE(databuf.dptr);
+               valstr = (char*)malloc(sizeof(char) * (databuf.dsize - TIMEOUT_LEN));
+               sscanf(entry, CACHE_DATA_FMT, (int*)(&timeout), valstr);
+               
+               DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
+                          keystr, valstr, ctime(&timeout)));
+               fn(keystr, valstr, timeout, data);
+               
+               SAFE_FREE(valstr);
+               SAFE_FREE(entry);
+               SAFE_FREE(keystr);
+               node = node->next;
+       }
+       
+       tdb_search_list_free(first_node);
+}
+
+/********************************************************************
+ lock a key
+********************************************************************/
+
+int gencache_lock_entry( const char *key )
+{
+       return tdb_lock_bystring(cache, key, 0);
+}
+
+/********************************************************************
+ unlock a key
+********************************************************************/
+
+void gencache_unlock_entry( const char *key )
+{
+       tdb_unlock_bystring(cache, key);
+       return;
+}
+
+
diff --git a/source4/lib/genparser.c b/source4/lib/genparser.c
new file mode 100644 (file)
index 0000000..39c455d
--- /dev/null
@@ -0,0 +1,786 @@
+/*
+   Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+   
+   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.
+*/
+
+/*
+  automatic marshalling/unmarshalling system for C structures
+*/
+
+#include "includes.h"
+
+/* see if a range of memory is all zero. Used to prevent dumping of zero elements */
+static int all_zero(const char *ptr, unsigned size)
+{
+       int i;
+       if (!ptr) return 1;
+       for (i=0;i<size;i++) {
+               if (ptr[i]) return 0;
+       }
+       return 1;
+}
+
+/* encode a buffer of bytes into a escaped string */
+static char *encode_bytes(TALLOC_CTX *mem_ctx, const char *ptr, unsigned len)
+{
+       const char *hexdig = "0123456789abcdef";
+       char *ret, *p;
+       unsigned i;
+       ret = talloc(mem_ctx, len*3 + 1); /* worst case size */
+       if (!ret) return NULL;
+       for (p=ret,i=0;i<len;i++) {
+               if (isalnum(ptr[i]) || isspace(ptr[i]) ||
+                   (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
+                       *p++ = ptr[i];
+               } else {
+                       unsigned char c = *(const unsigned char *)(ptr+i);
+                       if (c == 0 && all_zero(ptr+i, len-i)) break;
+                       p[0] = '\\';
+                       p[1] = hexdig[c>>4];
+                       p[2] = hexdig[c&0xF];
+                       p += 3;
+               }
+       }
+
+       *p = 0;
+
+       return ret;
+}
+
+/* decode an escaped string from encode_bytes() into a buffer */
+static char *decode_bytes(TALLOC_CTX *mem_ctx, const char *s, unsigned *len) 
+{
+       char *ret, *p;
+       unsigned i;
+       int slen = strlen(s) + 1;
+
+       ret = talloc(mem_ctx, slen); /* worst case length */
+       if (!ret)
+               return NULL;
+       memset(ret, 0, slen);
+
+       if (*s == '{') s++;
+
+       for (p=ret,i=0;s[i];i++) {
+               if (s[i] == '}') {
+                       break;
+               } else if (s[i] == '\\') {
+                       unsigned v;
+                       if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
+                               return NULL;
+                       }
+                       *(unsigned char *)p = v;
+                       p++;
+                       i += 2;
+               } else {
+                       *p++ = s[i];
+               }
+       }
+       *p = 0;
+
+       (*len) = (unsigned)(p - ret);
+       
+       return ret;
+}
+
+/* the add*() functions deal with adding things to a struct
+   parse_string */
+
+/* allocate more space if needed */
+static int addgen_alloc(TALLOC_CTX *mem_ctx, struct parse_string *p, int n)
+{
+       if (p->length + n <= p->allocated) return 0;
+       p->allocated = p->length + n + 200;
+       p->s = talloc_realloc(mem_ctx, p->s, p->allocated);
+       if (!p->s) {
+               errno = ENOMEM;
+               return -1;
+       }
+       return 0;
+}
+
+/* add a character to the buffer */
+static int addchar(TALLOC_CTX *mem_ctx, struct parse_string *p, char c)
+{
+       if (addgen_alloc(mem_ctx, p, 2) != 0) {
+               return -1;
+       }
+       p->s[p->length++] = c;
+       p->s[p->length] = 0;
+       return 0;
+}
+
+/* add a string to the buffer */
+int addstr(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s)
+{
+       int len = strlen(s);
+       if (addgen_alloc(mem_ctx, p, len+1) != 0) {
+               return -1;
+       }
+       memcpy(p->s + p->length, s, len+1);
+       p->length += len;
+       return 0;
+}
+
+/* add a string to the buffer with a tab prefix */
+static int addtabbed(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s, unsigned indent)
+{
+       int len = strlen(s);
+       if (addgen_alloc(mem_ctx, p, indent+len+1) != 0) {
+               return -1;
+       }
+       while (indent--) {
+               p->s[p->length++] = '\t';
+       }
+       memcpy(p->s + p->length, s, len+1);
+       p->length += len;
+       return 0;
+}
+
+/* note! this can only be used for results up to 60 chars wide! */
+int addshort(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+       char buf[60];
+       int n;
+       va_list ap;
+       va_start(ap, fmt);
+       n = vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+               return -1;
+       }
+       if (n != 0) {
+               memcpy(p->s + p->length, buf, n);
+       }
+       p->length += n;
+       p->s[p->length] = 0;
+       return 0;
+}
+
+/* 
+   this is here to make it easier for people to write dump functions 
+   for their own types
+ */
+int gen_addgen(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+       char *buf = NULL;
+       int n;
+       va_list ap;
+       va_start(ap, fmt);
+       n = vasprintf(&buf, fmt, ap);
+       va_end(ap);
+       if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+               if (buf) free(buf);
+               return -1;
+       }
+       if (n != 0) {
+               memcpy(p->s + p->length, buf, n);
+       }
+       p->length += n;
+       p->s[p->length] = 0;
+       if (buf) free(buf);
+       return 0;
+}
+
+/* dump a enumerated type */
+int gen_dump_enum(TALLOC_CTX *mem_ctx,
+                 const struct enum_struct *einfo,
+                 struct parse_string *p, 
+                 const char *ptr,
+                 unsigned indent)
+{
+       unsigned v = *(const unsigned *)ptr;
+       int i;
+       for (i=0;einfo[i].name;i++) {
+               if (v == einfo[i].value) {
+                       addstr(mem_ctx, p, einfo[i].name);
+                       return 0;
+               }
+       }
+       /* hmm, maybe we should just fail? */
+       return gen_dump_unsigned(mem_ctx, p, ptr, indent);
+}
+
+/* dump a single non-array element, hanlding struct and enum */
+static int gen_dump_one(TALLOC_CTX *mem_ctx,
+                       struct parse_string *p, 
+                       const struct parse_struct *pinfo,
+                       const char *ptr,
+                       unsigned indent)
+{
+       if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) {
+               char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+               if (addchar(mem_ctx, p,'{') ||
+                   addstr(mem_ctx, p, s) ||
+                   addstr(mem_ctx, p, "}")) {
+                       return -1;
+               }
+               return 0;
+       }
+
+       return pinfo->dump_fn(mem_ctx, p, ptr, indent);
+}
+
+/* handle dumping of an array of arbitrary type */
+static int gen_dump_array(TALLOC_CTX *mem_ctx,
+                         struct parse_string *p,
+                         const struct parse_struct *pinfo, 
+                         const char *ptr,
+                         int array_len,
+                         int indent)
+{
+       int i, count=0;
+
+       /* special handling of fixed length strings */
+       if (array_len != 0 && 
+           pinfo->ptr_count == 0 &&
+           pinfo->dump_fn == gen_dump_char) {
+               char *s = encode_bytes(mem_ctx, ptr, array_len);
+               if (!s) return -1;
+               if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+                   addstr(mem_ctx, p, " = {") ||
+                   addstr(mem_ctx, p, s) ||
+                   addstr(mem_ctx, p, "}\n")) {
+                       return -1;
+               }
+               free(s);
+               return 0;
+       }
+
+       for (i=0;i<array_len;i++) {
+               const char *p2 = ptr;
+               unsigned size = pinfo->size;
+
+               /* generic pointer dereference */
+               if (pinfo->ptr_count) {
+                       p2 = *(const char **)ptr;
+                       size = sizeof(void *);
+               }
+               
+               if ((count || pinfo->ptr_count) && 
+                   !(pinfo->flags & FLAG_ALWAYS) &&
+                   all_zero(ptr, size)) {
+                       ptr += size;
+                       continue;
+               }
+               if (count == 0) {
+                       if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+                           addshort(mem_ctx, p, " = %u:", i)) {
+                               return -1;
+                       }
+               } else {
+                       if (addshort(mem_ctx, p, ", %u:", i) != 0) {
+                               return -1;
+                       }
+               }
+               if (gen_dump_one(mem_ctx, p, pinfo, p2, indent) != 0) {
+                       return -1;
+               }
+               ptr += size;
+               count++;
+       }
+       if (count) {
+               return addstr(mem_ctx, p, "\n");
+       }
+       return 0;
+}
+
+/* find a variable by name in a loaded structure and return its value
+   as an integer. Used to support dynamic arrays */
+static int find_var(const struct parse_struct *pinfo,
+                   const char *data,
+                   const char *var)
+{
+       int i;
+       const char *ptr;
+
+       /* this allows for constant lengths */
+       if (isdigit(*var)) {
+               return atoi(var);
+       }
+
+       for (i=0;pinfo[i].name;i++) {
+               if (strcmp(pinfo[i].name, var) == 0) break;
+       }
+       if (!pinfo[i].name) return -1;
+
+       ptr = data + pinfo[i].offset;
+
+       switch (pinfo[i].size) {
+       case sizeof(int):
+               return *(const int *)ptr;
+       case sizeof(char):
+               return *(const char *)ptr;
+       }
+
+       return -1;
+}
+
+
+int gen_dump_struct(TALLOC_CTX *mem_ctx,
+                   const struct parse_struct *pinfo,
+                   struct parse_string *p, 
+                   const char *ptr, 
+                   unsigned indent)
+{
+       char *s = gen_dump(mem_ctx, pinfo, ptr, indent+1);
+       if (!s) return -1;
+       if (addstr(mem_ctx, p, "{\n") || 
+           addstr(mem_ctx, p, s) || 
+           addtabbed(mem_ctx, p, "}", indent)) {
+               return -1;
+       }
+       return 0;
+}
+
+static int gen_dump_string(TALLOC_CTX *mem_ctx,
+                          struct parse_string *p,
+                          const struct parse_struct *pinfo, 
+                          const char *data, 
+                          unsigned indent)
+{
+       const char *ptr = *(const char **)data;
+       char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+       if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+           addstr(mem_ctx, p, " = ") ||
+           addchar(mem_ctx, p, '{') ||
+           addstr(mem_ctx, p, s) ||
+           addstr(mem_ctx, p, "}\n")) {
+               return -1;
+       }
+       return 0;
+}
+
+/* 
+   find the length of a nullterm array
+*/
+static int len_nullterm(const char *ptr, int size, int array_len)
+{
+       int len;
+
+       if (size == 1) {
+               len = strnlen(ptr, array_len);
+       } else {
+               for (len=0; len < array_len; len++) {
+                       if (all_zero(ptr+len*size, size)) break;
+               }
+       }
+
+       if (len == 0) len = 1;
+
+       return len;
+}
+
+
+/* the generic dump routine. Scans the parse information for this structure
+   and processes it recursively */
+char *gen_dump(TALLOC_CTX *mem_ctx,
+              const struct parse_struct *pinfo, 
+              const char *data, 
+              unsigned indent)
+{
+       struct parse_string p;
+       int i;
+       
+       p.length = 0;
+       p.allocated = 0;
+       p.s = NULL;
+
+       if (addstr(mem_ctx, &p, "") != 0) {
+               return NULL;
+       }
+       
+       for (i=0;pinfo[i].name;i++) {
+               const char *ptr = data + pinfo[i].offset;
+               unsigned size = pinfo[i].size;
+
+               if (pinfo[i].ptr_count) {
+                       size = sizeof(void *);
+               }
+
+               /* special handling for array types */
+               if (pinfo[i].array_len) {
+                       unsigned len = pinfo[i].array_len;
+                       if (pinfo[i].flags & FLAG_NULLTERM) {
+                               len = len_nullterm(ptr, size, len);
+                       }
+                       if (gen_dump_array(mem_ctx, &p, &pinfo[i], ptr, 
+                                          len, indent)) {
+                               goto failed;
+                       }
+                       continue;
+               }
+
+               /* and dynamically sized arrays */
+               if (pinfo[i].dynamic_len) {
+                       int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+                       struct parse_struct p2 = pinfo[i];
+                       if (len < 0) {
+                               goto failed;
+                       }
+                       if (len > 0) {
+                               if (pinfo[i].flags & FLAG_NULLTERM) {
+                                       len = len_nullterm(*(const char **)ptr, 
+                                                          pinfo[i].size, len);
+                               }
+                               p2.ptr_count--;
+                               p2.dynamic_len = NULL;
+                               if (gen_dump_array(mem_ctx, &p, &p2,
+                                                  *(const char **)ptr, 
+                                                  len, indent) != 0) {
+                                       goto failed;
+                               }
+                       }
+                       continue;
+               }
+
+               /* don't dump zero elements */
+               if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue;
+
+               /* assume char* is a null terminated string */
+               if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 &&
+                   pinfo[i].dump_fn == gen_dump_char) {
+                       if (gen_dump_string(mem_ctx, &p, &pinfo[i], ptr, indent) != 0) {
+                               goto failed;
+                       }
+                       continue;
+               }
+
+               /* generic pointer dereference */
+               if (pinfo[i].ptr_count) {
+                       ptr = *(const char **)ptr;
+               }
+
+               if (addtabbed(mem_ctx, &p, pinfo[i].name, indent) ||
+                   addstr(mem_ctx, &p, " = ") ||
+                   gen_dump_one(mem_ctx, &p, &pinfo[i], ptr, indent) ||
+                   addstr(mem_ctx, &p, "\n")) {
+                       goto failed;
+               }
+       }
+       return p.s;
+
+failed:
+       return NULL;
+}
+
+/* search for a character in a string, skipping over sections within
+   matching braces */
+static char *match_braces(char *s, char c)
+{
+       int depth = 0;
+       while (*s) {
+               switch (*s) {
+               case '}':
+                       depth--;
+                       break;
+               case '{':
+                       depth++;
+                       break;
+               }
+               if (depth == 0 && *s == c) {
+                       return s;
+               }
+               s++;
+       }
+       return s;
+}
+
+/* parse routine for enumerated types */
+int gen_parse_enum(TALLOC_CTX *mem_ctx,
+                  const struct enum_struct *einfo, 
+                  char *ptr, 
+                  const char *str)
+{
+       unsigned v;
+       int i;
+
+       if (isdigit(*str)) {
+               if (sscanf(str, "%u", &v) != 1) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               *(unsigned *)ptr = v;
+               return 0;
+       }
+
+       for (i=0;einfo[i].name;i++) {
+               if (strcmp(einfo[i].name, str) == 0) {
+                       *(unsigned *)ptr = einfo[i].value;
+                       return 0;
+               }
+       }
+
+       /* unknown enum value?? */
+       return -1;
+}
+
+
+/* parse all base types */
+static int gen_parse_base(TALLOC_CTX *mem_ctx,
+                         const struct parse_struct *pinfo, 
+                         char *ptr, 
+                         const char *str)
+{
+       if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) {
+               unsigned len;
+               char *s = decode_bytes(mem_ctx, str, &len);
+               if (!s) return -1;
+               *(char **)ptr = s;
+               return 0;
+       }
+
+       if (pinfo->ptr_count) {
+               unsigned size = pinfo->ptr_count>1?sizeof(void *):pinfo->size;
+               struct parse_struct p2 = *pinfo;
+               *(void **)ptr = talloc(mem_ctx, size);
+               if (! *(void **)ptr) {
+                       return -1;
+               }
+               memset(*(void **)ptr, 0, size);
+               ptr = *(char **)ptr;
+               p2.ptr_count--;
+               return gen_parse_base(mem_ctx, &p2, ptr, str);
+       }
+
+       return pinfo->parse_fn(mem_ctx, ptr, str);
+}
+
+/* parse a generic array */
+static int gen_parse_array(TALLOC_CTX *mem_ctx,
+                          const struct parse_struct *pinfo, 
+                          char *ptr, 
+                          const char *str,
+                          int array_len)
+{
+       char *p, *p2;
+       unsigned size = pinfo->size;
+
+       /* special handling of fixed length strings */
+       if (array_len != 0 && 
+           pinfo->ptr_count == 0 &&
+           pinfo->dump_fn == gen_dump_char) {
+               unsigned len = 0;
+               char *s = decode_bytes(mem_ctx, str, &len);
+               if (!s || (len > array_len)) return -1;
+               memset(ptr, 0, array_len);
+               memcpy(ptr, s, len);
+               return 0;
+       }
+
+       if (pinfo->ptr_count) {
+               size = sizeof(void *);
+       }
+
+       while (*str) {
+               unsigned idx;
+               int done;
+
+               idx = atoi(str);
+               p = strchr(str,':');
+               if (!p) break;
+               p++;
+               p2 = match_braces(p, ',');
+               done = (*p2 != ',');
+               *p2 = 0;
+
+               if (*p == '{') {
+                       p++;
+                       p[strlen(p)-1] = 0;
+               }
+
+               if (gen_parse_base(mem_ctx, pinfo, ptr + idx*size, p) != 0) {
+                       return -1;
+               }
+
+               if (done) break;
+               str = p2+1;
+       }
+
+       return 0;
+}
+
+/* parse one element, hanlding dynamic and static arrays */
+static int gen_parse_one(TALLOC_CTX *mem_ctx,
+                        const struct parse_struct *pinfo, 
+                        const char *name, 
+                        char *data, 
+                        const char *str)
+{
+       int i;
+       for (i=0;pinfo[i].name;i++) {
+               if (strcmp(pinfo[i].name, name) == 0) {
+                       break;
+               }
+       }
+       if (pinfo[i].name == NULL) {
+               return 0;
+       }
+
+       if (pinfo[i].array_len) {
+               return gen_parse_array(mem_ctx, &pinfo[i],
+                                      data+pinfo[i].offset, 
+                                      str, pinfo[i].array_len);
+       }
+
+       if (pinfo[i].dynamic_len) {
+               int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+               if (len < 0) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (len > 0) {
+                       struct parse_struct p2 = pinfo[i];
+                       char *ptr;
+                       unsigned size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size;
+                       ptr = talloc(mem_ctx, len*size);
+                       if (!ptr) {
+                               errno = ENOMEM;
+                               return -1;
+                       }
+                       memset(ptr, 0, len*size);
+                       *((char **)(data + pinfo[i].offset)) = ptr;
+                       p2.ptr_count--;
+                       p2.dynamic_len = NULL;
+                       return gen_parse_array(mem_ctx, &p2, ptr, str, len);
+               }
+               return 0;
+       }
+
+       return gen_parse_base(mem_ctx, &pinfo[i], data + pinfo[i].offset, str);
+}
+
+int gen_parse_struct(TALLOC_CTX * mem_ctx, const struct parse_struct *pinfo, char *ptr, const char *str)
+{
+       return gen_parse(mem_ctx, pinfo, ptr, str);
+}
+
+/* the main parse routine */
+int gen_parse(TALLOC_CTX *mem_ctx, const struct parse_struct *pinfo, char *data, const char *s)
+{
+       char *str, *s0;
+       
+       s0 = strdup(s);
+       str = s0;
+
+       while (*str) {
+               char *p;
+               char *name;
+               char *value;
+
+               /* skip leading whitespace */
+               while (isspace(*str)) str++;
+
+               p = strchr(str, '=');
+               if (!p) break;
+               value = p+1;
+               while (p > str && isspace(*(p-1))) {
+                       p--;
+               }
+
+               *p = 0;
+               name = str;
+
+               while (isspace(*value)) value++;
+
+               if (*value == '{') {
+                       str = match_braces(value, '}');
+                       value++;
+               } else {
+                       str = match_braces(value, '\n');
+               }
+
+               *str++ = 0;
+               
+               if (gen_parse_one(mem_ctx, pinfo, name, data, value) != 0) {
+                       free(s0);
+                       return -1;
+               }
+       }
+
+       free(s0);
+       return 0;
+}
+
+
+
+/* for convenience supply some standard dumpers and parsers here */
+
+int gen_parse_char(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(unsigned char *)ptr = atoi(str);
+       return 0;
+}
+
+int gen_parse_int(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(int *)ptr = atoi(str);
+       return 0;
+}
+
+int gen_parse_unsigned(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(unsigned *)ptr = strtoul(str, NULL, 10);
+       return 0;
+}
+
+int gen_parse_time_t(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(time_t *)ptr = strtoul(str, NULL, 10);
+       return 0;
+}
+
+int gen_parse_double(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(double *)ptr = atof(str);
+       return 0;
+}
+
+int gen_parse_float(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(float *)ptr = atof(str);
+       return 0;
+}
+
+int gen_dump_char(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(unsigned char *)(ptr));
+}
+
+int gen_dump_int(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%d", *(int *)(ptr));
+}
+
+int gen_dump_unsigned(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(unsigned *)(ptr));
+}
+
+int gen_dump_time_t(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(time_t *)(ptr));
+}
+
+int gen_dump_double(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%lg", *(double *)(ptr));
+}
+
+int gen_dump_float(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%g", *(float *)(ptr));
+}
diff --git a/source4/lib/genparser_samba.c b/source4/lib/genparser_samba.c
new file mode 100644 (file)
index 0000000..bece587
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+   Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+   Copyright (C) Simo Sorce <idra@samba.org> 2002
+   
+   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 "genparser_samba.h"
+
+/* PARSE functions */
+
+int gen_parse_uint8(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(uint8 *)ptr = atoi(str);
+       return 0;
+}
+
+int gen_parse_uint16(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(uint16 *)ptr = atoi(str);
+       return 0;
+}
+
+int gen_parse_uint32(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       *(uint32 *)ptr = strtoul(str, NULL, 10);
+       return 0;
+}
+
+int gen_parse_NTTIME(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       if(sscanf(str, "%u,%u", &(((NTTIME *)(ptr))->high), &(((NTTIME *)(ptr))->low)) != 2) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+
+int gen_parse_DOM_SID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       if(!string_to_sid((DOM_SID *)ptr, str)) return -1;
+       return 0;
+}
+
+int gen_parse_SEC_ACCESS(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       ((SEC_ACCESS *)ptr)->mask = strtoul(str, NULL, 10);
+       return 0;
+}
+
+int gen_parse_GUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       int info[GUID_SIZE];
+       int i;
+       char *sc;
+               char *p;
+       char *m;
+
+       m = strdup(str);
+       if (!m) return -1;
+       sc = m;
+       
+       memset(info, 0, sizeof(info));
+       for (i = 0; i < GUID_SIZE; i++) {
+               p = strchr(sc, ',');
+               if (p != NULL) p = '\0';
+               info[i] = atoi(sc);
+               if (p != NULL) sc = p + 1;
+       }
+       free(m);
+               
+       for (i = 0; i < GUID_SIZE; i++) {
+               ((GUID *)ptr)->info[i] = info[i];
+       }
+               
+       return 0;
+}
+
+int gen_parse_SEC_ACE(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       return gen_parse_struct(mem_ctx, pinfo_security_ace_info, ptr, str);
+}
+
+int gen_parse_SEC_ACL(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       return gen_parse_struct(mem_ctx, pinfo_security_acl_info, ptr, str);
+}
+
+int gen_parse_SEC_DESC(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       return gen_parse_struct(mem_ctx, pinfo_security_descriptor_info, ptr, str);
+}
+
+int gen_parse_LUID_ATTR(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       return gen_parse_struct(mem_ctx, pinfo_luid_attr_info, ptr, str);
+}
+
+int gen_parse_LUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+       if(sscanf(str, "%u,%u", &(((LUID *)(ptr))->high), &(((LUID *)(ptr))->low)) != 2) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+
+
+
+/* DUMP functions */
+
+int gen_dump_uint8(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(uint8 *)(ptr));
+}
+
+int gen_dump_uint16(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(uint16 *)(ptr));
+}
+
+int gen_dump_uint32(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", *(uint32 *)(ptr));
+}
+
+int gen_dump_NTTIME(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       uint32 low, high;
+
+       high = ((NTTIME *)(ptr))->high;
+       low = ((NTTIME *)(ptr))->low;
+       return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
+int gen_dump_DOM_SID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       fstring sidstr;
+
+       sid_to_string(sidstr, (DOM_SID *)ptr);
+       return addstr(mem_ctx, p, sidstr);
+}
+
+int gen_dump_SEC_ACCESS(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return addshort(mem_ctx, p, "%u", ((SEC_ACCESS *)ptr)->mask);
+}
+
+int gen_dump_GUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       int i, r;
+
+       for (i = 0; i < (GUID_SIZE - 1); i++) {
+               if (!(r = addshort(mem_ctx, p, "%d,", ((GUID *)ptr)->info[i]))) return r;
+       }
+       return addshort(mem_ctx, p, "%d", ((GUID *)ptr)->info[i]);
+}
+
+int gen_dump_SEC_ACE(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return gen_dump_struct(mem_ctx, pinfo_security_ace_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_ACL(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return gen_dump_struct(mem_ctx, pinfo_security_acl_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_DESC(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return gen_dump_struct(mem_ctx, pinfo_security_descriptor_info, p, ptr, indent);
+}
+
+int gen_dump_LUID_ATTR(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       return gen_dump_struct(mem_ctx, pinfo_luid_attr_info, p, ptr, indent);
+}
+
+int gen_dump_LUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+       uint32 low, high;
+
+       high = ((LUID *)(ptr))->high;
+       low = ((LUID *)(ptr))->low;
+       return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
diff --git a/source4/lib/genrand.c b/source4/lib/genrand.c
new file mode 100644 (file)
index 0000000..e2e66f7
--- /dev/null
@@ -0,0 +1,267 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Functions to create reasonable random numbers for crypto use.
+
+   Copyright (C) Jeremy Allison 2001
+   
+   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"
+
+static unsigned char hash[258];
+static uint32 counter;
+static unsigned char *reseed_data;
+static size_t reseed_data_size;
+
+/**************************************************************** 
+ Copy any user given reseed data.
+*****************************************************************/
+
+void set_rand_reseed_data(unsigned char *data, size_t len)
+{
+       SAFE_FREE(reseed_data);
+       reseed_data_size = 0;
+
+       reseed_data = (unsigned char *)memdup(data, len);
+       if (reseed_data)
+               reseed_data_size = len;
+}
+
+/**************************************************************** 
+ Setup the seed.
+*****************************************************************/
+
+static void seed_random_stream(unsigned char *seedval, size_t seedlen)
+{
+       unsigned char j = 0;
+       size_t ind;
+
+       for (ind = 0; ind < 256; ind++)
+               hash[ind] = (unsigned char)ind;
+
+       for( ind = 0; ind < 256; ind++) {
+               unsigned char tc;
+
+               j += (hash[ind] + seedval[ind%seedlen]);
+
+               tc = hash[ind];
+               hash[ind] = hash[j];
+               hash[j] = tc;
+       }
+
+       hash[256] = 0;
+       hash[257] = 0;
+}
+
+/**************************************************************** 
+ Get datasize bytes worth of random data.
+*****************************************************************/
+
+static void get_random_stream(unsigned char *data, size_t datasize)
+{
+       unsigned char index_i = hash[256];
+       unsigned char index_j = hash[257];
+       size_t ind;
+
+       for( ind = 0; ind < datasize; ind++) {
+               unsigned char tc;
+               unsigned char t;
+
+               index_i++;
+               index_j += hash[index_i];
+
+               tc = hash[index_i];
+               hash[index_i] = hash[index_j];
+               hash[index_j] = tc;
+
+               t = hash[index_i] + hash[index_j];
+               data[ind] = hash[t];
+       }
+
+       hash[256] = index_i;
+       hash[257] = index_j;
+}
+
+/****************************************************************
+ Get a 16 byte hash from the contents of a file.
+ Note that the hash is not initialised.
+*****************************************************************/
+
+static void do_filehash(const char *fname, unsigned char *the_hash)
+{
+       unsigned char buf[1011]; /* deliberate weird size */
+       unsigned char tmp_md4[16];
+       int fd, n;
+
+       fd = sys_open(fname,O_RDONLY,0);
+       if (fd == -1)
+               return;
+
+       while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
+               mdfour(tmp_md4, buf, n);
+               for (n=0;n<16;n++)
+                       the_hash[n] ^= tmp_md4[n];
+       }
+       close(fd);
+}
+
+/**************************************************************
+ Try and get a good random number seed. Try a number of
+ different factors. Firstly, try /dev/urandom - use if exists.
+
+ We use /dev/urandom as a read of /dev/random can block if
+ the entropy pool dries up. This leads clients to timeout
+ or be very slow on connect.
+
+ If we can't use /dev/urandom then seed the stream random generator
+ above...
+**************************************************************/
+
+static int do_reseed(BOOL use_fd, int fd)
+{
+       unsigned char seed_inbuf[40];
+       uint32 v1, v2; struct timeval tval; pid_t mypid;
+       struct passwd *pw;
+
+       if (use_fd) {
+               if (fd != -1)
+                       return fd;
+
+               fd = sys_open( "/dev/urandom", O_RDONLY,0);
+               if(fd >= 0)
+                       return fd;
+       }
+
+       /* Add in some secret file contents */
+
+       do_filehash("/etc/shadow", &seed_inbuf[0]);
+       do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]);
+
+       /*
+        * Add in the root encrypted password.
+        * On any system where security is taken
+        * seriously this will be secret.
+        */
+
+       pw = getpwnam_alloc("root");
+       if (pw && pw->pw_passwd) {
+               size_t i;
+               unsigned char md4_tmp[16];
+               mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
+               for (i=0;i<16;i++)
+                       seed_inbuf[8+i] ^= md4_tmp[i];
+               passwd_free(&pw);
+       }
+
+       /*
+        * Add the counter, time of day, and pid.
+        */
+
+       GetTimeOfDay(&tval);
+       mypid = getpid();
+       v1 = (counter++) + mypid + tval.tv_sec;
+       v2 = (counter++) * mypid + tval.tv_usec;
+
+       SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
+       SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
+
+       /*
+        * Add any user-given reseed data.
+        */
+
+       if (reseed_data) {
+               size_t i;
+               for (i = 0; i < sizeof(seed_inbuf); i++)
+                       seed_inbuf[i] ^= reseed_data[i % reseed_data_size];
+       }
+
+       seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
+
+       return -1;
+}
+
+/*******************************************************************
+ Interface to the (hopefully) good crypto random number generator.
+********************************************************************/
+
+void generate_random_buffer( unsigned char *out, int len, BOOL do_reseed_now)
+{
+       static BOOL done_reseed = False;
+       static int urand_fd = -1;
+       unsigned char md4_buf[64];
+       unsigned char tmp_buf[16];
+       unsigned char *p;
+
+       if(!done_reseed || do_reseed_now) {
+               urand_fd = do_reseed(True, urand_fd);
+               done_reseed = True;
+       }
+
+       if (urand_fd != -1 && len > 0) {
+
+               if (read(urand_fd, out, len) == len)
+                       return; /* len bytes of random data read from urandom. */
+
+               /* Read of urand error, drop back to non urand method. */
+               close(urand_fd);
+               urand_fd = -1;
+               do_reseed(False, -1);
+               done_reseed = True;
+       }
+
+       /*
+        * Generate random numbers in chunks of 64 bytes,
+        * then md4 them & copy to the output buffer.
+        * This way the raw state of the stream is never externally
+        * seen.
+        */
+
+       p = out;
+       while(len > 0) {
+               int copy_len = len > 16 ? 16 : len;
+
+               get_random_stream(md4_buf, sizeof(md4_buf));
+               mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
+               memcpy(p, tmp_buf, copy_len);
+               p += copy_len;
+               len -= copy_len;
+       }
+}
+
+/*******************************************************************
+ Use the random number generator to generate a random string.
+********************************************************************/
+
+static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+
+char *generate_random_str(size_t len)
+{
+       static unsigned char retstr[256];
+       size_t i;
+
+       memset(retstr, '\0', sizeof(retstr));
+
+       if (len > sizeof(retstr)-1)
+               len = sizeof(retstr) -1;
+       generate_random_buffer( retstr, len, False);
+       for (i = 0; i < len; i++)
+               retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ];
+
+       retstr[i] = '\0';
+
+       return (char *)retstr;
+}
diff --git a/source4/lib/getsmbpass.c b/source4/lib/getsmbpass.c
new file mode 100644 (file)
index 0000000..b6ae09b
--- /dev/null
@@ -0,0 +1,156 @@
+/* Copyright (C) 1992-1998 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+/* Modified to use with samba by Jeremy Allison, 8th July 1995. */
+
+#include "includes.h"
+
+#ifdef REPLACE_GETPASS
+
+#ifdef SYSV_TERMIO 
+
+/* SYSTEM V TERMIO HANDLING */
+
+static struct termio t;
+
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+#ifndef TCSAFLUSH
+#define TCSAFLUSH 1
+#endif
+
+#ifndef TCSANOW
+#define TCSANOW 0
+#endif
+
+static int tcgetattr(int fd, struct termio *t)
+{
+       return ioctl(fd, TCGETA, t);
+}
+
+static int tcsetattr(int fd, int flags, struct termio *t)
+{
+       if(flags & TCSAFLUSH)
+               ioctl(fd, TCFLSH, TCIOFLUSH);
+       return ioctl(fd, TCSETS, t);
+}
+
+#elif !defined(TCSAFLUSH)
+
+/* BSD TERMIO HANDLING */
+
+static struct sgttyb t;  
+
+#define ECHO_IS_ON(t) ((t).sg_flags & ECHO)
+#define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO)
+
+#define TCSAFLUSH 1
+#define TCSANOW 0
+
+static int tcgetattr(int fd, struct sgttyb *t)
+{
+       return ioctl(fd, TIOCGETP, (char *)t);
+}
+
+static int tcsetattr(int fd, int flags, struct sgttyb *t)
+{
+       return ioctl(fd, TIOCSETP, (char *)t);
+}
+
+#else /* POSIX TERMIO HANDLING */
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+static struct termios t;
+#endif /* SYSV_TERMIO */
+
+char *getsmbpass(const char *prompt)
+{
+  FILE *in, *out;
+  int echo_off;
+  static char buf[256];
+  static size_t bufsize = sizeof(buf);
+  size_t nread;
+
+  /* Catch problematic signals */
+  CatchSignal(SIGINT, SIGNAL_CAST SIG_IGN);
+
+  /* Try to write to and read from the terminal if we can.
+     If we can't open the terminal, use stderr and stdin.  */
+
+  in = fopen ("/dev/tty", "w+");
+  if (in == NULL)
+    {
+      in = stdin;
+      out = stderr;
+    }
+  else
+    out = in;
+
+  setvbuf(in, NULL, _IONBF, 0);
+
+  /* Turn echoing off if it is on now.  */
+
+  if (tcgetattr (fileno (in), &t) == 0)
+    {
+         if (ECHO_IS_ON(t))
+       {
+               TURN_ECHO_OFF(t);
+               echo_off = tcsetattr (fileno (in), TCSAFLUSH, &t) == 0;
+               TURN_ECHO_ON(t);
+       }
+      else
+       echo_off = 0;
+    }
+  else
+    echo_off = 0;
+
+  /* Write the prompt.  */
+  fputs (prompt, out);
+  fflush (out);
+
+  /* Read the password.  */
+  buf[0] = 0;
+  fgets(buf, bufsize, in);
+  nread = strlen(buf);
+  if (buf[nread - 1] == '\n')
+    buf[nread - 1] = '\0';
+
+  /* Restore echoing.  */
+  if (echo_off)
+    (void) tcsetattr (fileno (in), TCSANOW, &t);
+
+  if (in != stdin)
+    /* We opened the terminal; now close it.  */
+    fclose (in);
+
+  /* Catch problematic signals */
+  CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL);
+
+  printf("\n");
+  return buf;
+}
+
+#else
+ void getsmbpasswd_dummy(void);
+ void getsmbpasswd_dummy(void) {;}
+#endif
diff --git a/source4/lib/hmacmd5.c b/source4/lib/hmacmd5.c
new file mode 100644 (file)
index 0000000..f436fd3
--- /dev/null
@@ -0,0 +1,134 @@
+/* 
+   Unix SMB/CIFS implementation.
+   HMAC MD5 code for use in NTLMv2
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Andrew Tridgell 1992-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.
+*/
+
+/* taken direct from rfc2104 implementation and modified for suitable use
+ * for ntlmv2.
+ */
+
+#include "includes.h"
+
+/***********************************************************************
+ the rfc 2104 version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_rfc2104(uchar*  key, int key_len, HMACMD5Context *ctx)
+{
+        int i;
+
+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
+        if (key_len > 64)
+       {
+               uchar tk[16];
+                struct MD5Context tctx;
+
+                MD5Init(&tctx);
+                MD5Update(&tctx, key, key_len);
+                MD5Final(tk, &tctx);
+
+                key = tk;
+                key_len = 16;
+        }
+
+        /* start out by storing key in pads */
+        ZERO_STRUCT(ctx->k_ipad);
+        ZERO_STRUCT(ctx->k_opad);
+        memcpy( ctx->k_ipad, key, key_len);
+        memcpy( ctx->k_opad, key, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++)
+       {
+                ctx->k_ipad[i] ^= 0x36;
+                ctx->k_opad[i] ^= 0x5c;
+        }
+
+        MD5Init(&ctx->ctx);
+        MD5Update(&ctx->ctx, ctx->k_ipad, 64);  
+}
+
+/***********************************************************************
+ the microsoft version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_limK_to_64(const uchar* key, int key_len,
+                       HMACMD5Context *ctx)
+{
+        int i;
+
+        /* if key is longer than 64 bytes truncate it */
+        if (key_len > 64)
+       {
+                key_len = 64;
+        }
+
+        /* start out by storing key in pads */
+        ZERO_STRUCT(ctx->k_ipad);
+        ZERO_STRUCT(ctx->k_opad);
+        memcpy( ctx->k_ipad, key, key_len);
+        memcpy( ctx->k_opad, key, key_len);
+
+        /* XOR key with ipad and opad values */
+        for (i=0; i<64; i++) {
+                ctx->k_ipad[i] ^= 0x36;
+                ctx->k_opad[i] ^= 0x5c;
+        }
+
+        MD5Init(&ctx->ctx);
+        MD5Update(&ctx->ctx, ctx->k_ipad, 64);  
+}
+
+/***********************************************************************
+ update hmac_md5 "inner" buffer
+***********************************************************************/
+void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx)
+{
+        MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+}
+
+/***********************************************************************
+ finish off hmac_md5 "inner" buffer and generate outer one.
+***********************************************************************/
+void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
+
+{
+        struct MD5Context ctx_o;
+
+        MD5Final(digest, &ctx->ctx);          
+
+        MD5Init(&ctx_o);
+        MD5Update(&ctx_o, ctx->k_opad, 64);   
+        MD5Update(&ctx_o, digest, 16); 
+        MD5Final(digest, &ctx_o);
+}
+
+/***********************************************************
+ single function to calculate an HMAC MD5 digest from data.
+ use the microsoft hmacmd5 init method because the key is 16 bytes.
+************************************************************/
+void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+{
+       HMACMD5Context ctx;
+       hmac_md5_init_limK_to_64(key, 16, &ctx);
+       if (data_len != 0)
+       {
+               hmac_md5_update(data, data_len, &ctx);
+       }
+       hmac_md5_final(digest, &ctx);
+}
+
diff --git a/source4/lib/iconv.c b/source4/lib/iconv.c
new file mode 100644 (file)
index 0000000..8f85e29
--- /dev/null
@@ -0,0 +1,526 @@
+/* 
+   Unix SMB/CIFS implementation.
+   minimal iconv implementation
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Jelmer Vernooij 2002
+   
+   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"
+
+
+/**
+ * @file
+ *
+ * @brief Samba wrapper/stub for iconv character set conversion.
+ *
+ * iconv is the XPG2 interface for converting between character
+ * encodings.  This file provides a Samba wrapper around it, and also
+ * a simple reimplementation that is used if the system does not
+ * implement iconv.
+ *
+ * Samba only works with encodings that are supersets of ASCII: ascii
+ * characters like whitespace can be tested for directly, multibyte
+ * sequences start with a byte with the high bit set, and strings are
+ * terminated by a nul byte.
+ *
+ * Note that the only function provided by iconv is conversion between
+ * characters.  It doesn't directly support operations like
+ * uppercasing or comparison.  We have to convert to UCS-2 and compare
+ * there.
+ *
+ * @sa Samba Developers Guide
+ **/
+
+static size_t ascii_pull(void *,const char **, size_t *, char **, size_t *);
+static size_t ascii_push(void *,const char **, size_t *, char **, size_t *);
+static size_t  utf8_pull(void *,const char **, size_t *, char **, size_t *);
+static size_t  utf8_push(void *,const char **, size_t *, char **, size_t *);
+static size_t ucs2hex_pull(void *,const char **, size_t *, char **, size_t *);
+static size_t ucs2hex_push(void *,const char **, size_t *, char **, size_t *);
+static size_t iconv_copy(void *,const char **, size_t *, char **, size_t *);
+
+static struct charset_functions builtin_functions[] = {
+       {"UCS-2LE",  iconv_copy, iconv_copy},
+       {"UTF8",   utf8_pull,  utf8_push},
+       {"ASCII", ascii_pull, ascii_push},
+       {"UCS2-HEX", ucs2hex_pull, ucs2hex_push},
+       {NULL, NULL, NULL}
+};
+
+static struct charset_functions *charsets = NULL;
+
+BOOL smb_register_charset(struct charset_functions *funcs) 
+{
+       struct charset_functions *c = charsets;
+
+       DEBUG(5, ("Attempting to register new charset %s\n", funcs->name));
+       /* Check whether we already have this charset... */
+       while(c) {
+               if(!strcasecmp(c->name, funcs->name)){ 
+                       DEBUG(2, ("Duplicate charset %s, not registering\n", funcs->name));
+                       return False;
+               }
+               c = c->next;
+       }
+
+       funcs->next = funcs->prev = NULL;
+       DEBUG(5, ("Registered charset %s\n", funcs->name));
+       DLIST_ADD(charsets, funcs);
+       return True;
+}
+
+static void lazy_initialize_iconv(void)
+{
+       static BOOL initialized = False;
+       int i;
+
+       if (!initialized) {
+               initialized = True;
+               for(i = 0; builtin_functions[i].name; i++) 
+                       smb_register_charset(&builtin_functions[i]);
+       }
+}
+
+#ifdef HAVE_NATIVE_ICONV
+/* if there was an error then reset the internal state,
+   this ensures that we don't have a shift state remaining for
+   character sets like SJIS */
+static size_t sys_iconv(void *cd, 
+                       const char **inbuf, size_t *inbytesleft,
+                       char **outbuf, size_t *outbytesleft)
+{
+       size_t ret = iconv((iconv_t)cd, 
+                          inbuf, inbytesleft, 
+                          outbuf, outbytesleft);
+       if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL);
+       return ret;
+}
+#endif
+
+/**
+ * This is a simple portable iconv() implementaion.
+ *
+ * It only knows about a very small number of character sets - just
+ * enough that Samba works on systems that don't have iconv.
+ **/
+size_t smb_iconv(smb_iconv_t cd, 
+                const char **inbuf, size_t *inbytesleft,
+                char **outbuf, size_t *outbytesleft)
+{
+       char cvtbuf[2048];
+       char *bufp = cvtbuf;
+       size_t bufsize;
+
+       /* in many cases we can go direct */
+       if (cd->direct) {
+               return cd->direct(cd->cd_direct, 
+                                 inbuf, inbytesleft, outbuf, outbytesleft);
+       }
+
+
+       /* otherwise we have to do it chunks at a time */
+       while (*inbytesleft > 0) {
+               bufp = cvtbuf;
+               bufsize = sizeof(cvtbuf);
+               
+               if (cd->pull(cd->cd_pull, 
+                            inbuf, inbytesleft, &bufp, &bufsize) == -1
+                   && errno != E2BIG) return -1;
+
+               bufp = cvtbuf;
+               bufsize = sizeof(cvtbuf) - bufsize;
+
+               if (cd->push(cd->cd_push, 
+                            &bufp, &bufsize, 
+                            outbuf, outbytesleft) == -1) return -1;
+       }
+
+       return 0;
+}
+
+/*
+  simple iconv_open() wrapper
+ */
+smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode)
+{
+       smb_iconv_t ret;
+       struct charset_functions *from, *to;
+       
+       lazy_initialize_iconv();
+       from = charsets;
+       to = charsets;
+
+       ret = (smb_iconv_t)malloc(sizeof(*ret));
+       if (!ret) {
+               errno = ENOMEM;
+               return (smb_iconv_t)-1;
+       }
+       memset(ret, 0, sizeof(*ret));
+
+       ret->from_name = strdup(fromcode);
+       ret->to_name = strdup(tocode);
+
+       /* check for the simplest null conversion */
+       if (strcmp(fromcode, tocode) == 0) {
+               ret->direct = iconv_copy;
+               return ret;
+       }
+
+       while (from) {
+               if (strcasecmp(from->name, fromcode) == 0) break;
+               from = from->next;
+       }
+
+       while (to) {
+               if (strcasecmp(to->name, tocode) == 0) break;
+               to = to->next;
+       }
+
+#ifdef HAVE_NATIVE_ICONV
+       if (!from) {
+               ret->pull = sys_iconv;
+               ret->cd_pull = iconv_open("UCS-2LE", fromcode);
+               if (ret->cd_pull == (iconv_t)-1) goto failed;
+       }
+
+       if (!to) {
+               ret->push = sys_iconv;
+               ret->cd_push = iconv_open(tocode, "UCS-2LE");
+               if (ret->cd_push == (iconv_t)-1) goto failed;
+       }
+#else
+       if (!from || !to) {
+               goto failed;
+       }
+#endif
+
+       /* check for conversion to/from ucs2 */
+       if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) {
+               ret->direct = to->push;
+               return ret;
+       }
+       if (strcasecmp(tocode, "UCS-2LE") == 0 && from) {
+               ret->direct = from->pull;
+               return ret;
+       }
+
+#ifdef HAVE_NATIVE_ICONV
+       if (strcasecmp(fromcode, "UCS-2LE") == 0) {
+               ret->direct = sys_iconv;
+               ret->cd_direct = ret->cd_push;
+               ret->cd_push = NULL;
+               return ret;
+       }
+       if (strcasecmp(tocode, "UCS-2LE") == 0) {
+               ret->direct = sys_iconv;
+               ret->cd_direct = ret->cd_pull;
+               ret->cd_pull = NULL;
+               return ret;
+       }
+#endif
+
+       /* the general case has to go via a buffer */
+       if (!ret->pull) ret->pull = from->pull;
+       if (!ret->push) ret->push = to->push;
+       return ret;
+
+failed:
+       SAFE_FREE(ret);
+       errno = EINVAL;
+       return (smb_iconv_t)-1;
+}
+
+/*
+  simple iconv_close() wrapper
+*/
+int smb_iconv_close (smb_iconv_t cd)
+{
+#ifdef HAVE_NATIVE_ICONV
+       if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct);
+       if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull);
+       if (cd->cd_push) iconv_close((iconv_t)cd->cd_push);
+#endif
+
+       SAFE_FREE(cd->from_name);
+       SAFE_FREE(cd->to_name);
+
+       memset(cd, 0, sizeof(*cd));
+       SAFE_FREE(cd);
+       return 0;
+}
+
+
+/**********************************************************************
+ the following functions implement the builtin character sets in Samba
+ and also the "test" character sets that are designed to test
+ multi-byte character set support for english users
+***********************************************************************/
+static size_t ascii_pull(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+               (*outbuf)[0] = (*inbuf)[0];
+               (*outbuf)[1] = 0;
+               (*inbytesleft)  -= 1;
+               (*outbytesleft) -= 2;
+               (*inbuf)  += 1;
+               (*outbuf) += 2;
+       }
+
+       if (*inbytesleft > 0) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+}
+
+static size_t ascii_push(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       int ir_count=0;
+
+       while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+               (*outbuf)[0] = (*inbuf)[0] & 0x7F;
+               if ((*inbuf)[1]) ir_count++;
+               (*inbytesleft)  -= 2;
+               (*outbytesleft) -= 1;
+               (*inbuf)  += 2;
+               (*outbuf) += 1;
+       }
+
+       if (*inbytesleft == 1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (*inbytesleft > 1) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return ir_count;
+}
+
+
+static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+               unsigned v;
+
+               if ((*inbuf)[0] != '@') {
+                       /* seven bit ascii case */
+                       (*outbuf)[0] = (*inbuf)[0];
+                       (*outbuf)[1] = 0;
+                       (*inbytesleft)  -= 1;
+                       (*outbytesleft) -= 2;
+                       (*inbuf)  += 1;
+                       (*outbuf) += 2;
+                       continue;
+               }
+               /* it's a hex character */
+               if (*inbytesleft < 5) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               
+               if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) {
+                       errno = EILSEQ;
+                       return -1;
+               }
+
+               (*outbuf)[0] = v&0xff;
+               (*outbuf)[1] = v>>8;
+               (*inbytesleft)  -= 5;
+               (*outbytesleft) -= 2;
+               (*inbuf)  += 5;
+               (*outbuf) += 2;
+       }
+
+       if (*inbytesleft > 0) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+}
+
+static size_t ucs2hex_push(void *cd, const char **inbuf, size_t *inbytesleft,
+                          char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+               char buf[6];
+
+               if ((*inbuf)[1] == 0 && 
+                   ((*inbuf)[0] & 0x80) == 0 &&
+                   (*inbuf)[0] != '@') {
+                       (*outbuf)[0] = (*inbuf)[0];
+                       (*inbytesleft)  -= 2;
+                       (*outbytesleft) -= 1;
+                       (*inbuf)  += 2;
+                       (*outbuf) += 1;
+                       continue;
+               }
+               if (*outbytesleft < 5) {
+                       errno = E2BIG;
+                       return -1;
+               }
+               snprintf(buf, 6, "@%04x", SVAL(*inbuf, 0));
+               memcpy(*outbuf, buf, 5);
+               (*inbytesleft)  -= 2;
+               (*outbytesleft) -= 5;
+               (*inbuf)  += 2;
+               (*outbuf) += 5;
+       }
+
+       if (*inbytesleft == 1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (*inbytesleft > 1) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+}
+
+
+static size_t iconv_copy(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       int n;
+
+       n = MIN(*inbytesleft, *outbytesleft);
+
+       memmove(*outbuf, *inbuf, n);
+
+       (*inbytesleft) -= n;
+       (*outbytesleft) -= n;
+       (*inbuf) += n;
+       (*outbuf) += n;
+
+       if (*inbytesleft > 0) {
+               errno = E2BIG;
+               return -1;
+       }
+
+       return 0;
+}
+
+static size_t utf8_pull(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+               const unsigned char *c = (const unsigned char *)*inbuf;
+               unsigned char *uc = (unsigned char *)*outbuf;
+               int len = 1;
+
+               if ((c[0] & 0x80) == 0) {
+                       uc[0] = c[0];
+                       uc[1] = 0;
+               } else if ((c[0] & 0xf0) == 0xe0) {
+                       if (*inbytesleft < 3) {
+                               DEBUG(0,("short utf8 char\n"));
+                               goto badseq;
+                       }
+                       uc[1] = ((c[0]&0xF)<<4) | ((c[1]>>2)&0xF);
+                       uc[0] = (c[1]<<6) | (c[2]&0x3f);
+                       len = 3;
+               } else if ((c[0] & 0xe0) == 0xc0) {
+                       if (*inbytesleft < 2) {
+                               DEBUG(0,("short utf8 char\n"));
+                               goto badseq;
+                       }
+                       uc[1] = (c[0]>>2) & 0x7;
+                       uc[0] = (c[0]<<6) | (c[1]&0x3f);
+                       len = 2;
+               }
+
+               (*inbuf)  += len;
+               (*inbytesleft)  -= len;
+               (*outbytesleft) -= 2;
+               (*outbuf) += 2;
+       }
+
+       if (*inbytesleft > 0) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+
+badseq:
+       errno = EINVAL;
+       return -1;
+}
+
+static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+               unsigned char *c = (unsigned char *)*outbuf;
+               const unsigned char *uc = (const unsigned char *)*inbuf;
+               int len=1;
+
+               if (uc[1] & 0xf8) {
+                       if (*outbytesleft < 3) {
+                               DEBUG(0,("short utf8 write\n"));
+                               goto toobig;
+                       }
+                       c[0] = 0xe0 | (uc[1]>>4);
+                       c[1] = 0x80 | ((uc[1]&0xF)<<2) | (uc[0]>>6);
+                       c[2] = 0x80 | (uc[0]&0x3f);
+                       len = 3;
+               } else if (uc[1] | (uc[0] & 0x80)) {
+                       if (*outbytesleft < 2) {
+                               DEBUG(0,("short utf8 write\n"));
+                               goto toobig;
+                       }
+                       c[0] = 0xc0 | (uc[1]<<2) | (uc[0]>>6);
+                       c[1] = 0x80 | (uc[0]&0x3f);
+                       len = 2;
+               } else {
+                       c[0] = uc[0];
+               }
+
+
+               (*inbytesleft)  -= 2;
+               (*outbytesleft) -= len;
+               (*inbuf)  += 2;
+               (*outbuf) += len;
+       }
+
+       if (*inbytesleft == 1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (*inbytesleft > 1) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+
+toobig:
+       errno = E2BIG;
+       return -1;
+}
+
diff --git a/source4/lib/interface.c b/source4/lib/interface.c
new file mode 100644 (file)
index 0000000..2540c89
--- /dev/null
@@ -0,0 +1,333 @@
+/* 
+   Unix SMB/CIFS implementation.
+   multiple interface handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+static struct iface_struct *probed_ifaces;
+static int total_probed;
+
+static struct in_addr allones_ip;
+struct in_addr loopback_ip;
+
+static struct interface *local_interfaces;
+
+#define ALLONES  ((uint32)0xFFFFFFFF)
+#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
+#define MKNETADDR(_IP, _NM) (_IP & _NM)
+
+/****************************************************************************
+Try and find an interface that matches an ip. If we cannot, return NULL
+  **************************************************************************/
+static struct interface *iface_find(struct in_addr ip, BOOL CheckMask)
+{
+       struct interface *i;
+       if (is_zero_ip(ip)) return local_interfaces;
+
+       for (i=local_interfaces;i;i=i->next)
+               if (CheckMask) {
+                       if (same_net(i->ip,ip,i->nmask)) return i;
+               } else if ((i->ip).s_addr == ip.s_addr) return i;
+
+       return NULL;
+}
+
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(struct in_addr ip, struct in_addr nmask)
+{
+       struct interface *iface;
+       if (iface_find(ip, False)) {
+               DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
+               return;
+       }
+
+       if (ip_equal(nmask, allones_ip)) {
+               DEBUG(3,("not adding non-broadcast interface %s\n",inet_ntoa(ip)));
+               return;
+       }
+
+       iface = (struct interface *)malloc(sizeof(*iface));
+       if (!iface) return;
+       
+       ZERO_STRUCTPN(iface);
+
+       iface->ip = ip;
+       iface->nmask = nmask;
+       iface->bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr);
+
+       DLIST_ADD(local_interfaces, iface);
+
+       DEBUG(2,("added interface ip=%s ",inet_ntoa(iface->ip)));
+       DEBUG(2,("bcast=%s ",inet_ntoa(iface->bcast)));
+       DEBUG(2,("nmask=%s\n",inet_ntoa(iface->nmask)));             
+}
+
+
+
+/****************************************************************************
+interpret a single element from a interfaces= config line 
+
+This handles the following different forms:
+
+1) wildcard interface name
+2) DNS name
+3) IP/masklen
+4) ip/mask
+5) bcast/mask
+****************************************************************************/
+static void interpret_interface(TALLOC_CTX *mem_ctx, const char *token)
+{
+       struct in_addr ip, nmask;
+       char *p;
+       int i, added=0;
+
+    zero_ip(&ip);
+    zero_ip(&nmask);
+       
+       /* first check if it is an interface name */
+       for (i=0;i<total_probed;i++) {
+               if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
+                       add_interface(probed_ifaces[i].ip,
+                                     probed_ifaces[i].netmask);
+                       added = 1;
+               }
+       }
+       if (added) return;
+
+       /* maybe it is a DNS name */
+       p = strchr_m(token,'/');
+       if (!p) {
+               ip = *interpret_addr2(mem_ctx, token);
+               for (i=0;i<total_probed;i++) {
+                       if (ip.s_addr == probed_ifaces[i].ip.s_addr &&
+                           !ip_equal(allones_ip, probed_ifaces[i].netmask)) {
+                               add_interface(probed_ifaces[i].ip,
+                                             probed_ifaces[i].netmask);
+                               return;
+                       }
+               }
+               DEBUG(2,("can't determine netmask for %s\n", token));
+               return;
+       }
+
+       /* parse it into an IP address/netmasklength pair */
+       *p++ = 0;
+
+       ip = *interpret_addr2(mem_ctx, token);
+
+       if (strlen(p) > 2) {
+               nmask = *interpret_addr2(mem_ctx, p);
+       } else {
+               nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES));
+       }
+
+       /* maybe the first component was a broadcast address */
+       if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) ||
+           ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) {
+               for (i=0;i<total_probed;i++) {
+                       if (same_net(ip, probed_ifaces[i].ip, nmask)) {
+                               add_interface(probed_ifaces[i].ip, nmask);
+                               return;
+                       }
+               }
+               DEBUG(2,("Can't determine ip for broadcast address %s\n", token));
+               return;
+       }
+
+       add_interface(ip, nmask);
+}
+
+
+/****************************************************************************
+load the list of network interfaces
+****************************************************************************/
+void load_interfaces(void)
+{
+       const char **ptr;
+       int i;
+       struct iface_struct ifaces[MAX_INTERFACES];
+       TALLOC_CTX *mem_ctx;
+
+       ptr = lp_interfaces();
+       mem_ctx = talloc_init("load_interfaces");
+    if (!mem_ctx) {
+       DEBUG(2,("no memory to load interfaces \n"));
+               return;
+    }
+
+       allones_ip = *interpret_addr2(mem_ctx, "255.255.255.255");
+       loopback_ip = *interpret_addr2(mem_ctx, "127.0.0.1");
+
+       SAFE_FREE(probed_ifaces);
+
+       /* dump the current interfaces if any */
+       while (local_interfaces) {
+               struct interface *iface = local_interfaces;
+               DLIST_REMOVE(local_interfaces, local_interfaces);
+               ZERO_STRUCTPN(iface);
+               SAFE_FREE(iface);
+       }
+
+       /* probe the kernel for interfaces */
+       total_probed = get_interfaces(ifaces, MAX_INTERFACES);
+
+       if (total_probed > 0) {
+               probed_ifaces = memdup(ifaces, sizeof(ifaces[0])*total_probed);
+       }
+
+       /* if we don't have a interfaces line then use all broadcast capable 
+          interfaces except loopback */
+       if (!ptr || !*ptr || !**ptr) {
+               if (total_probed <= 0) {
+                       DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
+                       exit(1);
+               }
+               for (i=0;i<total_probed;i++) {
+                       if (probed_ifaces[i].netmask.s_addr != allones_ip.s_addr &&
+                           probed_ifaces[i].ip.s_addr != loopback_ip.s_addr) {
+                               add_interface(probed_ifaces[i].ip, 
+                                             probed_ifaces[i].netmask);
+                       }
+               }
+               goto exit;
+       }
+
+       if (ptr) {
+               while (*ptr) {
+                       interpret_interface(mem_ctx, *ptr);
+                       ptr++;
+               }
+       }
+
+       if (!local_interfaces) {
+               DEBUG(0,("WARNING: no network interfaces found\n"));
+       }
+       
+exit:
+       talloc_destroy(mem_ctx);
+}
+
+
+/****************************************************************************
+return True if the list of probed interfaces has changed
+****************************************************************************/
+BOOL interfaces_changed(void)
+{
+       int n;
+       struct iface_struct ifaces[MAX_INTERFACES];
+
+       n = get_interfaces(ifaces, MAX_INTERFACES);
+
+       if ((n > 0 )&& (n != total_probed ||
+           memcmp(ifaces, probed_ifaces, sizeof(ifaces[0])*n))) {
+               return True;
+       }
+       
+       return False;
+}
+
+
+/****************************************************************************
+  check if an IP is one of mine
+  **************************************************************************/
+BOOL ismyip(struct in_addr ip)
+{
+       struct interface *i;
+       for (i=local_interfaces;i;i=i->next)
+               if (ip_equal(i->ip,ip)) return True;
+       return False;
+}
+
+/****************************************************************************
+  check if a packet is from a local (known) net
+  **************************************************************************/
+BOOL is_local_net(struct in_addr from)
+{
+       struct interface *i;
+       for (i=local_interfaces;i;i=i->next) {
+               if((from.s_addr & i->nmask.s_addr) == 
+                  (i->ip.s_addr & i->nmask.s_addr))
+                       return True;
+       }
+       return False;
+}
+
+/****************************************************************************
+  how many interfaces do we have
+  **************************************************************************/
+int iface_count(void)
+{
+       int ret = 0;
+       struct interface *i;
+
+       for (i=local_interfaces;i;i=i->next)
+               ret++;
+       return ret;
+}
+
+/****************************************************************************
+  return IP of the Nth interface
+  **************************************************************************/
+struct in_addr *iface_n_ip(int n)
+{
+       struct interface *i;
+  
+       for (i=local_interfaces;i && n;i=i->next)
+               n--;
+
+       if (i) return &i->ip;
+       return NULL;
+}
+
+/****************************************************************************
+  return bcast of the Nth interface
+  **************************************************************************/
+struct in_addr *iface_n_bcast(int n)
+{
+       struct interface *i;
+  
+       for (i=local_interfaces;i && n;i=i->next)
+               n--;
+
+       if (i) return &i->bcast;
+       return NULL;
+}
+
+
+/* these 3 functions return the ip/bcast/nmask for the interface
+   most appropriate for the given ip address. If they can't find
+   an appropriate interface they return the requested field of the
+   first known interface. */
+
+struct in_addr *iface_ip(struct in_addr ip)
+{
+       struct interface *i = iface_find(ip, True);
+       return(i ? &i->ip : &local_interfaces->ip);
+}
+
+/*
+  return True if a IP is directly reachable on one of our interfaces
+*/
+BOOL iface_local(struct in_addr ip)
+{
+       return iface_find(ip, True) ? True : False;
+}
diff --git a/source4/lib/interfaces.c b/source4/lib/interfaces.c
new file mode 100644 (file)
index 0000000..96f4b4c
--- /dev/null
@@ -0,0 +1,407 @@
+/* 
+   Unix SMB/CIFS implementation.
+   return a list of network interfaces
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+
+/* working out the interfaces for a OS is an incredibly non-portable
+   thing. We have several possible implementations below, and autoconf
+   tries each of them to see what works
+
+   Note that this file does _not_ include includes.h. That is so this code
+   can be called directly from the autoconf tests. That also means
+   this code cannot use any of the normal Samba debug stuff or defines.
+   This is standalone code.
+
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#ifdef AUTOCONF_TEST
+struct iface_struct {
+       char name[16];
+       struct in_addr ip;
+       struct in_addr netmask;
+};
+#else
+#include "config.h"
+#include "interfaces.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef SIOCGIFCONF
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#if HAVE_IFACE_IFCONF
+
+/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
+   V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
+
+   It probably also works on any BSD style system.  */
+
+/****************************************************************************
+  get the netmask address for a local interface
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{  
+       struct ifconf ifc;
+       char buff[8192];
+       int fd, i, n;
+       struct ifreq *ifr=NULL;
+       int total = 0;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+  
+       ifc.ifc_len = sizeof(buff);
+       ifc.ifc_buf = buff;
+
+       if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+               close(fd);
+               return -1;
+       } 
+
+       ifr = ifc.ifc_req;
+  
+       n = ifc.ifc_len / sizeof(struct ifreq);
+
+       /* Loop through interfaces, looking for given IP address */
+       for (i=n-1;i>=0 && total < max_interfaces;i--) {
+               if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
+                       continue;
+               }
+
+               iname = ifr[i].ifr_name;
+               ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
+
+               if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
+                       continue;
+               }  
+
+               if (!(ifr[i].ifr_flags & IFF_UP)) {
+                       continue;
+               }
+
+               if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
+                       continue;
+               }  
+
+               nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
+
+               strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+               ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+               ifaces[total].ip = ipaddr;
+               ifaces[total].netmask = nmask;
+               total++;
+       }
+
+       close(fd);
+
+       return total;
+}  
+
+#elif HAVE_IFACE_IFREQ
+
+#ifndef I_STR
+#include <sys/stropts.h>
+#endif
+
+/****************************************************************************
+this should cover most of the streams based systems
+Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+       struct ifreq ifreq;
+       struct strioctl strioctl;
+       char buff[8192];
+       int fd, i, n;
+       struct ifreq *ifr=NULL;
+       int total = 0;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+  
+       strioctl.ic_cmd = SIOCGIFCONF;
+       strioctl.ic_dp  = buff;
+       strioctl.ic_len = sizeof(buff);
+       if (ioctl(fd, I_STR, &strioctl) < 0) {
+               close(fd);
+               return -1;
+       } 
+
+       /* we can ignore the possible sizeof(int) here as the resulting
+          number of interface structures won't change */
+       n = strioctl.ic_len / sizeof(struct ifreq);
+
+       /* we will assume that the kernel returns the length as an int
+           at the start of the buffer if the offered size is a
+           multiple of the structure size plus an int */
+       if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
+               ifr = (struct ifreq *)(buff + sizeof(int));  
+       } else {
+               ifr = (struct ifreq *)buff;  
+       }
+
+       /* Loop through interfaces */
+
+       for (i = 0; i<n && total < max_interfaces; i++) {
+               ifreq = ifr[i];
+  
+               strioctl.ic_cmd = SIOCGIFFLAGS;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       continue;
+               }
+               
+               if (!(ifreq.ifr_flags & IFF_UP)) {
+                       continue;
+               }
+
+               strioctl.ic_cmd = SIOCGIFADDR;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       continue;
+               }
+
+               ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
+               iname = ifreq.ifr_name;
+
+               strioctl.ic_cmd = SIOCGIFNETMASK;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       continue;
+               }
+
+               nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
+
+               strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+               ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+               ifaces[total].ip = ipaddr;
+               ifaces[total].netmask = nmask;
+
+               total++;
+       }
+
+       close(fd);
+
+       return total;
+}
+
+#elif HAVE_IFACE_AIX
+
+/****************************************************************************
+this one is for AIX (tested on 4.2)
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+       char buff[8192];
+       int fd, i;
+       struct ifconf ifc;
+       struct ifreq *ifr=NULL;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+       int total = 0;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+
+
+       ifc.ifc_len = sizeof(buff);
+       ifc.ifc_buf = buff;
+
+       if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+               close(fd);
+               return -1;
+       }
+
+       ifr = ifc.ifc_req;
+
+       /* Loop through interfaces */
+       i = ifc.ifc_len;
+
+       while (i > 0 && total < max_interfaces) {
+               unsigned inc;
+
+               inc = ifr->ifr_addr.sa_len;
+
+               if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
+                       goto next;
+               }
+
+               ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
+               iname = ifr->ifr_name;
+
+               if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
+                       goto next;
+               }
+
+               if (!(ifr->ifr_flags & IFF_UP)) {
+                       goto next;
+               }
+
+               if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
+                       goto next;
+               }
+
+               nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+
+               strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+               ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+               ifaces[total].ip = ipaddr;
+               ifaces[total].netmask = nmask;
+
+               total++;
+
+       next:
+               /*
+                * Patch from Archie Cobbs (archie@whistle.com).  The
+                * addresses in the SIOCGIFCONF interface list have a
+                * minimum size. Usually this doesn't matter, but if
+                * your machine has tunnel interfaces, etc. that have
+                * a zero length "link address", this does matter.  */
+
+               if (inc < sizeof(ifr->ifr_addr))
+                       inc = sizeof(ifr->ifr_addr);
+               inc += IFNAMSIZ;
+
+               ifr = (struct ifreq*) (((char*) ifr) + inc);
+               i -= inc;
+       }
+  
+
+       close(fd);
+       return total;
+}
+
+#else /* a dummy version */
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+       return -1;
+}
+#endif
+
+
+static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
+{
+       int r;
+       r = strcmp(i1->name, i2->name);
+       if (r) return r;
+       r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
+       if (r) return r;
+       r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
+       return r;
+}
+
+/* this wrapper is used to remove duplicates from the interface list generated
+   above */
+int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+       int total, i, j;
+
+       total = _get_interfaces(ifaces, max_interfaces);
+       if (total <= 0) return total;
+
+       /* now we need to remove duplicates */
+       qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
+
+       for (i=1;i<total;) {
+               if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
+                       for (j=i-1;j<total-1;j++) {
+                               ifaces[j] = ifaces[j+1];
+                       }
+                       total--;
+               } else {
+                       i++;
+               }
+       }
+
+       return total;
+}
+
+
+#ifdef AUTOCONF_TEST
+/* this is the autoconf driver to test get_interfaces() */
+
+#define MAX_INTERFACES 128
+
+ int main()
+{
+       struct iface_struct ifaces[MAX_INTERFACES];
+       int total = get_interfaces(ifaces, MAX_INTERFACES);
+       int i;
+
+       printf("got %d interfaces:\n", total);
+       if (total <= 0) exit(1);
+
+       for (i=0;i<total;i++) {
+               printf("%-10s ", ifaces[i].name);
+               printf("IP=%s ", inet_ntoa(ifaces[i].ip));
+               printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
+       }
+       return 0;
+}
+#endif
diff --git a/source4/lib/ldap_escape.c b/source4/lib/ldap_escape.c
new file mode 100644 (file)
index 0000000..9e88b49
--- /dev/null
@@ -0,0 +1,90 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ldap filter argument escaping
+
+   Copyright (C) 1998, 1999, 2000 Luke Howard <lukeh@padl.com>,
+   Copyright (C) 2003 Andrew Bartlett <abartlet@samba.org>
+
+  
+   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"
+
+/**
+ * Escape a parameter to an LDAP filter string, so they cannot contain
+ * embeded ( ) * or \ chars which may cause it not to parse correctly. 
+ *
+ * @param s The input string
+ *
+ * @return A string allocated with malloc(), containing the escaped string, 
+ * and to be free()ed by the caller.
+ **/
+
+char *escape_ldap_string_alloc(const char *s)
+{
+       size_t len = strlen(s)+1;
+       char *output = malloc(len);
+       char *output_tmp;
+       const char *sub;
+       int i = 0;
+       char *p = output;
+       
+       while (*s)
+       {
+               switch (*s)
+               {
+               case '*':
+                       sub = "\\2a";
+                       break;
+               case '(':
+                       sub = "\\28";
+                       break;
+               case ')':
+                       sub = "\\29";
+                       break;
+               case '\\':
+                       sub = "\\5c";
+                       break;
+               default:
+                       sub = NULL;
+                       break;
+               }
+               
+               if (sub) {
+                       len = len + 3;
+                       output_tmp = realloc(output, len);
+                       if (!output_tmp) { 
+                               SAFE_FREE(output);
+                               return NULL;
+                       }
+                       output = output_tmp;
+                       
+                       p = &output[i];
+                       strncpy (p, sub, 3);
+                       p += 3;
+                       i += 3;
+
+               } else {
+                       *p = *s;
+                       p++;
+                       i++;
+               }
+               s++;
+       }
+       
+       *p = '\0';
+       return output;
+}
diff --git a/source4/lib/md4.c b/source4/lib/md4.c
new file mode 100644 (file)
index 0000000..417e87b
--- /dev/null
@@ -0,0 +1,175 @@
+/* 
+   Unix SMB/CIFS implementation.
+   a implementation of MD4 designed for use in the SMB authentication protocol
+   Copyright (C) Andrew Tridgell 1997-1998.
+   
+   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"
+
+/* NOTE: This code makes no attempt to be fast! 
+
+   It assumes that a int is at least 32 bits long
+*/
+
+struct mdfour_state {
+       uint32 A, B, C, D;
+};
+
+static uint32 F(uint32 X, uint32 Y, uint32 Z)
+{
+       return (X&Y) | ((~X)&Z);
+}
+
+static uint32 G(uint32 X, uint32 Y, uint32 Z)
+{
+       return (X&Y) | (X&Z) | (Y&Z); 
+}
+
+static uint32 H(uint32 X, uint32 Y, uint32 Z)
+{
+       return X^Y^Z;
+}
+
+static uint32 lshift(uint32 x, int s)
+{
+       x &= 0xFFFFFFFF;
+       return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(struct mdfour_state *s, uint32 *M)
+{
+       int j;
+       uint32 AA, BB, CC, DD;
+       uint32 X[16];
+
+       for (j=0;j<16;j++)
+               X[j] = M[j];
+
+       AA = s->A; BB = s->B; CC = s->C; DD = s->D;
+
+        ROUND1(s->A,s->B,s->C,s->D,  0,  3);  ROUND1(s->D,s->A,s->B,s->C,  1,  7);  
+       ROUND1(s->C,s->D,s->A,s->B,  2, 11);  ROUND1(s->B,s->C,s->D,s->A,  3, 19);
+        ROUND1(s->A,s->B,s->C,s->D,  4,  3);  ROUND1(s->D,s->A,s->B,s->C,  5,  7);  
+       ROUND1(s->C,s->D,s->A,s->B,  6, 11);  ROUND1(s->B,s->C,s->D,s->A,  7, 19);
+        ROUND1(s->A,s->B,s->C,s->D,  8,  3);  ROUND1(s->D,s->A,s->B,s->C,  9,  7);  
+       ROUND1(s->C,s->D,s->A,s->B, 10, 11);  ROUND1(s->B,s->C,s->D,s->A, 11, 19);
+        ROUND1(s->A,s->B,s->C,s->D, 12,  3);  ROUND1(s->D,s->A,s->B,s->C, 13,  7);  
+       ROUND1(s->C,s->D,s->A,s->B, 14, 11);  ROUND1(s->B,s->C,s->D,s->A, 15, 19);      
+
+        ROUND2(s->A,s->B,s->C,s->D,  0,  3);  ROUND2(s->D,s->A,s->B,s->C,  4,  5);  
+       ROUND2(s->C,s->D,s->A,s->B,  8,  9);  ROUND2(s->B,s->C,s->D,s->A, 12, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  1,  3);  ROUND2(s->D,s->A,s->B,s->C,  5,  5);  
+       ROUND2(s->C,s->D,s->A,s->B,  9,  9);  ROUND2(s->B,s->C,s->D,s->A, 13, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  2,  3);  ROUND2(s->D,s->A,s->B,s->C,  6,  5);  
+       ROUND2(s->C,s->D,s->A,s->B, 10,  9);  ROUND2(s->B,s->C,s->D,s->A, 14, 13);
+        ROUND2(s->A,s->B,s->C,s->D,  3,  3);  ROUND2(s->D,s->A,s->B,s->C,  7,  5);  
+       ROUND2(s->C,s->D,s->A,s->B, 11,  9);  ROUND2(s->B,s->C,s->D,s->A, 15, 13);
+
+       ROUND3(s->A,s->B,s->C,s->D,  0,  3);  ROUND3(s->D,s->A,s->B,s->C,  8,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  4, 11);  ROUND3(s->B,s->C,s->D,s->A, 12, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  2,  3);  ROUND3(s->D,s->A,s->B,s->C, 10,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  6, 11);  ROUND3(s->B,s->C,s->D,s->A, 14, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  1,  3);  ROUND3(s->D,s->A,s->B,s->C,  9,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  5, 11);  ROUND3(s->B,s->C,s->D,s->A, 13, 15);
+        ROUND3(s->A,s->B,s->C,s->D,  3,  3);  ROUND3(s->D,s->A,s->B,s->C, 11,  9);  
+       ROUND3(s->C,s->D,s->A,s->B,  7, 11);  ROUND3(s->B,s->C,s->D,s->A, 15, 15);
+
+       s->A += AA; 
+       s->B += BB; 
+       s->C += CC; 
+       s->D += DD;
+       
+       s->A &= 0xFFFFFFFF; 
+       s->B &= 0xFFFFFFFF;
+       s->C &= 0xFFFFFFFF; 
+       s->D &= 0xFFFFFFFF;
+
+       for (j=0;j<16;j++)
+               X[j] = 0;
+}
+
+static void copy64(uint32 *M, const unsigned char *in)
+{
+       int i;
+
+       for (i=0;i<16;i++)
+               M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+                       (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out, uint32 x)
+{
+       out[0] = x&0xFF;
+       out[1] = (x>>8)&0xFF;
+       out[2] = (x>>16)&0xFF;
+       out[3] = (x>>24)&0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void mdfour(unsigned char *out, const unsigned char *in, int n)
+{
+       unsigned char buf[128];
+       uint32 M[16];
+       uint32 b = n * 8;
+       int i;
+       struct mdfour_state state;
+
+       state.A = 0x67452301;
+       state.B = 0xefcdab89;
+       state.C = 0x98badcfe;
+       state.D = 0x10325476;
+
+       while (n > 64) {
+               copy64(M, in);
+               mdfour64(&state, M);
+               in += 64;
+               n -= 64;
+       }
+
+       for (i=0;i<128;i++)
+               buf[i] = 0;
+       memcpy(buf, in, n);
+       buf[n] = 0x80;
+       
+       if (n <= 55) {
+               copy4(buf+56, b);
+               copy64(M, buf);
+               mdfour64(&state, M);
+       } else {
+               copy4(buf+120, b); 
+               copy64(M, buf);
+               mdfour64(&state, M);
+               copy64(M, buf+64);
+               mdfour64(&state, M);
+       }
+
+       for (i=0;i<128;i++)
+               buf[i] = 0;
+       copy64(M, buf);
+
+       copy4(out, state.A);
+       copy4(out+4, state.B);
+       copy4(out+8, state.C);
+       copy4(out+12, state.D);
+}
+
+
diff --git a/source4/lib/md5.c b/source4/lib/md5.c
new file mode 100644 (file)
index 0000000..2121b17
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by 
+   abartlet@samba.org Jun 2001 */
+
+#include "includes.h"
+
+#include "md5.h"
+
+static void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32 t;
+    do {
+       t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    register uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memmove(p, buf, len);
+           return;
+       }
+       memmove(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memmove(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned int count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32 *) ctx->in)[14] = ctx->bits[0];
+    ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memmove(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+    register uint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/source4/lib/messages.c b/source4/lib/messages.c
new file mode 100644 (file)
index 0000000..cb26b35
--- /dev/null
@@ -0,0 +1,566 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba internal messaging functions
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) 2001 by Martin Pool
+   Copyright (C) 2002 by Jeremy Allison
+   
+   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.
+*/
+
+/**
+  @defgroup messages Internal messaging framework
+  @{
+  @file messages.c
+  
+  @brief  Module for internal messaging between Samba daemons. 
+
+   The idea is that if a part of Samba wants to do communication with
+   another Samba process then it will do a message_register() of a
+   dispatch function, and use message_send_pid() to send messages to
+   that process.
+
+   The dispatch function is given the pid of the sender, and it can
+   use that to reply by message_send_pid().  See ping_message() for a
+   simple example.
+
+   @caution Dispatch functions must be able to cope with incoming
+   messages on an *odd* byte boundary.
+
+   This system doesn't have any inherent size limitations but is not
+   very efficient for large messages or when messages are sent in very
+   quick succession.
+
+*/
+
+#include "includes.h"
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+static int received_signal;
+
+/* change the message version with any incompatible changes in the protocol */
+#define MESSAGE_VERSION 1
+
+struct message_rec {
+       int msg_version;
+       int msg_type;
+       pid_t dest;
+       pid_t src;
+       size_t len;
+};
+
+/* we have a linked list of dispatch handlers */
+static struct dispatch_fns {
+       struct dispatch_fns *next, *prev;
+       int msg_type;
+       void (*fn)(int msg_type, pid_t pid, void *buf, size_t len);
+} *dispatch_fns;
+
+/****************************************************************************
+ Notifications come in as signals.
+****************************************************************************/
+
+static void sig_usr1(void)
+{
+       received_signal = 1;
+       sys_select_signal();
+}
+
+/****************************************************************************
+ A useful function for testing the message system.
+****************************************************************************/
+
+static void ping_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+       const char *msg = buf ? buf : "none";
+       DEBUG(1,("INFO: Received PING message from PID %u [%s]\n",(unsigned int)src, msg));
+       message_send_pid(src, MSG_PONG, buf, len, True);
+}
+
+/****************************************************************************
+ Initialise the messaging functions. 
+****************************************************************************/
+
+BOOL message_init(void)
+{
+       TALLOC_CTX *mem_ctx;
+       
+       if (tdb) return True;
+
+       mem_ctx = talloc_init("message_init");
+       if (!mem_ctx) {
+               DEBUG(0,("ERROR: No memory to initialise messages database\n"));
+               return False;
+       }
+       tdb = tdb_open_log(lock_path(mem_ctx, "messages.tdb"), 
+                      0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, 
+                      O_RDWR|O_CREAT,0600);
+       talloc_destroy(mem_ctx);
+
+       if (!tdb) {
+               DEBUG(0,("ERROR: Failed to initialise messages database\n"));
+               return False;
+       }
+
+       CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
+
+       message_register(MSG_PING, ping_message);
+
+       return True;
+}
+
+/*******************************************************************
+ Form a static tdb key from a pid.
+******************************************************************/
+
+static TDB_DATA message_key_pid(pid_t pid)
+{
+       static char key[20];
+       TDB_DATA kbuf;
+
+       slprintf(key, sizeof(key)-1, "PID/%d", (int)pid);
+       
+       kbuf.dptr = (char *)key;
+       kbuf.dsize = strlen(key)+1;
+       return kbuf;
+}
+
+/****************************************************************************
+ Notify a process that it has a message. If the process doesn't exist 
+ then delete its record in the database.
+****************************************************************************/
+
+static BOOL message_notify(pid_t pid)
+{
+       /*
+        * Doing kill with a non-positive pid causes messages to be
+        * sent to places we don't want.
+        */
+
+       SMB_ASSERT(pid > 0);
+
+       if (kill(pid, SIGUSR1) == -1) {
+               if (errno == ESRCH) {
+                       DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
+                       tdb_delete(tdb, message_key_pid(pid));
+               } else {
+                       DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
+               }
+               return False;
+       }
+       return True;
+}
+
+/****************************************************************************
+ Send a message to a particular pid.
+****************************************************************************/
+
+static BOOL message_send_pid_internal(pid_t pid, int msg_type, const void *buf, size_t len,
+                     BOOL duplicates_allowed, unsigned int timeout)
+{
+       TDB_DATA kbuf;
+       TDB_DATA dbuf;
+       TDB_DATA old_dbuf;
+       struct message_rec rec;
+       char *ptr;
+       struct message_rec prec;
+
+       /*
+        * Doing kill with a non-positive pid causes messages to be
+        * sent to places we don't want.
+        */
+
+       SMB_ASSERT(pid > 0);
+
+       rec.msg_version = MESSAGE_VERSION;
+       rec.msg_type = msg_type;
+       rec.dest = pid;
+       rec.src = getpid();
+       rec.len = len;
+
+       kbuf = message_key_pid(pid);
+
+       dbuf.dptr = (void *)malloc(len + sizeof(rec));
+       if (!dbuf.dptr)
+               return False;
+
+       memcpy(dbuf.dptr, &rec, sizeof(rec));
+       if (len > 0)
+               memcpy((void *)((char*)dbuf.dptr+sizeof(rec)), buf, len);
+
+       dbuf.dsize = len + sizeof(rec);
+
+       if (duplicates_allowed) {
+
+               /* If duplicates are allowed we can just append the message and return. */
+
+               /* lock the record for the destination */
+               if (timeout) {
+                       if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+                               DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+                               return False;
+                       }
+               } else {
+                       if (tdb_chainlock(tdb, kbuf) == -1) {
+                               DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+                               return False;
+                       }
+               }       
+               tdb_append(tdb, kbuf, dbuf);
+               tdb_chainunlock(tdb, kbuf);
+
+               SAFE_FREE(dbuf.dptr);
+               errno = 0;                    /* paranoia */
+               return message_notify(pid);
+       }
+
+       /* lock the record for the destination */
+       if (timeout) {
+               if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+                       DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+                       return False;
+               }
+       } else {
+               if (tdb_chainlock(tdb, kbuf) == -1) {
+                       DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+                       return False;
+               }
+       }       
+
+       old_dbuf = tdb_fetch(tdb, kbuf);
+
+       if (!old_dbuf.dptr) {
+               /* its a new record */
+
+               tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+               tdb_chainunlock(tdb, kbuf);
+
+               SAFE_FREE(dbuf.dptr);
+               errno = 0;                    /* paranoia */
+               return message_notify(pid);
+       }
+
+       /* Not a new record. Check for duplicates. */
+
+       for(ptr = (char *)old_dbuf.dptr; ptr < old_dbuf.dptr + old_dbuf.dsize; ) {
+               /*
+                * First check if the message header matches, then, if it's a non-zero
+                * sized message, check if the data matches. If so it's a duplicate and
+                * we can discard it. JRA.
+                */
+
+               if (!memcmp(ptr, &rec, sizeof(rec))) {
+                       if (!len || (len && !memcmp( ptr + sizeof(rec), buf, len))) {
+                               tdb_chainunlock(tdb, kbuf);
+                               DEBUG(10,("message_send_pid_internal: discarding duplicate message.\n"));
+                               SAFE_FREE(dbuf.dptr);
+                               SAFE_FREE(old_dbuf.dptr);
+                               return True;
+                       }
+               }
+               memcpy(&prec, ptr, sizeof(prec));
+               ptr += sizeof(rec) + prec.len;
+       }
+
+       /* we're adding to an existing entry */
+
+       tdb_append(tdb, kbuf, dbuf);
+       tdb_chainunlock(tdb, kbuf);
+
+       SAFE_FREE(old_dbuf.dptr);
+       SAFE_FREE(dbuf.dptr);
+
+       errno = 0;                    /* paranoia */
+       return message_notify(pid);
+}
+
+/****************************************************************************
+ Send a message to a particular pid - no timeout.
+****************************************************************************/
+
+BOOL message_send_pid(pid_t pid, int msg_type, const void *buf, size_t len, BOOL duplicates_allowed)
+{
+       return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, 0);
+}
+
+/****************************************************************************
+ Send a message to a particular pid, with timeout in seconds.
+****************************************************************************/
+
+BOOL message_send_pid_with_timeout(pid_t pid, int msg_type, const void *buf, size_t len,
+               BOOL duplicates_allowed, unsigned int timeout)
+{
+       return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, timeout);
+}
+
+/****************************************************************************
+ Retrieve all messages for the current process.
+****************************************************************************/
+
+static BOOL retrieve_all_messages(char **msgs_buf, size_t *total_len)
+{
+       TDB_DATA kbuf;
+       TDB_DATA dbuf;
+       TDB_DATA null_dbuf;
+
+       ZERO_STRUCT(null_dbuf);
+
+       *msgs_buf = NULL;
+       *total_len = 0;
+
+       kbuf = message_key_pid(getpid());
+
+       tdb_chainlock(tdb, kbuf);
+       dbuf = tdb_fetch(tdb, kbuf);
+       /*
+        * Replace with an empty record to keep the allocated
+        * space in the tdb.
+        */
+       tdb_store(tdb, kbuf, null_dbuf, TDB_REPLACE);
+       tdb_chainunlock(tdb, kbuf);
+
+       if (dbuf.dptr == NULL || dbuf.dsize == 0) {
+               SAFE_FREE(dbuf.dptr);
+               return False;
+       }
+
+       *msgs_buf = dbuf.dptr;
+       *total_len = dbuf.dsize;
+
+       return True;
+}
+
+/****************************************************************************
+ Parse out the next message for the current process.
+****************************************************************************/
+
+static BOOL message_recv(char *msgs_buf, size_t total_len, int *msg_type, pid_t *src, char **buf, size_t *len)
+{
+       struct message_rec rec;
+       char *ret_buf = *buf;
+
+       *buf = NULL;
+       *len = 0;
+
+       if (total_len - (ret_buf - msgs_buf) < sizeof(rec))
+               return False;
+
+       memcpy(&rec, ret_buf, sizeof(rec));
+       ret_buf += sizeof(rec);
+
+       if (rec.msg_version != MESSAGE_VERSION) {
+               DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
+               return False;
+       }
+
+       if (rec.len > 0) {
+               if (total_len - (ret_buf - msgs_buf) < rec.len)
+                       return False;
+       }
+
+       *len = rec.len;
+       *msg_type = rec.msg_type;
+       *src = rec.src;
+       *buf = ret_buf;
+
+       return True;
+}
+
+/****************************************************************************
+ Receive and dispatch any messages pending for this process.
+ Notice that all dispatch handlers for a particular msg_type get called,
+ so you can register multiple handlers for a message.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_dispatch(void)
+{
+       int msg_type;
+       pid_t src;
+       char *buf;
+       char *msgs_buf;
+       size_t len, total_len;
+       struct dispatch_fns *dfn;
+       int n_handled;
+
+       if (!received_signal)
+               return;
+
+       DEBUG(10,("message_dispatch: received_signal = %d\n", received_signal));
+
+       received_signal = 0;
+
+       if (!retrieve_all_messages(&msgs_buf, &total_len))
+               return;
+
+       for (buf = msgs_buf; message_recv(msgs_buf, total_len, &msg_type, &src, &buf, &len); buf += len) {
+               DEBUG(10,("message_dispatch: received msg_type=%d src_pid=%u\n",
+                         msg_type, (unsigned int) src));
+               n_handled = 0;
+               for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
+                       if (dfn->msg_type == msg_type) {
+                               DEBUG(10,("message_dispatch: processing message of type %d.\n", msg_type));
+                               dfn->fn(msg_type, src, len ? (void *)buf : NULL, len);
+                               n_handled++;
+                       }
+               }
+               if (!n_handled) {
+                       DEBUG(5,("message_dispatch: warning: no handlers registed for "
+                                "msg_type %d in pid %u\n",
+                                msg_type, (unsigned int)getpid()));
+               }
+       }
+       SAFE_FREE(msgs_buf);
+}
+
+/****************************************************************************
+ Register a dispatch function for a particular message type.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_register(int msg_type, 
+                     void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
+{
+       struct dispatch_fns *dfn;
+
+       dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
+
+       if (dfn != NULL) {
+
+               ZERO_STRUCTPN(dfn);
+
+               dfn->msg_type = msg_type;
+               dfn->fn = fn;
+
+               DLIST_ADD(dispatch_fns, dfn);
+       }
+       else {
+       
+               DEBUG(0,("message_register: Not enough memory. malloc failed!\n"));
+       }
+}
+
+/****************************************************************************
+ De-register the function for a particular message type.
+****************************************************************************/
+
+void message_deregister(int msg_type)
+{
+       struct dispatch_fns *dfn, *next;
+
+       for (dfn = dispatch_fns; dfn; dfn = next) {
+               next = dfn->next;
+               if (dfn->msg_type == msg_type) {
+                       DLIST_REMOVE(dispatch_fns, dfn);
+                       SAFE_FREE(dfn);
+               }
+       }       
+}
+
+struct msg_all {
+       int msg_type;
+       uint32 msg_flag;
+       const void *buf;
+       size_t len;
+       BOOL duplicates;
+       int n_sent;
+};
+
+/****************************************************************************
+ Send one of the messages for the broadcast.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct connections_data crec;
+       struct msg_all *msg_all = (struct msg_all *)state;
+
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+       if (crec.cnum != -1)
+               return 0;
+
+       /* Don't send if the receiver hasn't registered an interest. */
+
+       if(!(crec.bcast_msg_flags & msg_all->msg_flag))
+               return 0;
+
+       /* If the msg send fails because the pid was not found (i.e. smbd died), 
+        * the msg has already been deleted from the messages.tdb.*/
+
+       if (!message_send_pid(crec.pid, msg_all->msg_type,
+                             msg_all->buf, msg_all->len,
+                             msg_all->duplicates)) {
+               
+               /* If the pid was not found delete the entry from connections.tdb */
+
+               if (errno == ESRCH) {
+                       DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+                                       (unsigned int)crec.pid, crec.cnum, crec.name));
+                       tdb_delete(the_tdb, kbuf);
+               }
+       }
+       msg_all->n_sent++;
+       return 0;
+}
+
+/**
+ * Send a message to all smbd processes.
+ *
+ * It isn't very efficient, but should be OK for the sorts of
+ * applications that use it. When we need efficient broadcast we can add
+ * it.
+ *
+ * @param n_sent Set to the number of messages sent.  This should be
+ * equal to the number of processes, but be careful for races.
+ *
+ * @retval True for success.
+ **/
+BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type,
+                     const void *buf, size_t len,
+                     BOOL duplicates_allowed,
+                     int *n_sent)
+{
+       struct msg_all msg_all;
+
+       msg_all.msg_type = msg_type;
+       if (msg_type < 1000)
+               msg_all.msg_flag = FLAG_MSG_GENERAL;
+       else if (msg_type > 1000 && msg_type < 2000)
+               msg_all.msg_flag = FLAG_MSG_NMBD;
+       else if (msg_type > 2000 && msg_type < 3000)
+               msg_all.msg_flag = FLAG_MSG_PRINTING;
+       else if (msg_type > 3000 && msg_type < 4000)
+               msg_all.msg_flag = FLAG_MSG_SMBD;
+       else
+               return False;
+
+       msg_all.buf = buf;
+       msg_all.len = len;
+       msg_all.duplicates = duplicates_allowed;
+       msg_all.n_sent = 0;
+
+       tdb_traverse(conn_tdb, traverse_fn, &msg_all);
+       if (n_sent)
+               *n_sent = msg_all.n_sent;
+       return True;
+}
+/** @} **/
diff --git a/source4/lib/module.c b/source4/lib/module.c
new file mode 100644 (file)
index 0000000..152e893
--- /dev/null
@@ -0,0 +1,128 @@
+/* 
+   Unix SMB/CIFS implementation.
+   module loading system
+
+   Copyright (C) Jelmer Vernooij 2002
+   
+   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"
+
+#ifdef HAVE_DLOPEN
+int smb_load_module(const char *module_name)
+{
+       void *handle;
+       init_module_function *init;
+       int status;
+       const char *error;
+
+       /* Always try to use LAZY symbol resolving; if the plugin has 
+        * backwards compatibility, there might be symbols in the 
+        * plugin referencing to old (removed) functions
+        */
+       handle = sys_dlopen(module_name, RTLD_LAZY);
+
+       if(!handle) {
+               DEBUG(0, ("Error loading module '%s': %s\n", module_name, sys_dlerror()));
+               return False;
+       }
+
+       init = sys_dlsym(handle, "init_module");
+
+       /* we must check sys_dlerror() to determine if it worked, because
+           sys_dlsym() can validly return NULL */
+       error = sys_dlerror();
+       if (error) {
+               DEBUG(0, ("Error trying to resolve symbol 'init_module' in %s: %s\n", module_name, error));
+               return False;
+       }
+
+       status = init();
+
+       DEBUG(2, ("Module '%s' loaded\n", module_name));
+
+       return status;
+}
+
+/* Load all modules in list and return number of 
+ * modules that has been successfully loaded */
+int smb_load_modules(const char **modules)
+{
+       int i;
+       int success = 0;
+
+       for(i = 0; modules[i]; i++){
+               if(smb_load_module(modules[i])) {
+                       success++;
+               }
+       }
+
+       DEBUG(2, ("%d modules successfully loaded\n", success));
+
+       return success;
+}
+
+int smb_probe_module(const char *subsystem, const char *module)
+{
+       char *full_path;
+       int rc;
+       TALLOC_CTX *mem_ctx;
+       
+       /* Check for absolute path */
+       if(module[0] == '/')return smb_load_module(module);
+       
+       mem_ctx = talloc_init("smb_probe_module");
+       if (!mem_ctx) {
+               DEBUG(0,("No memory for loading modules\n"));
+               return False;
+       }
+       full_path = talloc_strdup(mem_ctx, lib_path(mem_ctx, subsystem));
+       full_path = talloc_asprintf(mem_ctx, "%s/%s.%s", 
+               full_path, module, shlib_ext());
+       
+       rc = smb_load_module(full_path);
+       talloc_destroy(mem_ctx);
+       return rc;
+}
+
+#else /* HAVE_DLOPEN */
+
+int smb_load_module(const char *module_name)
+{
+       DEBUG(0,("This samba executable has not been built with plugin support"));
+       return False;
+}
+
+int smb_load_modules(const char **modules)
+{
+       DEBUG(0,("This samba executable has not been built with plugin support"));
+       return False;
+}
+
+int smb_probe_module(const char *subsystem, const char *module)
+{
+       DEBUG(0,("This samba executable has not been built with plugin support, not probing")); 
+       return False;
+}
+
+#endif /* HAVE_DLOPEN */
+
+void init_modules(void)
+{
+       if(lp_preload_modules()) 
+               smb_load_modules(lp_preload_modules());
+       /* FIXME: load static modules */
+}
diff --git a/source4/lib/ms_fnmatch.c b/source4/lib/ms_fnmatch.c
new file mode 100644 (file)
index 0000000..dd015a0
--- /dev/null
@@ -0,0 +1,226 @@
+/* 
+   Unix SMB/CIFS implementation.
+   filename matching routine
+   Copyright (C) Andrew Tridgell 1992-1998 
+
+   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.  */
+
+/*
+   This module was originally based on fnmatch.c copyright by the Free
+   Software Foundation. It bears little resemblence to that code now 
+*/  
+
+
+#if FNMATCH_TEST
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include "includes.h"
+#endif
+
+/* 
+   bugger. we need a separate wildcard routine for older versions
+   of the protocol. This is not yet perfect, but its a lot
+   better than what we had */
+static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern, 
+                                 const smb_ucs2_t *string)
+{
+       const smb_ucs2_t *p = pattern, *n = string;
+       smb_ucs2_t c;
+
+       if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match;
+
+       while ((c = *p++)) {
+               switch (c) {
+               case UCS2_CHAR('.'):
+                       if (! *n) goto next;
+                       if (*n != UCS2_CHAR('.')) goto nomatch;
+                       n++;
+                       break;
+
+               case UCS2_CHAR('?'):
+                       if (! *n) goto next;
+                       if ((*n == UCS2_CHAR('.') && 
+                            n[1] != UCS2_CHAR('.')) || ! *n) 
+                               goto next;
+                       n++;
+                       break;
+
+               case UCS2_CHAR('>'):
+                       if (! *n) goto next;
+                       if (n[0] == UCS2_CHAR('.')) {
+                               if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match;
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                               goto nomatch;
+                       }
+                       n++;
+                       break;
+
+               case UCS2_CHAR('*'):
+                       if (! *n) goto next;
+                       if (! *p) goto match;
+                       for (; *n; n++) {
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                       }
+                       break;
+
+               case UCS2_CHAR('<'):
+                       for (; *n; n++) {
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                               if (*n == UCS2_CHAR('.') && 
+                                   !strchr_w(n+1,UCS2_CHAR('.'))) {
+                                       n++;
+                                       break;
+                               }
+                       }
+                       break;
+
+               case UCS2_CHAR('"'):
+                       if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                       if (*n != UCS2_CHAR('.')) goto nomatch;
+                       n++;
+                       break;
+
+               default:
+                       if (c != *n) goto nomatch;
+                       n++;
+               }
+       }
+       
+       if (! *n) goto match;
+       
+ nomatch:
+       /*
+       if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+       */
+       return -1;
+
+next:
+       if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+        goto nomatch;
+
+ match:
+       /*
+       if (verbose) printf("MATCH   pattern=[%s] string=[%s]\n", pattern, string);
+       */
+       return 0;
+}
+
+static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, const smb_ucs2_t *string)
+{
+       if (!strpbrk_wa(pattern, "?*<>\"")) {
+               smb_ucs2_t s[] = {UCS2_CHAR('.'), 0};
+               if (strcmp_wa(string,"..") == 0) string = s;
+               return strcasecmp_w(pattern, string);
+       }
+
+       if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) {
+               smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0};
+               smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0};
+               return ms_fnmatch_lanman_core(pattern, dotdot) &&
+                       ms_fnmatch_lanman_core(pattern, dot);
+       }
+
+       return ms_fnmatch_lanman_core(pattern, string);
+}
+
+
+/* the following function was derived using the masktest utility -
+   after years of effort we finally have a perfect MS wildcard
+   matching routine! 
+
+   NOTE: this matches only filenames with no directory component
+
+   Returns 0 on match, -1 on fail.
+*/
+static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string, 
+                       enum protocol_types protocol)
+{
+       const smb_ucs2_t *p = pattern, *n = string;
+       smb_ucs2_t c;
+
+       if (protocol <= PROTOCOL_LANMAN2) {
+               return ms_fnmatch_lanman1(pattern, string);
+       }
+
+       while ((c = *p++)) {
+               switch (c) {
+               case UCS2_CHAR('?'):
+                       if (! *n) return -1;
+                       n++;
+                       break;
+
+               case UCS2_CHAR('>'):
+                       if (n[0] == UCS2_CHAR('.')) {
+                               if (! n[1] && ms_fnmatch_w(p, n+1, protocol) == 0) return 0;
+                               if (ms_fnmatch_w(p, n, protocol) == 0) return 0;
+                               return -1;
+                       }
+                       if (! *n) return ms_fnmatch_w(p, n, protocol);
+                       n++;
+                       break;
+
+               case UCS2_CHAR('*'):
+                       for (; *n; n++) {
+                               if (ms_fnmatch_w(p, n, protocol) == 0) return 0;
+                       }
+                       break;
+
+               case UCS2_CHAR('<'):
+                       for (; *n; n++) {
+                               if (ms_fnmatch_w(p, n, protocol) == 0) return 0;
+                               if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) {
+                                       n++;
+                                       break;
+                               }
+                       }
+                       break;
+
+               case UCS2_CHAR('"'):
+                       if (*n == 0 && ms_fnmatch_w(p, n, protocol) == 0) return 0;
+                       if (*n != UCS2_CHAR('.')) return -1;
+                       n++;
+                       break;
+
+               default:
+                       if (c != *n) return -1;
+                       n++;
+               }
+       }
+       
+       if (! *n) return 0;
+       
+       return -1;
+}
+
+
+int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol)
+{
+       wpstring p, s;
+       int ret;
+
+       pstrcpy_wa(p, pattern);
+       pstrcpy_wa(s, string);
+
+       ret = ms_fnmatch_w(p, s, protocol);
+/*     DEBUG(0,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret)); */
+       return ret;
+}
+
+/* a generic fnmatch function - uses for non-CIFS pattern matching */
+int gen_fnmatch(const char *pattern, const char *string)
+{
+       return ms_fnmatch(pattern, string, PROTOCOL_NT1);
+}
diff --git a/source4/lib/mutex.c b/source4/lib/mutex.c
new file mode 100644 (file)
index 0000000..1be23a5
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba mutex/lock functions
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers 2003
+   
+   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"
+        
+static mutex_t mutex_list[MUTEX_MAX];
+
+/* the registered mutex handlers */
+static struct {
+       const char *name;
+       struct mutex_ops ops;
+} mutex_handlers;
+
+int mutex_lock_by_id(enum mutex_id id, const char *name)
+{
+       return mutex_lock(&mutex_list[id], name);
+}
+
+int mutex_unlock_by_id(enum mutex_id id, const char *name)
+{
+       return mutex_unlock(&mutex_list[id], name);
+}
+
+int mutex_init(mutex_t *mutex, const char *name)
+{
+       if (mutex_handlers.ops.mutex_init) {
+               return mutex_handlers.ops.mutex_init(mutex, name);
+       }
+       return 0;
+}
+
+int mutex_destroy(mutex_t *mutex, const char *name)
+{
+       if (mutex_handlers.ops.mutex_destroy) {
+               return mutex_handlers.ops.mutex_destroy(mutex, name);
+       }
+       return 0;
+}
+
+int mutex_lock(mutex_t *mutex, const char *name)
+{
+       if (mutex_handlers.ops.mutex_lock) {
+               return mutex_handlers.ops.mutex_lock(mutex, name);
+       }
+       return 0;
+}
+
+int mutex_unlock(mutex_t *mutex, const char *name)
+{
+       if (mutex_handlers.ops.mutex_unlock) {
+               return mutex_handlers.ops.mutex_unlock(mutex, name);
+       }
+       return 0;
+}
+
+/* read/write lock routines */
+
+int rwlock_init(rwlock_t *rwlock, const char *name)
+{
+       if (mutex_handlers.ops.rwlock_init) {
+               return mutex_handlers.ops.rwlock_init(rwlock, name);
+       }
+       return 0;
+}
+
+int rwlock_destroy(rwlock_t *rwlock, const char *name)
+{
+       if (mutex_handlers.ops.rwlock_destroy) {
+               return mutex_handlers.ops.rwlock_destroy(rwlock, name);
+       }
+       return 0;
+}
+
+int rwlock_lock_write(rwlock_t *rwlock, const char *name)
+{
+       if (mutex_handlers.ops.rwlock_lock_write) {
+               return mutex_handlers.ops.rwlock_lock_write(rwlock, name);
+       }
+       return 0;
+}
+
+int rwlock_lock_read(rwlock_t *rwlock, const char *name)
+{
+       if (mutex_handlers.ops.rwlock_lock_read) {
+               return mutex_handlers.ops.rwlock_lock_read(rwlock, name);
+       }
+       return 0;
+}
+
+int rwlock_unlock(rwlock_t *rwlock, const char *name)
+{
+       if (mutex_handlers.ops.rwlock_unlock) {
+               return mutex_handlers.ops.rwlock_unlock(rwlock, name);
+       }
+       return 0;
+}
+
+
+/*
+  register a set of mutex/rwlock handlers. 
+  Should only be called once in the execution of smbd.
+*/
+BOOL register_mutex_handlers(const char *name, struct mutex_ops *ops)
+{
+       if (mutex_handlers.name != NULL) {
+               /* it's already registered! */
+               DEBUG(2,("mutex handler '%s' already registered - failed '%s'\n", 
+                        mutex_handlers.name, name));
+               return False;
+       }
+
+       mutex_handlers.name = name;
+       mutex_handlers.ops = *ops;
+
+       if (mutex_handlers.ops.mutex_init) {
+               enum mutex_id id;
+               for (id=0; id < MUTEX_MAX; id++) {
+                       mutex_handlers.ops.mutex_init(&mutex_list[id], "mutex_list");
+               }
+       }
+
+       DEBUG(2,("mutex handler '%s' registered\n", name));
+       return True;
+}
+
diff --git a/source4/lib/pam_errors.c b/source4/lib/pam_errors.c
new file mode 100644 (file)
index 0000000..925441f
--- /dev/null
@@ -0,0 +1,126 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  PAM error mapping functions
+ *  Copyright (C) Andrew Bartlett 2002
+ *  
+ *  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"
+
+#ifdef WITH_PAM
+#include <security/pam_appl.h>
+
+#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif 
+
+/* PAM -> NT_STATUS map */
+static const struct {
+       int pam_code;
+       NTSTATUS ntstatus;
+} pam_to_nt_status_map[] = {
+       {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
+       {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
+       {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
+       {PAM_SYSTEM_ERR,  NT_STATUS_UNSUCCESSFUL},
+       {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
+       {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
+       {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
+       {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME:  Is this correct? */
+       {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
+       {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
+       {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME:  Is this correct? */
+       {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
+       {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
+       {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
+       {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN},  /* FIXME:  Is this correct? */
+       {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},  /* FIXME:  Is this correct? */
+       {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
+       {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+       {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
+#endif
+       {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+       {PAM_SUCCESS, NT_STATUS_OK}
+};
+
+/* NT_STATUS -> PAM map */
+static const struct {
+       NTSTATUS ntstatus;
+       int pam_code;
+} nt_status_to_pam_map[] = {
+       {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
+       {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
+       {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
+       {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
+       {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
+       {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
+       {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
+       {NT_STATUS_OK, PAM_SUCCESS}
+};
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+       int i;
+       if (pam_error == 0) return NT_STATUS_OK;
+       
+       for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
+               if (pam_error == pam_to_nt_status_map[i].pam_code)
+                       return pam_to_nt_status_map[i].ntstatus;
+       }
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+       int i;
+       if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
+       
+       for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
+               if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
+                       return nt_status_to_pam_map[i].pam_code;
+       }
+       return PAM_SYSTEM_ERR;
+}
+
+#else 
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+       if (pam_error == 0) return NT_STATUS_OK;
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
+       return 4; /* PAM_SYSTEM_ERR */
+}
+
+#endif
+
diff --git a/source4/lib/pidfile.c b/source4/lib/pidfile.c
new file mode 100644 (file)
index 0000000..3471f27
--- /dev/null
@@ -0,0 +1,109 @@
+/* this code is broken - there is a race condition with the unlink (tridge) */
+
+/* 
+   Unix SMB/CIFS implementation.
+   pidfile handling
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK
+#endif
+
+/* return the pid in a pidfile. return 0 if the process (or pidfile)
+   does not exist */
+pid_t pidfile_pid(const char *name)
+{
+       int fd;
+       char pidstr[20];
+       unsigned ret;
+       pstring pidFile;
+
+       slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+       fd = sys_open(pidFile, O_NONBLOCK | O_RDONLY, 0644);
+       if (fd == -1) {
+               return 0;
+       }
+
+       ZERO_ARRAY(pidstr);
+
+       if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) {
+               goto noproc;
+       }
+
+       ret = atoi(pidstr);
+       
+       if (!process_exists((pid_t)ret)) {
+               goto noproc;
+       }
+
+       if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_RDLCK)) {
+               /* we could get the lock - it can't be a Samba process */
+               goto noproc;
+       }
+
+       close(fd);
+       return (pid_t)ret;
+
+ noproc:
+       close(fd);
+       unlink(pidFile);
+       return 0;
+}
+
+/* create a pid file in the pid directory. open it and leave it locked */
+void pidfile_create(const char *name)
+{
+       int     fd;
+       char    buf[20];
+       pstring pidFile;
+       pid_t pid;
+
+       slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+       pid = pidfile_pid(name);
+       if (pid != 0) {
+               DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n", 
+                        name, pidFile, (int)pid));
+               exit(1);
+       }
+
+       fd = sys_open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644);
+       if (fd == -1) {
+               DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile, 
+                        strerror(errno)));
+               exit(1);
+       }
+
+       if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_WRLCK)==False) {
+               DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n",  
+              name, pidFile, strerror(errno)));
+               exit(1);
+       }
+
+       memset(buf, 0, sizeof(buf));
+       slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) getpid());
+       if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
+               DEBUG(0,("ERROR: can't write to file %s: %s\n", 
+                        pidFile, strerror(errno)));
+               exit(1);
+       }
+       /* Leave pid file open & locked for the duration... */
+}
diff --git a/source4/lib/popt/CHANGES b/source4/lib/popt/CHANGES
new file mode 100644 (file)
index 0000000..b6ab2aa
--- /dev/null
@@ -0,0 +1,43 @@
+1.3 ->
+       - heavy dose of const's
+       - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+       - added support for single -
+       - misc bug fixes
+       - portability improvements
+
+1.2.2 -> 1.2.3
+       - fixed memset() in help message generation (Dale Hawkins)
+       - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+       - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+       - fixed bug in chaind alias happens which seems to have only
+         affected --triggers in rpm
+       - added POPT_ARG_VAL
+       - popt.3 installed by default
+
+1.2 -> 1.2.1
+       - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+       - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+       - added popt.3 man page (Robert Lynch)
+       - don't use mmap anymore (its lack of portability isn't worth the
+         trouble)
+       - added test script
+       - added support for exec
+       - removed support for *_POPT_ALIASES env variable -- it was a bad
+         idea
+       - reorganized into multiple source files
+       - added automatic help generation, POPT_AUTOHELP
+       - added table callbacks
+       - added table inclusion
+       - updated man page for new features
+       - added test scripts
+
+1.0 -> 1.1
+       - moved to autoconf (Fred Fish)
+       - added STRERROR replacement (Norbert Warmuth)
+       - added const keywords (Bruce Perens)
diff --git a/source4/lib/popt/COPYING b/source4/lib/popt/COPYING
new file mode 100644 (file)
index 0000000..b4c7ca8
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright (c) 1998  Red Hat Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/source4/lib/popt/README b/source4/lib/popt/README
new file mode 100644 (file)
index 0000000..7fccc83
--- /dev/null
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+       1) popt is fully reentrant
+       2) popt can parse arbitrary argv[] style arrays while 
+          getopt(2) makes this quite difficult
+       3) popt allows users to alias command line arguments
+       4) popt provides convience functions for parsting strings
+          into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt. 
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/source4/lib/popt/dummy.in b/source4/lib/popt/dummy.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/lib/popt/findme.c b/source4/lib/popt/findme.c
new file mode 100644 (file)
index 0000000..f2ad05b
--- /dev/null
@@ -0,0 +1,46 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0) {
+    char * path = getenv("PATH");
+    char * pathbuf;
+    char * start, * chptr;
+    char * buf, *local = NULL;
+
+    /* If there is a / in the argv[0], it has to be an absolute
+       path */
+    if (strchr(argv0, '/'))
+       return xstrdup(argv0);
+
+    if (!path) return NULL;
+
+    local = start = pathbuf = malloc(strlen(path) + 1);
+    buf = malloc(strlen(path) + strlen(argv0) + 2);
+    strcpy(pathbuf, path);
+
+    chptr = NULL;
+    do {
+       if ((chptr = strchr(start, ':')))
+           *chptr = '\0';
+       sprintf(buf, "%s/%s", start, argv0);
+
+       if (!access(buf, X_OK)) {
+               if (local) free(local);
+               return buf;
+       }
+
+       if (chptr) 
+           start = chptr + 1;
+       else
+           start = NULL;
+    } while (start && *start);
+
+    free(buf);
+    if (local) free(local);
+
+    return NULL;
+}
diff --git a/source4/lib/popt/findme.h b/source4/lib/popt/findme.h
new file mode 100644 (file)
index 0000000..5e93963
--- /dev/null
@@ -0,0 +1,10 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+const char * findProgramPath(const char * argv0);
+
+#endif
diff --git a/source4/lib/popt/popt.c b/source4/lib/popt/popt.c
new file mode 100644 (file)
index 0000000..9fa8650
--- /dev/null
@@ -0,0 +1,782 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef HAVE_STRERROR
+static char * strerror(int errno) {
+    extern int sys_nerr;
+    extern char * sys_errlist[];
+
+    if ((0 <= errno) && (errno < sys_nerr))
+       return sys_errlist[errno];
+    else
+       return POPT_("unknown errno");
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+    if (con->execPath) xfree(con->execPath);
+    con->execPath = xstrdup(path);
+    con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+                           int post) {
+    const struct poptOption * opt = table;
+    poptCallbackType cb;
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           invokeCallbacks(con, opt->arg, post);
+       } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+                  ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+                   ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+           cb = (poptCallbackType)opt->arg;
+           cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+              NULL, NULL, opt->descrip);
+       }
+       opt++;
+    }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+                          const struct poptOption * options, int flags) {
+    poptContext con = malloc(sizeof(*con));
+
+    memset(con, 0, sizeof(*con));
+
+    con->os = con->optionStack;
+    con->os->argc = argc;
+    con->os->argv = argv;
+    con->os->argb = NULL;
+
+    if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+       con->os->next = 1;                      /* skip argv[0] */
+
+    con->leftovers = calloc( (argc + 1), sizeof(char *) );
+    con->options = options;
+    con->aliases = NULL;
+    con->numAliases = 0;
+    con->flags = flags;
+    con->execs = NULL;
+    con->numExecs = 0;
+    con->finalArgvAlloced = argc * 2;
+    con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+    con->execAbsolute = 1;
+    con->arg_strip = NULL;
+
+    if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+       con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+    if (name)
+       con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+    invokeCallbacks(con, con->options, 0);
+
+    return con;
+}
+
+static void cleanOSE(struct optionStackEntry *os)
+{
+    if (os->nextArg) {
+       xfree(os->nextArg);
+       os->nextArg = NULL;
+    }
+    if (os->argv) {
+       xfree(os->argv);
+       os->argv = NULL;
+    }
+    if (os->argb) {
+       PBM_FREE(os->argb);
+       os->argb = NULL;
+    }
+}
+
+void poptResetContext(poptContext con) {
+    int i;
+
+    while (con->os > con->optionStack) {
+       cleanOSE(con->os--);
+    }
+    if (con->os->argb) {
+       PBM_FREE(con->os->argb);
+       con->os->argb = NULL;
+    }
+    con->os->currAlias = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->nextArg = NULL;
+    con->os->next = 1;                 /* skip argv[0] */
+
+    con->numLeftovers = 0;
+    con->nextLeftover = 0;
+    con->restLeftover = 0;
+    con->doExec = NULL;
+
+    for (i = 0; i < con->finalArgvCount; i++) {
+       if (con->finalArgv[i]) {
+           xfree(con->finalArgv[i]);
+           con->finalArgv[i] = NULL;
+       }
+    }
+
+    con->finalArgvCount = 0;
+
+    if (con->arg_strip) {
+       PBM_FREE(con->arg_strip);
+       con->arg_strip = NULL;
+    }
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+    int i;
+
+    i = con->numExecs - 1;
+    if (longName) {
+       while (i >= 0 && (!con->execs[i].longName ||
+           strcmp(con->execs[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->execs[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if (con->flags & POPT_CONTEXT_NO_EXEC)
+       return 1;
+
+    if (con->doExec == NULL) {
+       con->doExec = con->execs + i;
+       return 1;
+    }
+
+    /* We already have an exec to do; remember this option for next
+       time 'round */
+    if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+       con->finalArgvAlloced += 10;
+       con->finalArgv = realloc(con->finalArgv,
+                       sizeof(*con->finalArgv) * con->finalArgvAlloced);
+    }
+
+    i = con->finalArgvCount++;
+    {  char *s  = malloc((longName ? strlen(longName) : 0) + 3);
+       if (longName)
+           sprintf(s, "--%s", longName);
+       else
+           sprintf(s, "-%c", shortName);
+       con->finalArgv[i] = s;
+    }
+
+    return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, const char * longName, char shortName,
+                      /*@keep@*/ const char * nextCharArg) {
+    int i;
+
+    if (con->os->currAlias && con->os->currAlias->longName && longName &&
+       !strcmp(con->os->currAlias->longName, longName))
+       return 0;
+    if (con->os->currAlias && shortName &&
+           shortName == con->os->currAlias->shortName)
+       return 0;
+
+    i = con->numAliases - 1;
+    if (longName) {
+       while (i >= 0 && (!con->aliases[i].longName ||
+           strcmp(con->aliases[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->aliases[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    if (nextCharArg && *nextCharArg)
+       con->os->nextCharArg = nextCharArg;
+
+    con->os++;
+    con->os->next = 0;
+    con->os->stuffed = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = con->aliases + i;
+    poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+               &con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+
+    return 1;
+}
+
+static void execCommand(poptContext con) {
+    const char ** argv;
+    int pos = 0;
+    const char * script = con->doExec->script;
+
+    argv = malloc(sizeof(*argv) *
+                       (6 + con->numLeftovers + con->finalArgvCount));
+
+    if (!con->execAbsolute && strchr(script, '/')) return;
+
+    if (!strchr(script, '/') && con->execPath) {
+       char *s = malloc(strlen(con->execPath) + strlen(script) + 2);
+       sprintf(s, "%s/%s", con->execPath, script);
+       argv[pos] = s;
+    } else {
+       argv[pos] = script;
+    }
+    pos++;
+
+    argv[pos] = findProgramPath(con->os->argv[0]);
+    if (argv[pos]) pos++;
+    argv[pos++] = ";";
+
+    memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+    pos += con->finalArgvCount;
+
+    if (con->numLeftovers) {
+       argv[pos++] = "--";
+       memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+       pos += con->numLeftovers;
+    }
+
+    argv[pos++] = NULL;
+
+#ifdef __hpux
+    setresuid(getuid(), getuid(),-1);
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX         sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+    setuid(getuid());
+#elif defined (HAVE_SETREUID)
+    setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#else
+    ; /* Can't drop privileges */
+#endif
+#endif
+
+    execvp(argv[0], (char *const *)argv);
+}
+
+/*@observer@*/ static const struct poptOption *
+findOption(const struct poptOption * table, const char * longName,
+    char shortName,
+    /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData,
+    int singleDash)
+{
+    const struct poptOption * opt = table;
+    const struct poptOption * opt2;
+    const struct poptOption * cb = NULL;
+
+    /* This happens when a single - is given */
+    if (singleDash && !shortName && !*longName)
+       shortName = '-';
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           opt2 = findOption(opt->arg, longName, shortName, callback,
+                             callbackData, singleDash);
+           if (opt2) {
+               if (*callback && !*callbackData)
+                   *callbackData = opt->descrip;
+               return opt2;
+           }
+       } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+           cb = opt;
+       } else if (longName && opt->longName &&
+                  (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+                  !strcmp(longName, opt->longName)) {
+           break;
+       } else if (shortName && shortName == opt->shortName) {
+           break;
+       }
+       opt++;
+    }
+
+    if (!opt->longName && !opt->shortName) return NULL;
+    *callbackData = NULL;
+    *callback = NULL;
+    if (cb) {
+       *callback = (poptCallbackType)cb->arg;
+       if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+           *callbackData = cb->descrip;
+    }
+
+    return opt;
+}
+
+static const char *findNextArg(poptContext con, unsigned argx, int delete)
+{
+    struct optionStackEntry * os = con->os;
+    const char * arg;
+
+    do {
+       int i;
+       arg = NULL;
+       while (os->next == os->argc && os > con->optionStack) os--;
+       if (os->next == os->argc && os == con->optionStack) break;
+       for (i = os->next; i < os->argc; i++) {
+           if (os->argb && PBM_ISSET(i, os->argb)) continue;
+           if (*os->argv[i] == '-') continue;
+           if (--argx > 0) continue;
+           arg = os->argv[i];
+           if (delete) {
+               if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+               PBM_SET(i, os->argb);
+           }
+           break;
+       }
+       if (os > con->optionStack) os--;
+    } while (arg == NULL);
+    return arg;
+}
+
+static /*@only@*/ const char * expandNextArg(poptContext con, const char * s)
+{
+    const char *a;
+    size_t alen;
+    char *t, *te;
+    size_t tn = strlen(s) + 1;
+    char c;
+
+    te = t = malloc(tn);;
+    while ((c = *s++) != '\0') {
+       switch (c) {
+#if 0  /* XXX can't do this */
+       case '\\':      /* escape */
+           c = *s++;
+           break;
+#endif
+       case '!':
+           if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+               break;
+           if ((a = findNextArg(con, 1, 1)) == NULL)
+               break;
+           s += 3;
+
+           alen = strlen(a);
+           tn += alen;
+           *te = '\0';
+           t = realloc(t, tn);
+           te = t + strlen(t);
+           strncpy(te, a, alen); te += alen;
+           continue;
+           /*@notreached@*/ break;
+       default:
+           break;
+       }
+       *te++ = c;
+    }
+    *te = '\0';
+    t = realloc(t, strlen(t)+1);       /* XXX memory leak, hard to plug */
+    return t;
+}
+
+static void poptStripArg(poptContext con, int which)
+{
+    if(con->arg_strip == NULL) {
+       con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+    }
+    PBM_SET(which, con->arg_strip);
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+    const struct poptOption * opt = NULL;
+    int done = 0;
+
+    /* looks a bit tricky to get rid of alloca properly in this fn */
+#if HAVE_ALLOCA_H
+#define ALLOCA(x) alloca(x)
+#else
+#define ALLOCA(x) malloc(x)
+#endif
+
+
+    while (!done) {
+       const char * origOptString = NULL;
+       poptCallbackType cb = NULL;
+       const void * cbData = NULL;
+       const char * longArg = NULL;
+       int canstrip = 0;
+
+       while (!con->os->nextCharArg && con->os->next == con->os->argc
+               && con->os > con->optionStack) {
+           cleanOSE(con->os--);
+       }
+       if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+           invokeCallbacks(con, con->options, 1);
+           if (con->doExec) execCommand(con);
+           return -1;
+       }
+
+       /* Process next long option */
+       if (!con->os->nextCharArg) {
+           char * localOptString, * optString;
+           int thisopt;
+
+           if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+               con->os->next++;
+               continue;
+           }
+           thisopt=con->os->next;
+           origOptString = con->os->argv[con->os->next++];
+
+           if (con->restLeftover || *origOptString != '-') {
+               con->leftovers[con->numLeftovers++] = origOptString;
+               if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+                   con->restLeftover = 1;
+               continue;
+           }
+
+           /* Make a copy we can hack at */
+           localOptString = optString =
+                       strcpy(ALLOCA(strlen(origOptString) + 1),
+                       origOptString);
+
+           if (!optString[0])
+               return POPT_ERROR_BADOPT;
+
+           if (optString[1] == '-' && !optString[2]) {
+               con->restLeftover = 1;
+               continue;
+           } else {
+               char *oe;
+               int singleDash;
+
+               optString++;
+               if (*optString == '-')
+                   singleDash = 0, optString++;
+               else
+                   singleDash = 1;
+
+               /* XXX aliases with arg substitution need "--alias=arg" */
+               if (handleAlias(con, optString, '\0', NULL))
+                   continue;
+               if (handleExec(con, optString, '\0'))
+                   continue;
+
+               /* Check for "--long=arg" option. */
+               for (oe = optString; *oe && *oe != '='; oe++)
+                   ;
+               if (*oe == '=') {
+                   *oe++ = '\0';
+                   /* XXX longArg is mapped back to persistent storage. */
+                   longArg = origOptString + (oe - localOptString);
+               }
+
+               opt = findOption(con->options, optString, '\0', &cb, &cbData,
+                                singleDash);
+               if (!opt && !singleDash)
+                   return POPT_ERROR_BADOPT;
+           }
+
+           if (!opt) {
+               con->os->nextCharArg = origOptString + 1;
+           } else {
+               if(con->os == con->optionStack &&
+                  opt->argInfo & POPT_ARGFLAG_STRIP) {
+                   canstrip = 1;
+                   poptStripArg(con, thisopt);
+               }
+           }
+       }
+
+       /* Process next short option */
+       if (con->os->nextCharArg) {
+           origOptString = con->os->nextCharArg;
+
+           con->os->nextCharArg = NULL;
+
+           if (handleAlias(con, NULL, *origOptString,
+                           origOptString + 1)) {
+               origOptString++;
+               continue;
+           }
+           if (handleExec(con, NULL, *origOptString))
+               continue;
+
+           opt = findOption(con->options, NULL, *origOptString, &cb,
+                            &cbData, 0);
+           if (!opt)
+               return POPT_ERROR_BADOPT;
+
+           origOptString++;
+           if (*origOptString)
+               con->os->nextCharArg = origOptString;
+       }
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+           *((int *)opt->arg) = 1;
+       } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+           if (opt->arg)
+               *((int *) opt->arg) = opt->val;
+       } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+           if (con->os->nextArg) {
+               xfree(con->os->nextArg);
+               con->os->nextArg = NULL;
+           }
+           if (longArg) {
+               con->os->nextArg = expandNextArg(con, longArg);
+           } else if (con->os->nextCharArg) {
+               con->os->nextArg = expandNextArg(con, con->os->nextCharArg);
+               con->os->nextCharArg = NULL;
+           } else {
+               while (con->os->next == con->os->argc &&
+                      con->os > con->optionStack) {
+                   cleanOSE(con->os--);
+               }
+               if (con->os->next == con->os->argc)
+                   return POPT_ERROR_NOARG;
+
+               /* make sure this isn't part of a short arg or the
+                   result of an alias expansion */
+               if(con->os == con->optionStack &&
+                  opt->argInfo & POPT_ARGFLAG_STRIP &&
+                  canstrip) {
+                   poptStripArg(con, con->os->next);
+               }
+               
+               con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]);
+           }
+
+           if (opt->arg) {
+               long aLong;
+               char *end;
+
+               switch (opt->argInfo & POPT_ARG_MASK) {
+                 case POPT_ARG_STRING:
+                   /* XXX memory leak, hard to plug */
+                   *((const char **) opt->arg) = xstrdup(con->os->nextArg);
+                   break;
+
+                 case POPT_ARG_INT:
+                 case POPT_ARG_LONG:
+                   aLong = strtol(con->os->nextArg, &end, 0);
+                   if (!(end && *end == '\0'))
+                       return POPT_ERROR_BADNUMBER;
+
+                   if (aLong == LONG_MIN || aLong == LONG_MAX)
+                       return POPT_ERROR_OVERFLOW;
+                   if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+                       *((long *) opt->arg) = aLong;
+                   } else {
+                       if (aLong > INT_MAX || aLong < INT_MIN)
+                           return POPT_ERROR_OVERFLOW;
+                       *((int *) opt->arg) = aLong;
+                   }
+                   break;
+
+                 default:
+                   fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+                     opt->argInfo & POPT_ARG_MASK);
+                   exit(EXIT_FAILURE);
+               }
+           }
+       }
+
+       if (cb)
+           cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+       else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+           done = 1;
+
+       if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+           con->finalArgvAlloced += 10;
+           con->finalArgv = realloc(con->finalArgv,
+                           sizeof(*con->finalArgv) * con->finalArgvAlloced);
+       }
+
+       {    char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+           if (opt->longName)
+               sprintf(s, "--%s", opt->longName);
+           else
+               sprintf(s, "-%c", opt->shortName);
+           con->finalArgv[con->finalArgvCount++] = s;
+       }
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
+                    && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) {
+           con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg);
+       }
+    }
+
+    return opt->val;
+}
+
+const char * poptGetOptArg(poptContext con) {
+    const char * ret = con->os->nextArg;
+    con->os->nextArg = NULL;
+    return ret;
+}
+
+const char * poptGetArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return con->leftovers[con->nextLeftover++];
+}
+
+const char * poptPeekArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return con->leftovers[con->nextLeftover];
+}
+
+const char ** poptGetArgs(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+
+    /* some apps like [like RPM ;-) ] need this NULL terminated */
+    con->leftovers[con->numLeftovers] = NULL;
+
+    return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+    int i;
+
+    poptResetContext(con);
+    if (con->os->argb) free(con->os->argb);
+
+    for (i = 0; i < con->numAliases; i++) {
+       if (con->aliases[i].longName) xfree(con->aliases[i].longName);
+       free(con->aliases[i].argv);
+    }
+
+    for (i = 0; i < con->numExecs; i++) {
+       if (con->execs[i].longName) xfree(con->execs[i].longName);
+       xfree(con->execs[i].script);
+    }
+    if (con->execs) xfree(con->execs);
+
+    free(con->leftovers);
+    free(con->finalArgv);
+    if (con->appName) xfree(con->appName);
+    if (con->aliases) free(con->aliases);
+    if (con->otherHelp) xfree(con->otherHelp);
+    if (con->execPath) xfree(con->execPath);
+    if (con->arg_strip) PBM_FREE(con->arg_strip);
+    
+    free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias,
+               /*@unused@*/ int flags)
+{
+    int aliasNum = con->numAliases++;
+    struct poptAlias * alias;
+
+    /* SunOS won't realloc(NULL, ...) */
+    if (!con->aliases)
+       con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+    else
+       con->aliases = realloc(con->aliases,
+                              sizeof(newAlias) * con->numAliases);
+    alias = con->aliases + aliasNum;
+
+    alias->longName = (newAlias.longName)
+       ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName)
+       : NULL;
+    alias->shortName = newAlias.shortName;
+    alias->argc = newAlias.argc;
+    alias->argv = newAlias.argv;
+
+    return 0;
+}
+
+const char * poptBadOption(poptContext con, int flags) {
+    struct optionStackEntry * os;
+
+    if (flags & POPT_BADOPTION_NOALIAS)
+       os = con->optionStack;
+    else
+       os = con->os;
+
+    return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+
+const char *poptStrerror(const int error) {
+    switch (error) {
+      case POPT_ERROR_NOARG:
+       return POPT_("missing argument");
+      case POPT_ERROR_BADOPT:
+       return POPT_("unknown option");
+      case POPT_ERROR_OPTSTOODEEP:
+       return POPT_("aliases nested too deeply");
+      case POPT_ERROR_BADQUOTE:
+       return POPT_("error in paramter quoting");
+      case POPT_ERROR_BADNUMBER:
+       return POPT_("invalid numeric value");
+      case POPT_ERROR_OVERFLOW:
+       return POPT_("number too large or too small");
+      case POPT_ERROR_ERRNO:
+       return strerror(errno);
+      default:
+       return POPT_("unknown error");
+    }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv) {
+    int argc;
+
+    if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    for (argc = 0; argv[argc]; argc++)
+       ;
+
+    con->os++;
+    con->os->next = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = NULL;
+    poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+    con->os->stuffed = 1;
+
+    return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+    return con->os->argv[0];
+}
+
+int poptStrippedArgv(poptContext con, int argc, char **argv)
+{
+    int i,j=1, numargs=argc;
+    
+    for(i=1; i<argc; i++) {
+       if(PBM_ISSET(i, con->arg_strip)) {
+           numargs--;
+       }
+    }
+    
+    for(i=1; i<argc; i++) {
+       if(PBM_ISSET(i, con->arg_strip)) {
+           continue;
+       } else {
+           if(j<numargs) {
+               argv[j++]=argv[i];
+           } else {
+               argv[j++]='\0';
+           }
+       }
+    }
+    
+    return(numargs);
+}
diff --git a/source4/lib/popt/popt.h b/source4/lib/popt/popt.h
new file mode 100644 (file)
index 0000000..c33ceda
--- /dev/null
@@ -0,0 +1,130 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>                     /* for FILE * */
+
+#define POPT_OPTION_DEPTH      10
+
+#define POPT_ARG_NONE          0
+#define POPT_ARG_STRING                1
+#define POPT_ARG_INT           2
+#define POPT_ARG_LONG          3
+#define POPT_ARG_INCLUDE_TABLE 4       /* arg points to table */
+#define POPT_ARG_CALLBACK      5       /* table-wide callback... must be
+                                          set first in table; arg points 
+                                          to callback, descrip points to 
+                                          callback data to pass */
+#define POPT_ARG_INTL_DOMAIN    6       /* set the translation domain
+                                          for this table and any
+                                          included tables; arg points
+                                          to the domain string */
+#define POPT_ARG_VAL           7       /* arg should take value val */
+#define POPT_ARG_MASK          0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH   0x80000000  /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000  /* don't show in help/usage */
+#define POPT_ARGFLAG_STRIP     0x20000000  /* strip this arg from argv (only applies to long args) */
+#define POPT_CBFLAG_PRE                0x80000000  /* call the callback before parse */
+#define POPT_CBFLAG_POST       0x40000000  /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA   0x20000000  /* use data from the include line,
+                                              not the subtable */
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER   -17
+#define POPT_ERROR_OVERFLOW    -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS  (1 << 0)  /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC   (1 << 0)  /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST        (1 << 1)  /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+    /*@observer@*/ /*@null@*/ const char * longName;   /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argInfo;
+    /*@shared@*/ /*@null@*/ void * arg;                /* depends on argInfo */
+    int val;                   /* 0 means don't return, just update flag */
+    /*@shared@*/ /*@null@*/ const char * descrip;      /* description for autohelp -- may be NULL */
+    /*@shared@*/ /*@null@*/ const char * argDescrip;   /* argument description for autohelp */
+};
+
+struct poptAlias {
+    /*@owned@*/ /*@null@*/ const char * longName;      /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argc;
+    /*@owned@*/ const char ** argv;            /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+                       0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE, 
+                         POPT_CALLBACK_REASON_POST,
+                         POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con, 
+                                enum poptCallbackReason reason,
+                                const struct poptOption * opt,
+                                const char * arg, const void * data);
+
+/*@only@*/ poptContext poptGetContext(/*@keep@*/ const char * name,
+               int argc, /*@keep@*/ const char ** argv,
+               /*@keep@*/ const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+/*@observer@*/ /*@null@*/ const char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+/*@observer@*/ /*@null@*/ const char * poptGetArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char * poptPeekArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+/*@observer@*/ const char * poptBadOption(poptContext con, int flags);
+void poptFreeContext( /*@only@*/ poptContext con);
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, const char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment 
+   vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+   the same as " and both may include \ quotes */
+int poptDupArgv(int argc, const char **argv,
+               /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+int poptParseArgvString(const char * s,
+               /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+/*@observer@*/ const char *poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+/*@observer@*/ const char * poptGetInvocationName(poptContext con);
+/* shuffles argv pointers to remove stripped args, returns new argc */
+int poptStrippedArgv(poptContext con, int argc, char **argv);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/source4/lib/popt/poptconfig.c b/source4/lib/popt/poptconfig.c
new file mode 100644 (file)
index 0000000..eb76941
--- /dev/null
@@ -0,0 +1,142 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+    int nameLength = strlen(con->appName);
+    char * opt;
+    struct poptAlias alias;
+    char * entryType;
+    char * longName = NULL;
+    char shortName = '\0';
+    
+    if (strncmp(line, con->appName, nameLength)) return;
+    line += nameLength;
+    if (!*line || !isspace(*line)) return;
+    while (*line && isspace(*line)) line++;
+    entryType = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+    opt = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+
+    if (opt[0] == '-' && opt[1] == '-')
+       longName = opt + 2;
+    else if (opt[0] == '-' && !opt[2])
+       shortName = opt[1];
+
+    if (!strcmp(entryType, "alias")) {
+       if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+       alias.longName = longName, alias.shortName = shortName;
+       poptAddAlias(con, alias, 0);
+    } else if (!strcmp(entryType, "exec")) {
+       con->execs = realloc(con->execs,
+                               sizeof(*con->execs) * (con->numExecs + 1));
+       if (longName)
+           con->execs[con->numExecs].longName = xstrdup(longName);
+       else
+           con->execs[con->numExecs].longName = NULL;
+
+       con->execs[con->numExecs].shortName = shortName;
+       con->execs[con->numExecs].script = xstrdup(line);
+       
+       con->numExecs++;
+    }
+}
+
+int poptReadConfigFile(poptContext con, const char * fn) {
+    char * file=NULL, * chptr, * end;
+    char * buf=NULL, * dst;
+    int fd, rc;
+    int fileLength;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+       if (errno == ENOENT)
+           return 0;
+       else 
+           return POPT_ERROR_ERRNO;
+    }
+
+    fileLength = lseek(fd, 0, SEEK_END);
+    (void) lseek(fd, 0, 0);
+
+    file = malloc(fileLength + 1);
+    if (read(fd, file, fileLength) != fileLength) {
+       rc = errno;
+       close(fd);
+       errno = rc;
+       if (file) free(file);
+       return POPT_ERROR_ERRNO;
+    }
+    close(fd);
+
+    dst = buf = malloc(fileLength + 1);
+
+    chptr = file;
+    end = (file + fileLength);
+    while (chptr < end) {
+       switch (*chptr) {
+         case '\n':
+           *dst = '\0';
+           dst = buf;
+           while (*dst && isspace(*dst)) dst++;
+           if (*dst && *dst != '#') {
+               configLine(con, dst);
+           }
+           chptr++;
+           break;
+         case '\\':
+           *dst++ = *chptr++;
+           if (chptr < end) {
+               if (*chptr == '\n') 
+                   dst--, chptr++;     
+                   /* \ at the end of a line does not insert a \n */
+               else
+                   *dst++ = *chptr++;
+           }
+           break;
+         default:
+           *dst++ = *chptr++;
+           break;
+       }
+    }
+
+    free(file);
+    free(buf);
+
+    return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) {
+    char * fn, * home;
+    int rc;
+
+    if (!con->appName) return 0;
+
+    rc = poptReadConfigFile(con, "/etc/popt");
+    if (rc) return rc;
+    if (getuid() != geteuid()) return 0;
+
+    if ((home = getenv("HOME"))) {
+       fn = malloc(strlen(home) + 20);
+       strcpy(fn, home);
+       strcat(fn, "/.popt");
+       rc = poptReadConfigFile(con, fn);
+       free(fn);
+       if (rc) return rc;
+    }
+
+    return 0;
+}
+
diff --git a/source4/lib/popt/popthelp.c b/source4/lib/popt/popthelp.c
new file mode 100644 (file)
index 0000000..6b790a6
--- /dev/null
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con,
+               /*@unused@*/ enum poptCallbackReason foo,
+               struct poptOption * key, 
+               /*@unused@*/ const char * arg, /*@unused@*/ void * data) {
+    if (key->shortName== '?')
+       poptPrintHelp(con, stdout, 0);
+    else
+       poptPrintUsage(con, stdout, 0);
+    exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+    { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+    { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+    { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+    { NULL, '\0', 0, NULL, 0, NULL, NULL }
+} ;
+
+
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(const struct poptOption *table)
+{
+  const struct poptOption *opt;
+
+  for(opt = table;
+      opt->longName || opt->shortName || opt->arg;
+      opt++) {
+    if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
+      return opt->arg;
+  }
+
+  return NULL;
+}
+
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt, const char *translation_domain)
+{
+    if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+    if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+       if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+    if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+    return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol, 
+                            const struct poptOption * opt,
+                            const char *translation_domain) {
+    int indentLength = maxLeftCol + 5;
+    int lineLength = 79 - indentLength;
+    const char * help = D_(translation_domain, opt->descrip);
+    int helpLength;
+    const char * ch;
+    char format[10];
+    char * left;
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+    left = malloc(maxLeftCol + 1);
+    *left = '\0';
+
+    if (opt->longName && opt->shortName)
+       sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+    else if (opt->shortName) 
+       sprintf(left, "-%c", opt->shortName);
+    else if (opt->longName)
+       sprintf(left, "--%s", opt->longName);
+    if (!*left) return ;
+    if (argDescrip) {
+       strcat(left, "=");
+       strcat(left, argDescrip);
+    }
+
+    if (help)
+       fprintf(f,"  %-*s   ", maxLeftCol, left);
+    else {
+       fprintf(f,"  %s\n", left); 
+       goto out;
+    }
+
+    helpLength = strlen(help);
+    while (helpLength > lineLength) {
+       ch = help + lineLength - 1;
+       while (ch > help && !isspace(*ch)) ch--;
+       if (ch == help) break;          /* give up */
+       while (ch > (help + 1) && isspace(*ch)) ch--;
+       ch++;
+
+       sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+       fprintf(f, format, help, " ");
+       help = ch;
+       while (isspace(*help) && *help) help++;
+       helpLength = strlen(help);
+    }
+
+    if (helpLength) fprintf(f, "%s\n", help);
+
+out:
+    free(left);
+}
+
+static int maxArgWidth(const struct poptOption * opt,
+                      const char * translation_domain) {
+    int max = 0;
+    int this;
+    const char * s;
+    
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           this = maxArgWidth(opt->arg, translation_domain);
+           if (this > max) max = this;
+       } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+           this = opt->shortName ? 2 : 0;
+           if (opt->longName) {
+               if (this) this += 2;
+               this += strlen(opt->longName) + 2;
+           }
+
+           s = getArgDescrip(opt, translation_domain);
+           if (s)
+               this += strlen(s) + 1;
+           if (this > max) max = this;
+       }
+
+       opt++;
+    }
+    
+    return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table, 
+                           int left,
+                           const char *translation_domain) {
+    const struct poptOption * opt;
+    const char *sub_transdom;
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->longName || opt->shortName) && 
+           !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+           singleOptionHelp(f, left, opt, translation_domain);
+       opt++;
+    }
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           sub_transdom = getTableTranslationDomain(opt->arg);
+           if(!sub_transdom)
+               sub_transdom = translation_domain;
+           
+           if (opt->descrip)
+               fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+           singleTableHelp(f, opt->arg, left, sub_transdom);
+       }
+       opt++;
+    }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+    int len = 6;
+    const char * fn;
+
+    fprintf(f, POPT_("Usage:"));
+    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+       fn = con->optionStack->argv[0];
+       if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+       fprintf(f, " %s", fn);
+       len += strlen(fn) + 1;
+    }
+
+    return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) {
+    int leftColWidth;
+
+    showHelpIntro(con, f);
+    if (con->otherHelp)
+       fprintf(f, " %s\n", con->otherHelp);
+    else
+       fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+    leftColWidth = maxArgWidth(con->options, NULL);
+    singleTableHelp(f, con->options, leftColWidth, NULL);
+}
+
+static int singleOptionUsage(FILE * f, int cursor, 
+                            const struct poptOption * opt,
+                            const char *translation_domain) {
+    int len = 3;
+    char shortStr[2] = { '\0', '\0' };
+    const char * item = shortStr;
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+    if (opt->shortName) {
+       if (!(opt->argInfo & POPT_ARG_MASK)) 
+           return cursor;      /* we did these already */
+       len++;
+       *shortStr = opt->shortName;
+       shortStr[1] = '\0';
+    } else if (opt->longName) {
+       len += 1 + strlen(opt->longName);
+       item = opt->longName;
+    }
+
+    if (len == 3) return cursor;
+
+    if (argDescrip) 
+       len += strlen(argDescrip) + 1;
+
+    if ((cursor + len) > 79) {
+       fprintf(f, "\n       ");
+       cursor = 7;
+    } 
+
+    fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+           argDescrip ? (opt->shortName ? " " : "=") : "",
+           argDescrip ? argDescrip : "");
+
+    return cursor + len + 1;
+}
+
+static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
+                    const char *translation_domain) {
+    const struct poptOption * opt;
+    
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
+           translation_domain = (const char *)opt->arg;
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) 
+           cursor = singleTableUsage(f, cursor, opt->arg,
+                                     translation_domain);
+       else if ((opt->longName || opt->shortName) && 
+                !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+         cursor = singleOptionUsage(f, cursor, opt, translation_domain);
+
+       opt++;
+    }
+
+    return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f, 
+                           char * str) {
+    char s[300];               /* this is larger then the ascii set, so
+                                  it should do just fine */
+
+    s[0] = '\0';
+    if (str == NULL) {
+       memset(s, 0, sizeof(s));
+       str = s;
+    }
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+           str[strlen(str)] = opt->shortName;
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+           showShortOptions(opt->arg, f, str);
+
+       opt++;
+    } 
+
+    if (s != str || !*s)
+       return 0;
+
+    fprintf(f, " [-%s]", s);
+    return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) {
+    int cursor;
+
+    cursor = showHelpIntro(con, f);
+    cursor += showShortOptions(con->options, f, NULL);
+    singleTableUsage(f, cursor, con->options, NULL);
+
+    if (con->otherHelp) {
+       cursor += strlen(con->otherHelp) + 1;
+       if (cursor > 79) fprintf(f, "\n       ");
+       fprintf(f, " %s", con->otherHelp);
+    }
+
+    fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+    if (con->otherHelp) xfree(con->otherHelp);
+    con->otherHelp = xstrdup(text);
+}
diff --git a/source4/lib/popt/poptint.h b/source4/lib/popt/poptint.h
new file mode 100644 (file)
index 0000000..1847ffa
--- /dev/null
@@ -0,0 +1,71 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/* Bit mask macros. */
+typedef        unsigned int __pbm_bits;
+#define        __PBM_NBITS             (8 * sizeof (__pbm_bits))
+#define        __PBM_IX(d)             ((d) / __PBM_NBITS)
+#define __PBM_MASK(d)          ((__pbm_bits) 1 << ((d) % __PBM_NBITS))
+typedef struct {
+    __pbm_bits bits[1];
+} pbm_set;
+#define        __PBM_BITS(set) ((set)->bits)
+
+#define        PBM_ALLOC(d)    calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define        PBM_FREE(s)     free(s);
+#define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+    int argc;
+    /*@only@*/ const char ** argv;
+    /*@only@*/ pbm_set * argb;
+    int next;
+    /*@only@*/ const char * nextArg;
+    /*@keep@*/ const char * nextCharArg;
+    /*@dependent@*/ struct poptAlias * currAlias;
+    int stuffed;
+};
+
+struct execEntry {
+    const char * longName;
+    char shortName;
+    const char * script;
+};
+
+struct poptContext_s {
+    struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+    /*@dependent@*/ struct optionStackEntry * os;
+    /*@owned@*/ const char ** leftovers;
+    int numLeftovers;
+    int nextLeftover;
+    /*@keep@*/ const struct poptOption * options;
+    int restLeftover;
+    /*@only@*/ const char * appName;
+    /*@only@*/ struct poptAlias * aliases;
+    int numAliases;
+    int flags;
+    struct execEntry * execs;
+    int numExecs;
+    /*@only@*/ const char ** finalArgv;
+    int finalArgvCount;
+    int finalArgvAlloced;
+    /*@dependent@*/ struct execEntry * doExec;
+    /*@only@*/ const char * execPath;
+    int execAbsolute;
+    /*@only@*/ const char * otherHelp;
+    pbm_set * arg_strip;
+};
+
+#define        xfree(_a)       free((void *)_a)
+
+#define POPT_(foo) (foo)
+#define D_(dom, str) (str)
+#define N_(foo) (foo)
+
+#endif
diff --git a/source4/lib/popt/poptparse.c b/source4/lib/popt/poptparse.c
new file mode 100644 (file)
index 0000000..8f00769
--- /dev/null
@@ -0,0 +1,102 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+int poptDupArgv(int argc, const char **argv,
+               int * argcPtr, const char *** argvPtr)
+{
+    size_t nb = (argc + 1) * sizeof(*argv);
+    const char ** argv2;
+    char * dst;
+    int i;
+
+    for (i = 0; i < argc; i++) {
+       if (argv[i] == NULL)
+           return POPT_ERROR_NOARG;
+       nb += strlen(argv[i]) + 1;
+    }
+       
+    dst = malloc(nb);
+    argv2 = (void *) dst;
+    dst += (argc + 1) * sizeof(*argv);
+
+    for (i = 0; i < argc; i++) {
+       argv2[i] = dst;
+       dst += strlen(strcpy(dst, argv[i])) + 1;
+    }
+    argv2[argc] = NULL;
+
+    *argvPtr = argv2;
+    *argcPtr = argc;
+    return 0;
+}
+
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+    const char * src;
+    char quote = '\0';
+    int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+    const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+    int argc = 0;
+    int buflen = strlen(s) + 1;
+    char *buf0 = calloc(buflen, 1);
+    char *buf = buf0;
+
+    argv[argc] = buf;
+
+    for (src = s; *src; src++) {
+       if (quote == *src) {
+           quote = '\0';
+       } else if (quote) {
+           if (*src == '\\') {
+               src++;
+               if (!*src) {
+                   free(argv);
+                   free(buf0);
+                   return POPT_ERROR_BADQUOTE;
+               }
+               if (*src != quote) *buf++ = '\\';
+           }
+           *buf++ = *src;
+       } else if (isspace(*src)) {
+           if (*argv[argc]) {
+               buf++, argc++;
+               if (argc == argvAlloced) {
+                   argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+                   argv = realloc(argv, sizeof(*argv) * argvAlloced);
+               }
+               argv[argc] = buf;
+           }
+       } else switch (*src) {
+         case '"':
+         case '\'':
+           quote = *src;
+           break;
+         case '\\':
+           src++;
+           if (!*src) {
+               free(argv);
+               free(buf0);
+               return POPT_ERROR_BADQUOTE;
+           }
+           /*@fallthrough@*/
+         default:
+           *buf++ = *src;
+           break;
+       }
+    }
+
+    if (strlen(argv[argc])) {
+       argc++, buf++;
+    }
+
+    (void) poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+    free(argv);
+    free(buf0);
+    return 0;
+}
diff --git a/source4/lib/popt/system.h b/source4/lib/popt/system.h
new file mode 100644 (file)
index 0000000..059c045
--- /dev/null
@@ -0,0 +1,53 @@
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H 
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+/* AIX requires this to be the first thing in the file.  */ 
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+#pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define alloca __builtin_alloca
+#endif
+
+/*@only@*/ char * xstrdup (const char *str);
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+#define        vmefail()       (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xstrdup(_str)   (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
+#else
+#define        xstrdup(_str)   strdup(_str)
+#endif  /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+
+#include "popt.h"
diff --git a/source4/lib/popt_common.c b/source4/lib/popt_common.c
new file mode 100644 (file)
index 0000000..ccdecc9
--- /dev/null
@@ -0,0 +1,327 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Common popt routines
+
+   Copyright (C) Tim Potter 2001,2002
+   Copyright (C) Jelmer Vernooij 2002,2003
+
+   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"
+
+/* Handle command line options:
+ *             -d,--debuglevel 
+ *             -s,--configfile 
+ *             -O,--socket-options 
+ *             -V,--version
+ *             -l,--log-base
+ *             -n,--netbios-name
+ *             -W,--workgroup
+ *             -i,--scope
+ */
+
+extern pstring user_socket_options;
+extern BOOL AllowDebugChange;
+
+struct user_auth_info cmdline_auth_info;
+
+static void popt_common_callback(poptContext con, 
+                          enum poptCallbackReason reason,
+                          const struct poptOption *opt,
+                          const char *arg, const void *data)
+{
+       pstring logfile;
+       const char *pname;
+       
+       /* Find out basename of current program */
+       pname = strrchr_m(poptGetInvocationName(con),'/');
+
+       if (!pname)
+               pname = poptGetInvocationName(con);
+       else 
+               pname++;
+
+       if (reason == POPT_CALLBACK_REASON_PRE) {
+               pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname);
+               lp_set_cmdline("log file", logfile);
+               return;
+       }
+
+       switch(opt->val) {
+       case 'd':
+               lp_set_cmdline("log level", arg);
+               break;
+
+       case 'V':
+               printf( "Version %s\n", SAMBA_VERSION );
+               exit(0);
+               break;
+
+       case 's':
+               if (arg) {
+                       pstrcpy(dyn_CONFIGFILE, arg);
+               }
+               break;
+
+       case 'l':
+               if (arg) {
+                       pstr_sprintf(logfile, "%s/log.%s", arg, pname);
+                       lp_set_cmdline("log file", logfile);
+               }
+               break;
+               
+       case 'W':
+               lp_set_cmdline("workgroup", arg);
+               break;
+               
+       case 'n':
+               lp_set_cmdline("netbios name", arg);
+               break;
+               
+       case 'i':
+               lp_set_cmdline("netbios scope", arg);
+               break;
+       }
+}
+
+struct poptOption popt_common_connection[] = {
+       { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+       { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
+         "SOCKETOPTIONS" },
+       { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
+       { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
+       { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
+       POPT_TABLEEND
+};
+
+struct poptOption popt_common_samba[] = {
+       { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback },
+       { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
+       { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
+       { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
+       { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+       POPT_TABLEEND
+};
+
+struct poptOption popt_common_version[] = {
+       { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+       { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+       POPT_TABLEEND
+};
+
+
+
+/****************************************************************************
+ * get a password from a a file or file descriptor
+ * exit on failure
+ * ****************************************************************************/
+static void get_password_file(struct user_auth_info *a)
+{
+       int fd = -1;
+       char *p;
+       BOOL close_it = False;
+       pstring spec;
+       char pass[128];
+
+       if ((p = getenv("PASSWD_FD")) != NULL) {
+               pstrcpy(spec, "descriptor ");
+               pstrcat(spec, p);
+               sscanf(p, "%d", &fd);
+               close_it = False;
+       } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+               fd = sys_open(p, O_RDONLY, 0);
+               pstrcpy(spec, p);
+               if (fd < 0) {
+                       fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+                                       spec, strerror(errno));
+                       exit(1);
+               }
+               close_it = True;
+       }
+
+       for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+               p && p - pass < sizeof(pass);) {
+               switch (read(fd, p, 1)) {
+               case 1:
+                       if (*p != '\n' && *p != '\0') {
+                               *++p = '\0'; /* advance p, and null-terminate pass */
+                               break;
+                       }
+               case 0:
+                       if (p - pass) {
+                               *p = '\0'; /* null-terminate it, just in case... */
+                               p = NULL; /* then force the loop condition to become false */
+                               break;
+                       } else {
+                               fprintf(stderr, "Error reading password from file %s: %s\n",
+                                               spec, "empty password\n");
+                               exit(1);
+                       }
+
+               default:
+                       fprintf(stderr, "Error reading password from file %s: %s\n",
+                                       spec, strerror(errno));
+                       exit(1);
+               }
+       }
+       pstrcpy(a->password, pass);
+       if (close_it)
+               close(fd);
+}
+
+static void get_credentials_file(const char *file, struct user_auth_info *info) 
+{
+       XFILE *auth;
+       fstring buf;
+       uint16 len = 0;
+       char *ptr, *val, *param;
+
+       if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
+       {
+               /* fail if we can't open the credentials file */
+               d_printf("ERROR: Unable to open credentials file!\n");
+               exit(-1);
+       }
+
+       while (!x_feof(auth))
+       {
+               /* get a line from the file */
+               if (!x_fgets(buf, sizeof(buf), auth))
+                       continue;
+               len = strlen(buf);
+
+               if ((len) && (buf[len-1]=='\n'))
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }
+               if (len == 0)
+                       continue;
+
+               /* break up the line into parameter & value.
+                * will need to eat a little whitespace possibly */
+               param = buf;
+               if (!(ptr = strchr_m (buf, '=')))
+                       continue;
+
+               val = ptr+1;
+               *ptr = '\0';
+
+               /* eat leading white space */
+               while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+                       val++;
+
+               if (strwicmp("password", param) == 0)
+               {
+                       pstrcpy(info->password, val);
+                       info->got_pass = True;
+               }
+               else if (strwicmp("username", param) == 0)
+                       pstrcpy(info->username, val);
+               //else if (strwicmp("domain", param) == 0)
+               //      set_global_myworkgroup(val);
+               memset(buf, 0, sizeof(buf));
+       }
+       x_fclose(auth);
+}
+
+/* Handle command line options:
+ *             -U,--user
+ *             -A,--authentication-file
+ *             -k,--use-kerberos
+ *             -N,--no-pass
+ */
+
+
+static void popt_common_credentials_callback(poptContext con, 
+                                                                                        enum poptCallbackReason reason,
+                                                                                        const struct poptOption *opt,
+                                                                                        const char *arg, const void *data)
+{
+       char *p;
+
+       if (reason == POPT_CALLBACK_REASON_PRE) {
+               cmdline_auth_info.use_kerberos = False;
+               cmdline_auth_info.got_pass = False;
+               pstrcpy(cmdline_auth_info.username, "GUEST");   
+
+               if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
+
+               if (getenv("USER")) {
+                       pstrcpy(cmdline_auth_info.username,getenv("USER"));
+
+                       if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
+                               *p = 0;
+                               pstrcpy(cmdline_auth_info.password,p+1);
+                               cmdline_auth_info.got_pass = True;
+                               memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
+                       }
+               }
+
+               if (getenv("PASSWD")) {
+                       pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
+                       cmdline_auth_info.got_pass = True;
+               }
+
+               if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+                       get_password_file(&cmdline_auth_info);
+                       cmdline_auth_info.got_pass = True;
+               }
+
+               return;
+       }
+
+       switch(opt->val) {
+       case 'U':
+               {
+                       char *lp;
+
+                       pstrcpy(cmdline_auth_info.username,arg);
+                       if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
+                               *lp = 0;
+                               pstrcpy(cmdline_auth_info.password,lp+1);
+                               cmdline_auth_info.got_pass = True;
+                               memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
+                       }
+               }
+               break;
+
+       case 'A':
+               get_credentials_file(arg, &cmdline_auth_info);
+               break;
+
+       case 'k':
+#ifndef HAVE_KRB5
+               d_printf("No kerberos support compiled in\n");
+               exit(1);
+#else
+               cmdline_auth_info.use_kerberos = True;
+               cmdline_auth_info.got_pass = True;
+#endif
+               break;
+       }
+}
+
+
+
+struct poptOption popt_common_credentials[] = {
+       { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback },
+       { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
+       { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, True, "Don't ask for a password" },
+       { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" },
+       { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
+       POPT_TABLEEND
+};
diff --git a/source4/lib/readline.c b/source4/lib/readline.c
new file mode 100644 (file)
index 0000000..c5da88b
--- /dev/null
@@ -0,0 +1,159 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba readline wrapper implementation
+   Copyright (C) Simo Sorce 2001
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+#ifdef HAVE_LIBREADLINE
+#  ifdef HAVE_READLINE_READLINE_H
+#    include <readline/readline.h>
+#    ifdef HAVE_READLINE_HISTORY_H
+#      include <readline/history.h>
+#    endif
+#  else
+#    ifdef HAVE_READLINE_H
+#      include <readline.h>
+#      ifdef HAVE_HISTORY_H
+#        include <history.h>
+#      endif
+#    else
+#      undef HAVE_LIBREADLINE
+#    endif
+#  endif
+#endif
+
+#ifdef HAVE_NEW_LIBREADLINE
+#  define RL_COMPLETION_CAST (rl_completion_func_t *)
+#else
+/* This type is missing from libreadline<4.0  (approximately) */
+#  define RL_COMPLETION_CAST
+#endif /* HAVE_NEW_LIBREADLINE */
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly
+****************************************************************************/
+
+static char *smb_readline_replacement(const char *prompt, void (*callback)(void), 
+                               char **(completion_fn)(const char *text, int start, int end))
+{
+       fd_set fds;
+       static pstring line;
+       struct timeval timeout;
+       int fd = x_fileno(x_stdin);
+       char *ret;
+
+       do_debug("%s", prompt);
+
+       while (1) {
+               timeout.tv_sec = 5;
+               timeout.tv_usec = 0;
+
+               FD_ZERO(&fds);
+               FD_SET(fd,&fds);
+       
+               if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) {
+                       ret = x_fgets(line, sizeof(line), x_stdin);
+                       return ret;
+               }
+               if (callback)
+                       callback();
+       }
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly.
+****************************************************************************/
+
+char *smb_readline(const char *prompt, void (*callback)(void), 
+                  char **(completion_fn)(const char *text, int start, int end))
+{
+#if HAVE_LIBREADLINE
+       if (isatty(x_fileno(x_stdin))) {
+               char *ret;
+
+               /* Aargh!  Readline does bizzare things with the terminal width
+               that mucks up expect(1).  Set CLI_NO_READLINE in the environment
+               to force readline not to be used. */
+
+               if (getenv("CLI_NO_READLINE"))
+                       return smb_readline_replacement(prompt, callback, completion_fn);
+
+               if (completion_fn) {
+                       /* The callback prototype has changed slightly between
+                       different versions of Readline, so the same function
+                       works in all of them to date, but we get compiler
+                       warnings in some.  */
+                       rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn;
+               }
+
+               if (callback)
+                       rl_event_hook = (Function *)callback;
+               ret = readline(prompt);
+               if (ret && *ret)
+                       add_history(ret);
+               return ret;
+       } else
+#endif
+       return smb_readline_replacement(prompt, callback, completion_fn);
+}
+
+/****************************************************************************
+ * return line buffer text
+ ****************************************************************************/
+const char *smb_readline_get_line_buffer(void)
+{
+#if defined(HAVE_LIBREADLINE)
+       return rl_line_buffer;
+#else
+       return NULL;
+#endif
+}
+
+/****************************************************************************
+ * set completion append character
+ ***************************************************************************/
+void smb_readline_ca_char(char c)
+{
+#if defined(HAVE_LIBREADLINE)
+       rl_completion_append_character = c;
+#endif
+}
+
+
+/****************************************************************************
+history
+****************************************************************************/
+int cmd_history(void)
+{
+#if defined(HAVE_LIBREADLINE)
+       HIST_ENTRY **hlist;
+       int i;
+
+       hlist = history_list();
+       
+       for (i = 0; hlist && hlist[i]; i++) {
+               DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+       }
+#else
+       DEBUG(0,("no history without readline support\n"));
+#endif
+
+       return 0;
+}
diff --git a/source4/lib/replace.c b/source4/lib/replace.c
new file mode 100644 (file)
index 0000000..cda379c
--- /dev/null
@@ -0,0 +1,467 @@
+/* 
+   Unix SMB/CIFS implementation.
+   replacement routines for broken systems
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+ void replace_dummy(void);
+ void replace_dummy(void) {}
+
+#ifndef HAVE_FTRUNCATE
+ /*******************************************************************
+ftruncate for operating systems that don't have it
+********************************************************************/
+ int ftruncate(int f,SMB_OFF_T l)
+{
+      struct  flock   fl;
+
+      fl.l_whence = 0;
+      fl.l_len = 0;
+      fl.l_start = l;
+      fl.l_type = F_WRLCK;
+      return fcntl(f, F_FREESP, &fl);
+}
+#endif /* HAVE_FTRUNCATE */
+
+
+#ifndef HAVE_STRLCPY
+/* like strncpy but does not 0 fill the buffer and always null 
+   terminates. bufsize is the size of the destination buffer */
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+       size_t len = strlen(s);
+       size_t ret = len;
+       if (bufsize <= 0) return 0;
+       if (len >= bufsize) len = bufsize-1;
+       memcpy(d, s, len);
+       d[len] = 0;
+       return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/* like strncat but does not 0 fill the buffer and always null 
+   terminates. bufsize is the length of the buffer, which should
+   be one more than the maximum resulting string length */
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+       size_t len1 = strlen(d);
+       size_t len2 = strlen(s);
+       size_t ret = len1 + len2;
+
+       if (len1+len2 >= bufsize) {
+               len2 = bufsize - (len1+1);
+       }
+       if (len2 > 0) {
+               memcpy(d+len1, s, len2);
+               d[len1+len2] = 0;
+       }
+       return ret;
+}
+#endif
+
+#ifndef HAVE_MKTIME
+/*******************************************************************
+a mktime() replacement for those who don't have it - contributed by 
+C.A. Lademann <cal@zls.com>
+Corrections by richard.kettlewell@kewill.com
+********************************************************************/
+
+#define  MINUTE  60
+#define  HOUR    60*MINUTE
+#define  DAY             24*HOUR
+#define  YEAR    365*DAY
+ time_t mktime(struct tm *t)
+{
+  struct tm       *u;
+  time_t  epoch = 0;
+  int n;
+  int             mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+  y, m, i;
+
+  if(t->tm_year < 70)
+    return((time_t)-1);
+
+  n = t->tm_year + 1900 - 1;
+  epoch = (t->tm_year - 70) * YEAR + 
+    ((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY;
+
+  y = t->tm_year + 1900;
+  m = 0;
+
+  for(i = 0; i < t->tm_mon; i++) {
+    epoch += mon [m] * DAY;
+    if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
+      epoch += DAY;
+    
+    if(++m > 11) {
+      m = 0;
+      y++;
+    }
+  }
+
+  epoch += (t->tm_mday - 1) * DAY;
+  epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec;
+  
+  if((u = localtime(&epoch)) != NULL) {
+    t->tm_sec = u->tm_sec;
+    t->tm_min = u->tm_min;
+    t->tm_hour = u->tm_hour;
+    t->tm_mday = u->tm_mday;
+    t->tm_mon = u->tm_mon;
+    t->tm_year = u->tm_year;
+    t->tm_wday = u->tm_wday;
+    t->tm_yday = u->tm_yday;
+    t->tm_isdst = u->tm_isdst;
+  }
+
+  return(epoch);
+}
+#endif /* !HAVE_MKTIME */
+
+
+
+#ifndef HAVE_RENAME
+/* Rename a file. (from libiberty in GNU binutils)  */
+ int rename(const char *zfrom, const char *zto)
+{
+  if (link (zfrom, zto) < 0)
+    {
+      if (errno != EEXIST)
+       return -1;
+      if (unlink (zto) < 0
+         || link (zfrom, zto) < 0)
+       return -1;
+    }
+  return unlink (zfrom);
+}
+#endif /* HAVE_RENAME */
+
+
+#ifndef HAVE_INNETGR
+#if defined(HAVE_SETNETGRENT) && defined(HAVE_GETNETGRENT) && defined(HAVE_ENDNETGRENT)
+/*
+ * Search for a match in a netgroup. This replaces it on broken systems.
+ */
+ int innetgr(const char *group,const char *host,const char *user,const char *dom)
+{
+       char *hst, *usr, *dm;
+  
+       setnetgrent(group);
+       while (getnetgrent(&hst, &usr, &dm)) {
+               if (((host == 0) || (hst == 0) || !strcmp(host, hst)) &&
+                   ((user == 0) || (usr == 0) || !strcmp(user, usr)) &&
+                   ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) {
+                       endnetgrent();
+                       return (1);
+               }
+       }
+       endnetgrent();
+       return (0);
+}
+#endif /* HAVE_SETNETGRENT HAVE_GETNETGRENT HAVE_ENDNETGRENT */
+#endif /* HAVE_INNETGR */
+
+
+
+#ifndef HAVE_INITGROUPS
+/****************************************************************************
+ some systems don't have an initgroups call 
+****************************************************************************/
+ int initgroups(char *name,gid_t id)
+{
+#ifndef HAVE_SETGROUPS
+       static int done;
+       if (!done) {
+               DEBUG(1,("WARNING: running without setgroups\n"));
+               done=1;
+       }
+       /* yikes! no SETGROUPS or INITGROUPS? how can this work? */
+       return(0);
+#else /* HAVE_SETGROUPS */
+       gid_t *grouplst = NULL;
+       int max_gr = groups_max();
+       int ret;
+       int    i,j;
+       struct group *g;
+       char   *gr;
+       
+       if((grouplst = (gid_t *)malloc(sizeof(gid_t) * max_gr)) == NULL) {
+               DEBUG(0,("initgroups: malloc fail !\n"));
+               return -1;
+       }
+
+       grouplst[0] = id;
+       i = 1;
+       while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) {
+               if (g->gr_gid == id)
+                       continue;
+               j = 0;
+               gr = g->gr_mem[0];
+               while (gr && (*gr != (char)NULL)) {
+                       if (strcmp(name,gr) == 0) {
+                               grouplst[i] = g->gr_gid;
+                               i++;
+                               gr = (char *)NULL;
+                               break;
+                       }
+                       gr = g->gr_mem[++j];
+               }
+       }
+       endgrent();
+       ret = sys_setgroups(i,grouplst);
+       SAFE_FREE(grouplst);
+       return ret;
+#endif /* HAVE_SETGROUPS */
+}
+#endif /* HAVE_INITGROUPS */
+
+
+#if (defined(SecureWare) && defined(SCO))
+/* This is needed due to needing the nap() function but we don't want
+   to include the Xenix libraries since that will break other things...
+   BTW: system call # 0x0c28 is the same as calling nap() */
+ long nap(long milliseconds) {
+        return syscall(0x0c28, milliseconds);
+ }
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+/*******************************************************************
+safely copies memory, ensuring no overlap problems.
+this is only used if the machine does not have it's own memmove().
+this is not the fastest algorithm in town, but it will do for our
+needs.
+********************************************************************/
+ void *memmove(void *dest,const void *src,int size)
+{
+       unsigned long d,s;
+       int i;
+       if (dest==src || !size) return(dest);
+
+       d = (unsigned long)dest;
+       s = (unsigned long)src;
+
+       if ((d >= (s+size)) || (s >= (d+size))) {
+               /* no overlap */
+               memcpy(dest,src,size);
+               return(dest);
+       }
+
+       if (d < s) {
+               /* we can forward copy */
+               if (s-d >= sizeof(int) && 
+                   !(s%sizeof(int)) && 
+                   !(d%sizeof(int)) && 
+                   !(size%sizeof(int))) {
+                       /* do it all as words */
+                       int *idest = (int *)dest;
+                       int *isrc = (int *)src;
+                       size /= sizeof(int);
+                       for (i=0;i<size;i++) idest[i] = isrc[i];
+               } else {
+                       /* simplest */
+                       char *cdest = (char *)dest;
+                       char *csrc = (char *)src;
+                       for (i=0;i<size;i++) cdest[i] = csrc[i];
+               }
+       } else {
+               /* must backward copy */
+               if (d-s >= sizeof(int) && 
+                   !(s%sizeof(int)) && 
+                   !(d%sizeof(int)) && 
+                   !(size%sizeof(int))) {
+                       /* do it all as words */
+                       int *idest = (int *)dest;
+                       int *isrc = (int *)src;
+                       size /= sizeof(int);
+                       for (i=size-1;i>=0;i--) idest[i] = isrc[i];
+               } else {
+                       /* simplest */
+                       char *cdest = (char *)dest;
+                       char *csrc = (char *)src;
+                       for (i=size-1;i>=0;i--) cdest[i] = csrc[i];
+               }      
+       }
+       return(dest);
+}
+#endif /* HAVE_MEMMOVE */
+
+#ifndef HAVE_STRDUP
+/****************************************************************************
+duplicate a string
+****************************************************************************/
+ char *strdup(const char *s)
+{
+       size_t len;
+       char *ret;
+
+       if (!s) return(NULL);
+
+       len = strlen(s)+1;
+       ret = (char *)malloc(len);
+       if (!ret) return(NULL);
+       memcpy(ret,s,len);
+       return(ret);
+}
+#endif /* HAVE_STRDUP */
+#if 0  /* REWRITE: not thread safe */
+#ifdef REPLACE_INET_NTOA
+char *rep_inet_ntoa(struct in_addr ip)
+{
+       unsigned char *p = (unsigned char *)&ip.s_addr;
+       static char buf[18];
+       slprintf(buf, 17, "%d.%d.%d.%d", 
+                (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
+       return buf;
+}
+#endif /* REPLACE_INET_NTOA */
+#endif
+#ifndef HAVE_STRTOUL
+#ifndef ULONG_MAX
+#define        ULONG_MAX       ((unsigned long)(~0L))          /* 0xFFFFFFFF */
+#endif
+
+/*
+ * Convert a string to an unsigned long integer.
+ * Taken from libg++ - libiberty code.
+ *
+ * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+       const char *s = nptr;
+       unsigned long acc;
+       int c;
+       unsigned long cutoff;
+       int neg = 0, any, cutlim;
+
+       /*
+        * See strtol for comments as to the logic used.
+        */
+       do {
+               c = *s++;
+       } while (isspace(c));
+       if (c == '-') {
+               neg = 1;
+               c = *s++;
+       } else if (c == '+')
+               c = *s++;
+       if ((base == 0 || base == 16) &&
+           c == '0' && (*s == 'x' || *s == 'X')) {
+               c = s[1];
+               s += 2;
+               base = 16;
+       }
+       if (base == 0)
+               base = c == '0' ? 8 : 10;
+       cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+       cutlim = (int)((unsigned long)ULONG_MAX % (unsigned long)base);
+       for (acc = 0, any = 0;; c = *s++) {
+               if (isdigit(c))
+                       c -= '0';
+               else if (isalpha(c))
+                       c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+               else
+                       break;
+               if (c >= base)
+                       break;
+               if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+                       any = -1;
+               else {
+                       any = 1;
+                       acc *= base;
+                       acc += c;
+               }
+       }
+       if (any < 0) {
+               acc = ULONG_MAX;
+               errno = ERANGE;
+       } else if (neg)
+               acc = -acc;
+       if (endptr != 0)
+               *endptr = (char *) (any ? s - 1 : nptr);
+       return (acc);
+}
+#endif /* HAVE_STRTOUL */
+
+#ifndef HAVE_SETLINEBUF
+ int setlinebuf(FILE *stream)
+{
+       return setvbuf(stream, (char *)NULL, _IOLBF, 0);
+}
+#endif /* HAVE_SETLINEBUF */
+
+#ifndef HAVE_VSYSLOG
+#ifdef HAVE_SYSLOG
+ void vsyslog (int facility_priority, char *format, va_list arglist)
+{
+       char *msg = NULL;
+       vasprintf(&msg, format, arglist);
+       if (!msg)
+               return;
+       syslog(facility_priority, "%s", msg);
+       SAFE_FREE(msg);
+}
+#endif /* HAVE_SYSLOG */
+#endif /* HAVE_VSYSLOG */
+
+
+#ifndef HAVE_TIMEGM
+/*
+  yes, I know this looks insane, but its really needed. The function in the 
+  Linux timegm() manpage does not work on solaris.
+*/
+ time_t timegm(struct tm *tm) 
+{
+       struct tm tm2, tm3;
+       time_t t;
+
+       tm2 = *tm;
+
+       t = mktime(&tm2);
+       tm3 = *localtime(&t);
+       tm2 = *tm;
+       tm2.tm_isdst = tm3.tm_isdst;
+       t = mktime(&tm2);
+       t -= TimeDiff(t);
+
+       return t;
+}
+#endif
+
+#ifndef HAVE_SETENV
+ int setenv(const char *name, const char *value, int overwrite) 
+{
+       char *p = NULL;
+       int ret = -1;
+
+       asprintf(&p, "%s=%s", name, value);
+
+       if (overwrite || getenv(name)) {
+               if (p) ret = putenv(p);
+       } else {
+               ret = 0;
+       }
+
+       return ret;     
+}
+#endif
diff --git a/source4/lib/select.c b/source4/lib/select.c
new file mode 100644 (file)
index 0000000..5d7e4a0
--- /dev/null
@@ -0,0 +1,159 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   Samba select/poll implementation
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+/* This is here because it allows us to avoid a nasty race in signal handling. 
+   We need to guarantee that when we get a signal we get out of a select immediately
+   but doing that involves a race condition. We can avoid the race by getting the 
+   signal handler to write to a pipe that is in the select/poll list 
+
+   This means all Samba signal handlers should call sys_select_signal().
+*/
+
+static pid_t initialised;
+static int select_pipe[2];
+static VOLATILE unsigned pipe_written, pipe_read;
+
+/*******************************************************************
+ Call this from all Samba signal handlers if you want to avoid a 
+ nasty signal race condition.
+********************************************************************/
+
+void sys_select_signal(void)
+{
+       char c = 1;
+       if (!initialised) return;
+
+       if (pipe_written > pipe_read+256) return;
+
+       if (write(select_pipe[1], &c, 1) == 1) pipe_written++;
+}
+
+/*******************************************************************
+ Like select() but avoids the signal race using a pipe
+ it also guuarantees that fds on return only ever contains bits set
+ for file descriptors that were readable.
+********************************************************************/
+
+int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+       int ret, saved_errno;
+       fd_set *readfds2, readfds_buf;
+
+       if (initialised != getpid()) {
+               pipe(select_pipe);
+
+               /*
+                * These next two lines seem to fix a bug with the Linux
+                * 2.0.x kernel (and probably other UNIXes as well) where
+                * the one byte read below can block even though the
+                * select returned that there is data in the pipe and
+                * the pipe_written variable was incremented. Thanks to
+                * HP for finding this one. JRA.
+                */
+
+               if(set_blocking(select_pipe[0],0)==-1)
+                       smb_panic("select_pipe[0]: O_NONBLOCK failed.\n");
+               if(set_blocking(select_pipe[1],0)==-1)
+                       smb_panic("select_pipe[1]: O_NONBLOCK failed.\n");
+
+               initialised = getpid();
+       }
+
+       maxfd = MAX(select_pipe[0]+1, maxfd);
+
+       /* If readfds is NULL we need to provide our own set. */
+       if (readfds) {
+               readfds2 = readfds;
+       } else {
+               readfds2 = &readfds_buf;
+               FD_ZERO(readfds2);
+       }
+       FD_SET(select_pipe[0], readfds2);
+
+       errno = 0;
+       ret = select(maxfd,readfds2,writefds,errorfds,tval);
+
+       if (ret <= 0) {
+               FD_ZERO(readfds2);
+               if (writefds)
+                       FD_ZERO(writefds);
+               if (errorfds)
+                       FD_ZERO(errorfds);
+       }
+
+       if (FD_ISSET(select_pipe[0], readfds2)) {
+               char c;
+               saved_errno = errno;
+               if (read(select_pipe[0], &c, 1) == 1) {
+                       pipe_read++;
+               }
+               errno = saved_errno;
+               FD_CLR(select_pipe[0], readfds2);
+               ret--;
+               if (ret == 0) {
+                       ret = -1;
+                       errno = EINTR;
+               }
+       }
+
+       return ret;
+}
+
+/*******************************************************************
+ Similar to sys_select() but catch EINTR and continue.
+ This is what sys_select() used to do in Samba.
+********************************************************************/
+
+int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+       int ret;
+       fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf;
+       struct timeval tval2, *ptval;
+
+       readfds2 = (readfds ? &readfds_buf : NULL);
+       writefds2 = (writefds ? &writefds_buf : NULL);
+       errorfds2 = (errorfds ? &errorfds_buf : NULL);
+       ptval = (tval ? &tval2 : NULL);
+
+       do {
+               if (readfds)
+                       readfds_buf = *readfds;
+               if (writefds)
+                       writefds_buf = *writefds;
+               if (errorfds)
+                       errorfds_buf = *errorfds;
+               if (tval)
+                       tval2 = *tval;
+
+               ret = sys_select(maxfd, readfds2, writefds2, errorfds2, ptval);
+       } while (ret == -1 && errno == EINTR);
+
+       if (readfds)
+               *readfds = readfds_buf;
+       if (writefds)
+               *writefds = writefds_buf;
+       if (errorfds)
+               *errorfds = errorfds_buf;
+
+       return ret;
+}
diff --git a/source4/lib/sendfile.c b/source4/lib/sendfile.c
new file mode 100644 (file)
index 0000000..bcc8cb0
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 2.2.x / 3.0.x
+ sendfile implementations.
+ Copyright (C) Jeremy Allison 2002.
+
+ 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.
+*/
+
+/*
+ * This file handles the OS dependent sendfile implementations.
+ * The API is such that it returns -1 on error, else returns the
+ * number of bytes written.
+ */
+
+#include "includes.h"
+
+#if defined(LINUX_SENDFILE_API)
+
+#include <sys/sendfile.h>
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       size_t total=0;
+       ssize_t ret;
+       size_t hdr_len = 0;
+
+       /*
+        * Send the header first.
+        * Use MSG_MORE to cork the TCP output until sendfile is called.
+        */
+
+       if (header) {
+               hdr_len = header->length;
+               while (total < hdr_len) {
+                       ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+                       if (ret == -1)
+                               return -1;
+                       total += ret;
+               }
+       }
+
+       total = count;
+       while (total) {
+               ssize_t nwritten;
+               do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+                       nwritten = sendfile64(tofd, fromfd, &offset, total);
+#else
+                       nwritten = sendfile(tofd, fromfd, &offset, total);
+#endif
+               } while (nwritten == -1 && errno == EINTR);
+               if (nwritten == -1)
+                       return -1;
+               if (nwritten == 0)
+                       return -1; /* I think we're at EOF here... */
+               total -= nwritten;
+       }
+       return count + hdr_len;
+}
+
+#elif defined(LINUX_BROKEN_SENDFILE_API)
+
+/*
+ * We must use explicit 32 bit types here. This code path means Linux
+ * won't do proper 64-bit sendfile. JRA.
+ */
+
+extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
+
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       size_t total=0;
+       ssize_t ret;
+       ssize_t hdr_len = 0;
+       uint32 small_total = 0;
+       int32 small_offset;
+
+       /* 
+        * Fix for broken Linux 2.4 systems with no working sendfile64().
+        * If the offset+count > 2 GB then pretend we don't have the
+        * system call sendfile at all. The upper layer catches this
+        * and uses a normal read. JRA.
+        */
+
+       if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
+               errno = ENOSYS;
+               return -1;
+       }
+
+       /*
+        * Send the header first.
+        * Use MSG_MORE to cork the TCP output until sendfile is called.
+        */
+
+       if (header) {
+               hdr_len = header->length;
+               while (total < hdr_len) {
+                       ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+                       if (ret == -1)
+                               return -1;
+                       total += ret;
+               }
+       }
+
+       small_total = (uint32)count;
+       small_offset = (int32)offset;
+
+       while (small_total) {
+               int32 nwritten;
+               do {
+                       nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
+               } while (nwritten == -1 && errno == EINTR);
+               if (nwritten == -1)
+                       return -1;
+               if (nwritten == 0)
+                       return -1; /* I think we're at EOF here... */
+               small_total -= nwritten;
+       }
+       return count + hdr_len;
+}
+
+
+#elif defined(SOLARIS_SENDFILE_API)
+
+/*
+ * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
+ */
+
+#include <sys/sendfile.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       int sfvcnt;
+       size_t total, xferred;
+       struct sendfilevec vec[2];
+       ssize_t hdr_len = 0;
+
+       if (header) {
+               sfvcnt = 2;
+
+               vec[0].sfv_fd = SFV_FD_SELF;
+               vec[0].sfv_flag = 0;
+               vec[0].sfv_off = header->data;
+               vec[0].sfv_len = hdr_len = header->length;
+
+               vec[1].sfv_fd = fromfd;
+               vec[1].sfv_flag = 0;
+               vec[1].sfv_off = offset;
+               vec[1].sfv_len = count;
+
+       } else {
+               sfvcnt = 1;
+
+               vec[0].sfv_fd = fromfd;
+               vec[0].sfv_flag = 0;
+               vec[0].sfv_off = offset;
+               vec[0].sfv_len = count;
+       }
+
+       total = count + hdr_len;
+
+       while (total) {
+               ssize_t nwritten;
+
+               /*
+                * Although not listed in the API error returns, this is almost certainly
+                * a slow system call and will be interrupted by a signal with EINTR. JRA.
+                */
+
+               xferred = 0;
+
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
+                       nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+#else
+                       nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+#endif
+               if (nwritten == -1 && errno == EINTR) {
+                       if (xferred == 0)
+                               continue; /* Nothing written yet. */
+                       else
+                               nwritten = xferred;
+               }
+
+               if (nwritten == -1)
+                       return -1;
+               if (nwritten == 0)
+                       return -1; /* I think we're at EOF here... */
+
+               /*
+                * If this was a short (signal interrupted) write we may need
+                * to subtract it from the header data, or null out the header
+                * data altogether if we wrote more than vec[0].sfv_len bytes.
+                * We move vec[1].* to vec[0].* and set sfvcnt to 1
+                */
+
+               if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
+                       vec[1].sfv_off += nwritten - vec[0].sfv_len;
+                       vec[1].sfv_len -= nwritten - vec[0].sfv_len;
+
+                       /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
+                       vec[0] = vec[1];
+                       sfvcnt = 1;
+               } else {
+                       vec[0].sfv_off += nwritten;
+                       vec[0].sfv_len -= nwritten;
+               }
+               total -= nwritten;
+       }
+       return count + hdr_len;
+}
+
+#elif defined(HPUX_SENDFILE_API)
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       size_t total=0;
+       struct iovec hdtrl[2];
+       size_t hdr_len = 0;
+
+       if (header) {
+               /* Set up the header/trailer iovec. */
+               hdtrl[0].iov_base = header->data;
+               hdtrl[0].iov_len = hdr_len = header->length;
+       } else {
+               hdtrl[0].iov_base = NULL;
+               hdtrl[0].iov_len = hdr_len = 0;
+       }
+       hdtrl[1].iov_base = NULL;
+       hdtrl[1].iov_base = 0;
+
+       total = count;
+       while (total + hdtrl[0].iov_len) {
+               ssize_t nwritten;
+
+               /*
+                * HPUX guarantees that if any data was written before
+                * a signal interrupt then sendfile returns the number of
+                * bytes written (which may be less than requested) not -1.
+                * nwritten includes the header data sent.
+                */
+
+               do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+                       nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#else
+                       nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#endif
+               } while (nwritten == -1 && errno == EINTR);
+               if (nwritten == -1)
+                       return -1;
+               if (nwritten == 0)
+                       return -1; /* I think we're at EOF here... */
+
+               /*
+                * If this was a short (signal interrupted) write we may need
+                * to subtract it from the header data, or null out the header
+                * data altogether if we wrote more than hdtrl[0].iov_len bytes.
+                * We change nwritten to be the number of file bytes written.
+                */
+
+               if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
+                       if (nwritten >= hdtrl[0].iov_len) {
+                               nwritten -= hdtrl[0].iov_len;
+                               hdtrl[0].iov_base = NULL;
+                               hdtrl[0].iov_len = 0;
+                       } else {
+                               /* iov_base is defined as a void *... */
+                               hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
+                               hdtrl[0].iov_len -= nwritten;
+                               nwritten = 0;
+                       }
+               }
+               total -= nwritten;
+               offset += nwritten;
+       }
+       return count + hdr_len;
+}
+
+#elif defined(FREEBSD_SENDFILE_API)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       size_t total=0;
+       struct sf_hdtr hdr;
+       struct iovec hdtrl;
+       size_t hdr_len = 0;
+
+       hdr.headers = &hdtrl;
+       hdr.hdr_cnt = 1;
+       hdr.trailers = NULL;
+       hdr.trl_cnt = 0;
+
+       /* Set up the header iovec. */
+       if (header) {
+               hdtrl.iov_base = header->data;
+               hdtrl.iov_len = hdr_len = header->length;
+       } else {
+               hdtrl.iov_base = NULL;
+               hdtrl.iov_len = 0;
+       }
+
+       total = count;
+       while (total + hdtrl.iov_len) {
+               SMB_OFF_T nwritten;
+               int ret;
+
+               /*
+                * FreeBSD sendfile returns 0 on success, -1 on error.
+                * Remember, the tofd and fromfd are reversed..... :-).
+                * nwritten includes the header data sent.
+                */
+
+               do {
+                       ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+               } while (ret == -1 && errno == EINTR);
+               if (ret == -1)
+                       return -1;
+
+               if (nwritten == 0)
+                       return -1; /* I think we're at EOF here... */
+
+               /*
+                * If this was a short (signal interrupted) write we may need
+                * to subtract it from the header data, or null out the header
+                * data altogether if we wrote more than hdtrl.iov_len bytes.
+                * We change nwritten to be the number of file bytes written.
+                */
+
+               if (hdtrl.iov_base && hdtrl.iov_len) {
+                       if (nwritten >= hdtrl.iov_len) {
+                               nwritten -= hdtrl.iov_len;
+                               hdtrl.iov_base = NULL;
+                               hdtrl.iov_len = 0;
+                       } else {
+                               hdtrl.iov_base += nwritten;
+                               hdtrl.iov_len -= nwritten;
+                               nwritten = 0;
+                       }
+               }
+               total -= nwritten;
+               offset += nwritten;
+       }
+       return count + hdr_len;
+}
+
+#else /* No sendfile implementation. Return error. */
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+       /* No sendfile syscall. */
+       errno = ENOSYS;
+       return -1;
+}
+#endif
diff --git a/source4/lib/server_mutex.c b/source4/lib/server_mutex.c
new file mode 100644 (file)
index 0000000..878e549
--- /dev/null
@@ -0,0 +1,58 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Authenticate against a remote domain
+   Copyright (C) Andrew Tridgell 1992-2002
+   Copyright (C) Andrew Bartlett 2002
+   
+   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"
+
+/* For reasons known only to MS, many of their NT/Win2k versions
+   need serialised access only.  Two connections at the same time
+   may (in certain situations) cause connections to be reset,
+   or access to be denied.
+
+   This locking allows smbd's mutlithread architecture to look
+   like the single-connection that NT makes. */
+
+static char *mutex_server_name;
+/* FIXME. ref_count should be allocated per name... JRA. */
+size_t ref_count;
+
+BOOL grab_server_mutex(const char *name)
+{
+       mutex_server_name = strdup(name);
+       if (!mutex_server_name) {
+               DEBUG(0,("grab_server_mutex: malloc failed for %s\n", name));
+               return False;
+       }
+       if (!secrets_named_mutex(mutex_server_name, 10, &ref_count)) {
+               DEBUG(10,("grab_server_mutex: failed for %s\n", name));
+               SAFE_FREE(mutex_server_name);
+               return False;
+       }
+
+       return True;
+}
+
+void release_server_mutex(void)
+{
+       if (mutex_server_name) {
+               secrets_named_mutex_release(mutex_server_name, &ref_count);
+               SAFE_FREE(mutex_server_name);
+       }
+}
diff --git a/source4/lib/signal.c b/source4/lib/signal.c
new file mode 100644 (file)
index 0000000..bff4b91
--- /dev/null
@@ -0,0 +1,139 @@
+/* 
+   Unix SMB/CIFS implementation.
+   signal handling functions
+
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+/****************************************************************************
+ Catch child exits and reap the child zombie status.
+****************************************************************************/
+
+static void sig_cld(int signum)
+{
+       while (sys_waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0)
+               ;
+
+       /*
+        * Turns out it's *really* important not to
+        * restore the signal handler here if we have real POSIX
+        * signal handling. If we do, then we get the signal re-delivered
+        * immediately - hey presto - instant loop ! JRA.
+        */
+
+#if !defined(HAVE_SIGACTION)
+       CatchSignal(SIGCLD, sig_cld);
+#endif
+}
+
+/****************************************************************************
+catch child exits - leave status;
+****************************************************************************/
+
+static void sig_cld_leave_status(int signum)
+{
+       /*
+        * Turns out it's *really* important not to
+        * restore the signal handler here if we have real POSIX
+        * signal handling. If we do, then we get the signal re-delivered
+        * immediately - hey presto - instant loop ! JRA.
+        */
+
+#if !defined(HAVE_SIGACTION)
+       CatchSignal(SIGCLD, sig_cld_leave_status);
+#endif
+}
+
+/*******************************************************************
+ Block sigs.
+********************************************************************/
+
+void BlockSignals(BOOL block,int signum)
+{
+#ifdef HAVE_SIGPROCMASK
+       sigset_t set;
+       sigemptyset(&set);
+       sigaddset(&set,signum);
+       sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL);
+#elif defined(HAVE_SIGBLOCK)
+       if (block) {
+               sigblock(sigmask(signum));
+       } else {
+               sigsetmask(siggetmask() & ~sigmask(signum));
+       }
+#else
+       /* yikes! This platform can't block signals? */
+       static int done;
+       if (!done) {
+               DEBUG(0,("WARNING: No signal blocking available\n"));
+               done=1;
+       }
+#endif
+}
+
+/*******************************************************************
+ Catch a signal. This should implement the following semantics:
+
+ 1) The handler remains installed after being called.
+ 2) The signal should be blocked during handler execution.
+********************************************************************/
+
+void (*CatchSignal(int signum,void (*handler)(int )))(int)
+{
+#ifdef HAVE_SIGACTION
+       struct sigaction act;
+       struct sigaction oldact;
+
+       ZERO_STRUCT(act);
+
+       act.sa_handler = handler;
+#ifdef SA_RESTART
+       /*
+        * We *want* SIGALRM to interrupt a system call.
+        */
+       if(signum != SIGALRM)
+               act.sa_flags = SA_RESTART;
+#endif
+       sigemptyset(&act.sa_mask);
+       sigaddset(&act.sa_mask,signum);
+       sigaction(signum,&act,&oldact);
+       return oldact.sa_handler;
+#else /* !HAVE_SIGACTION */
+       /* FIXME: need to handle sigvec and systems with broken signal() */
+       return signal(signum, handler);
+#endif
+}
+
+/*******************************************************************
+ Ignore SIGCLD via whatever means is necessary for this OS.
+********************************************************************/
+
+void CatchChild(void)
+{
+       CatchSignal(SIGCLD, sig_cld);
+}
+
+/*******************************************************************
+ Catch SIGCLD but leave the child around so it's status can be reaped.
+********************************************************************/
+
+void CatchChildLeaveStatus(void)
+{
+       CatchSignal(SIGCLD, sig_cld_leave_status);
+}
diff --git a/source4/lib/smbpasswd.c b/source4/lib/smbpasswd.c
new file mode 100644 (file)
index 0000000..92ae1ff
--- /dev/null
@@ -0,0 +1,200 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   smbpasswd file format routines
+
+   Copyright (C) Andrew Tridgell 1992-1998 
+   Modified by Jeremy Allison 1995.
+   Modified by Gerald (Jerry) Carter 2000-2001
+   Copyright (C) Tim Potter 2001
+   
+   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.
+*/
+
+/*! \file lib/smbpasswd.c
+
+   The smbpasswd file is used to store encrypted passwords in a similar
+   fashion to the /etc/passwd file.  The format is colon separated fields
+   with one user per line like so:
+
+   <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time>
+
+   The username and uid must correspond to an entry in the /etc/passwd
+   file.  The lanman and nt password hashes are 32 hex digits corresponding
+   to the 16-byte lanman and nt hashes respectively.  
+
+   The password last change time is stored as a string of the format
+   LCD-<change time> where the change time is expressed as an 
+
+   'N'    No password
+   'D'    Disabled
+   'H'    Homedir required
+   'T'    Temp account.
+   'U'    User account (normal) 
+   'M'    MNS logon user account - what is this ? 
+   'W'    Workstation account
+   'S'    Server account 
+   'L'    Locked account
+   'X'    No Xpiry on password 
+   'I'    Interdomain trust account
+
+*/
+
+#include "includes.h"
+
+/*! Convert 32 hex characters into a 16 byte array. */
+
+BOOL smbpasswd_gethexpwd(char *p, unsigned char *pwd)
+{
+       int i;
+       unsigned char   lonybble, hinybble;
+       const char      *hexchars = "0123456789ABCDEF";
+       char           *p1, *p2;
+       
+       if (!p) return (False);
+       
+       for (i = 0; i < 32; i += 2)
+       {
+               hinybble = toupper(p[i]);
+               lonybble = toupper(p[i + 1]);
+
+               p1 = strchr_m(hexchars, hinybble);
+               p2 = strchr_m(hexchars, lonybble);
+
+               if (!p1 || !p2)
+               {
+                       return (False);
+               }
+
+               hinybble = PTR_DIFF(p1, hexchars);
+               lonybble = PTR_DIFF(p2, hexchars);
+
+               pwd[i / 2] = (hinybble << 4) | lonybble;
+       }
+       return (True);
+}
+
+/*! Convert a 16-byte array into 32 hex characters. */
+
+void smbpasswd_sethexpwd(fstring p, unsigned char *pwd, uint16 acb_info)
+{
+       if (pwd != NULL) {
+               int i;
+               for (i = 0; i < 16; i++)
+                       slprintf(&p[i*2], 3, "%02X", pwd[i]);
+       } else {
+               if (acb_info & ACB_PWNOTREQ)
+                       safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+               else
+                       safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+       }
+}
+
+/*! Decode the account control bits (ACB) info from a string. */
+
+uint16 smbpasswd_decode_acb_info(const char *p)
+{
+       uint16 acb_info = 0;
+       BOOL finished = False;
+
+       /*
+        * Check if the account type bits have been encoded after the
+        * NT password (in the form [NDHTUWSLXI]).
+        */
+
+       if (*p != '[') return 0;
+
+       for (p++; *p && !finished; p++)
+       {
+               switch (*p) {
+                case 'N': /* 'N'o password. */
+                        acb_info |= ACB_PWNOTREQ; 
+                        break;
+                case 'D': /* 'D'isabled. */
+                        acb_info |= ACB_DISABLED; 
+                        break; 
+                case 'H': /* 'H'omedir required. */
+                        acb_info |= ACB_HOMDIRREQ; 
+                        break;
+                case 'T': /* 'T'emp account. */
+                        acb_info |= ACB_TEMPDUP; 
+                        break;
+                case 'U': /* 'U'ser account (normal). */
+                        acb_info |= ACB_NORMAL;
+                        break;
+                case 'M': /* 'M'NS logon user account. What is this ? */
+                        acb_info |= ACB_MNS; 
+                        break; 
+                case 'W': /* 'W'orkstation account. */
+                        acb_info |= ACB_WSTRUST; 
+                        break; 
+                case 'S': /* 'S'erver account. */ 
+                        acb_info |= ACB_SVRTRUST; 
+                        break; 
+                case 'L': /* 'L'ocked account. */
+                        acb_info |= ACB_AUTOLOCK; 
+                        break; 
+                case 'X': /* No 'X'piry on password */
+                        acb_info |= ACB_PWNOEXP; 
+                        break; 
+                case 'I': /* 'I'nterdomain trust account. */
+                        acb_info |= ACB_DOMTRUST; 
+                        break; 
+
+                case ' ': 
+                        break;
+                case ':':
+                case '\n':
+                case '\0': 
+                case ']':
+                default:  
+                        finished = True;
+                        break;
+               }
+       }
+
+       return acb_info;
+}
+
+/*! Encode account control bits (ACBs) into a string. */
+
+char *smbpasswd_encode_acb_info(uint16 acb_info)
+{
+       static fstring acct_str;
+       size_t i = 0;
+
+       acct_str[i++] = '[';
+
+       if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+       if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D';
+       if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+       if (acb_info & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
+       if (acb_info & ACB_NORMAL   ) acct_str[i++] = 'U';
+       if (acb_info & ACB_MNS      ) acct_str[i++] = 'M';
+       if (acb_info & ACB_WSTRUST  ) acct_str[i++] = 'W';
+       if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S';
+       if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+       if (acb_info & ACB_PWNOEXP  ) acct_str[i++] = 'X';
+       if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+       for ( ; i < NEW_PW_FORMAT_SPACE_PADDED_LEN - 2 ; i++ )
+                acct_str[i] = ' ';
+
+       i = NEW_PW_FORMAT_SPACE_PADDED_LEN - 2;
+       acct_str[i++] = ']';
+       acct_str[i++] = '\0';
+
+       return acct_str;
+}     
diff --git a/source4/lib/smbrun.c b/source4/lib/smbrun.c
new file mode 100644 (file)
index 0000000..acb836b
--- /dev/null
@@ -0,0 +1,171 @@
+/* 
+   Unix SMB/CIFS implementation.
+   run a command as a specified user
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+/* need to move this from here!! need some sleep ... */
+struct current_user current_user;
+
+/****************************************************************************
+This is a utility function of smbrun().
+****************************************************************************/
+
+static int setup_out_fd(void)
+{  
+       int fd;
+       pstring path;
+
+       slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir());
+
+       /* now create the file */
+       fd = smb_mkstemp(path);
+
+       if (fd == -1) {
+               DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n",
+                       path, strerror(errno) ));
+               return -1;
+       }
+
+       DEBUG(10,("setup_out_fd: Created tmp file %s\n", path ));
+
+       /* Ensure file only kept around by open fd. */
+       unlink(path);
+       return fd;
+}
+
+/****************************************************************************
+run a command being careful about uid/gid handling and putting the output in
+outfd (or discard it if outfd is NULL).
+****************************************************************************/
+
+int smbrun(char *cmd, int *outfd)
+{
+       pid_t pid;
+       uid_t uid = current_user.uid;
+       gid_t gid = current_user.gid;
+       
+       /*
+        * Lose any kernel oplock capabilities we may have.
+        */
+       oplock_set_capability(False, False);
+
+       /* point our stdout at the file we want output to go into */
+
+       if (outfd && ((*outfd = setup_out_fd()) == -1)) {
+               return -1;
+       }
+
+       /* in this method we will exec /bin/sh with the correct
+          arguments, after first setting stdout to point at the file */
+
+       /*
+        * We need to temporarily stop CatchChild from eating
+        * SIGCLD signals as it also eats the exit status code. JRA.
+        */
+
+       CatchChildLeaveStatus();
+                                       
+       if ((pid=fork()) < 0) {
+               DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
+               CatchChild(); 
+               if (outfd) {
+                       close(*outfd);
+                       *outfd = -1;
+               }
+               return errno;
+    }
+
+       if (pid) {
+               /*
+                * Parent.
+                */
+               int status=0;
+               pid_t wpid;
+
+               
+               /* the parent just waits for the child to exit */
+               while((wpid = sys_waitpid(pid,&status,0)) < 0) {
+                       if(errno == EINTR) {
+                               errno = 0;
+                               continue;
+                       }
+                       break;
+               }
+
+               CatchChild(); 
+
+               if (wpid != pid) {
+                       DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno)));
+                       if (outfd) {
+                               close(*outfd);
+                               *outfd = -1;
+                       }
+                       return -1;
+               }
+
+               /* Reset the seek pointer. */
+               if (outfd) {
+                       sys_lseek(*outfd, 0, SEEK_SET);
+               }
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+               if (WIFEXITED(status)) {
+                       return WEXITSTATUS(status);
+               }
+#endif
+
+               return status;
+       }
+       
+       CatchChild(); 
+       
+       /* we are in the child. we exec /bin/sh to do the work for us. we
+          don't directly exec the command we want because it may be a
+          pipeline or anything else the config file specifies */
+       
+       /* point our stdout at the file we want output to go into */
+       if (outfd) {
+               close(1);
+               if (sys_dup2(*outfd,1) != 1) {
+                       DEBUG(2,("Failed to create stdout file descriptor\n"));
+                       close(*outfd);
+                       exit(80);
+               }
+       }
+
+       /* now completely lose our privileges. This is a fairly paranoid
+          way of doing it, but it does work on all systems that I know of */
+
+       become_user_permanently(uid, gid);
+
+       if (getuid() != uid || geteuid() != uid ||
+           getgid() != gid || getegid() != gid) {
+               /* we failed to lose our privileges - do not execute
+                   the command */
+               exit(81); /* we can't print stuff at this stage,
+                            instead use exit codes for debugging */
+       }
+       
+       execl("/bin/sh","sh","-c",cmd,NULL);  
+       
+       /* not reached */
+       exit(82);
+       return 1;
+}
diff --git a/source4/lib/snprintf.c b/source4/lib/snprintf.c
new file mode 100644 (file)
index 0000000..1eae2f0
--- /dev/null
@@ -0,0 +1,978 @@
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh.  This sort of thing is always nasty do deal with.  Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length.  This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ *  This was ugly.  It is still ugly.  I opted out of floating point
+ *  numbers, but the formatter understands just about everything
+ *  from the normal C string format, at least as far as I can tell from
+ *  the Solaris 2.5 printf(3S) man page.
+ *
+ *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ *    Ok, added some minimal floating point support, which means this
+ *    probably requires libm on most operating systems.  Don't yet
+ *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
+ *    was pretty badly broken, it just wasn't being exercised in ways
+ *    which showed it, so that's been fixed.  Also, formated the code
+ *    to mutt conventions, and removed dead code left over from the
+ *    original.  Also, there is now a builtin-test, just compile with:
+ *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ *    and run snprintf for results.
+ * 
+ *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ *    The PGP code was using unsigned hexadecimal formats. 
+ *    Unfortunately, unsigned formats simply didn't work.
+ *
+ *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ *    The original code assumed that both snprintf() and vsnprintf() were
+ *    missing.  Some systems only have snprintf() but not vsnprintf(), so
+ *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ *  Andrew Tridgell (tridge@samba.org) Oct 1998
+ *    fixed handling of %.0f
+ *    added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ *    got rid of fcvt code (twas buggy and made testing harder)
+ *    added C99 semantics
+ *
+ **************************************************************/
+
+#ifndef NO_CONFIG_H /* for some tests */
+#include "config.h"
+#else
+#define NULL 0
+#endif
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+#include <math.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include <stdio.h>
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void) {} 
+#else
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#ifndef VA_COPY
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format, 
+                  va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+                   char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+                   long value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+                  LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS   1
+#define DP_S_MIN     2
+#define DP_S_DOT     3
+#define DP_S_MAX     4
+#define DP_S_MOD     5
+#define DP_S_CONV    6
+#define DP_S_DONE    7
+
+/* format flags - Bits */
+#define DP_F_MINUS     (1 << 0)
+#define DP_F_PLUS      (1 << 1)
+#define DP_F_SPACE     (1 << 2)
+#define DP_F_NUM       (1 << 3)
+#define DP_F_ZERO      (1 << 4)
+#define DP_F_UP        (1 << 5)
+#define DP_F_UNSIGNED  (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT   1
+#define DP_C_LONG    2
+#define DP_C_LDOUBLE 3
+#define DP_C_LLONG   4
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+       char ch;
+       LLONG value;
+       LDOUBLE fvalue;
+       char *strvalue;
+       int min;
+       int max;
+       int state;
+       int flags;
+       int cflags;
+       size_t currlen;
+       va_list args;
+
+       VA_COPY(args, args_in);
+       
+       state = DP_S_DEFAULT;
+       currlen = flags = cflags = min = 0;
+       max = -1;
+       ch = *format++;
+       
+       while (state != DP_S_DONE) {
+               if (ch == '\0') 
+                       state = DP_S_DONE;
+
+               switch(state) {
+               case DP_S_DEFAULT:
+                       if (ch == '%') 
+                               state = DP_S_FLAGS;
+                       else 
+                               dopr_outch (buffer, &currlen, maxlen, ch);
+                       ch = *format++;
+                       break;
+               case DP_S_FLAGS:
+                       switch (ch) {
+                       case '-':
+                               flags |= DP_F_MINUS;
+                               ch = *format++;
+                               break;
+                       case '+':
+                               flags |= DP_F_PLUS;
+                               ch = *format++;
+                               break;
+                       case ' ':
+                               flags |= DP_F_SPACE;
+                               ch = *format++;
+                               break;
+                       case '#':
+                               flags |= DP_F_NUM;
+                               ch = *format++;
+                               break;
+                       case '0':
+                               flags |= DP_F_ZERO;
+                               ch = *format++;
+                               break;
+                       default:
+                               state = DP_S_MIN;
+                               break;
+                       }
+                       break;
+               case DP_S_MIN:
+                       if (isdigit((unsigned char)ch)) {
+                               min = 10*min + char_to_int (ch);
+                               ch = *format++;
+                       } else if (ch == '*') {
+                               min = va_arg (args, int);
+                               ch = *format++;
+                               state = DP_S_DOT;
+                       } else {
+                               state = DP_S_DOT;
+                       }
+                       break;
+               case DP_S_DOT:
+                       if (ch == '.') {
+                               state = DP_S_MAX;
+                               ch = *format++;
+                       } else { 
+                               state = DP_S_MOD;
+                       }
+                       break;
+               case DP_S_MAX:
+                       if (isdigit((unsigned char)ch)) {
+                               if (max < 0)
+                                       max = 0;
+                               max = 10*max + char_to_int (ch);
+                               ch = *format++;
+                       } else if (ch == '*') {
+                               max = va_arg (args, int);
+                               ch = *format++;
+                               state = DP_S_MOD;
+                       } else {
+                               state = DP_S_MOD;
+                       }
+                       break;
+               case DP_S_MOD:
+                       switch (ch) {
+                       case 'h':
+                               cflags = DP_C_SHORT;
+                               ch = *format++;
+                               break;
+                       case 'l':
+                               cflags = DP_C_LONG;
+                               ch = *format++;
+                               if (ch == 'l') {        /* It's a long long */
+                                       cflags = DP_C_LLONG;
+                                       ch = *format++;
+                               }
+                               break;
+                       case 'L':
+                               cflags = DP_C_LDOUBLE;
+                               ch = *format++;
+                               break;
+                       default:
+                               break;
+                       }
+                       state = DP_S_CONV;
+                       break;
+               case DP_S_CONV:
+                       switch (ch) {
+                       case 'd':
+                       case 'i':
+                               if (cflags == DP_C_SHORT) 
+                                       value = va_arg (args, int);
+                               else if (cflags == DP_C_LONG)
+                                       value = va_arg (args, long int);
+                               else if (cflags == DP_C_LLONG)
+                                       value = va_arg (args, LLONG);
+                               else
+                                       value = va_arg (args, int);
+                               fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+                               break;
+                       case 'o':
+                               flags |= DP_F_UNSIGNED;
+                               if (cflags == DP_C_SHORT)
+                                       value = va_arg (args, unsigned int);
+                               else if (cflags == DP_C_LONG)
+                                       value = (long)va_arg (args, unsigned long int);
+                               else if (cflags == DP_C_LLONG)
+                                       value = (long)va_arg (args, unsigned LLONG);
+                               else
+                                       value = (long)va_arg (args, unsigned int);
+                               fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+                               break;
+                       case 'u':
+                               flags |= DP_F_UNSIGNED;
+                               if (cflags == DP_C_SHORT)
+                                       value = va_arg (args, unsigned int);
+                               else if (cflags == DP_C_LONG)
+                                       value = (long)va_arg (args, unsigned long int);
+                               else if (cflags == DP_C_LLONG)
+                                       value = (LLONG)va_arg (args, unsigned LLONG);
+                               else
+                                       value = (long)va_arg (args, unsigned int);
+                               fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+                               break;
+                       case 'X':
+                               flags |= DP_F_UP;
+                       case 'x':
+                               flags |= DP_F_UNSIGNED;
+                               if (cflags == DP_C_SHORT)
+                                       value = va_arg (args, unsigned int);
+                               else if (cflags == DP_C_LONG)
+                                       value = (long)va_arg (args, unsigned long int);
+                               else if (cflags == DP_C_LLONG)
+                                       value = (LLONG)va_arg (args, unsigned LLONG);
+                               else
+                                       value = (long)va_arg (args, unsigned int);
+                               fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+                               break;
+                       case 'f':
+                               if (cflags == DP_C_LDOUBLE)
+                                       fvalue = va_arg (args, LDOUBLE);
+                               else
+                                       fvalue = va_arg (args, double);
+                               /* um, floating point? */
+                               fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+                               break;
+                       case 'E':
+                               flags |= DP_F_UP;
+                       case 'e':
+                               if (cflags == DP_C_LDOUBLE)
+                                       fvalue = va_arg (args, LDOUBLE);
+                               else
+                                       fvalue = va_arg (args, double);
+                               fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+                               break;
+                       case 'G':
+                               flags |= DP_F_UP;
+                       case 'g':
+                               if (cflags == DP_C_LDOUBLE)
+                                       fvalue = va_arg (args, LDOUBLE);
+                               else
+                                       fvalue = va_arg (args, double);
+                               fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+                               break;
+                       case 'c':
+                               dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+                               break;
+                       case 's':
+                               strvalue = va_arg (args, char *);
+                               if (!strvalue) strvalue = "(NULL)";
+                               if (max == -1) {
+                                       max = strlen(strvalue);
+                               }
+                               if (min > 0 && max >= 0 && min > max) max = min;
+                               fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+                               break;
+                       case 'p':
+                               strvalue = va_arg (args, void *);
+                               fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+                               break;
+                       case 'n':
+                               if (cflags == DP_C_SHORT) {
+                                       short int *num;
+                                       num = va_arg (args, short int *);
+                                       *num = currlen;
+                               } else if (cflags == DP_C_LONG) {
+                                       long int *num;
+                                       num = va_arg (args, long int *);
+                                       *num = (long int)currlen;
+                               } else if (cflags == DP_C_LLONG) {
+                                       LLONG *num;
+                                       num = va_arg (args, LLONG *);
+                                       *num = (LLONG)currlen;
+                               } else {
+                                       int *num;
+                                       num = va_arg (args, int *);
+                                       *num = currlen;
+                               }
+                               break;
+                       case '%':
+                               dopr_outch (buffer, &currlen, maxlen, ch);
+                               break;
+                       case 'w':
+                               /* not supported yet, treat as next char */
+                               ch = *format++;
+                               break;
+                       default:
+                               /* Unknown, skip */
+                               break;
+                       }
+                       ch = *format++;
+                       state = DP_S_DEFAULT;
+                       flags = cflags = min = 0;
+                       max = -1;
+                       break;
+               case DP_S_DONE:
+                       break;
+               default:
+                       /* hmm? */
+                       break; /* some picky compilers need this */
+               }
+       }
+       if (maxlen != 0) {
+               if (currlen < maxlen - 1) 
+                       buffer[currlen] = '\0';
+               else if (maxlen > 0) 
+                       buffer[maxlen - 1] = '\0';
+       }
+       
+       return currlen;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+                   char *value, int flags, int min, int max)
+{
+       int padlen, strln;     /* amount to pad */
+       int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+       printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+       if (value == 0) {
+               value = "<NULL>";
+       }
+
+       for (strln = 0; value[strln]; ++strln); /* strlen */
+       padlen = min - strln;
+       if (padlen < 0) 
+               padlen = 0;
+       if (flags & DP_F_MINUS) 
+               padlen = -padlen; /* Left Justify */
+       
+       while ((padlen > 0) && (cnt < max)) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               --padlen;
+               ++cnt;
+       }
+       while (*value && (cnt < max)) {
+               dopr_outch (buffer, currlen, maxlen, *value++);
+               ++cnt;
+       }
+       while ((padlen < 0) && (cnt < max)) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               ++padlen;
+               ++cnt;
+       }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+                   long value, int base, int min, int max, int flags)
+{
+       int signvalue = 0;
+       unsigned long uvalue;
+       char convert[20];
+       int place = 0;
+       int spadlen = 0; /* amount to space pad */
+       int zpadlen = 0; /* amount to zero pad */
+       int caps = 0;
+       
+       if (max < 0)
+               max = 0;
+       
+       uvalue = value;
+       
+       if(!(flags & DP_F_UNSIGNED)) {
+               if( value < 0 ) {
+                       signvalue = '-';
+                       uvalue = -value;
+               } else {
+                       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+                               signvalue = '+';
+                       else if (flags & DP_F_SPACE)
+                               signvalue = ' ';
+               }
+       }
+  
+       if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+       do {
+               convert[place++] =
+                       (caps? "0123456789ABCDEF":"0123456789abcdef")
+                       [uvalue % (unsigned)base  ];
+               uvalue = (uvalue / (unsigned)base );
+       } while(uvalue && (place < 20));
+       if (place == 20) place--;
+       convert[place] = 0;
+
+       zpadlen = max - place;
+       spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+       if (zpadlen < 0) zpadlen = 0;
+       if (spadlen < 0) spadlen = 0;
+       if (flags & DP_F_ZERO) {
+               zpadlen = MAX(zpadlen, spadlen);
+               spadlen = 0;
+       }
+       if (flags & DP_F_MINUS) 
+               spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+       printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+              zpadlen, spadlen, min, max, place);
+#endif
+
+       /* Spaces */
+       while (spadlen > 0) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               --spadlen;
+       }
+
+       /* Sign */
+       if (signvalue) 
+               dopr_outch (buffer, currlen, maxlen, signvalue);
+
+       /* Zeros */
+       if (zpadlen > 0) {
+               while (zpadlen > 0) {
+                       dopr_outch (buffer, currlen, maxlen, '0');
+                       --zpadlen;
+               }
+       }
+
+       /* Digits */
+       while (place > 0) 
+               dopr_outch (buffer, currlen, maxlen, convert[--place]);
+  
+       /* Left Justified spaces */
+       while (spadlen < 0) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               ++spadlen;
+       }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+       LDOUBLE result = value;
+
+       if (value < 0)
+               result = -value;
+       
+       return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+       LDOUBLE result = 1;
+       
+       while (exp) {
+               result *= 10;
+               exp--;
+       }
+  
+       return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+       LLONG intpart;
+
+       intpart = (LLONG)value;
+       value = value - intpart;
+       if (value >= 0.5) intpart++;
+       
+       return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+   be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+       int i;
+       long l;
+       double x = x0;
+       double f = 1.0;
+
+       for (i=0;i<100;i++) {
+               l = (long)x;
+               if (l <= (x+1) && l >= (x-1)) break;
+               x *= 0.1;
+               f *= 10.0;
+       }
+
+       if (i == 100) {
+               /* yikes! the number is beyond what we can handle. What do we do? */
+               (*iptr) = 0;
+               return 0;
+       }
+
+       if (i != 0) {
+               double i2;
+               double ret;
+
+               ret = my_modf(x0-l*f, &i2);
+               (*iptr) = l*f + i2;
+               return ret;
+       } 
+
+       (*iptr) = l;
+       return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+                  LDOUBLE fvalue, int min, int max, int flags)
+{
+       int signvalue = 0;
+       double ufvalue;
+       char iconvert[311];
+       char fconvert[311];
+       int iplace = 0;
+       int fplace = 0;
+       int padlen = 0; /* amount to pad */
+       int zpadlen = 0; 
+       int caps = 0;
+       int index;
+       double intpart;
+       double fracpart;
+       double temp;
+  
+       /* 
+        * AIX manpage says the default is 0, but Solaris says the default
+        * is 6, and sprintf on AIX defaults to 6
+        */
+       if (max < 0)
+               max = 6;
+
+       ufvalue = abs_val (fvalue);
+
+       if (fvalue < 0) {
+               signvalue = '-';
+       } else {
+               if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+                       signvalue = '+';
+               } else {
+                       if (flags & DP_F_SPACE)
+                               signvalue = ' ';
+               }
+       }
+
+#if 0
+       if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+        if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+       /* 
+        * Sorry, we only support 16 digits past the decimal because of our 
+        * conversion method
+        */
+       if (max > 16)
+               max = 16;
+
+       /* We "cheat" by converting the fractional part to integer by
+        * multiplying by a factor of 10
+        */
+
+       temp = ufvalue;
+       my_modf(temp, &intpart);
+
+       fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+       
+       if (fracpart >= POW10(max)) {
+               intpart++;
+               fracpart -= POW10(max);
+       }
+
+
+       /* Convert integer part */
+       do {
+               temp = intpart*0.1;
+               my_modf(temp, &intpart);
+               index = (int) ((temp -intpart +0.05)* 10.0);
+               /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+               /* printf ("%llf, %f, %x\n", temp, intpart, index); */
+               iconvert[iplace++] =
+                       (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
+       } while (intpart && (iplace < 311));
+       if (iplace == 311) iplace--;
+       iconvert[iplace] = 0;
+
+       /* Convert fractional part */
+       if (fracpart)
+       {
+               do {
+                       temp = fracpart*0.1;
+                       my_modf(temp, &fracpart);
+                       index = (int) ((temp -fracpart +0.05)* 10.0);
+                       /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
+                       /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
+                       fconvert[fplace++] =
+                       (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
+               } while(fracpart && (fplace < 311));
+               if (fplace == 311) fplace--;
+       }
+       fconvert[fplace] = 0;
+  
+       /* -1 for decimal point, another -1 if we are printing a sign */
+       padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
+       zpadlen = max - fplace;
+       if (zpadlen < 0) zpadlen = 0;
+       if (padlen < 0) 
+               padlen = 0;
+       if (flags & DP_F_MINUS) 
+               padlen = -padlen; /* Left Justifty */
+       
+       if ((flags & DP_F_ZERO) && (padlen > 0)) {
+               if (signvalue) {
+                       dopr_outch (buffer, currlen, maxlen, signvalue);
+                       --padlen;
+                       signvalue = 0;
+               }
+               while (padlen > 0) {
+                       dopr_outch (buffer, currlen, maxlen, '0');
+                       --padlen;
+               }
+       }
+       while (padlen > 0) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               --padlen;
+       }
+       if (signvalue) 
+               dopr_outch (buffer, currlen, maxlen, signvalue);
+       
+       while (iplace > 0) 
+               dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+       printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+       /*
+        * Decimal point.  This should probably use locale to find the correct
+        * char to print out.
+        */
+       if (max > 0) {
+               dopr_outch (buffer, currlen, maxlen, '.');
+               
+               while (zpadlen > 0) {
+                       dopr_outch (buffer, currlen, maxlen, '0');
+                       --zpadlen;
+               }
+
+               while (fplace > 0) 
+                       dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+       }
+
+       while (padlen < 0) {
+               dopr_outch (buffer, currlen, maxlen, ' ');
+               ++padlen;
+       }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+       if (*currlen < maxlen) {
+               buffer[(*currlen)] = c;
+       }
+       (*currlen)++;
+}
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+ int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+       return dopr(str, count, fmt, args);
+}
+#endif
+
+/* yes this really must be a ||. Don't muck wiith this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.  Perhaps
+ * these should really be smb_snprintf to avoid conflicts with buggy
+ * linkers? -- mbp
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_SNPRINTF)
+ int snprintf(char *str,size_t count,const char *fmt,...)
+{
+       size_t ret;
+       va_list ap;
+    
+       va_start(ap, fmt);
+       ret = vsnprintf(str, count, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+#endif
+
+#endif 
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+       int ret;
+       va_list ap2;
+
+       VA_COPY(ap2, ap);
+       
+       ret = vsnprintf(NULL, 0, format, ap2);
+       if (ret <= 0) return ret;
+
+       (*ptr) = (char *)malloc(ret+1);
+       if (!*ptr) return -1;
+
+       VA_COPY(ap2, ap);
+
+       ret = vsnprintf(*ptr, ret+1, format, ap2);
+
+       return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+       
+       *ptr = NULL;
+       va_start(ap, format);
+       ret = vasprintf(ptr, format, ap);
+       va_end(ap);
+
+       return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+
+ int main (void)
+{
+       char buf1[1024];
+       char buf2[1024];
+       char *fp_fmt[] = {
+               "%1.1f",
+               "%-1.5f",
+               "%1.5f",
+               "%123.9f",
+               "%10.5f",
+               "% 10.5f",
+               "%+22.9f",
+               "%+4.9f",
+               "%01.3f",
+               "%4f",
+               "%3.1f",
+               "%3.2f",
+               "%.0f",
+               "%f",
+               "-16.16f",
+               NULL
+       };
+       double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
+                            0.9996, 1.996, 4.136, 5.030201, 0};
+       char *int_fmt[] = {
+               "%-1.5d",
+               "%1.5d",
+               "%123.9d",
+               "%5.5d",
+               "%10.5d",
+               "% 10.5d",
+               "%+22.33d",
+               "%01.3d",
+               "%4d",
+               "%d",
+               NULL
+       };
+       long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+       char *str_fmt[] = {
+               "10.5s",
+               "5.10s",
+               "10.1s",
+               "0.10s",
+               "10.0s",
+               "1.10s",
+               "%s",
+               "%.1s",
+               "%.10s",
+               "%10s",
+               NULL
+       };
+       char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+       int x, y;
+       int fail = 0;
+       int num = 0;
+
+       printf ("Testing snprintf format codes against system sprintf...\n");
+
+       for (x = 0; fp_fmt[x] ; x++) {
+               for (y = 0; fp_nums[y] != 0 ; y++) {
+                       int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
+                       int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+                       sprintf (buf2, fp_fmt[x], fp_nums[y]);
+                       if (strcmp (buf1, buf2)) {
+                               printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
+                                      fp_fmt[x], buf1, buf2);
+                               fail++;
+                       }
+                       if (l1 != l2) {
+                               printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
+                               fail++;
+                       }
+                       num++;
+               }
+       }
+
+       for (x = 0; int_fmt[x] ; x++) {
+               for (y = 0; int_nums[y] != 0 ; y++) {
+                       int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
+                       int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+                       sprintf (buf2, int_fmt[x], int_nums[y]);
+                       if (strcmp (buf1, buf2)) {
+                               printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
+                                      int_fmt[x], buf1, buf2);
+                               fail++;
+                       }
+                       if (l1 != l2) {
+                               printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
+                               fail++;
+                       }
+                       num++;
+               }
+       }
+
+       for (x = 0; str_fmt[x] ; x++) {
+               for (y = 0; str_vals[y] != 0 ; y++) {
+                       int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
+                       int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+                       sprintf (buf2, str_fmt[x], str_vals[y]);
+                       if (strcmp (buf1, buf2)) {
+                               printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
+                                      str_fmt[x], buf1, buf2);
+                               fail++;
+                       }
+                       if (l1 != l2) {
+                               printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
+                               fail++;
+                       }
+                       num++;
+               }
+       }
+
+       printf ("%d tests failed out of %d.\n", fail, num);
+
+       printf("seeing how many digits we support\n");
+       {
+               double v0 = 0.12345678901234567890123456789012345678901;
+               for (x=0; x<100; x++) {
+                       double p = pow(10, x); 
+                       double r = v0*p;
+                       snprintf(buf1, sizeof(buf1), "%1.1f", r);
+                       sprintf(buf2,                "%1.1f", r);
+                       if (strcmp(buf1, buf2)) {
+                               printf("we seem to support %d digits\n", x-1);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+#endif /* SNPRINTF_TEST */
diff --git a/source4/lib/substitute.c b/source4/lib/substitute.c
new file mode 100644 (file)
index 0000000..4e4f0bc
--- /dev/null
@@ -0,0 +1,188 @@
+/* 
+   Unix SMB/CIFS implementation.
+   string substitution functions
+   Copyright (C) Andrew Tridgell 1992-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"
+
+/* oh bugger - I realy didn't want to have a top-level context
+   anywhere, but until we change all lp_*() calls to take a context
+   argument this is needed */
+static struct substitute_context *sub;
+
+void sub_set_context(struct substitute_context *subptr)
+{
+       sub = subptr;
+}
+
+/*
+  setup a string in the negotiate structure, using alpha_strcpy with SAFE_NETBIOS_CHARS
+*/
+static void setup_string(char **dest, const char *str)
+{
+       char *s;
+
+       s = strdup(str);
+       if (!s) {
+               return;
+       }
+
+       alpha_strcpy(s, str, SAFE_NETBIOS_CHARS, strlen(s)+1);
+
+       trim_string(s," "," ");
+       strlower(s);
+
+       SAFE_FREE(*dest);
+       (*dest) = s;
+}
+
+void sub_set_local_machine(const char *local_machine)
+{
+       if (!sub) return;
+       setup_string(&sub->local_machine, local_machine);
+}
+
+void sub_set_remote_machine(const char *remote_machine)
+{
+       if (!sub) return;
+       setup_string(&sub->remote_machine, remote_machine);
+}
+
+void sub_set_remote_proto(const char *str)
+{
+       if (!sub) return;
+       setup_string(&sub->remote_proto, str);
+}
+
+void sub_set_remote_arch(const char *str)
+{
+       if (!sub) return;
+       setup_string(&sub->remote_arch, str);
+}
+
+const char *sub_get_remote_machine(void) 
+{
+       if (!sub) return "UNKNOWN";
+       return sub->remote_machine;
+}
+
+const char *sub_get_local_machine(void) 
+{
+       if (!sub) return "UNKNOWN";
+       return sub->local_machine;
+}
+
+
+/*
+  setup the string used by %U substitution 
+*/
+void sub_set_user_name(const char *name)
+{
+       if (!sub) return;
+       setup_string(&sub->user_name, name);
+}
+
+/****************************************************************************
+FOO
+****************************************************************************/
+void standard_sub_basic(char *str,size_t len)
+{
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+char *talloc_sub_basic(TALLOC_CTX *mem_ctx, const char *smb_name, const char *str)
+{
+       return talloc_strdup(mem_ctx, str);
+}
+
+char *alloc_sub_basic(const char *smb_name, const char *str)
+{
+       return strdup(str);
+}
+
+/****************************************************************************
+ Do some specific substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+
+char *talloc_sub_specified(TALLOC_CTX *mem_ctx,
+                       const char *input_string,
+                       const char *username,
+                       const char *domain,
+                       uid_t uid,
+                       gid_t gid)
+{
+       return talloc_strdup(mem_ctx, input_string);
+}
+
+char *alloc_sub_specified(const char *input_string,
+                       const char *username,
+                       const char *domain,
+                       uid_t uid,
+                       gid_t gid)
+{
+       return strdup(input_string);
+}
+
+char *talloc_sub_advanced(TALLOC_CTX *mem_ctx,
+                       int snum,
+                       const char *user,
+                       const char *connectpath,
+                       gid_t gid,
+                       const char *smb_name,
+                       char *str)
+{
+       return talloc_strdup(mem_ctx, str);
+}
+
+char *alloc_sub_advanced(int snum, const char *user, 
+                                 const char *connectpath, gid_t gid, 
+                                 const char *smb_name, char *str)
+{
+       return strdup(str);
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+****************************************************************************/
+
+void standard_sub_conn(struct tcon_context *conn, char *str, size_t len)
+{
+}
+
+char *talloc_sub_conn(TALLOC_CTX *mem_ctx, struct tcon_context *conn, char *str)
+{
+       return talloc_strdup(mem_ctx, str);
+}
+
+char *alloc_sub_conn(struct tcon_context *conn, char *str)
+{
+       return strdup(str);
+}
+
+/****************************************************************************
+ Like standard_sub but by snum.
+****************************************************************************/
+
+void standard_sub_snum(int snum, char *str, size_t len)
+{
+}
diff --git a/source4/lib/sysacls.c b/source4/lib/sysacls.c
new file mode 100644 (file)
index 0000000..fe85b9e
--- /dev/null
@@ -0,0 +1,3198 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba system utilities for ACL support.
+   Copyright (C) Jeremy Allison 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"
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p
+ void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+ SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
+ int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+ SMB_ACL_T sys_acl_init( int count)
+ int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+ int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+ int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+ int sys_acl_valid( SMB_ACL_T theacl )
+ int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+
+ This next one is not POSIX complient - but we *have* to have it !
+ More POSIX braindamage.
+
+ int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+
+ The generic POSIX free is the following call. We split this into
+ several different free functions as we may need to add tag info
+ to structures when emulating the POSIX interface.
+
+ int sys_acl_free( void *obj_p)
+
+ The calls we actually use are :
+
+ int sys_acl_free_text(char *text) - free acl_to_text
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+ int sys_acl_free_qualifier(void *qualifier, SMB_ACL_TAG_T tagtype)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS)
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       return acl_get_entry( the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       return acl_get_file( path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       return acl_get_fd(fd);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       return acl_clear_perms(permset);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return acl_add_perm(permset, perm);
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+#if defined(HAVE_ACL_GET_PERM_NP)
+       /*
+        * Required for TrustedBSD-based ACL implementations where
+        * non-POSIX.1e functions are denoted by a _np (non-portable)
+        * suffix.
+        */
+       return acl_get_perm_np(permset, perm);
+#else
+       return acl_get_perm(permset, perm);
+#endif
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+       return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       return acl_set_file(name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       return acl_set_fd(fd, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl) 
+{
+       return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return acl_free(qual);
+}
+
+#elif defined(HAVE_TRU64_ACLS)
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ * 
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_ENTRY_T entry;
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+               return -1;
+       }
+
+       errno = 0;
+       if ((entry = acl_get_entry(the_acl)) != NULL) {
+               *entry_p = entry;
+               return 1;
+       }
+
+       return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       return acl_get_file((char *)path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       *permset = 0;           /* acl_clear_perm() is broken on Tru64  */
+
+       return 0;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       if (perm & ~(SMB_ACL_READ | SMB_ACL_WRITE | SMB_ACL_EXECUTE)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset |= perm;       /* acl_add_perm() is broken on Tru64    */
+
+       return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return *permset & perm; /* Tru64 doesn't have acl_get_perm() */
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+       return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       SMB_ACL_ENTRY_T entry;
+
+       if ((entry = acl_create_entry(pacl)) == NULL) {
+               return -1;
+       }
+
+       *pentry = entry;
+       return 0;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       acl_entry_t     entry;
+
+       return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       return acl_set_file((char *)name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       /*
+        * (void) cast and explicit return 0 are for DEC UNIX
+        *  which just #defines acl_free_text() to be free()
+        */
+       (void) acl_free_text(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl) 
+{
+       return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return acl_free_qualifier(qual, tagtype);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ *     sys_acl_valid()
+ *     sys_acl_set_file()
+ *     sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define        SETACL          ACL_SET
+#define        GETACL          ACL_GET
+#define        GETACLCNT       ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->count) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->acl[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->a_type;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = &entry_d->a_perm;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->a_type != SMB_ACL_USER
+           && entry_d->a_type != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define        INITIAL_ACL_SIZE        16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+       int             ndefault;       /* # of default ACL entries     */
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       /*
+        * If there isn't enough space for the ACL entries we use
+        * GETACLCNT to determine the actual number of ACL entries
+        * reallocate and try again. This is in a loop because it
+        * is possible that someone else could modify the ACL and
+        * increase the number of entries between the call to
+        * GETACLCNT and the call to GETACL.
+        */
+       while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+           && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access and default ACL entries
+        *
+        * Note: we assume that the acl() system call returned a
+        * well formed ACL which is sorted so that all of the
+        * access ACL entries preceed any default ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       ndefault = count - naccess;
+       
+       /*
+        * if the caller wants the default ACL we have to copy
+        * the entries down to the start of the acl[] buffer
+        * and mask out the ACL_DEFAULT flag from the type field
+        */
+       if (type == SMB_ACL_TYPE_DEFAULT) {
+               int     i, j;
+
+               for (i = 0, j = naccess; i < ndefault; i++, j++) {
+                       acl_d->acl[i] = acl_d->acl[j];
+                       acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+               }
+
+               acl_d->count = ndefault;
+       } else {
+               acl_d->count = naccess;
+       }
+
+       return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+           && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       
+       acl_d->count = naccess;
+
+       return acl_d;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       *permset_d = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset_d |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       int     i;
+       int     len, maxlen;
+       char    *text;
+
+       /*
+        * use an initial estimate of 20 bytes per ACL entry
+        * when allocating memory for the text representation
+        * of the ACL
+        */
+       len     = 0;
+       maxlen  = 20 * acl_d->count;
+       if ((text = malloc(maxlen)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       for (i = 0; i < acl_d->count; i++) {
+               struct acl      *ap     = &acl_d->acl[i];
+               struct passwd   *pw;
+               struct group    *gr;
+               char            tagbuf[12];
+               char            idbuf[12];
+               char            *tag;
+               char            *id     = "";
+               char            perms[4];
+               int             nbytes;
+
+               switch (ap->a_type) {
+                       /*
+                        * for debugging purposes it's probably more
+                        * useful to dump unknown tag types rather
+                        * than just returning an error
+                        */
+                       default:
+                               slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+                                       ap->a_type);
+                               tag = tagbuf;
+                               slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                       (long)ap->a_id);
+                               id = idbuf;
+                               break;
+
+                       case SMB_ACL_USER:
+                               id = uidtoname(ap->a_id);
+                       case SMB_ACL_USER_OBJ:
+                               tag = "user";
+                               break;
+
+                       case SMB_ACL_GROUP:
+                               if ((gr = getgrgid(ap->a_id)) == NULL) {
+                                       slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                               (long)ap->a_id);
+                                       id = idbuf;
+                               } else {
+                                       id = gr->gr_name;
+                               }
+                       case SMB_ACL_GROUP_OBJ:
+                               tag = "group";
+                               break;
+
+                       case SMB_ACL_OTHER:
+                               tag = "other";
+                               break;
+
+                       case SMB_ACL_MASK:
+                               tag = "mask";
+                               break;
+
+               }
+
+               perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+               perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+               perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+               perms[3] = '\0';
+
+               /*          <tag>      :  <qualifier>   :  rwx \n  \0 */
+               nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+               /*
+                * If this entry would overflow the buffer
+                * allocate enough additional memory for this
+                * entry and an estimate of another 20 bytes
+                * for each entry still to be processed
+                */
+               if ((len + nbytes) > maxlen) {
+                       char *oldtext = text;
+
+                       maxlen += nbytes + 20 * (acl_d->count - i);
+
+                       if ((text = Realloc(oldtext, maxlen)) == NULL) {
+                               SAFE_FREE(oldtext);
+                               errno = ENOMEM;
+                               return NULL;
+                       }
+               }
+
+               slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+               len += nbytes - 1;
+       }
+
+       if (len_p)
+               *len_p = len;
+
+       return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /*
+        * note that since the definition of the structure pointed
+        * to by the SMB_ACL_T includes the first element of the
+        * acl[] array, this actually allocates an ACL with room
+        * for (count+1) entries
+        */
+       if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->size = count + 1;
+       a->count = 0;
+       a->next = -1;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->count >= acl_d->size) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->acl[acl_d->count++];
+       entry_d->a_type = 0;
+       entry_d->a_id   = -1;
+       entry_d->a_perm = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->a_type = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->a_type != SMB_ACL_GROUP
+           && entry_d->a_type != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->a_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->a_perm = *permset_d;
+
+       return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+       int     fixmask = (acl_d->count <= 4);
+
+       if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       struct stat     s;
+       struct acl      *acl_p;
+       int             acl_count;
+       struct acl      *acl_buf        = NULL;
+       int             ret;
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       acl_p           = &acl_d->acl[0];
+       acl_count       = acl_d->count;
+
+       /*
+        * if it's a directory there is extra work to do
+        * since the acl() system call will replace both
+        * the access ACLs and the default ACLs (if any)
+        */
+       if (stat(name, &s) != 0) {
+               return -1;
+       }
+       if (S_ISDIR(s.st_mode)) {
+               SMB_ACL_T       acc_acl;
+               SMB_ACL_T       def_acl;
+               SMB_ACL_T       tmp_acl;
+               int             i;
+
+               if (type == SMB_ACL_TYPE_ACCESS) {
+                       acc_acl = acl_d;
+                       def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+               } else {
+                       def_acl = acl_d;
+                       acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+               }
+
+               if (tmp_acl == NULL) {
+                       return -1;
+               }
+
+               /*
+                * allocate a temporary buffer for the complete ACL
+                */
+               acl_count = acc_acl->count + def_acl->count;
+               acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+               if (acl_buf == NULL) {
+                       sys_acl_free_acl(tmp_acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               /*
+                * copy the access control and default entries into the buffer
+                */
+               memcpy(&acl_buf[0], &acc_acl->acl[0],
+                       acc_acl->count * sizeof(acl_buf[0]));
+
+               memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+                       def_acl->count * sizeof(acl_buf[0]));
+
+               /*
+                * set the ACL_DEFAULT flag on the default entries
+                */
+               for (i = acc_acl->count; i < acl_count; i++) {
+                       acl_buf[i].a_type |= ACL_DEFAULT;
+               }
+
+               sys_acl_free_acl(tmp_acl);
+
+       } else if (type != SMB_ACL_TYPE_ACCESS) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = acl(name, SETACL, acl_count, acl_p);
+
+       SAFE_FREE(acl_buf);
+
+       return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+       SMB_ACL_T       acl_d;
+       int             ret;
+
+       /*
+        * fetching the access ACL and rewriting it has
+        * the effect of deleting the default ACL
+        */
+       if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+               return -1;
+       }
+
+       ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+       sys_acl_free_acl(acl_d);
+       
+       return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+       SAFE_FREE(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       SAFE_FREE(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS)
+#include <dl.h>
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ *     sys_acl_valid()
+ *     sys_acl_set_file()
+ *     sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or   */
+/* higher is installed. If acl() was called when it    */
+/* isn't defined, it causes the process to core dump   */
+/* so it is important to check this and avoid acl()    */
+/* calls if it isn't there.                            */
+
+static BOOL hpux_acl_call_presence(void)
+{
+
+       shl_t handle = NULL;
+       void *value;
+       int ret_val=0;
+       //static BOOL already_checked=0;
+       // REWRITE: add this back in??
+       //if(already_checked)
+       //      return True;
+
+
+       ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+       if(ret_val != 0) {
+               DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+                       ret_val, errno, strerror(errno)));
+               DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+               return False;
+       }
+
+       DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+       already_checked = True;
+       return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->count) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->acl[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->a_type;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = &entry_d->a_perm;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->a_type != SMB_ACL_USER
+           && entry_d->a_type != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define        INITIAL_ACL_SIZE        16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+       int             ndefault;       /* # of default ACL entries     */
+
+       if(hpux_acl_call_presence() == False) {
+               /* Looks like we don't have the acl() system call on HPUX. 
+                * May be the system doesn't have the latest version of JFS.
+                */
+               return NULL; 
+       }
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       /*
+        * If there isn't enough space for the ACL entries we use
+        * ACL_CNT to determine the actual number of ACL entries
+        * reallocate and try again. This is in a loop because it
+        * is possible that someone else could modify the ACL and
+        * increase the number of entries between the call to
+        * ACL_CNT and the call to ACL_GET.
+        */
+       while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = acl(path_p, ACL_CNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access and default ACL entries
+        *
+        * Note: we assume that the acl() system call returned a
+        * well formed ACL which is sorted so that all of the
+        * access ACL entries preceed any default ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       ndefault = count - naccess;
+       
+       /*
+        * if the caller wants the default ACL we have to copy
+        * the entries down to the start of the acl[] buffer
+        * and mask out the ACL_DEFAULT flag from the type field
+        */
+       if (type == SMB_ACL_TYPE_DEFAULT) {
+               int     i, j;
+
+               for (i = 0, j = naccess; i < ndefault; i++, j++) {
+                       acl_d->acl[i] = acl_d->acl[j];
+                       acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+               }
+
+               acl_d->count = ndefault;
+       } else {
+               acl_d->count = naccess;
+       }
+
+       return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       /*
+        * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+        */
+
+       files_struct *fsp = file_find_fd(fd);
+
+       if (fsp == NULL) {
+               errno = EBADF;
+               return NULL;
+       }
+
+       /*
+        * We know we're in the same conn context. So we
+        * can use the relative path.
+        */
+
+       return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       *permset_d = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset_d |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       int     i;
+       int     len, maxlen;
+       char    *text;
+
+       /*
+        * use an initial estimate of 20 bytes per ACL entry
+        * when allocating memory for the text representation
+        * of the ACL
+        */
+       len     = 0;
+       maxlen  = 20 * acl_d->count;
+       if ((text = malloc(maxlen)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       for (i = 0; i < acl_d->count; i++) {
+               struct acl      *ap     = &acl_d->acl[i];
+               struct passwd   *pw;
+               struct group    *gr;
+               char            tagbuf[12];
+               char            idbuf[12];
+               char            *tag;
+               char            *id     = "";
+               char            perms[4];
+               int             nbytes;
+
+               switch (ap->a_type) {
+                       /*
+                        * for debugging purposes it's probably more
+                        * useful to dump unknown tag types rather
+                        * than just returning an error
+                        */
+                       default:
+                               slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+                                       ap->a_type);
+                               tag = tagbuf;
+                               slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                       (long)ap->a_id);
+                               id = idbuf;
+                               break;
+
+                       case SMB_ACL_USER:
+                               id = uidtoname(ap->a_id);
+                       case SMB_ACL_USER_OBJ:
+                               tag = "user";
+                               break;
+
+                       case SMB_ACL_GROUP:
+                               if ((gr = getgrgid(ap->a_id)) == NULL) {
+                                       slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                               (long)ap->a_id);
+                                       id = idbuf;
+                               } else {
+                                       id = gr->gr_name;
+                               }
+                       case SMB_ACL_GROUP_OBJ:
+                               tag = "group";
+                               break;
+
+                       case SMB_ACL_OTHER:
+                               tag = "other";
+                               break;
+
+                       case SMB_ACL_MASK:
+                               tag = "mask";
+                               break;
+
+               }
+
+               perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+               perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+               perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+               perms[3] = '\0';
+
+               /*          <tag>      :  <qualifier>   :  rwx \n  \0 */
+               nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+               /*
+                * If this entry would overflow the buffer
+                * allocate enough additional memory for this
+                * entry and an estimate of another 20 bytes
+                * for each entry still to be processed
+                */
+               if ((len + nbytes) > maxlen) {
+                       char *oldtext = text;
+
+                       maxlen += nbytes + 20 * (acl_d->count - i);
+
+                       if ((text = Realloc(oldtext, maxlen)) == NULL) {
+                               free(oldtext);
+                               errno = ENOMEM;
+                               return NULL;
+                       }
+               }
+
+               slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+               len += nbytes - 1;
+       }
+
+       if (len_p)
+               *len_p = len;
+
+       return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /*
+        * note that since the definition of the structure pointed
+        * to by the SMB_ACL_T includes the first element of the
+        * acl[] array, this actually allocates an ACL with room
+        * for (count+1) entries
+        */
+       if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->size = count + 1;
+       a->count = 0;
+       a->next = -1;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->count >= acl_d->size) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->acl[acl_d->count++];
+       entry_d->a_type = 0;
+       entry_d->a_id   = -1;
+       entry_d->a_perm = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->a_type = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->a_type != SMB_ACL_GROUP
+           && entry_d->a_type != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->a_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->a_perm = *permset_d;
+
+       return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+       int n_user;
+       int n_def_user;
+       int n_user_obj;
+       int n_def_user_obj;
+
+       int n_group;
+       int n_def_group;
+       int n_group_obj;
+       int n_def_group_obj;
+
+       int n_other;
+       int n_other_obj;
+       int n_def_other_obj;
+
+       int n_class_obj;
+       int n_def_class_obj;
+
+       int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count      - Count of ACLs in the array of ACL strucutres.
+ * aclp           - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ *                  allocated.
+ * Output: 
+ *
+ * acl_type_count - This structure is filled up with counts of various 
+ *                  acl types.
+ */
+
+static int hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+       int i;
+
+       memset(acl_type_count, 0, sizeof(struct hpux_acl_types));
+
+       for(i=0;i<acl_count;i++) {
+               switch(aclp[i].a_type) {
+               case USER: 
+                       acl_type_count->n_user++;
+                       break;
+               case USER_OBJ: 
+                       acl_type_count->n_user_obj++;
+                       break;
+               case DEF_USER_OBJ: 
+                       acl_type_count->n_def_user_obj++;
+                       break;
+               case GROUP: 
+                       acl_type_count->n_group++;
+                       break;
+               case GROUP_OBJ: 
+                       acl_type_count->n_group_obj++;
+                       break;
+               case DEF_GROUP_OBJ: 
+                       acl_type_count->n_def_group_obj++;
+                       break;
+               case OTHER_OBJ: 
+                       acl_type_count->n_other_obj++;
+                       break;
+               case DEF_OTHER_OBJ: 
+                       acl_type_count->n_def_other_obj++;
+                       break;
+               case CLASS_OBJ:
+                       acl_type_count->n_class_obj++;
+                       break;
+               case DEF_CLASS_OBJ:
+                       acl_type_count->n_def_class_obj++;
+                       break;
+               case DEF_USER:
+                       acl_type_count->n_def_user++;
+                       break;
+               case DEF_GROUP:
+                       acl_type_count->n_def_group++;
+                       break;
+               default: 
+                       acl_type_count->n_illegal_obj++;
+                       break;
+               }
+       }
+}
+
+/* swap_acl_entries:  Swaps two ACL entries. 
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+       struct acl temp_acl;
+
+       temp_acl.a_type = aclp0->a_type;
+       temp_acl.a_id = aclp0->a_id;
+       temp_acl.a_perm = aclp0->a_perm;
+
+       aclp0->a_type = aclp1->a_type;
+       aclp0->a_id = aclp1->a_id;
+       aclp0->a_perm = aclp1->a_perm;
+
+       aclp1->a_type = temp_acl.a_type;
+       aclp1->a_id = temp_acl.a_id;
+       aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or 
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs: 
+ *
+ * Return.. 
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */ 
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+       switch(acl_type) {
+               case USER:
+               case GROUP:
+               case DEF_USER: 
+               case DEF_GROUP:
+                       return True;
+               default:
+                       return False;
+       }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing 
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+       switch(aclp->a_type) {
+               case USER: 
+               case GROUP_OBJ: 
+               case GROUP: 
+               case DEF_USER_OBJ: 
+               case DEF_USER:
+               case DEF_GROUP_OBJ: 
+               case DEF_GROUP:
+               case DEF_CLASS_OBJ:
+               case DEF_OTHER_OBJ: 
+                       return aclp->a_perm;
+               default: 
+                       return 0;
+       }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass  - If this is not zero, then we compute the CLASS_OBJ
+ *             permissions.
+ * aclp      - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp     - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+       /*
+        * The aclsort() system call is availabe on the latest HPUX General
+        * Patch Bundles. So for HPUX, we developed our version of acl_sort 
+        * function. Because, we don't want to update to a new 
+        * HPUX GR bundle just for aclsort() call.
+        */
+
+       struct hpux_acl_types acl_obj_count;
+       int n_class_obj_perm = 0;
+       int i, j;
+       if(!acl_count) {
+               DEBUG(10,("Zero acl count passed. Returning Success\n"));
+               return 0;
+       }
+
+       if(aclp == NULL) {
+               DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+               return -1;
+       }
+
+       /* Count different types of ACLs in the ACLs array */
+
+       hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+       /* There should be only one entry each of type USER_OBJ, GROUP_OBJ, 
+        * CLASS_OBJ and OTHER_OBJ 
+        */
+
+       if( (acl_obj_count.n_user_obj  != 1) || 
+               (acl_obj_count.n_group_obj != 1) || 
+               (acl_obj_count.n_class_obj != 1) ||
+               (acl_obj_count.n_other_obj != 1) 
+       ) {
+               DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+               return -1;
+       }
+
+       /* If any of the default objects are present, there should be only
+        * one of them each.
+        */
+
+       if( (acl_obj_count.n_def_user_obj  > 1) || (acl_obj_count.n_def_group_obj > 1) || 
+                       (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) {
+               DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+               return -1;
+       }
+
+       /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl 
+        * structures.  
+        *
+        * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+        * same ACL type, sort by ACL id.
+        *
+        * I am using the trival kind of sorting method here because, performance isn't 
+        * really effected by the ACLs feature. More over there aren't going to be more
+        * than 17 entries on HPUX. 
+        */
+
+       for(i=0; i<acl_count;i++) {
+               for (j=i+1; j<acl_count; j++) {
+                       if( aclp[i].a_type > aclp[j].a_type ) {
+                               /* ACL entries out of order, swap them */
+
+                               hpux_swap_acl_entries((aclp+i), (aclp+j));
+
+                       } else if ( aclp[i].a_type == aclp[j].a_type ) {
+
+                               /* ACL entries of same type, sort by id */
+
+                               if(aclp[i].a_id > aclp[j].a_id) {
+                                       hpux_swap_acl_entries((aclp+i), (aclp+j));
+                               } else if (aclp[i].a_id == aclp[j].a_id) {
+                                       /* We have a duplicate entry. */
+                                       if(hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+                                               DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+                                                       aclp[i].a_type, aclp[i].a_id));
+                                               return -1;
+                                       }
+                               }
+
+                       }
+               }
+       }
+
+       /* set the class obj permissions to the computed one. */
+       if(calclass) {
+               int n_class_obj_index = -1;
+
+               for(i=0;i<acl_count;i++) {
+                       n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+
+                       if(aclp[i].a_type == CLASS_OBJ)
+                               n_class_obj_index = i;
+               }
+               aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+       }
+
+       return 0;
+#else
+       return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+       int fixmask = (acl_d->count <= 4);
+
+       if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       struct stat     s;
+       struct acl      *acl_p;
+       int             acl_count;
+       struct acl      *acl_buf        = NULL;
+       int             ret;
+
+       if(hpux_acl_call_presence() == False) {
+               /* Looks like we don't have the acl() system call on HPUX. 
+                * May be the system doesn't have the latest version of JFS.
+                */
+               errno=ENOSYS;
+               return -1; 
+       }
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       acl_p           = &acl_d->acl[0];
+       acl_count       = acl_d->count;
+
+       /*
+        * if it's a directory there is extra work to do
+        * since the acl() system call will replace both
+        * the access ACLs and the default ACLs (if any)
+        */
+       if (stat(name, &s) != 0) {
+               return -1;
+       }
+       if (S_ISDIR(s.st_mode)) {
+               SMB_ACL_T       acc_acl;
+               SMB_ACL_T       def_acl;
+               SMB_ACL_T       tmp_acl;
+               int             i;
+
+               if (type == SMB_ACL_TYPE_ACCESS) {
+                       acc_acl = acl_d;
+                       def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+               } else {
+                       def_acl = acl_d;
+                       acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+               }
+
+               if (tmp_acl == NULL) {
+                       return -1;
+               }
+
+               /*
+                * allocate a temporary buffer for the complete ACL
+                */
+               acl_count = acc_acl->count + def_acl->count;
+               acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+               if (acl_buf == NULL) {
+                       sys_acl_free_acl(tmp_acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               /*
+                * copy the access control and default entries into the buffer
+                */
+               memcpy(&acl_buf[0], &acc_acl->acl[0],
+                       acc_acl->count * sizeof(acl_buf[0]));
+
+               memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+                       def_acl->count * sizeof(acl_buf[0]));
+
+               /*
+                * set the ACL_DEFAULT flag on the default entries
+                */
+               for (i = acc_acl->count; i < acl_count; i++) {
+                       acl_buf[i].a_type |= ACL_DEFAULT;
+               }
+
+               sys_acl_free_acl(tmp_acl);
+
+       } else if (type != SMB_ACL_TYPE_ACCESS) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = acl(name, ACL_SET, acl_count, acl_p);
+
+       if (acl_buf) {
+               free(acl_buf);
+       }
+
+       return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       /*
+        * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+        */
+
+       files_struct *fsp = file_find_fd(fd);
+
+       if (fsp == NULL) {
+               errno = EBADF;
+               return NULL;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       /*
+        * We know we're in the same conn context. So we
+        * can use the relative path.
+        */
+
+       return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+       SMB_ACL_T       acl_d;
+       int             ret;
+
+       /*
+        * fetching the access ACL and rewriting it has
+        * the effect of deleting the default ACL
+        */
+       if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+               return -1;
+       }
+
+       ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+       sys_acl_free_acl(acl_d);
+       
+       return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+       free(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       free(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS)
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->aclp->acl_cnt) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->ae_tag;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = entry_d;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->ae_tag != SMB_ACL_USER
+           && entry_d->ae_tag != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->ae_id;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       a;
+
+       if ((a = malloc(sizeof(*a))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+               SAFE_FREE(a);
+               return NULL;
+       }
+       a->next = -1;
+       a->freeaclp = True;
+       return a;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       SMB_ACL_T       a;
+
+       if ((a = malloc(sizeof(*a))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       if ((a->aclp = acl_get_fd(fd)) == NULL) {
+               SAFE_FREE(a);
+               return NULL;
+       }
+       a->next = -1;
+       a->freeaclp = True;
+       return a;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       permset_d->ae_perm = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       permset_d->ae_perm |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return permset_d->ae_perm & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       return acl_to_text(acl_d->aclp, len_p);
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       if ((a = malloc(sizeof(*a) + sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->next = -1;
+       a->freeaclp = False;
+       a->aclp = (struct acl *)(&a->aclp + sizeof(struct acl *));
+       a->aclp->acl_cnt = 0;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+       entry_d->ae_tag = 0;
+       entry_d->ae_id  = 0;
+       entry_d->ae_perm        = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->ae_tag = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->ae_tag != SMB_ACL_GROUP
+           && entry_d->ae_tag != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->ae_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (permset_d->ae_perm & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->ae_perm = permset_d->ae_perm;
+
+       return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       return acl_set_file(name, type, acl_d->aclp);
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       return acl_set_fd(fd, acl_d->aclp);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       if (acl_d->freeaclp) {
+               acl_free(acl_d->aclp);
+       }
+       acl_free(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       struct acl_entry_link *link;
+       struct new_acl_entry *entry;
+       int keep_going;
+
+       DEBUG(10,("This is the count: %d\n",theacl->count));
+
+       /* Check if count was previously set to -1. *
+        * If it was, that means we reached the end *
+        * of the acl last time.                    */
+       if(theacl->count == -1)
+               return(0);
+
+       link = theacl;
+       /* To get to the next acl, traverse linked list until index *
+        * of acl matches the count we are keeping.  This count is  *
+        * incremented each time we return an acl entry.            */
+
+       for(keep_going = 0; keep_going < theacl->count; keep_going++)
+               link = link->nextp;
+
+       entry = *entry_p =  link->entryp;
+
+       DEBUG(10,("*entry_p is %d\n",entry_p));
+       DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access));
+
+       /* Increment count */
+       theacl->count++;
+       if(link->nextp == NULL)
+               theacl->count = -1;
+
+       return(1);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       /* Initialize tag type */
+
+       *tag_type_p = -1;
+       DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type));
+
+       /* Depending on what type of entry we have, *
+        * return tag type.                         */
+       switch(entry_d->ace_id->id_type) {
+       case ACEID_USER:
+               *tag_type_p = SMB_ACL_USER;
+               break;
+       case ACEID_GROUP:
+               *tag_type_p = SMB_ACL_GROUP;
+               break;
+
+       case SMB_ACL_USER_OBJ:
+       case SMB_ACL_GROUP_OBJ:
+       case SMB_ACL_OTHER:
+               *tag_type_p = entry_d->ace_id->id_type;
+               break;
+       default:
+               return(-1);
+       }
+
+       return(0);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       DEBUG(10,("Starting AIX sys_acl_get_permset\n"));
+       *permset_p = &entry_d->ace_access;
+       DEBUG(10,("**permset_p is %d\n",**permset_p));
+       if(!(**permset_p & S_IXUSR) &&
+               !(**permset_p & S_IWUSR) &&
+               !(**permset_p & S_IRUSR) &&
+               (**permset_p != 0))
+                       return(-1);
+
+       DEBUG(10,("Ending AIX sys_acl_get_permset\n"));
+       return(0);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return(entry_d->ace_id->id_data);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       struct acl *file_acl = (struct acl *)NULL;
+       struct acl_entry *acl_entry;
+       struct new_acl_entry *new_acl_entry;
+       struct ace_id *idp;
+       struct acl_entry_link *acl_entry_link;
+       struct acl_entry_link *acl_entry_link_head;
+       int i;
+       int rc = 0;
+       uid_t user_id;
+
+       /* Get the acl using statacl */
+       DEBUG(10,("Entering sys_acl_get_file\n"));
+       DEBUG(10,("path_p is %s\n",path_p));
+
+       file_acl = (struct acl *)malloc(BUFSIZ);
+       if(file_acl == NULL) {
+               errno=ENOMEM;
+               DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno));
+               return(NULL);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       rc = statacl((char *)path_p,0,file_acl,BUFSIZ);
+       if(rc == -1) {
+               DEBUG(0,("statacl returned %d with errno %d\n",rc,errno));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("Got facl and returned it\n"));
+
+       /* Point to the first acl entry in the acl */
+       acl_entry =  file_acl->acl_ext;
+
+       /* Begin setting up the head of the linked list *
+        * that will be used for the storing the acl    *
+        * in a way that is useful for the posix_acls.c *
+        * code.                                          */
+
+       acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+       if(acl_entry_link_head == NULL)
+               return(NULL);
+
+       acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+       if(acl_entry_link->entryp == NULL) {
+               SAFE_FREE(file_acl);
+               errno = ENOMEM;
+               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+               return(NULL);
+       }
+
+       DEBUG(10,("acl_entry is %d\n",acl_entry));
+       DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+       /* Check if the extended acl bit is on.   *
+        * If it isn't, do not show the           *
+        * contents of the acl since AIX intends *
+        * the extended info to remain unused     */
+
+       if(file_acl->acl_mode & S_IXACL){
+               /* while we are not pointing to the very end */
+               while(acl_entry < acl_last(file_acl)) {
+                       /* before we malloc anything, make sure this is  */
+                       /* a valid acl entry and one that we want to map */
+                       idp = id_nxt(acl_entry->ace_id);
+                       if((acl_entry->ace_type == ACC_SPECIFY ||
+                               (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+                                       acl_entry = acl_nxt(acl_entry);
+                                       continue;
+                       }
+
+                       idp = acl_entry->ace_id;
+
+                       /* Check if this is the first entry in the linked list. *
+                        * The first entry needs to keep prevp pointing to NULL *
+                        * and already has entryp allocated.                  */
+
+                       if(acl_entry_link_head->count != 0) {
+                               acl_entry_link->nextp = (struct acl_entry_link *)
+                                                                                       malloc(sizeof(struct acl_entry_link));
+
+                               if(acl_entry_link->nextp == NULL) {
+                                       SAFE_FREE(file_acl);
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                                       return(NULL);
+                               }
+
+                               acl_entry_link->nextp->prevp = acl_entry_link;
+                               acl_entry_link = acl_entry_link->nextp;
+                               acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+                               if(acl_entry_link->entryp == NULL) {
+                                       SAFE_FREE(file_acl);
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                                       return(NULL);
+                               }
+                               acl_entry_link->nextp = NULL;
+                       }
+
+                       acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+                       /* Don't really need this since all types are going *
+                        * to be specified but, it's better than leaving it 0 */
+
+                       acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+                       acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                       memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id));
+
+                       /* The access in the acl entries must be left shifted by *
+                        * three bites, because they will ultimately be compared *
+                        * to S_IRUSR, S_IWUSR, and S_IXUSR.                  */
+
+                       switch(acl_entry->ace_type){
+                       case ACC_PERMIT:
+                       case ACC_SPECIFY:
+                               acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       case ACC_DENY:
+                               /* Since there is no way to return a DENY acl entry *
+                                * change to PERMIT and then shift.                 */
+                               DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+                               acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+                               DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       default:
+                               return(0);
+                       }
+
+                       DEBUG(10,("acl_entry = %d\n",acl_entry));
+                       DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+                       acl_entry = acl_nxt(acl_entry);
+               }
+       } /* end of if enabled */
+
+       /* Since owner, group, other acl entries are not *
+        * part of the acl entries in an acl, they must  *
+        * be dummied up to become part of the list.     */
+
+       for( i = 1; i < 4; i++) {
+               DEBUG(10,("i is %d\n",i));
+               if(acl_entry_link_head->count != 0) {
+                       acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+                       if(acl_entry_link->nextp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                               return(NULL);
+                       }
+
+                       acl_entry_link->nextp->prevp = acl_entry_link;
+                       acl_entry_link = acl_entry_link->nextp;
+                       acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+                       if(acl_entry_link->entryp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                               return(NULL);
+                       }
+               }
+
+               acl_entry_link->nextp = NULL;
+
+               new_acl_entry = acl_entry_link->entryp;
+               idp = new_acl_entry->ace_id;
+
+               new_acl_entry->ace_len = sizeof(struct acl_entry);
+               new_acl_entry->ace_type = ACC_PERMIT;
+               idp->id_len = sizeof(struct ace_id);
+               DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+               memset(idp->id_data,0,sizeof(uid_t));
+
+               switch(i) {
+               case 2:
+                       new_acl_entry->ace_access = file_acl->g_access << 6;
+                       idp->id_type = SMB_ACL_GROUP_OBJ;
+                       break;
+
+               case 3:
+                       new_acl_entry->ace_access = file_acl->o_access << 6;
+                       idp->id_type = SMB_ACL_OTHER;
+                       break;
+               case 1:
+                       new_acl_entry->ace_access = file_acl->u_access << 6;
+                       idp->id_type = SMB_ACL_USER_OBJ;
+                       break;
+               default:
+                       return(NULL);
+
+               }
+
+               acl_entry_link_head->count++;
+               DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+       }
+
+       acl_entry_link_head->count = 0;
+       SAFE_FREE(file_acl);
+
+       return(acl_entry_link_head);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       struct acl *file_acl = (struct acl *)NULL;
+       struct acl_entry *acl_entry;
+       struct new_acl_entry *new_acl_entry;
+       struct ace_id *idp;
+       struct acl_entry_link *acl_entry_link;
+       struct acl_entry_link *acl_entry_link_head;
+       int i;
+       int rc = 0;
+       uid_t user_id;
+
+       /* Get the acl using fstatacl */
+   
+       DEBUG(10,("Entering sys_acl_get_fd\n"));
+       DEBUG(10,("fd is %d\n",fd));
+       file_acl = (struct acl *)malloc(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno=ENOMEM;
+               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+               return(NULL);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       rc = fstatacl(fd,0,file_acl,BUFSIZ);
+       if(rc == -1) {
+               DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("Got facl and returned it\n"));
+
+       /* Point to the first acl entry in the acl */
+
+       acl_entry =  file_acl->acl_ext;
+       /* Begin setting up the head of the linked list *
+        * that will be used for the storing the acl    *
+        * in a way that is useful for the posix_acls.c *
+        * code.                                        */
+
+       acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+       if(acl_entry_link_head == NULL){
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+       if(acl_entry_link->entryp == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("acl_entry is %d\n",acl_entry));
+       DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+       /* Check if the extended acl bit is on.   *
+        * If it isn't, do not show the           *
+        * contents of the acl since AIX intends  *
+        * the extended info to remain unused     */
+       if(file_acl->acl_mode & S_IXACL){
+               /* while we are not pointing to the very end */
+               while(acl_entry < acl_last(file_acl)) {
+                       /* before we malloc anything, make sure this is  */
+                       /* a valid acl entry and one that we want to map */
+
+                       idp = id_nxt(acl_entry->ace_id);
+                       if((acl_entry->ace_type == ACC_SPECIFY ||
+                               (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+                                       acl_entry = acl_nxt(acl_entry);
+                                       continue;
+                       }
+
+                       idp = acl_entry->ace_id;
+                       /* Check if this is the first entry in the linked list. *
+                        * The first entry needs to keep prevp pointing to NULL *
+                        * and already has entryp allocated.                 */
+
+                       if(acl_entry_link_head->count != 0) {
+                               acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+                               if(acl_entry_link->nextp == NULL) {
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+                                       SAFE_FREE(file_acl);
+                                       return(NULL);
+                               }
+                               acl_entry_link->nextp->prevp = acl_entry_link;
+                               acl_entry_link = acl_entry_link->nextp;
+                               acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+                               if(acl_entry_link->entryp == NULL) {
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+                                       SAFE_FREE(file_acl);
+                                       return(NULL);
+                               }
+
+                               acl_entry_link->nextp = NULL;
+                       }
+
+                       acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+                       /* Don't really need this since all types are going *
+                        * to be specified but, it's better than leaving it 0 */
+
+                       acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+                       acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+                       memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id));
+
+                       /* The access in the acl entries must be left shifted by *
+                        * three bites, because they will ultimately be compared *
+                        * to S_IRUSR, S_IWUSR, and S_IXUSR.                  */
+
+                       switch(acl_entry->ace_type){
+                       case ACC_PERMIT:
+                       case ACC_SPECIFY:
+                               acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       case ACC_DENY:
+                               /* Since there is no way to return a DENY acl entry *
+                                * change to PERMIT and then shift.                 */
+                               DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+                               acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+                               DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       default:
+                               return(0);
+                       }
+
+                       DEBUG(10,("acl_entry = %d\n",acl_entry));
+                       DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+                       acl_entry = acl_nxt(acl_entry);
+               }
+       } /* end of if enabled */
+
+       /* Since owner, group, other acl entries are not *
+        * part of the acl entries in an acl, they must  *
+        * be dummied up to become part of the list.     */
+
+       for( i = 1; i < 4; i++) {
+               DEBUG(10,("i is %d\n",i));
+               if(acl_entry_link_head->count != 0){
+                       acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+                       if(acl_entry_link->nextp == NULL) {
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+                               SAFE_FREE(file_acl);
+                               return(NULL);
+                       }
+
+                       acl_entry_link->nextp->prevp = acl_entry_link;
+                       acl_entry_link = acl_entry_link->nextp;
+                       acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+                       if(acl_entry_link->entryp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+                               return(NULL);
+                       }
+               }
+
+               acl_entry_link->nextp = NULL;
+               new_acl_entry = acl_entry_link->entryp;
+               idp = new_acl_entry->ace_id;
+               new_acl_entry->ace_len = sizeof(struct acl_entry);
+               new_acl_entry->ace_type = ACC_PERMIT;
+               idp->id_len = sizeof(struct ace_id);
+               DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+               memset(idp->id_data,0,sizeof(uid_t));
+               switch(i) {
+               case 2:
+                       new_acl_entry->ace_access = file_acl->g_access << 6;
+                       idp->id_type = SMB_ACL_GROUP_OBJ;
+                       break;
+               case 3:
+                       new_acl_entry->ace_access = file_acl->o_access << 6;
+                       idp->id_type = SMB_ACL_OTHER;
+                       break;
+               case 1:
+                       new_acl_entry->ace_access = file_acl->u_access << 6;
+                       idp->id_type = SMB_ACL_USER_OBJ;
+                       break;
+               default:
+                       return(NULL);
+               }
+               acl_entry_link_head->count++;
+               DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+       }
+
+       acl_entry_link_head->count = 0;
+       SAFE_FREE(file_acl);
+       return(acl_entry_link_head);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       *permset = *permset & ~0777;
+       return(0);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       if((perm != 0) &&
+                       (perm & (S_IXUSR | S_IWUSR | S_IRUSR)) == 0)
+               return(-1);
+
+       *permset |= perm;
+       DEBUG(10,("This is the permset now: %d\n",*permset));
+       return(0);
+}
+
+char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+{
+       return(NULL);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       struct acl_entry_link *theacl = NULL;
+       DEBUG(10,("Entering sys_acl_init\n"));
+
+       theacl = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+       if(theacl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_init is %d\n",errno));
+               return(NULL);
+       }
+
+       theacl->count = 0;
+       theacl->nextp = NULL;
+       theacl->prevp = NULL;
+       theacl->entryp = NULL;
+       DEBUG(10,("Exiting sys_acl_init\n"));
+       return(theacl);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       struct acl_entry_link *theacl;
+       struct acl_entry_link *acl_entryp;
+       struct acl_entry_link *temp_entry;
+       int counting;
+
+       DEBUG(10,("Entering the sys_acl_create_entry\n"));
+
+       theacl = acl_entryp = *pacl;
+
+       /* Get to the end of the acl before adding entry */
+
+       for(counting=0; counting < theacl->count; counting++){
+               DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+               temp_entry = acl_entryp;
+               acl_entryp = acl_entryp->nextp;
+       }
+
+       if(theacl->count != 0){
+               temp_entry->nextp = acl_entryp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+               if(acl_entryp == NULL) {
+                       errno = ENOMEM;
+                       DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+                       return(-1);
+               }
+
+               DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+               acl_entryp->prevp = temp_entry;
+               DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp));
+       }
+
+       *pentry = acl_entryp->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+       if(*pentry == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+               return(-1);
+       }
+
+       memset(*pentry,0,sizeof(struct new_acl_entry));
+       acl_entryp->entryp->ace_len = sizeof(struct acl_entry);
+       acl_entryp->entryp->ace_type = ACC_PERMIT;
+       acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id);
+       acl_entryp->nextp = NULL;
+       theacl->count++;
+       DEBUG(10,("Exiting sys_acl_create_entry\n"));
+       return(0);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_tag_type\n"));
+       entry->ace_id->id_type = tagtype;
+       DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type));
+       DEBUG(10,("Ending AIX sys_acl_set_tag_type\n"));
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_qualifier\n"));
+       memcpy(entry->ace_id->id_data,qual,sizeof(uid_t));
+       DEBUG(10,("Ending AIX sys_acl_set_qualifier\n"));
+       return(0);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_permset\n"));
+       if(!(*permset & S_IXUSR) &&
+               !(*permset & S_IWUSR) &&
+               !(*permset & S_IRUSR) &&
+               (*permset != 0))
+                       return(-1);
+
+       entry->ace_access = *permset;
+       DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+       DEBUG(10,("Ending AIX sys_acl_set_permset\n"));
+       return(0);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       int user_obj = 0;
+       int group_obj = 0;
+       int other_obj = 0;
+       struct acl_entry_link *acl_entry;
+
+       for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+               user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+               group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+               other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+       }
+
+       DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj));
+       if(user_obj != 1 || group_obj != 1 || other_obj != 1)
+               return(-1); 
+
+       return(0);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       struct acl_entry_link *acl_entry_link = NULL;
+       struct acl *file_acl = NULL;
+       struct acl *file_acl_temp = NULL;
+       struct acl_entry *acl_entry = NULL;
+       struct ace_id *ace_id = NULL;
+       uint id_type;
+       uint ace_access;
+       uint user_id;
+       uint acl_length;
+       uint rc;
+
+       DEBUG(10,("Entering sys_acl_set_file\n"));
+       DEBUG(10,("File name is %s\n",name));
+       /* AIX has no default ACL */
+       if(acltype == SMB_ACL_TYPE_DEFAULT)
+               return(0);
+
+       acl_length = BUFSIZ;
+       file_acl = (struct acl *)malloc(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+               return(-1);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       file_acl->acl_len = ACL_SIZ;
+       file_acl->acl_mode = S_IXACL;
+
+       for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+               acl_entry_link->entryp->ace_access >>= 6;
+               id_type = acl_entry_link->entryp->ace_id->id_type;
+
+               switch(id_type) {
+               case SMB_ACL_USER_OBJ:
+                       file_acl->u_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_GROUP_OBJ:
+                       file_acl->g_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_OTHER:
+                       file_acl->o_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_MASK:
+                       continue;
+               }
+
+               if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+                       acl_length += sizeof(struct acl_entry);
+                       file_acl_temp = (struct acl *)malloc(acl_length);
+                       if(file_acl_temp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+                               return(-1);
+                       }  
+
+                       memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+                       SAFE_FREE(file_acl);
+                       file_acl = file_acl_temp;
+               }
+
+               acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+               file_acl->acl_len += sizeof(struct acl_entry);
+               acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+               acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+               /* In order to use this, we'll need to wait until we can get denies */
+               /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+               acl_entry->ace_type = ACC_SPECIFY; */
+
+               acl_entry->ace_type = ACC_SPECIFY;
+               ace_id = acl_entry->ace_id;
+               ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id type is %d\n",ace_id->id_type));
+               ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+               memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+               memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t));
+       }
+
+       rc = chacl(name,file_acl,file_acl->acl_len);
+       DEBUG(10,("errno is %d\n",errno));
+       DEBUG(10,("return code is %d\n",rc));
+       SAFE_FREE(file_acl);
+       DEBUG(10,("Exiting the sys_acl_set_file\n"));
+       return(rc);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       struct acl_entry_link *acl_entry_link = NULL;
+       struct acl *file_acl = NULL;
+       struct acl *file_acl_temp = NULL;
+       struct acl_entry *acl_entry = NULL;
+       struct ace_id *ace_id = NULL;
+       uint id_type;
+       uint user_id;
+       uint acl_length;
+       uint rc;
+       DEBUG(10,("Entering sys_acl_set_fd\n"));
+       acl_length = BUFSIZ;
+       file_acl = (struct acl *)malloc(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+               return(-1);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+       file_acl->acl_len = ACL_SIZ;
+       file_acl->acl_mode = S_IXACL;
+
+       for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+               acl_entry_link->entryp->ace_access >>= 6;
+               id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id_type is %d\n",id_type));
+
+               switch(id_type) {
+               case SMB_ACL_USER_OBJ:
+                       file_acl->u_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_GROUP_OBJ:
+                       file_acl->g_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_OTHER:
+                       file_acl->o_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_MASK:
+                       continue;
+               }
+
+               if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+                       acl_length += sizeof(struct acl_entry);
+                       file_acl_temp = (struct acl *)malloc(acl_length);
+                       if(file_acl_temp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+                               return(-1);
+                       }
+
+                       memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+                       SAFE_FREE(file_acl);
+                       file_acl = file_acl_temp;
+               }
+
+               acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+               file_acl->acl_len += sizeof(struct acl_entry);
+               acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+               acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+               /* In order to use this, we'll need to wait until we can get denies */
+               /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+                       acl_entry->ace_type = ACC_SPECIFY; */
+               acl_entry->ace_type = ACC_SPECIFY;
+               ace_id = acl_entry->ace_id;
+               ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id type is %d\n",ace_id->id_type));
+               ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+               memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+               memcpy(ace_id->id_data, &user_id, sizeof(uid_t));
+       }
+       rc = fchacl(fd,file_acl,file_acl->acl_len);
+       DEBUG(10,("errno is %d\n",errno));
+       DEBUG(10,("return code is %d\n",rc));
+       SAFE_FREE(file_acl);
+       DEBUG(10,("Exiting sys_acl_set_fd\n"));
+       return(rc);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       /* AIX has no default ACL */
+       return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return(*permset & perm);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return(0);
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+       struct acl_entry_link *acl_entry_link;
+
+       for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+               SAFE_FREE(acl_entry_link->prevp->entryp);
+               SAFE_FREE(acl_entry_link->prevp);
+       }
+
+       SAFE_FREE(acl_entry_link->prevp->entryp);
+       SAFE_FREE(acl_entry_link->prevp);
+       SAFE_FREE(acl_entry_link->entryp);
+       SAFE_FREE(acl_entry_link);
+       return(0);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return(0);
+}
+
+#else /* No ACLs. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       errno = ENOSYS;
+       return (SMB_ACL_T)NULL;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       errno = ENOSYS;
+       return (SMB_ACL_T)NULL;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       errno = ENOSYS;
+       return (permset & perm) ? 1 : 0;
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+int sys_acl_free_text(char *text)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl) 
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+#endif /* No ACLs. */
diff --git a/source4/lib/system.c b/source4/lib/system.c
new file mode 100644 (file)
index 0000000..bafe689
--- /dev/null
@@ -0,0 +1,1114 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba system utilities
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Jeremy Allison 1998-2002
+   
+   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"
+
+/*
+   The idea is that this file will eventually have wrappers around all
+   important system calls in samba. The aims are:
+
+   - to enable easier porting by putting OS dependent stuff in here
+
+   - to allow for hooks into other "pseudo-filesystems"
+
+   - to allow easier integration of things like the japanese extensions
+
+   - to support the philosophy of Samba to expose the features of
+     the OS within the SMB model. In general whatever file/printer/variable
+     expansions/etc make sense to the OS should be acceptable to Samba.
+*/
+
+
+
+/*******************************************************************
+ A wrapper for usleep in case we don't have one.
+********************************************************************/
+
+int sys_usleep(long usecs)
+{
+#ifndef HAVE_USLEEP
+       struct timeval tval;
+#endif
+
+       /*
+        * We need this braindamage as the glibc usleep
+        * is not SPEC1170 complient... grumble... JRA.
+        */
+
+       if(usecs < 0 || usecs > 1000000) {
+               errno = EINVAL;
+               return -1;
+       }
+
+#if HAVE_USLEEP
+       usleep(usecs);
+       return 0;
+#else /* HAVE_USLEEP */
+       /*
+        * Fake it with select...
+        */
+       tval.tv_sec = 0;
+       tval.tv_usec = usecs/1000;
+       select(0,NULL,NULL,NULL,&tval);
+       return 0;
+#endif /* HAVE_USLEEP */
+}
+
+/*******************************************************************
+A read wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_read(int fd, void *buf, size_t count)
+{
+       ssize_t ret;
+
+       do {
+               ret = read(fd, buf, count);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A write wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_write(int fd, const void *buf, size_t count)
+{
+       ssize_t ret;
+
+       do {
+               ret = write(fd, buf, count);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A send wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_send(int s, const void *msg, size_t len, int flags)
+{
+       ssize_t ret;
+
+       do {
+               ret = send(s, msg, len, flags);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A sendto wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_sendto(int s,  const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+       ssize_t ret;
+
+       do {
+               ret = sendto(s, msg, len, flags, to, tolen);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A recvfrom wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+       ssize_t ret;
+
+       do {
+               ret = recvfrom(s, buf, len, flags, from, fromlen);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_ptr(int fd, int cmd, void *arg)
+{
+       int ret;
+
+       do {
+               ret = fcntl(fd, cmd, arg);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_long(int fd, int cmd, long arg)
+{
+       int ret;
+
+       do {
+               ret = fcntl(fd, cmd, arg);
+       } while (ret == -1 && errno == EINTR);
+       return ret;
+}
+
+/*******************************************************************
+A stat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+       int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_STAT64)
+       ret = stat64(fname, sbuf);
+#else
+       ret = stat(fname, sbuf);
+#endif
+       /* we always want directories to appear zero size */
+       if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+       return ret;
+}
+
+/*******************************************************************
+ An fstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf)
+{
+       int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FSTAT64)
+       ret = fstat64(fd, sbuf);
+#else
+       ret = fstat(fd, sbuf);
+#endif
+       /* we always want directories to appear zero size */
+       if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+       return ret;
+}
+
+/*******************************************************************
+ An lstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+       int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSTAT64)
+       ret = lstat64(fname, sbuf);
+#else
+       ret = lstat(fname, sbuf);
+#endif
+       /* we always want directories to appear zero size */
+       if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+       return ret;
+}
+
+/*******************************************************************
+ An ftruncate() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_ftruncate(int fd, SMB_OFF_T offset)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FTRUNCATE64)
+       return ftruncate64(fd, offset);
+#else
+       return ftruncate(fd, offset);
+#endif
+}
+
+/*******************************************************************
+ An lseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSEEK64)
+       return lseek64(fd, offset, whence);
+#else
+       return lseek(fd, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An fseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fseek(FILE *fp, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEK64)
+       return fseek64(fp, offset, whence);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEKO64)
+       return fseeko64(fp, offset, whence);
+#else
+       return fseek(fp, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An ftell() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_ftell(FILE *fp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELL64)
+       return (SMB_OFF_T)ftell64(fp);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELLO64)
+       return (SMB_OFF_T)ftello64(fp);
+#else
+       return (SMB_OFF_T)ftell(fp);
+#endif
+}
+
+/*******************************************************************
+ A creat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_creat(const char *path, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_CREAT64)
+       return creat64(path, mode);
+#else
+       /*
+        * If creat64 isn't defined then ensure we call a potential open64.
+        * JRA.
+        */
+       return sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+#endif
+}
+
+/*******************************************************************
+ An open() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_open(const char *path, int oflag, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPEN64)
+       return open64(path, oflag, mode);
+#else
+       return open(path, oflag, mode);
+#endif
+}
+
+/*******************************************************************
+ An fopen() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+FILE *sys_fopen(const char *path, const char *type)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_FOPEN64)
+       return fopen64(path, type);
+#else
+       return fopen(path, type);
+#endif
+}
+
+/*******************************************************************
+ A readdir wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_STRUCT_DIRENT *sys_readdir(DIR *dirp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64)
+       return readdir64(dirp);
+#else
+       return readdir(dirp);
+#endif
+}
+
+/*******************************************************************
+The wait() calls vary between systems
+********************************************************************/
+
+int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+       return waitpid(pid,status,options);
+#else /* HAVE_WAITPID */
+       return wait4(pid, status, options, NULL);
+#endif /* HAVE_WAITPID */
+}
+
+/*******************************************************************
+ System wrapper for getwd
+********************************************************************/
+
+char *sys_getwd(char *s)
+{
+       char *wd;
+#ifdef HAVE_GETCWD
+       wd = (char *)getcwd(s, sizeof (pstring));
+#else
+       wd = (char *)getwd(s);
+#endif
+       return wd;
+}
+
+/*******************************************************************
+system wrapper for link
+********************************************************************/
+
+int sys_link(const char *oldpath, const char *newpath)
+{
+#ifndef HAVE_LINK
+       errno = ENOSYS;
+       return -1;
+#else
+       return link(oldpath, newpath);
+#endif
+}
+
+/*******************************************************************
+os/2 also doesn't have chroot
+********************************************************************/
+int sys_chroot(const char *dname)
+{
+#ifndef HAVE_CHROOT
+       static int done;
+       if (!done) {
+               DEBUG(1,("WARNING: no chroot!\n"));
+               done=1;
+       }
+       errno = ENOSYS;
+       return -1;
+#else
+       return(chroot(dname));
+#endif
+}
+
+/**************************************************************************
+A wrapper for gethostbyname() that tries avoids looking up hostnames 
+in the root domain, which can cause dial-on-demand links to come up for no
+apparent reason.
+****************************************************************************/
+
+struct hostent *sys_gethostbyname(const char *name)
+{
+#ifdef REDUCE_ROOT_DNS_LOOKUPS
+       char query[256], hostname[256];
+       char *domain;
+
+       /* Does this name have any dots in it? If so, make no change */
+
+       if (strchr_m(name, '.'))
+               return(gethostbyname(name));
+
+       /* Get my hostname, which should have domain name 
+               attached. If not, just do the gethostname on the
+               original string. 
+       */
+
+       gethostname(hostname, sizeof(hostname) - 1);
+       hostname[sizeof(hostname) - 1] = 0;
+       if ((domain = strchr_m(hostname, '.')) == NULL)
+               return(gethostbyname(name));
+
+       /* Attach domain name to query and do modified query.
+               If names too large, just do gethostname on the
+               original string.
+       */
+
+       if((strlen(name) + strlen(domain)) >= sizeof(query))
+               return(gethostbyname(name));
+
+       slprintf(query, sizeof(query)-1, "%s%s", name, domain);
+       return(gethostbyname(query));
+#else /* REDUCE_ROOT_DNS_LOOKUPS */
+       return(gethostbyname(name));
+#endif /* REDUCE_ROOT_DNS_LOOKUPS */
+}
+
+
+#if defined(HAVE_IRIX_SPECIFIC_CAPABILITIES)
+/**************************************************************************
+ Try and abstract process capabilities (for systems that have them).
+****************************************************************************/
+static BOOL set_process_capability( uint32 cap_flag, BOOL enable )
+{
+       if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+               cap_t cap = cap_get_proc();
+
+               if (cap == NULL) {
+                       DEBUG(0,("set_process_capability: cap_get_proc failed. Error was %s\n",
+                               strerror(errno)));
+                       return False;
+               }
+
+               if(enable)
+                       cap->cap_effective |= CAP_NETWORK_MGT;
+               else
+                       cap->cap_effective &= ~CAP_NETWORK_MGT;
+
+               if (cap_set_proc(cap) == -1) {
+                       DEBUG(0,("set_process_capability: cap_set_proc failed. Error was %s\n",
+                               strerror(errno)));
+                       cap_free(cap);
+                       return False;
+               }
+
+               cap_free(cap);
+
+               DEBUG(10,("set_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+       }
+       return True;
+}
+
+/**************************************************************************
+ Try and abstract inherited process capabilities (for systems that have them).
+****************************************************************************/
+
+static BOOL set_inherited_process_capability( uint32 cap_flag, BOOL enable )
+{
+       if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+               cap_t cap = cap_get_proc();
+
+               if (cap == NULL) {
+                       DEBUG(0,("set_inherited_process_capability: cap_get_proc failed. Error was %s\n",
+                               strerror(errno)));
+                       return False;
+               }
+
+               if(enable)
+                       cap->cap_inheritable |= CAP_NETWORK_MGT;
+               else
+                       cap->cap_inheritable &= ~CAP_NETWORK_MGT;
+
+               if (cap_set_proc(cap) == -1) {
+                       DEBUG(0,("set_inherited_process_capability: cap_set_proc failed. Error was %s\n", 
+                               strerror(errno)));
+                       cap_free(cap);
+                       return False;
+               }
+
+               cap_free(cap);
+
+               DEBUG(10,("set_inherited_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+       }
+       return True;
+}
+#endif
+
+/****************************************************************************
+ Gain the oplock capability from the kernel if possible.
+****************************************************************************/
+
+void oplock_set_capability(BOOL this_process, BOOL inherit)
+{
+#if HAVE_KERNEL_OPLOCKS_IRIX
+       set_process_capability(KERNEL_OPLOCK_CAPABILITY,this_process);
+       set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,inherit);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for random().
+****************************************************************************/
+
+long sys_random(void)
+{
+#if defined(HAVE_RANDOM)
+       return (long)random();
+#elif defined(HAVE_RAND)
+       return (long)rand();
+#else
+       DEBUG(0,("Error - no random function available !\n"));
+       exit(1);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for srandom().
+****************************************************************************/
+
+void sys_srandom(unsigned int seed)
+{
+#if defined(HAVE_SRANDOM)
+       srandom(seed);
+#elif defined(HAVE_SRAND)
+       srand(seed);
+#else
+       DEBUG(0,("Error - no srandom function available !\n"));
+       exit(1);
+#endif
+}
+
+/**************************************************************************
+ Returns equivalent to NGROUPS_MAX - using sysconf if needed.
+****************************************************************************/
+
+int groups_max(void)
+{
+#if defined(SYSCONF_SC_NGROUPS_MAX)
+       int ret = sysconf(_SC_NGROUPS_MAX);
+       return (ret == -1) ? NGROUPS_MAX : ret;
+#else
+       return NGROUPS_MAX;
+#endif
+}
+
+/**************************************************************************
+ Wrapper for getgroups. Deals with broken (int) case.
+****************************************************************************/
+
+int sys_getgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_BROKEN_GETGROUPS)
+       return getgroups(setlen, gidset);
+#else
+
+       GID_T gid;
+       GID_T *group_list;
+       int i, ngroups;
+
+       if(setlen == 0) {
+               return getgroups(setlen, &gid);
+       }
+
+       /*
+        * Broken case. We need to allocate a
+        * GID_T array of size setlen.
+        */
+
+       if(setlen < 0) {
+               errno = EINVAL; 
+               return -1;
+       } 
+
+       if (setlen == 0)
+               setlen = groups_max();
+
+       if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+               DEBUG(0,("sys_getgroups: Malloc fail.\n"));
+               return -1;
+       }
+
+       if((ngroups = getgroups(setlen, group_list)) < 0) {
+               int saved_errno = errno;
+               SAFE_FREE(group_list);
+               errno = saved_errno;
+               return -1;
+       }
+
+       for(i = 0; i < ngroups; i++)
+               gidset[i] = (gid_t)group_list[i];
+
+       SAFE_FREE(group_list);
+       return ngroups;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+#ifdef HAVE_SETGROUPS
+
+/**************************************************************************
+ Wrapper for setgroups. Deals with broken (int) case. Automatically used
+ if we have broken getgroups.
+****************************************************************************/
+
+int sys_setgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_BROKEN_GETGROUPS)
+       return setgroups(setlen, gidset);
+#else
+
+       GID_T *group_list;
+       int i ; 
+
+       if (setlen == 0)
+               return 0 ;
+
+       if (setlen < 0 || setlen > groups_max()) {
+               errno = EINVAL; 
+               return -1;   
+       }
+
+       /*
+        * Broken case. We need to allocate a
+        * GID_T array of size setlen.
+        */
+
+       if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+               DEBUG(0,("sys_setgroups: Malloc fail.\n"));
+               return -1;    
+       }
+       for(i = 0; i < setlen; i++) 
+               group_list[i] = (GID_T) gidset[i]; 
+
+       if(setgroups(setlen, group_list) != 0) {
+               int saved_errno = errno;
+               SAFE_FREE(group_list);
+               errno = saved_errno;
+               return -1;
+       }
+       SAFE_FREE(group_list);
+       return 0 ;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+#endif /* HAVE_SETGROUPS */
+
+struct passwd *sys_getpwent(void)
+{
+       return getpwent();
+}
+
+void sys_endpwent(void)
+{
+       endpwent();
+}
+
+/**************************************************************************
+ Wrappers for getpwnam(), getpwuid(), getgrnam(), getgrgid()
+****************************************************************************/
+
+struct passwd *sys_getpwnam(const char *name)
+{
+       return getpwnam(name);
+}
+
+struct passwd *sys_getpwuid(uid_t uid)
+{
+       return getpwuid(uid);
+}
+
+struct group *sys_getgrnam(const char *name)
+{
+       return getgrnam(name);
+}
+
+struct group *sys_getgrgid(gid_t gid)
+{
+       return getgrgid(gid);
+}
+
+#if 0 /* NOT CURRENTLY USED - JRA */
+/**************************************************************************
+ The following are the UNICODE versions of *all* system interface functions
+ called within Samba. Ok, ok, the exceptions are the gethostbyXX calls,
+ which currently are left as ascii as they are not used other than in name
+ resolution.
+****************************************************************************/
+
+/**************************************************************************
+ Wide stat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_stat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+       pstring fname;
+       return sys_stat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide lstat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_lstat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+       pstring fname;
+       return sys_lstat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide creat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_creat(const smb_ucs2_t *wfname, mode_t mode)
+{
+       pstring fname;
+       return sys_creat(unicode_to_unix(fname,wfname,sizeof(fname)), mode);
+}
+
+/**************************************************************************
+ Wide open. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_open(const smb_ucs2_t *wfname, int oflag, mode_t mode)
+{
+       pstring fname;
+       return sys_open(unicode_to_unix(fname,wfname,sizeof(fname)), oflag, mode);
+}
+
+/**************************************************************************
+ Wide fopen. Just narrow and call sys_xxx.
+****************************************************************************/
+
+FILE *wsys_fopen(const smb_ucs2_t *wfname, const char *type)
+{
+       pstring fname;
+       return sys_fopen(unicode_to_unix(fname,wfname,sizeof(fname)), type);
+}
+
+/**************************************************************************
+ Wide opendir. Just narrow and call sys_xxx.
+****************************************************************************/
+
+DIR *wsys_opendir(const smb_ucs2_t *wfname)
+{
+       pstring fname;
+       return opendir(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide readdir. Return a structure pointer containing a wide filename.
+****************************************************************************/
+
+SMB_STRUCT_WDIRENT *wsys_readdir(DIR *dirp)
+{
+       static SMB_STRUCT_WDIRENT retval;
+       SMB_STRUCT_DIRENT *dirval = sys_readdir(dirp);
+
+       if(!dirval)
+               return NULL;
+
+       /*
+        * The only POSIX defined member of this struct is d_name.
+        */
+
+       unix_to_unicode(retval.d_name,dirval->d_name,sizeof(retval.d_name));
+
+       return &retval;
+}
+
+/**************************************************************************
+ Wide getwd. Call sys_xxx and widen. Assumes s points to a wpstring.
+****************************************************************************/
+
+smb_ucs2_t *wsys_getwd(smb_ucs2_t *s)
+{
+       pstring fname;
+       char *p = sys_getwd(fname);
+
+       if(!p)
+               return NULL;
+
+       return unix_to_unicode(s, p, sizeof(wpstring));
+}
+
+/**************************************************************************
+ Wide chown. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chown(const smb_ucs2_t *wfname, uid_t uid, gid_t gid)
+{
+       pstring fname;
+       return chown(unicode_to_unix(fname,wfname,sizeof(fname)), uid, gid);
+}
+
+/**************************************************************************
+ Wide chroot. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chroot(const smb_ucs2_t *wfname)
+{
+       pstring fname;
+       return chroot(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide getpwnam. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwnam(const smb_ucs2_t *wname)
+{
+       static SMB_STRUCT_WPASSWD retval;
+       fstring name;
+       struct passwd *pwret = sys_getpwnam(unicode_to_unix(name,wname,sizeof(name)));
+
+       if(!pwret)
+               return NULL;
+
+       unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+       retval.pw_passwd = pwret->pw_passwd;
+       retval.pw_uid = pwret->pw_uid;
+       retval.pw_gid = pwret->pw_gid;
+       unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+       unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+       unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+       return &retval;
+}
+
+/**************************************************************************
+ Wide getpwuid. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwuid(uid_t uid)
+{
+       static SMB_STRUCT_WPASSWD retval;
+       struct passwd *pwret = sys_getpwuid(uid);
+
+       if(!pwret)
+               return NULL;
+
+       unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+       retval.pw_passwd = pwret->pw_passwd;
+       retval.pw_uid = pwret->pw_uid;
+       retval.pw_gid = pwret->pw_gid;
+       unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+       unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+       unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+       return &retval;
+}
+#endif /* NOT CURRENTLY USED - JRA */
+
+/**************************************************************************
+ Extract a command into an arg list. Uses a static pstring for storage.
+ Caller frees returned arg list (which contains pointers into the static pstring).
+****************************************************************************/
+
+static char **extract_args(const char *command)
+{
+       static pstring trunc_cmd;
+       char *ptr;
+       int argcl;
+       char **argl = NULL;
+       int i;
+
+       pstrcpy(trunc_cmd, command);
+
+       if(!(ptr = strtok(trunc_cmd, " \t"))) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /*
+        * Count the args.
+        */
+
+       for( argcl = 1; ptr; ptr = strtok(NULL, " \t"))
+               argcl++;
+
+       if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL)
+               return NULL;
+
+       /*
+        * Now do the extraction.
+        */
+
+       pstrcpy(trunc_cmd, command);
+
+       ptr = strtok(trunc_cmd, " \t");
+       i = 0;
+       argl[i++] = ptr;
+
+       while((ptr = strtok(NULL, " \t")) != NULL)
+               argl[i++] = ptr;
+
+       argl[i++] = NULL;
+       return argl;
+}
+
+/**************************************************************************
+ Wrapper for popen. Safer as it doesn't search a path.
+ Modified from the glibc sources.
+ modified by tridge to return a file descriptor. We must kick our FILE* habit
+****************************************************************************/
+
+typedef struct _popen_list
+{
+       int fd;
+       pid_t child_pid;
+       struct _popen_list *next;
+} popen_list;
+
+static popen_list *popen_chain;
+
+int sys_popen(const char *command)
+{
+       int parent_end, child_end;
+       int pipe_fds[2];
+       popen_list *entry = NULL;
+       char **argl = NULL;
+
+       if (pipe(pipe_fds) < 0)
+               return -1;
+
+       parent_end = pipe_fds[0];
+       child_end = pipe_fds[1];
+
+       if (!*command) {
+               errno = EINVAL;
+               goto err_exit;
+       }
+
+       if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL)
+               goto err_exit;
+
+       ZERO_STRUCTP(entry);
+
+       /*
+        * Extract the command and args into a NULL terminated array.
+        */
+
+       if(!(argl = extract_args(command)))
+               goto err_exit;
+
+       entry->child_pid = fork();
+
+       if (entry->child_pid == -1) {
+               goto err_exit;
+       }
+
+       if (entry->child_pid == 0) {
+
+               /*
+                * Child !
+                */
+
+               int child_std_end = STDOUT_FILENO;
+               popen_list *p;
+
+               close(parent_end);
+               if (child_end != child_std_end) {
+                       dup2 (child_end, child_std_end);
+                       close (child_end);
+               }
+
+               /*
+                * POSIX.2:  "popen() shall ensure that any streams from previous
+                * popen() calls that remain open in the parent process are closed
+                * in the new child process."
+                */
+
+               for (p = popen_chain; p; p = p->next)
+                       close(p->fd);
+
+               execv(argl[0], argl);
+               _exit (127);
+       }
+
+       /*
+        * Parent.
+        */
+
+       close (child_end);
+       SAFE_FREE(argl);
+
+       /* Link into popen_chain. */
+       entry->next = popen_chain;
+       popen_chain = entry;
+       entry->fd = parent_end;
+
+       return entry->fd;
+
+err_exit:
+
+       SAFE_FREE(entry);
+       SAFE_FREE(argl);
+       close(pipe_fds[0]);
+       close(pipe_fds[1]);
+       return -1;
+}
+
+/**************************************************************************
+ Wrapper for pclose. Modified from the glibc sources.
+****************************************************************************/
+
+int sys_pclose(int fd)
+{
+       int wstatus;
+       popen_list **ptr = &popen_chain;
+       popen_list *entry = NULL;
+       pid_t wait_pid;
+       int status = -1;
+
+       /* Unlink from popen_chain. */
+       for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
+               if ((*ptr)->fd == fd) {
+                       entry = *ptr;
+                       *ptr = (*ptr)->next;
+                       status = 0;
+                       break;
+               }
+       }
+
+       if (status < 0 || close(entry->fd) < 0)
+               return -1;
+
+       /*
+        * As Samba is catching and eating child process
+        * exits we don't really care about the child exit
+        * code, a -1 with errno = ECHILD will do fine for us.
+        */
+
+       do {
+               wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0);
+       } while (wait_pid == -1 && errno == EINTR);
+
+       SAFE_FREE(entry);
+
+       if (wait_pid == -1)
+               return -1;
+       return wstatus;
+}
+
+/**************************************************************************
+ Wrappers for dlopen, dlsym, dlclose.
+****************************************************************************/
+
+void *sys_dlopen(const char *name, int flags)
+{
+#if defined(HAVE_DLOPEN)
+       return dlopen(name, flags);
+#else
+       return NULL;
+#endif
+}
+
+void *sys_dlsym(void *handle, const char *symbol)
+{
+#if defined(HAVE_DLSYM)
+    return dlsym(handle, symbol);
+#else
+    return NULL;
+#endif
+}
+
+int sys_dlclose (void *handle)
+{
+#if defined(HAVE_DLCLOSE)
+       return dlclose(handle);
+#else
+       return 0;
+#endif
+}
+
+const char *sys_dlerror(void)
+{
+#if defined(HAVE_DLERROR)
+       return dlerror();
+#else
+       return NULL;
+#endif
+}
+
+int sys_dup2(int oldfd, int newfd) 
+{
+#if defined(HAVE_DUP2)
+       return dup2(oldfd, newfd);
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
diff --git a/source4/lib/system_smbd.c b/source4/lib/system_smbd.c
new file mode 100644 (file)
index 0000000..3ae0a63
--- /dev/null
@@ -0,0 +1,119 @@
+/* 
+   Unix SMB/CIFS implementation.
+   system call wrapper interface.
+   Copyright (C) Andrew Tridgell 2002
+   Copyright (C) Andrew Barteltt 2002
+
+   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.
+*/
+
+/* 
+   This file may assume linkage with smbd - for things like become_root()
+   etc. 
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_GETGROUPLIST
+/*
+  This is a *much* faster way of getting the list of groups for a user
+  without changing the current supplemenrary group list. The old
+  method used getgrent() which could take 20 minutes on a really big
+  network with hundeds of thousands of groups and users. The new method
+  takes a couple of seconds.
+
+  NOTE!! this function only works if it is called as root!
+  */
+static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+       gid_t *gids_saved;
+       int ret, ngrp_saved, num_gids;
+
+       if (non_root_mode()) {
+               *grpcnt = 0;
+               return 0;
+       }
+
+       /* work out how many groups we need to save */
+       ngrp_saved = getgroups(0, NULL);
+       if (ngrp_saved == -1) {
+               /* this shouldn't happen */
+               return -1;
+       }
+       
+       gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1));
+       if (!gids_saved) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       ngrp_saved = getgroups(ngrp_saved, gids_saved);
+       if (ngrp_saved == -1) {
+               SAFE_FREE(gids_saved);
+               /* very strange! */
+               return -1;
+       }
+
+       if (initgroups(user, gid) != 0) {
+               DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n"));
+               SAFE_FREE(gids_saved);
+               return -1;
+       }
+
+       /* this must be done to cope with systems that put the current egid in the
+          return from getgroups() */
+       save_re_gid();
+       set_effective_gid(gid);
+       setgid(gid);
+
+       num_gids = getgroups(0, NULL);
+       if (num_gids + 1 > *grpcnt) {
+               *grpcnt = num_gids + 1;
+               ret = -1;
+       } else {
+               ret = getgroups(*grpcnt - 1, &groups[1]);
+               if (ret >= 0) {
+                       groups[0] = gid;
+                       *grpcnt = ret + 1;
+               }
+       }
+
+       restore_re_gid();
+
+       if (setgroups(ngrp_saved, gids_saved) != 0) {
+               /* yikes! */
+               DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
+               smb_panic("getgrouplist: failed to reset group list!\n");
+               free(gids_saved);
+               return -1;
+       }
+
+       free(gids_saved);
+       return ret;
+}
+#endif
+
+int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+#ifdef HAVE_GETGROUPLIST
+       return getgrouplist(user, gid, groups, grpcnt);
+#else
+       int retval;
+       become_root();
+       retval = getgrouplist_internals(user, gid, groups, grpcnt);
+       unbecome_root();
+       return retval;
+#endif
+}
diff --git a/source4/lib/talloc.c b/source4/lib/talloc.c
new file mode 100644 (file)
index 0000000..83a99d8
--- /dev/null
@@ -0,0 +1,515 @@
+/* 
+   Samba Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
+   
+   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.
+*/
+
+/**
+   @defgroup talloc Simple memory allocator
+   @{
+   
+   This is a very simple temporary memory allocator. To use it do the following:
+
+   1) when you first want to allocate a pool of meomry use
+   talloc_init() and save the resulting context pointer somewhere
+
+   2) to allocate memory use talloc()
+
+   3) when _all_ of the memory allocated using this context is no longer needed
+   use talloc_destroy()
+
+   talloc does not zero the memory. It guarantees memory of a
+   TALLOC_ALIGN alignment
+
+   @sa talloc.h
+*/
+
+/**
+ * @todo We could allocate both the talloc_chunk structure, and the
+ * memory it contains all in one allocation, which might be a bit
+ * faster and perhaps use less memory overhead.
+ *
+ * That smells like a premature optimization, though.  -- mbp
+ **/
+
+/**
+ * If you want testing for memory corruption, link with dmalloc or use
+ * Insure++.  It doesn't seem useful to duplicate them here.
+ **/
+
+#include "includes.h"
+
+struct talloc_chunk {
+       struct talloc_chunk *next;
+       size_t size;
+       void *ptr;
+};
+
+
+struct talloc_ctx {
+       struct talloc_chunk *list;
+       off_t total_alloc_size;
+
+       /** The name recorded for this pool, if any.  Should describe
+        * the purpose for which it was allocated.  The string is
+        * allocated within the pool. **/
+       char *name;
+
+       /** Pointer to the next allocate talloc pool, so that we can
+        * summarize all talloc memory usage. **/
+       struct talloc_ctx *next, *prev;
+};
+
+
+/**
+ * Start of linked list of all talloc pools.
+ *
+ * @todo We should turn the global list off when using Insure++,
+ * otherwise all the memory will be seen as still reachable.
+ **/
+static TALLOC_CTX *list_head;
+
+/**
+ * Add to the global list
+ **/
+static void talloc_enroll(TALLOC_CTX *t)
+{
+#if 0
+       /* disabled enrole/disenrole until we have __thread support */
+       MUTEX_LOCK_BY_ID(MUTEX_TALLOC);
+       DLIST_ADD(list_head, t);
+       MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC);
+#endif
+}
+
+
+static void talloc_disenroll(TALLOC_CTX *t)
+{
+#if 0
+       /* disabled enrole/disenrole until we have __thread support */
+       MUTEX_LOCK_BY_ID(MUTEX_TALLOC);
+       DLIST_REMOVE(list_head, t);
+       MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC);
+#endif
+}
+
+
+/** Create a new talloc context. **/
+static TALLOC_CTX *talloc_init_internal(void)
+{
+       TALLOC_CTX *t;
+
+       t = (TALLOC_CTX *)malloc(sizeof(TALLOC_CTX));
+       if (t) {
+               t->list = NULL;
+               t->total_alloc_size = 0;
+               t->name = NULL;
+               talloc_enroll(t);
+       }
+
+       return t;
+}
+
+
+
+/**
+ * Create a new talloc context, with a name specifying its purpose.
+ **/
+
+ TALLOC_CTX *talloc_init(char const *fmt, ...) 
+{
+       TALLOC_CTX *t;
+       va_list ap;
+
+       t = talloc_init_internal();
+       if (t && fmt) {
+               /*
+                * t->name must not be talloced.
+                * as destroying the pool would destroy it. JRA.
+                */
+               t->name = NULL;
+               va_start(ap, fmt);
+               vasprintf(&t->name, fmt, ap);
+               va_end(ap);
+               if (!t->name) {
+                       talloc_destroy(t);
+                       t = NULL;
+               }
+       }
+       
+       return t;
+}
+
+
+/** Allocate a bit of memory from the specified pool **/
+void *talloc(TALLOC_CTX *t, size_t size)
+{
+       void *p;
+       struct talloc_chunk *tc;
+
+       if (!t || size == 0) return NULL;
+
+       p = malloc(size);
+       if (p) {
+               tc = malloc(sizeof(*tc));
+               if (tc) {
+                       tc->ptr = p;
+                       tc->size = size;
+                       tc->next = t->list;
+                       t->list = tc;
+                       t->total_alloc_size += size;
+               }
+               else {
+                       SAFE_FREE(p);
+               }
+       }
+       return p;
+}
+
+/** A talloc version of realloc */
+void *talloc_realloc(TALLOC_CTX *t, void *ptr, size_t size)
+{
+       struct talloc_chunk *tc;
+       void *new_ptr;
+
+       /* size zero is equivalent to free() */
+       if (!t || size == 0)
+               return NULL;
+
+       /* realloc(NULL) is equavalent to malloc() */
+       if (ptr == NULL)
+               return talloc(t, size);
+
+       for (tc=t->list; tc; tc=tc->next) {
+               if (tc->ptr == ptr) {
+                       new_ptr = Realloc(ptr, size);
+                       if (new_ptr) {
+                               t->total_alloc_size += (size - tc->size);
+                               tc->size = size;
+                               tc->ptr = new_ptr;
+                       }
+                       return new_ptr;
+               }
+       }
+       return NULL;
+}
+
+/** Destroy all the memory allocated inside @p t, but not @p t
+ * itself. */
+void talloc_destroy_pool(TALLOC_CTX *t)
+{
+       struct talloc_chunk *c;
+       
+       if (!t)
+               return;
+
+       while (t->list) {
+               c = t->list->next;
+               SAFE_FREE(t->list->ptr);
+               SAFE_FREE(t->list);
+               t->list = c;
+       }
+
+       t->total_alloc_size = 0;
+}
+
+/** Destroy a whole pool including the context */
+void talloc_destroy(TALLOC_CTX *t)
+{
+       if (!t)
+               return;
+
+       talloc_destroy_pool(t);
+       talloc_disenroll(t);
+       SAFE_FREE(t->name);
+       SAFE_FREE(t);
+}
+
+/** Return the current total size of the pool. */
+size_t talloc_pool_size(TALLOC_CTX *t)
+{
+       return t->total_alloc_size;
+}
+
+const char *talloc_pool_name(TALLOC_CTX const *t)
+{
+       if (t) return t->name;
+
+       return NULL;
+}
+
+
+/** talloc and zero memory. */
+void *talloc_zero(TALLOC_CTX *t, size_t size)
+{
+       void *p = talloc(t, size);
+
+       if (p) {
+               memset(p, '\0', size);
+       }
+
+       return p;
+}
+
+
+/** memdup with a talloc. */
+void *talloc_memdup(TALLOC_CTX *t, const void *p, size_t size)
+{
+       void *newp = talloc(t,size);
+
+       if (newp) {
+               memcpy(newp, p, size);
+       }
+
+       return newp;
+}
+
+/** strdup with a talloc */
+char *talloc_strdup(TALLOC_CTX *t, const char *p)
+{
+       return talloc_memdup(t, p, strlen(p) + 1);
+}
+
+/** strndup with a talloc */
+char *talloc_strndup(TALLOC_CTX *t, const char *p, size_t n)
+{
+       size_t len = strnlen(p, n);
+       char *ret;
+
+       ret = talloc(t, len + 1);
+       if (!ret) { return NULL; }
+       memcpy(ret, p, len);
+       ret[len] = 0;
+       return ret;
+}
+
+/** strdup_w with a talloc */
+smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p)
+{
+       if (p)
+               return talloc_memdup(t, p, (strlen_w(p) + 1) * sizeof(smb_ucs2_t));
+       return NULL;
+}
+
+/**
+ * Perform string formatting, and return a pointer to newly allocated
+ * memory holding the result, inside a memory pool.
+ **/
+ char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+
+       va_start(ap, fmt);
+       ret = talloc_vasprintf(t, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+
+ char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+{      
+       int len;
+       char *ret;
+       va_list ap2;
+       
+       VA_COPY(ap2, ap);
+
+       len = vsnprintf(NULL, 0, fmt, ap2);
+
+       ret = talloc(t, len+1);
+       if (ret) {
+               VA_COPY(ap2, ap);
+               vsnprintf(ret, len+1, fmt, ap2);
+       }
+
+       return ret;
+}
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and return @p
+ * s, which may have moved.  Good for gradually accumulating output
+ * into a string buffer.
+ **/
+ char *talloc_asprintf_append(TALLOC_CTX *t, char *s,
+                             const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       s = talloc_vasprintf_append(t, s, fmt, ap);
+       va_end(ap);
+       return s;
+}
+
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved.  Good for gradually
+ * accumulating output into a string buffer.
+ **/
+ char *talloc_vasprintf_append(TALLOC_CTX *t, char *s,
+                              const char *fmt, va_list ap)
+{      
+       int len, s_len;
+       va_list ap2;
+
+       VA_COPY(ap2, ap);
+
+       s_len = strlen(s);
+       len = vsnprintf(NULL, 0, fmt, ap2);
+
+       s = talloc_realloc(t, s, s_len + len+1);
+       if (!s) return NULL;
+
+       VA_COPY(ap2, ap);
+
+       vsnprintf(s+s_len, len+1, fmt, ap2);
+
+       return s;
+}
+
+
+/**
+ * Return a human-readable description of all talloc memory usage.
+ * The result is allocated from @p t.
+ **/
+char *talloc_describe_all(TALLOC_CTX *rt)
+{
+       int n_pools = 0, total_chunks = 0;
+       size_t total_bytes = 0;
+       TALLOC_CTX *it;
+       char *s;
+
+       if (!rt) return NULL;
+
+       s = talloc_asprintf(rt, "global talloc allocations in pid: %u\n",
+                           (unsigned) getpid());
+       s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+                                  "name", "chunks", "bytes");
+       s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+                                  "----------------------------------------",
+                                  "--------",
+                                  "--------"); 
+       MUTEX_LOCK_BY_ID(MUTEX_TALLOC);
+       
+       for (it = list_head; it; it = it->next) {
+               size_t bytes;
+               int n_chunks;
+               fstring what;
+               
+               n_pools++;
+               
+               talloc_get_allocation(it, &bytes, &n_chunks);
+
+               if (it->name)
+                       fstrcpy(what, it->name);
+               else
+                       slprintf(what, sizeof(what), "@%p", it);
+               
+               s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+                                          what,
+                                          (unsigned) n_chunks,
+                                          (unsigned) bytes);
+               total_bytes += bytes;
+               total_chunks += n_chunks;
+       }
+
+       MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC);
+
+       s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+                                  "----------------------------------------",
+                                  "--------",
+                                  "--------"); 
+
+       s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+                                  "TOTAL",
+                                  (unsigned) total_chunks, (unsigned) total_bytes);
+
+       return s;
+}
+
+
+
+/**
+ * Return an estimated memory usage for the specified pool.  This does
+ * not include memory used by the underlying malloc implementation.
+ **/
+void talloc_get_allocation(TALLOC_CTX *t,
+                          size_t *total_bytes,
+                          int *n_chunks)
+{
+       struct talloc_chunk *chunk;
+
+       if (t) {
+               *total_bytes = 0;
+               *n_chunks = 0;
+
+               for (chunk = t->list; chunk; chunk = chunk->next) {
+                       n_chunks[0]++;
+                       *total_bytes += chunk->size;
+               }
+       }
+}
+
+
+/* 
+   move a lump of memory from one talloc context to another
+   return the ptr on success, or NULL if it could not be found
+   in the old context or could not be transferred
+*/
+const void *talloc_steal(TALLOC_CTX *old_ctx, TALLOC_CTX *new_ctx, const void *ptr)
+{
+       struct talloc_chunk *tc, *tc2;
+
+       if (!ptr || !old_ctx->list) return NULL;
+
+       /* as a special case, see if its the first element in the
+          list */
+       if (old_ctx->list->ptr == ptr) {
+               tc = old_ctx->list;
+               old_ctx->list = old_ctx->list->next;
+               tc->next = new_ctx->list;
+               new_ctx->list = tc;
+               old_ctx->total_alloc_size -= tc->size;
+               new_ctx->total_alloc_size += tc->size;
+               return ptr;
+       }
+
+       /* find it in the old context */
+       for (tc=old_ctx->list; tc->next; tc=tc->next) {
+               if (tc->next->ptr == ptr) break;
+       }
+
+       if (!tc->next) return NULL;
+
+       /* move it to the new context */
+       tc2 = tc->next;
+       tc->next = tc->next->next;
+       tc2->next = new_ctx->list;
+       new_ctx->list = tc2;
+       old_ctx->total_alloc_size -= tc2->size;
+       new_ctx->total_alloc_size += tc2->size;
+       
+       return ptr;
+}
+
+
+/** @} */
diff --git a/source4/lib/tallocmsg.c b/source4/lib/tallocmsg.c
new file mode 100644 (file)
index 0000000..bbe1ee6
--- /dev/null
@@ -0,0 +1,58 @@
+/* 
+   samba -- Unix SMB/CIFS implementation.
+   Copyright (C) 2001, 2002 by Martin Pool
+   
+   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"
+
+/**
+ * @file tallocmsg.c
+ *
+ * Glue code between talloc profiling and the Samba messaging system.
+ **/
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+void msg_pool_usage(int msg_type, pid_t src_pid,
+                   void *UNUSED(buf), size_t UNUSED(len))
+{
+       char *reply;
+       TALLOC_CTX *reply_pool = talloc_init("msg_pool_usage");
+
+       SMB_ASSERT(msg_type == MSG_REQ_POOL_USAGE);
+       
+       DEBUG(2,("Got POOL_USAGE\n"));
+
+       reply = talloc_describe_all(reply_pool);
+       
+       message_send_pid(src_pid, MSG_POOL_USAGE,
+                        reply, strlen(reply)+1, True);
+
+       talloc_destroy(reply_pool);
+}
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_msg_pool_usage(void)
+{
+       message_register(MSG_REQ_POOL_USAGE, msg_pool_usage);
+       DEBUG(2, ("Registered MSG_REQ_POOL_USAGE\n"));
+}      
diff --git a/source4/lib/talloctort.c b/source4/lib/talloctort.c
new file mode 100644 (file)
index 0000000..ad5de38
--- /dev/null
@@ -0,0 +1,65 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba temporary memory allocation functions -- torturer
+   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+   
+   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"
+
+#define NCTX 10
+#define NOBJ 20
+
+int main(void)
+{
+       int i;
+       TALLOC_CTX *ctx[NCTX];
+
+       for (i = 0; i < NCTX; i++) {
+               ctx[i] = talloc_init("torture(%d)", i);
+       }
+
+       for (i = 0; i < NCTX; i++) {
+               int j;
+               for (j = 0; j < NOBJ; j++) {
+                       char *p;
+                       size_t size = 1<<(i/3+j);
+
+                       p = talloc(ctx[i], size);
+                       if (!p) {
+                               fprintf(stderr,
+                                       "failed to talloc %.0f bytes\n",
+                                       (double) size);
+                               exit(1);
+                       }
+
+                       memset(p, 'A' + j, size);
+               }
+       }
+
+       for (i = 0; i < NCTX; i++) {
+               printf("talloc@%p %-40s %dkB\n", ctx[i],
+                      talloc_pool_name(ctx[i]),
+                      talloc_pool_size(ctx[i]) >> 10);
+       }
+
+       printf("%s", talloc_describe_all(ctx[0]));
+
+       for (i = NCTX - 1; i >= 0; i--)
+               talloc_destroy(ctx[i]);
+
+       return 0;
+}
diff --git a/source4/lib/tdb/README b/source4/lib/tdb/README
new file mode 100644 (file)
index 0000000..fac3eac
--- /dev/null
@@ -0,0 +1,167 @@
+tdb - a trivial database system
+tridge@linuxcare.com December 1999
+==================================
+
+This is a simple database API. It was inspired by the realisation that
+in Samba we have several ad-hoc bits of code that essentially
+implement small databases for sharing structures between parts of
+Samba. As I was about to add another I realised that a generic
+database module was called for to replace all the ad-hoc bits.
+
+I based the interface on gdbm. I couldn't use gdbm as we need to be
+able to have multiple writers to the databases at one time.
+
+Compilation
+-----------
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add TDB_DEBUG=1 for verbose debug info
+add NOLOCK=1 to disable locking code
+
+Testing
+-------
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+Interface
+---------
+
+The interface is very similar to gdbm except for the following:
+
+- different open interface. The tdb_open call is more similar to a
+  traditional open()
+- no tdbm_reorganise() function
+- no tdbm_sync() function. No operations are cached in the library anyway
+- added a tdb_traverse() function for traversing the whole database
+
+A general rule for using tdb is that the caller frees any returned
+TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
+return value called p. This is the same as gdbm.
+
+here is a full list of tdb functions with brief descriptions.
+
+
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+
+   open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the database
+   file. A flags value of O_WRONLY is invalid
+
+   The hash size is advisory, use zero for a default value. 
+
+   return is NULL on error
+
+   possible tdb_flags are:
+    TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
+    TDB_INTERNAL - don't use a file, instaed store the data in
+                   memory. The filename is ignored in this case.
+    TDB_NOLOCK - don't do any locking
+    TDB_NOMMAP - don't use mmap
+
+----------------------------------------------------------------------
+char *tdb_error(TDB_CONTEXT *tdb);
+
+     return a error string for the last tdb error
+
+----------------------------------------------------------------------
+int tdb_close(TDB_CONTEXT *tdb);
+
+   close a database
+
+----------------------------------------------------------------------
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
+
+   update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1
+
+----------------------------------------------------------------------
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   fetch an entry in the database given a key 
+   if the return value has a null dptr then a error occurred
+
+   caller must free the resulting data
+
+----------------------------------------------------------------------
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+
+----------------------------------------------------------------------
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
+                 TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
+
+   traverse the entire database - calling fn(tdb, key, data, state) on each 
+   element.
+
+   return -1 on error or the record count traversed
+
+   if fn is NULL then it is not called
+
+   a non-zero return value from fn() indicates that the traversal should stop
+
+----------------------------------------------------------------------
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+
+   find the first entry in the database and return its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   find the next entry in the database, returning its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   delete an entry in the database given a key
+
+----------------------------------------------------------------------
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+   store an element in the database, replacing any existing element
+   with the same key 
+
+   If flag==TDB_INSERT then don't overwrite an existing entry
+   If flag==TDB_MODIFY then don't create a new entry
+
+   return 0 on success, -1 on failure
+
+----------------------------------------------------------------------
+int tdb_writelock(TDB_CONTEXT *tdb);
+
+   lock the database. If we already have it locked then don't do anything
+
+----------------------------------------------------------------------
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+   unlock the database
+
+----------------------------------------------------------------------
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   lock one hash chain. This is meant to be used to reduce locking
+   contention - it cannot guarantee how many records will be locked
+
+----------------------------------------------------------------------
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   unlock one hash chain
diff --git a/source4/lib/tdb/spinlock.c b/source4/lib/tdb/spinlock.c
new file mode 100644 (file)
index 0000000..2370ce3
--- /dev/null
@@ -0,0 +1,430 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Anton Blanchard                   2001
+   
+   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.
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if STANDALONE
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+#else
+#include "includes.h"
+#endif
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int result;
+
+       asm volatile("ldstub    [%1], %0"
+               : "=r" (result)
+               : "r" (lock)
+               : "memory");
+
+       return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int result;
+
+       __asm__ __volatile__(
+"1:    lwarx           %0,0,%1\n\
+       cmpwi           0,%0,0\n\
+       li              %0,0\n\
+       bne-            2f\n\
+       li              %0,1\n\
+       stwcx.          %0,0,%1\n\
+       bne-            1b\n\
+       isync\n\
+2:"    : "=&r"(result)
+       : "r"(lock)
+       : "cr0", "memory");
+
+       return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("eieio":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       int oldval;
+
+       asm volatile("xchgl %0,%1"
+               : "=r" (oldval), "=m" (*lock)
+               : "0" (0)
+               : "memory");
+
+       return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) 
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+       unsigned int res;
+
+       __asm__ __volatile__("ll\t%0,(%1)"
+               : "=r" (res)
+               : "r" (addr));
+
+       return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+       unsigned int res;
+
+       __asm__ __volatile__("sc\t%0,(%2)"
+               : "=r" (res)
+               : "0" (value), "r" (addr));
+       return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int mw;
+
+       do {
+               mw = load_linked(lock);
+               if (mw) 
+                       return EBUSY;
+       } while (!store_conditional(lock, 1));
+
+       asm volatile("":::"memory");
+
+       return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+       struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+       sched_yield();
+#else
+       /* Linux will busy loop for delays < 2ms on real time tasks */
+       tm.tv_sec = 0;
+       tm.tv_nsec = 2000000L + 1;
+       nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+       return 0;
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+       int ntries = 0;
+
+       while(__spin_trylock(lock)) {
+               while(__spin_is_locked(lock)) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+       int ntries = 0;
+
+       while(1) {
+               __spin_lock(&rwlock->lock);
+
+               if (!(rwlock->count & RWLOCK_BIAS)) {
+                       rwlock->count++;
+                       __spin_unlock(&rwlock->lock);
+                       return;
+               }
+       
+               __spin_unlock(&rwlock->lock);
+
+               while(rwlock->count & RWLOCK_BIAS) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+       int ntries = 0;
+
+       while(1) {
+               __spin_lock(&rwlock->lock);
+
+               if (rwlock->count == 0) {
+                       rwlock->count |= RWLOCK_BIAS;
+                       __spin_unlock(&rwlock->lock);
+                       return;
+               }
+
+               __spin_unlock(&rwlock->lock);
+
+               while(rwlock->count != 0) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+       __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+       if (!(rwlock->count & RWLOCK_BIAS))
+               fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+       rwlock->count &= ~RWLOCK_BIAS;
+       __spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+       __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+       if (!rwlock->count)
+               fprintf(stderr, "bug: read_unlock\n");
+
+       if (rwlock->count & RWLOCK_BIAS)
+               fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+       rwlock->count--;
+       __spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+       tdb_rwlock_t *rwlocks;
+
+       if (!tdb->map_ptr) return -1;
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+       switch(rw_type) {
+       case F_RDLCK:
+               __read_lock(&rwlocks[list+1]);
+               break;
+
+       case F_WRLCK:
+               __write_lock(&rwlocks[list+1]);
+               break;
+
+       default:
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+       return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+       tdb_rwlock_t *rwlocks;
+
+       if (!tdb->map_ptr) return -1;
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+       switch(rw_type) {
+       case F_RDLCK:
+               __read_unlock(&rwlocks[list+1]);
+               break;
+
+       case F_WRLCK:
+               __write_unlock(&rwlocks[list+1]);
+               break;
+
+       default:
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+
+       return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+       unsigned size, i;
+       tdb_rwlock_t *rwlocks;
+
+       size = (hash_size + 1) * sizeof(tdb_rwlock_t);
+       rwlocks = malloc(size);
+       if (!rwlocks)
+               return -1;
+
+       for(i = 0; i < hash_size+1; i++) {
+               __spin_lock_init(&rwlocks[i].lock);
+               rwlocks[i].count = 0;
+       }
+
+       /* Write it out (appending to end) */
+       if (write(fd, rwlocks, size) != size) {
+               free(rwlocks);
+               return -1;
+       }
+       smp_machine = this_is_smp();
+       free(rwlocks);
+       return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+       tdb_rwlock_t *rwlocks;
+       unsigned i;
+
+       if (tdb->header.rwlocks == 0) return 0;
+       if (!tdb->map_ptr) return -1;
+
+       /* We're mmapped here */
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+       for(i = 0; i < tdb->header.hash_size+1; i++) {
+               __spin_lock_init(&rwlocks[i].lock);
+               rwlocks[i].count = 0;
+       }
+       return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+       tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+                               - (char *)&tdb->header);
+
+       tdb->header.rwlocks = 0;
+       if (lseek(tdb->fd, off, SEEK_SET) != off
+           || write(tdb->fd, (void *)&tdb->header.rwlocks,
+                    sizeof(tdb->header.rwlocks)) 
+           != sizeof(tdb->header.rwlocks))
+               return -1;
+       return 0;
+}
+#endif
diff --git a/source4/lib/tdb/spinlock.h b/source4/lib/tdb/spinlock.h
new file mode 100644 (file)
index 0000000..d6a2ac6
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+       spinlock_t lock;
+       volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#endif
+
+#endif
diff --git a/source4/lib/tdb/tdb.c b/source4/lib/tdb/tdb.c
new file mode 100644 (file)
index 0000000..097209f
--- /dev/null
@@ -0,0 +1,2020 @@
+ /* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell              1999-2000
+   Copyright (C) Luke Kenneth Casson Leighton      2000
+   Copyright (C) Paul `Rusty' Russell             2000
+   Copyright (C) Jeremy Allison                           2000-2003
+   
+   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.
+*/
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+#else
+#include "includes.h"
+#endif
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
+#define TDB_PAGE_SIZE 0x2000
+#define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+       if (tdb->flags & TDB_INTERNAL)
+               return 0;
+
+#ifdef HAVE_MMAP
+       if (tdb->map_ptr) {
+               int ret = munmap(tdb->map_ptr, tdb->map_size);
+               if (ret != 0)
+                       return ret;
+       }
+#endif
+       tdb->map_ptr = NULL;
+       return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+       if (tdb->flags & TDB_INTERNAL)
+               return;
+
+#ifdef HAVE_MMAP
+       if (!(tdb->flags & TDB_NOMMAP)) {
+               tdb->map_ptr = mmap(NULL, tdb->map_size, 
+                                   PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
+                                   MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+               /*
+                * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+                */
+
+               if (tdb->map_ptr == MAP_FAILED) {
+                       tdb->map_ptr = NULL;
+                       TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", 
+                                tdb->map_size, strerror(errno)));
+               }
+       } else {
+               tdb->map_ptr = NULL;
+       }
+#else
+       tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+       u32 i, *p = buf;
+       for (i = 0; i < size / 4; i++)
+               p[i] = TDB_BYTEREV(p[i]);
+       return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
+/* the body of the database is made of one list_struct for the free space
+   plus a separate data list for each hash value */
+struct list_struct {
+       tdb_off next; /* offset of the next record in the list */
+       tdb_len rec_len; /* total byte length of record */
+       tdb_len key_len; /* byte length of key */
+       tdb_len data_len; /* byte length of data */
+       u32 full_hash; /* the full 32 bit hash of the key */
+       u32 magic;   /* try to catch errors */
+       /* the following union is implied:
+               union {
+                       char record[rec_len];
+                       struct {
+                               char key[key_len];
+                               char data[data_len];
+                       }
+                       u32 totalsize; (tailer)
+               }
+       */
+};
+
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+       palarm_fired = palarm;
+}
+
+/* a byte range locking function - return 0 on success
+   this functions locks/unlocks 1 byte at the specified offset.
+
+   On error, errno is also set so that errors are passed back properly
+   through tdb_open(). */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, 
+                     int rw_type, int lck_type, int probe)
+{
+       struct flock fl;
+       int ret;
+
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+       if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+               errno = EACCES;
+               return -1;
+       }
+
+       fl.l_type = rw_type;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = offset;
+       fl.l_len = 1;
+       fl.l_pid = 0;
+
+       do {
+               ret = fcntl(tdb->fd,lck_type,&fl);
+               if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+                       break;
+       } while (ret == -1 && errno == EINTR);
+
+       if (ret == -1) {
+               if (!probe && lck_type != F_SETLK) {
+                       /* Ensure error code is set for log fun to examine. */
+                       if (errno == EINTR && palarm_fired && *palarm_fired)
+                               tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+                       else
+                               tdb->ecode = TDB_ERR_LOCK;
+                       TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+                                tdb->fd, offset, rw_type, lck_type));
+               }
+               /* Was it an alarm timeout ? */
+               if (errno == EINTR && palarm_fired && *palarm_fired)
+                       return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+               /* Otherwise - generic lock error. */
+               /* errno set by fcntl */
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+       return 0;
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+               TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", 
+                          list, ltype));
+               return -1;
+       }
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+
+       /* Since fcntl locks don't nest, we do a lock for the first one,
+          and simply bump the count for future ones */
+       if (tdb->locked[list+1].count == 0) {
+               if (!tdb->read_only && tdb->header.rwlocks) {
+                       if (tdb_spinlock(tdb, list, ltype)) {
+                               TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list ltype=%d\n", 
+                                          list, ltype));
+                               return -1;
+                       }
+               } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+                       TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", 
+                                          list, ltype, strerror(errno)));
+                       return -1;
+               }
+               tdb->locked[list+1].ltype = ltype;
+       }
+       tdb->locked[list+1].count++;
+       return 0;
+}
+
+/* unlock the database: returns void because it's too late for errors. */
+       /* changed to return int it may be interesting to know there
+          has been an error  --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+       int ret = -1;
+
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+
+       /* Sanity checks */
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+               TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+               return ret;
+       }
+
+       if (tdb->locked[list+1].count==0) {
+               TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+               return ret;
+       }
+
+       if (tdb->locked[list+1].count == 1) {
+               /* Down to last nested lock: unlock underneath */
+               if (!tdb->read_only && tdb->header.rwlocks) {
+                       ret = tdb_spinunlock(tdb, list, ltype);
+               } else {
+                       ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+               }
+       } else {
+               ret = 0;
+       }
+       tdb->locked[list+1].count--;
+
+       if (ret)
+               TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); 
+       return ret;
+}
+
+/* This is based on the hash algorithm from gdbm */
+static u32 tdb_hash(TDB_DATA *key)
+{
+       u32 value;      /* Used to compute the hash value.  */
+       u32   i;        /* Used to cycle through random values. */
+
+       /* Set the initial value from the key size. */
+       for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+               value = (value + (key->dptr[i] << (i*5 % 24)));
+
+       return (1103515243 * value + 12345);  
+}
+
+/* check for an out of bounds access - if it is out of bounds then
+   see if the database has been expanded by someone else and expand
+   if necessary 
+   note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
+{
+       struct stat st;
+       if (len <= tdb->map_size)
+               return 0;
+       if (tdb->flags & TDB_INTERNAL) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+                                (int)len, (int)tdb->map_size));
+               }
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+
+       if (fstat(tdb->fd, &st) == -1)
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+       if (st.st_size < (size_t)len) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+                                (int)len, (int)st.st_size));
+               }
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+
+       /* Unmap, update size, remap */
+       if (tdb_munmap(tdb) == -1)
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       tdb->map_size = st.st_size;
+       tdb_mmap(tdb);
+       return 0;
+}
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
+{
+       if (tdb_oob(tdb, off + len, 0) != 0)
+               return -1;
+
+       if (tdb->map_ptr)
+               memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+       else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+       else if (lseek(tdb->fd, off, SEEK_SET) != off
+                || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+                          off, len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+       return 0;
+}
+
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
+{
+       if (tdb_oob(tdb, off + len, 0) != 0)
+               return -1;
+
+       if (tdb->map_ptr)
+               memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+       else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+       else if (lseek(tdb->fd, off, SEEK_SET) != off
+                || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+                          off, len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+       if (cv)
+               convert(buf, len);
+       return 0;
+}
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+       char *buf;
+
+       if (!(buf = malloc(len))) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_OOM;
+               TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+                          len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_OOM, buf);
+       }
+       if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       return buf;
+}
+
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
+}
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       tdb_off off = *d;
+       return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
+}
+
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+               return -1;
+       if (TDB_BAD_MAGIC(rec)) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_CORRUPT;
+               TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+               return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+       }
+       return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       struct list_struct r = *rec;
+       return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
+}
+
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
+{
+       if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
+               return -1;
+
+       if (rec->magic == TDB_MAGIC) {
+               /* this happens when a app is showdown while deleting a record - we should
+                  not completely fail when this happens */
+               TDB_LOG((tdb, 0,"rec_free_read non-free magic at offset=%d - fixing\n", 
+                        rec->magic, off));
+               rec->magic = TDB_FREE_MAGIC;
+               if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+                       return -1;
+       }
+
+       if (rec->magic != TDB_FREE_MAGIC) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_CORRUPT;
+               TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", 
+                          rec->magic, off));
+               return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+       }
+       if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+               return -1;
+       return 0;
+}
+
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+                        const struct list_struct *rec)
+{
+       tdb_off totalsize;
+
+       /* Offset of tailer from record header */
+       totalsize = sizeof(*rec) + rec->rec_len;
+       return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+                        &totalsize);
+}
+
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+       struct list_struct rec;
+       tdb_off tailer_ofs, tailer;
+
+       if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+               printf("ERROR: failed to read record at %u\n", offset);
+               return 0;
+       }
+
+       printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+              offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
+
+       tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+       if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+               printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+               return rec.next;
+       }
+
+       if (tailer != rec.rec_len + sizeof(rec)) {
+               printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+                               (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
+       }
+       return rec.next;
+}
+
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+       tdb_off rec_ptr, top;
+
+       top = TDB_HASH_TOP(i);
+
+       if (tdb_lock(tdb, i, F_WRLCK) != 0)
+               return -1;
+
+       if (ofs_read(tdb, top, &rec_ptr) == -1)
+               return tdb_unlock(tdb, i, F_WRLCK);
+
+       if (rec_ptr)
+               printf("hash=%d\n", i);
+
+       while (rec_ptr) {
+               rec_ptr = tdb_dump_record(tdb, rec_ptr);
+       }
+
+       return tdb_unlock(tdb, i, F_WRLCK);
+}
+
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+       int i;
+       for (i=0;i<tdb->header.hash_size;i++) {
+               tdb_dump_chain(tdb, i);
+       }
+       printf("freelist:\n");
+       tdb_dump_chain(tdb, -1);
+}
+
+int tdb_printfreelist(TDB_CONTEXT *tdb)
+{
+       int ret;
+       long total_free = 0;
+       tdb_off offset, rec_ptr;
+       struct list_struct rec;
+
+       if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+               return ret;
+
+       offset = FREELIST_TOP;
+
+       /* read in the freelist top */
+       if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+               tdb_unlock(tdb, -1, F_WRLCK);
+               return 0;
+       }
+
+       printf("freelist top=[0x%08x]\n", rec_ptr );
+       while (rec_ptr) {
+               if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return -1;
+               }
+
+               if (rec.magic != TDB_FREE_MAGIC) {
+                       printf("bad magic 0x%08x in free list\n", rec.magic);
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return -1;
+               }
+
+               printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+               total_free += rec.rec_len;
+
+               /* move to the next record */
+               rec_ptr = rec.next;
+       }
+       printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, 
+               (int)total_free);
+
+       return tdb_unlock(tdb, -1, F_WRLCK);
+}
+
+/* Remove an element from the freelist.  Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
+{
+       tdb_off last_ptr, i;
+
+       /* read in the freelist top */
+       last_ptr = FREELIST_TOP;
+       while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+               if (i == off) {
+                       /* We've found it! */
+                       return ofs_write(tdb, last_ptr, &next);
+               }
+               /* Follow chain (next offset is at start of record) */
+               last_ptr = i;
+       }
+       TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+       return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+}
+
+/* Add an element into the freelist. Merge adjacent records if
+   neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       tdb_off right, left;
+
+       /* Allocation and tailer lock */
+       if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+               return -1;
+
+       /* set an initial tailer, so if we fail we don't leave a bogus record */
+       if (update_tailer(tdb, offset, rec) != 0) {
+               TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+               goto fail;
+       }
+
+       /* Look right first (I'm an Australian, dammit) */
+       right = offset + sizeof(*rec) + rec->rec_len;
+       if (right + sizeof(*rec) <= tdb->map_size) {
+               struct list_struct r;
+
+               if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+                       goto left;
+               }
+
+               /* If it's free, expand to include it. */
+               if (r.magic == TDB_FREE_MAGIC) {
+                       if (remove_from_freelist(tdb, right, r.next) == -1) {
+                               TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+                               goto left;
+                       }
+                       rec->rec_len += sizeof(r) + r.rec_len;
+               }
+       }
+
+left:
+       /* Look left */
+       left = offset - sizeof(tdb_off);
+       if (left > TDB_HASH_TOP(tdb->header.hash_size-1)) {
+               struct list_struct l;
+               tdb_off leftsize;
+
+               /* Read in tailer and jump back to header */
+               if (ofs_read(tdb, left, &leftsize) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+                       goto update;
+               }
+               left = offset - leftsize;
+
+               /* Now read in record */
+               if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+                       goto update;
+               }
+
+               /* If it's free, expand to include it. */
+               if (l.magic == TDB_FREE_MAGIC) {
+                       if (remove_from_freelist(tdb, left, l.next) == -1) {
+                               TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+                               goto update;
+                       } else {
+                               offset = left;
+                               rec->rec_len += leftsize;
+                       }
+               }
+       }
+
+update:
+       if (update_tailer(tdb, offset, rec) == -1) {
+               TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+               goto fail;
+       }
+
+       /* Now, prepend to free list */
+       rec->magic = TDB_FREE_MAGIC;
+
+       if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+           rec_write(tdb, offset, rec) == -1 ||
+           ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+               TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+               goto fail;
+       }
+
+       /* And we're done. */
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return -1;
+}
+
+
+/* expand a file.  we prefer to use ftruncate, as that is what posix
+  says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+       char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+       if (ftruncate(tdb->fd, size+addition) != 0) {
+               TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", 
+                          size+addition, strerror(errno)));
+               return -1;
+       }
+#else
+       char b = 0;
+
+#ifdef HAVE_PWRITE
+       if (pwrite(tdb->fd,  &b, 1, (size+addition) - 1) != 1) {
+#else
+       if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || 
+           write(tdb->fd, &b, 1) != 1) {
+#endif
+               TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", 
+                          size+addition, strerror(errno)));
+               return -1;
+       }
+#endif
+
+       /* now fill the file with something. This ensures that the file isn't sparse, which would be
+          very bad if we ran out of disk. This must be done with write, not via mmap */
+       memset(buf, 0x42, sizeof(buf));
+       while (addition) {
+               int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+               int ret = pwrite(tdb->fd, buf, n, size);
+#else
+               int ret;
+               if (lseek(tdb->fd, size, SEEK_SET) != size)
+                       return -1;
+               ret = write(tdb->fd, buf, n);
+#endif
+               if (ret != n) {
+                       TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", 
+                                  n, strerror(errno)));
+                       return -1;
+               }
+               addition -= n;
+               size += n;
+       }
+       return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+   file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
+{
+       struct list_struct rec;
+       tdb_off offset;
+
+       if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+               TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+               return -1;
+       }
+
+       /* must know about any previous expansions by another process */
+       tdb_oob(tdb, tdb->map_size + 1, 1);
+
+       /* always make room for at least 10 more records, and round
+           the database up to a multiple of TDB_PAGE_SIZE */
+       size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+       if (!(tdb->flags & TDB_INTERNAL))
+               tdb_munmap(tdb);
+
+       /*
+        * We must ensure the file is unmapped before doing this
+        * to ensure consistency with systems like OpenBSD where
+        * writes and mmaps are not consistent.
+        */
+
+       /* expand the file itself */
+       if (!(tdb->flags & TDB_INTERNAL)) {
+               if (expand_file(tdb, tdb->map_size, size) != 0)
+                       goto fail;
+       }
+
+       tdb->map_size += size;
+
+       if (tdb->flags & TDB_INTERNAL)
+               tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+       else {
+               /*
+                * We must ensure the file is remapped before adding the space
+                * to ensure consistency with systems like OpenBSD where
+                * writes and mmaps are not consistent.
+                */
+
+               /* We're ok if the mmap fails as we'll fallback to read/write */
+               tdb_mmap(tdb);
+       }
+
+       /* form a new freelist record */
+       memset(&rec,'\0',sizeof(rec));
+       rec.rec_len = size - sizeof(rec);
+
+       /* link it into the free list */
+       offset = tdb->map_size - size;
+       if (tdb_free(tdb, offset, &rec) == -1)
+               goto fail;
+
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+   to a unconnected list_struct within the database with room for at
+   least length bytes of total data
+
+   0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+                           struct list_struct *rec)
+{
+       tdb_off rec_ptr, last_ptr, newrec_ptr;
+       struct list_struct newrec;
+
+       if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+               return 0;
+
+       /* Extra bytes required for tailer */
+       length += sizeof(tdb_off);
+
+ again:
+       last_ptr = FREELIST_TOP;
+
+       /* read in the freelist top */
+       if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+               goto fail;
+
+       /* keep looking until we find a freelist record big enough */
+       while (rec_ptr) {
+               if (rec_free_read(tdb, rec_ptr, rec) == -1)
+                       goto fail;
+
+               if (rec->rec_len >= length) {
+                       /* found it - now possibly split it up  */
+                       if (rec->rec_len > length + MIN_REC_SIZE) {
+                               /* Length of left piece */
+                               length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+                               /* Right piece to go on free list */
+                               newrec.rec_len = rec->rec_len
+                                       - (sizeof(*rec) + length);
+                               newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+                               /* And left record is shortened */
+                               rec->rec_len = length;
+                       } else
+                               newrec_ptr = 0;
+
+                       /* Remove allocated record from the free list */
+                       if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+                               goto fail;
+
+                       /* Update header: do this before we drop alloc
+                           lock, otherwise tdb_free() might try to
+                           merge with us, thinking we're free.
+                           (Thanks Jeremy Allison). */
+                       rec->magic = TDB_MAGIC;
+                       if (rec_write(tdb, rec_ptr, rec) == -1)
+                               goto fail;
+
+                       /* Did we create new block? */
+                       if (newrec_ptr) {
+                               /* Update allocated record tailer (we
+                                   shortened it). */
+                               if (update_tailer(tdb, rec_ptr, rec) == -1)
+                                       goto fail;
+
+                               /* Free new record */
+                               if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+                                       goto fail;
+                       }
+
+                       /* all done - return the new record offset */
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return rec_ptr;
+               }
+               /* move to the next record */
+               last_ptr = rec_ptr;
+               rec_ptr = rec->next;
+       }
+       /* we didn't find enough space. See if we can expand the
+          database and if we can then try again */
+       if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+               goto again;
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+       struct tdb_header *newdb;
+       int size, ret = -1;
+
+       /* We make it up in memory, then write it out if not internal */
+       size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+       if (!(newdb = calloc(size, 1)))
+               return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+       /* Fill in the header */
+       newdb->version = TDB_VERSION;
+       newdb->hash_size = hash_size;
+#ifdef USE_SPINLOCKS
+       newdb->rwlocks = size;
+#endif
+       if (tdb->flags & TDB_INTERNAL) {
+               tdb->map_size = size;
+               tdb->map_ptr = (char *)newdb;
+               memcpy(&tdb->header, newdb, sizeof(tdb->header));
+               /* Convert the `ondisk' version if asked. */
+               CONVERT(*newdb);
+               return 0;
+       }
+       if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+               goto fail;
+
+       if (ftruncate(tdb->fd, 0) == -1)
+               goto fail;
+
+       /* This creates an endian-converted header, as if read from disk */
+       CONVERT(*newdb);
+       memcpy(&tdb->header, newdb, sizeof(tdb->header));
+       /* Don't endian-convert the magic food! */
+       memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+       if (write(tdb->fd, newdb, size) != size)
+               ret = -1;
+       else
+               ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+  fail:
+       SAFE_FREE(newdb);
+       return ret;
+}
+
+/* Returns 0 on fail.  On success, return offset of record, and fills
+   in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+                       struct list_struct *r)
+{
+       tdb_off rec_ptr;
+       
+       /* read in the hash top */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+               return 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               if (rec_read(tdb, rec_ptr, r) == -1)
+                       return 0;
+
+               if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+                       char *k;
+                       /* a very likely hit - read the key */
+                       k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), 
+                                          r->key_len);
+                       if (!k)
+                               return 0;
+
+                       if (memcmp(key.dptr, k, key.dsize) == 0) {
+                               SAFE_FREE(k);
+                               return rec_ptr;
+                       }
+                       SAFE_FREE(k);
+               }
+               rec_ptr = r->next;
+       }
+       return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
+
+/* If they do lockkeys, check that this hash is one they locked */
+static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash)
+{
+       u32 i;
+       if (!tdb->lockedkeys)
+               return 1;
+       for (i = 0; i < tdb->lockedkeys[0]; i++)
+               if (tdb->lockedkeys[i+1] == hash)
+                       return 1;
+       return TDB_ERRCODE(TDB_ERR_NOLOCK, 0);
+}
+
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock(TDB_CONTEXT *tdb, TDB_DATA key, int locktype,
+                            struct list_struct *rec)
+{
+       u32 hash, rec_ptr;
+
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return 0;
+       if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+               return 0;
+       if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+               tdb_unlock(tdb, BUCKET(hash), locktype);
+       return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+       return tdb->ecode;
+}
+
+static struct tdb_errname {
+       enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+            {TDB_ERR_CORRUPT, "Corrupt database"},
+            {TDB_ERR_IO, "IO Error"},
+            {TDB_ERR_LOCK, "Locking error"},
+            {TDB_ERR_OOM, "Out of memory"},
+            {TDB_ERR_EXISTS, "Record exists"},
+            {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+            {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+               if (tdb->ecode == emap[i].ecode)
+                       return emap[i].estring;
+       return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1.
+*/
+
+static int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+{
+       struct list_struct rec;
+       tdb_off rec_ptr;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
+               return -1;
+
+       /* must be long enough key, data and tailer */
+       if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+               tdb->ecode = TDB_SUCCESS; /* Not really an error */
+               return -1;
+       }
+
+       if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+                     dbuf.dptr, dbuf.dsize) == -1)
+               return -1;
+
+       if (dbuf.dsize != rec.data_len) {
+               /* update size */
+               rec.data_len = dbuf.dsize;
+               return rec_write(tdb, rec_ptr, &rec);
+       }
+       return 0;
+}
+
+/* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       TDB_DATA ret;
+
+       /* find which hash bucket it is in */
+       if (!(rec_ptr = tdb_find_lock(tdb,key,F_RDLCK,&rec)))
+               return tdb_null;
+
+       if (rec.data_len)
+               ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+                                         rec.data_len);
+       else
+               ret.dptr = NULL;
+       ret.dsize = rec.data_len;
+       tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+       return ret;
+}
+
+/* check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+*/
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       struct list_struct rec;
+       
+       if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0)
+               return 0;
+       tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+       return 1;
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+  Write locks override our own fcntl readlocks, so check it here.
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       struct tdb_traverse_lock *i;
+       for (i = &tdb->travlocks; i; i = i->next)
+               if (i->off == off)
+                       return -1;
+       return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
+
+/*
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+}
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       struct tdb_traverse_lock *i;
+       u32 count = 0;
+
+       if (off == 0)
+               return 0;
+       for (i = &tdb->travlocks; i; i = i->next)
+               if (i->off == off)
+                       count++;
+       return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
+{
+       tdb_off last_ptr, i;
+       struct list_struct lastrec;
+
+       if (tdb->read_only) return -1;
+
+       if (write_lock_record(tdb, rec_ptr) == -1) {
+               /* Someone traversing here: mark it as dead */
+               rec->magic = TDB_DEAD_MAGIC;
+               return rec_write(tdb, rec_ptr, rec);
+       }
+       if (write_unlock_record(tdb, rec_ptr) != 0)
+               return -1;
+
+       /* find previous record in hash chain */
+       if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+               return -1;
+       for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+               if (rec_read(tdb, i, &lastrec) == -1)
+                       return -1;
+
+       /* unlink it: next ptr is at start of record. */
+       if (last_ptr == 0)
+               last_ptr = TDB_HASH_TOP(rec->full_hash);
+       if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+               return -1;
+
+       /* recover the space */
+       if (tdb_free(tdb, rec_ptr, rec) == -1)
+               return -1;
+       return 0;
+}
+
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+                        struct list_struct *rec)
+{
+       int want_next = (tlock->off != 0);
+
+       /* No traversal allows if you've called tdb_lockkeys() */
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+
+       /* Lock each chain from the start one. */
+       for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+               if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+                       return -1;
+
+               /* No previous record?  Start at top of chain. */
+               if (!tlock->off) {
+                       if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+                                    &tlock->off) == -1)
+                               goto fail;
+               } else {
+                       /* Otherwise unlock the previous record. */
+                       if (unlock_record(tdb, tlock->off) != 0)
+                               goto fail;
+               }
+
+               if (want_next) {
+                       /* We have offset of old record: grab next */
+                       if (rec_read(tdb, tlock->off, rec) == -1)
+                               goto fail;
+                       tlock->off = rec->next;
+               }
+
+               /* Iterate through chain */
+               while( tlock->off) {
+                       tdb_off current;
+                       if (rec_read(tdb, tlock->off, rec) == -1)
+                               goto fail;
+                       if (!TDB_DEAD(rec)) {
+                               /* Woohoo: we found one! */
+                               if (lock_record(tdb, tlock->off) != 0)
+                                       goto fail;
+                               return tlock->off;
+                       }
+                       /* Try to clean dead ones from old traverses */
+                       current = tlock->off;
+                       tlock->off = rec->next;
+                       if (do_delete(tdb, current, rec) != 0)
+                               goto fail;
+               }
+               tdb_unlock(tdb, tlock->hash, F_WRLCK);
+               want_next = 0;
+       }
+       /* We finished iteration without finding anything */
+       return TDB_ERRCODE(TDB_SUCCESS, 0);
+
+ fail:
+       tlock->off = 0;
+       if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
+       return -1;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+   return -1 on error or the record count traversed
+   if fn is NULL then it is not called
+   a non-zero return value from fn() indicates that the traversal should stop
+  */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state)
+{
+       TDB_DATA key, dbuf;
+       struct list_struct rec;
+       struct tdb_traverse_lock tl = { NULL, 0, 0 };
+       int ret, count = 0;
+
+       /* This was in the initializaton, above, but the IRIX compiler
+        * did not like it.  crh
+        */
+       tl.next = tdb->travlocks.next;
+
+       /* fcntl locks don't stack: beware traverse inside traverse */
+       tdb->travlocks.next = &tl;
+
+       /* tdb_next_lock places locks on the record returned, and its chain */
+       while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+               count++;
+               /* now read the full record */
+               key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), 
+                                         rec.key_len + rec.data_len);
+               if (!key.dptr) {
+                       ret = -1;
+                       if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+                               goto out;
+                       if (unlock_record(tdb, tl.off) != 0)
+                               TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+                       goto out;
+               }
+               key.dsize = rec.key_len;
+               dbuf.dptr = key.dptr + rec.key_len;
+               dbuf.dsize = rec.data_len;
+
+               /* Drop chain lock, call out */
+               if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+                       ret = -1;
+                       goto out;
+               }
+               if (fn && fn(tdb, key, dbuf, state)) {
+                       /* They want us to terminate traversal */
+                       ret = count;
+                       if (unlock_record(tdb, tl.off) != 0) {
+                               TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+                               ret = -1;
+                       }
+                       tdb->travlocks.next = tl.next;
+                       SAFE_FREE(key.dptr);
+                       return count;
+               }
+               SAFE_FREE(key.dptr);
+       }
+out:
+       tdb->travlocks.next = tl.next;
+       if (ret < 0)
+               return -1;
+       else
+               return count;
+}
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+       TDB_DATA key;
+       struct list_struct rec;
+
+       /* release any old lock */
+       if (unlock_record(tdb, tdb->travlocks.off) != 0)
+               return tdb_null;
+       tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+       if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+               return tdb_null;
+       /* now read the key */
+       key.dsize = rec.key_len;
+       key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+       if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+       return key;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+       u32 oldhash;
+       TDB_DATA key = tdb_null;
+       struct list_struct rec;
+       char *k = NULL;
+
+       /* Is locked key the old key?  If so, traverse will be reliable. */
+       if (tdb->travlocks.off) {
+               if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+                       return tdb_null;
+               if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+                   || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+                                           rec.key_len))
+                   || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+                       /* No, it wasn't: unlock it and start from scratch */
+                       if (unlock_record(tdb, tdb->travlocks.off) != 0)
+                               return tdb_null;
+                       if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+                               return tdb_null;
+                       tdb->travlocks.off = 0;
+               }
+
+               SAFE_FREE(k);
+       }
+
+       if (!tdb->travlocks.off) {
+               /* No previous element: do normal find, and lock record */
+               tdb->travlocks.off = tdb_find_lock(tdb, oldkey, F_WRLCK, &rec);
+               if (!tdb->travlocks.off)
+                       return tdb_null;
+               tdb->travlocks.hash = BUCKET(rec.full_hash);
+               if (lock_record(tdb, tdb->travlocks.off) != 0) {
+                       TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+                       return tdb_null;
+               }
+       }
+       oldhash = tdb->travlocks.hash;
+
+       /* Grab next record: locks chain and returned record,
+          unlocks old record */
+       if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+               key.dsize = rec.key_len;
+               key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+                                         key.dsize);
+               /* Unlock the chain of this new record */
+               if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+                       TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+       }
+       /* Unlock the chain of old record */
+       if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+       return key;
+}
+
+/* delete an entry in the database given a key */
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       int ret;
+
+       if (!(rec_ptr = tdb_find_lock(tdb, key, F_WRLCK, &rec)))
+               return -1;
+       ret = do_delete(tdb, rec_ptr, &rec);
+       if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
+       return ret;
+}
+
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+       struct list_struct rec;
+       u32 hash;
+       tdb_off rec_ptr;
+       char *p = NULL;
+       int ret = 0;
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return -1;
+       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+               return -1;
+
+       /* check for it existing, on insert. */
+       if (flag == TDB_INSERT) {
+               if (tdb_exists(tdb, key)) {
+                       tdb->ecode = TDB_ERR_EXISTS;
+                       goto fail;
+               }
+       } else {
+               /* first try in-place update, on modify or replace. */
+               if (tdb_update(tdb, key, dbuf) == 0)
+                       goto out;
+               if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST)
+                       goto fail;
+       }
+       /* reset the error code potentially set by the tdb_update() */
+       tdb->ecode = TDB_SUCCESS;
+
+       /* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+       if (flag != TDB_INSERT)
+               tdb_delete(tdb, key);
+
+       /* Copy key+value *before* allocating free space in case malloc
+          fails and we are left with a dead spot in the tdb. */
+
+       if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+               tdb->ecode = TDB_ERR_OOM;
+               goto fail;
+       }
+
+       memcpy(p, key.dptr, key.dsize);
+       if (dbuf.dsize)
+               memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
+
+       /* now we're into insert / modify / replace of a record which
+        * we know could not be optimised by an in-place store (for
+        * various reasons).  */
+       if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+               goto fail;
+
+       /* Read hash top into next ptr */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+               goto fail;
+
+       rec.key_len = key.dsize;
+       rec.data_len = dbuf.dsize;
+       rec.full_hash = hash;
+       rec.magic = TDB_MAGIC;
+
+       /* write out and point the top of the hash chain at it */
+       if (rec_write(tdb, rec_ptr, &rec) == -1
+           || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+           || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+               /* Need to tdb_unallocate() here */
+               goto fail;
+       }
+ out:
+       SAFE_FREE(p); 
+       tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+       return ret;
+fail:
+       ret = -1;
+       goto out;
+}
+
+/* Attempt to append data to an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+       struct list_struct rec;
+       tdb_off rec_ptr;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
+               return -1;
+
+       /* Append of 0 is always ok. */
+       if (new_dbuf.dsize == 0)
+               return 0;
+
+       /* must be long enough for key, old data + new data and tailer */
+       if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+               /* No room. */
+               tdb->ecode = TDB_SUCCESS; /* Not really an error */
+               return -1;
+       }
+
+       if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+                     new_dbuf.dptr, new_dbuf.dsize) == -1)
+               return -1;
+
+       /* update size */
+       rec.data_len += new_dbuf.dsize;
+       return rec_write(tdb, rec_ptr, &rec);
+}
+
+/* Append to an entry. Create if not exist. */
+
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+       struct list_struct rec;
+       u32 hash;
+       tdb_off rec_ptr;
+       char *p = NULL;
+       int ret = 0;
+       size_t new_data_size = 0;
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return -1;
+       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+               return -1;
+
+       /* first try in-place. */
+       if (tdb_append_inplace(tdb, key, new_dbuf) == 0)
+               goto out;
+
+       /* reset the error code potentially set by the tdb_append_inplace() */
+       tdb->ecode = TDB_SUCCESS;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+               if (tdb->ecode != TDB_ERR_NOEXIST)
+                       goto fail;
+
+               /* Not found - create. */
+
+               ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+               goto out;
+       }
+
+       new_data_size = rec.data_len + new_dbuf.dsize;
+
+       /* Copy key+old_value+value *before* allocating free space in case malloc
+          fails and we are left with a dead spot in the tdb. */
+
+       if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+               tdb->ecode = TDB_ERR_OOM;
+               goto fail;
+       }
+
+       /* Copy the key in place. */
+       memcpy(p, key.dptr, key.dsize);
+
+       /* Now read the old data into place. */
+       if (rec.data_len &&
+               tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+                       goto fail;
+
+       /* Finally append the new data. */
+       if (new_dbuf.dsize)
+               memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
+
+       /* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+
+       tdb_delete(tdb, key);
+
+       if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+               goto fail;
+
+       /* Read hash top into next ptr */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+               goto fail;
+
+       rec.key_len = key.dsize;
+       rec.data_len = new_data_size;
+       rec.full_hash = hash;
+       rec.magic = TDB_MAGIC;
+
+       /* write out and point the top of the hash chain at it */
+       if (rec_write(tdb, rec_ptr, &rec) == -1
+           || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+           || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+               /* Need to tdb_unallocate() here */
+               goto fail;
+       }
+
+ out:
+       SAFE_FREE(p); 
+       tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+       return ret;
+
+fail:
+       ret = -1;
+       goto out;
+}
+
+static int tdb_already_open(dev_t device,
+                           ino_t ino)
+{
+       TDB_CONTEXT *i;
+       
+       for (i = tdbs; i; i = i->next) {
+               if (i->device == device && i->inode == ino) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the
+   database file. A flags value of O_WRONLY is invalid. The hash size
+   is advisory, use zero for a default value.
+
+   Return is NULL on error, in which case errno is also set.  Don't 
+   try to call tdb_error or tdb_errname, just do strerror(errno).
+
+   @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+{
+       return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL);
+}
+
+
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+                        int open_flags, mode_t mode,
+                        tdb_log_func log_fn)
+{
+       TDB_CONTEXT *tdb;
+       struct stat st;
+       int rev = 0, locked;
+       unsigned char *vp;
+       u32 vertest;
+
+       if (!(tdb = calloc(1, sizeof *tdb))) {
+               /* Can't log this */
+               errno = ENOMEM;
+               goto fail;
+       }
+       tdb->fd = -1;
+       tdb->name = NULL;
+       tdb->map_ptr = NULL;
+       tdb->lockedkeys = NULL;
+       tdb->flags = tdb_flags;
+       tdb->open_flags = open_flags;
+       tdb->log_fn = log_fn;
+       
+       if ((open_flags & O_ACCMODE) == O_WRONLY) {
+               TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+                        name));
+               errno = EINVAL;
+               goto fail;
+       }
+       
+       if (hash_size == 0)
+               hash_size = DEFAULT_HASH_SIZE;
+       if ((open_flags & O_ACCMODE) == O_RDONLY) {
+               tdb->read_only = 1;
+               /* read only databases don't do locking or clear if first */
+               tdb->flags |= TDB_NOLOCK;
+               tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+       }
+
+       /* internal databases don't mmap or lock, and start off cleared */
+       if (tdb->flags & TDB_INTERNAL) {
+               tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+               tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+               if (tdb_new_database(tdb, hash_size) != 0) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+                       goto fail;
+               }
+               goto internal;
+       }
+
+       if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+               TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+                        name, strerror(errno)));
+               goto fail;      /* errno set by open(2) */
+       }
+
+       /* ensure there is only one process initialising at once */
+       if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+               TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+                        name, strerror(errno)));
+               goto fail;      /* errno set by tdb_brlock */
+       }
+
+       /* we need to zero database if we are the only one with it open */
+       if ((locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))
+           && (tdb_flags & TDB_CLEAR_IF_FIRST)) {
+               open_flags |= O_CREAT;
+               if (ftruncate(tdb->fd, 0) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: "
+                                "failed to truncate %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail; /* errno set by ftruncate */
+               }
+       }
+
+       if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+           || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+           || tdb->header.version != TDB_VERSION
+           || (tdb->header.hash_size != hash_size
+               && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
+               /* its not a valid database - possibly initialise it */
+               if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+                       errno = EIO; /* ie bad format or something */
+                       goto fail;
+               }
+               rev = (tdb->flags & TDB_CONVERT);
+       }
+       vp = (unsigned char *)&tdb->header.version;
+       vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+                 (((u32)vp[2]) << 8) | (u32)vp[3];
+       tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+       if (!rev)
+               tdb->flags &= ~TDB_CONVERT;
+       else {
+               tdb->flags |= TDB_CONVERT;
+               convert(&tdb->header, sizeof(tdb->header));
+       }
+       if (fstat(tdb->fd, &st) == -1)
+               goto fail;
+
+       /* Is it already in the open list?  If so, fail. */
+       if (tdb_already_open(st.st_dev, st.st_ino)) {
+               TDB_LOG((tdb, 2, "tdb_open_ex: "
+                        "%s (%d,%d) is already open in this process\n",
+                        name, st.st_dev, st.st_ino));
+               errno = EBUSY;
+               goto fail;
+       }
+
+       if (!(tdb->name = (char *)strdup(name))) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       tdb->map_size = st.st_size;
+       tdb->device = st.st_dev;
+       tdb->inode = st.st_ino;
+       tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+       if (!tdb->locked) {
+               TDB_LOG((tdb, 2, "tdb_open_ex: "
+                        "failed to allocate lock structure for %s\n",
+                        name));
+               errno = ENOMEM;
+               goto fail;
+       }
+       tdb_mmap(tdb);
+       if (locked) {
+               if (!tdb->read_only)
+                       if (tdb_clear_spinlocks(tdb) != 0) {
+                               TDB_LOG((tdb, 0, "tdb_open_ex: "
+                               "failed to clear spinlock\n"));
+                               goto fail;
+                       }
+               if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: "
+                                "failed to take ACTIVE_LOCK on %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail;
+               }
+       }
+       /* leave this lock in place to indicate it's in use */
+       if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+               goto fail;
+
+ internal:
+       /* Internal (memory-only) databases skip all the code above to
+        * do with disk files, and resume here by releasing their
+        * global lock and hooking into the active list. */
+       if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+               goto fail;
+       tdb->next = tdbs;
+       tdbs = tdb;
+       return tdb;
+
+ fail:
+       { int save_errno = errno;
+
+       if (!tdb)
+               return NULL;
+       
+       if (tdb->map_ptr) {
+               if (tdb->flags & TDB_INTERNAL)
+                       SAFE_FREE(tdb->map_ptr);
+               else
+                       tdb_munmap(tdb);
+       }
+       SAFE_FREE(tdb->name);
+       if (tdb->fd != -1)
+               if (close(tdb->fd) != 0)
+                       TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+       SAFE_FREE(tdb->locked);
+       SAFE_FREE(tdb);
+       errno = save_errno;
+       return NULL;
+       }
+}
+
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
+int tdb_close(TDB_CONTEXT *tdb)
+{
+       TDB_CONTEXT **i;
+       int ret = 0;
+
+       if (tdb->map_ptr) {
+               if (tdb->flags & TDB_INTERNAL)
+                       SAFE_FREE(tdb->map_ptr);
+               else
+                       tdb_munmap(tdb);
+       }
+       SAFE_FREE(tdb->name);
+       if (tdb->fd != -1)
+               ret = close(tdb->fd);
+       SAFE_FREE(tdb->locked);
+       SAFE_FREE(tdb->lockedkeys);
+
+       /* Remove from contexts list */
+       for (i = &tdbs; *i; i = &(*i)->next) {
+               if (*i == tdb) {
+                       *i = tdb->next;
+                       break;
+               }
+       }
+
+       memset(tdb, 0, sizeof(*tdb));
+       SAFE_FREE(tdb);
+
+       return ret;
+}
+
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
+{
+       u32 i;
+
+       /* There are no locks on read-only dbs */
+       if (tdb->read_only)
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       for (i = 0; i < tdb->header.hash_size; i++) 
+               if (tdb_lock(tdb, i, F_WRLCK))
+                       break;
+
+       /* If error, release locks we have... */
+       if (i < tdb->header.hash_size) {
+               u32 j;
+
+               for ( j = 0; j < i; j++)
+                       tdb_unlock(tdb, j, F_WRLCK);
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       }
+
+       return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i=0; i < tdb->header.hash_size; i++)
+               tdb_unlock(tdb, i, F_WRLCK);
+}
+
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[])
+{
+       u32 i, j, hash;
+
+       /* Can't lock more keys if already locked */
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       if (!(tdb->lockedkeys = malloc(sizeof(u32) * (number+1))))
+               return TDB_ERRCODE(TDB_ERR_OOM, -1);
+       /* First number in array is # keys */
+       tdb->lockedkeys[0] = number;
+
+       /* Insertion sort by bucket */
+       for (i = 0; i < number; i++) {
+               hash = tdb_hash(&keys[i]);
+               for (j = 0; j < i && BUCKET(tdb->lockedkeys[j+1]) < BUCKET(hash); j++);
+                       memmove(&tdb->lockedkeys[j+2], &tdb->lockedkeys[j+1], sizeof(u32) * (i-j));
+               tdb->lockedkeys[j+1] = hash;
+       }
+       /* Finally, lock in order */
+       for (i = 0; i < number; i++)
+               if (tdb_lock(tdb, i, F_WRLCK))
+                       break;
+
+       /* If error, release locks we have... */
+       if (i < number) {
+               for ( j = 0; j < i; j++)
+                       tdb_unlock(tdb, j, F_WRLCK);
+               SAFE_FREE(tdb->lockedkeys);
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       }
+       return 0;
+}
+
+/* Unlock the keys previously locked by tdb_lockkeys() */
+void tdb_unlockkeys(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i = 0; i < tdb->lockedkeys[0]; i++)
+               tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK);
+       SAFE_FREE(tdb->lockedkeys);
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+   contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+       tdb->log_fn = fn;
+}
+
+
+/* reopen a tdb - this is used after a fork to ensure that we have an independent
+   seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+       struct stat st;
+
+       if (tdb_munmap(tdb) != 0) {
+               TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (close(tdb->fd) != 0)
+               TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+       tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+       if (tdb->fd == -1) {
+               TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (fstat(tdb->fd, &st) != 0) {
+               TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+               TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+               goto fail;
+       }
+       tdb_mmap(tdb);
+       if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) {
+               TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       tdb_close(tdb);
+       return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
+{
+       TDB_CONTEXT *tdb;
+
+       for (tdb=tdbs; tdb; tdb = tdb->next) {
+               if (tdb_reopen(tdb) != 0) return -1;
+       }
+
+       return 0;
+}
diff --git a/source4/lib/tdb/tdb.h b/source4/lib/tdb/tdb.h
new file mode 100644 (file)
index 0000000..6f3b1ff
--- /dev/null
@@ -0,0 +1,144 @@
+#ifndef __TDB_H__
+#define __TDB_H__
+
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell 1999
+   
+   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.
+*/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK   4 /* don't do any locking */
+#define TDB_NOMMAP   8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
+               TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT };
+
+#ifndef u32
+#define u32 unsigned
+#endif
+
+typedef struct {
+       char *dptr;
+       size_t dsize;
+} TDB_DATA;
+
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+       char magic_food[32]; /* for /etc/magic */
+       u32 version; /* version of the code */
+       u32 hash_size; /* number of hash entries */
+       tdb_off rwlocks;
+       tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+       u32 count;
+       u32 ltype;
+};
+
+struct tdb_traverse_lock {
+       struct tdb_traverse_lock *next;
+       u32 off;
+       u32 hash;
+};
+
+/* this is the context structure that is returned from a db open */
+typedef struct tdb_context {
+       char *name; /* the name of the database */
+       void *map_ptr; /* where it is currently mapped */
+       int fd; /* open file descriptor for the database */
+       tdb_len map_size; /* how much space has been mapped */
+       int read_only; /* opened read-only */
+       struct tdb_lock_type *locked; /* array of chain locks */
+       enum TDB_ERROR ecode; /* error code for last tdb error */
+       struct tdb_header header; /* a cached copy of the header */
+       u32 flags; /* the flags passed to tdb_open */
+       u32 *lockedkeys; /* array of locked keys: first is #keys */
+       struct tdb_traverse_lock travlocks; /* current traversal locks */
+       struct tdb_context *next; /* all tdbs to avoid multiple opens */
+       dev_t device;   /* uniquely identifies this tdb */
+       ino_t inode;    /* uniquely identifies this tdb */
+       void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
+       int open_flags; /* flags used in the open - needed by reopen */
+} TDB_CONTEXT;
+
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+                        int open_flags, mode_t mode,
+                        tdb_log_func log_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/source4/lib/tdb/tdb.magic b/source4/lib/tdb/tdb.magic
new file mode 100644 (file)
index 0000000..f5619e7
--- /dev/null
@@ -0,0 +1,10 @@
+# Magic file(1) information about tdb files.
+#
+# Install this into /etc/magic or the corresponding location for your
+# system, or pass as a -m argument to file(1).
+
+# You may use and redistribute this file without restriction.
+
+0      string  TDB\ file               TDB database
+>32    lelong  =0x2601196D             version 6, little-endian
+>>36   lelong  x                       hash size %d bytes
diff --git a/source4/lib/tdb/tdbutil.c b/source4/lib/tdb/tdbutil.c
new file mode 100644 (file)
index 0000000..0d8f612
--- /dev/null
@@ -0,0 +1,687 @@
+/* 
+   Unix SMB/CIFS implementation.
+   tdb utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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 <fnmatch.h>
+
+/* these are little tdb utility functions that are meant to make
+   dealing with a tdb database a little less cumbersome in Samba */
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+       gotalarm = 1;
+}
+
+/***************************************************************
+ Make a TDB_DATA and keep the const warning in one place
+****************************************************************/
+
+static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
+{
+       TDB_DATA ret;
+       ret.dptr = dptr;
+       ret.dsize = dsize;
+       return ret;
+}
+
+/****************************************************************************
+ Lock a chain with timeout (in seconds).
+****************************************************************************/
+
+static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
+{
+       /* Allow tdb_chainlock to be interrupted by an alarm. */
+       int ret;
+       gotalarm = 0;
+       tdb_set_lock_alarm(&gotalarm);
+
+       if (timeout) {
+               CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+               alarm(timeout);
+       }
+
+       if (rw_type == F_RDLCK)
+               ret = tdb_chainlock_read(tdb, key);
+       else
+               ret = tdb_chainlock(tdb, key);
+
+       if (timeout) {
+               alarm(0);
+               CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+               if (gotalarm) {
+                       DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
+                               timeout, key.dptr, tdb->name ));
+                       /* TODO: If we time out waiting for a lock, it might
+                        * be nice to use F_GETLK to get the pid of the
+                        * process currently holding the lock and print that
+                        * as part of the debugging message. -- mbp */
+                       return -1;
+               }
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ Write lock a chain. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
+{
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Unlock a chain by string.
+****************************************************************************/
+
+void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+       tdb_chainunlock(tdb, key);
+}
+
+/****************************************************************************
+ Read lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
+}
+
+/****************************************************************************
+ Read unlock a chain by string.
+****************************************************************************/
+
+void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       tdb_chainunlock_read(tdb, key);
+}
+
+
+/****************************************************************************
+ Fetch a int32 value by a arbitrary blob key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
+{
+       TDB_DATA key = make_tdb_data(keyval, len);
+       TDB_DATA data;
+       int32 ret;
+
+       data = tdb_fetch(tdb, key);
+       if (!data.dptr || data.dsize != sizeof(int32)) {
+               SAFE_FREE(data.dptr);
+               return -1;
+       }
+
+       ret = IVAL(data.dptr,0);
+       SAFE_FREE(data.dptr);
+       return ret;
+}
+
+/****************************************************************************
+ Fetch a int32 value by string key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
+{
+       return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
+}
+
+/****************************************************************************
+ Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
+{
+       TDB_DATA key = make_tdb_data(keystr, len);
+       TDB_DATA data;
+       int32 v_store;
+
+       SIVAL(&v_store,0,v);
+       data.dptr = (void *)&v_store;
+       data.dsize = sizeof(int32);
+
+       return tdb_store(tdb, key, data, TDB_REPLACE);
+}
+
+/****************************************************************************
+ Store a int32 value by string key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
+{
+       return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
+}
+
+/****************************************************************************
+ Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
+{
+       TDB_DATA key = make_tdb_data(keyval, len);
+       TDB_DATA data;
+
+       data = tdb_fetch(tdb, key);
+       if (!data.dptr || data.dsize != sizeof(uint32)) {
+               SAFE_FREE(data.dptr);
+               return False;
+       }
+
+       *value = IVAL(data.dptr,0);
+       SAFE_FREE(data.dptr);
+       return True;
+}
+
+/****************************************************************************
+ Fetch a uint32 value by string key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
+{
+       return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+
+/****************************************************************************
+ Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
+{
+       TDB_DATA key = make_tdb_data(keystr, len);
+       TDB_DATA data;
+       uint32 v_store;
+       BOOL ret = True;
+
+       SIVAL(&v_store, 0, value);
+       data.dptr = (void *)&v_store;
+       data.dsize = sizeof(uint32);
+
+       if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
+               ret = False;
+
+       return ret;
+}
+
+/****************************************************************************
+ Store a uint32 value by string key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
+{
+       return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+/****************************************************************************
+ Store a buffer by a null terminated string key.  Return 0 on success, -1
+ on failure.
+****************************************************************************/
+
+int tdb_store_by_string(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+       
+       return tdb_store(tdb, key, data, flags);
+}
+
+/****************************************************************************
+ Fetch a buffer using a null terminated string key.  Don't forget to call
+ free() on the result dptr.
+****************************************************************************/
+
+TDB_DATA tdb_fetch_by_string(TDB_CONTEXT *tdb, const char *keystr)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+       return tdb_fetch(tdb, key);
+}
+
+/****************************************************************************
+ Delete an entry using a null terminated string key. 
+****************************************************************************/
+
+int tdb_delete_by_string(TDB_CONTEXT *tdb, const char *keystr)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+       return tdb_delete(tdb, key);
+}
+
+/****************************************************************************
+ Atomic integer change. Returns old value. To create, set initial value in *oldval. 
+****************************************************************************/
+
+int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
+{
+       int32 val;
+       int32 ret = -1;
+
+       if (tdb_lock_bystring(tdb, keystr,0) == -1)
+               return -1;
+
+       if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
+               /* The lookup failed */
+               if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+                       /* but not becouse it didn't exist */
+                       goto err_out;
+               }
+               
+               /* Start with 'old' value */
+               val = *oldval;
+
+       } else {
+               /* It worked, set return value (oldval) to tdb data */
+               *oldval = val;
+       }
+
+       /* Increment value for storage and return next time */
+       val += change_val;
+               
+       if (tdb_store_int32(tdb, keystr, val) == -1)
+               goto err_out;
+
+       ret = 0;
+
+  err_out:
+
+       tdb_unlock_bystring(tdb, keystr);
+       return ret;
+}
+
+/****************************************************************************
+ Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval. 
+****************************************************************************/
+
+BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
+{
+       uint32 val;
+       BOOL ret = False;
+
+       if (tdb_lock_bystring(tdb, keystr,0) == -1)
+               return False;
+
+       if (!tdb_fetch_uint32(tdb, keystr, &val)) {
+               /* It failed */
+               if (tdb_error(tdb) != TDB_ERR_NOEXIST) { 
+                       /* and not becouse it didn't exist */
+                       goto err_out;
+               }
+
+               /* Start with 'old' value */
+               val = *oldval;
+
+       } else {
+               /* it worked, set return value (oldval) to tdb data */
+               *oldval = val;
+
+       }
+
+       /* get a new value to store */
+       val += change_val;
+               
+       if (!tdb_store_uint32(tdb, keystr, val))
+               goto err_out;
+
+       ret = True;
+
+  err_out:
+
+       tdb_unlock_bystring(tdb, keystr);
+       return ret;
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
+{
+       va_list ap;
+       uint16 w;
+       uint32 d;
+       int i;
+       void *p;
+       int len;
+       char *s;
+       char c;
+       char *buf0 = buf;
+       const char *fmt0 = fmt;
+       int bufsize0 = bufsize;
+
+       va_start(ap, fmt);
+
+       while (*fmt) {
+               switch ((c = *fmt++)) {
+               case 'w':
+                       len = 2;
+                       w = (uint16)va_arg(ap, int);
+                       if (bufsize >= len)
+                               SSVAL(buf, 0, w);
+                       break;
+               case 'd':
+                       len = 4;
+                       d = va_arg(ap, uint32);
+                       if (bufsize >= len)
+                               SIVAL(buf, 0, d);
+                       break;
+               case 'p':
+                       len = 4;
+                       p = va_arg(ap, void *);
+                       d = p?1:0;
+                       if (bufsize >= len)
+                               SIVAL(buf, 0, d);
+                       break;
+               case 'P':
+                       s = va_arg(ap,char *);
+                       w = strlen(s);
+                       len = w + 1;
+                       if (bufsize >= len)
+                               memcpy(buf, s, len);
+                       break;
+               case 'f':
+                       s = va_arg(ap,char *);
+                       w = strlen(s);
+                       len = w + 1;
+                       if (bufsize >= len)
+                               memcpy(buf, s, len);
+                       break;
+               case 'B':
+                       i = va_arg(ap, int);
+                       s = va_arg(ap, char *);
+                       len = 4+i;
+                       if (bufsize >= len) {
+                               SIVAL(buf, 0, i);
+                               memcpy(buf+4, s, i);
+                       }
+                       break;
+               default:
+                       DEBUG(0,("Unknown tdb_pack format %c in %s\n", 
+                                c, fmt));
+                       len = 0;
+                       break;
+               }
+
+               buf += len;
+               bufsize -= len;
+       }
+
+       va_end(ap);
+
+       DEBUG(18,("tdb_pack(%s, %d) -> %d\n", 
+                fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+       
+       return PTR_DIFF(buf, buf0);
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
+{
+       va_list ap;
+       uint16 *w;
+       uint32 *d;
+       int len;
+       int *i;
+       void **p;
+       char *s, **b;
+       char c;
+       char *buf0 = buf;
+       const char *fmt0 = fmt;
+       int bufsize0 = bufsize;
+
+       va_start(ap, fmt);
+       
+       while (*fmt) {
+               switch ((c=*fmt++)) {
+               case 'w':
+                       len = 2;
+                       w = va_arg(ap, uint16 *);
+                       if (bufsize < len)
+                               goto no_space;
+                       *w = SVAL(buf, 0);
+                       break;
+               case 'd':
+                       len = 4;
+                       d = va_arg(ap, uint32 *);
+                       if (bufsize < len)
+                               goto no_space;
+                       *d = IVAL(buf, 0);
+                       break;
+               case 'p':
+                       len = 4;
+                       p = va_arg(ap, void **);
+                       if (bufsize < len)
+                               goto no_space;
+                       *p = (void *)IVAL(buf, 0);
+                       break;
+               case 'P':
+                       s = va_arg(ap,char *);
+                       len = strlen(buf) + 1;
+                       if (bufsize < len || len > sizeof(pstring))
+                               goto no_space;
+                       memcpy(s, buf, len);
+                       break;
+               case 'f':
+                       s = va_arg(ap,char *);
+                       len = strlen(buf) + 1;
+                       if (bufsize < len || len > sizeof(fstring))
+                               goto no_space;
+                       memcpy(s, buf, len);
+                       break;
+               case 'B':
+                       i = va_arg(ap, int *);
+                       b = va_arg(ap, char **);
+                       len = 4;
+                       if (bufsize < len)
+                               goto no_space;
+                       *i = IVAL(buf, 0);
+                       if (! *i) {
+                               *b = NULL;
+                               break;
+                       }
+                       len += *i;
+                       if (bufsize < len)
+                               goto no_space;
+                       *b = (char *)malloc(*i);
+                       if (! *b)
+                               goto no_space;
+                       memcpy(*b, buf+4, *i);
+                       break;
+               default:
+                       DEBUG(0,("Unknown tdb_unpack format %c in %s\n", 
+                                c, fmt));
+
+                       len = 0;
+                       break;
+               }
+
+               buf += len;
+               bufsize -= len;
+       }
+
+       va_end(ap);
+
+       DEBUG(18,("tdb_unpack(%s, %d) -> %d\n", 
+                fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+       return PTR_DIFF(buf, buf0);
+
+ no_space:
+       return -1;
+}
+
+/****************************************************************************
+ Log tdb messages via DEBUG().
+****************************************************************************/
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+       char *ptr = NULL;
+
+       va_start(ap, format);
+       vasprintf(&ptr, format, ap);
+       va_end(ap);
+       
+       if (!ptr || !*ptr)
+               return;
+
+       DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+       SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+ Like tdb_open() but also setup a logging function that redirects to
+ the samba DEBUG() system.
+****************************************************************************/
+
+TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
+                         int open_flags, mode_t mode)
+{
+       TDB_CONTEXT *tdb;
+
+       if (!lp_use_mmap())
+               tdb_flags |= TDB_NOMMAP;
+
+       tdb = tdb_open_ex(name, hash_size, tdb_flags, 
+                                   open_flags, mode, tdb_log);
+       if (!tdb)
+               return NULL;
+
+       return tdb;
+}
+
+
+/****************************************************************************
+ Allow tdb_delete to be used as a tdb_traversal_fn.
+****************************************************************************/
+
+int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+                     void *state)
+{
+    return tdb_delete(the_tdb, key);
+}
+
+
+
+/**
+ * Search across the whole tdb for keys that match the given pattern
+ * return the result as a list of keys
+ *
+ * @param tdb pointer to opened tdb file context
+ * @param pattern searching pattern used by fnmatch(3) functions
+ *
+ * @return list of keys found by looking up with given pattern
+ **/
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
+{
+       TDB_DATA key, next;
+       TDB_LIST_NODE *list = NULL;
+       TDB_LIST_NODE *rec = NULL;
+       TDB_LIST_NODE *tmp = NULL;
+       
+       for (key = tdb_firstkey(tdb); key.dptr; key = next) {
+               /* duplicate key string to ensure null-termination */
+               char *key_str = (char*) strndup(key.dptr, key.dsize);
+               if (!key_str) {
+                       DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
+                       smb_panic("strndup failed!\n");
+               }
+               
+               DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
+               
+               next = tdb_nextkey(tdb, key);
+
+               /* do the pattern checking */
+               if (fnmatch(pattern, key_str, 0) == 0) {
+                       rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
+                       ZERO_STRUCTP(rec);
+
+                       rec->node_key = key;
+       
+                       DLIST_ADD_END(list, rec, tmp);
+               
+                       DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
+               } else {
+                       free(key.dptr);
+               }
+               
+               /* free duplicated key string */
+               free(key_str);
+       }
+       
+       return list;
+
+};
+
+
+/**
+ * Free the list returned by tdb_search_keys
+ *
+ * @param node list of results found by tdb_search_keys
+ **/
+void tdb_search_list_free(TDB_LIST_NODE* node)
+{
+       TDB_LIST_NODE *next_node;
+       
+       while (node) {
+               next_node = node->next;
+               SAFE_FREE(node);
+               node = next_node;
+       };
+};
+
+
diff --git a/source4/lib/tdb/tdbutil.h b/source4/lib/tdb/tdbutil.h
new file mode 100644 (file)
index 0000000..0147344
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+   Unix SMB/CIFS implementation.
+   tdb utility functions
+   Copyright (C) Andrew Tridgell 1999
+   
+   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.
+*/
+
+#ifndef __TDBUTIL_H__
+#define __TDBUTIL_H__
+
+
+/* single node of a list returned by tdb_search_keys */
+typedef struct keys_node 
+{
+       struct keys_node *prev, *next;
+       TDB_DATA node_key;
+} TDB_LIST_NODE;
+
+
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
+void tdb_search_list_free(TDB_LIST_NODE*);
+
+
+#endif /* __TDBUTIL_H__ */
diff --git a/source4/lib/time.c b/source4/lib/time.c
new file mode 100644 (file)
index 0000000..2844da0
--- /dev/null
@@ -0,0 +1,754 @@
+/* 
+   Unix SMB/CIFS implementation.
+   time handling functions
+   Copyright (C) Andrew Tridgell               1992-1998
+   Copyright (C) Stefan (metze) Metzmacher     2002   
+   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"
+
+/*
+  This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
+  in May 1996 
+  */
+
+
+int extra_time_offset = 0;
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef TIME_T_MIN
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+                   : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#endif
+#ifndef TIME_T_MAX
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#endif
+
+void get_nttime_max(NTTIME *t)
+{
+       /* FIXME: This is incorrect */
+       unix_to_nt_time(t, get_time_t_max());
+}
+
+/*******************************************************************
+ External access to time_t_min and time_t_max.
+********************************************************************/
+
+time_t get_time_t_max(void)
+{
+       return TIME_T_MAX;
+}
+
+/*******************************************************************
+a gettimeofday wrapper
+********************************************************************/
+void GetTimeOfDay(struct timeval *tval)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+       gettimeofday(tval,NULL);
+#else
+       gettimeofday(tval);
+#endif
+}
+
+#define TM_YEAR_BASE 1900
+
+/*******************************************************************
+yield the difference between *A and *B, in seconds, ignoring leap seconds
+********************************************************************/
+static int tm_diff(struct tm *a, struct tm *b)
+{
+  int ay = a->tm_year + (TM_YEAR_BASE - 1);
+  int by = b->tm_year + (TM_YEAR_BASE - 1);
+  int intervening_leap_days =
+    (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
+  int years = ay - by;
+  int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
+  int hours = 24*days + (a->tm_hour - b->tm_hour);
+  int minutes = 60*hours + (a->tm_min - b->tm_min);
+  int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
+
+  return seconds;
+}
+
+/*******************************************************************
+  return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
+  ******************************************************************/
+static int TimeZone(time_t t)
+{
+  struct tm *tm = gmtime(&t);
+  struct tm tm_utc;
+  if (!tm)
+    return 0;
+  tm_utc = *tm;
+  tm = localtime(&t);
+  if (!tm)
+    return 0;
+  return tm_diff(&tm_utc,tm);
+
+}
+
+static BOOL done_serverzone_init;
+
+/* Return the smb serverzone value */
+
+static int get_serverzone(void)
+{
+        static int serverzone;
+
+        if (!done_serverzone_init) {
+                serverzone = TimeZone(time(NULL));
+
+                if ((serverzone % 60) != 0) {
+                        DEBUG(1,("WARNING: Your timezone is not a multiple of 1 minute.\n"));
+                }
+
+                DEBUG(4,("Serverzone is %d\n",serverzone));
+
+                done_serverzone_init = True;
+        }
+
+        return serverzone;
+}
+
+/* Re-read the smb serverzone value */
+
+static struct timeval start_time_hires;
+
+void TimeInit(void)
+{
+       done_serverzone_init = False;
+       get_serverzone();
+       /* Save the start time of this process. */
+       if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0)
+               GetTimeOfDay(&start_time_hires);
+}
+
+/**********************************************************************
+ Return a timeval struct of the uptime of this process. As TimeInit is
+ done before a daemon fork then this is the start time from the parent
+ daemon start. JRA.
+***********************************************************************/
+
+void get_process_uptime(struct timeval *ret_time)
+{
+       struct timeval time_now_hires;
+
+       GetTimeOfDay(&time_now_hires);
+       ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec;
+       ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+       if (time_now_hires.tv_usec < start_time_hires.tv_usec) {
+               ret_time->tv_sec -= 1;
+               ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec);
+       } else
+               ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+}
+
+/*******************************************************************
+return the same value as TimeZone, but it should be more efficient.
+
+We keep a table of DST offsets to prevent calling localtime() on each 
+call of this function. This saves a LOT of time on many unixes.
+
+Updated by Paul Eggert <eggert@twinsun.com>
+********************************************************************/
+static int TimeZoneFaster(time_t t)
+{
+  static struct dst_table {time_t start,end; int zone;} *tdt, *dst_table = NULL;
+  static int table_size = 0;
+  int i;
+  int zone = 0;
+
+  if (t == 0) t = time(NULL);
+
+  /* Tunis has a 8 day DST region, we need to be careful ... */
+#define MAX_DST_WIDTH (365*24*60*60)
+#define MAX_DST_SKIP (7*24*60*60)
+
+  for (i=0;i<table_size;i++)
+    if (t >= dst_table[i].start && t <= dst_table[i].end) break;
+
+  if (i<table_size) {
+    zone = dst_table[i].zone;
+  } else {
+    time_t low,high;
+
+    zone = TimeZone(t);
+    tdt = (struct dst_table *)Realloc(dst_table,
+                                             sizeof(dst_table[0])*(i+1));
+    if (!tdt) {
+      DEBUG(0,("TimeZoneFaster: out of memory!\n"));
+      SAFE_FREE(dst_table);
+      table_size = 0;
+    } else {
+      dst_table = tdt;
+      table_size++;
+
+      dst_table[i].zone = zone; 
+      dst_table[i].start = dst_table[i].end = t;
+    
+      /* no entry will cover more than 6 months */
+      low = t - MAX_DST_WIDTH/2;
+      if (t < low)
+       low = TIME_T_MIN;
+      
+      high = t + MAX_DST_WIDTH/2;
+      if (high < t)
+       high = TIME_T_MAX;
+      
+      /* widen the new entry using two bisection searches */
+      while (low+60*60 < dst_table[i].start) {
+       if (dst_table[i].start - low > MAX_DST_SKIP*2)
+         t = dst_table[i].start - MAX_DST_SKIP;
+       else
+         t = low + (dst_table[i].start-low)/2;
+       if (TimeZone(t) == zone)
+         dst_table[i].start = t;
+       else
+         low = t;
+      }
+
+      while (high-60*60 > dst_table[i].end) {
+       if (high - dst_table[i].end > MAX_DST_SKIP*2)
+         t = dst_table[i].end + MAX_DST_SKIP;
+       else
+         t = high - (high-dst_table[i].end)/2;
+       if (TimeZone(t) == zone)
+         dst_table[i].end = t;
+       else
+         high = t;
+      }
+#if 0
+      DEBUG(1,("Added DST entry from %s ",
+              asctime(localtime(&dst_table[i].start))));
+      DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)),
+              dst_table[i].zone));
+#endif
+    }
+  }
+  return zone;
+}
+
+/****************************************************************************
+  return the UTC offset in seconds west of UTC, adjusted for extra time offset
+  **************************************************************************/
+int TimeDiff(time_t t)
+{
+  return TimeZoneFaster(t) + 60 * lp_time_offset();
+}
+
+
+/****************************************************************************
+  return the UTC offset in seconds west of UTC, adjusted for extra time
+  offset, for a local time value.  If ut = lt + LocTimeDiff(lt), then
+  lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
+  daylight savings transitions because some local times are ambiguous.
+  LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
+  +**************************************************************************/
+static int LocTimeDiff(time_t lte)
+{
+  time_t lt = lte - 60 * lp_time_offset();
+  int d = TimeZoneFaster(lt);
+  time_t t = lt + d;
+
+  /* if overflow occurred, ignore all the adjustments so far */
+  if (((lte < lt) ^ (lp_time_offset() < 0))  |  ((t < lt) ^ (d < 0)))
+    t = lte;
+
+  /* now t should be close enough to the true UTC to yield the right answer */
+  return TimeDiff(t);
+}
+
+
+/****************************************************************************
+try to optimise the localtime call, it can be quite expensive on some machines
+****************************************************************************/
+struct tm *LocalTime(time_t *t)
+{
+  time_t t2 = *t;
+
+  t2 -= TimeDiff(t2);
+
+  return(gmtime(&t2));
+}
+
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+/****************************************************************************
+interpret an 8 byte "filetime" structure to a time_t
+It's originally in "100ns units since jan 1st 1601"
+
+It appears to be kludge-GMT (at least for file listings). This means
+its the GMT you get by taking a localtime and adding the
+serverzone. This is NOT the same as GMT in some cases. This routine
+converts this to real GMT.
+****************************************************************************/
+time_t nt_time_to_unix(const NTTIME *nt)
+{
+  double d;
+  time_t ret;
+  /* The next two lines are a fix needed for the 
+     broken SCO compiler. JRA. */
+  time_t l_time_min = TIME_T_MIN;
+  time_t l_time_max = TIME_T_MAX;
+
+  if (nt->high == 0) return(0);
+
+  d = ((double)nt->high)*4.0*(double)(1<<30);
+  d += (nt->low&0xFFF00000);
+  d *= 1.0e-7;
+  /* now adjust by 369 years to make the secs since 1970 */
+  d -= TIME_FIXUP_CONSTANT;
+
+  if (!(l_time_min <= d && d <= l_time_max))
+    return(0);
+
+  ret = (time_t)(d+0.5);
+
+  /* this takes us from kludge-GMT to real GMT */
+  ret -= get_serverzone();
+  ret += LocTimeDiff(ret);
+
+  return(ret);
+}
+
+/****************************************************************************
+ Convert a NTTIME structure to a time_t.
+ It's originally in "100ns units".
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970
+ if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM
+****************************************************************************/
+
+time_t nt_time_to_unix_abs(NTTIME *nt)
+{
+       double d;
+       time_t ret;
+       /* The next two lines are a fix needed for the 
+          broken SCO compiler. JRA. */
+       time_t l_time_min = TIME_T_MIN;
+       time_t l_time_max = TIME_T_MAX;
+
+       if (nt->high == 0)
+               return(0);
+
+       if (nt->high==0x80000000 && nt->low==0)
+               return -1;
+
+       /* reverse the time */
+       /* it's a negative value, turn it to positive */
+       nt->high=~nt->high;
+       nt->low=~nt->low;
+
+       d = ((double)nt->high)*4.0*(double)(1<<30);
+       d += (nt->low&0xFFF00000);
+       d *= 1.0e-7;
+  
+       if (!(l_time_min <= d && d <= l_time_max))
+               return(0);
+
+       ret = (time_t)(d+0.5);
+
+       return(ret);
+}
+
+/****************************************************************************
+put a 8 byte filetime from a time_t
+This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+       double d;
+
+       if (t==0) {
+               nt->low = 0;
+               nt->high = 0;
+               return;
+       }
+       if (t == TIME_T_MAX) {
+               nt->low = 0xffffffff;
+               nt->high = 0x7fffffff;
+               return;
+       }               
+       if (t == -1) {
+               nt->low = 0xffffffff;
+               nt->high = 0xffffffff;
+               return;
+       }               
+
+       /* this converts GMT to kludge-GMT */
+       t -= TimeDiff(t) - get_serverzone(); 
+
+       d = (double)(t);
+       d += TIME_FIXUP_CONSTANT;
+       d *= 1.0e7;
+
+       nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+       nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+/****************************************************************************
+ Convert a time_t to a NTTIME structure
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601
+ If the nttime_t was 5 seconds, the NTTIME is 5 seconds. JFM
+****************************************************************************/
+
+void unix_to_nt_time_abs(NTTIME *nt, time_t t)
+{
+       double d;
+
+       if (t==0) {
+               nt->low = 0;
+               nt->high = 0;
+               return;
+       }
+
+       if (t == TIME_T_MAX) {
+               nt->low = 0xffffffff;
+               nt->high = 0x7fffffff;
+               return;
+       }
+               
+       if (t == -1) {
+               /* that's what NT uses for infinite */
+               nt->low = 0x0;
+               nt->high = 0x80000000;
+               return;
+       }               
+
+       d = (double)(t);
+       d *= 1.0e7;
+
+       nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+       nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+
+       /* convert to a negative value */
+       nt->high=~nt->high;
+       nt->low=~nt->low;
+}
+
+
+/****************************************************************************
+take an NTTIME structure, containing high / low time.  convert to unix time.
+lkclXXXX this may need 2 SIVALs not a memcpy.  we'll see...
+****************************************************************************/
+void put_long_date(char *p,time_t t)
+{
+       NTTIME nt;
+       unix_to_nt_time(&nt, t);
+       SIVAL(p, 0, nt.low);
+       SIVAL(p, 4, nt.high);
+}
+
+/****************************************************************************
+check if it's a null mtime
+****************************************************************************/
+BOOL null_mtime(time_t mtime)
+{
+       if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1)
+               return True;
+       return False;
+}
+
+/*******************************************************************
+  create a 16 bit dos packed date
+********************************************************************/
+static uint16 make_dos_date1(struct tm *t)
+{
+       uint16 ret=0;
+       ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
+       ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5));
+       return ret;
+}
+
+/*******************************************************************
+  create a 16 bit dos packed time
+********************************************************************/
+static uint16 make_dos_time1(struct tm *t)
+{
+       uint16 ret=0;
+       ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
+       ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5));
+       return ret;
+}
+
+/*******************************************************************
+  create a 32 bit dos packed date/time from some parameters
+  This takes a GMT time and returns a packed localtime structure
+********************************************************************/
+static uint32 make_dos_date(time_t unixdate)
+{
+       struct tm *t;
+       uint32 ret=0;
+
+       if (unixdate == 0) {
+               return 0;
+       }
+
+       t = LocalTime(&unixdate);
+       if (!t) {
+               return 0xFFFFFFFF;
+       }
+
+       ret = make_dos_date1(t);
+       ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
+
+       return ret;
+}
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date(char *buf,int offset,time_t unixdate)
+{
+       uint32 x = make_dos_date(unixdate);
+       SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date2(char *buf,int offset,time_t unixdate)
+{
+       uint32 x;
+       x = make_dos_date(unixdate);
+       x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+       SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time before putting it (most SMBs assume
+localtime for this sort of date)
+********************************************************************/
+void put_dos_date3(char *buf,int offset,time_t unixdate)
+{
+       if (!null_mtime(unixdate))
+               unixdate -= TimeDiff(unixdate);
+       SIVAL(buf,offset,unixdate);
+}
+
+/*******************************************************************
+  interpret a 32 bit dos packed date/time to some parameters
+********************************************************************/
+static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second)
+{
+  uint32 p0,p1,p2,p3;
+
+  p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; 
+  p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF;
+
+  *second = 2*(p0 & 0x1F);
+  *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3);
+  *hour = (p1>>3)&0xFF;
+  *day = (p2&0x1F);
+  *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1;
+  *year = ((p3>>1)&0xFF) + 80;
+}
+
+/*******************************************************************
+  create a unix date (int GMT) from a dos date (which is actually in
+  localtime)
+********************************************************************/
+time_t make_unix_date(void *date_ptr)
+{
+       uint32 dos_date=0;
+       struct tm t;
+       time_t ret;
+
+       dos_date = IVAL(date_ptr,0);
+
+       if (dos_date == 0) return (time_t)0;
+  
+       interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon,
+                          &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
+       t.tm_isdst = -1;
+  
+       /* mktime() also does the local to GMT time conversion for us */
+       ret = mktime(&t);
+
+       return(ret);
+}
+
+/*******************************************************************
+like make_unix_date() but the words are reversed
+********************************************************************/
+time_t make_unix_date2(void *date_ptr)
+{
+       uint32 x,x2;
+
+       x = IVAL(date_ptr,0);
+       x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+       SIVAL(&x,0,x2);
+
+       return make_unix_date((void *)&x);
+}
+
+/*******************************************************************
+  create a unix GMT date from a dos date in 32 bit "unix like" format
+  these generally arrive as localtimes, with corresponding DST
+  ******************************************************************/
+time_t make_unix_date3(void *date_ptr)
+{
+       time_t t = (time_t)IVAL(date_ptr,0);
+       if (!null_mtime(t))
+               t += LocTimeDiff(t);
+       return t;
+}
+
+
+/***************************************************************************
+return a HTTP/1.0 time string
+  ***************************************************************************/
+char *http_timestring(TALLOC_CTX *mem_ctx, time_t t)
+{
+  char *buf;
+  fstring tempTime;
+  struct tm *tm = LocalTime(&t);
+
+  if (!tm)
+    buf = talloc_asprintf(mem_ctx,"%ld seconds since the Epoch",(long)t);
+  else
+#ifndef HAVE_STRFTIME
+  buf = talloc_strdup(mem_ctx, asctime(tm));
+  if(buf[strlen(buf)-1] == '\n')
+    buf[strlen(buf)-1] = 0;
+#else /* !HAVE_STRFTIME */
+       strftime(tempTime, sizeof(tempTime)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
+  buf = talloc_strdup(mem_ctx, tempTime);
+#endif /* !HAVE_STRFTIME */
+  return buf;
+}
+
+
+
+/****************************************************************************
+ Return the date and time as a string
+****************************************************************************/
+
+char *timestring(TALLOC_CTX *mem_ctx, BOOL hires)
+{
+       char *TimeBuf;
+       fstring tempTime;
+       struct timeval tp;
+       time_t t;
+       struct tm *tm;
+
+       if (hires) {
+               GetTimeOfDay(&tp);
+               t = (time_t)tp.tv_sec;
+       } else {
+               t = time(NULL);
+       }
+       tm = LocalTime(&t);
+       if (!tm) {
+               if (hires) {
+                       TimeBuf = talloc_asprintf(mem_ctx,
+                                "%ld.%06ld seconds since the Epoch",
+                                (long)tp.tv_sec, 
+                                (long)tp.tv_usec);
+               } else {
+                       TimeBuf = talloc_asprintf(mem_ctx,
+                                "%ld seconds since the Epoch",
+                                (long)t);
+               }
+       } else {
+#ifdef HAVE_STRFTIME
+               if (hires) {
+                       strftime(tempTime,sizeof(tempTime)-1,"%Y/%m/%d %H:%M:%S",tm);
+                       TimeBuf = talloc_asprintf(mem_ctx, "%s.%06ld", 
+                                tempTime, (long)tp.tv_usec);
+               } else {
+                       strftime(tempTime,100,"%Y/%m/%d %H:%M:%S",tm);
+                       TimeBuf = talloc_strdup(mem_ctx, tempTime);
+               }
+#else
+               if (hires) {
+                       TimeBuf = talloc_asprintf(mem_ctx,
+                                "%s.%06ld", 
+                                asctime(tm), 
+                                (long)tp.tv_usec);
+               } else {
+                       TimeBuf = talloc_strdup(mem_ctx, asctime(tm));
+               }
+#endif
+       }
+       return(TimeBuf);
+}
+
+/****************************************************************************
+  return the best approximation to a 'create time' under UNIX from a stat
+  structure.
+****************************************************************************/
+
+time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+{
+  time_t ret, ret1;
+
+  if(S_ISDIR(st->st_mode) && fake_dirs)
+    return (time_t)315493200L;          /* 1/1/1980 */
+    
+  ret = MIN(st->st_ctime, st->st_mtime);
+  ret1 = MIN(ret, st->st_atime);
+
+  if(ret1 != (time_t)0)
+    return ret1;
+
+  /*
+   * One of ctime, mtime or atime was zero (probably atime).
+   * Just return MIN(ctime, mtime).
+   */
+  return ret;
+}
+
+/****************************************************************************
+initialise an NTTIME to -1, which means "unknown" or "don't expire"
+****************************************************************************/
+
+void init_nt_time(NTTIME *nt)
+{
+       nt->high = 0x7FFFFFFF;
+       nt->low = 0xFFFFFFFF;
+}
+
+/****************************************************************************
+check if NTTIME is 0
+****************************************************************************/
+BOOL nt_time_is_zero(NTTIME *nt)
+{
+       if(nt->high==0) 
+               return True;
+       return False;
+}
+
+/*
+  return a talloced string representing a NTTIME for human consumption
+*/
+const char *nt_time_string(TALLOC_CTX *mem_ctx, const NTTIME *nt)
+{
+       time_t t = nt_time_to_unix(nt);
+       return talloc_strdup(mem_ctx, http_timestring(mem_ctx, t));
+}
+
diff --git a/source4/lib/username.c b/source4/lib/username.c
new file mode 100644 (file)
index 0000000..c00ac66
--- /dev/null
@@ -0,0 +1,537 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Username handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Jeremy Allison 1997-2001.
+   
+   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"
+
+/* internal functions */
+static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (const char *), int N);
+static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (const char *), int N);
+
+/*****************************************************************
+ Check if a user or group name is local (this is a *local* name for
+ *local* people, there's nothing for you here...).
+*****************************************************************/
+
+static BOOL name_is_local(const char *name)
+{
+       return !(strchr_m(name, *lp_winbind_separator()));
+}
+
+/*****************************************************************
+ Splits passed user or group name to domain and user/group name parts
+ Returns True if name was splitted and False otherwise.
+*****************************************************************/
+
+BOOL split_domain_and_name(const char *name, char *domain, char* username)
+{
+       char *p = strchr(name,*lp_winbind_separator());
+       
+       
+       /* Parse a string of the form DOMAIN/user into a domain and a user */
+       DEBUG(10,("split_domain_and_name: checking whether name |%s| local or not\n", name));
+       
+       if (p) {
+               fstrcpy(username, p+1);
+               fstrcpy(domain, name);
+               domain[PTR_DIFF(p, name)] = 0;
+       } else if (lp_winbind_use_default_domain()) {
+               fstrcpy(username, name);
+               fstrcpy(domain, lp_workgroup());
+       } else {
+               return False;
+       }
+
+       DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username));
+       return True;
+}
+
+/****************************************************************************
+ Get a users home directory.
+****************************************************************************/
+
+char *get_user_home_dir(const char *user)
+{
+       struct passwd *pass;
+
+       /* Ensure the user exists. */
+
+       pass = Get_Pwnam(user);
+
+       if (!pass)
+               return(NULL);
+       /* Return home directory from struct passwd. */
+
+       return(pass->pw_dir);      
+}
+
+
+/****************************************************************************
+ * A wrapper for sys_getpwnam().  The following variations are tried:
+ *   - as transmitted
+ *   - in all lower case if this differs from transmitted
+ *   - in all upper case if this differs from transmitted
+ *   - using lp_usernamelevel() for permutations.
+****************************************************************************/
+
+static struct passwd *Get_Pwnam_ret = NULL;
+
+static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
+{
+       struct passwd *ret = NULL;
+
+       if (!user2 || !(*user2))
+               return(NULL);
+
+       if (!user || !(*user))
+               return(NULL);
+
+       /* Try in all lower case first as this is the most 
+          common case on UNIX systems */
+       strlower(user2);
+       DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
+       ret = getpwnam_alloc(user2);
+       if(ret)
+               goto done;
+
+       /* Try as given, if username wasn't originally lowercase */
+       if(strcmp(user, user2) != 0) {
+               DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user));
+               ret = getpwnam_alloc(user);
+               if(ret)
+                       goto done;
+       }
+
+       /* Try as uppercase, if username wasn't originally uppercase */
+       strupper(user2);
+       if(strcmp(user, user2) != 0) {
+               DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
+               ret = getpwnam_alloc(user2);
+               if(ret)
+                       goto done;
+       }
+
+       /* Try all combinations up to usernamelevel */
+       strlower(user2);
+       DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2));
+       ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel());
+
+done:
+       DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
+
+       /* This call used to just return the 'passwd' static buffer.
+          This could then have accidental reuse implications, so 
+          we now malloc a copy, and free it in the next use.
+
+          This should cause the (ab)user to segfault if it 
+          uses an old struct. 
+          
+          This is better than useing the wrong data in security
+          critical operations.
+
+          The real fix is to make the callers free the returned 
+          malloc'ed data.
+       */
+
+       if (Get_Pwnam_ret) {
+               passwd_free(&Get_Pwnam_ret);
+       }
+       
+       Get_Pwnam_ret = ret;
+
+       return ret;
+}
+
+/****************************************************************************
+ Get_Pwnam wrapper without modification.
+  NOTE: This with NOT modify 'user'! 
+****************************************************************************/
+
+struct passwd *Get_Pwnam(const char *user)
+{
+       fstring user2;
+       struct passwd *ret;
+
+       fstrcpy(user2, user);
+
+       DEBUG(5,("Finding user %s\n", user));
+
+       ret = Get_Pwnam_internals(user, user2);
+       
+       return ret;  
+}
+
+/****************************************************************************
+ Check if a user is in a netgroup user list.
+****************************************************************************/
+
+static BOOL user_in_netgroup_list(const char *user, const char *ngname)
+{
+#ifdef HAVE_NETGROUP
+       //static char *mydomain = NULL;
+       /* REWRITE: make thread safe if caching */
+       char *mydomain = NULL;
+       //if (mydomain == NULL)
+               yp_get_default_domain(&mydomain);
+
+       if(mydomain == NULL) {
+               DEBUG(5,("Unable to get default yp domain\n"));
+               return False;
+       }
+
+       DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
+               user, mydomain, ngname));
+       DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
+               ? "TRUE" : "FALSE"));
+
+       if (innetgr(ngname, NULL, user, mydomain))
+               return (True);
+#endif /* HAVE_NETGROUP */
+       return False;
+}
+
+/****************************************************************************
+ Check if a user is in a winbind group.
+****************************************************************************/
+  
+static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
+{
+       int num_groups;
+       int i;
+       gid_t *groups = NULL;
+       gid_t gid, gid_low, gid_high;
+       BOOL ret = False;
+       *winbind_answered = False;
+       if ((gid = nametogid(gname)) == (gid_t)-1) {
+               DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
+                       gname ));
+               goto err;
+       }
+
+       if (!lp_winbind_gid(&gid_low, &gid_high)) {
+               DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
+               goto err;
+       }
+
+       if (gid < gid_low || gid > gid_high) {
+               DEBUG(4, ("group %s is not a winbind group\n", gname));
+               goto err;
+       }
+       /*
+        * Get the gid's that this user belongs to.
+        */
+       if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1)
+               return False;
+       if (num_groups == 0) {
+               *winbind_answered = True;
+               return False;
+       }
+       if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) {
+               DEBUG(0,("user_in_winbind_group_list: malloc fail.\n"));
+               goto err;
+       }
+       if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) {
+               DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \
+failed with error %s\n", strerror(errno) ));
+               goto err;
+       }
+       /*
+        * Now we have the gid list for this user - convert the gname
+        * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
+        */
+       for (i = 0; i < num_groups; i++) {
+               if (gid == groups[i]) {
+                       ret = True;
+                       break;
+               }
+       }
+       *winbind_answered = True;
+       SAFE_FREE(groups);
+       return ret;
+   err:
+       *winbind_answered = False;
+       SAFE_FREE(groups);
+       return False;
+}            
+/****************************************************************************
+ Check if a user is in a UNIX group.
+****************************************************************************/
+static BOOL user_in_unix_group_list(const char *user,const char *gname)
+{
+       struct passwd *pass = Get_Pwnam(user);
+       struct sys_userlist *user_list;
+       struct sys_userlist *member;
+       TALLOC_CTX *mem_ctx;
+
+       DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
+
+       /*
+        * We need to check the users primary group as this
+        * group is implicit and often not listed in the group database.
+        */
+       mem_ctx = talloc_init("smbgroupedit talloc");
+       if (!mem_ctx) return -1;
+       if (pass) {
+               if (strequal(gname,gidtoname(mem_ctx, pass->pw_gid))) {
+                       DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
+                       goto exit;
+               }
+       }
+       user_list = get_users_in_group(gname);
+       if (user_list == NULL) {
+               DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
+               return False;
+       }
+
+       for (member = user_list; member; member = member->next) {
+               DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
+                       user, member->unix_name ));
+               if (strequal(member->unix_name,user)) {
+                       free_userlist(user_list);
+                       goto exit;
+               }
+       }
+
+       free_userlist(user_list);
+       talloc_destroy(mem_ctx);
+       return False;
+exit:
+       talloc_destroy(mem_ctx);
+       return True;
+}            
+
+/****************************************************************************
+ Check if a user is in a group list. Ask winbind first, then use UNIX.
+****************************************************************************/
+static BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
+{
+       BOOL winbind_answered = False;
+       BOOL ret;
+       gid_t gid;
+       unsigned i;
+
+       gid = nametogid(gname);
+       if (gid == (gid_t)-1) 
+               return False;
+
+       if (groups && n_groups > 0) {
+               for (i=0; i < n_groups; i++) {
+                       if (groups[i] == gid) {
+                               return True;
+                       }
+               }
+               return False;
+       }
+
+       /* fallback if we don't yet have the group list */
+
+       ret = user_in_winbind_group_list(user, gname, &winbind_answered);
+       if (!winbind_answered)
+               ret = user_in_unix_group_list(user, gname);
+
+       if (ret)
+               DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
+       return ret;
+}
+
+/****************************************************************************
+ Check if a user is in a user list - can check combinations of UNIX
+ and netgroup lists.
+****************************************************************************/
+
+BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
+{
+       if (!list || !*list)
+               return False;
+
+       DEBUG(10,("user_in_list: checking user %s in list\n", user));
+
+       while (*list) {
+
+               DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
+
+               /*
+                * Check raw username.
+                */
+               if (strequal(user, *list))
+                       return(True);
+
+               /*
+                * Now check to see if any combination
+                * of UNIX and netgroups has been specified.
+                */
+
+               if(**list == '@') {
+                       /*
+                        * Old behaviour. Check netgroup list
+                        * followed by UNIX list.
+                        */
+                       if(user_in_netgroup_list(user, *list +1))
+                               return True;
+                       if(user_in_group_list(user, *list +1, groups, n_groups))
+                               return True;
+               } else if (**list == '+') {
+
+                       if((*(*list +1)) == '&') {
+                               /*
+                                * Search UNIX list followed by netgroup.
+                                */
+                               if(user_in_group_list(user, *list +2, groups, n_groups))
+                                       return True;
+                               if(user_in_netgroup_list(user, *list +2))
+                                       return True;
+
+                       } else {
+
+                               /*
+                                * Just search UNIX list.
+                                */
+
+                               if(user_in_group_list(user, *list +1, groups, n_groups))
+                                       return True;
+                       }
+
+               } else if (**list == '&') {
+
+                       if(*(*list +1) == '+') {
+                               /*
+                                * Search netgroup list followed by UNIX list.
+                                */
+                               if(user_in_netgroup_list(user, *list +2))
+                                       return True;
+                               if(user_in_group_list(user, *list +2, groups, n_groups))
+                                       return True;
+                       } else {
+                               /*
+                                * Just search netgroup list.
+                                */
+                               if(user_in_netgroup_list(user, *list +1))
+                                       return True;
+                       }
+               } else if (!name_is_local(*list)) {
+                       /*
+                        * If user name did not match and token is not
+                        * a unix group and the token has a winbind separator in the
+                        * name then see if it is a Windows group.
+                        */
+
+                       DOM_SID g_sid;
+                       enum SID_NAME_USE name_type;
+                       BOOL winbind_answered = False;
+                       BOOL ret;
+                       fstring groupname, domain;
+                       
+                       /* Parse a string of the form DOMAIN/user into a domain and a user */
+
+                       char *p = strchr(*list,*lp_winbind_separator());
+                       
+                       DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
+
+                       if (p) {
+                               fstrcpy(groupname, p+1);
+                               fstrcpy(domain, *list);
+                               domain[PTR_DIFF(p, *list)] = 0;
+
+                               /* Check to see if name is a Windows group */
+                               if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) {
+                                       
+                               /* Check if user name is in the Windows group */
+                                       ret = user_in_winbind_group_list(user, *list, &winbind_answered);
+                                       
+                                       if (winbind_answered && ret == True) {
+                                               DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
+                                               return ret;
+                                       }
+                               }
+                       }
+               }
+    
+               list++;
+       }
+       return(False);
+}
+
+/* The functions below have been taken from password.c and slightly modified */
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
+{
+       ssize_t len = (ssize_t)strlen(s);
+       int i;
+       struct passwd *ret;
+
+       if (N <= 0 || offset >= len)
+               return(fn(s));
+
+       for (i=offset;i<(len-(N-1));i++) {
+               char c = s[i];
+               if (!islower((int)c))
+                       continue;
+               s[i] = toupper(c);
+               ret = uname_string_combinations2(s,i+1,fn,N-1);
+               if(ret)
+                       return(ret);
+               s[i] = c;
+       }
+       return(NULL);
+}
+
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with up to N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
+{
+       int n;
+       struct passwd *ret;
+
+       for (n=1;n<=N;n++) {
+               ret = uname_string_combinations2(s,0,fn,n);
+               if(ret)
+                       return(ret);
+       }  
+       return(NULL);
+}
+
diff --git a/source4/lib/util.c b/source4/lib/util.c
new file mode 100644 (file)
index 0000000..64e3dfe
--- /dev/null
@@ -0,0 +1,1000 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Jeremy Allison 2001-2002
+   Copyright (C) Simo Sorce 2001
+   Copyright (C) Anthony Liguori 2003
+   Copyright (C) James J Myers 2003
+   
+   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"
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+#ifdef WITH_NISPLUS_HOME
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+/*
+ * The following lines are needed due to buggy include files
+ * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
+ * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
+ * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
+ * an enum in /usr/include/rpcsvc/nis.h.
+ */
+
+#if defined(GROUP)
+#undef GROUP
+#endif
+
+#if defined(GROUP_OBJ)
+#undef GROUP_OBJ
+#endif
+
+#endif /* BROKEN_NISPLUS_INCLUDE_FILES */
+
+#include <rpcsvc/nis.h>
+
+#else /* !WITH_NISPLUS_HOME */
+
+#include "rpcsvc/ypclnt.h"
+
+#endif /* WITH_NISPLUS_HOME */
+#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */
+
+
+/**************************************************************************n
+ Find a suitable temporary directory. The result should be copied immediately
+ as it may be overwritten by a subsequent call.
+****************************************************************************/
+
+const char *tmpdir(void)
+{
+       char *p;
+       if ((p = getenv("TMPDIR")))
+               return p;
+       return "/tmp";
+}
+
+/****************************************************************************
+ Determine whether we are in the specified group.
+****************************************************************************/
+
+BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups)
+{
+       int i;
+
+       if (group == current_gid)
+               return(True);
+
+       for (i=0;i<ngroups;i++)
+               if (group == groups[i])
+                       return(True);
+
+       return(False);
+}
+
+/*******************************************************************
+ Check if a file exists - call vfs_file_exist for samba files.
+********************************************************************/
+
+BOOL file_exist(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+       SMB_STRUCT_STAT st;
+       if (!sbuf)
+               sbuf = &st;
+  
+       if (sys_stat(fname,sbuf) != 0) 
+               return(False);
+
+       return((S_ISREG(sbuf->st_mode)) || (S_ISFIFO(sbuf->st_mode)));
+}
+
+/*******************************************************************
+ Check a files mod time.
+********************************************************************/
+
+time_t file_modtime(const char *fname)
+{
+       SMB_STRUCT_STAT st;
+  
+       if (sys_stat(fname,&st) != 0) 
+               return(0);
+
+       return(st.st_mtime);
+}
+
+/*******************************************************************
+ Check if a directory exists.
+********************************************************************/
+
+BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st)
+{
+       SMB_STRUCT_STAT st2;
+       BOOL ret;
+
+       if (!st)
+               st = &st2;
+
+       if (sys_stat(dname,st) != 0) 
+               return(False);
+
+       ret = S_ISDIR(st->st_mode);
+       if(!ret)
+               errno = ENOTDIR;
+       return ret;
+}
+
+/*******************************************************************
+ Returns the size in bytes of the named file.
+********************************************************************/
+SMB_OFF_T get_file_size(char *file_name)
+{
+       SMB_STRUCT_STAT buf;
+       buf.st_size = 0;
+       if(sys_stat(file_name,&buf) != 0)
+               return (SMB_OFF_T)-1;
+       return(buf.st_size);
+}
+
+/*******************************************************************
+ Close the low 3 fd's and open dev/null in their place.
+********************************************************************/
+void close_low_fds(BOOL stderr_too)
+{
+#ifndef VALGRIND
+       int fd;
+       int i;
+
+       close(0);
+       close(1); 
+
+       if (stderr_too)
+               close(2);
+
+       /* try and use up these file descriptors, so silly
+               library routines writing to stdout etc won't cause havoc */
+       for (i=0;i<3;i++) {
+               if (i == 2 && !stderr_too)
+                       continue;
+
+               fd = sys_open("/dev/null",O_RDWR,0);
+               if (fd < 0)
+                       fd = sys_open("/dev/null",O_WRONLY,0);
+               if (fd < 0) {
+                       DEBUG(0,("Can't open /dev/null\n"));
+                       return;
+               }
+               if (fd != i) {
+                       DEBUG(0,("Didn't get file descriptor %d\n",i));
+                       return;
+               }
+       }
+#endif
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+  if SYSV use O_NDELAY
+  if BSD use FNDELAY
+****************************************************************************/
+
+int set_blocking(int fd, BOOL set)
+{
+       int val;
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+       if((val = sys_fcntl_long(fd, F_GETFL, 0)) == -1)
+               return -1;
+       if(set) /* Turn blocking on - ie. clear nonblock flag */
+               val &= ~FLAG_TO_SET;
+       else
+               val |= FLAG_TO_SET;
+       return sys_fcntl_long( fd, F_SETFL, val);
+#undef FLAG_TO_SET
+}
+
+
+/*******************************************************************
+ Sleep for a specified number of milliseconds.
+********************************************************************/
+
+void msleep(unsigned int t)
+{
+       struct timeval tval;  
+
+       tval.tv_sec = t/1000;
+       tval.tv_usec = 1000*(t%1000);
+       /* this should be the real select - do NOT replace
+          with sys_select() */
+       select(0,NULL,NULL,NULL,&tval);
+}
+
+/****************************************************************************
+ Become a daemon, discarding the controlling terminal.
+****************************************************************************/
+
+void become_daemon(BOOL Fork)
+{
+       if (Fork) {
+               if (fork()) {
+                       _exit(0);
+               }
+       }
+
+  /* detach from the terminal */
+#ifdef HAVE_SETSID
+       setsid();
+#elif defined(TIOCNOTTY)
+       {
+               int i = sys_open("/dev/tty", O_RDWR, 0);
+               if (i != -1) {
+                       ioctl(i, (int) TIOCNOTTY, (char *)0);      
+                       close(i);
+               }
+       }
+#endif /* HAVE_SETSID */
+
+       /* Close fd's 0,1,2. Needed if started by rsh */
+       close_low_fds(False);  /* Don't close stderr, let the debug system
+                                 attach it to the logfile */
+}
+
+
+/****************************************************************************
+ Expand a pointer to be a particular size.
+****************************************************************************/
+
+void *Realloc(void *p,size_t size)
+{
+       void *ret=NULL;
+
+       if (size == 0) {
+               SAFE_FREE(p);
+               DEBUG(5,("Realloc asked for 0 bytes\n"));
+               return NULL;
+       }
+
+       if (!p)
+               ret = (void *)malloc(size);
+       else
+               ret = (void *)realloc(p,size);
+
+       if (!ret)
+               DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+       return(ret);
+}
+
+/****************************************************************************
+ Free memory, checks for NULL.
+ Use directly SAFE_FREE()
+ Exists only because we need to pass a function pointer somewhere --SSS
+****************************************************************************/
+
+void safe_free(void *p)
+{
+       SAFE_FREE(p);
+}
+
+
+/*
+  see if a string matches either our primary or one of our secondary 
+  netbios aliases. do a case insensitive match
+*/
+BOOL is_myname(const char *name)
+{
+       const char **aliases;
+       int i;
+
+       if (strcasecmp(name, lp_netbios_name()) == 0) {
+               return True;
+       }
+
+       aliases = lp_netbios_aliases();
+       for (i=0; aliases && aliases[i]; i++) {
+               if (strcasecmp(name, aliases[i]) == 0) {
+                       return True;
+               }
+       }
+
+       return False;
+}
+
+
+/****************************************************************************
+ Get my own name, return in malloc'ed storage.
+****************************************************************************/
+
+char* get_myname(void)
+{
+       char *hostname;
+       const int host_name_max = 255;
+       char *p;
+
+       hostname = malloc(host_name_max+1);
+       *hostname = 0;
+
+       /* get my host name */
+       if (gethostname(hostname, host_name_max+1) == -1) {
+               DEBUG(0,("gethostname failed\n"));
+               return NULL;
+       } 
+
+       /* Ensure null termination. */
+       hostname[host_name_max] = '\0';
+
+       /* split off any parts after an initial . */
+       p = strchr_m(hostname,'.');
+
+       if (p)
+               *p = 0;
+       
+       return hostname;
+}
+
+/****************************************************************************
+ Get my own name, including domain.
+****************************************************************************/
+
+BOOL get_myfullname(char *my_name)
+{
+       pstring hostname;
+
+       *hostname = 0;
+
+       /* get my host name */
+       if (gethostname(hostname, sizeof(hostname)) == -1) {
+               DEBUG(0,("gethostname failed\n"));
+               return False;
+       } 
+
+       /* Ensure null termination. */
+       hostname[sizeof(hostname)-1] = '\0';
+
+       if (my_name)
+               fstrcpy(my_name, hostname);
+       return True;
+}
+
+/****************************************************************************
+ Get my own domain name.
+****************************************************************************/
+
+BOOL get_mydomname(fstring my_domname)
+{
+       pstring hostname;
+       char *p;
+
+       *hostname = 0;
+       /* get my host name */
+       if (gethostname(hostname, sizeof(hostname)) == -1) {
+               DEBUG(0,("gethostname failed\n"));
+               return False;
+       } 
+
+       /* Ensure null termination. */
+       hostname[sizeof(hostname)-1] = '\0';
+
+       p = strchr_m(hostname, '.');
+
+       if (!p)
+               return False;
+
+       p++;
+       
+       if (my_domname)
+               fstrcpy(my_domname, p);
+
+       return True;
+}
+
+/****************************************************************************
+ Interpret a protocol description string, with a default.
+****************************************************************************/
+
+int interpret_protocol(char *str,int def)
+{
+       if (strequal(str,"NT1"))
+               return(PROTOCOL_NT1);
+       if (strequal(str,"LANMAN2"))
+               return(PROTOCOL_LANMAN2);
+       if (strequal(str,"LANMAN1"))
+               return(PROTOCOL_LANMAN1);
+       if (strequal(str,"CORE"))
+               return(PROTOCOL_CORE);
+       if (strequal(str,"COREPLUS"))
+               return(PROTOCOL_COREPLUS);
+       if (strequal(str,"CORE+"))
+               return(PROTOCOL_COREPLUS);
+  
+       DEBUG(0,("Unrecognised protocol level %s\n",str));
+  
+       return(def);
+}
+
+/****************************************************************************
+ Return true if a string could be a pure IP address.
+****************************************************************************/
+
+BOOL is_ipaddress(const char *str)
+{
+       BOOL pure_address = True;
+       int i;
+  
+       for (i=0; pure_address && str[i]; i++)
+               if (!(isdigit((int)str[i]) || str[i] == '.'))
+                       pure_address = False;
+
+       /* Check that a pure number is not misinterpreted as an IP */
+       pure_address = pure_address && (strchr_m(str, '.') != NULL);
+
+       return pure_address;
+}
+
+/****************************************************************************
+ Interpret an internet address or name into an IP address in 4 byte form.
+****************************************************************************/
+
+uint32 interpret_addr(const char *str)
+{
+       struct hostent *hp;
+       uint32 res;
+
+       if (strcmp(str,"0.0.0.0") == 0)
+               return(0);
+       if (strcmp(str,"255.255.255.255") == 0)
+               return(0xFFFFFFFF);
+
+  /* if it's in the form of an IP address then get the lib to interpret it */
+       if (is_ipaddress(str)) {
+               res = inet_addr(str);
+       } else {
+               /* otherwise assume it's a network name of some sort and use 
+                       sys_gethostbyname */
+               if ((hp = sys_gethostbyname(str)) == 0) {
+                       DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str));
+                       return 0;
+               }
+
+               if(hp->h_addr == NULL) {
+                       DEBUG(3,("sys_gethostbyname: host address is invalid for host %s\n",str));
+                       return 0;
+               }
+               putip((char *)&res,(char *)hp->h_addr);
+       }
+
+       if (res == (uint32)-1)
+               return(0);
+
+       return(res);
+}
+
+/*******************************************************************
+ A convenient addition to interpret_addr().
+******************************************************************/
+
+struct in_addr *interpret_addr2(TALLOC_CTX *mem_ctx, const char *str)
+{
+       struct in_addr *ret;
+       uint32 a = interpret_addr(str);
+       
+       ret = talloc(mem_ctx, sizeof(struct in_addr));
+       if (!ret) return NULL;
+       ret->s_addr = a;
+       return(ret);
+}
+
+/*******************************************************************
+ Check if an IP is the 0.0.0.0.
+******************************************************************/
+
+BOOL is_zero_ip(struct in_addr ip)
+{
+       uint32 a;
+       putip((char *)&a,(char *)&ip);
+       return(a == 0);
+}
+
+/*******************************************************************
+ Set an IP to 0.0.0.0.
+******************************************************************/
+
+void zero_ip(struct in_addr *ip)
+{
+       *ip = inet_makeaddr(0,0);
+       return;
+}
+
+
+/*******************************************************************
+ Are two IPs on the same subnet?
+********************************************************************/
+
+BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
+{
+       uint32 net1,net2,nmask;
+
+       nmask = ntohl(mask.s_addr);
+       net1  = ntohl(ip1.s_addr);
+       net2  = ntohl(ip2.s_addr);
+            
+       return((net1 & nmask) == (net2 & nmask));
+}
+
+
+/****************************************************************************
+ Check if a process exists. Does this work on all unixes?
+****************************************************************************/
+
+BOOL process_exists(pid_t pid)
+{
+       /* Doing kill with a non-positive pid causes messages to be
+        * sent to places we don't want. */
+       SMB_ASSERT(pid > 0);
+       return(kill(pid,0) == 0 || errno != ESRCH);
+}
+
+/*******************************************************************
+ Convert a gid into a group name.
+********************************************************************/
+
+char *gidtoname(TALLOC_CTX *mem_ctx, gid_t gid)
+{
+       char *name;
+       struct group *grp;
+
+       grp = getgrgid(gid);
+       if (grp)
+               return(grp->gr_name);
+       name = talloc_asprintf(mem_ctx, "%d",(int)gid);
+       return(name);
+}
+
+
+/*******************************************************************
+ Convert a name to a gid_t if possible. Return -1 if not a group. 
+********************************************************************/
+
+gid_t nametogid(const char *name)
+{
+       struct group *grp;
+       char *p;
+       gid_t g;
+
+       g = (gid_t)strtol(name, &p, 0);
+       if ((p != name) && (*p == '\0'))
+               return g;
+
+       grp = sys_getgrnam(name);
+       if (grp)
+               return(grp->gr_gid);
+       return (gid_t)-1;
+}
+
+/*******************************************************************
+ Something really nasty happened - panic !
+********************************************************************/
+
+void smb_panic(const char *why)
+{
+       char *cmd = lp_panic_action();
+       int result;
+
+       if (cmd && *cmd) {
+               DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
+               result = system(cmd);
+
+               if (result == -1)
+                       DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
+                                 strerror(errno)));
+               else
+                       DEBUG(0, ("smb_panic(): action returned status %d\n",
+                                 WEXITSTATUS(result)));
+       }
+       DEBUG(0,("PANIC: %s\n", why));
+       abort();
+}
+
+/****************************************************************************
+ Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping
+ is dealt with in posix.c
+****************************************************************************/
+
+BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+       SMB_STRUCT_FLOCK lock;
+       int ret;
+
+       DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type));
+
+       lock.l_type = type;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = offset;
+       lock.l_len = count;
+       lock.l_pid = 0;
+
+       ret = sys_fcntl_ptr(fd,op,&lock);
+
+       if (ret == -1 && errno != 0)
+               DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
+
+       /* a lock query */
+       if (op == SMB_F_GETLK) {
+               if ((ret != -1) &&
+                               (lock.l_type != F_UNLCK) && 
+                               (lock.l_pid != 0) && 
+                               (lock.l_pid != getpid())) {
+                       DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid));
+                       return(True);
+               }
+
+               /* it must be not locked or locked by me */
+               return(False);
+       }
+
+       /* a lock set or unset */
+       if (ret == -1) {
+               DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n",
+                       (double)offset,(double)count,op,type,strerror(errno)));
+               return(False);
+       }
+
+       /* everything went OK */
+       DEBUG(8,("fcntl_lock: Lock call successful\n"));
+
+       return(True);
+}
+
+/*******************************************************************
+ Set the remote_arch string based on an enum. This is used in places
+where we desperately need to distinguish client type. 
+********************************************************************/
+void set_remote_arch(struct server_context *smb, enum remote_arch_types type)
+{
+       const char *arch;
+
+       smb->negotiate.ra_type = type;
+       switch (type) {
+       case RA_WFWG:
+               arch = "WfWg";
+               return;
+       case RA_OS2:
+               arch = "OS2";
+               return;
+       case RA_WIN95:
+               arch = "Win95";
+               return;
+       case RA_WINNT:
+               arch = "WinNT";
+               return;
+       case RA_WIN2K:
+               arch = "Win2K";
+               return;
+       case RA_WINXP:
+               arch = "WinXP";
+               return;
+       case RA_SAMBA:
+               arch = "Samba";
+               return;
+       default:
+               smb->negotiate.ra_type = RA_UNKNOWN;
+               arch = "UNKNOWN";
+               break;
+       }
+
+       sub_set_remote_arch(arch);
+}
+
+
+void print_asc(int level, const unsigned char *buf,int len)
+{
+       int i;
+       for (i=0;i<len;i++)
+               DEBUGADD(level,("%c", isprint(buf[i])?buf[i]:'.'));
+}
+
+void dump_data(int level, const char *buf1,int len)
+{
+       const unsigned char *buf = (const unsigned char *)buf1;
+       int i=0;
+       if (len<=0) return;
+
+       if (!DEBUGLVL(level)) return;
+       
+       DEBUGADD(level,("[%03X] ",i));
+       for (i=0;i<len;) {
+               DEBUGADD(level,("%02X ",(int)buf[i]));
+               i++;
+               if (i%8 == 0) DEBUGADD(level,(" "));
+               if (i%16 == 0) {      
+                       print_asc(level,&buf[i-16],8); DEBUGADD(level,(" "));
+                       print_asc(level,&buf[i-8],8); DEBUGADD(level,("\n"));
+                       if (i<len) DEBUGADD(level,("[%03X] ",i));
+               }
+       }
+       if (i%16) {
+               int n;
+               n = 16 - (i%16);
+               DEBUGADD(level,(" "));
+               if (n>8) DEBUGADD(level,(" "));
+               while (n--) DEBUGADD(level,("   "));
+               n = MIN(8,i%16);
+               print_asc(level,&buf[i-(i%16)],n); DEBUGADD(level,( " " ));
+               n = (i%16) - n;
+               if (n>0) print_asc(level,&buf[i-n],n); 
+               DEBUGADD(level,("\n"));    
+       }       
+}
+
+/*****************************************************************
+ Possibly replace mkstemp if it is broken.
+*****************************************************************/  
+
+int smb_mkstemp(char *template)
+{
+#if HAVE_SECURE_MKSTEMP
+       return mkstemp(template);
+#else
+       /* have a reasonable go at emulating it. Hope that
+          the system mktemp() isn't completly hopeless */
+       char *p = mktemp(template);
+       if (!p)
+               return -1;
+       return open(p, O_CREAT|O_EXCL|O_RDWR, 0600);
+#endif
+}
+
+/*****************************************************************
+ malloc that aborts with smb_panic on fail or zero size.
+ *****************************************************************/  
+
+void *smb_xmalloc(size_t size)
+{
+       void *p;
+       if (size == 0)
+               smb_panic("smb_xmalloc: called with zero size.\n");
+       if ((p = malloc(size)) == NULL)
+               smb_panic("smb_xmalloc: malloc fail.\n");
+       return p;
+}
+
+/**
+ Memdup with smb_panic on fail.
+**/
+
+void *smb_xmemdup(const void *p, size_t size)
+{
+       void *p2;
+       p2 = smb_xmalloc(size);
+       memcpy(p2, p, size);
+       return p2;
+}
+
+/**
+ strdup that aborts on malloc fail.
+**/
+
+char *smb_xstrdup(const char *s)
+{
+       char *s1 = strdup(s);
+       if (!s1)
+               smb_panic("smb_xstrdup: malloc fail\n");
+       return s1;
+}
+
+
+/*
+  vasprintf that aborts on malloc fail
+*/
+
+ int smb_xvasprintf(char **ptr, const char *format, va_list ap)
+{
+       int n;
+       va_list ap2;
+
+       VA_COPY(ap2, ap);
+
+       n = vasprintf(ptr, format, ap2);
+       if (n == -1 || ! *ptr)
+               smb_panic("smb_xvasprintf: out of memory");
+       return n;
+}
+
+/*****************************************************************
+ Like strdup but for memory.
+*****************************************************************/  
+
+void *memdup(const void *p, size_t size)
+{
+       void *p2;
+       if (size == 0)
+               return NULL;
+       p2 = malloc(size);
+       if (!p2)
+               return NULL;
+       memcpy(p2, p, size);
+       return p2;
+}
+
+/*****************************************************************
+ Get local hostname and cache result.
+*****************************************************************/  
+
+char *myhostname(TALLOC_CTX *mem_ctx)
+{
+       char *myname, *ret;
+       myname = get_myname();
+       ret = talloc_strdup(mem_ctx, myname);
+       free(myname);
+       return ret;
+
+}
+
+/*****************************************************************
+ A useful function for returning a path in the Samba lock directory.
+*****************************************************************/  
+
+char *lock_path(TALLOC_CTX* mem_ctx, const char *name)
+{
+       char *fname;
+
+       fname = talloc_strdup(mem_ctx, lp_lockdir());
+       trim_string(fname,"","/");
+       
+       if (!directory_exist(fname,NULL))
+               mkdir(fname,0755);
+       
+       fname = talloc_asprintf(mem_ctx, "%s/%s", fname, name);
+
+       return fname;
+}
+
+/**
+ * @brief Returns an absolute path to a file in the Samba lib directory.
+ *
+ * @param name File to find, relative to LIBDIR.
+ *
+ * @retval Pointer to a talloc'ed string containing the full path.
+ **/
+
+char *lib_path(TALLOC_CTX* mem_ctx, const char *name)
+{
+       char *fname;
+       fname = talloc_asprintf(mem_ctx, "%s/%s", dyn_LIBDIR, name);
+       return fname;
+}
+
+/**
+ * @brief Returns the platform specific shared library extension.
+ *
+ * @retval Pointer to a static #fstring containing the extension.
+ **/
+
+const char *shlib_ext(void)
+{
+  return dyn_SHLIBEXT;
+}
+
+
+/*********************************************************
+ Recursive routine that is called by unix_wild_match.
+*********************************************************/
+
+static BOOL unix_do_match(char *regexp, char *str)
+{
+       char *p;
+
+       for( p = regexp; *p && *str; ) {
+
+               switch(*p) {
+                       case '?':
+                               str++;
+                               p++;
+                               break;
+
+                       case '*':
+
+                               /*
+                                * Look for a character matching 
+                                * the one after the '*'.
+                                */
+                               p++;
+                               if(!*p)
+                                       return True; /* Automatic match */
+                               while(*str) {
+
+                                       while(*str && (*p != *str))
+                                               str++;
+
+                                       /*
+                                        * Patch from weidel@multichart.de. In the case of the regexp
+                                        * '*XX*' we want to ensure there are at least 2 'X' characters
+                                        * in the string after the '*' for a match to be made.
+                                        */
+
+                                       {
+                                               int matchcount=0;
+
+                                               /*
+                                                * Eat all the characters that match, but count how many there were.
+                                                */
+
+                                               while(*str && (*p == *str)) {
+                                                       str++;
+                                                       matchcount++;
+                                               }
+
+                                               /*
+                                                * Now check that if the regexp had n identical characters that
+                                                * matchcount had at least that many matches.
+                                                */
+
+                                               while ( *(p+1) && (*(p+1) == *p)) {
+                                                       p++;
+                                                       matchcount--;
+                                               }
+
+                                               if ( matchcount <= 0 )
+                                                       return False;
+                                       }
+
+                                       str--; /* We've eaten the match char after the '*' */
+
+                                       if(unix_do_match(p, str))
+                                               return True;
+
+                                       if(!*str)
+                                               return False;
+                                       else
+                                               str++;
+                               }
+                               return False;
+
+                       default:
+                               if(*str != *p)
+                                       return False;
+                               str++;
+                               p++;
+                               break;
+               }
+       }
+
+       if(!*p && !*str)
+               return True;
+
+       if (!*p && str[0] == '.' && str[1] == 0)
+               return(True);
+  
+       if (!*str && *p == '?') {
+               while (*p == '?')
+                       p++;
+               return(!*p);
+       }
+
+       if(!*str && (*p == '*' && p[1] == '\0'))
+               return True;
+
+       return False;
+}
diff --git a/source4/lib/util_file.c b/source4/lib/util_file.c
new file mode 100644 (file)
index 0000000..0eb8046
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
+ * 
+ * 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"
+
+static int gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+  gotalarm = 1;
+}
+
+/***************************************************************
+ Lock or unlock a fd for a known lock type. Abandon after waitsecs 
+ seconds.
+****************************************************************/
+
+BOOL do_file_lock(int fd, int waitsecs, int type)
+{
+  SMB_STRUCT_FLOCK lock;
+  int             ret;
+  void (*oldsig_handler)(int);
+
+  gotalarm = 0;
+  oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+
+  lock.l_type = type;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = 0;
+  lock.l_len = 1;
+  lock.l_pid = 0;
+
+  alarm(waitsecs);
+  /* Note we must *NOT* use sys_fcntl here ! JRA */
+  ret = fcntl(fd, SMB_F_SETLKW, &lock);
+  alarm(0);
+  CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
+
+  if (gotalarm) {
+    DEBUG(0, ("do_file_lock: failed to %s file.\n",
+                type == F_UNLCK ? "unlock" : "lock"));
+    return False;
+  }
+
+  return (ret == 0);
+}
+
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_lock(int fd, int type, int secs, int *plock_depth)
+{
+  if (fd < 0)
+    return False;
+
+  (*plock_depth)++;
+
+  if ((*plock_depth) == 0)
+  {
+    if (!do_file_lock(fd, secs, type)) {
+      DEBUG(10,("file_lock: locking file failed, error = %s.\n",
+                 strerror(errno)));
+      return False;
+    }
+  }
+
+  return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_unlock(int fd, int *plock_depth)
+{
+  BOOL ret=True;
+
+  if(*plock_depth == 1)
+    ret = do_file_lock(fd, 5, F_UNLCK);
+
+  (*plock_depth)--;
+
+  if(!ret)
+    DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n",
+                 strerror(errno)));
+  return ret;
+}
+
+
+/*************************************************************************
+ gets a line out of a file.
+ line is of format "xxxx:xxxxxx:xxxxx:".
+ lines with "#" at the front are ignored.
+*************************************************************************/
+int getfileline(void *vp, char *linebuf, int linebuf_size)
+{
+       /* Static buffers we will return. */
+       FILE *fp = (FILE *)vp;
+       unsigned char   c;
+       unsigned char  *p;
+       size_t            linebuf_len;
+
+       if (fp == NULL)
+       {
+               DEBUG(0,("getfileline: Bad file pointer.\n"));
+               return -1;
+       }
+
+       /*
+        * Scan the file, a line at a time.
+        */
+       while (!feof(fp))
+       {
+               linebuf[0] = '\0';
+
+               fgets(linebuf, linebuf_size, fp);
+               if (ferror(fp))
+               {
+                       return -1;
+               }
+
+               /*
+                * Check if the string is terminated with a newline - if not
+                * then we must keep reading and discard until we get one.
+                */
+
+               linebuf_len = strlen(linebuf);
+               if (linebuf_len == 0)
+               {
+                       linebuf[0] = '\0';
+                       return 0;
+               }
+
+               if (linebuf[linebuf_len - 1] != '\n')
+               {
+                       c = '\0';
+                       while (!ferror(fp) && !feof(fp))
+                       {
+                               c = fgetc(fp);
+                               if (c == '\n')
+                               {
+                                       break;
+                               }
+                       }
+               }
+               else
+               {
+                       linebuf[linebuf_len - 1] = '\0';
+               }
+
+#ifdef DEBUG_PASSWORD
+               DEBUG(100, ("getfileline: got line |%s|\n", linebuf));
+#endif
+               if ((linebuf[0] == 0) && feof(fp))
+               {
+                       DEBUG(4, ("getfileline: end of file reached\n"));
+                       return 0;
+               }
+
+               if (linebuf[0] == '#' || linebuf[0] == '\0')
+               {
+                       DEBUG(6, ("getfileline: skipping comment or blank line\n"));
+                       continue;
+               }
+
+               p = (unsigned char *) strchr_m(linebuf, ':');
+               if (p == NULL)
+               {
+                       DEBUG(0, ("getfileline: malformed line entry (no :)\n"));
+                       continue;
+               }
+               return linebuf_len;
+       }
+       return -1;
+}
+
+
+/****************************************************************************
+read a line from a file with possible \ continuation chars. 
+Blanks at the start or end of a line are stripped.
+The string will be allocated if s2 is NULL
+****************************************************************************/
+char *fgets_slash(char *s2,int maxlen,XFILE *f)
+{
+  char *s=s2;
+  int len = 0;
+  int c;
+  BOOL start_of_line = True;
+
+  if (x_feof(f))
+    return(NULL);
+
+  if (maxlen <2) return(NULL);
+
+  if (!s2)
+    {
+      maxlen = MIN(maxlen,8);
+      s = (char *)malloc(maxlen);
+    }
+
+  if (!s) return(NULL);
+
+  *s = 0;
+
+  while (len < maxlen-1)
+    {
+      c = x_getc(f);
+      switch (c)
+       {
+       case '\r':
+         break;
+       case '\n':
+         while (len > 0 && s[len-1] == ' ')
+           {
+             s[--len] = 0;
+           }
+         if (len > 0 && s[len-1] == '\\')
+           {
+             s[--len] = 0;
+             start_of_line = True;
+             break;
+           }
+         return(s);
+       case EOF:
+         if (len <= 0 && !s2) 
+           SAFE_FREE(s);
+         return(len>0?s:NULL);
+       case ' ':
+         if (start_of_line)
+           break;
+       default:
+         start_of_line = False;
+         s[len++] = c;
+         s[len] = 0;
+       }
+      if (!s2 && len > maxlen-3)
+       {
+         char *t;
+         
+         maxlen *= 2;
+         t = (char *)Realloc(s,maxlen);
+         if (!t) {
+           DEBUG(0,("fgets_slash: failed to expand buffer!\n"));
+           SAFE_FREE(s);
+           return(NULL);
+         } else s = t;
+       }
+    }
+  return(s);
+}
+
+
+/****************************************************************************
+load from a pipe into memory
+****************************************************************************/
+char *file_pload(char *syscmd, size_t *size)
+{
+       int fd, n;
+       char *p, *tp;
+       pstring buf;
+       size_t total;
+       
+       fd = sys_popen(syscmd);
+       if (fd == -1) return NULL;
+
+       p = NULL;
+       total = 0;
+
+       while ((n = read(fd, buf, sizeof(buf))) > 0) {
+               tp = Realloc(p, total + n + 1);
+               if (!tp) {
+                       DEBUG(0,("file_pload: failed to expand buffer!\n"));
+                       close(fd);
+                       SAFE_FREE(p);
+                       return NULL;
+               } else p = tp;
+               memcpy(p+total, buf, n);
+               total += n;
+       }
+       if (p) p[total] = 0;
+
+       /* FIXME: Perhaps ought to check that the command completed
+        * successfully (returned 0); if not the data may be
+        * truncated. */
+       sys_pclose(fd);
+
+       if (size) *size = total;
+
+       return p;
+}
+
+/****************************************************************************
+load a file into memory from a fd.
+****************************************************************************/ 
+
+char *fd_load(int fd, size_t *size)
+{
+       SMB_STRUCT_STAT sbuf;
+       char *p;
+
+       if (sys_fstat(fd, &sbuf) != 0) return NULL;
+
+       p = (char *)malloc(sbuf.st_size+1);
+       if (!p) return NULL;
+
+       if (read(fd, p, sbuf.st_size) != sbuf.st_size) {
+               SAFE_FREE(p);
+               return NULL;
+       }
+       p[sbuf.st_size] = 0;
+
+       if (size) *size = sbuf.st_size;
+
+       return p;
+}
+
+/****************************************************************************
+load a file into memory
+****************************************************************************/
+char *file_load(const char *fname, size_t *size)
+{
+       int fd;
+       char *p;
+
+       if (!fname || !*fname) return NULL;
+       
+       fd = open(fname,O_RDONLY);
+       if (fd == -1) return NULL;
+
+       p = fd_load(fd, size);
+
+       close(fd);
+
+       return p;
+}
+
+
+/*******************************************************************
+mmap (if possible) or read a file
+********************************************************************/
+void *map_file(char *fname, size_t size)
+{
+       size_t s2 = 0;
+       void *p = NULL;
+#ifdef HAVE_MMAP
+       if (lp_use_mmap()) {
+               int fd;
+               fd = open(fname, O_RDONLY, 0);
+               if (fd == -1) {
+                       DEBUG(2,("Failed to load %s - %s\n", fname, strerror(errno)));
+                       return NULL;
+               }
+               p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
+               close(fd);
+               if (p == MAP_FAILED) {
+                       DEBUG(1,("Failed to mmap %s - %s\n", fname, strerror(errno)));
+                       return NULL;
+               }
+       }
+#endif
+       if (!p) {
+               p = file_load(fname, &s2);
+               if (!p) return NULL;
+               if (s2 != size) {
+                       DEBUG(1,("incorrect size for %s - got %d expected %d\n",
+                                fname, s2, size));
+                       if (p) free(p);
+                       return NULL;
+               }
+       }
+
+       return p;
+}
+
+
+/****************************************************************************
+parse a buffer into lines
+****************************************************************************/
+static char **file_lines_parse(char *p, size_t size, int *numlines)
+{
+       int i;
+       char *s, **ret;
+
+       if (!p) return NULL;
+
+       for (s = p, i=0; s < p+size; s++) {
+               if (s[0] == '\n') i++;
+       }
+
+       ret = (char **)malloc(sizeof(ret[0])*(i+2));
+       if (!ret) {
+               SAFE_FREE(p);
+               return NULL;
+       }       
+       memset(ret, 0, sizeof(ret[0])*(i+2));
+       if (numlines) *numlines = i;
+
+       ret[0] = p;
+       for (s = p, i=0; s < p+size; s++) {
+               if (s[0] == '\n') {
+                       s[0] = 0;
+                       i++;
+                       ret[i] = s+1;
+               }
+               if (s[0] == '\r') s[0] = 0;
+       }
+
+       return ret;
+}
+
+
+/****************************************************************************
+load a file into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free(). 
+****************************************************************************/
+char **file_lines_load(const char *fname, int *numlines)
+{
+       char *p;
+       size_t size;
+
+       p = file_load(fname, &size);
+       if (!p) return NULL;
+
+       return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+load a fd into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free(). If convert is true calls unix_to_dos on
+the list.
+****************************************************************************/
+char **fd_lines_load(int fd, int *numlines)
+{
+       char *p;
+       size_t size;
+
+       p = fd_load(fd, &size);
+       if (!p) return NULL;
+
+       return file_lines_parse(p, size, numlines);
+}
+
+
+/****************************************************************************
+load a pipe into memory and return an array of pointers to lines in the data
+must be freed with file_lines_free(). 
+****************************************************************************/
+char **file_lines_pload(char *syscmd, int *numlines)
+{
+       char *p;
+       size_t size;
+
+       p = file_pload(syscmd, &size);
+       if (!p) return NULL;
+
+       return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+free lines loaded with file_lines_load
+****************************************************************************/
+void file_lines_free(char **lines)
+{
+       if (!lines) return;
+       SAFE_FREE(lines[0]);
+       SAFE_FREE(lines);
+}
+
+
+/****************************************************************************
+take a lislist of lines and modify them to produce a list where \ continues
+a line
+****************************************************************************/
+void file_lines_slashcont(char **lines)
+{
+       int i, j;
+
+       for (i=0; lines[i];) {
+               int len = strlen(lines[i]);
+               if (lines[i][len-1] == '\\') {
+                       lines[i][len-1] = ' ';
+                       if (lines[i+1]) {
+                               char *p = &lines[i][len];
+                               while (p < lines[i+1]) *p++ = ' ';
+                               for (j = i+1; lines[j]; j++) lines[j] = lines[j+1];
+                       }
+               } else {
+                       i++;
+               }
+       }
+}
+
+/*
+  save a lump of data into a file. Mostly used for debugging 
+*/
+BOOL file_save(const char *fname, void *packet, size_t length)
+{
+       int fd;
+       fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd == -1) {
+               return False;
+       }
+       if (write(fd, packet, length) != (size_t)length) {
+               return False;
+       }
+       close(fd);
+       return True;
+}
diff --git a/source4/lib/util_getent.c b/source4/lib/util_getent.c
new file mode 100644 (file)
index 0000000..32641db
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Simo Sorce 2001
+   Copyright (C) Jeremy Allison 2001
+
+   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"
+
+
+/****************************************************************
+ Returns a single linked list of group entries.
+ Use grent_free() to free it after use.
+****************************************************************/
+
+struct sys_grent * getgrent_list(void)
+{
+       struct sys_grent *glist;
+       struct sys_grent *gent;
+       struct group *grp;
+       
+       gent = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+       if (gent == NULL) {
+               DEBUG (0, ("Out of memory in getgrent_list!\n"));
+               return NULL;
+       }
+       memset(gent, '\0', sizeof(struct sys_grent));
+       glist = gent;
+       
+       setgrent();
+       grp = getgrent();
+       if (grp == NULL) {
+               endgrent();
+               SAFE_FREE(glist);
+               return NULL;
+       }
+
+       while (grp != NULL) {
+               int i,num;
+               
+               if (grp->gr_name) {
+                       if ((gent->gr_name = strdup(grp->gr_name)) == NULL)
+                               goto err;
+               }
+               if (grp->gr_passwd) {
+                       if ((gent->gr_passwd = strdup(grp->gr_passwd)) == NULL)
+                               goto err;
+               }
+               gent->gr_gid = grp->gr_gid;
+               
+               /* number of strings in gr_mem */
+               for (num = 0; grp->gr_mem[num]; num++)
+                       ;
+               
+               /* alloc space for gr_mem string pointers */
+               if ((gent->gr_mem = (char **) malloc((num+1) * sizeof(char *))) == NULL)
+                       goto err;
+
+               memset(gent->gr_mem, '\0', (num+1) * sizeof(char *));
+
+               for (i=0; i < num; i++) {
+                       if ((gent->gr_mem[i] = strdup(grp->gr_mem[i])) == NULL)
+                               goto err;
+               }
+               gent->gr_mem[num] = NULL;
+               
+               grp = getgrent();
+               if (grp) {
+                       gent->next = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+                       if (gent->next == NULL)
+                               goto err;
+                       gent = gent->next;
+                       memset(gent, '\0', sizeof(struct sys_grent));
+               }
+       }
+       
+       endgrent();
+       return glist;
+
+  err:
+
+       endgrent();
+       DEBUG(0, ("Out of memory in getgrent_list!\n"));
+       grent_free(glist);
+       return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of group entries made by
+ getgrent_list()
+****************************************************************/
+
+void grent_free (struct sys_grent *glist)
+{
+       while (glist) {
+               struct sys_grent *prev;
+               
+               SAFE_FREE(glist->gr_name);
+               SAFE_FREE(glist->gr_passwd);
+               if (glist->gr_mem) {
+                       int i;
+                       for (i = 0; glist->gr_mem[i]; i++)
+                               SAFE_FREE(glist->gr_mem[i]);
+                       SAFE_FREE(glist->gr_mem);
+               }
+               prev = glist;
+               glist = glist->next;
+               SAFE_FREE(prev);
+       }
+}
+
+/****************************************************************
+ Returns a single linked list of passwd entries.
+ Use pwent_free() to free it after use.
+****************************************************************/
+
+struct sys_pwent * getpwent_list(void)
+{
+       struct sys_pwent *plist;
+       struct sys_pwent *pent;
+       struct passwd *pwd;
+       
+       pent = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+       if (pent == NULL) {
+               DEBUG (0, ("Out of memory in getpwent_list!\n"));
+               return NULL;
+       }
+       plist = pent;
+       
+       setpwent();
+       pwd = getpwent();
+       while (pwd != NULL) {
+               memset(pent, '\0', sizeof(struct sys_pwent));
+               if (pwd->pw_name) {
+                       if ((pent->pw_name = strdup(pwd->pw_name)) == NULL)
+                               goto err;
+               }
+               if (pwd->pw_passwd) {
+                       if ((pent->pw_passwd = strdup(pwd->pw_passwd)) == NULL)
+                               goto err;
+               }
+               pent->pw_uid = pwd->pw_uid;
+               pent->pw_gid = pwd->pw_gid;
+               if (pwd->pw_gecos) {
+                       if ((pent->pw_name = strdup(pwd->pw_gecos)) == NULL)
+                               goto err;
+               }
+               if (pwd->pw_dir) {
+                       if ((pent->pw_name = strdup(pwd->pw_dir)) == NULL)
+                               goto err;
+               }
+               if (pwd->pw_shell) {
+                       if ((pent->pw_name = strdup(pwd->pw_shell)) == NULL)
+                               goto err;
+               }
+
+               pwd = getpwent();
+               if (pwd) {
+                       pent->next = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+                       if (pent->next == NULL)
+                               goto err;
+                       pent = pent->next;
+               }
+       }
+       
+       endpwent();
+       return plist;
+
+  err:
+
+       endpwent();
+       DEBUG(0, ("Out of memory in getpwent_list!\n"));
+       pwent_free(plist);
+       return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of passwd entries made by
+ getpwent_list()
+****************************************************************/
+
+void pwent_free (struct sys_pwent *plist)
+{
+       while (plist) {
+               struct sys_pwent *prev;
+               
+               SAFE_FREE(plist->pw_name);
+               SAFE_FREE(plist->pw_passwd);
+               SAFE_FREE(plist->pw_gecos);
+               SAFE_FREE(plist->pw_dir);
+               SAFE_FREE(plist->pw_shell);
+
+               prev = plist;
+               plist = plist->next;
+               SAFE_FREE(prev);
+       }
+}
+
+/****************************************************************
+ Add the individual group users onto the list.
+****************************************************************/
+
+static struct sys_userlist *add_members_to_userlist(struct sys_userlist *list_head, const struct group *grp)
+{
+       size_t num_users, i;
+
+       /* Count the number of users. */
+       for (num_users = 0; grp->gr_mem[num_users]; num_users++)
+               ;
+
+       for (i = 0; i < num_users; i++) {
+               struct sys_userlist *entry = (struct sys_userlist *)malloc(sizeof(*entry));
+               if (entry == NULL) {
+                       free_userlist(list_head);
+                       return NULL;
+               }
+               entry->unix_name = (char *)strdup(grp->gr_mem[i]);
+               if (entry->unix_name == NULL) {
+                       SAFE_FREE(entry);
+                       free_userlist(list_head);
+                       return NULL;
+               }
+               DLIST_ADD(list_head, entry);
+       }
+       return list_head;
+}
+
+/****************************************************************
+ Get the list of UNIX users in a group.
+ We have to enumerate the /etc/group file as some UNIX getgrnam()
+ calls won't do that for us (notably Tru64 UNIX).
+****************************************************************/
+
+struct sys_userlist *get_users_in_group(const char *gname)
+{
+       struct sys_userlist *list_head = NULL;
+       struct group *gptr;
+       fstring domain;
+       fstring groupname;
+       DOM_SID sid;
+       enum SID_NAME_USE name_type;
+
+       /* No point using winbind if we can't split it in the
+          first place */
+       if (split_domain_and_name(gname, domain, groupname)) {
+
+               /*
+                * If we're doing this via winbindd, don't do the
+                * entire group list enumeration as we know this is
+                * pointless (and slow).
+                */
+               
+               if (winbind_lookup_name(domain, groupname, &sid, &name_type) 
+                   && name_type == SID_NAME_DOM_GRP) {
+                       if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+                               return NULL;
+                       return add_members_to_userlist(list_head, gptr);
+               }
+       }
+       
+#if !defined(BROKEN_GETGRNAM)
+       if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+               return NULL;
+       return add_members_to_userlist(list_head, gptr);
+#else
+       /* BROKEN_GETGRNAM - True64 */
+       setgrent();
+       while((gptr = getgrent()) != NULL) {
+               if (strequal(gname, gptr->gr_name)) {
+                       list_head = add_members_to_userlist(list_head, gptr);
+                       if (list_head == NULL)
+                               return NULL;
+               }
+       }
+       endgrent();
+       return list_head;
+#endif
+}
+
+/****************************************************************
+ Free list allocated above.
+****************************************************************/
+
+void free_userlist(struct sys_userlist *list_head)
+{
+       while (list_head) {
+               struct sys_userlist *old_head = list_head;
+               DLIST_REMOVE(list_head, list_head);
+               SAFE_FREE(old_head->unix_name);
+               SAFE_FREE(old_head);
+       }
+}
diff --git a/source4/lib/util_pw.c b/source4/lib/util_pw.c
new file mode 100644 (file)
index 0000000..9d075a0
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Safe versions of getpw* calls
+
+   Copyright (C) Andrew Bartlett 2002
+   
+   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"
+
+static struct passwd *alloc_copy_passwd(const struct passwd *from) 
+{
+       struct passwd *ret = smb_xmalloc(sizeof(struct passwd));
+       ZERO_STRUCTP(ret);
+       ret->pw_name = smb_xstrdup(from->pw_name);
+       ret->pw_passwd = smb_xstrdup(from->pw_passwd);
+       ret->pw_uid = from->pw_uid;
+       ret->pw_gid = from->pw_gid;
+       ret->pw_gecos = smb_xstrdup(from->pw_gecos);
+       ret->pw_dir = smb_xstrdup(from->pw_dir);
+       ret->pw_shell = smb_xstrdup(from->pw_shell);
+       return ret;
+}
+
+void passwd_free (struct passwd **buf)
+{
+       if (!*buf) {
+               DEBUG(0, ("attempted double-free of allocated passwd\n"));
+               return;
+       }
+
+       SAFE_FREE((*buf)->pw_name);
+       SAFE_FREE((*buf)->pw_passwd);
+       SAFE_FREE((*buf)->pw_gecos);
+       SAFE_FREE((*buf)->pw_dir);
+       SAFE_FREE((*buf)->pw_shell);
+
+       SAFE_FREE(*buf);
+}
+
+struct passwd *getpwnam_alloc(const char *name) 
+{
+       struct passwd *temp;
+
+       temp = sys_getpwnam(name);
+       
+       if (!temp) {
+#if 0
+               if (errno == ENOMEM) {
+                       /* what now? */
+               }
+#endif
+               return NULL;
+       }
+
+       return alloc_copy_passwd(temp);
+}
+
+struct passwd *getpwuid_alloc(uid_t uid) 
+{
+       struct passwd *temp;
+
+       temp = sys_getpwuid(uid);
+       
+       if (!temp) {
+#if 0
+               if (errno == ENOMEM) {
+                       /* what now? */
+               }
+#endif
+               return NULL;
+       }
+
+       return alloc_copy_passwd(temp);
+}
diff --git a/source4/lib/util_seaccess.c b/source4/lib/util_seaccess.c
new file mode 100644 (file)
index 0000000..eba8cab
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+   Unix SMB/CIFS implementation.
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
+   Copyright (C) Tim Potter 2000.
+   Copyright (C) Re-written by Jeremy Allison 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"
+
+extern DOM_SID global_sid_Builtin;
+
+/**********************************************************************************
+ Check if this ACE has a SID in common with the token.
+**********************************************************************************/
+
+static BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace)
+{
+       size_t i;
+
+       for (i = 0; i < token->num_sids; i++) {
+               if (sid_equal(&ace->trustee, &token->user_sids[i]))
+                       return True;
+       }
+
+       return False;
+}
+
+/*********************************************************************************
+ Check an ACE against a SID.  We return the remaining needed permission
+ bits not yet granted. Zero means permission allowed (no more needed bits).
+**********************************************************************************/
+
+static uint32 check_ace(SEC_ACE *ace, const NT_USER_TOKEN *token, uint32 acc_desired, 
+                       NTSTATUS *status)
+{
+       uint32 mask = ace->info.mask;
+
+       /*
+        * Inherit only is ignored.
+        */
+
+       if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+               return acc_desired;
+       }
+
+       /*
+        * If this ACE has no SID in common with the token,
+        * ignore it as it cannot be used to make an access
+        * determination.
+        */
+
+       if (!token_sid_in_ace( token, ace))
+               return acc_desired;     
+
+       switch (ace->type) {
+               case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                       /*
+                        * This is explicitly allowed.
+                        * Remove the bits from the remaining
+                        * access required. Return the remaining
+                        * bits needed.
+                        */
+                       acc_desired &= ~mask;
+                       break;
+               case SEC_ACE_TYPE_ACCESS_DENIED:
+                       /*
+                        * This is explicitly denied.
+                        * If any bits match terminate here,
+                        * we are denied.
+                        */
+                       if (acc_desired & mask) {
+                               *status = NT_STATUS_ACCESS_DENIED;
+                               return 0xFFFFFFFF;
+                       }
+                       break;
+               case SEC_ACE_TYPE_SYSTEM_ALARM:
+               case SEC_ACE_TYPE_SYSTEM_AUDIT:
+                       *status = NT_STATUS_NOT_IMPLEMENTED;
+                       return 0xFFFFFFFF;
+               default:
+                       *status = NT_STATUS_INVALID_PARAMETER;
+                       return 0xFFFFFFFF;
+       }
+
+       return acc_desired;
+}
+
+/*********************************************************************************
+ Maximum access was requested. Calculate the max possible. Fail if it doesn't
+ include other bits requested.
+**********************************************************************************/ 
+
+static BOOL get_max_access( SEC_ACL *the_acl, const NT_USER_TOKEN *token, uint32 *granted, 
+                           uint32 desired, 
+                           NTSTATUS *status)
+{
+       uint32 acc_denied = 0;
+       uint32 acc_granted = 0;
+       size_t i;
+       
+       for ( i = 0 ; i < the_acl->num_aces; i++) {
+               SEC_ACE *ace = &the_acl->ace[i];
+               uint32 mask = ace->info.mask;
+
+               if (!token_sid_in_ace( token, ace))
+                       continue;
+
+               switch (ace->type) {
+                       case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                               acc_granted |= (mask & ~acc_denied);
+                               break;
+                       case SEC_ACE_TYPE_ACCESS_DENIED:
+                               acc_denied |= (mask & ~acc_granted);
+                               break;
+                       case SEC_ACE_TYPE_SYSTEM_ALARM:
+                       case SEC_ACE_TYPE_SYSTEM_AUDIT:
+                               *status = NT_STATUS_NOT_IMPLEMENTED;
+                               *granted = 0;
+                               return False;
+                       default:
+                               *status = NT_STATUS_INVALID_PARAMETER;
+                               *granted = 0;
+                               return False;
+               }                           
+       }
+
+       /*
+        * If we were granted no access, or we desired bits that we
+        * didn't get, then deny.
+        */
+
+       if ((acc_granted == 0) || ((acc_granted & desired) != desired)) {
+               *status = NT_STATUS_ACCESS_DENIED;
+               *granted = 0;
+               return False;
+       }
+
+       /*
+        * Return the access we did get.
+        */
+
+       *granted = acc_granted;
+       *status = NT_STATUS_OK;
+       return True;
+}
+
+/* Map generic access rights to object specific rights.  This technique is
+   used to give meaning to assigning read, write, execute and all access to
+   objects.  Each type of object has its own mapping of generic to object
+   specific access rights. */
+
+void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping)
+{
+       uint32 old_mask = *access_mask;
+
+       if (*access_mask & GENERIC_READ_ACCESS) {
+               *access_mask &= ~GENERIC_READ_ACCESS;
+               *access_mask |= mapping->generic_read;
+       }
+
+       if (*access_mask & GENERIC_WRITE_ACCESS) {
+               *access_mask &= ~GENERIC_WRITE_ACCESS;
+               *access_mask |= mapping->generic_write;
+       }
+
+       if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+               *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+               *access_mask |= mapping->generic_execute;
+       }
+
+       if (*access_mask & GENERIC_ALL_ACCESS) {
+               *access_mask &= ~GENERIC_ALL_ACCESS;
+               *access_mask |= mapping->generic_all;
+       }
+
+       if (old_mask != *access_mask) {
+               DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+                          old_mask, *access_mask));
+       }
+}
+
+/* Map standard access rights to object specific rights.  This technique is
+   used to give meaning to assigning read, write, execute and all access to
+   objects.  Each type of object has its own mapping of standard to object
+   specific access rights. */
+
+void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping)
+{
+       uint32 old_mask = *access_mask;
+
+       if (*access_mask & READ_CONTROL_ACCESS) {
+               *access_mask &= ~READ_CONTROL_ACCESS;
+               *access_mask |= mapping->std_read;
+       }
+
+       if (*access_mask & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS)) {
+               *access_mask &= ~(DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS);
+               *access_mask |= mapping->std_all;
+       }
+
+       if (old_mask != *access_mask) {
+               DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+                          old_mask, *access_mask));
+       }
+}
+
+/*****************************************************************************
+ Check access rights of a user against a security descriptor.  Look at
+ each ACE in the security descriptor until an access denied ACE denies
+ any of the desired rights to the user or any of the users groups, or one
+ or more ACEs explicitly grant all requested access rights.  See
+ "Access-Checking" document in MSDN.
+*****************************************************************************/ 
+
+BOOL se_access_check(const SEC_DESC *sd, const NT_USER_TOKEN *token,
+                    uint32 acc_desired, uint32 *acc_granted, 
+                    NTSTATUS *status)
+{
+       extern NT_USER_TOKEN anonymous_token;
+       size_t i;
+       SEC_ACL *the_acl;
+       fstring sid_str;
+       uint32 tmp_acc_desired = acc_desired;
+
+       if (!status || !acc_granted)
+               return False;
+
+       if (!token)
+               token = &anonymous_token;
+
+       *status = NT_STATUS_OK;
+       *acc_granted = 0;
+
+       DEBUG(10,("se_access_check: requested access 0x%08x, for NT token with %u entries and first sid %s.\n",
+                (unsigned int)acc_desired, (unsigned int)token->num_sids,
+               sid_to_string(sid_str, &token->user_sids[0])));
+
+       /*
+        * No security descriptor or security descriptor with no DACL
+        * present allows all access.
+        */
+
+       /* ACL must have something in it */
+
+       if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) {
+               *status = NT_STATUS_OK;
+               *acc_granted = acc_desired;
+               DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n"));
+               return True;
+       }
+
+       /* The user sid is the first in the token */
+       if (DEBUGLVL(3)) {
+               DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) ));
+               
+               for (i = 1; i < token->num_sids; i++) {
+                       DEBUGADD(3, ("se_access_check: also %s\n",
+                                 sid_to_string(sid_str, &token->user_sids[i])));
+               }
+       }
+
+       /* Is the token the owner of the SID ? */
+
+       if (sd->owner_sid) {
+               for (i = 0; i < token->num_sids; i++) {
+                       if (sid_equal(&token->user_sids[i], sd->owner_sid)) {
+                               /*
+                                * The owner always has SEC_RIGHTS_WRITE_DAC & READ_CONTROL.
+                                */
+                               if (tmp_acc_desired & WRITE_DAC_ACCESS)
+                                       tmp_acc_desired &= ~WRITE_DAC_ACCESS;
+                               if (tmp_acc_desired & READ_CONTROL_ACCESS)
+                                       tmp_acc_desired &= ~READ_CONTROL_ACCESS;
+                       }
+               }
+       }
+
+       the_acl = sd->dacl;
+
+       if (tmp_acc_desired & MAXIMUM_ALLOWED_ACCESS) {
+               tmp_acc_desired &= ~MAXIMUM_ALLOWED_ACCESS;
+               return get_max_access( the_acl, token, acc_granted, tmp_acc_desired, 
+                                      status);
+       }
+
+       for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) {
+               SEC_ACE *ace = &the_acl->ace[i];
+
+               DEBUGADD(10,("se_access_check: ACE %u: type %d, flags = 0x%02x, SID = %s mask = %x, current desired = %x\n",
+                         (unsigned int)i, ace->type, ace->flags,
+                         sid_to_string(sid_str, &ace->trustee),
+                         (unsigned int) ace->info.mask, 
+                         (unsigned int)tmp_acc_desired ));
+
+               tmp_acc_desired = check_ace( ace, token, tmp_acc_desired, status);
+               if (NT_STATUS_V(*status)) {
+                       *acc_granted = 0;
+                       DEBUG(5,("se_access_check: ACE %u denied with status %s.\n", (unsigned int)i, nt_errstr(*status)));
+                       return False;
+               }
+       }
+
+       /*
+        * If there are no more desired permissions left then
+        * access was allowed.
+        */
+
+       if (tmp_acc_desired == 0) {
+               *acc_granted = acc_desired;
+               *status = NT_STATUS_OK;
+               DEBUG(5,("se_access_check: access (%x) granted.\n", (unsigned int)acc_desired ));
+               return True;
+       }
+               
+       *acc_granted = 0;
+       *status = NT_STATUS_ACCESS_DENIED;
+       DEBUG(5,("se_access_check: access (%x) denied.\n", (unsigned int)acc_desired ));
+       return False;
+}
+
+/* Create a child security descriptor using another security descriptor as
+   the parent container.  This child object can either be a container or
+   non-container object. */
+
+SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr, 
+                                     BOOL child_container)
+{
+       SEC_DESC_BUF *sdb;
+       SEC_DESC *sd;
+       SEC_ACL *new_dacl, *the_acl;
+       SEC_ACE *new_ace_list = NULL;
+       unsigned int new_ace_list_ndx = 0, i;
+       size_t size;
+
+       /* Currently we only process the dacl when creating the child.  The
+          sacl should also be processed but this is left out as sacls are
+          not implemented in Samba at the moment.*/
+
+       the_acl = parent_ctr->dacl;
+
+       if (!(new_ace_list = talloc(ctx, sizeof(SEC_ACE) * the_acl->num_aces))) 
+               return NULL;
+
+       for (i = 0; the_acl && i < the_acl->num_aces; i++) {
+               SEC_ACE *ace = &the_acl->ace[i];
+               SEC_ACE *new_ace = &new_ace_list[new_ace_list_ndx];
+               uint8 new_flags = 0;
+               BOOL inherit = False;
+               fstring sid_str;
+
+               /* The OBJECT_INHERIT_ACE flag causes the ACE to be
+                  inherited by non-container children objects.  Container
+                  children objects will inherit it as an INHERIT_ONLY
+                  ACE. */
+
+               if (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
+
+                       if (!child_container) {
+                               new_flags |= SEC_ACE_FLAG_OBJECT_INHERIT;
+                       } else {
+                               new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                       }
+
+                       inherit = True;
+               }
+
+               /* The CONAINER_INHERIT_ACE flag means all child container
+                  objects will inherit and use the ACE. */
+
+               if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+                       if (!child_container) {
+                               inherit = False;
+                       } else {
+                               new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
+                       }
+               }
+
+               /* The INHERIT_ONLY_ACE is not used by the se_access_check()
+                  function for the parent container, but is inherited by
+                  all child objects as a normal ACE. */
+
+               if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+                       /* Move along, nothing to see here */
+               }
+
+               /* The SEC_ACE_FLAG_NO_PROPAGATE_INHERIT flag means the ACE
+                  is inherited by child objects but not grandchildren
+                  objects.  We clear the object inherit and container
+                  inherit flags in the inherited ACE. */
+
+               if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+                       new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT |
+                                      SEC_ACE_FLAG_CONTAINER_INHERIT);
+               }
+
+               /* Add ACE to ACE list */
+
+               if (!inherit)
+                       continue;
+
+               init_sec_access(&new_ace->info, ace->info.mask);
+               init_sec_ace(new_ace, &ace->trustee, ace->type,
+                            new_ace->info, new_flags);
+
+               sid_to_string(sid_str, &ace->trustee);
+
+               DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x "
+                         " inherited as %s:%d/0x%02x/0x%08x\n", sid_str,
+                         ace->type, ace->flags, ace->info.mask,
+                         sid_str, new_ace->type, new_ace->flags,
+                         new_ace->info.mask));
+
+               new_ace_list_ndx++;
+       }
+
+       /* Create child security descriptor to return */
+       
+       new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list);
+
+       /* Use the existing user and group sids.  I don't think this is
+          correct.  Perhaps the user and group should be passed in as
+          parameters by the caller? */
+
+       sd = make_sec_desc(ctx, SEC_DESC_REVISION,
+                          parent_ctr->owner_sid,
+                          parent_ctr->grp_sid,
+                          parent_ctr->sacl,
+                          new_dacl, &size);
+
+       sdb = make_sec_desc_buf(ctx, size, sd);
+
+       return sdb;
+}
+
+/*******************************************************************
+ samr_make_sam_obj_sd
+ ********************************************************************/
+
+NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+       extern DOM_SID global_sid_World;
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
+
+       SEC_ACE ace[3];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+       /*basic access for every one*/
+       init_sec_access(&mask, GENERIC_RIGHTS_SAM_EXECUTE | GENERIC_RIGHTS_SAM_READ);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_access(&mask, GENERIC_RIGHTS_SAM_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+       init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/lib/util_sid.c b/source4/lib/util_sid.c
new file mode 100644 (file)
index 0000000..9910a9d
--- /dev/null
@@ -0,0 +1,631 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell               1992-1998
+   Copyright (C) Luke Kenneth Caseson Leighton         1998-1999
+   Copyright (C) Jeremy Allison                1999
+   Copyright (C) Stefan (metze) Metzmacher     2002
+   Copyright (C) Simo Sorce                    2002
+      
+   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"
+
+/*
+ * Some useful sids
+ */
+
+DOM_SID global_sid_World_Domain;               /* Everyone domain */
+DOM_SID global_sid_World;                              /* Everyone */
+DOM_SID global_sid_Creator_Owner_Domain;    /* Creator Owner domain */
+DOM_SID global_sid_NT_Authority;               /* NT Authority */
+DOM_SID global_sid_System;             /* System */
+DOM_SID global_sid_NULL;                       /* NULL sid */
+DOM_SID global_sid_Authenticated_Users;                /* All authenticated rids */
+DOM_SID global_sid_Network;                    /* Network rids */
+
+DOM_SID global_sid_Creator_Owner;      /* Creator Owner */
+DOM_SID global_sid_Creator_Group;      /* Creator Group */
+DOM_SID global_sid_Anonymous;          /* Anonymous login */
+
+DOM_SID global_sid_Builtin;                    /* Local well-known domain */
+DOM_SID global_sid_Builtin_Administrators;     /* Builtin administrators */
+DOM_SID global_sid_Builtin_Users;              /* Builtin users */
+DOM_SID global_sid_Builtin_Guests;             /* Builtin guest users */
+DOM_SID global_sid_Builtin_Power_Users;                /* Builtin power users */
+DOM_SID global_sid_Builtin_Account_Operators;  /* Builtin account operators */
+DOM_SID global_sid_Builtin_Server_Operators;   /* Builtin server operators */
+DOM_SID global_sid_Builtin_Print_Operators;    /* Builtin print operators */
+DOM_SID global_sid_Builtin_Backup_Operators;   /* Builtin backup operators */
+DOM_SID global_sid_Builtin_Replicator;         /* Builtin replicator */
+
+#define SECURITY_NULL_SID_AUTHORITY    0
+#define SECURITY_WORLD_SID_AUTHORITY   1
+#define SECURITY_LOCAL_SID_AUTHORITY   2
+#define SECURITY_CREATOR_SID_AUTHORITY 3
+#define SECURITY_NT_AUTHORITY          5
+
+/*
+ * An NT compatible anonymous token.
+ */
+
+static DOM_SID anon_sid_array[3];
+
+NT_USER_TOKEN anonymous_token = {
+       3,
+       anon_sid_array
+};
+
+static DOM_SID system_sid_array[4];
+NT_USER_TOKEN system_token = {
+       1,
+       system_sid_array
+};
+
+/****************************************************************************
+ Lookup string names for SID types.
+****************************************************************************/
+
+static const struct {
+       enum SID_NAME_USE sid_type;
+       const char *string;
+} sid_name_type[] = {
+       {SID_NAME_USER, "User"},
+       {SID_NAME_DOM_GRP, "Domain Group"},
+       {SID_NAME_DOMAIN, "Domain"},
+       {SID_NAME_ALIAS, "Local Group"},
+       {SID_NAME_WKN_GRP, "Well-known Group"},
+       {SID_NAME_DELETED, "Deleted Account"},
+       {SID_NAME_INVALID, "Invalid Account"},
+       {SID_NAME_UNKNOWN, "UNKNOWN"},
+
+       {SID_NAME_USE_NONE, NULL}
+};
+
+const char *sid_type_lookup(uint32 sid_type) 
+{
+       int i = 0;
+
+       /* Look through list */
+       while(sid_name_type[i].sid_type != 0) {
+               if (sid_name_type[i].sid_type == sid_type)
+                       return sid_name_type[i].string;
+               i++;
+       }
+
+       /* Default return */
+       return "SID *TYPE* is INVALID";
+}
+
+/****************************************************************************
+ Creates some useful well known sids
+****************************************************************************/
+
+void generate_wellknown_sids(void)
+{
+       static BOOL initialised = False;
+
+       if (initialised) 
+               return;
+
+       /* SECURITY_NULL_SID_AUTHORITY */
+       string_to_sid(&global_sid_NULL, "S-1-0-0");
+
+       /* SECURITY_WORLD_SID_AUTHORITY */
+       string_to_sid(&global_sid_World_Domain, "S-1-1");
+       string_to_sid(&global_sid_World, "S-1-1-0");
+
+       /* SECURITY_CREATOR_SID_AUTHORITY */
+       string_to_sid(&global_sid_Creator_Owner_Domain, "S-1-3");
+       string_to_sid(&global_sid_Creator_Owner, "S-1-3-0");
+       string_to_sid(&global_sid_Creator_Group, "S-1-3-1");
+
+       /* SECURITY_NT_AUTHORITY */
+       string_to_sid(&global_sid_NT_Authority, "S-1-5");
+       string_to_sid(&global_sid_Network, "S-1-5-2");
+       string_to_sid(&global_sid_Anonymous, "S-1-5-7");
+       string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11");
+       string_to_sid(&global_sid_System, "S-1-5-18");
+
+       /* SECURITY_BUILTIN_DOMAIN_RID */
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+       string_to_sid(&global_sid_Builtin_Administrators, "S-1-5-32-544");
+       string_to_sid(&global_sid_Builtin_Users, "S-1-5-32-545");
+       string_to_sid(&global_sid_Builtin_Guests, "S-1-5-32-546");
+       string_to_sid(&global_sid_Builtin_Power_Users, "S-1-5-32-547");
+       string_to_sid(&global_sid_Builtin_Account_Operators, "S-1-5-32-548");
+       string_to_sid(&global_sid_Builtin_Server_Operators, "S-1-5-32-549");
+       string_to_sid(&global_sid_Builtin_Print_Operators, "S-1-5-32-550");
+       string_to_sid(&global_sid_Builtin_Backup_Operators, "S-1-5-32-551");
+       string_to_sid(&global_sid_Builtin_Replicator, "S-1-5-32-552");
+
+       /* Create the anon token. */
+       sid_copy( &anonymous_token.user_sids[0], &global_sid_World);
+       sid_copy( &anonymous_token.user_sids[1], &global_sid_Network);
+       sid_copy( &anonymous_token.user_sids[2], &global_sid_Anonymous);
+
+       /* Create the system token. */
+       sid_copy( &system_token.user_sids[0], &global_sid_System);
+       
+       initialised = True;
+}
+
+/**************************************************************************
+ Create the SYSTEM token.
+***************************************************************************/
+
+NT_USER_TOKEN *get_system_token(void) 
+{
+       generate_wellknown_sids(); /* The token is initialised here */
+       return &system_token;
+}
+
+/**************************************************************************
+ Splits a name of format \DOMAIN\name or name into its two components.
+ Sets the DOMAIN name to lp_netbios_name() if it has not been specified.
+***************************************************************************/
+
+void split_domain_name(const char *fullname, char *domain, char *name)
+{
+       pstring full_name;
+       const char *sep;
+       char *p;
+
+       sep = lp_winbind_separator();
+
+       *domain = *name = '\0';
+
+       if (fullname[0] == sep[0] || fullname[0] == '\\')
+               fullname++;
+
+       pstrcpy(full_name, fullname);
+       p = strchr_m(full_name+1, '\\');
+       if (!p) p = strchr_m(full_name+1, sep[0]);
+
+       if (p != NULL) {
+               *p = 0;
+               fstrcpy(domain, full_name);
+               fstrcpy(name, p+1);
+       } else {
+               fstrcpy(domain, lp_netbios_name());
+               fstrcpy(name, full_name);
+       }
+
+       DEBUG(10,("split_domain_name:name '%s' split into domain :'%s' and user :'%s'\n",
+                       fullname, domain, name));
+}
+
+/****************************************************************************
+ Test if a SID is wellknown and resolvable.
+****************************************************************************/
+
+BOOL resolvable_wellknown_sid(DOM_SID *sid)
+{
+       uint32 ia = (sid->id_auth[5]) +
+                       (sid->id_auth[4] << 8 ) +
+                       (sid->id_auth[3] << 16) +
+                       (sid->id_auth[2] << 24);
+
+       if (sid->sid_rev_num != SEC_DESC_REVISION || sid->num_auths < 1)
+               return False;
+
+       return (ia == SECURITY_WORLD_SID_AUTHORITY ||
+               ia == SECURITY_CREATOR_SID_AUTHORITY);
+}
+
+/*****************************************************************
+ Convert a SID to an ascii string.
+*****************************************************************/
+
+char *sid_to_string(fstring sidstr_out, const DOM_SID *sid)
+{
+       char subauth[16];
+       int i;
+       uint32 ia;
+  
+       if (!sid) {
+               fstrcpy(sidstr_out, "(NULL SID)");
+               return sidstr_out;
+       }
+
+       /*
+        * BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 
+        * in a range of 2^48.
+        */
+       ia = (sid->id_auth[5]) +
+               (sid->id_auth[4] << 8 ) +
+               (sid->id_auth[3] << 16) +
+               (sid->id_auth[2] << 24);
+
+       slprintf(sidstr_out, sizeof(fstring) - 1, "S-%u-%lu", (unsigned int)sid->sid_rev_num, (unsigned long)ia);
+
+       for (i = 0; i < sid->num_auths; i++) {
+               slprintf(subauth, sizeof(subauth)-1, "-%lu", (unsigned long)sid->sub_auths[i]);
+               fstrcat(sidstr_out, subauth);
+       }
+
+       return sidstr_out;
+}
+
+/*****************************************************************
+ Useful function for debug lines.
+*****************************************************************/  
+
+const char *sid_string_talloc(TALLOC_CTX *mem_ctx, const DOM_SID *sid)
+{
+       fstring tempSid;
+       sid_to_string(tempSid, sid);
+       return talloc_strdup(mem_ctx, tempSid);
+}
+
+/*****************************************************************
+ Convert a string to a SID. Returns True on success, False on fail.
+*****************************************************************/  
+   
+BOOL string_to_sid(DOM_SID *sidout, const char *sidstr)
+{
+       pstring tok;
+       char *q;
+       const char *p;
+       /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */
+       uint32 ia;
+  
+       if (StrnCaseCmp( sidstr, "S-", 2)) {
+               DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr));
+               return False;
+       }
+
+       memset((char *)sidout, '\0', sizeof(DOM_SID));
+
+       p = q = strdup(sidstr + 2);
+       if (p == NULL) {
+               DEBUG(0, ("string_to_sid: out of memory!\n"));
+               return False;
+       }
+
+       if (!next_token(&p, tok, "-", sizeof(tok))) {
+               DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+               SAFE_FREE(q);
+               return False;
+       }
+
+       /* Get the revision number. */
+       sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10);
+
+       if (!next_token(&p, tok, "-", sizeof(tok))) {
+               DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+               SAFE_FREE(q);
+               return False;
+       }
+
+       /* identauth in decimal should be <  2^32 */
+       ia = (uint32)strtoul(tok, NULL, 10);
+
+       /* NOTE - the ia value is in big-endian format. */
+       sidout->id_auth[0] = 0;
+       sidout->id_auth[1] = 0;
+       sidout->id_auth[2] = (ia & 0xff000000) >> 24;
+       sidout->id_auth[3] = (ia & 0x00ff0000) >> 16;
+       sidout->id_auth[4] = (ia & 0x0000ff00) >> 8;
+       sidout->id_auth[5] = (ia & 0x000000ff);
+
+       sidout->num_auths = 0;
+
+       while(next_token(&p, tok, "-", sizeof(tok)) && 
+               sidout->num_auths < MAXSUBAUTHS) {
+               /* 
+                * NOTE - the subauths are in native machine-endian format. They
+                * are converted to little-endian when linearized onto the wire.
+                */
+               sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10));
+       }
+
+       SAFE_FREE(q);
+       return True;
+}
+
+/*****************************************************************
+ Add a rid to the end of a sid
+*****************************************************************/  
+
+BOOL sid_append_rid(DOM_SID *sid, uint32 rid)
+{
+       if (sid->num_auths < MAXSUBAUTHS) {
+               sid->sub_auths[sid->num_auths++] = rid;
+               return True;
+       }
+       return False;
+}
+
+/*****************************************************************
+ Removes the last rid from the end of a sid
+*****************************************************************/  
+
+BOOL sid_split_rid(DOM_SID *sid, uint32 *rid)
+{
+       if (sid->num_auths > 0) {
+               sid->num_auths--;
+               *rid = sid->sub_auths[sid->num_auths];
+               return True;
+       }
+       return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+*****************************************************************/  
+
+BOOL sid_peek_rid(const DOM_SID *sid, uint32 *rid)
+{
+       if (!sid || !rid)
+               return False;           
+       
+       if (sid->num_auths > 0) {
+               *rid = sid->sub_auths[sid->num_auths - 1];
+               return True;
+       }
+       return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+ and check the sid against the exp_dom_sid  
+*****************************************************************/  
+
+BOOL sid_peek_check_rid(const DOM_SID *exp_dom_sid, const DOM_SID *sid, uint32 *rid)
+{
+       if (!exp_dom_sid || !sid || !rid)
+               return False;
+                       
+
+       if (sid_compare_domain(exp_dom_sid, sid)!=0){
+               *rid=(-1);
+               return False;
+       }
+       
+       return sid_peek_rid(sid, rid);
+}
+
+/*****************************************************************
+ Copies a sid
+*****************************************************************/  
+
+void sid_copy(DOM_SID *dst, const DOM_SID *src)
+{
+       int i;
+
+       ZERO_STRUCTP(dst);
+
+       dst->sid_rev_num = src->sid_rev_num;
+       dst->num_auths = src->num_auths;
+
+       memcpy(&dst->id_auth[0], &src->id_auth[0], sizeof(src->id_auth));
+
+       for (i = 0; i < src->num_auths; i++)
+               dst->sub_auths[i] = src->sub_auths[i];
+}
+
+/*****************************************************************
+ Write a sid out into on-the-wire format.
+*****************************************************************/  
+
+BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid)
+{
+       size_t i;
+
+       if (len < sid_size(sid))
+               return False;
+
+       SCVAL(outbuf,0,sid->sid_rev_num);
+       SCVAL(outbuf,1,sid->num_auths);
+       memcpy(&outbuf[2], sid->id_auth, 6);
+       for(i = 0; i < sid->num_auths; i++)
+               SIVAL(outbuf, 8 + (i*4), sid->sub_auths[i]);
+
+       return True;
+}
+
+/*****************************************************************
+ Parse a on-the-wire SID to a DOM_SID.
+*****************************************************************/  
+
+BOOL sid_parse(const char *inbuf, size_t len, DOM_SID *sid)
+{
+       int i;
+       if (len < 8)
+               return False;
+
+       ZERO_STRUCTP(sid);
+
+       sid->sid_rev_num = CVAL(inbuf, 0);
+       sid->num_auths = CVAL(inbuf, 1);
+       memcpy(sid->id_auth, inbuf+2, 6);
+       if (len < 8 + sid->num_auths*4)
+               return False;
+       for (i=0;i<sid->num_auths;i++)
+               sid->sub_auths[i] = IVAL(inbuf, 8+i*4);
+       return True;
+}
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/  
+
+static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+       int i;
+
+       if (sid1 == sid2)
+               return 0;
+       if (!sid1)
+               return -1;
+       if (!sid2)
+               return 1;
+
+       if (sid1->sid_rev_num != sid2->sid_rev_num)
+               return sid1->sid_rev_num - sid2->sid_rev_num;
+
+       for (i = 0; i < 6; i++)
+               if (sid1->id_auth[i] != sid2->id_auth[i])
+                       return sid1->id_auth[i] - sid2->id_auth[i];
+
+       return 0;
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+       int i;
+
+       if (sid1 == sid2)
+               return 0;
+       if (!sid1)
+               return -1;
+       if (!sid2)
+               return 1;
+
+       /* Compare most likely different rids, first: i.e start at end */
+       if (sid1->num_auths != sid2->num_auths)
+               return sid1->num_auths - sid2->num_auths;
+
+       for (i = sid1->num_auths-1; i >= 0; --i)
+               if (sid1->sub_auths[i] != sid2->sub_auths[i])
+                       return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+       return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ See if 2 SIDs are in the same domain
+ this just compares the leading sub-auths
+*****************************************************************/  
+
+int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+       int n, i;
+
+       n = MIN(sid1->num_auths, sid2->num_auths);
+
+       for (i = n-1; i >= 0; --i)
+               if (sid1->sub_auths[i] != sid2->sub_auths[i])
+                       return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+       return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+
+BOOL sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+       return sid_compare(sid1, sid2) == 0;
+}
+
+/*****************************************************************
+ Check if the SID is the builtin SID (S-1-5-32).
+*****************************************************************/  
+
+BOOL sid_check_is_builtin(const DOM_SID *sid)
+{
+       return sid_equal(sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Check if the SID is one of the builtin SIDs (S-1-5-32-a).
+*****************************************************************/  
+
+BOOL sid_check_is_in_builtin(const DOM_SID *sid)
+{
+       DOM_SID dom_sid;
+       uint32 rid;
+
+       sid_copy(&dom_sid, sid);
+       sid_split_rid(&dom_sid, &rid);
+       
+       return sid_equal(&dom_sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/  
+
+size_t sid_size(const DOM_SID *sid)
+{
+       if (sid == NULL)
+               return 0;
+
+       return sid->num_auths * sizeof(uint32) + 8;
+}
+
+/*****************************************************************
+ Returns true if SID is internal (and non-mappable).
+*****************************************************************/
+
+BOOL non_mappable_sid(DOM_SID *sid)
+{
+       DOM_SID dom;
+       uint32 rid;
+
+       sid_copy(&dom, sid);
+       sid_split_rid(&dom, &rid);
+
+       if (sid_equal(&dom, &global_sid_Builtin))
+               return True;
+
+       if (sid_equal(&dom, &global_sid_NT_Authority))
+               return True;
+
+       return False;
+}
+
+/*****************************************************************
+ Return the binary string representation of a DOM_SID.
+ Caller must free.
+*****************************************************************/
+
+char *sid_binstring(const DOM_SID *sid)
+{
+       char *buf, *s;
+       int len = sid_size(sid);
+       buf = malloc(len);
+       if (!buf)
+               return NULL;
+       sid_linearize(buf, len, sid);
+       s = binary_string(buf, len);
+       free(buf);
+       return s;
+}
+
+
+/*****************************************************************
+ Print a GUID structure for debugging.
+*****************************************************************/
+
+void print_guid(GUID *guid)
+{
+       int i;
+
+       d_printf("%08x-%04x-%04x", 
+                IVAL(guid->info, 0), SVAL(guid->info, 4), SVAL(guid->info, 6));
+       d_printf("-%02x%02x-", guid->info[8], guid->info[9]);
+       for (i=10;i<GUID_SIZE;i++)
+               d_printf("%02x", guid->info[i]);
+       d_printf("\n");
+}
diff --git a/source4/lib/util_smbd.c b/source4/lib/util_smbd.c
new file mode 100644 (file)
index 0000000..071f20b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba utility functions, used in smbd only
+   Copyright (C) Andrew Tridgell 2002
+
+   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"
+
+/* 
+   This function requires sys_getgrouplist - which is only
+   available in smbd due to it's use of become_root() in a 
+   legacy systems hack.
+*/
+
+/*
+  return a full list of groups for a user
+
+  returns the number of groups the user is a member of. The return will include the
+  users primary group.
+
+  remember to free the resulting gid_t array
+
+  NOTE! uses become_root() to gain correct priviages on systems
+  that lack a native getgroups() call (uses initgroups and getgroups)
+*/
+int getgroups_user(const char *user, gid_t **groups)
+{
+       struct passwd *pwd;
+       int ngrp, max_grp;
+
+       pwd = getpwnam_alloc(user);
+       if (!pwd) return -1;
+
+       max_grp = groups_max();
+       (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp);
+       if (! *groups) {
+               passwd_free(&pwd);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp);
+       if (ngrp <= 0) {
+               passwd_free(&pwd);
+               free(*groups);
+               return ngrp;
+       }
+
+       passwd_free(&pwd);
+       return ngrp;
+}
diff --git a/source4/lib/util_sock.c b/source4/lib/util_sock.c
new file mode 100644 (file)
index 0000000..42dc04f
--- /dev/null
@@ -0,0 +1,631 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Tim Potter      2000-2001
+   
+   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"
+
+
+/****************************************************************************
+ Determine if a file descriptor is in fact a socket.
+****************************************************************************/
+BOOL is_a_socket(int fd)
+{
+       int v,l;
+       l = sizeof(int);
+       return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0;
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+typedef struct smb_socket_option {
+       const char *name;
+       int level;
+       int option;
+       int value;
+       int opttype;
+} smb_socket_option;
+
+static const smb_socket_option socket_options[] = {
+  {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
+  {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
+  {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
+#ifdef TCP_NODELAY
+  {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+  {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+  {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
+#endif
+#ifdef SO_REUSEPORT
+  {"SO_REUSEPORT",      SOL_SOCKET,    SO_REUSEPORT,    0,                 OPT_BOOL},
+#endif
+#ifdef SO_SNDBUF
+  {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+  {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+  {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+  {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+  {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+  {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
+#endif
+  {NULL,0,0,0,0}};
+
+/****************************************************************************
+ Print socket options.
+****************************************************************************/
+
+static void print_socket_options(int s)
+{
+       int value, vlen = 4;
+       const smb_socket_option *p = &socket_options[0];
+
+       for (; p->name != NULL; p++) {
+               if (getsockopt(s, p->level, p->option, (void *)&value, &vlen) == -1) {
+                       DEBUG(5,("Could not test socket option %s.\n", p->name));
+               } else {
+                       DEBUG(5,("socket option %s = %d\n",p->name,value));
+               }
+       }
+ }
+
+/****************************************************************************
+ Set user socket options.
+****************************************************************************/
+
+void set_socket_options(int fd, const char *options)
+{
+       fstring tok;
+
+       while (next_token(&options,tok," \t,", sizeof(tok))) {
+               int ret=0,i;
+               int value = 1;
+               char *p;
+               BOOL got_value = False;
+
+               if ((p = strchr_m(tok,'='))) {
+                       *p = 0;
+                       value = atoi(p+1);
+                       got_value = True;
+               }
+
+               for (i=0;socket_options[i].name;i++)
+                       if (strequal(socket_options[i].name,tok))
+                               break;
+
+               if (!socket_options[i].name) {
+                       DEBUG(0,("Unknown socket option %s\n",tok));
+                       continue;
+               }
+
+               switch (socket_options[i].opttype) {
+               case OPT_BOOL:
+               case OPT_INT:
+                       ret = setsockopt(fd,socket_options[i].level,
+                                               socket_options[i].option,(char *)&value,sizeof(int));
+                       break;
+
+               case OPT_ON:
+                       if (got_value)
+                               DEBUG(0,("syntax error - %s does not take a value\n",tok));
+
+                       {
+                               int on = socket_options[i].value;
+                               ret = setsockopt(fd,socket_options[i].level,
+                                                       socket_options[i].option,(char *)&on,sizeof(int));
+                       }
+                       break;    
+               }
+      
+               if (ret != 0)
+                       DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
+       }
+
+       print_socket_options(fd);
+}
+
+/****************************************************************************
+ Read from a socket.
+****************************************************************************/
+
+ssize_t read_udp_socket(int fd, char *buf, size_t len, 
+                       struct in_addr *from_addr, int *from_port)
+{
+       ssize_t ret;
+       struct sockaddr_in sock;
+       socklen_t socklen = sizeof(sock);
+
+       ret = (ssize_t)sys_recvfrom(fd,buf,len, 0, (struct sockaddr *)&sock, &socklen);
+       if (ret <= 0) {
+               DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno)));
+               return 0;
+       }
+
+       if (from_addr) {
+               *from_addr = sock.sin_addr;
+       }
+       if (from_port) {
+               *from_port = ntohs(sock.sin_port);
+       }
+
+       return ret;
+}
+
+
+/****************************************************************************
+  read data from the client, reading exactly N bytes. 
+****************************************************************************/
+ssize_t read_data(int fd, char *buffer, size_t N)
+{
+       ssize_t ret;
+       size_t total=0;  
+       while (total < N) {
+               ret = sys_read(fd,buffer + total,N - total);
+               if (ret == 0) {
+                       return 0;
+               }
+               if (ret == -1) {
+                       return -1;
+               }
+               total += ret;
+       }
+       return (ssize_t)total;
+}
+
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+ssize_t write_data(int fd, const char *buffer, size_t N)
+{
+       size_t total=0;
+       ssize_t ret;
+
+       while (total < N) {
+               ret = sys_write(fd, buffer + total, N - total);
+               if (ret == -1) {
+                       return -1;
+               }
+               if (ret == 0)
+                       return total;
+
+               total += ret;
+       }
+       return (ssize_t)total;
+}
+
+
+/****************************************************************************
+send a keepalive packet (rfc1002)
+****************************************************************************/
+BOOL send_nbt_keepalive(int sock_fd)
+{
+       unsigned char buf[4];
+
+       buf[0] = SMBkeepalive;
+       buf[1] = buf[2] = buf[3] = 0;
+
+       return write_data(sock_fd,(char *)buf,4) == 4;
+}
+
+
+/****************************************************************************
+ Open a socket of the specified type, port, and address for incoming data.
+****************************************************************************/
+int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL rebind )
+{
+       struct sockaddr_in sock;
+       int res;
+
+       memset( (char *)&sock, '\0', sizeof(sock) );
+
+#ifdef HAVE_SOCK_SIN_LEN
+       sock.sin_len         = sizeof(sock);
+#endif
+       sock.sin_port        = htons( port );
+       sock.sin_family      = AF_INET;
+       sock.sin_addr.s_addr = socket_addr;
+
+       res = socket( AF_INET, type, 0 );
+       if( res == -1 ) {
+               DEBUG(0,("open_socket_in(): socket() call failed: %s\n", strerror(errno)));
+               return -1;
+       }
+
+       /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */
+       {
+               int val = rebind ? 1 : 0;
+               setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+               setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif
+       }
+
+       /* now we've got a socket - we need to bind it */
+       if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) {
+               DEBUG(0,("bind failed on port %d - %s\n", port, strerror(errno)));
+               close( res ); 
+               return( -1 ); 
+       }
+
+       DEBUG( 10, ( "bind succeeded on port %d\n", port ) );
+
+       return( res );
+ }
+
+
+/****************************************************************************
+  create an outgoing socket. timeout is in milliseconds.
+  **************************************************************************/
+int open_socket_out(int type, struct in_addr *addr, int port, int timeout)
+{
+       struct sockaddr_in sock_out;
+       int res,ret;
+       int connect_loop = 250; /* 250 milliseconds */
+       int loops = (timeout) / connect_loop;
+
+       /* create a socket to write to */
+       res = socket(PF_INET, type, 0);
+       if (res == -1) 
+       { DEBUG(0,("socket error\n")); return -1; }
+       
+       if (type != SOCK_STREAM) return(res);
+       
+       memset((char *)&sock_out,'\0',sizeof(sock_out));
+       putip((char *)&sock_out.sin_addr,(char *)addr);
+       
+       sock_out.sin_port = htons( port );
+       sock_out.sin_family = PF_INET;
+       
+       /* set it non-blocking */
+       set_blocking(res,False);
+       
+       DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
+       
+       /* and connect it to the destination */
+connect_again:
+       ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));
+       
+       /* Some systems return EAGAIN when they mean EINPROGRESS */
+       if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+                       errno == EAGAIN) && loops--) {
+               msleep(connect_loop);
+               goto connect_again;
+       }
+       
+       if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+                       errno == EAGAIN)) {
+               DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port));
+               close(res);
+               return -1;
+       }
+       
+#ifdef EISCONN
+       if (ret < 0 && errno == EISCONN) {
+               errno = 0;
+               ret = 0;
+       }
+#endif
+       
+       if (ret < 0) {
+               DEBUG(2,("error connecting to %s:%d (%s)\n",
+                        inet_ntoa(*addr),port,strerror(errno)));
+               close(res);
+               return -1;
+       }
+       
+       /* set it blocking again */
+       set_blocking(res,True);
+       
+       return res;
+}
+
+/*
+  open a connected UDP socket to host on port
+*/
+int open_udp_socket(const char *host, int port)
+{
+       int type = SOCK_DGRAM;
+       struct sockaddr_in sock_out;
+       int res;
+       struct in_addr *addr;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("open_udp_socket");
+       if (!mem_ctx) {
+               return -1;
+       }
+       addr = interpret_addr2(mem_ctx, host);
+
+       res = socket(PF_INET, type, 0);
+       if (res == -1) {
+               return -1;
+       }
+
+       memset((char *)&sock_out,'\0',sizeof(sock_out));
+       putip((char *)&sock_out.sin_addr,(char *)addr);
+       sock_out.sin_port = htons(port);
+       sock_out.sin_family = PF_INET;
+       
+       talloc_destroy(mem_ctx);
+
+       if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
+               close(res);
+               return -1;
+       }
+
+       return res;
+}
+
+
+/*******************************************************************
+ matchname - determine if host name matches IP address. Used to
+ confirm a hostname lookup to prevent spoof attacks
+ ******************************************************************/
+static BOOL matchname(char *remotehost, struct in_addr addr)
+{
+       struct hostent *hp;
+       int     i;
+       
+       if ((hp = sys_gethostbyname(remotehost)) == 0) {
+               DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", remotehost));
+               return False;
+       } 
+
+       /*
+        * Make sure that gethostbyname() returns the "correct" host name.
+        * Unfortunately, gethostbyname("localhost") sometimes yields
+        * "localhost.domain". Since the latter host name comes from the
+        * local DNS, we just have to trust it (all bets are off if the local
+        * DNS is perverted). We always check the address list, though.
+        */
+       
+       if (strcasecmp(remotehost, hp->h_name)
+           && strcasecmp(remotehost, "localhost")) {
+               DEBUG(0,("host name/name mismatch: %s != %s\n",
+                        remotehost, hp->h_name));
+               return False;
+       }
+       
+       /* Look up the host address in the address list we just got. */
+       for (i = 0; hp->h_addr_list[i]; i++) {
+               if (memcmp(hp->h_addr_list[i], (char *) & addr, sizeof(addr)) == 0)
+                       return True;
+       }
+       
+       /*
+        * The host name does not map to the original host address. Perhaps
+        * someone has compromised a name server. More likely someone botched
+        * it, but that could be dangerous, too.
+        */
+       
+       DEBUG(0,("host name/address mismatch: %s != %s\n",
+                inet_ntoa(addr), hp->h_name));
+       return False;
+}
+
+/*******************************************************************
+ return the DNS name of the remote end of a socket
+ ******************************************************************/
+char *get_socket_name(TALLOC_CTX *mem_ctx, int fd, BOOL force_lookup)
+{
+       char *name_buf;
+       char *addr_buf;
+       struct hostent *hp;
+       struct in_addr addr;
+       char *p;
+
+       /* reverse lookups can be *very* expensive, and in many
+          situations won't work because many networks don't link dhcp
+          with dns. To avoid the delay we avoid the lookup if
+          possible */
+       if (!lp_hostname_lookups() && (force_lookup == False)) {
+               return get_socket_addr(mem_ctx, fd);
+       }
+       
+       p = get_socket_addr(mem_ctx, fd);
+
+       name_buf = talloc_strdup(mem_ctx, "UNKNOWN");
+       if (fd == -1) return name_buf;
+
+       addr_buf = talloc_strdup(mem_ctx, p);
+
+       addr = *interpret_addr2(mem_ctx, p);
+       
+       /* Look up the remote host name. */
+       if ((hp = gethostbyaddr((char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET)) == 0) {
+               DEBUG(1,("Gethostbyaddr failed for %s\n",p));
+               name_buf = talloc_strdup(mem_ctx, p);
+       } else {
+               name_buf = talloc_strdup(mem_ctx, (char *)hp->h_name);
+               if (!matchname(name_buf, addr)) {
+                       DEBUG(0,("Matchname failed on %s %s\n",name_buf,p));
+                       name_buf = talloc_strdup(mem_ctx, "UNKNOWN");
+               }
+       }
+
+       alpha_strcpy(name_buf, name_buf, "_-.", sizeof(name_buf));
+       if (strstr(name_buf,"..")) {
+               name_buf = talloc_strdup(mem_ctx, "UNKNOWN");
+       }
+
+       return name_buf;
+}
+
+/*******************************************************************
+ return the IP addr of the remote end of a socket as a string 
+ ******************************************************************/
+char *get_socket_addr(TALLOC_CTX *mem_ctx, int fd)
+{
+       struct sockaddr sa;
+       struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+       int     length = sizeof(sa);
+       char *addr_buf;
+
+       addr_buf = talloc_strdup(mem_ctx, "0.0.0.0");
+
+       if (fd == -1) {
+               return addr_buf;
+       }
+       
+       if (getpeername(fd, &sa, &length) < 0) {
+               DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+               return addr_buf;
+       }
+       
+       addr_buf = talloc_strdup(mem_ctx, (char *)inet_ntoa(sockin->sin_addr));
+       
+       return addr_buf;
+}
+
+
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+static int socketpair_tcp(int fd[2])
+{
+       int listener;
+       struct sockaddr_in sock;
+       struct sockaddr_in sock2;
+       socklen_t socklen = sizeof(sock);
+       int connect_done = 0;
+       
+       fd[0] = fd[1] = listener = -1;
+
+       memset(&sock, 0, sizeof(sock));
+       
+       if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+        memset(&sock2, 0, sizeof(sock2));
+#ifdef HAVE_SOCK_SIN_LEN
+        sock2.sin_len = sizeof(sock2);
+#endif
+        sock2.sin_family = PF_INET;
+
+        bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
+
+       if (listen(listener, 1) != 0) goto failed;
+
+       if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
+
+       if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+       set_blocking(fd[1], 0);
+
+       sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
+               if (errno != EINPROGRESS) goto failed;
+       } else {
+               connect_done = 1;
+       }
+
+       if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
+
+       close(listener);
+       if (connect_done == 0) {
+               if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
+                   && errno != EISCONN) goto failed;
+       }
+
+       set_blocking(fd[1], 1);
+
+       /* all OK! */
+       return 0;
+
+ failed:
+       if (fd[0] != -1) close(fd[0]);
+       if (fd[1] != -1) close(fd[1]);
+       if (listener != -1) close(listener);
+       return -1;
+}
+
+
+/*******************************************************************
+run a program on a local tcp socket, this is used to launch smbd
+when regression testing
+the return value is a socket which is attached to a subprocess
+running "prog". stdin and stdout are attached. stderr is left
+attached to the original stderr
+ ******************************************************************/
+int sock_exec(const char *prog)
+{
+       int fd[2];
+       if (socketpair_tcp(fd) != 0) {
+               DEBUG(0,("socketpair_tcp failed (%s)\n", strerror(errno)));
+               return -1;
+       }
+       if (fork() == 0) {
+               close(fd[0]);
+               close(0);
+               close(1);
+               dup(fd[1]);
+               dup(fd[1]);
+               exit(system(prog));
+       }
+       close(fd[1]);
+       return fd[0];
+}
+
+
+/*
+  determine if a packet is pending for receive on a socket
+*/
+BOOL socket_pending(int fd)
+{
+       fd_set fds;
+       int selrtn;
+       struct timeval timeout;
+
+       FD_ZERO(&fds);
+       FD_SET(fd,&fds);
+       
+       /* immediate timeout */
+       timeout.tv_sec = 0;
+       timeout.tv_usec = 0;
+
+       /* yes, this is supposed to be a normal select not a sys_select() */
+       selrtn = select(fd+1,&fds,NULL,NULL,&timeout);
+               
+       if (selrtn == 1) {
+               /* the fd is readable */
+               return True;
+       }
+
+       return False;
+}
diff --git a/source4/lib/util_str.c b/source4/lib/util_str.c
new file mode 100644 (file)
index 0000000..19857cf
--- /dev/null
@@ -0,0 +1,1619 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-2001
+   Copyright (C) Simo Sorce      2001-2002
+   
+   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"
+
+/**
+ * Get the next token from a string, return False if none found.
+ * Handles double-quotes.
+ * 
+ * Based on a routine by GJC@VILLAGE.COM. 
+ * Extensively modified by Andrew.Tridgell@anu.edu.au
+ **/
+BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize)
+{
+       const char *s;
+       BOOL quoted;
+       size_t len=1;
+
+       if (!ptr)
+               return(False);
+
+       s = *ptr;
+
+       /* default to simple separators */
+       if (!sep)
+               sep = " \t\n\r";
+
+       /* find the first non sep char */
+       while (*s && strchr_m(sep,*s))
+               s++;
+       
+       /* nothing left? */
+       if (! *s)
+               return(False);
+       
+       /* copy over the token */
+       for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) {
+               if (*s == '\"') {
+                       quoted = !quoted;
+               } else {
+                       len++;
+                       *buff++ = *s;
+               }
+       }
+       
+       *ptr = (*s) ? s+1 : s;  
+       *buff = 0;
+       
+       return(True);
+}
+
+/**
+This is like next_token but is not re-entrant and "remembers" the first 
+parameter so you can pass NULL. This is useful for user interface code
+but beware the fact that it is not re-entrant!
+**/
+
+static char *last_ptr=NULL;
+
+BOOL next_token_nr(const char **ptr, char *buff, const char *sep, size_t bufsize)
+{
+       BOOL ret;
+       if (!ptr)
+               ptr = (const char **)&last_ptr;
+
+       ret = next_token(ptr, buff, sep, bufsize);
+       last_ptr = *ptr;
+       return ret;     
+}
+
+static uint16 tmpbuf[sizeof(pstring)];
+
+void set_first_token(char *ptr)
+{
+       last_ptr = ptr;
+}
+
+/**
+ Convert list of tokens to array; dependent on above routine.
+ Uses last_ptr from above - bit of a hack.
+**/
+
+char **toktocliplist(int *ctok, const char *sep)
+{
+       char *s=last_ptr;
+       int ictok=0;
+       char **ret, **iret;
+
+       if (!sep)
+               sep = " \t\n\r";
+
+       while(*s && strchr_m(sep,*s))
+               s++;
+
+       /* nothing left? */
+       if (!*s)
+               return(NULL);
+
+       do {
+               ictok++;
+               while(*s && (!strchr_m(sep,*s)))
+                       s++;
+               while(*s && strchr_m(sep,*s))
+                       *s++=0;
+       } while(*s);
+       
+       *ctok=ictok;
+       s=last_ptr;
+       
+       if (!(ret=iret=malloc(ictok*sizeof(char *))))
+               return NULL;
+       
+       while(ictok--) {    
+               *iret++=s;
+               while(*s++)
+                       ;
+               while(!*s)
+                       s++;
+       }
+
+       return ret;
+}
+
+/**
+ Case insensitive string compararison.
+**/
+
+int StrCaseCmp(const char *s, const char *t)
+{
+       pstring buf1, buf2;
+       unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
+       unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
+       return strcmp(buf1,buf2);
+}
+
+/**
+ Case insensitive string compararison, length limited.
+**/
+
+int StrnCaseCmp(const char *s, const char *t, size_t n)
+{
+       pstring buf1, buf2;
+       unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
+       unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
+       return strncmp(buf1,buf2,n);
+}
+
+/**
+ * Compare 2 strings.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strequal(const char *s1, const char *s2)
+{
+       if (s1 == s2)
+               return(True);
+       if (!s1 || !s2)
+               return(False);
+  
+       return(StrCaseCmp(s1,s2)==0);
+}
+
+/**
+ * Compare 2 strings up to and including the nth char.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strnequal(const char *s1,const char *s2,size_t n)
+{
+  if (s1 == s2)
+         return(True);
+  if (!s1 || !s2 || !n)
+         return(False);
+  
+  return(StrnCaseCmp(s1,s2,n)==0);
+}
+
+/**
+ Compare 2 strings (case sensitive).
+**/
+
+BOOL strcsequal(const char *s1,const char *s2)
+{
+  if (s1 == s2)
+         return(True);
+  if (!s1 || !s2)
+         return(False);
+  
+  return(strcmp(s1,s2)==0);
+}
+
+/**
+Do a case-insensitive, whitespace-ignoring string compare.
+**/
+
+int strwicmp(const char *psz1, const char *psz2)
+{
+       /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
+       /* appropriate value. */
+       if (psz1 == psz2)
+               return (0);
+       else if (psz1 == NULL)
+               return (-1);
+       else if (psz2 == NULL)
+               return (1);
+
+       /* sync the strings on first non-whitespace */
+       while (1) {
+               while (isspace((int)*psz1))
+                       psz1++;
+               while (isspace((int)*psz2))
+                       psz2++;
+               if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0'
+                   || *psz2 == '\0')
+                       break;
+               psz1++;
+               psz2++;
+       }
+       return (*psz1 - *psz2);
+}
+
+/**
+ Convert a string to upper case, but don't modify it.
+**/
+
+char *strupper_talloc(TALLOC_CTX *mem_ctx, const char *s)
+{
+       char *str;
+
+       str = talloc_strdup(mem_ctx, s);
+       strupper(str);
+
+       return str;
+}
+
+
+/**
+ String replace.
+ NOTE: oldc and newc must be 7 bit characters
+**/
+
+void string_replace(char *s,char oldc,char newc)
+{
+       if (strchr(s, oldc)) {
+               push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+               string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc));
+               pull_ucs2(NULL, s, tmpbuf, strlen(s)+1, sizeof(tmpbuf), STR_TERMINATE);
+       }
+}
+
+/**
+ Skip past some strings in a buffer.
+**/
+
+char *skip_string(char *buf,size_t n)
+{
+       while (n--)
+               buf += strlen(buf) + 1;
+       return(buf);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_charnum(const char *s)
+{
+       uint16 tmpbuf2[sizeof(pstring)];
+       push_ucs2(NULL, tmpbuf2,s, sizeof(tmpbuf2), STR_TERMINATE);
+       return strlen_w(tmpbuf2);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_ascii_charnum(const char *s)
+{
+       pstring tmpbuf2;
+       push_ascii(tmpbuf2, s, sizeof(tmpbuf2), STR_TERMINATE);
+       return strlen(tmpbuf2);
+}
+
+/**
+ Trim the specified elements off the front and back of a string.
+**/
+
+BOOL trim_string(char *s,const char *front,const char *back)
+{
+       BOOL ret = False;
+       size_t front_len;
+       size_t back_len;
+       size_t len;
+
+       /* Ignore null or empty strings. */
+       if (!s || (s[0] == '\0'))
+               return False;
+
+       front_len       = front? strlen(front) : 0;
+       back_len        = back? strlen(back) : 0;
+
+       len = strlen(s);
+
+       if (front_len) {
+               while (len && strncmp(s, front, front_len)==0) {
+                       memcpy(s, s+front_len, (len-front_len)+1);
+                       len -= front_len;
+                       ret=True;
+               }
+       }
+       
+       if (back_len) {
+               while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) {
+                       s[len-back_len]='\0';
+                       len -= back_len;
+                       ret=True;
+               }
+       }
+       return ret;
+}
+
+/**
+ Does a string have any uppercase chars in it?
+**/
+
+BOOL strhasupper(const char *s)
+{
+       smb_ucs2_t *ptr;
+       push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+       for(ptr=tmpbuf;*ptr;ptr++)
+               if(isupper_w(*ptr))
+                       return True;
+       return(False);
+}
+
+/**
+ Does a string have any lowercase chars in it?
+**/
+
+BOOL strhaslower(const char *s)
+{
+       smb_ucs2_t *ptr;
+       push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+       for(ptr=tmpbuf;*ptr;ptr++)
+               if(islower_w(*ptr))
+                       return True;
+       return(False);
+}
+
+/**
+ Find the number of 'c' chars in a string
+**/
+
+size_t count_chars(const char *s,char c)
+{
+       smb_ucs2_t *ptr;
+       int count;
+       push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+       for(count=0,ptr=tmpbuf;*ptr;ptr++)
+               if(*ptr==UCS2_CHAR(c))
+                       count++;
+       return(count);
+}
+
+/**
+Return True if a string consists only of one particular character.
+**/
+
+BOOL str_is_all(const char *s,char c)
+{
+       smb_ucs2_t *ptr;
+
+       if(s == NULL)
+               return False;
+       if(!*s)
+               return False;
+  
+       push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+       for(ptr=tmpbuf;*ptr;ptr++)
+               if(*ptr!=UCS2_CHAR(c))
+                       return False;
+
+       return True;
+}
+
+/**
+ Safe string copy into a known length string. maxlength does not
+ include the terminating zero.
+**/
+
+char *safe_strcpy(char *dest,const char *src, size_t maxlength)
+{
+       size_t len;
+
+       if (!dest) {
+               DEBUG(0,("ERROR: NULL dest in safe_strcpy\n"));
+               return NULL;
+       }
+
+#ifdef DEVELOPER
+       /* We intentionally write out at the extremity of the destination
+        * string.  If the destination is too short (e.g. pstrcpy into mallocd
+        * or fstring) then this should cause an error under a memory
+        * checker. */
+       dest[maxlength] = '\0';
+       if (PTR_DIFF(&len, dest) > 0) {  /* check if destination is on the stack, ok if so */
+               log_suspicious_usage("safe_strcpy", src);
+       }
+#endif
+
+       if (!src) {
+               *dest = 0;
+               return dest;
+       }  
+
+       len = strlen(src);
+
+       if (len > maxlength) {
+               DEBUG(0,("ERROR: string overflow by %u (%u - %u) in safe_strcpy [%.50s]\n",
+                        (unsigned int)(len-maxlength), len, maxlength, src));
+               len = maxlength;
+       }
+      
+       memmove(dest, src, len);
+       dest[len] = 0;
+       return dest;
+}  
+
+/**
+ Safe string cat into a string. maxlength does not
+ include the terminating zero.
+**/
+
+char *safe_strcat(char *dest, const char *src, size_t maxlength)
+{
+       size_t src_len, dest_len;
+
+       if (!dest) {
+               DEBUG(0,("ERROR: NULL dest in safe_strcat\n"));
+               return NULL;
+       }
+
+       if (!src)
+               return dest;
+       
+#ifdef DEVELOPER
+       if (PTR_DIFF(&src_len, dest) > 0) {  /* check if destination is on the stack, ok if so */
+               log_suspicious_usage("safe_strcat", src);
+       }
+#endif
+       src_len = strlen(src);
+       dest_len = strlen(dest);
+
+       if (src_len + dest_len > maxlength) {
+               DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n",
+                        (int)(src_len + dest_len - maxlength), src));
+               if (maxlength > dest_len) {
+                       memcpy(&dest[dest_len], src, maxlength - dest_len);
+               }
+               dest[maxlength] = 0;
+               return NULL;
+       }
+       
+       memcpy(&dest[dest_len], src, src_len);
+       dest[dest_len + src_len] = 0;
+       return dest;
+}
+
+/**
+ Paranoid strcpy into a buffer of given length (includes terminating
+ zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars
+ and replaces with '_'. Deliberately does *NOT* check for multibyte
+ characters. Don't change it !
+**/
+
+char *alpha_strcpy(char *dest, const char *src, const char *other_safe_chars, size_t maxlength)
+{
+       size_t len, i;
+
+       if (maxlength == 0) {
+               /* can't fit any bytes at all! */
+               return NULL;
+       }
+
+       if (!dest) {
+               DEBUG(0,("ERROR: NULL dest in alpha_strcpy\n"));
+               return NULL;
+       }
+
+       if (!src) {
+               *dest = 0;
+               return dest;
+       }  
+
+       len = strlen(src);
+       if (len >= maxlength)
+               len = maxlength - 1;
+
+       if (!other_safe_chars)
+               other_safe_chars = "";
+
+       for(i = 0; i < len; i++) {
+               int val = (src[i] & 0xff);
+               if (isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val))
+                       dest[i] = src[i];
+               else
+                       dest[i] = '_';
+       }
+
+       dest[i] = '\0';
+
+       return dest;
+}
+
+/**
+ Like strncpy but always null terminates. Make sure there is room!
+ The variable n should always be one less than the available size.
+**/
+
+char *StrnCpy(char *dest,const char *src,size_t n)
+{
+       char *d = dest;
+       if (!dest)
+               return(NULL);
+       if (!src) {
+               *dest = 0;
+               return(dest);
+       }
+       while (n-- && (*d++ = *src++))
+               ;
+       *d = 0;
+       return(dest);
+}
+
+/**
+ Like strncpy but copies up to the character marker.  always null terminates.
+ returns a pointer to the character marker in the source string (src).
+**/
+
+char *strncpyn(char *dest, const char *src, size_t n, char c)
+{
+       char *p;
+       size_t str_len;
+
+       p = strchr_m(src, c);
+       if (p == NULL) {
+               DEBUG(5, ("strncpyn: separator character (%c) not found\n", c));
+               return NULL;
+       }
+
+       str_len = PTR_DIFF(p, src);
+       strncpy(dest, src, MIN(n, str_len));
+       dest[str_len] = '\0';
+
+       return p;
+}
+
+/**
+ Routine to get hex characters and turn them into a 16 byte array.
+ the array can be variable length, and any non-hex-numeric
+ characters are skipped.  "0xnn" or "0Xnn" is specially catered
+ for.
+
+ valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
+
+**/
+
+size_t strhex_to_str(char *p, size_t len, const char *strhex)
+{
+       size_t i;
+       size_t num_chars = 0;
+       unsigned char   lonybble, hinybble;
+       const char     *hexchars = "0123456789ABCDEF";
+       char           *p1 = NULL, *p2 = NULL;
+
+       for (i = 0; i < len && strhex[i] != 0; i++) {
+               if (strnequal(hexchars, "0x", 2)) {
+                       i++; /* skip two chars */
+                       continue;
+               }
+
+               if (!(p1 = strchr_m(hexchars, toupper(strhex[i]))))
+                       break;
+
+               i++; /* next hex digit */
+
+               if (!(p2 = strchr_m(hexchars, toupper(strhex[i]))))
+                       break;
+
+               /* get the two nybbles */
+               hinybble = PTR_DIFF(p1, hexchars);
+               lonybble = PTR_DIFF(p2, hexchars);
+
+               p[num_chars] = (hinybble << 4) | lonybble;
+               num_chars++;
+
+               p1 = NULL;
+               p2 = NULL;
+       }
+       return num_chars;
+}
+
+/**
+ Check if a string is part of a list.
+**/
+
+BOOL in_list(const char *s, const char *list, BOOL casesensitive)
+{
+       pstring tok;
+       const char *p=list;
+
+       if (!list)
+               return(False);
+
+       while (next_token(&p,tok,LIST_SEP,sizeof(tok))) {
+               if (casesensitive) {
+                       if (strcmp(tok,s) == 0)
+                               return(True);
+               } else {
+                       if (StrCaseCmp(tok,s) == 0)
+                               return(True);
+               }
+       }
+       return(False);
+}
+
+/**
+ Set a string value, allocing the space for the string
+**/
+static BOOL string_init(char **dest,const char *src)
+{
+       size_t l;
+       if (!src) src = "";
+
+       l = strlen(src);
+
+       (*dest) = strdup(src);
+       if ((*dest) == NULL) {
+               DEBUG(0,("Out of memory in string_init\n"));
+               return False;
+       }
+       return True;
+}
+
+/**
+ Free a string value.
+**/
+void string_free(char **s)
+{
+       if (s) SAFE_FREE(*s);
+}
+
+/**
+ Set a string value, deallocating any existing space, and allocing the space
+ for the string
+**/
+BOOL string_set(char **dest, const char *src)
+{
+       string_free(dest);
+       return string_init(dest,src);
+}
+
+/**
+ Substitute a string for a pattern in another string. Make sure there is 
+ enough room!
+
+ This routine looks for pattern in s and replaces it with 
+ insert. It may do multiple replacements.
+
+ Any of " ; ' $ or ` in the insert string are replaced with _
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void string_sub(char *s,const char *pattern, const char *insert, size_t len)
+{
+       char *p;
+       ssize_t ls,lp,li, i;
+
+       if (!insert || !pattern || !*pattern || !s)
+               return;
+
+       ls = (ssize_t)strlen(s);
+       lp = (ssize_t)strlen(pattern);
+       li = (ssize_t)strlen(insert);
+
+       if (len == 0)
+               len = ls + 1; /* len is number of *bytes* */
+
+       while (lp <= ls && (p = strstr(s,pattern))) {
+               if (ls + (li-lp) >= len) {
+                       DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n", 
+                                (int)(ls + (li-lp) - len),
+                                pattern, (int)len));
+                       break;
+               }
+               if (li != lp) {
+                       memmove(p+li,p+lp,strlen(p+lp)+1);
+               }
+               for (i=0;i<li;i++) {
+                       switch (insert[i]) {
+                       case '`':
+                       case '"':
+                       case '\'':
+                       case ';':
+                       case '$':
+                       case '%':
+                       case '\r':
+                       case '\n':
+                               p[i] = '_';
+                               break;
+                       default:
+                               p[i] = insert[i];
+                       }
+               }
+               s = p + li;
+               ls += (li-lp);
+       }
+}
+
+void fstring_sub(char *s,const char *pattern,const char *insert)
+{
+       string_sub(s, pattern, insert, sizeof(fstring));
+}
+
+void pstring_sub(char *s,const char *pattern,const char *insert)
+{
+       string_sub(s, pattern, insert, sizeof(pstring));
+}
+
+/**
+ Similar to string_sub, but it will accept only allocated strings
+ and may realloc them so pay attention at what you pass on no
+ pointers inside strings, no pstrings or const may be passed
+ as string.
+**/
+
+char *realloc_string_sub(char *string, const char *pattern, const char *insert)
+{
+       char *p, *in;
+       char *s;
+       ssize_t ls,lp,li,ld, i;
+
+       if (!insert || !pattern || !*pattern || !string || !*string)
+               return NULL;
+
+       s = string;
+
+       in = strdup(insert);
+       if (!in) {
+               DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+               return NULL;
+       }
+       ls = (ssize_t)strlen(s);
+       lp = (ssize_t)strlen(pattern);
+       li = (ssize_t)strlen(insert);
+       ld = li - lp;
+       for (i=0;i<li;i++) {
+               switch (in[i]) {
+                       case '`':
+                       case '"':
+                       case '\'':
+                       case ';':
+                       case '$':
+                       case '%':
+                       case '\r':
+                       case '\n':
+                               in[i] = '_';
+                       default:
+                               /* ok */
+                               break;
+               }
+       }
+       
+       while ((p = strstr(s,pattern))) {
+               if (ld > 0) {
+                       char *t = Realloc(string, ls + ld + 1);
+                       if (!t) {
+                               DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+                               SAFE_FREE(in);
+                               return NULL;
+                       }
+                       string = t;
+                       p = t + (p - s);
+               }
+               if (li != lp) {
+                       memmove(p+li,p+lp,strlen(p+lp)+1);
+               }
+               memcpy(p, in, li);
+               s = p + li;
+               ls += ld;
+       }
+       SAFE_FREE(in);
+       return string;
+}
+
+/**
+ Similar to string_sub() but allows for any character to be substituted. 
+ Use with caution!
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
+{
+       char *p;
+       ssize_t ls,lp,li;
+
+       if (!insert || !pattern || !s)
+               return;
+
+       ls = (ssize_t)strlen(s);
+       lp = (ssize_t)strlen(pattern);
+       li = (ssize_t)strlen(insert);
+
+       if (!*pattern)
+               return;
+       
+       if (len == 0)
+               len = ls + 1; /* len is number of *bytes* */
+       
+       while (lp <= ls && (p = strstr(s,pattern))) {
+               if (ls + (li-lp) >= len) {
+                       DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n", 
+                                (int)(ls + (li-lp) - len),
+                                pattern, (int)len));
+                       break;
+               }
+               if (li != lp) {
+                       memmove(p+li,p+lp,strlen(p+lp)+1);
+               }
+               memcpy(p, insert, li);
+               s = p + li;
+               ls += (li-lp);
+       }
+}
+
+/**
+ Similar to all_string_sub but for unicode strings.
+ Return a new allocated unicode string.
+ similar to string_sub() but allows for any character to be substituted.
+ Use with caution!
+**/
+
+smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern,
+                               const smb_ucs2_t *insert)
+{
+       smb_ucs2_t *r, *rp;
+       const smb_ucs2_t *sp;
+       size_t  lr, lp, li, lt;
+
+       if (!insert || !pattern || !*pattern || !s)
+               return NULL;
+
+       lt = (size_t)strlen_w(s);
+       lp = (size_t)strlen_w(pattern);
+       li = (size_t)strlen_w(insert);
+
+       if (li > lp) {
+               const smb_ucs2_t *st = s;
+               int ld = li - lp;
+               while ((sp = strstr_w(st, pattern))) {
+                       st = sp + lp;
+                       lt += ld;
+               }
+       }
+
+       r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t)));
+       if (!r) {
+               DEBUG(0, ("all_string_sub_w: out of memory!\n"));
+               return NULL;
+       }
+
+       while ((sp = strstr_w(s, pattern))) {
+               memcpy(rp, s, (sp - s));
+               rp += ((sp - s) / sizeof(smb_ucs2_t));
+               memcpy(rp, insert, (li * sizeof(smb_ucs2_t)));
+               s = sp + lp;
+               rp += li;
+       }
+       lr = ((rp - r) / sizeof(smb_ucs2_t));
+       if (lr < lt) {
+               memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t)));
+               rp += (lt - lr);
+       }
+       *rp = 0;
+
+       return r;
+}
+
+smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern,
+                                            const char *insert)
+{
+       wpstring p, i;
+
+       if (!insert || !pattern || !s)
+               return NULL;
+       push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE);
+       push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE);
+       return all_string_sub_w(s, p, i);
+}
+
+/**
+ Splits out the front and back at a separator.
+**/
+
+void split_at_last_component(char *path, char *front, char sep, char *back)
+{
+       char *p = strrchr_m(path, sep);
+
+       if (p != NULL)
+               *p = 0;
+
+       if (front != NULL)
+               pstrcpy(front, path);
+
+       if (p != NULL) {
+               if (back != NULL)
+                       pstrcpy(back, p+1);
+               *p = '\\';
+       } else {
+               if (back != NULL)
+                       back[0] = 0;
+       }
+}
+
+/**
+ Write an octal as a string.
+**/
+
+const char *octal_string(int i)
+{
+       static char ret[64];
+       if (i == -1)
+               return "-1";
+       slprintf(ret, sizeof(ret)-1, "0%o", i);
+       return ret;
+}
+
+
+/**
+ Truncate a string at a specified length.
+**/
+
+char *string_truncate(char *s, int length)
+{
+       if (s && strlen(s) > length)
+               s[length] = 0;
+       return s;
+}
+
+/**
+ Strchr and strrchr_m are very hard to do on general multi-byte strings. 
+ We convert via ucs2 for now.
+**/
+
+char *strchr_m(const char *s, char c)
+{
+       wpstring ws;
+       pstring s2;
+       smb_ucs2_t *p;
+
+       push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+       p = strchr_w(ws, UCS2_CHAR(c));
+       if (!p)
+               return NULL;
+       *p = 0;
+       pull_ucs2_pstring(s2, ws);
+       return (char *)(s+strlen(s2));
+}
+
+char *strrchr_m(const char *s, char c)
+{
+       wpstring ws;
+       pstring s2;
+       smb_ucs2_t *p;
+
+       push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+       p = strrchr_w(ws, UCS2_CHAR(c));
+       if (!p)
+               return NULL;
+       *p = 0;
+       pull_ucs2_pstring(s2, ws);
+       return (char *)(s+strlen(s2));
+}
+
+/**
+ Convert a string to lower case.
+**/
+
+void strlower_m(char *s)
+{
+       /* this is quite a common operation, so we want it to be
+          fast. We optimise for the ascii case, knowing that all our
+          supported multi-byte character sets are ascii-compatible
+          (ie. they match for the first 128 chars) */
+
+       while (*s && !(((unsigned char)s[0]) & 0x7F)) {
+               *s = tolower((unsigned char)*s);
+               s++;
+       }
+
+       if (!*s)
+               return;
+
+       /* I assume that lowercased string takes the same number of bytes
+        * as source string even in UTF-8 encoding. (VIV) */
+       unix_strlower(s,strlen(s)+1,s,strlen(s)+1);     
+}
+
+/**
+ Duplicate convert a string to lower case.
+**/
+
+char *strdup_lower(const char *s)
+{
+       char *t = strdup(s);
+       if (t == NULL) {
+               DEBUG(0, ("strdup_lower: Out of memory!\n"));
+               return NULL;
+       }
+       strlower_m(t);
+       return t;
+}
+
+/**
+ Convert a string to upper case.
+**/
+
+void strupper_m(char *s)
+{
+       /* this is quite a common operation, so we want it to be
+          fast. We optimise for the ascii case, knowing that all our
+          supported multi-byte character sets are ascii-compatible
+          (ie. they match for the first 128 chars) */
+
+       while (*s && !(((unsigned char)s[0]) & 0x7F)) {
+               *s = toupper((unsigned char)*s);
+               s++;
+       }
+
+       if (!*s)
+               return;
+
+       /* I assume that lowercased string takes the same number of bytes
+        * as source string even in multibyte encoding. (VIV) */
+       unix_strupper(s,strlen(s)+1,s,strlen(s)+1);     
+}
+
+/**
+ Convert a string to upper case.
+**/
+
+char *strdup_upper(const char *s)
+{
+       char *t = strdup(s);
+       if (t == NULL) {
+               DEBUG(0, ("strdup_upper: Out of memory!\n"));
+               return NULL;
+       }
+       strupper_m(t);
+       return t;
+}
+
+/**
+ Return a RFC2254 binary string representation of a buffer.
+ Used in LDAP filters.
+ Caller must free.
+**/
+
+char *binary_string(char *buf, int len)
+{
+       char *s;
+       int i, j;
+       const char *hex = "0123456789ABCDEF";
+       s = malloc(len * 3 + 1);
+       if (!s)
+               return NULL;
+       for (j=i=0;i<len;i++) {
+               s[j] = '\\';
+               s[j+1] = hex[((unsigned char)buf[i]) >> 4];
+               s[j+2] = hex[((unsigned char)buf[i]) & 0xF];
+               j += 3;
+       }
+       s[j] = 0;
+       return s;
+}
+
+/**
+ Just a typesafety wrapper for snprintf into a pstring.
+**/
+
+ int pstr_sprintf(pstring s, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = vsnprintf(s, PSTRING_LEN, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+/**
+ Just a typesafety wrapper for snprintf into a fstring.
+**/
+
+ int fstr_sprintf(fstring s, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = vsnprintf(s, FSTRING_LEN, fmt, ap);
+       va_end(ap);
+       return ret;
+}
+
+#ifndef HAVE_STRNDUP
+/**
+ Some platforms don't have strndup.
+**/
+
+ char *strndup(const char *s, size_t n)
+{
+       char *ret;
+       
+       n = strnlen(s, n);
+       ret = malloc(n+1);
+       if (!ret)
+               return NULL;
+       memcpy(ret, s, n);
+       ret[n] = 0;
+
+       return ret;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+/**
+ Some platforms don't have strnlen
+**/
+
+ size_t strnlen(const char *s, size_t n)
+{
+       int i;
+       for (i=0; s[i] && i<n; i++)
+               /* noop */ ;
+       return i;
+}
+#endif
+
+/**
+ List of Strings manipulation functions
+**/
+
+#define S_LIST_ABS 16 /* List Allocation Block Size */
+
+char **str_list_make(const char *string, const char *sep)
+{
+       char **list, **rlist;
+       const char *str;
+       char *s;
+       int num, lsize;
+       pstring tok;
+       
+       if (!string || !*string)
+               return NULL;
+       s = strdup(string);
+       if (!s) {
+               DEBUG(0,("str_list_make: Unable to allocate memory"));
+               return NULL;
+       }
+       if (!sep) sep = LIST_SEP;
+       
+       num = lsize = 0;
+       list = NULL;
+       
+       str = s;
+       while (next_token(&str, tok, sep, sizeof(tok))) {               
+               if (num == lsize) {
+                       lsize += S_LIST_ABS;
+                       rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+                       if (!rlist) {
+                               DEBUG(0,("str_list_make: Unable to allocate memory"));
+                               str_list_free(&list);
+                               SAFE_FREE(s);
+                               return NULL;
+                       } else
+                               list = rlist;
+                       memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1)));
+               }
+               
+               list[num] = strdup(tok);
+               if (!list[num]) {
+                       DEBUG(0,("str_list_make: Unable to allocate memory"));
+                       str_list_free(&list);
+                       SAFE_FREE(s);
+                       return NULL;
+               }
+       
+               num++;  
+       }
+       
+       SAFE_FREE(s);
+       return list;
+}
+
+BOOL str_list_copy(char ***dest, const char **src)
+{
+       char **list, **rlist;
+       int num, lsize;
+       
+       *dest = NULL;
+       if (!src)
+               return False;
+       
+       num = lsize = 0;
+       list = NULL;
+               
+       while (src[num]) {
+               if (num == lsize) {
+                       lsize += S_LIST_ABS;
+                       rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+                       if (!rlist) {
+                               DEBUG(0,("str_list_copy: Unable to re-allocate memory"));
+                               str_list_free(&list);
+                               return False;
+                       } else
+                               list = rlist;
+                       memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1)));
+               }
+               
+               list[num] = strdup(src[num]);
+               if (!list[num]) {
+                       DEBUG(0,("str_list_copy: Unable to allocate memory"));
+                       str_list_free(&list);
+                       return False;
+               }
+
+               num++;
+       }
+       
+       *dest = list;
+       return True;    
+}
+
+/**
+ * Return true if all the elements of the list match exactly.
+ **/
+BOOL str_list_compare(char **list1, char **list2)
+{
+       int num;
+       
+       if (!list1 || !list2)
+               return (list1 == list2); 
+       
+       for (num = 0; list1[num]; num++) {
+               if (!list2[num])
+                       return False;
+               if (!strcsequal(list1[num], list2[num]))
+                       return False;
+       }
+       if (list2[num])
+               return False; /* if list2 has more elements than list1 fail */
+       
+       return True;
+}
+
+void str_list_free(char ***list)
+{
+       char **tlist;
+       
+       if (!list || !*list)
+               return;
+       tlist = *list;
+       for(; *tlist; tlist++)
+               SAFE_FREE(*tlist);
+       SAFE_FREE(*list);
+}
+
+BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
+{
+       char *p, *s, *t;
+       ssize_t ls, lp, li, ld, i, d;
+
+       if (!list)
+               return False;
+       if (!pattern)
+               return False;
+       if (!insert)
+               return False;
+
+       lp = (ssize_t)strlen(pattern);
+       li = (ssize_t)strlen(insert);
+       ld = li -lp;
+                       
+       while (*list) {
+               s = *list;
+               ls = (ssize_t)strlen(s);
+
+               while ((p = strstr(s, pattern))) {
+                       t = *list;
+                       d = p -t;
+                       if (ld) {
+                               t = (char *) malloc(ls +ld +1);
+                               if (!t) {
+                                       DEBUG(0,("str_list_substitute: Unable to allocate memory"));
+                                       return False;
+                               }
+                               memcpy(t, *list, d);
+                               memcpy(t +d +li, p +lp, ls -d -lp +1);
+                               SAFE_FREE(*list);
+                               *list = t;
+                               ls += ld;
+                               s = t +d +li;
+                       }
+                       
+                       for (i = 0; i < li; i++) {
+                               switch (insert[i]) {
+                                       case '`':
+                                       case '"':
+                                       case '\'':
+                                       case ';':
+                                       case '$':
+                                       case '%':
+                                       case '\r':
+                                       case '\n':
+                                               t[d +i] = '_';
+                                               break;
+                                       default:
+                                               t[d +i] = insert[i];
+                               }
+                       }       
+               }
+               
+               list++;
+       }
+       
+       return True;
+}
+
+
+#define IPSTR_LIST_SEP ","
+
+/**
+ * Add ip string representation to ipstr list. Used also
+ * as part of @function ipstr_list_make
+ *
+ * @param ipstr_list pointer to string containing ip list;
+ *        MUST BE already allocated and IS reallocated if necessary
+ * @param ipstr_size pointer to current size of ipstr_list (might be changed
+ *        as a result of reallocation)
+ * @param ip IP address which is to be added to list
+ * @return pointer to string appended with new ip and possibly
+ *         reallocated to new length
+ **/
+
+char* ipstr_list_add(char** ipstr_list, const struct in_addr *ip)
+{
+       char* new_ipstr = NULL;
+       
+       /* arguments checking */
+       if (!ipstr_list || !ip) return NULL;
+
+       /* attempt to convert ip to a string and append colon separator to it */
+       if (*ipstr_list) {
+               asprintf(&new_ipstr, "%s%s%s", *ipstr_list, IPSTR_LIST_SEP,inet_ntoa(*ip));
+               SAFE_FREE(*ipstr_list);
+       } else {
+               asprintf(&new_ipstr, "%s", inet_ntoa(*ip));
+       }
+       *ipstr_list = new_ipstr;
+       return *ipstr_list;
+}
+
+
+/**
+ * Allocate and initialise an ipstr list using ip adresses
+ * passed as arguments.
+ *
+ * @param ipstr_list pointer to string meant to be allocated and set
+ * @param ip_list array of ip addresses to place in the list
+ * @param ip_count number of addresses stored in ip_list
+ * @return pointer to allocated ip string
+ **/
+char* ipstr_list_make(char** ipstr_list, const struct in_addr* ip_list, int ip_count)
+{
+       int i;
+       
+       /* arguments checking */
+       if (!ip_list && !ipstr_list) return 0;
+
+       *ipstr_list = NULL;
+       
+       /* process ip addresses given as arguments */
+       for (i = 0; i < ip_count; i++)
+               *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]);
+       
+       return (*ipstr_list);
+}
+
+
+/**
+ * Parse given ip string list into array of ip addresses
+ * (as in_addr structures)
+ *
+ * @param ipstr ip string list to be parsed 
+ * @param ip_list pointer to array of ip addresses which is
+ *        allocated by this function and must be freed by caller
+ * @return number of succesfully parsed addresses
+ **/
+int ipstr_list_parse(const char* ipstr_list, struct in_addr** ip_list)
+{
+       fstring token_str;
+       int count;
+
+       if (!ipstr_list || !ip_list) return 0;
+       
+       for (*ip_list = NULL, count = 0;
+            next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN);
+            count++) {
+            
+               struct in_addr addr;
+
+               /* convert single token to ip address */
+               if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE )
+                       break;
+               
+               /* prepare place for another in_addr structure */
+               *ip_list = Realloc(*ip_list, (count + 1) * sizeof(struct in_addr));
+               if (!*ip_list) return -1;
+               
+               (*ip_list)[count] = addr;
+       }
+       
+       return count;
+}
+
+
+/**
+ * Safely free ip string list
+ *
+ * @param ipstr_list ip string list to be freed
+ **/
+
+void ipstr_list_free(char* ipstr_list)
+{
+       SAFE_FREE(ipstr_list);
+}
+
+
+/**
+ Unescape a URL encoded string, in place.
+**/
+
+void rfc1738_unescape(char *buf)
+{
+       char *p=buf;
+
+       while ((p=strchr_m(p,'+')))
+               *p = ' ';
+
+       p = buf;
+
+       while (p && *p && (p=strchr_m(p,'%'))) {
+               int c1 = p[1];
+               int c2 = p[2];
+
+               if (c1 >= '0' && c1 <= '9')
+                       c1 = c1 - '0';
+               else if (c1 >= 'A' && c1 <= 'F')
+                       c1 = 10 + c1 - 'A';
+               else if (c1 >= 'a' && c1 <= 'f')
+                       c1 = 10 + c1 - 'a';
+               else {p++; continue;}
+
+               if (c2 >= '0' && c2 <= '9')
+                       c2 = c2 - '0';
+               else if (c2 >= 'A' && c2 <= 'F')
+                       c2 = 10 + c2 - 'A';
+               else if (c2 >= 'a' && c2 <= 'f')
+                       c2 = 10 + c2 - 'a';
+               else {p++; continue;}
+                       
+               *p = (c1<<4) | c2;
+
+               memmove(p+1, p+3, strlen(p+3)+1);
+               p++;
+       }
+}
+
+static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * Decode a base64 string into a DATA_BLOB - simple and slow algorithm
+ **/
+DATA_BLOB base64_decode_data_blob(const char *s)
+{
+       int bit_offset, byte_offset, idx, i, n;
+       DATA_BLOB decoded = data_blob(s, strlen(s)+1);
+       unsigned char *d = decoded.data;
+       char *p;
+
+       n=i=0;
+
+       while (*s && (p=strchr_m(b64,*s))) {
+               idx = (int)(p - b64);
+               byte_offset = (i*6)/8;
+               bit_offset = (i*6)%8;
+               d[byte_offset] &= ~((1<<(8-bit_offset))-1);
+               if (bit_offset < 3) {
+                       d[byte_offset] |= (idx << (2-bit_offset));
+                       n = byte_offset+1;
+               } else {
+                       d[byte_offset] |= (idx >> (bit_offset-2));
+                       d[byte_offset+1] = 0;
+                       d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
+                       n = byte_offset+2;
+               }
+               s++; i++;
+       }
+
+       /* fix up length */
+       decoded.length = n;
+       return decoded;
+}
+
+/**
+ * Decode a base64 string in-place - wrapper for the above
+ **/
+void base64_decode_inplace(char *s)
+{
+       DATA_BLOB decoded = base64_decode_data_blob(s);
+       memcpy(s, decoded.data, decoded.length);
+       data_blob_free(&decoded);
+
+       /* null terminate */
+       s[decoded.length] = '\0';
+}
+
+/**
+ * Encode a base64 string into a malloc()ed string caller to free.
+ *
+ *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
+ **/
+char * base64_encode_data_blob(DATA_BLOB data)
+{
+       int bits = 0;
+       int char_count = 0;
+       size_t out_cnt = 0;
+       size_t len = data.length;
+       size_t output_len = data.length * 2;
+       char *result = malloc(output_len); /* get us plenty of space */
+
+       while (len-- && out_cnt < (data.length * 2) - 5) {
+               int c = (unsigned char) *(data.data++);
+               bits += c;
+               char_count++;
+               if (char_count == 3) {
+                       result[out_cnt++] = b64[bits >> 18];
+                       result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+                       result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+           result[out_cnt++] = b64[bits & 0x3f];
+           bits = 0;
+           char_count = 0;
+       } else {
+           bits <<= 8;
+       }
+    }
+    if (char_count != 0) {
+       bits <<= 16 - (8 * char_count);
+       result[out_cnt++] = b64[bits >> 18];
+       result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+       if (char_count == 1) {
+           result[out_cnt++] = '=';
+           result[out_cnt++] = '=';
+       } else {
+           result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+           result[out_cnt++] = '=';
+       }
+    }
+    result[out_cnt] = '\0';    /* terminate */
+    return result;
+}
+
+#ifdef VALGRIND
+size_t valgrind_strlen(const char *s)
+{
+       size_t count;
+       for(count = 0; *s++; count++)
+               ;
+       return count;
+}
+#endif
diff --git a/source4/lib/util_unistr.c b/source4/lib/util_unistr.c
new file mode 100644 (file)
index 0000000..e5d2b8c
--- /dev/null
@@ -0,0 +1,838 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-2001
+   Copyright (C) Simo Sorce 2001
+   
+   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"
+
+#ifndef MAXUNI
+#define MAXUNI 1024
+#endif
+
+/* these 3 tables define the unicode case handling.  They are loaded
+   at startup either via mmap() or read() from the lib directory */
+static smb_ucs2_t *upcase_table;
+static smb_ucs2_t *lowcase_table;
+static uint8 *valid_table;
+
+
+/*******************************************************************
+load the case handling tables
+********************************************************************/
+void load_case_tables(void)
+{
+       static int initialised;
+       int i;
+       TALLOC_CTX *mem_ctx;
+
+       if (initialised) return;
+       initialised = 1;
+
+       mem_ctx = talloc_init("load_case_tables");
+       if (!mem_ctx) {
+               smb_panic("No memory for case_tables");
+       }
+       upcase_table = map_file(lib_path(mem_ctx, "upcase.dat"), 0x20000);
+       lowcase_table = map_file(lib_path(mem_ctx, "lowcase.dat"), 0x20000);
+       talloc_destroy(mem_ctx);
+       
+       /* we would like Samba to limp along even if these tables are
+          not available */
+       if (!upcase_table) {
+               DEBUG(1,("creating lame upcase table\n"));
+               upcase_table = malloc(0x20000);
+               if (!upcase_table) {
+                       smb_panic("No memory for upcase tables");
+               }
+               for (i=0;i<0x10000;i++) {
+                       smb_ucs2_t v;
+                       SSVAL(&v, 0, i);
+                       upcase_table[v] = i;
+               }
+               for (i=0;i<256;i++) {
+                       smb_ucs2_t v;
+                       SSVAL(&v, 0, UCS2_CHAR(i));
+                       upcase_table[v] = UCS2_CHAR(islower(i)?toupper(i):i);
+               }
+       }
+
+       if (!lowcase_table) {
+               DEBUG(1,("creating lame lowcase table\n"));
+               lowcase_table = malloc(0x20000);
+               if (!lowcase_table) {
+                       smb_panic("No memory for lowcase tables");
+               }
+               for (i=0;i<0x10000;i++) {
+                       smb_ucs2_t v;
+                       SSVAL(&v, 0, i);
+                       lowcase_table[v] = i;
+               }
+               for (i=0;i<256;i++) {
+                       smb_ucs2_t v;
+                       SSVAL(&v, 0, UCS2_CHAR(i));
+                       lowcase_table[v] = UCS2_CHAR(isupper(i)?tolower(i):i);
+               }
+       }
+}
+
+/*
+  see if a ucs2 character can be mapped correctly to a dos character
+  and mapped back to the same character in ucs2
+*/
+static int check_dos_char(smb_ucs2_t c)
+{
+       char buf[10];
+       smb_ucs2_t c2 = 0;
+       int len1, len2;
+       len1 = convert_string(CH_UCS2, CH_DOS, &c, 2, buf, sizeof(buf));
+       if (len1 == 0) return 0;
+       len2 = convert_string(CH_DOS, CH_UCS2, buf, len1, &c2, 2);
+       if (len2 != 2) return 0;
+       return (c == c2);
+}
+
+/**
+ * Load the valid character map table from <tt>valid.dat</tt> or
+ * create from the configured codepage.
+ *
+ * This function is called whenever the configuration is reloaded.
+ * However, the valid character table is not changed if it's loaded
+ * from a file, because we can't unmap files.
+ **/
+void init_valid_table(void)
+{
+       static int mapped_file;
+       int i;
+       const char *allowed = ".!#$%&'()_-@^`~";
+       uint8 *valid_file;
+       TALLOC_CTX *mem_ctx;
+
+       if (mapped_file) {
+               /* Can't unmap files, so stick with what we have */
+               return;
+       }
+
+       mem_ctx = talloc_init("init_valid_table");
+       if (!mem_ctx) {
+               smb_panic("No memory for valid_table");
+       }
+       valid_file = map_file(lib_path(mem_ctx, "valid.dat"), 0x10000);
+       talloc_destroy(mem_ctx);
+       if (valid_file) {
+               valid_table = valid_file;
+               mapped_file = 1;
+               return;
+       }
+
+       /* Otherwise, we're using a dynamically created valid_table.
+        * It might need to be regenerated if the code page changed.
+        * We know that we're not using a mapped file, so we can
+        * free() the old one. */
+       if (valid_table) free(valid_table);
+
+       DEBUG(2,("creating default valid table\n"));
+       valid_table = malloc(0x10000);
+       if (!valid_table) {
+               smb_panic("No memory for valid_table");
+       }
+       for (i=0;i<128;i++)
+               valid_table[i] = isalnum(i) || strchr(allowed,i);
+       
+       for (;i<0x10000;i++) {
+               smb_ucs2_t c;
+               SSVAL(&c, 0, i);
+               valid_table[i] = check_dos_char(c);
+       }
+}
+
+
+
+/*******************************************************************
+ Write a string in (little-endian) unicode format. src is in
+ the current DOS codepage. len is the length in bytes of the
+ string pointed to by dst.
+
+ if null_terminate is True then null terminate the packet (adds 2 bytes)
+
+ the return value is the length in bytes consumed by the string, including the
+ null termination if applied
+********************************************************************/
+
+size_t dos_PutUniCode(char *dst,const char *src, ssize_t len, BOOL null_terminate)
+{
+       return push_ucs2(NULL, dst, src, len, 
+                        STR_UNICODE|STR_NOALIGN | (null_terminate?STR_TERMINATE:0));
+}
+
+
+/*******************************************************************
+ Skip past a unicode string, but not more than len. Always move
+ past a terminating zero if found.
+********************************************************************/
+
+char *skip_unibuf(char *src, size_t len)
+{
+    char *srcend = src + len;
+
+    while (src < srcend && SVAL(src,0))
+        src += 2;
+
+    if(!SVAL(src,0))
+        src += 2;
+
+    return src;
+}
+
+/* Copy a string from little-endian or big-endian unicode source (depending
+ * on flags) to internal samba format destination
+ */ 
+int rpcstr_pull(char* dest, void *src, int dest_len, int src_len, int flags)
+{
+       if (!src) return 0;
+       if(dest_len==-1) dest_len=MAXUNI-3;
+       return pull_ucs2(NULL, dest, src, dest_len, src_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/* Copy a string from a unistr2 source to internal samba format
+   destination.  Use this instead of direct calls to rpcstr_pull() to avoid
+   having to determine whether the source string is null terminated. */
+
+int rpcstr_pull_unistr2_fstring(char *dest, UNISTR2 *src)
+{
+        return pull_ucs2(NULL, dest, src->buffer, sizeof(fstring),
+                         src->uni_str_len * 2, 0);
+}
+
+/* Converts a string from internal samba format to unicode
+ */ 
+int rpcstr_push(void* dest, const char *src, int dest_len, int flags)
+{
+       return push_ucs2(NULL, dest, src, dest_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/*******************************************************************
+ Convert a (little-endian) UNISTR2 structure to an ASCII string
+********************************************************************/
+void unistr2_to_ascii(char *dest, const UNISTR2 *str, size_t maxlen)
+{
+       if (str == NULL) {
+               *dest='\0';
+               return;
+       }
+       pull_ucs2(NULL, dest, str->buffer, maxlen, str->uni_str_len*2, STR_NOALIGN);
+}
+
+/*******************************************************************
+give a static string for displaying a UNISTR2
+********************************************************************/
+const char *unistr2_static(TALLOC_CTX *mem_ctx, const UNISTR2 *str)
+{
+       pstring ret;
+       unistr2_to_ascii(ret, str, sizeof(ret));
+       return talloc_strdup(mem_ctx, ret);
+}
+
+
+/*******************************************************************
+ duplicate a UNISTR2 string into a null terminated char*
+ using a talloc context
+********************************************************************/
+char *unistr2_tdup(TALLOC_CTX *ctx, const UNISTR2 *str)
+{
+       char *s;
+       int maxlen = (str->uni_str_len+1)*4;
+       if (!str->buffer) return NULL;
+       s = (char *)talloc(ctx, maxlen); /* convervative */
+       if (!s) return NULL;
+       pull_ucs2(NULL, s, str->buffer, maxlen, str->uni_str_len*2, 
+                 STR_NOALIGN);
+       return s;
+}
+
+
+/*******************************************************************
+Return a number stored in a buffer
+********************************************************************/
+
+uint32 buffer2_to_uint32(BUFFER2 *str)
+{
+       if (str->buf_len == 4)
+               return IVAL(str->buffer, 0);
+       else
+               return 0;
+}
+
+/*******************************************************************
+ Convert a wchar to upper case.
+********************************************************************/
+
+smb_ucs2_t toupper_w(smb_ucs2_t val)
+{
+       return upcase_table[SVAL(&val,0)];
+}
+
+/*******************************************************************
+ Convert a wchar to lower case.
+********************************************************************/
+
+smb_ucs2_t tolower_w( smb_ucs2_t val )
+{
+       return lowcase_table[SVAL(&val,0)];
+
+}
+
+/*******************************************************************
+determine if a character is lowercase
+********************************************************************/
+BOOL islower_w(smb_ucs2_t c)
+{
+       return upcase_table[SVAL(&c,0)] != c;
+}
+
+/*******************************************************************
+determine if a character is uppercase
+********************************************************************/
+BOOL isupper_w(smb_ucs2_t c)
+{
+       return lowcase_table[SVAL(&c,0)] != c;
+}
+
+
+/*******************************************************************
+determine if a character is valid in a 8.3 name
+********************************************************************/
+BOOL isvalid83_w(smb_ucs2_t c)
+{
+       return valid_table[SVAL(&c,0)] != 0;
+}
+
+/*******************************************************************
+ Count the number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strlen_w(const smb_ucs2_t *src)
+{
+       size_t len;
+
+       for (len = 0; SVAL(src,0); len++, src++) ;
+
+       return len;
+}
+
+/*******************************************************************
+ Count up to max number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strnlen_w(const smb_ucs2_t *src, size_t max)
+{
+       size_t len;
+
+       for (len = 0; (len < max) && SVAL(src, 0); len++, src++) ;
+
+       return len;
+}
+
+/*******************************************************************
+wide strchr()
+********************************************************************/
+const smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+       while (*s != 0) {
+               if (c == *s) return s;
+               s++;
+       }
+       if (c == *s) return s;
+
+       return NULL;
+}
+
+const smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c)
+{
+       return strchr_w(s, UCS2_CHAR(c));
+}
+
+const smb_ucs2_t *strrchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+       const smb_ucs2_t *p = s;
+       int len = strlen_w(s);
+       if (len == 0) return NULL;
+       p += (len - 1);
+       do {
+               if (c == *p) return p;
+       } while (p-- != s);
+       return NULL;
+}
+
+/*******************************************************************
+wide strstr()
+********************************************************************/
+const smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins)
+{
+       const smb_ucs2_t *r;
+       size_t slen, inslen;
+
+       if (!s || !*s || !ins || !*ins) return NULL;
+       slen = strlen_w(s);
+       inslen = strlen_w(ins);
+       r = s;
+       while ((r = strchr_w(r, *ins))) {
+               if (strncmp_w(r, ins, inslen) == 0) return r;
+               r++;
+       }
+       return NULL;
+}
+
+/*******************************************************************
+ Convert a string to lower case.
+ return True if any char is converted
+********************************************************************/
+BOOL strlower_w(smb_ucs2_t *s)
+{
+       BOOL ret = False;
+       while (*s) {
+               smb_ucs2_t v = tolower_w(*s);
+               if (v != *s) {
+                       *s = v;
+                       ret = True;
+               }
+               s++;
+       }
+       return ret;
+}
+
+/*******************************************************************
+ Convert a string to upper case.
+ return True if any char is converted
+********************************************************************/
+BOOL strupper_w(smb_ucs2_t *s)
+{
+       BOOL ret = False;
+       while (*s) {
+               smb_ucs2_t v = toupper_w(*s);
+               if (v != *s) {
+                       *s = v;
+                       ret = True;
+               }
+               s++;
+       }
+       return ret;
+}
+
+int strcmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+       while (*b && *a == *b) { a++; b++; }
+       return (*a - *b);
+       /* warning: if *a != *b and both are not 0 we retrun a random
+               greater or lesser than 0 number not realted to which
+               string is longer */
+}
+
+int strncmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+       size_t n = 0;
+       while ((n < len) && *b && *a == *b) { a++; b++; n++;}
+       return (len - n)?(*a - *b):0;   
+}
+
+/*******************************************************************
+case insensitive string comparison
+********************************************************************/
+int strcasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+       while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
+       return (tolower_w(*a) - tolower_w(*b));
+}
+
+/*******************************************************************
+case insensitive string comparison, lenght limited
+********************************************************************/
+int strncasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+       size_t n = 0;
+       while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
+       return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
+}
+
+/*******************************************************************
+  compare 2 strings 
+********************************************************************/
+BOOL strequal_w(const smb_ucs2_t *s1, const smb_ucs2_t *s2)
+{
+       if (s1 == s2) return(True);
+       if (!s1 || !s2) return(False);
+  
+       return(strcasecmp_w(s1,s2)==0);
+}
+
+/*******************************************************************
+  compare 2 strings up to and including the nth char.
+  ******************************************************************/
+BOOL strnequal_w(const smb_ucs2_t *s1,const smb_ucs2_t *s2,size_t n)
+{
+  if (s1 == s2) return(True);
+  if (!s1 || !s2 || !n) return(False);
+  
+  return(strncasecmp_w(s1,s2,n)==0);
+}
+
+/*******************************************************************
+duplicate string
+********************************************************************/
+smb_ucs2_t *strdup_w(const smb_ucs2_t *src)
+{
+       return strndup_w(src, 0);
+}
+
+/* if len == 0 then duplicate the whole string */
+smb_ucs2_t *strndup_w(const smb_ucs2_t *src, size_t len)
+{
+       smb_ucs2_t *dest;
+       
+       if (!len) len = strlen_w(src);
+       dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t));
+       if (!dest) {
+               DEBUG(0,("strdup_w: out of memory!\n"));
+               return NULL;
+       }
+
+       memcpy(dest, src, len * sizeof(smb_ucs2_t));
+       dest[len] = 0;
+       
+       return dest;
+}
+
+/*******************************************************************
+copy a string with max len
+********************************************************************/
+
+smb_ucs2_t *strncpy_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{
+       size_t len;
+       
+       if (!dest || !src) return NULL;
+       
+       for (len = 0; (src[len] != 0) && (len < max); len++)
+               dest[len] = src[len];
+       while (len < max)
+               dest[len++] = 0;
+       
+       return dest;
+}
+
+
+/*******************************************************************
+append a string of len bytes and add a terminator
+********************************************************************/
+
+smb_ucs2_t *strncat_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{      
+       size_t start;
+       size_t len;     
+       
+       if (!dest || !src) return NULL;
+       
+       start = strlen_w(dest);
+       len = strnlen_w(src, max);
+
+       memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));                      
+       dest[start+len] = 0;
+       
+       return dest;
+}
+
+smb_ucs2_t *strcat_w(smb_ucs2_t *dest, const smb_ucs2_t *src)
+{      
+       size_t start;
+       size_t len;     
+       
+       if (!dest || !src) return NULL;
+       
+       start = strlen_w(dest);
+       len = strlen_w(src);
+
+       memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));                      
+       dest[start+len] = 0;
+       
+       return dest;
+}
+
+
+/*******************************************************************
+replace any occurence of oldc with newc in unicode string
+********************************************************************/
+
+void string_replace_w(smb_ucs2_t *s, smb_ucs2_t oldc, smb_ucs2_t newc)
+{
+       for(;*s;s++) {
+               if(*s==oldc) *s=newc;
+       }
+}
+
+/*******************************************************************
+trim unicode string
+********************************************************************/
+
+BOOL trim_string_w(smb_ucs2_t *s, const smb_ucs2_t *front,
+                                 const smb_ucs2_t *back)
+{
+       BOOL ret = False;
+       size_t len, front_len, back_len;
+
+       if (!s || !*s) return False;
+
+       len = strlen_w(s);
+
+       if (front && *front) {
+               front_len = strlen_w(front);
+               while (len && strncmp_w(s, front, front_len) == 0) {
+                       memmove(s, (s + front_len), (len - front_len + 1) * sizeof(smb_ucs2_t));
+                       len -= front_len;
+                       ret = True;
+               }
+       }
+       
+       if (back && *back) {
+               back_len = strlen_w(back);
+               while (len && strncmp_w((s + (len - back_len)), back, back_len) == 0) {
+                       s[len - back_len] = 0;
+                       len -= back_len;
+                       ret = True;
+               }
+       }
+
+       return ret;
+}
+
+/*
+  The *_wa() functions take a combination of 7 bit ascii
+  and wide characters They are used so that you can use string
+  functions combining C string constants with ucs2 strings
+
+  The char* arguments must NOT be multibyte - to be completely sure
+  of this only pass string constants */
+
+
+void pstrcpy_wa(smb_ucs2_t *dest, const char *src)
+{
+       int i;
+       for (i=0;i<PSTRING_LEN;i++) {
+               dest[i] = UCS2_CHAR(src[i]);
+               if (src[i] == 0) return;
+       }
+}
+
+int strcmp_wa(const smb_ucs2_t *a, const char *b)
+{
+       while (*b && *a == UCS2_CHAR(*b)) { a++; b++; }
+       return (*a - UCS2_CHAR(*b));
+}
+
+int strncmp_wa(const smb_ucs2_t *a, const char *b, size_t len)
+{
+       size_t n = 0;
+       while ((n < len) && *b && *a == UCS2_CHAR(*b)) { a++; b++; n++;}
+       return (len - n)?(*a - UCS2_CHAR(*b)):0;
+}
+
+const smb_ucs2_t *strpbrk_wa(const smb_ucs2_t *s, const char *p)
+{
+       while (*s != 0) {
+               int i;
+               for (i=0; p[i] && *s != UCS2_CHAR(p[i]); i++) 
+                       ;
+               if (p[i]) return s;
+               s++;
+       }
+       return NULL;
+}
+
+const smb_ucs2_t *strstr_wa(const smb_ucs2_t *s, const char *ins)
+{
+       const smb_ucs2_t *r;
+       size_t slen, inslen;
+
+       if (!s || !*s || !ins || !*ins) return NULL;
+       slen = strlen_w(s);
+       inslen = strlen(ins);
+       r = s;
+       while ((r = strchr_w(r, UCS2_CHAR(*ins)))) {
+               if (strncmp_wa(r, ins, inslen) == 0) return r;
+               r++;
+       }
+       return NULL;
+}
+
+/*******************************************************************
+copy a string with max len
+********************************************************************/
+
+smb_ucs2_t *strncpy_wa(smb_ucs2_t *dest, const char *src, const size_t max)
+{
+       smb_ucs2_t *ucs2_src;
+
+       if (!dest || !src) return NULL;
+       if (!(ucs2_src = acnv_uxu2(src)))
+               return NULL;
+       
+       strncpy_w(dest, ucs2_src, max);
+       SAFE_FREE(ucs2_src);
+       return dest;
+}
+
+/*******************************************************************
+convert and duplicate an ascii string
+********************************************************************/
+smb_ucs2_t *strdup_wa(const char *src)
+{
+       return strndup_wa(src, 0);
+}
+
+/* if len == 0 then duplicate the whole string */
+smb_ucs2_t *strndup_wa(const char *src, size_t len)
+{
+       smb_ucs2_t *dest, *s;
+
+       s = acnv_dosu2(src);    
+       if (!len) len = strlen_w(s);
+       dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t));
+       if (!dest) {
+               DEBUG(0,("strdup_w: out of memory!\n"));
+               SAFE_FREE(s);
+               return NULL;
+       }
+
+       memcpy(dest, src, len * sizeof(smb_ucs2_t));
+       dest[len] = 0;
+
+       SAFE_FREE(s);
+       return dest;
+}
+
+/*******************************************************************
+append a string of len bytes and add a terminator
+********************************************************************/
+
+smb_ucs2_t *strncat_wa(smb_ucs2_t *dest, const char *src, const size_t max)
+{
+       smb_ucs2_t *ucs2_src;
+
+       if (!dest || !src) return NULL;
+       if (!(ucs2_src = acnv_uxu2(src)))
+               return NULL;
+       
+       strncat_w(dest, ucs2_src, max);
+       SAFE_FREE(ucs2_src);
+       return dest;
+}
+
+smb_ucs2_t *strcat_wa(smb_ucs2_t *dest, const char *src)
+{      
+       smb_ucs2_t *ucs2_src;
+       
+       if (!dest || !src) return NULL;
+       if (!(ucs2_src = acnv_uxu2(src)))
+               return NULL;
+       
+       strcat_w(dest, ucs2_src);
+       SAFE_FREE(ucs2_src);
+       return dest;
+}
+
+BOOL trim_string_wa(smb_ucs2_t *s, const char *front,
+                                 const char *back)
+{
+       wpstring f, b;
+
+       if (front) push_ucs2(NULL, f, front, sizeof(wpstring) - 1, STR_TERMINATE);
+       else *f = 0;
+       if (back) push_ucs2(NULL, b, back, sizeof(wpstring) - 1, STR_TERMINATE);
+       else *b = 0;
+       return trim_string_w(s, f, b);
+}
+
+/*******************************************************************
+ returns the length in number of wide characters
+ ******************************************************************/
+int unistrlen(uint16 *s)
+{
+       int len;
+
+       if (!s)
+               return -1;
+
+       for (len=0; *s; s++,len++);
+
+       return len;
+}
+
+/*******************************************************************
+ Strcpy for unicode strings.  returns length (in num of wide chars)
+********************************************************************/
+
+int unistrcpy(uint16 *dst, uint16 *src)
+{
+       int num_wchars = 0;
+
+       while (*src) {
+               *dst++ = *src++;
+               num_wchars++;
+       }
+       *dst = 0;
+
+       return num_wchars;
+}
+
+/**
+ * Samba ucs2 type to UNISTR2 conversion
+ *
+ * @param ctx Talloc context to create the dst strcture (if null) and the 
+ *            contents of the unicode string.
+ * @param dst UNISTR2 destination. If equals null, then it's allocated.
+ * @param src smb_ucs2_t source.
+ * @param max_len maximum number of unicode characters to copy. If equals
+ *        null, then null-termination of src is taken
+ *
+ * @return copied UNISTR2 destination
+ **/
+UNISTR2* ucs2_to_unistr2(TALLOC_CTX *ctx, UNISTR2* dst, smb_ucs2_t* src)
+{
+       size_t len;
+
+       if (!src) return NULL;
+       len = strlen_w(src);
+       
+       /* allocate UNISTR2 destination if not given */
+       if (!dst) {
+               dst = (UNISTR2*) talloc(ctx, sizeof(UNISTR2));
+               if (!dst) return NULL;
+       }
+       if (!dst->buffer) {
+               dst->buffer = (uint16*) talloc(ctx, sizeof(uint16) * (len + 1));
+               if (!dst->buffer) return NULL;
+       }
+       
+       /* set UNISTR2 parameters */
+       dst->uni_max_len = len + 1;
+       dst->undoc = 0;
+       dst->uni_str_len = len;
+       
+       /* copy the actual unicode string */
+       strncpy_w(dst->buffer, src, dst->uni_max_len);
+       
+       return dst;
+};
+
diff --git a/source4/lib/util_uuid.c b/source4/lib/util_uuid.c
new file mode 100644 (file)
index 0000000..76eb93a
--- /dev/null
@@ -0,0 +1,104 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  UUID server routines
+ *  Copyright (C) Theodore Ts'o               1996, 1997,
+ *  Copyright (C) Jim McDonough                     2002.
+ *
+ *  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"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW  0x13814000
+
+struct uuid {
+        uint32   time_low;
+        uint16   time_mid;
+        uint16   time_hi_and_version;
+        uint8    clock_seq[2];
+        uint8    node[6];
+};
+
+
+static void uuid_pack(const struct uuid *uu, GUID *ptr)
+{
+       uint8 *out = ptr->info;
+
+       SIVAL(out, 0, uu->time_low);
+       SSVAL(out, 4, uu->time_mid);
+       SSVAL(out, 6, uu->time_hi_and_version);
+       memcpy(out+8, uu->clock_seq, 2);
+       memcpy(out+10, uu->node, 6);
+}
+
+static void uuid_unpack(const GUID in, struct uuid *uu)
+{
+       const uint8 *ptr = in.info;
+
+       uu->time_low = IVAL(ptr, 0);
+       uu->time_mid = SVAL(ptr, 4);
+       uu->time_hi_and_version = SVAL(ptr, 6);
+       memcpy(uu->clock_seq, ptr+8, 2);
+       memcpy(uu->node, ptr+10, 6);
+}
+
+void uuid_generate_random(GUID *out)
+{
+       GUID tmp;
+       struct uuid uu;
+
+       generate_random_buffer(tmp.info, sizeof(tmp.info), True);
+       uuid_unpack(tmp, &uu);
+
+       uu.clock_seq[0] = (uu.clock_seq[0] & 0x3F) | 0x80;
+       uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
+       uuid_pack(&uu, out);
+}
+
+char *guid_to_string(const GUID in)
+{
+       struct uuid uu;
+       char *out;
+
+       uuid_unpack(in, &uu);
+       
+       asprintf(&out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+                uu.time_low, uu.time_mid, uu.time_hi_and_version,
+                uu.clock_seq[0], uu.clock_seq[1],
+                uu.node[0], uu.node[1], uu.node[2], 
+                uu.node[3], uu.node[4], uu.node[5]);
+
+       return out;
+}
+
+const char *uuid_string(TALLOC_CTX *mem_ctx, const GUID in)
+{
+       struct uuid uu;
+       char *out;
+
+       uuid_unpack(in, &uu);
+       if (!out) return NULL;
+       out = talloc_asprintf(mem_ctx, 
+                "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+                uu.time_low, uu.time_mid, uu.time_hi_and_version,
+                uu.clock_seq[0], uu.clock_seq[1],
+                uu.node[0], uu.node[1], uu.node[2], 
+                uu.node[3], uu.node[4], uu.node[5]);
+       return out;
+}
diff --git a/source4/lib/wins_srv.c b/source4/lib/wins_srv.c
new file mode 100644 (file)
index 0000000..30e81b0
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba wins server helper functions
+   Copyright (C) Andrew Tridgell 1992-2002
+   Copyright (C) Christopher R. Hertel 2000
+   Copyright (C) Tim Potter 2003
+
+   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"
+
+/*
+  This is pretty much a complete rewrite of the earlier code. The main
+  aim of the rewrite is to add support for having multiple wins server
+  lists, so Samba can register with multiple groups of wins servers
+  and each group has a failover list of wins servers.
+
+  Central to the way it all works is the idea of a wins server
+  'tag'. A wins tag is a label for a group of wins servers. For
+  example if you use
+
+      wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
+
+  then you would have two groups of wins servers, one tagged with the
+  name 'fred' and the other with the name 'mary'. I would usually
+  recommend using interface names instead of 'fred' and 'mary' but
+  they can be any alpha string.
+
+  Now, how does it all work. Well, nmbd needs to register each of its
+  IPs with each of its names once with each group of wins servers. So
+  it tries registering with the first one mentioned in the list, then
+  if that fails it marks that WINS server dead and moves onto the next
+  one. 
+
+  In the client code things are a bit different. As each of the groups
+  of wins servers is a separate name space we need to try each of the
+  groups until we either succeed or we run out of wins servers to
+  try. If we get a negative response from a wins server then that
+  means the name doesn't exist in that group, so we give up on that
+  group and move to the next group. If we don't get a response at all
+  then maybe the wins server is down, in which case we need to
+  failover to the next one for that group.
+
+  confused yet? (tridge)
+*/
+
+/* how long a server is marked dead for */
+#define DEATH_TIME 600
+
+/* The list of dead wins servers is stored in gencache.tdb.  Each server is
+   marked dead from the point of view of a given source address. We keep a 
+   separate dead list for each src address to cope with multiple interfaces 
+   that are not routable to each other.
+  */
+
+#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */
+
+static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
+{
+       char *keystr;
+
+       if (asprintf(&keystr, WINS_SRV_FMT, inet_ntoa(wins_ip),
+                    inet_ntoa(src_ip)) == -1) {
+               DEBUG(0, ("wins_srv_is_dead: malloc error\n"));
+               return NULL;
+       }
+
+       return keystr;
+}
+
+/*
+  see if an ip is on the dead list
+*/
+
+BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
+{
+       char *keystr = wins_srv_keystr(wins_ip, src_ip);
+       BOOL result;
+
+       /* If the key exists then the WINS server has been marked as dead */
+
+       result = gencache_get(keystr, NULL, NULL);
+       SAFE_FREE(keystr);
+
+       DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
+                 result ? "dead" : "alive"));
+
+       return result;
+}
+
+
+/*
+  mark a wins server as being alive (for the moment)
+*/
+void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
+{
+       char *keystr = wins_srv_keystr(wins_ip, src_ip);
+
+       gencache_del(keystr);
+       SAFE_FREE(keystr);
+
+       DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n", 
+                 inet_ntoa(wins_ip)));
+}
+
+/*
+  mark a wins server as temporarily dead
+*/
+void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
+{
+       char *keystr;
+
+       if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
+               return;
+
+       keystr = wins_srv_keystr(wins_ip, src_ip);
+
+       gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
+
+       SAFE_FREE(keystr);
+
+       DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n",
+                inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
+}
+
+/*
+  return the total number of wins servers, dead or not
+*/
+unsigned wins_srv_count(void)
+{
+       const char **list;
+       int count = 0;
+
+       if (lp_wins_support()) {
+               /* simple - just talk to ourselves */
+               return 1;
+       }
+
+       list = lp_wins_server_list();
+       for (count=0; list && list[count]; count++)
+               /* nop */ ;
+
+       return count;
+}
+
+/* an internal convenience structure for an IP with a short string tag
+   attached */
+struct tagged_ip {
+       fstring tag;
+       struct in_addr ip;
+};
+
+/*
+  parse an IP string that might be in tagged format
+  the result is a tagged_ip structure containing the tag
+  and the ip in in_addr format. If there is no tag then
+  use the tag '*'
+*/
+static void parse_ip(TALLOC_CTX *mem_ctx, struct tagged_ip *ip, const char *str)
+{
+       char *s = strchr(str, ':');
+       if (!s) {
+               fstrcpy(ip->tag, "*");
+               ip->ip = *interpret_addr2(mem_ctx, str);
+               return;
+       } 
+
+       ip->ip = *interpret_addr2(mem_ctx, s+1);
+       fstrcpy(ip->tag, str);
+       s = strchr(ip->tag, ':');
+       if (s) *s = 0;
+}
+
+
+
+/*
+  return the list of wins server tags. A 'tag' is used to distinguish
+  wins server as either belonging to the same name space or a separate
+  name space. Usually you would setup your 'wins server' option to
+  list one or more wins server per interface and use the interface
+  name as your tag, but you are free to use any tag you like.
+*/
+char **wins_srv_tags(void)
+{
+       char **ret = NULL;
+       int count=0, i, j;
+       const char **list;
+       TALLOC_CTX *mem_ctx;
+
+       if (lp_wins_support()) {
+               /* give the caller something to chew on. This makes
+                  the rest of the logic simpler (ie. less special cases) */
+               ret = (char **)malloc(sizeof(char *)*2);
+               if (!ret) return NULL;
+               ret[0] = strdup("*");
+               ret[1] = NULL;
+               return ret;
+       }
+
+       list = lp_wins_server_list();
+       if (!list)
+               return NULL;
+
+       mem_ctx = talloc_init("wins_ssrv_tags");
+       if (!mem_ctx) {
+               return NULL;
+       }
+       /* yes, this is O(n^2) but n is very small */
+       for (i=0;list[i];i++) {
+               struct tagged_ip t_ip;
+               
+               parse_ip(mem_ctx, &t_ip, list[i]);
+
+               /* see if we already have it */
+               for (j=0;j<count;j++) {
+                       if (strcmp(ret[j], t_ip.tag) == 0) {
+                               break;
+                       }
+               }
+
+               if (j != count) {
+                       /* we already have it. Move along */
+                       continue;
+               }
+
+               /* add it to the list */
+               ret = (char **)Realloc(ret, (count+2) * sizeof(char *));
+               ret[count] = strdup(t_ip.tag);
+               if (!ret[count]) break;
+               count++;
+       }
+
+       if (count) {
+               /* make sure we null terminate */
+               ret[count] = NULL;
+       }
+
+       return ret;
+}
+
+/* free a list of wins server tags given by wins_srv_tags */
+void wins_srv_tags_free(char **list)
+{
+       int i;
+       if (!list) return;
+       for (i=0; list[i]; i++) {
+               free(list[i]);
+       }
+       free(list);
+}
+
+
+/*
+  return the IP of the currently active wins server for the given tag,
+  or the zero IP otherwise
+*/
+struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
+{
+       const char **list;
+       int i;
+       struct tagged_ip t_ip;
+       TALLOC_CTX *mem_ctx;
+
+       /* if we are a wins server then we always just talk to ourselves */
+       if (lp_wins_support()) {
+               extern struct in_addr loopback_ip;
+               return loopback_ip;
+       }
+
+       list = lp_wins_server_list();
+       if (!list || !list[0]) {
+               struct in_addr ip;
+               zero_ip(&ip);
+               return ip;
+       }
+
+       mem_ctx = talloc_init("wins_srv_ip_tag");
+       /* find the first live one for this tag */
+       for (i=0; list[i]; i++) {
+               parse_ip(mem_ctx, &t_ip, list[i]);
+               if (strcmp(tag, t_ip.tag) != 0) {
+                       /* not for the right tag. Move along */
+                       continue;
+               }
+               if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
+                       char *src_name;
+                       src_name = talloc_strdup(mem_ctx, inet_ntoa(src_ip));
+                       DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", 
+                                tag, 
+                                src_name,
+                                inet_ntoa(t_ip.ip)));
+                       goto exit;
+               }
+       }
+       
+       /* they're all dead - try the first one until they revive */
+       for (i=0; list[i]; i++) {
+               parse_ip(mem_ctx, &t_ip, list[i]);
+               if (strcmp(tag, t_ip.tag) != 0) {
+                       continue;
+               }
+               goto exit;
+       }
+
+       /* this can't happen?? */
+       zero_ip(&t_ip.ip);
+exit:
+       talloc_destroy(mem_ctx);
+       return t_ip.ip;
+}
+
+
+/*
+  return a count of the number of IPs for a particular tag, including
+  dead ones
+*/
+unsigned wins_srv_count_tag(const char *tag)
+{
+       const char **list;
+       int i, count=0;
+       TALLOC_CTX *mem_ctx;
+
+       /* if we are a wins server then we always just talk to ourselves */
+       if (lp_wins_support()) {
+               return 1;
+       }
+
+       list = lp_wins_server_list();
+       if (!list || !list[0]) {
+               return 0;
+       }
+
+       /* find the first live one for this tag */
+       mem_ctx = talloc_init("wins_srv_count_tag");
+       if (!mem_ctx) {
+               return 0;
+       }
+       for (i=0; list[i]; i++) {
+               struct tagged_ip t_ip;
+               parse_ip(mem_ctx, &t_ip, list[i]);
+               if (strcmp(tag, t_ip.tag) == 0) {
+                       count++;
+               }
+       }
+       talloc_destroy(mem_ctx);
+
+       return count;
+}
diff --git a/source4/lib/xfile.c b/source4/lib/xfile.c
new file mode 100644 (file)
index 0000000..1534dd8
--- /dev/null
@@ -0,0 +1,378 @@
+/* 
+   Unix SMB/CIFS implementation.
+   stdio replacement
+   Copyright (C) Andrew Tridgell 2001
+   
+   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.
+*/
+
+/*
+  stdio is very convenient, but on some systems the file descriptor
+  in FILE* is 8 bits, so it fails when more than 255 files are open. 
+
+  XFILE replaces stdio. It is less efficient, but at least it works
+  when you have lots of files open
+
+  The main restriction on XFILE is that it doesn't support seeking,
+  and doesn't support O_RDWR. That keeps the code simple.
+*/
+
+#include "includes.h"
+
+#define XBUFSIZE BUFSIZ
+
+static XFILE _x_stdin =  { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
+static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
+static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
+
+XFILE *x_stdin = &_x_stdin;
+XFILE *x_stdout = &_x_stdout;
+XFILE *x_stderr = &_x_stderr;
+
+#define X_FLAG_EOF 1
+#define X_FLAG_ERROR 2
+#define X_FLAG_EINVAL 3
+
+/* simulate setvbuf() */
+int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
+{
+       x_fflush(f);
+       if (f->bufused) return -1;
+
+       /* on files being read full buffering is the only option */
+       if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
+               mode = X_IOFBF;
+       }
+
+       /* destroy any earlier buffer */
+       SAFE_FREE(f->buf);
+       f->buf = 0;
+       f->bufsize = 0;
+       f->next = NULL;
+       f->bufused = 0;
+       f->buftype = mode;
+
+       if (f->buftype == X_IONBF) return 0;
+
+       /* if buffering then we need some size */
+       if (size == 0) size = XBUFSIZE;
+
+       f->bufsize = size;
+       f->bufused = 0;
+
+       return 0;
+}
+
+/* allocate the buffer */
+static int x_allocate_buffer(XFILE *f)
+{
+       if (f->buf) return 1;
+       if (f->bufsize == 0) return 0;
+       f->buf = malloc(f->bufsize);
+       if (!f->buf) return 0;
+       f->next = f->buf;
+       return 1;
+}
+
+
+/* this looks more like open() than fopen(), but that is quite deliberate.
+   I want programmers to *think* about O_EXCL, O_CREAT etc not just
+   get them magically added 
+*/
+XFILE *x_fopen(const char *fname, int flags, mode_t mode)
+{
+       XFILE *ret;
+
+       ret = (XFILE *)malloc(sizeof(XFILE));
+       if (!ret) return NULL;
+
+       memset(ret, 0, sizeof(XFILE));
+
+       if ((flags & O_ACCMODE) == O_RDWR) {
+               /* we don't support RDWR in XFILE - use file 
+                  descriptors instead */
+               errno = EINVAL;
+               return NULL;
+       }
+
+       ret->open_flags = flags;
+
+       ret->fd = sys_open(fname, flags, mode);
+       if (ret->fd == -1) {
+               SAFE_FREE(ret);
+               return NULL;
+       }
+
+       x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
+       
+       return ret;
+}
+
+/* simulate fclose() */
+int x_fclose(XFILE *f)
+{
+       int ret;
+
+       /* make sure we flush any buffered data */
+       x_fflush(f);
+
+       ret = close(f->fd);
+       f->fd = -1;
+       if (f->buf) {
+               /* make sure data can't leak into a later malloc */
+               memset(f->buf, 0, f->bufsize);
+               SAFE_FREE(f->buf);
+       }
+       SAFE_FREE(f);
+       return ret;
+}
+
+/* simulate fwrite() */
+size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
+{
+       ssize_t ret;
+       size_t total=0;
+
+       /* we might be writing unbuffered */
+       if (f->buftype == X_IONBF || 
+           (!f->buf && !x_allocate_buffer(f))) {
+               ret = write(f->fd, p, size*nmemb);
+               if (ret == -1) return -1;
+               return ret/size;
+       } 
+
+
+       while (total < size*nmemb) {
+               size_t n = f->bufsize - f->bufused;
+               n = MIN(n, (size*nmemb)-total);
+
+               if (n == 0) {
+                       /* it's full, flush it */
+                       x_fflush(f);
+                       continue;
+               }
+
+               memcpy(f->buf + f->bufused, total+(const char *)p, n);
+               f->bufused += n;
+               total += n;
+       }
+
+       /* when line buffered we need to flush at the last linefeed. This can
+          flush a bit more than necessary, but that is harmless */
+       if (f->buftype == X_IOLBF && f->bufused) {
+               int i;
+               for (i=(size*nmemb)-1; i>=0; i--) {
+                       if (*(i+(const char *)p) == '\n') {
+                               x_fflush(f);
+                               break;
+                       }
+               }
+       }
+
+       return total/size;
+}
+
+/* thank goodness for asprintf() */
+ int x_vfprintf(XFILE *f, const char *format, va_list ap)
+{
+       char *p;
+       int len, ret;
+       va_list ap2;
+
+       VA_COPY(ap2, ap);
+
+       len = vasprintf(&p, format, ap2);
+       if (len <= 0) return len;
+       ret = x_fwrite(p, 1, len, f);
+       SAFE_FREE(p);
+       return ret;
+}
+
+ int x_fprintf(XFILE *f, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, format);
+       ret = x_vfprintf(f, format, ap);
+       va_end(ap);
+       return ret;
+}
+
+/* at least fileno() is simple! */
+int x_fileno(XFILE *f)
+{
+       return f->fd;
+}
+
+/* simulate fflush() */
+int x_fflush(XFILE *f)
+{
+       int ret;
+
+       if (f->flags & X_FLAG_ERROR) return -1;
+
+       if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (f->bufused == 0) return 0;
+
+       ret = write(f->fd, f->buf, f->bufused);
+       if (ret == -1) return -1;
+       
+       f->bufused -= ret;
+       if (f->bufused > 0) {
+               f->flags |= X_FLAG_ERROR;
+               memmove(f->buf, ret + (char *)f->buf, f->bufused);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* simulate setbuffer() */
+void x_setbuffer(XFILE *f, char *buf, size_t size)
+{
+       x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
+}
+
+/* simulate setbuf() */
+void x_setbuf(XFILE *f, char *buf)
+{
+       x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
+}
+
+/* simulate setlinebuf() */
+void x_setlinebuf(XFILE *f)
+{
+       x_setvbuf(f, NULL, X_IOLBF, 0);
+}
+
+
+/* simulate feof() */
+int x_feof(XFILE *f)
+{
+       if (f->flags & X_FLAG_EOF) return 1;
+       return 0;
+}
+
+/* simulate ferror() */
+int x_ferror(XFILE *f)
+{
+       if (f->flags & X_FLAG_ERROR) return 1;
+       return 0;
+}
+
+/* fill the read buffer */
+static void x_fillbuf(XFILE *f)
+{
+       int n;
+
+       if (f->bufused) return;
+
+       if (!f->buf && !x_allocate_buffer(f)) return;
+
+       n = read(f->fd, f->buf, f->bufsize);
+       if (n <= 0) return;
+       f->bufused = n;
+       f->next = f->buf;
+}
+
+/* simulate fgetc() */
+int x_fgetc(XFILE *f)
+{
+       int ret;
+
+       if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
+       
+       if (f->bufused == 0) x_fillbuf(f);
+
+       if (f->bufused == 0) {
+               f->flags |= X_FLAG_EOF;
+               return EOF;
+       }
+
+       ret = *(unsigned char *)(f->next);
+       f->next++;
+       f->bufused--;
+       return ret;
+}
+
+/* simulate fread */
+size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
+{
+       size_t total = 0;
+       while (total < size*nmemb) {
+               int c = x_fgetc(f);
+               if (c == EOF) break;
+               (total+(char *)p)[0] = (char)c;
+               total++;
+       }
+       return total/size;
+}
+
+/* simulate fgets() */
+char *x_fgets(char *s, int size, XFILE *stream) 
+{
+       char *s0 = s;
+       int l = size;
+       while (l>1) {
+               int c = x_fgetc(stream);
+               if (c == EOF) break;
+               *s++ = (char)c;
+               l--;
+               if (c == '\n') break;
+       }
+       if (l==size || x_ferror(stream)) {
+               return 0;
+       }
+       *s = 0;
+       return s0;
+}
+
+/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
+ * set then an error is returned */
+off_t x_tseek(XFILE *f, off_t offset, int whence)
+{
+       if (f->flags & X_FLAG_ERROR)
+               return -1;
+
+       /* only SEEK_SET and SEEK_END are supported */
+       /* SEEK_CUR needs internal offset counter */
+       if (whence != SEEK_SET && whence != SEEK_END) {
+               f->flags |= X_FLAG_EINVAL;
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* empty the buffer */
+       switch (f->open_flags & O_ACCMODE) {
+       case O_RDONLY:
+               f->bufused = 0;
+               break;
+       case O_WRONLY:
+               if (x_fflush(f) != 0)
+                       return -1;
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       f->flags &= ~X_FLAG_EOF;
+       return (off_t)sys_lseek(f->fd, offset, whence);
+}
diff --git a/source4/libads/.cvsignore b/source4/libads/.cvsignore
new file mode 100644 (file)
index 0000000..5f2a5c4
--- /dev/null
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source4/libads/ads_ldap.c b/source4/libads/ads_ldap.c
new file mode 100644 (file)
index 0000000..97f12de
--- /dev/null
@@ -0,0 +1,155 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind ADS backend functions
+
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Andrew Bartlett 2002
+   
+   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"
+#ifdef HAVE_LDAP
+
+/* convert a single name to a sid in a domain */
+NTSTATUS ads_name_to_sid(ADS_STRUCT *ads,
+                        const char *name,
+                        DOM_SID *sid,
+                        enum SID_NAME_USE *type)
+{
+       const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
+       int count;
+       ADS_STATUS rc;
+       void *res = NULL;
+       char *exp;
+       uint32 t;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       char *escaped_name = escape_ldap_string_alloc(name);
+       char *escaped_realm = escape_ldap_string_alloc(ads->config.realm);
+
+       if (!escaped_name || !escaped_realm) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       if (asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", 
+                    escaped_name, escaped_name, escaped_realm) == -1) {
+               DEBUG(1,("ads_name_to_sid: asprintf failed!\n"));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       rc = ads_search_retry(ads, &res, exp, attrs);
+       free(exp);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
+               goto done;
+       }
+
+       count = ads_count_replies(ads, res);
+       if (count != 1) {
+               DEBUG(1,("name_to_sid: %s not found\n", name));
+               goto done;
+       }
+
+       if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+               DEBUG(1,("No sid for %s !?\n", name));
+               goto done;
+       }
+
+       if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
+               DEBUG(1,("No sAMAccountType for %s !?\n", name));
+               goto done;
+       }
+
+       *type = ads_atype_map(t);
+
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads name_to_sid mapped %s\n", name));
+
+done:
+       if (res) ads_msgfree(ads, res);
+
+       SAFE_FREE(escaped_name);
+       SAFE_FREE(escaped_realm);
+
+       return status;
+}
+
+/* convert a sid to a user or group name */
+NTSTATUS ads_sid_to_name(ADS_STRUCT *ads,
+                        TALLOC_CTX *mem_ctx,
+                        const DOM_SID *sid,
+                        char **name,
+                        enum SID_NAME_USE *type)
+{
+       const char *attrs[] = {"userPrincipalName", 
+                              "sAMAccountName",
+                              "sAMAccountType", NULL};
+       ADS_STATUS rc;
+       void *msg = NULL;
+       char *exp = NULL;
+       char *sidstr = NULL;
+       uint32 atype;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+       if (!(sidstr = sid_binstring(sid))) {
+               DEBUG(1,("ads_sid_to_name: sid_binstring failed!\n"));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       if (asprintf(&exp, "(objectSid=%s)", sidstr) == -1) {
+               DEBUG(1,("ads_sid_to_name: asprintf failed!\n"));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       rc = ads_search_retry(ads, &msg, exp, attrs);
+       if (!ADS_ERR_OK(rc)) {
+               status = ads_ntstatus(rc);
+               DEBUG(1,("ads_sid_to_name ads_search: %s\n", ads_errstr(rc)));
+               goto done;
+       }
+
+       if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
+               goto done;
+       }
+
+       *name = ads_pull_username(ads, mem_ctx, msg);
+       if (!*name) {
+               DEBUG(1,("ads_sid_to_name: ads_pull_username retuned NULL!\n"));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+               
+       *type = ads_atype_map(atype);
+
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads sid_to_name mapped %s\n", *name));
+
+done:
+       if (msg) ads_msgfree(ads, msg);
+
+       SAFE_FREE(exp);
+       SAFE_FREE(sidstr);
+
+       return status;
+}
+
+#endif
diff --git a/source4/libads/ads_status.c b/source4/libads/ads_status.c
new file mode 100644 (file)
index 0000000..80fdb99
--- /dev/null
@@ -0,0 +1,133 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001
+   Copyright (C) Andrew Bartlett 2001
+   
+   
+   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"
+
+/*
+  build a ADS_STATUS structure
+*/
+ADS_STATUS ads_build_error(enum ads_error_type etype, 
+                          int rc, int minor_status)
+{
+       ADS_STATUS ret;
+
+       if (etype == ADS_ERROR_NT) {
+               DEBUG(0,("don't use ads_build_error with ADS_ERROR_NT!\n"));
+               ret.err.rc = -1;
+               ret.error_type = ADS_ERROR_SYSTEM;
+               ret.minor_status = 0;
+               return ret;     
+       }       
+               
+       ret.err.rc = rc;
+       ret.error_type = etype;         
+       ret.minor_status = minor_status;
+       return ret;
+}
+
+ADS_STATUS ads_build_nt_error(enum ads_error_type etype, 
+                          NTSTATUS nt_status)
+{
+       ADS_STATUS ret;
+
+       if (etype != ADS_ERROR_NT) {
+               DEBUG(0,("don't use ads_build_nt_error without ADS_ERROR_NT!\n"));
+               ret.err.rc = -1;
+               ret.error_type = ADS_ERROR_SYSTEM;
+               ret.minor_status = 0;
+               return ret;     
+       }
+       ret.err.nt_status = nt_status;
+       ret.error_type = etype;         
+       ret.minor_status = 0;
+       return ret;
+}
+
+/*
+  do a rough conversion between ads error codes and NT status codes
+  we'll need to fill this in more
+*/
+NTSTATUS ads_ntstatus(ADS_STATUS status)
+{
+       if (status.error_type == ADS_ERROR_NT){
+               return status.err.nt_status;    
+       }
+#ifdef HAVE_LDAP
+       if ((status.error_type == ADS_ERROR_LDAP) 
+           && (status.err.rc == LDAP_NO_MEMORY)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+#endif
+       if (ADS_ERR_OK(status)) return NT_STATUS_OK;
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+  return a string for an error from a ads routine
+*/
+const char *ads_errstr(ADS_STATUS status)
+{
+       int msg_ctx;
+       static char *ret;
+
+       SAFE_FREE(ret);
+       msg_ctx = 0;
+
+       switch (status.error_type) {
+       case ADS_ERROR_SYSTEM:
+               return strerror(status.err.rc);
+#ifdef HAVE_LDAP
+       case ADS_ERROR_LDAP:
+               return ldap_err2string(status.err.rc);
+#endif
+#ifdef HAVE_KRB5
+       case ADS_ERROR_KRB5: 
+               return error_message(status.err.rc);
+#endif
+#ifdef HAVE_GSSAPI
+       case ADS_ERROR_GSS:
+       {
+               uint32 minor;
+               
+               gss_buffer_desc msg1, msg2;
+               msg1.value = NULL;
+               msg2.value = NULL;
+               gss_display_status(&minor, status.err.rc, GSS_C_GSS_CODE,
+                                  GSS_C_NULL_OID, &msg_ctx, &msg1);
+               gss_display_status(&minor, status.minor_status, GSS_C_MECH_CODE,
+                                  GSS_C_NULL_OID, &msg_ctx, &msg2);
+               asprintf(&ret, "%s : %s", (char *)msg1.value, (char *)msg2.value);
+               gss_release_buffer(&minor, &msg1);
+               gss_release_buffer(&minor, &msg2);
+               return ret;
+       }
+#endif
+       case ADS_ERROR_NT: 
+               return nt_errstr(ads_ntstatus(status));
+       default:
+               return "Unknown ADS error type!? (not compiled in?)";
+       }
+
+}
+
+
diff --git a/source4/libads/ads_struct.c b/source4/libads/ads_struct.c
new file mode 100644 (file)
index 0000000..652bfe3
--- /dev/null
@@ -0,0 +1,141 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Andrew Bartlett 2001
+   
+   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"
+
+/* return a ldap dn path from a string, given separators and field name
+   caller must free
+*/
+char *ads_build_path(const char *realm, const char *sep, const char *field, int reverse)
+{
+       char *p, *r;
+       int numbits = 0;
+       char *ret;
+       int len;
+       
+       r = strdup(realm);
+
+       if (!r || !*r)
+               return r;
+
+       for (p=r; *p; p++)
+               if (strchr(sep, *p))
+                       numbits++;
+
+       len = (numbits+1)*(strlen(field)+1) + strlen(r) + 1;
+
+       ret = malloc(len);
+       if (!ret)
+               return NULL;
+
+       strlcpy(ret,field, len);
+       p=strtok(r,sep); 
+       strlcat(ret, p, len);
+
+       while ((p=strtok(NULL,sep))) {
+               char *s;
+               if (reverse)
+                       asprintf(&s, "%s%s,%s", field, p, ret);
+               else
+                       asprintf(&s, "%s,%s%s", ret, field, p);
+               free(ret);
+               ret = s;
+       }
+
+       free(r);
+       return ret;
+}
+
+/* return a dn of the form "dc=AA,dc=BB,dc=CC" from a 
+   realm of the form AA.BB.CC 
+   caller must free
+*/
+char *ads_build_dn(const char *realm)
+{
+       return ads_build_path(realm, ".", "dc=", 0);
+}
+
+
+#ifndef LDAP_PORT
+#define LDAP_PORT 389
+#endif
+
+/*
+  initialise a ADS_STRUCT, ready for some ads_ ops
+*/
+ADS_STRUCT *ads_init(const char *realm, 
+                    const char *workgroup,
+                    const char *ldap_server)
+{
+       ADS_STRUCT *ads;
+       
+       ads = (ADS_STRUCT *)smb_xmalloc(sizeof(*ads));
+       ZERO_STRUCTP(ads);
+       
+       ads->server.realm = realm? strdup(realm) : NULL;
+       ads->server.workgroup = workgroup ? strdup(workgroup) : NULL;
+       ads->server.ldap_server = ldap_server? strdup(ldap_server) : NULL;
+
+       /* we need to know if this is a foreign realm to know if we can
+          use lp_ads_server() */
+       if (realm && *realm && strcasecmp(lp_realm(), realm) != 0) {
+               ads->server.foreign = 1;
+       }
+       if (workgroup && *workgroup && strcasecmp(lp_workgroup(), workgroup) != 0) {
+               ads->server.foreign = 1;
+       }
+
+       return ads;
+}
+
+/* a simpler ads_init() interface using all defaults */
+ADS_STRUCT *ads_init_simple(void)
+{
+       return ads_init(NULL, NULL, NULL);
+}
+
+/*
+  free the memory used by the ADS structure initialized with 'ads_init(...)'
+*/
+void ads_destroy(ADS_STRUCT **ads)
+{
+       if (ads && *ads) {
+#if HAVE_LDAP
+               if ((*ads)->ld) ldap_unbind((*ads)->ld);
+#endif
+               SAFE_FREE((*ads)->server.realm);
+               SAFE_FREE((*ads)->server.workgroup);
+               SAFE_FREE((*ads)->server.ldap_server);
+               SAFE_FREE((*ads)->server.ldap_uri);
+
+               SAFE_FREE((*ads)->auth.realm);
+               SAFE_FREE((*ads)->auth.password);
+               SAFE_FREE((*ads)->auth.user_name);
+               SAFE_FREE((*ads)->auth.kdc_server);
+
+               SAFE_FREE((*ads)->config.realm);
+               SAFE_FREE((*ads)->config.bind_path);
+               SAFE_FREE((*ads)->config.ldap_server_name);
+
+               ZERO_STRUCTP(*ads);
+               SAFE_FREE(*ads);
+       }
+}
diff --git a/source4/libads/ads_utils.c b/source4/libads/ads_utils.c
new file mode 100644 (file)
index 0000000..626c177
--- /dev/null
@@ -0,0 +1,181 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) utility library
+   
+   Copyright (C) Stefan (metze) Metzmacher 2002
+   Copyright (C) Andrew Tridgell 2001
+  
+   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"
+
+/* 
+translated the ACB_CTRL Flags to UserFlags (userAccountControl) 
+*/ 
+uint32 ads_acb2uf(uint16 acb)
+{
+       uint32 uf = 0x00000000;
+       
+       if (acb & ACB_DISABLED)         uf |= UF_ACCOUNTDISABLE;
+       if (acb & ACB_HOMDIRREQ)        uf |= UF_HOMEDIR_REQUIRED;
+       if (acb & ACB_PWNOTREQ)         uf |= UF_PASSWD_NOTREQD;        
+       if (acb & ACB_TEMPDUP)          uf |= UF_TEMP_DUPLICATE_ACCOUNT;        
+       if (acb & ACB_NORMAL)           uf |= UF_NORMAL_ACCOUNT;
+       if (acb & ACB_MNS)              uf |= UF_MNS_LOGON_ACCOUNT;
+       if (acb & ACB_DOMTRUST)         uf |= UF_INTERDOMAIN_TRUST_ACCOUNT;
+       if (acb & ACB_WSTRUST)          uf |= UF_WORKSTATION_TRUST_ACCOUNT;
+       if (acb & ACB_SVRTRUST)         uf |= UF_SERVER_TRUST_ACCOUNT;
+       if (acb & ACB_PWNOEXP)          uf |= UF_DONT_EXPIRE_PASSWD;
+       if (acb & ACB_AUTOLOCK)         uf |= UF_LOCKOUT;
+
+       return uf;
+}
+
+/*
+translated the UserFlags (userAccountControl) to ACB_CTRL Flags
+*/
+uint16 ads_uf2acb(uint32 uf)
+{
+       uint16 acb = 0x0000;
+       
+       if (uf & UF_ACCOUNTDISABLE)             acb |= ACB_DISABLED;
+       if (uf & UF_HOMEDIR_REQUIRED)           acb |= ACB_HOMDIRREQ;
+       if (uf & UF_PASSWD_NOTREQD)             acb |= ACB_PWNOTREQ;    
+       if (uf & UF_MNS_LOGON_ACCOUNT)          acb |= ACB_MNS;
+       if (uf & UF_DONT_EXPIRE_PASSWD)         acb |= ACB_PWNOEXP;
+       if (uf & UF_LOCKOUT)                    acb |= ACB_AUTOLOCK;
+       
+       switch (uf & UF_ACCOUNT_TYPE_MASK)
+       {
+               case UF_TEMP_DUPLICATE_ACCOUNT:         acb |= ACB_TEMPDUP;break;       
+               case UF_NORMAL_ACCOUNT:                 acb |= ACB_NORMAL;break;
+               case UF_INTERDOMAIN_TRUST_ACCOUNT:      acb |= ACB_DOMTRUST;break;
+               case UF_WORKSTATION_TRUST_ACCOUNT:      acb |= ACB_WSTRUST;break;
+               case UF_SERVER_TRUST_ACCOUNT:           acb |= ACB_SVRTRUST;break;
+               /*Fix Me: what should we do here? */
+               default:                                acb |= ACB_NORMAL;break;
+       }
+
+       return acb;
+}
+
+/* 
+get the accountType from the UserFlags
+*/
+uint32 ads_uf2atype(uint32 uf)
+{
+       uint32 atype = 0x00000000;
+               
+       if (uf & UF_NORMAL_ACCOUNT)                     atype = ATYPE_NORMAL_ACCOUNT;
+       else if (uf & UF_TEMP_DUPLICATE_ACCOUNT)        atype = ATYPE_NORMAL_ACCOUNT;
+       else if (uf & UF_SERVER_TRUST_ACCOUNT)          atype = ATYPE_WORKSTATION_TRUST;
+       else if (uf & UF_WORKSTATION_TRUST_ACCOUNT)     atype = ATYPE_WORKSTATION_TRUST;
+       else if (uf & UF_INTERDOMAIN_TRUST_ACCOUNT)     atype = ATYPE_INTERDOMAIN_TRUST;
+
+       return atype;
+} 
+
+/* 
+translated the GROUP_CTRL Flags to GroupType (groupType) 
+*/ 
+uint32 ads_gcb2gtype(uint16 gcb)
+{
+       uint32 gtype = 0x00000000;
+
+       if (gcb & GCB_ALIAS_GROUP)      gtype |= GTYPE_SECURITY_BUILTIN_LOCAL_GROUP;
+       else if(gcb & GCB_LOCAL_GROUP)  gtype |= GTYPE_SECURITY_DOMAIN_LOCAL_GROUP;
+       if (gcb & GCB_GLOBAL_GROUP)     gtype |= GTYPE_SECURITY_GLOBAL_GROUP;
+               
+       return gtype;
+}
+
+/*
+translated the GroupType (groupType) to GROUP_CTRL Flags
+*/
+uint16 ads_gtype2gcb(uint32 gtype)
+{
+       uint16 gcb = 0x0000;
+
+       switch(gtype) {
+               case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+                       gcb = GCB_ALIAS_GROUP;
+                       break;
+               case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+                       gcb = GCB_LOCAL_GROUP;
+                       break;
+               case GTYPE_SECURITY_GLOBAL_GROUP:
+                       gcb = GCB_GLOBAL_GROUP;
+                       break;
+
+               case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+                       gcb = GCB_GLOBAL_GROUP;
+                       break;
+               case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+                       gcb = GCB_LOCAL_GROUP;
+                       break;
+               case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+                       gcb = GCB_GLOBAL_GROUP;
+                       break;
+       }
+       
+       return gcb;
+}
+
+/* 
+get the accountType from the groupType
+*/
+uint32 ads_gtype2atype(uint32 gtype)
+{
+       uint32 atype = 0x00000000;
+       
+       switch(gtype) {
+               case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+                       atype = ATYPE_SECURITY_LOCAL_GROUP;
+                       break;
+               case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+                       atype = ATYPE_SECURITY_LOCAL_GROUP;
+                       break;
+               case GTYPE_SECURITY_GLOBAL_GROUP:
+                       atype = ATYPE_SECURITY_GLOBAL_GROUP;
+                       break;
+       
+               case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+                       atype = ATYPE_DISTRIBUTION_GLOBAL_GROUP;
+                       break;
+               case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+                       atype = ATYPE_DISTRIBUTION_UNIVERSAL_GROUP;
+                       break;
+               case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+                       atype = ATYPE_DISTRIBUTION_LOCAL_GROUP;
+                       break;
+       }
+
+       return atype;
+}
+
+/* turn a sAMAccountType into a SID_NAME_USE */
+enum SID_NAME_USE ads_atype_map(uint32 atype)
+{
+       switch (atype & 0xF0000000) {
+       case ATYPE_GLOBAL_GROUP:
+               return SID_NAME_DOM_GRP;
+       case ATYPE_ACCOUNT:
+               return SID_NAME_USER;
+       default:
+               DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
+       }
+       return SID_NAME_UNKNOWN;
+}
diff --git a/source4/libads/disp_sec.c b/source4/libads/disp_sec.c
new file mode 100644 (file)
index 0000000..c9de447
--- /dev/null
@@ -0,0 +1,159 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions. ADS stuff
+   Copyright (C) Alexey Kotovich 2002
+   
+   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"
+
+static struct perm_mask_str {
+       uint32  mask;
+       const char   *str;
+} perms[] = {
+       {SEC_RIGHTS_FULL_CTRL,          "[Full Control]"},
+
+       {SEC_RIGHTS_LIST_CONTENTS,      "[List Contents]"},
+       {SEC_RIGHTS_LIST_OBJECT,        "[List Object]"},
+
+       {SEC_RIGHTS_READ_ALL_PROP,      "[Read All Properties]"},       
+       {SEC_RIGHTS_READ_PERMS,         "[Read Permissions]"},  
+
+       {SEC_RIGHTS_WRITE_ALL_VALID,    "[All validate writes]"},
+       {SEC_RIGHTS_WRITE_ALL_PROP,     "[Write All Properties]"},
+
+       {SEC_RIGHTS_MODIFY_PERMS,       "[Modify Permissions]"},
+       {SEC_RIGHTS_MODIFY_OWNER,       "[Modify Owner]"},
+
+       {SEC_RIGHTS_CREATE_CHILD,       "[Create All Child Objects]"},
+
+       {SEC_RIGHTS_DELETE,             "[Delete]"},
+       {SEC_RIGHTS_DELETE_SUBTREE,     "[Delete Subtree]"},
+       {SEC_RIGHTS_DELETE_CHILD,       "[Delete All Child Objects]"},
+
+       {SEC_RIGHTS_CHANGE_PASSWD,      "[Change Password]"},   
+       {SEC_RIGHTS_RESET_PASSWD,       "[Reset Password]"},
+       {0,                             0}
+};
+
+/* convert a security permissions into a string */
+static void ads_disp_perms(uint32 type)
+{
+       int i = 0;
+       int j = 0;
+
+       printf("Permissions: ");
+       
+       if (type == SEC_RIGHTS_FULL_CTRL) {
+               printf("%s\n", perms[j].str);
+               return;
+       }
+
+       for (i = 0; i < 32; i++) {
+               if (type & (1 << i)) {
+                       for (j = 1; perms[j].str; j ++) {
+                               if (perms[j].mask == (((unsigned) 1) << i)) {
+                                       printf("\n\t%s", perms[j].str);
+                               }       
+                       }
+                       type &= ~(1 << i);
+               }
+       }
+
+       /* remaining bits get added on as-is */
+       if (type != 0) {
+               printf("[%08x]", type);
+       }
+       puts("");
+}
+
+/* display ACE */
+static void ads_disp_ace(SEC_ACE *sec_ace)
+{
+       const char *access_type = "UNKNOWN";
+
+       if (!sec_ace_object(sec_ace->type)) {
+               printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x)\n", 
+                 sec_ace->type,
+                 sec_ace->flags,
+                 sec_ace->size,
+                 sec_ace->info.mask);                  
+       } else {
+               printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x, object flags: 0x%x)\n", 
+                 sec_ace->type,
+                 sec_ace->flags,
+                 sec_ace->size,
+                 sec_ace->info.mask,
+                 sec_ace->obj_flags);
+       }
+       
+       if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+               access_type = "ALLOWED";
+       } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+               access_type = "DENIED";
+       } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT) {
+               access_type = "SYSTEM AUDIT";
+       } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
+               access_type = "ALLOWED OBJECT";
+       } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
+               access_type = "DENIED OBJECT";
+       } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) {
+               access_type = "AUDIT OBJECT";
+       }
+
+       printf("access SID:  %s\naccess type: %s\n", 
+               sid_string_static(&sec_ace->trustee), access_type);
+
+       ads_disp_perms(sec_ace->info.mask);
+}
+
+/* display ACL */
+static void ads_disp_acl(SEC_ACL *sec_acl, const char *type)
+{
+        if (!sec_acl)
+               printf("------- (%s) ACL not present\n", type);
+       else {
+               printf("------- (%s) ACL (revision: %d, size: %d, number of ACEs: %d)\n", 
+                      type,
+                      sec_acl->revision,
+                      sec_acl->size,
+                      sec_acl->num_aces);                      
+       }
+}
+
+/* display SD */
+void ads_disp_sd(SEC_DESC *sd)
+{
+       int i;
+       
+       printf("-------------- Security Descriptor (revision: %d, type: 0x%02x)\n", 
+               sd->revision,
+               sd->type);
+       printf("owner SID: %s\n", sid_string_static(sd->owner_sid));
+       printf("group SID: %s\n", sid_string_static(sd->grp_sid));
+
+       ads_disp_acl(sd->sacl, "system");
+       for (i = 0; i < sd->sacl->num_aces; i ++)
+               ads_disp_ace(&sd->sacl->ace[i]);
+       
+       ads_disp_acl(sd->dacl, "user");
+       for (i = 0; i < sd->dacl->num_aces; i ++)
+               ads_disp_ace(&sd->dacl->ace[i]);
+
+       printf("-------------- End Of Security Descriptor\n");
+}
+
+
diff --git a/source4/libads/kerberos.c b/source4/libads/kerberos.c
new file mode 100644 (file)
index 0000000..bef2feb
--- /dev/null
@@ -0,0 +1,140 @@
+/* 
+   Unix SMB/CIFS implementation.
+   kerberos utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001
+   
+   
+   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"
+
+#ifdef HAVE_KRB5
+
+/*
+  we use a prompter to avoid a crash bug in the kerberos libs when 
+  dealing with empty passwords
+  this prompter is just a string copy ...
+*/
+static krb5_error_code 
+kerb_prompter(krb5_context ctx, void *data,
+              const char *name,
+              const char *banner,
+              int num_prompts,
+              krb5_prompt prompts[])
+{
+       if (num_prompts == 0) return 0;
+
+       memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+       if (prompts[0].reply->length > 0) {
+               if (data) {
+                       strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+               } else {
+                       prompts[0].reply->length = 0;
+               }
+       }
+       return 0;
+}
+
+/*
+  simulate a kinit, putting the tgt in the default cache location
+  remus@snapserver.com
+*/
+int kerberos_kinit_password(const char *principal, const char *password, int time_offset)
+{
+       krb5_context ctx;
+       krb5_error_code code = 0;
+       krb5_ccache cc;
+       krb5_principal me;
+       krb5_creds my_creds;
+
+       if ((code = krb5_init_context(&ctx)))
+               return code;
+
+       if (time_offset != 0) {
+               krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
+       }
+       
+       if ((code = krb5_cc_default(ctx, &cc))) {
+               krb5_free_context(ctx);
+               return code;
+       }
+       
+       if ((code = krb5_parse_name(ctx, principal, &me))) {
+               krb5_free_context(ctx); 
+               return code;
+       }
+       
+       if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, NULL, 
+                                                kerb_prompter, 
+                                                password, 0, NULL, NULL))) {
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       if ((code = krb5_cc_initialize(ctx, cc, me))) {
+               krb5_free_cred_contents(ctx, &my_creds);
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+               krb5_cc_close(ctx, cc);
+               krb5_free_cred_contents(ctx, &my_creds);
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       krb5_cc_close(ctx, cc);
+       krb5_free_cred_contents(ctx, &my_creds);
+       krb5_free_principal(ctx, me);
+       krb5_free_context(ctx);         
+       
+       return 0;
+}
+
+
+
+/* run kinit to setup our ccache */
+int ads_kinit_password(ADS_STRUCT *ads)
+{
+       char *s;
+       int ret;
+
+       if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) {
+               return KRB5_CC_NOMEM;
+       }
+
+       if (!ads->auth.password) {
+               return KRB5_LIBOS_CANTREADPWD;
+       }
+       
+       ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset);
+
+       if (ret) {
+               DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
+                        s, error_message(ret)));
+       }
+       free(s);
+       return ret;
+}
+
+
+#endif
diff --git a/source4/libads/kerberos_verify.c b/source4/libads/kerberos_verify.c
new file mode 100644 (file)
index 0000000..9367f53
--- /dev/null
@@ -0,0 +1,173 @@
+/* 
+   Unix SMB/CIFS implementation.
+   kerberos utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001
+   Copyright (C) Luke Howard 2003   
+   
+   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"
+
+#ifdef HAVE_KRB5
+
+/*
+  verify an incoming ticket and parse out the principal name and 
+  authorization_data if available 
+*/
+NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket, 
+                          char **principal, DATA_BLOB *auth_data,
+                          DATA_BLOB *ap_rep,
+                          uint8 session_key[16])
+{
+       krb5_context context;
+       krb5_auth_context auth_context = NULL;
+       krb5_keytab keytab = NULL;
+       krb5_data packet;
+       krb5_ticket *tkt = NULL;
+       int ret, i;
+       krb5_keyblock * key;
+       krb5_principal host_princ;
+       char *host_princ_s;
+       fstring myname;
+       char *password_s;
+       krb5_data password;
+       krb5_enctype *enctypes = NULL;
+       BOOL auth_ok = False;
+
+       if (!secrets_init()) {
+               DEBUG(1,("secrets_init failed\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       password_s = secrets_fetch_machine_password();
+       if (!password_s) {
+               DEBUG(1,("failed to fetch machine password\n"));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       password.data = password_s;
+       password.length = strlen(password_s);
+
+       ret = krb5_init_context(&context);
+       if (ret) {
+               DEBUG(1,("krb5_init_context failed (%s)\n", error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       ret = krb5_set_default_realm(context, ads->auth.realm);
+       if (ret) {
+               DEBUG(1,("krb5_set_default_realm failed (%s)\n", error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* this whole process is far more complex than I would
+           like. We have to go through all this to allow us to store
+           the secret internally, instead of using /etc/krb5.keytab */
+       ret = krb5_auth_con_init(context, &auth_context);
+       if (ret) {
+               DEBUG(1,("krb5_auth_con_init failed (%s)\n", error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       fstrcpy(myname, lp_netbios_name());
+       strlower(myname);
+       asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
+       ret = krb5_parse_name(context, host_princ_s, &host_princ);
+       if (ret) {
+               DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) {
+               DEBUG(1,("krb5_get_permitted_enctypes failed (%s)\n", 
+                        error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* we need to setup a auth context with each possible encoding type in turn */
+       for (i=0;enctypes[i];i++) {
+               if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
+                       continue;
+               }
+
+               krb5_auth_con_setuseruserkey(context, auth_context, key);
+
+               packet.length = ticket->length;
+               packet.data = (krb5_pointer)ticket->data;
+
+               if (!(ret = krb5_rd_req(context, &auth_context, &packet, 
+                                      NULL, keytab, NULL, &tkt))) {
+                       free_kerberos_etypes(context, enctypes);
+                       auth_ok = True;
+                       break;
+               }
+       }
+
+       if (!auth_ok) {
+               DEBUG(3,("krb5_rd_req with auth failed (%s)\n", 
+                        error_message(ret)));
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       ret = krb5_mk_rep(context, auth_context, &packet);
+       if (ret) {
+               DEBUG(3,("Failed to generate mutual authentication reply (%s)\n",
+                       error_message(ret)));
+               krb5_auth_con_free(context, auth_context);
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       *ap_rep = data_blob(packet.data, packet.length);
+       free(packet.data);
+
+       krb5_get_smb_session_key(context, auth_context, session_key);
+       DEBUG(0,("SMB session key (from ticket) follows:\n"));
+       dump_data(0, session_key, 16);
+
+#if 0
+       file_save("/tmp/ticket.dat", ticket->data, ticket->length);
+#endif
+
+       get_auth_data_from_tkt(auth_data, tkt);
+
+#if 0
+       if (tkt->enc_part2) {
+               file_save("/tmp/authdata.dat",
+                         tkt->enc_part2->authorization_data[0]->contents,
+                         tkt->enc_part2->authorization_data[0]->length);
+#endif
+
+       if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
+                                    principal))) {
+               DEBUG(3,("krb5_unparse_name failed (%s)\n", 
+                        error_message(ret)));
+               data_blob_free(auth_data);
+               data_blob_free(ap_rep);
+               krb5_auth_con_free(context, auth_context);
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       krb5_auth_con_free(context, auth_context);
+
+       return NT_STATUS_OK;
+}
+
+#endif /* HAVE_KRB5 */
diff --git a/source4/libads/krb5_setpw.c b/source4/libads/krb5_setpw.c
new file mode 100644 (file)
index 0000000..9d8fb8d
--- /dev/null
@@ -0,0 +1,701 @@
+/* 
+   Unix SMB/CIFS implementation.
+   krb5 set password implementation
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
+   
+   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"
+
+#ifdef HAVE_KRB5
+
+#define DEFAULT_KPASSWD_PORT   464
+#define KRB5_KPASSWD_VERS_CHANGEPW             1
+#define KRB5_KPASSWD_VERS_SETPW                        2
+#define KRB5_KPASSWD_VERS_SETPW_MS             0xff80
+#define KRB5_KPASSWD_ACCESSDENIED              5
+#define KRB5_KPASSWD_BAD_VERSION               6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED       7
+
+/* Those are defined by kerberos-set-passwd-02.txt and are probably 
+ * not supported by M$ implementation */
+#define KRB5_KPASSWD_POLICY_REJECT             8
+#define KRB5_KPASSWD_BAD_PRINCIPAL             9
+#define KRB5_KPASSWD_ETYPE_NOSUPP              10
+
+/* This implements kerberos password change protocol as specified in 
+ * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
+ * as well as microsoft version of the protocol 
+ * as specified in kerberos-set-passwd-00.txt
+ */
+static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
+{
+        char* princ_part1 = NULL;
+       char* princ_part2 = NULL;
+       char* realm = NULL;
+       char* c;
+       char* princ;
+
+       ASN1_DATA req;
+       DATA_BLOB ret;
+
+
+       princ = strdup(principal);
+
+       if ((c = strchr(princ, '/')) == NULL) {
+           c = princ; 
+       } else {
+           *c = '\0';
+           c++;
+           princ_part1 = princ;
+       }
+
+       princ_part2 = c;
+
+       if ((c = strchr(c, '@')) != NULL) {
+           *c = '\0';
+           c++;
+           realm = c;
+       }
+
+       memset(&req, 0, sizeof(req));
+       
+       asn1_push_tag(&req, ASN1_SEQUENCE(0));
+       asn1_push_tag(&req, ASN1_CONTEXT(0));
+       asn1_write_OctetString(&req, password, strlen(password));
+       asn1_pop_tag(&req);
+
+       asn1_push_tag(&req, ASN1_CONTEXT(1));
+       asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+       asn1_push_tag(&req, ASN1_CONTEXT(0));
+       asn1_write_Integer(&req, 1);
+       asn1_pop_tag(&req);
+
+       asn1_push_tag(&req, ASN1_CONTEXT(1));
+       asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+       if (princ_part1) 
+           asn1_write_GeneralString(&req, princ_part1);
+       
+       asn1_write_GeneralString(&req, princ_part2);
+       asn1_pop_tag(&req);
+       asn1_pop_tag(&req);
+       asn1_pop_tag(&req);
+       asn1_pop_tag(&req);
+
+       asn1_push_tag(&req, ASN1_CONTEXT(2));
+       asn1_write_GeneralString(&req, realm);
+       asn1_pop_tag(&req);
+       asn1_pop_tag(&req);
+
+       ret = data_blob(req.data, req.length);
+       asn1_free(&req);
+
+       free(princ);
+
+       return ret;
+}      
+
+static krb5_error_code build_kpasswd_request(uint16 pversion,
+                                          krb5_context context,
+                                          krb5_auth_context auth_context,
+                                          krb5_data *ap_req,
+                                          const char *princ,
+                                          const char *passwd,
+                                          krb5_data *packet)
+{
+       krb5_error_code ret;
+       krb5_data cipherpw;
+       krb5_data encoded_setpw;
+       krb5_replay_data replay;
+       char *p;
+       DATA_BLOB setpw;
+
+       ret = krb5_auth_con_setflags(context,
+                                    auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+       if (ret) {
+               DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
+                        error_message(ret)));
+               return ret;
+       }
+
+       /* handle protocol differences in chpw and setpw */
+       if (pversion  == KRB5_KPASSWD_VERS_CHANGEPW)
+               setpw = data_blob(passwd, strlen(passwd));
+       else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
+                pversion == KRB5_KPASSWD_VERS_SETPW_MS)
+               setpw = encode_krb5_setpw(princ, passwd);
+       else
+               return EINVAL;
+
+       encoded_setpw.data = setpw.data;
+       encoded_setpw.length = setpw.length;
+
+       ret = krb5_mk_priv(context, auth_context,
+                          &encoded_setpw, &cipherpw, &replay);
+       
+       data_blob_free(&setpw);         /*from 'encode_krb5_setpw(...)' */
+       
+       if (ret) {
+               DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret)));
+               return ret;
+       }
+
+       packet->data = (char *)malloc(ap_req->length + cipherpw.length + 6);
+       if (!packet->data)
+               return -1;
+
+       /* see the RFC for details */
+       p = ((char *)packet->data) + 2;
+       RSSVAL(p, 0, pversion);
+       p += 2;
+       RSSVAL(p, 0, ap_req->length);
+       p += 2;
+       memcpy(p, ap_req->data, ap_req->length);
+       p += ap_req->length;
+       memcpy(p, cipherpw.data, cipherpw.length);
+       p += cipherpw.length;
+       packet->length = PTR_DIFF(p,packet->data);
+       RSSVAL(packet->data, 0, packet->length);
+       
+       free(cipherpw.data);    /* from 'krb5_mk_priv(...)' */
+
+       return 0;
+}
+
+static krb5_error_code krb5_setpw_result_code_string(krb5_context context,
+                                                    int result_code,
+                                                    char **code_string)
+{
+   switch (result_code) {
+   case KRB5_KPASSWD_MALFORMED:
+      *code_string = "Malformed request error";
+      break;
+   case KRB5_KPASSWD_HARDERROR:
+      *code_string = "Server error";
+      break;
+   case KRB5_KPASSWD_AUTHERROR:
+      *code_string = "Authentication error";
+      break;
+   case KRB5_KPASSWD_SOFTERROR:
+      *code_string = "Password change rejected";
+      break;
+   case KRB5_KPASSWD_ACCESSDENIED:
+      *code_string = "Client does not have proper authorization";
+      break;
+   case KRB5_KPASSWD_BAD_VERSION:
+      *code_string = "Protocol version not supported";
+      break;
+   case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+      *code_string = "Authorization ticket must have initial flag set";
+      break;
+   case KRB5_KPASSWD_POLICY_REJECT:
+      *code_string = "Password rejected due to policy requirements";
+      break;
+   case KRB5_KPASSWD_BAD_PRINCIPAL:
+      *code_string = "Target principal does not exist";
+      break;
+   case KRB5_KPASSWD_ETYPE_NOSUPP:
+      *code_string = "Unsupported encryption type";
+      break;
+   default:
+      *code_string = "Password change failed";
+      break;
+   }
+
+   return(0);
+}
+
+static krb5_error_code parse_setpw_reply(krb5_context context, 
+                                        krb5_auth_context auth_context,
+                                        krb5_data *packet)
+{
+       krb5_data ap_rep;
+       char *p;
+       int vnum, ret, res_code;
+       krb5_data cipherresult;
+       krb5_data clearresult;
+       krb5_ap_rep_enc_part *ap_rep_enc;
+       krb5_replay_data replay;
+       
+       if (packet->length < 4) {
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+       
+       p = packet->data;
+       
+       if (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e) {
+               /* it's an error packet. We should parse it ... */
+               DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
+                        ((char *)packet->data)[0]));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+       
+       if (RSVAL(p, 0) != packet->length) {
+               DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
+                        RSVAL(p, 0), packet->length));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+
+       p += 2;
+
+       vnum = RSVAL(p, 0); p += 2;
+
+       /* FIXME: According to standard there is only one type of reply */      
+       if (vnum != KRB5_KPASSWD_VERS_SETPW && 
+           vnum != KRB5_KPASSWD_VERS_SETPW_MS && 
+           vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
+               DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
+               return KRB5KDC_ERR_BAD_PVNO;
+       }
+       
+       ap_rep.length = RSVAL(p, 0); p += 2;
+       
+       if (p + ap_rep.length >= (char *)packet->data + packet->length) {
+               DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+       
+       if (ap_rep.length == 0) {
+               DEBUG(1,("got unencrypted setpw result?!\n"));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+
+       /* verify ap_rep */
+       ap_rep.data = p;
+       p += ap_rep.length;
+       
+       ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
+       if (ret) {
+               DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret)));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+       
+       krb5_free_ap_rep_enc_part(context, ap_rep_enc);
+       
+       cipherresult.data = p;
+       cipherresult.length = ((char *)packet->data + packet->length) - p;
+               
+       ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
+                          &replay);
+       if (ret) {
+               DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret)));
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+
+       if (clearresult.length < 2) {
+               free(clearresult.data);
+               ret = KRB5KRB_AP_ERR_MODIFIED;
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+       
+       p = clearresult.data;
+       
+       res_code = RSVAL(p, 0);
+       
+       free(clearresult.data);
+
+       if ((res_code < KRB5_KPASSWD_SUCCESS) || 
+           (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
+               return KRB5KRB_AP_ERR_MODIFIED;
+       }
+
+       if(res_code == KRB5_KPASSWD_SUCCESS)
+                       return 0;
+       else {
+               char *errstr;
+               krb5_setpw_result_code_string(context, res_code, &errstr);
+               DEBUG(1, ("Error changing password: %s\n", errstr));
+
+               switch(res_code) {
+                       case KRB5_KPASSWD_ACCESSDENIED:
+                               return KRB5KDC_ERR_BADOPTION;
+                               break;
+                       case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+                               return KRB5KDC_ERR_BADOPTION;
+                               /* return KV5M_ALT_METHOD; MIT-only define */
+                               break;
+                       case KRB5_KPASSWD_ETYPE_NOSUPP:
+                               return KRB5KDC_ERR_ETYPE_NOSUPP;
+                               break;
+                       case KRB5_KPASSWD_BAD_PRINCIPAL:
+                               return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+                               break;
+                       case KRB5_KPASSWD_POLICY_REJECT:
+                               return KRB5KDC_ERR_POLICY;
+                               break;
+                       default:
+                               return KRB5KRB_ERR_GENERIC;
+                               break;
+               }
+       }
+}
+
+static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
+                                         const char *kdc_host,
+                                         uint16 pversion,
+                                         krb5_creds *credsp,
+                                         const char *princ,
+                                         const char *newpw)
+{
+       krb5_auth_context auth_context = NULL;
+       krb5_data ap_req, chpw_req, chpw_rep;
+       int ret, sock, addr_len;
+       struct sockaddr remote_addr, local_addr;
+       krb5_address local_kaddr, remote_kaddr;
+
+       ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
+                                  NULL, credsp, &ap_req);
+       if (ret) {
+               DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
+       if (sock == -1) {
+               int rc = errno;
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("failed to open kpasswd socket to %s (%s)\n", 
+                        kdc_host, strerror(errno)));
+               return ADS_ERROR_SYSTEM(rc);
+       }
+       
+       addr_len = sizeof(remote_addr);
+       getpeername(sock, &remote_addr, &addr_len);
+       addr_len = sizeof(local_addr);
+       getsockname(sock, &local_addr, &addr_len);
+       
+       setup_kaddr(&remote_kaddr, &remote_addr);
+       setup_kaddr(&local_kaddr, &local_addr);
+
+       ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
+       if (ret) {
+               close(sock);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
+                                 princ, newpw, &chpw_req);
+       if (ret) {
+               close(sock);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       if (write(sock, chpw_req.data, chpw_req.length) != chpw_req.length) {
+               close(sock);
+               free(chpw_req.data);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+               return ADS_ERROR_SYSTEM(errno);
+       }
+
+       free(chpw_req.data);
+
+       chpw_rep.length = 1500;
+       chpw_rep.data = (char *) malloc(chpw_rep.length);
+       if (!chpw_rep.data) {
+               close(sock);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+               errno = ENOMEM;
+               return ADS_ERROR_SYSTEM(errno);
+       }
+
+       ret = read(sock, chpw_rep.data, chpw_rep.length);
+       if (ret < 0) {
+               close(sock);
+               free(chpw_rep.data);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
+               return ADS_ERROR_SYSTEM(errno);
+       }
+
+       close(sock);
+       chpw_rep.length = ret;
+
+       ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
+       if (ret) {
+               free(chpw_rep.data);
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", 
+                        error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       ret = parse_setpw_reply(context, auth_context, &chpw_rep);
+       free(chpw_rep.data);
+
+       if (ret) {
+               free(ap_req.data);
+               krb5_auth_con_free(context, auth_context);
+               DEBUG(1,("parse_setpw_reply failed (%s)\n", 
+                        error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       free(ap_req.data);
+       krb5_auth_con_free(context, auth_context);
+
+       return ADS_SUCCESS;
+}
+
+ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw, 
+                            int time_offset)
+{
+
+       ADS_STATUS aret;
+       krb5_error_code ret;
+       krb5_context context;
+       krb5_principal principal;
+       char *princ_name;
+       char *realm;
+       krb5_creds creds, *credsp;
+       krb5_ccache ccache;
+
+       ret = krb5_init_context(&context);
+       if (ret) {
+               DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       if (time_offset != 0) {
+               krb5_set_real_time(context, time(NULL) + time_offset, 0);
+       }
+
+       ret = krb5_cc_default(context, &ccache);
+       if (ret) {
+               krb5_free_context(context);
+               DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       ZERO_STRUCT(creds);
+       
+       realm = strchr(princ, '@');
+       realm++;
+
+       asprintf(&princ_name, "kadmin/changepw@%s", realm);
+       ret = krb5_parse_name(context, princ_name, &creds.server);
+       if (ret) {
+                krb5_free_context(context);
+               DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       free(princ_name);
+
+       /* parse the principal we got as a function argument */
+       ret = krb5_parse_name(context, princ, &principal);
+       if (ret) {
+                krb5_free_context(context);
+               DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       krb5_princ_set_realm(context, creds.server,
+                            krb5_princ_realm(context, principal));
+       
+       ret = krb5_cc_get_principal(context, ccache, &creds.client);
+       if (ret) {
+               krb5_free_principal(context, principal);
+                krb5_free_context(context);
+               DEBUG(1,("Failed to get principal from ccache (%s)\n", 
+                        error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 
+       if (ret) {
+               krb5_free_principal(context, creds.client);
+               krb5_free_principal(context, principal);
+               krb5_free_context(context);
+               DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       /* we might have to call krb5_free_creds(...) from now on ... */
+
+       aret = do_krb5_kpasswd_request(context, kdc_host,
+                                      KRB5_KPASSWD_VERS_SETPW_MS,
+                                      credsp, princ, newpw);
+
+       krb5_free_creds(context, credsp);
+       krb5_free_principal(context, creds.client);
+       krb5_free_principal(context, creds.server);
+       krb5_free_principal(context, principal);
+       krb5_free_context(context);
+
+       return aret;
+}
+
+/*
+  we use a prompter to avoid a crash bug in the kerberos libs when 
+  dealing with empty passwords
+  this prompter is just a string copy ...
+*/
+static krb5_error_code 
+kerb_prompter(krb5_context ctx, void *data,
+              const char *name,
+              const char *banner,
+              int num_prompts,
+              krb5_prompt prompts[])
+{
+       if (num_prompts == 0) return 0;
+
+       memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+       if (prompts[0].reply->length > 0) {
+               if (data) {
+                       strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+               } else {
+                       prompts[0].reply->length = 0;
+               }
+       }
+       return 0;
+}
+
+ADS_STATUS krb5_chg_password(const char *kdc_host,
+                               const char *principal,
+                               const char *oldpw, 
+                               const char *newpw, 
+                               int time_offset)
+{
+    ADS_STATUS aret;
+    krb5_error_code ret;
+    krb5_context context;
+    krb5_principal princ;
+    krb5_get_init_creds_opt opts;
+    krb5_creds creds;
+    char *chpw_princ = NULL, *password;
+
+    ret = krb5_init_context(&context);
+    if (ret) {
+       DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    if ((ret = krb5_parse_name(context, principal,
+                                    &princ))) {
+       krb5_free_context(context);
+       DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    krb5_get_init_creds_opt_init(&opts);
+    krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
+    krb5_get_init_creds_opt_set_renew_life(&opts, 0);
+    krb5_get_init_creds_opt_set_forwardable(&opts, 0);
+    krb5_get_init_creds_opt_set_proxiable(&opts, 0);
+
+    /* We have to obtain an INITIAL changepw ticket for changing password */
+    asprintf(&chpw_princ, "kadmin/changepw@%s",
+                               (char *) krb5_princ_realm(context, princ));
+    password = strdup(oldpw);
+    ret = krb5_get_init_creds_password(context, &creds, princ, password,
+                                          kerb_prompter, NULL, 
+                                          0, chpw_princ, &opts);
+    SAFE_FREE(chpw_princ);
+    SAFE_FREE(password);
+
+    if (ret) {
+      if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+       DEBUG(1,("Password incorrect while getting initial ticket"));
+      else
+       DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
+
+       krb5_free_principal(context, princ);
+       krb5_free_context(context);
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    aret = do_krb5_kpasswd_request(context, kdc_host,
+                                  KRB5_KPASSWD_VERS_CHANGEPW,
+                                  &creds, principal, newpw);
+
+    krb5_free_principal(context, princ);
+    krb5_free_context(context);
+
+    return aret;
+}
+
+
+ADS_STATUS kerberos_set_password(const char *kpasswd_server, 
+                                const char *auth_principal, const char *auth_password,
+                                const char *target_principal, const char *new_password,
+                                int time_offset)
+{
+    int ret;
+
+    if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset))) {
+       DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    if (!strcmp(auth_principal, target_principal))
+       return krb5_chg_password(kpasswd_server, target_principal,
+                                   auth_password, new_password, time_offset);
+    else
+       return krb5_set_password(kpasswd_server, target_principal,
+                                new_password, time_offset);
+}
+
+
+/**
+ * Set the machine account password
+ * @param ads connection to ads server
+ * @param hostname machine whose password is being set
+ * @param password new password
+ * @return status of password change
+ **/
+ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
+                                   const char *hostname, 
+                                   const char *password)
+{
+       ADS_STATUS status;
+       char *host = strdup(hostname);
+       char *principal; 
+
+       strlower(host);
+
+       /*
+         we need to use the '$' form of the name here, as otherwise the
+         server might end up setting the password for a user instead
+        */
+       asprintf(&principal, "%s$@%s", host, ads->auth.realm);
+       
+       status = krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset);
+       
+       free(host);
+       free(principal);
+
+       return status;
+}
+
+
+
+#endif
diff --git a/source4/libads/ldap.c b/source4/libads/ldap.c
new file mode 100644 (file)
index 0000000..0bcd76c
--- /dev/null
@@ -0,0 +1,2098 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001
+   Copyright (C) Jim McDonough 2002
+   
+   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"
+
+#ifdef HAVE_LDAP
+
+/**
+ * @file ldap.c
+ * @brief basic ldap client-side routines for ads server communications
+ *
+ * The routines contained here should do the necessary ldap calls for
+ * ads setups.
+ * 
+ * Important note: attribute names passed into ads_ routines must
+ * already be in UTF-8 format.  We do not convert them because in almost
+ * all cases, they are just ascii (which is represented with the same
+ * codepoints in UTF-8).  This may have to change at some point
+ **/
+
+
+/*
+  try a connection to a given ldap server, returning True and setting the servers IP
+  in the ads struct if successful
+ */
+static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
+{
+       char *srv;
+
+       if (!server || !*server) {
+               return False;
+       }
+
+       DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
+
+       /* this copes with inet_ntoa brokenness */
+       srv = strdup(server);
+
+       ads->ld = ldap_open(srv, port);
+       if (!ads->ld) {
+               free(srv);
+               return False;
+       }
+       ads->ldap_port = port;
+       ads->ldap_ip = *interpret_addr2(srv);
+       free(srv);
+
+       return True;
+}
+
+/*
+  try a connection to a given ldap server, based on URL, returning True if successful
+ */
+static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
+{
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+       DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", 
+                ads->server.ldap_uri));
+
+       
+       if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
+               return True;
+       }
+       DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
+       
+#else 
+
+       DEBUG(1, ("no URL support in LDAP libs!\n"));
+#endif
+
+       return False;
+}
+
+/* used by the IP comparison function */
+struct ldap_ip {
+       struct in_addr ip;
+       unsigned port;
+};
+
+/* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
+static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
+{
+       return ip_compare(&ip1->ip, &ip2->ip);
+}
+
+/* try connecting to a ldap server via DNS */
+static BOOL ads_try_dns(ADS_STRUCT *ads)
+{
+       const char *c_realm;
+       const char *ptr;
+       char *realm;
+       char *list = NULL;
+       pstring tok;
+       struct ldap_ip *ip_list;
+       int count, i=0;
+
+       c_realm = ads->server.realm;
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_realm();
+       }
+       if (!c_realm || !*c_realm) {
+               c_realm = ads->server.workgroup;
+       }
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_workgroup();
+       }
+       if (!c_realm) {
+               return False;
+       }
+       realm = smb_xstrdup(c_realm);
+
+       DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
+       if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
+               SAFE_FREE(realm);
+               return False;
+       }
+
+       DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
+       SAFE_FREE(realm);
+
+       count = count_chars(list, ' ') + 1;
+       ip_list = malloc(count * sizeof(struct ldap_ip));
+       if (!ip_list) {
+               return False;
+       }
+
+       ptr = list;
+       while (next_token(&ptr, tok, " ", sizeof(tok))) {
+               unsigned port = LDAP_PORT;
+               char *p = strchr(tok, ':');
+               if (p) {
+                       *p = 0;
+                       port = atoi(p+1);
+               }
+               ip_list[i].ip = *interpret_addr2(tok);
+               ip_list[i].port = port;
+               if (!is_zero_ip(ip_list[i].ip)) {
+                       i++;
+               }
+       }
+       free(list);
+
+       count = i;
+
+       /* we sort the list of addresses by closeness to our interfaces. This
+          tries to prevent us using a DC on the other side of the country */
+       if (count > 1) {
+               qsort(ip_list, count, sizeof(struct ldap_ip), 
+                     QSORT_CAST ldap_ip_compare);      
+       }
+
+       for (i=0;i<count;i++) {
+               if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
+                       free(ip_list);
+                       return True;
+               }
+       }
+
+       SAFE_FREE(ip_list);
+       return False;
+}
+
+/* try connecting to a ldap server via netbios */
+static BOOL ads_try_netbios(ADS_STRUCT *ads)
+{
+       struct in_addr *ip_list, pdc_ip;
+       int count;
+       int i;
+       const char *workgroup = ads->server.workgroup;
+       BOOL list_ordered;
+
+       if (!workgroup) {
+               workgroup = lp_workgroup();
+       }
+
+       DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
+
+       /* try the PDC first */
+       if (get_pdc_ip(workgroup, &pdc_ip)) { 
+               DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
+                        inet_ntoa(pdc_ip)));
+               if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
+                       return True;
+       }
+
+       /* now any DC, including backups */
+       if (get_dc_list(workgroup, &ip_list, &count, &list_ordered)) { 
+               for (i=0;i<count;i++) {
+                       DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
+                                inet_ntoa(ip_list[i])));
+                       if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
+                               free(ip_list);
+                               return True;
+                       }
+               }
+               free(ip_list);
+       }
+
+       return False;
+}
+
+/**
+ * Connect to the LDAP server
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect(ADS_STRUCT *ads)
+{
+       int version = LDAP_VERSION3;
+       ADS_STATUS status;
+
+       ads->last_attempt = time(NULL);
+       ads->ld = NULL;
+
+       /* try with a URL based server */
+
+       if (ads->server.ldap_uri &&
+           ads_try_connect_uri(ads)) {
+               goto got_connection;
+       }
+
+       /* try with a user specified server */
+       if (ads->server.ldap_server && 
+           ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
+               goto got_connection;
+       }
+
+       /* try with a smb.conf ads server setting if we are connecting
+           to the primary workgroup or realm */
+       if (!ads->server.foreign &&
+           ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
+               goto got_connection;
+       }
+
+       /* try via DNS */
+       if (ads_try_dns(ads)) {
+               goto got_connection;
+       }
+
+       /* try via netbios lookups */
+       if (!lp_disable_netbios() && ads_try_netbios(ads)) {
+               goto got_connection;
+       }
+
+       return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
+
+got_connection:
+       DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
+
+       status = ads_server_info(ads);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1,("Failed to get ldap server info\n"));
+               return status;
+       }
+
+       ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+       if (!ads->auth.user_name) {
+               /* by default use the machine account */
+               fstring myname;
+               fstrcpy(myname, lp_netbios_name());
+               strlower(myname);
+               asprintf(&ads->auth.user_name, "HOST/%s", myname);
+       }
+
+       if (!ads->auth.realm) {
+               ads->auth.realm = strdup(ads->config.realm);
+       }
+
+       if (!ads->auth.kdc_server) {
+               ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
+       }
+
+#if KRB5_DNS_HACK
+       /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
+          to MIT kerberos to work (tridge) */
+       {
+               char *env;
+               asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
+               setenv(env, ads->auth.kdc_server, 1);
+               free(env);
+       }
+#endif
+
+       if (ads->auth.flags & ADS_AUTH_NO_BIND) {
+               return ADS_SUCCESS;
+       }
+
+       if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
+               return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
+       }
+
+       if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
+               return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
+       }
+
+       return ads_sasl_bind(ads);
+}
+
+/*
+  Duplicate a struct berval into talloc'ed memory
+ */
+static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
+{
+       struct berval *value;
+
+       if (!in_val) return NULL;
+
+       value = talloc_zero(ctx, sizeof(struct berval));
+       if (value == NULL)
+               return NULL;
+       if (in_val->bv_len == 0) return value;
+
+       value->bv_len = in_val->bv_len;
+       value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
+       return value;
+}
+
+/*
+  Make a values list out of an array of (struct berval *)
+ */
+static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
+                                     const struct berval **in_vals)
+{
+       struct berval **values;
+       int i;
+       
+       if (!in_vals) return NULL;
+       for (i=0; in_vals[i]; i++); /* count values */
+       values = (struct berval **) talloc_zero(ctx, 
+                                               (i+1)*sizeof(struct berval *));
+       if (!values) return NULL;
+
+       for (i=0; in_vals[i]; i++) {
+               values[i] = dup_berval(ctx, in_vals[i]);
+       }
+       return values;
+}
+
+/*
+  UTF8-encode a values list out of an array of (char *)
+ */
+static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+       char **values;
+       int i;
+       
+       if (!in_vals) return NULL;
+       for (i=0; in_vals[i]; i++); /* count values */
+       values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
+       if (!values) return NULL;
+
+       for (i=0; in_vals[i]; i++) {
+               push_utf8_talloc(ctx, &values[i], in_vals[i]);
+       }
+       return values;
+}
+
+/*
+  Pull a (char *) array out of a UTF8-encoded values list
+ */
+static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+       char **values;
+       int i;
+       
+       if (!in_vals) return NULL;
+       for (i=0; in_vals[i]; i++); /* count values */
+       values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
+       if (!values) return NULL;
+
+       for (i=0; in_vals[i]; i++) {
+               pull_utf8_talloc(ctx, &values[i], in_vals[i]);
+       }
+       return values;
+}
+
+/**
+ * Do a search with paged results.  cookie must be null on the first
+ *  call, and then returned on each subsequent call.  It will be null
+ *  again when the entire search is complete 
+ * @param ads connection to ads server 
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
+ * @param exp Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in utf8 or ascii
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param count Number of entries retrieved on this page
+ * @param cookie The paged results cookie to be returned on subsequent calls
+ * @return status of search
+ **/
+ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
+                              int scope, const char *exp,
+                              const char **attrs, void **res, 
+                              int *count, void **cookie)
+{
+       int rc, i, version;
+       char *utf8_exp, *utf8_path, **search_attrs;
+       LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; 
+       BerElement *cookie_be = NULL;
+       struct berval *cookie_bv= NULL;
+       TALLOC_CTX *ctx;
+
+       *res = NULL;
+
+       if (!(ctx = talloc_init("ads_do_paged_search")))
+               return ADS_ERROR(LDAP_NO_MEMORY);
+
+       /* 0 means the conversion worked but the result was empty 
+          so we only fail if it's -1.  In any case, it always 
+          at least nulls out the dest */
+       if ((push_utf8_talloc(ctx, &utf8_exp, exp) == (size_t)-1) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+               rc = LDAP_NO_MEMORY;
+               goto done;
+       }
+
+       if (!attrs || !(*attrs))
+               search_attrs = NULL;
+       else {
+               /* This would be the utf8-encoded version...*/
+               /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
+               if (!(str_list_copy(&search_attrs, attrs))) {
+                       rc = LDAP_NO_MEMORY;
+                       goto done;
+               }
+       }
+               
+               
+       /* Paged results only available on ldap v3 or later */
+       ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+       if (version < LDAP_VERSION3) {
+               rc =  LDAP_NOT_SUPPORTED;
+               goto done;
+       }
+
+       cookie_be = ber_alloc_t(LBER_USE_DER);
+       if (cookie && *cookie) {
+               ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
+               ber_bvfree(*cookie); /* don't need it from last time */
+               *cookie = NULL;
+       } else {
+               ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
+       }
+       ber_flatten(cookie_be, &cookie_bv);
+       PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
+       PagedResults.ldctl_iscritical = (char) 1;
+       PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
+       PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
+
+       NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
+       NoReferrals.ldctl_iscritical = (char) 0;
+       NoReferrals.ldctl_value.bv_len = 0;
+       NoReferrals.ldctl_value.bv_val = "";
+
+
+       controls[0] = &NoReferrals;
+       controls[1] = &PagedResults;
+       controls[2] = NULL;
+
+       *res = NULL;
+
+       /* we need to disable referrals as the openldap libs don't
+          handle them and paged results at the same time.  Using them
+          together results in the result record containing the server 
+          page control being removed from the result list (tridge/jmcd) 
+       
+          leaving this in despite the control that says don't generate
+          referrals, in case the server doesn't support it (jmcd)
+       */
+       ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp, 
+                              search_attrs, 0, controls,
+                              NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+       ber_free(cookie_be, 1);
+       ber_bvfree(cookie_bv);
+
+       if (rc) {
+               DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
+               goto done;
+       }
+
+       rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
+                                       NULL, &rcontrols,  0);
+
+       if (!rcontrols) {
+               goto done;
+       }
+
+       for (i=0; rcontrols[i]; i++) {
+               if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
+                       cookie_be = ber_init(&rcontrols[i]->ldctl_value);
+                       ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
+                                 &cookie_bv);
+                       /* the berval is the cookie, but must be freed when
+                          it is all done */
+                       if (cookie_bv->bv_len) /* still more to do */
+                               *cookie=ber_bvdup(cookie_bv);
+                       else
+                               *cookie=NULL;
+                       ber_bvfree(cookie_bv);
+                       ber_free(cookie_be, 1);
+                       break;
+               }
+       }
+       ldap_controls_free(rcontrols);
+
+done:
+       talloc_destroy(ctx);
+       /* if/when we decide to utf8-encode attrs, take out this next line */
+       str_list_free(&search_attrs);
+
+       return ADS_ERROR(rc);
+}
+
+
+/**
+ * Get all results for a search.  This uses ads_do_paged_search() to return 
+ * all entries in a large search.
+ * @param ads connection to ads server 
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
+ * @param exp Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
+                            int scope, const char *exp,
+                            const char **attrs, void **res)
+{
+       void *cookie = NULL;
+       int count = 0;
+       ADS_STATUS status;
+
+       status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
+                                    &count, &cookie);
+
+       if (!ADS_ERR_OK(status)) return status;
+
+       while (cookie) {
+               void *res2 = NULL;
+               ADS_STATUS status2;
+               LDAPMessage *msg, *next;
+
+               status2 = ads_do_paged_search(ads, bind_path, scope, exp, 
+                                             attrs, &res2, &count, &cookie);
+
+               if (!ADS_ERR_OK(status2)) break;
+
+               /* this relies on the way that ldap_add_result_entry() works internally. I hope
+                  that this works on all ldap libs, but I have only tested with openldap */
+               for (msg = ads_first_entry(ads, res2); msg; msg = next) {
+                       next = ads_next_entry(ads, msg);
+                       ldap_add_result_entry((LDAPMessage **)res, msg);
+               }
+               /* note that we do not free res2, as the memory is now
+                   part of the main returned list */
+       }
+
+       return status;
+}
+
+/**
+ * Run a function on all results for a search.  Uses ads_do_paged_search() and
+ *  runs the function as each page is returned, using ads_process_results()
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
+ * @param exp Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
+ * @param fn Function which takes attr name, values list, and data_area
+ * @param data_area Pointer which is passed to function on each call
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
+                               int scope, const char *exp, const char **attrs,
+                               BOOL(*fn)(char *, void **, void *), 
+                               void *data_area)
+{
+       void *cookie = NULL;
+       int count = 0;
+       ADS_STATUS status;
+       void *res;
+
+       status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
+                                    &count, &cookie);
+
+       if (!ADS_ERR_OK(status)) return status;
+
+       ads_process_results(ads, res, fn, data_area);
+       ads_msgfree(ads, res);
+
+       while (cookie) {
+               status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
+                                            &res, &count, &cookie);
+
+               if (!ADS_ERR_OK(status)) break;
+               
+               ads_process_results(ads, res, fn, data_area);
+               ads_msgfree(ads, res);
+       }
+
+       return status;
+}
+
+/**
+ * Do a search with a timeout.
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
+ * @param exp Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
+                        const char *exp,
+                        const char **attrs, void **res)
+{
+       struct timeval timeout;
+       int rc;
+       char *utf8_exp, *utf8_path, **search_attrs = NULL;
+       TALLOC_CTX *ctx;
+
+       if (!(ctx = talloc_init("ads_do_search"))) {
+               DEBUG(1,("ads_do_search: talloc_init() failed!"));
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       /* 0 means the conversion worked but the result was empty 
+          so we only fail if it's negative.  In any case, it always 
+          at least nulls out the dest */
+       if ((push_utf8_talloc(ctx, &utf8_exp, exp) == (size_t)-1) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+               DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
+               rc = LDAP_NO_MEMORY;
+               goto done;
+       }
+
+       if (!attrs || !(*attrs))
+               search_attrs = NULL;
+       else {
+               /* This would be the utf8-encoded version...*/
+               /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
+               if (!(str_list_copy(&search_attrs, attrs)))
+               {
+                       DEBUG(1,("ads_do_search: str_list_copy() failed!"));
+                       rc = LDAP_NO_MEMORY;
+                       goto done;
+               }
+       }
+
+       timeout.tv_sec = ADS_SEARCH_TIMEOUT;
+       timeout.tv_usec = 0;
+       *res = NULL;
+
+       /* see the note in ads_do_paged_search - we *must* disable referrals */
+       ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
+                              search_attrs, 0, NULL, NULL, 
+                              &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+       if (rc == LDAP_SIZELIMIT_EXCEEDED) {
+               DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
+               rc = 0;
+       }
+
+ done:
+       talloc_destroy(ctx);
+       /* if/when we decide to utf8-encode attrs, take out this next line */
+       str_list_free(&search_attrs);
+       return ADS_ERROR(rc);
+}
+/**
+ * Do a general ADS search
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param exp Search expression
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
+                     const char *exp, 
+                     const char **attrs)
+{
+       return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
+                            exp, attrs, res);
+}
+
+/**
+ * Do a search on a specific DistinguishedName
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param dn DistinguishName to search
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res, 
+                        const char *dn, 
+                        const char **attrs)
+{
+       return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
+}
+
+/**
+ * Free up memory from a ads_search
+ * @param ads connection to ads server
+ * @param msg Search results to free
+ **/
+void ads_msgfree(ADS_STRUCT *ads, void *msg)
+{
+       if (!msg) return;
+       ldap_msgfree(msg);
+}
+
+/**
+ * Free up memory from various ads requests
+ * @param ads connection to ads server
+ * @param mem Area to free
+ **/
+void ads_memfree(ADS_STRUCT *ads, void *mem)
+{
+       SAFE_FREE(mem);
+}
+
+/**
+ * Get a dn from search results
+ * @param ads connection to ads server
+ * @param res Search results
+ * @return dn string
+ **/
+char *ads_get_dn(ADS_STRUCT *ads, void *res)
+{
+       char *utf8_dn, *unix_dn;
+
+       utf8_dn = ldap_get_dn(ads->ld, res);
+       pull_utf8_allocate((void **) &unix_dn, utf8_dn);
+       ldap_memfree(utf8_dn);
+       return unix_dn;
+}
+
+/**
+ * Find a machine account given a hostname
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param host Hostname to search for
+ * @return status of search
+ **/
+ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
+{
+       ADS_STATUS status;
+       char *exp;
+       const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+       /* the easiest way to find a machine account anywhere in the tree
+          is to look for hostname$ */
+       if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) {
+               DEBUG(1, ("asprintf failed!\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+       
+       status = ads_search(ads, res, exp, attrs);
+       free(exp);
+       return status;
+}
+
+/**
+ * Initialize a list of mods to be used in a modify request
+ * @param ctx An initialized TALLOC_CTX
+ * @return allocated ADS_MODLIST
+ **/
+ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
+{
+#define ADS_MODLIST_ALLOC_SIZE 10
+       LDAPMod **mods;
+       
+       if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) * 
+                                            (ADS_MODLIST_ALLOC_SIZE + 1))))
+               /* -1 is safety to make sure we don't go over the end.
+                  need to reset it to NULL before doing ldap modify */
+               mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+       
+       return mods;
+}
+
+
+/*
+  add an attribute to the list, with values list already constructed
+*/
+static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                                 int mod_op, const char *name, 
+                                 const void **invals)
+{
+       int curmod;
+       LDAPMod **modlist = (LDAPMod **) *mods;
+       struct berval **ber_values = NULL;
+       char **char_values = NULL;
+
+       if (!invals) {
+               mod_op = LDAP_MOD_DELETE;
+       } else {
+               if (mod_op & LDAP_MOD_BVALUES)
+                       ber_values = ads_dup_values(ctx, 
+                                               (const struct berval **)invals);
+               else
+                       char_values = ads_push_strvals(ctx, 
+                                                 (const char **) invals);
+       }
+
+       /* find the first empty slot */
+       for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
+            curmod++);
+       if (modlist[curmod] == (LDAPMod *) -1) {
+               if (!(modlist = talloc_realloc(ctx, modlist, 
+                       (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               memset(&modlist[curmod], 0, 
+                      ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
+               modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+               *mods = modlist;
+       }
+               
+       if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       modlist[curmod]->mod_type = talloc_strdup(ctx, name);
+       if (mod_op & LDAP_MOD_BVALUES) {
+               modlist[curmod]->mod_bvalues = ber_values;
+       } else if (mod_op & LDAP_MOD_DELETE) {
+               modlist[curmod]->mod_values = NULL;
+       } else {
+               modlist[curmod]->mod_values = char_values;
+       }
+
+       modlist[curmod]->mod_op = mod_op;
+       return ADS_ERROR(LDAP_SUCCESS);
+}
+
+/**
+ * Add a single string value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                      const char *name, const char *val)
+{
+       const char *values[2];
+
+       values[0] = val;
+       values[1] = NULL;
+
+       if (!val)
+               return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+       return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, 
+                              (const void **) values);
+}
+
+/**
+ * Add an array of string values to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param vals The array of string values to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+                          const char *name, const char **vals)
+{
+       if (!vals)
+               return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+       return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
+                              name, (const void **) vals);
+}
+
+/**
+ * Add a single ber-encoded value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                             const char *name, const struct berval *val)
+{
+       const struct berval *values[2];
+
+       values[0] = val;
+       values[1] = NULL;
+       if (!val)
+               return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+       return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
+                              name, (const void **) values);
+}
+
+/**
+ * Perform an ldap modify
+ * @param ads connection to ads server
+ * @param mod_dn DistinguishedName to modify
+ * @param mods list of modifications to perform
+ * @return status of modify
+ **/
+ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
+{
+       int ret,i;
+       char *utf8_dn = NULL;
+       /* 
+          this control is needed to modify that contains a currently 
+          non-existent attribute (but allowable for the object) to run
+       */
+       LDAPControl PermitModify = {
+               ADS_PERMIT_MODIFY_OID,
+               {0, NULL},
+               (char) 1};
+       LDAPControl *controls[2];
+
+       controls[0] = &PermitModify;
+       controls[1] = NULL;
+
+       if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       /* find the end of the list, marked by NULL or -1 */
+       for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+       /* make sure the end of the list is NULL */
+       mods[i] = NULL;
+       ret = ldap_modify_ext_s(ads->ld, utf8_dn,
+                               (LDAPMod **) mods, controls, NULL);
+       SAFE_FREE(utf8_dn);
+       return ADS_ERROR(ret);
+}
+
+/**
+ * Perform an ldap add
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to add
+ * @param mods list of attributes and values for DN
+ * @return status of add
+ **/
+ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
+{
+       int ret, i;
+       char *utf8_dn = NULL;
+
+       if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
+               DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+       
+       /* find the end of the list, marked by NULL or -1 */
+       for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+       /* make sure the end of the list is NULL */
+       mods[i] = NULL;
+
+       ret = ldap_add_s(ads->ld, utf8_dn, mods);
+       SAFE_FREE(utf8_dn);
+       return ADS_ERROR(ret);
+}
+
+/**
+ * Delete a DistinguishedName
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to delete
+ * @return status of delete
+ **/
+ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
+{
+       int ret;
+       char *utf8_dn = NULL;
+       if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
+               DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+       
+       ret = ldap_delete(ads->ld, utf8_dn);
+       return ADS_ERROR(ret);
+}
+
+/**
+ * Build an org unit string
+ *  if org unit is Computers or blank then assume a container, otherwise
+ *  assume a \ separated list of organisational units
+ * @param org_unit Organizational unit
+ * @return org unit string - caller must free
+ **/
+char *ads_ou_string(const char *org_unit)
+{      
+       if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
+               return strdup("cn=Computers");
+       }
+
+       return ads_build_path(org_unit, "\\/", "ou=", 1);
+}
+
+
+
+/*
+  add a machine account to the ADS server
+*/
+static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
+                                      const char *org_unit)
+{
+       ADS_STATUS ret, status;
+       char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
+       char *ou_str;
+       TALLOC_CTX *ctx;
+       ADS_MODLIST mods;
+       const char *objectClass[] = {"top", "person", "organizationalPerson",
+                                    "user", "computer", NULL};
+       const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
+       char *psp, *psp2;
+       unsigned acct_control;
+
+       if (!(ctx = talloc_init("machine_account")))
+               return ADS_ERROR(LDAP_NO_MEMORY);
+
+       ret = ADS_ERROR(LDAP_NO_MEMORY);
+
+       if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
+               goto done;
+       if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
+               goto done;
+       ou_str = ads_ou_string(org_unit);
+       if (!ou_str) {
+               DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
+               goto done;
+       }
+       new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
+                                ads->config.bind_path);
+       servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
+       psp = talloc_asprintf(ctx, "HOST/%s.%s", 
+                             hostname, 
+                             ads->config.realm);
+       strlower(&psp[5]);
+       servicePrincipalName[1] = psp;
+       servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
+       psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
+                              hostname, 
+                              ads->config.realm);
+       strlower(&psp2[5]);
+       servicePrincipalName[3] = psp2;
+
+       free(ou_str);
+       if (!new_dn)
+               goto done;
+
+       if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
+               goto done;
+
+       acct_control = UF_WORKSTATION_TRUST_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+#ifndef ENCTYPE_ARCFOUR_HMAC
+       acct_control |= UF_USE_DES_KEY_ONLY;
+#endif
+       if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
+               goto done;
+
+       if (!(mods = ads_init_mods(ctx)))
+               goto done;
+       
+       ads_mod_str(ctx, &mods, "cn", hostname);
+       ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
+       ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+       ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
+       ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+       ads_mod_str(ctx, &mods, "dNSHostName", hostname);
+       ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+       ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
+       ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
+
+       ret = ads_gen_add(ads, new_dn, mods);
+
+       if (!ADS_ERR_OK(ret))
+               goto done;
+
+       /* Do not fail if we can't set security descriptor
+        * it shouldn't be mandatory and probably we just 
+        * don't have enough rights to do it.
+        */
+       status = ads_set_machine_sd(ads, hostname, new_dn);
+
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
+                               ads_errstr(status)));
+       }
+done:
+       talloc_destroy(ctx);
+       return ret;
+}
+
+/*
+  dump a binary result from ldap
+*/
+static void dump_binary(const char *field, struct berval **values)
+{
+       int i, j;
+       for (i=0; values[i]; i++) {
+               printf("%s: ", field);
+               for (j=0; j<values[i]->bv_len; j++) {
+                       printf("%02X", (unsigned char)values[i]->bv_val[j]);
+               }
+               printf("\n");
+       }
+}
+
+struct uuid {
+        uint32   i1;
+        uint16   i2;
+        uint16   i3;
+        uint8    s[8];
+};
+
+static void dump_guid(const char *field, struct berval **values)
+{
+       int i;
+       GUID guid;
+       TALLOC_CTX *mem_ctx;
+       mem_ctx = talloc_init("dump_guid");
+       if (!mem_ctx) return;
+       for (i=0; values[i]; i++) {
+               memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
+               printf("%s: %s\n", field, uuid_string(mem_ctx, guid));
+       }
+       talloc_destroy(mem_ctx);
+}
+
+/*
+  dump a sid result from ldap
+*/
+static void dump_sid(const char *field, struct berval **values)
+{
+       int i;
+       for (i=0; values[i]; i++) {
+               DOM_SID sid;
+               sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
+               printf("%s: %s\n", field, sid_string_static(&sid));
+       }
+}
+
+/*
+  dump ntSecurityDescriptor
+*/
+static void dump_sd(const char *filed, struct berval **values)
+{
+       prs_struct ps;
+       
+       SEC_DESC   *psd = 0;
+       TALLOC_CTX *ctx = 0;
+
+       if (!(ctx = talloc_init("sec_io_desc")))
+               return;
+
+       /* prepare data */
+       prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
+       prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+       prs_set_offset(&ps,0);
+
+       /* parse secdesc */
+       if (!sec_io_desc("sd", &psd, &ps, 1)) {
+               prs_mem_free(&ps);
+               talloc_destroy(ctx);
+               return;
+       }
+       if (psd) ads_disp_sd(psd);
+
+       prs_mem_free(&ps);
+       talloc_destroy(ctx);
+}
+
+/*
+  dump a string result from ldap
+*/
+static void dump_string(const char *field, char **values)
+{
+       int i;
+       for (i=0; values[i]; i++) {
+               printf("%s: %s\n", field, values[i]);
+       }
+}
+
+/*
+  dump a field from LDAP on stdout
+  used for debugging
+*/
+
+static BOOL ads_dump_field(char *field, void **values, void *data_area)
+{
+       const struct {
+               const char *name;
+               BOOL string;
+               void (*handler)(const char *, struct berval **);
+       } handlers[] = {
+               {"objectGUID", False, dump_guid},
+               {"nTSecurityDescriptor", False, dump_sd},
+               {"dnsRecord", False, dump_binary},
+               {"objectSid", False, dump_sid},
+               {"tokenGroups", False, dump_sid},
+               {NULL, True, NULL}
+       };
+       int i;
+
+       if (!field) { /* must be end of an entry */
+               printf("\n");
+               return False;
+       }
+
+       for (i=0; handlers[i].name; i++) {
+               if (StrCaseCmp(handlers[i].name, field) == 0) {
+                       if (!values) /* first time, indicate string or not */
+                               return handlers[i].string;
+                       handlers[i].handler(field, (struct berval **) values);
+                       break;
+               }
+       }
+       if (!handlers[i].name) {
+               if (!values) /* first time, indicate string conversion */
+                       return True;
+               dump_string(field, (char **)values);
+       }
+       return False;
+}
+
+/**
+ * Dump a result from LDAP on stdout
+ *  used for debugging
+ * @param ads connection to ads server
+ * @param res Results to dump
+ **/
+
+void ads_dump(ADS_STRUCT *ads, void *res)
+{
+       ads_process_results(ads, res, ads_dump_field, NULL);
+}
+
+/**
+ * Walk through results, calling a function for each entry found.
+ *  The function receives a field name, a berval * array of values,
+ *  and a data area passed through from the start.  The function is
+ *  called once with null for field and values at the end of each
+ *  entry.
+ * @param ads connection to ads server
+ * @param res Results to process
+ * @param fn Function for processing each result
+ * @param data_area user-defined area to pass to function
+ **/
+void ads_process_results(ADS_STRUCT *ads, void *res,
+                        BOOL(*fn)(char *, void **, void *),
+                        void *data_area)
+{
+       void *msg;
+       TALLOC_CTX *ctx;
+
+       if (!(ctx = talloc_init("ads_process_results")))
+               return;
+
+       for (msg = ads_first_entry(ads, res); msg; 
+            msg = ads_next_entry(ads, msg)) {
+               char *utf8_field;
+               BerElement *b;
+       
+               for (utf8_field=ldap_first_attribute(ads->ld,
+                                                    (LDAPMessage *)msg,&b); 
+                    utf8_field;
+                    utf8_field=ldap_next_attribute(ads->ld,
+                                                   (LDAPMessage *)msg,b)) {
+                       struct berval **ber_vals;
+                       char **str_vals, **utf8_vals;
+                       char *field;
+                       BOOL string; 
+
+                       pull_utf8_talloc(ctx, &field, utf8_field);
+                       string = fn(field, NULL, data_area);
+
+                       if (string) {
+                               utf8_vals = ldap_get_values(ads->ld,
+                                                (LDAPMessage *)msg, field);
+                               str_vals = ads_pull_strvals(ctx, 
+                                                 (const char **) utf8_vals);
+                               fn(field, (void **) str_vals, data_area);
+                               ldap_value_free(utf8_vals);
+                       } else {
+                               ber_vals = ldap_get_values_len(ads->ld, 
+                                                (LDAPMessage *)msg, field);
+                               fn(field, (void **) ber_vals, data_area);
+
+                               ldap_value_free_len(ber_vals);
+                       }
+                       ldap_memfree(utf8_field);
+               }
+               ber_free(b, 0);
+               talloc_destroy_pool(ctx);
+               fn(NULL, NULL, data_area); /* completed an entry */
+
+       }
+       talloc_destroy(ctx);
+}
+
+/**
+ * count how many replies are in a LDAPMessage
+ * @param ads connection to ads server
+ * @param res Results to count
+ * @return number of replies
+ **/
+int ads_count_replies(ADS_STRUCT *ads, void *res)
+{
+       return ldap_count_entries(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * Join a machine to a realm
+ *  Creates the machine account and sets the machine password
+ * @param ads connection to ads server
+ * @param hostname name of host to add
+ * @param org_unit Organizational unit to place machine in
+ * @return status of join
+ **/
+ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
+{
+       ADS_STATUS status;
+       LDAPMessage *res;
+       char *host;
+
+       /* hostname must be lowercase */
+       host = strdup(hostname);
+       strlower(host);
+
+       status = ads_find_machine_acct(ads, (void **)&res, host);
+       if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+               DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
+               status = ads_leave_realm(ads, host);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
+                                 host, ads->config.realm));
+                       return status;
+               }
+       }
+
+       status = ads_add_machine_acct(ads, host, org_unit);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
+               return status;
+       }
+
+       status = ads_find_machine_acct(ads, (void **)&res, host);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("Host account test failed\n"));
+               return status;
+       }
+
+       free(host);
+
+       return status;
+}
+
+/**
+ * Delete a machine from the realm
+ * @param ads connection to ads server
+ * @param hostname Machine to remove
+ * @return status of delete
+ **/
+ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
+{
+       ADS_STATUS status;
+       void *res;
+       char *hostnameDN, *host; 
+       int rc;
+
+       /* hostname must be lowercase */
+       host = strdup(hostname);
+       strlower(host);
+
+       status = ads_find_machine_acct(ads, &res, host);
+       if (!ADS_ERR_OK(status)) {
+           DEBUG(0, ("Host account for %s does not exist.\n", host));
+           return status;
+       }
+
+       hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
+       rc = ldap_delete_s(ads->ld, hostnameDN);
+       ads_memfree(ads, hostnameDN);
+       if (rc != LDAP_SUCCESS) {
+               return ADS_ERROR(rc);
+       }
+
+       status = ads_find_machine_acct(ads, &res, host);
+       if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+               DEBUG(0, ("Failed to remove host account.\n"));
+               return status;
+       }
+
+       free(host);
+
+       return status;
+}
+
+/**
+ * add machine account to existing security descriptor 
+ * @param ads connection to ads server
+ * @param hostname machine to add
+ * @param dn DN of security descriptor
+ * @return status
+ **/
+ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
+{
+       const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
+       char           *exp     = 0;
+       size_t          sd_size = 0;
+       struct berval   bval = {0, NULL};
+       prs_struct      ps_wire;
+       char           *escaped_hostname = escape_ldap_string_alloc(hostname);
+
+       LDAPMessage *res  = 0;
+       LDAPMessage *msg  = 0;
+       ADS_MODLIST  mods = 0;
+
+       NTSTATUS    status;
+       ADS_STATUS  ret;
+       DOM_SID     sid;
+       SEC_DESC   *psd = NULL;
+       TALLOC_CTX *ctx = NULL; 
+
+       /* Avoid segmentation fault in prs_mem_free if
+        * we have to bail out before prs_init */
+       ps_wire.is_dynamic = False;
+
+       if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
+
+       ret = ADS_ERROR(LDAP_SUCCESS);
+
+       if (!escaped_hostname) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       if (asprintf(&exp, "(samAccountName=%s$)", escaped_hostname) == -1) {
+               DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
+               SAFE_FREE(escaped_hostname);
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       SAFE_FREE(escaped_hostname);
+
+       ret = ads_search(ads, (void *) &res, exp, attrs);
+
+       if (!ADS_ERR_OK(ret)) return ret;
+
+       if ( !(msg = ads_first_entry(ads, res) )) {
+               ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               goto ads_set_sd_error;
+       }
+
+       if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
+               ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               goto ads_set_sd_error;
+       }
+
+       if (!(ctx = talloc_init("sec_io_desc"))) {
+               ret =  ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
+
+       if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
+               ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               goto ads_set_sd_error;
+       }
+
+       status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               ret = ADS_ERROR_NT(status);
+               goto ads_set_sd_error;
+       }
+
+       if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
+               ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
+
+#if 0
+       file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
+#endif
+       if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
+
+       bval.bv_len = prs_offset(&ps_wire);
+       bval.bv_val = talloc(ctx, bval.bv_len);
+       if (!bval.bv_val) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
+
+       prs_set_offset(&ps_wire, 0);
+
+       if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;          
+       }
+
+       ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
+       if (ADS_ERR_OK(ret)) {
+               ret = ads_gen_mod(ads, dn, mods);
+       }
+
+ads_set_sd_error:
+       ads_msgfree(ads, res);
+       prs_mem_free(&ps_wire);
+       talloc_destroy(ctx);
+       return ret;
+}
+
+/**
+ * pull the first entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return first entry from result
+ **/
+void *ads_first_entry(ADS_STRUCT *ads, void *res)
+{
+       return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull the next entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return next entry from result
+ **/
+void *ads_next_entry(ADS_STRUCT *ads, void *res)
+{
+       return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull a single string from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result string in talloc context
+ **/
+char *ads_pull_string(ADS_STRUCT *ads, 
+                     TALLOC_CTX *mem_ctx, void *msg, const char *field)
+{
+       char **values;
+       char *ret = NULL;
+       char *ux_string;
+       size_t rc;
+
+       values = ldap_get_values(ads->ld, msg, field);
+       if (!values)
+               return NULL;
+       
+       if (values[0]) {
+               rc = pull_utf8_talloc(mem_ctx, &ux_string, 
+                                     values[0]);
+               if (rc != (size_t)-1)
+                       ret = ux_string;
+               
+       }
+       ldap_value_free(values);
+       return ret;
+}
+
+/**
+ * pull an array of strings from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings(ADS_STRUCT *ads, 
+                      TALLOC_CTX *mem_ctx, void *msg, const char *field)
+{
+       char **values;
+       char **ret = NULL;
+       int i, n;
+
+       values = ldap_get_values(ads->ld, msg, field);
+       if (!values)
+               return NULL;
+
+       for (i=0;values[i];i++)
+               /* noop */ ;
+       n = i;
+
+       ret = talloc(mem_ctx, sizeof(char *) * (n+1));
+       if (!ret) {
+               ldap_value_free(values);
+               return NULL;
+       }
+
+       for (i=0;i<n;i++) {
+               if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+                       ldap_value_free(values);
+                       return NULL;
+               }
+       }
+       ret[i] = NULL;
+
+       ldap_value_free(values);
+       return ret;
+}
+
+
+/**
+ * pull a single uint32 from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param v Pointer to int to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_uint32(ADS_STRUCT *ads, 
+                    void *msg, const char *field, uint32 *v)
+{
+       char **values;
+
+       values = ldap_get_values(ads->ld, msg, field);
+       if (!values)
+               return False;
+       if (!values[0]) {
+               ldap_value_free(values);
+               return False;
+       }
+
+       *v = atoi(values[0]);
+       ldap_value_free(values);
+       return True;
+}
+
+/**
+ * pull a single objectGUID from an ADS result
+ * @param ads connection to ADS server
+ * @param msg results of search
+ * @param guid 37-byte area to receive text guid
+ * @return boolean indicating success
+ **/
+BOOL ads_pull_guid(ADS_STRUCT *ads,
+                  void *msg, GUID *guid)
+{
+       char **values;
+
+       values = ldap_get_values(ads->ld, msg, "objectGUID");
+       if (!values)
+               return False;
+       
+       if (values[0]) {
+               memcpy(guid, values[0], sizeof(GUID));
+               ldap_value_free(values);
+               return True;
+       }
+       ldap_value_free(values);
+       return False;
+
+}
+
+
+/**
+ * pull a single DOM_SID from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sid Pointer to sid to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sid(ADS_STRUCT *ads, 
+                 void *msg, const char *field, DOM_SID *sid)
+{
+       struct berval **values;
+       BOOL ret = False;
+
+       values = ldap_get_values_len(ads->ld, msg, field);
+
+       if (!values)
+               return False;
+
+       if (values[0])
+               ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
+       
+       ldap_value_free_len(values);
+       return ret;
+}
+
+/**
+ * pull an array of DOM_SIDs from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sids pointer to sid array to allocate
+ * @return the count of SIDs pulled
+ **/
+int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+                 void *msg, const char *field, DOM_SID **sids)
+{
+       struct berval **values;
+       BOOL ret;
+       int count, i;
+
+       values = ldap_get_values_len(ads->ld, msg, field);
+
+       if (!values)
+               return 0;
+
+       for (i=0; values[i]; i++)
+               /* nop */ ;
+
+       (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
+       if (!(*sids)) {
+               ldap_value_free_len(values);
+               return 0;
+       }
+
+       count = 0;
+       for (i=0; values[i]; i++) {
+               ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
+               if (ret) {
+                       fstring sid;
+                       DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
+                       count++;
+               }
+       }
+       
+       ldap_value_free_len(values);
+       return count;
+}
+
+/**
+ * pull a SEC_DESC from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+                 void *msg, const char *field, SEC_DESC **sd)
+{
+       struct berval **values;
+       prs_struct      ps;
+       BOOL ret = False;
+
+       values = ldap_get_values_len(ads->ld, msg, field);
+
+       if (!values) return False;
+
+       if (values[0]) {
+               prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
+               prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+               prs_set_offset(&ps,0);
+
+               ret = sec_io_desc("sd", sd, &ps, 1);
+       }
+       
+       ldap_value_free_len(values);
+       return ret;
+}
+
+/* 
+ * in order to support usernames longer than 21 characters we need to 
+ * use both the sAMAccountName and the userPrincipalName attributes 
+ * It seems that not all users have the userPrincipalName attribute set
+ *
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @return the username
+ */
+char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
+{
+       char *ret, *p;
+
+       ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
+       if (ret && (p = strchr(ret, '@'))) {
+               *p = 0;
+               return ret;
+       }
+       return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
+}
+
+
+/**
+ * find the update serial number - this is the core of the ldap cache
+ * @param ads connection to ads server
+ * @param ads connection to ADS server
+ * @param usn Pointer to retrieved update serial number
+ * @return status of search
+ **/
+ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
+{
+       const char *attrs[] = {"highestCommittedUSN", NULL};
+       ADS_STATUS status;
+       void *res;
+
+       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+       if (!ADS_ERR_OK(status)) return status;
+
+       if (ads_count_replies(ads, res) != 1) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
+       ads_msgfree(ads, res);
+       return ADS_SUCCESS;
+}
+
+/* parse a ADS timestring - typical string is
+   '20020917091222.0Z0' which means 09:12.22 17th September
+   2002, timezone 0 */
+static time_t ads_parse_time(const char *str)
+{
+       struct tm tm;
+
+       ZERO_STRUCT(tm);
+
+       if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
+                  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
+                  &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+               return 0;
+       }
+       tm.tm_year -= 1900;
+       tm.tm_mon -= 1;
+
+       return timegm(&tm);
+}
+
+
+/**
+ * Find the servers name and realm - this can be done before authentication 
+ *  The ldapServiceName field on w2k  looks like this:
+ *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
+ * @param ads connection to ads server
+ * @return status of search
+ **/
+ADS_STATUS ads_server_info(ADS_STRUCT *ads)
+{
+       const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
+       ADS_STATUS status;
+       void *res;
+       char *value;
+       char *p;
+       char *timestr;
+       TALLOC_CTX *ctx;
+
+       if (!(ctx = talloc_init("ads_server_info"))) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+       if (!ADS_ERR_OK(status)) return status;
+
+       value = ads_pull_string(ads, ctx, res, "ldapServiceName");
+       if (!value) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       timestr = ads_pull_string(ads, ctx, res, "currentTime");
+       if (!timestr) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       ldap_msgfree(res);
+
+       p = strchr(value, ':');
+       if (!p) {
+               talloc_destroy(ctx);
+               DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
+               return ADS_ERROR(LDAP_DECODING_ERROR);
+       }
+
+       SAFE_FREE(ads->config.ldap_server_name);
+
+       ads->config.ldap_server_name = strdup(p+1);
+       p = strchr(ads->config.ldap_server_name, '$');
+       if (!p || p[1] != '@') {
+               talloc_destroy(ctx);
+               DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
+               SAFE_FREE(ads->config.ldap_server_name);
+               return ADS_ERROR(LDAP_DECODING_ERROR);
+       }
+
+       *p = 0;
+
+       SAFE_FREE(ads->config.realm);
+       SAFE_FREE(ads->config.bind_path);
+
+       ads->config.realm = strdup(p+2);
+       ads->config.bind_path = ads_build_dn(ads->config.realm);
+
+       DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
+                ads->config.ldap_server_name, ads->config.realm,
+                ads->config.bind_path));
+
+       ads->config.current_time = ads_parse_time(timestr);
+
+       if (ads->config.current_time != 0) {
+               ads->auth.time_offset = ads->config.current_time - time(NULL);
+               DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
+       }
+
+       talloc_destroy(ctx);
+
+       return ADS_SUCCESS;
+}
+
+
+/**
+ * find the list of trusted domains
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating results
+ * @param num_trusts pointer to number of trusts
+ * @param names pointer to trusted domain name list
+ * @param sids pointer to list of sids of trusted domains
+ * @return the count of SIDs pulled
+ **/
+ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
+                              int *num_trusts, 
+                              char ***names, 
+                              char ***alt_names,
+                              DOM_SID **sids)
+{
+       const char *attrs[] = {"name", "flatname", "securityIdentifier", 
+                              "trustDirection", NULL};
+       ADS_STATUS status;
+       void *res, *msg;
+       int count, i;
+
+       *num_trusts = 0;
+
+       status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
+       if (!ADS_ERR_OK(status)) return status;
+
+       count = ads_count_replies(ads, res);
+       if (count == 0) {
+               ads_msgfree(ads, res);
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       (*names) = talloc(mem_ctx, sizeof(char *) * count);
+       (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
+       (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
+       if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
+
+       for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+               uint32 direction;
+
+               /* direction is a 2 bit bitfield, 1 means they trust us 
+                  but we don't trust them, so we should not list them
+                  as users from that domain can't login */
+               if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
+                   direction == 1) {
+                       continue;
+               }
+               
+               (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
+               (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
+
+               if ((*alt_names)[i] && (*alt_names)[i][0]) {
+                       /* we prefer the flatname as the primary name
+                          for consistency with RPC */
+                       char *name = (*alt_names)[i];
+                       (*alt_names)[i] = (*names)[i];
+                       (*names)[i] = name;
+               }
+               if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
+                       i++;
+               }
+       }
+
+       ads_msgfree(ads, res);
+
+       *num_trusts = i;
+
+       return ADS_SUCCESS;
+}
+
+/**
+ * find the domain sid for our domain
+ * @param ads connection to ads server
+ * @param sid Pointer to domain sid
+ * @return status of search
+ **/
+ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
+{
+       const char *attrs[] = {"objectSid", NULL};
+       void *res;
+       ADS_STATUS rc;
+
+       rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
+                          attrs, &res);
+       if (!ADS_ERR_OK(rc)) return rc;
+       if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+               return ADS_ERROR_SYSTEM(ENOENT);
+       }
+       ads_msgfree(ads, res);
+       
+       return ADS_SUCCESS;
+}
+
+/* this is rather complex - we need to find the allternate (netbios) name
+   for the domain, but there isn't a simple query to do this. Instead
+   we look for the principle names on the DCs account and find one that has 
+   the right form, then extract the netbios name of the domain from that
+
+   NOTE! better method is this:
+
+bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
+
+but you need to force the bind path to match the configurationNamingContext from the rootDSE
+
+*/
+ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
+{
+       char *exp;
+       ADS_STATUS rc;
+       char **principles;
+       char *prefix;
+       int prefix_length;
+       int i;
+       void *res;
+       const char *attrs[] = {"servicePrincipalName", NULL};
+
+       (*workgroup) = NULL;
+
+       asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
+                ads->config.ldap_server_name, ads->config.realm);
+       rc = ads_search(ads, &res, exp, attrs);
+       free(exp);
+
+       if (!ADS_ERR_OK(rc)) {
+               return rc;
+       }
+
+       principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
+
+       ads_msgfree(ads, res);
+
+       if (!principles) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       asprintf(&prefix, "HOST/%s.%s/", 
+                ads->config.ldap_server_name, 
+                ads->config.realm);
+
+       prefix_length = strlen(prefix);
+
+       for (i=0;principles[i]; i++) {
+               if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
+                   strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
+                   !strchr(principles[i]+prefix_length, '.')) {
+                       /* found an alternate (short) name for the domain. */
+                       DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
+                                principles[i]+prefix_length, 
+                                ads->config.realm));
+                       (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
+                       break;
+               }
+       }
+       free(prefix);
+
+       if (!*workgroup) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+       
+       return ADS_SUCCESS;
+}
+
+#endif
diff --git a/source4/libads/ldap_printer.c b/source4/libads/ldap_printer.c
new file mode 100644 (file)
index 0000000..f5cd4f2
--- /dev/null
@@ -0,0 +1,341 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) printer utility library
+   Copyright (C) Jim McDonough 2002
+   
+   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"
+
+#ifdef HAVE_ADS
+
+/*
+  find a printer given the name and the hostname
+    Note that results "res" may be allocated on return so that the
+    results can be used.  It should be freed using ads_msgfree.
+*/
+ADS_STATUS ads_find_printer_on_server(ADS_STRUCT *ads, void **res,
+                                     const char *printer, const char *servername)
+{
+       ADS_STATUS status;
+       char *srv_dn, **srv_cn, *exp;
+       const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+       status = ads_find_machine_acct(ads, res, servername);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1, ("ads_add_printer: cannot find host %s in ads\n",
+                         servername));
+               return status;
+       }
+       srv_dn = ldap_get_dn(ads->ld, *res);
+       srv_cn = ldap_explode_dn(srv_dn, 1);
+       ads_msgfree(ads, *res);
+
+       asprintf(&exp, "(cn=%s-%s)", srv_cn[0], printer);
+       status = ads_search(ads, res, exp, attrs);
+
+       ldap_memfree(srv_dn);
+       ldap_value_free(srv_cn);
+       free(exp);
+       return status;  
+}
+
+/*
+  modify a printer entry in the directory
+*/
+ADS_STATUS ads_mod_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+                                TALLOC_CTX *ctx, const ADS_MODLIST *mods)
+{
+       return ads_gen_mod(ads, prt_dn, *mods);
+}
+
+/*
+  add a printer to the directory
+*/
+ADS_STATUS ads_add_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+                                       TALLOC_CTX *ctx, ADS_MODLIST *mods)
+{
+       ads_mod_str(ctx, mods, "objectClass", "printQueue");
+       return ads_gen_add(ads, prt_dn, *mods);
+}
+
+/*
+  map a REG_SZ to an ldap mod
+*/
+static BOOL map_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                           const REGISTRY_VALUE *value)
+{
+       char *str_value = NULL;
+       ADS_STATUS status;
+
+       if (value->type != REG_SZ)
+               return False;
+
+       if (value->size && *((smb_ucs2_t *) value->data_p)) {
+               pull_ucs2_talloc(ctx, &str_value, (const smb_ucs2_t *) value->data_p);
+               status = ads_mod_str(ctx, mods, value->valuename, str_value);
+               return ADS_ERR_OK(status);
+       }
+       return True;
+               
+}
+
+/*
+  map a REG_DWORD to an ldap mod
+*/
+static BOOL map_dword(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                     const REGISTRY_VALUE *value)
+{
+       char *str_value = NULL;
+       ADS_STATUS status;
+
+       if (value->type != REG_DWORD)
+               return False;
+       str_value = talloc_asprintf(ctx, "%d", *((uint32 *) value->data_p));
+       status = ads_mod_str(ctx, mods, value->valuename, str_value);
+       return ADS_ERR_OK(status);
+}
+
+/*
+  map a boolean REG_BINARY to an ldap mod
+*/
+static BOOL map_bool(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+                    const REGISTRY_VALUE *value)
+{
+       char *str_value;
+       ADS_STATUS status;
+
+       if ((value->type != REG_BINARY) || (value->size != 1))
+               return False;
+       str_value =  talloc_asprintf(ctx, "%s", 
+                                    *(value->data_p) ? "TRUE" : "FALSE");
+       status = ads_mod_str(ctx, mods, value->valuename, str_value);
+       return ADS_ERR_OK(status);
+}
+
+/*
+  map a REG_MULTI_SZ to an ldap mod
+*/
+static BOOL map_multi_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+                        const REGISTRY_VALUE *value)
+{
+       char **str_values = NULL;
+       smb_ucs2_t *cur_str = (smb_ucs2_t *) value->data_p;
+        uint32 size = 0, num_vals = 0, i=0;
+       ADS_STATUS status;
+
+       if (value->type != REG_MULTI_SZ)
+               return False;
+
+       while(cur_str && *cur_str && (size < value->size)) {            
+               size += 2 * (strlen_w(cur_str) + 1);
+               cur_str += strlen_w(cur_str) + 1;
+               num_vals++;
+       };
+
+       if (num_vals) {
+               str_values = talloc(ctx, 
+                                   (num_vals + 1) * sizeof(smb_ucs2_t *));
+               memset(str_values, '\0', 
+                      (num_vals + 1) * sizeof(smb_ucs2_t *));
+
+               cur_str = (smb_ucs2_t *) value->data_p;
+               for (i=0; i < num_vals; i++)
+                       cur_str += pull_ucs2_talloc(ctx, &str_values[i],
+                                                   cur_str);
+
+               status = ads_mod_strlist(ctx, mods, value->valuename, 
+                                        (const char **) str_values);
+               return ADS_ERR_OK(status);
+       } 
+       return True;
+}
+
+struct valmap_to_ads {
+       const char *valname;
+       BOOL (*fn)(TALLOC_CTX *, ADS_MODLIST *, const REGISTRY_VALUE *);
+};
+
+/*
+  map a REG_SZ to an ldap mod
+*/
+static void map_regval_to_ads(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
+                             REGISTRY_VALUE *value)
+{
+       const struct valmap_to_ads map[] = {
+               {SPOOL_REG_ASSETNUMBER, map_sz},
+               {SPOOL_REG_BYTESPERMINUTE, map_dword},
+               {SPOOL_REG_DEFAULTPRIORITY, map_dword},
+               {SPOOL_REG_DESCRIPTION, map_sz},
+               {SPOOL_REG_DRIVERNAME, map_sz},
+               {SPOOL_REG_DRIVERVERSION, map_dword},
+               {SPOOL_REG_FLAGS, map_dword},
+               {SPOOL_REG_LOCATION, map_sz},
+               {SPOOL_REG_OPERATINGSYSTEM, map_sz},
+               {SPOOL_REG_OPERATINGSYSTEMHOTFIX, map_sz},
+               {SPOOL_REG_OPERATINGSYSTEMSERVICEPACK, map_sz},
+               {SPOOL_REG_OPERATINGSYSTEMVERSION, map_sz},
+               {SPOOL_REG_PORTNAME, map_multi_sz},
+               {SPOOL_REG_PRINTATTRIBUTES, map_dword},
+               {SPOOL_REG_PRINTBINNAMES, map_multi_sz},
+               {SPOOL_REG_PRINTCOLLATE, map_bool},
+               {SPOOL_REG_PRINTCOLOR, map_bool},
+               {SPOOL_REG_PRINTDUPLEXSUPPORTED, map_bool},
+               {SPOOL_REG_PRINTENDTIME, map_dword},
+               {SPOOL_REG_PRINTFORMNAME, map_sz},
+               {SPOOL_REG_PRINTKEEPPRINTEDJOBS, map_bool},
+               {SPOOL_REG_PRINTLANGUAGE, map_multi_sz},
+               {SPOOL_REG_PRINTMACADDRESS, map_sz},
+               {SPOOL_REG_PRINTMAXCOPIES, map_sz},
+               {SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED, map_dword},
+               {SPOOL_REG_PRINTMAXXEXTENT, map_dword},
+               {SPOOL_REG_PRINTMAXYEXTENT, map_dword},
+               {SPOOL_REG_PRINTMEDIAREADY, map_multi_sz},
+               {SPOOL_REG_PRINTMEDIASUPPORTED, map_multi_sz},
+               {SPOOL_REG_PRINTMEMORY, map_dword},
+               {SPOOL_REG_PRINTMINXEXTENT, map_dword},
+               {SPOOL_REG_PRINTMINYEXTENT, map_dword},
+               {SPOOL_REG_PRINTNETWORKADDRESS, map_sz},
+               {SPOOL_REG_PRINTNOTIFY, map_sz},
+               {SPOOL_REG_PRINTNUMBERUP, map_dword},
+               {SPOOL_REG_PRINTORIENTATIONSSUPPORTED, map_multi_sz},
+               {SPOOL_REG_PRINTOWNER, map_sz},
+               {SPOOL_REG_PRINTPAGESPERMINUTE, map_dword},
+               {SPOOL_REG_PRINTRATE, map_dword},
+               {SPOOL_REG_PRINTRATEUNIT, map_sz},
+               {SPOOL_REG_PRINTSEPARATORFILE, map_sz},
+               {SPOOL_REG_PRINTSHARENAME, map_sz},
+               {SPOOL_REG_PRINTSPOOLING, map_sz},
+               {SPOOL_REG_PRINTSTAPLINGSUPPORTED, map_bool},
+               {SPOOL_REG_PRINTSTARTTIME, map_dword},
+               {SPOOL_REG_PRINTSTATUS, map_sz},
+               {SPOOL_REG_PRIORITY, map_dword},
+               {SPOOL_REG_SERVERNAME, map_sz},
+               {SPOOL_REG_SHORTSERVERNAME, map_sz},
+               {SPOOL_REG_UNCNAME, map_sz},
+               {SPOOL_REG_URL, map_sz},
+               {SPOOL_REG_VERSIONNUMBER, map_dword},
+               {NULL, NULL}
+       };
+       int i;
+
+       for (i=0; map[i].valname; i++) {
+               if (StrCaseCmp(map[i].valname, value->valuename) == 0) {
+                       if (!map[i].fn(ctx, mods, value)) {
+                               DEBUG(5, ("Add of value %s to modlist failed\n", value->valuename));
+                       } else {
+                               DEBUG(7, ("Mapped value %s\n", value->valuename));
+                       }
+                       
+               }
+       }
+}
+
+
+WERROR get_remote_printer_publishing_data(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         ADS_MODLIST *mods,
+                                         const char *printer)
+{
+       WERROR result;
+       char *printername, *servername;
+       REGVAL_CTR dsdriver_ctr, dsspooler_ctr;
+       BOOL got_dsdriver = False, got_dsspooler = False;
+       uint32 needed, i;
+       POLICY_HND pol;
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       asprintf(&printername, "%s\\%s", servername, printer);
+       if (!servername || !printername) {
+               DEBUG(3, ("Insufficient memory\n"));
+               return WERR_NOMEM;
+       }
+       
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, cli->user_name, &pol);
+       if (!W_ERROR_IS_OK(result)) {
+               DEBUG(3, ("Unable to open printer %s, error is %s.\n",
+                         printername, dos_errstr(result)));
+               return result;
+       }
+       
+       result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed, 
+                                              &pol, SPOOL_DSDRIVER_KEY, NULL);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed, 
+                                                      NULL, &pol, 
+                                                      SPOOL_DSDRIVER_KEY,
+                                                      &dsdriver_ctr);
+
+       if (!W_ERROR_IS_OK(result)) {
+               DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+                         printername, dos_errstr(result)));
+       } else {
+
+               /* Have the data we need now, so start building */
+               got_dsdriver = True;
+               for (i=0; i < dsdriver_ctr.num_values; i++)
+                       map_regval_to_ads(mem_ctx, mods, 
+                                         dsdriver_ctr.values[i]);
+       }
+       
+       result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed, 
+                                              &pol, SPOOL_DSSPOOLER_KEY, 
+                                              NULL);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed, 
+                                                      NULL, &pol, 
+                                                      SPOOL_DSSPOOLER_KEY,
+                                                      &dsspooler_ctr);
+
+       if (!W_ERROR_IS_OK(result)) {
+               DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+                         printername, dos_errstr(result)));
+       } else {
+               got_dsspooler = True;
+               for (i=0; i < dsspooler_ctr.num_values; i++)
+                       map_regval_to_ads(mem_ctx, mods, 
+                                         dsspooler_ctr.values[i]);
+       }
+       
+       ads_mod_str(mem_ctx, mods, SPOOL_REG_PRINTERNAME, printer);
+
+       if (got_dsdriver) regval_ctr_destroy(&dsdriver_ctr);
+       if (got_dsspooler) regval_ctr_destroy(&dsspooler_ctr);
+       cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return result;
+}
+
+BOOL get_local_printer_publishing_data(TALLOC_CTX *mem_ctx,
+                                      ADS_MODLIST *mods,
+                                      NT_PRINTER_DATA *data)
+{
+       uint32 key,val;
+
+       for (key=0; key < data->num_keys; key++) {
+               REGVAL_CTR ctr = data->keys[key].values;
+               for (val=0; val < ctr.num_values; val++)
+                       map_regval_to_ads(mem_ctx, mods, ctr.values[val]);
+       }
+       return True;
+}
+
+#endif
+
diff --git a/source4/libads/ldap_user.c b/source4/libads/ldap_user.c
new file mode 100644 (file)
index 0000000..7efe533
--- /dev/null
@@ -0,0 +1,119 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads (active directory) utility library
+   Copyright (C) Jim McDonough 2002
+   
+   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"
+
+#ifdef HAVE_ADS
+
+/*
+  find a user account
+*/
+ADS_STATUS ads_find_user_acct(ADS_STRUCT *ads, void **res, const char *user)
+{
+       ADS_STATUS status;
+       char *exp;
+       const char *attrs[] = {"*", NULL};
+       char *escaped_user = escape_ldap_string_alloc(user);
+       if (!escaped_user) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       asprintf(&exp, "(samAccountName=%s)", escaped_user);
+       status = ads_search(ads, res, exp, attrs);
+       SAFE_FREE(exp);
+       SAFE_FREE(escaped_user);
+       return status;
+}
+
+ADS_STATUS ads_add_user_acct(ADS_STRUCT *ads, const char *user, 
+                            const char *container, const char *fullname)
+{
+       TALLOC_CTX *ctx;
+       ADS_MODLIST mods;
+       ADS_STATUS status;
+       const char *upn, *new_dn, *name, *controlstr;
+       const char *objectClass[] = {"top", "person", "organizationalPerson",
+                                    "user", NULL};
+
+       if (fullname && *fullname) name = fullname;
+       else name = user;
+
+       if (!(ctx = talloc_init("ads_add_user_acct")))
+               return ADS_ERROR(LDAP_NO_MEMORY);
+
+       status = ADS_ERROR(LDAP_NO_MEMORY);
+
+       if (!(upn = talloc_asprintf(ctx, "%s@%s", user, ads->config.realm)))
+               goto done;
+       if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", name, container,
+                                      ads->config.bind_path)))
+               goto done;
+       if (!(controlstr = talloc_asprintf(ctx, "%u", UF_NORMAL_ACCOUNT)))
+               goto done;
+       if (!(mods = ads_init_mods(ctx)))
+               goto done;
+
+       ads_mod_str(ctx, &mods, "cn", name);
+       ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+       ads_mod_str(ctx, &mods, "userPrincipalName", upn);
+       ads_mod_str(ctx, &mods, "name", name);
+       ads_mod_str(ctx, &mods, "displayName", name);
+       ads_mod_str(ctx, &mods, "sAMAccountName", user);
+       ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+       status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+       talloc_destroy(ctx);
+       return status;
+}
+
+ADS_STATUS ads_add_group_acct(ADS_STRUCT *ads, const char *group, 
+                             const char *container, const char *comment)
+{
+       TALLOC_CTX *ctx;
+       ADS_MODLIST mods;
+       ADS_STATUS status;
+       char *new_dn;
+       const char *objectClass[] = {"top", "group", NULL};
+
+       if (!(ctx = talloc_init("ads_add_group_acct")))
+               return ADS_ERROR(LDAP_NO_MEMORY);
+
+       status = ADS_ERROR(LDAP_NO_MEMORY);
+
+       if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", group, container,
+                                      ads->config.bind_path)))
+               goto done;
+       if (!(mods = ads_init_mods(ctx)))
+               goto done;
+
+       ads_mod_str(ctx, &mods, "cn", group);
+       ads_mod_strlist(ctx, &mods, "objectClass",objectClass);
+       ads_mod_str(ctx, &mods, "name", group);
+       if (comment && *comment) 
+               ads_mod_str(ctx, &mods, "description", comment);
+       ads_mod_str(ctx, &mods, "sAMAccountName", group);
+       status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+       talloc_destroy(ctx);
+       return status;
+}
+#endif
diff --git a/source4/libads/ldap_utils.c b/source4/libads/ldap_utils.c
new file mode 100644 (file)
index 0000000..907f7c8
--- /dev/null
@@ -0,0 +1,96 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Some Helpful wrappers on LDAP 
+
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+#ifdef HAVE_LDAP
+/*
+  a wrapper around ldap_search_s that retries depending on the error code
+  this is supposed to catch dropped connections and auto-reconnect
+*/
+ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, 
+                              const char *exp,
+                              const char **attrs, void **res)
+{
+       ADS_STATUS status;
+       int count = 3;
+       char *bp;
+
+       if (!ads->ld &&
+           time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
+               return ADS_ERROR(LDAP_SERVER_DOWN);
+       }
+
+       bp = strdup(bind_path);
+
+       if (!bp) 
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+
+       while (count--) {
+               status = ads_do_search_all(ads, bp, scope, exp, attrs, res);
+               if (ADS_ERR_OK(status)) {
+                       DEBUG(5,("Search for %s gave %d replies\n",
+                                exp, ads_count_replies(ads, *res)));
+                       free(bp);
+                       return status;
+               }
+
+               if (*res) ads_msgfree(ads, *res);
+               *res = NULL;
+               DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n", 
+                        ads->config.realm, ads_errstr(status)));
+               if (ads->ld) {
+                       ldap_unbind(ads->ld); 
+               }
+               ads->ld = NULL;
+               status = ads_connect(ads);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
+                                ads_errstr(status)));
+                       ads_destroy(&ads);
+                       free(bp);
+                       return status;
+               }
+       }
+       free(bp);
+
+       DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(status)));
+       return status;
+}
+
+
+ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res, 
+                           const char *exp, 
+                           const char **attrs)
+{
+       return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
+                                  exp, attrs, res);
+}
+
+ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
+                              const char *dn, 
+                              const char **attrs)
+{
+       return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
+                                  "(objectclass=*)", attrs, res);
+}
+#endif
diff --git a/source4/libads/sasl.c b/source4/libads/sasl.c
new file mode 100644 (file)
index 0000000..c33255b
--- /dev/null
@@ -0,0 +1,427 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ads sasl code
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+#ifdef HAVE_LDAP
+
+/* 
+   perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
+   we fit on one socket??)
+*/
+static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
+{
+       const char *mechs[] = {OID_NTLMSSP, NULL};
+       DATA_BLOB msg1;
+       DATA_BLOB blob, chal1, chal2, auth;
+       uint8 challenge[8];
+       uint8 nthash[24], lmhash[24], sess_key[16];
+       uint32 neg_flags;
+       struct berval cred, *scred;
+       ADS_STATUS status;
+       int rc;
+
+       if (!ads->auth.password) {
+               /* No password, don't segfault below... */
+               return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
+       }
+
+       neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
+               NTLMSSP_NEGOTIATE_128 | 
+               NTLMSSP_NEGOTIATE_NTLM;
+
+       memset(sess_key, 0, 16);
+
+       /* generate the ntlmssp negotiate packet */
+       msrpc_gen(&blob, "CddB",
+                 "NTLMSSP",
+                 NTLMSSP_NEGOTIATE,
+                 neg_flags,
+                 sess_key, 16);
+
+       /* and wrap it in a SPNEGO wrapper */
+       msg1 = gen_negTokenTarg(mechs, blob);
+       data_blob_free(&blob);
+
+       cred.bv_val = msg1.data;
+       cred.bv_len = msg1.length;
+
+       rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+       if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+               status = ADS_ERROR(rc);
+               goto failed;
+       }
+
+       blob = data_blob(scred->bv_val, scred->bv_len);
+
+       /* the server gives us back two challenges */
+       if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
+               DEBUG(3,("Failed to parse challenges\n"));
+               status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+               goto failed;
+       }
+
+       data_blob_free(&blob);
+
+       /* encrypt the password with the challenge */
+       memcpy(challenge, chal1.data + 24, 8);
+       SMBencrypt(ads->auth.password, challenge,lmhash);
+       SMBNTencrypt(ads->auth.password, challenge,nthash);
+
+       data_blob_free(&chal1);
+       data_blob_free(&chal2);
+
+       /* this generates the actual auth packet */
+       msrpc_gen(&blob, "CdBBUUUBd", 
+                 "NTLMSSP", 
+                 NTLMSSP_AUTH, 
+                 lmhash, 24,
+                 nthash, 24,
+                 lp_workgroup(), 
+                 ads->auth.user_name, 
+                 lp_netbios_name(),
+                 sess_key, 16,
+                 neg_flags);
+
+       /* wrap it in SPNEGO */
+       auth = spnego_gen_auth(blob);
+
+       data_blob_free(&blob);
+
+       /* now send the auth packet and we should be done */
+       cred.bv_val = auth.data;
+       cred.bv_len = auth.length;
+
+       rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+       return ADS_ERROR(rc);
+
+failed:
+       return status;
+}
+
+/* 
+   perform a LDAP/SASL/SPNEGO/KRB5 bind
+*/
+static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
+{
+       DATA_BLOB blob;
+       struct berval cred, *scred;
+       int rc;
+
+       blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
+
+       if (!blob.data) {
+               return ADS_ERROR(LDAP_OPERATIONS_ERROR);
+       }
+
+       /* now send the auth packet and we should be done */
+       cred.bv_val = blob.data;
+       cred.bv_len = blob.length;
+
+       rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+       data_blob_free(&blob);
+
+       return ADS_ERROR(rc);
+}
+
+/* 
+   this performs a SASL/SPNEGO bind
+*/
+static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
+{
+       struct berval *scred=NULL;
+       int rc, i;
+       ADS_STATUS status;
+       DATA_BLOB blob;
+       char *principal;
+       char *OIDs[ASN1_MAX_OIDS];
+       BOOL got_kerberos_mechanism = False;
+
+       rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
+
+       if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+               status = ADS_ERROR(rc);
+               goto failed;
+       }
+
+       blob = data_blob(scred->bv_val, scred->bv_len);
+
+#if 0
+       file_save("sasl_spnego.dat", blob.data, blob.length);
+#endif
+
+       /* the server sent us the first part of the SPNEGO exchange in the negprot 
+          reply */
+       if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+               data_blob_free(&blob);
+               status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+               goto failed;
+       }
+       data_blob_free(&blob);
+
+       /* make sure the server understands kerberos */
+       for (i=0;OIDs[i];i++) {
+               DEBUG(3,("got OID=%s\n", OIDs[i]));
+               if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+                   strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+                       got_kerberos_mechanism = True;
+               }
+               free(OIDs[i]);
+       }
+       DEBUG(3,("got principal=%s\n", principal));
+
+#ifdef HAVE_KRB5
+       if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
+           got_kerberos_mechanism) {
+               status = ads_sasl_spnego_krb5_bind(ads, principal);
+               if (ADS_ERR_OK(status))
+                       return status;
+               if (ads_kinit_password(ads) == 0) {
+                       status = ads_sasl_spnego_krb5_bind(ads, principal);
+               }
+               if (ADS_ERR_OK(status))
+                       return status;
+       }
+#endif
+
+       /* lets do NTLMSSP ... this has the big advantage that we don't need
+          to sync clocks, and we don't rely on special versions of the krb5 
+          library for HMAC_MD4 encryption */
+       return ads_sasl_spnego_ntlmssp_bind(ads);
+
+failed:
+       return status;
+}
+
+#ifdef HAVE_GSSAPI
+#define MAX_GSS_PASSES 3
+
+/* this performs a SASL/gssapi bind
+   we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
+   is very dependent on correctly configured DNS whereas
+   this routine is much less fragile
+   see RFC2078 and RFC2222 for details
+*/
+static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
+{
+       int minor_status;
+       gss_name_t serv_name;
+       gss_buffer_desc input_name;
+       gss_ctx_id_t context_handle;
+       gss_OID mech_type = GSS_C_NULL_OID;
+       gss_buffer_desc output_token, input_token;
+       OM_uint32 ret_flags, conf_state;
+       struct berval cred;
+       struct berval *scred;
+       int i=0;
+       int gss_rc, rc;
+       uint8 *p;
+       uint32 max_msg_size;
+       char *sname;
+       unsigned sec_layer;
+       ADS_STATUS status;
+       krb5_principal principal;
+       krb5_context ctx;
+       krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+                       ENCTYPE_ARCFOUR_HMAC,
+#endif
+                       ENCTYPE_DES_CBC_MD5,
+                       ENCTYPE_NULL};
+       gss_OID_desc nt_principal = 
+       {10, "\052\206\110\206\367\022\001\002\002\002"};
+
+       /* we need to fetch a service ticket as the ldap user in the
+          servers realm, regardless of our realm */
+       asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
+       krb5_init_context(&ctx);
+       krb5_set_default_tgs_ktypes(ctx, enc_types);
+       krb5_parse_name(ctx, sname, &principal);
+       free(sname);
+       krb5_free_context(ctx); 
+
+       input_name.value = &principal;
+       input_name.length = sizeof(principal);
+
+       gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
+       if (gss_rc) {
+               return ADS_ERROR_GSS(gss_rc, minor_status);
+       }
+
+       context_handle = GSS_C_NO_CONTEXT;
+
+       input_token.value = NULL;
+       input_token.length = 0;
+
+       for (i=0; i < MAX_GSS_PASSES; i++) {
+               gss_rc = gss_init_sec_context(&minor_status,
+                                         GSS_C_NO_CREDENTIAL,
+                                         &context_handle,
+                                         serv_name,
+                                         mech_type,
+                                         GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+                                         0,
+                                         NULL,
+                                         &input_token,
+                                         NULL,
+                                         &output_token,
+                                         &ret_flags,
+                                         NULL);
+
+               if (input_token.value) {
+                       gss_release_buffer(&minor_status, &input_token);
+               }
+
+               if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
+                       status = ADS_ERROR_GSS(gss_rc, minor_status);
+                       goto failed;
+               }
+
+               cred.bv_val = output_token.value;
+               cred.bv_len = output_token.length;
+
+               rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
+                                     &scred);
+               if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+                       status = ADS_ERROR(rc);
+                       goto failed;
+               }
+
+               if (output_token.value) {
+                       gss_release_buffer(&minor_status, &output_token);
+               }
+
+               if (scred) {
+                       input_token.value = scred->bv_val;
+                       input_token.length = scred->bv_len;
+               } else {
+                       input_token.value = NULL;
+                       input_token.length = 0;
+               }
+
+               if (gss_rc == 0) break;
+       }
+
+       gss_release_name(&minor_status, &serv_name);
+
+       gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
+                           &conf_state,NULL);
+       if (gss_rc) {
+               status = ADS_ERROR_GSS(gss_rc, minor_status);
+               goto failed;
+       }
+
+       gss_release_buffer(&minor_status, &input_token);
+
+       p = (uint8 *)output_token.value;
+
+       file_save("sasl_gssapi.dat", output_token.value, output_token.length);
+
+       max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+       sec_layer = *p;
+
+       gss_release_buffer(&minor_status, &output_token);
+
+       output_token.value = malloc(strlen(ads->config.bind_path) + 8);
+       p = output_token.value;
+
+       *p++ = 1; /* no sign & seal selection */
+       /* choose the same size as the server gave us */
+       *p++ = max_msg_size>>16;
+       *p++ = max_msg_size>>8;
+       *p++ = max_msg_size;
+       snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
+       p += strlen(p);
+
+       output_token.length = PTR_DIFF(p, output_token.value);
+
+       gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
+                         &output_token, &conf_state,
+                         &input_token);
+       if (gss_rc) {
+               status = ADS_ERROR_GSS(gss_rc, minor_status);
+               goto failed;
+       }
+
+       free(output_token.value);
+
+       cred.bv_val = input_token.value;
+       cred.bv_len = input_token.length;
+
+       rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
+                             &scred);
+       status = ADS_ERROR(rc);
+
+       gss_release_buffer(&minor_status, &input_token);
+
+failed:
+       return status;
+}
+#endif
+
+/* mapping between SASL mechanisms and functions */
+static struct {
+       const char *name;
+       ADS_STATUS (*fn)(ADS_STRUCT *);
+} sasl_mechanisms[] = {
+       {"GSS-SPNEGO", ads_sasl_spnego_bind},
+#ifdef HAVE_GSSAPI
+       {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
+#endif
+       {NULL, NULL}
+};
+
+ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
+{
+       const char *attrs[] = {"supportedSASLMechanisms", NULL};
+       char **values;
+       ADS_STATUS status;
+       int i, j;
+       void *res;
+
+       /* get a list of supported SASL mechanisms */
+       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+       if (!ADS_ERR_OK(status)) return status;
+
+       values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
+
+       /* try our supported mechanisms in order */
+       for (i=0;sasl_mechanisms[i].name;i++) {
+               /* see if the server supports it */
+               for (j=0;values && values[j];j++) {
+                       if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
+                               DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+                               status = sasl_mechanisms[i].fn(ads);
+                               ldap_value_free(values);
+                               ldap_msgfree(res);
+                               return status;
+                       }
+               }
+       }
+
+       ldap_value_free(values);
+       ldap_msgfree(res);
+       return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
+}
+
+#endif
+
diff --git a/source4/libads/util.c b/source4/libads/util.c
new file mode 100644 (file)
index 0000000..335cabc
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+   Unix SMB/CIFS implementation.
+   krb5 set password implementation
+   Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
+   
+   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"
+
+#ifdef HAVE_KRB5
+
+ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal)
+{
+    char *tmp_password;
+    char *password;
+    char *new_password;
+    char *service_principal;
+    ADS_STATUS ret;
+
+    if ((password = secrets_fetch_machine_password()) == NULL) {
+       DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal));
+       return ADS_ERROR_SYSTEM(ENOENT);
+    }
+
+    tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+    new_password = strdup(tmp_password);
+    asprintf(&service_principal, "HOST/%s", host_principal);
+
+    ret = kerberos_set_password(ads->auth.kdc_server, service_principal, password, service_principal, new_password, ads->auth.time_offset);
+
+    if (!ADS_ERR_OK(ret)) goto failed;
+
+    if (!secrets_store_machine_password(new_password)) {
+           DEBUG(1,("Failed to save machine password\n"));
+           return ADS_ERROR_SYSTEM(EACCES);
+    }
+
+failed:
+    SAFE_FREE(service_principal);
+    SAFE_FREE(new_password);
+
+    return ret;
+}
+
+
+
+#endif
diff --git a/source4/libcli/.cvsignore b/source4/libcli/.cvsignore
new file mode 100644 (file)
index 0000000..2588860
--- /dev/null
@@ -0,0 +1,3 @@
+*.po\r
+*.po32\r
+\r
diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c
new file mode 100644 (file)
index 0000000..da8a842
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+   Unix SMB/CIFS implementation.
+   client connect/disconnect routines
+   Copyright (C) Andrew Tridgell 2003
+
+   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"
+
+/*
+  wrapper around cli_sock_connect()
+*/
+BOOL cli_socket_connect(struct cli_state *cli, const char *server, struct in_addr *ip)
+{
+       struct cli_socket *sock;
+
+       sock = cli_sock_init();
+       if (!sock) return False;
+
+       if (!cli_sock_connect_byname(sock, server, 0)) {
+               cli_sock_close(sock);
+               return False;
+       }
+       
+       cli->transport = cli_transport_init(sock);
+       if (!cli->transport) {
+               cli_sock_close(sock);
+               return False;
+       }
+
+       return True;
+}
+
+/* wrapper around cli_transport_connect() */
+BOOL cli_transport_establish(struct cli_state *cli, 
+                            struct nmb_name *calling,
+                            struct nmb_name *called)
+{
+       return cli_transport_connect(cli->transport, calling, called);
+}
+
+/* wrapper around smb_raw_negotiate() */
+BOOL cli_negprot(struct cli_state *cli)
+{
+       NTSTATUS status;
+       status = smb_raw_negotiate(cli->transport);
+       return NT_STATUS_IS_OK(status);
+}
+
+/* wrapper around smb_raw_session_setup() */
+BOOL cli_session_setup(struct cli_state *cli, 
+                      const char *user, 
+                      const char *password, 
+                      const char *domain)
+{
+       union smb_sesssetup setup;
+       NTSTATUS status;
+       TALLOC_CTX *mem_ctx;
+
+       cli->session = cli_session_init(cli->transport);
+       if (!cli->session) return False;
+
+       mem_ctx = talloc_init("cli_session_setup");
+       if (!mem_ctx) return False;
+
+       setup.generic.level = RAW_SESSSETUP_GENERIC;
+       setup.generic.in.sesskey = cli->transport->negotiate.sesskey;
+       setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 | 
+               CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | 
+               CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX;
+       setup.generic.in.password = password;
+       setup.generic.in.user = user;
+       setup.generic.in.domain = domain;
+
+       status = smb_raw_session_setup(cli->session, mem_ctx, &setup);
+
+       cli->session->vuid = setup.generic.out.vuid;
+
+       talloc_destroy(mem_ctx);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+/* wrapper around smb_tree_connect() */
+BOOL cli_send_tconX(struct cli_state *cli, const char *sharename, const char *devtype,
+                   const char *password)
+{
+       union smb_tcon tcon;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       cli->tree = cli_tree_init(cli->session);
+       if (!cli->tree) return False;
+
+       cli->tree->reference_count++;
+
+       /* setup a tree connect */
+       tcon.generic.level = RAW_TCON_TCONX;
+       tcon.tconx.in.flags = 0;
+       tcon.tconx.in.password = data_blob(password, strlen(password)+1);
+       tcon.tconx.in.path = sharename;
+       tcon.tconx.in.device = devtype;
+       
+       mem_ctx = talloc_init("tcon");
+       if (!mem_ctx) {
+               return False;
+       }
+
+       status = smb_tree_connect(cli->tree, mem_ctx, &tcon);
+
+       cli->tree->tid = tcon.tconx.out.cnum;
+
+       talloc_destroy(mem_ctx);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/*
+  easy way to get to a fully connected cli_state in one call
+*/
+NTSTATUS cli_full_connection(struct cli_state **ret_cli, 
+                            const char *myname,
+                            const char *host,
+                            struct in_addr *ip,
+                            const char *sharename,
+                            const char *devtype,
+                            const char *username,
+                            const char *domain,
+                            const char *password,
+                            uint_t flags,
+                            BOOL *retry)
+{
+       struct cli_tree *tree;
+       NTSTATUS status;
+
+       *ret_cli = NULL;
+
+       status = cli_tree_full_connection(&tree, myname, host, 0, sharename, devtype,
+                                         username, domain, password);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       (*ret_cli) = cli_state_init();
+
+       (*ret_cli)->tree = tree;
+       (*ret_cli)->session = tree->session;
+       (*ret_cli)->transport = tree->session->transport;
+       tree->reference_count++;
+
+       return status;
+}
+
+
+/*
+  disconnect the tree
+*/
+BOOL cli_tdis(struct cli_state *cli)
+{
+       NTSTATUS status;
+       status = smb_tree_disconnect(cli->tree);
+       return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+ Initialise a client state structure.
+****************************************************************************/
+struct cli_state *cli_state_init(void)
+{
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("cli_state");
+       if (!mem_ctx) return NULL;
+
+       cli = talloc_zero(mem_ctx, sizeof(*cli));
+       cli->mem_ctx = mem_ctx;
+
+       return cli;
+}
+
+/****************************************************************************
+ Shutdown a client structure.
+****************************************************************************/
+void cli_shutdown(struct cli_state *cli)
+{
+       if (!cli) return;
+       cli->tree->reference_count++;
+       cli_tree_close(cli->tree);
+       if (cli->mem_ctx) {
+               talloc_destroy(cli->mem_ctx);
+       }
+}
diff --git a/source4/libcli/clideltree.c b/source4/libcli/clideltree.c
new file mode 100644 (file)
index 0000000..8769b8d
--- /dev/null
@@ -0,0 +1,117 @@
+/* 
+   Unix SMB/CIFS implementation.
+   useful function for deleting a whole directory tree
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+struct delete_state {
+       struct cli_state *cli;
+       int total_deleted;
+       BOOL failed;
+};
+
+/* 
+   callback function for torture_deltree() 
+*/
+static void delete_fn(file_info *finfo, const char *name, void *state)
+{
+       struct delete_state *dstate = state;
+       char *s, *n;
+       if (strcmp(finfo->name, ".") == 0 ||
+           strcmp(finfo->name, "..") == 0) return;
+
+       n = strdup(name);
+       n[strlen(n)-1] = 0;
+       asprintf(&s, "%s%s", n, finfo->name);
+
+       if (finfo->mode & FILE_ATTRIBUTE_READONLY) {
+               if (!cli_setatr(dstate->cli, s, 0, 0)) {
+                       DEBUG(2,("Failed to remove READONLY on %s - %s\n",
+                                s, cli_errstr(dstate->cli)));                  
+               }
+       }
+
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
+               char *s2;
+               asprintf(&s2, "%s\\*", s);
+               cli_unlink(dstate->cli, s2);
+               cli_list(dstate->cli, s2, 
+                        FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, 
+                        delete_fn, state);
+               free(s2);
+               if (!cli_rmdir(dstate->cli, s)) {
+                       DEBUG(2,("Failed to delete %s - %s\n", 
+                                s, cli_errstr(dstate->cli)));
+                       dstate->failed = True;
+               }
+               dstate->total_deleted++;
+       } else {
+               if (!cli_unlink(dstate->cli, s)) {
+                       DEBUG(2,("Failed to delete %s - %s\n", 
+                                s, cli_errstr(dstate->cli)));
+                       dstate->failed = True;
+               }
+               dstate->total_deleted++;
+       }
+       free(s);
+       free(n);
+}
+
+/* 
+   recursively descend a tree deleting all files
+   returns the number of files deleted, or -1 on error
+*/
+int cli_deltree(struct cli_state *cli, const char *dname)
+{
+       char *mask;
+       struct delete_state dstate;
+
+       dstate.cli = cli;
+       dstate.total_deleted = 0;
+       dstate.failed = False;
+
+       /* it might be a file */
+       if (cli_unlink(cli, dname)) {
+               return 1;
+       }
+       if (NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+           NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+           NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_NO_SUCH_FILE)) {
+               return 0;
+       }
+
+       asprintf(&mask, "%s\\*", dname);
+       cli_unlink(cli, mask);
+       cli_list(dstate.cli, mask, 
+                FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, 
+                delete_fn, &dstate);
+       free(mask);
+       if (!cli_rmdir(dstate.cli, dname)) {
+               DEBUG(2,("Failed to delete %s - %s\n", 
+                        dname, cli_errstr(dstate.cli)));
+               return -1;
+       }
+       dstate.total_deleted++;
+
+       if (dstate.failed) {
+               return -1;
+       }
+
+       return dstate.total_deleted;
+}
diff --git a/source4/libcli/clidfs.c b/source4/libcli/clidfs.c
new file mode 100644 (file)
index 0000000..fc24ccc
--- /dev/null
@@ -0,0 +1,558 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Dfs routines
+   Copyright (C) James Myers 2003
+   
+   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"
+
+BOOL cli_client_initialize(struct cli_client* context,
+                          const char* sockops,
+                          char* username, char* password, char* workgroup,
+                          int flags)
+{
+       int i;
+       for (i=0; i < DFS_MAX_CLUSTER_SIZE ; i++) {
+               context->cli[i] = cli_raw_initialise();
+       }
+       context->sockops = sockops;
+       context->username = username;
+       context->password = password;
+       context->workgroup = workgroup;
+       context->connection_flags = flags;
+       if (flags & CLI_FULL_CONNECTION_USE_DFS)
+               context->use_dfs = True;
+       context->number_members = DFS_MAX_CLUSTER_SIZE; 
+       return True;
+}
+
+/****************************************************************************
+ Interpret a Dfs referral structure.
+ The length of the structure is returned
+ The structure of a Dfs referral depends on the info level.
+****************************************************************************/
+
+static int interpret_referral(struct cli_state *cli,
+                                  int level,char *p,referral_info *rinfo)
+{
+       char* q;
+       int version, size;
+
+       version = SVAL(p,0);
+       size = SVAL(p,2);
+       rinfo->server_type = SVAL(p,4);
+       rinfo->referral_flags = SVAL(p,6);
+       rinfo->proximity = SVAL(p,8);
+       rinfo->ttl = SVAL(p,10);
+       rinfo->pathOffset = SVAL(p,12);
+       rinfo->altPathOffset = SVAL(p,14);
+       rinfo->nodeOffset = SVAL(p,16);
+       DEBUG(3,("referral version=%d, size=%d, server_type=%d, flags=0x%x, proximity=%d, ttl=%d, pathOffset=%d, altPathOffset=%d, nodeOffset=%d\n",
+               version, size, rinfo->server_type, rinfo->referral_flags,
+               rinfo->proximity, rinfo->ttl, rinfo->pathOffset,
+               rinfo->altPathOffset, rinfo->nodeOffset));
+
+       q = (char*)(p + (rinfo->pathOffset));
+       //printf("p=%p, q=%p, offset=%d\n", p, q, rinfo->pathOffset);
+       //printf("hex=0x%x, string=%s\n", q, q);
+       clistr_pull(cli, rinfo->path, q,
+                   sizeof(rinfo->path),
+                   -1, STR_TERMINATE);
+       DEBUG(4,("referral path=%s\n", rinfo->path));
+       q = (char*)(p + (rinfo->altPathOffset)/sizeof(char));
+       if (rinfo->altPathOffset > 0)
+               clistr_pull(cli, rinfo->altPath, q,
+                   sizeof(rinfo->altPath),
+                   -1, STR_TERMINATE);
+       DEBUG(4,("referral alt path=%s\n", rinfo->altPath));
+       q = (char*)(p + (rinfo->nodeOffset)/sizeof(char));
+       if (rinfo->nodeOffset > 0)
+               clistr_pull(cli, rinfo->node, q,
+                   sizeof(rinfo->node),
+                   -1, STR_TERMINATE);
+       DEBUG(4,("referral node=%s\n", rinfo->node));
+       fstrcpy(rinfo->host, &rinfo->node[1]);
+       p = strchr_m(&rinfo->host[1],'\\');
+       if (!p) {
+               printf("invalid referral node %s\n", rinfo->node);
+               return -1;
+       }
+       *p = 0;
+       rinfo->share = talloc_strdup(cli->mem_ctx, p+1);
+       DEBUG(3,("referral host=%s share=%s\n",
+               rinfo->host, rinfo->share));
+       return size;
+}
+
+#if 0
+int cli_select_dfs_referral(struct cli_state *cli, dfs_info* dinfo)
+{
+       return (int)sys_random()%dinfo->number_referrals;
+}
+
+int cli_get_dfs_referral(struct cli_state *cli,const char *Fname, dfs_info* dinfo)
+{
+       struct smb_trans2 parms;
+       int info_level;
+       char *p;
+       pstring fname;
+       int i;
+       char *rparam=NULL, *rdata=NULL;
+       int param_len, data_len;        
+       uint16 setup;
+       pstring param;
+       DATA_BLOB trans_param, trans_data;
+
+       /* NT uses 260, OS/2 uses 2. Both accept 1. */
+       info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
+
+       pstrcpy(fname,Fname);
+
+       setup = TRANSACT2_GET_DFS_REFERRAL ;
+       SSVAL(param,0,CLI_DFS_MAX_REFERRAL_LEVEL); /* attribute */
+       p = param+2;
+       p += clistr_push(cli, param+2, fname, -1, 
+                        STR_TERMINATE);
+
+       param_len = PTR_DIFF(p, param);
+       DEBUG(3,("cli_get_dfs_referral: sending request\n"));
+       
+       trans_param.length = param_len;
+       trans_param.data = param;
+       trans_data.length = 0;
+       trans_data.data = NULL;
+
+       if (!cli_send_trans(cli, SMBtrans2, 
+                           NULL,                   /* Name */
+                           -1, 0,                  /* fid, flags */
+                           &setup, 1, 0,           /* setup, length, max */
+                           &trans_param, 10,   /* param, length, max */
+                           &trans_data, 
+                           cli->max_xmit /* data, length, max */
+                           )) {
+               return 0;
+       }
+
+       if (!cli_receive_trans(cli, SMBtrans2, 
+                              &rparam, &param_len,
+                              &rdata, &data_len) &&
+                   cli_is_dos_error(cli)) {
+           return 0;
+       }
+       //printf("cli_get_dfs_referral: received response, rdata=%p, rparam=%p\n",
+       //      rdata, rparam);
+
+    if (cli_is_error(cli) || !rdata) 
+               return 0;
+
+       /* parse out some important return info */
+       //printf("cli_get_dfs_referral: valid response\n");
+       p = rdata;
+       dinfo->path_consumed = SVAL(p,0);
+       dinfo->number_referrals = SVAL(p,2);
+       dinfo->referral_flags = SVAL(p,4);
+       DEBUG(3,("cli_get_dfs_referral: path_consumed=%d, # referrals=%d, flags=0x%x\n",
+               dinfo->path_consumed, dinfo->number_referrals,
+               dinfo->referral_flags));
+
+       /* point to the referral bytes */
+       p+=8;
+       for (i=0; i < dinfo->number_referrals; i++) {
+               p += interpret_referral(cli,info_level,p,&dinfo->referrals[i]);
+       }
+
+       SAFE_FREE(rdata);
+       SAFE_FREE(rparam);
+
+       DEBUG(3,("received %d Dfs referrals\n",
+                        dinfo->number_referrals));
+                        
+       dinfo->selected_referral = cli_select_dfs_referral(cli, dinfo);
+       DEBUG(3, ("selected Dfs referral %d %s\n",
+               dinfo->selected_referral, dinfo->referrals[dinfo->selected_referral].node));
+
+       return(dinfo->number_referrals);
+}
+#endif
+
+/* check if the server produced Dfs redirect */
+BOOL cli_check_dfs_redirect(struct cli_state* c, char* fname,
+               dfs_info* dinfo)
+{
+               //printf("check_dfs_redirect: error %s\n",
+               //      cli_errstr(c));
+        if (cli_is_dos_error(c)) {
+                       printf("got dos error\n");
+                return False;
+
+        } else {
+                NTSTATUS status;
+
+                /* Check NT error */
+
+                status = cli_nt_error(c);
+                //printf("got nt error 0x%x\n", status);
+
+                               if (NT_STATUS_V(NT_STATUS_PATH_NOT_COVERED) != NT_STATUS_V(status)) {
+                        return False;
+                }
+        }
+    /* execute trans2 getdfsreferral */
+    //printf("check_dfs_redirect: process referral\n");
+    //cli_get_dfs_referral(c, fname, dinfo);
+       return True;
+}
+
+int cli_dfs_open_connection(struct cli_client* cluster,
+               char* host, char* share, int flags)
+{
+       int i;
+       BOOL retry;
+       struct cli_state* c;
+       
+       // check if already connected
+       for (i=0; i < DFS_MAX_CLUSTER_SIZE; i++) {
+               if (cluster->cli[i]->in_use && strequal(host, cli_state_get_host(cluster->cli[i]))
+                               && strequal(share, cli_state_get_share(cluster->cli[i]))) {
+                       DEBUG(3,("cli_dfs_open_connection: already connected to \\\\%s\\%s\n", host, share));
+                       return i;
+               }
+       }
+       // open connection
+       DEBUG(3,("cli_dfs_open_connection: opening \\\\%s\\%s %s@%s\n",
+               host, share, cluster->username, cluster->workgroup));
+       for (i=0; i < DFS_MAX_CLUSTER_SIZE; i++) {
+               if (!cluster->cli[i]->in_use) {
+                       break;
+               }
+       }
+       if (i >= DFS_MAX_CLUSTER_SIZE)
+               return -1;
+
+       c = cluster->cli[i];
+       if (NT_STATUS_IS_ERR(cli_full_connection(&c,
+                            NULL, host, NULL, 0,
+                            share, "?????",
+                            cluster->username, cluster->workgroup, 
+                            cluster->password, flags,
+                            &retry)))
+               return -1;
+       cli_state_set_sockopt(cluster->cli[i], cluster->sockops);
+       cli_state_set_host(cluster->cli[i], host);
+       cli_state_set_share(cluster->cli[i], share);
+       cluster->cli[i]->in_use = True;
+       DEBUG(3,("cli_dfs_open_connection: connected \\\\%s\\%s (%d) %s@%s\n",
+               cli_state_get_host(cluster->cli[i]), cli_state_get_share(cluster->cli[i]), i,
+               cluster->username, cluster->workgroup));
+
+       return i;
+}
+
+/**********************************************************************
+  Parse the pathname  of the form \hostname\service\reqpath
+  into the dfs_path structure 
+ **********************************************************************/
+
+BOOL cli_parse_dfs_path(char* pathname, struct dfs_path* pdp)
+{
+       pstring pathname_local;
+       char* p,*temp;
+
+       pstrcpy(pathname_local,pathname);
+       p = temp = pathname_local;
+
+       ZERO_STRUCTP(pdp);
+
+       trim_string(temp,"\\","\\");
+       DEBUG(10,("temp in cli_parse_dfs_path: .%s. after trimming \\'s\n",temp));
+
+       /* now tokenize */
+       /* parse out hostname */
+       p = strchr(temp,'\\');
+       if(p == NULL)
+               return False;
+       *p = '\0';
+       pstrcpy(pdp->hostname,temp);
+       DEBUG(10,("hostname: %s\n",pdp->hostname));
+
+       /* parse out servicename */
+       temp = p+1;
+       p = strchr(temp,'\\');
+       if(p == NULL) {
+               pstrcpy(pdp->servicename,temp);
+               pdp->reqpath[0] = '\0';
+               return True;
+       }
+       *p = '\0';
+       pstrcpy(pdp->servicename,temp);
+       DEBUG(10,("servicename: %s\n",pdp->servicename));
+
+       /* rest is reqpath */
+       pstrcpy(pdp->reqpath, p+1);
+
+       DEBUG(10,("rest of the path: %s\n",pdp->reqpath));
+       return True;
+}
+
+char* rebuild_filename(char *referral_fname, struct cli_state* c,
+               char* fname, int path_consumed)
+{
+       const char *template = "\\\\%s\\%s\\%s";
+       struct dfs_path dp;
+       
+       // TODO: handle consumed length
+       DEBUG(3,("rebuild_filename: %s, %d consumed of %d\n",
+               fname, path_consumed, strlen(fname)));
+       if (cli_parse_dfs_path(fname, &dp)) {
+               DEBUG(3,("rebuild_filename: reqpath=%s\n",
+                       dp.reqpath));
+               asprintf(&referral_fname,
+                       template, cli_state_get_host(c),
+                       cli_state_get_share(c), dp.reqpath);
+       }
+       else
+               return NULL;
+       DEBUG(3,("rebuild_filename: %s -> %s\n", fname, referral_fname));
+       return referral_fname;
+}
+
+/****************************************************************************
+ Open a file (allowing for Dfs referral).
+****************************************************************************/
+
+int cli_dfs_open(struct cli_client* cluster, int *server,
+       char *fname_src, int flags, int share_mode)
+{
+       int referral_number;
+       dfs_info dinfo;
+       char *referral_fname;
+       int fnum;
+       
+       DEBUG(3,("cli_dfs_open: open %s on server %s(%d)\n",
+                       fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+       cluster->cli[*server]->dfs_referral = *server;
+       if ((fnum = cli_open(cluster->cli[*server], fname_src, flags, share_mode)) < 0) {
+               if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) {
+                       // choose referral, check if already connected, open if not
+                       referral_number = dinfo.selected_referral;
+                       DEBUG(3,("cli_dfs_open: redirecting to %s\n", dinfo.referrals[referral_number].node));
+                       cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster,
+                               dinfo.referrals[referral_number].host,
+                               dinfo.referrals[referral_number].share,
+                               cluster->connection_flags);
+                       *server = cluster->cli[*server]->dfs_referral;
+                       if (server < 0)
+                               return False;
+                       // rebuild file name and retry operation.
+                       if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL)
+                               return False;
+                       fname_src = referral_fname;
+                       DEBUG(3,("cli_dfs_open: Dfs open %s on server %s(%d)\n",
+                               fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+                       fnum = cli_open(cluster->cli[*server], fname_src, flags, share_mode);
+               }
+               if (cli_is_error(cluster->cli[*server])) {
+                       printf("cli_dfs_open: open of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+                       return -1;
+               }
+       }
+       DEBUG(3,("cli_dfs_open: open %s fnum=%d\n",
+                       fname_src, fnum));
+       return fnum;
+}
+
+/****************************************************************************
+ Delete a file (allowing for Dfs referral).
+****************************************************************************/
+
+NTSTATUS cli_nt_unlink(struct cli_client* cluster, int *server,
+       char *fname_src, uint16 FileAttributes)
+{
+       int referral_number;
+       dfs_info dinfo;
+       char *referral_fname;
+       struct smb_unlink parms;
+       
+       DEBUG(3,("cli_nt_unlink: delete %s on server %s(%d), attributes=0x%x\n",
+                       fname_src, cli_state_get_host(cluster->cli[*server]), *server,
+                       FileAttributes));
+       cluster->cli[*server]->dfs_referral = *server;
+       parms.in.pattern = fname_src;
+       parms.in.dirtype = FileAttributes;                      
+       if (NT_STATUS_IS_ERR(cli_raw_unlink(cluster->cli[*server], &parms))) {
+               printf("cli_nt_unlink: delete of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+               if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) {
+                       // choose referral, check if already connected, open if not
+                       referral_number = dinfo.selected_referral;
+                       DEBUG(3,("cli_nt_unlink: redirecting to %s\n", dinfo.referrals[referral_number].node));
+                       cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster,
+                               dinfo.referrals[referral_number].host,
+                               dinfo.referrals[referral_number].share,
+                               cluster->connection_flags);
+                       *server = cluster->cli[*server]->dfs_referral;
+                       if (server < 0)
+                               return NT_STATUS_INTERNAL_ERROR;
+                       // rebuild file name and retry operation.
+                       if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL)
+                               return NT_STATUS_INTERNAL_ERROR;
+                       fname_src = referral_fname;
+                       DEBUG(3,("cli_nt_unlink: Dfs delete %s on server %s(%d)\n",
+                               fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+                       cli_raw_unlink(cluster->cli[*server], &parms);
+               }
+               if (cli_is_error(cluster->cli[*server])) {
+                       printf("cli_nt_unlink: delete of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+               }
+       }
+       return cli_nt_error(cluster->cli[*server]);
+}
+
+/****************************************************************************
+ Rename a file (allowing for Dfs referral).
+****************************************************************************/
+
+BOOL cli_dfs_rename(struct cli_client* cluster, int *server,
+       char *fname_src, char *fname_dst)
+{
+       int referral_number;
+       dfs_info dinfo;
+       char *referral_fname;
+       
+       DEBUG(3,("cli_dfs_rename: rename %s to %s on server %s(%d)\n",
+                       fname_src, fname_dst, cli_state_get_host(cluster->cli[*server]), *server));
+       cluster->cli[*server]->dfs_referral = *server;
+       if (!cli_rename(cluster->cli[*server], fname_src, fname_dst)) {
+               if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) {
+                       // choose referral, check if already connected, open if not
+                       referral_number = dinfo.selected_referral;
+                       DEBUG(3,("cli_dfs_rename: redirecting to %s\n", dinfo.referrals[referral_number].node));
+                       cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster,
+                               dinfo.referrals[referral_number].host,
+                               dinfo.referrals[referral_number].share,
+                               cluster->connection_flags);
+                       *server = cluster->cli[*server]->dfs_referral;
+                       if (server < 0)
+                               return False;
+                       // rebuild file name and retry operation.
+                       if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL)
+                               return False;
+                       fname_src = referral_fname;
+                       DEBUG(3,("cli_dfs_rename: Dfs rename %s to %s on server %s(%d)\n",
+                               fname_src, fname_dst, cli_state_get_host(cluster->cli[*server]), *server));
+                       cli_rename(cluster->cli[*server], fname_src, fname_dst);
+               }
+               if (cli_is_error(cluster->cli[*server])) {
+                       printf("cli_dfs_rename: rename of %s to %s failed (%s)\n",
+                               fname_src, fname_dst, cli_errstr(cluster->cli[*server]));
+                       return False;
+               }
+       }
+       return True;
+}
+
+/****************************************************************************
+ Make directory (allowing for Dfs referral).
+****************************************************************************/
+
+BOOL cli_dfs_mkdir(struct cli_client* cluster, int *server,
+       char *fname_src)
+{
+       int referral_number;
+       dfs_info dinfo;
+       char *referral_fname;
+       
+       DEBUG(3,("cli_dfs_mkdir: mkdir %s on server %s(%d)\n",
+                       fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+       cluster->cli[*server]->dfs_referral = *server;                  
+       if (!cli_mkdir(cluster->cli[*server], fname_src)) {
+               printf("cli_dfs_mkdir: mkdir of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+               if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) {
+                       // choose referral, check if already connected, open if not
+                       referral_number = dinfo.selected_referral;
+                       DEBUG(3,("cli_dfs_mkdir: redirecting to %s\n", dinfo.referrals[referral_number].node));
+                       cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster,
+                               dinfo.referrals[referral_number].host,
+                               dinfo.referrals[referral_number].share,
+                               cluster->connection_flags);
+                       *server = cluster->cli[*server]->dfs_referral;
+                       if (server < 0)
+                               return False;
+                       // rebuild file name and retry operation.
+                       if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL)
+                               return False;
+                       fname_src = referral_fname;
+                       DEBUG(3,("cli_dfs_mkdir: Dfs mkdir %s on server %s(%d)\n",
+                               fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+                       cli_mkdir(cluster->cli[*server], fname_src);
+               }
+               if (cli_is_error(cluster->cli[*server])) {
+                       printf("cli_dfs_mkdir: mkdir of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+                       return False;
+               }
+       }
+       return True;
+}
+
+/****************************************************************************
+ Remove directory (allowing for Dfs referral).
+****************************************************************************/
+
+BOOL cli_dfs_rmdir(struct cli_client* cluster, int *server,
+       char *fname_src)
+{
+       int referral_number;
+       dfs_info dinfo;
+       char *referral_fname;
+       
+       DEBUG(3,("cli_dfs_rmdir: rmdir %s on server %s(%d)\n",
+                       fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+       cluster->cli[*server]->dfs_referral = *server;                  
+       if (!cli_rmdir(cluster->cli[*server], fname_src)) {
+               printf("cli_dfs_rmdir: rmdir of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+               if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) {
+                       // choose referral, check if already connected, open if not
+                       referral_number = dinfo.selected_referral;
+                       DEBUG(3,("cli_dfs_rmdir: redirecting to %s\n", dinfo.referrals[referral_number].node));
+                       cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster,
+                               dinfo.referrals[referral_number].host,
+                               dinfo.referrals[referral_number].share,
+                               cluster->connection_flags);
+                       *server = cluster->cli[*server]->dfs_referral;
+                       if (server < 0)
+                               return False;
+                       // rebuild file name and retry operation.
+                       if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL)
+                               return False;
+                       fname_src = referral_fname;
+                       DEBUG(3,("cli_dfs_rmdir: Dfs rmdir %s on server %s(%d)\n",
+                               fname_src, cli_state_get_host(cluster->cli[*server]), *server));
+                       cli_rmdir(cluster->cli[*server], fname_src);
+               }
+               if (cli_is_error(cluster->cli[*server])) {
+                       printf("cli_dfs_rmdir: rmdir of %s failed (%s)\n",
+                               fname_src, cli_errstr(cluster->cli[*server]));
+                       return False;
+               }
+       }
+       return True;
+}
diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c
new file mode 100644 (file)
index 0000000..c203e46
--- /dev/null
@@ -0,0 +1,647 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client file operations
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Jeremy Allison 2001-2002
+   Copyright (C) James Myers 2003
+   
+   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"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+****************************************************************************/
+
+static BOOL cli_link_internal(struct cli_state *cli, 
+                             const char *fname_src, 
+                             const char *fname_dst, BOOL hard_link)
+{
+       union smb_setfileinfo parms;
+       NTSTATUS status;
+
+       if (hard_link) {
+               parms.generic.level = SMB_SFILEINFO_UNIX_HLINK;
+               parms.unix_hlink.file.fname = fname_src;
+               parms.unix_hlink.in.link_dest = fname_dst;
+       } else {
+               parms.generic.level = SMB_SFILEINFO_UNIX_LINK;
+               parms.unix_link.file.fname = fname_src;
+               parms.unix_link.in.link_dest = fname_dst;
+       }
+       
+       status = smb_raw_setpathinfo(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+static uint32  unix_perms_to_wire(mode_t perms)
+{
+        unsigned int ret = 0;
+
+        ret |= ((perms & S_IXOTH) ?  UNIX_X_OTH : 0);
+        ret |= ((perms & S_IWOTH) ?  UNIX_W_OTH : 0);
+        ret |= ((perms & S_IROTH) ?  UNIX_R_OTH : 0);
+        ret |= ((perms & S_IXGRP) ?  UNIX_X_GRP : 0);
+        ret |= ((perms & S_IWGRP) ?  UNIX_W_GRP : 0);
+        ret |= ((perms & S_IRGRP) ?  UNIX_R_GRP : 0);
+        ret |= ((perms & S_IXUSR) ?  UNIX_X_USR : 0);
+        ret |= ((perms & S_IWUSR) ?  UNIX_W_USR : 0);
+        ret |= ((perms & S_IRUSR) ?  UNIX_R_USR : 0);
+#ifdef S_ISVTX
+        ret |= ((perms & S_ISVTX) ?  UNIX_STICKY : 0);
+#endif
+#ifdef S_ISGID
+        ret |= ((perms & S_ISGID) ?  UNIX_SET_GID : 0);
+#endif
+#ifdef S_ISUID
+        ret |= ((perms & S_ISUID) ?  UNIX_SET_UID : 0);
+#endif
+        return ret;
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+BOOL cli_unix_symlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+       return cli_link_internal(cli, fname_src, fname_dst, False);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+BOOL cli_unix_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+       return cli_link_internal(cli, fname_src, fname_dst, True);
+}
+
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+static BOOL cli_unix_chmod_chown_internal(struct cli_state *cli, const char *fname, 
+                                         uint32 mode, uint32 uid, uint32 gid)
+{
+       union smb_setfileinfo parms;
+       NTSTATUS status;
+
+       parms.generic.level = SMB_SFILEINFO_UNIX_BASIC;
+       parms.unix_basic.file.fname = fname;
+       parms.unix_basic.in.uid = uid;
+       parms.unix_basic.in.gid = gid;
+       parms.unix_basic.in.mode = mode;
+       
+       status = smb_raw_setpathinfo(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_chmod(struct cli_state *cli, const char *fname, mode_t mode)
+{
+       return cli_unix_chmod_chown_internal(cli, fname, 
+               unix_perms_to_wire(mode), SMB_UID_NO_CHANGE, SMB_GID_NO_CHANGE);
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+BOOL cli_unix_chown(struct cli_state *cli, const char *fname, uid_t uid, gid_t gid)
+{
+       return cli_unix_chmod_chown_internal(cli, fname, SMB_MODE_NO_CHANGE, (uint32)uid, (uint32)gid);
+}
+
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+BOOL cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+       struct smb_rename parms;
+
+       parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+       parms.in.pattern1 = fname_src;
+       parms.in.pattern2 = fname_dst;
+       return NT_STATUS_IS_OK(smb_raw_rename(cli->tree, &parms));
+}
+
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+BOOL cli_unlink(struct cli_state *cli, const char *fname)
+{
+       struct smb_unlink parms;
+
+       parms.in.pattern = fname;
+       if (strchr(fname, '*')) {
+               parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+       } else {
+               parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+       }
+       return NT_STATUS_IS_OK(smb_raw_unlink(cli->tree, &parms));
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+BOOL cli_mkdir(struct cli_state *cli, const char *dname)
+{
+       union smb_mkdir parms;
+
+       parms.mkdir.level = RAW_MKDIR_MKDIR;
+       parms.mkdir.in.path = dname;
+
+       return NT_STATUS_IS_OK(smb_raw_mkdir(cli->tree, &parms));
+}
+
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+BOOL cli_rmdir(struct cli_state *cli, const char *dname)
+{
+       struct smb_rmdir parms;
+
+       parms.in.path = dname;
+       return NT_STATUS_IS_OK(smb_raw_rmdir(cli->tree, &parms));
+}
+
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+BOOL cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag)
+{
+       union smb_setfileinfo parms;
+       NTSTATUS status;
+
+       parms.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+       parms.disposition_info.file.fnum = fnum;
+       parms.disposition_info.in.delete_on_close = flag;
+       
+       status = smb_raw_setfileinfo(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Create/open a file - exposing the full horror of the NT API :-).
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+int cli_nt_create_full(struct cli_state *cli, const char *fname,
+                uint32 CreatFlags, uint32 DesiredAccess,
+                uint32 FileAttributes, uint32 ShareAccess,
+                uint32 CreateDisposition, uint32 CreateOptions,
+                uint8 SecurityFlags)
+{
+       union smb_open open_parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("raw_open");
+       if (!mem_ctx) return -1;
+
+       open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
+       open_parms.ntcreatex.in.flags = CreatFlags;
+       open_parms.ntcreatex.in.root_fid = 0;
+       open_parms.ntcreatex.in.access_mask = DesiredAccess;
+       open_parms.ntcreatex.in.file_attr = FileAttributes;
+       open_parms.ntcreatex.in.alloc_size = 0;
+       open_parms.ntcreatex.in.share_access = ShareAccess;
+       open_parms.ntcreatex.in.open_disposition = CreateDisposition;
+       open_parms.ntcreatex.in.create_options = CreateOptions;
+       open_parms.ntcreatex.in.impersonation = 0;
+       open_parms.ntcreatex.in.security_flags = SecurityFlags;
+       open_parms.ntcreatex.in.fname = fname;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
+       talloc_destroy(mem_ctx);
+
+       if (NT_STATUS_IS_OK(status)) {
+               return open_parms.ntcreatex.out.fnum;
+       }
+       
+       return -1;
+}
+
+
+/****************************************************************************
+ Open a file (using SMBopenx)
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode)
+{
+       union smb_open open_parms;
+       unsigned openfn=0;
+       unsigned accessmode=0;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("raw_open");
+       if (!mem_ctx) return -1;
+
+       if (flags & O_CREAT) {
+               openfn |= OPENX_OPEN_FUNC_CREATE;
+       }
+       if (!(flags & O_EXCL)) {
+               if (flags & O_TRUNC) {
+                       openfn |= OPENX_OPEN_FUNC_TRUNC;
+               } else {
+                       openfn |= OPENX_OPEN_FUNC_OPEN;
+               }
+       }
+
+       accessmode = (share_mode<<OPENX_MODE_DENY_SHIFT);
+
+       if ((flags & O_ACCMODE) == O_RDWR) {
+               accessmode |= OPENX_MODE_ACCESS_RDWR;
+       } else if ((flags & O_ACCMODE) == O_WRONLY) {
+               accessmode |= OPENX_MODE_ACCESS_WRITE;
+       } 
+
+#if defined(O_SYNC)
+       if ((flags & O_SYNC) == O_SYNC) {
+               accessmode |= OPENX_MODE_WRITE_THRU;
+       }
+#endif
+
+       if (share_mode == DENY_FCB) {
+               accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+       }
+
+       open_parms.openx.level = RAW_OPEN_OPENX;
+       open_parms.openx.in.flags = 0;
+       open_parms.openx.in.open_mode = accessmode;
+       open_parms.openx.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+       open_parms.openx.in.file_attrs = 0;
+       open_parms.openx.in.write_time = 0;
+       open_parms.openx.in.open_func = openfn;
+       open_parms.openx.in.size = 0;
+       open_parms.openx.in.timeout = 0;
+       open_parms.openx.in.fname = fname;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
+       talloc_destroy(mem_ctx);
+
+       if (NT_STATUS_IS_OK(status)) {
+               return open_parms.openx.out.fnum;
+       }
+
+       return -1;
+}
+
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+BOOL cli_close(struct cli_state *cli, int fnum)
+{
+       union smb_close close_parms;
+       NTSTATUS status;
+
+       close_parms.close.level = RAW_CLOSE_CLOSE;
+       close_parms.close.in.fnum = fnum;
+       close_parms.close.in.write_time = 0;
+       status = smb_raw_close(cli->tree, &close_parms);
+       return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+ send a lock with a specified locktype 
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+NTSTATUS cli_locktype(struct cli_state *cli, int fnum, 
+                     uint32 offset, uint32 len, int timeout, unsigned char locktype)
+{
+       union smb_lock parms;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+
+       parms.lockx.level = RAW_LOCK_LOCKX;
+       parms.lockx.in.fnum = fnum;
+       parms.lockx.in.mode = locktype;
+       parms.lockx.in.timeout = timeout;
+       parms.lockx.in.ulock_cnt = 0;
+       parms.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = offset;
+       lock[0].count = len;
+       parms.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &parms);
+       
+       return status;
+}
+
+
+/****************************************************************************
+ Lock a file.
+****************************************************************************/
+BOOL cli_lock(struct cli_state *cli, int fnum, 
+             uint32 offset, uint32 len, int timeout, enum brl_type lock_type)
+{
+       union smb_lock parms;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+
+       parms.lockx.level = RAW_LOCK_LOCKX;
+       parms.lockx.in.fnum = fnum;
+       parms.lockx.in.mode = (lock_type == READ_LOCK? 1 : 0);
+       parms.lockx.in.timeout = timeout;
+       parms.lockx.in.ulock_cnt = 0;
+       parms.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = offset;
+       lock[0].count = len;
+       parms.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len)
+{
+       union smb_lock parms;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+
+       parms.lockx.level = RAW_LOCK_LOCKX;
+       parms.lockx.in.fnum = fnum;
+       parms.lockx.in.mode = 0;
+       parms.lockx.in.timeout = 0;
+       parms.lockx.in.ulock_cnt = 1;
+       parms.lockx.in.lock_cnt = 0;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = offset;
+       lock[0].count = len;
+       parms.lockx.in.locks = &lock[0];
+       
+       status = smb_raw_lock(cli->tree, &parms);
+       return NT_STATUS_IS_OK(status);
+}
+       
+
+/****************************************************************************
+ Lock a file with 64 bit offsets.
+****************************************************************************/
+BOOL cli_lock64(struct cli_state *cli, int fnum, 
+               SMB_OFF_T offset, SMB_OFF_T len, int timeout, enum brl_type lock_type)
+{
+       union smb_lock parms;
+       int ltype;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+
+       if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+               return cli_lock(cli, fnum, offset, len, timeout, lock_type);
+       }
+
+       parms.lockx.level = RAW_LOCK_LOCKX;
+       parms.lockx.in.fnum = fnum;
+       
+       ltype = (lock_type == READ_LOCK? 1 : 0);
+       ltype |= LOCKING_ANDX_LARGE_FILES;
+       parms.lockx.in.mode = ltype;
+       parms.lockx.in.timeout = timeout;
+       parms.lockx.in.ulock_cnt = 0;
+       parms.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = offset;
+       lock[0].count = len;
+       parms.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &parms);
+       
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_OFF_T offset, SMB_OFF_T len)
+{
+       union smb_lock parms;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+
+       if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+               return cli_unlock(cli, fnum, offset, len);
+       }
+
+       parms.lockx.level = RAW_LOCK_LOCKX;
+       parms.lockx.in.fnum = fnum;
+       parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       parms.lockx.in.timeout = 0;
+       parms.lockx.in.ulock_cnt = 1;
+       parms.lockx.in.lock_cnt = 0;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = offset;
+       lock[0].count = len;
+       parms.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+BOOL cli_getattrE(struct cli_state *cli, int fd,
+               uint16 *attr, size_t *size,
+               time_t *c_time, time_t *a_time, time_t *m_time)
+{              
+       union smb_fileinfo parms;
+       NTSTATUS status;
+
+       parms.getattre.level = RAW_FILEINFO_GETATTRE;
+       parms.getattre.in.fnum = fd;
+
+       status = smb_raw_fileinfo(cli->tree, NULL, &parms);
+
+       if (!NT_STATUS_IS_OK(status))
+               return False;
+
+       if (size) {
+               *size = parms.getattre.out.size;
+       }
+
+       if (attr) {
+               *attr = parms.getattre.out.attrib;
+       }
+
+       if (c_time) {
+               *c_time = parms.getattre.out.create_time;
+       }
+
+       if (a_time) {
+               *a_time = &parms.getattre.out.access_time;
+       }
+
+       if (m_time) {
+               *m_time = &parms.getattre.out.write_time;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+BOOL cli_getatr(struct cli_state *cli, const char *fname, 
+               uint16 *attr, size_t *size, time_t *t)
+{
+       union smb_fileinfo parms;
+       NTSTATUS status;
+
+       parms.getattr.level = RAW_FILEINFO_GETATTR;
+       parms.getattr.in.fname = fname;
+
+       status = smb_raw_pathinfo(cli->tree, NULL, &parms);
+       
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (size) {
+               *size = parms.getattr.out.size;
+       }
+
+       if (t) {
+               *t = parms.getattr.out.write_time;
+       }
+
+       if (attr) {
+               *attr = parms.getattr.out.attrib;
+       }
+
+       return True;
+}
+
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+BOOL cli_setatr(struct cli_state *cli, const char *fname, uint16 mode, time_t t)
+{
+       union smb_setfileinfo parms;
+       NTSTATUS status;
+
+       parms.setattr.level = RAW_SFILEINFO_SETATTR;
+       parms.setattr.in.attrib = mode;
+       parms.setattr.in.write_time = t;
+       parms.setattr.file.fname = fname;
+       
+       status = smb_raw_setpathinfo(cli->tree, &parms);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir.
+****************************************************************************/
+BOOL cli_chkpath(struct cli_state *cli, const char *path)
+{
+       struct smb_chkpath parms;
+       char *path2;
+       NTSTATUS status;
+
+       path2 = strdup(path);
+       trim_string(path2,NULL,"\\");
+       if (!*path2) {
+               free(path2);
+               path2 = strdup("\\");
+       }
+
+       parms.in.path = path2;
+       
+       status = smb_raw_chkpath(cli->tree, &parms);
+
+       free(path2);
+
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+BOOL cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+{
+       union smb_fsinfo fsinfo_parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_dskattr");
+
+       fsinfo_parms.dskattr.level = RAW_QFS_DSKATTR;
+       status = smb_raw_fsinfo(cli->tree, mem_ctx, &fsinfo_parms);
+       if (NT_STATUS_IS_OK(status)) {
+               *bsize = fsinfo_parms.dskattr.out.block_size;
+               *total = fsinfo_parms.dskattr.out.units_total;
+               *avail = fsinfo_parms.dskattr.out.units_free;
+       }
+
+       talloc_destroy(mem_ctx);
+       
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path)
+{
+       union smb_open open_parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("raw_open");
+       if (!mem_ctx) return -1;
+
+       open_parms.openx.level = RAW_OPEN_CTEMP;
+       open_parms.ctemp.in.attrib = 0;
+       open_parms.ctemp.in.directory = path;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &open_parms);
+       if (tmp_path) {
+               *tmp_path = strdup(open_parms.ctemp.out.name);
+       }
+       talloc_destroy(mem_ctx);
+       if (NT_STATUS_IS_OK(status)) {
+               return open_parms.ctemp.out.fnum;
+       }
+       return -1;
+}
+
diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c
new file mode 100644 (file)
index 0000000..620e438
--- /dev/null
@@ -0,0 +1,313 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client directory list routines
+   Copyright (C) Andrew Tridgell 1994-2003
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+struct search_private {
+       file_info *dirlist;
+       TALLOC_CTX *mem_ctx;
+       int dirlist_len;
+       int ff_searchcount;  /* total received in 1 server trip */
+       int total_received;  /* total received all together */
+       int info_level;
+       DATA_BLOB status;       /* used for old-style search */
+};
+
+
+/****************************************************************************
+ Interpret a long filename structure.
+****************************************************************************/
+static BOOL interpret_long_filename(int level,
+                                   union smb_search_data *info,
+                                   file_info *finfo)
+{
+       file_info finfo2;
+
+       if (!finfo) finfo = &finfo2;
+       ZERO_STRUCTP(finfo);
+       
+       finfo->size = info->both_directory_info.size;
+       finfo->ctime = nt_time_to_unix(&info->both_directory_info.create_time);
+       finfo->atime = nt_time_to_unix(&info->both_directory_info.access_time);
+       finfo->mtime = nt_time_to_unix(&info->both_directory_info.write_time);
+       finfo->mode = info->both_directory_info.attrib; /* 32 bit->16 bit attrib */
+       if (info->both_directory_info.short_name.s) {
+               strncpy(finfo->short_name, info->both_directory_info.short_name.s, 
+                       sizeof(finfo->short_name)-1);
+       }
+       finfo->name = info->both_directory_info.name.s;
+       return True;
+}
+
+/* callback function used for trans2 search */
+static BOOL cli_list_new_callback(void *private, union smb_search_data *file)
+{
+       struct search_private *state = (struct search_private*) private;
+       file_info *tdl;
+       /* add file info to the dirlist pool */
+       tdl = talloc_realloc(state->mem_ctx, state->dirlist,
+                       state->dirlist_len + sizeof(struct file_info));
+
+       if (!tdl) {
+               return False;
+       }
+       state->dirlist = tdl;
+       state->dirlist_len += sizeof(struct file_info);
+
+       interpret_long_filename(state->info_level, file, &state->dirlist[state->total_received]);
+
+       state->total_received++;
+       state->ff_searchcount++;
+       
+       return True;
+}
+
+int cli_list_new(struct cli_state *cli, const char *Mask, uint16 attribute, 
+                void (*fn)(file_info *, const char *, void *), void *caller_state)
+{
+       union smb_search_first first_parms;
+       union smb_search_next next_parms;
+       struct search_private state;  /* for callbacks */
+       int received = 0;
+       BOOL first = True;
+       int num_received = 0;
+       int max_matches = 512;
+       char *mask;
+       int ff_eos = 0, i, ff_searchcount;
+       int ff_dir_handle=0;
+       int level;
+
+       /* initialize state for search */
+       state.dirlist = NULL;
+       state.mem_ctx = talloc_init("cli_list_new");
+       state.dirlist_len = 0;
+       state.total_received = 0;
+       
+       mask = talloc_strdup(state.mem_ctx, Mask);
+
+       if (cli->transport->negotiate.capabilities & CAP_NT_SMBS) {
+               level = RAW_SEARCH_BOTH_DIRECTORY_INFO;
+       } else {
+               level = RAW_SEARCH_STANDARD;
+       }
+
+       while (1) {
+               state.ff_searchcount = 0;
+               if (first) {
+                       NTSTATUS status;
+
+                       first_parms.t2ffirst.level = level;
+                       first_parms.t2ffirst.in.max_count = max_matches;
+                       first_parms.t2ffirst.in.search_attrib = attribute;
+                       first_parms.t2ffirst.in.pattern = mask;
+                       first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+                       first_parms.t2ffirst.in.storage_type = 0;
+                       
+                       status = smb_raw_search_first(cli->tree, 
+                                                     state.mem_ctx, &first_parms,
+                                                     (void*)&state, cli_list_new_callback);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               talloc_destroy(state.mem_ctx);
+                               return -1;
+                       }
+               
+                       ff_dir_handle = first_parms.t2ffirst.out.handle;
+                       ff_searchcount = first_parms.t2ffirst.out.count;
+                       ff_eos = first_parms.t2ffirst.out.end_of_search;
+                       
+                       received = first_parms.t2ffirst.out.count;
+                       if (received <= 0) break;
+                       if (ff_eos) break;
+                       first = False;
+               } else {
+                       NTSTATUS status;
+
+                       next_parms.t2fnext.level = level;
+                       next_parms.t2fnext.in.max_count = max_matches;
+                       next_parms.t2fnext.in.last_name = mask;
+                       next_parms.t2fnext.in.handle = ff_dir_handle;
+                       next_parms.t2fnext.in.resume_key = 0;
+                       next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE | FLAG_TRANS2_FIND_CLOSE_IF_END;
+                       
+                       status = smb_raw_search_next(cli->tree, 
+                                                    state.mem_ctx,
+                                                    &next_parms,
+                                                    (void*)&state, 
+                                                    cli_list_new_callback);
+                       
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return -1;
+                       }
+                       ff_searchcount = next_parms.t2fnext.out.count;
+                       ff_eos = next_parms.t2fnext.out.end_of_search;
+                       received = next_parms.t2fnext.out.count;
+                       if (received <= 0) break;
+                       if (ff_eos) break;
+               }
+               
+               num_received += received;
+       }
+
+       for (i=0;i<state.total_received;i++) {
+               fn(&state.dirlist[i], Mask, caller_state);
+       }
+
+       talloc_destroy(state.mem_ctx);
+
+       return state.total_received;
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+static BOOL interpret_short_filename(int level,
+                               union smb_search_data *info,
+                               file_info *finfo)
+{
+       file_info finfo2;
+
+       if (!finfo) finfo = &finfo2;
+       ZERO_STRUCTP(finfo);
+       
+       finfo->ctime = info->search.write_time;
+       finfo->atime = info->search.write_time;
+       finfo->mtime = info->search.write_time;
+       finfo->size = info->search.size;
+       finfo->mode = info->search.attrib;
+       finfo->name = info->search.name;
+       return True;
+}
+
+/* callback function used for smb_search */
+static BOOL cli_list_old_callback(void *private, union smb_search_data *file)
+{
+       struct search_private *state = (struct search_private*) private;
+       file_info *tdl;
+       
+       /* add file info to the dirlist pool */
+       tdl = talloc_realloc(state->mem_ctx, state->dirlist,
+                       state->dirlist_len + sizeof(struct file_info));
+
+       if (!tdl) {
+               return False;
+       }
+       state->dirlist = tdl;
+       state->dirlist_len += sizeof(struct file_info);
+
+       interpret_short_filename(state->info_level, file, &state->dirlist[state->total_received]);
+
+       state->total_received++;
+       state->ff_searchcount++;
+       state->status = file->search.search_id; /* return resume info */
+       
+       return True;
+}
+
+int cli_list_old(struct cli_state *cli, const char *Mask, uint16 attribute, 
+                void (*fn)(file_info *, const char *, void *), void *caller_state)
+{
+       union smb_search_first first_parms;
+       union smb_search_next next_parms;
+       struct search_private state;  /* for callbacks */
+       const int num_asked = 500;
+       int received = 0;
+       BOOL first = True;
+       int num_received = 0;
+       char *mask;
+       int i;
+
+       /* initialize state for search */
+       state.dirlist = NULL;
+       state.mem_ctx = talloc_init("cli_list_old");
+       state.dirlist_len = 0;
+       state.total_received = 0;
+       
+       mask = talloc_strdup(state.mem_ctx, Mask);
+  
+       while (1) {
+               state.ff_searchcount = 0;
+               if (first) {
+                       NTSTATUS status;
+
+                       first_parms.search_first.level = RAW_SEARCH_SEARCH;
+                       first_parms.search_first.in.max_count = num_asked;
+                       first_parms.search_first.in.search_attrib = attribute;
+                       first_parms.search_first.in.pattern = mask;
+                       
+                       status = smb_raw_search_first(cli->tree, state.mem_ctx, 
+                                                          &first_parms,
+                                                          (void*)&state, 
+                                                          cli_list_old_callback);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               talloc_destroy(state.mem_ctx);
+                               return -1;
+                       }
+               
+                       received = first_parms.search_first.out.count;
+                       if (received <= 0) break;
+                       first = False;
+               } else {
+                       NTSTATUS status;
+
+                       next_parms.search_next.level = RAW_SEARCH_SEARCH;
+                       next_parms.search_next.in.max_count = num_asked;
+                       next_parms.search_next.in.search_attrib = attribute;
+                       next_parms.search_next.in.search_id = state.status;
+                       
+                       status = smb_raw_search_next(cli->tree, state.mem_ctx,
+                                                         &next_parms,
+                                                         (void*)&state, 
+                                                         cli_list_old_callback);
+                       
+                       if (!NT_STATUS_IS_OK(status)) {
+                               talloc_destroy(state.mem_ctx);
+                               return -1;
+                       }
+                       received = next_parms.search_next.out.count;
+                       if (received <= 0) break;
+               }
+               
+               num_received += received;
+       }
+
+       for (i=0;i<state.total_received;i++) {
+               fn(&state.dirlist[i], Mask, caller_state);
+       }
+
+       talloc_destroy(state.mem_ctx);
+
+       return state.total_received;
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
+int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
+            void (*fn)(file_info *, const char *, void *), void *state)
+{
+       if (cli->transport->negotiate.protocol <= PROTOCOL_LANMAN1)
+               return cli_list_old(cli, Mask, attribute, fn, state);
+       return cli_list_new(cli, Mask, attribute, fn, state);
+}
diff --git a/source4/libcli/climessage.c b/source4/libcli/climessage.c
new file mode 100644 (file)
index 0000000..ad5d415
--- /dev/null
@@ -0,0 +1,93 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client message handling routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James J Myers 2003  <myersjj@samba.org>
+   
+   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"
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+BOOL cli_message_start(struct cli_state *cli, char *host, char *username, 
+                             int *grp)
+{
+       struct cli_request *req; 
+       
+       req = cli_request_setup(cli->tree, SMBsendstrt, 0, 0);
+       cli_req_append_string(req, username, STR_TERMINATE);
+       cli_req_append_string(req, host, STR_TERMINATE);
+       if (!cli_request_send(req) || 
+           !cli_request_receive(req) ||
+           cli_is_error(cli)) {
+               cli_request_destroy(req);
+               return False;
+       }
+
+       *grp = SVAL(req->in.vwv, VWV(0));
+       cli_request_destroy(req);
+
+       return True;
+}
+
+
+/****************************************************************************
+send a message 
+****************************************************************************/
+BOOL cli_message_text(struct cli_state *cli, char *msg, int len, int grp)
+{
+       struct cli_request *req; 
+       
+       req = cli_request_setup(cli->tree, SMBsendtxt, 1, 0);
+       SSVAL(req->out.vwv, VWV(0), grp);
+
+       cli_req_append_bytes(req, msg, len);
+
+       if (!cli_request_send(req) || 
+           !cli_request_receive(req) ||
+           cli_is_error(cli)) {
+               cli_request_destroy(req);
+               return False;
+       }
+
+       cli_request_destroy(req);
+       return True;
+}      
+
+/****************************************************************************
+end a message 
+****************************************************************************/
+BOOL cli_message_end(struct cli_state *cli, int grp)
+{
+       struct cli_request *req; 
+       
+       req = cli_request_setup(cli->tree, SMBsendend, 1, 0);
+       SSVAL(req->out.vwv, VWV(0), grp);
+
+       if (!cli_request_send(req) || 
+           !cli_request_receive(req) ||
+           cli_is_error(cli)) {
+               cli_request_destroy(req);
+               return False;
+       }
+
+       cli_request_destroy(req);
+       return True;
+}      
+
diff --git a/source4/libcli/clireadwrite.c b/source4/libcli/clireadwrite.c
new file mode 100644 (file)
index 0000000..e1d154b
--- /dev/null
@@ -0,0 +1,155 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client file read/write routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003
+   
+   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"
+
+/****************************************************************************
+  Read size bytes at offset offset using SMBreadX.
+****************************************************************************/
+ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
+{
+       union smb_read parms;
+       int readsize;
+       ssize_t total = 0;
+       
+       if (size == 0) {
+               return 0;
+       }
+
+       parms.readx.level = RAW_READ_READX;
+       parms.readx.in.fnum = fnum;
+
+       /*
+        * Set readsize to the maximum size we can handle in one readX,
+        * rounded down to a multiple of 1024.
+        */
+       readsize = (cli->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)) & ~1023;
+
+       while (total < size) {
+               NTSTATUS status;
+
+               readsize = MIN(readsize, size-total);
+
+               parms.readx.in.offset    = offset;
+               parms.readx.in.mincnt    = readsize;
+               parms.readx.in.maxcnt    = readsize;
+               parms.readx.in.remaining = size - total;
+               parms.readx.out.data     = buf + total;
+               
+               status = smb_raw_read(cli->tree, &parms);
+               
+               if (!NT_STATUS_IS_OK(status)) {
+                       return -1;
+               }
+
+               total += parms.readx.out.nread;
+               offset += parms.readx.out.nread;
+
+               /* If the server returned less than we asked for we're at EOF */
+               if (parms.readx.out.nread < readsize)
+                       break;
+       }
+
+       return total;
+}
+
+
+/****************************************************************************
+  write to a file
+  write_mode: 0x0001 disallow write cacheing
+              0x0002 return bytes remaining
+              0x0004 use raw named pipe protocol
+              0x0008 start of message mode named pipe protocol
+****************************************************************************/
+ssize_t cli_write(struct cli_state *cli,
+                 int fnum, uint16 write_mode,
+                 const char *buf, off_t offset, size_t size)
+{
+       union smb_write parms;
+       int block = (cli->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)) & ~1023;
+       ssize_t total = 0;
+
+       if (size == 0) {
+               return 0;
+       }
+
+       parms.writex.level = RAW_WRITE_WRITEX;
+       parms.writex.in.fnum = fnum;
+       parms.writex.in.wmode = write_mode;
+       parms.writex.in.remaining = 0;
+       
+       while (total < size) {
+               NTSTATUS status;
+
+               block = MIN(block, size - total);
+
+               parms.writex.in.offset = offset;
+               parms.writex.in.count = block;
+               parms.writex.in.data = buf;
+
+               status = smb_raw_write(cli->tree, &parms);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       return -1;
+               }
+
+               offset += parms.writex.out.nwritten;
+               total += parms.writex.out.nwritten;
+               buf += parms.writex.out.nwritten;
+       }
+
+       return total;
+}
+
+/****************************************************************************
+  write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+ssize_t cli_smbwrite(struct cli_state *cli,
+                    int fnum, char *buf, off_t offset, size_t size1)
+{
+       union smb_write parms;
+       ssize_t total = 0;
+
+       parms.write.level = RAW_WRITE_WRITE;
+       parms.write.in.remaining = 0;
+       
+       do {
+               size_t size = MIN(size1, cli->transport->negotiate.max_xmit - 48);
+               
+               parms.write.in.fnum = fnum;
+               parms.write.in.offset = offset;
+               parms.write.in.count = size;
+               parms.write.in.data = buf + total;
+
+               if (NT_STATUS_IS_ERR(smb_raw_write(cli->tree, &parms)))
+                       return -1;
+
+               size = parms.write.out.nwritten;
+               if (size == 0)
+                       break;
+
+               size1 -= size;
+               total += size;
+               offset += size;
+       } while (size1);
+
+       return total;
+}
diff --git a/source4/libcli/clisecdesc.c b/source4/libcli/clisecdesc.c
new file mode 100644 (file)
index 0000000..6627225
--- /dev/null
@@ -0,0 +1,121 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client security descriptor functions
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+/****************************************************************************
+  query the security descriptor for a open file
+ ****************************************************************************/
+SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum, 
+                           TALLOC_CTX *mem_ctx)
+{
+       struct smb_nttrans parms;
+       char param[8];
+       DATA_BLOB param_blob;
+       prs_struct pd;
+       SEC_DESC *psd = NULL;
+       NTSTATUS status;
+
+       param_blob.length = 8;
+       param_blob.data = &param[0];
+
+       SIVAL(param, 0, fnum);
+       SSVAL(param, 4, 0x7);
+
+       parms.in.max_param = 1024;
+       parms.in.max_data = 1024;
+       parms.in.max_setup = 0;
+       parms.in.setup_count = 18;
+       parms.in.function = NT_TRANSACT_QUERY_SECURITY_DESC;
+       parms.in.params = param_blob;
+       parms.in.data = data_blob(NULL, 0);
+       
+       status = smb_raw_nttrans(cli->tree, mem_ctx, &parms);
+       
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+               goto cleanup;
+       }
+
+       prs_init(&pd, parms.out.data.length, mem_ctx, UNMARSHALL);
+       prs_copy_data_in(&pd, parms.out.data.data, parms.out.data.length);
+       prs_set_offset(&pd,0);
+
+       if (!sec_io_desc("sd data", &psd, &pd, 1)) {
+               DEBUG(1,("Failed to parse secdesc\n"));
+               goto cleanup;
+       }
+
+ cleanup:
+       prs_mem_free(&pd);
+       return psd;
+}
+
+/****************************************************************************
+  set the security descriptor for a open file
+ ****************************************************************************/
+BOOL cli_set_secdesc(struct cli_state *cli, int fnum, SEC_DESC *sd)
+{
+       struct smb_nttrans parms;
+       char param[8];
+       DATA_BLOB param_blob;
+       prs_struct pd;
+       BOOL ret = False;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_set_secdesc");
+
+       prs_init(&pd, 0, mem_ctx, MARSHALL);
+       prs_give_memory(&pd, NULL, 0, True);
+
+       if (!sec_io_desc("sd data", &sd, &pd, 1)) {
+               DEBUG(1,("Failed to marshall secdesc\n"));
+               goto cleanup;
+       }
+       
+       param_blob.length = 8;
+       param_blob.data = &param[0];
+
+       SIVAL(param, 0, fnum);
+       SSVAL(param, 4, 0x7);
+
+       parms.in.max_param = 1000;
+       parms.in.max_data = 1000;
+       parms.in.max_setup = 0;
+       parms.in.setup_count = 18;
+       parms.in.function = NT_TRANSACT_SET_SECURITY_DESC;
+       parms.in.params = param_blob;
+       parms.in.data = data_blob(NULL, 0);
+       
+       status = smb_raw_nttrans(cli->tree, mem_ctx, &parms);
+       
+       if (NT_STATUS_IS_ERR(status)) {
+               DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n"));
+               goto cleanup;
+       }
+       ret = True;
+
+ cleanup:
+       prs_mem_free(&pd);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/libcli/clitrans2.c b/source4/libcli/clitrans2.c
new file mode 100644 (file)
index 0000000..6fd5c2c
--- /dev/null
@@ -0,0 +1,221 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client trans2 calls
+   Copyright (C) James J Myers 2003    <myersjj@samba.org>
+   
+   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"
+
+/****************************************************************************
+send a qpathinfo call
+****************************************************************************/
+BOOL cli_qpathinfo(struct cli_state *cli, const char *fname, 
+                  time_t *c_time, time_t *a_time, time_t *m_time, 
+                  size_t *size, uint16 *mode)
+{
+       union smb_fileinfo parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_qpathinfo");
+       if (!mem_ctx) return False;
+
+       parms.standard.level = RAW_FILEINFO_STANDARD;
+       parms.standard.in.fname = fname;
+
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms);
+       talloc_destroy(mem_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (c_time) {
+               *c_time = parms.standard.out.create_time;
+       }
+       if (a_time) {
+               *a_time = parms.standard.out.access_time;
+       }
+       if (m_time) {
+               *m_time = parms.standard.out.write_time;
+       }
+       if (size) {
+               *size = parms.standard.out.size;
+       }
+       if (mode) {
+               *mode = parms.standard.out.attrib;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
+****************************************************************************/
+BOOL cli_qpathinfo2(struct cli_state *cli, const char *fname, 
+                   time_t *c_time, time_t *a_time, time_t *m_time, 
+                   time_t *w_time, size_t *size, uint16 *mode,
+                   SMB_INO_T *ino)
+{
+       union smb_fileinfo parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_qfilename");
+       if (!mem_ctx) return False;
+
+       parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+       parms.all_info.in.fname = fname;
+
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms);
+       talloc_destroy(mem_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (c_time) {
+               *c_time = nt_time_to_unix(&parms.all_info.out.create_time);
+       }
+       if (a_time) {
+               *a_time = nt_time_to_unix(&parms.all_info.out.access_time);
+       }
+       if (m_time) {
+               *m_time = nt_time_to_unix(&parms.all_info.out.change_time);
+       }
+       if (w_time) {
+               *w_time = nt_time_to_unix(&parms.all_info.out.write_time);
+       }
+       if (size) {
+               *size = parms.all_info.out.size;
+       }
+       if (mode) {
+               *mode = parms.all_info.out.attrib;
+       }
+
+       return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+BOOL cli_qfilename(struct cli_state *cli, int fnum, 
+                  const char **name)
+{
+       union smb_fileinfo parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_qfilename");
+       if (!mem_ctx) return False;
+
+       parms.name_info.level = RAW_FILEINFO_NAME_INFO;
+       parms.name_info.in.fnum = fnum;
+
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &parms);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_destroy(mem_ctx);
+               *name = NULL;
+               return False;
+       }
+
+       *name = strdup(parms.name_info.out.fname.s);
+
+       talloc_destroy(mem_ctx);
+
+       return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+BOOL cli_qfileinfo(struct cli_state *cli, int fnum, 
+                  uint16 *mode, size_t *size,
+                  time_t *c_time, time_t *a_time, time_t *m_time, 
+                  time_t *w_time, SMB_INO_T *ino)
+{
+       union smb_fileinfo parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("cli_qfileinfo");
+       if (!mem_ctx) return False;
+
+       parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+       parms.all_info.in.fnum = fnum;
+
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &parms);
+       talloc_destroy(mem_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       if (c_time) {
+               *c_time = nt_time_to_unix(&parms.all_info.out.create_time);
+       }
+       if (a_time) {
+               *a_time = nt_time_to_unix(&parms.all_info.out.access_time);
+       }
+       if (m_time) {
+               *m_time = nt_time_to_unix(&parms.all_info.out.change_time);
+       }
+       if (w_time) {
+               *w_time = nt_time_to_unix(&parms.all_info.out.write_time);
+       }
+       if (mode) {
+               *mode = parms.all_info.out.attrib;
+       }
+       if (size) {
+               *size = (size_t)parms.all_info.out.size;
+       }
+       if (ino) {
+               *ino = 0;
+       }
+
+       return True;
+}
+
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, 
+                               const char **alt_name)
+{
+       union smb_fileinfo parms;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       parms.alt_name_info.level = RAW_FILEINFO_ALT_NAME_INFO;
+       parms.alt_name_info.in.fname = fname;
+
+       mem_ctx = talloc_init("cli_qpathinfo_alt_name");
+       if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_destroy(mem_ctx);
+               *alt_name = NULL;
+               return cli_nt_error(cli);
+       }
+
+       *alt_name = strdup(parms.alt_name_info.out.fname.s);
+
+       talloc_destroy(mem_ctx);
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/libcli/namecache.c b/source4/libcli/namecache.c
new file mode 100644 (file)
index 0000000..9f4796a
--- /dev/null
@@ -0,0 +1,246 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   NetBIOS name cache module on top of gencache mechanism.
+   
+   Copyright (C) Tim Potter         2002
+   Copyright (C) Rafal Szczesniak   2002
+   
+   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"
+
+#define NBTKEY_FMT  "NBT/%s#%02X"
+
+
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ * 
+ * @return true upon successful initialisation of the cache or
+ *         false on failure
+ **/
+
+BOOL namecache_enableTODO(void)
+{
+       /*
+        * Check if name caching disabled by setting the name cache
+        * timeout to zero.
+        */ 
+
+       if (lp_name_cache_timeout() == 0) {
+               DEBUG(5, ("namecache_enable: disabling netbios name cache\n"));
+               return False;
+       }
+
+       /* Init namecache by calling gencache initialisation */
+
+       if (!gencache_init()) {
+               DEBUG(2, ("namecache_enable: Couldn't initialise namecache on top of gencache.\n"));
+               return False;
+       }
+
+       /* I leave it for now, though I don't think we really need this (mimir, 27.09.2002) */
+       DEBUG(5, ("namecache_enable: enabling netbios namecache, timeout %d "
+                 "seconds\n", lp_name_cache_timeout()));
+
+       return True;
+}
+
+
+/**
+ * Shutdown namecache. Routine calls gencache close function
+ * to safely close gencache file.
+ *
+ * @return true upon successful shutdown of the cache or
+ *         false on failure
+ **/
+BOOL namecache_shutdownTODO(void)
+{
+       if (!gencache_shutdown()) {
+               DEBUG(2, ("namecache_shutdown: Couldn't close namecache on top of gencache.\n"));
+               return False;
+       }
+       
+       DEBUG(5, ("namecache_shutdown: netbios namecache closed successfully.\n"));
+       return True;
+}
+
+
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ *         type number
+ */
+
+static char* namecache_key(TALLOC_CTX *mem_ctx, const char *name, int name_type)
+{
+       char *keystr;
+       asprintf(&keystr, NBTKEY_FMT, strupper_talloc(mem_ctx, name), name_type);
+
+       return keystr;
+}
+
+
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ *        ip addresses being stored
+ **/
+
+BOOL namecache_store(TALLOC_CTX *mem_ctx, const char *name, int name_type,
+                     int num_names, struct in_addr *ip_list)
+{
+       time_t expiry;
+       char *key, *value_string;
+       int i;
+
+       /*
+        * we use gecache call to avoid annoying debug messages about
+        * initialised namecache again and again...
+        */
+       if (!gencache_init()) return False;
+
+       DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
+                 num_names, num_names == 1 ? "": "es", name, name_type));
+
+       for (i = 0; i < num_names; i++) 
+               DEBUGADD(5, ("%s%s", inet_ntoa(ip_list[i]),
+                            i == (num_names - 1) ? "" : ", "));
+
+       DEBUGADD(5, ("\n"));
+
+       key = namecache_key(mem_ctx, name, name_type);
+
+       /* 
+        * Cache pdc location or dc lists for only a little while
+        * otherwise if we lock on to a bad DC we can potentially be
+        * out of action for the entire cache timeout time!
+        */
+
+       if (name_type == 0x1b || name_type == 0x1c)
+               expiry = time(NULL) + 10;
+       else
+               expiry = time(NULL) + lp_name_cache_timeout();
+
+       /*
+        * Generate string representation of ip addresses list
+        * First, store the number of ip addresses and then
+        * place each single ip
+        */
+       ipstr_list_make(&value_string, ip_list, num_names);
+       
+       /* set the entry */
+       return (gencache_set(key, value_string, expiry));
+}
+
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ *        NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ *         false if name isn't found in the cache or has expired
+ **/
+
+BOOL namecache_fetch(TALLOC_CTX *mem_ctx, const char *name, int name_type, struct in_addr **ip_list,
+                     int *num_names)
+{
+       char *key, *value;
+       time_t timeout;
+
+       *num_names = 0;
+
+       /* exit now if null pointers were passed as they're required further */
+       if (!ip_list || !num_names) return False;
+
+       if (!gencache_init())
+               return False;
+
+       /* 
+        * Use gencache interface - lookup the key
+        */
+       key = namecache_key(mem_ctx, name, name_type);
+
+       if (!gencache_get(key, &value, &timeout)) {
+               DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+               SAFE_FREE(key);
+               return False;
+       } else {
+               DEBUG(5, ("name %s#%02X found.\n", name, name_type));
+       }
+       
+       /*
+        * Split up the stored value into the list of IP adresses
+        */
+       *num_names = ipstr_list_parse(value, ip_list);
+       
+       SAFE_FREE(key);
+       SAFE_FREE(value);                
+       return *num_names > 0;          /* true only if some ip has been fetched */
+}
+
+
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+       gencache_del(key);
+       DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return True upon successful deletion or
+ *         False in case of an error
+ **/
+
+void namecache_flush(void)
+{
+       if (!gencache_init())
+               return;
+
+       /* 
+        * iterate through each NBT cache's entry and flush it
+        * by flush_netbios_name function
+        */
+       gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+       DEBUG(5, ("Namecache flushed\n"));
+}
+
diff --git a/source4/libcli/namequery.c b/source4/libcli/namequery.c
new file mode 100644 (file)
index 0000000..2f6343d
--- /dev/null
@@ -0,0 +1,1333 @@
+/* 
+   Unix SMB/CIFS implementation.
+   name query routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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"
+
+/* nmbd.c sets this to True. */
+BOOL global_in_nmbd = False;
+
+/****************************************************************************
+generate a random trn_id
+****************************************************************************/
+static int generate_trn_id(void)
+{
+       static int trn_id;
+
+       if (trn_id == 0) {
+               sys_srandom(getpid());
+       }
+
+       trn_id = sys_random();
+
+       return trn_id % (unsigned)0x7FFF;
+}
+
+
+/****************************************************************************
+ parse a node status response into an array of structures
+****************************************************************************/
+static struct node_status *parse_node_status(char *p, int *num_names)
+{
+       struct node_status *ret;
+       int i;
+
+       *num_names = CVAL(p,0);
+
+       if (*num_names == 0) return NULL;
+
+       ret = (struct node_status *)malloc(sizeof(struct node_status)* (*num_names));
+       if (!ret) return NULL;
+
+       p++;
+       for (i=0;i< *num_names;i++) {
+               StrnCpy(ret[i].name,p,15);
+               trim_string(ret[i].name,NULL," ");
+               ret[i].type = CVAL(p,15);
+               ret[i].flags = p[16];
+               p += 18;
+               DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name, 
+                          ret[i].type, ret[i].flags));
+       }
+       return ret;
+}
+
+
+/****************************************************************************
+do a NBT node status query on an open socket and return an array of
+structures holding the returned names or NULL if the query failed
+**************************************************************************/
+struct node_status *node_status_query(int fd,struct nmb_name *name,
+                                     struct in_addr to_ip, int *num_names)
+{
+       BOOL found=False;
+       int retries = 2;
+       int retry_time = 2000;
+       struct timeval tval;
+       struct packet_struct p;
+       struct packet_struct *p2;
+       struct nmb_packet *nmb = &p.packet.nmb;
+       struct node_status *ret;
+
+       ZERO_STRUCT(p);
+
+       nmb->header.name_trn_id = generate_trn_id();
+       nmb->header.opcode = 0;
+       nmb->header.response = False;
+       nmb->header.nm_flags.bcast = False;
+       nmb->header.nm_flags.recursion_available = False;
+       nmb->header.nm_flags.recursion_desired = False;
+       nmb->header.nm_flags.trunc = False;
+       nmb->header.nm_flags.authoritative = False;
+       nmb->header.rcode = 0;
+       nmb->header.qdcount = 1;
+       nmb->header.ancount = 0;
+       nmb->header.nscount = 0;
+       nmb->header.arcount = 0;
+       nmb->question.question_name = *name;
+       nmb->question.question_type = 0x21;
+       nmb->question.question_class = 0x1;
+
+       p.ip = to_ip;
+       p.port = NMB_PORT;
+       p.fd = fd;
+       p.timestamp = time(NULL);
+       p.packet_type = NMB_PACKET;
+       
+       GetTimeOfDay(&tval);
+  
+       if (!send_packet(&p)) 
+               return NULL;
+
+       retries--;
+
+       while (1) {
+               struct timeval tval2;
+               GetTimeOfDay(&tval2);
+               if (TvalDiff(&tval,&tval2) > retry_time) {
+                       if (!retries)
+                               break;
+                       if (!found && !send_packet(&p))
+                               return NULL;
+                       GetTimeOfDay(&tval);
+                       retries--;
+               }
+
+               if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {     
+                       struct nmb_packet *nmb2 = &p2->packet.nmb;
+                       debug_nmb_packet(p2);
+                       
+                       if (nmb2->header.opcode != 0 ||
+                           nmb2->header.nm_flags.bcast ||
+                           nmb2->header.rcode ||
+                           !nmb2->header.ancount ||
+                           nmb2->answers->rr_type != 0x21) {
+                               /* XXXX what do we do with this? could be a
+                                  redirect, but we'll discard it for the
+                                  moment */
+                               free_packet(p2);
+                               continue;
+                       }
+
+                       ret = parse_node_status(&nmb2->answers->rdata[0], num_names);
+                       free_packet(p2);
+                       return ret;
+               }
+       }
+       
+       return NULL;
+}
+
+
+/****************************************************************************
+find the first type XX name in a node status reply - used for finding
+a servers name given its IP
+return the matched name in *name
+**************************************************************************/
+
+BOOL name_status_find(const char *q_name, int q_type, int type, struct in_addr to_ip, char *name)
+{
+       struct node_status *status = NULL;
+       struct nmb_name nname;
+       int count, i;
+       int sock;
+       BOOL result = False;
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n", q_name, q_type));
+               return False;
+       }
+
+       DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name, 
+                  q_type, inet_ntoa(to_ip)));
+
+       sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True);
+       if (sock == -1)
+               goto done;
+
+       /* W2K PDC's seem not to respond to '*'#0. JRA */
+       make_nmb_name(&nname, q_name, q_type);
+       status = node_status_query(sock, &nname, to_ip, &count);
+       close(sock);
+       if (!status)
+               goto done;
+
+       for (i=0;i<count;i++) {
+               if (status[i].type == type)
+                       break;
+       }
+       if (i == count)
+               goto done;
+
+       pull_ascii(name, status[i].name, 16, 15, STR_TERMINATE);
+       result = True;
+
+ done:
+       SAFE_FREE(status);
+
+       DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+       if (result)
+               DEBUGADD(10, (", ip address is %s", inet_ntoa(to_ip)));
+
+       DEBUG(10, ("\n"));      
+
+       return result;
+}
+
+
+/*
+  comparison function used by sort_ip_list
+*/
+int ip_compare(struct in_addr *ip1, struct in_addr *ip2)
+{
+       int max_bits1=0, max_bits2=0;
+       int num_interfaces = iface_count();
+       int i;
+
+       for (i=0;i<num_interfaces;i++) {
+               struct in_addr ip;
+               int bits1, bits2;
+               ip = *iface_n_bcast(i);
+               bits1 = matching_quad_bits((uchar *)&ip1->s_addr, (uchar *)&ip.s_addr);
+               bits2 = matching_quad_bits((uchar *)&ip2->s_addr, (uchar *)&ip.s_addr);
+               max_bits1 = MAX(bits1, max_bits1);
+               max_bits2 = MAX(bits2, max_bits2);
+       }       
+       
+       /* bias towards directly reachable IPs */
+       if (iface_local(*ip1)) {
+               max_bits1 += 32;
+       }
+       if (iface_local(*ip2)) {
+               max_bits2 += 32;
+       }
+
+       return max_bits2 - max_bits1;
+}
+
+/*
+  sort an IP list so that names that are close to one of our interfaces 
+  are at the top. This prevents the problem where a WINS server returns an IP that
+  is not reachable from our subnet as the first match
+*/
+static void sort_ip_list(struct in_addr *iplist, int count)
+{
+       if (count <= 1) {
+               return;
+       }
+
+       qsort(iplist, count, sizeof(struct in_addr), QSORT_CAST ip_compare);    
+}
+
+
+/****************************************************************************
+ Do a netbios name query to find someones IP.
+ Returns an array of IP addresses or NULL if none.
+ *count will be set to the number of addresses returned.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+struct in_addr *name_query(int fd,const char *name,int name_type, 
+                          BOOL bcast,BOOL recurse,
+                          struct in_addr to_ip, int *count, int *flags,
+                          BOOL *timed_out)
+{
+       BOOL found=False;
+       int i, retries = 3;
+       int retry_time = bcast?250:2000;
+       struct timeval tval;
+       struct packet_struct p;
+       struct packet_struct *p2;
+       struct nmb_packet *nmb = &p.packet.nmb;
+       struct in_addr *ip_list = NULL;
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("name_query(%s#%02x): netbios is disabled\n", name, name_type));
+               return NULL;
+       }
+
+       if (timed_out) {
+               *timed_out = False;
+       }
+       
+       memset((char *)&p,'\0',sizeof(p));
+       (*count) = 0;
+       (*flags) = 0;
+       
+       nmb->header.name_trn_id = generate_trn_id();
+       nmb->header.opcode = 0;
+       nmb->header.response = False;
+       nmb->header.nm_flags.bcast = bcast;
+       nmb->header.nm_flags.recursion_available = False;
+       nmb->header.nm_flags.recursion_desired = recurse;
+       nmb->header.nm_flags.trunc = False;
+       nmb->header.nm_flags.authoritative = False;
+       nmb->header.rcode = 0;
+       nmb->header.qdcount = 1;
+       nmb->header.ancount = 0;
+       nmb->header.nscount = 0;
+       nmb->header.arcount = 0;
+       
+       make_nmb_name(&nmb->question.question_name,name,name_type);
+       
+       nmb->question.question_type = 0x20;
+       nmb->question.question_class = 0x1;
+       
+       p.ip = to_ip;
+       p.port = NMB_PORT;
+       p.fd = fd;
+       p.timestamp = time(NULL);
+       p.packet_type = NMB_PACKET;
+       
+       GetTimeOfDay(&tval);
+       
+       if (!send_packet(&p)) 
+               return NULL;
+       
+       retries--;
+       
+       while (1) {
+               struct timeval tval2;
+               struct in_addr *tmp_ip_list;
+               
+               GetTimeOfDay(&tval2);
+               if (TvalDiff(&tval,&tval2) > retry_time) {
+                       if (!retries)
+                               break;
+                       if (!found && !send_packet(&p))
+                               return NULL;
+                       GetTimeOfDay(&tval);
+                       retries--;
+               }
+               
+               if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {     
+                       struct nmb_packet *nmb2 = &p2->packet.nmb;
+                       debug_nmb_packet(p2);
+                       
+                       /* If we get a Negative Name Query Response from a WINS
+                        * server, we should report it and give up.
+                        */
+                       if( 0 == nmb2->header.opcode            /* A query response   */
+                           && !(bcast)                 /* from a WINS server */
+                           && nmb2->header.rcode               /* Error returned     */
+                               ) {
+                               
+                               if (DEBUGLVL(3)) {
+                                       /* Only executed if DEBUGLEVEL >= 3 */
+                                       DEBUG(3,("Negative name query response, rcode 0x%02x: ", nmb2->header.rcode ));
+                                       switch( nmb2->header.rcode ) {
+                                       case 0x01:
+                                               DEBUG(3,("Request was invalidly formatted.\n" ));
+                                               break;
+                                       case 0x02:
+                                               DEBUG(3,("Problem with NBNS, cannot process name.\n"));
+                                               break;
+                                       case 0x03:
+                                               DEBUG(3,("The name requested does not exist.\n" ));
+                                               break;
+                                       case 0x04:
+                                               DEBUG(3,("Unsupported request error.\n" ));
+                                               break;
+                                       case 0x05:
+                                               DEBUG(3,("Query refused error.\n" ));
+                                               break;
+                                       default:
+                                               DEBUG(3,("Unrecognized error code.\n" ));
+                                               break;
+                                       }
+                               }
+                               free_packet(p2);
+                               return( NULL );
+                       }
+                       
+                       if (nmb2->header.opcode != 0 ||
+                           nmb2->header.nm_flags.bcast ||
+                           nmb2->header.rcode ||
+                           !nmb2->header.ancount) {
+                               /* 
+                                * XXXX what do we do with this? Could be a
+                                * redirect, but we'll discard it for the
+                                * moment.
+                                */
+                               free_packet(p2);
+                               continue;
+                       }
+                       
+                       tmp_ip_list = (struct in_addr *)Realloc( ip_list, sizeof( ip_list[0] )
+                                                                * ( (*count) + nmb2->answers->rdlength/6 ) );
+                       
+                       if (!tmp_ip_list) {
+                               DEBUG(0,("name_query: Realloc failed.\n"));
+                               SAFE_FREE(ip_list);
+                       }
+                       
+                       ip_list = tmp_ip_list;
+                       
+                       if (ip_list) {
+                               DEBUG(2,("Got a positive name query response from %s ( ", inet_ntoa(p2->ip)));
+                               for (i=0;i<nmb2->answers->rdlength/6;i++) {
+                                       putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
+                                       DEBUGADD(2,("%s ",inet_ntoa(ip_list[(*count)])));
+                                       (*count)++;
+                               }
+                               DEBUGADD(2,(")\n"));
+                       }
+                       
+                       found=True;
+                       retries=0;
+                       /* We add the flags back ... */
+                       if (nmb2->header.response)
+                               (*flags) |= NM_FLAGS_RS;
+                       if (nmb2->header.nm_flags.authoritative)
+                               (*flags) |= NM_FLAGS_AA;
+                       if (nmb2->header.nm_flags.trunc)
+                               (*flags) |= NM_FLAGS_TC;
+                       if (nmb2->header.nm_flags.recursion_desired)
+                               (*flags) |= NM_FLAGS_RD;
+                       if (nmb2->header.nm_flags.recursion_available)
+                               (*flags) |= NM_FLAGS_RA;
+                       if (nmb2->header.nm_flags.bcast)
+                               (*flags) |= NM_FLAGS_B;
+                       free_packet(p2);
+                       /*
+                        * If we're doing a unicast lookup we only
+                        * expect one reply. Don't wait the full 2
+                        * seconds if we got one. JRA.
+                        */
+                       if(!bcast && found)
+                               break;
+               }
+       }
+
+       if (timed_out) {
+               *timed_out = True;
+       }
+
+       /* sort the ip list so we choose close servers first if possible */
+       sort_ip_list(ip_list, *count);
+
+       return ip_list;
+}
+
+/********************************************************
+ Start parsing the lmhosts file.
+*********************************************************/
+
+XFILE *startlmhosts(char *fname)
+{
+       XFILE *fp = x_fopen(fname,O_RDONLY, 0);
+       if (!fp) {
+               DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
+                        fname, strerror(errno)));
+               return NULL;
+       }
+       return fp;
+}
+
+/********************************************************
+ Parse the next line in the lmhosts file.
+*********************************************************/
+
+BOOL getlmhostsent( TALLOC_CTX *mem_ctx,
+               XFILE *fp, pstring name, int *name_type, struct in_addr *ipaddr)
+{
+  pstring line;
+
+  while(!x_feof(fp) && !x_ferror(fp)) {
+    pstring ip,flags,extra;
+    const char *ptr;
+    char *ptr1;
+    int count = 0;
+
+    *name_type = -1;
+
+    if (!fgets_slash(line,sizeof(pstring),fp))
+      continue;
+
+    if (*line == '#')
+      continue;
+
+    pstrcpy(ip,"");
+    pstrcpy(name,"");
+    pstrcpy(flags,"");
+
+    ptr = line;
+
+    if (next_token(&ptr,ip   ,NULL,sizeof(ip)))
+      ++count;
+    if (next_token(&ptr,name ,NULL, sizeof(pstring)))
+      ++count;
+    if (next_token(&ptr,flags,NULL, sizeof(flags)))
+      ++count;
+    if (next_token(&ptr,extra,NULL, sizeof(extra)))
+      ++count;
+
+    if (count <= 0)
+      continue;
+
+    if (count > 0 && count < 2)
+    {
+      DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
+      continue;
+    }
+
+    if (count >= 4)
+    {
+      DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
+      continue;
+    }
+
+    DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
+
+    if (strchr_m(flags,'G') || strchr_m(flags,'S'))
+    {
+      DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
+      continue;
+    }
+
+    *ipaddr = *interpret_addr2(mem_ctx, ip);
+
+    /* Extra feature. If the name ends in '#XX', where XX is a hex number,
+       then only add that name type. */
+    if((ptr1 = strchr_m(name, '#')) != NULL)
+    {
+      char *endptr;
+
+      ptr1++;
+      *name_type = (int)strtol(ptr1, &endptr, 16);
+
+      if(!*ptr1 || (endptr == ptr1))
+      {
+        DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
+        continue;
+      }
+
+      *(--ptr1) = '\0'; /* Truncate at the '#' */
+    }
+
+    return True;
+  }
+
+  return False;
+}
+
+/********************************************************
+ Finish parsing the lmhosts file.
+*********************************************************/
+
+void endlmhosts(XFILE *fp)
+{
+       x_fclose(fp);
+}
+
+
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+BOOL name_resolve_bcast(const char *name, int name_type,
+                       struct in_addr **return_ip_list, int *return_count)
+{
+       int sock, i;
+       int num_interfaces = iface_count();
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n", name, name_type));
+               return False;
+       }
+
+       *return_ip_list = NULL;
+       *return_count = 0;
+       
+       /*
+        * "bcast" means do a broadcast lookup on all the local interfaces.
+        */
+
+       DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup for name %s<0x%x>\n", name, name_type));
+
+       sock = open_socket_in( SOCK_DGRAM, 0, 3,
+                              interpret_addr(lp_socket_address()), True );
+
+       if (sock == -1) return False;
+
+       set_socket_options(sock,"SO_BROADCAST");
+       /*
+        * Lookup the name on all the interfaces, return on
+        * the first successful match.
+        */
+       for( i = num_interfaces-1; i >= 0; i--) {
+               struct in_addr sendto_ip;
+               int flags;
+               /* Done this way to fix compiler error on IRIX 5.x */
+               sendto_ip = *iface_n_bcast(i);
+               *return_ip_list = name_query(sock, name, name_type, True, 
+                                   True, sendto_ip, return_count, &flags, NULL);
+               if(*return_ip_list != NULL) {
+                       close(sock);
+                       return True;
+               }
+       }
+
+       close(sock);
+       return False;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+BOOL resolve_wins(TALLOC_CTX *mem_ctx, const char *name, int name_type,
+                 struct in_addr **return_iplist, int *return_count)
+{
+       int sock, t, i;
+       char **wins_tags;
+       struct in_addr src_ip;
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n", name, name_type));
+               return False;
+       }
+
+       *return_iplist = NULL;
+       *return_count = 0;
+       
+       DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n", name, name_type));
+
+       if (wins_srv_count() < 1) {
+               DEBUG(3,("resolve_wins: WINS server resolution selected and no WINS servers listed.\n"));
+               return False;
+       }
+
+       /* we try a lookup on each of the WINS tags in turn */
+       wins_tags = wins_srv_tags();
+
+       if (!wins_tags) {
+               /* huh? no tags?? give up in disgust */
+               return False;
+       }
+
+       /* the address we will be sending from */
+       src_ip = *interpret_addr2(mem_ctx, lp_socket_address());
+
+       /* in the worst case we will try every wins server with every
+          tag! */
+       for (t=0; wins_tags && wins_tags[t]; t++) {
+               int srv_count = wins_srv_count_tag(wins_tags[t]);
+               for (i=0; i<srv_count; i++) {
+                       struct in_addr wins_ip;
+                       int flags;
+                       BOOL timed_out;
+
+                       wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+
+                       if (global_in_nmbd && ismyip(wins_ip)) {
+                               /* yikes! we'll loop forever */
+                               continue;
+                       }
+
+                       /* skip any that have been unresponsive lately */
+                       if (wins_srv_is_dead(wins_ip, src_ip)) {
+                               continue;
+                       }
+
+                       DEBUG(3,("resolve_wins: using WINS server %s and tag '%s'\n", inet_ntoa(wins_ip), wins_tags[t]));
+
+                       sock = open_socket_in(SOCK_DGRAM, 0, 3, src_ip.s_addr, True);
+                       if (sock == -1) {
+                               continue;
+                       }
+
+                       *return_iplist = name_query(sock,name,name_type, False, 
+                                                   True, wins_ip, return_count, &flags, 
+                                                   &timed_out);
+                       if (*return_iplist != NULL) {
+                               goto success;
+                       }
+                       close(sock);
+
+                       if (timed_out) {
+                               /* Timed out wating for WINS server to respond.  Mark it dead. */
+                               wins_srv_died(wins_ip, src_ip);
+                       } else {
+                               /* The name definately isn't in this
+                                  group of WINS servers. goto the next group  */
+                               break;
+                       }
+               }
+       }
+
+       wins_srv_tags_free(wins_tags);
+       return False;
+
+success:
+       wins_srv_tags_free(wins_tags);
+       close(sock);
+       return True;
+}
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static BOOL resolve_hosts(const char *name,
+                         struct in_addr **return_iplist, int *return_count)
+{
+       /*
+        * "host" means do a localhost, or dns lookup.
+        */
+       struct hostent *hp;
+
+       *return_iplist = NULL;
+       *return_count = 0;
+
+       DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x20>\n", name));
+       
+       if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
+               struct in_addr return_ip;
+               putip((char *)&return_ip,(char *)hp->h_addr);
+               *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr));
+               if(*return_iplist == NULL) {
+                       DEBUG(3,("resolve_hosts: malloc fail !\n"));
+                       return False;
+               }
+               **return_iplist = return_ip;
+               *return_count = 1;
+               return True;
+       }
+       return False;
+}
+
+/********************************************************
+ Internal interface to resolve a name into an IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+static BOOL internal_resolve_name(TALLOC_CTX *mem_ctx, const char *name, int name_type,
+                                 struct in_addr **return_iplist, int *return_count)
+{
+  char *name_resolve_list;
+  fstring tok;
+  const char *ptr;
+  BOOL allones = (strcmp(name,"255.255.255.255") == 0);
+  BOOL allzeros = (strcmp(name,"0.0.0.0") == 0);
+  BOOL is_address = is_ipaddress(name);
+  BOOL result = False;
+  struct in_addr *nodupes_iplist;
+  int i;
+
+  *return_iplist = NULL;
+  *return_count = 0;
+
+  DEBUG(10, ("internal_resolve_name: looking up %s#%x\n", name, name_type));
+
+  if (allzeros || allones || is_address) {
+       *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr));
+       if(*return_iplist == NULL) {
+               DEBUG(3,("internal_resolve_name: malloc fail !\n"));
+               return False;
+       }
+       if(is_address) { 
+               /* if it's in the form of an IP address then get the lib to interpret it */
+               if (((*return_iplist)->s_addr = inet_addr(name)) == 0xFFFFFFFF ){
+                       DEBUG(1,("internal_resolve_name: inet_addr failed on %s\n", name));
+                       return False;
+               }
+       } else {
+               (*return_iplist)->s_addr = allones ? 0xFFFFFFFF : 0;
+               *return_count = 1;
+       }
+    return True;
+  }
+  
+  /* Check netbios name cache */
+
+  if (namecache_fetch(mem_ctx, name, name_type, return_iplist, return_count)) {
+
+         /* This could be a negative response */
+
+         return (*return_count > 0);
+  }
+
+  name_resolve_list = talloc_strdup(mem_ctx, lp_name_resolve_order());
+  ptr = name_resolve_list;
+  if (!ptr || !*ptr)
+    ptr = "host";
+
+  while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
+         if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+                 if (name_type == 0x20) {
+                         if (resolve_hosts(name, return_iplist, return_count)) {
+                                 result = True;
+                                 goto done;
+                         }
+                 }
+         } else if(strequal( tok, "lmhosts")) {
+                       /* REWRITE: add back in? */
+                       DEBUG(0,("resolve_name: REWRITE: add lmhosts back?? %s\n", tok));
+         } else if(strequal( tok, "wins")) {
+                 /* don't resolve 1D via WINS */
+                 if (name_type != 0x1D &&
+                     resolve_wins(mem_ctx, name, name_type, return_iplist, return_count)) {
+                   result = True;
+                   goto done;
+                 }
+         } else if(strequal( tok, "bcast")) {
+                 if (name_resolve_bcast(name, name_type, return_iplist, return_count)) {
+                   result = True;
+                   goto done;
+                 }
+         } else {
+                 DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
+         }
+  }
+
+  /* All of the resolve_* functions above have returned false. */
+
+  SAFE_FREE(*return_iplist);
+  *return_count = 0;
+
+  return False;
+
+ done:
+
+  /* Remove duplicate entries.  Some queries, notably #1c (domain
+     controllers) return the PDC in iplist[0] and then all domain
+     controllers including the PDC in iplist[1..n].  Iterating over
+     the iplist when the PDC is down will cause two sets of timeouts. */
+
+  if (*return_count && (nodupes_iplist = (struct in_addr *)
+       malloc(sizeof(struct in_addr) * (*return_count)))) {
+         int nodupes_count = 0;
+
+         /* Iterate over return_iplist looking for duplicates */
+
+         for (i = 0; i < *return_count; i++) {
+                 BOOL is_dupe = False;
+                 int j;
+
+                 for (j = i + 1; j < *return_count; j++) {
+                         if (ip_equal((*return_iplist)[i], 
+                                      (*return_iplist)[j])) {
+                                 is_dupe = True;
+                                 break;
+                         }
+                 }
+
+                 if (!is_dupe) {
+
+                         /* This one not a duplicate */
+
+                         nodupes_iplist[nodupes_count] = (*return_iplist)[i];
+                         nodupes_count++;
+                 }
+         }
+         
+         /* Switcheroo with original list */
+         
+         free(*return_iplist);
+
+         *return_iplist = nodupes_iplist;
+         *return_count = nodupes_count;
+  }
+  /* Save in name cache */
+  for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++)
+    DEBUG(100, ("Storing name %s of type %d (ip: %s)\n", name,
+                name_type, inet_ntoa((*return_iplist)[i])));
+    
+  namecache_store(mem_ctx, name, name_type, *return_count, *return_iplist);
+
+  /* Display some debugging info */
+
+  DEBUG(10, ("internal_resolve_name: returning %d addresses: ", 
+            *return_count));
+
+  for (i = 0; i < *return_count; i++)
+         DEBUGADD(10, ("%s ", inet_ntoa((*return_iplist)[i])));
+
+  DEBUG(10, ("\n"));
+
+  return result;
+}
+
+/********************************************************
+ Internal interface to resolve a name into one IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+BOOL resolve_name(TALLOC_CTX *mem_ctx, const char *name, struct in_addr *return_ip, int name_type)
+{
+       struct in_addr *ip_list = NULL;
+       int count = 0;
+
+       if (is_ipaddress(name)) {
+               *return_ip = *interpret_addr2(mem_ctx, name);
+               return True;
+       }
+
+       if (internal_resolve_name(mem_ctx, name, name_type, &ip_list, &count)) {
+               int i;
+               /* only return valid addresses for TCP connections */
+               for (i=0; i<count; i++) {
+                       char *ip_str = inet_ntoa(ip_list[i]);
+                       if (ip_str &&
+                           strcmp(ip_str, "255.255.255.255") != 0 &&
+                           strcmp(ip_str, "0.0.0.0") != 0) {
+                               *return_ip = ip_list[i];
+                               SAFE_FREE(ip_list);
+                               return True;
+                       }
+               }
+       }
+       SAFE_FREE(ip_list);
+       return False;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+BOOL find_master_ip(TALLOC_CTX *mem_ctx, const char *group, struct in_addr *master_ip)
+{
+       struct in_addr *ip_list = NULL;
+       int count = 0;
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+               return False;
+       }
+
+       if (internal_resolve_name(mem_ctx, group, 0x1D, &ip_list, &count)) {
+               *master_ip = ip_list[0];
+               SAFE_FREE(ip_list);
+               return True;
+       }
+       if(internal_resolve_name(mem_ctx, group, 0x1B, &ip_list, &count)) {
+               *master_ip = ip_list[0];
+               SAFE_FREE(ip_list);
+               return True;
+       }
+
+       SAFE_FREE(ip_list);
+       return False;
+}
+
+/********************************************************
+ Lookup a DC name given a Domain name and IP address.
+*********************************************************/
+
+BOOL lookup_dc_name(const char *srcname, const char *domain, 
+                   struct in_addr *dc_ip, char *ret_name)
+{
+#if !defined(I_HATE_WINDOWS_REPLY_CODE)        
+       fstring dc_name;
+       BOOL ret;
+
+       if (lp_disable_netbios()) {
+               DEBUG(5,("lookup_dc_name(%s): netbios is disabled\n", domain));
+               return False;
+       }
+       
+       /*
+        * Due to the fact win WinNT *sucks* we must do a node status
+        * query here... JRA.
+        */
+       
+       *dc_name = '\0';
+       
+       ret = name_status_find(domain, 0x1c, 0x20, *dc_ip, dc_name);
+
+       if(ret && *dc_name) {
+               fstrcpy(ret_name, dc_name);
+               return True;
+       }
+       
+       return False;
+
+#else /* defined(I_HATE_WINDOWS_REPLY_CODE) */
+
+JRA - This code is broken with BDC rollover - we need to do a full
+NT GETDC call, UNICODE, NT domain SID and uncle tom cobbley and all...
+
+       int retries = 3;
+       int retry_time = 2000;
+       struct timeval tval;
+       struct packet_struct p;
+       struct dgram_packet *dgram = &p.packet.dgram;
+       char *ptr,*p2;
+       char tmp[4];
+       int len;
+       struct sockaddr_in sock_name;
+       int sock_len = sizeof(sock_name);
+       const char *mailslot = NET_LOGON_MAILSLOT;
+       char *mailslot_name;
+       char buffer[1024];
+       char *bufp;
+       int dgm_id = generate_trn_id();
+       int sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True );
+       
+       if(sock == -1)
+               return False;
+       
+       /* Find out the transient UDP port we have been allocated. */
+       if(getsockname(sock, (struct sockaddr *)&sock_name, &sock_len)<0) {
+               DEBUG(0,("lookup_pdc_name: Failed to get local UDP port. Error was %s\n",
+                        strerror(errno)));
+               close(sock);
+               return False;
+       }
+
+       /*
+        * Create the request data.
+        */
+
+       memset(buffer,'\0',sizeof(buffer));
+       bufp = buffer;
+       SSVAL(bufp,0,QUERYFORPDC);
+       bufp += 2;
+       fstrcpy(bufp,srcname);
+       bufp += (strlen(bufp) + 1);
+       slprintf(bufp, sizeof(fstring)-1, "\\MAILSLOT\\NET\\GETDC%d", dgm_id);
+       mailslot_name = bufp;
+       bufp += (strlen(bufp) + 1);
+       bufp = ALIGN2(bufp, buffer);
+       bufp += push_ucs2(NULL, bufp, srcname, sizeof(buffer) - (bufp - buffer), STR_TERMINATE);        
+       
+       SIVAL(bufp,0,1);
+       SSVAL(bufp,4,0xFFFF); 
+       SSVAL(bufp,6,0xFFFF); 
+       bufp += 8;
+       len = PTR_DIFF(bufp,buffer);
+
+       memset((char *)&p,'\0',sizeof(p));
+
+       /* DIRECT GROUP or UNIQUE datagram. */
+       dgram->header.msg_type = 0x10;
+       dgram->header.flags.node_type = M_NODE;
+       dgram->header.flags.first = True;
+       dgram->header.flags.more = False;
+       dgram->header.dgm_id = dgm_id;
+       dgram->header.source_ip = *iface_ip(*pdc_ip);
+       dgram->header.source_port = ntohs(sock_name.sin_port);
+       dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+       dgram->header.packet_offset = 0;
+       
+       make_nmb_name(&dgram->source_name,srcname,0);
+       make_nmb_name(&dgram->dest_name,domain,0x1C);
+       
+       ptr = &dgram->data[0];
+       
+       /* Setup the smb part. */
+       ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+       memcpy(tmp,ptr,4);
+       set_message(ptr,17,17 + len,True);
+       memcpy(ptr,tmp,4);
+
+       CVAL(ptr,smb_com) = SMBtrans;
+       SSVAL(ptr,smb_vwv1,len);
+       SSVAL(ptr,smb_vwv11,len);
+       SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+       SSVAL(ptr,smb_vwv13,3);
+       SSVAL(ptr,smb_vwv14,1);
+       SSVAL(ptr,smb_vwv15,1);
+       SSVAL(ptr,smb_vwv16,2);
+       p2 = smb_buf(ptr);
+       pstrcpy(p2,mailslot);
+       p2 = skip_string(p2,1);
+       
+       memcpy(p2,buffer,len);
+       p2 += len;
+       
+       dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+       
+       p.ip = *pdc_ip;
+       p.port = DGRAM_PORT;
+       p.fd = sock;
+       p.timestamp = time(NULL);
+       p.packet_type = DGRAM_PACKET;
+       
+       GetTimeOfDay(&tval);
+       
+       if (!send_packet(&p)) {
+               DEBUG(0,("lookup_pdc_name: send_packet failed.\n"));
+               close(sock);
+               return False;
+       }
+       
+       retries--;
+       
+       while (1) {
+               struct timeval tval2;
+               struct packet_struct *p_ret;
+               
+               GetTimeOfDay(&tval2);
+               if (TvalDiff(&tval,&tval2) > retry_time) {
+                       if (!retries)
+                               break;
+                       if (!send_packet(&p)) {
+                               DEBUG(0,("lookup_pdc_name: send_packet failed.\n"));
+                               close(sock);
+                               return False;
+                       }
+                       GetTimeOfDay(&tval);
+                       retries--;
+               }
+
+               if ((p_ret = receive_dgram_packet(sock,90,mailslot_name))) {
+                       struct dgram_packet *dgram2 = &p_ret->packet.dgram;
+                       char *buf;
+                       char *buf2;
+
+                       buf = &dgram2->data[0];
+                       buf -= 4;
+
+                       if (CVAL(buf,smb_com) != SMBtrans) {
+                               DEBUG(0,("lookup_pdc_name: datagram type %u != SMBtrans(%u)\n", (unsigned int)
+                                        CVAL(buf,smb_com), (unsigned int)SMBtrans ));
+                               free_packet(p_ret);
+                               continue;
+                       }
+                       
+                       len = SVAL(buf,smb_vwv11);
+                       buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
+                       
+                       if (len <= 0) {
+                               DEBUG(0,("lookup_pdc_name: datagram len < 0 (%d)\n", len ));
+                               free_packet(p_ret);
+                               continue;
+                       }
+
+                       DEBUG(4,("lookup_pdc_name: datagram reply from %s to %s IP %s for %s of type %d len=%d\n",
+                                nmb_namestr(&dgram2->source_name),nmb_namestr(&dgram2->dest_name),
+                                inet_ntoa(p_ret->ip), smb_buf(buf),SVAL(buf2,0),len));
+
+                       if(SVAL(buf2,0) != QUERYFORPDC_R) {
+                               DEBUG(0,("lookup_pdc_name: datagram type (%u) != QUERYFORPDC_R(%u)\n",
+                                        (unsigned int)SVAL(buf,0), (unsigned int)QUERYFORPDC_R ));
+                               free_packet(p_ret);
+                               continue;
+                       }
+
+                       buf2 += 2;
+                       /* Note this is safe as it is a bounded strcpy. */
+                       fstrcpy(ret_name, buf2);
+                       ret_name[sizeof(fstring)-1] = '\0';
+                       close(sock);
+                       free_packet(p_ret);
+                       return True;
+               }
+       }
+       
+       close(sock);
+       return False;
+#endif /* defined(I_HATE_WINDOWS_REPLY_CODE) */
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+BOOL get_pdc_ip(TALLOC_CTX *mem_ctx, const char *domain, struct in_addr *ip)
+{
+       struct in_addr *ip_list;
+       int count;
+       int i = 0;
+
+       /* Look up #1B name */
+
+       if (!internal_resolve_name(mem_ctx, domain, 0x1b, &ip_list, &count))
+               return False;
+
+       /* if we get more than 1 IP back we have to assume it is a
+          multi-homed PDC and not a mess up */
+          
+       if ( count > 1 ) {
+               DEBUG(6,("get_pdc_ip: PDC has %d IP addresses!\n", count));
+                               
+               /* look for a local net */
+               for ( i=0; i<count; i++ ) {
+                       if ( is_local_net( ip_list[i] ) )
+                               break;
+               }
+               
+               /* if we hit then end then just grab the first 
+                  one from the list */
+                  
+               if ( i == count )
+                       i = 0;
+       }
+
+       *ip = ip_list[i];
+       
+       SAFE_FREE(ip_list);
+
+       return True;
+}
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+BOOL get_dc_list(TALLOC_CTX *mem_ctx, const char *domain, struct in_addr **ip_list, int *count, int *ordered)
+{
+
+       *ordered = False;
+               
+       /* If it's our domain then use the 'password server' parameter. */
+
+       if (strequal(domain, lp_workgroup())) {
+               char *p;
+               char *pserver = lp_passwordserver(); /* UNIX charset. */
+               fstring name;
+               int num_addresses = 0;
+               int  local_count, i, j;
+               struct in_addr *return_iplist = NULL;
+               struct in_addr *auto_ip_list = NULL;
+               BOOL done_auto_lookup = False;
+               int auto_count = 0;
+               
+
+               if (!*pserver)
+                       return internal_resolve_name(mem_ctx,
+                               domain, 0x1C, ip_list, count);
+
+               p = pserver;
+
+               /*
+                * if '*' appears in the "password server" list then add
+                * an auto lookup to the list of manually configured
+                * DC's.  If any DC is listed by name, then the list should be 
+                * considered to be ordered 
+                */
+                
+               while (next_token(&p,name,LIST_SEP,sizeof(name))) {
+                       if (strequal(name, "*")) {
+                               if ( internal_resolve_name(mem_ctx, domain, 0x1C, &auto_ip_list, &auto_count) )
+                                       num_addresses += auto_count;
+                               done_auto_lookup = True;
+                               DEBUG(8,("Adding %d DC's from auto lookup\n", auto_count));
+                       }
+                       else 
+                               num_addresses++;
+               }
+
+               /* if we have no addresses and haven't done the auto lookup, then
+                  just return the list of DC's */
+                  
+               if ( (num_addresses == 0) && !done_auto_lookup )
+                       return internal_resolve_name(mem_ctx, domain, 0x1C, ip_list, count);
+
+               return_iplist = (struct in_addr *)malloc(num_addresses * sizeof(struct in_addr));
+
+               if (return_iplist == NULL) {
+                       DEBUG(3,("get_dc_list: malloc fail !\n"));
+                       return False;
+               }
+
+               p = pserver;
+               local_count = 0;
+
+               /* fill in the return list now with real IP's */
+                               
+               while ( (local_count<num_addresses) && next_token(&p,name,LIST_SEP,sizeof(name)) ) {
+                       struct in_addr name_ip;
+                       
+                       /* copy any addersses from the auto lookup */
+                       
+                       if ( strequal(name, "*") ) {
+                               for ( j=0; j<auto_count; j++ ) 
+                                       return_iplist[local_count++] = auto_ip_list[j];
+                               continue;
+                       }
+                       
+                       /* explicit lookup; resolve_name() will handle names & IP addresses */
+                                       
+                       if ( resolve_name( mem_ctx, name, &name_ip, 0x20) ) {
+                               return_iplist[local_count++] = name_ip;
+                               *ordered = True;
+                       }
+                               
+               }
+                               
+               SAFE_FREE(auto_ip_list);
+
+               /* need to remove duplicates in the list if we have 
+                  any explicit password servers */
+                  
+               if ( *ordered ) {               
+                       /* one loop to remove duplicates */
+                       for ( i=0; i<local_count; i++ ) {
+                               if ( is_zero_ip(return_iplist[i]) )
+                                       continue;
+                                       
+                               for ( j=i+1; j<local_count; j++ ) {
+                                       if ( ip_equal( return_iplist[i], return_iplist[j]) )
+                                               zero_ip(&return_iplist[j]);
+                               }
+                       }
+                       
+                       /* one loop to clean up any holes we left */
+                       /* first ip should never be a zero_ip() */
+                       for (i = 0; i<local_count; ) {
+                               if ( is_zero_ip(return_iplist[i]) ) {
+                                       if (i != local_count-1 )
+                                               memmove(&return_iplist[i], &return_iplist[i+1],
+                                                       (local_count - i - 1)*sizeof(return_iplist[i]));
+                                       local_count--;
+                                       continue;
+                               }
+                               i++;
+                       }
+               }
+               
+               *ip_list = return_iplist;
+               *count = local_count;
+               
+               DEBUG(8,("get_dc_list: return %d ip addresses\n", *count));
+
+               return (*count != 0);
+       }
+       
+       return internal_resolve_name(mem_ctx, domain, 0x1C, ip_list, count);
+}
diff --git a/source4/libcli/namequery_dc.c b/source4/libcli/namequery_dc.c
new file mode 100644 (file)
index 0000000..ffc6413
--- /dev/null
@@ -0,0 +1,104 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon connection manager
+
+   Copyright (C) Tim Potter 2001
+   Copyright (C) Andrew Bartlett 2002
+   
+   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"
+
+
+/*
+  find the DC for a domain using methods appropriate for a RPC domain
+*/
+BOOL rpc_find_dc(const char *domain, fstring srv_name, struct in_addr *ip_out)
+{
+       struct in_addr *ip_list = NULL, dc_ip, exclude_ip;
+       int count, i;
+       BOOL list_ordered;
+       BOOL use_pdc_only;
+       
+       zero_ip(&exclude_ip);
+
+       use_pdc_only = must_use_pdc(domain);
+       
+       /* Lookup domain controller name */
+          
+       if ( use_pdc_only && get_pdc_ip(domain, &dc_ip) ) {
+               DEBUG(10,("rpc_find_dc: Atempting to lookup PDC to avoid sam sync delays\n"));
+               
+               if (name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name)) {
+                       goto done;
+               }
+               /* Didn't get name, remember not to talk to this DC. */
+               exclude_ip = dc_ip;
+       }
+
+       /* get a list of all domain controllers */
+       
+       if (!get_dc_list( domain, &ip_list, &count, &list_ordered) ) {
+               DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+               return False;
+       }
+
+       /* Remove the entry we've already failed with (should be the PDC). */
+
+       if ( use_pdc_only ) {
+               for (i = 0; i < count; i++) {   
+                       if (ip_equal( exclude_ip, ip_list[i]))
+                               zero_ip(&ip_list[i]);
+               }
+       }
+
+       /* Pick a nice close server, but only if the list was not ordered */
+       if (!list_ordered && (count > 1) ) {
+               qsort(ip_list, count, sizeof(struct in_addr), QSORT_CAST ip_compare);
+       }
+
+       for (i = 0; i < count; i++) {
+               if (is_zero_ip(ip_list[i]))
+                       continue;
+
+               if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
+                       dc_ip = ip_list[i];
+                       goto done;
+               }
+       }
+
+
+       SAFE_FREE(ip_list);
+
+       return False;
+done:
+       /* We have the netbios name and IP address of a domain controller.
+          Ideally we should sent a SAMLOGON request to determine whether
+          the DC is alive and kicking.  If we can catch a dead DC before
+          performing a cli_connect() we can avoid a 30-second timeout. */
+
+       DEBUG(3, ("rpc_find_dc: Returning DC %s (%s) for domain %s\n", srv_name,
+                 inet_ntoa(dc_ip), domain));
+
+       *ip_out = dc_ip;
+
+       SAFE_FREE(ip_list);
+
+       return True;
+}
+
diff --git a/source4/libcli/nmblib.c b/source4/libcli/nmblib.c
new file mode 100644 (file)
index 0000000..a875f46
--- /dev/null
@@ -0,0 +1,1287 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios library routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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"
+
+static const struct opcode_names {
+       const char *nmb_opcode_name;
+       int opcode;
+} nmb_header_opcode_names[] = {
+       {"Query",           0 },
+       {"Registration",      5 },
+       {"Release",           6 },
+       {"WACK",              7 },
+       {"Refresh",           8 },
+       {"Refresh(altcode)",  9 },
+       {"Multi-homed Registration", 15 },
+       {0, -1 }
+};
+
+/****************************************************************************
+ * Lookup a nmb opcode name.
+ ****************************************************************************/
+static const char *lookup_opcode_name( int opcode )
+{
+  const struct opcode_names *op_namep;
+  int i;
+
+  for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) {
+    op_namep = &nmb_header_opcode_names[i];
+    if(opcode == op_namep->opcode)
+      return op_namep->nmb_opcode_name;
+  }
+  return "<unknown opcode>";
+}
+
+/****************************************************************************
+  print out a res_rec structure
+  ****************************************************************************/
+static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
+{
+  int i, j;
+
+  DEBUGADD( 4, ( "    %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+                 hdr,
+                 nmb_namestr(&res->rr_name),
+                 res->rr_type,
+                 res->rr_class,
+                 res->ttl ) );
+
+  if( res->rdlength == 0 || res->rdata == NULL )
+    return;
+
+  for (i = 0; i < res->rdlength; i+= 16)
+    {
+      DEBUGADD(4, ("    %s %3x char ", hdr, i));
+
+      for (j = 0; j < 16; j++)
+       {
+         uchar x = res->rdata[i+j];
+         if (x < 32 || x > 127) x = '.';
+         
+         if (i+j >= res->rdlength) break;
+         DEBUGADD(4, ("%c", x));
+       }
+      
+      DEBUGADD(4, ("   hex "));
+
+      for (j = 0; j < 16; j++)
+       {
+         if (i+j >= res->rdlength) break;
+         DEBUGADD(4, ("%02X", (uchar)res->rdata[i+j]));
+       }
+      
+      DEBUGADD(4, ("\n"));
+    }
+}
+
+/****************************************************************************
+  process a nmb packet
+  ****************************************************************************/
+void debug_nmb_packet(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+
+  if (DEBUGLVL(4)) {
+         DEBUG(4, ("nmb packet from %s(%d) header: id=%d opcode=%s(%d) response=%s\n",
+                   inet_ntoa(p->ip), p->port,
+                   nmb->header.name_trn_id,
+                   lookup_opcode_name(nmb->header.opcode),
+                   nmb->header.opcode,
+                   BOOLSTR(nmb->header.response)));
+         DEBUG(4, ("    header: flags: bcast=%s rec_avail=%s rec_des=%s trunc=%s auth=%s\n",
+                   BOOLSTR(nmb->header.nm_flags.bcast),
+                   BOOLSTR(nmb->header.nm_flags.recursion_available),
+                   BOOLSTR(nmb->header.nm_flags.recursion_desired),
+                   BOOLSTR(nmb->header.nm_flags.trunc),
+                   BOOLSTR(nmb->header.nm_flags.authoritative)));
+         DEBUG(4, ("    header: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d\n",
+                   nmb->header.rcode,
+                   nmb->header.qdcount,
+                   nmb->header.ancount,
+                   nmb->header.nscount,
+                   nmb->header.arcount));
+    }
+
+  if (nmb->header.qdcount)
+    {
+      DEBUGADD( 4, ( "    question: q_name=%s q_type=%d q_class=%d\n",
+                     nmb_namestr(&nmb->question.question_name),
+                     nmb->question.question_type,
+                     nmb->question.question_class) );
+    }
+
+  if (nmb->answers && nmb->header.ancount)
+    {
+      debug_nmb_res_rec(nmb->answers,"answers");
+    }
+  if (nmb->nsrecs && nmb->header.nscount)
+    {
+      debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+    }
+  if (nmb->additional && nmb->header.arcount)
+    {
+      debug_nmb_res_rec(nmb->additional,"additional");
+    }
+}
+
+/*******************************************************************
+  handle "compressed" name pointers
+  ******************************************************************/
+static BOOL handle_name_ptrs(uchar *ubuf,int *offset,int length,
+                            BOOL *got_pointer,int *ret)
+{
+  int loop_count=0;
+  
+  while ((ubuf[*offset] & 0xC0) == 0xC0) {
+    if (!*got_pointer) (*ret) += 2;
+    (*got_pointer)=True;
+    (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+    if (loop_count++ == 10 || (*offset) < 0 || (*offset)>(length-2)) {
+      return(False);
+    }
+  }
+  return(True);
+}
+
+/*******************************************************************
+  parse a nmb name from "compressed" format to something readable
+  return the space taken by the name, or 0 if the name is invalid
+  ******************************************************************/
+static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name)
+{
+  int m,n=0;
+  uchar *ubuf = (uchar *)inbuf;
+  int ret = 0;
+  BOOL got_pointer=False;
+  int loop_count=0;
+  int offset = ofs;
+
+  if (length - offset < 2)
+    return(0);  
+
+  /* handle initial name pointers */
+  if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+    return(0);
+  
+  m = ubuf[offset];
+
+  if (!m)
+    return(0);
+  if ((m & 0xC0) || offset+m+2 > length)
+    return(0);
+
+  memset((char *)name,'\0',sizeof(*name));
+
+  /* the "compressed" part */
+  if (!got_pointer)
+    ret += m + 2;
+  offset++;
+  while (m > 0) {
+    uchar c1,c2;
+    c1 = ubuf[offset++]-'A';
+    c2 = ubuf[offset++]-'A';
+    if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1))
+      return(0);
+    name->name[n++] = (c1<<4) | c2;
+    m -= 2;
+  }
+  name->name[n] = 0;
+
+  if (n==16) {
+    /* parse out the name type, 
+       its always in the 16th byte of the name */
+    name->name_type = ((uchar)name->name[15]) & 0xff;
+  
+    /* remove trailing spaces */
+    name->name[15] = 0;
+    n = 14;
+    while (n && name->name[n]==' ')
+      name->name[n--] = 0;  
+  }
+
+  /* now the domain parts (if any) */
+  n = 0;
+  while (ubuf[offset]) {
+    /* we can have pointers within the domain part as well */
+    if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+      return(0);
+
+    m = ubuf[offset];
+    /*
+     * Don't allow null domain parts.
+     */
+    if (!m)
+      return(0);
+    if (!got_pointer)
+      ret += m+1;
+    if (n)
+      name->scope[n++] = '.';
+    if (m+2+offset>length || n+m+1>sizeof(name->scope))
+      return(0);
+    offset++;
+    while (m--)
+      name->scope[n++] = (char)ubuf[offset++];
+
+    /*
+     * Watch for malicious loops.
+     */
+    if (loop_count++ == 10)
+      return 0;
+  }
+  name->scope[n++] = 0;  
+
+  return(ret);
+}
+
+
+/*******************************************************************
+  put a compressed nmb name into a buffer. return the length of the
+  compressed name
+
+  compressed names are really weird. The "compression" doubles the
+  size. The idea is that it also means that compressed names conform
+  to the doman name system. See RFC1002.
+  ******************************************************************/
+static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
+{
+  int ret,m;
+  fstring buf1;
+  char *p;
+
+  if (strcmp(name->name,"*") == 0) {
+    /* special case for wildcard name */
+    memset(buf1,'\0',20);
+    buf1[0] = '*';
+    buf1[15] = name->name_type;
+  } else {
+    slprintf(buf1, sizeof(buf1) - 1,"%-15.15s%c",name->name,name->name_type);
+  }
+
+  buf[offset] = 0x20;
+
+  ret = 34;
+
+  for (m=0;m<16;m++) {
+    buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+    buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+  }
+  offset += 33;
+
+  buf[offset] = 0;
+
+  if (name->scope[0]) {
+    /* XXXX this scope handling needs testing */
+    ret += strlen(name->scope) + 1;
+    pstrcpy(&buf[offset+1],name->scope);  
+  
+    p = &buf[offset+1];
+    while ((p = strchr_m(p,'.'))) {
+      buf[offset] = PTR_DIFF(p,&buf[offset+1]);
+      offset += (buf[offset] + 1);
+      p = &buf[offset+1];
+    }
+    buf[offset] = strlen(&buf[offset+1]);
+  }
+
+  return(ret);
+}
+
+/*******************************************************************
+  useful for debugging messages
+  ******************************************************************/
+char *nmb_namestr(struct nmb_name *n)
+{
+  static int i=0;
+  static fstring ret[4];
+  char *p = ret[i];
+
+  if (!n->scope[0])
+    slprintf(p,sizeof(fstring)-1, "%s<%02x>",n->name,n->name_type);
+  else
+    slprintf(p,sizeof(fstring)-1, "%s<%02x>.%s",n->name,n->name_type,n->scope);
+
+  i = (i+1)%4;
+  return(p);
+}
+
+/*******************************************************************
+  allocate and parse some resource records
+  ******************************************************************/
+static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length,
+                               struct res_rec **recs, int count)
+{
+  int i;
+  *recs = (struct res_rec *)malloc(sizeof(**recs)*count);
+  if (!*recs) return(False);
+
+  memset((char *)*recs,'\0',sizeof(**recs)*count);
+
+  for (i=0;i<count;i++) {
+    int l = parse_nmb_name(inbuf,*offset,length,&(*recs)[i].rr_name);
+    (*offset) += l;
+    if (!l || (*offset)+10 > length) {
+      SAFE_FREE(*recs);
+      return(False);
+    }
+    (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+    (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+    (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+    (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+    (*offset) += 10;
+    if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) || 
+       (*offset)+(*recs)[i].rdlength > length) {
+      SAFE_FREE(*recs);
+      return(False);
+    }
+    memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+    (*offset) += (*recs)[i].rdlength;    
+  }
+  return(True);
+}
+
+/*******************************************************************
+  put a resource record into a packet
+  ******************************************************************/
+static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count)
+{
+  int ret=0;
+  int i;
+
+  for (i=0;i<count;i++) {
+    int l = put_nmb_name(buf,offset,&recs[i].rr_name);
+    offset += l;
+    ret += l;
+    RSSVAL(buf,offset,recs[i].rr_type);
+    RSSVAL(buf,offset+2,recs[i].rr_class);
+    RSIVAL(buf,offset+4,recs[i].ttl);
+    RSSVAL(buf,offset+8,recs[i].rdlength);
+    memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+    offset += 10+recs[i].rdlength;
+    ret += 10+recs[i].rdlength;
+  }
+
+  return(ret);
+}
+
+/*******************************************************************
+  put a compressed name pointer record into a packet
+  ******************************************************************/
+static int put_compressed_name_ptr(uchar *buf,int offset,struct res_rec *rec,int ptr_offset)
+{  
+  int ret=0;
+  buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+  buf[offset+1] = (ptr_offset & 0xFF);
+  offset += 2;
+  ret += 2;
+  RSSVAL(buf,offset,rec->rr_type);
+  RSSVAL(buf,offset+2,rec->rr_class);
+  RSIVAL(buf,offset+4,rec->ttl);
+  RSSVAL(buf,offset+8,rec->rdlength);
+  memcpy(buf+offset+10,rec->rdata,rec->rdlength);
+  offset += 10+rec->rdlength;
+  ret += 10+rec->rdlength;
+    
+  return(ret);
+}
+
+/*******************************************************************
+  parse a dgram packet. Return False if the packet can't be parsed 
+  or is invalid for some reason, True otherwise 
+
+  this is documented in section 4.4.1 of RFC1002
+  ******************************************************************/
+static BOOL parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+  int offset;
+  int flags;
+
+  memset((char *)dgram,'\0',sizeof(*dgram));
+
+  if (length < 14) return(False);
+
+  dgram->header.msg_type = CVAL(inbuf,0);
+  flags = CVAL(inbuf,1);
+  dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+  if (flags & 1) dgram->header.flags.more = True;
+  if (flags & 2) dgram->header.flags.first = True;
+  dgram->header.dgm_id = RSVAL(inbuf,2);
+  putip((char *)&dgram->header.source_ip,inbuf+4);
+  dgram->header.source_port = RSVAL(inbuf,8);
+  dgram->header.dgm_length = RSVAL(inbuf,10);
+  dgram->header.packet_offset = RSVAL(inbuf,12);
+
+  offset = 14;
+
+  if (dgram->header.msg_type == 0x10 ||
+      dgram->header.msg_type == 0x11 ||
+      dgram->header.msg_type == 0x12) {      
+    offset += parse_nmb_name(inbuf,offset,length,&dgram->source_name);
+    offset += parse_nmb_name(inbuf,offset,length,&dgram->dest_name);
+  }
+
+  if (offset >= length || (length-offset > sizeof(dgram->data))) 
+    return(False);
+
+  dgram->datasize = length-offset;
+  memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+  return(True);
+}
+
+
+/*******************************************************************
+  parse a nmb packet. Return False if the packet can't be parsed 
+  or is invalid for some reason, True otherwise 
+  ******************************************************************/
+static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+  int nm_flags,offset;
+
+  memset((char *)nmb,'\0',sizeof(*nmb));
+
+  if (length < 12) return(False);
+
+  /* parse the header */
+  nmb->header.name_trn_id = RSVAL(inbuf,0);
+
+  DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id));
+
+  nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF;
+  nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False;
+  nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
+  nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+  nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False;
+  nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False;
+  nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False;
+  nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False;  
+  nmb->header.rcode = CVAL(inbuf,3) & 0xF;
+  nmb->header.qdcount = RSVAL(inbuf,4);
+  nmb->header.ancount = RSVAL(inbuf,6);
+  nmb->header.nscount = RSVAL(inbuf,8);
+  nmb->header.arcount = RSVAL(inbuf,10);
+  
+  if (nmb->header.qdcount) {
+    offset = parse_nmb_name(inbuf,12,length,&nmb->question.question_name);
+    if (!offset) return(False);
+
+    if (length - (12+offset) < 4) return(False);
+    nmb->question.question_type = RSVAL(inbuf,12+offset);
+    nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+    offset += 12+4;
+  } else {
+    offset = 12;
+  }
+
+  /* and any resource records */
+  if (nmb->header.ancount && 
+      !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+                          nmb->header.ancount))
+    return(False);
+
+  if (nmb->header.nscount && 
+      !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+                          nmb->header.nscount))
+    return(False);
+  
+  if (nmb->header.arcount && 
+      !parse_alloc_res_rec(inbuf,&offset,length,&nmb->additional,
+                          nmb->header.arcount))
+    return(False);
+
+  return(True);
+}
+
+/*******************************************************************
+  'Copy constructor' for an nmb packet
+  ******************************************************************/
+static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
+{  
+  struct nmb_packet *nmb;
+  struct nmb_packet *copy_nmb;
+  struct packet_struct *pkt_copy;
+
+  if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL)
+  {
+    DEBUG(0,("copy_nmb_packet: malloc fail.\n"));
+    return NULL;
+  }
+
+  /* Structure copy of entire thing. */
+
+  *pkt_copy = *packet;
+
+  /* Ensure this copy is not locked. */
+  pkt_copy->locked = False;
+
+  /* Ensure this copy has no resource records. */
+  nmb = &packet->packet.nmb;
+  copy_nmb = &pkt_copy->packet.nmb;
+
+  copy_nmb->answers = NULL;
+  copy_nmb->nsrecs = NULL;
+  copy_nmb->additional = NULL;
+
+  /* Now copy any resource records. */
+
+  if (nmb->answers)
+  {
+    if((copy_nmb->answers = (struct res_rec *)
+                  malloc(nmb->header.ancount * sizeof(struct res_rec))) == NULL)
+      goto free_and_exit;
+    memcpy((char *)copy_nmb->answers, (char *)nmb->answers, 
+           nmb->header.ancount * sizeof(struct res_rec));
+  }
+  if (nmb->nsrecs)
+  {
+    if((copy_nmb->nsrecs = (struct res_rec *)
+                  malloc(nmb->header.nscount * sizeof(struct res_rec))) == NULL)
+      goto free_and_exit;
+    memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs, 
+           nmb->header.nscount * sizeof(struct res_rec));
+  }
+  if (nmb->additional)
+  {
+    if((copy_nmb->additional = (struct res_rec *)
+                  malloc(nmb->header.arcount * sizeof(struct res_rec))) == NULL)
+      goto free_and_exit;
+    memcpy((char *)copy_nmb->additional, (char *)nmb->additional, 
+           nmb->header.arcount * sizeof(struct res_rec));
+  }
+
+  return pkt_copy;
+
+free_and_exit:
+
+  SAFE_FREE(copy_nmb->answers);
+  SAFE_FREE(copy_nmb->nsrecs);
+  SAFE_FREE(copy_nmb->additional);
+  SAFE_FREE(pkt_copy);
+
+  DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n"));
+  return NULL;
+}
+
+/*******************************************************************
+  'Copy constructor' for a dgram packet
+  ******************************************************************/
+static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
+{ 
+  struct packet_struct *pkt_copy;
+
+  if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL)
+  {
+    DEBUG(0,("copy_dgram_packet: malloc fail.\n"));
+    return NULL;
+  }
+
+  /* Structure copy of entire thing. */
+
+  *pkt_copy = *packet;
+
+  /* Ensure this copy is not locked. */
+  pkt_copy->locked = False;
+
+  /* There are no additional pointers in a dgram packet,
+     we are finished. */
+  return pkt_copy;
+}
+
+/*******************************************************************
+  'Copy constructor' for a generic packet
+  ******************************************************************/
+struct packet_struct *copy_packet(struct packet_struct *packet)
+{  
+  if(packet->packet_type == NMB_PACKET)
+    return copy_nmb_packet(packet);
+  else if (packet->packet_type == DGRAM_PACKET)
+    return copy_dgram_packet(packet);
+  return NULL;
+}
+/*******************************************************************
+  free up any resources associated with an nmb packet
+  ******************************************************************/
+static void free_nmb_packet(struct nmb_packet *nmb)
+{  
+  SAFE_FREE(nmb->answers);
+  SAFE_FREE(nmb->nsrecs);
+  SAFE_FREE(nmb->additional);
+}
+
+/*******************************************************************
+  free up any resources associated with a dgram packet
+  ******************************************************************/
+static void free_dgram_packet(struct dgram_packet *nmb)
+{  
+  /* We have nothing to do for a dgram packet. */
+}
+
+/*******************************************************************
+  free up any resources associated with a packet
+  ******************************************************************/
+void free_packet(struct packet_struct *packet)
+{  
+       if (packet->locked) 
+               return;
+       if (packet->packet_type == NMB_PACKET)
+               free_nmb_packet(&packet->packet.nmb);
+       else if (packet->packet_type == DGRAM_PACKET)
+               free_dgram_packet(&packet->packet.dgram);
+       ZERO_STRUCTPN(packet);
+       SAFE_FREE(packet);
+}
+
+/*******************************************************************
+parse a packet buffer into a packet structure
+  ******************************************************************/
+struct packet_struct *parse_packet(char *buf,int length,
+                                  enum packet_type packet_type)
+{
+       struct packet_struct *p;
+       BOOL ok=False;
+
+       p = (struct packet_struct *)malloc(sizeof(*p));
+       if (!p) return(NULL);
+
+       p->next = NULL;
+       p->prev = NULL;
+       p->locked = False;
+       p->timestamp = time(NULL);
+       p->packet_type = packet_type;
+
+       switch (packet_type) {
+       case NMB_PACKET:
+               ok = parse_nmb(buf,length,&p->packet.nmb);
+               break;
+               
+       case DGRAM_PACKET:
+               ok = parse_dgram(buf,length,&p->packet.dgram);
+               break;
+       }
+
+       if (!ok) {
+               free_packet(p);
+               return NULL;
+       }
+
+       return p;
+}
+
+/*******************************************************************
+  read a packet from a socket and parse it, returning a packet ready
+  to be used or put on the queue. This assumes a UDP socket
+  ******************************************************************/
+struct packet_struct *read_packet(int fd,enum packet_type packet_type)
+{
+       struct packet_struct *packet;
+       char buf[MAX_DGRAM_SIZE];
+       int length;
+       struct in_addr addr;
+       int port;
+       
+       length = read_udp_socket(fd, buf, sizeof(buf), &addr, &port);
+       if (length < MIN_DGRAM_SIZE) return(NULL);
+       
+       packet = parse_packet(buf, length, packet_type);
+       if (!packet) return NULL;
+
+       packet->fd = fd;
+       packet->ip = addr;
+       packet->port = port;
+       
+       DEBUG(5,("Received a packet of len %d from (%s) port %d\n",
+                length, inet_ntoa(packet->ip), packet->port));
+       
+       return packet;
+}
+                                        
+
+/*******************************************************************
+  send a udp packet on a already open socket
+  ******************************************************************/
+static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+  BOOL ret = False;
+  int i;
+  struct sockaddr_in sock_out;
+
+  /* set the address and port */
+  memset((char *)&sock_out,'\0',sizeof(sock_out));
+  putip((char *)&sock_out.sin_addr,(char *)&ip);
+  sock_out.sin_port = htons( port );
+  sock_out.sin_family = AF_INET;
+  
+  DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n",
+             len, inet_ntoa(ip), port ) );
+
+  /*
+   * Patch to fix asynch error notifications from Linux kernel.
+   */
+       
+  for (i = 0; i < 5; i++) {
+    ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, sizeof(sock_out)) >= 0);
+    if (ret || errno != ECONNREFUSED)
+      break;
+  }
+
+  if (!ret)
+    DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+            inet_ntoa(ip),port,strerror(errno)));
+
+  return(ret);
+}
+
+/*******************************************************************
+  build a dgram packet ready for sending
+
+  XXXX This currently doesn't handle packets too big for one
+  datagram. It should split them and use the packet_offset, more and
+  first flags to handle the fragmentation. Yuck.
+
+    [...but it isn't clear that we would ever need to send a
+    a fragmented NBT Datagram.  The IP layer does its own
+    fragmentation to ensure that messages can fit into the path
+    MTU.  It *is* important to be able to receive and rebuild
+    fragmented NBT datagrams, just in case someone out there
+    really has implemented this 'feature'.  crh -)------ ]
+
+  ******************************************************************/
+static int build_dgram(char *buf,struct packet_struct *p)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  uchar *ubuf = (uchar *)buf;
+  int offset=0;
+
+  /* put in the header */
+  ubuf[0] = dgram->header.msg_type;
+  ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+  if (dgram->header.flags.more) ubuf[1] |= 1;
+  if (dgram->header.flags.first) ubuf[1] |= 2;
+  RSSVAL(ubuf,2,dgram->header.dgm_id);
+  putip(ubuf+4,(char *)&dgram->header.source_ip);
+  RSSVAL(ubuf,8,dgram->header.source_port);
+  RSSVAL(ubuf,12,dgram->header.packet_offset);
+
+  offset = 14;
+
+  if (dgram->header.msg_type == 0x10 ||
+      dgram->header.msg_type == 0x11 ||
+      dgram->header.msg_type == 0x12) {      
+    offset += put_nmb_name((char *)ubuf,offset,&dgram->source_name);
+    offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name);
+  }
+
+  memcpy(ubuf+offset,dgram->data,dgram->datasize);
+  offset += dgram->datasize;
+
+  /* automatically set the dgm_length
+   * NOTE: RFC1002 says the dgm_length does *not*
+   *       include the fourteen-byte header. crh
+   */
+  dgram->header.dgm_length = (offset - 14);
+  RSSVAL(ubuf,10,dgram->header.dgm_length); 
+
+  return(offset);
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+       memset( (char *)n, '\0', sizeof(struct nmb_name) );
+       push_ascii(n->name, name, 16, STR_TERMINATE|STR_UPPER);
+       n->name_type = (unsigned int)type & 0xFF;
+       StrnCpy( n->scope, lp_netbios_scope(), 63 );
+       strupper( n->scope );
+}
+
+/*******************************************************************
+  Compare two nmb names
+  ******************************************************************/
+
+BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2)
+{
+  return ((n1->name_type == n2->name_type) &&
+         strequal(n1->name ,n2->name ) &&
+         strequal(n1->scope,n2->scope));
+}
+
+/*******************************************************************
+  build a nmb packet ready for sending
+
+  XXXX this currently relies on not being passed something that expands
+  to a packet too big for the buffer. Eventually this should be
+  changed to set the trunc bit so the receiver can request the rest
+  via tcp (when that becomes supported)
+  ******************************************************************/
+static int build_nmb(char *buf,struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  uchar *ubuf = (uchar *)buf;
+  int offset=0;
+
+  /* put in the header */
+  RSSVAL(ubuf,offset,nmb->header.name_trn_id);
+  ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
+  if (nmb->header.response) ubuf[offset+2] |= (1<<7);
+  if (nmb->header.nm_flags.authoritative && 
+      nmb->header.response) ubuf[offset+2] |= 0x4;
+  if (nmb->header.nm_flags.trunc) ubuf[offset+2] |= 0x2;
+  if (nmb->header.nm_flags.recursion_desired) ubuf[offset+2] |= 0x1;
+  if (nmb->header.nm_flags.recursion_available &&
+      nmb->header.response) ubuf[offset+3] |= 0x80;
+  if (nmb->header.nm_flags.bcast) ubuf[offset+3] |= 0x10;
+  ubuf[offset+3] |= (nmb->header.rcode & 0xF);
+
+  RSSVAL(ubuf,offset+4,nmb->header.qdcount);
+  RSSVAL(ubuf,offset+6,nmb->header.ancount);
+  RSSVAL(ubuf,offset+8,nmb->header.nscount);
+  RSSVAL(ubuf,offset+10,nmb->header.arcount);
+  
+  offset += 12;
+  if (nmb->header.qdcount) {
+    /* XXXX this doesn't handle a qdcount of > 1 */
+    offset += put_nmb_name((char *)ubuf,offset,&nmb->question.question_name);
+    RSSVAL(ubuf,offset,nmb->question.question_type);
+    RSSVAL(ubuf,offset+2,nmb->question.question_class);
+    offset += 4;
+  }
+
+  if (nmb->header.ancount)
+    offset += put_res_rec((char *)ubuf,offset,nmb->answers,
+                         nmb->header.ancount);
+
+  if (nmb->header.nscount)
+    offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs,
+                         nmb->header.nscount);
+
+  /*
+   * The spec says we must put compressed name pointers
+   * in the following outgoing packets :
+   * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
+   * NAME_RELEASE_REQUEST.
+   */
+
+  if((nmb->header.response == False) &&
+     ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
+      (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
+      (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+      (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
+      (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
+     (nmb->header.arcount == 1)) {
+
+    offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12);
+
+  } else if (nmb->header.arcount) {
+    offset += put_res_rec((char *)ubuf,offset,nmb->additional,
+                         nmb->header.arcount);  
+  }
+  return(offset);
+}
+
+
+/*******************************************************************
+linearise a packet
+  ******************************************************************/
+int build_packet(char *buf, struct packet_struct *p)
+{
+       int len = 0;
+
+       switch (p->packet_type) {
+       case NMB_PACKET:
+               len = build_nmb(buf,p);
+               break;
+
+       case DGRAM_PACKET:
+               len = build_dgram(buf,p);
+               break;
+       }
+
+       return len;
+}
+
+/*******************************************************************
+  send a packet_struct
+  ******************************************************************/
+BOOL send_packet(struct packet_struct *p)
+{
+  char buf[1024];
+  int len=0;
+
+  memset(buf,'\0',sizeof(buf));
+
+  len = build_packet(buf, p);
+
+  if (!len) return(False);
+
+  return(send_udp(p->fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+  receive a packet with timeout on a open UDP filedescriptor
+  The timeout is in milliseconds
+  ***************************************************************************/
+struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
+{
+       fd_set fds;
+       struct timeval timeout;
+       int ret;
+
+       FD_ZERO(&fds);
+       FD_SET(fd,&fds);
+       timeout.tv_sec = t/1000;
+       timeout.tv_usec = 1000*(t%1000);
+
+       if ((ret = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout)) == -1) {
+               /* errno should be EBADF or EINVAL. */
+               DEBUG(0,("select returned -1, errno = %s (%d)\n", strerror(errno), errno));
+               return NULL;
+       }
+
+       if (ret == 0) /* timeout */
+               return NULL;
+
+       if (FD_ISSET(fd,&fds)) 
+               return(read_packet(fd,type));
+       
+       return(NULL);
+}
+
+
+/****************************************************************************
+  receive a UDP/137 packet either via UDP or from the unexpected packet
+  queue. The packet must be a reply packet and have the specified trn_id
+  The timeout is in milliseconds
+  ***************************************************************************/
+struct packet_struct *receive_nmb_packet(int fd, int t, int trn_id)
+{
+       struct packet_struct *p;
+
+       p = receive_packet(fd, NMB_PACKET, t);
+
+       if (p && p->packet.nmb.header.response &&
+           p->packet.nmb.header.name_trn_id == trn_id) {
+               return p;
+       }
+       if (p) free_packet(p);
+
+       /* try the unexpected packet queue */
+       return receive_unexpected(NMB_PACKET, trn_id, NULL);
+}
+
+/****************************************************************************
+  receive a UDP/138 packet either via UDP or from the unexpected packet
+  queue. The packet must be a reply packet and have the specified mailslot name
+  The timeout is in milliseconds
+  ***************************************************************************/
+struct packet_struct *receive_dgram_packet(int fd, int t, const char *mailslot_name)
+{
+       struct packet_struct *p;
+
+       p = receive_packet(fd, DGRAM_PACKET, t);
+
+       if (p && match_mailslot_name(p, mailslot_name)) {
+               return p;
+       }
+       if (p) free_packet(p);
+
+       /* try the unexpected packet queue */
+       return receive_unexpected(DGRAM_PACKET, 0, mailslot_name);
+}
+
+
+/****************************************************************************
+ see if a datagram has the right mailslot name
+***************************************************************************/
+BOOL match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+       struct dgram_packet *dgram = &p->packet.dgram;
+       char *buf;
+
+       buf = &dgram->data[0];
+       buf -= 4;
+
+       buf = smb_buf(buf);
+
+       if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+               return True;
+       }
+
+       return False;
+}
+
+
+/****************************************************************************
+return the number of bits that match between two 4 character buffers
+  ***************************************************************************/
+int matching_quad_bits(uchar *p1, uchar *p2)
+{
+       int i, j, ret = 0;
+       for (i=0; i<4; i++) {
+               if (p1[i] != p2[i]) break;
+               ret += 8;
+       }
+
+       if (i==4) return ret;
+
+       for (j=0; j<8; j++) {
+               if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j)))) break;
+               ret++;
+       }       
+       
+       return ret;
+}
+
+
+static uchar sort_ip[4];
+
+/****************************************************************************
+compare two query reply records
+  ***************************************************************************/
+static int name_query_comp(uchar *p1, uchar *p2)
+{
+       return matching_quad_bits(p2+2, sort_ip) - matching_quad_bits(p1+2, sort_ip);
+}
+
+/****************************************************************************
+sort a set of 6 byte name query response records so that the IPs that
+have the most leading bits in common with the specified address come first
+  ***************************************************************************/
+void sort_query_replies(char *data, int n, struct in_addr ip)
+{
+       if (n <= 1) return;
+
+       putip(sort_ip, (char *)&ip);
+
+       qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+
+#define TRUNCATE_NETBIOS_NAME 1
+
+/*******************************************************************
+ convert, possibly using a stupid microsoft-ism which has destroyed
+ the transport independence of netbios (for CIFS vendors that usually
+ use the Win95-type methods, not for NT to NT communication, which uses
+ DCE/RPC and therefore full-length unicode strings...) a dns name into
+ a netbios name.
+
+ the netbios name (NOT necessarily null-terminated) is truncated to 15
+ characters.
+
+ ******************************************************************/
+char *dns_to_netbios_name(char *dns_name)
+{
+       static char netbios_name[16];
+       int i;
+       StrnCpy(netbios_name, dns_name, 15);
+       netbios_name[15] = 0;
+       
+#ifdef TRUNCATE_NETBIOS_NAME
+       /* ok.  this is because of a stupid microsoft-ism.  if the called host
+          name contains a '.', microsoft clients expect you to truncate the
+          netbios name up to and including the '.'  this even applies, by
+          mistake, to workgroup (domain) names, which is _really_ daft.
+        */
+       for (i = 15; i >= 0; i--)
+       {
+               if (netbios_name[i] == '.')
+               {
+                       netbios_name[i] = 0;
+                       break;
+               }
+       }
+#endif /* TRUNCATE_NETBIOS_NAME */
+
+       return netbios_name;
+}
+
+
+/****************************************************************************
+interpret the weird netbios "name". Return the name type
+****************************************************************************/
+static int name_interpret(char *in,char *out)
+{
+  int ret;
+  int len = (*in++) / 2;
+
+  *out=0;
+
+  if (len > 30 || len<1) return(0);
+
+  while (len--)
+    {
+      if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+       *out = 0;
+       return(0);
+      }
+      *out = ((in[0]-'A')<<4) + (in[1]-'A');
+      in += 2;
+      out++;
+    }
+  *out = 0;
+  ret = out[-1];
+
+#ifdef NETBIOS_SCOPE
+  /* Handle any scope names */
+  while(*in) 
+    {
+      *out++ = '.'; /* Scope names are separated by periods */
+      len = *(uchar *)in++;
+      StrnCpy(out, in, len);
+      out += len;
+      *out=0;
+      in += len;
+    }
+#endif
+  return(ret);
+}
+
+
+/****************************************************************************
+return the number of bytes that would be occupied by the result of
+name_mangle()
+****************************************************************************/
+uint_t nbt_mangled_name_len(void)
+{
+       const char *scope = lp_netbios_scope();
+       uint_t ret = 34;
+       if (scope && *scope) {
+               ret += strlen(scope) + 1;
+       }
+       return ret;
+}
+
+/****************************************************************************
+mangle a name into netbios format
+
+  Note:  <Out> must be nbt_mangled_name_len() in length
+****************************************************************************/
+int name_mangle(char *In, char *Out, char name_type)
+{
+       int   i;
+       int   c;
+       int   len;
+       char  buf[20];
+       char *p = Out;
+       const char *scope = lp_netbios_scope();
+
+       /* Safely copy the input string, In, into buf[]. */
+       memset( buf, 0, 20 );
+       if (strcmp(In,"*") == 0) {
+               buf[0] = '*';
+       } else {
+               slprintf( buf, sizeof(buf) - 1, "%-15.15s%c", In, name_type);
+       }
+
+       /* Place the length of the first field into the output buffer. */
+       p[0] = 32;
+       p++;
+
+       /* Now convert the name to the rfc1001/1002 format. */
+       for ( i = 0; i < 16; i++ ) {
+               c = toupper( buf[i] );
+               p[i*2]     = ( (c >> 4) & 0xF ) + 'A';
+               p[(i*2)+1] = (c & 0xF) + 'A';
+       }
+       p += 32;
+       p[0] = '\0';
+
+       if (!scope || !*scope) {
+               return name_len(Out);
+       }
+
+       /* Add the scope string. */
+       for (i = 0, len = 0; scope[i]; i++, len++) {
+               switch(scope[i]) {
+               case '.':
+                       p[0] = len;
+                       p   += (len + 1);
+                       len  = -1;
+                       break;
+               default:
+                       p[len+1] = scope[i];
+                       break;
+               }
+       }
+
+       p[0] = len;
+       if (len > 0) {  
+               p[len+1] = 0;
+       }
+
+       return name_len(Out);
+}
+
+/****************************************************************************
+find a pointer to a netbios name
+****************************************************************************/
+static char *name_ptr(char *buf,int ofs)
+{
+  uchar c = *(uchar *)(buf+ofs);
+
+  if ((c & 0xC0) == 0xC0)
+    {
+      uint16 l = RSVAL(buf, ofs) & 0x3FFF;
+      DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
+      return(buf + l);
+    }
+  else
+    return(buf+ofs);
+}  
+
+/****************************************************************************
+extract a netbios name from a buf
+****************************************************************************/
+int name_extract(char *buf,int ofs,char *name)
+{
+       char *p = name_ptr(buf,ofs);
+       int d = PTR_DIFF(p,buf+ofs);
+       pstrcpy(name,"");
+       if (d < -50 || d > 50) return(0);
+       return(name_interpret(p,name));
+}
+  
+/****************************************************************************
+return the total storage length of a mangled name
+****************************************************************************/
+int name_len(char *s1)
+{
+       /* NOTE: this argument _must_ be unsigned */
+       uchar *s = (uchar *)s1;
+       int len;
+
+       /* If the two high bits of the byte are set, return 2. */
+       if (0xC0 == (*s & 0xC0))
+               return(2);
+
+       /* Add up the length bytes. */
+       for (len = 1; (*s); s += (*s) + 1) {
+               len += *s + 1;
+               SMB_ASSERT(len < 80);
+       }
+
+       return(len);
+} /* name_len */
diff --git a/source4/libcli/ntlmssp.c b/source4/libcli/ntlmssp.c
new file mode 100644 (file)
index 0000000..c4ad260
--- /dev/null
@@ -0,0 +1,625 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   handle NLTMSSP, server side
+
+   Copyright (C) Andrew Tridgell      2001
+   Copyright (C) Andrew Bartlett 2001-2003
+
+   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"
+
+/**
+ * Print out the NTLMSSP flags for debugging 
+ * @param neg_flags The flags from the packet
+ */
+
+void debug_ntlmssp_flags(uint32 neg_flags)
+{
+       DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
+       
+       if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_UNICODE\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_OEM) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_OEM\n"));
+       if (neg_flags & NTLMSSP_REQUEST_TARGET) 
+               DEBUGADD(4, ("  NTLMSSP_REQUEST_TARGET\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SIGN\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SEAL\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_LM_KEY\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NETWARE\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM2\n"));
+       if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) 
+               DEBUGADD(4, ("  NTLMSSP_CHAL_TARGET_INFO\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_128) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_128\n"));
+       if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) 
+               DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
+}
+
+/**
+ * Default challenge generation code.
+ *
+ */
+   
+static const uint8 *get_challenge(struct ntlmssp_state *ntlmssp_state)
+{
+       static uchar chal[8];
+       generate_random_buffer(chal, sizeof(chal), False);
+
+       return chal;
+}
+
+/**
+ * Determine correct target name flags for reply, given server role 
+ * and negoitated falgs
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param neg_flags The flags from the packet
+ * @param chal_flags The flags to be set in the reply packet
+ * @return The 'target name' string.
+ */
+
+static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state,
+                                      uint32 neg_flags, uint32 *chal_flags) 
+{
+       if (neg_flags & NTLMSSP_REQUEST_TARGET) {
+               *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
+               *chal_flags |= NTLMSSP_REQUEST_TARGET;
+               if (ntlmssp_state->server_role == ROLE_STANDALONE) {
+                       *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+                       return ntlmssp_state->get_global_myname();
+               } else {
+                       *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
+                       return ntlmssp_state->get_domain();
+               };
+       } else {
+               return "";
+       }
+}
+
+/**
+ * Next state function for the Negotiate packet
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. 
+ */
+
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+                                        const DATA_BLOB request, DATA_BLOB *reply) 
+{
+       DATA_BLOB struct_blob;
+       fstring dnsname, dnsdomname;
+       uint32 ntlmssp_command, neg_flags, chal_flags;
+       char *cliname=NULL, *domname=NULL;
+       const uint8 *cryptkey;
+       const char *target_name;
+
+       /* parse the NTLMSSP packet */
+#if 0
+       file_save("ntlmssp_negotiate.dat", request.data, request.length);
+#endif
+
+       if (!msrpc_parse(&request, "CddAA",
+                        "NTLMSSP",
+                        &ntlmssp_command,
+                        &neg_flags,
+                        &cliname,
+                        &domname)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       SAFE_FREE(cliname);
+       SAFE_FREE(domname);
+  
+       debug_ntlmssp_flags(neg_flags);
+
+       cryptkey = ntlmssp_state->get_challenge(ntlmssp_state);
+
+       data_blob_free(&ntlmssp_state->chal);
+       ntlmssp_state->chal = data_blob(cryptkey, 8);
+
+       /* Give them the challenge. For now, ignore neg_flags and just
+          return the flags we want. Obviously this is not correct */
+       
+       chal_flags = 
+               NTLMSSP_NEGOTIATE_128 | 
+               NTLMSSP_NEGOTIATE_NTLM;
+       
+       if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+               chal_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+               ntlmssp_state->unicode = True;
+       } else {
+               chal_flags |= NTLMSSP_NEGOTIATE_OEM;
+       }
+
+       target_name = ntlmssp_target_name(ntlmssp_state, 
+                                         neg_flags, &chal_flags); 
+
+       /* This should be a 'netbios domain -> DNS domain' mapping */
+       dnsdomname[0] = '\0';
+       get_mydomname(dnsdomname);
+       strlower(dnsdomname);
+       
+       dnsname[0] = '\0';
+       get_myfullname(dnsname);
+       strlower(dnsname);
+       
+       if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) 
+       {
+               const char *target_name_dns = "";
+               if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
+                       target_name_dns = dnsdomname;
+               } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
+                       target_name_dns = dnsname;
+               }
+
+               /* the numbers here are the string type flags */
+               msrpc_gen(&struct_blob, "aaaaa",
+                         ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN, target_name,
+                         ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
+                         ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN_DNS, target_name_dns,
+                         ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER_DNS, dnsdomname,
+                         ntlmssp_state->unicode, 0, "");
+       } else {
+               struct_blob = data_blob(NULL, 0);
+       }
+
+       {
+               const char *gen_string;
+               if (ntlmssp_state->unicode) {
+                       gen_string = "CdUdbddB";
+               } else {
+                       gen_string = "CdAdbddB";
+               }
+               
+               msrpc_gen(reply, gen_string,
+                         "NTLMSSP", 
+                         NTLMSSP_CHALLENGE,
+                         target_name,
+                         chal_flags,
+                         cryptkey, 8,
+                         0, 0,
+                         struct_blob.data, struct_blob.length);
+       }
+               
+       data_blob_free(&struct_blob);
+
+       ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+       return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK. 
+ */
+
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+                                   const DATA_BLOB request, DATA_BLOB *reply) 
+{
+       DATA_BLOB sess_key;
+       uint32 ntlmssp_command, neg_flags;
+       NTSTATUS nt_status;
+
+       const char *parse_string;
+
+       /* parse the NTLMSSP packet */
+#if 0
+       file_save("ntlmssp_auth.dat", request.data, request.length);
+#endif
+
+       if (ntlmssp_state->unicode) {
+               parse_string = "CdBBUUUBd";
+       } else {
+               parse_string = "CdBBAAABd";
+       }
+
+       data_blob_free(&ntlmssp_state->lm_resp);
+       data_blob_free(&ntlmssp_state->nt_resp);
+
+       SAFE_FREE(ntlmssp_state->user);
+       SAFE_FREE(ntlmssp_state->domain);
+       SAFE_FREE(ntlmssp_state->workstation);
+
+       /* now the NTLMSSP encoded auth hashes */
+       if (!msrpc_parse(&request, parse_string,
+                        "NTLMSSP", 
+                        &ntlmssp_command, 
+                        &ntlmssp_state->lm_resp,
+                        &ntlmssp_state->nt_resp,
+                        &ntlmssp_state->domain, 
+                        &ntlmssp_state->user, 
+                        &ntlmssp_state->workstation,
+                        &sess_key,
+                        &neg_flags)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data_blob_free(&sess_key);
+       
+       DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%d len2=%d\n",
+                ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, ntlmssp_state->lm_resp.length, ntlmssp_state->nt_resp.length));
+
+#if 0
+       file_save("nthash1.dat",  &ntlmssp_state->nt_resp.data,  &ntlmssp_state->nt_resp.length);
+       file_save("lmhash1.dat",  &ntlmssp_state->lm_resp.data,  &ntlmssp_state->lm_resp.length);
+#endif
+
+       nt_status = ntlmssp_state->check_password(ntlmssp_state);
+       
+       *reply = data_blob(NULL, 0);
+
+       return nt_status;
+}
+
+/**
+ * Create an NTLMSSP state machine
+ * 
+ * @param ntlmssp_state NTLMSSP State, allocated by this funciton
+ */
+
+NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("NTLMSSP context");
+       
+       *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+       if (!*ntlmssp_state) {
+               DEBUG(0,("ntlmssp_server_start: talloc failed!\n"));
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*ntlmssp_state)->mem_ctx = mem_ctx;
+       (*ntlmssp_state)->get_challenge = get_challenge;
+
+       (*ntlmssp_state)->get_global_myname = lp_netbios_name;
+       (*ntlmssp_state)->get_domain = lp_workgroup;
+       (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
+
+       (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE;
+
+       return NT_STATUS_OK;
+}
+
+/**
+ * End an NTLMSSP state machine
+ * 
+ * @param ntlmssp_state NTLMSSP State, free()ed by this funciton
+ */
+
+NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state)
+{
+       TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
+
+       data_blob_free(&(*ntlmssp_state)->chal);
+       data_blob_free(&(*ntlmssp_state)->lm_resp);
+       data_blob_free(&(*ntlmssp_state)->nt_resp);
+
+       SAFE_FREE((*ntlmssp_state)->user);
+       SAFE_FREE((*ntlmssp_state)->domain);
+       SAFE_FREE((*ntlmssp_state)->workstation);
+
+       talloc_destroy(mem_ctx);
+       *ntlmssp_state = NULL;
+       return NT_STATUS_OK;
+}
+
+/**
+ * Next state function for the NTLMSSP state machine
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. 
+ */
+
+NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, 
+                              const DATA_BLOB request, DATA_BLOB *reply) 
+{
+       uint32 ntlmssp_command;
+       *reply = data_blob(NULL, 0);
+
+       if (!msrpc_parse(&request, "Cd",
+                        "NTLMSSP",
+                        &ntlmssp_command)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (ntlmssp_command != ntlmssp_state->expected_state) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (ntlmssp_command == NTLMSSP_NEGOTIATE) {
+               return ntlmssp_server_negotiate(ntlmssp_state, request, reply);
+       } else if (ntlmssp_command == NTLMSSP_AUTH) {
+               return ntlmssp_server_auth(ntlmssp_state, request, reply);
+       } else {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+}
+
+/*********************************************************************
+ Client side NTLMSSP
+*********************************************************************/
+
+/**
+ * Next state function for the Initial packet
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB.  reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK. 
+ */
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_client_state *ntlmssp_state, 
+                                 DATA_BLOB reply, DATA_BLOB *next_request) 
+{
+       if (ntlmssp_state->unicode) {
+               ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+       }
+       
+       /* generate the ntlmssp negotiate packet */
+       msrpc_gen(next_request, "CddAA",
+                 "NTLMSSP",
+                 NTLMSSP_NEGOTIATE,
+                 ntlmssp_state->neg_flags,
+                 ntlmssp_state->get_domain(), 
+                 ntlmssp_state->get_global_myname());
+
+       return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Challenge Packet.  Generate an auth packet.
+ * 
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB.  reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK. 
+ */
+
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_client_state *ntlmssp_state, 
+                                        const DATA_BLOB reply, DATA_BLOB *next_request) 
+{
+       uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
+       DATA_BLOB server_domain_blob;
+       DATA_BLOB challenge_blob;
+       DATA_BLOB struct_blob;
+       char *server_domain;
+       const char *chal_parse_string;
+       const char *auth_gen_string;
+       DATA_BLOB lm_response = data_blob(NULL, 0);
+       DATA_BLOB nt_response = data_blob(NULL, 0);
+       DATA_BLOB session_key = data_blob(NULL, 0);
+       uint8 datagram_sess_key[16];
+
+       ZERO_STRUCT(datagram_sess_key);
+
+       if (!msrpc_parse(&reply, "CdBd",
+                        "NTLMSSP",
+                        &ntlmssp_command, 
+                        &server_domain_blob,
+                        &chal_flags)) {
+               DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       data_blob_free(&server_domain_blob);
+
+       if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+               chal_parse_string = "CdUdbddB";
+               auth_gen_string = "CdBBUUUBd";
+               ntlmssp_state->unicode = True;
+               ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+       } else if (chal_flags & NTLMSSP_NEGOTIATE_OEM) {
+               chal_parse_string = "CdAdbddB";
+               auth_gen_string = "CdBBAAABd";
+               ntlmssp_state->unicode = False;
+               ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+               ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+       } else {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!msrpc_parse(&reply, chal_parse_string,
+                        "NTLMSSP",
+                        &ntlmssp_command, 
+                        &server_domain,
+                        &chal_flags,
+                        &challenge_blob, 8,
+                        &unkn1, &unkn2,
+                        &struct_blob)) {
+               DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       SAFE_FREE(server_domain);
+       data_blob_free(&struct_blob);
+       
+       if (challenge_blob.length != 8) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (ntlmssp_state->use_ntlmv2) {
+
+               /* TODO: if the remote server is standalone, then we should replace 'domain'
+                  with the server name as supplied above */
+               
+               if (!SMBNTLMv2encrypt(ntlmssp_state->user, 
+                                     ntlmssp_state->domain, 
+                                     ntlmssp_state->password, challenge_blob, 
+                                     &lm_response, &nt_response, &session_key)) {
+                       data_blob_free(&challenge_blob);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               uchar nt_hash[16];
+               E_md4hash(ntlmssp_state->password, nt_hash);
+               
+               /* non encrypted password supplied. Ignore ntpass. */
+               if (lp_client_lanman_auth()) {
+                       lm_response = data_blob(NULL, 24);
+                       SMBencrypt(ntlmssp_state->password,challenge_blob.data,
+                                  lm_response.data);
+               }
+               
+               nt_response = data_blob(NULL, 24);
+               SMBNTencrypt(ntlmssp_state->password,challenge_blob.data,
+                            nt_response.data);
+               session_key = data_blob(NULL, 16);
+               SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+       }
+       
+       data_blob_free(&challenge_blob);
+
+       /* this generates the actual auth packet */
+       if (!msrpc_gen(next_request, auth_gen_string, 
+                      "NTLMSSP", 
+                      NTLMSSP_AUTH, 
+                      lm_response.data, lm_response.length,
+                      nt_response.data, nt_response.length,
+                      ntlmssp_state->domain, 
+                      ntlmssp_state->user, 
+                      ntlmssp_state->get_global_myname(), 
+                      datagram_sess_key, 0,
+                      ntlmssp_state->neg_flags)) {
+               
+               data_blob_free(&lm_response);
+               data_blob_free(&nt_response);
+               data_blob_free(&session_key);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       data_blob_free(&lm_response);
+       data_blob_free(&nt_response);
+
+       ntlmssp_state->session_key = session_key;
+
+       return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_STATE **ntlmssp_state)
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("NTLMSSP Client context");
+       
+       *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+       if (!*ntlmssp_state) {
+               DEBUG(0,("ntlmssp_server_start: talloc failed!\n"));
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*ntlmssp_state)->mem_ctx = mem_ctx;
+
+       (*ntlmssp_state)->get_global_myname = lp_netbios_name;
+       (*ntlmssp_state)->get_domain = lp_workgroup;
+
+       (*ntlmssp_state)->unicode = True;
+
+       (*ntlmssp_state)->neg_flags = 
+               NTLMSSP_NEGOTIATE_128 | 
+               NTLMSSP_NEGOTIATE_NTLM |
+               NTLMSSP_REQUEST_TARGET;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state)
+{
+       TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
+
+       data_blob_free(&(*ntlmssp_state)->session_key);
+       talloc_destroy(mem_ctx);
+       *ntlmssp_state = NULL;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state, 
+                              DATA_BLOB reply, DATA_BLOB *next_request) 
+{
+       uint32 ntlmssp_command;
+       *next_request = data_blob(NULL, 0);
+
+       if (!reply.length) {
+               return ntlmssp_client_initial(ntlmssp_state, reply, next_request);
+       }               
+
+       if (!msrpc_parse(&reply, "Cd",
+                        "NTLMSSP",
+                        &ntlmssp_command)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (ntlmssp_command == NTLMSSP_CHALLENGE) {
+               return ntlmssp_client_challenge(ntlmssp_state, reply, next_request);
+       }
+       return NT_STATUS_INVALID_PARAMETER;
+}
+
+NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *user) 
+{
+       ntlmssp_state->user = talloc_strdup(ntlmssp_state->mem_ctx, user);
+       if (!ntlmssp_state->user) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_set_password(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *password) 
+{
+       ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password);
+       if (!ntlmssp_state->password) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_set_domain(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *domain) 
+{
+       ntlmssp_state->domain = talloc_strdup(ntlmssp_state->mem_ctx, domain);
+       if (!ntlmssp_state->domain) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       return NT_STATUS_OK;
+}
diff --git a/source4/libcli/ntlmssp_parse.c b/source4/libcli/ntlmssp_parse.c
new file mode 100644 (file)
index 0000000..ac779a3
--- /dev/null
@@ -0,0 +1,303 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple kerberos5/SPNEGO routines
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Jim McDonough   2002
+   Copyright (C) Andrew Bartlett 2002-2003
+   
+   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"
+
+/*
+  this is a tiny msrpc packet generator. I am only using this to
+  avoid tying this code to a particular varient of our rpc code. This
+  generator is not general enough for all our rpc needs, its just
+  enough for the spnego/ntlmssp code
+
+  format specifiers are:
+
+  U = unicode string (input is unix string)
+  a = address (input is BOOL unicode, char *unix_string)
+      (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+  A = ASCII string (input is unix string)
+  B = data blob (pointer + length)
+  b = data blob in header (pointer + length)
+  D
+  d = word (4 bytes)
+  C = constant ascii string
+ */
+BOOL msrpc_gen(DATA_BLOB *blob,
+              const char *format, ...)
+{
+       int i, n;
+       va_list ap;
+       char *s;
+       uint8 *b;
+       int head_size=0, data_size=0;
+       int head_ofs, data_ofs;
+       BOOL unicode;
+
+       /* first scan the format to work out the header and body size */
+       va_start(ap, format);
+       for (i=0; format[i]; i++) {
+               switch (format[i]) {
+               case 'U':
+                       s = va_arg(ap, char *);
+                       head_size += 8;
+                       data_size += str_charnum(s) * 2;
+                       break;
+               case 'A':
+                       s = va_arg(ap, char *);
+                       head_size += 8;
+                       data_size += str_ascii_charnum(s);
+                       break;
+               case 'a':
+                       unicode = va_arg(ap, BOOL);
+                       n = va_arg(ap, int);
+                       s = va_arg(ap, char *);
+                       if (unicode) {
+                               data_size += (str_charnum(s) * 2) + 4;
+                       } else {
+                               data_size += (str_ascii_charnum(s)) + 4;
+                       }
+                       break;
+               case 'B':
+                       b = va_arg(ap, uint8 *);
+                       head_size += 8;
+                       data_size += va_arg(ap, int);
+                       break;
+               case 'b':
+                       b = va_arg(ap, uint8 *);
+                       head_size += va_arg(ap, int);
+                       break;
+               case 'd':
+                       n = va_arg(ap, int);
+                       head_size += 4;
+                       break;
+               case 'C':
+                       s = va_arg(ap, char *);
+                       head_size += str_charnum(s) + 1;
+                       break;
+               }
+       }
+       va_end(ap);
+
+       /* allocate the space, then scan the format again to fill in the values */
+       *blob = data_blob(NULL, head_size + data_size);
+
+       head_ofs = 0;
+       data_ofs = head_size;
+
+       va_start(ap, format);
+       for (i=0; format[i]; i++) {
+               switch (format[i]) {
+               case 'U':
+                       s = va_arg(ap, char *);
+                       n = str_charnum(s);
+                       SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+                       SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+                       SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+                       push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
+                       data_ofs += n*2;
+                       break;
+               case 'A':
+                       s = va_arg(ap, char *);
+                       n = str_ascii_charnum(s);
+                       SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+                       SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+                       SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+                       push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
+                       data_ofs += n;
+                       break;
+               case 'a':
+                       unicode = va_arg(ap, BOOL);
+                       n = va_arg(ap, int);
+                       SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+                       s = va_arg(ap, char *);
+                       if (unicode) {
+                               n = str_charnum(s);
+                               SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
+                               if (0 < n) {
+                                       push_string(NULL, blob->data+data_ofs, s, n*2,
+                                                   STR_UNICODE|STR_NOALIGN);
+                               }
+                               data_ofs += n*2;
+                       } else {
+                               n = str_ascii_charnum(s);
+                               SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+                               if (0 < n) {
+                                       push_string(NULL, blob->data+data_ofs, s, n,
+                                                   STR_ASCII|STR_NOALIGN);
+                               }
+                               data_ofs += n;
+                       }
+                       break;
+
+               case 'B':
+                       b = va_arg(ap, uint8 *);
+                       n = va_arg(ap, int);
+                       SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+                       SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+                       SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+                       memcpy(blob->data+data_ofs, b, n);
+                       data_ofs += n;
+                       break;
+               case 'd':
+                       n = va_arg(ap, int);
+                       SIVAL(blob->data, head_ofs, n); head_ofs += 4;
+                       break;
+               case 'b':
+                       b = va_arg(ap, uint8 *);
+                       n = va_arg(ap, int);
+                       memcpy(blob->data + head_ofs, b, n);
+                       head_ofs += n;
+                       break;
+               case 'C':
+                       s = va_arg(ap, char *);
+                       head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
+                                               STR_ASCII|STR_TERMINATE);
+                       break;
+               }
+       }
+       va_end(ap);
+
+       return True;
+}
+
+
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if ((head_ofs + amount) > blob->length) { \
+        return False; \
+}
+
+/*
+  this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+  format specifiers are:
+
+  U = unicode string (output is unix string)
+  A = ascii string
+  B = data blob
+  b = data blob in header
+  d = word (4 bytes)
+  C = constant ascii string
+ */
+
+BOOL msrpc_parse(const DATA_BLOB *blob,
+                const char *format, ...)
+{
+       int i;
+       va_list ap;
+       char **ps, *s;
+       DATA_BLOB *b;
+       size_t head_ofs = 0;
+       uint16 len1, len2;
+       uint32 ptr;
+       uint32 *v;
+       pstring p;
+
+       va_start(ap, format);
+       for (i=0; format[i]; i++) {
+               switch (format[i]) {
+               case 'U':
+                       NEED_DATA(8);
+                       len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
+
+                       /* make sure its in the right format - be strict */
+                       if (len1 != len2 || ptr + len1 > blob->length) {
+                               return False;
+                       }
+                       if (len1 & 1) {
+                               /* if odd length and unicode */
+                               return False;
+                       }
+
+                       ps = va_arg(ap, char **);
+                       if (0 < len1) {
+                               pull_string(NULL, p, blob->data + ptr, sizeof(p), 
+                                           len1, 
+                                           STR_UNICODE|STR_NOALIGN);
+                               (*ps) = smb_xstrdup(p);
+                       } else {
+                               (*ps) = smb_xstrdup("");
+                       }
+                       break;
+               case 'A':
+                       NEED_DATA(8);
+                       len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
+
+                       /* make sure its in the right format - be strict */
+                       if (len1 != len2 || ptr + len1 > blob->length) {
+                               return False;
+                       }
+
+                       ps = va_arg(ap, char **);
+                       if (0 < len1) {
+                               pull_string(NULL, p, blob->data + ptr, sizeof(p), 
+                                           len1, 
+                                           STR_ASCII|STR_NOALIGN);
+                               (*ps) = smb_xstrdup(p);
+                       } else {
+                               (*ps) = smb_xstrdup("");
+                       }
+                       break;
+               case 'B':
+                       NEED_DATA(8);
+                       len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+                       ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
+                       /* make sure its in the right format - be strict */
+                       if (len1 != len2 || ptr + len1 > blob->length) {
+                               return False;
+                       }
+                       b = (DATA_BLOB *)va_arg(ap, void *);
+                       *b = data_blob(blob->data + ptr, len1);
+                       break;
+               case 'b':
+                       b = (DATA_BLOB *)va_arg(ap, void *);
+                       len1 = va_arg(ap, unsigned);
+                       /* make sure its in the right format - be strict */
+                       NEED_DATA(len1);
+                       *b = data_blob(blob->data + head_ofs, len1);
+                       head_ofs += len1;
+                       break;
+               case 'd':
+                       v = va_arg(ap, uint32 *);
+                       NEED_DATA(4);
+                       *v = IVAL(blob->data, head_ofs); head_ofs += 4;
+                       break;
+               case 'C':
+                       s = va_arg(ap, char *);
+                       head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p), 
+                                               blob->length - head_ofs, 
+                                               STR_ASCII|STR_TERMINATE);
+                       if (strcmp(s, p) != 0) {
+                               return False;
+                       }
+                       break;
+               }
+       }
+       va_end(ap);
+
+       return True;
+}
+
diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README
new file mode 100644 (file)
index 0000000..cb3e507
--- /dev/null
@@ -0,0 +1,5 @@
+Design notes for client library restructure:
+
+1 - no references to cli_state should exist in libcli/raw.
+2 - all interfaces to functions in this directory should use cli_session or cli_tree as
+       the primary context structure
\ No newline at end of file
diff --git a/source4/libcli/raw/clikrb5.c b/source4/libcli/raw/clikrb5.c
new file mode 100644 (file)
index 0000000..5edc56d
--- /dev/null
@@ -0,0 +1,399 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple kerberos5 routines for active directory
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Luke Howard 2002-2003
+   
+   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"
+
+#ifdef HAVE_KRB5
+
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+#define KRB5_KEY_TYPE(k)       ((k)->keytype)
+#define KRB5_KEY_LENGTH(k)     ((k)->keyvalue.length)
+#define KRB5_KEY_DATA(k)       ((k)->keyvalue.data)
+#else
+#define        KRB5_KEY_TYPE(k)        ((k)->enctype)
+#define KRB5_KEY_LENGTH(k)     ((k)->length)
+#define KRB5_KEY_DATA(k)       ((k)->contents)
+#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+/*
+ * This function is not in the Heimdal mainline.
+ */
+ krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
+{
+       krb5_error_code ret;
+       int32_t sec, usec;
+
+       ret = krb5_us_timeofday(context, &sec, &usec);
+       if (ret)
+               return ret;
+
+       context->kdc_sec_offset = seconds - sec;
+       context->kdc_usec_offset = microseconds - usec;
+
+       return 0;
+}
+#endif
+
+#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+       return krb5_set_default_in_tkt_etypes(ctx, enc);
+}
+#endif
+
+#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
+/* HEIMDAL */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+       pkaddr->addr_type = KRB5_ADDRESS_INET;
+       pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+       pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
+/* MIT */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+       pkaddr->addrtype = ADDRTYPE_INET;
+       pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+       pkaddr->contents = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#else
+ __ERROR__XX__UNKNOWN_ADDRTYPE
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
+ int create_kerberos_key_from_string(krb5_context context,
+                                       krb5_principal host_princ,
+                                       krb5_data *password,
+                                       krb5_keyblock *key,
+                                       krb5_enctype enctype)
+{
+       int ret;
+       krb5_data salt;
+       krb5_encrypt_block eblock;
+
+       ret = krb5_principal2salt(context, host_princ, &salt);
+       if (ret) {
+               DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
+               return ret;
+       }
+       krb5_use_enctype(context, &eblock, enctype);
+       return krb5_string_to_key(context, &eblock, key, password, &salt);
+}
+#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
+ int create_kerberos_key_from_string(krb5_context context,
+                                       krb5_principal host_princ,
+                                       krb5_data *password,
+                                       krb5_keyblock *key,
+                                       krb5_enctype enctype)
+{
+       int ret;
+       krb5_salt salt;
+
+       ret = krb5_get_pw_salt(context, host_princ, &salt);
+       if (ret) {
+               DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
+               return ret;
+       }
+       return krb5_string_to_key_salt(context, enctype, password->data,
+               salt, key);
+}
+#else
+ __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 
+                                           krb5_enctype **enctypes)
+{
+       return krb5_get_permitted_enctypes(context, enctypes);
+}
+#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 
+                                           krb5_enctype **enctypes)
+{
+       return krb5_get_default_in_tkt_etypes(context, enctypes);
+}
+#else
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#endif
+
+ void free_kerberos_etypes(krb5_context context, 
+                          krb5_enctype *enctypes)
+{
+#if defined(HAVE_KRB5_FREE_KTYPES)
+       krb5_free_ktypes(context, enctypes);
+       return;
+#else
+       SAFE_FREE(enctypes);
+       return;
+#endif
+}
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
+                                       krb5_auth_context auth_context,
+                                       krb5_keyblock *keyblock)
+{
+       return krb5_auth_con_setkey(context, auth_context, keyblock);
+}
+#endif
+
+ void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+       if (tkt->enc_part2)
+               *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents,
+                       tkt->enc_part2->authorization_data[0]->length);
+#else
+       if (tkt->ticket.authorization_data && tkt->ticket.authorization_data->len)
+               *auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data,
+                       tkt->ticket.authorization_data->val->ad_data.length);
+#endif
+}
+
+ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+       return tkt->enc_part2->client;
+#else
+       return tkt->client;
+#endif
+}
+
+#if !defined(HAVE_KRB5_LOCATE_KDC)
+ krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+       krb5_krbhst_handle hnd;
+       krb5_krbhst_info *hinfo;
+       krb5_error_code rc;
+       int num_kdcs, i;
+       struct sockaddr *sa;
+
+       *addr_pp = NULL;
+       *naddrs = 0;
+
+       rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
+       if (rc) {
+               DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
+               return rc;
+       }
+
+       for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
+               ;
+
+       krb5_krbhst_reset(ctx, hnd);
+
+       if (!num_kdcs) {
+               DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n"));
+               krb5_krbhst_free(ctx, hnd);
+               return -1;
+       }
+
+       sa = malloc( sizeof(struct sockaddr) * num_kdcs );
+       if (!sa) {
+               DEBUG(0, ("krb5_locate_kdc: malloc failed\n"));
+               krb5_krbhst_free(ctx, hnd);
+               naddrs = 0;
+               return -1;
+       }
+
+       memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+
+       for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
+               if (hinfo->ai->ai_family == AF_INET)
+                       memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
+       }
+
+       krb5_krbhst_free(ctx, hnd);
+
+       *naddrs = num_kdcs;
+       *addr_pp = sa;
+       return 0;
+}
+#endif
+
+/*
+  we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static krb5_error_code krb5_mk_req2(krb5_context context, 
+                                   krb5_auth_context *auth_context, 
+                                   const krb5_flags ap_req_options,
+                                   const char *principal,
+                                   krb5_ccache ccache, 
+                                   krb5_data *outbuf)
+{
+       krb5_error_code           retval;
+       krb5_principal    server;
+       krb5_creds              * credsp;
+       krb5_creds                creds;
+       krb5_data in_data;
+       
+       retval = krb5_parse_name(context, principal, &server);
+       if (retval) {
+               DEBUG(1,("Failed to parse principal %s\n", principal));
+               return retval;
+       }
+       
+       /* obtain ticket & session key */
+       memset((char *)&creds, 0, sizeof(creds));
+       if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+               DEBUG(1,("krb5_copy_principal failed (%s)\n", 
+                        error_message(retval)));
+               goto cleanup_princ;
+       }
+       
+       if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+               DEBUG(1,("krb5_cc_get_principal failed (%s)\n", 
+                        error_message(retval)));
+               goto cleanup_creds;
+       }
+
+       if ((retval = krb5_get_credentials(context, 0,
+                                          ccache, &creds, &credsp))) {
+               DEBUG(1,("krb5_get_credentials failed for %s (%s)\n", 
+                        principal, error_message(retval)));
+               goto cleanup_creds;
+       }
+
+       /* cope with the ticket being in the future due to clock skew */
+       if ((unsigned)credsp->times.starttime > time(NULL)) {
+               time_t t = time(NULL);
+               int time_offset = (unsigned)credsp->times.starttime - t;
+               DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+               krb5_set_real_time(context, t + time_offset + 1, 0);
+       }
+
+       in_data.length = 0;
+       retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 
+                                     &in_data, credsp, outbuf);
+       if (retval) {
+               DEBUG(1,("krb5_mk_req_extended failed (%s)\n", 
+                        error_message(retval)));
+       }
+       
+       krb5_free_creds(context, credsp);
+
+cleanup_creds:
+       krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+       krb5_free_principal(context, server);
+
+       return retval;
+}
+
+/*
+  get a kerberos5 ticket for the given service 
+*/
+DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
+{
+       krb5_error_code retval;
+       krb5_data packet;
+       krb5_ccache ccdef;
+       krb5_context context;
+       krb5_auth_context auth_context = NULL;
+       DATA_BLOB ret;
+       krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+               ENCTYPE_ARCFOUR_HMAC,
+#endif 
+               ENCTYPE_DES_CBC_MD5, 
+               ENCTYPE_DES_CBC_CRC, 
+               ENCTYPE_NULL};
+       
+       retval = krb5_init_context(&context);
+       if (retval) {
+               DEBUG(1,("krb5_init_context failed (%s)\n", 
+                        error_message(retval)));
+               goto failed;
+       }
+
+       if (time_offset != 0) {
+               krb5_set_real_time(context, time(NULL) + time_offset, 0);
+       }
+
+       if ((retval = krb5_cc_default(context, &ccdef))) {
+               DEBUG(1,("krb5_cc_default failed (%s)\n",
+                        error_message(retval)));
+               goto failed;
+       }
+
+       if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+               DEBUG(1,("krb5_set_default_tgs_ktypes failed (%s)\n",
+                        error_message(retval)));
+               goto failed;
+       }
+
+       if ((retval = krb5_mk_req2(context, 
+                                  &auth_context, 
+                                  0, 
+                                  principal,
+                                  ccdef, &packet))) {
+               goto failed;
+       }
+
+       ret = data_blob(packet.data, packet.length);
+/* Hmm, heimdal dooesn't have this - what's the correct call? */
+/*     krb5_free_data_contents(context, &packet); */
+       krb5_free_context(context);
+       return ret;
+
+failed:
+       if ( context )
+               krb5_free_context(context);
+               
+       return data_blob(NULL, 0);
+}
+
+ BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16])
+ {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+       krb5_keyblock *skey;
+#endif
+       BOOL ret = False;
+
+       memset(session_key, 0, 16);
+
+#ifdef ENCTYPE_ARCFOUR_HMAC
+       if (krb5_auth_con_getremotesubkey(context, auth_context, &skey) == 0 && skey != NULL) {
+               if (KRB5_KEY_TYPE(skey) ==
+                   ENCTYPE_ARCFOUR_HMAC
+                   && KRB5_KEY_LENGTH(skey) == 16) {
+                       memcpy(session_key, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+                       ret = True;
+               }
+               krb5_free_keyblock(context, skey);
+       }
+#endif /* ENCTYPE_ARCFOUR_HMAC */
+
+       return ret;
+ }
+#else /* HAVE_KRB5 */
+ /* this saves a few linking headaches */
+DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset)
+ {
+        DEBUG(0,("NO KERBEROS SUPPORT\n"));
+        return data_blob(NULL, 0);
+ }
+
+#endif
diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c
new file mode 100644 (file)
index 0000000..8f69716
--- /dev/null
@@ -0,0 +1,57 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client oplock functions
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+BOOL cli_oplock_ack(struct cli_tree *tree, uint16 fnum, uint16 ack_level)
+{
+       BOOL ret;
+       struct cli_request *req;
+
+       req = cli_request_setup(tree, SMBlockingX, 8, 0);
+
+       SSVAL(req->out.vwv,VWV(0),0xFF);
+       SSVAL(req->out.vwv,VWV(1),0);
+       SSVAL(req->out.vwv,VWV(2),fnum);
+       SSVAL(req->out.vwv,VWV(3),ack_level);
+       SIVAL(req->out.vwv,VWV(4),0);
+       SSVAL(req->out.vwv,VWV(6),0);
+       SSVAL(req->out.vwv,VWV(7),0);
+
+       ret = cli_request_send(req);    
+       cli_request_destroy(req);
+
+       return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+void cli_oplock_handler(struct cli_transport *transport, 
+                       BOOL (*handler)(struct cli_transport *, uint16, uint16, uint8, void *),
+                       void *private)
+{
+       transport->oplock.handler = handler;
+       transport->oplock.private = private;
+}
diff --git a/source4/libcli/raw/clirewrite.c b/source4/libcli/raw/clirewrite.c
new file mode 100644 (file)
index 0000000..2d2e2e3
--- /dev/null
@@ -0,0 +1,22 @@
+#include "includes.h"
+
+/*
+
+ this is a set of temporary stub functions used during the libsmb rewrite.
+ This file will need to go away before the rewrite is complete.
+*/
+
+void become_root(void)
+{}
+
+void unbecome_root(void)
+{}
+
+BOOL become_user_permanently(uid_t uid, gid_t gid)
+{ return True; }
+
+void set_effective_uid(uid_t uid)
+{}
+
+uid_t sec_initial_uid(void)
+{ return 0; }
diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c
new file mode 100644 (file)
index 0000000..406491e
--- /dev/null
@@ -0,0 +1,444 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client session context management functions
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+#define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
+       req = cli_request_setup_session(session, cmd, wct, buflen); \
+       if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ Initialize the session context
+****************************************************************************/
+struct cli_session *cli_session_init(struct cli_transport *transport)
+{
+       struct cli_session *session;
+       TALLOC_CTX *mem_ctx = talloc_init("cli_session");
+       if (mem_ctx == NULL) {
+               return NULL;
+       }
+
+       session = talloc_zero(mem_ctx, sizeof(*session));
+       if (!session) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       session->mem_ctx = mem_ctx;
+       session->transport = transport;
+       session->pid = (uint16)getpid();
+       session->vuid = UID_FIELD_INVALID;
+       session->transport->reference_count++;
+
+       return session;
+}
+
+/****************************************************************************
+reduce reference_count and destroy is <= 0
+****************************************************************************/
+void cli_session_close(struct cli_session *session)
+{
+       session->reference_count--;
+       if (session->reference_count <= 0) {
+               cli_transport_close(session->transport);
+               talloc_destroy(session->mem_ctx);
+       }
+}
+
+/****************************************************************************
+ Perform a session setup (async send)
+****************************************************************************/
+struct cli_request *smb_raw_session_setup_send(struct cli_session *session, union smb_sesssetup *parms) 
+{
+       struct cli_request *req;
+
+       switch (parms->generic.level) {
+       case RAW_SESSSETUP_GENERIC:
+               /* handled elsewhere */
+               return NULL;
+
+       case RAW_SESSSETUP_OLD:
+               SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
+               SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
+               SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
+               SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
+               SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
+               cli_req_append_blob(req, &parms->old.in.password);
+               cli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
+               cli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
+               cli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
+               cli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
+               break;
+
+       case RAW_SESSSETUP_NT1:
+               SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
+               SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
+               SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
+               SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
+               SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
+               SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
+               SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
+               SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
+               cli_req_append_blob(req, &parms->nt1.in.password1);
+               cli_req_append_blob(req, &parms->nt1.in.password2);
+               cli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
+               cli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
+               cli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
+               cli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
+               break;
+
+       case RAW_SESSSETUP_SPNEGO:
+               SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
+               SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
+               SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
+               SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
+               SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
+               SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
+               cli_req_append_blob(req, &parms->spnego.in.secblob);
+               cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
+               cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
+               break;
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+/****************************************************************************
+ Perform a session setup (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_session_setup_recv(struct cli_request *req, 
+                                   TALLOC_CTX *mem_ctx, 
+                                   union smb_sesssetup *parms) 
+{
+       uint16 len;
+       char *p;
+
+       if (!cli_request_receive(req)) {
+               return cli_request_destroy(req);
+       }
+       
+       if (!NT_STATUS_IS_OK(req->status) &&
+           !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               return cli_request_destroy(req);
+       }
+
+       switch (parms->generic.level) {
+       case RAW_SESSSETUP_GENERIC:
+               /* handled elsewhere */
+               return NT_STATUS_INVALID_LEVEL;
+
+       case RAW_SESSSETUP_OLD:
+               CLI_CHECK_WCT(req, 3);
+               ZERO_STRUCT(parms->old.out);
+               parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
+               parms->old.out.action = SVAL(req->in.vwv, VWV(2));
+               p = req->in.data;
+               if (p) {
+                       p += cli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
+                       p += cli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
+                       p += cli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
+               }
+               break;
+
+       case RAW_SESSSETUP_NT1:
+               CLI_CHECK_WCT(req, 3);
+               ZERO_STRUCT(parms->nt1.out);
+               parms->nt1.out.vuid   = SVAL(req->in.hdr, HDR_UID);
+               parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
+               p = req->in.data;
+               if (p) {
+                       p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
+                       p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
+                       if (p < (req->in.data + req->in.data_size)) {
+                               p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
+                       }
+               }
+               break;
+
+       case RAW_SESSSETUP_SPNEGO:
+               CLI_CHECK_WCT(req, 4);
+               ZERO_STRUCT(parms->spnego.out);
+               parms->spnego.out.vuid   = SVAL(req->in.hdr, HDR_UID);
+               parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
+               len                      = SVAL(req->in.vwv, VWV(3));
+               p = req->in.data;
+               if (!p) {
+                       break;
+               }
+
+               parms->spnego.out.secblob = cli_req_pull_blob(req, mem_ctx, p, len);
+               p += parms->spnego.out.secblob.length;
+               p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
+               p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
+               p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE);
+               break;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+/*
+  form an encrypted lanman password from a plaintext password
+  and the server supplied challenge
+*/
+static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
+{
+       DATA_BLOB blob = data_blob(NULL, 24);
+       SMBencrypt(pass, challenge.data, blob.data);
+       return blob;
+}
+
+/*
+  form an encrypted NT password from a plaintext password
+  and the server supplied challenge
+*/
+static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
+{
+       DATA_BLOB blob = data_blob(NULL, 24);
+       SMBNTencrypt(pass, challenge.data, blob.data);
+       return blob;
+}
+
+/*
+  setup signing for a NT1 style session setup
+*/
+static void setup_nt1_signing(struct cli_transport *transport, const char *password)
+{
+       uchar nt_hash[16];
+       uchar session_key[16];
+       DATA_BLOB nt_response;
+
+       E_md4hash(password, nt_hash);
+       SMBsesskeygen_ntv1(nt_hash, NULL, session_key);
+       nt_response = nt_blob(password, transport->negotiate.secblob);
+
+       cli_transport_simple_set_signing(transport, session_key, nt_response);
+}
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface and the old
+ style sesssetup call
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session, 
+                                                 TALLOC_CTX *mem_ctx, 
+                                                 union smb_sesssetup *parms) 
+{
+       NTSTATUS status;
+       union smb_sesssetup s2;
+
+       /* use the old interface */
+       s2.generic.level = RAW_SESSSETUP_OLD;
+       s2.old.in.bufsize = ~0;
+       s2.old.in.mpx_max = 50;
+       s2.old.in.vc_num = 1;
+       s2.old.in.sesskey = parms->generic.in.sesskey;
+       s2.old.in.domain = parms->generic.in.domain;
+       s2.old.in.user = parms->generic.in.user;
+       s2.old.in.os = "Unix";
+       s2.old.in.lanman = "Samba";
+       
+       if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+               s2.old.in.password = lanman_blob(parms->generic.in.password, 
+                                                session->transport->negotiate.secblob);
+       } else {
+               s2.old.in.password = data_blob(parms->generic.in.password, 
+                                              strlen(parms->generic.in.password));
+       }
+       
+       status = smb_raw_session_setup(session, mem_ctx, &s2);
+       
+       data_blob_free(&s2.old.in.password);
+       
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       
+       parms->generic.out.vuid = s2.old.out.vuid;
+       parms->generic.out.os = s2.old.out.os;
+       parms->generic.out.lanman = s2.old.out.lanman;
+       parms->generic.out.domain = s2.old.out.domain;
+       
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface and the NT1
+ style sesssetup call
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, 
+                                                 TALLOC_CTX *mem_ctx,
+                                                 union smb_sesssetup *parms) 
+{
+       NTSTATUS status;
+       union smb_sesssetup s2;
+
+       s2.generic.level = RAW_SESSSETUP_NT1;
+       s2.nt1.in.bufsize = ~0;
+       s2.nt1.in.mpx_max = 50;
+       s2.nt1.in.vc_num = 1;
+       s2.nt1.in.sesskey = parms->generic.in.sesskey;
+       s2.nt1.in.capabilities = parms->generic.in.capabilities;
+       s2.nt1.in.domain = parms->generic.in.domain;
+       s2.nt1.in.user = parms->generic.in.user;
+       s2.nt1.in.os = "Unix";
+       s2.nt1.in.lanman = "Samba";
+
+       if (session->transport->negotiate.sec_mode & 
+           NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+               s2.nt1.in.password1 = lanman_blob(parms->generic.in.password, 
+                                                 session->transport->negotiate.secblob);
+               s2.nt1.in.password2 = nt_blob(parms->generic.in.password, 
+                                             session->transport->negotiate.secblob);
+               setup_nt1_signing(session->transport, parms->generic.in.password);
+       } else {
+               s2.nt1.in.password1 = data_blob(parms->generic.in.password, 
+                                               strlen(parms->generic.in.password));
+               s2.nt1.in.password2 = data_blob(NULL, 0);
+       }
+
+       status = smb_raw_session_setup(session, mem_ctx, &s2);
+               
+       data_blob_free(&s2.nt1.in.password1);
+       data_blob_free(&s2.nt1.in.password2);
+               
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       parms->generic.out.vuid = s2.nt1.out.vuid;
+       parms->generic.out.os = s2.nt1.out.os;
+       parms->generic.out.lanman = s2.nt1.out.lanman;
+       parms->generic.out.domain = s2.nt1.out.domain;
+
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Perform a session setup (sync interface) using generic interface
+****************************************************************************/
+static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, 
+                                             TALLOC_CTX *mem_ctx,
+                                             union smb_sesssetup *parms) 
+{
+       if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
+               /* no session setup at all in earliest protocols */
+               ZERO_STRUCT(parms->generic.out);
+               return NT_STATUS_OK;
+       }
+
+       /* see if we need to use the original session setup interface */
+       if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
+               return smb_raw_session_setup_generic_old(session, mem_ctx, parms);
+       }
+
+       /* see if we should use the NT1 interface */
+       if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) ||
+           !session->transport->options.use_spnego) {
+               return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms);
+       }
+
+       /* default to using SPNEGO/NTLMSSP */
+       DEBUG(0,("Need to add client SPNEGO code back in\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/****************************************************************************
+ Perform a session setup (sync interface)
+this interface allows for RAW_SESSSETUP_GENERIC to auto-select session
+setup varient based on negotiated protocol options
+****************************************************************************/
+NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx, 
+                              union smb_sesssetup *parms) 
+{
+       struct cli_request *req;
+
+       if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
+               return smb_raw_session_setup_generic(session, mem_ctx, parms);
+       }
+
+       req = smb_raw_session_setup_send(session, parms);
+       return smb_raw_session_setup_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a uloggoff (async send)
+*****************************************************************************/
+struct cli_request *smb_raw_ulogoff_send(struct cli_session *session)
+{
+       struct cli_request *req;
+
+       SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Send a uloggoff (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_ulogoff(struct cli_session *session)
+{
+       struct cli_request *req = smb_raw_ulogoff_send(session);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Send a SMBexit
+****************************************************************************/
+NTSTATUS smb_raw_exit(struct cli_session *session)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup_session(session, SMBexit, 0, 0);
+
+       if (cli_request_send(req)) {
+               cli_request_receive(req);
+       }
+       return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c
new file mode 100644 (file)
index 0000000..f0e0508
--- /dev/null
@@ -0,0 +1,148 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client socket context management functions
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+
+/*
+  create a cli_socket context
+*/
+struct cli_socket *cli_sock_init(void)
+{
+       struct cli_socket *sock;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("cli_socket");
+       if (!mem_ctx) return NULL;
+
+       sock = talloc_zero(mem_ctx, sizeof(*sock));
+       if (!sock) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       sock->mem_ctx = mem_ctx;
+       sock->fd = -1;
+       sock->port = 445;
+       /* 20 second default timeout */
+       sock->timeout = 20000;
+
+       return sock;
+}
+
+/*
+  connect a cli_socket context to an IP/port pair
+  if port is 0 then choose 445 then 139
+*/
+BOOL cli_sock_connect(struct cli_socket *sock, struct in_addr *ip, int port)
+{
+       if (getenv("LIBSMB_PROG")) {
+               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
+               return sock->fd != -1;
+       }
+
+       if (port == 0) {
+               return cli_sock_connect(sock, ip, 445) ||
+                       cli_sock_connect(sock, ip, 139);
+       }
+
+       sock->dest_ip = *ip;
+       sock->port = port;
+       sock->fd = open_socket_out(SOCK_STREAM,
+                                  &sock->dest_ip,
+                                  sock->port, 
+                                  LONG_CONNECT_TIMEOUT);
+       return (sock->fd != -1);
+}
+
+
+/****************************************************************************
+ reduce socket reference count - if it becomes zero then close
+****************************************************************************/
+void cli_sock_close(struct cli_socket *sock)
+{
+       sock->reference_count--;
+       if (sock->reference_count <= 0 && sock->fd != -1) {
+               close(sock->fd);
+               sock->fd = -1;
+       }
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+void cli_sock_set_options(struct cli_socket *sock, const char *options)
+{
+       set_socket_options(sock->fd, options);
+}
+
+/****************************************************************************
+ Write to socket. Return amount written.
+****************************************************************************/
+ssize_t cli_sock_write(struct cli_socket *sock, const char *data, size_t len)
+{
+       return write_data(sock->fd, data, len);
+}
+
+
+/****************************************************************************
+ Read from socket. return amount read
+****************************************************************************/
+ssize_t cli_sock_read(struct cli_socket *sock, char *data, size_t len)
+{
+       return read_data(sock->fd, data, len);
+}
+
+/****************************************************************************
+resolve a hostname and connect 
+****************************************************************************/
+BOOL cli_sock_connect_byname(struct cli_socket *sock, const char *host, int port)
+{
+       int name_type = 0x20;
+       struct in_addr ip;
+       TALLOC_CTX *mem_ctx;
+       char *name, *p;
+
+       if (getenv("LIBSMB_PROG")) {
+               sock->fd = sock_exec(getenv("LIBSMB_PROG"));
+               return sock->fd != -1;
+       }
+
+       mem_ctx = talloc_init("cli_sock_connect_byname");
+       if (!mem_ctx) return False;
+
+       name = talloc_strdup(mem_ctx, host);
+
+       /* allow hostnames of the form NAME#xx and do a netbios lookup */
+       if ((p = strchr(name, '#'))) {
+               name_type = strtol(p+1, NULL, 16);
+               *p = 0;
+       }
+
+       if (!resolve_name(mem_ctx, name, &ip, name_type)) {
+               talloc_destroy(mem_ctx);
+               return False;
+       }
+
+       talloc_destroy(mem_ctx);
+
+       return cli_sock_connect(sock, &ip, port);
+}
diff --git a/source4/libcli/raw/clispnego.c b/source4/libcli/raw/clispnego.c
new file mode 100644 (file)
index 0000000..53f7eb6
--- /dev/null
@@ -0,0 +1,533 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple kerberos5/SPNEGO routines
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Jim McDonough   2002
+   Copyright (C) Luke Howard     2003
+   
+   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"
+
+/*
+  generate a negTokenInit packet given a GUID, a list of supported
+  OIDs (the mechanisms) and a principal name string 
+*/
+DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], 
+                                 const char *OIDs[], 
+                                 const char *principal)
+{
+       int i;
+       ASN1_DATA data;
+       DATA_BLOB ret;
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_write(&data, guid, 16);
+       asn1_push_tag(&data,ASN1_APPLICATION(0));
+       asn1_write_OID(&data,OID_SPNEGO);
+       asn1_push_tag(&data,ASN1_CONTEXT(0));
+       asn1_push_tag(&data,ASN1_SEQUENCE(0));
+
+       asn1_push_tag(&data,ASN1_CONTEXT(0));
+       asn1_push_tag(&data,ASN1_SEQUENCE(0));
+       for (i=0; OIDs[i]; i++) {
+               asn1_write_OID(&data,OIDs[i]);
+       }
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(3));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_write_GeneralString(&data,principal);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+       }
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+
+       return ret;
+}
+
+/*
+  Generate a negTokenInit as used by the client side ... It has a mechType
+  (OID), and a mechToken (a security blob) ... 
+
+  Really, we need to break out the NTLMSSP stuff as well, because it could be
+  raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+       ASN1_DATA data;
+       DATA_BLOB ret;
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_push_tag(&data, ASN1_APPLICATION(0));
+       asn1_write_OID(&data,OID_SPNEGO);
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+       asn1_write_OID(&data, OID);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(2));
+       asn1_write_OctetString(&data,blob.data,blob.length);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+       }
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+
+       return ret;
+}
+
+/*
+  parse a negTokenInit packet giving a GUID, a list of supported
+  OIDs (the mechanisms) and a principal name string 
+*/
+BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
+                              char *OIDs[ASN1_MAX_OIDS], 
+                              char **principal)
+{
+       int i;
+       BOOL ret;
+       ASN1_DATA data;
+
+       asn1_load(&data, blob);
+
+       asn1_start_tag(&data,ASN1_APPLICATION(0));
+       asn1_check_OID(&data,OID_SPNEGO);
+       asn1_start_tag(&data,ASN1_CONTEXT(0));
+       asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+       asn1_start_tag(&data,ASN1_CONTEXT(0));
+       asn1_start_tag(&data,ASN1_SEQUENCE(0));
+       for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+               char *oid = NULL;
+               asn1_read_OID(&data,&oid);
+               OIDs[i] = oid;
+       }
+       OIDs[i] = NULL;
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       asn1_start_tag(&data, ASN1_CONTEXT(3));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_start_tag(&data, ASN1_CONTEXT(0));
+       asn1_read_GeneralString(&data,principal);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       asn1_end_tag(&data);
+
+       ret = !data.has_error;
+       asn1_free(&data);
+       return ret;
+}
+
+
+/*
+  generate a negTokenTarg packet given a list of OIDs and a security blob
+*/
+DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
+{
+       int i;
+       ASN1_DATA data;
+       DATA_BLOB ret;
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_push_tag(&data, ASN1_APPLICATION(0));
+       asn1_write_OID(&data,OID_SPNEGO);
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+       for (i=0; OIDs[i]; i++) {
+               asn1_write_OID(&data,OIDs[i]);
+       }
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(2));
+       asn1_write_OctetString(&data,blob.data,blob.length);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+       }
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+
+       return ret;
+}
+
+
+/*
+  parse a negTokenTarg packet giving a list of OIDs and a security blob
+*/
+BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
+{
+       int i;
+       ASN1_DATA data;
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data, ASN1_APPLICATION(0));
+       asn1_check_OID(&data,OID_SPNEGO);
+       asn1_start_tag(&data, ASN1_CONTEXT(0));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+
+       asn1_start_tag(&data, ASN1_CONTEXT(0));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+               char *oid = NULL;
+               asn1_read_OID(&data,&oid);
+               OIDs[i] = oid;
+       }
+       OIDs[i] = NULL;
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       asn1_start_tag(&data, ASN1_CONTEXT(2));
+       asn1_read_OctetString(&data,secblob);
+       asn1_end_tag(&data);
+
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       asn1_end_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+               return False;
+       }
+
+       asn1_free(&data);
+       return True;
+}
+
+/*
+  generate a krb5 GSS-API wrapper packet given a ticket
+*/
+DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8 tok_id[2])
+{
+       ASN1_DATA data;
+       DATA_BLOB ret;
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_push_tag(&data, ASN1_APPLICATION(0));
+       asn1_write_OID(&data, OID_KERBEROS5);
+
+       asn1_write(&data, tok_id, 2);
+       asn1_write(&data, ticket.data, ticket.length);
+       asn1_pop_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
+               asn1_free(&data);
+       }
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+
+       return ret;
+}
+
+/*
+  parse a krb5 GSS-API wrapper packet giving a ticket
+*/
+BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
+{
+       BOOL ret;
+       ASN1_DATA data;
+       int data_remaining;
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data, ASN1_APPLICATION(0));
+       asn1_check_OID(&data, OID_KERBEROS5);
+
+       data_remaining = asn1_tag_remaining(&data);
+
+       if (data_remaining < 3) {
+               data.has_error = True;
+       } else {
+               asn1_read(&data, tok_id, 2);
+               data_remaining -= 2;
+               *ticket = data_blob(NULL, data_remaining);
+               asn1_read(&data, ticket->data, ticket->length);
+       }
+
+       asn1_end_tag(&data);
+
+       ret = !data.has_error;
+
+       asn1_free(&data);
+
+       return ret;
+}
+
+
+/* 
+   generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
+   kerberos session setup 
+*/
+DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset)
+{
+       DATA_BLOB tkt, tkt_wrapped, targ;
+       const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
+
+       /* get a kerberos ticket for the service */
+       tkt = krb5_get_ticket(principal, time_offset);
+
+       /* wrap that up in a nice GSS-API wrapping */
+       tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+
+       /* and wrap that in a shiny SPNEGO wrapper */
+       targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
+
+       data_blob_free(&tkt_wrapped);
+       data_blob_free(&tkt);
+
+       return targ;
+}
+
+
+/*
+  parse a spnego NTLMSSP challenge packet giving two security blobs
+*/
+BOOL spnego_parse_challenge(const DATA_BLOB blob,
+                           DATA_BLOB *chal1, DATA_BLOB *chal2)
+{
+       BOOL ret;
+       ASN1_DATA data;
+
+       ZERO_STRUCTP(chal1);
+       ZERO_STRUCTP(chal2);
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data,ASN1_CONTEXT(1));
+       asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+       asn1_start_tag(&data,ASN1_CONTEXT(0));
+       asn1_check_enumerated(&data,1);
+       asn1_end_tag(&data);
+
+       asn1_start_tag(&data,ASN1_CONTEXT(1));
+       asn1_check_OID(&data, OID_NTLMSSP);
+       asn1_end_tag(&data);
+
+       asn1_start_tag(&data,ASN1_CONTEXT(2));
+       asn1_read_OctetString(&data, chal1);
+       asn1_end_tag(&data);
+
+       /* the second challenge is optional (XP doesn't send it) */
+       if (asn1_tag_remaining(&data)) {
+               asn1_start_tag(&data,ASN1_CONTEXT(3));
+               asn1_read_OctetString(&data, chal2);
+               asn1_end_tag(&data);
+       }
+
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       ret = !data.has_error;
+       asn1_free(&data);
+       return ret;
+}
+
+
+/*
+ generate a SPNEGO auth packet. This will contain the encrypted passwords
+*/
+DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
+{
+       ASN1_DATA data;
+       DATA_BLOB ret;
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_push_tag(&data, ASN1_CONTEXT(1));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+       asn1_push_tag(&data, ASN1_CONTEXT(2));
+       asn1_write_OctetString(&data,blob.data,blob.length);    
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       ret = data_blob(data.data, data.length);
+
+       asn1_free(&data);
+
+       return ret;
+}
+
+/*
+ parse a SPNEGO auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
+{
+       ASN1_DATA data;
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data, ASN1_CONTEXT(1));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_start_tag(&data, ASN1_CONTEXT(2));
+       asn1_read_OctetString(&data,auth);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
+               asn1_free(&data);
+               return False;
+       }
+
+       asn1_free(&data);
+       return True;
+}
+
+/*
+  generate a minimal SPNEGO response packet.  Doesn't contain much.
+*/
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+                                  const char *mechOID)
+{
+       ASN1_DATA data;
+       DATA_BLOB ret;
+       uint8 negResult;
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+               negResult = SPNEGO_NEG_RESULT_ACCEPT;
+       } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
+       } else {
+               negResult = SPNEGO_NEG_RESULT_REJECT; 
+       }
+
+       ZERO_STRUCT(data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(1));
+       asn1_push_tag(&data, ASN1_SEQUENCE(0));
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+       asn1_write_enumerated(&data, negResult);
+       asn1_pop_tag(&data);
+
+       if (reply->data != NULL) {
+               asn1_push_tag(&data,ASN1_CONTEXT(1));
+               asn1_write_OID(&data, mechOID);
+               asn1_pop_tag(&data);
+               
+               asn1_push_tag(&data,ASN1_CONTEXT(2));
+               asn1_write_OctetString(&data, reply->data, reply->length);
+               asn1_pop_tag(&data);
+       }
+
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       ret = data_blob(data.data, data.length);
+       asn1_free(&data);
+       return ret;
+}
+
+/*
+ parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status, 
+                               DATA_BLOB *auth)
+{
+       ASN1_DATA data;
+       uint8 negResult;
+
+       if (NT_STATUS_IS_OK(nt_status)) {
+               negResult = SPNEGO_NEG_RESULT_ACCEPT;
+       } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+       } else {
+               negResult = SPNEGO_NEG_RESULT_REJECT;
+       }
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data, ASN1_CONTEXT(1));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_start_tag(&data, ASN1_CONTEXT(0));
+       asn1_check_enumerated(&data, negResult);
+       asn1_end_tag(&data);
+
+       if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+               asn1_start_tag(&data,ASN1_CONTEXT(1));
+               asn1_check_OID(&data, OID_NTLMSSP);
+               asn1_end_tag(&data);
+               
+               asn1_start_tag(&data,ASN1_CONTEXT(2));
+               asn1_read_OctetString(&data, auth);
+               asn1_end_tag(&data);
+       }
+
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       if (data.has_error) {
+               DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
+               asn1_free(&data);
+               data_blob_free(auth);
+               return False;
+       }
+
+       asn1_free(&data);
+       return True;
+}
+
diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c
new file mode 100644 (file)
index 0000000..80bb1e3
--- /dev/null
@@ -0,0 +1,218 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client transport context management functions
+   Copyright (C) Andrew Tridgell 1994-2003
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+/*
+  create a transport structure based on an established socket
+*/
+struct cli_transport *cli_transport_init(struct cli_socket *sock)
+{
+       TALLOC_CTX *mem_ctx;
+       struct cli_transport *transport;
+
+       mem_ctx = talloc_init("cli_transport");
+       if (!mem_ctx) return NULL;
+
+       transport = talloc_zero(mem_ctx, sizeof(*transport));
+       if (!transport) return NULL;
+
+       transport->mem_ctx = mem_ctx;
+       transport->socket = sock;
+       transport->negotiate.protocol = PROTOCOL_NT1;
+       transport->negotiate.max_xmit = ~0;
+       cli_null_set_signing(transport);
+       transport->socket->reference_count++;
+
+       return transport;
+}
+
+/*
+  decrease reference count on a transport, and destroy if it becomes
+  zero
+*/
+void cli_transport_close(struct cli_transport *transport)
+{
+       transport->reference_count--;
+       if (transport->reference_count <= 0) {
+               cli_sock_close(transport->socket);
+               talloc_destroy(transport->mem_ctx);
+       }
+}
+
+
+
+/****************************************************************************
+send a session request (if appropriate)
+****************************************************************************/
+BOOL cli_transport_connect(struct cli_transport *transport,
+                          struct nmb_name *calling, 
+                          struct nmb_name *called)
+{
+       char *p;
+       int len = NBT_HDR_SIZE;
+       struct cli_request *req;
+
+       /* 445 doesn't have session request */
+       if (transport->socket->port == 445) {
+               return True;
+       }
+
+       /* allocate output buffer */
+       req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + 2*nbt_mangled_name_len());
+
+       /* put in the destination name */
+       p = req->out.buffer + NBT_HDR_SIZE;
+       name_mangle(called->name, p, called->name_type);
+       len += name_len(p);
+
+       /* and my name */
+       p = req->out.buffer+len;
+       name_mangle(calling->name, p, calling->name_type);
+       len += name_len(p);
+
+       _smb_setlen(req->out.buffer,len-4);
+       SCVAL(req->out.buffer,0,0x81);
+
+       if (!cli_request_send(req) ||
+           !cli_request_receive(req)) {
+               cli_request_destroy(req);
+               return False;
+       }
+       
+       if (CVAL(req->in.buffer,0) != 0x82) {
+               transport->error.etype = ETYPE_NBT;
+               transport->error.e.nbt_error = CVAL(req->in.buffer,4);
+               cli_request_destroy(req);
+               return False;
+       }
+
+       cli_request_destroy(req);
+       return True;
+}
+
+
+/****************************************************************************
+get next mid in sequence
+****************************************************************************/
+uint16 cli_transport_next_mid(struct cli_transport *transport)
+{
+       uint16 mid;
+       struct cli_request *req;
+
+       mid = transport->next_mid;
+
+again:
+       /* now check to see if this mid is being used by one of the 
+          pending requests. This is quite efficient because the list is
+          usually very short */
+
+       /* the zero mid is reserved for requests that don't have a mid */
+       if (mid == 0) mid = 1;
+
+       for (req=transport->pending_requests; req; req=req->next) {
+               if (req->mid == mid) {
+                       mid++;
+                       goto again;
+               }
+       }
+
+       transport->next_mid = mid+1;
+       return mid;
+}
+
+/*
+  setup the idle handler for a transport
+*/
+void cli_transport_idle_handler(struct cli_transport *transport, 
+                               void (*idle_func)(struct cli_transport *, void *),
+                               uint_t period,
+                               void *private)
+{
+       transport->idle.func = idle_func;
+       transport->idle.private = private;
+       transport->idle.period = period;
+}
+
+
+/*
+  determine if a packet is pending for receive on a transport
+*/
+BOOL cli_transport_pending(struct cli_transport *transport)
+{
+       return socket_pending(transport->socket->fd);
+}
+
+
+
+/*
+  wait for data on a transport, periodically calling a wait function
+  if one has been defined
+  return True if a packet is received
+*/
+BOOL cli_transport_select(struct cli_transport *transport)
+{
+       fd_set fds;
+       int selrtn;
+       int fd;
+       struct timeval timeout;
+
+       fd = transport->socket->fd;
+
+       if (fd == -1) {
+               return False;
+       }
+
+       do {
+               uint_t period = 1000;
+
+               FD_ZERO(&fds);
+               FD_SET(fd,&fds);
+       
+               if (transport->idle.func) {
+                       period = transport->idle.period;
+               }
+
+               timeout.tv_sec = period / 1000;
+               timeout.tv_usec = 1000*(period%1000);
+               
+               selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout);
+               
+               if (selrtn == 1) {
+                       /* the fd is readable */
+                       return True;
+               }
+               
+               if (selrtn == -1) {
+                       /* sys_select_intr() already handles EINTR, so this
+                          is an error. The socket is probably dead */
+                       return False;
+               }
+               
+               /* only other possibility is that we timed out - call the idle function
+                  if there is one */
+               if (transport->idle.func) {
+                       transport->idle.func(transport, transport->idle.private);
+               }
+       } while (selrtn == 0);
+
+       return True;
+}
diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c
new file mode 100644 (file)
index 0000000..2a41273
--- /dev/null
@@ -0,0 +1,290 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client tree context management functions
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
+       req = cli_request_setup(tree, cmd, wct, buflen); \
+       if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Initialize the tree context
+****************************************************************************/
+struct cli_tree *cli_tree_init(struct cli_session *session)
+{
+       struct cli_tree *tree;
+       TALLOC_CTX *mem_ctx = talloc_init("cli_tree");
+       if (mem_ctx == NULL) {
+               return NULL;
+       }
+
+       tree = talloc_zero(mem_ctx, sizeof(*tree));
+       if (!tree) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       tree->mem_ctx = mem_ctx;
+       tree->session = session;
+       tree->session->reference_count++;
+
+       return tree;
+}
+
+/****************************************************************************
+reduce reference count on a tree and destroy if <= 0
+****************************************************************************/
+void cli_tree_close(struct cli_tree *tree)
+{
+       if (!tree) return;
+       tree->reference_count--;
+       if (tree->reference_count <= 0) {
+               cli_session_close(tree->session);
+               talloc_destroy(tree->mem_ctx);
+       }
+}
+
+
+/****************************************************************************
+ Send a tconX (async send)
+****************************************************************************/
+struct cli_request *smb_tree_connect_send(struct cli_tree *tree, union smb_tcon *parms)
+{
+       struct cli_request *req;
+
+       switch (parms->tcon.level) {
+       case RAW_TCON_TCON:
+               SETUP_REQUEST_TREE(SMBtcon, 0, 0);
+               cli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII);
+               cli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII);
+               cli_req_append_ascii4(req, parms->tcon.in.dev,     STR_ASCII);
+               break;
+
+       case RAW_TCON_TCONX:
+               SETUP_REQUEST_TREE(SMBtconX, 4, 0);
+               SSVAL(req->out.vwv, VWV(0), 0xFF);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags);
+               SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length);
+               cli_req_append_blob(req, &parms->tconx.in.password);
+               cli_req_append_string(req, parms->tconx.in.path,   STR_TERMINATE | STR_UPPER);
+               cli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII);
+               break;
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Send a tconX (async recv)
+****************************************************************************/
+NTSTATUS smb_tree_connect_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
+{
+       char *p;
+
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               goto failed;
+       }
+
+       switch (parms->tcon.level) {
+       case RAW_TCON_TCON:
+               CLI_CHECK_WCT(req, 2);
+               parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0));
+               parms->tcon.out.cnum = SVAL(req->in.vwv, VWV(1));
+               break;
+
+       case RAW_TCON_TCONX:
+               ZERO_STRUCT(parms->tconx.out);
+               CLI_CHECK_MIN_WCT(req, 0);  /* this depends on the protocol level */
+               parms->tconx.out.cnum = SVAL(req->in.hdr, HDR_TID);
+               if (req->in.wct >= 4) {
+                       parms->tconx.out.options = SVAL(req->in.vwv, VWV(3));
+               }
+
+               /* output is actual service name */
+               p = req->in.data;
+               if (!p) break;
+
+               p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.dev_type, 
+                                        p, -1, STR_ASCII | STR_TERMINATE);
+               p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.fs_type, 
+                                        p, -1, STR_TERMINATE);
+               break;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a tconX (sync interface)
+****************************************************************************/
+NTSTATUS smb_tree_connect(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms)
+{
+       struct cli_request *req = smb_tree_connect_send(tree, parms);
+       return smb_tree_connect_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+NTSTATUS smb_tree_disconnect(struct cli_tree *tree)
+{
+       struct cli_request *req;
+
+       if (!tree) return NT_STATUS_OK;
+       req = cli_request_setup(tree, SMBtdis, 0, 0);
+
+       if (cli_request_send(req)) {
+               cli_request_receive(req);
+       }
+       return cli_request_destroy(req);
+}
+
+
+/*
+  a convenient function to establish a cli_tree from scratch, using reasonable default
+  parameters
+*/
+NTSTATUS cli_tree_full_connection(struct cli_tree **ret_tree, 
+                                 const char *my_name, 
+                                 const char *dest_host, int port,
+                                 const char *service, const char *service_type,
+                                 const char *user, const char *domain, 
+                                 const char *password)
+{
+       struct cli_socket *sock;
+       struct cli_transport *transport;
+       struct cli_session *session;
+       struct cli_tree *tree;
+       NTSTATUS status;
+       struct nmb_name calling;
+       struct nmb_name called;
+       union smb_sesssetup setup;
+       union smb_tcon tcon;
+       TALLOC_CTX *mem_ctx;
+
+       *ret_tree = NULL;
+
+       sock = cli_sock_init();
+       if (!sock) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* open a TCP socket to the server */
+       if (!cli_sock_connect_byname(sock, dest_host, port)) {
+               DEBUG(2,("Failed to establish socket connection - %s\n", strerror(errno)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       transport = cli_transport_init(sock);
+       if (!transport) {
+               cli_sock_close(sock);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* send a NBT session request, if applicable */
+       make_nmb_name(&calling, my_name, 0x0);
+       make_nmb_name(&called,  dest_host, 0x20);
+
+       if (!cli_transport_connect(transport, &calling, &called)) {
+               cli_transport_close(transport);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+
+       /* negotiate protocol options with the server */
+       status = smb_raw_negotiate(transport);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_transport_close(transport);
+               return status;
+       }
+
+       session = cli_session_init(transport);
+       if (!session) {
+               cli_transport_close(transport);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* prepare a session setup to establish a security context */
+       setup.generic.level = RAW_SESSSETUP_GENERIC;
+       setup.generic.in.sesskey = transport->negotiate.sesskey;
+       setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 | 
+               CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | 
+               CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX;
+       setup.generic.in.password = password;
+       setup.generic.in.user = user;
+       setup.generic.in.domain = domain;
+
+       mem_ctx = talloc_init("tcon");
+       if (!mem_ctx) {
+               cli_tree_close(tree);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = smb_raw_session_setup(session, mem_ctx, &setup);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_session_close(session);
+               talloc_destroy(mem_ctx);
+               return status;
+       }
+
+       session->vuid = setup.generic.out.vuid;
+
+       tree = cli_tree_init(session);
+       if (!tree) {
+               cli_session_close(session);
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* connect to a share using a tree connect */
+       tcon.generic.level = RAW_TCON_TCONX;
+       tcon.tconx.in.flags = 0;
+       tcon.tconx.in.password = data_blob(NULL, 0);
+       tcon.tconx.in.path = service;
+       tcon.tconx.in.device = service_type;
+       
+       status = smb_tree_connect(tree, mem_ctx, &tcon);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_tree_close(tree);
+               talloc_destroy(mem_ctx);
+               return status;
+       }
+
+       tree->tid = tcon.tconx.out.cnum;
+       tree->device = talloc_strdup(tree->mem_ctx, tcon.tconx.out.dev_type);
+       tree->fs_type = talloc_strdup(tree->mem_ctx, tcon.tconx.out.fs_type);
+
+       talloc_destroy(mem_ctx);
+
+       *ret_tree = tree;
+       return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c
new file mode 100644 (file)
index 0000000..ce0368c
--- /dev/null
@@ -0,0 +1,147 @@
+/* 
+   Unix SMB/CIFS implementation.
+   parsing of EA (extended attribute) lists
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/*
+  work out how many bytes on the wire a ea list will consume. 
+  This assumes the names are strict ascii, which should be a
+  reasonable assumption
+*/
+uint_t ea_list_size(uint_t num_eas, struct ea_struct *eas)
+{
+       uint_t total = 4;
+       int i;
+       for (i=0;i<num_eas;i++) {
+               total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+       }
+       return total;
+}
+
+/*
+  put a ea_list into a pre-allocated buffer - buffer must be at least
+  of size ea_list_size()
+*/
+void ea_put_list(char *data, uint_t num_eas, struct ea_struct *eas)
+{
+       int i;
+       uint32 ea_size;
+
+       ea_size = ea_list_size(num_eas, eas);
+
+       SIVAL(data, 0, ea_size);
+       data += 4;
+
+       for (i=0;i<num_eas;i++) {
+               uint_t nlen = strlen(eas[i].name.s);
+               SCVAL(data, 0, eas[i].flags);
+               SCVAL(data, 1, nlen);
+               SSVAL(data, 2, eas[i].value.length);
+               memcpy(data+4, eas[i].name.s, nlen+1);
+               memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length);
+               data += 4+nlen+1+eas[i].value.length;
+       }
+}
+
+
+/*
+  pull a ea_struct from a buffer. Return the number of bytes consumed
+*/
+uint_t ea_pull_struct(const DATA_BLOB *blob, 
+                     TALLOC_CTX *mem_ctx,
+                     struct ea_struct *ea)
+{
+       uint8 nlen;
+       uint16 vlen;
+
+       if (blob->length < 6) {
+               return 0;
+       }
+
+       ea->flags = CVAL(blob->data, 0);
+       nlen = CVAL(blob->data, 1);
+       vlen = SVAL(blob->data, 2);
+
+       if (nlen+1+vlen > blob->length-4) {
+               return 0;
+       }
+
+       ea->name.s = talloc_strndup(mem_ctx, blob->data+4, nlen);
+       ea->name.private_length = nlen;
+       ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1);
+       if (!ea->value.data) return 0;
+       if (vlen) {
+               memcpy(ea->value.data, blob->data+4+nlen+1, vlen);
+       }
+       ea->value.data[vlen] = 0;
+       ea->value.length--;
+
+       return 4 + nlen+1 + vlen;
+}
+
+
+/*
+  pull a ea_list from a buffer
+*/
+NTSTATUS ea_pull_list(const DATA_BLOB *blob, 
+                     TALLOC_CTX *mem_ctx,
+                     uint_t *num_eas, struct ea_struct **eas)
+{
+       int n;
+       uint32 ea_size, ofs;
+
+       if (blob->length < 4) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
+
+       ea_size = IVAL(blob->data, 0);
+       if (ea_size > blob->length) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       ofs = 4;        
+       n = 0;
+       *num_eas = 0;
+       *eas = NULL;
+
+       while (ofs < ea_size) {
+               uint_t len;
+               DATA_BLOB blob2;
+
+               blob2.data = blob->data + ofs;
+               blob2.length = ea_size - ofs;
+
+               *eas = talloc_realloc(mem_ctx, *eas, sizeof(**eas) * (n+1));
+               if (! *eas) return NT_STATUS_NO_MEMORY;
+
+               len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
+               if (len == 0) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               ofs += len;
+               n++;
+       }
+
+       *num_eas = n;
+
+       return NT_STATUS_OK;
+}
+
diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c
new file mode 100644 (file)
index 0000000..279dfcf
--- /dev/null
@@ -0,0 +1,687 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client file operations
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Jeremy Allison 2001-2002
+   Copyright (C) James Myers 2003
+   
+   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"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+       req = cli_request_setup(tree, cmd, wct, buflen); \
+       if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Rename a file - async interface
+****************************************************************************/
+struct cli_request *smb_raw_rename_send(struct cli_tree *tree,
+                                       struct smb_rename *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBmv, 1, 0);
+       
+       SSVAL(req->out.vwv, VWV(0), parms->in.attrib);
+       
+       cli_req_append_ascii4(req, parms->in.pattern1, STR_TERMINATE);
+       cli_req_append_ascii4(req, parms->in.pattern2, STR_TERMINATE);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Rename a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_rename(struct cli_tree *tree,
+                       struct smb_rename *parms)
+{
+       struct cli_request *req = smb_raw_rename_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Delete a file - async interface
+****************************************************************************/
+struct cli_request *smb_raw_unlink_send(struct cli_tree *tree,
+                                       struct smb_unlink *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBunlink, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), parms->in.attrib);
+       cli_req_append_ascii4(req, parms->in.pattern, STR_TERMINATE);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+       return req;
+}
+
+/*
+  delete a file - sync interface
+*/
+NTSTATUS smb_raw_unlink(struct cli_tree *tree,
+                       struct smb_unlink *parms)
+{
+       struct cli_request *req = smb_raw_unlink_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ create a directory  using TRANSACT2_MKDIR - async interface
+****************************************************************************/
+static struct cli_request *smb_raw_t2mkdir_send(struct cli_tree *tree, 
+                                               union smb_mkdir *parms)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_MKDIR;
+       TALLOC_CTX *mem_ctx;
+       struct cli_request *req;
+       uint16 data_total;
+
+       mem_ctx = talloc_init("t2mkdir");
+
+       data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+       t2.in.max_param = 0;
+       t2.in.max_data = 0;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+       t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total);
+
+       SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */
+
+       cli_blob_append_string(tree->session, mem_ctx, 
+                              &t2.in.params, parms->t2mkdir.in.path, 0);
+
+       ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+       req = smb_raw_trans2_send(tree, &t2);
+
+       talloc_destroy(mem_ctx);
+
+       return req;
+}
+
+/****************************************************************************
+ Create a directory - async interface
+****************************************************************************/
+struct cli_request *smb_raw_mkdir_send(struct cli_tree *tree,
+                                      union smb_mkdir *parms)
+{
+       struct cli_request *req; 
+
+       if (parms->generic.level == RAW_MKDIR_T2MKDIR) {
+               return smb_raw_t2mkdir_send(tree, parms);
+       }
+
+       if (parms->generic.level != RAW_MKDIR_MKDIR) {
+               return NULL;
+       }
+
+       SETUP_REQUEST(SMBmkdir, 0, 0);
+       
+       cli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE);
+
+       if (!cli_request_send(req)) {
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Create a directory - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_mkdir(struct cli_tree *tree,
+                      union smb_mkdir *parms)
+{
+       struct cli_request *req = smb_raw_mkdir_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ Remove a directory - async interface
+****************************************************************************/
+struct cli_request *smb_raw_rmdir_send(struct cli_tree *tree,
+                                      struct smb_rmdir *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBrmdir, 0, 0);
+       
+       cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Remove a directory - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_rmdir(struct cli_tree *tree,
+                      struct smb_rmdir *parms)
+{
+       struct cli_request *req = smb_raw_rmdir_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async send 
+****************************************************************************/
+static struct cli_request *smb_raw_t2open_send(struct cli_tree *tree, 
+                                              union smb_open *parms)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_OPEN;
+       TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open");
+       struct cli_request *req;
+       uint16 list_size;
+
+       list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+       t2.in.max_param = 30;
+       t2.in.max_data = 0;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob_talloc(mem_ctx, NULL, 28);
+       t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size);
+
+       SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags);
+       SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode);
+       SSVAL(t2.in.params.data, VWV(2), 0); /* reserved */
+       SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs);
+       put_dos_date(t2.in.params.data, VWV(4), parms->t2open.in.write_time);
+       SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func);
+       SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size);
+       SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout);
+       SIVAL(t2.in.params.data, VWV(11), 0);
+       SSVAL(t2.in.params.data, VWV(13), 0);
+
+       cli_blob_append_string(tree->session, mem_ctx, 
+                              &t2.in.params, parms->t2open.in.fname, 
+                              STR_TERMINATE);
+
+       ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+       req = smb_raw_trans2_send(tree, &t2);
+
+       talloc_destroy(mem_ctx);
+
+       return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async recv
+****************************************************************************/
+static NTSTATUS smb_raw_t2open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+       struct smb_trans2 t2;
+       NTSTATUS status;
+
+       status = smb_raw_trans2_recv(req, mem_ctx, &t2);
+       if (!NT_STATUS_IS_OK(status)) return status;
+
+       if (t2.out.params.length < 30) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
+
+       parms->t2open.out.fnum =        SVAL(t2.out.params.data, VWV(0));
+       parms->t2open.out.attrib =      SVAL(t2.out.params.data, VWV(1));
+       parms->t2open.out.write_time = make_unix_date3(t2.out.params.data + VWV(2));
+       parms->t2open.out.size =        IVAL(t2.out.params.data, VWV(4));
+       parms->t2open.out.access =      SVAL(t2.out.params.data, VWV(6));
+       parms->t2open.out.ftype =       SVAL(t2.out.params.data, VWV(7));
+       parms->t2open.out.devstate =    SVAL(t2.out.params.data, VWV(8));
+       parms->t2open.out.action =      SVAL(t2.out.params.data, VWV(9));
+       parms->t2open.out.unknown =     SVAL(t2.out.params.data, VWV(10));
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Open a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_open_send(struct cli_tree *tree, union smb_open *parms)
+{
+       int len;
+       struct cli_request *req = NULL; 
+
+       switch (parms->open.level) {
+       case RAW_OPEN_T2OPEN:
+               return smb_raw_t2open_send(tree, parms);
+
+       case RAW_OPEN_OPEN:
+               SETUP_REQUEST(SMBopen, 2, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->open.in.flags);
+               SSVAL(req->out.vwv, VWV(1), parms->open.in.search_attrs);
+               cli_req_append_ascii4(req, parms->open.in.fname, STR_TERMINATE);
+               break;
+               
+       case RAW_OPEN_OPENX:
+               SETUP_REQUEST(SMBopenX, 15, 0);
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->openx.in.flags);
+               SSVAL(req->out.vwv, VWV(3), parms->openx.in.open_mode);
+               SSVAL(req->out.vwv, VWV(4), parms->openx.in.search_attrs);
+               SSVAL(req->out.vwv, VWV(5), parms->openx.in.file_attrs);
+               put_dos_date3(req->out.vwv, VWV(6), parms->openx.in.write_time);
+               SSVAL(req->out.vwv, VWV(8), parms->openx.in.open_func);
+               SIVAL(req->out.vwv, VWV(9), parms->openx.in.size);
+               SIVAL(req->out.vwv, VWV(11),parms->openx.in.timeout);
+               SIVAL(req->out.vwv, VWV(13),0); /* reserved */
+               cli_req_append_string(req, parms->openx.in.fname, STR_TERMINATE);
+               break;
+               
+       case RAW_OPEN_MKNEW:
+               SETUP_REQUEST(SMBmknew, 3, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->mknew.in.attrib);
+               put_dos_date3(req->out.vwv, VWV(1), parms->mknew.in.write_time);
+               cli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE);
+               break;
+               
+       case RAW_OPEN_CTEMP:
+               SETUP_REQUEST(SMBctemp, 3, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib);
+               put_dos_date3(req->out.vwv, VWV(1), parms->ctemp.in.write_time);
+               cli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE);
+               break;
+               
+       case RAW_OPEN_SPLOPEN:
+               SETUP_REQUEST(SMBsplopen, 2, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length);
+               SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode);
+               break;
+               
+       case RAW_OPEN_NTCREATEX:
+               SETUP_REQUEST(SMBntcreateX, 24, 0);
+               SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1),0);
+               SCVAL(req->out.vwv, VWV(2),0); /* padding */
+               SIVAL(req->out.vwv,  7, parms->ntcreatex.in.flags);
+               SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid);
+               SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask);
+               SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size);
+               SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr);
+               SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access);
+               SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition);
+               SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options);
+               SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation);
+               SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags);
+               
+               cli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len);
+               SSVAL(req->out.vwv, 5, len);
+               break;
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Open a file - async recv
+****************************************************************************/
+NTSTATUS smb_raw_open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               goto failed;
+       }
+
+       switch (parms->open.level) {
+       case RAW_OPEN_T2OPEN:
+               return smb_raw_t2open_recv(req, mem_ctx, parms);
+
+       case RAW_OPEN_OPEN:
+               CLI_CHECK_WCT(req, 7);
+               parms->open.out.fnum = SVAL(req->in.vwv, VWV(0));
+               parms->open.out.attrib = SVAL(req->in.vwv, VWV(1));
+               parms->open.out.write_time = make_unix_date3(req->in.vwv + VWV(2));
+               parms->open.out.size = IVAL(req->in.vwv, VWV(4));
+               parms->open.out.rmode = SVAL(req->in.vwv, VWV(6));
+               break;
+
+       case RAW_OPEN_OPENX:
+               CLI_CHECK_MIN_WCT(req, 15);
+               parms->openx.out.fnum = SVAL(req->in.vwv, VWV(2));
+               parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3));
+               parms->openx.out.write_time = make_unix_date3(req->in.vwv + VWV(4));
+               parms->openx.out.size = IVAL(req->in.vwv, VWV(6));
+               parms->openx.out.access = SVAL(req->in.vwv, VWV(8));
+               parms->openx.out.ftype = SVAL(req->in.vwv, VWV(9));
+               parms->openx.out.devstate = SVAL(req->in.vwv, VWV(10));
+               parms->openx.out.action = SVAL(req->in.vwv, VWV(11));
+               parms->openx.out.unique_fid = IVAL(req->in.vwv, VWV(12));
+               if (req->in.wct >= 19) {
+                       parms->openx.out.access_mask = IVAL(req->in.vwv, VWV(15));
+                       parms->openx.out.unknown =     IVAL(req->in.vwv, VWV(17));
+               } else {
+                       parms->openx.out.access_mask = 0;
+                       parms->openx.out.unknown = 0;
+               }
+               break;
+
+       case RAW_OPEN_MKNEW:
+               CLI_CHECK_WCT(req, 1);
+               parms->mknew.out.fnum = SVAL(req->in.vwv, VWV(0));
+               break;
+
+       case RAW_OPEN_CTEMP:
+               CLI_CHECK_WCT(req, 1);
+               parms->ctemp.out.fnum = SVAL(req->in.vwv, VWV(0));
+               cli_req_pull_string(req, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII);
+               break;
+
+       case RAW_OPEN_SPLOPEN:
+               CLI_CHECK_WCT(req, 1);
+               parms->splopen.out.fnum = SVAL(req->in.vwv, VWV(0));
+               break;
+
+       case RAW_OPEN_NTCREATEX:
+               CLI_CHECK_MIN_WCT(req, 34);
+               parms->ntcreatex.out.oplock_level =              CVAL(req->in.vwv, 4);
+               parms->ntcreatex.out.fnum =                      SVAL(req->in.vwv, 5);
+               parms->ntcreatex.out.create_action =             IVAL(req->in.vwv, 7);
+               parms->ntcreatex.out.create_time =   cli_pull_nttime(req->in.vwv, 11);
+               parms->ntcreatex.out.access_time =   cli_pull_nttime(req->in.vwv, 19);
+               parms->ntcreatex.out.write_time =    cli_pull_nttime(req->in.vwv, 27);
+               parms->ntcreatex.out.change_time =   cli_pull_nttime(req->in.vwv, 35);
+               parms->ntcreatex.out.attrib =                   IVAL(req->in.vwv, 43);
+               parms->ntcreatex.out.alloc_size =               BVAL(req->in.vwv, 47);
+               parms->ntcreatex.out.size =                     BVAL(req->in.vwv, 55);
+               parms->ntcreatex.out.file_type =                SVAL(req->in.vwv, 63);
+               parms->ntcreatex.out.ipc_state =                SVAL(req->in.vwv, 65);
+               parms->ntcreatex.out.is_directory =             CVAL(req->in.vwv, 67);
+               break;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Open a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_open(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+       struct cli_request *req = smb_raw_open_send(tree, parms);
+       return smb_raw_open_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Close a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_close_send(struct cli_tree *tree, union smb_close *parms)
+{
+       struct cli_request *req; 
+
+       switch (parms->generic.level) {
+       case RAW_CLOSE_GENERIC:
+               return NULL;
+
+       case RAW_CLOSE_CLOSE:
+               SETUP_REQUEST(SMBclose, 3, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->close.in.fnum);
+               put_dos_date3(req->out.vwv, VWV(1), parms->close.in.write_time);
+               break;
+
+       case RAW_CLOSE_SPLCLOSE:
+               SETUP_REQUEST(SMBsplclose, 3, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->splclose.in.fnum);
+               SIVAL(req->out.vwv, VWV(1), 0); /* reserved */
+               break;
+       }
+
+       if (!req) return NULL;
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+/****************************************************************************
+ Close a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_close(struct cli_tree *tree, union smb_close *parms)
+{
+       struct cli_request *req = smb_raw_close_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Locking calls - async interface
+****************************************************************************/
+struct cli_request *smb_raw_lock_send(struct cli_tree *tree, union smb_lock *parms)
+{
+       struct cli_request *req; 
+
+       switch (parms->generic.level) {
+       case RAW_LOCK_GENERIC:
+               return NULL;
+
+       case RAW_LOCK_LOCK:
+               SETUP_REQUEST(SMBlock, 5, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->lock.in.fnum);
+               SIVAL(req->out.vwv, VWV(1), parms->lock.in.count);
+               SIVAL(req->out.vwv, VWV(3), parms->lock.in.offset);
+               break;
+               
+       case RAW_LOCK_UNLOCK:
+               SETUP_REQUEST(SMBunlock, 5, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->unlock.in.fnum);
+               SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count);
+               SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset);
+               break;
+               
+       case RAW_LOCK_LOCKX: {
+               struct smb_lock_entry *lockp;
+               uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10;
+               uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt;
+               int i;
+
+               SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count);
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->lockx.in.fnum);
+               SSVAL(req->out.vwv, VWV(3), parms->lockx.in.mode);
+               SIVAL(req->out.vwv, VWV(4), parms->lockx.in.timeout);
+               SSVAL(req->out.vwv, VWV(6), parms->lockx.in.ulock_cnt);
+               SSVAL(req->out.vwv, VWV(7), parms->lockx.in.lock_cnt);
+               
+               /* copy in all the locks */
+               lockp = &parms->lockx.in.locks[0];
+               for (i = 0; i < lock_count; i++) {
+                       char *p = req->out.data + lck_size * i;
+                       SSVAL(p, 0, lockp[i].pid);
+                       if (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+                               SSVAL(p,  2, 0); /* reserved */
+                               SIVAL(p,  4, lockp[i].offset>>32);
+                               SIVAL(p,  8, lockp[i].offset);
+                               SIVAL(p, 12, lockp[i].count>>32);
+                               SIVAL(p, 16, lockp[i].count);
+                       } else {
+                               SIVAL(p, 2, lockp[i].offset);
+                               SIVAL(p, 6, lockp[i].count);
+                       }
+               }       
+       }
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Locking calls - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_lock(struct cli_tree *tree, union smb_lock *parms)
+{
+       struct cli_request *req = smb_raw_lock_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+       
+
+/****************************************************************************
+ Check for existence of a dir - async send
+****************************************************************************/
+struct cli_request *smb_raw_chkpath_send(struct cli_tree *tree, struct smb_chkpath *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBchkpth, 0, 0);
+
+       cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Check for existence of a dir - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_chkpath(struct cli_tree *tree, struct smb_chkpath *parms)
+{
+       struct cli_request *req = smb_raw_chkpath_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+
+
+/****************************************************************************
+ flush a file - async send
+ a flush to fnum 0xFFFF will flush all files
+****************************************************************************/
+struct cli_request *smb_raw_flush_send(struct cli_tree *tree, struct smb_flush *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBflush, 1, 0);
+       SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+/****************************************************************************
+ flush a file - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_flush(struct cli_tree *tree, struct smb_flush *parms)
+{
+       struct cli_request *req = smb_raw_flush_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ seek a file - async send
+****************************************************************************/
+struct cli_request *smb_raw_seek_send(struct cli_tree *tree,
+                                     struct smb_seek *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBlseek, 4, 0);
+
+       SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+       SSVAL(req->out.vwv, VWV(1), parms->in.mode);
+       SIVALS(req->out.vwv, VWV(2), parms->in.offset);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+       return req;
+}
+
+/****************************************************************************
+ seek a file - async receive
+****************************************************************************/
+NTSTATUS smb_raw_seek_recv(struct cli_request *req,
+                                     struct smb_seek *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+
+       CLI_CHECK_WCT(req, 2);  
+       parms->out.offset = IVAL(req->in.vwv, VWV(0));
+
+failed:        
+       return cli_request_destroy(req);
+}
+
+/*
+  seek a file - sync interface
+*/
+NTSTATUS smb_raw_seek(struct cli_tree *tree,
+                     struct smb_seek *parms)
+{
+       struct cli_request *req = smb_raw_seek_send(tree, parms);
+       return smb_raw_seek_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c
new file mode 100644 (file)
index 0000000..f685cef
--- /dev/null
@@ -0,0 +1,527 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client trans2 operations
+   Copyright (C) James Myers 2003
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/* local macros to make the code more readable */
+#define FINFO_CHECK_MIN_SIZE(size) if (blob->length < (size)) { \
+      DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected min of %d\n", \
+              blob->length, parms->generic.level, (size))); \
+      return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+#define FINFO_CHECK_SIZE(size) if (blob->length != (size)) { \
+      DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected %d\n", \
+              blob->length, parms->generic.level, (size))); \
+      return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static NTSTATUS smb_raw_info_backend(struct cli_session *session,
+                                    TALLOC_CTX *mem_ctx,
+                                    union smb_fileinfo *parms, 
+                                    DATA_BLOB *blob)
+{      
+       uint_t len, ofs;
+
+       switch (parms->generic.level) {
+       case RAW_FILEINFO_GENERIC:
+       case RAW_FILEINFO_GETATTR:
+       case RAW_FILEINFO_GETATTRE:
+               /* not handled here */
+               return NT_STATUS_INVALID_LEVEL;
+
+       case RAW_FILEINFO_STANDARD:
+               FINFO_CHECK_SIZE(22);
+               parms->standard.out.create_time = make_unix_date2(blob->data +  0);
+               parms->standard.out.access_time = make_unix_date2(blob->data +  4);
+               parms->standard.out.write_time =  make_unix_date2(blob->data +  8);
+               parms->standard.out.size =        IVAL(blob->data,             12);
+               parms->standard.out.alloc_size =  IVAL(blob->data,             16);
+               parms->standard.out.attrib =      SVAL(blob->data,             20);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_SIZE:
+               FINFO_CHECK_SIZE(26);
+               parms->ea_size.out.create_time = make_unix_date2(blob->data +  0);
+               parms->ea_size.out.access_time = make_unix_date2(blob->data +  4);
+               parms->ea_size.out.write_time =  make_unix_date2(blob->data +  8);
+               parms->ea_size.out.size =        IVAL(blob->data,             12);
+               parms->ea_size.out.alloc_size =  IVAL(blob->data,             16);
+               parms->ea_size.out.attrib =      SVAL(blob->data,             20);
+               parms->ea_size.out.ea_size =     IVAL(blob->data,             22);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALL_EAS:
+               FINFO_CHECK_MIN_SIZE(4);
+               return ea_pull_list(blob, mem_ctx, 
+                                   &parms->all_eas.out.num_eas,
+                                   &parms->all_eas.out.eas);
+
+       case RAW_FILEINFO_IS_NAME_VALID:
+               /* no data! */
+               FINFO_CHECK_SIZE(0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_BASIC_INFO:
+       case RAW_FILEINFO_BASIC_INFORMATION:
+               /* some servers return 40 bytes and some 36. w2k3 return 40, so thats
+                  what we should do, but we need to accept 36 */
+               if (blob->length != 36) {
+                       FINFO_CHECK_SIZE(40);
+               }
+               parms->basic_info.out.create_time = cli_pull_nttime(blob->data, 0);
+               parms->basic_info.out.access_time = cli_pull_nttime(blob->data, 8);
+               parms->basic_info.out.write_time =  cli_pull_nttime(blob->data, 16);
+               parms->basic_info.out.change_time = cli_pull_nttime(blob->data, 24);
+               parms->basic_info.out.attrib =                 IVAL(blob->data, 32);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STANDARD_INFO:
+       case RAW_FILEINFO_STANDARD_INFORMATION:
+               FINFO_CHECK_SIZE(24);
+               parms->standard_info.out.alloc_size =     BVAL(blob->data, 0);
+               parms->standard_info.out.size =           BVAL(blob->data, 8);
+               parms->standard_info.out.nlink =          IVAL(blob->data, 16);
+               parms->standard_info.out.delete_pending = CVAL(blob->data, 20);
+               parms->standard_info.out.directory =      CVAL(blob->data, 21);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_INFO:
+       case RAW_FILEINFO_EA_INFORMATION:
+               FINFO_CHECK_SIZE(4);
+               parms->ea_info.out.ea_size = IVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_NAME_INFO:
+       case RAW_FILEINFO_NAME_INFORMATION:
+               FINFO_CHECK_MIN_SIZE(4);
+               cli_blob_pull_string(session, mem_ctx, blob, 
+                                    &parms->name_info.out.fname, 0, 4, STR_UNICODE);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALL_INFO:
+       case RAW_FILEINFO_ALL_INFORMATION:
+               FINFO_CHECK_MIN_SIZE(72);
+               parms->all_info.out.create_time =           cli_pull_nttime(blob->data, 0);
+               parms->all_info.out.access_time =           cli_pull_nttime(blob->data, 8);
+               parms->all_info.out.write_time =            cli_pull_nttime(blob->data, 16);
+               parms->all_info.out.change_time =           cli_pull_nttime(blob->data, 24);
+               parms->all_info.out.attrib =                IVAL(blob->data, 32);
+               parms->all_info.out.alloc_size =            BVAL(blob->data, 40);
+               parms->all_info.out.size =                  BVAL(blob->data, 48);
+               parms->all_info.out.nlink =                 IVAL(blob->data, 56);
+               parms->all_info.out.delete_pending =        CVAL(blob->data, 60);
+               parms->all_info.out.directory =             CVAL(blob->data, 61);
+               parms->all_info.out.ea_size =               IVAL(blob->data, 64);
+               cli_blob_pull_string(session, mem_ctx, blob,
+                                    &parms->all_info.out.fname, 68, 72, STR_UNICODE);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALT_NAME_INFO:
+       case RAW_FILEINFO_ALT_NAME_INFORMATION:
+               FINFO_CHECK_MIN_SIZE(4);
+               cli_blob_pull_string(session, mem_ctx, blob, 
+                                    &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STREAM_INFO:
+       case RAW_FILEINFO_STREAM_INFORMATION:
+               FINFO_CHECK_MIN_SIZE(0);
+               ofs = 0;
+               parms->stream_info.out.num_streams = 0;
+               parms->stream_info.out.streams = NULL;
+
+               while (blob->length - ofs >= 24) {
+                       uint_t n = parms->stream_info.out.num_streams;
+                       parms->stream_info.out.streams = 
+                               talloc_realloc(mem_ctx,parms->stream_info.out.streams,
+                                              (n+1) * sizeof(parms->stream_info.out.streams[0]));
+                       if (!parms->stream_info.out.streams) {
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       parms->stream_info.out.streams[n].size =       BVAL(blob->data, ofs +  8);
+                       parms->stream_info.out.streams[n].alloc_size = BVAL(blob->data, ofs + 16);
+                       cli_blob_pull_string(session, mem_ctx, blob, 
+                                            &parms->stream_info.out.streams[n].stream_name, 
+                                            ofs+4, ofs+24, STR_UNICODE);
+                       parms->stream_info.out.num_streams++;
+                       len = IVAL(blob->data, ofs);
+                       if (len > blob->length - ofs) return NT_STATUS_INFO_LENGTH_MISMATCH;
+                       if (len == 0) break;
+                       ofs += len;
+               }
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_INTERNAL_INFORMATION:
+               FINFO_CHECK_SIZE(8);
+               parms->internal_information.out.device = IVAL(blob->data, 0);
+               parms->internal_information.out.inode =  IVAL(blob->data, 4);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ACCESS_INFORMATION:
+               FINFO_CHECK_SIZE(4);
+               parms->access_information.out.access_flags = IVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_POSITION_INFORMATION:
+               FINFO_CHECK_SIZE(8);
+               parms->position_information.out.position = BVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_MODE_INFORMATION:
+               FINFO_CHECK_SIZE(4);
+               parms->mode_information.out.mode = IVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+               FINFO_CHECK_SIZE(4);
+               parms->alignment_information.out.alignment_requirement 
+                       = IVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_COMPRESSION_INFO:
+       case RAW_FILEINFO_COMPRESSION_INFORMATION:
+               FINFO_CHECK_SIZE(16);
+               parms->compression_info.out.compressed_size = BVAL(blob->data,  0);
+               parms->compression_info.out.format          = SVAL(blob->data,  8);
+               parms->compression_info.out.unit_shift      = CVAL(blob->data, 10);
+               parms->compression_info.out.chunk_shift     = CVAL(blob->data, 11);
+               parms->compression_info.out.cluster_shift   = CVAL(blob->data, 12);
+               /* 3 bytes of padding */
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_UNIX_BASIC:
+               FINFO_CHECK_SIZE(100);
+               parms->unix_basic_info.out.end_of_file        =            BVAL(blob->data,  0);
+               parms->unix_basic_info.out.num_bytes          =            BVAL(blob->data,  8);
+               parms->unix_basic_info.out.status_change_time = cli_pull_nttime(blob->data, 16);
+               parms->unix_basic_info.out.access_time        = cli_pull_nttime(blob->data, 24);
+               parms->unix_basic_info.out.change_time        = cli_pull_nttime(blob->data, 32);
+               parms->unix_basic_info.out.uid                =            BVAL(blob->data, 40);
+               parms->unix_basic_info.out.gid                =            BVAL(blob->data, 48);
+               parms->unix_basic_info.out.file_type          =            IVAL(blob->data, 52);
+               parms->unix_basic_info.out.dev_major          =            BVAL(blob->data, 60);
+               parms->unix_basic_info.out.dev_minor          =            BVAL(blob->data, 68);
+               parms->unix_basic_info.out.unique_id          =            BVAL(blob->data, 76);
+               parms->unix_basic_info.out.permissions        =            BVAL(blob->data, 84);
+               parms->unix_basic_info.out.nlink              =            BVAL(blob->data, 92);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_UNIX_LINK:
+               FINFO_CHECK_MIN_SIZE(0);
+               cli_blob_pull_string(session, mem_ctx, blob, 
+                                    &parms->unix_link_info.out.link_dest, 0, 4, STR_UNICODE);
+               return NT_STATUS_OK;
+               
+       case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:             
+               FINFO_CHECK_SIZE(56);
+               parms->network_open_information.out.create_time = cli_pull_nttime(blob->data,  0);
+               parms->network_open_information.out.access_time = cli_pull_nttime(blob->data,  8);
+               parms->network_open_information.out.write_time =  cli_pull_nttime(blob->data, 16);
+               parms->network_open_information.out.change_time = cli_pull_nttime(blob->data, 24);
+               parms->network_open_information.out.alloc_size =             BVAL(blob->data, 32);
+               parms->network_open_information.out.size =                   BVAL(blob->data, 40);
+               parms->network_open_information.out.attrib =                 IVAL(blob->data, 48);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+               FINFO_CHECK_SIZE(8);
+               parms->attribute_tag_information.out.attrib =      IVAL(blob->data, 0);
+               parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4);
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_fileinfo_blob_send(struct cli_tree *tree,
+                                                     uint16 fnum, uint16 info_level)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_QFILEINFO;
+       struct cli_request *req;
+       TALLOC_CTX *mem_ctx = talloc_init("raw_fileinfo");
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.data = data_blob(NULL, 0);
+       tp.in.max_param = 2;
+       tp.in.max_data = 0xFFFF;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+       if (!tp.in.params.data) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       SIVAL(tp.in.params.data, 0, fnum);
+       SSVAL(tp.in.params.data, 2, info_level);
+
+       req = smb_raw_trans2_send(tree, &tp);
+
+       talloc_destroy(mem_ctx);
+
+       return req;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_fileinfo_blob_recv(struct cli_request *req,
+                                          TALLOC_CTX *mem_ctx,
+                                          DATA_BLOB *blob)
+{
+       struct smb_trans2 tp;
+       NTSTATUS status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+       if (NT_STATUS_IS_OK(status)) {
+               *blob = tp.out.data;
+       }
+       return status;
+}
+
+/****************************************************************************
+ Very raw query path info - returns param/data blobs (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_pathinfo_blob_send(struct cli_tree *tree,
+                                                     const char *fname,
+                                                     uint16 info_level)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_QPATHINFO;
+       struct cli_request *req;
+       TALLOC_CTX *mem_ctx = talloc_init("raw_pathinfo");
+
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.data = data_blob(NULL, 0);
+       tp.in.max_param = 2;
+       tp.in.max_data = 0xFFFF;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+       if (!tp.in.params.data) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       SSVAL(tp.in.params.data, 0, info_level);
+       SIVAL(tp.in.params.data, 2, 0);
+       cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+                              fname, STR_TERMINATE);
+       
+       req = smb_raw_trans2_send(tree, &tp);
+
+       talloc_destroy(mem_ctx);
+
+       return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_getattr_send(struct cli_tree *tree,
+                                               union smb_fileinfo *parms)
+{
+       struct cli_request *req;
+       
+       req = cli_request_setup(tree, SMBgetatr, 0, 0);
+       if (!req) return NULL;
+
+       cli_req_append_ascii4(req, parms->getattr.in.fname, STR_TERMINATE);
+       
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_getattr_recv(struct cli_request *req,
+                                    union smb_fileinfo *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+
+       CLI_CHECK_WCT(req, 10);
+       parms->getattr.out.attrib =     SVAL(req->in.vwv, VWV(0));
+       parms->getattr.out.write_time = make_unix_date3(req->in.vwv + VWV(1));
+       parms->getattr.out.size =       IVAL(req->in.vwv, VWV(3));
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_getattrE_send(struct cli_tree *tree,
+                                                union smb_fileinfo *parms)
+{
+       struct cli_request *req;
+       
+       req = cli_request_setup(tree, SMBgetattrE, 1, 0);
+       if (!req) return NULL;
+       
+       SSVAL(req->out.vwv, VWV(0), parms->getattre.in.fnum);
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static NTSTATUS smb_raw_getattrE_recv(struct cli_request *req,
+                                     union smb_fileinfo *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+       
+       CLI_CHECK_WCT(req, 11);
+       parms->getattre.out.create_time =      make_unix_date2(req->in.vwv + VWV(0));
+       parms->getattre.out.access_time =      make_unix_date2(req->in.vwv + VWV(2));
+       parms->getattre.out.write_time  =      make_unix_date2(req->in.vwv + VWV(4));
+       parms->getattre.out.size =       IVAL(req->in.vwv, VWV(6));
+       parms->getattre.out.alloc_size = IVAL(req->in.vwv, VWV(8));
+       parms->getattre.out.attrib =     SVAL(req->in.vwv, VWV(10));
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Query file info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_fileinfo_send(struct cli_tree *tree,
+                                         union smb_fileinfo *parms)
+{
+       /* pass off the non-trans2 level to specialised functions */
+       if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+               return smb_raw_getattrE_send(tree, parms);
+       }
+       if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+               return NULL;
+       }
+
+       return smb_raw_fileinfo_blob_send(tree, 
+                                         parms->generic.in.fnum,
+                                         parms->generic.level);
+}
+
+/****************************************************************************
+ Query file info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo_recv(struct cli_request *req,
+                              TALLOC_CTX *mem_ctx,
+                              union smb_fileinfo *parms)
+{
+       DATA_BLOB blob;
+       NTSTATUS status;
+       struct cli_session *session = req?req->session:NULL;
+
+       if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+               return smb_raw_getattrE_recv(req, parms);
+       }
+       if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+               return smb_raw_getattr_recv(req, parms);
+       }
+
+       status = smb_raw_fileinfo_blob_recv(req, mem_ctx, &blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return smb_raw_info_backend(session, mem_ctx, parms, &blob);
+}
+
+/****************************************************************************
+ Query file info (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo(struct cli_tree *tree,
+                         TALLOC_CTX *mem_ctx,
+                         union smb_fileinfo *parms)
+{
+       struct cli_request *req = smb_raw_fileinfo_send(tree, parms);
+       return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_pathinfo_send(struct cli_tree *tree,
+                                         union smb_fileinfo *parms)
+{
+       if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+               return smb_raw_getattr_send(tree, parms);
+       }
+       if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+               return NULL;
+       }
+       
+       return smb_raw_pathinfo_blob_send(tree, parms->generic.in.fname,
+                                         parms->generic.level);
+}
+
+/****************************************************************************
+ Query path info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_pathinfo_recv(struct cli_request *req,
+                              TALLOC_CTX *mem_ctx,
+                              union smb_fileinfo *parms)
+{
+       /* recv is idential to fileinfo */
+       return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_pathinfo(struct cli_tree *tree,
+                         TALLOC_CTX *mem_ctx,
+                         union smb_fileinfo *parms)
+{
+       struct cli_request *req = smb_raw_pathinfo_send(tree, parms);
+       return smb_raw_pathinfo_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c
new file mode 100644 (file)
index 0000000..362063b
--- /dev/null
@@ -0,0 +1,282 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   RAW_QFS_* operations
+
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_dskattr_send(struct cli_tree *tree, 
+                                               union smb_fsinfo *fsinfo)
+{
+       struct cli_request *req; 
+
+       req = cli_request_setup(tree, SMBdskattr, 0, 0);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_dskattr_recv(struct cli_request *req,
+                                    union smb_fsinfo *fsinfo)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               goto failed;
+       }
+
+       CLI_CHECK_WCT(req, 5);
+       fsinfo->dskattr.out.units_total =     SVAL(req->in.vwv, VWV(0));
+       fsinfo->dskattr.out.blocks_per_unit = SVAL(req->in.vwv, VWV(1));
+       fsinfo->dskattr.out.block_size =      SVAL(req->in.vwv, VWV(2));
+       fsinfo->dskattr.out.units_free =      SVAL(req->in.vwv, VWV(3));
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_qfsinfo_send(struct cli_tree *tree,
+                                               TALLOC_CTX *mem_ctx,
+                                               uint16 info_level)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_QFSINFO;
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.max_param = 0;
+       tp.in.max_data = 0x1000; /* plenty for all possible QFS levels */
+       tp.in.setup = &setup;
+       tp.in.data = data_blob(NULL, 0);
+       tp.in.timeout = 0;
+
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 2);
+       if (!tp.in.params.data) {
+               return NULL;
+       }
+       SSVAL(tp.in.params.data, 0, info_level);
+
+       return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_qfsinfo_blob_recv(struct cli_request *req,
+                                         TALLOC_CTX *mem_ctx,
+                                         DATA_BLOB *blob)
+{
+       struct smb_trans2 tp;
+       NTSTATUS status;
+       
+       status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+       
+       if (NT_STATUS_IS_OK(status)) {
+               (*blob) = tp.out.data;
+       }
+
+       return status;
+}
+
+
+/* local macros to make the code more readable */
+#define QFS_CHECK_MIN_SIZE(size) if (blob.length < (size)) { \
+      DEBUG(1,("Unexpected QFS reply size %d for level %u - expected min of %d\n", \
+              blob.length, fsinfo->generic.level, (size))); \
+      status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+      goto failed; \
+}
+#define QFS_CHECK_SIZE(size) if (blob.length != (size)) { \
+      DEBUG(1,("Unexpected QFS reply size %d for level %u - expected %d\n", \
+              blob.length, fsinfo->generic.level, (size))); \
+      status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+      goto failed; \
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async send)
+****************************************************************************/
+struct cli_request *smb_raw_fsinfo_send(struct cli_tree *tree, 
+                                       TALLOC_CTX *mem_ctx, 
+                                       union smb_fsinfo *fsinfo)
+{
+       uint16 info_level;
+
+       /* handle the only non-trans2 call separately */
+       if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+               return smb_raw_dskattr_send(tree, fsinfo);
+       }
+       if (fsinfo->generic.level >= RAW_QFS_GENERIC) {
+               return NULL;
+       }
+
+       /* the headers map the trans2 levels direct to info levels */
+       info_level = (uint16)fsinfo->generic.level;
+
+       return smb_raw_qfsinfo_send(tree, mem_ctx, info_level);
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo_recv(struct cli_request *req, 
+                            TALLOC_CTX *mem_ctx, 
+                            union smb_fsinfo *fsinfo)
+{
+       DATA_BLOB blob;
+       NTSTATUS status;
+       int i;
+       struct cli_session *session = req?req->session:NULL;
+
+       if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+               return smb_raw_dskattr_recv(req, fsinfo);
+       }
+
+       status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* parse the results */
+       switch (fsinfo->generic.level) {
+       case RAW_QFS_GENERIC:
+       case RAW_QFS_DSKATTR:
+               /* handled above */
+               break;
+
+       case RAW_QFS_ALLOCATION:
+               QFS_CHECK_SIZE(18);
+               fsinfo->allocation.out.fs_id =             IVAL(blob.data,  0);
+               fsinfo->allocation.out.sectors_per_unit =  IVAL(blob.data,  4);
+               fsinfo->allocation.out.total_alloc_units = IVAL(blob.data,  8);
+               fsinfo->allocation.out.avail_alloc_units = IVAL(blob.data, 12);
+               fsinfo->allocation.out.bytes_per_sector =  SVAL(blob.data, 16); 
+               break;          
+
+       case RAW_QFS_VOLUME:
+               QFS_CHECK_MIN_SIZE(5);
+               fsinfo->volume.out.serial_number = IVAL(blob.data, 0);
+               cli_blob_pull_string(session, mem_ctx, &blob, 
+                                    &fsinfo->volume.out.volume_name,
+                                    4, 5, STR_LEN8BIT | STR_NOALIGN);
+               break;          
+
+       case RAW_QFS_VOLUME_INFO:
+       case RAW_QFS_VOLUME_INFORMATION:
+               QFS_CHECK_MIN_SIZE(18);
+               fsinfo->volume_info.out.create_time   = cli_pull_nttime(blob.data, 0);
+               fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8);
+               cli_blob_pull_string(session, mem_ctx, &blob, 
+                                    &fsinfo->volume_info.out.volume_name,
+                                    12, 18, STR_UNICODE);
+               break;          
+
+       case RAW_QFS_SIZE_INFO:
+       case RAW_QFS_SIZE_INFORMATION:
+               QFS_CHECK_SIZE(24);
+               fsinfo->size_info.out.total_alloc_units = BVAL(blob.data,  0);
+               fsinfo->size_info.out.avail_alloc_units = BVAL(blob.data,  8);
+               fsinfo->size_info.out.sectors_per_unit =  IVAL(blob.data, 16);
+               fsinfo->size_info.out.bytes_per_sector =  IVAL(blob.data, 20); 
+               break;          
+
+       case RAW_QFS_DEVICE_INFO:
+       case RAW_QFS_DEVICE_INFORMATION:
+               QFS_CHECK_SIZE(8);
+               fsinfo->device_info.out.device_type     = IVAL(blob.data,  0);
+               fsinfo->device_info.out.characteristics = IVAL(blob.data,  4);
+               break;          
+
+       case RAW_QFS_ATTRIBUTE_INFO:
+       case RAW_QFS_ATTRIBUTE_INFORMATION:
+               QFS_CHECK_MIN_SIZE(12);
+               fsinfo->attribute_info.out.fs_attr   =                 IVAL(blob.data, 0);
+               fsinfo->attribute_info.out.max_file_component_length = IVAL(blob.data, 4);
+               cli_blob_pull_string(session, mem_ctx, &blob, 
+                                    &fsinfo->attribute_info.out.fs_type,
+                                    8, 12, STR_UNICODE);
+               break;          
+
+       case RAW_QFS_UNIX_INFO:
+               QFS_CHECK_SIZE(12);
+               fsinfo->unix_info.out.major_version = SVAL(blob.data, 0);
+               fsinfo->unix_info.out.minor_version = SVAL(blob.data, 2);
+               fsinfo->unix_info.out.capability    = SVAL(blob.data, 4);
+               break;
+
+       case RAW_QFS_QUOTA_INFORMATION:
+               QFS_CHECK_SIZE(48);
+               fsinfo->quota_information.out.unknown[0] =  BVAL(blob.data,  0);
+               fsinfo->quota_information.out.unknown[1] =  BVAL(blob.data,  8);
+               fsinfo->quota_information.out.unknown[2] =  BVAL(blob.data, 16);
+               fsinfo->quota_information.out.quota_soft =  BVAL(blob.data, 24);
+               fsinfo->quota_information.out.quota_hard =  BVAL(blob.data, 32);
+               fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40);
+               break;          
+
+       case RAW_QFS_FULL_SIZE_INFORMATION:
+               QFS_CHECK_SIZE(32);
+               fsinfo->full_size_information.out.total_alloc_units =        BVAL(blob.data,  0);
+               fsinfo->full_size_information.out.call_avail_alloc_units =   BVAL(blob.data,  8);
+               fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16);
+               fsinfo->full_size_information.out.sectors_per_unit =         IVAL(blob.data, 24);
+               fsinfo->full_size_information.out.bytes_per_sector =         IVAL(blob.data, 28);
+               break;          
+
+       case RAW_QFS_OBJECTID_INFORMATION:
+               QFS_CHECK_SIZE(64);
+               memcpy(fsinfo->objectid_information.out.guid.info, blob.data, GUID_SIZE);
+               for (i=0;i<6;i++) {
+                       fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8);
+               }
+               break;          
+       }
+
+failed:
+       return status;
+}
+
+/****************************************************************************
+ Query FSInfo raw interface (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo(struct cli_tree *tree, 
+                       TALLOC_CTX *mem_ctx, 
+                       union smb_fsinfo *fsinfo)
+{
+       struct cli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo);
+       return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo);
+}
diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c
new file mode 100644 (file)
index 0000000..506bddd
--- /dev/null
@@ -0,0 +1,118 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client file operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+       req = cli_request_setup(tree, cmd, wct, buflen); \
+       if (!req) return NULL; \
+} while (0)
+
+/* 
+   send a raw ioctl - async send
+*/
+struct cli_request *smb_raw_ioctl_send(struct cli_tree *tree, struct smb_ioctl *parms)
+{
+       struct cli_request *req; 
+
+       SETUP_REQUEST(SMBioctl, 3, 0);
+
+       SSVAL(req->out.vwv, VWV(0), parms->in.fnum);
+       SIVAL(req->out.vwv, VWV(1), parms->in.request);
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/* 
+   send a raw ioctl - async recv
+*/
+NTSTATUS smb_raw_ioctl_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+
+       parms->out.blob = cli_req_pull_blob(req, mem_ctx, req->in.data, -1);
+       return cli_request_destroy(req);
+}
+
+/* 
+   send a raw ioctl - sync interface
+*/
+NTSTATUS smb_raw_ioctl(struct cli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms)
+{
+       struct cli_request *req = smb_raw_ioctl_send(tree, parms);
+       return smb_raw_ioctl_recv(req, mem_ctx, parms);
+}
+
+
+
+
+/****************************************************************************
+NT ioctl (async send)
+****************************************************************************/
+struct cli_request *smb_raw_ntioctl_send(struct cli_tree *tree, 
+                                        struct smb_ntioctl *parms)
+{
+       struct smb_nttrans nt;
+       uint16 setup[4];
+
+       nt.in.max_setup = 0;
+       nt.in.max_param = 0;
+       nt.in.max_data = 0;
+       nt.in.setup_count = 4;
+       nt.in.setup = setup;
+       SIVAL(setup, 0, parms->in.function);
+       SSVAL(setup, 4, parms->in.fnum);
+       SCVAL(setup, 6, parms->in.fsctl);
+       SCVAL(setup, 7, parms->in.filter);
+       nt.in.function = NT_TRANSACT_IOCTL;
+       nt.in.params = data_blob(NULL, 0);
+       nt.in.data = data_blob(NULL, 0);
+
+       return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+NT ioctl (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_ntioctl_recv(struct cli_request *req, 
+                             struct smb_ntioctl *parms)
+{
+       struct smb_nttrans nt;
+
+       return smb_raw_nttrans_recv(req, req->mem_ctx, &nt);
+}
+
+/****************************************************************************
+NT ioctl (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_ntioctl(struct cli_tree *tree, 
+                        struct smb_ntioctl *parms)
+{
+       struct cli_request *req = smb_raw_ntioctl_send(tree, parms);
+       return smb_raw_ntioctl_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c
new file mode 100644 (file)
index 0000000..78b2e00
--- /dev/null
@@ -0,0 +1,157 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client negotiate context management functions
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+static const struct {
+       int prot;
+       const char *name;
+} prots[] = {
+       {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+       {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+       {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+       {PROTOCOL_LANMAN1,"LANMAN1.0"},
+       {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
+       {PROTOCOL_LANMAN2,"LM1.2X002"},
+       {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+       {PROTOCOL_LANMAN2,"Samba"},
+       {PROTOCOL_NT1,"NT LANMAN 1.0"},
+       {PROTOCOL_NT1,"NT LM 0.12"},
+};
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+struct cli_request *smb_negprot_send(struct cli_transport *transport, int maxprotocol)
+{
+       struct cli_request *req;
+       int i;
+
+       req = cli_request_setup_transport(transport, SMBnegprot, 0, 0);
+       if (!req) {
+               return NULL;
+       }
+
+       /* setup the protocol strings */
+       for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) {
+               cli_req_append_bytes(req, "\2", 1);
+               cli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII);
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+NTSTATUS smb_raw_negotiate(struct cli_transport *transport) 
+{
+       struct cli_request *req;
+       int protocol;
+
+       req = smb_negprot_send(transport, PROTOCOL_NT1);
+       if (!req) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+
+       CLI_CHECK_MIN_WCT(req, 1);
+
+       protocol = SVALS(req->in.vwv, VWV(0));
+
+       if (protocol >= ARRAY_SIZE(prots) || protocol < 0) {
+               req->status = NT_STATUS_UNSUCCESSFUL;
+               return cli_request_destroy(req);
+       }
+
+       transport->negotiate.protocol = prots[protocol].prot;
+
+       if (transport->negotiate.protocol >= PROTOCOL_NT1) {
+               NTTIME ntt;
+
+               /* NT protocol */
+               CLI_CHECK_WCT(req, 17);
+               transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1));
+               transport->negotiate.max_mux  = SVAL(req->in.vwv,VWV(1)+1);
+               transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1);
+               transport->negotiate.sesskey  = IVAL(req->in.vwv,VWV(7)+1);
+               transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60;
+
+               /* this time arrives in real GMT */
+               ntt = cli_pull_nttime(req->in.vwv, VWV(11)+1);
+               transport->negotiate.server_time = nt_time_to_unix(&ntt);
+               transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1);
+
+               transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, req->in.data_size);
+               if (transport->negotiate.capabilities & CAP_RAW_MODE) {
+                       transport->negotiate.readbraw_supported = True;
+                       transport->negotiate.writebraw_supported = True;
+               }
+
+               /* work out if they sent us a workgroup */
+               if ((transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) &&
+                   req->in.data_size > 16) {
+                       cli_req_pull_string(req, transport->mem_ctx, &transport->negotiate.server_domain,
+                                           req->in.data+16,
+                                           req->in.data_size-16, STR_UNICODE|STR_NOALIGN);
+               }
+       } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) {
+               CLI_CHECK_WCT(req, 13);
+               transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1));
+               transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2));
+               transport->negotiate.sesskey =  IVAL(req->in.vwv,VWV(6));
+               transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60;
+               
+               /* this time is converted to GMT by make_unix_date */
+               transport->negotiate.server_time = make_unix_date(req->in.vwv+VWV(8));
+               if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) {
+                       transport->negotiate.readbraw_supported = 1;
+               }
+               if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) {
+                       transport->negotiate.writebraw_supported = 1;
+               }
+               transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, 
+                                                                req->in.data, req->in.data_size);
+       } else {
+               /* the old core protocol */
+               transport->negotiate.sec_mode = 0;
+               transport->negotiate.server_time = time(NULL);
+               transport->negotiate.max_xmit = ~0;
+               transport->negotiate.server_zone = TimeDiff(time(NULL));
+       }
+
+       /* a way to force ascii SMB */
+       if (getenv("CLI_FORCE_ASCII")) {
+               transport->negotiate.capabilities &= ~CAP_UNICODE;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c
new file mode 100644 (file)
index 0000000..7d635da
--- /dev/null
@@ -0,0 +1,116 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client change notify operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/****************************************************************************
+change notify (async send)
+****************************************************************************/
+struct cli_request *smb_raw_changenotify_send(struct cli_tree *tree, struct smb_notify *parms)
+{
+       struct smb_nttrans nt;
+       uint16 setup[4];
+
+       nt.in.max_setup = 0;
+       nt.in.max_param = parms->in.buffer_size;
+       nt.in.max_data = 0;
+       nt.in.setup_count = 4;
+       nt.in.setup = setup;
+       SIVAL(setup, 0, parms->in.completion_filter);
+       SSVAL(setup, 4, parms->in.fnum);
+       SSVAL(setup, 6, parms->in.recursive);   
+       nt.in.function = NT_TRANSACT_NOTIFY_CHANGE;
+       nt.in.params = data_blob(NULL, 0);
+       nt.in.data = data_blob(NULL, 0);
+
+       return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+change notify (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_changenotify_recv(struct cli_request *req, 
+                                  TALLOC_CTX *mem_ctx, struct smb_notify *parms)
+{
+       struct smb_nttrans nt;
+       NTSTATUS status;
+       uint32 ofs, i;
+       struct cli_session *session = req?req->session:NULL;
+
+       status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       parms->out.changes = NULL;
+       parms->out.num_changes = 0;
+       
+       /* count them */
+       for (ofs=0; nt.out.params.length - ofs > 12; ) {
+               uint32 next = IVAL(nt.out.params.data, ofs);
+               parms->out.num_changes++;
+               if (next == 0 ||
+                   ofs + next >= nt.out.params.length) break;
+               ofs += next;
+       }
+
+       /* allocate array */
+       parms->out.changes = talloc(mem_ctx, sizeof(parms->out.changes[0]) * 
+                                   parms->out.num_changes);
+       if (!parms->out.changes) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=ofs=0; i<parms->out.num_changes; i++) {
+               parms->out.changes[i].action = IVAL(nt.out.params.data, ofs+4);
+               cli_blob_pull_string(session, mem_ctx, &nt.out.params, 
+                                    &parms->out.changes[i].name, 
+                                    ofs+8, ofs+12, STR_UNICODE);
+               ofs += IVAL(nt.out.params.data, ofs);
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Send a NT Cancel request - used to hurry along a pending request. Usually
+ used to cancel a pending change notify request
+ note that this request does not expect a response!
+****************************************************************************/
+NTSTATUS smb_raw_ntcancel(struct cli_request *oldreq)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0);
+
+       SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID));   
+       SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID));   
+       SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID));   
+       SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID));   
+
+       /* this request does not expect a reply, so tell the signing
+          subsystem not to allocate an id for a reply */
+       req->one_way_request = 1;
+
+       cli_request_send(req);
+
+       return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c
new file mode 100644 (file)
index 0000000..84c7e3c
--- /dev/null
@@ -0,0 +1,321 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client file read/write routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003
+   
+   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"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+       req = cli_request_setup(tree, cmd, wct, buflen); \
+       if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ low level read operation (async send)
+****************************************************************************/
+struct cli_request *smb_raw_read_send(struct cli_tree *tree, union smb_read *parms)
+{
+       BOOL bigoffset = False;
+       struct cli_request *req; 
+
+       switch (parms->generic.level) {
+       case RAW_READ_GENERIC:
+               return NULL;
+               
+       case RAW_READ_READBRAW:
+               if (parms->readbraw.in.offset >= 0x80000000) {
+                       bigoffset = True;
+               }
+               SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.fnum);
+               SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset);
+               SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt);
+               SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt);
+               SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout);
+               SSVAL(req->out.vwv, VWV(7), 0); /* reserved */
+               if (bigoffset) {
+                       SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32);
+               }
+               break;
+
+       case RAW_READ_LOCKREAD:
+               SETUP_REQUEST(SMBlockread, 5, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->lockread.in.fnum);
+               SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count);
+               SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset);
+               SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining);
+               break;
+
+       case RAW_READ_READ:
+               SETUP_REQUEST(SMBread, 5, 0);
+               SSVAL(req->out.vwv, VWV(0), parms->read.in.fnum);
+               SSVAL(req->out.vwv, VWV(1), parms->read.in.count);
+               SIVAL(req->out.vwv, VWV(2), parms->read.in.offset);
+               SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining);
+               break;
+
+       case RAW_READ_READX:
+               if (parms->readx.in.offset >= 0x80000000) {
+                       bigoffset = True;
+               }
+               SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0);
+               SSVAL(req->out.vwv, VWV(0), 0xFF);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->readx.in.fnum);
+               SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset);
+               SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt);
+               SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt);
+               SIVAL(req->out.vwv, VWV(7), 0); /* reserved */
+               SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining);
+               if (bigoffset) {
+                       SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32);
+               }
+               break;
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       /* the transport layer needs to know that a readbraw is pending
+          and handle receives a little differently */
+       if (parms->generic.level == RAW_READ_READBRAW) {
+               tree->session->transport->readbraw_pending = 1;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ low level read operation (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_read_recv(struct cli_request *req, union smb_read *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               goto failed;
+       }
+
+       switch (parms->generic.level) {
+       case RAW_READ_GENERIC:
+               /* handled in _send() */
+               break;
+
+       case RAW_READ_READBRAW:
+               parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE;
+               if (parms->readbraw.out.nread > 
+                   MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) {
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       goto failed;
+               }
+               memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread);
+               break;
+               
+       case RAW_READ_LOCKREAD:
+               CLI_CHECK_WCT(req, 5);
+               parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0));
+               if (parms->lockread.out.nread > parms->lockread.in.count ||
+                   !cli_raw_pull_data(req, req->in.data+3, 
+                                      parms->lockread.out.nread, parms->lockread.out.data)) {
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+               }
+               break;
+
+       case RAW_READ_READ:
+               /* there are 4 reserved words in the reply */
+               CLI_CHECK_WCT(req, 5);
+               parms->read.out.nread = SVAL(req->in.vwv, VWV(0));
+               if (parms->read.out.nread > parms->read.in.count ||
+                   !cli_raw_pull_data(req, req->in.data+3, 
+                                      parms->read.out.nread, parms->read.out.data)) {
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+               }
+               break;
+
+       case RAW_READ_READX:
+               /* there are 5 reserved words in the reply */
+               CLI_CHECK_WCT(req, 12);
+               parms->readx.out.remaining       = SVAL(req->in.vwv, VWV(2));
+               parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+               parms->readx.out.nread = SVAL(req->in.vwv, VWV(5));
+               if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
+                   !cli_raw_pull_data(req, req->in.hdr + SVAL(req->in.vwv, VWV(6)), 
+                                      parms->readx.out.nread, 
+                                      parms->readx.out.data)) {
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+               }
+               break;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ low level read operation (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_read(struct cli_tree *tree, union smb_read *parms)
+{
+       struct cli_request *req = smb_raw_read_send(tree, parms);
+       return smb_raw_read_recv(req, parms);
+}
+
+
+/****************************************************************************
+ raw write interface (async send)
+****************************************************************************/
+struct cli_request *smb_raw_write_send(struct cli_tree *tree, union smb_write *parms)
+{
+       BOOL bigoffset = False;
+       struct cli_request *req; 
+
+       switch (parms->generic.level) {
+       case RAW_WRITE_GENERIC:
+               return NULL;
+               
+       case RAW_WRITE_WRITEUNLOCK:
+               SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count);
+               SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.fnum);
+               SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count);
+               SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset);
+               SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining);
+               SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+               SSVAL(req->out.data, 1, parms->writeunlock.in.count);
+               if (parms->writeunlock.in.count > 0) {
+                       memcpy(req->out.data+3, parms->writeunlock.in.data, 
+                              parms->writeunlock.in.count);
+               }
+               break;
+
+       case RAW_WRITE_WRITE:
+               SETUP_REQUEST(SMBwrite, 5,  3 + parms->write.in.count);
+               SSVAL(req->out.vwv, VWV(0), parms->write.in.fnum);
+               SSVAL(req->out.vwv, VWV(1), parms->write.in.count);
+               SIVAL(req->out.vwv, VWV(2), parms->write.in.offset);
+               SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining);
+               SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+               SSVAL(req->out.data, 1, parms->write.in.count);
+               if (parms->write.in.count > 0) {
+                       memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count);
+               }
+               break;
+
+       case RAW_WRITE_WRITECLOSE:
+               SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count);
+               SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.fnum);
+               SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count);
+               SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset);
+               put_dos_date3(req->out.vwv, VWV(4), parms->writeclose.in.mtime);
+               SCVAL(req->out.data, 0, 0);
+               if (parms->writeclose.in.count > 0) {
+                       memcpy(req->out.data+1, parms->writeclose.in.data, 
+                              parms->writeclose.in.count);
+               }
+               break;
+
+       case RAW_WRITE_WRITEX:
+               if (parms->writex.in.offset >= 0x80000000) {
+                       bigoffset = True;
+               }
+               SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count);
+               SSVAL(req->out.vwv, VWV(0), 0xFF);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), parms->writex.in.fnum);
+               SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset);
+               SIVAL(req->out.vwv, VWV(5), 0); /* reserved */
+               SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode);
+               SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining);
+               SSVAL(req->out.vwv, VWV(9), 0); /* reserved */
+               SSVAL(req->out.vwv, VWV(10), parms->writex.in.count);
+               SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr));
+               if (bigoffset) {
+                       SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32);
+               }
+               if (parms->writex.in.count > 0) {
+                       memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count);
+               }
+               break;
+
+       case RAW_WRITE_SPLWRITE:
+               SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count);
+               SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.fnum);
+               if (parms->splwrite.in.count > 0) {
+                       memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count);
+               }
+               break;
+       }
+
+       if (!cli_request_send(req)) {
+cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+/****************************************************************************
+ raw write interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_write_recv(struct cli_request *req, union smb_write *parms)
+{
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               goto failed;
+       }
+
+       switch (parms->generic.level) {
+       case RAW_WRITE_GENERIC:
+               break;
+       case RAW_WRITE_WRITEUNLOCK:
+               CLI_CHECK_WCT(req, 1);          
+               parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0));
+               break;
+       case RAW_WRITE_WRITE:
+               CLI_CHECK_WCT(req, 1);
+               parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0));
+               break;
+       case RAW_WRITE_WRITECLOSE:
+               CLI_CHECK_WCT(req, 1);
+               parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0));
+               break;
+       case RAW_WRITE_WRITEX:
+               CLI_CHECK_WCT(req, 6);
+               parms->writex.out.nwritten  = SVAL(req->in.vwv, VWV(2));
+               parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16);
+               parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3));
+               break;
+       case RAW_WRITE_SPLWRITE:
+               break;
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ raw write interface (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_write(struct cli_tree *tree, union smb_write *parms)
+{
+       struct cli_request *req = smb_raw_write_send(tree, parms);
+       return smb_raw_write_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c
new file mode 100644 (file)
index 0000000..9c2b2c7
--- /dev/null
@@ -0,0 +1,1019 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Andrew Tridgell  2003
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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.
+*/
+
+/*
+  this file implements functions for manipulating the 'struct cli_request' structure in libsmb
+*/
+
+#include "includes.h"
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 256
+
+/* assume that a character will not consume more than 3 bytes per char */
+#define MAX_BYTES_PER_CHAR 3
+
+/* destroy a request structure and return final status */
+NTSTATUS cli_request_destroy(struct cli_request *req)
+{
+       NTSTATUS status;
+
+       /* this is the error code we give the application for when a
+          _send() call fails completely */
+       if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+       /* remove it from the list of pending requests (a null op if
+          its not in the list) */
+       DLIST_REMOVE(req->transport->pending_requests, req);
+
+       /* ahh, its so nice to destroy a complex structure in such a
+          simple way! */
+       status = req->status;
+       talloc_destroy(req->mem_ctx);
+       return status;
+}
+
+
+/*
+  low-level function to setup a request buffer for a non-SMB packet 
+  at the transport level
+*/
+struct cli_request *cli_request_setup_nonsmb(struct cli_transport *transport, uint_t size)
+{
+       struct cli_request *req;
+       TALLOC_CTX *mem_ctx;
+       
+       /* each request gets its own talloc context. The request
+          structure itself is also allocated inside this context,
+          so we need to allocate it before we construct the request
+       */
+       mem_ctx = talloc_init("cli_request");
+       if (!mem_ctx) {
+               return NULL;
+       }
+
+       req = talloc(mem_ctx, sizeof(struct cli_request));
+       if (!req) {
+               return NULL;
+       }
+       ZERO_STRUCTP(req);
+
+       /* setup the request context */
+       req->mem_ctx = mem_ctx;
+       req->transport = transport;
+       req->session = NULL;
+       req->tree = NULL;
+       req->out.size = size;
+
+       /* over allocate by a small amount */
+       req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 
+
+       req->out.buffer = talloc(req->mem_ctx, req->out.allocated);
+       if (!req->out.buffer) {
+               return NULL;
+       }
+
+       SIVAL(req->out.buffer, 0, 0);
+
+       return req;
+}
+
+
+/*
+  setup a SMB packet at transport level
+*/
+struct cli_request *cli_request_setup_transport(struct cli_transport *transport,
+                                               uint8 command, unsigned wct, unsigned buflen)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);
+
+       if (!req) return NULL;
+       
+       req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+       req->out.vwv = req->out.hdr + HDR_VWV;
+       req->out.wct = wct;
+       req->out.data = req->out.vwv + VWV(wct) + 2;
+       req->out.data_size = buflen;
+       req->out.ptr = req->out.data;
+
+       SCVAL(req->out.hdr, HDR_WCT, wct);
+       SSVAL(req->out.vwv, VWV(wct), buflen);
+
+       memcpy(req->out.hdr, "\377SMB", 4);
+       SCVAL(req->out.hdr,HDR_COM,command);
+
+       SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);
+       SSVAL(req->out.hdr,HDR_FLG2, 0);
+
+       /* assign a mid */
+       req->mid = cli_transport_next_mid(transport);
+
+       /* copy the pid, uid and mid to the request */
+       SSVAL(req->out.hdr, HDR_PID, 0);
+       SSVAL(req->out.hdr, HDR_UID, 0);
+       SSVAL(req->out.hdr, HDR_MID, req->mid);
+       SSVAL(req->out.hdr, HDR_TID,0);
+       SSVAL(req->out.hdr, HDR_PIDHIGH,0);
+       SIVAL(req->out.hdr, HDR_RCLS, 0);
+       memset(req->out.hdr+HDR_SS_FIELD, 0, 10);
+       
+       return req;
+}
+
+/*
+  setup a reply in req->out with the given word count and initial data
+  buffer size.  the caller will then fill in the command words and
+  data before calling cli_request_send() to send the reply on its
+  way. This interface is used before a session is setup.
+*/
+struct cli_request *cli_request_setup_session(struct cli_session *session,
+                                             uint8 command, unsigned wct, unsigned buflen)
+{
+       struct cli_request *req;
+       uint16 flags2;
+       uint32 capabilities;
+
+       req = cli_request_setup_transport(session->transport, command, wct, buflen);
+
+       if (!req) return NULL;
+
+       req->session = session;
+       
+       flags2 = FLAGS2_LONG_PATH_COMPONENTS;
+       capabilities = session->transport->negotiate.capabilities;
+
+       if (capabilities & CAP_UNICODE) {
+               flags2 |= FLAGS2_UNICODE_STRINGS;
+       }
+       if (capabilities & CAP_STATUS32) {
+               flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+       }
+       if (capabilities & CAP_EXTENDED_SECURITY) {
+               flags2 |= FLAGS2_EXTENDED_SECURITY;
+       }
+       if (session->transport->negotiate.sign_info.doing_signing) {
+               flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+       }
+
+       SSVAL(req->out.hdr, HDR_FLG2, flags2);
+       SSVAL(req->out.hdr, HDR_PID, session->pid);
+       SSVAL(req->out.hdr, HDR_UID, session->vuid);
+       
+       return req;
+}
+
+/*
+  setup a request for tree based commands
+*/
+struct cli_request *cli_request_setup(struct cli_tree *tree,
+                                     uint8 command, 
+                                     unsigned wct, unsigned buflen)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup_session(tree->session, command, wct, buflen);
+       if (req) {
+               req->tree = tree;
+               SSVAL(req->out.hdr,HDR_TID,tree->tid);
+       }
+       return req;
+}
+
+/*
+  grow the allocation of the data buffer portion of a reply
+  packet. Note that as this can reallocate the packet buffer this
+  invalidates any local pointers into the packet.
+
+  To cope with this req->out.ptr is supplied. This will be updated to
+  point at the same offset into the packet as before this call
+*/
+static void cli_req_grow_allocation(struct cli_request *req, unsigned new_size)
+{
+       int delta;
+       char *buf2;
+
+       delta = new_size - req->out.data_size;
+       if (delta + req->out.size <= req->out.allocated) {
+               /* it fits in the preallocation */
+               return;
+       }
+
+       /* we need to realloc */
+       req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+       buf2 = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated);
+       if (buf2 == NULL) {
+               smb_panic("out of memory in req_grow_allocation");
+       }
+
+       if (buf2 == req->out.buffer) {
+               /* the malloc library gave us the same pointer */
+               return;
+       }
+       
+       /* update the pointers into the packet */
+       req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+       req->out.ptr  = buf2 + PTR_DIFF(req->out.ptr,  req->out.buffer);
+       req->out.vwv  = buf2 + PTR_DIFF(req->out.vwv,  req->out.buffer);
+       req->out.hdr  = buf2 + PTR_DIFF(req->out.hdr,  req->out.buffer);
+
+       req->out.buffer = buf2;
+}
+
+
+/*
+  grow the data buffer portion of a reply packet. Note that as this
+  can reallocate the packet buffer this invalidates any local pointers
+  into the packet. 
+
+  To cope with this req->out.ptr is supplied. This will be updated to
+  point at the same offset into the packet as before this call
+*/
+static void cli_req_grow_data(struct cli_request *req, unsigned new_size)
+{
+       int delta;
+
+       cli_req_grow_allocation(req, new_size);
+
+       delta = new_size - req->out.data_size;
+
+       req->out.size += delta;
+       req->out.data_size += delta;
+
+       /* set the BCC to the new data size */
+       SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+/*
+  send a message
+*/
+BOOL cli_request_send(struct cli_request *req)
+{
+       if (IVAL(req->out.buffer, 0) == 0) {
+               _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+       }
+
+       cli_request_calculate_sign_mac(req);
+
+       if (req->out.size != cli_sock_write(req->transport->socket, req->out.buffer, req->out.size)) {
+               req->transport->error.etype = ETYPE_SOCKET;
+               req->transport->error.e.socket_error = SOCKET_WRITE_ERROR;
+               DEBUG(0,("Error writing %d bytes to server - %s\n",
+                        (int)req->out.size, strerror(errno)));
+               return False;
+       }
+
+       /* add it to the list of pending requests */
+       DLIST_ADD(req->transport->pending_requests, req);
+       
+       return True;
+}
+
+
+/*
+  receive a response to a packet
+*/
+BOOL cli_request_receive(struct cli_request *req)
+{
+       /* req can be NULL when a send has failed. This eliminates lots of NULL
+          checks in each module */
+       if (!req) return False;
+
+       /* keep receiving packets until this one is replied to */
+       while (!req->in.buffer) {
+               if (!cli_transport_select(req->transport)) {
+                       return False;
+               }
+
+               cli_request_receive_next(req->transport);
+       }
+
+       return True;
+}
+
+
+/*
+  handle oplock break requests from the server - return True if the request was
+  an oplock break
+*/
+static BOOL handle_oplock_break(struct cli_transport *transport, uint_t len, const char *hdr, const char *vwv)
+{
+       /* we must be very fussy about what we consider an oplock break to avoid
+          matching readbraw replies */
+       if (len != MIN_SMB_SIZE + VWV(8) ||
+           (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
+           CVAL(hdr,HDR_COM) != SMBlockingX ||
+           SVAL(hdr, HDR_MID) != 0xFFFF ||
+           SVAL(vwv,VWV(6)) != 0 ||
+           SVAL(vwv,VWV(7)) != 0) {
+               return False;
+       }
+
+       if (transport->oplock.handler) {
+               uint16 tid = SVAL(hdr, HDR_TID);
+               uint16 fnum = SVAL(vwv,VWV(2));
+               uint8 level = CVAL(vwv,VWV(3));
+               transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private);
+       }
+
+       return True;
+}
+
+
+/*
+  receive an async message from the server
+  this function assumes that the caller already knows that the socket is readable
+  and that there is a packet waiting
+
+  The packet is not actually returned by this function, instead any
+  registered async message handlers are called
+
+  return True if a packet was successfully received and processed
+  return False if the socket appears to be dead
+*/
+BOOL cli_request_receive_next(struct cli_transport *transport)
+{
+       BOOL ret;
+       int len;
+       char header[NBT_HDR_SIZE];
+       char *buffer, *hdr, *vwv;
+       TALLOC_CTX *mem_ctx;
+       struct cli_request *req;
+       uint16 wct, mid = 0;
+
+       len = cli_sock_read(transport->socket, header, 4);
+       if (len != 4) {
+               return False;
+       }
+       
+       len = smb_len(header);
+
+       mem_ctx = talloc_init("cli_request_receive_next");
+       
+       /* allocate the incoming buffer at the right size */
+       buffer = talloc(mem_ctx, len+NBT_HDR_SIZE);
+       if (!buffer) {
+               talloc_destroy(mem_ctx);
+               return False;
+       }
+
+       /* fill in the already received header */
+       memcpy(buffer, header, NBT_HDR_SIZE);
+
+       ret = cli_sock_read(transport->socket, buffer + NBT_HDR_SIZE, len);
+       /* If the server is not responding, note that now */
+       if (ret != len) {
+               return False;
+       }
+
+       hdr = buffer+NBT_HDR_SIZE;
+       vwv = hdr + HDR_VWV;
+
+       /* see if it could be an oplock break request */
+       if (handle_oplock_break(transport, len, hdr, vwv)) {
+               goto done;
+       }
+
+       /* at this point we need to check for a readbraw reply, as these can be any length */
+       if (transport->readbraw_pending) {
+               transport->readbraw_pending = 0;
+
+               /* it must match the first entry in the pending queue as the client is not allowed
+                  to have outstanding readbraw requests */
+               req = transport->pending_requests;
+               if (!req) goto done;
+
+               req->in.buffer = buffer;
+               talloc_steal(mem_ctx, req->mem_ctx, buffer);
+               req->in.size = len + NBT_HDR_SIZE;
+               req->in.allocated = req->in.size;
+               goto async;
+       }
+
+       if (len >= MIN_SMB_SIZE) {
+               /* extract the mid for matching to pending requests */
+               mid = SVAL(hdr, HDR_MID);
+               wct = CVAL(hdr, HDR_WCT);
+       }
+
+       /* match the incoming request against the list of pending requests */
+       for (req=transport->pending_requests; req; req=req->next) {
+               if (req->mid == mid) break;
+       }
+
+       if (!req) {
+               DEBUG(3,("Discarding unmatched reply with mid %d\n", mid));
+               goto done;
+       }
+
+       /* fill in the 'in' portion of the matching request */
+       req->in.buffer = buffer;
+       talloc_steal(mem_ctx, req->mem_ctx, buffer);
+       req->in.size = len + NBT_HDR_SIZE;
+       req->in.allocated = req->in.size;
+
+       /* handle non-SMB replies */
+       if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
+               goto done;
+       }
+
+       if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+               DEBUG(2,("bad reply size for mid %d\n", mid));
+               req->status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       req->in.hdr = hdr;
+       req->in.vwv = vwv;
+       req->in.wct = wct;
+       if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+               req->in.data = req->in.vwv + VWV(wct) + 2;
+               req->in.data_size = SVAL(req->in.vwv, VWV(wct));
+               if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
+                       DEBUG(3,("bad data size for mid %d\n", mid));
+                       /* blergh - w2k3 gives a bogus data size values in some
+                          openX replies */
+                       req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
+               }
+       }
+       req->in.ptr = req->in.data;
+       req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+       if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
+               transport->error.etype = ETYPE_DOS;
+               transport->error.e.dos.eclass = CVAL(req->in.hdr,HDR_RCLS);
+               transport->error.e.dos.ecode = SVAL(req->in.hdr,HDR_ERR);
+               req->status = dos_to_ntstatus(transport->error.e.dos.eclass, 
+                                             transport->error.e.dos.ecode);
+       } else {
+               transport->error.etype = ETYPE_NT;
+               transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
+               req->status = transport->error.e.nt_status;
+       }
+
+       if (!cli_request_check_sign_mac(req)) {
+               transport->error.etype = ETYPE_SOCKET;
+               transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
+               return False;
+       };
+
+async:
+       /* if this request has an async handler then call that to
+          notify that the reply has been received. This might destroy
+          the request so it must happen last */
+       if (req->async.fn) {
+               req->async.fn(req);
+       }
+
+done:
+       talloc_destroy(mem_ctx);
+       return True;
+}
+
+
+/*
+  wait for a reply to be received for a packet that just returns an error
+  code and nothing more
+*/
+NTSTATUS cli_request_simple_recv(struct cli_request *req)
+{
+       cli_request_receive(req);
+       return cli_request_destroy(req);
+}
+
+
+/* Return true if the last packet was in error */
+BOOL cli_request_is_error(struct cli_request *req)
+{
+       return NT_STATUS_IS_ERR(req->status);
+}
+
+/*
+  append a string into the data portion of the request packet
+
+  return the number of bytes added to the packet
+*/
+size_t cli_req_append_string(struct cli_request *req, const char *str, unsigned flags)
+{
+       size_t len;
+
+       /* determine string type to use */
+       if (!(flags & (STR_ASCII|STR_UNICODE))) {
+               flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+       }
+
+       len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;             
+
+       cli_req_grow_allocation(req, len + req->out.data_size);
+
+       len = push_string(NULL, req->out.data + req->out.data_size, str, len, flags);
+
+       cli_req_grow_data(req, len + req->out.data_size);
+
+       return len;
+}
+
+/*
+  this is like cli_req_append_string but it also return the
+  non-terminated string byte length, which can be less than the number
+  of bytes consumed in the packet for 2 reasons:
+
+   1) the string in the packet may be null terminated
+   2) the string in the packet may need a 1 byte UCS2 alignment
+
+ this is used in places where the non-terminated string byte length is
+ placed in the packet as a separate field  
+*/
+size_t cli_req_append_string_len(struct cli_request *req, const char *str, unsigned flags, int *len)
+{
+       int diff = 0;
+       size_t ret;
+
+       /* determine string type to use */
+       if (!(flags & (STR_ASCII|STR_UNICODE))) {
+               flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+       }
+
+       /* see if an alignment byte will be used */
+       if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
+               diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);
+       }
+
+       /* do the hard work */
+       ret = cli_req_append_string(req, str, flags);
+
+       /* see if we need to subtract the termination */
+       if (flags & STR_TERMINATE) {
+               diff += (flags & STR_UNICODE) ? 2 : 1;
+       }
+
+       if (ret >= diff) {
+               (*len) = ret - diff;
+       } else {
+               (*len) = ret;
+       }
+
+       return ret;
+}
+
+
+/*
+  push a string into the data portion of the request packet, growing it if necessary
+  this gets quite tricky - please be very careful to cover all cases when modifying this
+
+  if dest is NULL, then put the string at the end of the data portion of the packet
+
+  if dest_len is -1 then no limit applies
+*/
+size_t cli_req_append_ascii4(struct cli_request *req, const char *str, unsigned flags)
+{
+       size_t size;
+       cli_req_append_bytes(req, (const uint8 *)"\4", 1);
+       size = cli_req_append_string(req, str, flags);
+       return size + 1;
+}
+
+
+/*
+  push a blob into the data portion of the request packet, growing it if necessary
+  this gets quite tricky - please be very careful to cover all cases when modifying this
+
+  if dest is NULL, then put the blob at the end of the data portion of the packet
+*/
+size_t cli_req_append_blob(struct cli_request *req, const DATA_BLOB *blob)
+{
+       cli_req_grow_allocation(req, req->out.data_size + blob->length);
+       memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
+       cli_req_grow_data(req, req->out.data_size + blob->length);
+       return blob->length;
+}
+
+/*
+  append raw bytes into the data portion of the request packet
+  return the number of bytes added
+*/
+size_t cli_req_append_bytes(struct cli_request *req, const uint8 *bytes, size_t byte_len)
+{
+       cli_req_grow_allocation(req, byte_len + req->out.data_size);
+       memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+       cli_req_grow_data(req, byte_len + req->out.data_size);
+       return byte_len;
+}
+
+/*
+  append variable block (type 5 buffer) into the data portion of the request packet
+  return the number of bytes added
+*/
+size_t cli_req_append_var_block(struct cli_request *req, const uint8 *bytes, uint16 byte_len)
+{
+       cli_req_grow_allocation(req, byte_len + 3 + req->out.data_size);
+       SCVAL(req->out.data + req->out.data_size, 0, 5);
+       SSVAL(req->out.data + req->out.data_size, 1, byte_len);         /* add field length */
+       if (byte_len > 0) {
+               memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
+       }
+       cli_req_grow_data(req, byte_len + 3 + req->out.data_size);
+       return byte_len + 3;
+}
+
+
+/*
+  pull a UCS2 string from a request packet, returning a talloced unix string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+static size_t cli_req_pull_ucs2(struct cli_request *req, TALLOC_CTX *mem_ctx,
+                               char **dest, const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2, alignment=0;
+       ssize_t ret;
+
+       if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
+               src++;
+               alignment=1;
+               if (byte_len != -1) {
+                       byte_len--;
+               }
+       }
+
+       src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+       if (src_len < 0) {
+               *dest = NULL;
+               return 0;
+       }
+       if (byte_len != -1 && src_len > byte_len) {
+               src_len = byte_len;
+       }
+
+       src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
+       if (src_len2 < src_len - 2) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2 += 2;
+       }
+
+       /* ucs2 strings must be at least 2 bytes long */
+       if (src_len2 < 2) {
+               *dest = NULL;
+               return 0;
+       }
+
+       ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return src_len2 + alignment;
+}
+
+/*
+  pull a ascii string from a request packet, returning a talloced string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+size_t cli_req_pull_ascii(struct cli_request *req, TALLOC_CTX *mem_ctx,
+                         char **dest, const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2;
+       ssize_t ret;
+
+       src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+       if (src_len < 0) {
+               *dest = NULL;
+               return 0;
+       }
+       if (byte_len != -1 && src_len > byte_len) {
+               src_len = byte_len;
+       }
+       src_len2 = strnlen(src, src_len);
+       if (src_len2 < src_len - 1) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2++;
+       }
+
+       ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
+
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return ret;
+}
+
+/*
+  pull a string from a request packet, returning a talloced string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+size_t cli_req_pull_string(struct cli_request *req, TALLOC_CTX *mem_ctx, 
+                          char **dest, const char *src, int byte_len, unsigned flags)
+{
+       if (!(flags & STR_ASCII) && 
+           ((flags & STR_UNICODE || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
+               return cli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags);
+       }
+
+       return cli_req_pull_ascii(req, mem_ctx, dest, src, byte_len, flags);
+}
+
+
+/*
+  pull a DATA_BLOB from a reply packet, returning a talloced blob
+  make sure we don't go past end of packet
+
+  if byte_len is -1 then limit the blob only by packet size
+*/
+DATA_BLOB cli_req_pull_blob(struct cli_request *req, TALLOC_CTX *mem_ctx, const char *src, int byte_len)
+{
+       int src_len;
+
+       src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+
+       if (src_len < 0) {
+               return data_blob(NULL, 0);
+       }
+
+       if (byte_len != -1 && src_len > byte_len) {
+               src_len = byte_len;
+       }
+
+       return data_blob_talloc(mem_ctx, src, src_len);
+}
+
+/* check that a lump of data in a request is within the bounds of the data section of
+   the packet */
+static BOOL cli_req_data_oob(struct cli_request *req, const char *ptr, uint32 count)
+{
+       /* be careful with wraparound! */
+       if (ptr < req->in.data ||
+           ptr >= req->in.data + req->in.data_size ||
+           count > req->in.data_size ||
+           ptr + count > req->in.data + req->in.data_size) {
+               return True;
+       }
+       return False;
+}
+
+/*
+  pull a lump of data from a request packet
+
+  return False if any part is outside the data portion of the packet
+*/
+BOOL cli_raw_pull_data(struct cli_request *req, const char *src, int len, char *dest)
+{
+       if (len == 0) return True;
+
+       if (cli_req_data_oob(req, src, len)) {
+               return False;
+       }
+
+       memcpy(dest, src, len);
+       return True;
+}
+
+
+/*
+  put a NTTIME into a packet
+*/
+
+void cli_push_nttime(void *base, uint16 offset, NTTIME *t)
+{
+       SIVAL(base, offset,   t->low);
+       SIVAL(base, offset+4, t->high);
+}
+
+/*
+  pull a NTTIME from a packet
+*/
+NTTIME cli_pull_nttime(void *base, uint16 offset)
+{
+       NTTIME ret;
+       ret.low = IVAL(base, offset);
+       ret.high = IVAL(base, offset+4);
+       return ret;
+}
+
+/*
+  pull a UCS2 string from a blob, returning a talloced unix string
+
+  the string length is limited by the 3 things:
+   - the data size in the blob
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the blob is returned
+*/
+static size_t cli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
+                                DATA_BLOB *blob, const char **dest, 
+                                const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2, alignment=0;
+       ssize_t ret;
+
+       if (src < (const char *)blob->data ||
+           src >= (const char *)(blob->data + blob->length)) {
+               *dest = NULL;
+               return 0;
+       }
+
+       src_len = blob->length - PTR_DIFF(src, blob->data);
+
+       if (byte_len != -1 && src_len > byte_len) {
+               src_len = byte_len;
+       }
+
+       if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) {
+               src++;
+               alignment=1;
+               src_len--;
+       }
+
+       if (src_len < 2) {
+               *dest = NULL;
+               return 0;
+       }
+
+       src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
+
+       if (src_len2 < src_len - 2) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2 += 2;
+       }
+
+       ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return src_len2 + alignment;
+}
+
+/*
+  pull a ascii string from a blob, returning a talloced string
+
+  the string length is limited by the 3 things:
+   - the data size in the blob
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the blob
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the blob is returned
+*/
+static size_t cli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
+                                 DATA_BLOB *blob, const char **dest, 
+                                 const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2;
+       ssize_t ret;
+
+       src_len = blob->length - PTR_DIFF(src, blob->data);
+       if (src_len < 0) {
+               *dest = NULL;
+               return 0;
+       }
+       if (byte_len != -1 && src_len > byte_len) {
+               src_len = byte_len;
+       }
+       src_len2 = strnlen(src, src_len);
+
+       if (src_len2 < src_len - 1) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2++;
+       }
+
+       ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
+
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return ret;
+}
+
+/*
+  pull a string from a blob, returning a talloced WIRE_STRING
+
+  the string length is limited by the 3 things:
+   - the data size in the blob
+   - length field on the wire
+   - the end of string (null termination)
+
+   if STR_LEN8BIT is set in the flags then assume the length field is
+   8 bits, instead of 32
+
+  on failure zero is returned and dest->s is set to NULL, otherwise the number
+  of bytes consumed in the blob is returned
+*/
+size_t cli_blob_pull_string(struct cli_session *session,
+                           TALLOC_CTX *mem_ctx,
+                           DATA_BLOB *blob, 
+                           WIRE_STRING *dest, 
+                           uint16 len_offset, uint16 str_offset, 
+                           unsigned flags)
+{
+       dest->s = NULL;
+
+       if (len_offset > blob->length-4) {
+               return 0;
+       }
+       if (flags & STR_LEN8BIT) {
+               dest->private_length = CVAL(blob->data, len_offset);
+       } else {
+               dest->private_length = IVAL(blob->data, len_offset);
+       }
+       dest->s = NULL;
+       if (!(flags & STR_ASCII) && 
+           ((flags & STR_UNICODE) || 
+            (session->transport->negotiate.capabilities & CAP_UNICODE))) {
+               if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+                       str_offset++;
+               }
+               return cli_blob_pull_ucs2(mem_ctx, blob, &dest->s, 
+                                         blob->data+str_offset, dest->private_length, flags);
+       }
+
+       return cli_blob_pull_ascii(mem_ctx, blob, &dest->s, 
+                                  blob->data+str_offset, dest->private_length, flags);
+}
+
+/*
+  append a string into a blob
+*/
+size_t cli_blob_append_string(struct cli_session *session,
+                             TALLOC_CTX *mem_ctx, DATA_BLOB *blob, 
+                             const char *str, unsigned flags)
+{
+       size_t max_len;
+       int len;
+
+       if (!str) return 0;
+
+       /* determine string type to use */
+       if (!(flags & (STR_ASCII|STR_UNICODE))) {
+               flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+       }
+
+       max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;         
+
+       blob->data = talloc_realloc(mem_ctx, blob->data, blob->length + max_len);
+       if (!blob->data) {
+               return 0;
+       }
+
+       len = push_string(NULL, blob->data + blob->length, str, max_len, flags);
+
+       blob->length += len;
+
+       return len;
+}
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
new file mode 100644 (file)
index 0000000..bdc39bb
--- /dev/null
@@ -0,0 +1,569 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client directory search routines
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+/****************************************************************************
+ Old style search backend - process output.
+****************************************************************************/
+static void smb_raw_search_backend(struct cli_request *req,
+                                  TALLOC_CTX *mem_ctx,
+                                  uint16 count, 
+                                  void *private,
+                                  BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+       union smb_search_data search_data;
+       int i;
+       char *p;
+
+       if (req->in.data_size < 3 + count*43) {
+               req->status = NT_STATUS_INVALID_PARAMETER;
+               return;
+       }
+       
+       p = req->in.data + 3;
+
+       for (i=0; i < count; i++) {
+               search_data.search.search_id  = cli_req_pull_blob(req, mem_ctx, p, 21);
+               search_data.search.attrib     = CVAL(p,            21);
+               search_data.search.write_time = make_unix_date(p + 22);
+               search_data.search.size       = IVAL(p,            26);
+               cli_req_pull_ascii(req, mem_ctx, &search_data.search.name, p+30, 13, STR_ASCII);
+               if (!callback(private, &search_data)) {
+                       break;
+               }
+               p += 43;
+       }
+}
+
+/****************************************************************************
+ Old style search first.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
+                                        TALLOC_CTX *mem_ctx,
+                                        union smb_search_first *io, void *private,
+                                        BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+       struct cli_request *req; 
+       
+       req = cli_request_setup(tree, SMBsearch, 2, 0);
+       if (!req) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
+       SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
+       cli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
+       cli_req_append_var_block(req, NULL, 0);
+
+       if (!cli_request_send(req) || 
+           !cli_request_receive(req)) {
+               return cli_request_destroy(req);
+       }
+
+       if (NT_STATUS_IS_OK(req->status)) {
+               io->search_first.out.count = SVAL(req->in.vwv, VWV(0)); 
+               smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private, callback);
+       }
+
+       return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
+                                       TALLOC_CTX *mem_ctx,
+                                       union smb_search_next *io, void *private,
+                                       BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+       struct cli_request *req; 
+       
+       req = cli_request_setup(tree, SMBsearch, 2, 0);
+       if (!req) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
+       SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
+       cli_req_append_ascii4(req, "", STR_TERMINATE);
+       cli_req_append_var_block(req, io->search_next.in.search_id.data, 21);
+
+       if (!cli_request_send(req) ||
+           !cli_request_receive(req)) {
+               return cli_request_destroy(req);
+       }
+
+       if (NT_STATUS_IS_OK(req->status)) {
+               io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
+               smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private, callback);
+       }
+       
+       return cli_request_destroy(req);
+}
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_blob(struct cli_tree *tree,
+                                         TALLOC_CTX *mem_ctx,  /* used to allocate output blobs */
+                                         union smb_search_first *io,
+                                         uint16 info_level,
+                                         DATA_BLOB *out_param_blob,
+                                         DATA_BLOB *out_data_blob)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_FINDFIRST;
+       NTSTATUS status;
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.data = data_blob(NULL, 0);
+       tp.in.max_param = 1024;
+       tp.in.max_data = 8192;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+       if (!tp.in.params.data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
+       SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count); 
+       SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
+       SSVAL(tp.in.params.data, 6, info_level);
+       SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
+
+       cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+                              io->t2ffirst.in.pattern, STR_TERMINATE);
+
+       status = smb_raw_trans2(tree, mem_ctx, &tp);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       out_param_blob->length = tp.out.params.length;
+       out_param_blob->data = tp.out.params.data;
+       out_data_blob->length = tp.out.data.length;
+       out_data_blob->data = tp.out.data.data;
+
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_blob(struct cli_tree *tree,
+                                        TALLOC_CTX *mem_ctx,
+                                        union smb_search_next *io,
+                                        uint16 info_level,
+                                        DATA_BLOB *out_param_blob,
+                                        DATA_BLOB *out_data_blob)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_FINDNEXT;
+       NTSTATUS status;
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.data = data_blob(NULL, 0);
+       tp.in.max_param = 1024;
+       tp.in.max_data = 8192;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+       if (!tp.in.params.data) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
+       SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);  
+       SSVAL(tp.in.params.data, 4, info_level);
+       SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
+       SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
+
+       cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+                              io->t2fnext.in.last_name,
+                              STR_TERMINATE);
+
+       status = smb_raw_trans2(tree, mem_ctx, &tp);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       out_param_blob->length = tp.out.params.length;
+       out_param_blob->data = tp.out.params.data;
+       out_data_blob->length = tp.out.data.length;
+       out_data_blob->data = tp.out.data.data;
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  parse a trans2 search response. 
+  Return the number of bytes consumed
+  return 0 for success with end of list
+  return -1 for a parse error
+*/
+static int parse_trans2_search(struct cli_tree *tree,
+                              TALLOC_CTX *mem_ctx, 
+                              enum search_level level,
+                              uint16 flags,
+                              DATA_BLOB *blob,
+                              union smb_search_data *data)
+{
+       uint_t len, ofs;
+
+       switch (level) {
+       case RAW_SEARCH_GENERIC:
+       case RAW_SEARCH_SEARCH:
+               /* handled elsewhere */
+               return -1;
+
+       case RAW_SEARCH_STANDARD:
+               if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+                       if (blob->length < 4) return -1;
+                       data->standard.resume_key = IVAL(blob->data, 0);
+                       blob->data += 4;
+                       blob->length -= 4;
+               }
+               if (blob->length < 24) return -1;
+               data->standard.create_time = make_unix_date2(blob->data + 0);
+               data->standard.access_time = make_unix_date2(blob->data + 4);
+               data->standard.write_time  = make_unix_date2(blob->data + 8);
+               data->standard.size        = IVAL(blob->data, 12);
+               data->standard.alloc_size  = IVAL(blob->data, 16);
+               data->standard.attrib      = SVAL(blob->data, 20);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->standard.name,
+                                          22, 23, STR_LEN8BIT);
+               return (len + 23 + 3) & ~3;
+
+       case RAW_SEARCH_EA_SIZE:
+               if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+                       if (blob->length < 4) return -1;
+                       data->ea_size.resume_key = IVAL(blob->data, 0);
+                       blob->data += 4;
+                       blob->length -= 4;
+               }
+               if (blob->length < 28) return -1;
+               data->ea_size.create_time = make_unix_date2(blob->data + 0);
+               data->ea_size.access_time = make_unix_date2(blob->data + 4);
+               data->ea_size.write_time  = make_unix_date2(blob->data + 8);
+               data->ea_size.size        = IVAL(blob->data, 12);
+               data->ea_size.alloc_size  = IVAL(blob->data, 16);
+               data->ea_size.attrib      = SVAL(blob->data, 20);
+               data->ea_size.ea_size     = IVAL(blob->data, 22);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->ea_size.name,
+                                          26, 27, STR_LEN8BIT | STR_NOALIGN);
+               return len + 27;
+
+       case RAW_SEARCH_DIRECTORY_INFO:
+               if (blob->length < 65) return -1;
+               ofs                                     = IVAL(blob->data,             0);
+               data->directory_info.file_index  = IVAL(blob->data,             4);
+               data->directory_info.create_time = cli_pull_nttime(blob->data,  8);
+               data->directory_info.access_time = cli_pull_nttime(blob->data, 16);
+               data->directory_info.write_time  = cli_pull_nttime(blob->data, 24);
+               data->directory_info.change_time = cli_pull_nttime(blob->data, 32);
+               data->directory_info.size        = BVAL(blob->data,            40);
+               data->directory_info.alloc_size  = BVAL(blob->data,            48);
+               data->directory_info.attrib      = IVAL(blob->data,            56);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->directory_info.name,
+                                          60, 64, 0);
+               if (ofs != 0 && ofs < 64+len) {
+                       return -1;
+               }
+               return ofs;
+
+       case RAW_SEARCH_FULL_DIRECTORY_INFO:
+               if (blob->length < 69) return -1;
+               ofs                                          = IVAL(blob->data,             0);
+               data->full_directory_info.file_index  = IVAL(blob->data,             4);
+               data->full_directory_info.create_time = cli_pull_nttime(blob->data,  8);
+               data->full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
+               data->full_directory_info.write_time  = cli_pull_nttime(blob->data, 24);
+               data->full_directory_info.change_time = cli_pull_nttime(blob->data, 32);
+               data->full_directory_info.size        = BVAL(blob->data,            40);
+               data->full_directory_info.alloc_size  = BVAL(blob->data,            48);
+               data->full_directory_info.attrib      = IVAL(blob->data,            56);
+               data->full_directory_info.ea_size     = IVAL(blob->data,            64);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->full_directory_info.name,
+                                          60, 68, 0);
+               if (ofs != 0 && ofs < 68+len) {
+                       return -1;
+               }
+               return ofs;
+
+       case RAW_SEARCH_NAME_INFO:
+               if (blob->length < 13) return -1;
+               ofs                         = IVAL(blob->data,             0);
+               data->name_info.file_index  = IVAL(blob->data,             4);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->name_info.name,
+                                          8, 12, 0);
+               if (ofs != 0 && ofs < 12+len) {
+                       return -1;
+               }
+               return ofs;
+
+
+       case RAW_SEARCH_BOTH_DIRECTORY_INFO:
+               if (blob->length < 95) return -1;
+               ofs                                          = IVAL(blob->data,             0);
+               data->both_directory_info.file_index  = IVAL(blob->data,             4);
+               data->both_directory_info.create_time = cli_pull_nttime(blob->data,  8);
+               data->both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
+               data->both_directory_info.write_time  = cli_pull_nttime(blob->data, 24);
+               data->both_directory_info.change_time = cli_pull_nttime(blob->data, 32);
+               data->both_directory_info.size        = BVAL(blob->data,            40);
+               data->both_directory_info.alloc_size  = BVAL(blob->data,            48);
+               data->both_directory_info.attrib      = IVAL(blob->data,            56);
+               data->both_directory_info.ea_size     = IVAL(blob->data,            64);
+               cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                    &data->both_directory_info.short_name,
+                                    68, 70, STR_LEN8BIT | STR_UNICODE);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->both_directory_info.name,
+                                          60, 94, 0);
+               if (ofs != 0 && ofs < 94+len) {
+                       return -1;
+               }
+               return ofs;
+
+
+       case RAW_SEARCH_261:
+               if (blob->length < 81) return -1;
+               ofs                                          = IVAL(blob->data,             0);
+               data->level_261.file_index  = IVAL(blob->data,             4);
+               data->level_261.create_time = cli_pull_nttime(blob->data,  8);
+               data->level_261.access_time = cli_pull_nttime(blob->data, 16);
+               data->level_261.write_time  = cli_pull_nttime(blob->data, 24);
+               data->level_261.change_time = cli_pull_nttime(blob->data, 32);
+               data->level_261.size        = BVAL(blob->data,            40);
+               data->level_261.alloc_size  = BVAL(blob->data,            48);
+               data->level_261.attrib      = IVAL(blob->data,            56);
+               data->level_261.ea_size     = IVAL(blob->data,            64);
+               data->level_261.unknown[0]  = IVAL(blob->data,            68);
+               data->level_261.unknown[1]  = IVAL(blob->data,            72);
+               data->level_261.unknown[2]  = IVAL(blob->data,            76);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->level_261.name,
+                                          60, 80, 0);
+               if (ofs != 0 && ofs < 80+len) {
+                       return -1;
+               }
+               return ofs;
+
+       case RAW_SEARCH_262:
+               if (blob->length < 105) return -1;
+               ofs                                          = IVAL(blob->data,             0);
+               data->level_262.file_index  = IVAL(blob->data,             4);
+               data->level_262.create_time = cli_pull_nttime(blob->data,  8);
+               data->level_262.access_time = cli_pull_nttime(blob->data, 16);
+               data->level_262.write_time  = cli_pull_nttime(blob->data, 24);
+               data->level_262.change_time = cli_pull_nttime(blob->data, 32);
+               data->level_262.size        = BVAL(blob->data,            40);
+               data->level_262.alloc_size  = BVAL(blob->data,            48);
+               data->level_262.attrib      = SVAL(blob->data,            56);
+               data->level_262.ea_size     = IVAL(blob->data,            64);
+               cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                    &data->level_262.short_name,
+                                    68, 70, STR_LEN8BIT | STR_UNICODE);
+               data->level_262.unknown[0]  = IVAL(blob->data,            94);
+               data->level_262.unknown[1]  = IVAL(blob->data,            98);
+               len = cli_blob_pull_string(tree->session, mem_ctx, blob,
+                                          &data->level_262.name,
+                                          60, 104, 0);
+               if (ofs != 0 && ofs < 104+len) {
+                       return -1;
+               }
+               return ofs;
+       }
+       
+       /* invalid level */
+       return -1;
+}
+
+/****************************************************************************
+ Trans2 search backend - process output.
+****************************************************************************/
+static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
+                                        TALLOC_CTX *mem_ctx,
+                                        enum search_level level,
+                                        uint16 flags,
+                                        int16 count,
+                                        DATA_BLOB *blob,
+                                        void *private,
+                                        BOOL (*callback)(void *private, union smb_search_data *file))
+
+{
+       int i;
+       DATA_BLOB blob2;
+
+       blob2.data = blob->data;
+       blob2.length = blob->length;
+
+       for (i=0; i < count; i++) {
+               union smb_search_data search_data;
+               uint_t len;
+
+               len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
+               if (len == -1) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               /* the callback function can tell us that no more will
+                  fit - in that case we stop, but it isn't an error */
+               if (!callback(private, &search_data)) {
+                       break;
+               }
+
+               if (len == 0) break;
+
+               blob2.data += len;
+               blob2.length -= len;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/* Implements trans2findfirst2 and old search
+ */
+NTSTATUS smb_raw_search_first(struct cli_tree *tree,
+                             TALLOC_CTX *mem_ctx,
+                             union smb_search_first *io, void *private,
+                             BOOL (*callback)(void *private, union smb_search_data *file))
+{
+       uint16 info_level = 0;
+       DATA_BLOB p_blob, d_blob;
+       NTSTATUS status;
+                       
+       if (io->generic.level == RAW_SEARCH_SEARCH) {
+               return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
+       }
+       if (io->generic.level >= RAW_SEARCH_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+       info_level = (uint16)io->generic.level;
+
+       status = smb_raw_search_first_blob(tree, mem_ctx,
+                                          io, info_level, &p_blob, &d_blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       
+       if (p_blob.length != 10) {
+               DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
+                       p_blob.length));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* process output data */
+       io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
+       io->t2ffirst.out.count = SVAL(p_blob.data, 2);
+       io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
+               
+       status = smb_raw_t2search_backend(tree, mem_ctx,
+                                         io->generic.level, 
+                                         io->t2ffirst.in.flags, io->t2ffirst.out.count,
+                                         &d_blob, private, callback);
+       
+       return status;
+}
+
+/* Implements trans2findnext2 and old smbsearch
+ */
+NTSTATUS smb_raw_search_next(struct cli_tree *tree,
+                            TALLOC_CTX *mem_ctx,
+                            union smb_search_next *io, void *private,
+                            BOOL (*callback)(void *private, union smb_search_data *file))
+{
+       uint16 info_level = 0;
+       DATA_BLOB p_blob, d_blob;
+       NTSTATUS status;
+
+       if (io->generic.level == RAW_SEARCH_SEARCH) {
+               return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
+       }
+       if (io->generic.level >= RAW_SEARCH_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+       info_level = (uint16)io->generic.level;
+
+       status = smb_raw_search_next_blob(tree, mem_ctx,
+                                         io, info_level, &p_blob, &d_blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       
+       if (p_blob.length != 8) {
+               DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
+                       p_blob.length));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* process output data */
+       io->t2fnext.out.count = SVAL(p_blob.data, 0);
+       io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
+               
+       status = smb_raw_t2search_backend(tree, mem_ctx,
+                                         io->generic.level, 
+                                         io->t2fnext.in.flags, io->t2fnext.out.count,
+                                         &d_blob, private, callback);
+       
+       return status;
+}
+
+/* 
+   Implements trans2findclose2
+ */
+NTSTATUS smb_raw_search_close(struct cli_tree *tree,
+                             union smb_search_close *io)
+{
+       struct cli_request *req;
+       
+       req = cli_request_setup(tree, SMBfindclose, 1, 0);
+       if (!req) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
+
+       if (cli_request_send(req)) {
+               cli_request_receive(req);
+       }
+
+       return cli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c
new file mode 100644 (file)
index 0000000..4044686
--- /dev/null
@@ -0,0 +1,335 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_SFILEINFO_* calls
+   Copyright (C) James Myers 2003
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static BOOL smb_raw_setinfo_backend(struct cli_tree *tree,
+                                   TALLOC_CTX *mem_ctx,
+                                   union smb_setfileinfo *parms, 
+                                   DATA_BLOB *blob)
+{      
+       uint_t len;
+
+#define NEED_BLOB(n) do { \
+         *blob = data_blob_talloc(mem_ctx, NULL, n); \
+         if (blob->data == NULL) return False; \
+        } while (0)
+
+       switch (parms->generic.level) {
+       case RAW_SFILEINFO_GENERIC:
+       case RAW_SFILEINFO_SETATTR:
+       case RAW_SFILEINFO_SETATTRE:
+               /* not handled here */
+               return False;
+
+       case RAW_SFILEINFO_STANDARD:
+               NEED_BLOB(12);
+               put_dos_date2(blob->data, 0, parms->standard.in.create_time);
+               put_dos_date2(blob->data, 4, parms->standard.in.access_time);
+               put_dos_date2(blob->data, 8, parms->standard.in.write_time);
+               return True;
+
+       case RAW_SFILEINFO_EA_SET:
+               NEED_BLOB(ea_list_size(1, &parms->ea_set.in.ea));
+               ea_put_list(blob->data, 1, &parms->ea_set.in.ea);
+               return True;
+
+       case RAW_SFILEINFO_BASIC_INFO:
+       case RAW_SFILEINFO_BASIC_INFORMATION:
+               NEED_BLOB(40);
+               cli_push_nttime(blob->data,  0, &parms->basic_info.in.create_time);
+               cli_push_nttime(blob->data,  8, &parms->basic_info.in.access_time);
+               cli_push_nttime(blob->data, 16, &parms->basic_info.in.write_time);
+               cli_push_nttime(blob->data, 24, &parms->basic_info.in.change_time);
+               SIVAL(blob->data,           32, parms->basic_info.in.attrib);
+               SIVAL(blob->data,           36, 0); /* padding */
+               return True;
+
+       case RAW_SFILEINFO_UNIX_BASIC:
+               NEED_BLOB(92);
+               SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file);
+               SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes);
+               cli_push_nttime(blob->data, 16, &parms->unix_basic.in.status_change_time);
+               cli_push_nttime(blob->data, 24, &parms->unix_basic.in.access_time);
+               cli_push_nttime(blob->data, 32, &parms->unix_basic.in.change_time);
+               SBVAL(blob->data, 40, parms->unix_basic.in.uid);
+               SBVAL(blob->data, 48, parms->unix_basic.in.gid);
+               SIVAL(blob->data, 56, parms->unix_basic.in.file_type);
+               SBVAL(blob->data, 60, parms->unix_basic.in.dev_major);
+               SBVAL(blob->data, 68, parms->unix_basic.in.dev_minor);
+               SBVAL(blob->data, 76, parms->unix_basic.in.unique_id);
+               SBVAL(blob->data, 84, parms->unix_basic.in.nlink);
+               return True;
+
+       case RAW_SFILEINFO_DISPOSITION_INFO:
+       case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+               NEED_BLOB(4);
+               SIVAL(blob->data, 0, parms->disposition_info.in.delete_on_close);
+               return True;
+
+       case RAW_SFILEINFO_ALLOCATION_INFO:
+       case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               NEED_BLOB(8);
+               SBVAL(blob->data, 0, parms->allocation_info.in.alloc_size);
+               return True;
+
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               NEED_BLOB(8);
+               SBVAL(blob->data, 0, parms->end_of_file_info.in.size);
+               return True;
+
+       case RAW_SFILEINFO_RENAME_INFORMATION:
+               NEED_BLOB(12);
+               SIVAL(blob->data, 0, parms->rename_information.in.overwrite);
+               SIVAL(blob->data, 4, parms->rename_information.in.root_fid);
+               len = cli_blob_append_string(tree->session, mem_ctx, blob,
+                                            parms->rename_information.in.new_name, 
+                                            STR_UNICODE|STR_TERMINATE);
+               SIVAL(blob->data, 8, len - 2);
+               return True;
+
+       case RAW_SFILEINFO_POSITION_INFORMATION:
+               NEED_BLOB(8);
+               SBVAL(blob->data, 0, parms->position_information.in.position);
+               return True;
+
+       case RAW_SFILEINFO_MODE_INFORMATION:
+               NEED_BLOB(4);
+               SIVAL(blob->data, 0, parms->mode_information.in.mode);
+               return True;
+       }
+
+       return False;
+}
+
+/****************************************************************************
+ Very raw set file info - takes data blob (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_setfileinfo_blob_send(struct cli_tree *tree,
+                                                        TALLOC_CTX *mem_ctx,
+                                                        uint16 fnum,
+                                                        uint16 info_level,
+                                                        DATA_BLOB *blob)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_SETFILEINFO;
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.max_param = 2;
+       tp.in.max_data = 0;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+       if (!tp.in.params.data) {
+               return NULL;
+       }
+       SSVAL(tp.in.params.data, 0, fnum);
+       SSVAL(tp.in.params.data, 2, info_level);
+       SSVAL(tp.in.params.data, 4, 0); /* reserved */
+
+       tp.in.data = *blob;
+
+       return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Very raw set path info - takes data blob
+****************************************************************************/
+static struct cli_request *smb_raw_setpathinfo_blob_send(struct cli_tree *tree,
+                                                        TALLOC_CTX *mem_ctx,
+                                                        const char *fname,
+                                                        uint16 info_level,
+                                                        DATA_BLOB *blob)
+{
+       struct smb_trans2 tp;
+       uint16 setup = TRANSACT2_SETPATHINFO;
+       
+       tp.in.max_setup = 0;
+       tp.in.flags = 0; 
+       tp.in.timeout = 0;
+       tp.in.setup_count = 1;
+       tp.in.max_param = 2;
+       tp.in.max_data = 0;
+       tp.in.setup = &setup;
+       
+       tp.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+       if (!tp.in.params.data) {
+               return NULL;
+       }
+       SSVAL(tp.in.params.data, 0, info_level);
+       SSVAL(tp.in.params.data, 2, 0);
+       cli_blob_append_string(tree->session, mem_ctx, 
+                              &tp.in.params,
+                              fname, STR_TERMINATE);
+
+       tp.in.data = *blob;
+
+       return smb_raw_trans2_send(tree, &tp);
+}
+               
+/****************************************************************************
+ Handle setattr (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_setattr_send(struct cli_tree *tree,
+                                               union smb_setfileinfo *parms)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup(tree, SMBsetatr, 8, 0);
+       if (!req) return NULL;
+       
+       SSVAL(req->out.vwv,         VWV(0), parms->setattr.in.attrib);
+       put_dos_date3(req->out.vwv, VWV(1), parms->setattr.in.write_time);
+       memset(req->out.vwv + VWV(3), 0, 10); /* reserved */
+       cli_req_append_ascii4(req, parms->setattr.file.fname, STR_TERMINATE);
+       cli_req_append_ascii4(req, "", STR_TERMINATE);
+       
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+               
+/****************************************************************************
+ Handle setattrE. (async send)
+****************************************************************************/
+static struct cli_request *smb_raw_setattrE_send(struct cli_tree *tree,
+                                                union smb_setfileinfo *parms)
+{
+       struct cli_request *req;
+
+       req = cli_request_setup(tree, SMBsetattrE, 7, 0);
+       if (!req) return NULL;
+       
+       SSVAL(req->out.vwv,         VWV(0), parms->setattre.file.fnum);
+       put_dos_date2(req->out.vwv, VWV(1), parms->setattre.in.create_time);
+       put_dos_date2(req->out.vwv, VWV(3), parms->setattre.in.access_time);
+       put_dos_date2(req->out.vwv, VWV(5), parms->setattre.in.write_time);
+       
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_setfileinfo_send(struct cli_tree *tree,
+                                            union smb_setfileinfo *parms)
+{
+       DATA_BLOB blob;
+       TALLOC_CTX *mem_ctx;
+       struct cli_request *req;
+
+       if (parms->generic.level == RAW_SFILEINFO_SETATTRE) {
+               return smb_raw_setattrE_send(tree, parms);
+       }
+       if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+               return NULL;
+       }
+
+       mem_ctx = talloc_init("setpathinfo");
+       if (!mem_ctx) return NULL;
+
+       if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+       
+       /* send request and process the output */
+       req = smb_raw_setfileinfo_blob_send(tree, 
+                                           mem_ctx,
+                                           parms->generic.file.fnum, 
+                                           parms->generic.level, 
+                                           &blob);
+
+       talloc_destroy(mem_ctx);
+       return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+NTSTATUS smb_raw_setfileinfo(struct cli_tree *tree,
+                            union smb_setfileinfo *parms)
+{
+       struct cli_request *req = smb_raw_setfileinfo_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Set path info (async send)
+****************************************************************************/
+struct cli_request *smb_raw_setpathinfo_send(struct cli_tree *tree,
+                                            union smb_setfileinfo *parms)
+{
+       DATA_BLOB blob;
+       TALLOC_CTX *mem_ctx;
+       struct cli_request *req;
+
+       if (parms->generic.level == RAW_SFILEINFO_SETATTR) {
+               return smb_raw_setattr_send(tree, parms);
+       }
+       if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+               return NULL;
+       }
+
+       mem_ctx = talloc_init("setpathinfo");
+       if (!mem_ctx) return NULL;
+
+       if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+               talloc_destroy(mem_ctx);
+               return NULL;
+       }
+
+       /* send request and process the output */
+       req = smb_raw_setpathinfo_blob_send(tree, 
+                                           mem_ctx,
+                                           parms->generic.file.fname, 
+                                           parms->generic.level,
+                                           &blob);
+
+       talloc_destroy(mem_ctx);
+       return req;
+}
+
+/****************************************************************************
+ Set path info (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_setpathinfo(struct cli_tree *tree,
+                            union smb_setfileinfo *parms)
+{
+       struct cli_request *req = smb_raw_setpathinfo_send(tree, parms);
+       return cli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c
new file mode 100644 (file)
index 0000000..508f920
--- /dev/null
@@ -0,0 +1,489 @@
+/* 
+   Unix SMB/CIFS implementation.
+   raw trans/trans2/nttrans operations
+
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+
+/*
+  check out of bounds for incoming data
+*/
+static BOOL raw_trans_oob(struct cli_request *req,
+                         uint_t offset, uint_t count)
+{
+       char *ptr;
+
+       if (count == 0) {
+               return False;
+       }
+
+       ptr = req->in.hdr + offset;
+       
+       /* be careful with wraparound! */
+       if (ptr < req->in.data ||
+           ptr >= req->in.data + req->in.data_size ||
+           count > req->in.data_size ||
+           ptr + count > req->in.data + req->in.data_size) {
+               return True;
+       }
+       return False;   
+}
+
+/****************************************************************************
+  receive a SMB trans or trans2 response allocating the necessary memory
+  ****************************************************************************/
+NTSTATUS smb_raw_trans2_recv(struct cli_request *req,
+                            TALLOC_CTX *mem_ctx,
+                            struct smb_trans2 *parms)
+{
+       int total_data=0;
+       int total_param=0;
+       char *tdata;
+       char *tparam;
+
+       parms->out.data.length = 0;
+       parms->out.data.data = NULL;
+       parms->out.params.length = 0;
+       parms->out.params.data = NULL;
+
+       if (!cli_request_receive(req)) {
+               req->status = NT_STATUS_UNSUCCESSFUL;
+               return cli_request_destroy(req);
+       }
+       
+       /*
+        * An NT RPC pipe call can return ERRDOS, ERRmoredata
+        * to a trans call. This is not an error and should not
+        * be treated as such.
+        */
+       if (NT_STATUS_IS_ERR(req->status)) {
+               return cli_request_destroy(req);
+       }
+
+       CLI_CHECK_MIN_WCT(req, 10);
+
+       /* parse out the lengths */
+       total_data = SVAL(req->in.vwv, VWV(1));
+       total_param = SVAL(req->in.vwv, VWV(0));
+
+       /* allocate it */
+       if (total_data != 0) {
+               tdata = talloc_realloc(mem_ctx, parms->out.data.data,total_data);
+               if (!tdata) {
+                       DEBUG(0,("smb_raw_receive_trans: failed to enlarge data buffer to %d bytes\n", total_data));
+                       req->status = NT_STATUS_NO_MEMORY;
+                       return cli_request_destroy(req);
+               }
+               parms->out.data.data = tdata;
+       }
+
+       if (total_param != 0) {
+               tparam = talloc_realloc(mem_ctx, parms->out.params.data,total_param);
+               if (!tparam) {
+                       DEBUG(0,("smb_raw_receive_trans: failed to enlarge param buffer to %d bytes\n", total_param));
+                       req->status = NT_STATUS_NO_MEMORY;
+                       return cli_request_destroy(req);
+               }
+               parms->out.params.data = tparam;
+       }
+
+       parms->out.setup_count = SVAL(req->in.vwv, VWV(9));
+       CLI_CHECK_WCT(req, 10 + parms->out.setup_count);
+
+       if (parms->out.setup_count > 0) {
+               int i;
+               parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count);
+               if (!parms->out.setup) {
+                       req->status = NT_STATUS_NO_MEMORY;
+                       return cli_request_destroy(req);
+               }
+               for (i=0;i<parms->out.setup_count;i++) {
+                       parms->out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
+               }
+       }
+
+       while (1)  {
+               uint16 param_count, param_ofs, param_disp;
+               uint16 data_count, data_ofs, data_disp;
+               uint16 total_data2, total_param2;
+
+               /* parse out the total lengths again - they can shrink! */
+               total_data2 = SVAL(req->in.vwv, VWV(1));
+               total_param2 = SVAL(req->in.vwv, VWV(0));
+
+               if (total_data2 > total_data ||
+                   total_param2 > total_param) {
+                       /* they must *only* shrink */
+                       DEBUG(1,("smb_raw_receive_trans: data/params expanded!\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+
+               total_data = total_data2;
+               total_param = total_param2;             
+
+               /* parse params for this lump */
+               param_count = SVAL(req->in.vwv, VWV(3));
+               param_ofs   = SVAL(req->in.vwv, VWV(4));
+               param_disp  = SVAL(req->in.vwv, VWV(5));
+
+               data_count = SVAL(req->in.vwv, VWV(6));
+               data_ofs   = SVAL(req->in.vwv, VWV(7));
+               data_disp  = SVAL(req->in.vwv, VWV(8));
+
+               if (data_count + data_disp > total_data ||
+                   param_count + param_disp > total_param) {
+                       DEBUG(1,("smb_raw_receive_trans: Buffer overflow\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+               
+               /* check the server isn't being nasty */
+               if (raw_trans_oob(req, param_ofs, param_count) ||
+                   raw_trans_oob(req, data_ofs, data_count)) {
+                       DEBUG(1,("smb_raw_receive_trans: out of bounds parameters!\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+
+               if (data_count) {
+                       memcpy(parms->out.data.data + data_disp,
+                              req->in.hdr + data_ofs, 
+                              data_count);
+               }
+
+               if (param_count) {
+                       memcpy(parms->out.params.data + param_disp,
+                              req->in.hdr + param_ofs, 
+                              param_count);
+               }
+
+               parms->out.data.length += data_count;
+               parms->out.params.length += param_count;
+
+               if (total_data <= parms->out.data.length && total_param <= parms->out.params.length)
+                       break;
+       
+               /* to receive more requests we need to mark this request as not received */
+               req->in.buffer = NULL;
+       
+               if (!cli_request_receive(req)) {
+                       req->status = NT_STATUS_UNSUCCESSFUL;
+                       return cli_request_destroy(req);
+               }
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ trans2 raw async interface - only BLOBs used in this interface.
+note that this doesn't yet support multi-part requests
+****************************************************************************/
+struct cli_request *smb_raw_trans2_send(struct cli_tree *tree,
+                                       struct smb_trans2 *parms)
+{
+       uint8 command = SMBtrans2;
+       int wct = 14 + parms->in.setup_count;
+       struct cli_request *req; 
+       char *outdata,*outparam;
+       int data_sent, param_sent;
+       int i;
+       const int padding = 3;
+       
+       req = cli_request_setup(tree, command, wct, padding);
+       if (!req) {
+               return NULL;
+       }
+       
+       /* fill in SMB parameters */
+       data_sent = parms->in.data.length;
+       param_sent = parms->in.params.length;
+       outparam = req->out.data + padding;
+       outdata = outparam + param_sent;
+
+       /* make sure we don't leak data via the padding */
+       memset(req->out.data, 0, padding);
+
+       /* primary request */
+       SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
+       SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
+       SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
+       SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
+       SSVAL(req->out.vwv,VWV(4),parms->in.max_setup);
+       SSVAL(req->out.vwv,VWV(5),parms->in.flags);
+       SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
+       SSVAL(req->out.vwv,VWV(8),0); /* reserved */
+       SSVAL(req->out.vwv,VWV(9),parms->in.params.length);
+       SSVAL(req->out.vwv,VWV(10),PTR_DIFF(outparam,req->out.hdr));
+       SSVAL(req->out.vwv,VWV(11),parms->in.data.length);
+       SSVAL(req->out.vwv,VWV(12),PTR_DIFF(outdata,req->out.hdr));
+       SSVAL(req->out.vwv,VWV(13),parms->in.setup_count);
+       for (i=0;i<parms->in.setup_count;i++)   {
+               SSVAL(req->out.vwv,VWV(14)+i*2,parms->in.setup[i]);
+       }
+       if (parms->in.params.data)      {
+               cli_req_append_blob(req, &parms->in.params);
+       }
+       if (parms->in.data.data) {
+               cli_req_append_blob(req, &parms->in.data);
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+       
+       return req;
+}
+
+/*
+  trans2 synchronous blob interface
+*/
+NTSTATUS smb_raw_trans2(struct cli_tree *tree,
+                       TALLOC_CTX *mem_ctx,
+                       struct smb_trans2 *parms)
+{
+       struct cli_request *req;
+       req = smb_raw_trans2_send(tree, parms);
+       if (!req) return NT_STATUS_UNSUCCESSFUL;
+       return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+  receive a SMB nttrans response allocating the necessary memory
+  ****************************************************************************/
+NTSTATUS smb_raw_nttrans_recv(struct cli_request *req,
+                             TALLOC_CTX *mem_ctx,
+                             struct smb_nttrans *parms)
+{
+       uint32 total_data, recvd_data=0;
+       uint32 total_param, recvd_param=0;
+
+       if (!cli_request_receive(req) ||
+           cli_request_is_error(req)) {
+               return cli_request_destroy(req);
+       }
+
+       /* sanity check */
+       if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+               DEBUG(0,("smb_raw_receive_nttrans: Expected %s response, got command 0x%02x\n",
+                        "SMBnttrans", 
+                        CVAL(req->in.hdr,HDR_COM)));
+               req->status = NT_STATUS_UNSUCCESSFUL;
+               return cli_request_destroy(req);
+       }
+
+       CLI_CHECK_MIN_WCT(req, 18);
+
+       /* parse out the lengths */
+       total_param = IVAL(req->in.vwv, 3);
+       total_data  = IVAL(req->in.vwv, 7);
+
+       parms->out.data = data_blob_talloc(mem_ctx, NULL, total_data);
+       parms->out.params = data_blob_talloc(mem_ctx, NULL, total_param);
+
+       if (parms->out.data.length != total_data ||
+           parms->out.params.length != total_param) {
+               req->status = NT_STATUS_NO_MEMORY;
+               return cli_request_destroy(req);
+       }
+
+       parms->out.setup_count = CVAL(req->in.vwv, 35);
+       CLI_CHECK_WCT(req, 18 + parms->out.setup_count);
+
+       if (parms->out.setup_count > 0) {
+               int i;
+               parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count);
+               if (!parms->out.setup) {
+                       req->status = NT_STATUS_NO_MEMORY;
+                       return cli_request_destroy(req);
+               }
+               for (i=0;i<parms->out.setup_count;i++) {
+                       parms->out.setup[i] = SVAL(req->in.vwv, VWV(18+i));
+               }
+       }
+       
+       while (recvd_data < total_data || 
+              recvd_param < total_param)  {
+               uint32 param_count, param_ofs, param_disp;
+               uint32 data_count, data_ofs, data_disp;
+               uint32 total_data2, total_param2;
+
+               /* parse out the total lengths again - they can shrink! */
+               total_param2 = IVAL(req->in.vwv, 3);
+               total_data2  = IVAL(req->in.vwv, 7);
+
+               if (total_data2 > total_data ||
+                   total_param2 > total_param) {
+                       /* they must *only* shrink */
+                       DEBUG(1,("smb_raw_receive_nttrans: data/params expanded!\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+
+               total_data = total_data2;
+               total_param = total_param2;
+               parms->out.data.length = total_data;
+               parms->out.params.length = total_param;
+
+               /* parse params for this lump */
+               param_count = IVAL(req->in.vwv, 11);
+               param_ofs   = IVAL(req->in.vwv, 15);
+               param_disp  = IVAL(req->in.vwv, 19);
+
+               data_count = IVAL(req->in.vwv, 23);
+               data_ofs   = IVAL(req->in.vwv, 27);
+               data_disp  = IVAL(req->in.vwv, 31);
+
+               if (data_count + data_disp > total_data ||
+                   param_count + param_disp > total_param) {
+                       DEBUG(1,("smb_raw_receive_nttrans: Buffer overflow\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+               
+               /* check the server isn't being nasty */
+               if (raw_trans_oob(req, param_ofs, param_count) ||
+                   raw_trans_oob(req, data_ofs, data_count)) {
+                       DEBUG(1,("smb_raw_receive_nttrans: out of bounds parameters!\n"));
+                       req->status = NT_STATUS_BUFFER_TOO_SMALL;
+                       return cli_request_destroy(req);
+               }
+
+               if (data_count) {
+                       memcpy(parms->out.data.data + data_disp,
+                              req->in.hdr + data_ofs, 
+                              data_count);
+               }
+
+               if (param_count) {
+                       memcpy(parms->out.params.data + param_disp,
+                              req->in.hdr + param_ofs, 
+                              param_count);
+               }
+
+               recvd_param += param_count;
+               recvd_data += data_count;
+
+               if (recvd_data >= total_data &&
+                   recvd_param >= total_param) {
+                       break;
+               }
+               
+               if (!cli_request_receive(req) ||
+                   cli_request_is_error(req)) {
+                       return cli_request_destroy(req);
+               }
+               
+               /* sanity check */
+               if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+                       DEBUG(0,("smb_raw_receive_nttrans: Expected nttranss, got command 0x%02x\n",
+                                CVAL(req->in.hdr, HDR_COM)));
+                       req->status = NT_STATUS_UNSUCCESSFUL;
+                       return cli_request_destroy(req);
+               }
+       }
+
+failed:
+       return cli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ nttrans raw - only BLOBs used in this interface.
+ at the moment we only handle a single primary request 
+****************************************************************************/
+struct cli_request *smb_raw_nttrans_send(struct cli_tree *tree,
+                                        struct smb_nttrans *parms)
+{
+       struct cli_request *req; 
+       char *outdata, *outparam;
+       int i;
+       int align = 0;
+
+       /* only align if there are parameters or data */
+       if (parms->in.params.length || parms->in.data.length) {
+               align = 3;
+       }
+       
+       req = cli_request_setup(tree, SMBnttrans, 
+                               19 + parms->in.setup_count, 
+                               align +
+                               parms->in.params.length +
+                               parms->in.data.length);
+       if (!req) {
+               return NULL;
+       }
+       
+       /* fill in SMB parameters */
+       outparam = req->out.data + align;
+       outdata = outparam + parms->in.params.length;
+
+       SCVAL(req->out.vwv,  0, parms->in.max_setup);
+       SSVAL(req->out.vwv,  1, 0); /* reserved */
+       SIVAL(req->out.vwv,  3, parms->in.params.length);
+       SIVAL(req->out.vwv,  7, parms->in.data.length);
+       SIVAL(req->out.vwv, 11, parms->in.max_param);
+       SIVAL(req->out.vwv, 15, parms->in.max_data);
+       SIVAL(req->out.vwv, 19, parms->in.params.length);
+       SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr));
+       SIVAL(req->out.vwv, 27, parms->in.data.length);
+       SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr));
+       SCVAL(req->out.vwv, 35, parms->in.setup_count);
+       SSVAL(req->out.vwv, 36, parms->in.function);
+       for (i=0;i<parms->in.setup_count;i++) {
+               SSVAL(req->out.vwv,VWV(19+i),parms->in.setup[i]);
+       }
+       if (parms->in.params.length) {
+               memcpy(outparam, parms->in.params.data, parms->in.params.length);
+       }
+       if (parms->in.data.length) {
+               memcpy(outparam, parms->in.data.data, parms->in.data.length);
+       }
+
+       if (!cli_request_send(req)) {
+               cli_request_destroy(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+/****************************************************************************
+  receive a SMB nttrans response allocating the necessary memory
+  ****************************************************************************/
+NTSTATUS smb_raw_nttrans(struct cli_tree *tree,
+                        TALLOC_CTX *mem_ctx,
+                        struct smb_nttrans *parms)
+{
+       struct cli_request *req;
+
+       req = smb_raw_nttrans_send(tree, parms);
+       if (!req) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return smb_raw_nttrans_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644 (file)
index 0000000..2ab61aa
--- /dev/null
@@ -0,0 +1,341 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB Signing Code
+   Copyright (C) Jeremy Allison 2002.
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+   Copyright (C) James J Myers <myersjj@samba.org> 2003
+   
+   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"
+
+struct smb_basic_signing_context {
+       DATA_BLOB mac_key;
+       uint32 next_seq_num;
+};
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static BOOL set_smb_signing_common(struct cli_transport *transport)
+{
+       if (!(transport->negotiate.sec_mode & 
+             (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+               return False;
+       }
+
+       if (transport->negotiate.sign_info.doing_signing) {
+               return False;
+       }
+       
+       if (transport->negotiate.sign_info.free_signing_context)
+               transport->negotiate.sign_info.free_signing_context(transport);
+
+       /* These calls are INCOMPATIBLE with SMB signing */
+       transport->negotiate.readbraw_supported = False;
+       transport->negotiate.writebraw_supported = False;
+       
+       return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+static BOOL set_smb_signing_real_common(struct cli_transport *transport) 
+{
+       if (transport->negotiate.sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+               DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+               transport->negotiate.sign_info.doing_signing = True;
+       }
+
+       DEBUG(5, ("SMB signing enabled!\n"));
+
+       return True;
+}
+
+static void mark_packet_signed(struct cli_request *req) 
+{
+       uint16 flags2;
+       flags2 = SVAL(req->out.hdr, HDR_FLG2);
+       flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+       SSVAL(req->out.hdr, HDR_FLG2, flags2);
+}
+
+static BOOL signing_good(struct cli_request *req, BOOL good) 
+{
+       if (good && !req->transport->negotiate.sign_info.doing_signing) {
+               req->transport->negotiate.sign_info.doing_signing = True;
+       }
+
+       if (!good) {
+               if (req->transport->negotiate.sign_info.doing_signing) {
+                       DEBUG(1, ("SMB signature check failed!\n"));
+                       return False;
+               } else {
+                       DEBUG(3, ("Server did not sign reply correctly\n"));
+                       cli_transport_free_signing_context(req->transport);
+                       return False;
+               }
+       }
+       return True;
+}      
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
+{
+       unsigned char calc_md5_mac[16];
+       struct MD5Context md5_ctx;
+       struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+
+#if 0
+       /* enable this when packet signing is preventing you working out why valgrind 
+          says that data is uninitialised */
+       file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+       req->seq_num = data->next_seq_num;
+       
+       /* some requests (eg. NTcancel) are one way, and the sequence number
+          should be increased by 1 not 2 */
+       if (req->one_way_request) {
+               data->next_seq_num += 1;
+       } else {
+               data->next_seq_num += 2;
+       }
+
+       /*
+        * Firstly put the sequence number into the first 4 bytes.
+        * and zero out the next 4 bytes.
+        */
+       SIVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num);
+       SIVAL(req->out.hdr, HDR_SS_FIELD + 4, 0);
+
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(req);
+
+       /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+       MD5Init(&md5_ctx);
+       MD5Update(&md5_ctx, data->mac_key.data, 
+                 data->mac_key.length); 
+       MD5Update(&md5_ctx, 
+                 req->out.buffer + NBT_HDR_SIZE, 
+                 req->out.size - NBT_HDR_SIZE);
+       MD5Final(calc_md5_mac, &md5_ctx);
+
+       memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8);
+
+/*     req->out.hdr[HDR_SS_FIELD+2]=0; 
+       Uncomment this to test if the remote server actually verifies signitures...*/
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_simple_check_incoming_message(struct cli_request *req)
+{
+       BOOL good;
+       unsigned char calc_md5_mac[16];
+       unsigned char server_sent_mac[8];
+       unsigned char sequence_buf[8];
+       struct MD5Context md5_ctx;
+       struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+       const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+       int i;
+       const int sign_range = 0;
+
+       /* its quite bogus to be guessing sequence numbers, but very useful
+          when debugging signing implementations */
+       for (i = 1-sign_range; i <= 1+sign_range; i++) {
+               /*
+                * Firstly put the sequence number into the first 4 bytes.
+                * and zero out the next 4 bytes.
+                */
+               SIVAL(sequence_buf, 0, req->seq_num+i);
+               SIVAL(sequence_buf, 4, 0);
+               
+               /* get a copy of the server-sent mac */
+               memcpy(server_sent_mac, &req->in.hdr[HDR_SS_FIELD], sizeof(server_sent_mac));
+               
+               /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+               MD5Init(&md5_ctx);
+               MD5Update(&md5_ctx, data->mac_key.data, 
+                         data->mac_key.length); 
+               MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD);
+               MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+               
+               MD5Update(&md5_ctx, req->in.hdr + offset_end_of_sig, 
+                         req->in.size - NBT_HDR_SIZE - (offset_end_of_sig));
+               MD5Final(calc_md5_mac, &md5_ctx);
+               
+               good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+               if (good) break;
+       }
+
+       if (good && i != 1) {
+               DEBUG(0,("SIGNING OFFSET %d\n", i));
+       }
+
+       if (!good) {
+               DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+               dump_data(5, calc_md5_mac, 8);
+               
+               DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: got SMB signature of\n"));
+               dump_data(5, server_sent_mac, 8);
+       }
+       return signing_good(req, good);
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+static void cli_transport_simple_free_signing_context(struct cli_transport *transport)
+{
+       struct smb_basic_signing_context *data = transport->negotiate.sign_info.signing_context;
+
+       data_blob_free(&data->mac_key);
+       SAFE_FREE(transport->negotiate.sign_info.signing_context);
+
+       return;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+BOOL cli_transport_simple_set_signing(struct cli_transport *transport,
+                                     const uchar user_transport_key[16], const DATA_BLOB response)
+{
+       struct smb_basic_signing_context *data;
+
+       if (!set_smb_signing_common(transport)) {
+               return False;
+       }
+
+       if (!set_smb_signing_real_common(transport)) {
+               return False;
+       }
+
+       data = smb_xmalloc(sizeof(*data));
+       transport->negotiate.sign_info.signing_context = data;
+       
+       data->mac_key = data_blob(NULL, MIN(response.length + 16, 40));
+
+       memcpy(&data->mac_key.data[0], user_transport_key, 16);
+       memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16));
+
+       /* Initialise the sequence number */
+       data->next_seq_num = 0;
+
+       transport->negotiate.sign_info.sign_outgoing_message = cli_request_simple_sign_outgoing_message;
+       transport->negotiate.sign_info.check_incoming_message = cli_request_simple_check_incoming_message;
+       transport->negotiate.sign_info.free_signing_context = cli_transport_simple_free_signing_context;
+
+       return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_null_sign_outgoing_message(struct cli_request *req)
+{
+       /* we can't zero out the sig, as we might be trying to send a
+          transport request - which is NBT-level, not SMB level and doesn't
+          have the field */
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_null_check_incoming_message(struct cli_request *req)
+{
+       return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+static void cli_null_free_signing_context(struct cli_transport *transport)
+{
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+       shut down a real signing mechanism
+*/
+BOOL cli_null_set_signing(struct cli_transport *transport)
+{
+       transport->negotiate.sign_info.signing_context = NULL;
+       
+       transport->negotiate.sign_info.sign_outgoing_message = cli_request_null_sign_outgoing_message;
+       transport->negotiate.sign_info.check_incoming_message = cli_request_null_check_incoming_message;
+       transport->negotiate.sign_info.free_signing_context = cli_null_free_signing_context;
+
+       return True;
+}
+
+
+/**
+ * Free the signing context
+ */
+void cli_transport_free_signing_context(struct cli_transport *transport) 
+{
+       if (transport->negotiate.sign_info.free_signing_context) {
+               transport->negotiate.sign_info.free_signing_context(transport);
+       }
+
+       cli_null_set_signing(transport);
+}
+
+
+/**
+ * Sign a packet with the current mechanism
+ */
+void cli_request_calculate_sign_mac(struct cli_request *req)
+{
+       req->transport->negotiate.sign_info.sign_outgoing_message(req);
+}
+
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ *         which had a back checksum, True otherwise
+ */
+BOOL cli_request_check_sign_mac(struct cli_request *req) 
+{
+       BOOL good;
+
+       if (req->in.size < (HDR_SS_FIELD + 8)) {
+               good = False;
+       } else {
+               good = req->transport->negotiate.sign_info.check_incoming_message(req);
+       }
+
+       if (!good && req->transport->negotiate.sign_info.doing_signing) {
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/libcli/unexpected.c b/source4/libcli/unexpected.c
new file mode 100644 (file)
index 0000000..c80dfa0
--- /dev/null
@@ -0,0 +1,172 @@
+/* 
+   Unix SMB/CIFS implementation.
+   handle unexpected packets
+   Copyright (C) Andrew Tridgell 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"
+
+static TDB_CONTEXT *tdbd = NULL;
+
+/* the key type used in the unexpeceted packet database */
+struct unexpected_key {
+       enum packet_type packet_type;
+       time_t timestamp;
+       int count;
+};
+
+
+
+/****************************************************************************
+ all unexpected packets are passed in here, to be stored in a unexpected
+ packet database. This allows nmblookup and other tools to receive packets
+ erroneoously sent to the wrong port by broken MS systems
+  **************************************************************************/
+void unexpected_packet(struct packet_struct *p)
+{
+       static int count;
+       TDB_DATA kbuf, dbuf;
+       struct unexpected_key key;
+       char buf[1024];
+       int len=0;
+       TALLOC_CTX *mem_ctx;
+
+       if (!tdbd) {
+               mem_ctx = talloc_init("receive_unexpected");
+               if (!mem_ctx) return;
+               tdbd = tdb_open_log(lock_path(mem_ctx, "unexpected.tdb"), 0, 
+                              TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+                              O_RDWR | O_CREAT, 0644);
+               talloc_destroy(mem_ctx);
+               if (!tdbd) {
+                       DEBUG(0,("Failed to open unexpected.tdb\n"));
+                       return;
+               }
+       }
+
+       memset(buf,'\0',sizeof(buf));
+       
+       len = build_packet(buf, p);
+
+       key.packet_type = p->packet_type;
+       key.timestamp = p->timestamp;
+       key.count = count++;
+
+       kbuf.dptr = (char *)&key;
+       kbuf.dsize = sizeof(key);
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
+
+       tdb_store(tdbd, kbuf, dbuf, TDB_REPLACE);
+}
+
+
+static time_t lastt;
+
+/****************************************************************************
+delete the record if it is too old
+  **************************************************************************/
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct unexpected_key key;
+
+       memcpy(&key, kbuf.dptr, sizeof(key));
+
+       if (lastt - key.timestamp > NMBD_UNEXPECTED_TIMEOUT) {
+               tdb_delete(ttdb, kbuf);
+       }
+
+       return 0;
+}
+
+
+/****************************************************************************
+delete all old unexpected packets
+  **************************************************************************/
+void clear_unexpected(time_t t)
+{
+       if (!tdbd) return;
+
+       if ((lastt != 0) && (t < lastt + NMBD_UNEXPECTED_TIMEOUT))
+               return;
+
+       lastt = t;
+
+       tdb_traverse(tdbd, traverse_fn, NULL);
+}
+
+
+static struct packet_struct *matched_packet;
+static int match_id;
+static enum packet_type match_type;
+static const char *match_name;
+
+/****************************************************************************
+tdb traversal fn to find a matching 137 packet
+  **************************************************************************/
+static int traverse_match(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct unexpected_key key;
+       struct packet_struct *p;
+
+       memcpy(&key, kbuf.dptr, sizeof(key));
+
+       if (key.packet_type != match_type) return 0;
+
+       p = parse_packet(dbuf.dptr, dbuf.dsize, match_type);
+
+       if ((match_type == NMB_PACKET && 
+            p->packet.nmb.header.name_trn_id == match_id) ||
+           (match_type == DGRAM_PACKET && 
+            match_mailslot_name(p, match_name))) {
+               matched_packet = p;
+               return -1;
+       }
+
+       free_packet(p);
+
+       return 0;
+}
+
+
+/****************************************************************************
+check for a particular packet in the unexpected packet queue
+  **************************************************************************/
+struct packet_struct *receive_unexpected(enum packet_type packet_type, int id, 
+                                        const char *mailslot_name)
+{
+       TDB_CONTEXT *tdb2;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("receive_unexpected");
+       if (!mem_ctx) return NULL;
+       tdb2 = tdb_open_log(lock_path(mem_ctx, "unexpected.tdb"), 0, 0, O_RDONLY, 0);
+       talloc_destroy(mem_ctx);
+       if (!tdb2) return NULL;
+
+       matched_packet = NULL;
+       match_id = id;
+       match_type = packet_type;
+       match_name = mailslot_name;
+
+       tdb_traverse(tdb2, traverse_match, NULL);
+
+       tdb_close(tdb2);
+
+       return matched_packet;
+}
diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c
new file mode 100644 (file)
index 0000000..09d4fbb
--- /dev/null
@@ -0,0 +1,428 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple SPNEGO routines
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+/* free an asn1 structure */
+void asn1_free(ASN1_DATA *data)
+{
+       SAFE_FREE(data->data);
+}
+
+/* write to the ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
+{
+       if (data->has_error) return False;
+       if (data->length < data->ofs+len) {
+               uint8 *newp;
+               newp = Realloc(data->data, data->ofs+len);
+               if (!newp) {
+                       SAFE_FREE(data->data);
+                       data->has_error = True;
+                       return False;
+               }
+               data->data = newp;
+               data->length = data->ofs+len;
+       }
+       memcpy(data->data + data->ofs, p, len);
+       data->ofs += len;
+       return True;
+}
+
+/* useful fn for writing a uint8 */
+BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
+{
+       return asn1_write(data, &v, 1);
+}
+
+/* push a tag onto the asn1 data buffer. Used for nested structures */
+BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
+{
+       struct nesting *nesting;
+
+       asn1_write_uint8(data, tag);
+       nesting = (struct nesting *)malloc(sizeof(struct nesting));
+       if (!nesting) {
+               data->has_error = True;
+               return False;
+       }
+
+       nesting->start = data->ofs;
+       nesting->next = data->nesting;
+       data->nesting = nesting;
+       return asn1_write_uint8(data, 0xff);
+}
+
+/* pop a tag */
+BOOL asn1_pop_tag(ASN1_DATA *data)
+{
+       struct nesting *nesting;
+       size_t len;
+
+       nesting = data->nesting;
+
+       if (!nesting) {
+               data->has_error = True;
+               return False;
+       }
+       len = data->ofs - (nesting->start+1);
+       /* yes, this is ugly. We don't know in advance how many bytes the length
+          of a tag will take, so we assumed 1 byte. If we were wrong then we 
+          need to correct our mistake */
+       if (len > 255) {
+               data->data[nesting->start] = 0x82;
+               if (!asn1_write_uint8(data, 0)) return False;
+               if (!asn1_write_uint8(data, 0)) return False;
+               memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
+               data->data[nesting->start+1] = len>>8;
+               data->data[nesting->start+2] = len&0xff;
+       } else if (len > 127) {
+               data->data[nesting->start] = 0x81;
+               if (!asn1_write_uint8(data, 0)) return False;
+               memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
+               data->data[nesting->start+1] = len;
+       } else {
+               data->data[nesting->start] = len;
+       }
+
+       data->nesting = nesting->next;
+       free(nesting);
+       return True;
+}
+
+
+/* write an integer */
+BOOL asn1_write_Integer(ASN1_DATA *data, int i)
+{
+       if (!asn1_push_tag(data, ASN1_INTEGER)) return False;
+       do {
+               asn1_write_uint8(data, i);
+               i = i >> 8;
+       } while (i);
+       return asn1_pop_tag(data);
+}
+
+/* write an object ID to a ASN1 buffer */
+BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
+{
+       unsigned v, v2;
+       const char *p = (const char *)OID;
+       char *newp;
+
+       if (!asn1_push_tag(data, ASN1_OID))
+               return False;
+       v = strtol(p, &newp, 10);
+       p = newp;
+       v2 = strtol(p, &newp, 10);
+       p = newp;
+       if (!asn1_write_uint8(data, 40*v + v2))
+               return False;
+
+       while (*p) {
+               v = strtol(p, &newp, 10);
+               p = newp;
+               if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
+               if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
+               if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
+               if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
+               if (!asn1_write_uint8(data, v&0x7f))
+                       return False;
+       }
+       return asn1_pop_tag(data);
+}
+
+/* write an octet string */
+BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
+{
+       asn1_push_tag(data, ASN1_OCTET_STRING);
+       asn1_write(data, p, length);
+       asn1_pop_tag(data);
+       return !data->has_error;
+}
+
+/* write a general string */
+BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
+{
+       asn1_push_tag(data, ASN1_GENERAL_STRING);
+       asn1_write(data, s, strlen(s));
+       asn1_pop_tag(data);
+       return !data->has_error;
+}
+
+/* write a BOOLEAN */
+BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+       asn1_write_uint8(data, ASN1_BOOLEAN);
+       asn1_write_uint8(data, v);
+       return !data->has_error;
+}
+
+/* write a BOOLEAN - hmm, I suspect this one is the correct one, and the 
+   above boolean is bogus. Need to check */
+BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
+{
+       asn1_push_tag(data, ASN1_BOOLEAN);
+       asn1_write_uint8(data, v);
+       asn1_pop_tag(data);
+       return !data->has_error;
+}
+
+/* check a BOOLEAN */
+BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+       uint8 b = 0;
+
+       asn1_read_uint8(data, &b);
+       if (b != ASN1_BOOLEAN) {
+               data->has_error = True;
+               return False;
+       }
+       asn1_read_uint8(data, &b);
+       if (b != v) {
+               data->has_error = True;
+               return False;
+       }
+       return !data->has_error;
+}
+
+
+/* load a ASN1_DATA structure with a lump of data, ready to be parsed */
+BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
+{
+       ZERO_STRUCTP(data);
+       data->data = memdup(blob.data, blob.length);
+       if (!data->data) {
+               data->has_error = True;
+               return False;
+       }
+       data->length = blob.length;
+       return True;
+}
+
+/* read from a ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_read(ASN1_DATA *data, void *p, int len)
+{
+       if (data->ofs + len > data->length) {
+               data->has_error = True;
+               return False;
+       }
+       memcpy(p, data->data + data->ofs, len);
+       data->ofs += len;
+       return True;
+}
+
+/* read a uint8 from a ASN1 buffer */
+BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
+{
+       return asn1_read(data, v, 1);
+}
+
+/* start reading a nested asn1 structure */
+BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
+{
+       uint8 b;
+       struct nesting *nesting;
+       
+       if (!asn1_read_uint8(data, &b))
+               return False;
+
+       if (b != tag) {
+               data->has_error = True;
+               return False;
+       }
+       nesting = (struct nesting *)malloc(sizeof(struct nesting));
+       if (!nesting) {
+               data->has_error = True;
+               return False;
+       }
+
+       if (!asn1_read_uint8(data, &b)) {
+               return False;
+       }
+
+       if (b & 0x80) {
+               int n = b & 0x7f;
+               if (!asn1_read_uint8(data, &b))
+                       return False;
+               nesting->taglen = b;
+               while (n > 1) {
+                       if (!asn1_read_uint8(data, &b)) 
+                               return False;
+                       nesting->taglen = (nesting->taglen << 8) | b;
+                       n--;
+               }
+       } else {
+               nesting->taglen = b;
+       }
+       nesting->start = data->ofs;
+       nesting->next = data->nesting;
+       data->nesting = nesting;
+       return !data->has_error;
+}
+
+
+/* stop reading a tag */
+BOOL asn1_end_tag(ASN1_DATA *data)
+{
+       struct nesting *nesting;
+
+       /* make sure we read it all */
+       if (asn1_tag_remaining(data) != 0) {
+               data->has_error = True;
+               return False;
+       }
+
+       nesting = data->nesting;
+
+       if (!nesting) {
+               data->has_error = True;
+               return False;
+       }
+
+       data->nesting = nesting->next;
+       free(nesting);
+       return True;
+}
+
+/* work out how many bytes are left in this nested tag */
+int asn1_tag_remaining(ASN1_DATA *data)
+{
+       if (!data->nesting) {
+               data->has_error = True;
+               return -1;
+       }
+       return data->nesting->taglen - (data->ofs - data->nesting->start);
+}
+
+/* read an object ID from a ASN1 buffer */
+BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
+{
+       uint8 b;
+       pstring oid;
+       fstring el;
+
+       if (!asn1_start_tag(data, ASN1_OID)) return False;
+       asn1_read_uint8(data, &b);
+
+       oid[0] = 0;
+       snprintf(el, sizeof(el), "%u",  b/40);
+       pstrcat(oid, el);
+       snprintf(el, sizeof(el), " %u",  b%40);
+       pstrcat(oid, el);
+
+       while (asn1_tag_remaining(data) > 0) {
+               unsigned v = 0;
+               do {
+                       asn1_read_uint8(data, &b);
+                       v = (v<<7) | (b&0x7f);
+               } while (!data->has_error && b & 0x80);
+               snprintf(el, sizeof(el), " %u",  v);
+               pstrcat(oid, el);
+       }
+
+       asn1_end_tag(data);
+
+       *OID = strdup(oid);
+
+       return !data->has_error;
+}
+
+/* check that the next object ID is correct */
+BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
+{
+       char *id;
+
+       if (!asn1_read_OID(data, &id)) return False;
+
+       if (strcmp(id, OID) != 0) {
+               data->has_error = True;
+               return False;
+       }
+       free(id);
+       return True;
+}
+
+/* read a GeneralString from a ASN1 buffer */
+BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
+{
+       int len;
+       if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
+       len = asn1_tag_remaining(data);
+       *s = malloc(len+1);
+       if (! *s) {
+               data->has_error = True;
+               return False;
+       }
+       asn1_read(data, *s, len);
+       (*s)[len] = 0;
+       asn1_end_tag(data);
+       return !data->has_error;
+}
+
+/* read a octet string blob */
+BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
+{
+       int len;
+       ZERO_STRUCTP(blob);
+       if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
+       len = asn1_tag_remaining(data);
+       *blob = data_blob(NULL, len);
+       asn1_read(data, blob->data, len);
+       asn1_end_tag(data);
+       return !data->has_error;
+}
+
+/* read an interger */
+BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
+{
+       uint8 b;
+       *i = 0;
+       
+       if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
+       while (asn1_tag_remaining(data)>0) {
+               asn1_read_uint8(data, &b);
+               *i = (*i << 8) + b;
+       }
+       return asn1_end_tag(data);      
+       
+}
+
+/* check a enumarted value is correct */
+BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
+{
+       uint8 b;
+       if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
+       asn1_read_uint8(data, &b);
+       asn1_end_tag(data);
+
+       if (v != b)
+               data->has_error = False;
+
+       return !data->has_error;
+}
+
+/* write an enumarted value to the stream */
+BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
+{
+       if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
+       asn1_write_uint8(data, v);
+       asn1_pop_tag(data);
+       return !data->has_error;
+}
diff --git a/source4/libcli/util/clierror.c b/source4/libcli/util/clierror.c
new file mode 100644 (file)
index 0000000..4fa1daa
--- /dev/null
@@ -0,0 +1,99 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client error handling routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) James Myers 2003
+   
+   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"
+
+
+/***************************************************************************
+ Return an error message from the last response
+****************************************************************************/
+const char *cli_errstr(struct cli_state *cli)
+{   
+       switch (cli->transport->error.etype) {
+       case ETYPE_DOS:
+               return dos_errstr(cli->transport->error.e.dos.eclass, 
+                                 cli->transport->error.e.dos.ecode);
+       case ETYPE_NT:
+               return nt_errstr(cli->transport->error.e.nt_status);
+
+       case ETYPE_SOCKET:
+               return "socket_error";
+
+       case ETYPE_NBT:
+               return "nbt_error";
+
+       case ETYPE_NONE:
+               return "no_error";
+       }
+       return NULL;
+}
+
+
+/* Return the 32-bit NT status code from the last packet */
+NTSTATUS cli_nt_error(struct cli_state *cli)
+{
+       switch (cli->transport->error.etype) {
+       case ETYPE_NT:
+               return cli->transport->error.e.nt_status;
+
+       case ETYPE_DOS:
+               return dos_to_ntstatus(cli->transport->error.e.dos.eclass,
+                                      cli->transport->error.e.dos.ecode);
+       case ETYPE_SOCKET:
+               return NT_STATUS_UNSUCCESSFUL;
+
+       case ETYPE_NBT:
+               return NT_STATUS_UNSUCCESSFUL;
+
+       case ETYPE_NONE:
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/* Return the DOS error from the last packet - an error class and an error
+   code. */
+void cli_dos_error(struct cli_state *cli, uint8 *eclass, uint32 *ecode)
+{
+       if (cli->transport->error.etype == ETYPE_DOS) {
+               ntstatus_to_dos(cli->transport->error.e.nt_status, 
+                               eclass, ecode);
+               return;
+       }
+
+       if (eclass) *eclass = cli->transport->error.e.dos.eclass;
+       if (ecode)  *ecode  = cli->transport->error.e.dos.ecode;
+}
+
+
+/* Return true if the last packet was an error */
+BOOL cli_is_error(struct cli_state *cli)
+{
+       return NT_STATUS_IS_ERR(cli_nt_error(cli));
+}
+
+/* Return true if the last error was a DOS error */
+BOOL cli_is_dos_error(struct cli_state *cli)
+{
+       return cli->transport->error.etype == ETYPE_DOS;
+}
diff --git a/source4/libcli/util/cliutil.c b/source4/libcli/util/cliutil.c
new file mode 100644 (file)
index 0000000..47f9499
--- /dev/null
@@ -0,0 +1,110 @@
+/* 
+   Unix SMB/CIFS implementation.
+   client utility routines
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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"
+/*******************************************************************
+ Functions nicked from lib/util.c needed by client.
+*******************************************************************/
+
+/* a default finfo structure to ensure all fields are sensible */
+file_info def_finfo = {-1,0,0,0,0,0,0,"",""};
+
+/*******************************************************************
+ A wrapper that handles case sensitivity and the special handling
+ of the ".." name.
+*******************************************************************/
+
+BOOL mask_match(struct cli_state *cli, const char *string, char *pattern, BOOL is_case_sensitive)
+{
+       fstring p2, s2;
+
+       if (strcmp(string,"..") == 0)
+               string = ".";
+       if (strcmp(pattern,".") == 0)
+               return False;
+       
+       if (is_case_sensitive)
+               return ms_fnmatch(pattern, string, 
+                                 cli->transport->negotiate.protocol) == 0;
+
+       fstrcpy(p2, pattern);
+       fstrcpy(s2, string);
+       strlower(p2); 
+       strlower(s2);
+       return ms_fnmatch(p2, s2, cli->transport->negotiate.protocol) == 0;
+}
+
+/****************************************************************************
+ Put up a yes/no prompt.
+****************************************************************************/
+
+BOOL yesno(char *p)
+{
+       pstring ans;
+       printf("%s",p);
+
+       if (!fgets(ans,sizeof(ans)-1,stdin))
+               return(False);
+
+       if (*ans == 'y' || *ans == 'Y')
+               return(True);
+
+       return(False);
+}
+
+/*******************************************************************
+  A readdir wrapper which just returns the file name.
+ ********************************************************************/
+
+const char *readdirname(DIR *p)
+{
+       SMB_STRUCT_DIRENT *ptr;
+       char *dname;
+
+       if (!p)
+               return(NULL);
+  
+       ptr = (SMB_STRUCT_DIRENT *)sys_readdir(p);
+       if (!ptr)
+               return(NULL);
+
+       dname = ptr->d_name;
+
+#ifdef NEXT2
+       if (telldir(p) < 0)
+               return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+       /* using /usr/ucb/cc is BAD */
+       dname = dname - 2;
+#endif
+
+       {
+               static pstring buf;
+               int len = NAMLEN(ptr);
+               memcpy(buf, dname, len);
+               buf[len] = 0;
+               dname = buf;
+       }
+
+       return(dname);
+}
diff --git a/source4/libcli/util/credentials.c b/source4/libcli/util/credentials.c
new file mode 100644 (file)
index 0000000..0d521ba
--- /dev/null
@@ -0,0 +1,215 @@
+/* 
+   Unix SMB/CIFS implementation.
+   code to manipulate domain credentials
+   Copyright (C) Andrew Tridgell 1997-1998
+   
+   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"
+
+/****************************************************************************
+represent a credential as a string
+****************************************************************************/
+char *credstr(const uchar *cred)
+{
+       static fstring buf;
+       slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
+               cred[0], cred[1], cred[2], cred[3], 
+               cred[4], cred[5], cred[6], cred[7]);
+       return buf;
+}
+
+
+/****************************************************************************
+  setup the session key. 
+Input: 8 byte challenge block
+       8 byte server challenge block
+      16 byte md4 encrypted password
+Output:
+      8 byte session key
+****************************************************************************/
+void cred_session_key(const DOM_CHAL *clnt_chal, const DOM_CHAL *srv_chal, const uchar *pass, 
+                     uchar session_key[8])
+{
+       uint32 sum[2];
+       unsigned char sum2[8];
+
+       sum[0] = IVAL(clnt_chal->data, 0) + IVAL(srv_chal->data, 0);
+       sum[1] = IVAL(clnt_chal->data, 4) + IVAL(srv_chal->data, 4);
+
+       SIVAL(sum2,0,sum[0]);
+       SIVAL(sum2,4,sum[1]);
+
+       cred_hash1(session_key, sum2, pass);
+
+       /* debug output */
+       DEBUG(4,("cred_session_key\n"));
+
+       DEBUG(5,("      clnt_chal: %s\n", credstr(clnt_chal->data)));
+       DEBUG(5,("      srv_chal : %s\n", credstr(srv_chal->data)));
+       DEBUG(5,("      clnt+srv : %s\n", credstr(sum2)));
+       DEBUG(5,("      sess_key : %s\n", credstr(session_key)));
+}
+
+
+/****************************************************************************
+create a credential
+
+Input:
+      8 byte sesssion key
+      8 byte stored credential
+      4 byte timestamp
+
+Output:
+      8 byte credential
+****************************************************************************/
+void cred_create(uchar session_key[8], DOM_CHAL *stor_cred, UTIME timestamp, 
+                DOM_CHAL *cred)
+{
+       DOM_CHAL time_cred;
+
+       SIVAL(time_cred.data, 0, IVAL(stor_cred->data, 0) + timestamp.time);
+       SIVAL(time_cred.data, 4, IVAL(stor_cred->data, 4));
+
+       cred_hash2(cred->data, time_cred.data, session_key);
+
+       /* debug output*/
+       DEBUG(4,("cred_create\n"));
+
+       DEBUG(5,("      sess_key : %s\n", credstr(session_key)));
+       DEBUG(5,("      stor_cred: %s\n", credstr(stor_cred->data)));
+       DEBUG(5,("      timestamp: %x\n"    , timestamp.time));
+       DEBUG(5,("      timecred : %s\n", credstr(time_cred.data)));
+       DEBUG(5,("      calc_cred: %s\n", credstr(cred->data)));
+}
+
+
+/****************************************************************************
+  check a supplied credential
+
+Input:
+      8 byte received credential
+      8 byte sesssion key
+      8 byte stored credential
+      4 byte timestamp
+
+Output:
+      returns 1 if computed credential matches received credential
+      returns 0 otherwise
+****************************************************************************/
+int cred_assert(DOM_CHAL *cred, uchar session_key[8], DOM_CHAL *stored_cred,
+               UTIME timestamp)
+{
+       DOM_CHAL cred2;
+
+       cred_create(session_key, stored_cred, timestamp, &cred2);
+
+       /* debug output*/
+       DEBUG(4,("cred_assert\n"));
+
+       DEBUG(5,("      challenge : %s\n", credstr(cred->data)));
+       DEBUG(5,("      calculated: %s\n", credstr(cred2.data)));
+
+       if (memcmp(cred->data, cred2.data, 8) == 0)
+       {
+               DEBUG(5, ("credentials check ok\n"));
+               return True;
+       }
+       else
+       {
+               DEBUG(5, ("credentials check wrong\n"));
+               return False;
+       }
+}
+
+
+/****************************************************************************
+  checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL clnt_deal_with_creds(uchar sess_key[8],
+                         DOM_CRED *sto_clnt_cred, DOM_CRED *rcv_srv_cred)
+{
+       UTIME new_clnt_time;
+       uint32 new_cred;
+
+       DEBUG(5,("clnt_deal_with_creds: %d\n", __LINE__));
+
+       /* increment client time by one second */
+       new_clnt_time.time = sto_clnt_cred->timestamp.time + 1;
+
+       /* check that the received server credentials are valid */
+       if (!cred_assert(&rcv_srv_cred->challenge, sess_key,
+                        &sto_clnt_cred->challenge, new_clnt_time))
+       {
+               return False;
+       }
+
+       /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+       new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+       new_cred += new_clnt_time.time;
+
+       /* store new seed in client credentials */
+       SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+       DEBUG(5,("      new clnt cred: %s\n", credstr(sto_clnt_cred->challenge.data)));
+       return True;
+}
+
+
+/****************************************************************************
+  checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL deal_with_creds(uchar sess_key[8],
+                    DOM_CRED *sto_clnt_cred, 
+                    DOM_CRED *rcv_clnt_cred, DOM_CRED *rtn_srv_cred)
+{
+       UTIME new_clnt_time;
+       uint32 new_cred;
+
+       DEBUG(5,("deal_with_creds: %d\n", __LINE__));
+
+       /* check that the received client credentials are valid */
+       if (!cred_assert(&rcv_clnt_cred->challenge, sess_key,
+                    &sto_clnt_cred->challenge, rcv_clnt_cred->timestamp))
+       {
+               return False;
+       }
+
+       /* increment client time by one second */
+       new_clnt_time.time = rcv_clnt_cred->timestamp.time + 1;
+
+       /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+       new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+       new_cred += new_clnt_time.time;
+
+       DEBUG(5,("deal_with_creds: new_cred[0]=%x\n", new_cred));
+
+       /* doesn't matter that server time is 0 */
+       rtn_srv_cred->timestamp.time = 0;
+
+       DEBUG(5,("deal_with_creds: new_clnt_time=%x\n", new_clnt_time.time));
+
+       /* create return credentials for inclusion in the reply */
+       cred_create(sess_key, &sto_clnt_cred->challenge, new_clnt_time,
+                   &rtn_srv_cred->challenge);
+       
+       DEBUG(5,("deal_with_creds: clnt_cred=%s\n", credstr(sto_clnt_cred->challenge.data)));
+
+       /* store new seed in client credentials */
+       SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+       return True;
+}
diff --git a/source4/libcli/util/doserr.c b/source4/libcli/util/doserr.c
new file mode 100644 (file)
index 0000000..28bad61
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  DOS error routines
+ *  Copyright (C) Tim Potter 2002.
+ *  
+ *  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.
+ */
+
+/* DOS error codes.  please read doserr.h */
+
+#include "includes.h"
+
+typedef const struct
+{
+       const char *dos_errstr;
+       WERROR werror;
+} werror_code_struct;
+
+werror_code_struct dos_errs[] =
+{
+       { "WERR_OK", WERR_OK },
+       { "WERR_BADFILE", WERR_BADFILE },
+       { "WERR_ACCESS_DENIED", WERR_ACCESS_DENIED },
+       { "WERR_BADFID", WERR_BADFID },
+       { "WERR_BADFUNC", WERR_BADFUNC },
+       { "WERR_INSUFFICIENT_BUFFER", WERR_INSUFFICIENT_BUFFER },
+       { "WERR_NO_SUCH_SHARE", WERR_NO_SUCH_SHARE },
+       { "WERR_ALREADY_EXISTS", WERR_ALREADY_EXISTS },
+       { "WERR_INVALID_PARAM", WERR_INVALID_PARAM },
+       { "WERR_NOT_SUPPORTED", WERR_NOT_SUPPORTED },
+       { "WERR_BAD_PASSWORD", WERR_BAD_PASSWORD },
+       { "WERR_NOMEM", WERR_NOMEM },
+       { "WERR_INVALID_NAME", WERR_INVALID_NAME },
+       { "WERR_UNKNOWN_LEVEL", WERR_UNKNOWN_LEVEL },
+       { "WERR_OBJECT_PATH_INVALID", WERR_OBJECT_PATH_INVALID },
+       { "WERR_NO_MORE_ITEMS", WERR_NO_MORE_ITEMS },
+       { "WERR_MORE_DATA", WERR_MORE_DATA },
+       { "WERR_UNKNOWN_PRINTER_DRIVER", WERR_UNKNOWN_PRINTER_DRIVER },
+       { "WERR_INVALID_PRINTER_NAME", WERR_INVALID_PRINTER_NAME },
+       { "WERR_PRINTER_ALREADY_EXISTS", WERR_PRINTER_ALREADY_EXISTS },
+       { "WERR_INVALID_DATATYPE", WERR_INVALID_DATATYPE },
+       { "WERR_INVALID_ENVIRONMENT", WERR_INVALID_ENVIRONMENT },
+       { "WERR_INVALID_FORM_NAME", WERR_INVALID_FORM_NAME },
+       { "WERR_INVALID_FORM_SIZE", WERR_INVALID_FORM_SIZE },
+       { "WERR_BUF_TOO_SMALL", WERR_BUF_TOO_SMALL },
+       { "WERR_JOB_NOT_FOUND", WERR_JOB_NOT_FOUND },
+       { "WERR_DEST_NOT_FOUND", WERR_DEST_NOT_FOUND },
+       { "WERR_NOT_LOCAL_DOMAIN", WERR_NOT_LOCAL_DOMAIN },
+       { "WERR_PRINTER_DRIVER_IN_USE", WERR_PRINTER_DRIVER_IN_USE },
+       { "WERR_STATUS_MORE_ENTRIES  ", WERR_STATUS_MORE_ENTRIES },
+       { "WERR_DFS_NO_SUCH_VOL", WERR_DFS_NO_SUCH_VOL },
+       { "WERR_DFS_NO_SUCH_SHARE", WERR_DFS_NO_SUCH_SHARE },
+       { "WERR_DFS_NO_SUCH_SERVER", WERR_DFS_NO_SUCH_SERVER },
+       { "WERR_DFS_INTERNAL_ERROR", WERR_DFS_INTERNAL_ERROR },
+       { "WERR_DFS_CANT_CREATE_JUNCT", WERR_DFS_CANT_CREATE_JUNCT },
+       { "WERR_INVALID_SECURITY_DESCRIPTOR", WERR_INVALID_SECURITY_DESCRIPTOR },
+       { "WERR_INVALID_OWNER", WERR_INVALID_OWNER },
+       { NULL, W_ERROR(0) }
+};
+
+/*****************************************************************************
+ returns a windows error message.  not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *win_errstr(WERROR werror)
+{
+        static pstring msg;
+        int idx = 0;
+
+       slprintf(msg, sizeof(msg), "DOS code 0x%08x", W_ERROR_V(werror));
+
+       while (dos_errs[idx].dos_errstr != NULL) {
+               if (W_ERROR_V(dos_errs[idx].werror) == 
+                    W_ERROR_V(werror))
+                        return dos_errs[idx].dos_errstr;
+               idx++;
+       }
+
+        return msg;
+}
diff --git a/source4/libcli/util/errormap.c b/source4/libcli/util/errormap.c
new file mode 100644 (file)
index 0000000..a257c2d
--- /dev/null
@@ -0,0 +1,1546 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  error mapping functions
+ *  Copyright (C) Andrew Tridgell 2001
+ *  Copyright (C) Andrew Bartlett 2001
+ *  Copyright (C) Tim Potter 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"
+
+/* This map was extracted by the ERRMAPEXTRACT smbtorture command. 
+   The setup was a Samba HEAD (2002-01-03) PDC and an Win2k member 
+   workstation.  The PDC was modified (by using the 'name_to_nt_status'
+   authentication module) to convert the username (in hex) into the
+   corresponding NTSTATUS error return. 
+
+   By opening two nbt sessions to the Win2k workstation, one negotiating
+   DOS and one negotiating NT errors it was possible to extract the
+   error mapping.  (Because the server only supplies NT errors, the 
+   NT4 workstation had to use its own error tables to convert these
+   to dos errors). 
+
+   Some errors show up as 'squashed' because the NT error connection
+   got back a different error to the one it sent, so a mapping could
+   not be determined (a guess has been made in this case, to map the
+   error as squashed).  This is done mainly to prevent users from getting
+   NT_STATUS_WRONG_PASSWORD and NT_STATUS_NO_SUCH_USER errors (they get
+   NT_STATUS_LOGON_FAILURE instead.
+
+   -- abartlet (2002-01-03)
+*/
+
+/* NT status -> dos error map */
+static const struct {
+       uint8 dos_class;
+       uint32 dos_code;
+       NTSTATUS ntstatus;
+} ntstatus_to_dos_map[] = {
+       {ERRDOS,        ERRgeneral,     NT_STATUS_UNSUCCESSFUL},
+       {ERRDOS,        ERRbadfunc,     NT_STATUS_NOT_IMPLEMENTED},
+       {ERRDOS,        87,     NT_STATUS_INVALID_INFO_CLASS},
+       {ERRDOS,        24,     NT_STATUS_INFO_LENGTH_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ACCESS_VIOLATION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IN_PAGE_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PAGEFILE_QUOTA},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_INVALID_HANDLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_INITIAL_STACK},
+       {ERRDOS,        193,    NT_STATUS_BAD_INITIAL_PC},
+       {ERRDOS,        87,     NT_STATUS_INVALID_CID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TIMER_NOT_CANCELED},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER},
+       {ERRDOS,        ERRbadfile,     NT_STATUS_NO_SUCH_DEVICE},
+       {ERRDOS,        ERRbadfile,     NT_STATUS_NO_SUCH_FILE},
+       {ERRDOS,        ERRbadfunc,     NT_STATUS_INVALID_DEVICE_REQUEST},
+       {ERRDOS,        38,     NT_STATUS_END_OF_FILE},
+       {ERRDOS,        34,     NT_STATUS_WRONG_VOLUME},
+       {ERRDOS,        21,     NT_STATUS_NO_MEDIA_IN_DEVICE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNRECOGNIZED_MEDIA},
+       {ERRDOS,        27,     NT_STATUS_NONEXISTENT_SECTOR},
+/** Session setup succeeded.  This shouldn't happen...*/
+/** Session setup succeeded.  This shouldn't happen...*/
+/** NT error on DOS connection! (NT_STATUS_OK) */
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK 
+        during the session setup }
+*/
+#if 0
+       {SUCCESS,       0,      NT_STATUS_OK},
+#endif
+       {ERRDOS,        ERRnomem,       NT_STATUS_NO_MEMORY},
+       {ERRDOS,        487,    NT_STATUS_CONFLICTING_ADDRESSES},
+       {ERRDOS,        487,    NT_STATUS_NOT_MAPPED_VIEW},
+       {ERRDOS,        87,     NT_STATUS_UNABLE_TO_FREE_VM},
+       {ERRDOS,        87,     NT_STATUS_UNABLE_TO_DELETE_SECTION},
+       {ERRDOS,        2142,   NT_STATUS_INVALID_SYSTEM_SERVICE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ILLEGAL_INSTRUCTION},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_INVALID_LOCK_SEQUENCE},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_INVALID_VIEW_SIZE},
+       {ERRDOS,        193,    NT_STATUS_INVALID_FILE_FOR_SECTION},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_ALREADY_COMMITTED},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 
+        during the session setup }
+*/
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_ACCESS_DENIED},
+       {ERRDOS,        111,    NT_STATUS_BUFFER_TOO_SMALL},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_OBJECT_TYPE_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NONCONTINUABLE_EXCEPTION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_DISPOSITION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNWIND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_STACK},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_UNWIND_TARGET},
+       {ERRDOS,        158,    NT_STATUS_NOT_LOCKED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PARITY_ERROR},
+       {ERRDOS,        487,    NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+       {ERRDOS,        487,    NT_STATUS_NOT_COMMITTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_PORT_ATTRIBUTES},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PORT_MESSAGE_TOO_LONG},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_MIX},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_QUOTA_LOWER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DISK_CORRUPT_ERROR},
+       {ERRDOS,        ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+       {ERRDOS,        ERRbadfile,     NT_STATUS_OBJECT_NAME_NOT_FOUND},
+       {ERRDOS,        183,    NT_STATUS_OBJECT_NAME_COLLISION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_HANDLE_NOT_WAITABLE},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_PORT_DISCONNECTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DEVICE_ALREADY_ATTACHED},
+       {ERRDOS,        161,    NT_STATUS_OBJECT_PATH_INVALID},
+       {ERRDOS,        ERRbadpath,     NT_STATUS_OBJECT_PATH_NOT_FOUND},
+       {ERRDOS,        161,    NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DATA_OVERRUN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DATA_LATE_ERROR},
+       {ERRDOS,        23,     NT_STATUS_DATA_ERROR},
+       {ERRDOS,        23,     NT_STATUS_CRC_ERROR},
+       {ERRDOS,        ERRnomem,       NT_STATUS_SECTION_TOO_BIG},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_PORT_CONNECTION_REFUSED},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_INVALID_PORT_HANDLE},
+       {ERRDOS,        ERRbadshare,    NT_STATUS_SHARING_VIOLATION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_QUOTA_EXCEEDED},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PAGE_PROTECTION},
+       {ERRDOS,        288,    NT_STATUS_MUTANT_NOT_OWNED},
+       {ERRDOS,        298,    NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+       {ERRDOS,        87,     NT_STATUS_PORT_ALREADY_SET},
+       {ERRDOS,        87,     NT_STATUS_SECTION_NOT_IMAGE},
+       {ERRDOS,        156,    NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_THREAD_IS_TERMINATING},
+       {ERRDOS,        87,     NT_STATUS_BAD_WORKING_SET_LIMIT},
+       {ERRDOS,        87,     NT_STATUS_INCOMPATIBLE_FILE_MAP},
+       {ERRDOS,        87,     NT_STATUS_SECTION_PROTECTION},
+       {ERRDOS,        282,    NT_STATUS_EAS_NOT_SUPPORTED},
+       {ERRDOS,        255,    NT_STATUS_EA_TOO_LARGE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_EAS_ON_FILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_EA_CORRUPT_ERROR},
+       {ERRDOS,        ERRlock,        NT_STATUS_FILE_LOCK_CONFLICT},
+       {ERRDOS,        ERRlock,        NT_STATUS_LOCK_NOT_GRANTED},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_DELETE_PENDING},
+       {ERRDOS,        ERRunsup,       NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNKNOWN_REVISION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REVISION_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_OWNER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_PRIMARY_GROUP},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_IMPERSONATION_TOKEN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANT_DISABLE_MANDATORY},
+       {ERRDOS,        2215,   NT_STATUS_NO_LOGON_SERVERS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_LOGON_SESSION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_PRIVILEGE},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_PRIVILEGE_NOT_HELD},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_ACCOUNT_NAME},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_USER_EXISTS},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE 
+        during the session setup }
+*/
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NO_SUCH_USER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_GROUP_EXISTS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_GROUP},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MEMBER_IN_GROUP},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MEMBER_NOT_IN_GROUP},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LAST_ADMIN},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE 
+        during the session setup }
+*/
+       {ERRSRV,        ERRbadpw,       NT_STATUS_WRONG_PASSWORD},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ILL_FORMED_PASSWORD},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PASSWORD_RESTRICTION},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_LOGON_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ACCOUNT_RESTRICTION},
+       {ERRSRV,        2241,   NT_STATUS_INVALID_LOGON_HOURS},
+       {ERRSRV,        2240,   NT_STATUS_INVALID_WORKSTATION},
+       {ERRSRV,        2242,   NT_STATUS_PASSWORD_EXPIRED},
+       {ERRSRV,        2239,   NT_STATUS_ACCOUNT_DISABLED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NONE_MAPPED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LUIDS_EXHAUSTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_SUB_AUTHORITY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_ACL},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_SID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_SECURITY_DESCR},
+       {ERRDOS,        127,    NT_STATUS_PROCEDURE_NOT_FOUND},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_FORMAT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_TOKEN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_INHERITANCE_ACL},
+       {ERRDOS,        158,    NT_STATUS_RANGE_NOT_LOCKED},
+       {ERRDOS,        112,    NT_STATUS_DISK_FULL},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SERVER_DISABLED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SERVER_NOT_DISABLED},
+       {ERRDOS,        68,     NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+       {ERRDOS,        259,    NT_STATUS_GUIDS_EXHAUSTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_ID_AUTHORITY},
+       {ERRDOS,        259,    NT_STATUS_AGENTS_EXHAUSTED},
+       {ERRDOS,        154,    NT_STATUS_INVALID_VOLUME_LABEL},
+       {ERRDOS,        ERRres, NT_STATUS_SECTION_NOT_EXTENDED},
+       {ERRDOS,        487,    NT_STATUS_NOT_MAPPED_DATA},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_DENORMAL_OPERAND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_INEXACT_RESULT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_INVALID_OPERATION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_OVERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_STACK_CHECK},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOAT_UNDERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+       {ERRDOS,        534,    NT_STATUS_INTEGER_OVERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PRIVILEGED_INSTRUCTION},
+       {ERRDOS,        ERRnomem,       NT_STATUS_TOO_MANY_PAGING_FILES},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FILE_INVALID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES 
+        during the session setup }
+*/
+       {ERRDOS,        ERRnomem,       NT_STATUS_INSUFFICIENT_RESOURCES},
+       {ERRDOS,        ERRbadpath,     NT_STATUS_DFS_EXIT_PATH_FOUND},
+       {ERRDOS,        23,     NT_STATUS_DEVICE_DATA_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DEVICE_NOT_CONNECTED},
+       {ERRDOS,        21,     NT_STATUS_DEVICE_POWER_FAILURE},
+       {ERRDOS,        487,    NT_STATUS_FREE_VM_NOT_AT_BASE},
+       {ERRDOS,        487,    NT_STATUS_MEMORY_NOT_ALLOCATED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_WORKING_SET_QUOTA},
+       {ERRDOS,        19,     NT_STATUS_MEDIA_WRITE_PROTECTED},
+       {ERRDOS,        21,     NT_STATUS_DEVICE_NOT_READY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_IMPERSONATION_LEVEL},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANT_OPEN_ANONYMOUS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_VALIDATION_CLASS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_TOKEN_TYPE},
+       {ERRDOS,        87,     NT_STATUS_BAD_MASTER_BOOT_RECORD},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INSTRUCTION_MISALIGNMENT},
+       {ERRDOS,        ERRpipebusy,    NT_STATUS_INSTANCE_NOT_AVAILABLE},
+       {ERRDOS,        ERRpipebusy,    NT_STATUS_PIPE_NOT_AVAILABLE},
+       {ERRDOS,        ERRbadpipe,     NT_STATUS_INVALID_PIPE_STATE},
+       {ERRDOS,        ERRpipebusy,    NT_STATUS_PIPE_BUSY},
+       {ERRDOS,        ERRbadfunc,     NT_STATUS_ILLEGAL_FUNCTION},
+       {ERRDOS,        ERRnotconnected,        NT_STATUS_PIPE_DISCONNECTED},
+       {ERRDOS,        ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PIPE_CONNECTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PIPE_LISTENING},
+       {ERRDOS,        ERRbadpipe,     NT_STATUS_INVALID_READ_MODE},
+       {ERRDOS,        121,    NT_STATUS_IO_TIMEOUT},
+       {ERRDOS,        38,     NT_STATUS_FILE_FORCED_CLOSED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PROFILING_NOT_STARTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PROFILING_NOT_STOPPED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_COULD_NOT_INTERPRET},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_FILE_IS_A_DIRECTORY},
+       {ERRDOS,        ERRunsup,       NT_STATUS_NOT_SUPPORTED},
+       {ERRDOS,        51,     NT_STATUS_REMOTE_NOT_LISTENING},
+       {ERRDOS,        52,     NT_STATUS_DUPLICATE_NAME},
+       {ERRDOS,        53,     NT_STATUS_BAD_NETWORK_PATH},
+       {ERRDOS,        54,     NT_STATUS_NETWORK_BUSY},
+       {ERRDOS,        55,     NT_STATUS_DEVICE_DOES_NOT_EXIST},
+       {ERRDOS,        56,     NT_STATUS_TOO_MANY_COMMANDS},
+       {ERRDOS,        57,     NT_STATUS_ADAPTER_HARDWARE_ERROR},
+       {ERRDOS,        58,     NT_STATUS_INVALID_NETWORK_RESPONSE},
+       {ERRDOS,        59,     NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+       {ERRDOS,        60,     NT_STATUS_BAD_REMOTE_ADAPTER},
+       {ERRDOS,        61,     NT_STATUS_PRINT_QUEUE_FULL},
+       {ERRDOS,        62,     NT_STATUS_NO_SPOOL_SPACE},
+       {ERRDOS,        63,     NT_STATUS_PRINT_CANCELLED},
+       {ERRDOS,        64,     NT_STATUS_NETWORK_NAME_DELETED},
+       {ERRDOS,        65,     NT_STATUS_NETWORK_ACCESS_DENIED},
+       {ERRDOS,        66,     NT_STATUS_BAD_DEVICE_TYPE},
+       {ERRDOS,        ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+       {ERRDOS,        68,     NT_STATUS_TOO_MANY_NAMES},
+       {ERRDOS,        69,     NT_STATUS_TOO_MANY_SESSIONS},
+       {ERRDOS,        70,     NT_STATUS_SHARING_PAUSED},
+       {ERRDOS,        71,     NT_STATUS_REQUEST_NOT_ACCEPTED},
+       {ERRDOS,        72,     NT_STATUS_REDIRECTOR_PAUSED},
+       {ERRDOS,        88,     NT_STATUS_NET_WRITE_FAULT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PROFILING_AT_LIMIT},
+       {ERRDOS,        ERRdiffdevice,  NT_STATUS_NOT_SAME_DEVICE},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_FILE_RENAMED},
+       {ERRDOS,        240,    NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SECURITY_ON_OBJECT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANT_WAIT},
+       {ERRDOS,        ERRpipeclosing, NT_STATUS_PIPE_EMPTY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANT_TERMINATE_SELF},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_SERVER_STATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_DOMAIN_STATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_DOMAIN_ROLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_DOMAIN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DOMAIN_EXISTS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+       {ERRDOS,        300,    NT_STATUS_OPLOCK_NOT_GRANTED},
+       {ERRDOS,        301,    NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INTERNAL_DB_CORRUPTION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INTERNAL_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_GENERIC_NOT_MAPPED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_USER_BUFFER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNEXPECTED_IO_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NOT_LOGON_PROCESS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGON_SESSION_EXISTS},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_1},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_2},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_3},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_4},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_5},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_6},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_7},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_8},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_9},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_10},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_11},
+       {ERRDOS,        87,     NT_STATUS_INVALID_PARAMETER_12},
+       {ERRDOS,        ERRbadpath,     NT_STATUS_REDIRECTOR_NOT_STARTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REDIRECTOR_STARTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_STACK_OVERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_PACKAGE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_FUNCTION_TABLE},
+       {ERRDOS,        203,    NT_STATUS(0xc0000100)},
+       {ERRDOS,        145,    NT_STATUS_DIRECTORY_NOT_EMPTY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FILE_CORRUPT_ERROR},
+       {ERRDOS,        267,    NT_STATUS_NOT_A_DIRECTORY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_LOGON_SESSION_STATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGON_SESSION_COLLISION},
+       {ERRDOS,        206,    NT_STATUS_NAME_TOO_LONG},
+       {ERRDOS,        2401,   NT_STATUS_FILES_OPEN},
+       {ERRDOS,        2404,   NT_STATUS_CONNECTION_IN_USE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MESSAGE_NOT_FOUND},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_PROCESS_IS_TERMINATING},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LOGON_TYPE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_GUID_TRANSLATION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANNOT_IMPERSONATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IMAGE_ALREADY_LOADED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_NOT_PRESENT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_LID_NOT_EXIST},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_NOT_LID_OWNER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_COMMAND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_LID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_SELECTOR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_LDT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_SIZE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_OFFSET},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_DESCRIPTOR},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RXACT_INVALID_STATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RXACT_COMMIT_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+       {ERRDOS,        ERRnofids,      NT_STATUS_TOO_MANY_OPENED_FILES},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANCELLED},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_CANNOT_DELETE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_COMPUTER_NAME},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_FILE_DELETED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SPECIAL_ACCOUNT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SPECIAL_GROUP},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SPECIAL_USER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MEMBERS_PRIMARY_GROUP},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_FILE_CLOSED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_THREADS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_THREAD_NOT_IN_PROCESS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOKEN_ALREADY_IN_USE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_COMMITMENT_LIMIT},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_NOT_MZ},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_PROTECT},
+       {ERRDOS,        193,    NT_STATUS_INVALID_IMAGE_WIN_16},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGON_SERVER_CONFLICT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TIME_DIFFERENCE_AT_DC},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SYNCHRONIZATION_REQUIRED},
+       {ERRDOS,        126,    NT_STATUS_DLL_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_OPEN_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IO_PRIVILEGE_FAILED},
+       {ERRDOS,        182,    NT_STATUS_ORDINAL_NOT_FOUND},
+       {ERRDOS,        127,    NT_STATUS_ENTRYPOINT_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONTROL_C_EXIT},
+       {ERRDOS,        64,     NT_STATUS_LOCAL_DISCONNECT},
+       {ERRDOS,        64,     NT_STATUS_REMOTE_DISCONNECT},
+       {ERRDOS,        51,     NT_STATUS_REMOTE_RESOURCES},
+       {ERRDOS,        59,     NT_STATUS_LINK_FAILED},
+       {ERRDOS,        59,     NT_STATUS_LINK_TIMEOUT},
+       {ERRDOS,        59,     NT_STATUS_INVALID_CONNECTION},
+       {ERRDOS,        59,     NT_STATUS_INVALID_ADDRESS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DLL_INIT_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MISSING_SYSTEMFILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNHANDLED_EXCEPTION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_APP_INIT_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PAGEFILE_CREATE_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_PAGEFILE},
+       {ERRDOS,        124,    NT_STATUS_INVALID_LEVEL},
+       {ERRDOS,        86,     NT_STATUS_WRONG_PASSWORD_CORE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+       {ERRDOS,        109,    NT_STATUS_PIPE_BROKEN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REGISTRY_CORRUPT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REGISTRY_IO_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_EVENT_PAIR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNRECOGNIZED_VOLUME},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SERIAL_NO_DEVICE_INITED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_ALIAS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MEMBER_NOT_IN_ALIAS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MEMBER_IN_ALIAS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ALIAS_EXISTS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGON_NOT_GRANTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_SECRETS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SECRET_TOO_LONG},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INTERNAL_DB_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FULLSCREEN_MODE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_CONTEXT_IDS},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NOT_REGISTRY_FILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FT_MISSING_MEMBER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ILLEGAL_CHARACTER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNMAPPABLE_CHARACTER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNDEFINED_CHARACTER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOPPY_VOLUME},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOPPY_WRONG_CYLINDER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FLOPPY_BAD_REGISTERS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DISK_RECALIBRATE_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DISK_OPERATION_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DISK_RESET_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SHARED_IRQ_BUSY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FT_ORPHANING},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000016e)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000016f)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc0000170)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc0000171)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PARTITION_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_BLOCK_LENGTH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DEVICE_NOT_PARTITIONED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_EOM_OVERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_MEDIA},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc0000179)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_SUCH_MEMBER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_MEMBER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_KEY_DELETED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_LOG_SPACE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_SIDS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_KEY_HAS_CHILDREN},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CHILD_MUST_BE_VOLATILE},
+       {ERRDOS,        87,     NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DRIVER_INTERNAL_ERROR},
+       {ERRDOS,        22,     NT_STATUS_INVALID_DEVICE_STATE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IO_DEVICE_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DEVICE_PROTOCOL_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BACKUP_CONTROLLER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOG_FILE_FULL},
+       {ERRDOS,        19,     NT_STATUS_TOO_LATE},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NO_TRUST_LSA_SECRET},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 
+        during the session setup }
+*/
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_EVENTLOG_FILE_CORRUPT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_EVENTLOG_CANT_START},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_TRUST_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+       {ERRDOS,        ERRinvgroup,    NT_STATUS_NETLOGON_NOT_STARTED},
+       {ERRSRV,        2239,   NT_STATUS_ACCOUNT_EXPIRED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_POSSIBLE_DEADLOCK},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REMOTE_SESSION_LIMIT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_EVENTLOG_FILE_CHANGED},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/*     { This NT error code was 'sqashed'
+        from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE 
+        during the session setup }
+*/
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FS_DRIVER_REQUIRED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_USER_SESSION_KEY},
+       {ERRDOS,        59,     NT_STATUS_USER_SESSION_DELETED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+       {ERRDOS,        ERRnomem,       NT_STATUS_INSUFF_SERVER_RESOURCES},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_BUFFER_SIZE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_ADDRESS_COMPONENT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_ADDRESS_WILDCARD},
+       {ERRDOS,        68,     NT_STATUS_TOO_MANY_ADDRESSES},
+       {ERRDOS,        52,     NT_STATUS_ADDRESS_ALREADY_EXISTS},
+       {ERRDOS,        64,     NT_STATUS_ADDRESS_CLOSED},
+       {ERRDOS,        64,     NT_STATUS_CONNECTION_DISCONNECTED},
+       {ERRDOS,        64,     NT_STATUS_CONNECTION_RESET},
+       {ERRDOS,        68,     NT_STATUS_TOO_MANY_NODES},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_ABORTED},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_TIMED_OUT},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_NO_RELEASE},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_NO_MATCH},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_RESPONDED},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_INVALID_ID},
+       {ERRDOS,        59,     NT_STATUS_TRANSACTION_INVALID_TYPE},
+       {ERRDOS,        ERRunsup,       NT_STATUS_NOT_SERVER_SESSION},
+       {ERRDOS,        ERRunsup,       NT_STATUS_NOT_CLIENT_SESSION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DEBUG_ATTACH_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DATA_NOT_ACCEPTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_VDM_HARD_ERROR},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REPLY_MESSAGE_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MAPPED_ALIGNMENT},
+       {ERRDOS,        193,    NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOST_WRITEBEHIND_DATA},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+       {ERRSRV,        2242,   NT_STATUS_PASSWORD_MUST_CHANGE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NOT_TINY_STREAM},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RECOVERY_FAILURE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_STACK_OVERFLOW_READ},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FAIL_CHECK},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DUPLICATE_OBJECTID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_OBJECTID_EXISTS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONVERT_TO_LARGE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_RETRY},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FOUND_OUT_OF_SCOPE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ALLOCATE_BUCKET},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PROPSET_NOT_FOUND},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_MARSHALL_OVERFLOW},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_VARIANT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_ACCOUNT_LOCKED_OUT},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_HANDLE_NOT_CLOSABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONNECTION_REFUSED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_GRACEFUL_DISCONNECT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONNECTION_INVALID},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONNECTION_ACTIVE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NETWORK_UNREACHABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_HOST_UNREACHABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PROTOCOL_UNREACHABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PORT_UNREACHABLE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REQUEST_ABORTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONNECTION_ABORTED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_COMPRESSION_BUFFER},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_USER_MAPPED_FILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_AUDIT_FAILED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_CONNECTION_COUNT_LIMIT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGIN_TIME_RESTRICTION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+       {ERRDOS,        193,    NT_STATUS_IMAGE_MP_UP_MISMATCH},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024a)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024b)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024c)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024d)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024e)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000024f)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INSUFFICIENT_LOGON_INFO},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_DLL_ENTRYPOINT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LPC_REPLY_LOST},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IP_ADDRESS_CONFLICT1},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_IP_ADDRESS_CONFLICT2},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_REGISTRY_QUOTA_LIMIT},
+       {ERRSRV,        ERRbadtype,     NT_STATUS_PATH_NOT_COVERED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_NO_CALLBACK_ACTIVE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PWD_TOO_SHORT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PWD_TOO_RECENT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PWD_HISTORY_CONFLICT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000025d)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_PLUGPLAY_NO_DEVICE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNSUPPORTED_COMPRESSION},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_HW_PROFILE},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+       {ERRDOS,        182,    NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+       {ERRDOS,        127,    NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+       {ERRDOS,        288,    NT_STATUS_RESOURCE_NOT_OWNED},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_TOO_MANY_LINKS},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_QUOTA_LIST_INCONSISTENT},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_FILE_IS_OFFLINE},
+       {ERRDOS,        21,     NT_STATUS(0xc000026e)},
+       {ERRDOS,        161,    NT_STATUS(0xc0000281)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc000028a)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc000028b)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS(0xc000028c)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc000028d)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc000028e)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc000028f)},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS(0xc0000290)},
+       {ERRDOS,        ERRbadfunc,     NT_STATUS(0xc000029c)},
+};
+
+
+/* dos -> nt status error map */
+static const struct {
+       uint8 dos_class;
+       uint32 dos_code;
+       NTSTATUS ntstatus;
+} dos_to_ntstatus_map[] = {
+       {ERRDOS,        ERRbadfunc,     NT_STATUS_NOT_IMPLEMENTED},
+       {ERRDOS,        ERRbadfile,     NT_STATUS_NO_SUCH_FILE},
+       {ERRDOS,        ERRbadpath,     NT_STATUS_OBJECT_PATH_NOT_FOUND},
+       {ERRDOS,        ERRnofids,      NT_STATUS_TOO_MANY_OPENED_FILES},
+       {ERRDOS,        ERRnoaccess,    NT_STATUS_ACCESS_DENIED},
+       {ERRDOS,        ERRbadfid,      NT_STATUS_INVALID_HANDLE},
+       {ERRDOS,        ERRnomem,       NT_STATUS_INSUFFICIENT_RESOURCES},
+       {ERRDOS,        ERRbadaccess,   NT_STATUS_INVALID_LOCK_SEQUENCE},
+       {ERRDOS,        ERRbaddata,     NT_STATUS_DATA_ERROR},
+       {ERRDOS,        14,     NT_STATUS_SECTION_NOT_EXTENDED},
+       {ERRDOS,        ERRremcd,       NT_STATUS_DIRECTORY_NOT_EMPTY},
+       {ERRDOS,        ERRdiffdevice,  NT_STATUS_NOT_SAME_DEVICE},
+       {ERRDOS,        ERRnofiles,     NT_STATUS(0x80000006)},
+       {ERRDOS,        19,     NT_STATUS_MEDIA_WRITE_PROTECTED},
+       {ERRDOS,        21,     NT_STATUS_NO_MEDIA_IN_DEVICE},
+       {ERRDOS,        22,     NT_STATUS_INVALID_DEVICE_STATE},
+       {ERRDOS,        23,     NT_STATUS_DATA_ERROR},
+       {ERRDOS,        24,     NT_STATUS_DATA_ERROR},
+       {ERRDOS,        26,     NT_STATUS_DISK_CORRUPT_ERROR},
+       {ERRDOS,        27,     NT_STATUS_NONEXISTENT_SECTOR},
+       {ERRDOS,        28,     NT_STATUS(0x8000000e)},
+       {ERRDOS,        31,     NT_STATUS_UNSUCCESSFUL},
+       {ERRDOS,        ERRbadshare,    NT_STATUS_SHARING_VIOLATION},
+       {ERRDOS,        ERRlock,        NT_STATUS_FILE_LOCK_CONFLICT},
+       {ERRDOS,        34,     NT_STATUS_WRONG_VOLUME},
+       {ERRDOS,        38,     NT_STATUS_END_OF_FILE},
+       {ERRDOS,        ERRunsup,       NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+       {ERRDOS,        51,     NT_STATUS_REMOTE_NOT_LISTENING},
+       {ERRDOS,        52,     NT_STATUS_DUPLICATE_NAME},
+       {ERRDOS,        53,     NT_STATUS_BAD_NETWORK_PATH},
+       {ERRDOS,        54,     NT_STATUS_NETWORK_BUSY},
+       {ERRDOS,        55,     NT_STATUS_DEVICE_DOES_NOT_EXIST},
+       {ERRDOS,        56,     NT_STATUS_TOO_MANY_COMMANDS},
+       {ERRDOS,        57,     NT_STATUS_ADAPTER_HARDWARE_ERROR},
+       {ERRDOS,        58,     NT_STATUS_INVALID_NETWORK_RESPONSE},
+       {ERRDOS,        59,     NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+       {ERRDOS,        60,     NT_STATUS_BAD_REMOTE_ADAPTER},
+       {ERRDOS,        61,     NT_STATUS_PRINT_QUEUE_FULL},
+       {ERRDOS,        62,     NT_STATUS_NO_SPOOL_SPACE},
+       {ERRDOS,        63,     NT_STATUS_PRINT_CANCELLED},
+       {ERRDOS,        64,     NT_STATUS_NETWORK_NAME_DELETED},
+       {ERRDOS,        65,     NT_STATUS_NETWORK_ACCESS_DENIED},
+       {ERRDOS,        66,     NT_STATUS_BAD_DEVICE_TYPE},
+       {ERRDOS,        ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+       {ERRDOS,        68,     NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+       {ERRDOS,        69,     NT_STATUS_TOO_MANY_SESSIONS},
+       {ERRDOS,        70,     NT_STATUS_SHARING_PAUSED},
+       {ERRDOS,        71,     NT_STATUS_REQUEST_NOT_ACCEPTED},
+       {ERRDOS,        72,     NT_STATUS_REDIRECTOR_PAUSED},
+       {ERRDOS,        ERRfilexists,   NT_STATUS_OBJECT_NAME_COLLISION},
+       {ERRDOS,        86,     NT_STATUS_WRONG_PASSWORD},
+       {ERRDOS,        87,     NT_STATUS_INVALID_INFO_CLASS},
+       {ERRDOS,        88,     NT_STATUS_NET_WRITE_FAULT},
+       {ERRDOS,        109,    NT_STATUS_PIPE_BROKEN},
+       {ERRDOS,        111,    STATUS_MORE_ENTRIES},
+       {ERRDOS,        112,    NT_STATUS_DISK_FULL},
+       {ERRDOS,        121,    NT_STATUS_IO_TIMEOUT},
+       {ERRDOS,        122,    NT_STATUS_BUFFER_TOO_SMALL},
+       {ERRDOS,        ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+       {ERRDOS,        124,    NT_STATUS_INVALID_LEVEL},
+       {ERRDOS,        126,    NT_STATUS_DLL_NOT_FOUND},
+       {ERRDOS,        127,    NT_STATUS_PROCEDURE_NOT_FOUND},
+       {ERRDOS,        145,    NT_STATUS_DIRECTORY_NOT_EMPTY},
+       {ERRDOS,        154,    NT_STATUS_INVALID_VOLUME_LABEL},
+       {ERRDOS,        156,    NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+       {ERRDOS,        158,    NT_STATUS_NOT_LOCKED},
+       {ERRDOS,        161,    NT_STATUS_OBJECT_PATH_INVALID},
+       {ERRDOS,        170,    NT_STATUS(0x80000011)},
+       {ERRDOS,        182,    NT_STATUS_ORDINAL_NOT_FOUND},
+       {ERRDOS,        183,    NT_STATUS_OBJECT_NAME_COLLISION},
+       {ERRDOS,        193,    NT_STATUS_BAD_INITIAL_PC},
+       {ERRDOS,        203,    NT_STATUS(0xc0000100)},
+       {ERRDOS,        206,    NT_STATUS_NAME_TOO_LONG},
+       {ERRDOS,        ERRbadpipe,     NT_STATUS_INVALID_INFO_CLASS},
+       {ERRDOS,        ERRpipebusy,    NT_STATUS_INSTANCE_NOT_AVAILABLE},
+       {ERRDOS,        ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+       {ERRDOS,        ERRnotconnected,        NT_STATUS_PIPE_DISCONNECTED},
+       {ERRDOS,        ERRmoredata,    NT_STATUS_MORE_PROCESSING_REQUIRED},
+       {ERRDOS,        240,    NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+       {ERRDOS,        254,    NT_STATUS(0x80000013)},
+       {ERRDOS,        255,    NT_STATUS_EA_TOO_LARGE},
+       {ERRDOS,        259,    NT_STATUS_GUIDS_EXHAUSTED},
+       {ERRDOS,        267,    NT_STATUS_NOT_A_DIRECTORY},
+       {ERRDOS,        275,    NT_STATUS_EA_TOO_LARGE},
+       {ERRDOS,        276,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRDOS,        277,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRDOS,        278,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRDOS,        282,    NT_STATUS_EAS_NOT_SUPPORTED},
+       {ERRDOS,        288,    NT_STATUS_MUTANT_NOT_OWNED},
+       {ERRDOS,        298,    NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+       {ERRDOS,        299,    NT_STATUS(0x8000000d)},
+       {ERRDOS,        300,    NT_STATUS_OPLOCK_NOT_GRANTED},
+       {ERRDOS,        301,    NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+       {ERRDOS,        487,    NT_STATUS_CONFLICTING_ADDRESSES},
+       {ERRDOS,        534,    NT_STATUS_INTEGER_OVERFLOW},
+       {ERRDOS,        535,    NT_STATUS_PIPE_CONNECTED},
+       {ERRDOS,        536,    NT_STATUS_PIPE_LISTENING},
+       {ERRDOS,        995,    NT_STATUS_CANCELLED},
+       {ERRDOS,        997,    NT_STATUS(0x00000103)},
+       {ERRDOS,        998,    NT_STATUS_ACCESS_VIOLATION},
+       {ERRDOS,        999,    NT_STATUS_IN_PAGE_ERROR},
+       {ERRDOS,        1001,   NT_STATUS_BAD_INITIAL_STACK},
+       {ERRDOS,        1005,   NT_STATUS_UNRECOGNIZED_VOLUME},
+       {ERRDOS,        1006,   NT_STATUS_FILE_INVALID},
+       {ERRDOS,        1007,   NT_STATUS_FULLSCREEN_MODE},
+       {ERRDOS,        1008,   NT_STATUS_NO_TOKEN},
+       {ERRDOS,        1009,   NT_STATUS_REGISTRY_CORRUPT},
+       {ERRDOS,        1016,   NT_STATUS_REGISTRY_IO_FAILED},
+       {ERRDOS,        1017,   NT_STATUS_NOT_REGISTRY_FILE},
+       {ERRDOS,        1018,   NT_STATUS_KEY_DELETED},
+       {ERRDOS,        1019,   NT_STATUS_NO_LOG_SPACE},
+       {ERRDOS,        1020,   NT_STATUS_KEY_HAS_CHILDREN},
+       {ERRDOS,        1021,   NT_STATUS_CHILD_MUST_BE_VOLATILE},
+       {ERRDOS,        1022,   NT_STATUS(0x0000010c)},
+       {ERRSRV,        ERRbadpw,       NT_STATUS_WRONG_PASSWORD},
+       {ERRSRV,        ERRbadtype,     NT_STATUS_BAD_DEVICE_TYPE},
+       {ERRSRV,        ERRaccess,      NT_STATUS_NETWORK_ACCESS_DENIED},
+       {ERRSRV,        ERRinvnid,      NT_STATUS_NETWORK_NAME_DELETED},
+       {ERRSRV,        ERRinvnetname,  NT_STATUS_BAD_NETWORK_NAME},
+       {ERRSRV,        ERRinvdevice,   NT_STATUS_BAD_DEVICE_TYPE},
+       {ERRSRV,        ERRqfull,       NT_STATUS_PRINT_QUEUE_FULL},
+       {ERRSRV,        ERRqtoobig,     NT_STATUS_NO_SPOOL_SPACE},
+       {ERRSRV,        ERRinvpfid,     NT_STATUS_PRINT_CANCELLED},
+       {ERRSRV,        ERRsmbcmd,      NT_STATUS_NOT_IMPLEMENTED},
+       {ERRSRV,        ERRbadpermits,  NT_STATUS_NETWORK_ACCESS_DENIED},
+       {ERRSRV,        ERRpaused,      NT_STATUS_SHARING_PAUSED},
+       {ERRSRV,        ERRmsgoff,      NT_STATUS_REQUEST_NOT_ACCEPTED},
+       {ERRSRV,        ERRnoroom,      NT_STATUS_DISK_FULL},
+       {ERRSRV,        ERRnoresource,  NT_STATUS_REQUEST_NOT_ACCEPTED},
+       {ERRSRV,        ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS},
+       {ERRSRV,        123,    NT_STATUS_OBJECT_NAME_INVALID},
+       {ERRSRV,        206,    NT_STATUS_OBJECT_NAME_INVALID},
+       {ERRHRD,        1,      NT_STATUS_NOT_IMPLEMENTED},
+       {ERRHRD,        2,      NT_STATUS_NO_SUCH_DEVICE},
+       {ERRHRD,        3,      NT_STATUS_OBJECT_PATH_NOT_FOUND},
+       {ERRHRD,        4,      NT_STATUS_TOO_MANY_OPENED_FILES},
+       {ERRHRD,        5,      NT_STATUS_INVALID_LOCK_SEQUENCE},
+       {ERRHRD,        6,      NT_STATUS_INVALID_HANDLE},
+       {ERRHRD,        8,      NT_STATUS_INSUFFICIENT_RESOURCES},
+       {ERRHRD,        12,     NT_STATUS_INVALID_LOCK_SEQUENCE},
+       {ERRHRD,        13,     NT_STATUS_DATA_ERROR},
+       {ERRHRD,        14,     NT_STATUS_SECTION_NOT_EXTENDED},
+       {ERRHRD,        16,     NT_STATUS_DIRECTORY_NOT_EMPTY},
+       {ERRHRD,        17,     NT_STATUS_NOT_SAME_DEVICE},
+       {ERRHRD,        18,     NT_STATUS(0x80000006)},
+       {ERRHRD,        ERRnowrite,     NT_STATUS_MEDIA_WRITE_PROTECTED},
+       {ERRHRD,        ERRnotready,    NT_STATUS_NO_MEDIA_IN_DEVICE},
+       {ERRHRD,        ERRbadcmd,      NT_STATUS_INVALID_DEVICE_STATE},
+       {ERRHRD,        ERRdata,        NT_STATUS_DATA_ERROR},
+       {ERRHRD,        ERRbadreq,      NT_STATUS_DATA_ERROR},
+       {ERRHRD,        ERRbadmedia,    NT_STATUS_DISK_CORRUPT_ERROR},
+       {ERRHRD,        ERRbadsector,   NT_STATUS_NONEXISTENT_SECTOR},
+       {ERRHRD,        ERRnopaper,     NT_STATUS(0x8000000e)},
+       {ERRHRD,        ERRgeneral,     NT_STATUS_UNSUCCESSFUL},
+       {ERRHRD,        ERRbadshare,    NT_STATUS_SHARING_VIOLATION},
+       {ERRHRD,        ERRlock,        NT_STATUS_FILE_LOCK_CONFLICT},
+       {ERRHRD,        ERRwrongdisk,   NT_STATUS_WRONG_VOLUME},
+       {ERRHRD,        38,     NT_STATUS_END_OF_FILE},
+       {ERRHRD,        ERRdiskfull,    NT_STATUS_DISK_FULL},
+       {ERRHRD,        50,     NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+       {ERRHRD,        51,     NT_STATUS_REMOTE_NOT_LISTENING},
+       {ERRHRD,        52,     NT_STATUS_DUPLICATE_NAME},
+       {ERRHRD,        53,     NT_STATUS_BAD_NETWORK_PATH},
+       {ERRHRD,        54,     NT_STATUS_NETWORK_BUSY},
+       {ERRHRD,        55,     NT_STATUS_DEVICE_DOES_NOT_EXIST},
+       {ERRHRD,        56,     NT_STATUS_TOO_MANY_COMMANDS},
+       {ERRHRD,        57,     NT_STATUS_ADAPTER_HARDWARE_ERROR},
+       {ERRHRD,        58,     NT_STATUS_INVALID_NETWORK_RESPONSE},
+       {ERRHRD,        59,     NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+       {ERRHRD,        60,     NT_STATUS_BAD_REMOTE_ADAPTER},
+       {ERRHRD,        61,     NT_STATUS_PRINT_QUEUE_FULL},
+       {ERRHRD,        62,     NT_STATUS_NO_SPOOL_SPACE},
+       {ERRHRD,        63,     NT_STATUS_PRINT_CANCELLED},
+       {ERRHRD,        64,     NT_STATUS_NETWORK_NAME_DELETED},
+       {ERRHRD,        65,     NT_STATUS_NETWORK_ACCESS_DENIED},
+       {ERRHRD,        66,     NT_STATUS_BAD_DEVICE_TYPE},
+       {ERRHRD,        67,     NT_STATUS_BAD_NETWORK_NAME},
+       {ERRHRD,        68,     NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+       {ERRHRD,        69,     NT_STATUS_TOO_MANY_SESSIONS},
+       {ERRHRD,        70,     NT_STATUS_SHARING_PAUSED},
+       {ERRHRD,        71,     NT_STATUS_REQUEST_NOT_ACCEPTED},
+       {ERRHRD,        72,     NT_STATUS_REDIRECTOR_PAUSED},
+       {ERRHRD,        80,     NT_STATUS_OBJECT_NAME_COLLISION},
+       {ERRHRD,        86,     NT_STATUS_WRONG_PASSWORD},
+       {ERRHRD,        87,     NT_STATUS_INVALID_INFO_CLASS},
+       {ERRHRD,        88,     NT_STATUS_NET_WRITE_FAULT},
+       {ERRHRD,        109,    NT_STATUS_PIPE_BROKEN},
+       {ERRHRD,        111,    STATUS_MORE_ENTRIES},
+       {ERRHRD,        112,    NT_STATUS_DISK_FULL},
+       {ERRHRD,        121,    NT_STATUS_IO_TIMEOUT},
+       {ERRHRD,        122,    NT_STATUS_BUFFER_TOO_SMALL},
+       {ERRHRD,        123,    NT_STATUS_OBJECT_NAME_INVALID},
+       {ERRHRD,        124,    NT_STATUS_INVALID_LEVEL},
+       {ERRHRD,        126,    NT_STATUS_DLL_NOT_FOUND},
+       {ERRHRD,        127,    NT_STATUS_PROCEDURE_NOT_FOUND},
+       {ERRHRD,        145,    NT_STATUS_DIRECTORY_NOT_EMPTY},
+       {ERRHRD,        154,    NT_STATUS_INVALID_VOLUME_LABEL},
+       {ERRHRD,        156,    NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+       {ERRHRD,        158,    NT_STATUS_NOT_LOCKED},
+       {ERRHRD,        161,    NT_STATUS_OBJECT_PATH_INVALID},
+       {ERRHRD,        170,    NT_STATUS(0x80000011)},
+       {ERRHRD,        182,    NT_STATUS_ORDINAL_NOT_FOUND},
+       {ERRHRD,        183,    NT_STATUS_OBJECT_NAME_COLLISION},
+       {ERRHRD,        193,    NT_STATUS_BAD_INITIAL_PC},
+       {ERRHRD,        203,    NT_STATUS(0xc0000100)},
+       {ERRHRD,        206,    NT_STATUS_NAME_TOO_LONG},
+       {ERRHRD,        230,    NT_STATUS_INVALID_INFO_CLASS},
+       {ERRHRD,        231,    NT_STATUS_INSTANCE_NOT_AVAILABLE},
+       {ERRHRD,        232,    NT_STATUS_PIPE_CLOSING},
+       {ERRHRD,        233,    NT_STATUS_PIPE_DISCONNECTED},
+       {ERRHRD,        234,    STATUS_MORE_ENTRIES},
+       {ERRHRD,        240,    NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+       {ERRHRD,        254,    NT_STATUS(0x80000013)},
+       {ERRHRD,        255,    NT_STATUS_EA_TOO_LARGE},
+       {ERRHRD,        259,    NT_STATUS_GUIDS_EXHAUSTED},
+       {ERRHRD,        267,    NT_STATUS_NOT_A_DIRECTORY},
+       {ERRHRD,        275,    NT_STATUS_EA_TOO_LARGE},
+       {ERRHRD,        276,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRHRD,        277,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRHRD,        278,    NT_STATUS_NONEXISTENT_EA_ENTRY},
+       {ERRHRD,        282,    NT_STATUS_EAS_NOT_SUPPORTED},
+       {ERRHRD,        288,    NT_STATUS_MUTANT_NOT_OWNED},
+       {ERRHRD,        298,    NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+       {ERRHRD,        299,    NT_STATUS(0x8000000d)},
+       {ERRHRD,        300,    NT_STATUS_OPLOCK_NOT_GRANTED},
+       {ERRHRD,        301,    NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+       {ERRHRD,        487,    NT_STATUS_CONFLICTING_ADDRESSES},
+       {ERRHRD,        534,    NT_STATUS_INTEGER_OVERFLOW},
+       {ERRHRD,        535,    NT_STATUS_PIPE_CONNECTED},
+       {ERRHRD,        536,    NT_STATUS_PIPE_LISTENING},
+       {ERRHRD,        995,    NT_STATUS_CANCELLED},
+       {ERRHRD,        997,    NT_STATUS(0x00000103)},
+       {ERRHRD,        998,    NT_STATUS_ACCESS_VIOLATION},
+       {ERRHRD,        999,    NT_STATUS_IN_PAGE_ERROR},
+       {ERRHRD,        1001,   NT_STATUS_BAD_INITIAL_STACK},
+       {ERRHRD,        1005,   NT_STATUS_UNRECOGNIZED_VOLUME},
+       {ERRHRD,        1006,   NT_STATUS_FILE_INVALID},
+       {ERRHRD,        1007,   NT_STATUS_FULLSCREEN_MODE},
+       {ERRHRD,        1008,   NT_STATUS_NO_TOKEN},
+       {ERRHRD,        1009,   NT_STATUS_REGISTRY_CORRUPT},
+       {ERRHRD,        1016,   NT_STATUS_REGISTRY_IO_FAILED},
+       {ERRHRD,        1017,   NT_STATUS_NOT_REGISTRY_FILE},
+       {ERRHRD,        1018,   NT_STATUS_KEY_DELETED},
+       {ERRHRD,        1019,   NT_STATUS_NO_LOG_SPACE},
+       {ERRHRD,        1020,   NT_STATUS_KEY_HAS_CHILDREN},
+       {ERRHRD,        1021,   NT_STATUS_CHILD_MUST_BE_VOLATILE},
+       {ERRHRD,        1022,   NT_STATUS(0x0000010c)},
+};
+
+/* errmap NTSTATUS->Win32 */
+static const struct {
+       NTSTATUS ntstatus;
+       WERROR werror;
+} ntstatus_to_werror_map[] = {
+       {NT_STATUS(0x103), W_ERROR(0x3e5)},
+       {NT_STATUS(0x105), W_ERROR(0xea)},
+       {NT_STATUS(0x106), W_ERROR(0x514)},
+       {NT_STATUS(0x107), W_ERROR(0x515)},
+       {NT_STATUS(0x10c), W_ERROR(0x3fe)},
+       {NT_STATUS(0x10d), W_ERROR(0x516)},
+       {NT_STATUS(0x121), W_ERROR(0x2009)},
+       {NT_STATUS(0xc0000001), W_ERROR(0x1f)},
+       {NT_STATUS(0xc0000002), W_ERROR(0x1)},
+       {NT_STATUS(0xc0000003), W_ERROR(0x57)},
+       {NT_STATUS(0xc0000004), W_ERROR(0x18)},
+       {NT_STATUS(0xc0000005), W_ERROR(0x3e6)},
+       {NT_STATUS(0xc0000006), W_ERROR(0x3e7)},
+       {NT_STATUS(0xc0000007), W_ERROR(0x5ae)},
+       {NT_STATUS(0xc0000008), W_ERROR(0x6)},
+       {NT_STATUS(0xc0000009), W_ERROR(0x3e9)},
+       {NT_STATUS(0xc000000a), W_ERROR(0xc1)},
+       {NT_STATUS(0xc000000b), W_ERROR(0x57)},
+       {NT_STATUS(0xc000000d), W_ERROR(0x57)},
+       {NT_STATUS(0xc000000e), W_ERROR(0x2)},
+       {NT_STATUS(0xc000000f), W_ERROR(0x2)},
+       {NT_STATUS(0xc0000010), W_ERROR(0x1)},
+       {NT_STATUS(0xc0000011), W_ERROR(0x26)},
+       {NT_STATUS(0xc0000012), W_ERROR(0x22)},
+       {NT_STATUS(0xc0000013), W_ERROR(0x15)},
+       {NT_STATUS(0xc0000014), W_ERROR(0x6f9)},
+       {NT_STATUS(0xc0000015), W_ERROR(0x1b)},
+       {NT_STATUS(0xc0000016), W_ERROR(0xea)},
+       {NT_STATUS(0xc0000017), W_ERROR(0x8)},
+       {NT_STATUS(0xc0000018), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc0000019), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc000001a), W_ERROR(0x57)},
+       {NT_STATUS(0xc000001b), W_ERROR(0x57)},
+       {NT_STATUS(0xc000001c), W_ERROR(0x1)},
+       {NT_STATUS(0xc000001d), W_ERROR(0xc000001d)},
+       {NT_STATUS(0xc000001e), W_ERROR(0x5)},
+       {NT_STATUS(0xc000001f), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000020), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000021), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000022), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000023), W_ERROR(0x7a)},
+       {NT_STATUS(0xc0000024), W_ERROR(0x6)},
+       {NT_STATUS(0xc0000025), W_ERROR(0xc0000025)},
+       {NT_STATUS(0xc0000026), W_ERROR(0xc0000026)},
+       {NT_STATUS(0xc000002a), W_ERROR(0x9e)},
+       {NT_STATUS(0xc000002b), W_ERROR(0xc000002b)},
+       {NT_STATUS(0xc000002c), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc000002d), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc0000030), W_ERROR(0x57)},
+       {NT_STATUS(0xc0000032), W_ERROR(0x571)},
+       {NT_STATUS(0xc0000033), W_ERROR(0x7b)},
+       {NT_STATUS(0xc0000034), W_ERROR(0x2)},
+       {NT_STATUS(0xc0000035), W_ERROR(0xb7)},
+       {NT_STATUS(0xc0000037), W_ERROR(0x6)},
+       {NT_STATUS(0xc0000039), W_ERROR(0xa1)},
+       {NT_STATUS(0xc000003a), W_ERROR(0x3)},
+       {NT_STATUS(0xc000003b), W_ERROR(0xa1)},
+       {NT_STATUS(0xc000003c), W_ERROR(0x45d)},
+       {NT_STATUS(0xc000003d), W_ERROR(0x45d)},
+       {NT_STATUS(0xc000003e), W_ERROR(0x17)},
+       {NT_STATUS(0xc000003f), W_ERROR(0x17)},
+       {NT_STATUS(0xc0000040), W_ERROR(0x8)},
+       {NT_STATUS(0xc0000041), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000042), W_ERROR(0x6)},
+       {NT_STATUS(0xc0000043), W_ERROR(0x20)},
+       {NT_STATUS(0xc0000044), W_ERROR(0x718)},
+       {NT_STATUS(0xc0000045), W_ERROR(0x57)},
+       {NT_STATUS(0xc0000046), W_ERROR(0x120)},
+       {NT_STATUS(0xc0000047), W_ERROR(0x12a)},
+       {NT_STATUS(0xc0000048), W_ERROR(0x57)},
+       {NT_STATUS(0xc0000049), W_ERROR(0x57)},
+       {NT_STATUS(0xc000004a), W_ERROR(0x9c)},
+       {NT_STATUS(0xc000004b), W_ERROR(0x5)},
+       {NT_STATUS(0xc000004c), W_ERROR(0x57)},
+       {NT_STATUS(0xc000004d), W_ERROR(0x57)},
+       {NT_STATUS(0xc000004e), W_ERROR(0x57)},
+       {NT_STATUS(0xc000004f), W_ERROR(0x11a)},
+       {NT_STATUS(0xc0000050), W_ERROR(0xff)},
+       {NT_STATUS(0xc0000051), W_ERROR(0x570)},
+       {NT_STATUS(0xc0000052), W_ERROR(0x570)},
+       {NT_STATUS(0xc0000053), W_ERROR(0x570)},
+       {NT_STATUS(0xc0000054), W_ERROR(0x21)},
+       {NT_STATUS(0xc0000055), W_ERROR(0x21)},
+       {NT_STATUS(0xc0000056), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000057), W_ERROR(0x32)},
+       {NT_STATUS(0xc0000058), W_ERROR(0x519)},
+       {NT_STATUS(0xc0000059), W_ERROR(0x51a)},
+       {NT_STATUS(0xc000005a), W_ERROR(0x51b)},
+       {NT_STATUS(0xc000005b), W_ERROR(0x51c)},
+       {NT_STATUS(0xc000005c), W_ERROR(0x51d)},
+       {NT_STATUS(0xc000005d), W_ERROR(0x51e)},
+       {NT_STATUS(0xc000005e), W_ERROR(0x51f)},
+       {NT_STATUS(0xc000005f), W_ERROR(0x520)},
+       {NT_STATUS(0xc0000060), W_ERROR(0x521)},
+       {NT_STATUS(0xc0000061), W_ERROR(0x522)},
+       {NT_STATUS(0xc0000062), W_ERROR(0x523)},
+       {NT_STATUS(0xc0000063), W_ERROR(0x524)},
+       {NT_STATUS(0xc0000064), W_ERROR(0x525)},
+       {NT_STATUS(0xc0000065), W_ERROR(0x526)},
+       {NT_STATUS(0xc0000066), W_ERROR(0x527)},
+       {NT_STATUS(0xc0000067), W_ERROR(0x528)},
+       {NT_STATUS(0xc0000068), W_ERROR(0x529)},
+       {NT_STATUS(0xc0000069), W_ERROR(0x52a)},
+       {NT_STATUS(0xc000006a), W_ERROR(0x56)},
+       {NT_STATUS(0xc000006b), W_ERROR(0x52c)},
+       {NT_STATUS(0xc000006c), W_ERROR(0x52d)},
+       {NT_STATUS(0xc000006d), W_ERROR(0x52e)},
+       {NT_STATUS(0xc000006e), W_ERROR(0x52f)},
+       {NT_STATUS(0xc000006f), W_ERROR(0x530)},
+       {NT_STATUS(0xc0000070), W_ERROR(0x531)},
+       {NT_STATUS(0xc0000071), W_ERROR(0x532)},
+       {NT_STATUS(0xc0000072), W_ERROR(0x533)},
+       {NT_STATUS(0xc0000073), W_ERROR(0x534)},
+       {NT_STATUS(0xc0000074), W_ERROR(0x535)},
+       {NT_STATUS(0xc0000075), W_ERROR(0x536)},
+       {NT_STATUS(0xc0000076), W_ERROR(0x537)},
+       {NT_STATUS(0xc0000077), W_ERROR(0x538)},
+       {NT_STATUS(0xc0000078), W_ERROR(0x539)},
+       {NT_STATUS(0xc0000079), W_ERROR(0x53a)},
+       {NT_STATUS(0xc000007a), W_ERROR(0x7f)},
+       {NT_STATUS(0xc000007b), W_ERROR(0xc1)},
+       {NT_STATUS(0xc000007c), W_ERROR(0x3f0)},
+       {NT_STATUS(0xc000007d), W_ERROR(0x53c)},
+       {NT_STATUS(0xc000007e), W_ERROR(0x9e)},
+       {NT_STATUS(0xc000007f), W_ERROR(0x70)},
+       {NT_STATUS(0xc0000080), W_ERROR(0x53d)},
+       {NT_STATUS(0xc0000081), W_ERROR(0x53e)},
+       {NT_STATUS(0xc0000082), W_ERROR(0x44)},
+       {NT_STATUS(0xc0000083), W_ERROR(0x103)},
+       {NT_STATUS(0xc0000084), W_ERROR(0x53f)},
+       {NT_STATUS(0xc0000085), W_ERROR(0x103)},
+       {NT_STATUS(0xc0000086), W_ERROR(0x9a)},
+       {NT_STATUS(0xc0000087), W_ERROR(0xe)},
+       {NT_STATUS(0xc0000088), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc0000089), W_ERROR(0x714)},
+       {NT_STATUS(0xc000008a), W_ERROR(0x715)},
+       {NT_STATUS(0xc000008b), W_ERROR(0x716)},
+       {NT_STATUS(0xc000008c), W_ERROR(0xc000008c)},
+       {NT_STATUS(0xc000008d), W_ERROR(0xc000008d)},
+       {NT_STATUS(0xc000008e), W_ERROR(0xc000008e)},
+       {NT_STATUS(0xc000008f), W_ERROR(0xc000008f)},
+       {NT_STATUS(0xc0000090), W_ERROR(0xc0000090)},
+       {NT_STATUS(0xc0000091), W_ERROR(0xc0000091)},
+       {NT_STATUS(0xc0000092), W_ERROR(0xc0000092)},
+       {NT_STATUS(0xc0000093), W_ERROR(0xc0000093)},
+       {NT_STATUS(0xc0000094), W_ERROR(0xc0000094)},
+       {NT_STATUS(0xc0000095), W_ERROR(0x216)},
+       {NT_STATUS(0xc0000096), W_ERROR(0xc0000096)},
+       {NT_STATUS(0xc0000097), W_ERROR(0x8)},
+       {NT_STATUS(0xc0000098), W_ERROR(0x3ee)},
+       {NT_STATUS(0xc0000099), W_ERROR(0x540)},
+       {NT_STATUS(0xc000009a), W_ERROR(0x5aa)},
+       {NT_STATUS(0xc000009b), W_ERROR(0x3)},
+       {NT_STATUS(0xc000009c), W_ERROR(0x17)},
+       {NT_STATUS(0xc000009d), W_ERROR(0x48f)},
+       {NT_STATUS(0xc000009e), W_ERROR(0x15)},
+       {NT_STATUS(0xc000009f), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc00000a0), W_ERROR(0x1e7)},
+       {NT_STATUS(0xc00000a1), W_ERROR(0x5ad)},
+       {NT_STATUS(0xc00000a2), W_ERROR(0x13)},
+       {NT_STATUS(0xc00000a3), W_ERROR(0x15)},
+       {NT_STATUS(0xc00000a4), W_ERROR(0x541)},
+       {NT_STATUS(0xc00000a5), W_ERROR(0x542)},
+       {NT_STATUS(0xc00000a6), W_ERROR(0x543)},
+       {NT_STATUS(0xc00000a7), W_ERROR(0x544)},
+       {NT_STATUS(0xc00000a8), W_ERROR(0x545)},
+       {NT_STATUS(0xc00000a9), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000ab), W_ERROR(0xe7)},
+       {NT_STATUS(0xc00000ac), W_ERROR(0xe7)},
+       {NT_STATUS(0xc00000ad), W_ERROR(0xe6)},
+       {NT_STATUS(0xc00000ae), W_ERROR(0xe7)},
+       {NT_STATUS(0xc00000af), W_ERROR(0x1)},
+       {NT_STATUS(0xc00000b0), W_ERROR(0xe9)},
+       {NT_STATUS(0xc00000b1), W_ERROR(0xe8)},
+       {NT_STATUS(0xc00000b2), W_ERROR(0x217)},
+       {NT_STATUS(0xc00000b3), W_ERROR(0x218)},
+       {NT_STATUS(0xc00000b4), W_ERROR(0xe6)},
+       {NT_STATUS(0xc00000b5), W_ERROR(0x79)},
+       {NT_STATUS(0xc00000b6), W_ERROR(0x26)},
+       {NT_STATUS(0xc00000ba), W_ERROR(0x5)},
+       {NT_STATUS(0xc00000bb), W_ERROR(0x32)},
+       {NT_STATUS(0xc00000bc), W_ERROR(0x33)},
+       {NT_STATUS(0xc00000bd), W_ERROR(0x34)},
+       {NT_STATUS(0xc00000be), W_ERROR(0x35)},
+       {NT_STATUS(0xc00000bf), W_ERROR(0x36)},
+       {NT_STATUS(0xc00000c0), W_ERROR(0x37)},
+       {NT_STATUS(0xc00000c1), W_ERROR(0x38)},
+       {NT_STATUS(0xc00000c2), W_ERROR(0x39)},
+       {NT_STATUS(0xc00000c3), W_ERROR(0x3a)},
+       {NT_STATUS(0xc00000c4), W_ERROR(0x3b)},
+       {NT_STATUS(0xc00000c5), W_ERROR(0x3c)},
+       {NT_STATUS(0xc00000c6), W_ERROR(0x3d)},
+       {NT_STATUS(0xc00000c7), W_ERROR(0x3e)},
+       {NT_STATUS(0xc00000c8), W_ERROR(0x3f)},
+       {NT_STATUS(0xc00000c9), W_ERROR(0x40)},
+       {NT_STATUS(0xc00000ca), W_ERROR(0x41)},
+       {NT_STATUS(0xc00000cb), W_ERROR(0x42)},
+       {NT_STATUS(0xc00000cc), W_ERROR(0x43)},
+       {NT_STATUS(0xc00000cd), W_ERROR(0x44)},
+       {NT_STATUS(0xc00000ce), W_ERROR(0x45)},
+       {NT_STATUS(0xc00000cf), W_ERROR(0x46)},
+       {NT_STATUS(0xc00000d0), W_ERROR(0x47)},
+       {NT_STATUS(0xc00000d1), W_ERROR(0x48)},
+       {NT_STATUS(0xc00000d2), W_ERROR(0x58)},
+       {NT_STATUS(0xc00000d4), W_ERROR(0x11)},
+       {NT_STATUS(0xc00000d5), W_ERROR(0x5)},
+       {NT_STATUS(0xc00000d6), W_ERROR(0xf0)},
+       {NT_STATUS(0xc00000d7), W_ERROR(0x546)},
+       {NT_STATUS(0xc00000d9), W_ERROR(0xe8)},
+       {NT_STATUS(0xc00000da), W_ERROR(0x547)},
+       {NT_STATUS(0xc00000dc), W_ERROR(0x548)},
+       {NT_STATUS(0xc00000dd), W_ERROR(0x549)},
+       {NT_STATUS(0xc00000de), W_ERROR(0x54a)},
+       {NT_STATUS(0xc00000df), W_ERROR(0x54b)},
+       {NT_STATUS(0xc00000e0), W_ERROR(0x54c)},
+       {NT_STATUS(0xc00000e1), W_ERROR(0x54d)},
+       {NT_STATUS(0xc00000e2), W_ERROR(0x12c)},
+       {NT_STATUS(0xc00000e3), W_ERROR(0x12d)},
+       {NT_STATUS(0xc00000e4), W_ERROR(0x54e)},
+       {NT_STATUS(0xc00000e5), W_ERROR(0x54f)},
+       {NT_STATUS(0xc00000e6), W_ERROR(0x550)},
+       {NT_STATUS(0xc00000e7), W_ERROR(0x551)},
+       {NT_STATUS(0xc00000e8), W_ERROR(0x6f8)},
+       {NT_STATUS(0xc00000ed), W_ERROR(0x552)},
+       {NT_STATUS(0xc00000ee), W_ERROR(0x553)},
+       {NT_STATUS(0xc00000ef), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f0), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f1), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f2), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f3), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f4), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f5), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f6), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f7), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f8), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000f9), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000fa), W_ERROR(0x57)},
+       {NT_STATUS(0xc00000fb), W_ERROR(0x3)},
+       {NT_STATUS(0xc00000fd), W_ERROR(0x3e9)},
+       {NT_STATUS(0xc00000fe), W_ERROR(0x554)},
+       {NT_STATUS(0xc0000100), W_ERROR(0xcb)},
+       {NT_STATUS(0xc0000101), W_ERROR(0x91)},
+       {NT_STATUS(0xc0000102), W_ERROR(0x570)},
+       {NT_STATUS(0xc0000103), W_ERROR(0x10b)},
+       {NT_STATUS(0xc0000104), W_ERROR(0x555)},
+       {NT_STATUS(0xc0000105), W_ERROR(0x556)},
+       {NT_STATUS(0xc0000106), W_ERROR(0xce)},
+       {NT_STATUS(0xc0000107), W_ERROR(0x961)},
+       {NT_STATUS(0xc0000108), W_ERROR(0x964)},
+       {NT_STATUS(0xc000010a), W_ERROR(0x5)},
+       {NT_STATUS(0xc000010b), W_ERROR(0x557)},
+       {NT_STATUS(0xc000010d), W_ERROR(0x558)},
+       {NT_STATUS(0xc000010e), W_ERROR(0x420)},
+       {NT_STATUS(0xc0000117), W_ERROR(0x5a4)},
+       {NT_STATUS(0xc000011b), W_ERROR(0xc1)},
+       {NT_STATUS(0xc000011c), W_ERROR(0x559)},
+       {NT_STATUS(0xc000011d), W_ERROR(0x55a)},
+       {NT_STATUS(0xc000011e), W_ERROR(0x3ee)},
+       {NT_STATUS(0xc000011f), W_ERROR(0x4)},
+       {NT_STATUS(0xc0000120), W_ERROR(0x3e3)},
+       {NT_STATUS(0xc0000121), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000122), W_ERROR(0x4ba)},
+       {NT_STATUS(0xc0000123), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000124), W_ERROR(0x55b)},
+       {NT_STATUS(0xc0000125), W_ERROR(0x55c)},
+       {NT_STATUS(0xc0000126), W_ERROR(0x55d)},
+       {NT_STATUS(0xc0000127), W_ERROR(0x55e)},
+       {NT_STATUS(0xc0000128), W_ERROR(0x6)},
+       {NT_STATUS(0xc000012b), W_ERROR(0x55f)},
+       {NT_STATUS(0xc000012d), W_ERROR(0x5af)},
+       {NT_STATUS(0xc000012e), W_ERROR(0xc1)},
+       {NT_STATUS(0xc000012f), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000130), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000131), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000133), W_ERROR(0x576)},
+       {NT_STATUS(0xc0000135), W_ERROR(0x7e)},
+       {NT_STATUS(0xc0000138), W_ERROR(0xb6)},
+       {NT_STATUS(0xc0000139), W_ERROR(0x7f)},
+       {NT_STATUS(0xc000013b), W_ERROR(0x40)},
+       {NT_STATUS(0xc000013c), W_ERROR(0x40)},
+       {NT_STATUS(0xc000013d), W_ERROR(0x33)},
+       {NT_STATUS(0xc000013e), W_ERROR(0x3b)},
+       {NT_STATUS(0xc000013f), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000140), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000141), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000142), W_ERROR(0x45a)},
+       {NT_STATUS(0xc0000148), W_ERROR(0x7c)},
+       {NT_STATUS(0xc0000149), W_ERROR(0x56)},
+       {NT_STATUS(0xc000014b), W_ERROR(0x6d)},
+       {NT_STATUS(0xc000014c), W_ERROR(0x3f1)},
+       {NT_STATUS(0xc000014d), W_ERROR(0x3f8)},
+       {NT_STATUS(0xc000014f), W_ERROR(0x3ed)},
+       {NT_STATUS(0xc0000150), W_ERROR(0x45e)},
+       {NT_STATUS(0xc0000151), W_ERROR(0x560)},
+       {NT_STATUS(0xc0000152), W_ERROR(0x561)},
+       {NT_STATUS(0xc0000153), W_ERROR(0x562)},
+       {NT_STATUS(0xc0000154), W_ERROR(0x563)},
+       {NT_STATUS(0xc0000155), W_ERROR(0x564)},
+       {NT_STATUS(0xc0000156), W_ERROR(0x565)},
+       {NT_STATUS(0xc0000157), W_ERROR(0x566)},
+       {NT_STATUS(0xc0000158), W_ERROR(0x567)},
+       {NT_STATUS(0xc0000159), W_ERROR(0x3ef)},
+       {NT_STATUS(0xc000015a), W_ERROR(0x568)},
+       {NT_STATUS(0xc000015b), W_ERROR(0x569)},
+       {NT_STATUS(0xc000015c), W_ERROR(0x3f9)},
+       {NT_STATUS(0xc000015d), W_ERROR(0x56a)},
+       {NT_STATUS(0xc000015f), W_ERROR(0x45d)},
+       {NT_STATUS(0xc0000162), W_ERROR(0x459)},
+       {NT_STATUS(0xc0000165), W_ERROR(0x462)},
+       {NT_STATUS(0xc0000166), W_ERROR(0x463)},
+       {NT_STATUS(0xc0000167), W_ERROR(0x464)},
+       {NT_STATUS(0xc0000168), W_ERROR(0x465)},
+       {NT_STATUS(0xc0000169), W_ERROR(0x466)},
+       {NT_STATUS(0xc000016a), W_ERROR(0x467)},
+       {NT_STATUS(0xc000016b), W_ERROR(0x468)},
+       {NT_STATUS(0xc000016c), W_ERROR(0x45f)},
+       {NT_STATUS(0xc000016d), W_ERROR(0x45d)},
+       {NT_STATUS(0xc0000172), W_ERROR(0x451)},
+       {NT_STATUS(0xc0000173), W_ERROR(0x452)},
+       {NT_STATUS(0xc0000174), W_ERROR(0x453)},
+       {NT_STATUS(0xc0000175), W_ERROR(0x454)},
+       {NT_STATUS(0xc0000176), W_ERROR(0x455)},
+       {NT_STATUS(0xc0000177), W_ERROR(0x469)},
+       {NT_STATUS(0xc0000178), W_ERROR(0x458)},
+       {NT_STATUS(0xc000017a), W_ERROR(0x56b)},
+       {NT_STATUS(0xc000017b), W_ERROR(0x56c)},
+       {NT_STATUS(0xc000017c), W_ERROR(0x3fa)},
+       {NT_STATUS(0xc000017d), W_ERROR(0x3fb)},
+       {NT_STATUS(0xc000017e), W_ERROR(0x56d)},
+       {NT_STATUS(0xc000017f), W_ERROR(0x56e)},
+       {NT_STATUS(0xc0000180), W_ERROR(0x3fc)},
+       {NT_STATUS(0xc0000181), W_ERROR(0x3fd)},
+       {NT_STATUS(0xc0000182), W_ERROR(0x57)},
+       {NT_STATUS(0xc0000183), W_ERROR(0x45d)},
+       {NT_STATUS(0xc0000184), W_ERROR(0x16)},
+       {NT_STATUS(0xc0000185), W_ERROR(0x45d)},
+       {NT_STATUS(0xc0000186), W_ERROR(0x45d)},
+       {NT_STATUS(0xc0000188), W_ERROR(0x5de)},
+       {NT_STATUS(0xc0000189), W_ERROR(0x13)},
+       {NT_STATUS(0xc000018a), W_ERROR(0x6fa)},
+       {NT_STATUS(0xc000018b), W_ERROR(0x6fb)},
+       {NT_STATUS(0xc000018c), W_ERROR(0x6fc)},
+       {NT_STATUS(0xc000018d), W_ERROR(0x6fd)},
+       {NT_STATUS(0xc000018e), W_ERROR(0x5dc)},
+       {NT_STATUS(0xc000018f), W_ERROR(0x5dd)},
+       {NT_STATUS(0xc0000190), W_ERROR(0x6fe)},
+       {NT_STATUS(0xc0000192), W_ERROR(0x700)},
+       {NT_STATUS(0xc0000193), W_ERROR(0x701)},
+       {NT_STATUS(0xc0000194), W_ERROR(0x46b)},
+       {NT_STATUS(0xc0000195), W_ERROR(0x4c3)},
+       {NT_STATUS(0xc0000196), W_ERROR(0x4c4)},
+       {NT_STATUS(0xc0000197), W_ERROR(0x5df)},
+       {NT_STATUS(0xc0000198), W_ERROR(0x70f)},
+       {NT_STATUS(0xc0000199), W_ERROR(0x710)},
+       {NT_STATUS(0xc000019a), W_ERROR(0x711)},
+       {NT_STATUS(0xc000019b), W_ERROR(0x712)},
+       {NT_STATUS(0xc0000202), W_ERROR(0x572)},
+       {NT_STATUS(0xc0000203), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000204), W_ERROR(0x717)},
+       {NT_STATUS(0xc0000205), W_ERROR(0x46a)},
+       {NT_STATUS(0xc0000206), W_ERROR(0x6f8)},
+       {NT_STATUS(0xc0000207), W_ERROR(0x4be)},
+       {NT_STATUS(0xc0000208), W_ERROR(0x4be)},
+       {NT_STATUS(0xc0000209), W_ERROR(0x44)},
+       {NT_STATUS(0xc000020a), W_ERROR(0x34)},
+       {NT_STATUS(0xc000020b), W_ERROR(0x40)},
+       {NT_STATUS(0xc000020c), W_ERROR(0x40)},
+       {NT_STATUS(0xc000020d), W_ERROR(0x40)},
+       {NT_STATUS(0xc000020e), W_ERROR(0x44)},
+       {NT_STATUS(0xc000020f), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000210), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000211), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000212), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000213), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000214), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000215), W_ERROR(0x3b)},
+       {NT_STATUS(0xc0000216), W_ERROR(0x32)},
+       {NT_STATUS(0xc0000217), W_ERROR(0x32)},
+       {NT_STATUS(0xc000021c), W_ERROR(0x17e6)},
+       {NT_STATUS(0xc0000220), W_ERROR(0x46c)},
+       {NT_STATUS(0xc0000221), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000224), W_ERROR(0x773)},
+       {NT_STATUS(0xc0000225), W_ERROR(0x490)},
+       {NT_STATUS(0xc000022a), W_ERROR(0xc000022a)},
+       {NT_STATUS(0xc000022b), W_ERROR(0xc000022b)},
+       {NT_STATUS(0xc000022d), W_ERROR(0x4d5)},
+       {NT_STATUS(0xc0000230), W_ERROR(0x492)},
+       {NT_STATUS(0xc0000233), W_ERROR(0x774)},
+       {NT_STATUS(0xc0000234), W_ERROR(0x775)},
+       {NT_STATUS(0xc0000235), W_ERROR(0x6)},
+       {NT_STATUS(0xc0000236), W_ERROR(0x4c9)},
+       {NT_STATUS(0xc0000237), W_ERROR(0x4ca)},
+       {NT_STATUS(0xc0000238), W_ERROR(0x4cb)},
+       {NT_STATUS(0xc0000239), W_ERROR(0x4cc)},
+       {NT_STATUS(0xc000023a), W_ERROR(0x4cd)},
+       {NT_STATUS(0xc000023b), W_ERROR(0x4ce)},
+       {NT_STATUS(0xc000023c), W_ERROR(0x4cf)},
+       {NT_STATUS(0xc000023d), W_ERROR(0x4d0)},
+       {NT_STATUS(0xc000023e), W_ERROR(0x4d1)},
+       {NT_STATUS(0xc000023f), W_ERROR(0x4d2)},
+       {NT_STATUS(0xc0000240), W_ERROR(0x4d3)},
+       {NT_STATUS(0xc0000241), W_ERROR(0x4d4)},
+       {NT_STATUS(0xc0000243), W_ERROR(0x4c8)},
+       {NT_STATUS(0xc0000246), W_ERROR(0x4d6)},
+       {NT_STATUS(0xc0000247), W_ERROR(0x4d7)},
+       {NT_STATUS(0xc0000248), W_ERROR(0x4d8)},
+       {NT_STATUS(0xc0000249), W_ERROR(0xc1)},
+       {NT_STATUS(0xc0000253), W_ERROR(0x54f)},
+       {NT_STATUS(0xc0000257), W_ERROR(0x4d0)},
+       {NT_STATUS(0xc0000259), W_ERROR(0x573)},
+       {NT_STATUS(0xc000025e), W_ERROR(0x422)},
+       {NT_STATUS(0xc0000262), W_ERROR(0xb6)},
+       {NT_STATUS(0xc0000263), W_ERROR(0x7f)},
+       {NT_STATUS(0xc0000264), W_ERROR(0x120)},
+       {NT_STATUS(0xc0000265), W_ERROR(0x476)},
+       {NT_STATUS(0xc0000267), W_ERROR(0x10fe)},
+       {NT_STATUS(0xc000026c), W_ERROR(0x7d1)},
+       {NT_STATUS(0xc000026d), W_ERROR(0x4b1)},
+       {NT_STATUS(0xc000026e), W_ERROR(0x15)},
+       {NT_STATUS(0xc0000272), W_ERROR(0x491)},
+       {NT_STATUS(0xc0000275), W_ERROR(0x1126)},
+       {NT_STATUS(0xc0000276), W_ERROR(0x1129)},
+       {NT_STATUS(0xc0000277), W_ERROR(0x112a)},
+       {NT_STATUS(0xc0000278), W_ERROR(0x1128)},
+       {NT_STATUS(0xc0000279), W_ERROR(0x780)},
+       {NT_STATUS(0xc0000280), W_ERROR(0x781)},
+       {NT_STATUS(0xc0000281), W_ERROR(0xa1)},
+       {NT_STATUS(0xc0000283), W_ERROR(0x488)},
+       {NT_STATUS(0xc0000284), W_ERROR(0x489)},
+       {NT_STATUS(0xc0000285), W_ERROR(0x48a)},
+       {NT_STATUS(0xc0000286), W_ERROR(0x48b)},
+       {NT_STATUS(0xc0000287), W_ERROR(0x48c)},
+       {NT_STATUS(0xc000028a), W_ERROR(0x5)},
+       {NT_STATUS(0xc000028b), W_ERROR(0x5)},
+       {NT_STATUS(0xc000028d), W_ERROR(0x5)},
+       {NT_STATUS(0xc000028e), W_ERROR(0x5)},
+       {NT_STATUS(0xc000028f), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000290), W_ERROR(0x5)},
+       {NT_STATUS(0xc0000291), W_ERROR(0x1777)},
+       {NT_STATUS(0xc0000292), W_ERROR(0x1778)},
+       {NT_STATUS(0xc0000293), W_ERROR(0x1772)},
+       {NT_STATUS(0xc0000295), W_ERROR(0x1068)},
+       {NT_STATUS(0xc0000296), W_ERROR(0x1069)},
+       {NT_STATUS(0xc0000297), W_ERROR(0x106a)},
+       {NT_STATUS(0xc0000298), W_ERROR(0x106b)},
+       {NT_STATUS(0xc0000299), W_ERROR(0x201a)},
+       {NT_STATUS(0xc000029a), W_ERROR(0x201b)},
+       {NT_STATUS(0xc000029b), W_ERROR(0x201c)},
+       {NT_STATUS(0xc000029c), W_ERROR(0x1)},
+       {NT_STATUS(0xc000029d), W_ERROR(0x10ff)},
+       {NT_STATUS(0xc000029e), W_ERROR(0x1100)},
+       {NT_STATUS(0xc000029f), W_ERROR(0x494)},
+       {NT_STATUS(0xc00002a1), W_ERROR(0x200a)},
+       {NT_STATUS(0xc00002a2), W_ERROR(0x200b)},
+       {NT_STATUS(0xc00002a3), W_ERROR(0x200c)},
+       {NT_STATUS(0xc00002a4), W_ERROR(0x200d)},
+       {NT_STATUS(0xc00002a5), W_ERROR(0x200e)},
+       {NT_STATUS(0xc00002a6), W_ERROR(0x200f)},
+       {NT_STATUS(0xc00002a7), W_ERROR(0x2010)},
+       {NT_STATUS(0xc00002a8), W_ERROR(0x2011)},
+       {NT_STATUS(0xc00002a9), W_ERROR(0x2012)},
+       {NT_STATUS(0xc00002aa), W_ERROR(0x2013)},
+       {NT_STATUS(0xc00002ab), W_ERROR(0x2014)},
+       {NT_STATUS(0xc00002ac), W_ERROR(0x2015)},
+       {NT_STATUS(0xc00002ad), W_ERROR(0x2016)},
+       {NT_STATUS(0xc00002ae), W_ERROR(0x2017)},
+       {NT_STATUS(0xc00002af), W_ERROR(0x2018)},
+       {NT_STATUS(0xc00002b0), W_ERROR(0x2019)},
+       {NT_STATUS(0xc00002b1), W_ERROR(0x211e)},
+       {NT_STATUS(0xc00002b2), W_ERROR(0x1127)},
+       {NT_STATUS(0xc00002b6), W_ERROR(0x651)},
+       {NT_STATUS(0xc00002b7), W_ERROR(0x49a)},
+       {NT_STATUS(0xc00002b8), W_ERROR(0x49b)},
+       {NT_STATUS(0xc00002c1), W_ERROR(0x2024)},
+       {NT_STATUS(0xc00002c3), W_ERROR(0x575)},
+       {NT_STATUS(0xc00002c5), W_ERROR(0x3e6)},
+       {NT_STATUS(0xc00002c6), W_ERROR(0x1075)},
+       {NT_STATUS(0xc00002c7), W_ERROR(0x1076)},
+       {NT_STATUS(0xc00002ca), W_ERROR(0x10e8)},
+       {NT_STATUS(0xc00002cb), W_ERROR(0x2138)},
+       {NT_STATUS(0xc00002cc), W_ERROR(0x4e3)},
+       {NT_STATUS(0xc00002cd), W_ERROR(0x2139)},
+       {NT_STATUS(0xc00002cf), W_ERROR(0x49d)},
+       {NT_STATUS(0xc00002d0), W_ERROR(0x213a)},
+       {NT_STATUS(0xc00002d4), W_ERROR(0x2141)},
+       {NT_STATUS(0xc00002d5), W_ERROR(0x2142)},
+       {NT_STATUS(0xc00002d6), W_ERROR(0x2143)},
+       {NT_STATUS(0xc00002d7), W_ERROR(0x2144)},
+       {NT_STATUS(0xc00002d8), W_ERROR(0x2145)},
+       {NT_STATUS(0xc00002d9), W_ERROR(0x2146)},
+       {NT_STATUS(0xc00002da), W_ERROR(0x2147)},
+       {NT_STATUS(0xc00002db), W_ERROR(0x2148)},
+       {NT_STATUS(0xc00002dc), W_ERROR(0x2149)},
+       {NT_STATUS(0xc00002dd), W_ERROR(0x32)},
+       {NT_STATUS(0xc00002df), W_ERROR(0x2151)},
+       {NT_STATUS(0xc00002e0), W_ERROR(0x2152)},
+       {NT_STATUS(0xc00002e1), W_ERROR(0x2153)},
+       {NT_STATUS(0xc00002e2), W_ERROR(0x2154)},
+       {NT_STATUS(0xc00002e3), W_ERROR(0x215d)},
+       {NT_STATUS(0xc00002e4), W_ERROR(0x2163)},
+       {NT_STATUS(0xc00002e5), W_ERROR(0x2164)},
+       {NT_STATUS(0xc00002e6), W_ERROR(0x2165)},
+       {NT_STATUS(0xc00002e7), W_ERROR(0x216d)},
+       {NT_STATUS(0xc00002fe), W_ERROR(0x45b)},
+       {NT_STATUS(0xc00002ff), W_ERROR(0x4e7)},
+       {NT_STATUS(0xc0000300), W_ERROR(0x4e6)},
+       {NT_STATUS(0x80000001), W_ERROR(0x80000001)},
+       {NT_STATUS(0x80000002), W_ERROR(0x3e6)},
+       {NT_STATUS(0x80000003), W_ERROR(0x80000003)},
+       {NT_STATUS(0x80000004), W_ERROR(0x80000004)},
+       {NT_STATUS(0x80000005), W_ERROR(0xea)},
+       {NT_STATUS(0x80000006), W_ERROR(0x12)},
+       {NT_STATUS(0x8000000b), W_ERROR(0x56f)},
+       {NT_STATUS(0x8000000d), W_ERROR(0x12b)},
+       {NT_STATUS(0x8000000e), W_ERROR(0x1c)},
+       {NT_STATUS(0x8000000f), W_ERROR(0x15)},
+       {NT_STATUS(0x80000010), W_ERROR(0x15)},
+       {NT_STATUS(0x80000011), W_ERROR(0xaa)},
+       {NT_STATUS(0x80000012), W_ERROR(0x103)},
+       {NT_STATUS(0x80000013), W_ERROR(0xfe)},
+       {NT_STATUS(0x80000014), W_ERROR(0xff)},
+       {NT_STATUS(0x80000015), W_ERROR(0xff)},
+       {NT_STATUS(0x80000016), W_ERROR(0x456)},
+       {NT_STATUS(0x8000001a), W_ERROR(0x103)},
+       {NT_STATUS(0x8000001b), W_ERROR(0x44d)},
+       {NT_STATUS(0x8000001c), W_ERROR(0x456)},
+       {NT_STATUS(0x8000001d), W_ERROR(0x457)},
+       {NT_STATUS(0x8000001e), W_ERROR(0x44c)},
+       {NT_STATUS(0x8000001f), W_ERROR(0x44e)},
+       {NT_STATUS(0x80000021), W_ERROR(0x44f)},
+       {NT_STATUS(0x80000022), W_ERROR(0x450)},
+       {NT_STATUS(0x80000025), W_ERROR(0x962)},
+       {NT_STATUS(0x80000288), W_ERROR(0x48d)},
+       {NT_STATUS(0x80000289), W_ERROR(0x48e)},
+       {NT_STATUS_OK, WERR_OK}};
+
+
+/*****************************************************************************
+convert a dos eclas/ecode to a NT status32 code
+ *****************************************************************************/
+NTSTATUS dos_to_ntstatus(uint8 eclass, uint32 ecode)
+{
+       int i;
+       if (eclass == 0 && ecode == 0) return NT_STATUS_OK;
+       for (i=0; NT_STATUS_V(dos_to_ntstatus_map[i].ntstatus); i++) {
+               if (eclass == dos_to_ntstatus_map[i].dos_class &&
+                   ecode == dos_to_ntstatus_map[i].dos_code) {
+                       return dos_to_ntstatus_map[i].ntstatus;
+               }
+       }
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+void ntstatus_to_dos(NTSTATUS ntstatus, uint8 *eclass, uint32 *ecode)
+{
+       int i;
+       if (NT_STATUS_IS_OK(ntstatus)) {
+               *eclass = 0;
+               *ecode = 0;
+               return;
+       }
+       for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) {
+               if (NT_STATUS_V(ntstatus) == 
+                   NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) {
+                       *eclass = ntstatus_to_dos_map[i].dos_class;
+                       *ecode = ntstatus_to_dos_map[i].dos_code;
+                       return;
+               }
+       }
+       *eclass = ERRHRD;
+       *ecode = ERRgeneral;
+}
+
+
+/*****************************************************************************
+convert a WERROR to a NT status32 code
+ *****************************************************************************/
+NTSTATUS werror_to_ntstatus(WERROR error)
+{
+       int i;
+       if (W_ERROR_IS_OK(error)) return NT_STATUS_OK;
+       for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+               if (W_ERROR_V(error) == 
+                   W_ERROR_V(ntstatus_to_werror_map[i].werror)) {
+                       return ntstatus_to_werror_map[i].ntstatus;
+               }
+       }
+
+       /* just guess ... */
+       return NT_STATUS(W_ERROR_V(error) | 0xc0000000);
+}
+
+/*****************************************************************************
+convert a NTSTATUS to a WERROR
+ *****************************************************************************/
+WERROR ntstatus_to_werror(NTSTATUS error)
+{
+       int i;
+       if (NT_STATUS_IS_OK(error)) return WERR_OK;
+       for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+               if (NT_STATUS_V(error) == 
+                   NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) {
+                       return ntstatus_to_werror_map[i].werror;
+               }
+       }
+
+       /* a lame guess */
+       return W_ERROR(NT_STATUS_V(error) & 0xffff);
+}
+
+/* Mapping between Unix, DOS and NT error numbers */
+
+const struct unix_error_map unix_dos_nt_errmap[] = {
+       { EPERM, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+       { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+       { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+       { ENOTDIR, ERRDOS, ERRbadpath,  NT_STATUS_OBJECT_PATH_NOT_FOUND },
+       { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR },
+       { EBADF, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+       { EINVAL, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+       { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+       { ENFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+       { EMFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+       { ENOSPC, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+       { EISDIR, ERRDOS, ERRbadpath, NT_STATUS_FILE_IS_A_DIRECTORY },
+#ifdef EDQUOT
+       { EDQUOT, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+#ifdef ENOTEMPTY
+       { ENOTEMPTY, ERRDOS, ERRnoaccess, NT_STATUS_DIRECTORY_NOT_EMPTY },
+#endif
+#ifdef EXDEV
+       { EXDEV, ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE },
+#endif
+#ifdef EROFS
+       { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED },
+#endif
+#ifdef ENAMETOOLONG
+       { ENAMETOOLONG, ERRDOS, 206, NT_STATUS_OBJECT_NAME_INVALID },
+#endif
+#ifdef EFBIG
+       { EFBIG, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+#ifdef EFBIG
+       { EBUSY, ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION },
+#endif
+       { 0, 0, 0, NT_STATUS_OK }
+};
+
+/*********************************************************************
+ Map an NT error code from a Unix error code.
+*********************************************************************/
+
+NTSTATUS map_nt_error_from_unix(int unix_error)
+{
+       int i = 0;
+
+       if (unix_error == 0)
+               return NT_STATUS_OK;
+
+       /* Look through list */
+       while(unix_dos_nt_errmap[i].unix_error != 0) {
+               if (unix_dos_nt_errmap[i].unix_error == unix_error)
+                       return unix_dos_nt_errmap[i].nt_error;
+               i++;
+       }
+
+       /* Default return */
+       return NT_STATUS_ACCESS_DENIED;
+}
diff --git a/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c
new file mode 100644 (file)
index 0000000..6c4b7c8
--- /dev/null
@@ -0,0 +1,715 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
+ *  
+ *  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.
+ */
+
+/* NT error codes.  please read nterr.h */
+
+#include "includes.h"
+
+typedef struct
+{
+       const char *nt_errstr;
+       NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+static const nt_err_code_struct nt_errs[] =
+{
+       { "NT_STATUS_OK", NT_STATUS_OK },
+       { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL },
+       { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED },
+       { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS },
+       { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH },
+       { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION },
+       { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW },
+       { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR },
+       { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA },
+       { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE },
+       { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK },
+       { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC },
+       { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID },
+       { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED },
+       { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER },
+       { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE },
+       { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE },
+       { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST },
+       { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE },
+       { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME },
+       { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE },
+       { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA },
+       { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR },
+       { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED },
+       { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY },
+       { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES },
+       { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW },
+       { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM },
+       { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION },
+       { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE },
+       { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION },
+       { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE },
+       { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE },
+       { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION },
+       { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED },
+       { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED },
+       { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL },
+       { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH },
+       { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION },
+       { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION },
+       { "NT_STATUS_UNWIND", NT_STATUS_UNWIND },
+       { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK },
+       { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET },
+       { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED },
+       { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR },
+       { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM },
+       { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED },
+       { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES },
+       { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG },
+       { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX },
+       { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER },
+       { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR },
+       { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID },
+       { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND },
+       { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION },
+       { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE },
+       { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED },
+       { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED },
+       { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID },
+       { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND },
+       { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD },
+       { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN },
+       { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR },
+       { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR },
+       { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR },
+       { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG },
+       { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED },
+       { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE },
+       { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION },
+       { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED },
+       { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION },
+       { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED },
+       { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED },
+       { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET },
+       { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE },
+       { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED },
+       { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING },
+       { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT },
+       { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP },
+       { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION },
+       { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED },
+       { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE },
+       { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY },
+       { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE },
+       { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR },
+       { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT },
+       { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED },
+       { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING },
+       { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED },
+       { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION },
+       { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH },
+       { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER },
+       { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP },
+       { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN },
+       { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY },
+       { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS },
+       { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION },
+       { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE },
+       { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD },
+       { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME },
+       { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS },
+       { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER },
+       { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS },
+       { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP },
+       { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP },
+       { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP },
+       { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN },
+       { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD },
+       { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD },
+       { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION },
+       { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE },
+       { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION },
+       { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS },
+       { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION },
+       { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED },
+       { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED },
+       { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED },
+       { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED },
+       { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED },
+       { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY },
+       { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL },
+       { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID },
+       { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR },
+       { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND },
+       { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT },
+       { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN },
+       { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL },
+       { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED },
+       { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL },
+       { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED },
+       { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED },
+       { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED },
+       { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED },
+       { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY },
+       { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED },
+       { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL },
+       { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED },
+       { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA },
+       { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND },
+       { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND },
+       { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND },
+       { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED },
+       { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND },
+       { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO },
+       { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT },
+       { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION },
+       { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW },
+       { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK },
+       { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW },
+       { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO },
+       { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW },
+       { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION },
+       { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES },
+       { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID },
+       { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED },
+       { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES },
+       { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND },
+       { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR },
+       { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED },
+       { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE },
+       { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE },
+       { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED },
+       { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA },
+       { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED },
+       { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY },
+       { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES },
+       { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL },
+       { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS },
+       { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS },
+       { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE },
+       { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD },
+       { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT },
+       { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE },
+       { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE },
+       { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE },
+       { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY },
+       { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION },
+       { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED },
+       { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING },
+       { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED },
+       { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING },
+       { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE },
+       { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT },
+       { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED },
+       { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED },
+       { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED },
+       { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET },
+       { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY },
+       { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED },
+       { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING },
+       { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME },
+       { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH },
+       { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY },
+       { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST },
+       { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS },
+       { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR },
+       { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE },
+       { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR },
+       { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER },
+       { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL },
+       { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE },
+       { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED },
+       { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED },
+       { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED },
+       { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE },
+       { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME },
+       { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES },
+       { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS },
+       { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED },
+       { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED },
+       { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED },
+       { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT },
+       { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT },
+       { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE },
+       { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED },
+       { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED },
+       { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT },
+       { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT },
+       { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY },
+       { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO },
+       { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF },
+       { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE },
+       { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE },
+       { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE },
+       { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN },
+       { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS },
+       { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+       { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED },
+       { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL },
+       { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION },
+       { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR },
+       { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED },
+       { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT },
+       { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER },
+       { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR },
+       { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR },
+       { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR },
+       { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR },
+       { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS },
+       { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS },
+       { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 },
+       { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 },
+       { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 },
+       { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 },
+       { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 },
+       { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 },
+       { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 },
+       { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 },
+       { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 },
+       { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 },
+       { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 },
+       { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 },
+       { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED },
+       { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED },
+       { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW },
+       { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE },
+       { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE },
+       { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY },
+       { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR },
+       { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY },
+       { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE },
+       { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION },
+       { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG },
+       { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN },
+       { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE },
+       { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND },
+       { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING },
+       { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE },
+       { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION },
+       { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE },
+       { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED },
+       { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT },
+       { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST },
+       { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED },
+       { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER },
+       { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND },
+       { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID },
+       { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE },
+       { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR },
+       { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT },
+       { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE },
+       { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET },
+       { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR },
+       { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT },
+       { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE },
+       { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE },
+       { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO },
+       { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES },
+       { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED },
+       { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE },
+       { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME },
+       { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED },
+       { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT },
+       { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP },
+       { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER },
+       { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP },
+       { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED },
+       { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS },
+       { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS },
+       { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE },
+       { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED },
+       { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT },
+       { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT },
+       { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ },
+       { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT },
+       { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 },
+       { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT },
+       { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+       { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED },
+       { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND },
+       { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED },
+       { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED },
+       { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND },
+       { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND },
+       { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT },
+       { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT },
+       { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT },
+       { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES },
+       { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED },
+       { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT },
+       { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION },
+       { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS },
+       { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED },
+       { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE },
+       { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION },
+       { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE },
+       { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED },
+       { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE },
+       { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL },
+       { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE },
+       { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT },
+       { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN },
+       { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT },
+       { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED },
+       { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR },
+       { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME },
+       { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED },
+       { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS },
+       { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS },
+       { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS },
+       { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS },
+       { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED },
+       { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS },
+       { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG },
+       { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR },
+       { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE },
+       { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS },
+       { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED },
+       { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE },
+       { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED },
+       { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR },
+       { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER },
+       { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY },
+       { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER },
+       { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER },
+       { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER },
+       { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME },
+       { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND },
+       { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER },
+       { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR },
+       { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS },
+       { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED },
+       { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED },
+       { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED },
+       { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY },
+       { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING },
+       { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE },
+       { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH },
+       { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED },
+       { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA },
+       { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA },
+       { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW },
+       { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA },
+       { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER },
+       { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER },
+       { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED },
+       { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE },
+       { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS },
+       { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+       { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN },
+       { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE },
+       { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR },
+       { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR },
+       { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE },
+       { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR },
+       { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR },
+       { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER },
+       { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL },
+       { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE },
+       { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET },
+       { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+       { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+       { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+       { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT },
+       { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START },
+       { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE },
+       { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED },
+       { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED },
+       { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED },
+       { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK },
+       { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+       { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT },
+       { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED },
+       { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+       { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+       { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+       { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+       { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED },
+       { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY },
+       { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED },
+       { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND },
+       { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES },
+       { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE },
+       { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT },
+       { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD },
+       { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES },
+       { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS },
+       { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED },
+       { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED },
+       { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET },
+       { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES },
+       { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED },
+       { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT },
+       { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE },
+       { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH },
+       { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED },
+       { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID },
+       { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE },
+       { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION },
+       { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION },
+       { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE },
+       { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED },
+       { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED },
+       { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED },
+       { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND },
+       { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR },
+       { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT },
+       { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH },
+       { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT },
+       { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH },
+       { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA },
+       { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID },
+       { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE },
+       { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND },
+       { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM },
+       { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE },
+       { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ },
+       { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK },
+       { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID },
+       { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS },
+       { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE },
+       { "NT_STATUS_RETRY", NT_STATUS_RETRY },
+       { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE },
+       { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET },
+       { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND },
+       { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW },
+       { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT },
+       { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+       { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT },
+       { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE },
+       { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED },
+       { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT },
+       { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+       { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+       { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID },
+       { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE },
+       { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE },
+       { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE },
+       { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE },
+       { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE },
+       { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED },
+       { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED },
+       { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER },
+       { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE },
+       { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED },
+       { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET },
+       { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT },
+       { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION },
+       { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION },
+       { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH },
+       { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+       { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT },
+       { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT },
+       { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST },
+       { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 },
+       { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 },
+       { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT },
+       { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED },
+       { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE },
+       { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+       { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT },
+       { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT },
+       { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT },
+       { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE },
+       { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION },
+       { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE },
+       { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH },
+       { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND },
+       { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND },
+       { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED },
+       { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS },
+       { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT },
+       { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE },
+        { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+       { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+       { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+       { NULL, NT_STATUS(0) }
+};
+
+static const nt_err_code_struct nt_err_desc[] =
+{
+       { "Success",                            NT_STATUS_OK },
+       { "Undetermined error",                 NT_STATUS_UNSUCCESSFUL },
+       { "Access denied",                      NT_STATUS_ACCESS_DENIED },
+       { "Account locked out",                 NT_STATUS_ACCOUNT_LOCKED_OUT },
+       { "Must change password",               NT_STATUS_PASSWORD_MUST_CHANGE },
+       { "Password is too short",              NT_STATUS_PWD_TOO_SHORT },
+       { "Password is too recent",             NT_STATUS_PWD_TOO_RECENT },
+       { "Password history conflict",          NT_STATUS_PWD_HISTORY_CONFLICT },
+       { "No logon servers",                   NT_STATUS_NO_LOGON_SERVERS },
+       { "Improperly formed account name",     NT_STATUS_INVALID_ACCOUNT_NAME },
+       { "User exists",                        NT_STATUS_USER_EXISTS },
+       { "No such user",                       NT_STATUS_NO_SUCH_USER },
+       { "Group exists",                       NT_STATUS_GROUP_EXISTS },
+       { "No such group",                      NT_STATUS_NO_SUCH_GROUP },
+       { "Member not in group",                NT_STATUS_MEMBER_NOT_IN_GROUP },
+       { "Wrong Password",                     NT_STATUS_WRONG_PASSWORD },
+       { "Ill formed password",                NT_STATUS_ILL_FORMED_PASSWORD },
+       { "Password restriction",               NT_STATUS_PASSWORD_RESTRICTION },
+       { "Logon failure",                      NT_STATUS_LOGON_FAILURE },
+       { "Account restriction",                NT_STATUS_ACCOUNT_RESTRICTION },
+       { "Invalid logon hours",                NT_STATUS_INVALID_LOGON_HOURS },
+       { "Invalid workstation",                NT_STATUS_INVALID_WORKSTATION },
+       { "Password expired",                   NT_STATUS_PASSWORD_EXPIRED },
+       { "Account disabled",                   NT_STATUS_ACCOUNT_DISABLED },
+       { "Unexpected information received",    NT_STATUS_INVALID_PARAMETER },
+       { "Memory allocation error",            NT_STATUS_NO_MEMORY },
+       { "No domain controllers located",      NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+       { "Account locked out",                 NT_STATUS_ACCOUNT_LOCKED_OUT },
+       { "Named pipe not available",           NT_STATUS_PIPE_NOT_AVAILABLE },
+       { "Not implemented",                    NT_STATUS_NOT_IMPLEMENTED },
+       { "Invalid information class",          NT_STATUS_INVALID_INFO_CLASS },
+       { "Information length mismatch",        NT_STATUS_INFO_LENGTH_MISMATCH },
+       { "Access violation",                   NT_STATUS_ACCESS_VIOLATION },
+       { "Invalid handle",                     NT_STATUS_INVALID_HANDLE },
+       { "Invalid parameter",                  NT_STATUS_INVALID_PARAMETER },
+       { "No memory",                          NT_STATUS_NO_MEMORY },
+       { "Buffer too small",                   NT_STATUS_BUFFER_TOO_SMALL },
+       { "Revision mismatch",                  NT_STATUS_REVISION_MISMATCH },
+       { "No logon servers",                   NT_STATUS_NO_LOGON_SERVERS },
+       { "No such logon session",              NT_STATUS_NO_SUCH_LOGON_SESSION },
+       { "No such privilege",                  NT_STATUS_NO_SUCH_PRIVILEGE },
+       { "Procedure not found",                NT_STATUS_PROCEDURE_NOT_FOUND },
+       { "Server disabled",                    NT_STATUS_SERVER_DISABLED },
+       { "Invalid pipe state",                 NT_STATUS_INVALID_PIPE_STATE },
+       { "Named pipe busy",                    NT_STATUS_PIPE_BUSY },
+       { "Illegal function",                   NT_STATUS_ILLEGAL_FUNCTION },
+       { "Named pipe dicconnected",            NT_STATUS_PIPE_DISCONNECTED },
+       { "Named pipe closing",                 NT_STATUS_PIPE_CLOSING },
+       { "Remote host not listening",          NT_STATUS_REMOTE_NOT_LISTENING },
+       { "Duplicate name on network",          NT_STATUS_DUPLICATE_NAME },
+       { "Print queue is full",                NT_STATUS_PRINT_QUEUE_FULL },
+       { "No print spool space available",     NT_STATUS_NO_SPOOL_SPACE },
+       { "Too many names",                     NT_STATUS_TOO_MANY_NAMES },
+       { "Too many sessions",                  NT_STATUS_TOO_MANY_SESSIONS },
+       { "Invalid server state",               NT_STATUS_INVALID_SERVER_STATE },
+       { "Invalid domain state",               NT_STATUS_INVALID_DOMAIN_STATE },
+       { "Invalid domain role",                NT_STATUS_INVALID_DOMAIN_ROLE },
+       { "No such domain",                     NT_STATUS_NO_SUCH_DOMAIN },
+       { "Domain exists",                      NT_STATUS_DOMAIN_EXISTS },
+       { "Domain limit exceeded",              NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+       { "Bad logon session state",            NT_STATUS_BAD_LOGON_SESSION_STATE },
+       { "Logon session collision",            NT_STATUS_LOGON_SESSION_COLLISION },
+       { "Invalid logon type",                 NT_STATUS_INVALID_LOGON_TYPE },
+       { "Cancelled",                          NT_STATUS_CANCELLED },
+       { "Invalid computer name",              NT_STATUS_INVALID_COMPUTER_NAME },      
+       { "Logon server conflict",              NT_STATUS_LOGON_SERVER_CONFLICT },
+       { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+       { "Pipe broken",                        NT_STATUS_PIPE_BROKEN },
+       { "Registry corrupt",                   NT_STATUS_REGISTRY_CORRUPT },
+       { "Too many secrets",                   NT_STATUS_TOO_MANY_SECRETS },
+       { "Too many SIDs",                      NT_STATUS_TOO_MANY_SIDS },
+       { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+       { "Log file full",                      NT_STATUS_LOG_FILE_FULL },
+       { "No trusted LSA secret",              NT_STATUS_NO_TRUST_LSA_SECRET },
+       { "No trusted SAM account",             NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+       { "Trusted domain failure",             NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+       { "Trust relationship failure",         NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+       { "Trust failure",                      NT_STATUS_TRUST_FAILURE },
+       { "Netlogon service not started",       NT_STATUS_NETLOGON_NOT_STARTED },
+       { "Account expired",                    NT_STATUS_ACCOUNT_EXPIRED },
+       { "Network credential conflict",        NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+       { "Remote session limit",               NT_STATUS_REMOTE_SESSION_LIMIT },
+       { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+       { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+       { "No logon server trust account",      NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+       { "Domain trust inconsistent",          NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+       { "No user session key available",      NT_STATUS_NO_USER_SESSION_KEY },
+       { "User session deleted",               NT_STATUS_USER_SESSION_DELETED },
+       { "Insufficient server resources",      NT_STATUS_INSUFF_SERVER_RESOURCES },
+       { "Insufficient logon information",     NT_STATUS_INSUFFICIENT_LOGON_INFO },
+       
+       { "License quota exceeded",             NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+
+       { NULL, NT_STATUS(0) }
+};
+
+
+/*****************************************************************************
+ returns an NT error message.  not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *nt_errstr(NTSTATUS nt_code)
+{
+        static pstring msg;
+        int idx = 0;
+
+       slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code));
+
+       while (nt_errs[idx].nt_errstr != NULL) {
+               if (NT_STATUS_V(nt_errs[idx].nt_errcode) == 
+                    NT_STATUS_V(nt_code)) {
+                        return nt_errs[idx].nt_errstr;
+               }
+               idx++;
+       }
+
+        return msg;
+}
+
+/************************************************************************
+ Print friendler version fo NT error code
+ ***********************************************************************/
+const char *get_friendly_nt_error_msg(NTSTATUS nt_code)
+{
+        int idx = 0;
+
+       while (nt_err_desc[idx].nt_errstr != NULL) {
+               if (NT_STATUS_V(nt_err_desc[idx].nt_errcode) == NT_STATUS_V(nt_code)) {
+                        return nt_err_desc[idx].nt_errstr;
+               }
+               idx++;
+       }
+       
+       /* fall back to NT_STATUS_XXX string */
+       return nt_errstr(nt_code);
+}
+
+/*****************************************************************************
+ returns an NT_STATUS constant as a string for inclusion in autogen C code
+ *****************************************************************************/
+const char *get_nt_error_c_code(NTSTATUS nt_code)
+{
+        static pstring out;
+        int idx = 0;
+
+       while (nt_errs[idx].nt_errstr != NULL) {
+               if (NT_STATUS_V(nt_errs[idx].nt_errcode) == 
+                    NT_STATUS_V(nt_code)) {
+                        return nt_errs[idx].nt_errstr;
+               }
+               idx++;
+       }
+
+       slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code));
+
+        return out;
+}
+
+/*****************************************************************************
+ returns the NT_STATUS constant matching the string supplied (as an NTSTATUS)
+ *****************************************************************************/
+NTSTATUS nt_status_string_to_code(char *nt_status_str)
+{
+        int idx = 0;
+
+       while (nt_errs[idx].nt_errstr != NULL) {
+               if (strcmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) {
+                        return nt_errs[idx].nt_errcode;
+               }
+               idx++;
+       }
+       return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source4/libcli/util/ntlmssp_sign.c b/source4/libcli/util/ntlmssp_sign.c
new file mode 100644 (file)
index 0000000..bd6d64d
--- /dev/null
@@ -0,0 +1,226 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  Version 3.0
+ *  NTLMSSP Signing routines
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2001
+ *  Copyright (C) Andrew Bartlett 2003
+ *  
+ *  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.
+ */
+
+#include "includes.h"
+
+#define CLI_SIGN "session key to client-to-server signing key magic constant"
+#define CLI_SEAL "session key to client-to-server sealing key magic constant"
+#define SRV_SIGN "session key to server-to-client signing key magic constant"
+#define SRV_SEAL "session key to server-to-client sealing key magic constant"
+
+static void NTLMSSPcalc_ap( unsigned char *hash, unsigned char *data, int len)
+{
+    unsigned char index_i = hash[256];
+    unsigned char index_j = hash[257];
+    int ind;
+
+    for (ind = 0; ind < len; ind++)
+    {
+        unsigned char tc;
+        unsigned char t;
+
+        index_i++;
+        index_j += hash[index_i];
+
+        tc = hash[index_i];
+        hash[index_i] = hash[index_j];
+        hash[index_j] = tc;
+
+        t = hash[index_i] + hash[index_j];
+        data[ind] = data[ind] ^ hash[t];
+    }
+
+    hash[256] = index_i;
+    hash[257] = index_j;
+}
+
+static void calc_hash(unsigned char *hash, const char *k2, int k2l)
+{
+       unsigned char j = 0;
+       int ind;
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               hash[ind] = (unsigned char)ind;
+       }
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               unsigned char tc;
+
+               j += (hash[ind] + k2[ind%k2l]);
+
+               tc = hash[ind];
+               hash[ind] = hash[j];
+               hash[j] = tc;
+       }
+
+       hash[256] = 0;
+       hash[257] = 0;
+}
+
+static void calc_ntlmv2_hash(unsigned char hash[16], char digest[16],
+                            const char encrypted_response[16], 
+                            const char *constant)
+{
+       struct MD5Context ctx3;
+
+       MD5Init(&ctx3);
+       MD5Update(&ctx3, encrypted_response, 5);
+       MD5Update(&ctx3, constant, strlen(constant));
+       MD5Final(digest, &ctx3);
+
+       calc_hash(hash, digest, 16);
+}
+
+enum ntlmssp_direction {
+       NTLMSSP_SEND,
+       NTLMSSP_RECEIVE
+};
+
+static NTSTATUS ntlmssp_make_packet_signiture(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                             const uchar *data, size_t length, 
+                                             enum ntlmssp_direction direction,
+                                             DATA_BLOB *sig) 
+{
+       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+               HMACMD5Context ctx;
+               char seq_num[4];
+               uchar digest[16];
+               SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
+
+               hmac_md5_init_limK_to_64(ntlmssp_state->cli_sign_const, 16, &ctx);
+               hmac_md5_update(seq_num, 4, &ctx);
+               hmac_md5_update(data, length, &ctx);
+               hmac_md5_final(digest, &ctx);
+
+               if (!msrpc_gen(sig, "Bd", digest, sizeof(digest), ntlmssp_state->ntlmssp_seq_num)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               switch (direction) {
+               case NTLMSSP_SEND:
+                       NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash,  sig->data, sig->length);
+                       break;
+               case NTLMSSP_RECEIVE:
+                       NTLMSSPcalc_ap(ntlmssp_state->srv_sign_hash,  sig->data, sig->length);
+                       break;
+               }
+       } else {
+               uint32 crc;
+               crc = crc32_buffer(data, length);
+               if (!msrpc_gen(sig, "ddd", 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               
+               NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data, sig->length);
+       }
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                          const uchar *data, size_t length, 
+                                          DATA_BLOB *sig) 
+{
+       ntlmssp_state->ntlmssp_seq_num++;
+       return ntlmssp_make_packet_signiture(ntlmssp_state, data, length, NTLMSSP_SEND, sig);
+}
+
+/**
+ * Check the signature of an incoming packet 
+ * @note caller *must* check that the signature is the size it expects 
+ *
+ */
+
+NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                          const uchar *data, size_t length, 
+                                          const DATA_BLOB *sig) 
+{
+       DATA_BLOB local_sig;
+       NTSTATUS nt_status;
+
+       if (sig->length < 8) {
+               DEBUG(0, ("NTLMSSP packet check failed due to short signiture (%u bytes)!\n", 
+                         sig->length));
+       }
+
+       nt_status = ntlmssp_make_packet_signiture(ntlmssp_state, data, 
+                                                 length, NTLMSSP_RECEIVE, &local_sig);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+               return nt_status;
+       }
+       
+       if (memcmp(sig->data, local_sig.data, MIN(sig->length, local_sig.length)) == 0) {
+               return NT_STATUS_OK;
+       } else {
+               DEBUG(5, ("BAD SIG: wanted signature of\n"));
+               dump_data(5, local_sig.data, local_sig.length);
+               
+               DEBUG(5, ("BAD SIG: got signature of\n"));
+               dump_data(5, sig->data, sig->length);
+
+               DEBUG(0, ("NTLMSSP packet check failed due to invalid signiture!\n"));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+}
+
+/**
+   Initialise the state for NTLMSSP signing.
+*/
+NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state)
+{
+       unsigned char p24[24];
+       unsigned char lm_hash[16];
+
+       if (!ntlmssp_state->lm_resp.data) {
+               /* can't sign or check signitures yet */ 
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+                           
+       E_deshash(ntlmssp_state->password, lm_hash);
+               
+       NTLMSSPOWFencrypt(lm_hash, ntlmssp_state->lm_resp.data, p24);
+       
+       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+       {
+               calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash, ntlmssp_state->cli_sign_const, p24, CLI_SIGN);
+               calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash, ntlmssp_state->cli_seal_const, p24, CLI_SEAL);
+               calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash, ntlmssp_state->srv_sign_const, p24, SRV_SIGN);
+               calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash, ntlmssp_state->srv_seal_const, p24, SRV_SEAL);
+       }
+       else
+       {
+               char k2[8];
+               memcpy(k2, p24, 5);
+               k2[5] = 0xe5;
+               k2[6] = 0x38;
+               k2[7] = 0xb0;
+               
+               calc_hash(ntlmssp_state->ntlmssp_hash, k2, 8);
+       }
+
+       ntlmssp_state->ntlmssp_seq_num = 0;
+
+       ZERO_STRUCT(lm_hash);
+       return NT_STATUS_OK;
+}
diff --git a/source4/libcli/util/pwd_cache.c b/source4/libcli/util/pwd_cache.c
new file mode 100644 (file)
index 0000000..0d84f04
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password cacheing.  obfuscation is planned
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+   
+   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"
+
+/****************************************************************************
+ Initialises a password structure.
+****************************************************************************/
+
+static void pwd_init(struct pwd_info *pwd)
+{
+       memset((char *)pwd->password  , '\0', sizeof(pwd->password  ));
+       memset((char *)pwd->smb_lm_pwd, '\0', sizeof(pwd->smb_lm_pwd));
+       memset((char *)pwd->smb_nt_pwd, '\0', sizeof(pwd->smb_nt_pwd));
+       memset((char *)pwd->smb_lm_owf, '\0', sizeof(pwd->smb_lm_owf));
+       memset((char *)pwd->smb_nt_owf, '\0', sizeof(pwd->smb_nt_owf));
+
+       pwd->null_pwd  = True; /* safest option... */
+       pwd->cleartext = False;
+       pwd->crypted   = False;
+}
+
+/****************************************************************************
+ Makes lm and nt hashed passwords.
+****************************************************************************/
+
+static void pwd_make_lm_nt_16(struct pwd_info *pwd, const char *clr)
+{
+       pstring dos_passwd;
+
+       pwd_init(pwd);
+
+       push_ascii_pstring(dos_passwd, clr);
+
+       nt_lm_owf_gen(dos_passwd, pwd->smb_nt_pwd, pwd->smb_lm_pwd);
+       pwd->null_pwd  = False;
+       pwd->cleartext = False;
+       pwd->crypted = False;
+}
+
+/****************************************************************************
+ Stores a cleartext password.
+****************************************************************************/
+
+void pwd_set_cleartext(struct pwd_info *pwd, const char *clr)
+{
+       pwd_init(pwd);
+       push_ascii_fstring(pwd->password, clr);
+       pwd->cleartext = True;
+       pwd->null_pwd  = False;
+       pwd->crypted   = False;
+       pwd_make_lm_nt_16(pwd, clr);
+}
+
+
diff --git a/source4/libcli/util/smbdes.c b/source4/libcli/util/smbdes.c
new file mode 100644 (file)
index 0000000..cde77f9
--- /dev/null
@@ -0,0 +1,415 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   a partial implementation of DES designed for use in the 
+   SMB authentication protocol
+
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+/* NOTES: 
+
+   This code makes no attempt to be fast! In fact, it is a very
+   slow implementation 
+
+   This code is NOT a complete DES implementation. It implements only
+   the minimum necessary for SMB authentication, as used by all SMB
+   products (including every copy of Microsoft Windows95 ever sold)
+
+   In particular, it can only do a unchained forward DES pass. This
+   means it is not possible to use this code for encryption/decryption
+   of data, instead it is only useful as a "hash" algorithm.
+
+   There is no entry point into this code that allows normal DES operation.
+
+   I believe this means that this code does not come under ITAR
+   regulations but this is NOT a legal opinion. If you are concerned
+   about the applicability of ITAR regulations to this code then you
+   should confirm it for yourself (and maybe let me know if you come
+   up with a different answer to the one above)
+*/
+
+
+#define uchar unsigned char
+
+static const uchar perm1[56] = {57, 49, 41, 33, 25, 17,  9,
+                        1, 58, 50, 42, 34, 26, 18,
+                       10,  2, 59, 51, 43, 35, 27,
+                       19, 11,  3, 60, 52, 44, 36,
+                       63, 55, 47, 39, 31, 23, 15,
+                        7, 62, 54, 46, 38, 30, 22,
+                       14,  6, 61, 53, 45, 37, 29,
+                       21, 13,  5, 28, 20, 12,  4};
+
+static const uchar perm2[48] = {14, 17, 11, 24,  1,  5,
+                         3, 28, 15,  6, 21, 10,
+                        23, 19, 12,  4, 26,  8,
+                        16,  7, 27, 20, 13,  2,
+                        41, 52, 31, 37, 47, 55,
+                        30, 40, 51, 45, 33, 48,
+                        44, 49, 39, 56, 34, 53,
+                        46, 42, 50, 36, 29, 32};
+
+static const uchar perm3[64] = {58, 50, 42, 34, 26, 18, 10,  2,
+                       60, 52, 44, 36, 28, 20, 12,  4,
+                       62, 54, 46, 38, 30, 22, 14,  6,
+                       64, 56, 48, 40, 32, 24, 16,  8,
+                       57, 49, 41, 33, 25, 17,  9,  1,
+                       59, 51, 43, 35, 27, 19, 11,  3,
+                       61, 53, 45, 37, 29, 21, 13,  5,
+                       63, 55, 47, 39, 31, 23, 15,  7};
+
+static const uchar perm4[48] = {   32,  1,  2,  3,  4,  5,
+                            4,  5,  6,  7,  8,  9,
+                            8,  9, 10, 11, 12, 13,
+                           12, 13, 14, 15, 16, 17,
+                           16, 17, 18, 19, 20, 21,
+                           20, 21, 22, 23, 24, 25,
+                           24, 25, 26, 27, 28, 29,
+                           28, 29, 30, 31, 32,  1};
+
+static const uchar perm5[32] = {      16,  7, 20, 21,
+                              29, 12, 28, 17,
+                               1, 15, 23, 26,
+                               5, 18, 31, 10,
+                               2,  8, 24, 14,
+                              32, 27,  3,  9,
+                              19, 13, 30,  6,
+                              22, 11,  4, 25};
+
+
+static const uchar perm6[64] ={ 40,  8, 48, 16, 56, 24, 64, 32,
+                        39,  7, 47, 15, 55, 23, 63, 31,
+                        38,  6, 46, 14, 54, 22, 62, 30,
+                        37,  5, 45, 13, 53, 21, 61, 29,
+                        36,  4, 44, 12, 52, 20, 60, 28,
+                        35,  3, 43, 11, 51, 19, 59, 27,
+                        34,  2, 42, 10, 50, 18, 58, 26,
+                        33,  1, 41,  9, 49, 17, 57, 25};
+
+
+static const uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uchar sbox[8][4][16] = {
+       {{14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7},
+        {0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8},
+        {4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0},
+        {15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13}},
+
+       {{15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10},
+        {3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5},
+        {0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15},
+        {13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9}},
+
+       {{10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8},
+        {13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1},
+        {13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7},
+        {1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12}},
+
+       {{7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15},
+        {13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9},
+        {10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4},
+        {3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14}},
+
+       {{2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9},
+        {14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6},
+        {4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14},
+        {11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3}},
+
+       {{12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11},
+        {10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8},
+        {9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6},
+        {4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13}},
+
+       {{4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1},
+        {13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6},
+        {1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2},
+        {6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12}},
+
+       {{13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7},
+        {1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2},
+        {7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8},
+        {2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11}}};
+
+static void permute(char *out, const char *in, const uchar *p, int n)
+{
+       int i;
+       for (i=0;i<n;i++)
+               out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+       char out[64];
+       int i;
+       for (i=0;i<n;i++)
+               out[i] = d[(i+count)%n];
+       for (i=0;i<n;i++)
+               d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+       while (l1--)
+               *out++ = *in1++;
+       while (l2--)
+               *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+       int i;
+       for (i=0;i<n;i++)
+               out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key, int forw)
+{
+       int i, j, k;
+       char pk1[56];
+       char c[28];
+       char d[28];
+       char cd[56];
+       char ki[16][48];
+       char pd1[64];
+       char l[32], r[32];
+       char rl[64];
+
+       permute(pk1, key, perm1, 56);
+
+       for (i=0;i<28;i++)
+               c[i] = pk1[i];
+       for (i=0;i<28;i++)
+               d[i] = pk1[i+28];
+
+       for (i=0;i<16;i++) {
+               lshift(c, sc[i], 28);
+               lshift(d, sc[i], 28);
+
+               concat(cd, c, d, 28, 28); 
+               permute(ki[i], cd, perm2, 48); 
+       }
+
+       permute(pd1, in, perm3, 64);
+
+       for (j=0;j<32;j++) {
+               l[j] = pd1[j];
+               r[j] = pd1[j+32];
+       }
+
+       for (i=0;i<16;i++) {
+               char er[48];
+               char erk[48];
+               char b[8][6];
+               char cb[32];
+               char pcb[32];
+               char r2[32];
+
+               permute(er, r, perm4, 48);
+
+               xor(erk, er, ki[forw ? i : 15 - i], 48);
+
+               for (j=0;j<8;j++)
+                       for (k=0;k<6;k++)
+                               b[j][k] = erk[j*6 + k];
+
+               for (j=0;j<8;j++) {
+                       int m, n;
+                       m = (b[j][0]<<1) | b[j][5];
+
+                       n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4]; 
+
+                       for (k=0;k<4;k++) 
+                               b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0; 
+               }
+
+               for (j=0;j<8;j++)
+                       for (k=0;k<4;k++)
+                               cb[j*4+k] = b[j][k];
+               permute(pcb, cb, perm5, 32);
+
+               xor(r2, l, pcb, 32);
+
+               for (j=0;j<32;j++)
+                       l[j] = r[j];
+
+               for (j=0;j<32;j++)
+                       r[j] = r2[j];
+       }
+
+       concat(rl, r, l, 32, 32);
+
+       permute(out, rl, perm6, 64);
+}
+
+static void str_to_key(const unsigned char *str,unsigned char *key)
+{
+       int i;
+
+       key[0] = str[0]>>1;
+       key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+       key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+       key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+       key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+       key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+       key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+       key[7] = str[6]&0x7F;
+       for (i=0;i<8;i++) {
+               key[i] = (key[i]<<1);
+       }
+}
+
+
+static void smbhash(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+       int i;
+       char outb[64];
+       char inb[64];
+       char keyb[64];
+       unsigned char key2[8];
+
+       str_to_key(key, key2);
+
+       for (i=0;i<64;i++) {
+               inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+               keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+               outb[i] = 0;
+       }
+
+       dohash(outb, inb, keyb, forw);
+
+       for (i=0;i<8;i++) {
+               out[i] = 0;
+       }
+
+       for (i=0;i<64;i++) {
+               if (outb[i])
+                       out[i/8] |= (1<<(7-(i%8)));
+       }
+}
+
+void E_P16(const unsigned char *p14,unsigned char *p16)
+{
+       unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+       smbhash(p16, sp8, p14, 1);
+       smbhash(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const unsigned char *p21, const unsigned char *c8, unsigned char *p24)
+{
+       smbhash(p24, c8, p21, 1);
+       smbhash(p24+8, c8, p21+7, 1);
+       smbhash(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+       smbhash(out, in, p14, 0);
+        smbhash(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+        smbhash(out, in, p14, 1);
+        smbhash(out+8, in+8, p14+7, 1);
+}
+
+void cred_hash1(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+       unsigned char buf[8];
+
+       smbhash(buf, in, key, 1);
+       smbhash(out, buf, key+9, 1);
+}
+
+void cred_hash2(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+       unsigned char buf[8];
+       static unsigned char key2[8];
+
+       smbhash(buf, in, key, 1);
+       key2[0] = key[7];
+       smbhash(out, buf, key2, 1);
+}
+
+void cred_hash3(unsigned char *out, unsigned char *in, const unsigned char *key, int forw)
+{
+        static unsigned char key2[8];
+
+        smbhash(out, in, key, forw);
+        key2[0] = key[7];
+        smbhash(out + 8, in + 8, key2, forw);
+}
+
+void SamOEMhash( unsigned char *data, const unsigned char *key, int val)
+{
+  unsigned char s_box[256];
+  unsigned char index_i = 0;
+  unsigned char index_j = 0;
+  unsigned char j = 0;
+  int ind;
+
+  for (ind = 0; ind < 256; ind++)
+  {
+    s_box[ind] = (unsigned char)ind;
+  }
+
+  for( ind = 0; ind < 256; ind++)
+  {
+     unsigned char tc;
+
+     j += (s_box[ind] + key[ind%16]);
+
+     tc = s_box[ind];
+     s_box[ind] = s_box[j];
+     s_box[j] = tc;
+  }
+  for( ind = 0; ind < val; ind++)
+  {
+    unsigned char tc;
+    unsigned char t;
+
+    index_i++;
+    index_j += s_box[index_i];
+
+    tc = s_box[index_i];
+    s_box[index_i] = s_box[index_j];
+    s_box[index_j] = tc;
+
+    t = s_box[index_i] + s_box[index_j];
+    data[ind] = data[ind] ^ s_box[t];
+  }
+}
+
+/* Decode a sam password hash into a password.  The password hash is the
+   same method used to store passwords in the NT registry.  The DES key
+   used is based on the RID of the user. */
+
+void sam_pwd_hash(unsigned int rid, const uchar *in, uchar *out, int forw)
+{
+       uchar s[14];
+
+       s[0] = s[4] = s[8] = s[12] = (uchar)(rid & 0xFF);
+       s[1] = s[5] = s[9] = s[13] = (uchar)((rid >> 8) & 0xFF);
+       s[2] = s[6] = s[10]        = (uchar)((rid >> 16) & 0xFF);
+       s[3] = s[7] = s[11]        = (uchar)((rid >> 24) & 0xFF);
+
+       smbhash(out, in, s, forw);
+       smbhash(out+8, in+8, s+7, forw);
+}
diff --git a/source4/libcli/util/smbencrypt.c b/source4/libcli/util/smbencrypt.c
new file mode 100644 (file)
index 0000000..00c2b58
--- /dev/null
@@ -0,0 +1,418 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB parameters and setup
+   Copyright (C) Andrew Tridgell 1992-1998
+   Modified by Jeremy Allison 1995.
+   Copyright (C) Jeremy Allison 1995-2000.
+   Copyright (C) Luke Kennethc Casson Leighton 1996-2000.
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+   
+   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 "byteorder.h"
+
+/*
+   This implements the X/Open SMB password encryption
+   It takes a password ('unix' string), a 8 byte "crypt key" 
+   and puts 24 bytes of encrypted password into p24 */
+void SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24])
+{
+       uchar p21[21];
+
+       memset(p21,'\0',21);
+       E_deshash(passwd, p21); 
+
+       SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("SMBencrypt: lm#, challenge, response\n"));
+       dump_data(100, (char *)p21, 16);
+       dump_data(100, (const char *)c8, 8);
+       dump_data(100, (char *)p24, 24);
+#endif
+}
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+void E_md4hash(const char *passwd, uchar p16[16])
+{
+       int len;
+       smb_ucs2_t wpwd[129];
+       
+       /* Password must be converted to NT unicode - null terminated. */
+       push_ucs2(NULL, wpwd, (const char *)passwd, 256, STR_UNICODE|STR_NOALIGN|STR_TERMINATE);
+       /* Calculate length in bytes */
+       len = strlen_w(wpwd) * sizeof(int16);
+
+       mdfour(p16, (unsigned char *)wpwd, len);
+       ZERO_STRUCT(wpwd);      
+}
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ */
+void E_deshash(const char *passwd, uchar p16[16])
+{
+       fstring dospwd; 
+       ZERO_STRUCT(dospwd);
+       ZERO_STRUCTP(p16);
+       
+       /* Password must be converted to DOS charset - null terminated, uppercase. */
+       push_ascii(dospwd, (const char *)passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE);
+
+       /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+       E_P16(dospwd, p16);
+
+       ZERO_STRUCT(dospwd);    
+}
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.  
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+/* Does both the NT and LM owfs of a user's password */
+void nt_lm_owf_gen(const char *pwd, uchar nt_p16[16], uchar p16[16])
+{
+       /* Calculate the MD4 hash (NT compatible) of the password */
+       memset(nt_p16, '\0', 16);
+       E_md4hash(pwd, nt_p16);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n"));
+       dump_data(120, pwd, strlen(pwd));
+       dump_data(100, (char *)nt_p16, 16);
+#endif
+
+       E_deshash(pwd, (uchar *)p16);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n"));
+       dump_data(120, pwd, strlen(pwd));
+       dump_data(100, (char *)p16, 16);
+#endif
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+BOOL ntv2_owf_gen(const uchar owf[16],
+                 const char *user_in, const char *domain_in, uchar kr_buf[16])
+{
+       smb_ucs2_t *user;
+       smb_ucs2_t *domain;
+       
+       size_t user_byte_len;
+       size_t domain_byte_len;
+
+       HMACMD5Context ctx;
+
+       user_byte_len = push_ucs2_allocate(&user, user_in);
+       if (user_byte_len == (size_t)-1) {
+               DEBUG(0, ("push_uss2_allocate() for user returned -1 (probably malloc() failure)\n"));
+               return False;
+       }
+
+       domain_byte_len = push_ucs2_allocate(&domain, domain_in);
+       if (domain_byte_len == (size_t)-1) {
+               DEBUG(0, ("push_uss2_allocate() for domain returned -1 (probably malloc() failure)\n"));
+               return False;
+       }
+
+       strupper_w(user);
+       strupper_w(domain);
+
+       SMB_ASSERT(user_byte_len >= 2);
+       SMB_ASSERT(domain_byte_len >= 2);
+
+       /* We don't want null termination */
+       user_byte_len = user_byte_len - 2;
+       domain_byte_len = domain_byte_len - 2;
+       
+       hmac_md5_init_limK_to_64(owf, 16, &ctx);
+       hmac_md5_update((const unsigned char *)user, user_byte_len, &ctx);
+       hmac_md5_update((const unsigned char *)domain, domain_byte_len, &ctx);
+       hmac_md5_final(kr_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+       dump_data(100, (const char *)user, user_byte_len);
+       dump_data(100, (const char *)domain, domain_byte_len);
+       dump_data(100, owf, 16);
+       dump_data(100, kr_buf, 16);
+#endif
+
+       SAFE_FREE(user);
+       SAFE_FREE(domain);
+       return True;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void SMBOWFencrypt(const uchar passwd[16], const uchar *c8, uchar p24[24])
+{
+       uchar p21[21];
+
+       ZERO_STRUCT(p21);
+       memcpy(p21, passwd, 16);    
+       E_P24(p21, c8, p24);
+}
+
+/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
+void NTLMSSPOWFencrypt(const uchar passwd[8], const uchar *ntlmchalresp, uchar p24[24])
+{
+       uchar p21[21];
+       memset(p21,'\0',21);
+       memcpy(p21, passwd, 8);    
+       memset(p21 + 8, 0xbd, 8);    
+
+       E_P24(p21, ntlmchalresp, p24);
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("NTLMSSPOWFencrypt: p21, c8, p24\n"));
+       dump_data(100, (char *)p21, 21);
+       dump_data(100, (const char *)ntlmchalresp, 8);
+       dump_data(100, (char *)p24, 24);
+#endif
+}
+
+
+/* Does the NT MD4 hash then des encryption. */
+void SMBNTencrypt(const char *passwd, uchar *c8, uchar *p24)
+{
+       uchar p21[21];
+       memset(p21,'\0',21);
+       E_md4hash(passwd, p21);    
+       SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n"));
+       dump_data(100, (char *)p21, 16);
+       dump_data(100, (char *)c8, 8);
+       dump_data(100, (char *)p24, 24);
+#endif
+}
+
+BOOL make_oem_passwd_hash(char data[516], const char *passwd, uchar old_pw_hash[16], BOOL unicode)
+{
+       int new_pw_len = strlen(passwd) * (unicode ? 2 : 1);
+
+       if (new_pw_len > 512)
+       {
+               DEBUG(0,("make_oem_passwd_hash: new password is too long.\n"));
+               return False;
+       }
+
+       /*
+        * Now setup the data area.
+        * We need to generate a random fill
+        * for this area to make it harder to
+        * decrypt. JRA.
+        */
+       generate_random_buffer((unsigned char *)data, 516, False);
+       push_string(NULL, &data[512 - new_pw_len], passwd, new_pw_len, 
+                   STR_NOALIGN | (unicode?STR_UNICODE:STR_ASCII));
+       SIVAL(data, 512, new_pw_len);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("make_oem_passwd_hash\n"));
+       dump_data(100, data, 516);
+#endif
+       SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
+
+       return True;
+}
+
+/* Does the md5 encryption from the NT hash for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uchar kr[16],
+                       const DATA_BLOB srv_chal,
+                       const DATA_BLOB cli_chal,
+                       uchar resp_buf[16])
+{
+       HMACMD5Context ctx;
+
+       hmac_md5_init_limK_to_64(kr, 16, &ctx);
+       hmac_md5_update(srv_chal.data, srv_chal.length, &ctx);
+       hmac_md5_update(cli_chal.data, cli_chal.length, &ctx);
+       hmac_md5_final(resp_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, cli_chal, resp_buf\n"));
+       dump_data(100, srv_chal.data, srv_chal.length);
+       dump_data(100, cli_chal.data, cli_chal.length);
+       dump_data(100, resp_buf, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv2(const uchar kr[16],
+                       const uchar * nt_resp, uint8 sess_key[16])
+{
+       HMACMD5Context ctx;
+
+       hmac_md5_init_limK_to_64(kr, 16, &ctx);
+       hmac_md5_update(nt_resp, 16, &ctx);
+       hmac_md5_final((unsigned char *)sess_key, &ctx);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+       dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv1(const uchar kr[16],
+                       const uchar * nt_resp, uint8 sess_key[16])
+{
+       mdfour((unsigned char *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+       dump_data(100, sess_key, 16);
+#endif
+}
+
+DATA_BLOB NTLMv2_generate_response(uchar ntlm_v2_hash[16],
+                                  DATA_BLOB server_chal, size_t client_chal_length)
+{
+       uchar ntlmv2_response[16];
+       DATA_BLOB ntlmv2_client_data;
+       DATA_BLOB final_response;
+       
+       /* NTLMv2 */
+
+       /* We also get to specify some random data */
+       ntlmv2_client_data = data_blob(NULL, client_chal_length);
+       generate_random_buffer(ntlmv2_client_data.data, ntlmv2_client_data.length, False);
+       
+       /* Given that data, and the challenge from the server, generate a response */
+       SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, ntlmv2_client_data, ntlmv2_response);
+       
+       /* put it into nt_response, for the code below to put into the packet */
+       final_response = data_blob(NULL, ntlmv2_client_data.length + sizeof(ntlmv2_response));
+       memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response));
+       /* after the first 16 bytes is the random data we generated above, so the server can verify us with it */
+       memcpy(final_response.data + sizeof(ntlmv2_response), ntlmv2_client_data.data, ntlmv2_client_data.length);
+       data_blob_free(&ntlmv2_client_data);
+
+       return final_response;
+}
+
+BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password, 
+                     const DATA_BLOB server_chal, 
+                     DATA_BLOB *lm_response, DATA_BLOB *nt_response, 
+                     DATA_BLOB *session_key) 
+{
+       uchar nt_hash[16];
+       uchar ntlm_v2_hash[16];
+       E_md4hash(password, nt_hash);
+
+       /* We don't use the NT# directly.  Instead we use it mashed up with
+          the username and domain.
+          This prevents username swapping during the auth exchange
+       */
+       if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) {
+               return False;
+       }
+       
+       *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 64 /* pick a number, > 8 */);
+       
+       /* LMv2 */
+       
+       *lm_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 8);
+       
+       *session_key = data_blob(NULL, 16);
+       
+       /* The NTLMv2 calculations also provide a session key, for signing etc later */
+       /* use only the first 16 bytes of nt_response for session key */
+       SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, session_key->data);
+
+       return True;
+}
+
+/***********************************************************
+ encode a password buffer.  The caller gets to figure out 
+ what to put in it.
+************************************************************/
+BOOL encode_pw_buffer(char buffer[516], char *new_pw, int new_pw_length)
+{
+       generate_random_buffer((unsigned char *)buffer, 516, True);
+
+       memcpy(&buffer[512 - new_pw_length], new_pw, new_pw_length);
+
+       /* 
+        * The length of the new password is in the last 4 bytes of
+        * the data buffer.
+        */
+       SIVAL(buffer, 512, new_pw_length);
+
+       return True;
+}
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_len is the length in bytes of the possibly mulitbyte
+ returned password including termination.
+************************************************************/
+BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd,
+                     int new_pwrd_size, uint32 *new_pw_len)
+{
+       int byte_len=0;
+
+       /*
+         Warning !!! : This function is called from some rpc call.
+         The password IN the buffer is a UNICODE string.
+         The password IN new_pwrd is an ASCII string
+         If you reuse that code somewhere else check first.
+       */
+
+       /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+       byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+       dump_data(100, in_buffer, 516);
+#endif
+
+       /* Password cannot be longer than 128 characters */
+       if ( (byte_len < 0) || (byte_len > new_pwrd_size - 1)) {
+               DEBUG(0, ("decode_pw_buffer: incorrect password length (%d).\n", byte_len));
+               DEBUG(0, ("decode_pw_buffer: check that 'encrypt passwords = yes'\n"));
+               return False;
+       }
+
+       /* decode into the return buffer.  Buffer must be a pstring */
+       *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, byte_len, STR_UNICODE);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+       dump_data(100, (char *)new_pwrd, *new_pw_len);
+       DEBUG(100,("multibyte len:%d\n", *new_pw_len));
+       DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+       
+       return True;
+}
diff --git a/source4/libcli/util/smberr.c b/source4/libcli/util/smberr.c
new file mode 100644 (file)
index 0000000..d6eabcf
--- /dev/null
@@ -0,0 +1,181 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Copyright (C) Andrew Tridgell 1998-2003
+   
+   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"
+
+/*
+  error code stuff - put together by Merik Karman
+  merik@blackadder.dsh.oz.au 
+*/
+
+struct err_code_struct {
+       const char *name;
+       int code;
+       const char *message;
+};
+
+/* Dos Error Messages */
+static const struct err_code_struct dos_msgs[] = {
+  {"ERRbadfunc",ERRbadfunc,"Invalid function."},
+  {"ERRbadfile",ERRbadfile,"File not found."},
+  {"ERRbadpath",ERRbadpath,"Directory invalid."},
+  {"ERRnofids",ERRnofids,"No file descriptors available"},
+  {"ERRnoaccess",ERRnoaccess,"Access denied."},
+  {"ERRbadfid",ERRbadfid,"Invalid file handle."},
+  {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."},
+  {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."},
+  {"ERRbadmem",ERRbadmem,"Invalid memory block address."},
+  {"ERRbadenv",ERRbadenv,"Invalid environment."},
+  {"ERRbadformat",11,"Invalid format."},
+  {"ERRbadaccess",ERRbadaccess,"Invalid open mode."},
+  {"ERRbaddata",ERRbaddata,"Invalid data."},
+  {"ERRres",ERRres,"reserved."},
+  {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."},
+  {"ERRremcd",ERRremcd,"A Delete Directory request attempted  to  remove  the  server's  current directory."},
+  {"ERRdiffdevice",ERRdiffdevice,"Not same device."},
+  {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."},
+  {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing  FIDs  on the file."},
+  {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an  invalid mode,  or an Unlock requested attempted to remove a lock held by another process."},
+  {"ERRunsup", ERRunsup, "The operation is unsupported"},
+  {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"},
+  {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make  New  File  or  Link  request already exists."},
+  {"ERRinvalidname",ERRinvalidname, "Invalid name"},
+  {"ERRbadpipe",ERRbadpipe,"Pipe invalid."},
+  {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."},
+  {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."},
+  {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."},
+  {"ERRmoredata",ERRmoredata,"There is more data to be returned."},
+  {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"},
+  {"ERRlogonfailure",ERRlogonfailure,"Logon failure"},
+  {"ERRdiskfull",ERRdiskfull,"Disk full"},
+  {"ERRgeneral",ERRgeneral, "General failure"},
+  {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"},
+  {NULL,-1,NULL}};
+
+/* Server Error Messages */
+static const struct err_code_struct server_msgs[] = {
+  {"ERRerror",1,"Non-specific error code."},
+  {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+  {"ERRbadtype",3,"reserved."},
+  {"ERRaccess",4,"The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID."},
+  {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+  {"ERRinvnetname",6,"Invalid network name in tree connect."},
+  {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection."},
+  {"ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+  {"ERRqtoobig",50,"Print queue full -- no space."},
+  {"ERRqeof",51,"EOF on print queue dump."},
+  {"ERRinvpfid",52,"Invalid print file FID."},
+  {"ERRsmbcmd",64,"The server did not recognize the command received."},
+  {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+  {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid  combination of values."},
+  {"ERRreserved",68,"reserved."},
+  {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute."},
+  {"ERRreserved",70,"reserved."},
+  {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+  {"ERRpaused",81,"Server is paused."},
+  {"ERRmsgoff",82,"Not receiving messages."},
+  {"ERRnoroom",83,"No room to buffer message."},
+  {"ERRrmuns",87,"Too many remote user names."},
+  {"ERRtimeout",88,"Operation timed out."},
+  {"ERRnoresource",89,"No resources currently available for request."},
+  {"ERRtoomanyuids",90,"Too many UIDs active on this session."},
+  {"ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+  {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+  {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+  {"ERRcontmpx",252,"Continue in MPX mode."},
+  {"ERRreserved",253,"reserved."},
+  {"ERRreserved",254,"reserved."},
+  {"ERRnosupport",0xFFFF,"Function not supported."},
+  {NULL,-1,NULL}};
+
+/* Hard Error Messages */
+static const struct err_code_struct hard_msgs[] = {
+  {"ERRnowrite",19,"Attempt to write on write-protected diskette."},
+  {"ERRbadunit",20,"Unknown unit."},
+  {"ERRnotready",21,"Drive not ready."},
+  {"ERRbadcmd",22,"Unknown command."},
+  {"ERRdata",23,"Data error (CRC)."},
+  {"ERRbadreq",24,"Bad request structure length."},
+  {"ERRseek",25 ,"Seek error."},
+  {"ERRbadmedia",26,"Unknown media type."},
+  {"ERRbadsector",27,"Sector not found."},
+  {"ERRnopaper",28,"Printer out of paper."},
+  {"ERRwrite",29,"Write fault."},
+  {"ERRread",30,"Read fault."},
+  {"ERRgeneral",31,"General failure."},
+  {"ERRbadshare",32,"An open conflicts with an existing open."},
+  {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+  {"ERRwrongdisk",34,"The wrong disk was found in a drive."},
+  {"ERRFCBUnavail",35,"No FCBs are available to process request."},
+  {"ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+  {NULL,-1,NULL}};
+
+
+static const struct {
+       uint8 class;
+       const char *class_name;
+       const struct err_code_struct *err_msgs;
+} err_classes[] = { 
+  {0,"SUCCESS",NULL},
+  {0x01,"ERRDOS",dos_msgs},
+  {0x02,"ERRSRV",server_msgs},
+  {0x03,"ERRHRD",hard_msgs},
+  {0x04,"ERRXOS",NULL},
+  {0xE1,"ERRRMX1",NULL},
+  {0xE2,"ERRRMX2",NULL},
+  {0xE3,"ERRRMX3",NULL},
+  {0xFF,"ERRCMD",NULL},
+  {-1,NULL,NULL}};
+
+
+/* return a dos error string given a error class and error code */
+const char *dos_errstr(uint8 class, uint16 code)
+{
+       static char *msg;
+       int i, j;
+       const struct err_code_struct *err_msgs;
+
+       if (msg) {
+               free(msg);
+               msg = NULL;
+       }
+
+       for (i=0;err_classes[i].class_name;i++) {
+               if (class == err_classes[i].class) break;
+       }
+       if (!err_classes[i].class_name) {
+               asprintf(&msg, "Unknown DOS error %d:%d\n", class, code);
+               return msg;
+       }
+
+       err_msgs = err_classes[i].err_msgs;
+
+       for (j=0;err_msgs && err_msgs[j].name;j++) {
+               if (err_msgs[j].code == code) {
+                       asprintf(&msg, "%s:%s (%s)\n", 
+                                err_classes[i].class_name, 
+                                err_msgs[j].name, 
+                                err_msgs[j].message);
+                       return msg;
+               }
+       }
+
+       asprintf(&msg, "Unknown DOS error %s:%d\n", err_classes[i].class_name, code);
+       return msg;
+}
diff --git a/source4/locking/brlock.c b/source4/locking/brlock.c
new file mode 100644 (file)
index 0000000..4cd885f
--- /dev/null
@@ -0,0 +1,698 @@
+/* 
+   Unix SMB/CIFS implementation.
+   byte range locking code
+   Updated to handle range splits/merges.
+
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Jeremy Allison 1992-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.
+*/
+
+/* This module implements a tdb based byte range locking service,
+   replacing the fcntl() based byte range locking previously
+   used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+
+#define ZERO_ZERO 0
+
+/* This contains elements that differentiate locks. The smbpid is a
+   client supplied pid, and is essentially the locking context for
+   this client */
+
+struct lock_context {
+       uint16 smbpid;
+       uint16 tid;
+       pid_t pid;
+};
+
+/* The data in brlock records is an unsorted linear array of these
+   records.  It is unnecessary to store the count as tdb provides the
+   size of the record */
+
+struct lock_struct {
+       struct lock_context context;
+       br_off start;
+       br_off size;
+       int fnum;
+       enum brl_type lock_type;
+};
+
+/* The key used in the brlock database. */
+
+struct lock_key {
+       SMB_DEV_T device;
+       SMB_INO_T inode;
+};
+
+/* The open brlock.tdb database. */
+
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Create a locking key - ensuring zero filled for pad purposes.
+****************************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+        static struct lock_key key;
+        TDB_DATA kbuf;
+
+        memset(&key, '\0', sizeof(key));
+        key.device = dev;
+        key.inode = inode;
+        kbuf.dptr = (char *)&key;
+        kbuf.dsize = sizeof(key);
+        return kbuf;
+}
+
+/****************************************************************************
+ See if two locking contexts are equal.
+****************************************************************************/
+
+static BOOL brl_same_context(struct lock_context *ctx1, 
+                            struct lock_context *ctx2)
+{
+       return (ctx1->pid == ctx2->pid) &&
+               (ctx1->smbpid == ctx2->smbpid) &&
+               (ctx1->tid == ctx2->tid);
+}
+
+/****************************************************************************
+ See if lock2 can be added when lock1 is in place.
+****************************************************************************/
+
+static BOOL brl_conflict(struct lock_struct *lck1, 
+                        struct lock_struct *lck2)
+{
+       if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+               return False;
+
+       if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+               return False;
+       }
+
+       if (brl_same_context(&lck1->context, &lck2->context) &&
+           lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+               return False;
+       }
+
+       if (lck1->start >= (lck2->start + lck2->size) ||
+           lck2->start >= (lck1->start + lck1->size)) {
+               return False;
+       }
+           
+       return True;
+} 
+
+#if ZERO_ZERO
+static BOOL brl_conflict1(struct lock_struct *lck1, 
+                        struct lock_struct *lck2)
+{
+       if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+               return False;
+
+       if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+               return False;
+       }
+
+       if (brl_same_context(&lck1->context, &lck2->context) &&
+           lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+               return False;
+       }
+
+       if (lck2->start == 0 && lck2->size == 0 && lck1->size != 0) {
+               return True;
+       }
+
+       if (lck1->start >= (lck2->start + lck2->size) ||
+           lck2->start >= (lck1->start + lck1->size)) {
+               return False;
+       }
+           
+       return True;
+} 
+#endif
+
+/****************************************************************************
+ Check to see if this lock conflicts, but ignore our own locks on the
+ same fnum only.
+****************************************************************************/
+
+static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
+{
+       if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+               return False;
+
+       if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) 
+               return False;
+
+       /*
+        * Incoming WRITE locks conflict with existing READ locks even
+        * if the context is the same. JRA. See LOCKTEST7 in smbtorture.
+        */
+
+       if (!(lck2->lock_type == WRITE_LOCK && lck1->lock_type == READ_LOCK)) {
+               if (brl_same_context(&lck1->context, &lck2->context) &&
+                                       lck1->fnum == lck2->fnum)
+                       return False;
+       }
+
+       if (lck1->start >= (lck2->start + lck2->size) ||
+           lck2->start >= (lck1->start + lck1->size)) return False;
+           
+       return True;
+} 
+
+
+#if DONT_DO_THIS
+       /* doing this traversal could kill solaris machines under high load (tridge) */
+       /* delete any dead locks */
+
+/****************************************************************************
+ Delete a record if it is for a dead process, if check_self is true, then
+ delete any records belonging to this pid also (there shouldn't be any).
+****************************************************************************/
+
+static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct lock_struct *locks;
+       int count, i;
+       BOOL check_self = *(BOOL *)state;
+       pid_t mypid = sys_getpid();
+
+       tdb_chainlock(tdb, kbuf);
+
+       locks = (struct lock_struct *)dbuf.dptr;
+
+       count = dbuf.dsize / sizeof(*locks);
+       for (i=0; i<count; i++) {
+               struct lock_struct *lock = &locks[i];
+
+               /* If check_self is true we want to remove our own records. */
+               if (check_self && (mypid == lock->context.pid)) {
+
+                       DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
+                                       (unsigned int)lock->context.pid ));
+
+               } else if (process_exists(lock->context.pid)) {
+
+                       DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid ));
+                       continue;
+               }
+
+               DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
+                               (unsigned int)lock->context.pid ));
+
+               if (count > 1 && i < count-1) {
+                       memmove(&locks[i], &locks[i+1], 
+                               sizeof(*locks)*((count-1) - i));
+               }
+               count--;
+               i--;
+       }
+
+       if (count == 0) {
+               tdb_delete(tdb, kbuf);
+       } else if (count < (dbuf.dsize / sizeof(*locks))) {
+               dbuf.dsize = count * sizeof(*locks);
+               tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+       }
+
+       tdb_chainunlock(tdb, kbuf);
+       return 0;
+}
+#endif
+
+/****************************************************************************
+ Open up the brlock.tdb database.
+****************************************************************************/
+
+void brl_init(int read_only)
+{
+       if (tdb)
+               return;
+       tdb = tdb_open_log(lock_path("brlock.tdb"), 0,  TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
+                      read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+       if (!tdb) {
+               DEBUG(0,("Failed to open byte range locking database\n"));
+               return;
+       }
+
+#if DONT_DO_THIS
+       /* doing this traversal could kill solaris machines under high load (tridge) */
+       /* delete any dead locks */
+       if (!read_only) {
+               BOOL check_self = False;
+               tdb_traverse(tdb, delete_fn, &check_self);
+       }
+#endif
+}
+
+/****************************************************************************
+ Close down the brlock.tdb database.
+****************************************************************************/
+
+void brl_shutdown(int read_only)
+{
+       if (!tdb)
+               return;
+
+#if DONT_DO_THIS
+       /* doing this traversal could kill solaris machines under high load (tridge) */
+       /* delete any dead locks */
+       if (!read_only) {
+               BOOL check_self = True;
+               tdb_traverse(tdb, delete_fn, &check_self);
+       }
+#endif
+
+       tdb_close(tdb);
+}
+
+#if ZERO_ZERO
+/****************************************************************************
+compare two locks for sorting
+****************************************************************************/
+static int lock_compare(struct lock_struct *lck1, 
+                        struct lock_struct *lck2)
+{
+       if (lck1->start != lck2->start) return (lck1->start - lck2->start);
+       if (lck2->size != lck1->size) {
+               return ((int)lck1->size - (int)lck2->size);
+       }
+       return 0;
+}
+#endif
+
+/****************************************************************************
+ Lock a range of bytes.
+****************************************************************************/
+
+NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+                 uint16 smbpid, pid_t pid, uint16 tid,
+                 br_off start, br_off size, 
+                 enum brl_type lock_type)
+{
+       TDB_DATA kbuf, dbuf;
+       int count, i;
+       struct lock_struct lock, *locks;
+       char *tp;
+       NTSTATUS status = NT_STATUS_OK;
+       static int last_failed = -1;
+       static br_off last_failed_start;
+
+       kbuf = locking_key(dev,ino);
+
+       dbuf.dptr = NULL;
+
+#if !ZERO_ZERO
+       if (start == 0 && size == 0) {
+               DEBUG(0,("client sent 0/0 lock - please report this\n"));
+       }
+#endif
+
+       tdb_chainlock(tdb, kbuf);
+       dbuf = tdb_fetch(tdb, kbuf);
+
+       lock.context.smbpid = smbpid;
+       lock.context.pid = pid;
+       lock.context.tid = tid;
+       lock.start = start;
+       lock.size = size;
+       lock.fnum = fnum;
+       lock.lock_type = lock_type;
+
+       if (dbuf.dptr) {
+               /* there are existing locks - make sure they don't conflict */
+               locks = (struct lock_struct *)dbuf.dptr;
+               count = dbuf.dsize / sizeof(*locks);
+               for (i=0; i<count; i++) {
+                       if (brl_conflict(&locks[i], &lock)) {
+                               status = NT_STATUS_LOCK_NOT_GRANTED;
+                               goto fail;
+                       }
+#if ZERO_ZERO
+                       if (lock.start == 0 && lock.size == 0 && 
+                           locks[i].size == 0) {
+                               break;
+                       }
+#endif
+               }
+       }
+
+       /* no conflicts - add it to the list of locks */
+       tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
+       if (!tp) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       } else {
+               dbuf.dptr = tp;
+       }
+       memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
+       dbuf.dsize += sizeof(lock);
+
+#if ZERO_ZERO
+       /* sort the lock list */
+       qsort(dbuf.dptr, dbuf.dsize/sizeof(lock), sizeof(lock), lock_compare);
+#endif
+
+       tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+       return NT_STATUS_OK;
+
+ fail:
+       /* this is a nasty hack to try to simulate the lock result cache code in w2k.
+          It isn't completely accurate as I haven't yet worked out the correct
+          semantics (tridge)
+       */
+       if (last_failed == fnum &&
+           last_failed_start == start &&
+           NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+               status = NT_STATUS_FILE_LOCK_CONFLICT;
+       }
+       last_failed = fnum;
+       last_failed_start = start;
+
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+       return status;
+}
+
+/****************************************************************************
+ Check if an unlock overlaps a pending lock.
+****************************************************************************/
+
+static BOOL brl_pending_overlap(struct lock_struct *lock, struct lock_struct *pend_lock)
+{
+       if ((lock->start <= pend_lock->start) && (lock->start + lock->size > pend_lock->start))
+               return True;
+       if ((lock->start >= pend_lock->start) && (lock->start <= pend_lock->start + pend_lock->size))
+               return True;
+       return False;
+}
+
+/****************************************************************************
+ Unlock a range of bytes.
+****************************************************************************/
+
+BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+               uint16 smbpid, pid_t pid, uint16 tid,
+               br_off start, br_off size,
+               BOOL remove_pending_locks_only)
+{
+       TDB_DATA kbuf, dbuf;
+       int count, i, j;
+       struct lock_struct *locks;
+       struct lock_context context;
+
+       kbuf = locking_key(dev,ino);
+
+       dbuf.dptr = NULL;
+
+       tdb_chainlock(tdb, kbuf);
+       dbuf = tdb_fetch(tdb, kbuf);
+
+       if (!dbuf.dptr) {
+               DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
+               goto fail;
+       }
+
+       context.smbpid = smbpid;
+       context.pid = pid;
+       context.tid = tid;
+
+       /* there are existing locks - find a match */
+       locks = (struct lock_struct *)dbuf.dptr;
+       count = dbuf.dsize / sizeof(*locks);
+
+#if ZERO_ZERO
+       for (i=0; i<count; i++) {
+               struct lock_struct *lock = &locks[i];
+
+               if (lock->lock_type == WRITE_LOCK &&
+                   brl_same_context(&lock->context, &context) &&
+                   lock->fnum == fnum &&
+                   lock->start == start &&
+                   lock->size == size) {
+                       /* found it - delete it */
+                       if (count == 1) {
+                               tdb_delete(tdb, kbuf);
+                       } else {
+                               if (i < count-1) {
+                                       memmove(&locks[i], &locks[i+1], 
+                                               sizeof(*locks)*((count-1) - i));
+                               }
+                               dbuf.dsize -= sizeof(*locks);
+                               tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+                       }
+
+                       SAFE_FREE(dbuf.dptr);
+                       tdb_chainunlock(tdb, kbuf);
+                       return True;
+               }
+       }
+#endif
+
+       locks = (struct lock_struct *)dbuf.dptr;
+       count = dbuf.dsize / sizeof(*locks);
+       for (i=0; i<count; i++) {
+               struct lock_struct *lock = &locks[i];
+
+               if (brl_same_context(&lock->context, &context) &&
+                               lock->fnum == fnum &&
+                               lock->start == start &&
+                               lock->size == size) {
+
+                       if (remove_pending_locks_only && lock->lock_type != PENDING_LOCK)
+                               continue;
+
+                       if (lock->lock_type != PENDING_LOCK) {
+                               /* Send unlock messages to any pending waiters that overlap. */
+                               for (j=0; j<count; j++) {
+                                       struct lock_struct *pend_lock = &locks[j];
+
+                                       /* Ignore non-pending locks. */
+                                       if (pend_lock->lock_type != PENDING_LOCK)
+                                               continue;
+
+                                       /* We could send specific lock info here... */
+                                       if (brl_pending_overlap(lock, pend_lock)) {
+                                               DEBUG(10,("brl_unlock: sending unlock message to pid %u\n",
+                                                                       (unsigned int)pend_lock->context.pid ));
+
+                                               message_send_pid(pend_lock->context.pid,
+                                                               MSG_SMB_UNLOCK,
+                                                               NULL, 0, True);
+                                       }
+                               }
+                       }
+
+                       /* found it - delete it */
+                       if (count == 1) {
+                               tdb_delete(tdb, kbuf);
+                       } else {
+                               if (i < count-1) {
+                                       memmove(&locks[i], &locks[i+1], 
+                                               sizeof(*locks)*((count-1) - i));
+                               }
+                               dbuf.dsize -= sizeof(*locks);
+                               tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+                       }
+
+                       SAFE_FREE(dbuf.dptr);
+                       tdb_chainunlock(tdb, kbuf);
+                       return True;
+               }
+       }
+
+       /* we didn't find it */
+
+ fail:
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+       return False;
+}
+
+
+/****************************************************************************
+ Test if we could add a lock if we wanted to.
+****************************************************************************/
+
+BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+                 uint16 smbpid, pid_t pid, uint16 tid,
+                 br_off start, br_off size, 
+                 enum brl_type lock_type, int check_self)
+{
+       TDB_DATA kbuf, dbuf;
+       int count, i;
+       struct lock_struct lock, *locks;
+
+       kbuf = locking_key(dev,ino);
+
+       dbuf.dptr = NULL;
+
+       tdb_chainlock(tdb, kbuf);
+       dbuf = tdb_fetch(tdb, kbuf);
+
+       lock.context.smbpid = smbpid;
+       lock.context.pid = pid;
+       lock.context.tid = tid;
+       lock.start = start;
+       lock.size = size;
+       lock.fnum = fnum;
+       lock.lock_type = lock_type;
+
+       if (dbuf.dptr) {
+               /* there are existing locks - make sure they don't conflict */
+               locks = (struct lock_struct *)dbuf.dptr;
+               count = dbuf.dsize / sizeof(*locks);
+               for (i=0; i<count; i++) {
+                       if (check_self) {
+                               if (brl_conflict(&locks[i], &lock))
+                                       goto fail;
+                       } else {
+                               /*
+                                * Our own locks don't conflict.
+                                */
+                               if (brl_conflict_other(&locks[i], &lock))
+                                       goto fail;
+                       }
+               }
+       }
+
+       /* no conflicts - we could have added it */
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+       return True;
+
+ fail:
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+       return False;
+}
+
+/****************************************************************************
+ Remove any locks associated with a open file.
+****************************************************************************/
+
+void brl_close(SMB_DEV_T dev, SMB_INO_T ino, pid_t pid, int tid, int fnum)
+{
+       TDB_DATA kbuf, dbuf;
+       int count, i, j, dcount=0;
+       struct lock_struct *locks;
+
+       kbuf = locking_key(dev,ino);
+
+       dbuf.dptr = NULL;
+
+       tdb_chainlock(tdb, kbuf);
+       dbuf = tdb_fetch(tdb, kbuf);
+
+       if (!dbuf.dptr) goto fail;
+
+       /* there are existing locks - remove any for this fnum */
+       locks = (struct lock_struct *)dbuf.dptr;
+       count = dbuf.dsize / sizeof(*locks);
+
+       for (i=0; i<count; i++) {
+               struct lock_struct *lock = &locks[i];
+
+               if (lock->context.tid == tid &&
+                   lock->context.pid == pid &&
+                   lock->fnum == fnum) {
+
+                       /* Send unlock messages to any pending waiters that overlap. */
+                       for (j=0; j<count; j++) {
+                               struct lock_struct *pend_lock = &locks[j];
+
+                               /* Ignore our own or non-pending locks. */
+                               if (pend_lock->lock_type != PENDING_LOCK)
+                                       continue;
+
+                               if (pend_lock->context.tid == tid &&
+                                   pend_lock->context.pid == pid &&
+                                   pend_lock->fnum == fnum)
+                                       continue;
+
+                               /* We could send specific lock info here... */
+                               if (brl_pending_overlap(lock, pend_lock))
+                                       message_send_pid(pend_lock->context.pid,
+                                                       MSG_SMB_UNLOCK,
+                                                       NULL, 0, True);
+                       }
+
+                       /* found it - delete it */
+                       if (count > 1 && i < count-1) {
+                               memmove(&locks[i], &locks[i+1], 
+                                       sizeof(*locks)*((count-1) - i));
+                       }
+                       count--;
+                       i--;
+                       dcount++;
+               }
+       }
+
+       if (count == 0) {
+               tdb_delete(tdb, kbuf);
+       } else if (count < (dbuf.dsize / sizeof(*locks))) {
+               dbuf.dsize -= dcount * sizeof(*locks);
+               tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+       }
+
+       /* we didn't find it */
+ fail:
+       SAFE_FREE(dbuf.dptr);
+       tdb_chainunlock(tdb, kbuf);
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each lock.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct lock_struct *locks;
+       struct lock_key *key;
+       int i;
+
+       BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
+
+       locks = (struct lock_struct *)dbuf.dptr;
+       key = (struct lock_key *)kbuf.dptr;
+
+       for (i=0;i<dbuf.dsize/sizeof(*locks);i++) {
+               traverse_callback(key->device, key->inode,
+                                 locks[i].context.pid,
+                                 locks[i].lock_type,
+                                 locks[i].start,
+                                 locks[i].size);
+       }
+       return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each lock in the database.
+********************************************************************/
+
+int brl_forall(BRLOCK_FN(fn))
+{
+       if (!tdb) return 0;
+       return tdb_traverse(tdb, traverse_fn, (void *)fn);
+}
diff --git a/source4/locking/locking.c b/source4/locking/locking.c
new file mode 100644 (file)
index 0000000..63e1f3a
--- /dev/null
@@ -0,0 +1,849 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Locking functions
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Jeremy Allison 1992-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.
+
+   Revision History:
+
+   12 aug 96: Erik.Devriendt@te6.siemens.be
+   added support for shared memory implementation of share mode locking
+
+   May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
+   locking to deal with multiple share modes per open file.
+
+   September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
+   support.
+
+   rewrtten completely to use new tdb code. Tridge, Dec '99
+
+   Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *lock_type_name(enum brl_type lock_type)
+{
+       return (lock_type == READ_LOCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Utility function called to see if a file region is locked.
+ If check_self is True, then checks on our own fd with the same locking context
+ are still made. If check_self is False, then checks are not made on our own fd
+ with the same locking context are not made.
+****************************************************************************/
+
+BOOL is_locked(files_struct *fsp,struct tcon_context *conn,
+              SMB_BIG_UINT count,SMB_BIG_UINT offset, 
+              enum brl_type lock_type, BOOL check_self)
+{
+       int snum = SNUM(conn);
+       BOOL ret;
+       
+       if (count == 0)
+               return(False);
+
+       if (!lp_locking(snum) || !lp_strict_locking(snum))
+               return(False);
+
+       ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum,
+                           global_smbpid, sys_getpid(), conn->cnum, 
+                           offset, count, lock_type, check_self);
+
+       DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n",
+                       (double)offset, (double)count, ret ? "locked" : "unlocked",
+                       fsp->fsp_name ));
+
+       /*
+        * There is no lock held by an SMB daemon, check to
+        * see if there is a POSIX lock from a UNIX or NFS process.
+        */
+
+       if(!ret && lp_posix_locking(snum)) {
+               ret = is_posix_locked(fsp, offset, count, lock_type);
+
+               DEBUG(10,("is_locked: posix start=%.0f len=%.0f %s for file %s\n",
+                               (double)offset, (double)count, ret ? "locked" : "unlocked",
+                               fsp->fsp_name ));
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ Utility function called by locking requests.
+****************************************************************************/
+
+static NTSTATUS do_lock(files_struct *fsp,struct tcon_context *conn, uint16 lock_pid,
+                SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type)
+{
+       NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED;
+
+       if (!lp_locking(SNUM(conn)))
+               return NT_STATUS_OK;
+
+       /* NOTE! 0 byte long ranges ARE allowed and should be stored  */
+
+       DEBUG(10,("do_lock: lock type %s start=%.0f len=%.0f requested for file %s\n",
+                 lock_type_name(lock_type), (double)offset, (double)count, fsp->fsp_name ));
+
+       if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) {
+               status = brl_lock(fsp->dev, fsp->inode, fsp->fnum,
+                                 lock_pid, sys_getpid(), conn->cnum, 
+                                 offset, count, 
+                                 lock_type);
+
+               if (NT_STATUS_IS_OK(status) && lp_posix_locking(SNUM(conn))) {
+
+                       /*
+                        * Try and get a POSIX lock on this range.
+                        * Note that this is ok if it is a read lock
+                        * overlapping on a different fd. JRA.
+                        */
+
+                       if (!set_posix_lock(fsp, offset, count, lock_type)) {
+                               status = NT_STATUS_LOCK_NOT_GRANTED;
+                               /*
+                                * We failed to map - we must now remove the brl
+                                * lock entry.
+                                */
+                               (void)brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+                                                               lock_pid, sys_getpid(), conn->cnum, 
+                                                               offset, count, False);
+                       }
+               }
+       }
+
+       return status;
+}
+
+/****************************************************************************
+ Utility function called by locking requests. This is *DISGUSTING*. It also
+ appears to be "What Windows Does" (tm). Andrew, ever wonder why Windows 2000
+ is so slow on the locking tests...... ? This is the reason. Much though I hate
+ it, we need this. JRA.
+****************************************************************************/
+
+NTSTATUS do_lock_spin(files_struct *fsp,struct tcon_context *conn, uint16 lock_pid,
+                SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type)
+{
+       int j, maxj = lp_lock_spin_count();
+       int sleeptime = lp_lock_sleep_time();
+       NTSTATUS status, ret;
+
+       if (maxj <= 0)
+               maxj = 1;
+
+       ret = NT_STATUS_OK; /* to keep dumb compilers happy */
+
+       for (j = 0; j < maxj; j++) {
+               status = do_lock(fsp, conn, lock_pid, count, offset, lock_type);
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) &&
+                   !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+                       return status;
+               }
+               /* if we do fail then return the first error code we got */
+               if (j == 0) {
+                       ret = status;
+               }
+               if (sleeptime)
+                       sys_usleep(sleeptime);
+       }
+       return ret;
+}
+
+/****************************************************************************
+ Utility function called by unlocking requests.
+****************************************************************************/
+
+NTSTATUS do_unlock(files_struct *fsp,struct tcon_context *conn, uint16 lock_pid,
+                  SMB_BIG_UINT count,SMB_BIG_UINT offset)
+{
+       BOOL ok = False;
+       
+       if (!lp_locking(SNUM(conn)))
+               return NT_STATUS_OK;
+       
+       if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) {
+               return NT_STATUS_INVALID_HANDLE;
+       }
+       
+       DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n",
+                 (double)offset, (double)count, fsp->fsp_name ));
+
+       /*
+        * Remove the existing lock record from the tdb lockdb
+        * before looking at POSIX locks. If this record doesn't
+        * match then don't bother looking to remove POSIX locks.
+        */
+
+       ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+                       lock_pid, sys_getpid(), conn->cnum, offset, count, False);
+   
+       if (!ok) {
+               DEBUG(10,("do_unlock: returning ERRlock.\n" ));
+               return NT_STATUS_RANGE_NOT_LOCKED;
+       }
+
+       if (!lp_posix_locking(SNUM(conn)))
+               return NT_STATUS_OK;
+
+       (void)release_posix_lock(fsp, offset, count);
+
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void locking_close_file(files_struct *fsp)
+{
+       pid_t pid = sys_getpid();
+
+       if (!lp_locking(SNUM(fsp->conn)))
+               return;
+
+       /*
+        * Just release all the brl locks, no need to release individually.
+        */
+
+       brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
+
+       if(lp_posix_locking(SNUM(fsp->conn))) {
+
+               /* 
+                * Release all the POSIX locks.
+                */
+               posix_locking_close_file(fsp);
+
+       }
+}
+
+/****************************************************************************
+ Initialise the locking functions.
+****************************************************************************/
+
+static int open_read_only;
+
+BOOL locking_init(int read_only)
+{
+       brl_init(read_only);
+
+       if (tdb)
+               return True;
+
+       tdb = tdb_open_log(lock_path("locking.tdb"), 
+                      0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), 
+                      read_only?O_RDONLY:O_RDWR|O_CREAT,
+                      0644);
+
+       if (!tdb) {
+               DEBUG(0,("ERROR: Failed to initialise locking database\n"));
+               return False;
+       }
+       
+       if (!posix_locking_init(read_only))
+               return False;
+
+       open_read_only = read_only;
+
+       return True;
+}
+
+/*******************************************************************
+ Deinitialize the share_mode management.
+******************************************************************/
+
+BOOL locking_end(void)
+{
+
+       brl_shutdown(open_read_only);
+       if (tdb) {
+
+               if (tdb_close(tdb) != 0)
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+       static struct locking_key key;
+       TDB_DATA kbuf;
+
+       memset(&key, '\0', sizeof(key));
+       key.dev = dev;
+       key.inode = inode;
+       kbuf.dptr = (char *)&key;
+       kbuf.dsize = sizeof(key);
+       return kbuf;
+}
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+       return locking_key(fsp->dev, fsp->inode);
+}
+
+/*******************************************************************
+ Lock a hash bucket entry.
+******************************************************************/
+
+BOOL lock_share_entry(struct tcon_context *conn,
+                     SMB_DEV_T dev, SMB_INO_T inode)
+{
+       return tdb_chainlock(tdb, locking_key(dev, inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry(struct tcon_context *conn,
+                       SMB_DEV_T dev, SMB_INO_T inode)
+{
+       tdb_chainunlock(tdb, locking_key(dev, inode));
+}
+
+/*******************************************************************
+ Lock a hash bucket entry. use a fsp for convenience
+******************************************************************/
+
+BOOL lock_share_entry_fsp(files_struct *fsp)
+{
+       return tdb_chainlock(tdb, locking_key(fsp->dev, fsp->inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry_fsp(files_struct *fsp)
+{
+       tdb_chainunlock(tdb, locking_key(fsp->dev, fsp->inode));
+}
+
+/*******************************************************************
+ Print out a share mode.
+********************************************************************/
+
+static char *share_mode_str(int num, share_mode_entry *e)
+{
+       static pstring share_str;
+
+       slprintf(share_str, sizeof(share_str)-1, "share_mode_entry[%d]: \
+pid = %u, share_mode = 0x%x, desired_access = 0x%x, port = 0x%x, type= 0x%x, file_id = %lu, dev = 0x%x, inode = %.0f",
+       num, e->pid, e->share_mode, (unsigned int)e->desired_access, e->op_port, e->op_type, e->share_file_id,
+       (unsigned int)e->dev, (double)e->inode );
+
+       return share_str;
+}
+
+/*******************************************************************
+ Print out a share mode table.
+********************************************************************/
+
+static void print_share_mode_table(struct locking_data *data)
+{
+       int num_share_modes = data->u.num_share_mode_entries;
+       share_mode_entry *shares = (share_mode_entry *)(data + 1);
+       int i;
+
+       for (i = 0; i < num_share_modes; i++) {
+               share_mode_entry *entry_p = &shares[i];
+               DEBUG(10,("print_share_mode_table: %s\n", share_mode_str(i, entry_p) ));
+       }
+}
+
+/*******************************************************************
+ Get all share mode entries for a dev/inode pair.
+********************************************************************/
+
+int get_share_modes(struct tcon_context *conn, 
+                   SMB_DEV_T dev, SMB_INO_T inode, 
+                   share_mode_entry **pp_shares)
+{
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       int num_share_modes;
+       share_mode_entry *shares = NULL;
+
+       *pp_shares = NULL;
+
+       dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+       if (!dbuf.dptr)
+               return 0;
+
+       data = (struct locking_data *)dbuf.dptr;
+       num_share_modes = data->u.num_share_mode_entries;
+       if(num_share_modes) {
+               int i;
+               int del_count = 0;
+
+               shares = (share_mode_entry *)memdup(dbuf.dptr + sizeof(*data),  
+                                               num_share_modes * sizeof(share_mode_entry));
+
+               if (!shares) {
+                       SAFE_FREE(dbuf.dptr);
+                       return 0;
+               }
+
+               /*
+                * Ensure that each entry has a real process attached.
+                */
+
+               for (i = 0; i < num_share_modes; ) {
+                       share_mode_entry *entry_p = &shares[i];
+                       if (process_exists(entry_p->pid)) {
+                               DEBUG(10,("get_share_modes: %s\n", share_mode_str(i, entry_p) ));
+                               i++;
+                       } else {
+                               DEBUG(10,("get_share_modes: deleted %s\n", share_mode_str(i, entry_p) ));
+                               memcpy( &shares[i], &shares[i+1],
+                                       sizeof(share_mode_entry) * (num_share_modes - i - 1));
+                               num_share_modes--;
+                               del_count++;
+                       }
+               }
+
+               /* Did we delete any ? If so, re-store in tdb. */
+               if (del_count) {
+                       data->u.num_share_mode_entries = num_share_modes;
+                       
+                       if (num_share_modes)
+                               memcpy(dbuf.dptr + sizeof(*data), shares,
+                                               num_share_modes * sizeof(share_mode_entry));
+
+                       /* The record has shrunk a bit */
+                       dbuf.dsize -= del_count * sizeof(share_mode_entry);
+
+                       if (tdb_store(tdb, locking_key(dev, inode), dbuf, TDB_REPLACE) == -1) {
+                               SAFE_FREE(shares);
+                               SAFE_FREE(dbuf.dptr);
+                               return 0;
+                       }
+               }
+       }
+
+       SAFE_FREE(dbuf.dptr);
+       *pp_shares = shares;
+       return num_share_modes;
+}
+
+/*******************************************************************
+ Fill a share mode entry.
+********************************************************************/
+
+static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
+{
+       share_mode_entry *e = (share_mode_entry *)p;
+       void *x = &e->time; /* Needed to force alignment. p may not be aligned.... */
+
+       memset(e, '\0', sizeof(share_mode_entry));
+       e->pid = sys_getpid();
+       e->share_mode = fsp->share_mode;
+       e->desired_access = fsp->desired_access;
+       e->op_port = port;
+       e->op_type = op_type;
+       memcpy(x, &fsp->open_time, sizeof(struct timeval));
+       e->share_file_id = fsp->file_id;
+       e->dev = fsp->dev;
+       e->inode = fsp->inode;
+}
+
+/*******************************************************************
+ Check if two share mode entries are identical, ignoring oplock 
+ and port info and desired_access.
+********************************************************************/
+
+BOOL share_modes_identical( share_mode_entry *e1, share_mode_entry *e2)
+{
+#if 1 /* JRA PARANOIA TEST - REMOVE LATER */
+       if (e1->pid == e2->pid &&
+               e1->share_file_id == e2->share_file_id &&
+               e1->dev == e2->dev &&
+               e1->inode == e2->inode &&
+               (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) != (e2->share_mode & ~DELETE_ON_CLOSE_FLAG)) {
+                       DEBUG(0,("PANIC: share_modes_identical: share_mode missmatch (e1 = %u, e2 = %u). Logic error.\n",
+                               (unsigned int)(e1->share_mode & ~DELETE_ON_CLOSE_FLAG),
+                               (unsigned int)(e2->share_mode & ~DELETE_ON_CLOSE_FLAG) ));
+               smb_panic("PANIC: share_modes_identical logic error.\n");
+       }
+#endif
+
+       return (e1->pid == e2->pid &&
+               (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) == (e2->share_mode & ~DELETE_ON_CLOSE_FLAG) &&
+               e1->dev == e2->dev &&
+               e1->inode == e2->inode &&
+               e1->share_file_id == e2->share_file_id );
+}
+
+/*******************************************************************
+ Delete a specific share mode. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted (if required).
+ Ignore if no entry deleted.
+********************************************************************/
+
+ssize_t del_share_entry( SMB_DEV_T dev, SMB_INO_T inode,
+                       share_mode_entry *entry, share_mode_entry **ppse)
+{
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       int i, del_count=0;
+       share_mode_entry *shares;
+       ssize_t count = 0;
+
+       if (ppse)
+               *ppse = NULL;
+
+       /* read in the existing share modes */
+       dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+       if (!dbuf.dptr)
+               return -1;
+
+       data = (struct locking_data *)dbuf.dptr;
+       shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+       /*
+        * Find any with this pid and delete it
+        * by overwriting with the rest of the data 
+        * from the record.
+        */
+
+       DEBUG(10,("del_share_entry: num_share_modes = %d\n", data->u.num_share_mode_entries ));
+
+       for (i=0;i<data->u.num_share_mode_entries;) {
+               if (share_modes_identical(&shares[i], entry)) {
+                       DEBUG(10,("del_share_entry: deleted %s\n",
+                               share_mode_str(i, &shares[i]) ));
+                       if (ppse)
+                               *ppse = memdup(&shares[i], sizeof(*shares));
+                       data->u.num_share_mode_entries--;
+                       memmove(&shares[i], &shares[i+1], 
+                               dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
+                       del_count++;
+
+                       DEBUG(10,("del_share_entry: deleting entry %d\n", i ));
+
+               } else {
+                       i++;
+               }
+       }
+
+       if (del_count) {
+               /* the record may have shrunk a bit */
+               dbuf.dsize -= del_count * sizeof(*shares);
+
+               count = (ssize_t)data->u.num_share_mode_entries;
+
+               /* store it back in the database */
+               if (data->u.num_share_mode_entries == 0) {
+                       if (tdb_delete(tdb, locking_key(dev, inode)) == -1)
+                               count = -1;
+               } else {
+                       if (tdb_store(tdb, locking_key(dev, inode), dbuf, TDB_REPLACE) == -1)
+                               count = -1;
+               }
+       }
+       DEBUG(10,("del_share_entry: Remaining table.\n"));
+       print_share_mode_table((struct locking_data *)dbuf.dptr);
+       SAFE_FREE(dbuf.dptr);
+       return count;
+}
+
+/*******************************************************************
+ Del the share mode of a file for this process. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted.
+********************************************************************/
+
+ssize_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
+{
+       share_mode_entry entry;
+
+       /*
+        * Fake up a share_mode_entry for comparisons.
+        */
+
+       fill_share_mode((char *)&entry, fsp, 0, 0);
+       return del_share_entry(fsp->dev, fsp->inode, &entry, ppse);
+}
+
+/*******************************************************************
+ Set the share mode of a file. Return False on fail, True on success.
+********************************************************************/
+
+BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
+{
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       char *p=NULL;
+       int size;
+       BOOL ret = True;
+               
+       /* read in the existing share modes if any */
+       dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
+       if (!dbuf.dptr) {
+               size_t offset;
+               /* we'll need to create a new record */
+               pstring fname;
+
+               pstrcpy(fname, fsp->conn->connectpath);
+               pstrcat(fname, "/");
+               pstrcat(fname, fsp->fsp_name);
+
+               size = sizeof(*data) + sizeof(share_mode_entry) + strlen(fname) + 1;
+               p = (char *)malloc(size);
+               if (!p)
+                       return False;
+               data = (struct locking_data *)p;
+               data->u.num_share_mode_entries = 1;
+       
+               DEBUG(10,("set_share_mode: creating entry for file %s. num_share_modes = 1\n",
+                       fsp->fsp_name ));
+
+               offset = sizeof(*data) + sizeof(share_mode_entry);
+               safe_strcpy(p + offset, fname, size - offset - 1);
+               fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+               dbuf.dptr = p;
+               dbuf.dsize = size;
+               if (tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE) == -1)
+                       ret = False;
+
+               print_share_mode_table((struct locking_data *)p);
+
+               SAFE_FREE(p);
+               return ret;
+       }
+
+       /* we're adding to an existing entry - this is a bit fiddly */
+       data = (struct locking_data *)dbuf.dptr;
+
+       data->u.num_share_mode_entries++;
+       
+       DEBUG(10,("set_share_mode: adding entry for file %s. new num_share_modes = %d\n",
+               fsp->fsp_name, data->u.num_share_mode_entries ));
+
+       size = dbuf.dsize + sizeof(share_mode_entry);
+       p = malloc(size);
+       if (!p) {
+               SAFE_FREE(dbuf.dptr);
+               return False;
+       }
+       memcpy(p, dbuf.dptr, sizeof(*data));
+       fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+       memcpy(p + sizeof(*data) + sizeof(share_mode_entry), dbuf.dptr + sizeof(*data),
+              dbuf.dsize - sizeof(*data));
+       SAFE_FREE(dbuf.dptr);
+       dbuf.dptr = p;
+       dbuf.dsize = size;
+       if (tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE) == -1)
+               ret = False;
+       print_share_mode_table((struct locking_data *)p);
+       SAFE_FREE(p);
+       return ret;
+}
+
+/*******************************************************************
+ A generic in-place modification call for share mode entries.
+********************************************************************/
+
+static BOOL mod_share_mode( SMB_DEV_T dev, SMB_INO_T inode, share_mode_entry *entry,
+                          void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
+                          void *param)
+{
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       int i;
+       share_mode_entry *shares;
+       BOOL need_store=False;
+       BOOL ret = True;
+
+       /* read in the existing share modes */
+       dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+       if (!dbuf.dptr)
+               return False;
+
+       data = (struct locking_data *)dbuf.dptr;
+       shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+       /* find any with our pid and call the supplied function */
+       for (i=0;i<data->u.num_share_mode_entries;i++) {
+               if (share_modes_identical(entry, &shares[i])) {
+                       mod_fn(&shares[i], dev, inode, param);
+                       need_store=True;
+               }
+       }
+
+       /* if the mod fn was called then store it back */
+       if (need_store) {
+               if (data->u.num_share_mode_entries == 0) {
+                       if (tdb_delete(tdb, locking_key(dev, inode)) == -1)
+                               ret = False;
+               } else {
+                       if (tdb_store(tdb, locking_key(dev, inode), dbuf, TDB_REPLACE) == -1)
+                               ret = False;
+               }
+       }
+
+       SAFE_FREE(dbuf.dptr);
+       return ret;
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
+                                   void *param)
+{
+       DEBUG(10,("remove_share_oplock_fn: removing oplock info for entry dev=%x ino=%.0f\n",
+                 (unsigned int)dev, (double)inode ));
+       /* Delete the oplock info. */
+       entry->op_port = 0;
+       entry->op_type = NO_OPLOCK;
+}
+
+/*******************************************************************
+ Remove an oplock port and mode entry from a share mode.
+********************************************************************/
+
+BOOL remove_share_oplock(files_struct *fsp)
+{
+       share_mode_entry entry;
+       /*
+        * Fake up an entry for comparisons...
+        */
+       fill_share_mode((char *)&entry, fsp, 0, 0);
+       return mod_share_mode(fsp->dev, fsp->inode, &entry, remove_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
+                                   void *param)
+{
+       DEBUG(10,("downgrade_share_oplock_fn: downgrading oplock info for entry dev=%x ino=%.0f\n",
+                 (unsigned int)dev, (double)inode ));
+       entry->op_type = LEVEL_II_OPLOCK;
+}
+
+/*******************************************************************
+ Downgrade a oplock type from exclusive to level II.
+********************************************************************/
+
+BOOL downgrade_share_oplock(files_struct *fsp)
+{
+       share_mode_entry entry;
+       /*
+        * Fake up an entry for comparisons...
+        */
+       fill_share_mode((char *)&entry, fsp, 0, 0);
+       return mod_share_mode(fsp->dev, fsp->inode, &entry, downgrade_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Get/Set the delete on close flag in a set of share modes.
+ Return False on fail, True on success.
+********************************************************************/
+
+BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
+{
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       int i;
+       share_mode_entry *shares;
+
+       /* read in the existing share modes */
+       dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+       if (!dbuf.dptr)
+               return False;
+
+       data = (struct locking_data *)dbuf.dptr;
+       shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+       /* Set/Unset the delete on close element. */
+       for (i=0;i<data->u.num_share_mode_entries;i++,shares++) {
+               shares->share_mode = (delete_on_close ?
+                            (shares->share_mode | DELETE_ON_CLOSE_FLAG) :
+                            (shares->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+       }
+
+       /* store it back */
+       if (data->u.num_share_mode_entries) {
+               if (tdb_store(tdb, locking_key(dev,inode), dbuf, TDB_REPLACE)==-1) {
+                       SAFE_FREE(dbuf.dptr);
+                       return False;
+               }
+       }
+
+       SAFE_FREE(dbuf.dptr);
+       return True;
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each share mode
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
+                       void* state)
+{
+       struct locking_data *data;
+       share_mode_entry *shares;
+       char *name;
+       int i;
+
+       SHAREMODE_FN(traverse_callback) = (SHAREMODE_FN_CAST())state;
+
+       data = (struct locking_data *)dbuf.dptr;
+       shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+       name = dbuf.dptr + sizeof(*data) + data->u.num_share_mode_entries*sizeof(*shares);
+
+       for (i=0;i<data->u.num_share_mode_entries;i++) {
+               traverse_callback(&shares[i], name);
+       }
+       return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each entry under management by the
+ share mode system.
+********************************************************************/
+
+int share_mode_forall(SHAREMODE_FN(fn))
+{
+       if (!tdb)
+               return 0;
+       return tdb_traverse(tdb, traverse_fn, (void*)fn);
+}
diff --git a/source4/locking/posix.c b/source4/locking/posix.c
new file mode 100644 (file)
index 0000000..81690ba
--- /dev/null
@@ -0,0 +1,1313 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Locking functions
+   Copyright (C) Jeremy Allison 1992-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.
+
+   Revision History:
+
+   POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+
+/*
+ * The POSIX locking database handle.
+ */
+
+static TDB_CONTEXT *posix_lock_tdb;
+
+/*
+ * The pending close database handle.
+ */
+
+static TDB_CONTEXT *posix_pending_close_tdb;
+
+/*
+ * The data in POSIX lock records is an unsorted linear array of these
+ * records.  It is unnecessary to store the count as tdb provides the
+ * size of the record.
+ */
+
+struct posix_lock {
+       int fd;
+       SMB_OFF_T start;
+       SMB_OFF_T size;
+       int lock_type;
+};
+
+/*
+ * The data in POSIX pending close records is an unsorted linear array of int
+ * records.  It is unnecessary to store the count as tdb provides the
+ * size of the record.
+ */
+
+/* The key used in both the POSIX databases. */
+
+struct posix_lock_key {
+       SMB_DEV_T device;
+       SMB_INO_T inode;
+}; 
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+       static struct posix_lock_key key;
+       TDB_DATA kbuf;
+
+       memset(&key, '\0', sizeof(key));
+       key.device = dev;
+       key.inode = inode;
+       kbuf.dptr = (char *)&key;
+       kbuf.dsize = sizeof(key);
+       return kbuf;
+}
+
+/*******************************************************************
+ Convenience function to get a key from an fsp.
+******************************************************************/
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+       return locking_key(fsp->dev, fsp->inode);
+}
+
+/****************************************************************************
+ Add an fd to the pending close tdb.
+****************************************************************************/
+
+static BOOL add_fd_to_close_entry(files_struct *fsp)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       char *tp;
+
+       dbuf.dptr = NULL;
+
+       dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+       tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(int));
+       if (!tp) {
+               DEBUG(0,("add_fd_to_close_entry: Realloc fail !\n"));
+               SAFE_FREE(dbuf.dptr);
+               return False;
+       } else
+               dbuf.dptr = tp;
+
+       memcpy(dbuf.dptr + dbuf.dsize, &fsp->fd, sizeof(int));
+       dbuf.dsize += sizeof(int);
+
+       if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+               DEBUG(0,("add_fd_to_close_entry: tdb_store fail !\n"));
+       }
+
+       SAFE_FREE(dbuf.dptr);
+       return True;
+}
+
+/****************************************************************************
+ Remove all fd entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_close_entries(files_struct *fsp)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+
+       if (tdb_delete(posix_pending_close_tdb, kbuf) == -1)
+               DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Get the array of POSIX pending close records for an open fsp. Caller must
+ free. Returns number of entries.
+****************************************************************************/
+
+static size_t get_posix_pending_close_entries(files_struct *fsp, int **entries)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       size_t count = 0;
+
+       *entries = NULL;
+       dbuf.dptr = NULL;
+
+       dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+       if (!dbuf.dptr) {
+               return 0;
+       }
+
+       *entries = (int *)dbuf.dptr;
+       count = (size_t)(dbuf.dsize / sizeof(int));
+
+       return count;
+}
+
+/****************************************************************************
+ Get the array of POSIX locks for an fsp. Caller must free. Returns
+ number of entries.
+****************************************************************************/
+
+static size_t get_posix_lock_entries(files_struct *fsp, struct posix_lock **entries)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       size_t count = 0;
+
+       *entries = NULL;
+
+       dbuf.dptr = NULL;
+
+       dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+       if (!dbuf.dptr) {
+               return 0;
+       }
+
+       *entries = (struct posix_lock *)dbuf.dptr;
+       count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+
+       return count;
+}
+
+/****************************************************************************
+ Deal with pending closes needed by POSIX locking support.
+ Note that posix_locking_close_file() is expected to have been called
+ to delete all locks on this fsp before this function is called.
+****************************************************************************/
+
+int fd_close_posix(struct tcon_context *conn, files_struct *fsp)
+{
+       int saved_errno = 0;
+       int ret;
+       size_t count, i;
+       struct posix_lock *entries = NULL;
+       int *fd_array = NULL;
+       BOOL locks_on_other_fds = False;
+
+       if (!lp_posix_locking(SNUM(conn))) {
+               /*
+                * No POSIX to worry about, just close.
+                */
+               ret = conn->vfs_ops.close(fsp,fsp->fd);
+               fsp->fd = -1;
+               return ret;
+       }
+
+       /*
+        * Get the number of outstanding POSIX locks on this dev/inode pair.
+        */
+
+       count = get_posix_lock_entries(fsp, &entries);
+
+       /*
+        * Check if there are any outstanding locks belonging to
+        * other fd's. This should never be the case if posix_locking_close_file()
+        * has been called first, but it never hurts to be *sure*.
+        */
+
+       for (i = 0; i < count; i++) {
+               if (entries[i].fd != fsp->fd) {
+                       locks_on_other_fds = True;
+                       break;
+               }
+       }
+
+       if (locks_on_other_fds) {
+
+               /*
+                * There are outstanding locks on this dev/inode pair on other fds.
+                * Add our fd to the pending close tdb and set fsp->fd to -1.
+                */
+
+               if (!add_fd_to_close_entry(fsp)) {
+                       SAFE_FREE(entries);
+                       return False;
+               }
+
+               SAFE_FREE(entries);
+               fsp->fd = -1;
+               return 0;
+       }
+
+       SAFE_FREE(entries);
+
+       /*
+        * No outstanding POSIX locks. Get the pending close fd's
+        * from the tdb and close them all.
+        */
+
+       count = get_posix_pending_close_entries(fsp, &fd_array);
+
+       if (count) {
+               DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)count ));
+
+               for(i = 0; i < count; i++) {
+                       if (conn->vfs_ops.close(fsp,fd_array[i]) == -1) {
+                               saved_errno = errno;
+                       }
+               }
+
+               /*
+                * Delete all fd's stored in the tdb
+                * for this dev/inode pair.
+                */
+
+               delete_close_entries(fsp);
+       }
+
+       SAFE_FREE(fd_array);
+
+       /*
+        * Finally close the fd associated with this fsp.
+        */
+
+       ret = conn->vfs_ops.close(fsp,fsp->fd);
+
+       if (saved_errno != 0) {
+        errno = saved_errno;
+               ret = -1;
+    } 
+
+       fsp->fd = -1;
+
+       return ret;
+}
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *posix_lock_type_name(int lock_type)
+{
+       return (lock_type == F_RDLCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Delete a POSIX lock entry by index number. Used if the tdb add succeeds, but
+ then the POSIX fcntl lock fails.
+****************************************************************************/
+
+static BOOL delete_posix_lock_entry_by_index(files_struct *fsp, size_t entry)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       struct posix_lock *locks;
+       size_t count;
+
+       dbuf.dptr = NULL;
+       
+       dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+       if (!dbuf.dptr) {
+               DEBUG(10,("delete_posix_lock_entry_by_index: tdb_fetch failed !\n"));
+               goto fail;
+       }
+
+       count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+       locks = (struct posix_lock *)dbuf.dptr;
+
+       if (count == 1) {
+               tdb_delete(posix_lock_tdb, kbuf);
+       } else {
+               if (entry < count-1) {
+                       memmove(&locks[entry], &locks[entry+1], sizeof(*locks)*((count-1) - entry));
+               }
+               dbuf.dsize -= sizeof(*locks);
+               tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+       }
+
+       SAFE_FREE(dbuf.dptr);
+
+       return True;
+
+ fail:
+
+       SAFE_FREE(dbuf.dptr);
+       return False;
+}
+
+/****************************************************************************
+ Add an entry into the POSIX locking tdb. We return the index number of the
+ added lock (used in case we need to delete *exactly* this entry). Returns
+ False on fail, True on success.
+****************************************************************************/
+
+static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, int lock_type, size_t *pentry_num)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       struct posix_lock pl;
+       char *tp;
+
+       dbuf.dptr = NULL;
+
+       dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+       *pentry_num = (size_t)(dbuf.dsize / sizeof(pl));
+
+       /*
+        * Add new record.
+        */
+
+       pl.fd = fsp->fd;
+       pl.start = start;
+       pl.size = size;
+       pl.lock_type = lock_type;
+
+       tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
+       if (!tp) {
+               DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
+               goto fail;
+       } else
+               dbuf.dptr = tp;
+
+       memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
+       dbuf.dsize += sizeof(pl);
+
+       if (tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+               DEBUG(0,("add_posix_lock: Failed to add lock entry on file %s\n", fsp->fsp_name));
+               goto fail;
+       }
+
+       SAFE_FREE(dbuf.dptr);
+
+       DEBUG(10,("add_posix_lock: File %s: type = %s: start=%.0f size=%.0f: dev=%.0f inode=%.0f\n",
+                       fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size,
+                       (double)fsp->dev, (double)fsp->inode ));
+
+       return True;
+
+ fail:
+
+       SAFE_FREE(dbuf.dptr);
+       return False;
+}
+
+/****************************************************************************
+ Calculate if locks have any overlap at all.
+****************************************************************************/
+
+static BOOL does_lock_overlap(SMB_OFF_T start1, SMB_OFF_T size1, SMB_OFF_T start2, SMB_OFF_T size2)
+{
+       if (start1 >= start2 && start1 <= start2 + size2)
+               return True;
+
+       if (start1 < start2 && start1 + size1 > start2)
+               return True;
+
+       return False;
+}
+
+/****************************************************************************
+ Delete an entry from the POSIX locking tdb. Returns a copy of the entry being
+ deleted and the number of records that are overlapped by this one, or -1 on error.
+****************************************************************************/
+
+static int delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, struct posix_lock *pl)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       struct posix_lock *locks;
+       size_t i, count;
+       BOOL found = False;
+       int num_overlapping_records = 0;
+
+       dbuf.dptr = NULL;
+       
+       dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+       if (!dbuf.dptr) {
+               DEBUG(10,("delete_posix_lock_entry: tdb_fetch failed !\n"));
+               goto fail;
+       }
+
+       /* There are existing locks - find a match. */
+       locks = (struct posix_lock *)dbuf.dptr;
+       count = (size_t)(dbuf.dsize / sizeof(*locks));
+
+       /*
+        * Search for and delete the first record that matches the
+        * unlock criteria.
+        */
+
+       for (i=0; i<count; i++) { 
+               struct posix_lock *entry = &locks[i];
+
+               if (entry->fd == fsp->fd &&
+                       entry->start == start &&
+                       entry->size == size) {
+
+                       /* Make a copy if requested. */
+                       if (pl)
+                               *pl = *entry;
+
+                       /* Found it - delete it. */
+                       if (count == 1) {
+                               tdb_delete(posix_lock_tdb, kbuf);
+                       } else {
+                               if (i < count-1) {
+                                       memmove(&locks[i], &locks[i+1], sizeof(*locks)*((count-1) - i));
+                               }
+                               dbuf.dsize -= sizeof(*locks);
+                               tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+                       }
+                       count--;
+                       found = True;
+                       break;
+               }
+       }
+
+       if (!found)
+               goto fail;
+
+       /*
+        * Count the number of entries that are
+        * overlapped by this unlock request.
+        */
+
+       for (i = 0; i < count; i++) {
+               struct posix_lock *entry = &locks[i];
+
+               if (fsp->fd == entry->fd &&
+                       does_lock_overlap( start, size, entry->start, entry->size))
+                               num_overlapping_records++;
+       }
+
+       DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d\n",
+                       posix_lock_type_name(pl->lock_type), (double)pl->start, (double)pl->size,
+                               (unsigned int)num_overlapping_records ));
+
+       SAFE_FREE(dbuf.dptr);
+
+       return num_overlapping_records;
+
+ fail:
+
+       SAFE_FREE(dbuf.dptr);
+       return -1;
+}
+
+/****************************************************************************
+ Utility function to map a lock type correctly depending on the open
+ mode of a file.
+****************************************************************************/
+
+static int map_posix_lock_type( files_struct *fsp, enum brl_type lock_type)
+{
+       if((lock_type == WRITE_LOCK) && !fsp->can_write) {
+               /*
+                * Many UNIX's cannot get a write lock on a file opened read-only.
+                * Win32 locking semantics allow this.
+                * Do the best we can and attempt a read-only lock.
+                */
+               DEBUG(10,("map_posix_lock_type: Downgrading write lock to read due to read-only file.\n"));
+               return F_RDLCK;
+       } else if((lock_type == READ_LOCK) && !fsp->can_read) {
+               /*
+                * Ditto for read locks on write only files.
+                */
+               DEBUG(10,("map_posix_lock_type: Changing read lock to write due to write-only file.\n"));
+               return F_WRLCK;
+       }
+
+  /*
+   * This return should be the most normal, as we attempt
+   * to always open files read/write.
+   */
+
+  return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
+}
+
+/****************************************************************************
+ Check to see if the given unsigned lock range is within the possible POSIX
+ range. Modifies the given args to be in range if possible, just returns
+ False if not.
+****************************************************************************/
+
+static BOOL posix_lock_in_range(SMB_OFF_T *offset_out, SMB_OFF_T *count_out,
+                                                               SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+       SMB_OFF_T offset = (SMB_OFF_T)u_offset;
+       SMB_OFF_T count = (SMB_OFF_T)u_count;
+
+       /*
+        * For the type of system we are, attempt to
+        * find the maximum positive lock offset as an SMB_OFF_T.
+        */
+
+#if defined(MAX_POSITIVE_LOCK_OFFSET) /* Some systems have arbitrary limits. */
+
+       SMB_OFF_T max_positive_lock_offset = (MAX_POSITIVE_LOCK_OFFSET);
+
+#elif defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
+
+       /*
+        * In this case SMB_OFF_T is 64 bits,
+        * and the underlying system can handle 64 bit signed locks.
+        */
+
+    SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
+    SMB_OFF_T mask = (mask2<<1);
+    SMB_OFF_T max_positive_lock_offset = ~mask;
+
+#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+       /*
+        * In this case either SMB_OFF_T is 32 bits,
+        * or the underlying system cannot handle 64 bit signed locks.
+        * All offsets & counts must be 2^31 or less.
+        */
+
+    SMB_OFF_T max_positive_lock_offset = 0x7FFFFFFF;
+
+#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+       /*
+        * POSIX locks of length zero mean lock to end-of-file.
+        * Win32 locks of length zero are point probes. Ignore
+        * any Win32 locks of length zero. JRA.
+        */
+
+       if (count == (SMB_OFF_T)0) {
+               DEBUG(10,("posix_lock_in_range: count = 0, ignoring.\n"));
+               return False;
+       }
+
+       /*
+        * If the given offset was > max_positive_lock_offset then we cannot map this at all
+        * ignore this lock.
+        */
+
+       if (u_offset & ~((SMB_BIG_UINT)max_positive_lock_offset)) {
+               DEBUG(10,("posix_lock_in_range: (offset = %.0f) offset > %.0f and we cannot handle this. Ignoring lock.\n",
+                               (double)u_offset, (double)((SMB_BIG_UINT)max_positive_lock_offset) ));
+               return False;
+       }
+
+       /*
+        * We must truncate the count to less than max_positive_lock_offset.
+        */
+
+       if (u_count & ~((SMB_BIG_UINT)max_positive_lock_offset))
+               count = max_positive_lock_offset;
+
+       /*
+        * Truncate count to end at max lock offset.
+        */
+
+       if (offset + count < 0 || offset + count > max_positive_lock_offset)
+               count = max_positive_lock_offset - offset;
+
+       /*
+        * If we ate all the count, ignore this lock.
+        */
+
+       if (count == 0) {
+               DEBUG(10,("posix_lock_in_range: Count = 0. Ignoring lock u_offset = %.0f, u_count = %.0f\n",
+                               (double)u_offset, (double)u_count ));
+               return False;
+       }
+
+       /*
+        * The mapping was successful.
+        */
+
+       DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
+                       (double)offset, (double)count ));
+
+       *offset_out = offset;
+       *count_out = count;
+       
+       return True;
+}
+
+/****************************************************************************
+ Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
+****************************************************************************/
+
+static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+       int ret;
+       struct tcon_context *conn = fsp->conn;
+
+       DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type));
+
+       ret = conn->vfs_ops.lock(fsp,fsp->fd,op,offset,count,type);
+
+       if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno ==  EINVAL))) {
+
+               DEBUG(0,("posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned\n",
+                                       (double)offset,(double)count));
+               DEBUG(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno)));
+               DEBUG(0,("on 32 bit NFS mounted file systems.\n"));
+
+               /*
+                * If the offset is > 0x7FFFFFFF then this will cause problems on
+                * 32 bit NFS mounted filesystems. Just ignore it.
+                */
+
+               if (offset & ~((SMB_OFF_T)0x7fffffff)) {
+                       DEBUG(0,("Offset greater than 31 bits. Returning success.\n"));
+                       return True;
+               }
+
+               if (count & ~((SMB_OFF_T)0x7fffffff)) {
+                       /* 32 bit NFS file system, retry with smaller offset */
+                       DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n"));
+                       errno = 0;
+                       count &= 0x7fffffff;
+                       ret = conn->vfs_ops.lock(fsp,fsp->fd,op,offset,count,type);
+               }
+       }
+
+       DEBUG(8,("posix_fcntl_lock: Lock call %s\n", ret ? "successful" : "failed"));
+
+       return ret;
+}
+
+/****************************************************************************
+ POSIX function to see if a file region is locked. Returns True if the
+ region is locked, False otherwise.
+****************************************************************************/
+
+BOOL is_posix_locked(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+       SMB_OFF_T offset;
+       SMB_OFF_T count;
+       int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+       DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
+                       fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+       /*
+        * If the requested lock won't fit in the POSIX range, we will
+        * never set it, so presume it is not locked.
+        */
+
+       if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+               return False;
+
+       /*
+        * Note that most UNIX's can *test* for a write lock on
+        * a read-only fd, just not *set* a write lock on a read-only
+        * fd. So we don't need to use map_lock_type here.
+        */ 
+
+       return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type);
+}
+
+/*
+ * Structure used when splitting a lock range
+ * into a POSIX lock range. Doubly linked list.
+ */
+
+struct lock_list {
+    struct lock_list *next;
+    struct lock_list *prev;
+    SMB_OFF_T start;
+    SMB_OFF_T size;
+};
+
+/****************************************************************************
+ Create a list of lock ranges that don't overlap a given range. Used in calculating
+ POSIX locks and unlocks. This is a difficult function that requires ASCII art to
+ understand it :-).
+****************************************************************************/
+
+static struct lock_list *posix_lock_list(TALLOC_CTX *ctx, struct lock_list *lhead, files_struct *fsp)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+       TDB_DATA dbuf;
+       struct posix_lock *locks;
+       size_t num_locks, i;
+
+       dbuf.dptr = NULL;
+
+       dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+       if (!dbuf.dptr)
+               return lhead;
+       
+       locks = (struct posix_lock *)dbuf.dptr;
+       num_locks = (size_t)(dbuf.dsize / sizeof(*locks));
+
+       /*
+        * Check the current lock list on this dev/inode pair.
+        * Quit if the list is deleted.
+        */
+
+       DEBUG(10,("posix_lock_list: curr: start=%.0f,size=%.0f\n",
+               (double)lhead->start, (double)lhead->size ));
+
+       for (i=0; i<num_locks && lhead; i++) {
+
+               struct posix_lock *lock = &locks[i];
+               struct lock_list *l_curr;
+
+               /*
+                * Walk the lock list, checking for overlaps. Note that
+                * the lock list can expand within this loop if the current
+                * range being examined needs to be split.
+                */
+
+               for (l_curr = lhead; l_curr;) {
+
+                       DEBUG(10,("posix_lock_list: lock: fd=%d: start=%.0f,size=%.0f:type=%s", lock->fd,
+                               (double)lock->start, (double)lock->size, posix_lock_type_name(lock->lock_type) ));
+
+                       if ( (l_curr->start >= (lock->start + lock->size)) ||
+                                (lock->start >= (l_curr->start + l_curr->size))) {
+
+                               /* No overlap with this lock - leave this range alone. */
+/*********************************************
+                                             +---------+
+                                             | l_curr  |
+                                             +---------+
+                                +-------+
+                                | lock  |
+                                +-------+
+OR....
+             +---------+
+             |  l_curr |
+             +---------+
+**********************************************/
+
+                               DEBUG(10,("no overlap case.\n" ));
+
+                               l_curr = l_curr->next;
+
+                       } else if ( (l_curr->start >= lock->start) &&
+                                               (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+                               /*
+                                * This unlock is completely overlapped by this existing lock range
+                                * and thus should have no effect (not be unlocked). Delete it from the list.
+                                */
+/*********************************************
+                +---------+
+                |  l_curr |
+                +---------+
+        +---------------------------+
+        |       lock                |
+        +---------------------------+
+**********************************************/
+                               /* Save the next pointer */
+                               struct lock_list *ul_next = l_curr->next;
+
+                               DEBUG(10,("delete case.\n" ));
+
+                               DLIST_REMOVE(lhead, l_curr);
+                               if(lhead == NULL)
+                                       break; /* No more list... */
+
+                               l_curr = ul_next;
+                               
+                       } else if ( (l_curr->start >= lock->start) &&
+                                               (l_curr->start < lock->start + lock->size) &&
+                                               (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+
+                               /*
+                                * This unlock overlaps the existing lock range at the high end.
+                                * Truncate by moving start to existing range end and reducing size.
+                                */
+/*********************************************
+                +---------------+
+                |  l_curr       |
+                +---------------+
+        +---------------+
+        |    lock       |
+        +---------------+
+BECOMES....
+                        +-------+
+                        | l_curr|
+                        +-------+
+**********************************************/
+
+                               l_curr->size = (l_curr->start + l_curr->size) - (lock->start + lock->size);
+                               l_curr->start = lock->start + lock->size;
+
+                               DEBUG(10,("truncate high case: start=%.0f,size=%.0f\n",
+                                                               (double)l_curr->start, (double)l_curr->size ));
+
+                               l_curr = l_curr->next;
+
+                       } else if ( (l_curr->start < lock->start) &&
+                                               (l_curr->start + l_curr->size > lock->start) &&
+                                               (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+                               /*
+                                * This unlock overlaps the existing lock range at the low end.
+                                * Truncate by reducing size.
+                                */
+/*********************************************
+   +---------------+
+   |  l_curr       |
+   +---------------+
+           +---------------+
+           |    lock       |
+           +---------------+
+BECOMES....
+   +-------+
+   | l_curr|
+   +-------+
+**********************************************/
+
+                               l_curr->size = lock->start - l_curr->start;
+
+                               DEBUG(10,("truncate low case: start=%.0f,size=%.0f\n",
+                                                               (double)l_curr->start, (double)l_curr->size ));
+
+                               l_curr = l_curr->next;
+               
+                       } else if ( (l_curr->start < lock->start) &&
+                                               (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+                               /*
+                                * Worst case scenario. Unlock request completely overlaps an existing
+                                * lock range. Split the request into two, push the new (upper) request
+                                * into the dlink list, and continue with the entry after ul_new (as we
+                                * know that ul_new will not overlap with this lock).
+                                */
+/*********************************************
+        +---------------------------+
+        |        l_curr             |
+        +---------------------------+
+                +---------+
+                | lock    |
+                +---------+
+BECOMES.....
+        +-------+         +---------+
+        | l_curr|         | l_new   |
+        +-------+         +---------+
+**********************************************/
+                               struct lock_list *l_new = (struct lock_list *)talloc(ctx,
+                                                                                                       sizeof(struct lock_list));
+
+                               if(l_new == NULL) {
+                                       DEBUG(0,("posix_lock_list: talloc fail.\n"));
+                                       return NULL; /* The talloc_destroy takes care of cleanup. */
+                               }
+
+                               ZERO_STRUCTP(l_new);
+                               l_new->start = lock->start + lock->size;
+                               l_new->size = l_curr->start + l_curr->size - l_new->start;
+
+                               /* Truncate the l_curr. */
+                               l_curr->size = lock->start - l_curr->start;
+
+                               DEBUG(10,("split case: curr: start=%.0f,size=%.0f \
+new: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size,
+                                                               (double)l_new->start, (double)l_new->size ));
+
+                               /*
+                                * Add into the dlink list after the l_curr point - NOT at lhead. 
+                                * Note we can't use DLINK_ADD here as this inserts at the head of the given list.
+                                */
+
+                               l_new->prev = l_curr;
+                               l_new->next = l_curr->next;
+                               l_curr->next = l_new;
+
+                               /* And move after the link we added. */
+                               l_curr = l_new->next;
+
+                       } else {
+
+                               /*
+                                * This logic case should never happen. Ensure this is the
+                                * case by forcing an abort.... Remove in production.
+                                */
+                               pstring msg;
+
+                               slprintf(msg, sizeof(msg)-1, "logic flaw in cases: l_curr: start = %.0f, size = %.0f : \
+lock: start = %.0f, size = %.0f\n", (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size );
+
+                               smb_panic(msg);
+                       }
+               } /* end for ( l_curr = lhead; l_curr;) */
+       } /* end for (i=0; i<num_locks && ul_head; i++) */
+
+       SAFE_FREE(dbuf.dptr);
+       
+       return lhead;
+}
+
+/****************************************************************************
+ POSIX function to acquire a lock. Returns True if the
+ lock could be granted, False if not.
+****************************************************************************/
+
+BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+       SMB_OFF_T offset;
+       SMB_OFF_T count;
+       BOOL ret = True;
+       size_t entry_num = 0;
+       size_t lock_count;
+       TALLOC_CTX *l_ctx = NULL;
+       struct lock_list *llist = NULL;
+       struct lock_list *ll = NULL;
+       int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+       DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n",
+                       fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+       /*
+        * If the requested lock won't fit in the POSIX range, we will
+        * pretend it was successful.
+        */
+
+       if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+               return True;
+
+       /*
+        * Windows is very strange. It allows read locks to be overlayed
+        * (even over a write lock), but leaves the write lock in force until the first
+        * unlock. It also reference counts the locks. This means the following sequence :
+        *
+        * process1                                      process2
+        * ------------------------------------------------------------------------
+        * WRITE LOCK : start = 2, len = 10
+        *                                            READ LOCK: start =0, len = 10 - FAIL
+        * READ LOCK : start = 0, len = 14 
+        *                                            READ LOCK: start =0, len = 10 - FAIL
+        * UNLOCK : start = 2, len = 10
+        *                                            READ LOCK: start =0, len = 10 - OK
+        *
+        * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but
+        * would leave a single read lock over the 0-14 region. In order to
+        * re-create Windows semantics mapped to POSIX locks, we create multiple TDB
+        * entries, one for each overlayed lock request. We are guarenteed by the brlock
+        * semantics that if a write lock is added, then it will be first in the array.
+        */
+       
+       if ((l_ctx = talloc_init("set_posix_lock")) == NULL) {
+               DEBUG(0,("set_posix_lock: unable to init talloc context.\n"));
+               return True; /* Not a fatal error. */
+       }
+
+       if ((ll = (struct lock_list *)talloc(l_ctx, sizeof(struct lock_list))) == NULL) {
+               DEBUG(0,("set_posix_lock: unable to talloc unlock list.\n"));
+               talloc_destroy(l_ctx);
+               return True; /* Not a fatal error. */
+       }
+
+       /*
+        * Create the initial list entry containing the
+        * lock we want to add.
+        */
+
+       ZERO_STRUCTP(ll);
+       ll->start = offset;
+       ll->size = count;
+
+       DLIST_ADD(llist, ll);
+
+       /*
+        * The following call calculates if there are any
+        * overlapping locks held by this process on
+        * fd's open on the same file and splits this list
+        * into a list of lock ranges that do not overlap with existing
+        * POSIX locks.
+        */
+
+       llist = posix_lock_list(l_ctx, llist, fsp);
+
+       /*
+        * Now we have the list of ranges to lock it is safe to add the
+        * entry into the POSIX lock tdb. We take note of the entry we
+        * added here in case we have to remove it on POSIX lock fail.
+        */
+
+       if (!add_posix_lock_entry(fsp,offset,count,posix_lock_type,&entry_num)) {
+               DEBUG(0,("set_posix_lock: Unable to create posix lock entry !\n"));
+               talloc_destroy(l_ctx);
+               return False;
+       }
+
+       /*
+        * Add the POSIX locks on the list of ranges returned.
+        * As the lock is supposed to be added atomically, we need to
+        * back out all the locks if any one of these calls fail.
+        */
+
+       for (lock_count = 0, ll = llist; ll; ll = ll->next, lock_count++) {
+               offset = ll->start;
+               count = ll->size;
+
+               DEBUG(5,("set_posix_lock: Real lock: Type = %s: offset = %.0f, count = %.0f\n",
+                       posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+               if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+                       DEBUG(5,("set_posix_lock: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
+                               posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
+                       ret = False;
+                       break;
+               }
+       }
+
+       if (!ret) {
+
+               /*
+                * Back out all the POSIX locks we have on fail.
+                */
+
+               for (ll = llist; lock_count; ll = ll->next, lock_count--) {
+                       offset = ll->start;
+                       count = ll->size;
+
+                       DEBUG(5,("set_posix_lock: Backing out locks: Type = %s: offset = %.0f, count = %.0f\n",
+                               posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+                       posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK);
+               }
+
+               /*
+                * Remove the tdb entry for this lock.
+                */
+
+               delete_posix_lock_entry_by_index(fsp,entry_num);
+       }
+
+       talloc_destroy(l_ctx);
+       return ret;
+}
+
+/****************************************************************************
+ POSIX function to release a lock. Returns True if the
+ lock could be released, False if not.
+****************************************************************************/
+
+BOOL release_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+       SMB_OFF_T offset;
+       SMB_OFF_T count;
+       BOOL ret = True;
+       TALLOC_CTX *ul_ctx = NULL;
+       struct lock_list *ulist = NULL;
+       struct lock_list *ul = NULL;
+       struct posix_lock deleted_lock;
+       int num_overlapped_entries;
+
+       DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
+               fsp->fsp_name, (double)u_offset, (double)u_count ));
+
+       /*
+        * If the requested lock won't fit in the POSIX range, we will
+        * pretend it was successful.
+        */
+
+       if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+               return True;
+
+       /*
+        * We treat this as one unlock request for POSIX accounting purposes even
+        * if it may later be split into multiple smaller POSIX unlock ranges.
+        * num_overlapped_entries is the number of existing locks that have any
+        * overlap with this unlock request.
+        */ 
+
+       num_overlapped_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock);
+
+       if (num_overlapped_entries == -1) {
+        smb_panic("release_posix_lock: unable find entry to delete !\n");
+       }
+
+       /*
+        * If num_overlapped_entries is > 0, and the lock_type we just deleted from the tdb was
+        * a POSIX write lock, then before doing the unlock we need to downgrade
+        * the POSIX lock to a read lock. This allows any overlapping read locks
+        * to be atomically maintained.
+        */
+
+       if (num_overlapped_entries > 0 && deleted_lock.lock_type == F_WRLCK) {
+               if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) {
+                       DEBUG(0,("release_posix_lock: downgrade of lock failed with error %s !\n", strerror(errno) ));
+                       return False;
+               }
+       }
+
+       if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) {
+               DEBUG(0,("release_posix_lock: unable to init talloc context.\n"));
+               return True; /* Not a fatal error. */
+       }
+
+       if ((ul = (struct lock_list *)talloc(ul_ctx, sizeof(struct lock_list))) == NULL) {
+               DEBUG(0,("release_posix_lock: unable to talloc unlock list.\n"));
+               talloc_destroy(ul_ctx);
+               return True; /* Not a fatal error. */
+       }
+
+       /*
+        * Create the initial list entry containing the
+        * lock we want to remove.
+        */
+
+       ZERO_STRUCTP(ul);
+       ul->start = offset;
+       ul->size = count;
+
+       DLIST_ADD(ulist, ul);
+
+       /*
+        * The following call calculates if there are any
+        * overlapping locks held by this process on
+        * fd's open on the same file and creates a
+        * list of unlock ranges that will allow
+        * POSIX lock ranges to remain on the file whilst the
+        * unlocks are performed.
+        */
+
+       ulist = posix_lock_list(ul_ctx, ulist, fsp);
+
+       /*
+        * Release the POSIX locks on the list of ranges returned.
+        */
+
+       for(; ulist; ulist = ulist->next) {
+               offset = ulist->start;
+               count = ulist->size;
+
+               DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n",
+                       (double)offset, (double)count ));
+
+               if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK))
+                       ret = False;
+       }
+
+       talloc_destroy(ul_ctx);
+
+       return ret;
+}
+
+/****************************************************************************
+ Remove all lock entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_posix_lock_entries(files_struct *fsp)
+{
+       TDB_DATA kbuf = locking_key_fsp(fsp);
+
+       if (tdb_delete(posix_lock_tdb, kbuf) == -1)
+               DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Debug function.
+****************************************************************************/
+
+static void dump_entry(struct posix_lock *pl)
+{
+       DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n",
+               (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void posix_locking_close_file(files_struct *fsp)
+{
+       struct posix_lock *entries = NULL;
+       size_t count, i;
+
+       /*
+        * Optimization for the common case where we are the only
+        * opener of a file. If all fd entries are our own, we don't
+        * need to explicitly release all the locks via the POSIX functions,
+        * we can just remove all the entries in the tdb and allow the
+        * close to remove the real locks.
+        */
+
+       count = get_posix_lock_entries(fsp, &entries);
+
+       if (count == 0) {
+               DEBUG(10,("posix_locking_close_file: file %s has no outstanding locks.\n", fsp->fsp_name ));
+               return;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (entries[i].fd != fsp->fd )
+                       break;
+
+               dump_entry(&entries[i]);
+       }
+
+       if (i == count) {
+               /* All locks are ours. */
+               DEBUG(10,("posix_locking_close_file: file %s has %u outstanding locks, but all on one fd.\n", 
+                       fsp->fsp_name, (unsigned int)count ));
+               SAFE_FREE(entries);
+               delete_posix_lock_entries(fsp);
+               return;
+       }
+
+       /*
+        * Difficult case. We need to delete all our locks, whilst leaving
+        * all other POSIX locks in place.
+        */
+
+       for (i = 0; i < count; i++) {
+               struct posix_lock *pl = &entries[i];
+               if (pl->fd == fsp->fd)
+                       release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size );
+       }
+       SAFE_FREE(entries);
+}
+
+/*******************************************************************
+ Create the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_init(int read_only)
+{
+       if (posix_lock_tdb && posix_pending_close_tdb)
+               return True;
+       
+       if (!posix_lock_tdb)
+               posix_lock_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+                                         read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+       if (!posix_lock_tdb) {
+               DEBUG(0,("Failed to open POSIX byte range locking database.\n"));
+               return False;
+       }
+       if (!posix_pending_close_tdb)
+               posix_pending_close_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+                                                  read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+       if (!posix_pending_close_tdb) {
+               DEBUG(0,("Failed to open POSIX pending close database.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Delete the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_end(void)
+{
+    if (posix_lock_tdb && tdb_close(posix_lock_tdb) != 0)
+               return False;
+    if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0)
+               return False;
+       return True;
+}
diff --git a/source4/modules/developer.c b/source4/modules/developer.c
new file mode 100644 (file)
index 0000000..a697abc
--- /dev/null
@@ -0,0 +1,132 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba module with developer tools
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Jelmer Vernooij 2002
+   
+   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"
+
+static struct {
+       char from;
+       char *to;
+       int len;
+} weird_table[] = {
+       {'q', "^q^", 3},
+       {'Q', "^Q^", 3},
+       {0, NULL}
+};
+
+static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+               int i;
+               int done = 0;
+               for (i=0;weird_table[i].from;i++) {
+                       if (strncmp((*inbuf), 
+                                   weird_table[i].to, 
+                                   weird_table[i].len) == 0) {
+                               if (*inbytesleft < weird_table[i].len) {
+                                       DEBUG(0,("ERROR: truncated weird string\n"));
+                                       /* smb_panic("weird_pull"); */
+
+                               } else {
+                                       (*outbuf)[0] = weird_table[i].from;
+                                       (*outbuf)[1] = 0;
+                                       (*inbytesleft)  -= weird_table[i].len;
+                                       (*outbytesleft) -= 2;
+                                       (*inbuf)  += weird_table[i].len;
+                                       (*outbuf) += 2;
+                                       done = 1;
+                                       break;
+                               }
+                       }
+               }
+               if (done) continue;
+               (*outbuf)[0] = (*inbuf)[0];
+               (*outbuf)[1] = 0;
+               (*inbytesleft)  -= 1;
+               (*outbytesleft) -= 2;
+               (*inbuf)  += 1;
+               (*outbuf) += 2;
+       }
+
+       if (*inbytesleft > 0) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return 0;
+}
+
+static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft,
+                        char **outbuf, size_t *outbytesleft)
+{
+       int ir_count=0;
+
+       while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+               int i;
+               int done=0;
+               for (i=0;weird_table[i].from;i++) {
+                       if ((*inbuf)[0] == weird_table[i].from &&
+                           (*inbuf)[1] == 0) {
+                               if (*outbytesleft < weird_table[i].len) {
+                                       DEBUG(0,("No room for weird character\n"));
+                                       /* smb_panic("weird_push"); */
+                               } else {
+                                       memcpy(*outbuf, weird_table[i].to, 
+                                              weird_table[i].len);
+                                       (*inbytesleft)  -= 2;
+                                       (*outbytesleft) -= weird_table[i].len;
+                                       (*inbuf)  += 2;
+                                       (*outbuf) += weird_table[i].len;
+                                       done = 1;
+                                       break;
+                               }
+                       }
+               }
+               if (done) continue;
+
+               (*outbuf)[0] = (*inbuf)[0];
+               if ((*inbuf)[1]) ir_count++;
+               (*inbytesleft)  -= 2;
+               (*outbytesleft) -= 1;
+               (*inbuf)  += 2;
+               (*outbuf) += 1;
+       }
+
+       if (*inbytesleft == 1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (*inbytesleft > 1) {
+               errno = E2BIG;
+               return -1;
+       }
+       
+       return ir_count;
+}
+
+struct charset_functions weird_functions = {"WEIRD", weird_pull, weird_push};
+
+int init_module(void)
+{
+       smb_register_charset(&weird_functions);
+       return 1;
+}
diff --git a/source4/modules/mysql.c b/source4/modules/mysql.c
new file mode 100644 (file)
index 0000000..1d58192
--- /dev/null
@@ -0,0 +1,1043 @@
+
+/*
+ * MySQL password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * 
+ * 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 <mysql/mysql.h>
+
+#define CONFIG_TABLE_DEFAULT                           "user"
+#define CONFIG_LOGON_TIME_DEFAULT                      "logon_time"
+#define CONFIG_LOGOFF_TIME_DEFAULT                     "logoff_time"
+#define CONFIG_KICKOFF_TIME_DEFAULT                    "kickoff_time"
+#define CONFIG_PASS_LAST_SET_TIME_DEFAULT      "pass_last_set_time"
+#define CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT    "pass_can_change_time"
+#define CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT "pass_must_change_time"
+#define CONFIG_USERNAME_DEFAULT                        "username"
+#define CONFIG_DOMAIN_DEFAULT                          "domain"
+#define CONFIG_NT_USERNAME_DEFAULT             "nt_username"
+#define CONFIG_FULLNAME_DEFAULT                                "nt_fullname"
+#define CONFIG_HOME_DIR_DEFAULT                                "home_dir"
+#define CONFIG_DIR_DRIVE_DEFAULT                       "dir_drive"
+#define CONFIG_LOGON_SCRIPT_DEFAULT                    "logon_script"
+#define CONFIG_PROFILE_PATH_DEFAULT                    "profile_path"
+#define CONFIG_ACCT_DESC_DEFAULT                       "acct_desc"
+#define CONFIG_WORKSTATIONS_DEFAULT                    "workstations"
+#define CONFIG_UNKNOWN_STR_DEFAULT                     "unknown_str"
+#define CONFIG_MUNGED_DIAL_DEFAULT                     "munged_dial"
+#define CONFIG_UID_DEFAULT                                     "uid"
+#define CONFIG_GID_DEFAULT                                     "gid"
+#define CONFIG_USER_SID_DEFAULT                                "user_sid"
+#define CONFIG_GROUP_SID_DEFAULT                       "group_sid"
+#define CONFIG_LM_PW_DEFAULT                           "lm_pw"
+#define CONFIG_NT_PW_DEFAULT                           "nt_pw"
+#define CONFIG_PLAIN_PW_DEFAULT                                "NULL"
+#define CONFIG_ACCT_CTRL_DEFAULT                       "acct_ctrl"
+#define CONFIG_UNKNOWN_3_DEFAULT                       "unknown_3"
+#define CONFIG_LOGON_DIVS_DEFAULT                      "logon_divs"
+#define CONFIG_HOURS_LEN_DEFAULT                       "hours_len"
+#define CONFIG_UNKNOWN_5_DEFAULT                       "unknown_5"
+#define CONFIG_UNKNOWN_6_DEFAULT                       "unknown_6"
+#define CONFIG_HOST_DEFAULT                                    "localhost"
+#define CONFIG_USER_DEFAULT                                    "samba"
+#define CONFIG_PASS_DEFAULT                                    ""
+#define CONFIG_PORT_DEFAULT                                    "3306"
+#define CONFIG_DB_DEFAULT                                      "samba"
+
+static int mysqlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS mysqlsam_debug_level
+
+typedef struct pdb_mysql_data {
+       MYSQL *handle;
+       MYSQL_RES *pwent;
+       const char *location;
+} pdb_mysql_data;
+
+/* Used to construct insert and update queries */
+
+typedef struct pdb_mysql_query {
+       char update;
+       TALLOC_CTX *mem_ctx;
+       char *part1;
+       char *part2;
+} pdb_mysql_query;
+#define SET_DATA(data,methods) { \
+       if(!methods){ \
+               DEBUG(0, ("invalid methods!\n")); \
+                       return NT_STATUS_INVALID_PARAMETER; \
+       } \
+       data = (struct pdb_mysql_data *)methods->private_data; \
+               if(!data || !(data->handle)){ \
+                       DEBUG(0, ("invalid handle!\n")); \
+                               return NT_STATUS_INVALID_HANDLE; \
+               } \
+}
+
+static void pdb_mysql_int_field(struct pdb_methods *m,
+                                       struct pdb_mysql_query *q, char *name, int value)
+{
+       if (!name || strchr(name, '\''))
+               return;                 /* This field shouldn't be set by us */
+
+       if (q->update) {
+               q->part1 =
+                       talloc_asprintf_append(q->mem_ctx, q->part1,
+                                                                  "%s = %d,", name, value);
+       } else {
+               q->part1 =
+                       talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+               q->part2 =
+                       talloc_asprintf_append(q->mem_ctx, q->part2, "%d,", value);
+       }
+}
+
+static NTSTATUS pdb_mysql_string_field(struct pdb_methods *methods,
+                                          struct pdb_mysql_query *q,
+                                          char *name, const char *value)
+{
+       char *esc_value;
+       struct pdb_mysql_data *data;
+       char *tmp_value;
+
+       SET_DATA(data, methods);
+
+       if (!name || !value || !strcmp(value, "") || strchr(name, '\''))
+               return NT_STATUS_INVALID_PARAMETER;   /* This field shouldn't be set by module */
+
+       esc_value = malloc(strlen(value) * 2 + 1);
+
+       tmp_value = smb_xstrdup(value);
+       mysql_real_escape_string(data->handle, esc_value, tmp_value,
+                                                        strlen(tmp_value));
+       SAFE_FREE(tmp_value);
+
+       if (q->update) {
+               q->part1 =
+                       talloc_asprintf_append(q->mem_ctx, q->part1,
+                                                                  "%s = '%s',", name, esc_value);
+       } else {
+               q->part1 =
+                       talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+               q->part2 =
+                       talloc_asprintf_append(q->mem_ctx, q->part2, "'%s',",
+                                                                  esc_value);
+       }
+
+       SAFE_FREE(esc_value);
+
+       return NT_STATUS_OK;
+}
+
+static char * config_value(pdb_mysql_data * data, char *name, char *default_value)
+{
+       if (lp_parm_string(NULL, data->location, name))
+               return lp_parm_string(NULL, data->location, name);
+
+       return default_value;
+}
+
+static char * config_value_write(pdb_mysql_data * data, char *name, char *default_value) {
+       char *v = config_value(data, name, NULL);
+       char *swrite;
+
+       if (!v)
+               return default_value;
+
+       swrite = strchr(v, ':');
+
+       /* Default to the same field as read field */
+       if (!swrite)
+               return v;
+
+       swrite++;
+
+       /* If the field is 0 chars long, we shouldn't write to it */
+       if (!strlen(swrite) || !strcmp(swrite, "NULL"))
+               return NULL;
+
+       /* Otherwise, use the additionally specified */
+       return swrite;
+}
+
+static const char * config_value_read(pdb_mysql_data * data, char *name, char *default_value)
+{
+       char *v = config_value(data, name, NULL);
+       char *swrite;
+
+       if (!v)
+               return default_value;
+
+       swrite = strchr(v, ':');
+
+       /* If no write is specified, there are no problems */
+       if (!swrite) {
+               if (strlen(v) == 0)
+                       return "NULL";
+               return v;
+       }
+
+       /* Otherwise, we have to cut the ':write_part' */
+       *swrite = '\0';
+       if (strlen(v) == 0)
+               return "NULL";
+
+       return v;
+}
+
+/* Wrapper for atol that returns 0 if 'a' points to NULL */
+static long xatol(char *a)
+{
+       long ret = 0;
+
+       if (a != NULL)
+               ret = atol(a);
+
+       return ret;
+}
+
+static NTSTATUS row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u)
+{
+       MYSQL_ROW row;
+       pstring temp;
+       unsigned int num_fields;
+       DOM_SID sid;
+
+       num_fields = mysql_num_fields(r);
+       row = mysql_fetch_row(r);
+       if (!row)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       pdb_set_logon_time(u, xatol(row[0]), PDB_SET);
+       pdb_set_logoff_time(u, xatol(row[1]), PDB_SET);
+       pdb_set_kickoff_time(u, xatol(row[2]), PDB_SET);
+       pdb_set_pass_last_set_time(u, xatol(row[3]), PDB_SET);
+       pdb_set_pass_can_change_time(u, xatol(row[4]), PDB_SET);
+       pdb_set_pass_must_change_time(u, xatol(row[5]), PDB_SET);
+       pdb_set_username(u, row[6], PDB_SET);
+       pdb_set_domain(u, row[7], PDB_SET);
+       pdb_set_nt_username(u, row[8], PDB_SET);
+       pdb_set_fullname(u, row[9], PDB_SET);
+       pdb_set_homedir(u, row[10], PDB_SET);
+       pdb_set_dir_drive(u, row[11], PDB_SET);
+       pdb_set_logon_script(u, row[12], PDB_SET);
+       pdb_set_profile_path(u, row[13], PDB_SET);
+       pdb_set_acct_desc(u, row[14], PDB_SET);
+       pdb_set_workstations(u, row[15], PDB_SET);
+       pdb_set_unknown_str(u, row[16], PDB_SET);
+       pdb_set_munged_dial(u, row[17], PDB_SET);
+
+       if (row[18])
+               pdb_set_uid(u, xatol(row[18]), PDB_SET);
+       if (row[19])
+               pdb_set_gid(u, xatol(row[19]), PDB_SET);
+
+       string_to_sid(&sid, row[20]);
+       pdb_set_user_sid(u, &sid, PDB_SET);
+       string_to_sid(&sid, row[21]);
+       pdb_set_group_sid(u, &sid, PDB_SET);
+
+       if (pdb_gethexpwd(row[22], temp), PDB_SET)
+               pdb_set_lanman_passwd(u, temp, PDB_SET);
+       if (pdb_gethexpwd(row[23], temp), PDB_SET)
+               pdb_set_nt_passwd(u, temp, PDB_SET);
+
+       /* Only use plaintext password storage when lanman and nt are
+        * NOT used */
+       if (!row[22] || !row[23])
+               pdb_set_plaintext_passwd(u, row[24]);
+
+       pdb_set_acct_ctrl(u, xatol(row[25]), PDB_SET);
+       pdb_set_unknown_3(u, xatol(row[26]), PDB_SET);
+       pdb_set_logon_divs(u, xatol(row[27]), PDB_SET);
+       pdb_set_hours_len(u, xatol(row[28]), PDB_SET);
+       pdb_set_unknown_5(u, xatol(row[29]), PDB_SET);
+       pdb_set_unknown_6(u, xatol(row[30]), PDB_SET);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+       struct pdb_mysql_data *data =
+               (struct pdb_mysql_data *) methods->private_data;
+       char *query;
+       int ret;
+
+       if (!data || !(data->handle)) {
+               DEBUG(0, ("invalid handle!\n"));
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       asprintf(&query,
+                        "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s",
+                        config_value_read(data, "logon time column",
+                                                          CONFIG_LOGON_TIME_DEFAULT),
+                        config_value_read(data, "logoff time column",
+                                                          CONFIG_LOGOFF_TIME_DEFAULT),
+                        config_value_read(data, "kickoff time column",
+                                                          CONFIG_KICKOFF_TIME_DEFAULT),
+                        config_value_read(data, "pass last set time column",
+                                                          CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+                        config_value_read(data, "pass can change time column",
+                                                          CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+                        config_value_read(data, "pass must change time column",
+                                                          CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+                        config_value_read(data, "username column",
+                                                          CONFIG_USERNAME_DEFAULT),
+                        config_value_read(data, "domain column",
+                                                          CONFIG_DOMAIN_DEFAULT),
+                        config_value_read(data, "nt username column",
+                                                          CONFIG_NT_USERNAME_DEFAULT),
+                        config_value_read(data, "fullname column",
+                                                          CONFIG_FULLNAME_DEFAULT),
+                        config_value_read(data, "home dir column",
+                                                          CONFIG_HOME_DIR_DEFAULT),
+                        config_value_read(data, "dir drive column",
+                                                          CONFIG_DIR_DRIVE_DEFAULT),
+                        config_value_read(data, "logon script column",
+                                                          CONFIG_LOGON_SCRIPT_DEFAULT),
+                        config_value_read(data, "profile path column",
+                                                          CONFIG_PROFILE_PATH_DEFAULT),
+                        config_value_read(data, "acct desc column",
+                                                          CONFIG_ACCT_DESC_DEFAULT),
+                        config_value_read(data, "workstations column",
+                                                          CONFIG_WORKSTATIONS_DEFAULT),
+                        config_value_read(data, "unknown string column",
+                                                          CONFIG_UNKNOWN_STR_DEFAULT),
+                        config_value_read(data, "munged dial column",
+                                                          CONFIG_MUNGED_DIAL_DEFAULT),
+                        config_value_read(data, "uid column", CONFIG_UID_DEFAULT),
+                        config_value_read(data, "gid column", CONFIG_GID_DEFAULT),
+                        config_value_read(data, "user sid column",
+                                                          CONFIG_USER_SID_DEFAULT),
+                        config_value_read(data, "group sid column",
+                                                          CONFIG_GROUP_SID_DEFAULT),
+                        config_value_read(data, "lanman pass column",
+                                                          CONFIG_LM_PW_DEFAULT),
+                        config_value_read(data, "nt pass column",
+                                                          CONFIG_NT_PW_DEFAULT),
+                        config_value_read(data, "plain pass column",
+                                                          CONFIG_PLAIN_PW_DEFAULT),
+                        config_value_read(data, "acct ctrl column",
+                                                          CONFIG_ACCT_CTRL_DEFAULT),
+                        config_value_read(data, "unknown 3 column",
+                                                          CONFIG_UNKNOWN_3_DEFAULT),
+                        config_value_read(data, "logon divs column",
+                                                          CONFIG_LOGON_DIVS_DEFAULT),
+                        config_value_read(data, "hours len column",
+                                                          CONFIG_HOURS_LEN_DEFAULT),
+                        config_value_read(data, "unknown 5 column",
+                                                          CONFIG_UNKNOWN_5_DEFAULT),
+                        config_value_read(data, "unknown 6 column",
+                                                          CONFIG_UNKNOWN_6_DEFAULT),
+                        config_value(data, "table", CONFIG_TABLE_DEFAULT)
+                                );
+       DEBUG(5, ("Executing query %s\n", query));
+       
+       ret = mysql_query(data->handle, query);
+       SAFE_FREE(query);
+
+       if (ret) {
+               DEBUG(0,
+                          ("Error executing MySQL query %s\n", mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       data->pwent = mysql_store_result(data->handle);
+
+       if (data->pwent == NULL) {
+               DEBUG(0,
+                       ("Error storing results: %s\n", mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       DEBUG(5,
+               ("mysqlsam_setsampwent succeeded(%lu results)!\n",
+                               mysql_num_rows(data->pwent)));
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************
+  End enumeration of the passwd list.
+ ****************************************************************/
+
+static void mysqlsam_endsampwent(struct pdb_methods *methods)
+{
+       struct pdb_mysql_data *data =
+               (struct pdb_mysql_data *) methods->private_data;
+
+       if (data == NULL) {
+               DEBUG(0, ("invalid handle!\n"));
+               return;
+       }
+
+       if (data->pwent != NULL)
+               mysql_free_result(data->pwent);
+
+       data->pwent = NULL;
+
+       DEBUG(5, ("mysql_endsampwent called\n"));
+}
+
+/*****************************************************************
+  Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS mysqlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+       struct pdb_mysql_data *data;
+
+       SET_DATA(data, methods);
+
+       if (data->pwent == NULL) {
+               DEBUG(0, ("invalid pwent\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       return row_to_sam_account(data->pwent, user);
+}
+
+static NTSTATUS mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user,
+                                                const char *field, const char *sname)
+{
+       char *esc_sname;
+       char *query;
+       NTSTATUS ret;
+       MYSQL_RES *res;
+       int mysql_ret;
+       struct pdb_mysql_data *data;
+       char *tmp_sname;
+
+       SET_DATA(data, methods);
+
+       esc_sname = malloc(strlen(sname) * 2 + 1);
+       if (!esc_sname) {
+               return NT_STATUS_NO_MEMORY; 
+       }
+
+       DEBUG(5,
+                 ("mysqlsam_select_by_field: getting data where %s = %s(nonescaped)\n",
+                  field, sname));
+
+       tmp_sname = smb_xstrdup(sname);
+       
+       /* Escape sname */
+       mysql_real_escape_string(data->handle, esc_sname, tmp_sname,
+                                                        strlen(tmp_sname));
+
+       SAFE_FREE(tmp_sname);
+
+       if (user == NULL) {
+               DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+               SAFE_FREE(esc_sname);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       asprintf(&query,
+                        "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s WHERE %s = '%s'",
+                        config_value_read(data, "logon time column",
+                                                          CONFIG_LOGON_TIME_DEFAULT),
+                        config_value_read(data, "logoff time column",
+                                                          CONFIG_LOGOFF_TIME_DEFAULT),
+                        config_value_read(data, "kickoff time column",
+                                                          CONFIG_KICKOFF_TIME_DEFAULT),
+                        config_value_read(data, "pass last set time column",
+                                                          CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+                        config_value_read(data, "pass can change time column",
+                                                          CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+                        config_value_read(data, "pass must change time column",
+                                                          CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+                        config_value_read(data, "username column",
+                                                          CONFIG_USERNAME_DEFAULT),
+                        config_value_read(data, "domain column",
+                                                          CONFIG_DOMAIN_DEFAULT),
+                        config_value_read(data, "nt username column",
+                                                          CONFIG_NT_USERNAME_DEFAULT),
+                        config_value_read(data, "fullname column",
+                                                          CONFIG_FULLNAME_DEFAULT),
+                        config_value_read(data, "home dir column",
+                                                          CONFIG_HOME_DIR_DEFAULT),
+                        config_value_read(data, "dir drive column",
+                                                          CONFIG_DIR_DRIVE_DEFAULT),
+                        config_value_read(data, "logon script column",
+                                                          CONFIG_LOGON_SCRIPT_DEFAULT),
+                        config_value_read(data, "profile path column",
+                                                          CONFIG_PROFILE_PATH_DEFAULT),
+                        config_value_read(data, "acct desc column",
+                                                          CONFIG_ACCT_DESC_DEFAULT),
+                        config_value_read(data, "workstations column",
+                                                          CONFIG_WORKSTATIONS_DEFAULT),
+                        config_value_read(data, "unknown string column",
+                                                          CONFIG_UNKNOWN_STR_DEFAULT),
+                        config_value_read(data, "munged dial column",
+                                                          CONFIG_MUNGED_DIAL_DEFAULT),
+                        config_value_read(data, "uid column", CONFIG_UID_DEFAULT),
+                        config_value_read(data, "gid column", CONFIG_GID_DEFAULT),
+                        config_value_read(data, "user sid column",
+                                                          CONFIG_USER_SID_DEFAULT),
+                        config_value_read(data, "group sid column",
+                                                          CONFIG_GROUP_SID_DEFAULT),
+                        config_value_read(data, "lanman pass column",
+                                                          CONFIG_LM_PW_DEFAULT),
+                        config_value_read(data, "nt pass column",
+                                                          CONFIG_NT_PW_DEFAULT),
+                        config_value_read(data, "plain pass column",
+                                                          CONFIG_PLAIN_PW_DEFAULT),
+                        config_value_read(data, "acct ctrl column",
+                                                          CONFIG_ACCT_CTRL_DEFAULT),
+                        config_value_read(data, "unknown 3 column",
+                                                          CONFIG_UNKNOWN_3_DEFAULT),
+                        config_value_read(data, "logon divs column",
+                                                          CONFIG_LOGON_DIVS_DEFAULT),
+                        config_value_read(data, "hours len column",
+                                                          CONFIG_HOURS_LEN_DEFAULT),
+                        config_value_read(data, "unknown 5 column",
+                                                          CONFIG_UNKNOWN_5_DEFAULT),
+                        config_value_read(data, "unknown 6 column",
+                                                          CONFIG_UNKNOWN_6_DEFAULT),
+                        config_value(data, "table", CONFIG_TABLE_DEFAULT), field,
+                        esc_sname);
+       
+       SAFE_FREE(esc_sname);
+
+       DEBUG(5, ("Executing query %s\n", query));
+       
+       mysql_ret = mysql_query(data->handle, query);
+       
+       SAFE_FREE(query);
+       
+       if (mysql_ret) {
+               DEBUG(0,
+                       ("Error while executing MySQL query %s\n", 
+                               mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       res = mysql_store_result(data->handle);
+       if (res == NULL) {
+               DEBUG(0,
+                       ("Error storing results: %s\n", mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       ret = row_to_sam_account(res, user);
+       mysql_free_result(res);
+
+       return ret;
+}
+
+/******************************************************************
+  Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
+                                        const char *sname)
+{
+       struct pdb_mysql_data *data;
+
+       SET_DATA(data, methods);
+
+       if (!sname) {
+               DEBUG(0, ("invalid name specified"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       return mysqlsam_select_by_field(methods, user,
+                       config_value_read(data, "username column",
+                               CONFIG_USERNAME_DEFAULT), sname);
+}
+
+
+/***************************************************************************
+  Search by sid
+ **************************************************************************/
+
+static NTSTATUS mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
+                                        const DOM_SID * sid)
+{
+       struct pdb_mysql_data *data;
+       fstring sid_str;
+
+       SET_DATA(data, methods);
+
+       sid_to_string(sid_str, sid);
+
+       return mysqlsam_select_by_field(methods, user,
+                       config_value_read(data, "user sid column",
+                               CONFIG_USER_SID_DEFAULT), sid_str);
+}
+
+/***************************************************************************
+  Delete a SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS mysqlsam_delete_sam_account(struct pdb_methods *methods,
+                                                       SAM_ACCOUNT * sam_pass)
+{
+       const char *sname = pdb_get_username(sam_pass);
+       char *esc;
+       char *query;
+       int ret;
+       struct pdb_mysql_data *data;
+       char *tmp_sname;
+
+       SET_DATA(data, methods);
+
+       if (!methods) {
+               DEBUG(0, ("invalid methods!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data = (struct pdb_mysql_data *) methods->private_data;
+       if (!data || !(data->handle)) {
+               DEBUG(0, ("invalid handle!\n"));
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       if (!sname) {
+               DEBUG(0, ("invalid name specified\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Escape sname */
+       esc = malloc(strlen(sname) * 2 + 1);
+       if (!esc) {
+               DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       tmp_sname = smb_xstrdup(sname);
+       
+       mysql_real_escape_string(data->handle, esc, tmp_sname,
+                                                        strlen(tmp_sname));
+
+       SAFE_FREE(tmp_sname);
+
+       asprintf(&query, "DELETE FROM %s WHERE %s = '%s'",
+                        config_value(data, "table", CONFIG_TABLE_DEFAULT),
+                        config_value_read(data, "username column",
+                                                          CONFIG_USERNAME_DEFAULT), esc);
+
+       SAFE_FREE(esc);
+
+       ret = mysql_query(data->handle, query);
+
+       SAFE_FREE(query);
+
+       if (ret) {
+               DEBUG(0,
+                         ("Error while executing query: %s\n",
+                          mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       DEBUG(5, ("User '%s' deleted\n", sname));
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_replace_sam_account(struct pdb_methods *methods,
+                                                        const SAM_ACCOUNT * newpwd, char isupdate)
+{
+       pstring temp;
+       struct pdb_mysql_data *data;
+       pdb_mysql_query query;
+       fstring sid_str;
+
+       if (!methods) {
+               DEBUG(0, ("invalid methods!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data = (struct pdb_mysql_data *) methods->private_data;
+       if (data == NULL || data->handle == NULL) {
+               DEBUG(0, ("invalid handle!\n"));
+               return NT_STATUS_INVALID_HANDLE;
+       }
+       query.update = isupdate;
+
+       /* I know this is somewhat overkill but only the talloc 
+        * functions have asprint_append and the 'normal' asprintf 
+        * is a GNU extension */
+       query.mem_ctx = talloc_init("mysqlsam_replace_sam_account");
+       query.part2 = talloc_asprintf(query.mem_ctx, "%s", "");
+       if (query.update) {
+               query.part1 =
+                       talloc_asprintf(query.mem_ctx, "UPDATE %s SET ",
+                                                       config_value(data, "table",
+                                                                                CONFIG_TABLE_DEFAULT));
+       } else {
+               query.part1 =
+                       talloc_asprintf(query.mem_ctx, "INSERT INTO %s (",
+                                                       config_value(data, "table",
+                                                                                CONFIG_TABLE_DEFAULT));
+       }
+
+       pdb_mysql_int_field(methods, &query,
+                                               config_value_write(data, "acct ctrl column",
+                                                                                  CONFIG_ACCT_CTRL_DEFAULT),
+                                               pdb_get_acct_ctrl(newpwd));
+
+       if (pdb_get_init_flags(newpwd, PDB_LOGONTIME) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "logon time column",
+                                                                                          CONFIG_LOGON_TIME_DEFAULT),
+                                                       pdb_get_logon_time(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_LOGOFFTIME) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "logoff time column",
+                                                                                          CONFIG_LOGOFF_TIME_DEFAULT),
+                                                       pdb_get_logoff_time(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_KICKOFFTIME) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "kickoff time column",
+                                                                                          CONFIG_KICKOFF_TIME_DEFAULT),
+                                                       pdb_get_kickoff_time(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_CANCHANGETIME) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "pass can change time column",
+                                                                                          CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+                                                       pdb_get_pass_can_change_time(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_MUSTCHANGETIME) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "pass must change time column",
+                                                                                          CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+                                                       pdb_get_pass_must_change_time(newpwd));
+       }
+
+       if (pdb_get_pass_last_set_time(newpwd)) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "pass last set time column",
+                                                                                          CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+                                                       pdb_get_pass_last_set_time(newpwd));
+       }
+
+       if (pdb_get_hours_len(newpwd)) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "hours len column",
+                                                                                          CONFIG_HOURS_LEN_DEFAULT),
+                                                       pdb_get_hours_len(newpwd));
+       }
+
+       if (pdb_get_logon_divs(newpwd)) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data,
+                                                                                          "logon divs column",
+                                                                                          CONFIG_LOGON_DIVS_DEFAULT),
+                                                       pdb_get_logon_divs(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_UID) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data, "uid column",
+                                                                                          CONFIG_UID_DEFAULT),
+                                                       pdb_get_uid(newpwd));
+       }
+
+       if (pdb_get_init_flags(newpwd, PDB_GID) != PDB_DEFAULT) {
+               pdb_mysql_int_field(methods, &query,
+                                                       config_value_write(data, "gid column",
+                                                                                          CONFIG_GID_DEFAULT),
+                                                       pdb_get_gid(newpwd));
+       }
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "user sid column",
+                                                                                         CONFIG_USER_SID_DEFAULT),
+                                                  sid_to_string(sid_str, 
+                                                                                pdb_get_user_sid(newpwd)));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "group sid column",
+                                                                                         CONFIG_GROUP_SID_DEFAULT),
+                                                  sid_to_string(sid_str,
+                                                                                pdb_get_group_sid(newpwd)));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "username column",
+                                                                                         CONFIG_USERNAME_DEFAULT),
+                                                  pdb_get_username(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "domain column",
+                                                                                         CONFIG_DOMAIN_DEFAULT),
+                                                  pdb_get_domain(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "nt username column",
+                                                                                         CONFIG_NT_USERNAME_DEFAULT),
+                                                  pdb_get_nt_username(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "fullname column",
+                                                                                         CONFIG_FULLNAME_DEFAULT),
+                                                  pdb_get_fullname(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "logon script column",
+                                                                                         CONFIG_LOGON_SCRIPT_DEFAULT),
+                                                  pdb_get_logon_script(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "profile path column",
+                                                                                         CONFIG_PROFILE_PATH_DEFAULT),
+                                                  pdb_get_profile_path(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "dir drive column",
+                                                                                         CONFIG_DIR_DRIVE_DEFAULT),
+                                                  pdb_get_dir_drive(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "home dir column",
+                                                                                         CONFIG_HOME_DIR_DEFAULT),
+                                                  pdb_get_homedir(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "workstations column",
+                                                                                         CONFIG_WORKSTATIONS_DEFAULT),
+                                                  pdb_get_workstations(newpwd));
+
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "unknown string column",
+                                                                                         CONFIG_UNKNOWN_STR_DEFAULT),
+                                                  pdb_get_workstations(newpwd));
+
+       pdb_sethexpwd(temp, pdb_get_lanman_passwd(newpwd),
+                                 pdb_get_acct_ctrl(newpwd));
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data,
+                                                                                         "lanman pass column",
+                                                                                         CONFIG_LM_PW_DEFAULT), temp);
+
+       pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd),
+                                 pdb_get_acct_ctrl(newpwd));
+       pdb_mysql_string_field(methods, &query,
+                                                  config_value_write(data, "nt pass column",
+                                                                                         CONFIG_NT_PW_DEFAULT), temp);
+
+       if (query.update) {
+               query.part1[strlen(query.part1) - 1] = '\0';
+               query.part1 =
+                       talloc_asprintf_append(query.mem_ctx, query.part1,
+                                                                  " WHERE %s = '%s'",
+                                                                  config_value_read(data,
+                                                                                                        "user sid column",
+                                                                                                        CONFIG_USER_SID_DEFAULT),
+                                                                  sid_to_string(sid_str, pdb_get_user_sid (newpwd)));
+       } else {
+               query.part2[strlen(query.part2) - 1] = ')';
+               query.part1[strlen(query.part1) - 1] = ')';
+               query.part1 =
+                       talloc_asprintf_append(query.mem_ctx, query.part1,
+                                                                  " VALUES (%s", query.part2);
+       }
+
+       DEBUG(0, ("%s\n", query.part1));
+       /* Execute the query */
+       if (mysql_query(data->handle, query.part1)) {
+               DEBUG(0,
+                         ("Error executing %s, %s\n", query.part1,
+                          mysql_error(data->handle)));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       talloc_destroy(query.mem_ctx);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
+{
+       return mysqlsam_replace_sam_account(methods, newpwd, 0);
+}
+
+static NTSTATUS mysqlsam_update_sam_account(struct pdb_methods *methods,
+                                                       SAM_ACCOUNT * newpwd)
+{
+       return mysqlsam_replace_sam_account(methods, newpwd, 1);
+}
+
+static NTSTATUS mysqlsam_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+                               DOM_SID sid, BOOL with_priv)
+{
+       return get_group_map_from_sid(sid, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+                               gid_t gid, BOOL with_priv)
+{
+       return get_group_map_from_gid(gid, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+                               char *name, BOOL with_priv)
+{
+       return get_group_map_from_ntname(name, map, with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_add_group_mapping_entry(struct pdb_methods *methods,
+                                              GROUP_MAP *map)
+{
+       return add_mapping_entry(map, TDB_INSERT) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_update_group_mapping_entry(struct pdb_methods *methods,
+                                                 GROUP_MAP *map)
+{
+       return add_mapping_entry(map, TDB_REPLACE) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_delete_group_mapping_entry(struct pdb_methods *methods,
+                                                 DOM_SID sid)
+{
+       return group_map_remove(sid) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS mysqlsam_enum_group_mapping(struct pdb_methods *methods,
+                                         enum SID_NAME_USE sid_name_use,
+                                         GROUP_MAP **rmap, int *num_entries,
+                                         BOOL unix_only, BOOL with_priv)
+{
+       return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only,
+                                 with_priv) ?
+               NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+static NTSTATUS mysqlsam_init(struct pdb_context * pdb_context, struct pdb_methods ** pdb_method,
+                const char *location)
+{
+       NTSTATUS nt_status;
+       struct pdb_mysql_data *data;
+
+       mysqlsam_debug_level = debug_add_class("mysqlsam");
+       if (mysqlsam_debug_level == -1) {
+               mysqlsam_debug_level = DBGC_ALL;
+               DEBUG(0,
+                         ("mysqlsam: Couldn't register custom debugging class!\n"));
+       }
+
+       if (!pdb_context) {
+               DEBUG(0, ("invalid pdb_methods specified\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!NT_STATUS_IS_OK
+               (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "mysqlsam";
+
+       (*pdb_method)->setsampwent = mysqlsam_setsampwent;
+       (*pdb_method)->endsampwent = mysqlsam_endsampwent;
+       (*pdb_method)->getsampwent = mysqlsam_getsampwent;
+       (*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
+       (*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
+       (*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
+       (*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
+       (*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
+       (*pdb_method)->getgrsid = mysqlsam_getgrsid;
+       (*pdb_method)->getgrgid = mysqlsam_getgrgid;
+       (*pdb_method)->getgrnam = mysqlsam_getgrnam;
+       (*pdb_method)->add_group_mapping_entry = mysqlsam_add_group_mapping_entry;
+       (*pdb_method)->update_group_mapping_entry = mysqlsam_update_group_mapping_entry;
+       (*pdb_method)->delete_group_mapping_entry = mysqlsam_delete_group_mapping_entry;
+       (*pdb_method)->enum_group_mapping = mysqlsam_enum_group_mapping;
+
+       data = talloc(pdb_context->mem_ctx, sizeof(struct pdb_mysql_data));
+       (*pdb_method)->private_data = data;
+       data->handle = NULL;
+       data->pwent = NULL;
+
+       if (!location) {
+               DEBUG(0, ("No identifier specified. See README for details\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data->location = smb_xstrdup(location);
+
+       DEBUG(1,
+                 ("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %ld\n",
+                  config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+                  config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+                  config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+                  config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+                  xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
+
+       /* Do the mysql initialization */
+       data->handle = mysql_init(NULL);
+       if (!data->handle) {
+               DEBUG(0, ("Failed to connect to server\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       /* Process correct entry in $HOME/.my.conf */
+       if (!mysql_real_connect(data->handle,
+                       config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+                       config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+                       config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+                       config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+                       xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)), 
+                       NULL, 0)) {
+               DEBUG(0,
+                         ("Failed to connect to mysql database: error: %s\n",
+                          mysql_error(data->handle)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       DEBUG(5, ("Connected to mysql db\n"));
+
+       return NT_STATUS_OK;
+}
+
+int init_module(void);
+
+int init_module() 
+{
+       if(smb_register_passdb("mysql", mysqlsam_init, PASSDB_INTERFACE_VERSION))
+               return 0;
+
+       return 1;
+}
diff --git a/source4/modules/vfs_audit.c b/source4/modules/vfs_audit.c
new file mode 100644 (file)
index 0000000..b99d93d
--- /dev/null
@@ -0,0 +1,278 @@
+/* 
+ * Auditing VFS module for samba.  Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#include <syslog.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <includes.h>
+#include <vfs.h>
+
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY   LOG_USER
+#endif
+
+#ifndef SYSLOG_PRIORITY
+#define SYSLOG_PRIORITY   LOG_NOTICE
+#endif
+
+/* Function prototypes */
+
+static int audit_connect(struct tcon_context *conn, const char *svc, const char *user);
+static void audit_disconnect(struct tcon_context *conn);
+static DIR *audit_opendir(struct tcon_context *conn, const char *fname);
+static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode);
+static int audit_rmdir(struct tcon_context *conn, const char *path);
+static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(struct files_struct *fsp, int fd);
+static int audit_rename(struct tcon_context *conn, const char *old, const char *new);
+static int audit_unlink(struct tcon_context *conn, const char *path);
+static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode);
+static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
+static struct smb_vfs_handle_struct *audit_handle;
+
+static vfs_op_tuple audit_ops[] = {
+    
+       /* Disk operations */
+
+       {audit_connect,         SMB_VFS_OP_CONNECT,     SMB_VFS_LAYER_LOGGER},
+       {audit_disconnect,      SMB_VFS_OP_DISCONNECT,  SMB_VFS_LAYER_LOGGER},
+
+       /* Directory operations */
+
+       {audit_opendir,         SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_LOGGER},
+       {audit_mkdir,           SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_LOGGER},
+       {audit_rmdir,           SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_LOGGER},
+
+       /* File operations */
+
+       {audit_open,            SMB_VFS_OP_OPEN,        SMB_VFS_LAYER_LOGGER},
+       {audit_close,           SMB_VFS_OP_CLOSE,       SMB_VFS_LAYER_LOGGER},
+       {audit_rename,          SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_LOGGER},
+       {audit_unlink,          SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_LOGGER},
+       {audit_chmod,           SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_LOGGER},
+       {audit_fchmod,          SMB_VFS_OP_FCHMOD,      SMB_VFS_LAYER_LOGGER},
+       {audit_chmod_acl,       SMB_VFS_OP_CHMOD_ACL,   SMB_VFS_LAYER_LOGGER},
+       {audit_fchmod_acl,      SMB_VFS_OP_FCHMOD_ACL,  SMB_VFS_LAYER_LOGGER},
+       
+       /* Finish VFS operations definition */
+       
+       {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
+};
+
+/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
+
+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, 
+                       struct smb_vfs_handle_struct *vfs_handle)
+{
+       *vfs_version = SMB_VFS_INTERFACE_VERSION;
+       memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
+       
+       audit_handle = vfs_handle;
+
+       openlog("smbd_audit", LOG_PID, SYSLOG_FACILITY);
+       syslog(SYSLOG_PRIORITY, "VFS_INIT: vfs_ops loaded\n");
+       return audit_ops;
+}
+
+/* VFS finalization function. */
+void vfs_done(struct tcon_context *conn)
+{
+       syslog(SYSLOG_PRIORITY, "VFS_DONE: vfs module unloaded\n");
+}
+
+/* Implementation of vfs_ops.  Pass everything on to the default
+   operation but log event first. */
+
+static int audit_connect(struct tcon_context *conn, const char *svc, const char *user)
+{
+       syslog(SYSLOG_PRIORITY, "connect to service %s by user %s\n", 
+              svc, user);
+
+       return default_vfs_ops.connect(conn, svc, user);
+}
+
+static void audit_disconnect(struct tcon_context *conn)
+{
+       syslog(SYSLOG_PRIORITY, "disconnected\n");
+       default_vfs_ops.disconnect(conn);
+}
+
+static DIR *audit_opendir(struct tcon_context *conn, const char *fname)
+{
+       DIR *result = default_vfs_ops.opendir(conn, fname);
+
+       syslog(SYSLOG_PRIORITY, "opendir %s %s%s\n",
+              fname,
+              (result == NULL) ? "failed: " : "",
+              (result == NULL) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result = default_vfs_ops.mkdir(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "mkdir %s %s%s\n", 
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_rmdir(struct tcon_context *conn, const char *path)
+{
+       int result = default_vfs_ops.rmdir(conn, path);
+
+       syslog(SYSLOG_PRIORITY, "rmdir %s %s%s\n", 
+              path, 
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode)
+{
+       int result = default_vfs_ops.open(conn, fname, flags, mode);
+
+       syslog(SYSLOG_PRIORITY, "open %s (fd %d) %s%s%s\n", 
+              fname, result,
+              ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "", 
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_close(struct files_struct *fsp, int fd)
+{
+       int result = default_vfs_ops.close(fsp, fd);
+
+       syslog(SYSLOG_PRIORITY, "close fd %d %s%s\n",
+              fd,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_rename(struct tcon_context *conn, const char *old, const char *new)
+{
+       int result = default_vfs_ops.rename(conn, old, new);
+
+       syslog(SYSLOG_PRIORITY, "rename %s -> %s %s%s\n",
+              old, new,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;    
+}
+
+static int audit_unlink(struct tcon_context *conn, const char *path)
+{
+       int result = default_vfs_ops.unlink(conn, path);
+
+       syslog(SYSLOG_PRIORITY, "unlink %s %s%s\n",
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result = default_vfs_ops.chmod(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "chmod %s mode 0x%x %s%s\n",
+              path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_chmod_acl(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result;
+
+       if ( !default_vfs_ops.chmod_acl )
+               return 0;
+
+       result = default_vfs_ops.chmod_acl(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "chmod_acl %s mode 0x%x %s%s\n",
+              path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode)
+{
+       int result = default_vfs_ops.fchmod(fsp, fd, mode);
+
+       syslog(SYSLOG_PRIORITY, "fchmod %s mode 0x%x %s%s\n",
+              fsp->fsp_name, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
+
+static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode)
+{
+       int result;
+
+       if ( !default_vfs_ops.fchmod_acl )
+               return 0;
+
+       result = default_vfs_ops.fchmod_acl(fsp, fd, mode);
+
+       syslog(SYSLOG_PRIORITY, "fchmod_acl %s mode 0x%x %s%s\n",
+              fsp->fsp_name, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+
+       return result;
+}
diff --git a/source4/modules/vfs_extd_audit.c b/source4/modules/vfs_extd_audit.c
new file mode 100644 (file)
index 0000000..e9b6ed5
--- /dev/null
@@ -0,0 +1,319 @@
+/* 
+ * Auditing VFS module for samba.  Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) John H Terpstra, 2003
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#include <syslog.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <includes.h>
+#include <vfs.h>
+
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY   LOG_USER
+#endif
+
+#ifndef SYSLOG_PRIORITY
+#define SYSLOG_PRIORITY   LOG_NOTICE
+#endif
+
+/* Function prototypes */
+
+static int audit_connect(struct tcon_context *conn, const char *svc, const char *user);
+static void audit_disconnect(struct tcon_context *conn);
+static DIR *audit_opendir(struct tcon_context *conn, const char *fname);
+static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode);
+static int audit_rmdir(struct tcon_context *conn, const char *path);
+static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(struct files_struct *fsp, int fd);
+static int audit_rename(struct tcon_context *conn, const char *old, const char *new);
+static int audit_unlink(struct tcon_context *conn, const char *path);
+static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode);
+static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
+static struct smb_vfs_handle_struct *audit_handle;
+
+static vfs_op_tuple audit_ops[] = {
+    
+       /* Disk operations */
+
+       {audit_connect,         SMB_VFS_OP_CONNECT,     SMB_VFS_LAYER_LOGGER},
+       {audit_disconnect,      SMB_VFS_OP_DISCONNECT,  SMB_VFS_LAYER_LOGGER},
+
+       /* Directory operations */
+
+       {audit_opendir,         SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_LOGGER},
+       {audit_mkdir,           SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_LOGGER},
+       {audit_rmdir,           SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_LOGGER},
+
+       /* File operations */
+
+       {audit_open,            SMB_VFS_OP_OPEN,        SMB_VFS_LAYER_LOGGER},
+       {audit_close,           SMB_VFS_OP_CLOSE,       SMB_VFS_LAYER_LOGGER},
+       {audit_rename,          SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_LOGGER},
+       {audit_unlink,          SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_LOGGER},
+       {audit_chmod,           SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_LOGGER},
+       {audit_fchmod,          SMB_VFS_OP_FCHMOD,      SMB_VFS_LAYER_LOGGER},
+       {audit_chmod_acl,       SMB_VFS_OP_CHMOD_ACL,   SMB_VFS_LAYER_LOGGER},
+       {audit_fchmod_acl,      SMB_VFS_OP_FCHMOD_ACL,  SMB_VFS_LAYER_LOGGER},
+       
+       /* Finish VFS operations definition */
+       
+       {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
+};
+
+/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
+
+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, 
+                       struct smb_vfs_handle_struct *vfs_handle)
+{
+       *vfs_version = SMB_VFS_INTERFACE_VERSION;
+       memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
+       
+       audit_handle = vfs_handle;
+
+       openlog("smbd_audit", LOG_PID, SYSLOG_FACILITY);
+       syslog(SYSLOG_PRIORITY, "VFS_INIT: vfs_ops loaded\n");
+
+       return audit_ops;
+}
+
+/* VFS finalization function. */
+
+void vfs_done(struct tcon_context *conn)
+{
+       syslog(SYSLOG_PRIORITY, "VFS_DONE: vfs module unloaded\n");
+}
+
+/* Implementation of vfs_ops.  Pass everything on to the default
+   operation but log event first. */
+
+static int audit_connect(struct tcon_context *conn, const char *svc, const char *user)
+{
+       syslog(SYSLOG_PRIORITY, "connect to service %s by user %s\n", 
+              svc, user);
+       DEBUG(10, ("Connected to service %s as user %s\n",
+              svc, user));
+
+       return default_vfs_ops.connect(conn, svc, user);
+}
+
+static void audit_disconnect(struct tcon_context *conn)
+{
+       syslog(SYSLOG_PRIORITY, "disconnected\n");
+       DEBUG(10, ("Disconnected from VFS module extd_audit\n"));
+
+       default_vfs_ops.disconnect(conn);
+}
+
+static DIR *audit_opendir(struct tcon_context *conn, const char *fname)
+{
+       DIR *result = default_vfs_ops.opendir(conn, fname);
+
+       syslog(SYSLOG_PRIORITY, "opendir %s %s%s\n",
+              fname,
+              (result == NULL) ? "failed: " : "",
+              (result == NULL) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: opendir %s %s %s",
+              fname,
+              (result == NULL) ? "failed: " : "",
+              (result == NULL) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result = default_vfs_ops.mkdir(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "mkdir %s %s%s\n", 
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(0, ("vfs_extd_audit: mkdir %s %s %s\n",
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_rmdir(struct tcon_context *conn, const char *path)
+{
+       int result = default_vfs_ops.rmdir(conn, path);
+
+       syslog(SYSLOG_PRIORITY, "rmdir %s %s%s\n", 
+              path, 
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(0, ("vfs_extd_audit: rmdir %s %s %s\n",
+               path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode)
+{
+       int result = default_vfs_ops.open(conn, fname, flags, mode);
+
+       syslog(SYSLOG_PRIORITY, "open %s (fd %d) %s%s%s\n", 
+              fname, result,
+              ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "", 
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(2, ("vfs_extd_audit: open %s %s %s\n",
+              fname,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_close(struct files_struct *fsp, int fd)
+{
+       int result = default_vfs_ops.close(fsp, fd);
+
+       syslog(SYSLOG_PRIORITY, "close fd %d %s%s\n",
+              fd,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(2, ("vfs_extd_audit: close fd %d %s %s\n",
+              fd,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_rename(struct tcon_context *conn, const char *old, const char *new)
+{
+       int result = default_vfs_ops.rename(conn, old, new);
+
+       syslog(SYSLOG_PRIORITY, "rename %s -> %s %s%s\n",
+              old, new,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: rename old: %s new: %s  %s %s\n",
+              old, new,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;    
+}
+
+static int audit_unlink(struct tcon_context *conn, const char *path)
+{
+       int result = default_vfs_ops.unlink(conn, path);
+
+       syslog(SYSLOG_PRIORITY, "unlink %s %s%s\n",
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(0, ("vfs_extd_audit: unlink %s %s %s\n",
+              path,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result = default_vfs_ops.chmod(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "chmod %s mode 0x%x %s%s\n",
+              path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: chmod %s mode 0x%x %s %s\n",
+              path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_chmod_acl(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int result = default_vfs_ops.chmod_acl(conn, path, mode);
+
+       syslog(SYSLOG_PRIORITY, "chmod_acl %s mode 0x%x %s%s\n",
+              path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: chmod_acl %s mode 0x%x %s %s\n",
+               path, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode)
+{
+       int result = default_vfs_ops.fchmod(fsp, fd, mode);
+
+       syslog(SYSLOG_PRIORITY, "fchmod %s mode 0x%x %s%s\n",
+              fsp->fsp_name, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: fchmod %s mode 0x%x %s %s",
+              fsp->fsp_name,  mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
+
+static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode)
+{
+       int result = default_vfs_ops.fchmod_acl(fsp, fd, mode);
+
+       syslog(SYSLOG_PRIORITY, "fchmod_acl %s mode 0x%x %s%s\n",
+              fsp->fsp_name, mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : "");
+       DEBUG(1, ("vfs_extd_audit: fchmod_acl %s mode 0x%x %s %s",
+              fsp->fsp_name,  mode,
+              (result < 0) ? "failed: " : "",
+              (result < 0) ? strerror(errno) : ""));
+
+       return result;
+}
diff --git a/source4/modules/vfs_fake_perms.c b/source4/modules/vfs_fake_perms.c
new file mode 100644 (file)
index 0000000..63ef66c
--- /dev/null
@@ -0,0 +1,471 @@
+/* 
+ * Fake Perms VFS module.  Implements passthrough operation of all VFS
+ * calls to disk functions, except for file permissions, which are now
+ * mode 0700 for the current uid/gid.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) Andrew Bartlett, 2002
+ *
+ * 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 "config.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+#include <includes.h>
+#include <vfs.h>
+
+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
+static struct smb_vfs_handle_struct *fake_perms_handle; /* use fake_perms_handle->data for storing per-instance private data */
+
+static int fake_perms_connect(struct tcon_context *conn, const char *service, const char *user)    
+{
+       return default_vfs_ops.connect(conn, service, user);
+}
+
+static void fake_perms_disconnect(struct tcon_context *conn)
+{
+       default_vfs_ops.disconnect(conn);
+}
+
+static SMB_BIG_UINT fake_perms_disk_free(struct tcon_context *conn, const char *path,
+       BOOL small_query, SMB_BIG_UINT *bsize,
+       SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+       return default_vfs_ops.disk_free(conn, path, small_query, bsize, 
+                                        dfree, dsize);
+}
+
+static DIR *fake_perms_opendir(struct tcon_context *conn, const char *fname)
+{
+       return default_vfs_ops.opendir(conn, fname);
+}
+
+static struct dirent *fake_perms_readdir(struct tcon_context *conn, DIR *dirp)
+{
+       return default_vfs_ops.readdir(conn, dirp);
+}
+
+static int fake_perms_mkdir(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       return default_vfs_ops.mkdir(conn, path, mode);
+}
+
+static int fake_perms_rmdir(struct tcon_context *conn, const char *path)
+{
+       return default_vfs_ops.rmdir(conn, path);
+}
+
+static int fake_perms_closedir(struct tcon_context *conn, DIR *dir)
+{
+       return default_vfs_ops.closedir(conn, dir);
+}
+
+static int fake_perms_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode)
+{
+       return default_vfs_ops.open(conn, fname, flags, mode);
+}
+
+static int fake_perms_close(struct files_struct *fsp, int fd)
+{
+       return default_vfs_ops.close(fsp, fd);
+}
+
+static ssize_t fake_perms_read(struct files_struct *fsp, int fd, void *data, size_t n)
+{
+       return default_vfs_ops.read(fsp, fd, data, n);
+}
+
+static ssize_t fake_perms_write(struct files_struct *fsp, int fd, const void *data, size_t n)
+{
+       return default_vfs_ops.write(fsp, fd, data, n);
+}
+
+static SMB_OFF_T fake_perms_lseek(struct files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+       return default_vfs_ops.lseek(fsp, filedes, offset, whence);
+}
+
+static int fake_perms_rename(struct tcon_context *conn, const char *old, const char *new)
+{
+       return default_vfs_ops.rename(conn, old, new);
+}
+
+static int fake_perms_fsync(struct files_struct *fsp, int fd)
+{
+       return default_vfs_ops.fsync(fsp, fd);
+}
+
+static int fake_perms_stat(struct tcon_context *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+       int ret = default_vfs_ops.stat(conn, fname, sbuf);
+       extern struct current_user current_user;
+       
+       if (S_ISDIR(sbuf->st_mode)) {
+               sbuf->st_mode = S_IFDIR | S_IRWXU;
+       } else {
+               sbuf->st_mode = S_IRWXU;
+       }
+       sbuf->st_uid = current_user.uid;
+       sbuf->st_gid = current_user.gid;
+       return ret;
+}
+
+static int fake_perms_fstat(struct files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+       return default_vfs_ops.fstat(fsp, fd, sbuf);
+}
+
+static int fake_perms_lstat(struct tcon_context *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+       return default_vfs_ops.lstat(conn, path, sbuf);
+}
+
+static int fake_perms_unlink(struct tcon_context *conn, const char *path)
+{
+       return default_vfs_ops.unlink(conn, path);
+}
+
+static int fake_perms_chmod(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       return default_vfs_ops.chmod(conn, path, mode);
+}
+
+static int fake_perms_fchmod(struct files_struct *fsp, int fd, mode_t mode)
+{
+       return default_vfs_ops.fchmod(fsp, fd, mode);
+}
+
+static int fake_perms_chown(struct tcon_context *conn, const char *path, uid_t uid, gid_t gid)
+{
+       return default_vfs_ops.chown(conn, path, uid, gid);
+}
+
+static int fake_perms_fchown(struct files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+       return default_vfs_ops.fchown(fsp, fd, uid, gid);
+}
+
+static int fake_perms_chdir(struct tcon_context *conn, const char *path)
+{
+       return default_vfs_ops.chdir(conn, path);
+}
+
+static char *fake_perms_getwd(struct tcon_context *conn, char *buf)
+{
+       return default_vfs_ops.getwd(conn, buf);
+}
+
+static int fake_perms_utime(struct tcon_context *conn, const char *path, struct utimbuf *times)
+{
+       return default_vfs_ops.utime(conn, path, times);
+}
+
+static int fake_perms_ftruncate(struct files_struct *fsp, int fd, SMB_OFF_T offset)
+{
+       return default_vfs_ops.ftruncate(fsp, fd, offset);
+}
+
+static BOOL fake_perms_lock(struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+       return default_vfs_ops.lock(fsp, fd, op, offset, count, type);
+}
+
+static BOOL fake_perms_symlink(struct tcon_context *conn, const char *oldpath, const char *newpath)
+{
+       return default_vfs_ops.symlink(conn, oldpath, newpath);
+}
+
+static BOOL fake_perms_readlink(struct tcon_context *conn, const char *path, char *buf, size_t bufsiz)
+{
+       return default_vfs_ops.readlink(conn, path, buf, bufsiz);
+}
+
+static int fake_perms_link(struct tcon_context *conn, const char *oldpath, const char *newpath)
+{
+       return default_vfs_ops.link(conn, oldpath, newpath);
+}
+
+static int fake_perms_mknod(struct tcon_context *conn, const char *path, mode_t mode, SMB_DEV_T dev)
+{
+       return default_vfs_ops.mknod(conn, path, mode, dev);
+}
+
+static char *fake_perms_realpath(struct tcon_context *conn, const char *path, char *resolved_path)
+{
+       return default_vfs_ops.realpath(conn, path, resolved_path);
+}
+
+static size_t fake_perms_fget_nt_acl(struct files_struct *fsp, int fd, struct security_descriptor_info **ppdesc)
+{
+       return default_vfs_ops.fget_nt_acl(fsp, fd, ppdesc);
+}
+
+static size_t fake_perms_get_nt_acl(struct files_struct *fsp, const char *name, struct security_descriptor_info **ppdesc)
+{
+       return default_vfs_ops.get_nt_acl(fsp, name, ppdesc);
+}
+
+static BOOL fake_perms_fset_nt_acl(struct files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+       return default_vfs_ops.fset_nt_acl(fsp, fd, security_info_sent, psd);
+}
+
+static BOOL fake_perms_set_nt_acl(struct files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+       return default_vfs_ops.set_nt_acl(fsp, name, security_info_sent, psd);
+}
+
+static BOOL fake_perms_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode)
+{
+       return default_vfs_ops.chmod_acl(conn, name, mode);
+}
+
+static BOOL fake_perms_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode)
+{
+       return default_vfs_ops.fchmod_acl(fsp, fd, mode);
+}
+
+static int fake_perms_sys_acl_get_entry(struct tcon_context *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       return default_vfs_ops.sys_acl_get_entry(conn, theacl, entry_id, entry_p);
+}
+
+static int fake_perms_sys_acl_get_tag_type(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       return default_vfs_ops.sys_acl_get_tag_type(conn, entry_d, tag_type_p);
+}
+
+static int fake_perms_sys_acl_get_permset(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       return default_vfs_ops.sys_acl_get_permset(conn, entry_d, permset_p);
+}
+
+static void *fake_perms_sys_acl_get_qualifier(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d)
+{
+       return default_vfs_ops.sys_acl_get_qualifier(conn, entry_d);
+}
+
+static SMB_ACL_T fake_perms_sys_acl_get_file(struct tcon_context *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+       return default_vfs_ops.sys_acl_get_file(conn, path_p, type);
+}
+
+static SMB_ACL_T fake_perms_sys_acl_get_fd(struct files_struct *fsp, int fd)
+{
+       return default_vfs_ops.sys_acl_get_fd(fsp, fd);
+}
+
+static int fake_perms_sys_acl_clear_perms(struct tcon_context *conn, SMB_ACL_PERMSET_T permset)
+{
+       return default_vfs_ops.sys_acl_clear_perms(conn, permset);
+}
+
+static int fake_perms_sys_acl_add_perm(struct tcon_context *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return default_vfs_ops.sys_acl_add_perm(conn, permset, perm);
+}
+
+static char *fake_perms_sys_acl_to_text(struct tcon_context *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+       return default_vfs_ops.sys_acl_to_text(conn, theacl, plen);
+}
+
+static SMB_ACL_T fake_perms_sys_acl_init(struct tcon_context *conn, int count)
+{
+       return default_vfs_ops.sys_acl_init(conn, count);
+}
+
+static int fake_perms_sys_acl_create_entry(struct tcon_context *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       return default_vfs_ops.sys_acl_create_entry(conn, pacl, pentry);
+}
+
+static int fake_perms_sys_acl_set_tag_type(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       return default_vfs_ops.sys_acl_set_tag_type(conn, entry, tagtype);
+}
+
+static int fake_perms_sys_acl_set_qualifier(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+       return default_vfs_ops.sys_acl_set_qualifier(conn, entry, qual);
+}
+
+static int fake_perms_sys_acl_set_permset(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       return default_vfs_ops.sys_acl_set_permset(conn, entry, permset);
+}
+
+static int fake_perms_sys_acl_valid(struct tcon_context *conn, SMB_ACL_T theacl )
+{
+       return default_vfs_ops.sys_acl_valid(conn, theacl );
+}
+
+static int fake_perms_sys_acl_set_file(struct tcon_context *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       return default_vfs_ops.sys_acl_set_file(conn, name, acltype, theacl);
+}
+
+static int fake_perms_sys_acl_set_fd(struct files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+       return default_vfs_ops.sys_acl_set_fd(fsp, fd, theacl);
+}
+
+static int fake_perms_sys_acl_delete_def_file(struct tcon_context *conn, const char *path)
+{
+       return default_vfs_ops.sys_acl_delete_def_file(conn, path);
+}
+
+static int fake_perms_sys_acl_get_perm(struct tcon_context *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return default_vfs_ops.sys_acl_get_perm(conn, permset, perm);
+}
+
+static int fake_perms_sys_acl_free_text(struct tcon_context *conn, char *text)
+{
+       return default_vfs_ops.sys_acl_free_text(conn, text);
+}
+
+static int fake_perms_sys_acl_free_acl(struct tcon_context *conn, SMB_ACL_T posix_acl)
+{
+       return default_vfs_ops.sys_acl_free_acl(conn, posix_acl);
+}
+
+static int fake_perms_sys_acl_free_qualifier(struct tcon_context *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+       return default_vfs_ops.sys_acl_free_qualifier(conn, qualifier, tagtype);
+}
+
+
+/* VFS operations structure */
+
+static vfs_op_tuple fake_perms_ops[] = {
+
+       /* Disk operations */
+
+       {fake_perms_connect,                    SMB_VFS_OP_CONNECT,             SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_disconnect,         SMB_VFS_OP_DISCONNECT,          SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_disk_free,          SMB_VFS_OP_DISK_FREE,           SMB_VFS_LAYER_TRANSPARENT},
+       
+       /* Directory operations */
+
+       {fake_perms_opendir,                    SMB_VFS_OP_OPENDIR,             SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_readdir,                    SMB_VFS_OP_READDIR,             SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_mkdir,                      SMB_VFS_OP_MKDIR,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_rmdir,                      SMB_VFS_OP_RMDIR,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_closedir,                   SMB_VFS_OP_CLOSEDIR,            SMB_VFS_LAYER_TRANSPARENT},
+
+       /* File operations */
+
+       {fake_perms_open,                       SMB_VFS_OP_OPEN,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_close,                      SMB_VFS_OP_CLOSE,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_read,                       SMB_VFS_OP_READ,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_write,                      SMB_VFS_OP_WRITE,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_lseek,                      SMB_VFS_OP_LSEEK,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_rename,                     SMB_VFS_OP_RENAME,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fsync,                      SMB_VFS_OP_FSYNC,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_stat,                       SMB_VFS_OP_STAT,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fstat,                      SMB_VFS_OP_FSTAT,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_lstat,                      SMB_VFS_OP_LSTAT,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_unlink,                     SMB_VFS_OP_UNLINK,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_chmod,                      SMB_VFS_OP_CHMOD,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fchmod,                     SMB_VFS_OP_FCHMOD,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_chown,                      SMB_VFS_OP_CHOWN,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fchown,                     SMB_VFS_OP_FCHOWN,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_chdir,                      SMB_VFS_OP_CHDIR,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_getwd,                      SMB_VFS_OP_GETWD,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_utime,                      SMB_VFS_OP_UTIME,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_ftruncate,          SMB_VFS_OP_FTRUNCATE,           SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_lock,                       SMB_VFS_OP_LOCK,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_symlink,                    SMB_VFS_OP_SYMLINK,             SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_readlink,                   SMB_VFS_OP_READLINK,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_link,                       SMB_VFS_OP_LINK,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_mknod,                      SMB_VFS_OP_MKNOD,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_realpath,                   SMB_VFS_OP_REALPATH,            SMB_VFS_LAYER_TRANSPARENT},
+
+       /* NT File ACL operations */
+
+       {fake_perms_fget_nt_acl,                SMB_VFS_OP_FGET_NT_ACL,         SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_get_nt_acl,         SMB_VFS_OP_GET_NT_ACL,          SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fset_nt_acl,                SMB_VFS_OP_FSET_NT_ACL,         SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_set_nt_acl,         SMB_VFS_OP_SET_NT_ACL,          SMB_VFS_LAYER_TRANSPARENT},
+
+       /* POSIX ACL operations */
+
+       {fake_perms_chmod_acl,          SMB_VFS_OP_CHMOD_ACL,           SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_fchmod_acl,         SMB_VFS_OP_FCHMOD_ACL,          SMB_VFS_LAYER_TRANSPARENT},
+
+       {fake_perms_sys_acl_get_entry,  SMB_VFS_OP_SYS_ACL_GET_ENTRY,           SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_tag_type,       SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE,        SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_permset,        SMB_VFS_OP_SYS_ACL_GET_PERMSET,         SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_qualifier,      SMB_VFS_OP_SYS_ACL_GET_QUALIFIER,       SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_file,           SMB_VFS_OP_SYS_ACL_GET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_fd,             SMB_VFS_OP_SYS_ACL_GET_FD,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_clear_perms,        SMB_VFS_OP_SYS_ACL_CLEAR_PERMS,         SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_add_perm,           SMB_VFS_OP_SYS_ACL_ADD_PERM,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_to_text,            SMB_VFS_OP_SYS_ACL_TO_TEXT,             SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_init,               SMB_VFS_OP_SYS_ACL_INIT,                SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_create_entry,       SMB_VFS_OP_SYS_ACL_CREATE_ENTRY,        SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_set_tag_type,       SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE,        SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_set_qualifier,      SMB_VFS_OP_SYS_ACL_SET_QUALIFIER,       SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_set_permset,        SMB_VFS_OP_SYS_ACL_SET_PERMSET,         SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_valid,              SMB_VFS_OP_SYS_ACL_VALID,               SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_set_file,           SMB_VFS_OP_SYS_ACL_SET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_set_fd,             SMB_VFS_OP_SYS_ACL_SET_FD,              SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_delete_def_file,    SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,     SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_get_perm,           SMB_VFS_OP_SYS_ACL_GET_PERM,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_free_text,  SMB_VFS_OP_SYS_ACL_FREE_TEXT,           SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_free_acl,           SMB_VFS_OP_SYS_ACL_FREE_ACL,            SMB_VFS_LAYER_TRANSPARENT},
+       {fake_perms_sys_acl_free_qualifier,     SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,      SMB_VFS_LAYER_TRANSPARENT},
+       
+       {NULL,  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
+};
+
+/* VFS initialisation - return initialized vfs_op_tuple array back to Samba */
+
+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
+                       struct smb_vfs_handle_struct *vfs_handle)
+{
+       DEBUG(3, ("Initialising default vfs hooks\n"));
+
+       *vfs_version = SMB_VFS_INTERFACE_VERSION;
+       memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
+       
+       /* Remember vfs_handle for further allocation and referencing of private
+          information in vfs_handle->data
+       */
+       fake_perms_handle = vfs_handle;
+       return fake_perms_ops;
+}
+
+/* VFS finalization function */
+void vfs_done(struct tcon_context *conn)
+{
+       DEBUG(3, ("Finalizing default vfs hooks\n"));
+}
diff --git a/source4/modules/vfs_netatalk.c b/source4/modules/vfs_netatalk.c
new file mode 100644 (file)
index 0000000..0c1eb8d
--- /dev/null
@@ -0,0 +1,429 @@
+/* 
+ * AppleTalk VFS module for Samba-3.x
+ *
+ * Copyright (C) Alexei Kotovich, 2002
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <includes.h>
+#include <vfs.h>
+
+#define APPLEDOUBLE    ".AppleDouble"
+#define ADOUBLEMODE    0777
+
+/* atalk functions */
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
+  const char *fname, char **adbl_path, char **orig_path, 
+  SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
+
+static int atalk_unlink_file(const char *path);
+
+static struct vfs_ops default_vfs_ops; /* For passthrough operation */
+static struct smb_vfs_handle_struct *atalk_handle;
+
+static int atalk_get_path_ptr(char *path)
+{
+       int i   = 0;
+       int ptr = 0;
+       
+       for (i = 0; path[i]; i ++) {
+               if (path[i] == '/')
+                       ptr = i;
+               /* get out some 'spam';) from win32's file name */
+               else if (path[i] == ':') {
+                       path[i] = '\0';
+                       break;
+               }
+       }
+       
+       return ptr;
+}
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
+                              char **adbl_path, char **orig_path,
+                              SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
+{
+       int ptr0 = 0;
+       int ptr1 = 0;
+       char *dname = 0;
+       char *name  = 0;
+
+       if (!ctx || !path || !fname || !adbl_path || !orig_path ||
+               !adbl_info || !orig_info)
+               return -1;
+#if 0
+       DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
+#endif
+       if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
+               DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
+               return -1;
+       }
+
+       if (fname[0] == '.') ptr0 ++;
+       if (fname[1] == '/') ptr0 ++;
+
+       *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
+
+       /* get pointer to last '/' */
+       ptr1 = atalk_get_path_ptr(*orig_path);
+
+       sys_lstat(*orig_path, orig_info);
+
+       if (S_ISDIR(orig_info->st_mode)) {
+               *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/", 
+                 path, &fname[ptr0], APPLEDOUBLE);
+       } else {
+               dname = talloc_strdup(ctx, *orig_path);
+               dname[ptr1] = '\0';
+               name = *orig_path;
+               *adbl_path = talloc_asprintf(ctx, "%s/%s/%s", 
+                 dname, APPLEDOUBLE, &name[ptr1 + 1]);
+       }
+#if 0
+       DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path)); 
+#endif
+       sys_lstat(*adbl_path, adbl_info);
+       return 0;
+}
+
+static int atalk_unlink_file(const char *path)
+{
+       int ret = 0;
+
+       become_root();
+       ret = unlink(path);
+       unbecome_root();
+       
+       return ret;
+}
+
+static void atalk_add_to_list(name_compare_entry **list)
+{
+       int i, count = 0;
+       name_compare_entry *new_list = 0;
+       name_compare_entry *cur_list = 0;
+
+       cur_list = *list;
+
+       if (cur_list) {
+               for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
+                       if (strstr(cur_list[i].name, APPLEDOUBLE))
+                               return;
+               }
+       }
+
+       if (!(new_list = calloc(1, 
+         (count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
+               return;
+
+       for (i = 0; i < count; i ++) {
+               new_list[i].name    = strdup(cur_list[i].name);
+               new_list[i].is_wild = cur_list[i].is_wild;
+       }
+
+       new_list[i].name    = strdup(APPLEDOUBLE);
+       new_list[i].is_wild = False;
+
+       free_namearray(*list);
+
+       *list = new_list;
+       new_list = 0;
+       cur_list = 0;
+}
+
+static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
+{
+       char *dpath;
+       struct dirent *dent = 0;
+       DIR *dir;
+
+       if (!path) return;
+
+       dir = opendir(path);
+       if (!dir) return;
+
+       while (NULL != (dent = readdir(dir))) {
+               if (strcmp(dent->d_name, ".") == 0 ||
+                   strcmp(dent->d_name, "..") == 0)
+                       continue;
+               if (!(dpath = talloc_asprintf(ctx, "%s/%s", 
+                                             path, dent->d_name)))
+                       continue;
+               atalk_unlink_file(dpath);
+       }
+
+       closedir(dir);
+}
+
+/* Disk operations */
+
+/* Directory operations */
+
+DIR *atalk_opendir(struct tcon_context *conn, const char *fname)
+{
+       DIR *ret = 0;
+       
+       ret = default_vfs_ops.opendir(conn, fname);
+
+       /*
+        * when we try to perform delete operation upon file which has fork
+        * in ./.AppleDouble and this directory wasn't hidden by Samba,
+        * MS Windows explorer causes the error: "Cannot find the specified file"
+        * There is some workaround to avoid this situation, i.e. if
+        * connection has not .AppleDouble entry in either veto or hide 
+        * list then it would be nice to add one.
+        */
+
+       atalk_add_to_list(&conn->hide_list);
+       atalk_add_to_list(&conn->veto_list);
+
+       return ret;
+}
+
+static int atalk_rmdir(struct tcon_context *conn, const char *path)
+{
+       BOOL add = False;
+       TALLOC_CTX *ctx = 0;
+       char *dpath;
+
+       if (!conn || !conn->origpath || !path) goto exit_rmdir;
+
+       /* due to there is no way to change bDeleteVetoFiles variable
+        * from this module, gotta use talloc stuff..
+        */
+
+       strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
+
+       if (!(ctx = talloc_init("remove_directory")))
+               goto exit_rmdir;
+
+       if (!(dpath = talloc_asprintf(ctx, "%s/%s%s", 
+         conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
+               goto exit_rmdir;
+
+       atalk_rrmdir(ctx, dpath);
+
+exit_rmdir:
+       talloc_destroy(ctx);
+       return default_vfs_ops.rmdir(conn, path);
+}
+
+/* File operations */
+
+static int atalk_rename(struct tcon_context *conn, const char *old, const char *new)
+{
+       int ret = 0;
+       char *adbl_path = 0;
+       char *orig_path = 0;
+       SMB_STRUCT_STAT adbl_info;
+       SMB_STRUCT_STAT orig_info;
+       TALLOC_CTX *ctx;
+
+       ret = default_vfs_ops.rename(conn, old, new);
+
+       if (!conn || !old) return ret;
+
+       if (!(ctx = talloc_init("rename_file")))
+               return ret;
+
+       if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path, 
+         &adbl_info, &orig_info) != 0)
+               return ret;
+
+       if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+               DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));              
+               goto exit_rename;
+       }
+
+       atalk_unlink_file(adbl_path);
+
+exit_rename:
+       talloc_destroy(ctx);
+       return ret;
+}
+
+static int atalk_unlink(struct tcon_context *conn, const char *path)
+{
+       int ret = 0, i;
+       char *adbl_path = 0;
+       char *orig_path = 0;
+       SMB_STRUCT_STAT adbl_info;
+       SMB_STRUCT_STAT orig_info;
+       TALLOC_CTX *ctx;
+
+       ret = default_vfs_ops.unlink(conn, path);
+
+       if (!conn || !path) return ret;
+
+       /* no .AppleDouble sync if veto or hide list is empty,
+        * otherwise "Cannot find the specified file" error will be caused
+        */
+
+       if (!conn->veto_list) return ret;
+       if (!conn->hide_list) return ret;
+
+       for (i = 0; conn->veto_list[i].name; i ++) {
+               if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
+                       break;
+       }
+
+       if (!conn->veto_list[i].name) {
+               for (i = 0; conn->hide_list[i].name; i ++) {
+                       if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
+                               break;
+                       else {
+                               DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
+                                 APPLEDOUBLE));                
+                               return ret;
+                       }
+               }
+       }
+
+       if (!(ctx = talloc_init("unlink_file")))
+               return ret;
+
+       if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path, 
+         &adbl_info, &orig_info) != 0)
+               return ret;
+
+       if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+               DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
+               goto exit_unlink;
+       }
+
+       atalk_unlink_file(adbl_path);
+
+exit_unlink:   
+       talloc_destroy(ctx);
+       return ret;
+}
+
+static int atalk_chmod(struct tcon_context *conn, const char *path, mode_t mode)
+{
+       int ret = 0;
+       char *adbl_path = 0;
+       char *orig_path = 0;
+       SMB_STRUCT_STAT adbl_info;
+       SMB_STRUCT_STAT orig_info;
+       TALLOC_CTX *ctx;
+
+       ret = default_vfs_ops.chmod(conn, path, mode);
+
+       if (!conn || !path) return ret;
+
+       if (!(ctx = talloc_init("chmod_file")))
+               return ret;
+
+       if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+         &adbl_info, &orig_info) != 0)
+               return ret;
+
+       if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+               DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
+               goto exit_chmod;
+       }
+
+       chmod(adbl_path, ADOUBLEMODE);
+
+exit_chmod:    
+       talloc_destroy(ctx);
+       return ret;
+}
+
+static int atalk_chown(struct tcon_context *conn, const char *path, uid_t uid, gid_t gid)
+{
+       int ret = 0;
+       char *adbl_path = 0;
+       char *orig_path = 0;
+       SMB_STRUCT_STAT adbl_info;
+       SMB_STRUCT_STAT orig_info;
+       TALLOC_CTX *ctx;
+
+       ret = default_vfs_ops.chown(conn, path, uid, gid);
+
+       if (!conn || !path) return ret;
+
+       if (!(ctx = talloc_init("chown_file")))
+               return ret;
+
+       if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+         &adbl_info, &orig_info) != 0)
+               return ret;
+
+       if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+               DEBUG(3, ("ATALK: %s has passed..\n", orig_path));              
+               goto exit_chown;
+       }
+
+       chown(adbl_path, uid, gid);
+
+exit_chown:    
+       talloc_destroy(ctx);
+       return ret;
+}
+
+static vfs_op_tuple atalk_ops[] = {
+    
+       /* Directory operations */
+
+       {atalk_opendir,         SMB_VFS_OP_OPENDIR,     SMB_VFS_LAYER_TRANSPARENT},
+       {atalk_rmdir,           SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
+
+       /* File operations */
+
+       {atalk_rename,          SMB_VFS_OP_RENAME,      SMB_VFS_LAYER_TRANSPARENT},
+       {atalk_unlink,          SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_TRANSPARENT},
+       {atalk_chmod,           SMB_VFS_OP_CHMOD,       SMB_VFS_LAYER_TRANSPARENT},
+       {atalk_chown,           SMB_VFS_OP_CHOWN,       SMB_VFS_LAYER_TRANSPARENT},
+       
+       /* Finish VFS operations definition */
+       
+       {NULL,                  SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
+};
+
+/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
+  struct smb_vfs_handle_struct *vfs_handle)
+{
+       *vfs_version = SMB_VFS_INTERFACE_VERSION;
+       memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
+       
+       atalk_handle = vfs_handle;
+
+       DEBUG(3, ("ATALK: vfs module loaded\n"));
+       return atalk_ops;
+}
+
+/* VFS finalization function. */
+void vfs_done(struct tcon_context *conn)
+{
+       DEBUG(3, ("ATALK: vfs module unloaded\n"));
+}
diff --git a/source4/modules/vfs_recycle.c b/source4/modules/vfs_recycle.c
new file mode 100644 (file)
index 0000000..2a017ac
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * Recycle bin VFS module for Samba.
+ *
+ * Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
+ * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
+ * Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
+ * Copyright (C) 2002, Juergen Hasch - added some options.
+ * Copyright (C) 2002, Simo Sorce
+ *
+ * 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"
+
+#define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
+
+static int vfs_recycle_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_recycle_debug_level
+
+static const char *delimiter = "|";            /* delimiter for options */
+
+/* One per connection */
+
+typedef struct recycle_bin_struct
+{
+       TALLOC_CTX *mem_ctx;
+       char    *repository;            /* name of the recycle bin directory */
+       BOOL    keep_dir_tree;          /* keep directory structure of deleted file in recycle bin */
+       BOOL    versions;               /* create versions of deleted files with identical name */
+       BOOL    touch;                  /* touch access date of deleted file */
+       char    *exclude;               /* which files to exclude */
+       char    *exclude_dir;           /* which directories to exclude */
+       char    *noversions;            /* which files to exclude from versioning */
+       SMB_OFF_T maxsize;              /* maximum file size to be saved */
+} recycle_bin_struct;
+
+typedef struct recycle_bin_connections {
+       int conn;
+       recycle_bin_struct *data;
+       struct recycle_bin_connections *next;
+} recycle_bin_connections;
+
+typedef struct recycle_bin_private_data {
+       TALLOC_CTX *mem_ctx;
+       recycle_bin_connections *conns;
+} recycle_bin_private_data;
+
+struct smb_vfs_handle_struct *recycle_bin_private_handle;
+
+/* VFS operations */
+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
+
+static int recycle_connect(struct tcon_context *conn, const char *service, const char *user);
+static void recycle_disconnect(struct tcon_context *conn);
+static int recycle_unlink(struct tcon_context *, const char *);
+
+#define VFS_OP(x) ((void *) x)
+
+static vfs_op_tuple recycle_ops[] = {
+
+       /* Disk operations */
+       {VFS_OP(recycle_connect),       SMB_VFS_OP_CONNECT,     SMB_VFS_LAYER_TRANSPARENT},
+       {VFS_OP(recycle_disconnect),    SMB_VFS_OP_DISCONNECT,  SMB_VFS_LAYER_TRANSPARENT},
+
+       /* File operations */
+       {VFS_OP(recycle_unlink),        SMB_VFS_OP_UNLINK,      SMB_VFS_LAYER_TRANSPARENT},
+
+       {NULL,                          SMB_VFS_OP_NOOP,        SMB_VFS_LAYER_NOOP}
+};
+
+/**
+ * VFS initialisation function.
+ *
+ * @retval initialised vfs_op_tuple array
+ **/
+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
+                       struct smb_vfs_handle_struct *vfs_handle)
+{
+       TALLOC_CTX *mem_ctx = NULL;
+
+       DEBUG(10, ("Initializing VFS module recycle\n"));
+       *vfs_version = SMB_VFS_INTERFACE_VERSION;
+       memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
+       vfs_recycle_debug_level = debug_add_class("vfs_recycle_bin");
+       if (vfs_recycle_debug_level == -1) {
+               vfs_recycle_debug_level = DBGC_VFS;
+               DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
+       } else {
+               DEBUG(0, ("vfs_recycle: Debug class number of 'vfs_recycle': %d\n", vfs_recycle_debug_level));
+       }
+
+       recycle_bin_private_handle = vfs_handle;
+       if (!(mem_ctx = talloc_init("recycle bin data"))) {
+               DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
+               return NULL;
+       }
+
+       recycle_bin_private_handle->data = talloc(mem_ctx, sizeof(recycle_bin_private_data));
+       if (recycle_bin_private_handle->data == NULL) {
+               DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
+               return NULL;
+       }
+       ((recycle_bin_private_data *)(recycle_bin_private_handle->data))->mem_ctx = mem_ctx;
+       ((recycle_bin_private_data *)(recycle_bin_private_handle->data))->conns = NULL;
+
+       return recycle_ops;
+}
+
+/**
+ * VFS finalization function.
+ *
+ **/
+void vfs_done(void)
+{
+       recycle_bin_private_data *recdata;
+       recycle_bin_connections *recconn;
+
+       DEBUG(10, ("Unloading/Cleaning VFS module recycle bin\n"));
+
+       if (recycle_bin_private_handle)
+               recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data);
+       else {
+               DEBUG(0, ("Recycle bin not initialized!\n"));
+               return;
+       }
+
+       if (recdata) {
+               if (recdata->conns) {
+                       recconn = recdata->conns;
+                       while (recconn) {
+                               talloc_destroy(recconn->data->mem_ctx);
+                               recconn = recconn->next;
+                       }
+               }
+               if (recdata->mem_ctx) {
+                       talloc_destroy(recdata->mem_ctx);
+               }
+               recdata = NULL;
+       }
+}
+
+static int recycle_connect(struct tcon_context *conn, const char *service, const char *user)
+{
+       TALLOC_CTX *ctx = NULL;
+       recycle_bin_struct *recbin;
+       recycle_bin_connections *recconn;
+       recycle_bin_connections *recconnbase;
+       recycle_bin_private_data *recdata;
+       char *tmp_str;
+
+       DEBUG(10, ("Called for service %s (%d) as user %s\n", service, SNUM(conn), user));
+
+       if (recycle_bin_private_handle)
+               recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data);
+       else {
+               DEBUG(0, ("Recycle bin not initialized!\n"));
+               return -1;
+       }
+
+       if (!(ctx = talloc_init("recycle bin connection"))) {
+               DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
+               return -1;
+       }
+
+       recbin = talloc(ctx, sizeof(recycle_bin_struct));
+       if (recbin == NULL) {
+               DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
+               return -1;
+       }
+       recbin->mem_ctx = ctx;
+
+       /* Set defaults */
+       recbin->repository = talloc_strdup(recbin->mem_ctx, ".recycle");
+       ALLOC_CHECK(recbin->repository, error);
+       recbin->keep_dir_tree = False;
+       recbin->versions = False;
+       recbin->touch = False;
+       recbin->exclude = "";
+       recbin->exclude_dir = "";
+       recbin->noversions = "";
+       recbin->maxsize = 0;
+
+       /* parse configuration options */
+       if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "repository")) != NULL) {
+               recbin->repository = talloc_sub_conn(recbin->mem_ctx, conn, tmp_str);
+               ALLOC_CHECK(recbin->repository, error);
+               trim_string(recbin->repository, "/", "/");
+               DEBUG(5, ("recycle.bin: repository = %s\n", recbin->repository));
+       }
+       
+       recbin->keep_dir_tree = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "keeptree", False);
+       DEBUG(5, ("recycle.bin: keeptree = %d\n", recbin->keep_dir_tree));
+       
+       recbin->versions = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "versions", False);
+       DEBUG(5, ("recycle.bin: versions = %d\n", recbin->versions));
+       
+       recbin->touch = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "touch", False);
+       DEBUG(5, ("recycle.bin: touch = %d\n", recbin->touch));
+
+       recbin->maxsize = lp_parm_ulong(SNUM(conn), "vfs_recycle_bin", "maxsize");
+       if (recbin->maxsize == 0) {
+               recbin->maxsize = -1;
+               DEBUG(5, ("recycle.bin: maxsize = -infinite-\n"));
+       } else {
+               DEBUG(5, ("recycle.bin: maxsize = %ld\n", (long int)recbin->maxsize));
+       }
+
+       if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "exclude")) != NULL) {
+               recbin->exclude = talloc_strdup(recbin->mem_ctx, tmp_str);
+               ALLOC_CHECK(recbin->exclude, error);
+               DEBUG(5, ("recycle.bin: exclude = %s\n", recbin->exclude));
+       }
+       if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "exclude_dir")) != NULL) {
+               recbin->exclude_dir = talloc_strdup(recbin->mem_ctx, tmp_str);
+               ALLOC_CHECK(recbin->exclude_dir, error);
+               DEBUG(5, ("recycle.bin: exclude_dir = %s\n", recbin->exclude_dir));
+       }
+       if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "noversions")) != NULL) {
+               recbin->noversions = talloc_strdup(recbin->mem_ctx, tmp_str);
+               ALLOC_CHECK(recbin->noversions, error);
+               DEBUG(5, ("recycle.bin: noversions = %s\n", recbin->noversions));
+       }
+
+       recconn = talloc(recdata->mem_ctx, sizeof(recycle_bin_connections));
+       if (recconn == NULL) {
+               DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
+               goto error;
+       }
+       recconn->conn = SNUM(conn);
+       recconn->data = recbin;
+       recconn->next = NULL;
+       if (recdata->conns) {
+               recconnbase = recdata->conns;
+               while (recconnbase->next != NULL) recconnbase = recconnbase->next;
+               recconnbase->next = recconn;
+       } else {
+               recdata->conns = recconn;
+       }
+       return default_vfs_ops.connect(conn, service, user);
+
+error:
+       talloc_destroy(ctx);
+       return -1;
+}
+
+static void recycle_disconnect(struct tcon_context *conn)
+{
+       recycle_bin_private_data *recdata;
+       recycle_bin_connections *recconn;
+
+       DEBUG(10, ("Disconnecting VFS module recycle bin\n"));
+
+       if (recycle_bin_private_handle)
+               recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data);
+       else {
+               DEBUG(0, ("Recycle bin not initialized!\n"));
+               return;
+       }
+
+       if (recdata) {
+               if (recdata->conns) {
+                       if (recdata->conns->conn == SNUM(conn)) {
+                               talloc_destroy(recdata->conns->data->mem_ctx);
+                               recdata->conns = recdata->conns->next;
+                       } else {
+                               recconn = recdata->conns;
+                               while (recconn->next) {
+                                       if (recconn->next->conn == SNUM(conn)) {
+                                               talloc_destroy(recconn->next->data->mem_ctx);
+                                               recconn->next = recconn->next->next;
+                                               break;
+                                       }
+                                       recconn = recconn->next;
+                               }
+                       }
+               }
+       }
+       default_vfs_ops.disconnect(conn);
+}
+
+static BOOL recycle_directory_exist(struct tcon_context *conn, const char *dname)
+{
+       SMB_STRUCT_STAT st;
+
+       if (default_vfs_ops.stat(conn, dname, &st) == 0) {
+               if (S_ISDIR(st.st_mode)) {
+                       return True;
+               }
+       }
+
+       return False;
+}
+
+static BOOL recycle_file_exist(struct tcon_context *conn, const char *fname)
+{
+       SMB_STRUCT_STAT st;
+
+       if (default_vfs_ops.stat(conn, fname, &st) == 0) {
+               if (S_ISREG(st.st_mode)) {
+                       return True;
+               }
+       }
+
+       return False;
+}
+
+/**
+ * Return file size
+ * @param conn connection
+ * @param fname file name
+ * @return size in bytes
+ **/
+static SMB_OFF_T recycle_get_file_size(struct tcon_context *conn, const char *fname)
+{
+       SMB_STRUCT_STAT st;
+       if (default_vfs_ops.stat(conn, fname, &st) != 0) {
+               DEBUG(0,("recycle.bin: stat for %s returned %s\n", fname, strerror(errno)));
+               return (SMB_OFF_T)0;
+       }
+       return(st.st_size);
+}
+
+/**
+ * Create directory tree
+ * @param conn connection
+ * @param dname Directory tree to be created
+ * @return Returns True for success
+ **/
+static BOOL recycle_create_dir(struct tcon_context *conn, const char *dname)
+{
+       int len;
+       mode_t mode;
+       char *new_dir = NULL;
+       char *tmp_str = NULL;
+       char *token;
+       char *tok_str;
+       BOOL ret = False;
+
+       mode = S_IREAD | S_IWRITE | S_IEXEC;
+
+       tmp_str = strdup(dname);
+       ALLOC_CHECK(tmp_str, done);
+       tok_str = tmp_str;
+
+       len = strlen(dname);
+       new_dir = (char *)malloc(len + 1);
+       ALLOC_CHECK(new_dir, done);
+       *new_dir = '\0';
+
+       /* Create directory tree if neccessary */
+       for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
+               safe_strcat(new_dir, token, len);
+               if (recycle_directory_exist(conn, new_dir))
+                       DEBUG(10, ("recycle.bin: dir %s already exists\n", new_dir));
+               else {
+                       DEBUG(5, ("recycle.bin: creating new dir %s\n", new_dir));
+                       if (default_vfs_ops.mkdir(conn, new_dir, mode) != 0) {
+                               DEBUG(1,("recycle.bin: mkdir failed for %s with error: %s\n", new_dir, strerror(errno)));
+                               ret = False;
+                               goto done;
+                       }
+               }
+               safe_strcat(new_dir, "/", len);
+               }
+
+       ret = True;
+done:
+       SAFE_FREE(tmp_str);
+       SAFE_FREE(new_dir);
+       return ret;
+}
+
+/**
+ * Check if needle is contained exactly in haystack
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exactly to haystack
+ * @return True if found
+ **/
+static BOOL checkparam(const char *haystack, const char *needle)
+{
+       char *token;
+       char *tok_str;
+       char *tmp_str;
+       BOOL ret = False;
+
+       if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) {
+               return False;
+       }
+
+       tmp_str = strdup(haystack);
+       ALLOC_CHECK(tmp_str, done);
+       token = tok_str = tmp_str;
+
+       for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) {
+               if(strcmp(token, needle) == 0) {
+                       ret = True;
+                       goto done;
+               }
+       }
+done:
+       SAFE_FREE(tmp_str);
+       return ret;
+}
+
+/**
+ * Check if needle is contained in haystack, * and ? patterns are resolved
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exectly to haystack including pattern matching
+ * @return True if found
+ **/
+static BOOL matchparam(const char *haystack, const char *needle)
+{
+       char *token;
+       char *tok_str;
+       char *tmp_str;
+       BOOL ret = False;
+
+       if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) {
+               return False;
+       }
+
+       tmp_str = strdup(haystack);
+       ALLOC_CHECK(tmp_str, done);
+       token = tok_str = tmp_str;
+
+       for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) {
+               if (!unix_wild_match(token, needle)) {
+                       ret = True;
+                       goto done;
+               }
+       }
+done:
+       SAFE_FREE(tmp_str);
+       return ret;
+}
+
+/**
+ * Touch access date
+ **/
+static void recycle_touch(struct tcon_context *conn, const char *fname)
+{
+       SMB_STRUCT_STAT st;
+       struct utimbuf tb;
+       time_t currtime;
+
+       if (default_vfs_ops.stat(conn, fname, &st) != 0) {
+               DEBUG(0,("recycle.bin: stat for %s returned %s\n", fname, strerror(errno)));
+               return;
+       }
+       currtime = time(&currtime);
+       tb.actime = currtime;
+       tb.modtime = st.st_mtime;
+
+       if (default_vfs_ops.utime(conn, fname, &tb) == -1 )
+               DEBUG(0, ("recycle.bin: touching %s failed, reason = %s\n", fname, strerror(errno)));
+       }
+
+/**
+ * Check if file should be recycled
+ **/
+static int recycle_unlink(struct tcon_context *conn, const char *file_name)
+{
+       recycle_bin_private_data *recdata;
+       recycle_bin_connections *recconn;
+       recycle_bin_struct *recbin;
+       char *path_name = NULL;
+               char *temp_name = NULL;
+       char *final_name = NULL;
+       const char *base;
+       int i;
+/*     SMB_BIG_UINT dfree, dsize, bsize;       */
+       SMB_OFF_T file_size; /* space_avail;    */
+       BOOL exist;
+       int rc = -1;
+
+       recbin = NULL;
+       if (recycle_bin_private_handle) {
+               recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data);
+               if (recdata) {
+                       if (recdata->conns) {
+                               recconn = recdata->conns;
+                               while (recconn && recconn->conn != SNUM(conn)) recconn = recconn->next;
+                               if (recconn != NULL) {
+                                       recbin = recconn->data;
+                               }
+                       }
+               }
+       }
+       if (recbin == NULL) {
+               DEBUG(0, ("Recycle bin not initialized!\n"));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       if(!recbin->repository || *(recbin->repository) == '\0') {
+               DEBUG(3, ("Recycle path not set, purging %s...\n", file_name));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       /* we don't recycle the recycle bin... */
+       if (strncmp(file_name, recbin->repository, strlen(recbin->repository)) == 0) {
+               DEBUG(3, ("File is within recycling bin, unlinking ...\n"));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       file_size = recycle_get_file_size(conn, file_name);
+       /* it is wrong to purge filenames only because they are empty imho
+        *   --- simo
+        *
+       if(fsize == 0) {
+               DEBUG(3, ("File %s is empty, purging...\n", file_name));
+               rc = default_vfs_ops.unlink(conn,file_name);
+               goto done;
+       }
+        */
+
+       /* FIXME: this is wrong, we should check the hole size of the recycle bin is
+        * not greater then maxsize, not the size of the single file, also it is better
+        * to remove older files
+        */
+       if(recbin->maxsize > 0 && file_size > recbin->maxsize) {
+               DEBUG(3, ("File %s exceeds maximum recycle size, purging... \n", file_name));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       /* FIXME: this is wrong: moving files with rename does not change the disk space
+        * allocation
+        *
+       space_avail = default_vfs_ops.disk_free(conn, ".", True, &bsize, &dfree, &dsize) * 1024L;
+       DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size));
+       if(space_avail < file_size) {
+               DEBUG(3, ("Not enough diskspace, purging file %s\n", file_name));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+        */
+
+       /* extract filename and path */
+       path_name = (char *)malloc(PATH_MAX);
+       ALLOC_CHECK(path_name, done);
+       *path_name = '\0';
+       safe_strcpy(path_name, file_name, PATH_MAX - 1);
+       base = strrchr(path_name, '/');
+       if (base == NULL) {
+               base = file_name;
+               safe_strcpy(path_name, "/", PATH_MAX - 1);
+       }
+       else {
+               base++;
+       }
+
+       DEBUG(10, ("recycle.bin: fname = %s\n", file_name));    /* original filename with path */
+       DEBUG(10, ("recycle.bin: fpath = %s\n", path_name));    /* original path */
+       DEBUG(10, ("recycle.bin: base = %s\n", base));          /* filename without path */
+
+       if (matchparam(recbin->exclude, base)) {
+               DEBUG(3, ("recycle.bin: file %s is excluded \n", base));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       /* FIXME: this check will fail if we have more than one level of directories,
+        * we shoud check for every level 1, 1/2, 1/2/3, 1/2/3/4 .... 
+        *      ---simo
+        */
+       if (checkparam(recbin->exclude_dir, path_name)) {
+               DEBUG(3, ("recycle.bin: directory %s is excluded \n", path_name));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       temp_name = (char *)strdup(recbin->repository);
+       ALLOC_CHECK(temp_name, done);
+
+       /* see if we need to recreate the original directory structure in the recycle bin */
+       if (recbin->keep_dir_tree == True) {
+               safe_strcat(temp_name, "/", PATH_MAX - 1);
+               safe_strcat(temp_name, path_name, PATH_MAX - 1);
+       }
+
+       exist = recycle_directory_exist(conn, temp_name);
+       if (exist) {
+               DEBUG(10, ("recycle.bin: Directory already exists\n"));
+       } else {
+               DEBUG(10, ("recycle.bin: Creating directory %s\n", temp_name));
+               if (recycle_create_dir(conn, temp_name) == False) {
+                       DEBUG(3, ("Could not create directory, purging %s...\n", file_name));
+                       rc = default_vfs_ops.unlink(conn, file_name);
+                       goto done;
+               }
+       }
+
+       final_name = NULL;
+       asprintf(&final_name, "%s/%s", temp_name, base);
+       ALLOC_CHECK(final_name, done);
+       DEBUG(10, ("recycle.bin: recycled file name%s\n", temp_name));          /* new filename with path */
+
+       /* check if we should delete file from recycle bin */
+       if (recycle_file_exist(conn, final_name)) {
+               if (recbin->versions == False || matchparam(recbin->noversions, base) == True) {
+                       DEBUG(3, ("recycle.bin: Removing old file %s from recycle bin\n", final_name));
+                       if (default_vfs_ops.unlink(conn, final_name) != 0) {
+                               DEBUG(1, ("recycle.bin: Error deleting old file: %s\n", strerror(errno)));
+                       }
+               }
+       }
+
+       /* rename file we move to recycle bin */
+       i = 1;
+       while (recycle_file_exist(conn, final_name)) {
+               snprintf(final_name, PATH_MAX, "%s/Copy #%d of %s", temp_name, i++, base);
+       }
+
+       DEBUG(10, ("recycle.bin: Moving %s to %s\n", file_name, final_name));
+       rc = default_vfs_ops.rename(conn, file_name, final_name);
+       if (rc != 0) {
+               DEBUG(3, ("recycle.bin: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name));
+               rc = default_vfs_ops.unlink(conn, file_name);
+               goto done;
+       }
+
+       /* touch access date of moved file */
+       if (recbin->touch == True )
+               recycle_touch(conn, final_name);
+
+done:
+       SAFE_FREE(path_name);
+       SAFE_FREE(temp_name);
+       SAFE_FREE(final_name);
+       return rc;
+}
diff --git a/source4/modules/xml.c b/source4/modules/xml.c
new file mode 100644 (file)
index 0000000..ead3e3a
--- /dev/null
@@ -0,0 +1,575 @@
+
+/*
+ * XML password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Some parts based on the libxml gjobread example by Daniel Veillard
+ * 
+ * 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.
+ */
+
+/* FIXME: 
+ * - Support stdin input by using '-'
+ * - Be faster. Don't rewrite the whole file when adding a user, but store it in the memory and save it when exiting. Requires changes to samba source.
+ * - Gives the ability to read/write to standard input/output
+ * - Do locking!
+ * - Better names!
+ */
+
+
+#define XML_URL "http://www.samba.org/ns"
+
+#include "includes.h"
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+static int xmlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS xmlsam_debug_level
+
+static char * iota(int a) {
+       static char tmp[10];
+
+       snprintf(tmp, 9, "%d", a);
+       return tmp;
+}
+
+BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+       pstring temp;
+
+       cur = cur->xmlChildrenNode;
+       while (cur != NULL) {
+               if (strcmp(cur->name, "crypt"))
+                       DEBUG(0, ("Unknown element %s\n", cur->name));
+               else {
+                       if (!strcmp(xmlGetProp(cur, "type"), "nt")
+                               &&
+                               pdb_gethexpwd(xmlNodeListGetString
+                                                         (doc, cur->xmlChildrenNode, 1), temp))
+                               pdb_set_nt_passwd(u, temp, PDB_SET);
+                       else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
+                                        &&
+                                        pdb_gethexpwd(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1), temp))
+                               pdb_set_lanman_passwd(u, temp, PDB_SET);
+                       else
+                               DEBUG(0,
+                                         ("Unknown crypt type: %s\n",
+                                          xmlGetProp(cur, "type")));
+               }
+               cur = cur->next;
+       }
+       return True;
+}
+
+BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+       char *tmp;
+       DOM_SID sid;
+
+       tmp = xmlGetProp(cur, "sid");
+       if (tmp){
+               string_to_sid(&sid, tmp);
+               pdb_set_user_sid(u, &sid, PDB_SET);
+       }
+       tmp = xmlGetProp(cur, "uid");
+       if (tmp)
+               pdb_set_uid(u, atol(tmp), PDB_SET);
+       pdb_set_username(u, xmlGetProp(cur, "name"), PDB_SET);
+       /* We don't care what the top level element name is */
+       cur = cur->xmlChildrenNode;
+       while (cur != NULL) {
+               if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
+                       tmp = xmlGetProp(cur, "gid");
+                       if (tmp)
+                               pdb_set_gid(u, atol(tmp), PDB_SET);
+                       tmp = xmlGetProp(cur, "sid");
+                       if (tmp){
+                               string_to_sid(&sid, tmp);
+                               pdb_set_group_sid(u, &sid, PDB_SET);
+                       }
+               }
+
+               else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
+                       pdb_set_domain(u,
+                                                  xmlNodeListGetString(doc, cur->xmlChildrenNode,
+                                                                                               1), PDB_SET);
+
+               else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
+                       pdb_set_fullname(u,
+                                                        xmlNodeListGetString(doc,
+                                                                                                 cur->xmlChildrenNode,
+                                                                                                 1), PDB_SET);
+
+               else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
+                       pdb_set_nt_username(u,
+                                                               xmlNodeListGetString(doc,
+                                                                                                        cur->xmlChildrenNode,
+                                                                                                        1), PDB_SET);
+
+               else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
+                       pdb_set_logon_script(u,
+                                                                xmlNodeListGetString(doc,
+                                                                                                         cur->xmlChildrenNode,
+                                                                                                         1), PDB_SET);
+
+               else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
+                       pdb_set_profile_path(u,
+                                                                xmlNodeListGetString(doc,
+                                                                                                         cur->xmlChildrenNode,
+                                                                                                         1), PDB_SET);
+
+               else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
+                       pdb_set_logon_time(u,
+                                                          atol(xmlNodeListGetString
+                                                                       (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
+                       pdb_set_logoff_time(u,
+                                                               atol(xmlNodeListGetString
+                                                                        (doc, cur->xmlChildrenNode, 1)),
+                                                               PDB_SET);
+
+               else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
+                       pdb_set_kickoff_time(u,
+                                                                atol(xmlNodeListGetString
+                                                                         (doc, cur->xmlChildrenNode, 1)),
+                                                                PDB_SET);
+
+               else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
+                       pdb_set_logon_divs(u,
+                                                          atol(xmlNodeListGetString
+                                                                       (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
+                       pdb_set_hours_len(u,
+                                                         atol(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "unknown_3") && cur->ns == ns)
+                       pdb_set_unknown_3(u,
+                                                         atol(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "unknown_5") && cur->ns == ns)
+                       pdb_set_unknown_5(u,
+                                                         atol(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
+                       pdb_set_unknown_6(u,
+                                                         atol(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
+                       pdb_set_homedir(u,
+                                                       xmlNodeListGetString(doc, cur->xmlChildrenNode,
+                                                                                                1), PDB_SET);
+
+               else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
+                       pdb_set_unknown_str(u,
+                                                               xmlNodeListGetString(doc,
+                                                                                                        cur->xmlChildrenNode,
+                                                                                                        1), PDB_SET);
+
+               else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
+                       pdb_set_dir_drive(u,
+                                                         xmlNodeListGetString(doc,
+                                                                                                  cur->xmlChildrenNode,
+                                                                                                  1), PDB_SET);
+
+               else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
+                       pdb_set_munged_dial(u,
+                                                               xmlNodeListGetString(doc,
+                                                                                                        cur->xmlChildrenNode,
+                                                                                                        1), PDB_SET);
+
+               else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
+                       pdb_set_acct_desc(u,
+                                                         xmlNodeListGetString(doc,
+                                                                                                  cur->xmlChildrenNode,
+                                                                                                  1), PDB_SET);
+
+               else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
+                       pdb_set_acct_ctrl(u,
+                                                         atol(xmlNodeListGetString
+                                                                  (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+               else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
+                       pdb_set_workstations(u,
+                                                                xmlNodeListGetString(doc,
+                                                                                                         cur->xmlChildrenNode,
+                                                                                                         1), PDB_SET);
+
+               else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
+                       tmp = xmlGetProp(cur, "last_set");
+                       if (tmp)
+                               pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
+                       tmp = xmlGetProp(cur, "must_change");
+                       if (tmp)
+                               pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
+                       tmp = xmlGetProp(cur, "can_change");
+                       if (tmp)
+                               pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
+                       parsePass(doc, ns, cur, u);
+               }
+
+               else
+                       DEBUG(0, ("Unknown element %s\n", cur->name));
+               cur = cur->next;
+       }
+
+       return True;
+}
+
+typedef struct pdb_xml {
+       char *location;
+       char written;
+       xmlDocPtr doc;
+       xmlNodePtr users;
+       xmlNodePtr pwent;
+       xmlNsPtr ns;
+} pdb_xml;
+
+xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
+{
+       xmlNodePtr cur;
+
+       data->doc = xmlParseFile(data->location);
+       if (data->doc == NULL)
+               return NULL;
+
+       cur = xmlDocGetRootElement(data->doc);
+       if (!cur) {
+               DEBUG(0, ("empty document\n"));
+               xmlFreeDoc(data->doc);
+               return NULL;
+       }
+       data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
+       if (!data->ns) {
+               DEBUG(0,
+                         ("document of the wrong type, samba user namespace not found\n"));
+               xmlFreeDoc(data->doc);
+               return NULL;
+       }
+       if (strcmp(cur->name, "samba")) {
+               DEBUG(0, ("document of the wrong type, root node != samba"));
+               xmlFreeDoc(data->doc);
+               return NULL;
+       }
+
+       cur = cur->xmlChildrenNode;
+       while (cur && xmlIsBlankNode(cur)) {
+               cur = cur->next;
+       }
+       if (!cur)
+               return NULL;
+       if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
+               DEBUG(0, ("document of the wrong type, was '%s', users expected",
+                                 cur->name));
+               DEBUG(0, ("xmlDocDump follows\n"));
+               xmlDocDump(stderr, data->doc);
+               DEBUG(0, ("xmlDocDump finished\n"));
+               xmlFreeDoc(data->doc);
+               return NULL;
+       }
+       data->users = cur;
+       cur = cur->xmlChildrenNode;
+       return cur;
+}
+
+static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+       pdb_xml *data;
+
+       if (!methods) {
+               DEBUG(0, ("Invalid methods\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       data = (pdb_xml *) methods->private_data;
+       if (!data) {
+               DEBUG(0, ("Invalid pdb_xml_data\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       data->pwent = parseSambaXMLFile(data);
+       if (!data->pwent)
+               return NT_STATUS_UNSUCCESSFUL;
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************
+  End enumeration of the passwd list.
+ ****************************************************************/
+
+static void xmlsam_endsampwent(struct pdb_methods *methods)
+{
+       pdb_xml *data;
+
+       if (!methods) {
+               DEBUG(0, ("Invalid methods\n"));
+               return;
+       }
+
+       data = (pdb_xml *) methods->private_data;
+
+       if (!data) {
+               DEBUG(0, ("Invalid pdb_xml_data\n"));
+               return;
+       }
+
+       xmlFreeDoc(data->doc);
+       data->doc = NULL;
+       data->pwent = NULL;
+}
+
+/*****************************************************************
+  Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+       pdb_xml *data;
+
+       if (!methods) {
+               DEBUG(0, ("Invalid methods\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       data = (pdb_xml *) methods->private_data;
+
+       if (!data) {
+               DEBUG(0, ("Invalid pdb_xml_data\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       while (data->pwent) {
+               if ((!strcmp(data->pwent->name, "user")) &&
+                       (data->pwent->ns == data->ns)) {
+
+                       parseUser(data->doc, data->ns, data->pwent, user);
+                       data->pwent = data->pwent->next;
+                       return NT_STATUS_OK;
+               }
+               data->pwent = data->pwent->next;
+       }
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+  Adds an existing SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
+{
+       pstring temp;
+       fstring sid_str;
+       xmlNodePtr cur, user, pass, root;
+       pdb_xml *data;
+
+       DEBUG(10, ("xmlsam_add_sam_account called!\n"));
+
+       if (!methods) {
+               DEBUG(0, ("Invalid methods\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       data = (pdb_xml *) methods->private_data;
+       if (!data) {
+               DEBUG(0, ("Invalid pdb_xml_data\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Create a new document if we can't open the current one */
+       if (!parseSambaXMLFile(data)) {
+               DEBUG(0, ("Can't load current XML file, creating a new one\n"));
+               data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
+               root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
+               cur = xmlDocSetRootElement(data->doc, root);
+               data->ns = xmlNewNs(root, XML_URL, "samba");
+               data->users = xmlNewChild(root, data->ns, "users", NULL);
+       }
+
+       user = xmlNewChild(data->users, data->ns, "user", NULL);
+       xmlNewProp(user, "sid",
+                          sid_to_string(sid_str, pdb_get_user_sid(u)));
+       if (pdb_get_init_flags(u, PDB_UID) != PDB_DEFAULT)
+               xmlNewProp(user, "uid", iota(pdb_get_uid(u)));
+
+       if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
+               xmlNewProp(user, "name", pdb_get_username(u));
+
+       cur = xmlNewChild(user, data->ns, "group", NULL);
+       
+       xmlNewProp(cur, "sid",
+                          sid_to_string(sid_str, pdb_get_group_sid(u)));
+       if (pdb_get_init_flags(u, PDB_GID) != PDB_DEFAULT)
+               xmlNewProp(cur, "gid", iota(pdb_get_gid(u)));
+
+       if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT)
+               xmlNewChild(user, data->ns, "login_time",
+                                       iota(pdb_get_logon_time(u)));
+
+       if (pdb_get_init_flags(u, PDB_LOGOFFTIME) != PDB_DEFAULT)
+               xmlNewChild(user, data->ns, "logoff_time",
+                                       iota(pdb_get_logoff_time(u)));
+
+       if (pdb_get_init_flags(u, PDB_KICKOFFTIME) != PDB_DEFAULT)
+               xmlNewChild(user, data->ns, "kickoff_time",
+                                       iota(pdb_get_kickoff_time(u)));
+
+       if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
+               xmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
+
+       if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
+               xmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
+
+       if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
+               xmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
+
+       if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
+               xmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
+
+       if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
+               xmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
+
+       if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
+               xmlNewChild(user, data->ns, "logon_script",
+                                       pdb_get_logon_script(u));
+
+       if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
+               xmlNewChild(user, data->ns, "profile_path",
+                                       pdb_get_profile_path(u));
+
+       if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
+               xmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
+
+       if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
+               xmlNewChild(user, data->ns, "workstations",
+                                       pdb_get_workstations(u));
+
+       if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
+               xmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
+
+       if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
+               xmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
+
+
+       /* Password stuff */
+       pass = xmlNewChild(user, data->ns, "password", NULL);
+       if (pdb_get_pass_last_set_time(u))
+               xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
+       if (pdb_get_init_flags(u, PDB_CANCHANGETIME) != PDB_DEFAULT)
+               xmlNewProp(pass, "can_change",
+                                  iota(pdb_get_pass_can_change_time(u)));
+
+       if (pdb_get_init_flags(u, PDB_MUSTCHANGETIME) != PDB_DEFAULT)
+               xmlNewProp(pass, "must_change",
+                                  iota(pdb_get_pass_must_change_time(u)));
+
+
+       if (pdb_get_lanman_passwd(u)) {
+               pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
+                                         pdb_get_acct_ctrl(u));
+               cur = xmlNewChild(pass, data->ns, "crypt", temp);
+               xmlNewProp(cur, "type", "lanman");
+       }
+
+       if (pdb_get_nt_passwd(u)) {
+               pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
+               cur = xmlNewChild(pass, data->ns, "crypt", temp);
+               xmlNewProp(cur, "type", "nt");
+       }
+
+       xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
+       xmlNewChild(user, data->ns, "unknown_3", iota(pdb_get_unknown_3(u)));
+
+       if (pdb_get_logon_divs(u))
+               xmlNewChild(user, data->ns, "logon_divs",
+                                       iota(pdb_get_logon_divs(u)));
+
+       if (pdb_get_hours_len(u))
+               xmlNewChild(user, data->ns, "hours_len",
+                                       iota(pdb_get_hours_len(u)));
+
+       xmlNewChild(user, data->ns, "unknown_5", iota(pdb_get_unknown_5(u)));
+       xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u)));
+       xmlSaveFile(data->location, data->doc);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
+                const char *location)
+{
+       NTSTATUS nt_status;
+       pdb_xml *data;
+
+       xmlsam_debug_level = debug_add_class("xmlsam");
+       if (xmlsam_debug_level == -1) {
+               xmlsam_debug_level = DBGC_ALL;
+               DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
+       }
+
+       if (!pdb_context) {
+               DEBUG(0, ("invalid pdb_methods specified\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!NT_STATUS_IS_OK
+               (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "xmlsam";
+
+       (*pdb_method)->setsampwent = xmlsam_setsampwent;
+       (*pdb_method)->endsampwent = xmlsam_endsampwent;
+       (*pdb_method)->getsampwent = xmlsam_getsampwent;
+       (*pdb_method)->add_sam_account = xmlsam_add_sam_account;
+       (*pdb_method)->getsampwnam = NULL;
+       (*pdb_method)->getsampwsid = NULL;
+       (*pdb_method)->update_sam_account = NULL;
+       (*pdb_method)->delete_sam_account = NULL;
+       (*pdb_method)->getgrsid = NULL;
+       (*pdb_method)->getgrgid = NULL;
+       (*pdb_method)->getgrnam = NULL;
+       (*pdb_method)->add_group_mapping_entry = NULL;
+       (*pdb_method)->update_group_mapping_entry = NULL;
+       (*pdb_method)->delete_group_mapping_entry = NULL;
+       (*pdb_method)->enum_group_mapping = NULL;
+
+       data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
+       data->location =
+               (location ? talloc_strdup(pdb_context->mem_ctx, location) : "-");
+       data->pwent = NULL;
+       data->written = 0;
+       (*pdb_method)->private_data = data;
+
+       LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
+
+       return NT_STATUS_OK;
+}
+
+int init_module(void);
+
+int init_module() 
+{
+       if(smb_register_passdb("xml", xmlsam_init, PASSDB_INTERFACE_VERSION))
+               return 0;
+
+       return 1;
+}
diff --git a/source4/msdfs/README b/source4/msdfs/README
new file mode 100644 (file)
index 0000000..0e924b3
--- /dev/null
@@ -0,0 +1,32 @@
+Setting up MS Dfs in Samba
+kalele@veritas.com March 2000
+
+Currently, MS Dfs support is a configure time parameter (--with-msdfs). Can be changed later to always compile it in..
+
+To have a server announce itself as a Dfs server, add a "host msdfs=yes" entry to smb.conf.
+
+To make a share a Dfs root, add a "msdfs root=yes" entry to the share definition 
+in the smb.conf file.
+e.g. 
+[pub]
+       path = /export/publicsmb
+       msdfs root = yes
+
+To create dfs volumes/junctions in the share, create symbolic links of the
+format msdfs:server1\share1,server2\share2 and so on.
+
+In the above example, create a dfs volume "dfsstorage" in the [pub] share as:
+cd /export/publicsmb
+ln -s msdfs:serverA\\share dfsstorage
+
+Clicking on dfsstorage from a dfs-aware client will show you the contents of 
+\\serverA\share
+
+Shares with "msdfs root = no" (which is the default) entries are served as normal 
+shares and the client stops talking Dfs with Samba after a tconX.
+
+NOTES: 
+* Windows clients need to be rebooted if a non-dfs root is made a dfs root or
+  vice versa. A better option is to introduce a new share and make it the dfs root.
+* Currently there's a restriction that msdfs symlink names should be all 
+  lowercase.
diff --git a/source4/msdfs/msdfs.c b/source4/msdfs/msdfs.c
new file mode 100644 (file)
index 0000000..ac9566f
--- /dev/null
@@ -0,0 +1,913 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   MSDfs services for Samba
+   Copyright (C) Shirish Kalele 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"
+
+extern fstring local_machine;
+extern uint32 global_client_caps;
+#ifdef 1
+static uint32 dfs_referral_tries = 0;
+#endif
+
+/**********************************************************************
+  Parse the pathname  of the form \hostname\service\reqpath
+  into the dfs_path structure 
+ **********************************************************************/
+
+static BOOL parse_dfs_path(const char *pathname, struct dfs_path* pdp)
+{
+       pstring pathname_local;
+       char* p,*temp;
+
+       pstrcpy(pathname_local,pathname);
+       p = temp = pathname_local;
+
+       ZERO_STRUCTP(pdp);
+
+       trim_string(temp,"\\","\\");
+       DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
+
+       /* now tokenize */
+       /* parse out hostname */
+       p = strchr(temp,'\\');
+       if(p == NULL)
+               return False;
+       *p = '\0';
+       pstrcpy(pdp->hostname,temp);
+       DEBUG(10,("hostname: %s\n",pdp->hostname));
+
+       /* parse out servicename */
+       temp = p+1;
+       p = strchr(temp,'\\');
+       if(p == NULL) {
+               pstrcpy(pdp->servicename,temp);
+               pdp->reqpath[0] = '\0';
+               return True;
+       }
+       *p = '\0';
+       pstrcpy(pdp->servicename,temp);
+       DEBUG(10,("servicename: %s\n",pdp->servicename));
+
+       /* rest is reqpath */
+       pstrcpy(pdp->reqpath, p+1);
+       p = pdp->reqpath;
+       while (*p) {
+               if (*p == '\\') *p = '/';
+               p++;
+       }
+
+       DEBUG(10,("rest of the path: %s\n",pdp->reqpath));
+       return True;
+}
+
+/********************************************************
+ Fake up a connection struct for the VFS layer.
+*********************************************************/
+
+static BOOL create_conn_struct( struct tcon_context *conn, int snum, char *path)
+{
+       ZERO_STRUCTP(conn);
+       conn->service = snum;
+       conn->connectpath = path;
+       pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
+
+       if (!smbd_vfs_init(conn)) {
+               DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
+               return False;
+       }
+       return True;
+}
+
+
+/**********************************************************************
+ Parse the contents of a symlink to verify if it is an msdfs referral
+ A valid referral is of the form: msdfs:server1\share1,server2\share2
+ **********************************************************************/
+static BOOL parse_symlink(char* buf,struct referral** preflist, 
+                                int* refcount)
+{
+       pstring temp;
+       char* prot;
+       char* alt_path[MAX_REFERRAL_COUNT];
+       int count=0, i;
+       struct referral* reflist;
+
+       pstrcpy(temp,buf);
+  
+       prot = strtok(temp,":");
+
+       if (!strequal(prot, "msdfs"))
+               return False;
+
+       /* No referral list requested. Just yes/no. */
+       if (!preflist) 
+               return True;
+
+       /* parse out the alternate paths */
+       while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
+               count++;
+
+       DEBUG(10,("parse_symlink: count=%d\n", count));
+
+       reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
+       if(reflist == NULL) {
+               DEBUG(0,("parse_symlink: Malloc failed!\n"));
+               return False;
+       }
+       
+       for(i=0;i<count;i++) {
+               /* replace / in the alternate path by a \ */
+               char* p = strchr(alt_path[i],'/');
+               int j = i;
+#ifdef 1
+               // used for testing splitting files between servers
+               if ((count == 2) &&
+                       (dfs_referral_tries & 0xfffffffe)    // this will reverse the order of the list
+                       j = count - 1;
+               dfs_referral_tries++;
+#endif                                 
+               if(p)
+                       *p = '\\'; 
+
+               pstrcpy(reflist[j].alternate_path, "\\");
+               pstrcat(reflist[j].alternate_path, alt_path[i]);
+               reflist[j].proximity = 0;
+               reflist[j].ttl = REFERRAL_TTL;
+               DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[j].alternate_path));
+       }
+
+       if(refcount)
+               *refcount = count;
+
+       return True;
+}
+/**********************************************************************
+ Returns true if the unix path is a valid msdfs symlink
+ **********************************************************************/
+BOOL is_msdfs_link(struct tcon_context *conn, char* path,
+                  struct referral** reflistp, int* refcnt,
+                  SMB_STRUCT_STAT *sbufp)
+{
+       SMB_STRUCT_STAT st;
+       pstring referral;
+       int referral_len = 0;
+
+       if (!path || !conn)
+               return False;
+
+       strlower(path);
+
+       if (sbufp == NULL)
+               sbufp = &st;
+
+       if (conn->vfs_ops.lstat(conn, path, sbufp) != 0) {
+               DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
+               return False;
+       }
+  
+       if (S_ISLNK(sbufp->st_mode)) {
+               /* open the link and read it */
+               referral_len = conn->vfs_ops.readlink(conn, path, referral, 
+                                                     sizeof(pstring));
+               if (referral_len == -1) {
+                       DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
+                       return False;
+               }
+
+               referral[referral_len] = '\0';
+               DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
+               if (parse_symlink(referral, reflistp, refcnt))
+                       return True;
+       }
+       return False;
+}
+
+/*****************************************************************
+ Used by other functions to decide if a dfs path is remote,
+and to get the list of referred locations for that remote path.
+findfirst_flag: For findfirsts, dfs links themselves are not
+redirected, but paths beyond the links are. For normal smb calls,
+even dfs links need to be redirected.
+
+self_referralp: clients expect a dfs referral for the same share when
+they request referrals for dfs roots on a server. 
+
+consumedcntp: how much of the dfs path is being redirected. the client
+should try the remaining path on the redirected server.
+*****************************************************************/
+static BOOL resolve_dfs_path(char* dfspath, struct dfs_path* dp, 
+                     struct tcon_context *conn,
+                     BOOL findfirst_flag,
+                     struct referral** reflistpp, int* refcntp,
+                     BOOL* self_referralp, int* consumedcntp)
+{
+       fstring localpath;
+
+       char *p;
+       fstring reqpath;
+
+       if (!dp || !conn) {
+               DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL tcon_context!\n"));
+               return False;
+       }
+
+       if (dp->reqpath[0] == '\0') {
+               if (self_referralp) {
+                       DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
+                       *self_referralp = True;
+               }
+               return False;
+       }
+
+       /* check if need to redirect */
+       fstrcpy(localpath, conn->connectpath);
+       fstrcat(localpath, "/");
+       fstrcat(localpath, dp->reqpath);
+       if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL))
+       {
+               if (findfirst_flag) {
+                       DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
+                                "for dfs link %s.\n", dfspath));
+                       return False;
+               } else {                
+                       DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
+                                dfspath));
+                       if (consumedcntp) 
+                               *consumedcntp = strlen(dfspath);
+                       return True;
+               }
+       } 
+
+       /* also redirect if the parent directory is a dfs link */
+       fstrcpy(reqpath, dp->reqpath);
+       p = strrchr(reqpath, '/');
+       if (p) {
+               *p = '\0';
+               fstrcpy(localpath, conn->connectpath);
+               fstrcat(localpath, "/");
+               fstrcat(localpath, reqpath);
+               if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
+                       DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
+
+                       /* To find the path consumed, we truncate the original
+                          DFS pathname passed to use to remove the last
+                          component. The length of the resulting string is
+                          the path consumed 
+                       */
+                       if (consumedcntp) {
+                               char *q;
+                               pstring buf;
+                               pstrcpy(buf, dfspath);
+                               trim_string(buf, NULL, "\\");
+                               q = strrchr(buf, '\\');
+                               if (q) 
+                                       *q = '\0';
+                               *consumedcntp = strlen(buf);
+                               DEBUG(10, ("resolve_dfs_path: Path consumed: %d\n", *consumedcntp));
+                       }
+                       
+                       return True;
+               }
+       }
+       
+       return False;
+}
+
+/*****************************************************************
+  Decides if a dfs pathname should be redirected or not.
+  If not, the pathname is converted to a tcon-relative local unix path
+*****************************************************************/
+BOOL dfs_redirect(const char *pathname, struct tcon_context *conn,
+                 BOOL findfirst_flag)
+{
+       struct dfs_path dp;
+       
+       if (!conn || !pathname)
+               return False;
+
+       parse_dfs_path(pathname, &dp);
+
+       /* if dfs pathname for a non-dfs share, convert to tcon-relative
+          path and return false */
+       if (!lp_msdfs_root(SNUM(conn))) {
+               fstrcpy(pathname, dp.reqpath);
+               return False;
+       }
+       
+       if (strcasecmp(dp.servicename, lp_servicename(SNUM(conn)) ) != 0) 
+               return False;
+
+       if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
+                            NULL, NULL, NULL, NULL)) {
+               DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
+               return True;
+       } else {
+               DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
+               
+               /* Form non-dfs tcon-relative path */
+               fstrcpy(pathname, dp.reqpath);
+               DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
+                        pathname));
+               return False;
+       }
+       /* never reached */
+       return False;
+}
+
+/**********************************************************************
+ Gets valid referrals for a dfs path and fills up the
+ junction_map structure
+ **********************************************************************/
+BOOL get_referred_path(char *pathname, struct junction_map* jn,
+                      int* consumedcntp, BOOL* self_referralp)
+{
+       struct dfs_path dp;
+
+       struct tcon_context conns;
+       struct tcon_context *conn = &conns;
+       pstring conn_path;
+       int snum;
+
+       BOOL self_referral = False;
+
+       if (!pathname || !jn)
+               return False;
+
+       if (self_referralp)
+               *self_referralp = False;
+       else
+               self_referralp = &self_referral;
+
+       parse_dfs_path(pathname, &dp);
+
+       /* Verify hostname in path */
+       if (local_machine && (strcasecmp(local_machine, dp.hostname) != 0)) {
+
+          /* Hostname mismatch, check if one of our IP addresses */
+          if (!ismyip(*interpret_addr2(dp.hostname))) {
+               
+               DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
+                         dp.hostname, pathname));
+               return False;
+          }
+       }
+
+       pstrcpy(jn->service_name, dp.servicename);
+       pstrcpy(jn->volume_name, dp.reqpath);
+
+       /* Verify the share is a dfs root */
+       snum = lp_servicenumber(jn->service_name);
+       if(snum < 0) {
+               if ((snum = find_service(jn->service_name)) < 0)
+                       return False;
+       }
+       
+       pstrcpy(conn_path, lp_pathname(snum));
+       if (!create_conn_struct(conn, snum, conn_path))
+               return False;
+       
+       if (!lp_msdfs_root(SNUM(conn))) {
+               DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
+                        dp.servicename, pathname));
+               return False;
+       }
+
+       if (*lp_msdfs_proxy(snum) != '\0') {
+               struct referral* ref;
+               jn->referral_count = 1;
+               if ((ref = (struct referral*) malloc(sizeof(struct referral)))
+                   == NULL) {
+                       DEBUG(0, ("malloc failed for referral\n"));
+                       return False;
+               }
+
+               pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+               if (dp.reqpath[0] != '\0')
+                       pstrcat(ref->alternate_path, dp.reqpath);
+               ref->proximity = 0;
+               ref->ttl = REFERRAL_TTL;
+               jn->referral_list = ref;
+               if (consumedcntp)
+                       *consumedcntp = strlen(pathname);
+               return True;
+       }
+
+       /* If not remote & not a self referral, return False */
+       if (!resolve_dfs_path(pathname, &dp, conn, False, 
+                             &jn->referral_list, &jn->referral_count,
+                             self_referralp, consumedcntp)) {
+               if (!*self_referralp) {
+                       DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
+                       return False;
+               }
+       }
+       
+       /* if self_referral, fill up the junction map */
+       if (*self_referralp) {
+               struct referral* ref;
+               jn->referral_count = 1;
+               if((ref = (struct referral*) malloc(sizeof(struct referral)))
+                  == NULL) {
+                       DEBUG(0,("malloc failed for referral\n"));
+                       return False;
+               }
+      
+               pstrcpy(ref->alternate_path,pathname);
+               ref->proximity = 0;
+               ref->ttl = REFERRAL_TTL;
+               jn->referral_list = ref;
+               if (consumedcntp)
+                       *consumedcntp = strlen(pathname);
+       }
+
+       return True;
+}
+
+static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
+                                  struct junction_map* junction,
+                                  int consumedcnt,
+                                  BOOL self_referral)
+{
+       char* pdata = *ppdata;
+
+       unsigned char uni_requestedpath[1024];
+       int uni_reqpathoffset1,uni_reqpathoffset2;
+       int uni_curroffset;
+       int requestedpathlen=0;
+       int offset;
+       int reply_size = 0;
+       int i=0;
+
+       DEBUG(10,("setting up version2 referral\nRequested path:\n"));
+
+       requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
+                                      STR_TERMINATE);
+
+       dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
+
+       DEBUG(10,("ref count = %u\n",junction->referral_count));
+
+       uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
+                       VERSION2_REFERRAL_SIZE * junction->referral_count;
+
+       uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
+
+       uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
+
+       reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
+                                       2 * requestedpathlen;
+       DEBUG(10,("reply_size: %u\n",reply_size));
+
+       /* add up the unicode lengths of all the referral paths */
+       for(i=0;i<junction->referral_count;i++) {
+               DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+               reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+       }
+
+       DEBUG(10,("reply_size = %u\n",reply_size));
+       /* add the unexplained 0x16 bytes */
+       reply_size += 0x16;
+
+       pdata = Realloc(pdata,reply_size);
+       if(pdata == NULL) {
+               DEBUG(0,("malloc failed for Realloc!\n"));
+               return -1;
+       } else
+               *ppdata = pdata;
+
+       /* copy in the dfs requested paths.. required for offset calculations */
+       memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
+       memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
+
+       /* create the header */
+       SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+       SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
+       if(self_referral)
+               SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
+       else
+               SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+
+       offset = 8;
+       /* add the referral elements */
+       for(i=0;i<junction->referral_count;i++) {
+               struct referral* ref = &(junction->referral_list[i]);
+               int unilen;
+
+               SSVAL(pdata,offset,2); /* version 2 */
+               SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
+               if(self_referral)
+                       SSVAL(pdata,offset+4,1);
+               else
+                       SSVAL(pdata,offset+4,0);
+               SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+               SIVAL(pdata,offset+8,ref->proximity);
+               SIVAL(pdata,offset+12,ref->ttl);
+
+               SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
+               SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
+               /* copy referred path into current offset */
+               unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
+                                    -1, STR_UNICODE);
+
+               SSVAL(pdata,offset+20,uni_curroffset-offset);
+
+               uni_curroffset += unilen;
+               offset += VERSION2_REFERRAL_SIZE;
+       }
+       /* add in the unexplained 22 (0x16) bytes at the end */
+       memset(pdata+uni_curroffset,'\0',0x16);
+       return reply_size;
+}
+
+static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
+                                  struct junction_map* junction,
+                                  int consumedcnt,
+                                  BOOL self_referral)
+{
+       char* pdata = *ppdata;
+
+       unsigned char uni_reqpath[1024];
+       int uni_reqpathoffset1, uni_reqpathoffset2;
+       int uni_curroffset;
+       int reply_size = 0;
+
+       int reqpathlen = 0;
+       int offset,i=0;
+       
+       DEBUG(10,("setting up version3 referral\n"));
+
+       reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
+       
+       dump_data(10, (char *) uni_reqpath,reqpathlen);
+
+       uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
+       uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
+       reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
+
+       for(i=0;i<junction->referral_count;i++) {
+               DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+               reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+       }
+
+       pdata = Realloc(pdata,reply_size);
+       if(pdata == NULL) {
+               DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
+               return -1;
+       } else
+               *ppdata = pdata;
+
+       /* create the header */
+       SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+       SSVAL(pdata,2,junction->referral_count); /* number of referral */
+       if(self_referral)
+               SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
+       else
+               SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+       
+       /* copy in the reqpaths */
+       memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
+       memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
+       
+       offset = 8;
+       for(i=0;i<junction->referral_count;i++) {
+               struct referral* ref = &(junction->referral_list[i]);
+               int unilen;
+
+               SSVAL(pdata,offset,3); /* version 3 */
+               SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
+               if(self_referral)
+                       SSVAL(pdata,offset+4,1);
+               else
+                       SSVAL(pdata,offset+4,0);
+
+               SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+               SIVAL(pdata,offset+8,ref->ttl);
+           
+               SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
+               SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
+               /* copy referred path into current offset */
+               unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
+                                    -1, STR_UNICODE | STR_TERMINATE);
+               SSVAL(pdata,offset+16,uni_curroffset-offset);
+               /* copy 0x10 bytes of 00's in the ServiceSite GUID */
+               memset(pdata+offset+18,'\0',16);
+
+               uni_curroffset += unilen;
+               offset += VERSION3_REFERRAL_SIZE;
+       }
+       return reply_size;
+}
+
+/******************************************************************
+ * Set up the Dfs referral for the dfs pathname
+ ******************************************************************/
+
+int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
+{
+       struct junction_map junction;
+       int consumedcnt;
+       BOOL self_referral = False;
+       pstring buf;
+       int reply_size = 0;
+       char *pathnamep = pathname;
+
+       ZERO_STRUCT(junction);
+
+       /* get the junction entry */
+       if (!pathnamep)
+               return -1;
+
+       /* Trim pathname sent by client so it begins with only one backslash.
+          Two backslashes confuse some dfs clients
+        */
+       while (strlen(pathnamep) > 1 && pathnamep[0] == '\\'
+              && pathnamep[1] == '\\')
+               pathnamep++;
+
+       pstrcpy(buf, pathnamep);
+       if (!get_referred_path(buf, &junction, &consumedcnt,
+                              &self_referral))
+               return -1;
+       
+       if (!self_referral)
+       {
+               pathnamep[consumedcnt] = '\0';
+
+               if( DEBUGLVL( 3 ) ) {
+                       int i=0;
+                       dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
+                       for(i=0;i<junction.referral_count;i++)
+                               dbgtext(" %s",junction.referral_list[i].alternate_path);
+                       dbgtext(".\n");
+               }
+       }
+
+       /* create the referral depeding on version */
+       DEBUG(10,("max_referral_level :%d\n",max_referral_level));
+       if(max_referral_level<2 || max_referral_level>3)
+               max_referral_level = 2;
+
+       switch(max_referral_level) {
+       case 2:
+               {
+               reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
+                                                    consumedcnt, self_referral);
+               SAFE_FREE(junction.referral_list);
+               break;
+               }
+       case 3:
+               {
+               reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
+                                                    consumedcnt, self_referral);
+               SAFE_FREE(junction.referral_list);
+               break;
+               }
+       default:
+               {
+               DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
+               return -1;
+               }
+       }
+      
+       DEBUG(10,("DFS Referral pdata:\n"));
+       dump_data(10,*ppdata,reply_size);
+       return reply_size;
+}
+
+/**********************************************************************
+ The following functions are called by the NETDFS RPC pipe functions
+ **********************************************************************/
+
+/**********************************************************************
+ Creates a junction structure from a Dfs pathname
+ **********************************************************************/
+BOOL create_junction(char* pathname, struct junction_map* jn)
+{
+        struct dfs_path dp;
+        parse_dfs_path(pathname,&dp);
+
+        /* check if path is dfs : validate first token */
+        if (local_machine && (strcasecmp(local_machine,dp.hostname)!=0)) {
+           
+          /* Hostname mismatch, check if one of our IP addresses */
+          if (!ismyip(*interpret_addr2(dp.hostname))) {
+                DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
+                        dp.hostname, pathname));
+                return False;
+          }
+        }
+
+        /* Check for a non-DFS share */
+        if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
+                DEBUG(4,("create_junction: %s is not an msdfs root.\n", 
+                        dp.servicename));
+                return False;
+        }
+
+        pstrcpy(jn->service_name,dp.servicename);
+        pstrcpy(jn->volume_name,dp.reqpath);
+        return True;
+}
+
+/**********************************************************************
+ Forms a valid Unix pathname from the junction 
+ **********************************************************************/
+
+static BOOL junction_to_local_path(struct junction_map* jn, char* path,
+                                  int max_pathlen, struct tcon_context *conn)
+{
+       int snum;
+       pstring conn_path;
+
+       if(!path || !jn)
+               return False;
+
+       snum = lp_servicenumber(jn->service_name);
+       if(snum < 0)
+               return False;
+
+       safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
+       safe_strcat(path, "/", max_pathlen-1);
+       strlower(jn->volume_name);
+       safe_strcat(path, jn->volume_name, max_pathlen-1);
+
+       pstrcpy(conn_path, lp_pathname(snum));
+       if (!create_conn_struct(conn, snum, conn_path))
+               return False;
+
+       return True;
+}
+
+BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
+{
+       pstring path;
+       pstring msdfs_link;
+       struct tcon_context conns;
+       struct tcon_context *conn = &conns;
+       int i=0;
+       BOOL insert_comma = False;
+
+       if(!junction_to_local_path(jn, path, sizeof(path), conn))
+               return False;
+  
+       /* form the msdfs_link contents */
+       pstrcpy(msdfs_link, "msdfs:");
+       for(i=0; i<jn->referral_count; i++) {
+               char* refpath = jn->referral_list[i].alternate_path;
+      
+               trim_string(refpath, "\\", "\\");
+               if(*refpath == '\0') {
+                       if (i == 0)
+                               insert_comma = False;
+                       continue;
+               }
+               if (i > 0 && insert_comma)
+                       pstrcat(msdfs_link, ",");
+
+               pstrcat(msdfs_link, refpath);
+               if (!insert_comma)
+                       insert_comma = True;
+               
+       }
+
+       DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
+
+       if(exists)
+               if(conn->vfs_ops.unlink(conn,path)!=0)
+                       return False;
+
+       if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) {
+               DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
+                               path, msdfs_link, strerror(errno)));
+               return False;
+       }
+       return True;
+}
+
+BOOL remove_msdfs_link(struct junction_map* jn)
+{
+       pstring path;
+       struct tcon_context conns;
+       struct tcon_context *conn = &conns;
+
+       if(!junction_to_local_path(jn, path, sizeof(path), conn))
+               return False;
+     
+       if(conn->vfs_ops.unlink(conn, path)!=0)
+               return False;
+  
+       return True;
+}
+
+static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
+{
+       int cnt = *jn_count;
+       DIR *dirp;
+       char* dname;
+       pstring connect_path;
+       char* service_name = lp_servicename(snum);
+       struct tcon_context conns;
+       struct tcon_context *conn = &conns;
+       struct referral *ref = NULL;
+       pstrcpy(connect_path,lp_pathname(snum));
+
+       if(*connect_path == '\0')
+               return False;
+
+       /*
+        * Fake up a connection struct for the VFS layer.
+        */
+
+       if (!create_conn_struct(conn, snum, connect_path))
+               return False;
+
+       /* form a junction for the msdfs root - convention 
+          DO NOT REMOVE THIS: NT clients will not work with us
+          if this is not present
+       */ 
+       pstrcpy(jn[cnt].service_name, service_name);
+       jn[cnt].volume_name[0] = '\0';
+       jn[cnt].referral_count = 1;
+
+       ref = jn[cnt].referral_list
+               = (struct referral*) malloc(sizeof(struct referral));
+       if (jn[cnt].referral_list == NULL) {
+               DEBUG(0, ("Malloc failed!\n"));
+               return False;
+       }
+
+       ref->proximity = 0;
+       ref->ttl = REFERRAL_TTL;
+       if (*lp_msdfs_proxy(snum) != '\0') {
+               pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+               *jn_count = ++cnt;
+               return True;
+       }
+               
+       slprintf(ref->alternate_path, sizeof(pstring)-1,
+                "\\\\%s\\%s", local_machine, service_name);
+       cnt++;
+       
+       /* Now enumerate all dfs links */
+       dirp = conn->vfs_ops.opendir(conn, connect_path);
+       if(!dirp)
+               return False;
+
+       while((dname = vfs_readdirname(conn, dirp)) != NULL) {
+               pstring pathreal;
+
+               pstrcpy(pathreal, connect_path);
+               pstrcat(pathreal, "/");
+               pstrcat(pathreal, dname);
+               if (is_msdfs_link(conn, pathreal, &(jn[cnt].referral_list),
+                                 &(jn[cnt].referral_count), NULL)) {
+                       pstrcpy(jn[cnt].service_name, service_name);
+                       pstrcpy(jn[cnt].volume_name, dname);
+                       cnt++;
+               }
+       }
+       
+       conn->vfs_ops.closedir(conn,dirp);
+       *jn_count = cnt;
+       return True;
+}
+
+int enum_msdfs_links(struct junction_map* jn)
+{
+       int i=0;
+       int jn_count = 0;
+
+       if(!lp_host_msdfs())
+               return -1;
+
+       for(i=0;*lp_servicename(i);i++) {
+               if(lp_msdfs_root(i)) 
+                       form_junctions(i,jn,&jn_count);
+       }
+       return jn_count;
+}
+
diff --git a/source4/nmbd/.cvsignore b/source4/nmbd/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/nmbd/asyncdns.c b/source4/nmbd/asyncdns.c
new file mode 100644 (file)
index 0000000..c86ee69
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+   Unix SMB/CIFS implementation.
+   a async DNS handler
+   Copyright (C) Andrew Tridgell 1997-1998
+   
+   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"
+
+/***************************************************************************
+  Add a DNS result to the name cache.
+****************************************************************************/
+
+static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
+{
+  int name_type = question->name_type;
+  char *qname = question->name;
+  
+  
+  if (!addr.s_addr) {
+    /* add the fail to WINS cache of names. give it 1 hour in the cache */
+    DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
+    (void)add_name_to_subnet( wins_server_subnet, qname, name_type,
+                              NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr );
+    return( NULL );
+  }
+
+  /* add it to our WINS cache of names. give it 2 hours in the cache */
+  DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
+
+  return( add_name_to_subnet( wins_server_subnet, qname, name_type,
+                              NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr ) );
+}
+
+
+
+#ifndef SYNC_DNS
+
+static int fd_in = -1, fd_out = -1;
+static pid_t child_pid = -1;
+static int in_dns;
+
+/* this is the structure that is passed between the parent and child */
+struct query_record {
+       struct nmb_name name;
+       struct in_addr result;
+};
+
+/* a queue of pending requests waiting to be sent to the DNS child */
+static struct packet_struct *dns_queue;
+
+/* the packet currently being processed by the dns child */
+static struct packet_struct *dns_current;
+
+
+/***************************************************************************
+  return the fd used to gather async dns replies. This is added to the select
+  loop
+  ****************************************************************************/
+int asyncdns_fd(void)
+{
+       return fd_in;
+}
+
+/***************************************************************************
+  handle DNS queries arriving from the parent
+  ****************************************************************************/
+static void asyncdns_process(void)
+{
+       struct query_record r;
+       fstring qname;
+
+       DEBUGLEVEL = -1;
+
+       while (1) {
+               if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r)) 
+                       break;
+
+               fstrcpy(qname, r.name.name);
+
+               r.result.s_addr = interpret_addr(qname);
+
+               if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r))
+                       break;
+       }
+
+       _exit(0);
+}
+
+/**************************************************************************** **
+  catch a sigterm (in the child process - the parent has a different handler
+  see nmbd.c for details).
+  We need a separate term handler here so we don't release any 
+  names that our parent is going to release, or overwrite a 
+  WINS db that our parent is going to write.
+ **************************************************************************** */
+
+static void sig_term(int sig)
+{
+  _exit(0);
+}
+
+/***************************************************************************
+ Called by the parent process when it receives a SIGTERM - also kills the
+ child so we don't get child async dns processes lying around, causing trouble.
+  ****************************************************************************/
+
+void kill_async_dns_child(void)
+{
+       if (child_pid > 0) {
+               kill(child_pid, SIGTERM);
+               child_pid = -1;
+       }
+}
+
+/***************************************************************************
+  create a child process to handle DNS lookups
+  ****************************************************************************/
+void start_async_dns(void)
+{
+       int fd1[2], fd2[2];
+
+       CatchChild();
+
+       if (pipe(fd1) || pipe(fd2)) {
+               DEBUG(0,("can't create asyncdns pipes\n"));
+               return;
+       }
+
+       child_pid = sys_fork();
+
+       if (child_pid) {
+               fd_in = fd1[0];
+               fd_out = fd2[1];
+               close(fd1[1]);
+               close(fd2[0]);
+               DEBUG(0,("started asyncdns process %d\n", (int)child_pid));
+               return;
+       }
+
+       fd_in = fd2[0];
+       fd_out = fd1[1];
+
+       CatchSignal(SIGUSR2, SIG_IGN);
+       CatchSignal(SIGUSR1, SIG_IGN);
+       CatchSignal(SIGHUP, SIG_IGN);
+        CatchSignal(SIGTERM, SIGNAL_CAST sig_term );
+
+       asyncdns_process();
+}
+
+
+/***************************************************************************
+check if a particular name is already being queried
+  ****************************************************************************/
+static BOOL query_current(struct query_record *r)
+{
+       return dns_current &&
+               nmb_name_equal(&r->name, 
+                          &dns_current->packet.nmb.question.question_name);
+}
+
+
+/***************************************************************************
+  write a query to the child process
+  ****************************************************************************/
+static BOOL write_child(struct packet_struct *p)
+{
+       struct query_record r;
+
+       r.name = p->packet.nmb.question.question_name;
+
+       return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r);
+}
+
+/***************************************************************************
+  check the DNS queue
+  ****************************************************************************/
+void run_dns_queue(void)
+{
+       struct query_record r;
+       struct packet_struct *p, *p2;
+       struct name_record *namerec;
+       int size;
+
+       if (fd_in == -1)
+               return;
+
+        /* Allow SIGTERM to kill us. */
+        BlockSignals(False, SIGTERM);
+
+       if (!process_exists(child_pid)) {
+               close(fd_in);
+               start_async_dns();
+       }
+
+       if ((size=read_data(fd_in, (char *)&r, sizeof(r))) != sizeof(r)) {
+               if (size) {
+                       DEBUG(0,("Incomplete DNS answer from child!\n"));
+                       fd_in = -1;
+               }
+                BlockSignals(True, SIGTERM);
+               return;
+       }
+
+        BlockSignals(True, SIGTERM);
+
+       namerec = add_dns_result(&r.name, r.result);
+
+       if (dns_current) {
+               if (query_current(&r)) {
+                       DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+                       in_dns = 1;
+                        if(namerec == NULL)
+                          send_wins_name_query_response(NAM_ERR, dns_current, NULL);
+                        else
+                         send_wins_name_query_response(0,dns_current,namerec);
+                       in_dns = 0;
+               }
+
+               dns_current->locked = False;
+               free_packet(dns_current);
+               dns_current = NULL;
+       }
+
+       /* loop over the whole dns queue looking for entries that
+          match the result we just got */
+       for (p = dns_queue; p;) {
+               struct nmb_packet *nmb = &p->packet.nmb;
+               struct nmb_name *question = &nmb->question.question_name;
+
+               if (nmb_name_equal(question, &r.name)) {
+                       DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+                       in_dns = 1;
+                        if(namerec == NULL)
+                         send_wins_name_query_response(NAM_ERR, p, NULL);
+                        else
+                          send_wins_name_query_response(0,p,namerec);
+                       in_dns = 0;
+                       p->locked = False;
+
+                       if (p->prev)
+                               p->prev->next = p->next;
+                       else
+                               dns_queue = p->next;
+                       if (p->next)
+                               p->next->prev = p->prev;
+                       p2 = p->next;
+                       free_packet(p);
+                       p = p2;
+               } else {
+                       p = p->next;
+               }
+       }
+
+       if (dns_queue) {
+               dns_current = dns_queue;
+               dns_queue = dns_queue->next;
+               if (dns_queue) dns_queue->prev = NULL;
+               dns_current->next = NULL;
+
+               if (!write_child(dns_current)) {
+                       DEBUG(3,("failed to send DNS query to child!\n"));
+                       return;
+               }
+       }
+
+}
+
+/***************************************************************************
+queue a DNS query
+  ****************************************************************************/
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+                    struct name_record **n)
+{
+       if (in_dns || fd_in == -1)
+               return False;
+
+       if (!dns_current) {
+               if (!write_child(p)) {
+                       DEBUG(3,("failed to send DNS query to child!\n"));
+                       return False;
+               }
+               dns_current = p;
+               p->locked = True;
+       } else {
+               p->locked = True;
+               p->next = dns_queue;
+               p->prev = NULL;
+               if (p->next)
+                       p->next->prev = p;
+               dns_queue = p;
+       }
+
+       DEBUG(3,("added DNS query for %s\n", nmb_namestr(question)));
+       return True;
+}
+
+#else
+
+
+/***************************************************************************
+  we use this when we can't do async DNS lookups
+  ****************************************************************************/
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+                    struct name_record **n)
+{
+       char *qname = question->name;
+       struct in_addr dns_ip;
+
+       DEBUG(3,("DNS search for %s - ", nmb_namestr(question)));
+
+        /* Unblock TERM signal so we can be killed in DNS lookup. */
+        BlockSignals(False, SIGTERM);
+
+       dns_ip.s_addr = interpret_addr(qname);
+
+        /* Re-block TERM signal. */
+        BlockSignals(True, SIGTERM);
+
+       *n = add_dns_result(question, dns_ip);
+        if(*n == NULL)
+          send_wins_name_query_response(NAM_ERR, p, NULL);
+        else
+          send_wins_name_query_response(0, p, *n);
+       return False;
+}
+
+/***************************************************************************
+ With sync dns there is no child to kill on SIGTERM.
+  ****************************************************************************/
+void kill_async_dns_child(void)
+{
+  return;
+}
+#endif
diff --git a/source4/nmbd/nmbd.c b/source4/nmbd/nmbd.c
new file mode 100644 (file)
index 0000000..2b7d803
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Jeremy Allison 1997-2002
+   Copyright (C) Jelmer Vernooij 2002 (Conversion to popt)
+   
+   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"
+
+int ClientNMB       = -1;
+int ClientDGRAM     = -1;
+int global_nmb_port = -1;
+
+extern BOOL global_in_nmbd;
+
+/* are we running as a daemon ? */
+static BOOL is_daemon = False;
+
+/* fork or run in foreground ? */
+static BOOL Fork = True;
+
+/* log to standard output ? */
+static BOOL log_stdout = False;
+
+/* have we found LanMan clients yet? */
+BOOL found_lm_clients = False;
+
+/* what server type are we currently */
+
+time_t StartupTime = 0;
+
+/**************************************************************************** **
+ Handle a SIGTERM in band.
+ **************************************************************************** */
+
+static void terminate(void)
+{
+       DEBUG(0,("Got SIGTERM: going down...\n"));
+  
+       /* Write out wins.dat file if samba is a WINS server */
+       wins_write_database(False);
+  
+       /* Remove all SELF registered names from WINS */
+       release_wins_names();
+  
+       /* Announce all server entries as 0 time-to-live, 0 type. */
+       announce_my_servers_removed();
+
+       /* If there was an async dns child - kill it. */
+       kill_async_dns_child();
+
+       exit(0);
+}
+
+/**************************************************************************** **
+ Handle a SHUTDOWN message from smbcontrol.
+ **************************************************************************** */
+
+static void nmbd_terminate(int msg_type, pid_t src, void *buf, size_t len)
+{
+       terminate();
+}
+
+/**************************************************************************** **
+ Catch a SIGTERM signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T got_sig_term;
+
+static void sig_term(int sig)
+{
+       got_sig_term = 1;
+       sys_select_signal();
+}
+
+/**************************************************************************** **
+ Catch a SIGHUP signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T reload_after_sighup;
+
+static void sig_hup(int sig)
+{
+       reload_after_sighup = 1;
+       sys_select_signal();
+}
+
+/*******************************************************************
+ Print out all talloc memory info.
+********************************************************************/
+
+void return_all_talloc_info(int msg_type, pid_t src_pid, void *buf, size_t len)
+{
+       TALLOC_CTX *ctx = talloc_init("info context");
+       char *info = NULL;
+
+       if (!ctx)
+               return;
+
+       info = talloc_describe_all(ctx);
+       if (info)
+               DEBUG(10,(info));
+       message_send_pid(src_pid, MSG_TALLOC_USAGE, info, info ? strlen(info) + 1 : 0, True);
+       talloc_destroy(ctx);
+}
+
+#if DUMP_CORE
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+       char *p;
+       pstring dname;
+       pstrcpy( dname, lp_logfile() );
+       if ((p=strrchr_m(dname,'/')))
+               *p=0;
+       pstrcat( dname, "/corefiles" );
+       mkdir( dname, 0700 );
+       sys_chown( dname, getuid(), getgid() );
+       chmod( dname, 0700 );
+       if ( chdir(dname) )
+               return( False );
+       umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+       {
+               struct rlimit rlp;
+               getrlimit( RLIMIT_CORE, &rlp );
+               rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+               setrlimit( RLIMIT_CORE, &rlp );
+               getrlimit( RLIMIT_CORE, &rlp );
+               DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+       }
+#endif
+#endif
+
+
+       DEBUG(0,("Dumping core in %s\n",dname));
+       abort();
+       return( True );
+}
+#endif
+
+/**************************************************************************** **
+ Possibly continue after a fault.
+ **************************************************************************** */
+
+static void fault_continue(void)
+{
+#if DUMP_CORE
+       dump_core();
+#endif
+}
+
+/**************************************************************************** **
+ Expire old names from the namelist and server list.
+ **************************************************************************** */
+
+static void expire_names_and_servers(time_t t)
+{
+       static time_t lastrun = 0;
+  
+       if ( !lastrun )
+               lastrun = t;
+       if ( t < (lastrun + 5) )
+               return;
+       lastrun = t;
+
+       /*
+        * Expire any timed out names on all the broadcast
+        * subnets and those registered with the WINS server.
+        * (nmbd_namelistdb.c)
+        */
+
+       expire_names(t);
+
+       /*
+        * Go through all the broadcast subnets and for each
+        * workgroup known on that subnet remove any expired
+        * server names. If a workgroup has an empty serverlist
+        * and has itself timed out then remove the workgroup.
+        * (nmbd_workgroupdb.c)
+        */
+
+       expire_workgroups_and_servers(t);
+}
+
+/************************************************************************** **
+ Reload the list of network interfaces.
+ ************************************************************************** */
+
+static BOOL reload_interfaces(time_t t)
+{
+       static time_t lastt;
+       int n;
+       struct subnet_record *subrec;
+       extern BOOL rescan_listen_set;
+       extern struct in_addr loopback_ip;
+
+       if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) return False;
+       lastt = t;
+
+       if (!interfaces_changed()) return False;
+
+       /* the list of probed interfaces has changed, we may need to add/remove
+          some subnets */
+       load_interfaces();
+
+       /* find any interfaces that need adding */
+       for (n=iface_count() - 1; n >= 0; n--) {
+               struct interface *iface = get_interface(n);
+
+               /*
+                * We don't want to add a loopback interface, in case
+                * someone has added 127.0.0.1 for smbd, nmbd needs to
+                * ignore it here. JRA.
+                */
+
+               if (ip_equal(iface->ip, loopback_ip)) {
+                       DEBUG(2,("reload_interfaces: Ignoring loopback interface %s\n", inet_ntoa(iface->ip)));
+                       continue;
+               }
+
+               for (subrec=subnetlist; subrec; subrec=subrec->next) {
+                       if (ip_equal(iface->ip, subrec->myip) &&
+                           ip_equal(iface->nmask, subrec->mask_ip)) break;
+               }
+
+               if (!subrec) {
+                       /* it wasn't found! add it */
+                       DEBUG(2,("Found new interface %s\n", 
+                                inet_ntoa(iface->ip)));
+                       subrec = make_normal_subnet(iface);
+                       if (subrec) register_my_workgroup_one_subnet(subrec);
+               }
+       }
+
+       /* find any interfaces that need deleting */
+       for (subrec=subnetlist; subrec; subrec=subrec->next) {
+               for (n=iface_count() - 1; n >= 0; n--) {
+                       struct interface *iface = get_interface(n);
+                       if (ip_equal(iface->ip, subrec->myip) &&
+                           ip_equal(iface->nmask, subrec->mask_ip)) break;
+               }
+               if (n == -1) {
+                       /* oops, an interface has disapeared. This is
+                        tricky, we don't dare actually free the
+                        interface as it could be being used, so
+                        instead we just wear the memory leak and
+                        remove it from the list of interfaces without
+                        freeing it */
+                       DEBUG(2,("Deleting dead interface %s\n", 
+                                inet_ntoa(subrec->myip)));
+                       close_subnet(subrec);
+               }
+       }
+       
+       rescan_listen_set = True;
+
+       /* We need to shutdown if there are no subnets... */
+       if (FIRST_SUBNET == NULL) {
+               DEBUG(0,("reload_interfaces: No subnets to listen to. Shutting down...\n"));
+               return True;
+       }
+       return False;
+}
+
+/**************************************************************************** **
+ Reload the services file.
+ **************************************************************************** */
+
+static BOOL reload_nmbd_services(BOOL test)
+{
+       BOOL ret;
+
+       set_remote_machine_name("nmbd");
+
+       if ( lp_loaded() ) {
+               pstring fname;
+               pstrcpy( fname,lp_configfile());
+               if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+                       pstrcpy(dyn_CONFIGFILE,fname);
+                       test = False;
+               }
+       }
+
+       if ( test && !lp_file_list_changed() )
+               return(True);
+
+       ret = lp_load( dyn_CONFIGFILE, True , False, False);
+
+       /* perhaps the config filename is now set */
+       if ( !test ) {
+               DEBUG( 3, ( "services not loaded\n" ) );
+               reload_nmbd_services( True );
+       }
+
+       return(ret);
+}
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+
+static void process(void)
+{
+       BOOL run_election;
+
+       while( True ) {
+               time_t t = time(NULL);
+
+               /* Check for internal messages */
+
+               message_dispatch();
+
+               /*
+                * Check all broadcast subnets to see if
+                * we need to run an election on any of them.
+                * (nmbd_elections.c)
+                */
+
+               run_election = check_elections();
+
+               /*
+                * Read incoming UDP packets.
+                * (nmbd_packets.c)
+                */
+
+               if(listen_for_packets(run_election))
+                       return;
+
+               /*
+                * Handle termination inband.
+                */
+
+               if (got_sig_term) {
+                       got_sig_term = 0;
+                       terminate();
+               }
+
+               /*
+                * Process all incoming packets
+                * read above. This calls the success and
+                * failure functions registered when response
+                * packets arrrive, and also deals with request
+                * packets from other sources.
+                * (nmbd_packets.c)
+                */
+
+               run_packet_queue();
+
+               /*
+                * Run any elections - initiate becoming
+                * a local master browser if we have won.
+                * (nmbd_elections.c)
+                */
+
+               run_elections(t);
+
+               /*
+                * Send out any broadcast announcements
+                * of our server names. This also announces
+                * the workgroup name if we are a local
+                * master browser.
+                * (nmbd_sendannounce.c)
+                */
+
+               announce_my_server_names(t);
+
+               /*
+                * Send out any LanMan broadcast announcements
+                * of our server names.
+                * (nmbd_sendannounce.c)
+                */
+
+               announce_my_lm_server_names(t);
+
+               /*
+                * If we are a local master browser, periodically
+                * announce ourselves to the domain master browser.
+                * This also deals with syncronising the domain master
+                * browser server lists with ourselves as a local
+                * master browser.
+                * (nmbd_sendannounce.c)
+                */
+
+               announce_myself_to_domain_master_browser(t);
+
+               /*
+                * Fullfill any remote announce requests.
+                * (nmbd_sendannounce.c)
+                */
+
+               announce_remote(t);
+
+               /*
+                * Fullfill any remote browse sync announce requests.
+                * (nmbd_sendannounce.c)
+                */
+
+               browse_sync_remote(t);
+
+               /*
+                * Scan the broadcast subnets, and WINS client
+                * namelists and refresh any that need refreshing.
+                * (nmbd_mynames.c)
+                */
+
+               refresh_my_names(t);
+
+               /*
+                * Scan the subnet namelists and server lists and
+                * expire thos that have timed out.
+                * (nmbd.c)
+                */
+
+               expire_names_and_servers(t);
+
+               /*
+                * Write out a snapshot of our current browse list into
+                * the browse.dat file. This is used by smbd to service
+                * incoming NetServerEnum calls - used to synchronise
+                * browse lists over subnets.
+                * (nmbd_serverlistdb.c)
+                */
+
+               write_browse_list(t, False);
+
+               /*
+                * If we are a domain master browser, we have a list of
+                * local master browsers we should synchronise browse
+                * lists with (these are added by an incoming local
+                * master browser announcement packet). Expire any of
+                * these that are no longer current, and pull the server
+                * lists from each of these known local master browsers.
+                * (nmbd_browsesync.c)
+                */
+
+               dmb_expire_and_sync_browser_lists(t);
+
+               /*
+                * Check that there is a local master browser for our
+                * workgroup for all our broadcast subnets. If one
+                * is not found, start an election (which we ourselves
+                * may or may not participate in, depending on the
+                * setting of the 'local master' parameter.
+                * (nmbd_elections.c)
+                */
+
+               check_master_browser_exists(t);
+
+               /*
+                * If we are configured as a logon server, attempt to
+                * register the special NetBIOS names to become such
+                * (WORKGROUP<1c> name) on all broadcast subnets and
+                * with the WINS server (if used). If we are configured
+                * to become a domain master browser, attempt to register
+                * the special NetBIOS name (WORKGROUP<1b> name) to
+                * become such.
+                * (nmbd_become_dmb.c)
+                */
+
+               add_domain_names(t);
+
+               /*
+                * If we are a WINS server, do any timer dependent
+                * processing required.
+                * (nmbd_winsserver.c)
+                */
+
+               initiate_wins_processing(t);
+
+               /*
+                * If we are a domain master browser, attempt to contact the
+                * WINS server to get a list of all known WORKGROUPS/DOMAINS.
+                * This will only work to a Samba WINS server.
+                * (nmbd_browsesync.c)
+                */
+
+               if (lp_enhanced_browsing())
+                       collect_all_workgroup_names_from_wins_server(t);
+
+               /*
+                * Go through the response record queue and time out or re-transmit
+                * and expired entries.
+                * (nmbd_packets.c)
+                */
+
+               retransmit_or_expire_response_records(t);
+
+               /*
+                * check to see if any remote browse sync child processes have completed
+                */
+
+               sync_check_completion();
+
+               /*
+                * regularly sync with any other DMBs we know about 
+                */
+
+               if (lp_enhanced_browsing())
+                       sync_all_dmbs(t);
+
+               /*
+                * clear the unexpected packet queue 
+                */
+
+               clear_unexpected(t);
+
+               /*
+                * Reload the services file if we got a sighup.
+                */
+
+               if(reload_after_sighup) {
+                       DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
+                       write_browse_list( 0, True );
+                       dump_all_namelists();
+                       reload_nmbd_services( True );
+                       reopen_logs();
+                       if(reload_interfaces(0))
+                               return;
+                       reload_after_sighup = 0;
+               }
+
+               /* check for new network interfaces */
+
+               if(reload_interfaces(t))
+                       return;
+
+               /* free up temp memory */
+                       lp_talloc_free();
+       }
+}
+
+/**************************************************************************** **
+ Open the socket communication.
+ **************************************************************************** */
+
+static BOOL open_sockets(BOOL isdaemon, int port)
+{
+       /*
+        * The sockets opened here will be used to receive broadcast
+        * packets *only*. Interface specific sockets are opened in
+        * make_subnet() in namedbsubnet.c. Thus we bind to the
+        * address "0.0.0.0". The parameter 'socket address' is
+        * now deprecated.
+        */
+
+       if ( isdaemon )
+               ClientNMB = open_socket_in(SOCK_DGRAM, port,0,0,True);
+       else
+               ClientNMB = 0;
+  
+       ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0,True);
+
+       if ( ClientNMB == -1 )
+               return( False );
+
+       /* we are never interested in SIGPIPE */
+       BlockSignals(True,SIGPIPE);
+
+       set_socket_options( ClientNMB,   "SO_BROADCAST" );
+       set_socket_options( ClientDGRAM, "SO_BROADCAST" );
+
+       DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
+       return( True );
+}
+
+/**************************************************************************** **
+ main program
+ **************************************************************************** */
+ int main(int argc, const char *argv[])
+{
+       static BOOL opt_interactive = False;
+       poptContext pc;
+       struct poptOption long_options[] = {
+       POPT_AUTOHELP
+       {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon(default)" },
+       {"interactive", 'i', POPT_ARG_VAL, &opt_interactive, True, "Run interactive (not a daemon)" },
+       {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+       {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+       {"hosts", 'H', POPT_ARG_STRING, dyn_LMHOSTSFILE, 'H', "Load a netbios hosts file"},
+       {"port", 'p', POPT_ARG_INT, &global_nmb_port, NMB_PORT, "Listen on the specified port" },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_socket_options },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_netbios_name },
+       {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_log_base },
+       { NULL }
+       };
+       int opt;
+       pstring logfile;
+
+  global_nmb_port = NMB_PORT;
+  global_in_nmbd = True;
+
+  StartupTime = time(NULL);
+
+  sys_srandom(time(NULL) ^ sys_getpid());
+
+  slprintf(logfile, sizeof(logfile)-1, "%s/log.nmbd", dyn_LOGFILEBASE);
+  lp_set_logfile(logfile);
+
+  fault_setup((void (*)(void *))fault_continue );
+
+  /* POSIX demands that signals are inherited. If the invoking process has
+   * these signals masked, we will have problems, as we won't recieve them. */
+  BlockSignals(False, SIGHUP);
+  BlockSignals(False, SIGUSR1);
+  BlockSignals(False, SIGTERM);
+
+  CatchSignal( SIGHUP,  SIGNAL_CAST sig_hup );
+  CatchSignal( SIGTERM, SIGNAL_CAST sig_term );
+
+#if defined(SIGFPE)
+  /* we are never interested in SIGFPE */
+  BlockSignals(True,SIGFPE);
+#endif
+
+  /* We no longer use USR2... */
+#if defined(SIGUSR2)
+  BlockSignals(True, SIGUSR2);
+#endif
+  pc = poptGetContext("nmbd", argc, argv, long_options, 0);
+  
+  while((opt = poptGetNextOpt(pc)) != -1)
+    { }
+
+  poptFreeContext(pc);
+
+  if ( opt_interactive ) {
+    Fork = False;
+    log_stdout = True;
+  }
+
+  if ( log_stdout && Fork ) {
+    DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+    exit(1);
+  }
+
+  setup_logging( argv[0], log_stdout );
+
+  reopen_logs();
+
+  DEBUG( 0, ( "Netbios nameserver version %s started.\n", VERSION ) );
+  DEBUGADD( 0, ( "Copyright Andrew Tridgell and the Samba Team 1994-2002\n" ) );
+
+  if ( !reload_nmbd_services(False) )
+    return(-1);
+
+  if(!init_names())
+    return -1;
+
+  reload_nmbd_services( True );
+
+  if (strequal(lp_workgroup(),"*"))
+  {
+    DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n"));
+    exit(1);
+  }
+
+  set_samba_nb_type();
+
+  if (!is_daemon && !is_a_socket(0))
+  {
+    DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+    is_daemon = True;
+  }
+  
+  if (is_daemon && !opt_interactive)
+  {
+    DEBUG( 2, ( "Becoming a daemon.\n" ) );
+    become_daemon(Fork);
+  }
+
+#if HAVE_SETPGID
+  /*
+   * If we're interactive we want to set our own process group for 
+   * signal management.
+   */
+  if (opt_interactive)
+    setpgid( (pid_t)0, (pid_t)0 );
+#endif
+
+#ifndef SYNC_DNS
+  /* Setup the async dns. We do it here so it doesn't have all the other
+     stuff initialised and thus chewing memory and sockets */
+  if(lp_we_are_a_wins_server() && lp_dns_proxy()) {
+         start_async_dns();
+  }
+#endif
+
+  if (!directory_exist(lp_lockdir(), NULL)) {
+         mkdir(lp_lockdir(), 0755);
+  }
+
+  pidfile_create("nmbd");
+  message_init();
+  message_register(MSG_FORCE_ELECTION, nmbd_message_election);
+  message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry);
+  message_register(MSG_SHUTDOWN, nmbd_terminate);
+  message_register(MSG_REQ_TALLOC_USAGE, return_all_talloc_info);
+
+  DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
+
+  if ( !open_sockets( is_daemon, global_nmb_port ) ) {
+    kill_async_dns_child();
+    return 1;
+  }
+
+  /* Determine all the IP addresses we have. */
+  load_interfaces();
+
+  /* Create an nmbd subnet record for each of the above. */
+  if( False == create_subnets() )
+  {
+    DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n"));
+    kill_async_dns_child();
+    exit(1);
+  }
+
+  /* Load in any static local names. */ 
+  load_lmhosts_file(dyn_LMHOSTSFILE);
+  DEBUG(3,("Loaded hosts file %s\n", dyn_LMHOSTSFILE));
+
+  /* If we are acting as a WINS server, initialise data structures. */
+  if( !initialise_wins() )
+  {
+    DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) );
+    kill_async_dns_child();
+    exit(1);
+  }
+
+  /* 
+   * Register nmbd primary workgroup and nmbd names on all
+   * the broadcast subnets, and on the WINS server (if specified).
+   * Also initiate the startup of our primary workgroup (start
+   * elections if we are setup as being able to be a local
+   * master browser.
+   */
+
+  if( False == register_my_workgroup_and_names() )
+  {
+    DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n"));
+    kill_async_dns_child();
+    exit(1);
+  }
+
+  /* We can only take signals in the select. */
+  BlockSignals( True, SIGTERM );
+
+  process();
+
+  if (dbf)
+    x_fclose(dbf);
+  kill_async_dns_child();
+  return(0);
+}
diff --git a/source4/nmbd/nmbd_become_dmb.c b/source4/nmbd/nmbd_become_dmb.c
new file mode 100644 (file)
index 0000000..d812277
--- /dev/null
@@ -0,0 +1,396 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+static void become_domain_master_browser_bcast(const char *);
+
+/****************************************************************************
+  Fail to become a Domain Master Browser on a subnet.
+  ****************************************************************************/
+
+static void become_domain_master_fail(struct subnet_record *subrec,
+                                      struct response_record *rrec,
+                                      struct nmb_name *fail_name)
+{
+  struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name);
+  struct server_record *servrec;
+
+  if(!work)
+  {
+    DEBUG(0,("become_domain_master_fail: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+    return;
+  }
+
+  /* Set the state back to DOMAIN_NONE. */
+  work->dom_state = DOMAIN_NONE;
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+            lp_netbios_name(), work->work_group, subrec->subnet_name));
+    return;
+  }
+
+  /* Update our server status. */
+  servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+       work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/****************************************************************************
+  Become a Domain Master Browser on a subnet.
+  ****************************************************************************/
+
+static void become_domain_master_stage2(struct subnet_record *subrec, 
+                                        struct userdata_struct *userdata,
+                                        struct nmb_name *registered_name,
+                                        uint16 nb_flags,
+                                        int ttl, struct in_addr registered_ip)
+{
+  struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+  struct server_record *servrec;
+
+  if(!work)
+  {
+    DEBUG(0,("become_domain_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+    return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n", 
+            lp_netbios_name(), registered_name->name, subrec->subnet_name));
+    work->dom_state = DOMAIN_NONE;
+    return;
+  }
+
+  /* Set the state in the workgroup structure. */
+  work->dom_state = DOMAIN_MST; /* Become domain master. */
+
+  /* Update our server status. */
+  servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER);
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  if( DEBUGLVL( 0 ) )
+    {
+    dbgtext( "*****\n\nSamba server %s ", lp_netbios_name() );
+    dbgtext( "is now a domain master browser for " );
+    dbgtext( "workgroup %s ", work->work_group );
+    dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+    }
+
+  if( subrec == unicast_subnet )
+  {
+    struct nmb_name nmbname;
+    struct in_addr my_first_ip;
+
+    /* Put our name and first IP address into the 
+       workgroup struct as domain master browser. This
+       will stop us syncing with ourself if we are also
+       a local master browser. */
+
+    make_nmb_name(&nmbname, lp_netbios_name(), 0x20);
+
+    work->dmb_name = nmbname;
+    /* Pick the first interface ip address as the domain master browser ip. */
+    my_first_ip = *iface_n_ip(0);
+
+    putip((char *)&work->dmb_addr, &my_first_ip);
+
+    /* We successfully registered by unicast with the
+       WINS server.  We now expect to become the domain
+       master on the local subnets. If this fails, it's
+       probably a 1.9.16p2 to 1.9.16p11 server's fault.
+
+       This is a configuration issue that should be addressed
+       by the network administrator - you shouldn't have
+       several machines configured as a domain master browser
+       for the same WINS scope (except if they are 1.9.17 or
+       greater, and you know what you're doing.
+
+       see docs/DOMAIN.txt.
+
+     */
+    become_domain_master_browser_bcast(work->work_group);
+  }
+  else
+  {
+    /*
+     * Now we are a domain master on a broadcast subnet, we need to add
+     * the WORKGROUP<1b> name to the unicast subnet so that we can answer
+     * unicast requests sent to this name. This bug wasn't found for a while
+     * as it is strange to have a DMB without using WINS. JRA.
+     */
+    insert_permanent_name_into_unicast(subrec, registered_name, nb_flags);
+  }
+}
+
+/****************************************************************************
+  Start the name registration process when becoming a Domain Master Browser
+  on a subnet.
+  ****************************************************************************/
+
+static void become_domain_master_stage1(struct subnet_record *subrec, char *wg_name)
+{ 
+  struct work_record *work;
+
+  DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \
+workgroup %s on subnet %s\n", wg_name, subrec->subnet_name));
+
+  /* First, find the workgroup on the subnet. */
+  if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL)
+  {
+    DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n",
+          wg_name, subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n"));
+  work->dom_state = DOMAIN_WAIT;
+
+  /* WORKGROUP<1b> is the domain master browser name. */
+  register_name(subrec, work->work_group,0x1b,samba_nb_type,
+                become_domain_master_stage2,
+                become_domain_master_fail, NULL);
+}
+
+/****************************************************************************
+  Function called when a query for a WORKGROUP<1b> name succeeds.
+  This is normally a fail condition as it means there is already
+  a domain master browser for a workgroup and we were trying to
+  become one.
+****************************************************************************/
+
+static void become_domain_master_query_success(struct subnet_record *subrec,
+                        struct userdata_struct *userdata,
+                        struct nmb_name *nmbname, struct in_addr ip, 
+                        struct res_rec *rrec)
+{
+  /* If the given ip is not ours, then we can't become a domain
+     controler as the name is already registered.
+   */
+
+ /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast
+    address or zero ip for this query. Pretend this is ok. */
+
+  if(ismyip(ip) || ip_equal(allones_ip, ip) || is_zero_ip(ip))
+  {
+    if( DEBUGLVL( 3 ) )
+    {
+      dbgtext( "become_domain_master_query_success():\n" );
+      dbgtext( "Our address (%s) ", inet_ntoa(ip) );
+      dbgtext( "returned in query for name %s ", nmb_namestr(nmbname) );
+      dbgtext( "(domain master browser name) " );
+      dbgtext( "on subnet %s.\n", subrec->subnet_name );
+      dbgtext( "Continuing with domain master code.\n" );
+    }
+
+    become_domain_master_stage1(subrec, nmbname->name);
+  }
+  else
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "become_domain_master_query_success:\n" );
+      dbgtext( "There is already a domain master browser at " );
+      dbgtext( "IP %s for workgroup %s ", inet_ntoa(ip), nmbname->name );
+      dbgtext( "registered on subnet %s.\n", subrec->subnet_name );
+      }
+  }
+}
+
+/****************************************************************************
+  Function called when a query for a WORKGROUP<1b> name fails.
+  This is normally a success condition as it then allows us to register
+  our own Domain Master Browser name.
+  ****************************************************************************/
+
+static void become_domain_master_query_fail(struct subnet_record *subrec,
+                                    struct response_record *rrec,
+                                    struct nmb_name *question_name, int fail_code)
+{
+  /* If the query was unicast, and the error is not NAM_ERR (name didn't exist),
+     then this is a failure. Otherwise, not finding the name is what we want. */
+  if((subrec == unicast_subnet) && (fail_code != NAM_ERR))
+  {
+    DEBUG(0,("become_domain_master_query_fail: Error %d returned when \
+querying WINS server for name %s.\n", 
+                  fail_code, nmb_namestr(question_name)));
+    return;
+  }
+
+  /* Otherwise - not having the name allows us to register it. */
+  become_domain_master_stage1(subrec, question_name->name);
+}
+
+/****************************************************************************
+  Attempt to become a domain master browser on all broadcast subnets.
+  ****************************************************************************/
+
+static void become_domain_master_browser_bcast(const char *workgroup_name)
+{
+  struct subnet_record *subrec;
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  { 
+    struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+    if (work && (work->dom_state == DOMAIN_NONE))
+    {
+      struct nmb_name nmbname;
+      make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+      /*
+       * Check for our name on the given broadcast subnet first, only initiate
+       * further processing if we cannot find it.
+       */
+
+      if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL)
+      {
+        if( DEBUGLVL( 0 ) )
+          {
+          dbgtext( "become_domain_master_browser_bcast:\n" );
+          dbgtext( "Attempting to become domain master browser on " );
+          dbgtext( "workgroup %s on subnet %s\n",
+                    workgroup_name, subrec->subnet_name );
+          }
+
+        /* Send out a query to establish whether there's a 
+           domain controller on the local subnet. If not,
+           we can become a domain controller. 
+         */
+
+        DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \
+for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name));
+
+        query_name(subrec, nmbname.name, nmbname.name_type,
+                   become_domain_master_query_success, 
+                   become_domain_master_query_fail,
+                   NULL);
+      }
+    }
+  }
+}
+
+/****************************************************************************
+  Attempt to become a domain master browser by registering with WINS.
+  ****************************************************************************/
+
+static void become_domain_master_browser_wins(const char *workgroup_name)
+{
+  struct work_record *work;
+
+  work = find_workgroup_on_subnet(unicast_subnet, workgroup_name);
+
+  if (work && (work->dom_state == DOMAIN_NONE))
+  {
+    struct nmb_name nmbname;
+
+    make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+    /*
+     * Check for our name on the unicast subnet first, only initiate
+     * further processing if we cannot find it.
+     */
+
+    if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL)
+    {
+      if( DEBUGLVL( 0 ) )
+        {
+        dbgtext( "become_domain_master_browser_wins:\n" );
+        dbgtext( "Attempting to become domain master browser " );
+        dbgtext( "on workgroup %s, subnet %s.\n",
+                  workgroup_name, unicast_subnet->subnet_name );
+        }
+
+      /* Send out a query to establish whether there's a 
+         domain master broswer registered with WINS. If not,
+         we can become a domain master browser. 
+       */
+
+      DEBUG(0,("become_domain_master_browser_wins: querying WINS server from IP %s \
+for domain master browser name %s on workgroup %s\n",
+         inet_ntoa(unicast_subnet->myip), nmb_namestr(&nmbname), workgroup_name));
+
+      query_name(unicast_subnet, nmbname.name, nmbname.name_type,
+                   become_domain_master_query_success,
+                   become_domain_master_query_fail,
+                   NULL);
+    }
+  }
+}
+
+/****************************************************************************
+  Add the domain logon server and domain master browser names
+  if we are set up to do so.
+  **************************************************************************/
+
+void add_domain_names(time_t t)
+{
+  static time_t lastrun = 0;
+
+  if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60)))
+    return;
+
+  lastrun = t;
+
+  /* Do the "internet group" - <1c> names. */
+  if (lp_domain_logons())
+    add_logon_names();
+
+  /* Do the domain master names. */
+  if(lp_server_role() == ROLE_DOMAIN_PDC)
+  {
+    if(we_are_a_wins_client())
+    {
+      /* We register the WORKGROUP<1b> name with the WINS
+         server first, and call add_domain_master_bcast()
+         only if this is successful.
+
+         This results in domain logon services being gracefully provided,
+         as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11.
+         1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c,
+         cannot provide domain master / domain logon services.
+       */
+      become_domain_master_browser_wins(lp_workgroup());
+    }
+    else
+      become_domain_master_browser_bcast(lp_workgroup());
+  }
+}
diff --git a/source4/nmbd/nmbd_become_lmb.c b/source4/nmbd/nmbd_become_lmb.c
new file mode 100644 (file)
index 0000000..8b87ca7
--- /dev/null
@@ -0,0 +1,605 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+void insert_permanent_name_into_unicast( struct subnet_record *subrec, 
+                                                struct nmb_name *nmbname, uint16 nb_type )
+{
+  struct name_record *namerec;
+
+  if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL)
+  {
+    /* The name needs to be created on the unicast subnet. */
+    (void)add_name_to_subnet( unicast_subnet, nmbname->name,
+                              nmbname->name_type, nb_type,
+                              PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip);
+  }
+  else
+  {
+    /* The name already exists on the unicast subnet. Add our local
+       IP for the given broadcast subnet to the name. */
+    add_ip_to_name_record( namerec, subrec->myip);
+  }
+}
+
+/*******************************************************************
+ Utility function to remove a name from the unicast subnet.
+******************************************************************/
+
+static void remove_permanent_name_from_unicast( struct subnet_record *subrec,
+                                                struct nmb_name *nmbname )
+{
+  struct name_record *namerec;
+
+  if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL)
+  {
+    /* Remove this broadcast subnet IP address from the name. */
+    remove_ip_from_name_record( namerec, subrec->myip);
+    if(namerec->data.num_ips == 0)
+      remove_name_from_namelist( unicast_subnet, namerec);
+  }
+}
+
+/*******************************************************************
+ Utility function always called to set our workgroup and server
+ state back to potential browser, or none.
+******************************************************************/
+
+static void reset_workgroup_state( struct subnet_record *subrec, char *workgroup_name,
+                                   BOOL force_new_election )
+{
+  struct work_record *work;
+  struct server_record *servrec;
+  struct nmb_name nmbname;
+
+  if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL)
+  {
+    DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, subrec->subnet_name ));
+    return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+            lp_netbios_name(), work->work_group, subrec->subnet_name));
+    work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+    return;
+  }
+
+  /* Update our server status - remove any master flag and replace
+   it with the potential browser flag. */
+  servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER;
+  servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0);
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  /* Reset our election flags. */
+  work->ElectionCriterion &= ~0x4;
+
+  work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+
+  /* Forget who the local master browser was for
+     this workgroup. */
+
+  set_workgroup_local_master_browser_name( work, "");
+
+  /*
+   * Ensure the IP address of this subnet is not registered as one
+   * of the IP addresses of the WORKGROUP<1d> name on the unicast
+   * subnet. This undoes what we did below when we became a local
+   * master browser.
+   */
+
+  make_nmb_name(&nmbname, work->work_group, 0x1d);
+
+  remove_permanent_name_from_unicast( subrec, &nmbname);
+
+  if(force_new_election)
+    work->needelection = True;
+}
+
+/*******************************************************************
+  Unbecome the local master browser name release success function.
+******************************************************************/
+
+static void unbecome_local_master_success(struct subnet_record *subrec,
+                             struct userdata_struct *userdata,
+                             struct nmb_name *released_name,
+                             struct in_addr released_ip)
+{ 
+  BOOL force_new_election = False;
+
+  memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+  DEBUG(3,("unbecome_local_master_success: released name %s.\n",
+             nmb_namestr(released_name)));
+
+  /* Now reset the workgroup and server state. */
+  reset_workgroup_state( subrec, released_name->name, force_new_election );
+
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "*****\n\n" );
+    dbgtext( "Samba name server %s ", lp_netbios_name() );
+    dbgtext( "has stopped being a local master browser " );
+    dbgtext( "for workgroup %s ", released_name->name );
+    dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+  }
+
+}
+
+/*******************************************************************
+  Unbecome the local master browser name release fail function.
+******************************************************************/
+
+static void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec,
+                       struct nmb_name *fail_name)
+{
+  struct name_record *namerec;
+  struct userdata_struct *userdata = rrec->userdata;
+  BOOL force_new_election = False;
+
+  memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+  DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \
+Removing from namelist anyway.\n", nmb_namestr(fail_name)));
+
+  /* Do it anyway. */
+  namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+  if(namerec)
+    remove_name_from_namelist(subrec, namerec);
+
+  /* Now reset the workgroup and server state. */
+  reset_workgroup_state( subrec, fail_name->name, force_new_election );
+
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "*****\n\n" );
+    dbgtext( "Samba name server %s ", lp_netbios_name() );
+    dbgtext( "has stopped being a local master browser " );
+    dbgtext( "for workgroup %s ", fail_name->name );
+    dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+  }
+}
+
+/*******************************************************************
+ Utility function to remove the WORKGROUP<1d> name.
+******************************************************************/
+
+static void release_1d_name( struct subnet_record *subrec, char *workgroup_name,
+                             BOOL force_new_election)
+{
+  struct nmb_name nmbname;
+  struct name_record *namerec;
+
+  make_nmb_name(&nmbname, workgroup_name, 0x1d);
+  if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL)
+  {
+    struct userdata_struct *userdata;
+    int size = sizeof(struct userdata_struct) + sizeof(BOOL);
+
+    if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
+    {
+      DEBUG(0,("release_1d_name: malloc fail.\n"));
+      return;
+    }
+
+    userdata->copy_fn = NULL;
+    userdata->free_fn = NULL;
+    userdata->userdata_len = sizeof(BOOL);
+    memcpy((char *)userdata->data, &force_new_election, sizeof(BOOL));
+
+    release_name(subrec, namerec,
+                 unbecome_local_master_success,
+                 unbecome_local_master_fail,
+                 userdata);
+
+    zero_free(userdata, size);
+  }
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release success function.
+******************************************************************/
+
+static void release_msbrowse_name_success(struct subnet_record *subrec,
+                      struct userdata_struct *userdata,
+                      struct nmb_name *released_name,
+                      struct in_addr released_ip)
+{
+  DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.",
+           nmb_namestr(released_name), subrec->subnet_name ));
+
+  /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+  remove_permanent_name_from_unicast( subrec, released_name);
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release fail function.
+******************************************************************/
+
+static void release_msbrowse_name_fail( struct subnet_record *subrec, 
+                       struct response_record *rrec,
+                       struct nmb_name *fail_name)
+{
+  struct name_record *namerec;
+
+  DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.",
+           nmb_namestr(fail_name), subrec->subnet_name ));
+
+  /* Release the name anyway. */
+  namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+  if(namerec)
+    remove_name_from_namelist(subrec, namerec);
+
+  /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+  remove_permanent_name_from_unicast( subrec, fail_name);
+}
+
+/*******************************************************************
+  Unbecome the local master browser. If force_new_election is true, restart
+  the election process after we've unbecome the local master.
+******************************************************************/
+
+void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work,
+                                   BOOL force_new_election)
+{
+  struct name_record *namerec;
+  struct nmb_name nmbname;
+
+  /* Sanity check. */
+
+  DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \
+on subnet %s\n",work->work_group, subrec->subnet_name));
+  
+  if(find_server_in_workgroup( work, lp_netbios_name()) == NULL)
+  {
+    DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), work->work_group, subrec->subnet_name));
+    work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+    return;
+  }
+  
+  /* Set the state to unbecoming. */
+  work->mst_state = MST_UNBECOMING_MASTER;
+
+  /*
+   * Release the WORKGROUP<1d> name asap to allow another machine to
+   * claim it.
+   */
+
+  release_1d_name( subrec, work->work_group, force_new_election);
+
+  /* Deregister any browser names we may have. */
+  make_nmb_name(&nmbname, MSBROWSE, 0x1);
+  if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL)
+  {
+    release_name(subrec, namerec,
+                 release_msbrowse_name_success,
+                 release_msbrowse_name_fail,
+                 NULL);
+  }
+
+  /*
+   * Ensure we have sent and processed these release packets
+   * before returning - we don't want to process any election
+   * packets before dealing with the 1d release.
+   */
+
+  retransmit_or_expire_response_records(time(NULL));
+}
+
+/****************************************************************************
+  Success in registering the WORKGROUP<1d> name.
+  We are now *really* a local master browser.
+  ****************************************************************************/
+
+static void become_local_master_stage2(struct subnet_record *subrec,
+                                        struct userdata_struct *userdata,
+                                        struct nmb_name *registered_name,
+                                        uint16 nb_flags,
+                                        int ttl, struct in_addr registered_ip)
+{
+  int i = 0;
+  struct server_record *sl;
+  struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+  struct server_record *servrec;
+
+  if(!work)
+  {
+    DEBUG(0,("become_local_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+    return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), registered_name->name, subrec->subnet_name));
+    work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+    return;
+  }
+  
+  DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \
+on subnet %s\n", work->work_group, subrec->subnet_name));
+
+  work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
+
+  /* update our server status */
+  servrec->serv.type |= SV_TYPE_MASTER_BROWSER;
+  servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER;
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  /* Add this name to the workgroup as local master browser. */
+  set_workgroup_local_master_browser_name( work, lp_netbios_name());
+
+  /* Count the number of servers we have on our list. If it's
+     less than 10 (just a heuristic) request the servers
+     to announce themselves.
+   */
+  for( sl = work->serverlist; sl != NULL; sl = sl->next)
+    i++;
+
+  if (i < 10)
+  {
+    /* Ask all servers on our local net to announce to us. */
+    broadcast_announce_request(subrec, work);
+  }
+
+  /*
+   * Now we are a local master on a broadcast subnet, we need to add
+   * the WORKGROUP<1d> name to the unicast subnet so that we can answer
+   * unicast requests sent to this name. We can create this name directly on
+   * the unicast subnet as a WINS server always returns true when registering
+   * this name, and discards the registration. We use the number of IP
+   * addresses registered to this name as a reference count, as we
+   * remove this broadcast subnet IP address from it when we stop becoming a local
+   * master browser for this broadcast subnet.
+   */
+
+  insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+  /* Reset the announce master browser timer so that we try and tell a domain
+     master browser as soon as possible that we are a local master browser. */
+  reset_announce_timer();
+
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "*****\n\n" );
+    dbgtext( "Samba name server %s ", lp_netbios_name() );
+    dbgtext( "is now a local master browser " );
+    dbgtext( "for workgroup %s ", work->work_group );
+    dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+  }
+
+}
+
+/****************************************************************************
+  Failed to register the WORKGROUP<1d> name.
+  ****************************************************************************/
+static void become_local_master_fail2(struct subnet_record *subrec,
+                                      struct response_record *rrec,
+                                      struct nmb_name *fail_name)
+{
+  struct work_record *work = find_workgroup_on_subnet( subrec, fail_name->name);
+
+  DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \
+Failed to become a local master browser.\n", nmb_namestr(fail_name), subrec->subnet_name));
+
+  if(!work)
+  {
+    DEBUG(0,("become_local_master_fail2: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+    return;
+  }
+
+  /* Roll back all the way by calling unbecome_local_master_browser(). */
+  unbecome_local_master_browser(subrec, work, False);
+}
+
+/****************************************************************************
+  Success in registering the MSBROWSE name.
+  ****************************************************************************/
+
+static void become_local_master_stage1(struct subnet_record *subrec,
+                                        struct userdata_struct *userdata,
+                                        struct nmb_name *registered_name,
+                                        uint16 nb_flags,
+                                        int ttl, struct in_addr registered_ip)
+{
+  char *work_name = userdata->data;
+  struct work_record *work = find_workgroup_on_subnet( subrec, work_name);
+
+  if(!work)
+  {
+    DEBUG(0,("become_local_master_stage1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n",
+            work->work_group));
+
+  work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */
+
+  /*
+   * We registered the MSBROWSE name on a broadcast subnet, now need to add
+   * the MSBROWSE name to the unicast subnet so that we can answer
+   * unicast requests sent to this name. We create this name directly on
+   * the unicast subnet.
+   */
+
+  insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+  /* Attempt to register the WORKGROUP<1d> name. */
+  register_name(subrec, work->work_group,0x1d,samba_nb_type,
+                become_local_master_stage2,
+                become_local_master_fail2,
+                NULL);
+}
+
+/****************************************************************************
+  Failed to register the MSBROWSE name.
+  ****************************************************************************/
+
+static void become_local_master_fail1(struct subnet_record *subrec,
+                                      struct response_record *rrec,
+                                      struct nmb_name *fail_name)
+{
+  char *work_name = rrec->userdata->data;
+  struct work_record *work = find_workgroup_on_subnet(subrec, work_name);
+
+  if(!work)
+  {
+    DEBUG(0,("become_local_master_fail1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+    return;
+  }
+
+  if(find_server_in_workgroup(work, lp_netbios_name()) == NULL)
+  {
+    DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), work->work_group, subrec->subnet_name));
+    return;
+  }
+
+  reset_workgroup_state( subrec, work->work_group, False );
+
+  DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+       work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/******************************************************************
+  Become the local master browser on a subnet.
+  This gets called if we win an election on this subnet.
+
+  Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1.
+  Stage 2: mst_state was MST_BACKUP  - go to MST_MSB  and register WORKGROUP<1d>.
+  Stage 3: mst_state was MST_MSB  - go to MST_BROWSER.
+******************************************************************/
+
+void become_local_master_browser(struct subnet_record *subrec, struct work_record *work)
+{
+  struct userdata_struct *userdata;
+  int size = sizeof(struct userdata_struct) + sizeof(fstring) + 1;
+
+  /* Sanity check. */
+  if (!lp_local_master())
+  { 
+    DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n"));
+    return;
+  }
+
+  if(!AM_POTENTIAL_MASTER_BROWSER(work))
+  {
+    DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n",
+              work->mst_state ));
+    return;
+  }
+
+  if(find_server_in_workgroup( work, lp_netbios_name()) == NULL)
+  {
+    DEBUG(0,("become_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), work->work_group, subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \
+%s on subnet %s\n", work->work_group, subrec->subnet_name));
+  
+  DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n"));
+  work->mst_state = MST_BACKUP; /* an election win was successful */
+
+  work->ElectionCriterion |= 0x5;
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  /* Setup the userdata_struct. */
+  if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
+  {
+    DEBUG(0,("become_local_master_browser: malloc fail.\n"));
+    return;
+  }
+
+  userdata->copy_fn = NULL;
+  userdata->free_fn = NULL;
+  userdata->userdata_len = strlen(work->work_group)+1;
+  fstrcpy(userdata->data, work->work_group);
+
+  /* Register the special browser group name. */
+  register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP,
+                become_local_master_stage1,
+                become_local_master_fail1,
+                userdata);
+
+  zero_free(userdata, size);
+}
+
+/***************************************************************
+ Utility function to set the local master browser name. Does
+ some sanity checking as old versions of Samba seem to sometimes
+ say that the master browser name for a workgroup is the same
+ as the workgroup name.
+****************************************************************/
+
+void set_workgroup_local_master_browser_name( struct work_record *work, const char *newname)
+{
+  DEBUG(5,("set_workgroup_local_master_browser_name: setting local master name to '%s' \
+for workgroup %s.\n", newname, work->work_group ));
+
+#if 0
+  /*
+   * Apparently some sites use the workgroup name as the local
+   * master browser name. Arrrrggghhhhh ! (JRA).
+   */
+  if(strequal( work->work_group, newname))
+  {
+    DEBUG(5, ("set_workgroup_local_master_browser_name: Refusing to set \
+local_master_browser_name for workgroup %s to workgroup name.\n",
+         work->work_group ));
+    return;
+  }
+#endif
+
+  StrnCpy(work->local_master_browser_name, newname,
+            sizeof(work->local_master_browser_name)-1);
+}
diff --git a/source4/nmbd/nmbd_browserdb.c b/source4/nmbd/nmbd_browserdb.c
new file mode 100644 (file)
index 0000000..a4ef98e
--- /dev/null
@@ -0,0 +1,182 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   Copyright (C) Christopher R. Hertel 1998
+   
+   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.
+   
+*/
+/* -------------------------------------------------------------------------- **
+ * Modified July 1998 by CRH.
+ *  I converted this module to use the canned doubly-linked lists.  I also
+ *  added comments above the functions where possible.
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ *  lmb_browserlist - This is our local master browser list. 
+ */
+
+ubi_dlNewList( lmb_browserlist );
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Remove and free a browser list entry.
+ *
+ *  Input:  browc - A pointer to the entry to be removed from the list and
+ *                  freed.
+ *  Output: none.
+ *
+ * ************************************************************************** **
+ */
+static void remove_lmb_browser_entry( struct browse_cache_record *browc )
+  {
+  safe_free( ubi_dlRemThis( lmb_browserlist, browc ) );
+  } /* remove_lmb_browser_entry */
+
+/* ************************************************************************** **
+ * Update a browser death time.
+ *
+ *  Input:  browc - Pointer to the entry to be updated.
+ *  Output: none.
+ *
+ * ************************************************************************** **
+ */
+void update_browser_death_time( struct browse_cache_record *browc )
+  {
+  /* Allow the new lmb to miss an announce period before we remove it. */
+  browc->death_time = time(NULL) + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+  } /* update_browser_death_time */
+
+/* ************************************************************************** **
+ * Create a browser entry and add it to the local master browser list.
+ *
+ *  Input:  work_name
+ *          browser_name
+ *          ip
+ *
+ *  Output: Pointer to the new entry, or NULL if malloc() failed.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *create_browser_in_lmb_cache( char *work_name, 
+                                                         char *browser_name, 
+                                                         struct in_addr ip )
+  {
+  struct browse_cache_record *browc;
+  time_t now = time( NULL );
+
+  browc = (struct browse_cache_record *)malloc( sizeof( *browc ) );
+
+  if( NULL == browc )
+    {
+    DEBUG( 0, ("create_browser_in_lmb_cache: malloc fail !\n") );
+    return( NULL );
+    }
+
+  memset( (char *)browc, '\0', sizeof( *browc ) );
+  
+  /* For a new lmb entry we want to sync with it after one minute. This
+     will allow it time to send out a local announce and build its
+     browse list.
+   */
+  browc->sync_time = now + 60;
+
+  /* Allow the new lmb to miss an announce period before we remove it. */
+  browc->death_time = now + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+
+  StrnCpy(  browc->lmb_name, browser_name, sizeof(browc->lmb_name)-1 );
+  StrnCpy(  browc->work_group, work_name, sizeof(browc->work_group)-1 );
+  strupper( browc->lmb_name );
+  strupper( browc->work_group );
+  
+  browc->ip = ip;
+  (void)ubi_dlAddTail( lmb_browserlist, browc );
+
+  if( DEBUGLVL( 3 ) )
+    {
+    Debug1( "nmbd_browserdb:create_browser_in_lmb_cache()\n" );
+    Debug1( "  Added lmb cache entry for workgroup %s ", browc->work_group );
+    Debug1( "name %s IP %s ", browc->lmb_name, inet_ntoa(ip) );
+    Debug1( "ttl %d\n", (int)browc->death_time );
+    }
+  
+  return( browc );
+  } /* create_browser_in_lmb_cache */
+
+/* ************************************************************************** **
+ * Find a browser entry in the local master browser list.
+ *
+ *  Input:  browser_name  - The name for which to search.
+ *
+ *  Output: A pointer to the matching entry, or NULL if no match was found.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *find_browser_in_lmb_cache( char *browser_name )
+  {
+  struct browse_cache_record *browc;
+
+  for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+       browc;
+       browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
+    if( strequal( browser_name, browc->lmb_name ) )
+      break;
+
+  return( browc );
+  } /* find_browser_in_lmb_cache */
+
+/* ************************************************************************** **
+ *  Expire timed out browsers in the browserlist.
+ *
+ *  Input:  t - Expiration time.  Entries with death times less than this
+ *              value will be removed from the list.
+ *  Output: none.
+ *
+ * ************************************************************************** **
+ */
+void expire_lmb_browsers( time_t t )
+  {
+  struct browse_cache_record *browc;
+  struct browse_cache_record *nextbrowc;
+
+  for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+       browc;
+       browc = nextbrowc )
+    {
+    nextbrowc = (struct browse_cache_record *)ubi_dlNext( browc );
+
+    if( browc->death_time < t )
+      {
+      if( DEBUGLVL( 3 ) )
+        {
+        Debug1( "nmbd_browserdb:expire_lmb_browsers()\n" );
+        Debug1( "  Removing timed out lmb entry %s\n", browc->lmb_name );
+        }
+      remove_lmb_browser_entry( browc );
+      }
+    }
+  } /* expire_lmb_browsers */
diff --git a/source4/nmbd/nmbd_browsesync.c b/source4/nmbd/nmbd_browsesync.c
new file mode 100644 (file)
index 0000000..ff022a7
--- /dev/null
@@ -0,0 +1,699 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/* This is our local master browser list database. */
+extern ubi_dlList lmb_browserlist[];
+
+/****************************************************************************
+As a domain master browser, do a sync with a local master browser.
+**************************************************************************/
+static void sync_with_lmb(struct browse_cache_record *browc)
+{                     
+  struct work_record *work;
+
+  if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) )
+  {
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "sync_with_lmb:\n" );
+      dbgtext( "Failed to get a workgroup for a local master browser " );
+      dbgtext( "cache entry workgroup " );
+      dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
+    }
+    return;
+  }
+
+  /* We should only be doing this if we are a domain master browser for
+     the given workgroup. Ensure this is so. */
+
+  if(!AM_DOMAIN_MASTER_BROWSER(work))
+  {
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "sync_with_lmb:\n" );
+      dbgtext( "We are trying to sync with a local master browser " );
+      dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
+      dbgtext( "and we are not a domain master browser on this workgroup.\n" );
+      dbgtext( "Error!\n" );
+    }
+    return;
+  }
+
+  if( DEBUGLVL( 2 ) )
+  {
+    dbgtext( "sync_with_lmb:\n" );
+    dbgtext( "Initiating sync with local master browser " );
+    dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
+    dbgtext( "for workgroup %s\n", browc->work_group );
+  }
+
+  sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
+
+  browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
+}
+
+/****************************************************************************
+Sync or expire any local master browsers.
+**************************************************************************/
+void dmb_expire_and_sync_browser_lists(time_t t)
+{
+  static time_t last_run = 0;
+  struct browse_cache_record *browc;
+
+  /* Only do this every 20 seconds. */  
+  if (t - last_run < 20) 
+   return;
+
+  last_run = t;
+
+  expire_lmb_browsers(t);
+
+  for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+       browc;
+       browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
+  {
+    if (browc->sync_time < t)
+      sync_with_lmb(browc);
+  }
+}
+
+/****************************************************************************
+As a local master browser, send an announce packet to the domain master browser.
+**************************************************************************/
+
+static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
+{
+  pstring outbuf;
+  char *p;
+
+  if(ismyip(work->dmb_addr))
+  {
+    if( DEBUGLVL( 2 ) )
+    {
+      dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+      dbgtext( "We are both a domain and a local master browser for " );
+      dbgtext( "workgroup %s.  ", work->work_group );
+      dbgtext( "Do not announce to ourselves.\n" );
+    }
+    return;
+  }
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf;
+  SCVAL(p,0,ANN_MasterAnnouncement);
+  p++;
+
+  StrnCpy(p,lp_netbios_name(),15);
+  strupper(p);
+  p = skip_string(p,1);
+
+  if( DEBUGLVL( 4 ) )
+  {
+    dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+    dbgtext( "Sending local master announce to " );
+    dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
+                                       work->work_group );
+  }
+
+  send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+               lp_netbios_name(), 0x0, work->dmb_name.name, 0x0, 
+               work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
+
+}
+
+/****************************************************************************
+As a local master browser, do a sync with a domain master browser.
+**************************************************************************/
+
+static void sync_with_dmb(struct work_record *work)
+{
+  if( DEBUGLVL( 2 ) )
+  {
+    dbgtext( "sync_with_dmb:\n" );
+    dbgtext( "Initiating sync with domain master browser " );
+    dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
+    dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
+    dbgtext( "for workgroup %s\n", work->work_group );
+  }
+
+  sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type, 
+                    work->dmb_addr, False, True);
+}
+
+/****************************************************************************
+  Function called when a node status query to a domain master browser IP succeeds.
+****************************************************************************/
+
+static void domain_master_node_status_success(struct subnet_record *subrec,
+                                              struct userdata_struct *userdata,
+                                              struct res_rec *answers,
+                                              struct in_addr from_ip)
+{
+  struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
+
+  if( work == NULL )
+  {
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "domain_master_node_status_success:\n" );
+      dbgtext( "Unable to find workgroup " );
+      dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
+    }
+    return;
+  }
+
+  if( DEBUGLVL( 3 ) )
+  {
+    dbgtext( "domain_master_node_status_success:\n" );
+    dbgtext( "Success in node status for workgroup " );
+    dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
+  }
+
+  /* Go through the list of names found at answers->rdata and look for
+     the first SERVER<0x20> name. */
+
+  if(answers->rdata != NULL)
+  {
+    char *p = answers->rdata;
+    int numnames = CVAL(p, 0);
+
+    p += 1;
+
+    while (numnames--)
+    {
+      char qname[17];
+      uint16 nb_flags;
+      int name_type;
+
+      StrnCpy(qname,p,15);
+      name_type = CVAL(p,15);
+      nb_flags = get_nb_flags(&p[16]);
+      trim_string(qname,NULL," ");
+
+      p += 18;
+
+      if(!(nb_flags & NB_GROUP) && (name_type == 0x20))
+      {
+        struct nmb_name nmbname;
+
+        make_nmb_name(&nmbname, qname, name_type);
+
+        /* Copy the dmb name and IP address
+           into the workgroup struct. */
+
+        work->dmb_name = nmbname;
+        putip((char *)&work->dmb_addr, &from_ip);
+
+        /* Do the local master browser announcement to the domain
+           master browser name and IP. */
+        announce_local_master_browser_to_domain_master_browser( work );
+
+        /* Now synchronise lists with the domain master browser. */
+        sync_with_dmb(work);
+        break;
+      }
+    }
+  }
+  else
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "domain_master_node_status_success:\n" );
+      dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
+      dbgtext( "%s.\n", inet_ntoa(from_ip) );
+    }
+}
+
+/****************************************************************************
+  Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void domain_master_node_status_fail(struct subnet_record *subrec,
+                       struct response_record *rrec)
+{
+  struct userdata_struct *userdata = rrec->userdata;
+
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "domain_master_node_status_fail:\n" );
+    dbgtext( "Doing a node status request to the domain master browser\n" );
+    dbgtext( "for workgroup %s ", userdata->data );
+    dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+    dbgtext( "Cannot sync browser lists.\n" );
+  }
+}
+
+/****************************************************************************
+  Function called when a query for a WORKGROUP<1b> name succeeds.
+****************************************************************************/
+
+static void find_domain_master_name_query_success(struct subnet_record *subrec,
+                        struct userdata_struct *userdata_in,
+                        struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+  /* 
+   * Unfortunately, finding the IP address of the Domain Master Browser,
+   * as we have here, is not enough. We need to now do a sync to the
+   * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
+   * respond to the SMBSERVER name. To get this name from IP
+   * address we do a Node status request, and look for the first
+   * NAME<0x20> in the response, and take that as the server name.
+   * We also keep a cache of the Domain Master Browser name for this
+   * workgroup in the Workgroup struct, so that if the same IP addess
+   * is returned every time, we don't need to do the node status
+   * request.
+   */
+
+  struct work_record *work;
+  struct nmb_name nmbname;
+  struct userdata_struct *userdata;
+  int size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
+
+  if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) )
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "find_domain_master_name_query_success:\n" );
+      dbgtext( "Failed to find workgroup %s\n", q_name->name );
+      }
+    return;
+  }
+
+  /* First check if we already have a dmb for this workgroup. */
+
+  if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip))
+  {
+    /* Do the local master browser announcement to the domain
+       master browser name and IP. */
+    announce_local_master_browser_to_domain_master_browser( work );
+
+    /* Now synchronise lists with the domain master browser. */
+    sync_with_dmb(work);
+    return;
+  }
+  else
+    zero_ip(&work->dmb_addr);
+
+  /* Now initiate the node status request. */
+  make_nmb_name(&nmbname,"*",0x0);
+
+  /* Put the workgroup name into the userdata so we know
+     what workgroup we're talking to when the reply comes
+     back. */
+
+  /* Setup the userdata_struct - this is copied so we can use
+     a stack variable for this. */
+  if((userdata = (struct userdata_struct *)malloc(size)) == NULL)
+  {
+    DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
+    return;
+  }
+
+  userdata->copy_fn = NULL;
+  userdata->free_fn = NULL;
+  userdata->userdata_len = strlen(work->work_group)+1;
+  fstrcpy(userdata->data, work->work_group);
+
+  node_status( subrec, &nmbname, answer_ip, 
+               domain_master_node_status_success,
+               domain_master_node_status_fail,
+               userdata);
+
+  zero_free(userdata, size);
+}
+
+/****************************************************************************
+  Function called when a query for a WORKGROUP<1b> name fails.
+  ****************************************************************************/
+static void find_domain_master_name_query_fail(struct subnet_record *subrec,
+                                    struct response_record *rrec,
+                                    struct nmb_name *question_name, int fail_code)
+{
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "find_domain_master_name_query_fail:\n" );
+    dbgtext( "Unable to find the Domain Master Browser name " );
+    dbgtext( "%s for the workgroup %s.\n",
+             nmb_namestr(question_name), question_name->name );
+    dbgtext( "Unable to sync browse lists in this workgroup.\n" );
+  }
+}
+
+/****************************************************************************
+As a local master browser for a workgroup find the domain master browser
+name, announce ourselves as local master browser to it and then pull the
+full domain browse lists from it onto the given subnet.
+**************************************************************************/
+
+void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
+                                                   struct work_record *work)
+{
+  struct nmb_name nmbname;
+
+  /* Only do this if we are using a WINS server. */
+  if(we_are_a_wins_client() == False)
+  {
+    if( DEBUGLVL( 10 ) )
+    {
+      dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
+      dbgtext( "Ignoring, as we are not a WINS client.\n" );
+    }
+    return;
+  }
+
+  make_nmb_name(&nmbname,work->work_group,0x1b);
+
+  /* First, query for the WORKGROUP<1b> name from the WINS server. */
+  query_name(unicast_subnet, nmbname.name, nmbname.name_type,
+             find_domain_master_name_query_success,
+             find_domain_master_name_query_fail,
+             NULL);
+
+}
+
+/****************************************************************************
+  Function called when a node status query to a domain master browser IP succeeds.
+  This function is only called on query to a Samba 1.9.18 or above WINS server.
+
+  Note that adding the workgroup name is enough for this workgroup to be
+  browsable by clients, as clients query the WINS server or broadcast 
+  nets for the WORKGROUP<1b> name when they want to browse a workgroup
+  they are not in. We do not need to do a sync with this Domain Master
+  Browser in order for our browse clients to see machines in this workgroup.
+  JRA.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
+                                              struct userdata_struct *userdata,
+                                              struct res_rec *answers,
+                                              struct in_addr from_ip)
+{
+  struct work_record *work;
+  fstring server_name;
+
+  server_name[0] = 0;
+
+  if( DEBUGLVL( 3 ) )
+  {
+    dbgtext( "get_domain_master_name_node_status_success:\n" );
+    dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
+  }
+
+  /* 
+   * Go through the list of names found at answers->rdata and look for
+   * the first WORKGROUP<0x1b> name.
+   */
+
+  if(answers->rdata != NULL)
+  {
+    char *p = answers->rdata;
+    int numnames = CVAL(p, 0);
+
+    p += 1;
+
+    while (numnames--)
+    {
+      char qname[17];
+      uint16 nb_flags;
+      int name_type;
+
+      StrnCpy(qname,p,15);
+      name_type = CVAL(p,15);
+      nb_flags = get_nb_flags(&p[16]);
+      trim_string(qname,NULL," ");
+
+      p += 18;
+
+      if(!(nb_flags & NB_GROUP) && (name_type == 0x00) && 
+        server_name[0] == 0) {
+             /* this is almost certainly the server netbios name */
+             fstrcpy(server_name, qname);
+             continue;
+      }
+
+      if(!(nb_flags & NB_GROUP) && (name_type == 0x1b))
+      {
+        if( DEBUGLVL( 5 ) )
+        {
+          dbgtext( "get_domain_master_name_node_status_success:\n" );
+          dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
+          dbgtext( "is a domain master browser for workgroup " );
+          dbgtext( "%s. Adding this name.\n", qname );
+        }
+
+        /* 
+         * If we don't already know about this workgroup, add it
+         * to the workgroup list on the unicast_subnet.
+         */
+        if((work = find_workgroup_on_subnet( subrec, qname)) == NULL)
+       {
+               struct nmb_name nmbname;
+               /* 
+                * Add it - with an hour in the cache.
+                */
+               if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
+                       return;
+
+               /* remember who the master is */
+               fstrcpy(work->local_master_browser_name, server_name);
+               make_nmb_name(&nmbname, server_name, 0x20);
+               work->dmb_name = nmbname;
+               work->dmb_addr = from_ip;
+        }
+        break;
+      }
+    }
+  }
+  else
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "get_domain_master_name_node_status_success:\n" );
+      dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
+      dbgtext( "%s.\n", inet_ntoa(from_ip) );
+    }
+}
+
+/****************************************************************************
+  Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
+                       struct response_record *rrec)
+{
+  if( DEBUGLVL( 0 ) )
+  {
+    dbgtext( "get_domain_master_name_node_status_fail:\n" );
+    dbgtext( "Doing a node status request to the domain master browser " );
+    dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+    dbgtext( "Cannot get workgroup name.\n" );
+  }
+}
+
+/****************************************************************************
+  Function called when a query for *<1b> name succeeds.
+****************************************************************************/
+
+static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
+                        struct userdata_struct *userdata_in,
+                        struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+  /* 
+   * We now have a list of all the domain master browsers for all workgroups
+   * that have registered with the WINS server. Now do a node status request
+   * to each one and look for the first 1b name in the reply. This will be
+   * the workgroup name that we will add to the unicast subnet as a 'non-local'
+   * workgroup.
+   */
+
+  struct nmb_name nmbname;
+  struct in_addr send_ip;
+  int i;
+
+  if( DEBUGLVL( 5 ) )
+  {
+    dbgtext( "find_all_domain_master_names_query_succes:\n" );
+    dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
+    dbgtext( "IP addresses for Domain Master Browsers.\n" );
+  }
+
+  for(i = 0; i < rrec->rdlength / 6; i++)
+  {
+    /* Initiate the node status requests. */
+    make_nmb_name(&nmbname, "*", 0);
+
+    putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
+
+    /* 
+     * Don't send node status requests to ourself.
+     */
+
+    if(ismyip( send_ip ))
+    {
+      if( DEBUGLVL( 5 ) )
+      {
+        dbgtext( "find_all_domain_master_names_query_succes:\n" );
+        dbgtext( "Not sending node status to our own IP " );
+        dbgtext( "%s.\n", inet_ntoa(send_ip) );
+      }
+      continue;
+    }
+
+    if( DEBUGLVL( 5 ) )
+    {
+      dbgtext( "find_all_domain_master_names_query_success:\n" );
+      dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
+    }
+
+    node_status( subrec, &nmbname, send_ip, 
+                 get_domain_master_name_node_status_success,
+                 get_domain_master_name_node_status_fail,
+                 NULL);
+  }
+}
+
+/****************************************************************************
+  Function called when a query for *<1b> name fails.
+  ****************************************************************************/
+static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
+                                    struct response_record *rrec,
+                                    struct nmb_name *question_name, int fail_code)
+{
+  if( DEBUGLVL( 10 ) )
+  {
+    dbgtext( "find_domain_master_name_query_fail:\n" );
+    dbgtext( "WINS server did not reply to a query for name " );
+    dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
+    dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
+  }
+}
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a query to the
+ WINS server for the *<1b> name. This will only work to a Samba WINS server,
+ so ignore it if we fail. If we succeed, contact each of the IP addresses in
+ turn and do a node status request to them. If this succeeds then look for a
+ <1b> name in the reply - this is the workgroup name. Add this to the unicast
+ subnet. This is expensive, so we only do this every 15 minutes.
+**************************************************************************/
+void collect_all_workgroup_names_from_wins_server(time_t t)
+{
+  static time_t lastrun = 0;
+  struct work_record *work;
+  struct nmb_name nmbname;
+
+  /* Only do this if we are using a WINS server. */
+  if(we_are_a_wins_client() == False)
+    return;
+
+  /* Check to see if we are a domain master browser on the unicast subnet. */
+  if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL)
+  {
+    if( DEBUGLVL( 0 ) )
+    {
+      dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
+      dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
+      dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
+    }
+    return;
+  }
+
+  if(!AM_DOMAIN_MASTER_BROWSER(work))
+    return;
+
+  if ((lastrun != 0) && (t < lastrun + (15 * 60)))
+    return;
+     
+  lastrun = t;
+
+  make_nmb_name(&nmbname,"*",0x1b);
+
+  /* First, query for the *<1b> name from the WINS server. */
+  query_name(unicast_subnet, nmbname.name, nmbname.name_type,
+             find_all_domain_master_names_query_success,
+             find_all_domain_master_names_query_fail,
+             NULL);
+} 
+
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a regular sync
+ with all other DMBs that we know of on that subnet.
+
+To prevent exponential network traffic with large numbers of workgroups
+we use a randomised system where sync probability is inversely proportional
+to the number of known workgroups
+**************************************************************************/
+void sync_all_dmbs(time_t t)
+{
+       static time_t lastrun = 0;
+       struct work_record *work;
+       int count=0;
+
+       /* Only do this if we are using a WINS server. */
+       if(we_are_a_wins_client() == False)
+               return;
+
+       /* Check to see if we are a domain master browser on the
+           unicast subnet. */
+       work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
+       if (!work) return;
+
+       if (!AM_DOMAIN_MASTER_BROWSER(work))
+               return;
+
+       if ((lastrun != 0) && (t < lastrun + (5 * 60)))
+               return;
+     
+       /* count how many syncs we might need to do */
+       for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+               if (strcmp(lp_workgroup(), work->work_group)) {
+                       count++;
+               }
+       }
+
+       /* sync with a probability of 1/count */
+       for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+               if (strcmp(lp_workgroup(), work->work_group)) {
+                       if (((unsigned)sys_random()) % count != 0) continue;
+
+                       lastrun = t;
+
+                       if (!work->dmb_name.name[0]) {
+                               /* we don't know the DMB - assume it is
+                                  the same as the unicast local master */
+                               make_nmb_name(&work->dmb_name, 
+                                             work->local_master_browser_name,
+                                             0x20);
+                       }
+
+                       DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
+                                work->dmb_name.name, 
+                                inet_ntoa(work->dmb_addr)));
+                       sync_browse_lists(work, 
+                                         work->dmb_name.name,
+                                         work->dmb_name.name_type, 
+                                         work->dmb_addr, False, False);
+               }
+       }
+} 
diff --git a/source4/nmbd/nmbd_elections.c b/source4/nmbd/nmbd_elections.c
new file mode 100644 (file)
index 0000000..759379c
--- /dev/null
@@ -0,0 +1,403 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/* Election parameters. */
+extern time_t StartupTime;
+
+/****************************************************************************
+  Send an election datagram packet.
+**************************************************************************/
+static void send_election_dgram(struct subnet_record *subrec, const char *workgroup_name,
+                                uint32 criterion, int timeup,const char *server_name)
+{
+  pstring outbuf;
+  char *p;
+
+  DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
+       workgroup_name, subrec->subnet_name ));
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf;
+  SCVAL(p,0,ANN_Election); /* Election opcode. */
+  p++;
+
+  SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
+  SIVAL(p,1,criterion);
+  SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
+  p += 13;
+  pstrcpy(p,server_name);
+  strupper(p);
+  p = skip_string(p,1);
+  
+  send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+                lp_netbios_name(), 0,
+                workgroup_name, 0x1e,
+                subrec->bcast_ip, subrec->myip, DGRAM_PORT);
+}
+
+/*******************************************************************
+ We found a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_success(struct subnet_record *subrec,
+                                 struct userdata_struct *userdata,
+                                 struct nmb_name *answer_name,
+                                 struct in_addr answer_ip, struct res_rec *rrec)
+{
+  DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
+IP %s (just checking).\n", answer_name->name, inet_ntoa(answer_ip) ));
+}
+
+/*******************************************************************
+ We failed to find a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_fail( struct subnet_record *subrec,
+                                           struct response_record *rrec,
+                                           struct nmb_name *question_name,
+                                           int fail_code)
+{
+  char *workgroup_name = question_name->name;
+  struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+  if(work == NULL)
+  {
+    DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
+          workgroup_name, subrec->subnet_name ));
+    return;
+  }
+
+  if (strequal(work->work_group, lp_workgroup()))
+  {
+
+    if (lp_local_master())
+    {
+      /* We have discovered that there is no local master
+         browser, and we are configured to initiate
+         an election that we will participate in.
+       */
+      DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
+               work->work_group, subrec->subnet_name ));
+
+      /* Setting this means we will participate when the
+         election is run in run_elections(). */
+      work->needelection = True;
+    }
+    else
+    {
+      /* We need to force an election, because we are configured
+         not to become the local master, but we still need one,
+         having detected that one doesn't exist.
+       */
+      send_election_dgram(subrec, work->work_group, 0, 0, "");
+    }
+  }
+}
+
+/*******************************************************************
+  Ensure there is a local master browser for a workgroup on our
+  broadcast interfaces.
+******************************************************************/
+
+void check_master_browser_exists(time_t t)
+{
+  static time_t lastrun=0;
+  struct subnet_record *subrec;
+  const char *workgroup_name = lp_workgroup();
+
+  if (!lastrun)
+    lastrun = t;
+
+  if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
+    return;
+
+  lastrun = t;
+
+  dump_workgroups(False);
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work))
+      {
+        /* Do a name query for the local master browser on this net. */
+        query_name( subrec, work->work_group, 0x1d,
+                    check_for_master_browser_success,
+                    check_for_master_browser_fail,
+                    NULL);
+      }
+    }
+  }
+}
+
+/*******************************************************************
+  Run an election.
+******************************************************************/
+
+void run_elections(time_t t)
+{
+  static time_t lastime = 0;
+  
+  struct subnet_record *subrec;
+  
+  /* Send election packets once every 2 seconds - note */
+  if (lastime && (t - lastime < 2))
+    return;
+  
+  lastime = t;
+  
+  START_PROFILE(run_elections);
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      if (work->RunningElection)
+      {
+        /*
+         * We can only run an election for a workgroup if we have
+         * registered the WORKGROUP<1e> name, as that's the name
+         * we must listen to.
+         */
+        struct nmb_name nmbname;
+
+        make_nmb_name(&nmbname, work->work_group, 0x1e);
+        if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+          DEBUG(8,("run_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+          continue;
+        }
+
+        send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
+                      t - StartupTime, lp_netbios_name());
+             
+        if (work->ElectionCount++ >= 4)
+        {
+          /* Won election (4 packets were sent out uncontested. */
+          DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
+                    work->work_group, subrec->subnet_name ));
+
+          work->RunningElection = False;
+
+          become_local_master_browser(subrec, work);
+        }
+      }
+    }
+  }
+  END_PROFILE(run_elections);
+}
+
+/*******************************************************************
+  Determine if I win an election.
+******************************************************************/
+
+static BOOL win_election(struct work_record *work, int version,
+                         uint32 criterion, int timeup, char *server_name)
+{  
+  int mytimeup = time(NULL) - StartupTime;
+  uint32 mycriterion = work->ElectionCriterion;
+
+  /* If local master is false then never win
+     in election broadcasts. */
+  if(!lp_local_master())
+  {
+    DEBUG(3,("win_election: Losing election as local master == False\n"));
+    return False;
+  }
+  DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
+        version, ELECTION_VERSION,
+        criterion, mycriterion,
+        timeup, mytimeup,
+        server_name, lp_netbios_name()));
+
+  if (version > ELECTION_VERSION)
+    return(False);
+  if (version < ELECTION_VERSION)
+    return(True);
+  
+  if (criterion > mycriterion)
+    return(False);
+  if (criterion < mycriterion)
+    return(True);
+
+  if (timeup > mytimeup)
+    return(False);
+  if (timeup < mytimeup)
+    return(True);
+
+  if (strcasecmp(lp_netbios_name(), server_name) > 0)
+    return(False);
+  
+  return(True);
+}
+
+/*******************************************************************
+  Process an incoming election datagram packet.
+******************************************************************/
+
+void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int version = CVAL(buf,0);
+  uint32 criterion = IVAL(buf,1);
+  int timeup = IVAL(buf,5)/1000;
+  char *server_name = buf+13;
+  struct work_record *work;
+  char *workgroup_name = dgram->dest_name.name;
+
+  START_PROFILE(election);
+  server_name[15] = 0;  
+
+  DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
+      server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
+
+  DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
+
+  if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+  {
+    DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
+          workgroup_name, subrec->subnet_name ));
+    goto done;
+  }
+
+  if (!strequal(work->work_group, lp_workgroup()))
+  {
+    DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
+is not my workgroup.\n", work->work_group, subrec->subnet_name ));
+    goto done;
+  }
+
+  if (win_election(work, version,criterion,timeup,server_name))
+  {
+    /* We take precedence over the requesting server. */
+    if (!work->RunningElection)
+    {
+      /* We weren't running an election - start running one. */
+
+      work->needelection = True;
+      work->ElectionCount=0;
+    }
+
+    /* Note that if we were running an election for this workgroup on this
+       subnet already, we just ignore the server we take precedence over. */
+  }
+  else
+  {
+    /* We lost. Stop participating. */
+    work->needelection = False;
+
+    if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work))
+    {
+      work->RunningElection = False;
+      DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
+            work->work_group, subrec->subnet_name ));
+      if (AM_LOCAL_MASTER_BROWSER(work))
+        unbecome_local_master_browser(subrec, work, False);
+    }
+  }
+done:
+  END_PROFILE(election);
+}
+
+/****************************************************************************
+  This function looks over all the workgroups known on all the broadcast
+  subnets and decides if a browser election is to be run on that workgroup.
+  It returns True if any election packets need to be sent (this will then
+  be done by run_elections().
+***************************************************************************/
+
+BOOL check_elections(void)
+{
+  struct subnet_record *subrec;
+  BOOL run_any_election = False;
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      run_any_election |= work->RunningElection;
+
+      /* 
+       * Start an election if we have any chance of winning.
+       * Note this is a change to the previous code, that would
+       * only run an election if nmbd was in the potential browser
+       * state. We need to run elections in any state if we're told
+       * to. JRA.
+       */
+
+      if (work->needelection && !work->RunningElection && lp_local_master())
+      {
+        /*
+         * We can only run an election for a workgroup if we have
+         * registered the WORKGROUP<1e> name, as that's the name
+         * we must listen to.
+         */
+        struct nmb_name nmbname;
+
+        make_nmb_name(&nmbname, work->work_group, 0x1e);
+        if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+          DEBUG(8,("check_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+          continue;
+        }
+
+        DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
+              work->work_group, subrec->subnet_name ));
+
+        work->ElectionCount = 0;
+        work->RunningElection = True;
+        work->needelection = False;
+      }
+    }
+  }
+  return run_any_election;
+}
+
+
+
+/****************************************************************************
+process a internal Samba message forcing an election
+***************************************************************************/
+void nmbd_message_election(int msg_type, pid_t src, void *buf, size_t len)
+{
+       struct subnet_record *subrec;
+
+       for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+               struct work_record *work;
+               for (work = subrec->workgrouplist; work; work = work->next) {
+                       if (strequal(work->work_group, lp_workgroup())) {
+                               work->needelection = True;
+                               work->ElectionCount=0;
+                               work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+                       }
+               }
+       }
+}
diff --git a/source4/nmbd/nmbd_incomingdgrams.c b/source4/nmbd/nmbd_incomingdgrams.c
new file mode 100644 (file)
index 0000000..3000c34
--- /dev/null
@@ -0,0 +1,858 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern BOOL found_lm_clients;
+
+#if 0
+
+/* XXXX note: This function is currently unsuitable for use, as it
+   does not properly check that a server is in a fit state to become
+   a backup browser before asking it to be one.
+   The code is left here to be worked on at a later date.
+*/
+
+/****************************************************************************
+Tell a server to become a backup browser
+**************************************************************************/
+
+void tell_become_backup(void)
+{
+  struct subnet_record *subrec;
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      struct server_record *servrec;
+      int num_servers = 0;
+      int num_backups = 0;
+         
+      for (servrec = work->serverlist; servrec; servrec = servrec->next)
+      {
+        num_servers++;
+             
+        if (is_myname(servrec->serv.name))
+          continue;
+             
+        if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER) 
+        {
+          num_backups++;
+          continue;
+        }
+             
+        if (servrec->serv.type & SV_TYPE_MASTER_BROWSER)
+          continue;
+             
+        if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER))
+          continue;
+             
+        DEBUG(3,("num servers: %d num backups: %d\n", 
+              num_servers, num_backups));
+             
+        /* make first server a backup server. thereafter make every
+           tenth server a backup server */
+        if (num_backups != 0 && (num_servers+9) / num_backups > 10)
+          continue;
+             
+        DEBUG(2,("sending become backup to %s %s for %s\n",
+             servrec->serv.name, inet_ntoa(subrec->bcast_ip),
+             work->work_group));
+             
+        /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+        do_announce_request(servrec->serv.name, work->work_group,
+              ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip);
+      }
+    }
+  }
+}
+#endif
+
+/*******************************************************************
+  Process an incoming host announcement packet.
+*******************************************************************/
+
+void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int ttl = IVAL(buf,1)/1000;
+  char *announce_name = buf+5;
+  uint32 servertype = IVAL(buf,23);
+  char *comment = buf+31;
+  struct work_record *work;
+  struct server_record *servrec;
+  const char *work_name;
+  char *source_name = dgram->source_name.name;
+
+  START_PROFILE(host_announce);
+  comment[43] = 0;
+  
+  DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+              nmb_namestr(&dgram->dest_name),announce_name));
+
+  DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n",
+          ttl, servertype,comment));
+
+  /* Filter servertype to remove impossible bits. */
+  servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+  /* A host announcement must be sent to the name WORKGROUP<1d>. */
+  if(dgram->dest_name.name_type != 0x1d)
+  {
+    DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1d. Allowing packet anyway.\n",
+              inet_ntoa(p->ip), dgram->dest_name.name_type));
+    /* Change it so it was. */
+    dgram->dest_name.name_type = 0x1d;
+  }
+
+  /* For a host announce the workgroup name is the destination name. */
+  work_name = dgram->dest_name.name;
+
+  /*
+   * Syntax servers version 5.1 send HostAnnounce packets to
+   * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+   * instead of WORKGROUP<1d> name. So to fix this we check if
+   * the workgroup name is our own name, and if so change it
+   * to be our primary workgroup name.
+   */
+
+  if(strequal(work_name, lp_netbios_name()))
+    work_name = lp_workgroup();
+
+  /*
+   * We are being very agressive here in adding a workgroup
+   * name on the basis of a host announcing itself as being
+   * in that workgroup. Maybe we should wait for the workgroup
+   * announce instead ? JRA.
+   */
+
+  work = find_workgroup_on_subnet(subrec, work_name);
+
+  if(servertype != 0)
+  {
+    if (work ==NULL )
+    {
+      /* We have no record of this workgroup. Add it. */
+      if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+        goto done;
+    }
+  
+    if((servrec = find_server_in_workgroup( work, announce_name))==NULL)
+    {
+      /* If this server is not already in the workgroup, add it. */
+      create_server_on_workgroup(work, announce_name, 
+                                 servertype|SV_TYPE_LOCAL_LIST_ONLY, 
+                                 ttl, comment);
+    }
+    else
+    {
+      /* Update the record. */
+      servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+      update_server_ttl( servrec, ttl);
+      StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+    }
+  }
+  else
+  {
+    /*
+     * This server is announcing it is going down. Remove it from the 
+     * workgroup.
+     */
+    if(!is_myname(announce_name) && (work != NULL) &&
+       ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)
+      )
+    {
+      remove_server_from_workgroup( work, servrec);
+    }
+  }
+  subrec->work_changed = True;
+done:
+  END_PROFILE(host_announce);
+}
+
+/*******************************************************************
+  Process an incoming WORKGROUP announcement packet.
+*******************************************************************/
+
+void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int ttl = IVAL(buf,1)/1000;
+  char *workgroup_announce_name = buf+5;
+  uint32 servertype = IVAL(buf,23);
+  char *master_name = buf+31;
+  struct work_record *work;
+  char *source_name = dgram->source_name.name;
+
+  START_PROFILE(workgroup_announce);
+  master_name[43] = 0;
+
+  DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \
+%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+              nmb_namestr(&dgram->dest_name),workgroup_announce_name));
+
+  DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n",
+           ttl, servertype, master_name));
+
+  /* Workgroup announcements must only go to the MSBROWSE name. */
+  if (!strequal(dgram->dest_name.name, MSBROWSE) || (dgram->dest_name.name_type != 0x1))
+  {
+    DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n",
+              inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+    goto done;
+  }
+
+  if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL)
+  {
+    /* We have no record of this workgroup. Add it. */
+    if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL)
+      goto done;
+  }
+  else
+  {
+    /* Update the workgroup death_time. */
+    update_workgroup_ttl(work, ttl);
+  }
+
+  if(*work->local_master_browser_name == '\0')
+  {
+    /* Set the master browser name. */
+    set_workgroup_local_master_browser_name( work, master_name );
+  }
+
+  subrec->work_changed = True;
+done:
+  END_PROFILE(workgroup_announce);
+}
+
+/*******************************************************************
+  Process an incoming local master browser announcement packet.
+*******************************************************************/
+
+void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int ttl = IVAL(buf,1)/1000;
+  char *server_name = buf+5;
+  uint32 servertype = IVAL(buf,23);
+  char *comment = buf+31;
+  char *work_name;
+  struct work_record *work;
+  struct server_record *servrec;
+  char *source_name = dgram->source_name.name;
+
+  START_PROFILE(local_master_announce);
+  comment[43] = 0;
+
+  DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+              nmb_namestr(&dgram->dest_name),server_name));
+
+  DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n",
+           ttl, servertype, comment));
+
+  /* A local master announcement must be sent to the name WORKGROUP<1e>. */
+  if(dgram->dest_name.name_type != 0x1e)
+  {
+    DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1e. Ignoring packet.\n",
+              inet_ntoa(p->ip), dgram->dest_name.name_type));
+    goto done;
+  }
+
+  /* Filter servertype to remove impossible bits. */
+  servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+  /* For a local master announce the workgroup name is the destination name. */
+  work_name = dgram->dest_name.name;
+
+  if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL)
+  {
+    /* Don't bother adding if it's a local master release announce. */
+    if(servertype == 0)
+      goto done;
+
+    /* We have no record of this workgroup. Add it. */
+    if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+      goto done;
+  }
+
+  /* If we think we're the local master browser for this workgroup,
+     we should never have got this packet. We don't see our own
+     packets.
+   */
+  if(AM_LOCAL_MASTER_BROWSER(work))
+  {
+    DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \
+a local master browser for workgroup %s and we think we are master. Forcing election.\n",
+      server_name, inet_ntoa(p->ip), work_name));
+
+    /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when
+       they have become a local master browser once, they will never
+       stop sending local master announcements. To fix this we send
+       them a reset browser packet, with level 0x2 on the __SAMBA__
+       name that only they should be listening to. */
+   
+    send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip);
+
+    /* We should demote ourself and force an election. */
+
+    unbecome_local_master_browser( subrec, work, True);
+
+    /* The actual election requests are handled in
+       nmbd_election.c */
+    goto done;
+  }  
+
+  /* Find the server record on this workgroup. If it doesn't exist, add it. */
+
+  if(servertype != 0)
+  {
+    if((servrec = find_server_in_workgroup( work, server_name))==NULL)
+    {
+      /* If this server is not already in the workgroup, add it. */
+      create_server_on_workgroup(work, server_name, 
+                                 servertype|SV_TYPE_LOCAL_LIST_ONLY, 
+                                 ttl, comment);
+    }
+    else
+    {
+      /* Update the record. */
+      servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+      update_server_ttl(servrec, ttl);
+      StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+    }
+
+    set_workgroup_local_master_browser_name( work, server_name );
+  }
+  else
+  {
+    /*
+     * This server is announcing it is going down. Remove it from the
+     * workgroup.
+     */
+    if(!is_myname(server_name) && (work != NULL) &&
+       ((servrec = find_server_in_workgroup( work, server_name))!=NULL)
+      )
+    {
+      remove_server_from_workgroup( work, servrec);
+    }
+  }
+
+  subrec->work_changed = True;
+done:
+  END_PROFILE(local_master_announce);
+}
+
+/*******************************************************************
+  Process a domain master announcement frame.
+  Domain master browsers receive these from local masters. The Domain
+  master should then issue a sync with the local master, asking for
+  that machines local server list.
+******************************************************************/
+
+void process_master_browser_announce(struct subnet_record *subrec, 
+                                     struct packet_struct *p,char *buf)
+{
+  char *local_master_name = buf;
+  struct work_record *work;
+  struct browse_cache_record *browrec;
+
+  START_PROFILE(master_browser_announce);
+  local_master_name[15] = 0;
+  
+  DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n",
+           local_master_name, inet_ntoa(p->ip)));
+  
+  if (!lp_domain_master()) 
+  {
+    DEBUG(0,("process_master_browser_announce: Not configured as domain \
+master - ignoring master announce.\n"));
+    goto done;
+  }
+  
+  if((work = find_workgroup_on_subnet(subrec, lp_workgroup())) == NULL)
+  {
+    DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n",
+           lp_workgroup(), subrec->subnet_name));
+    goto done;
+  }
+
+  if(!AM_DOMAIN_MASTER_BROWSER(work))
+  {
+    DEBUG(0,("process_master_browser_announce: Local master announce made to us from \
+%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip)));
+    goto done;
+  }
+
+  /* Add this host as a local master browser entry on the browse lists.
+     This causes a sync request to be made to it at a later date.
+   */
+
+  if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL)
+  {
+    /* Add it. */
+    create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip);
+  }
+  else
+    update_browser_death_time(browrec);
+done:
+  END_PROFILE(master_browser_announce);
+}
+
+/*******************************************************************
+  Process an incoming LanMan host announcement packet.
+*******************************************************************/
+
+void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  uint32 servertype = IVAL(buf,1);
+  int osmajor=CVAL(buf,5);           /* major version of node software */
+  int osminor=CVAL(buf,6);           /* minor version of node software */
+  int ttl = SVAL(buf,7);
+  char *announce_name = buf+9;
+  struct work_record *work;
+  struct server_record *servrec;
+  const char *work_name;
+  char *source_name = dgram->source_name.name;
+  pstring comment;
+  char *s = buf+9;
+
+  START_PROFILE(lm_host_announce);
+  s = skip_string(s,1);
+  StrnCpy(comment, s, 43);
+
+  DEBUG(3,("process_lm_host_announce: LM Announcement from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+              nmb_namestr(&dgram->dest_name),announce_name));
+
+  DEBUG(5,("process_lm_host_announce: os=(%d,%d) ttl=%d server type=%08x comment=%s\n",
+          osmajor, osminor, ttl, servertype,comment));
+
+  if ((osmajor < 36) || (osmajor > 38) || (osminor !=0))
+  {
+    DEBUG(5,("process_lm_host_announce: LM Announcement packet does not \
+originate from OS/2 Warp client. Ignoring packet.\n"));
+    /* Could have been from a Windows machine (with its LM Announce enabled),
+       or a Samba server. Then don't disrupt the current browse list. */
+    goto done;
+  }
+
+  /* Filter servertype to remove impossible bits. */
+  servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+  /* A LanMan host announcement must be sent to the name WORKGROUP<00>. */
+  if(dgram->dest_name.name_type != 0x00)
+  {
+    DEBUG(2,("process_lm_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x00. Allowing packet anyway.\n",
+              inet_ntoa(p->ip), dgram->dest_name.name_type));
+    /* Change it so it was. */
+    dgram->dest_name.name_type = 0x00;
+  }
+
+  /* For a LanMan host announce the workgroup name is the destination name. */
+  work_name = dgram->dest_name.name;
+
+  /*
+   * Syntax servers version 5.1 send HostAnnounce packets to
+   * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+   * instead of WORKGROUP<1d> name. So to fix this we check if
+   * the workgroup name is our own name, and if so change it
+   * to be our primary workgroup name. This code is probably
+   * not needed in the LanMan announce code, but it won't hurt.
+   */
+
+  if(strequal(work_name, lp_netbios_name()))
+    work_name = lp_workgroup();
+
+  /*
+   * We are being very agressive here in adding a workgroup
+   * name on the basis of a host announcing itself as being
+   * in that workgroup. Maybe we should wait for the workgroup
+   * announce instead ? JRA.
+   */
+
+  work = find_workgroup_on_subnet(subrec, work_name);
+
+  if(servertype != 0)
+  {
+    if (work == NULL)
+    {
+      /* We have no record of this workgroup. Add it. */
+      if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+        goto done;
+    }
+
+    if((servrec = find_server_in_workgroup( work, announce_name))==NULL)
+    {
+      /* If this server is not already in the workgroup, add it. */
+      create_server_on_workgroup(work, announce_name,
+                                 servertype|SV_TYPE_LOCAL_LIST_ONLY,
+                                 ttl, comment);
+    }
+    else
+    {
+      /* Update the record. */
+      servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+      update_server_ttl( servrec, ttl);
+      StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+    }
+  }
+  else
+  {
+    /*
+     * This server is announcing it is going down. Remove it from the
+     * workgroup.
+     */
+    if(!is_myname(announce_name) && (work != NULL) &&
+       ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)
+      )
+    {
+      remove_server_from_workgroup( work, servrec);
+    }
+  }
+
+  subrec->work_changed = True;
+  found_lm_clients = True;
+done:
+  END_PROFILE(lm_host_announce);
+}
+
+/****************************************************************************
+  Send a backup list response.
+*****************************************************************************/
+static void send_backup_list_response(struct subnet_record *subrec, 
+                                     struct work_record *work,
+                                     struct nmb_name *send_to_name,
+                                     unsigned char max_number_requested,
+                                     uint32 token, struct in_addr sendto_ip,
+                                     int port)
+{                     
+  char outbuf[1024];
+  char *p, *countptr;
+  unsigned int count = 0;
+#if 0
+  struct server_record *servrec;
+#endif
+
+  memset(outbuf,'\0',sizeof(outbuf));
+
+  DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n",
+          work->work_group, nmb_namestr(send_to_name), inet_ntoa(sendto_ip)));
+  
+  p = outbuf;
+  
+  SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */
+  p++;
+
+  countptr = p;
+  p++;
+
+  SIVAL(p,0,token); /* The sender's unique info. */
+  p += 4;
+  
+  /* We always return at least one name - our own. */
+  count = 1;
+  StrnCpy(p,lp_netbios_name(),15);
+  strupper(p);
+  p = skip_string(p,1);
+
+  /* Look for backup browsers in this workgroup. */
+
+#if 0
+  /* we don't currently send become_backup requests so we should never
+     send any other servers names out as backups for our
+     workgroup. That's why this is commented out (tridge) */
+
+  /*
+   * NB. Note that the struct work_record here is not neccessarily
+   * attached to the subnet *subrec.
+   */
+
+  for (servrec = work->serverlist; servrec; servrec = servrec->next)
+  { 
+    int len = PTR_DIFF(p, outbuf);
+    if((sizeof(outbuf) - len) < 16)
+      break;
+
+    if(count >= (unsigned int)max_number_requested)
+      break;
+
+    if(strnequal(servrec->serv.name, lp_netbios_name(),15))
+      continue;
+
+    if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER))
+      continue;
+
+    StrnCpy(p, servrec->serv.name, 15);
+    strupper(p);
+    count++;
+
+    DEBUG(5,("send_backup_list_response: Adding server %s number %d\n",
+              p, count));
+
+    p = skip_string(p,1);
+  }
+#endif
+
+  SCVAL(countptr, 0, count);
+
+  DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n",
+          send_to_name->name, inet_ntoa(sendto_ip), count));
+
+  send_mailslot(True, BROWSE_MAILSLOT,
+                outbuf,PTR_DIFF(p,outbuf),
+                lp_netbios_name(), 0, 
+                send_to_name->name,0,
+                sendto_ip, subrec->myip, port);
+}
+
+/*******************************************************************
+  Process a send backup list request packet.
+
+  A client sends a backup list request to ask for a list of servers on
+  the net that maintain server lists for a domain. A server is then
+  chosen from this list to send NetServerEnum commands to to list
+  available servers.
+
+********************************************************************/
+
+void process_get_backup_list_request(struct subnet_record *subrec,
+                                     struct packet_struct *p,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  struct work_record *work;
+  unsigned char max_number_requested = CVAL(buf,0);
+  uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */
+  int name_type = dgram->dest_name.name_type;
+  char *workgroup_name = dgram->dest_name.name;
+  struct subnet_record *search_subrec = subrec;
+
+  START_PROFILE(get_backup_list);
+  DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n",
+           nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+           nmb_namestr(&dgram->dest_name)));
+  
+  /* We have to be a master browser, or a domain master browser
+     for the requested workgroup. That means it must be our
+     workgroup. */
+
+  if(strequal(workgroup_name, lp_workgroup()) == False)
+  {
+    DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n",
+           workgroup_name));
+    goto done;
+  }
+
+  if((work = find_workgroup_on_subnet(search_subrec, workgroup_name)) == NULL)
+  {
+    DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, search_subrec->subnet_name));
+    goto done;
+  }
+
+  /* 
+   * If the packet was sent to WORKGROUP<1b> instead
+   * of WORKGROUP<1d> then it was unicast to us a domain master
+   * browser. Change search subrec to unicast.
+   */
+
+  if(name_type == 0x1b)
+  {
+    /* We must be a domain master browser in order to
+       process this packet. */
+
+    if(!AM_DOMAIN_MASTER_BROWSER(work))
+    {
+      DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a domain master browser.\n", workgroup_name));
+      goto done;
+    }
+
+    search_subrec = unicast_subnet;
+  }
+  else if (name_type == 0x1d)
+  {
+    /* We must be a local master browser in order to
+       process this packet. */
+
+    if(!AM_LOCAL_MASTER_BROWSER(work))
+    {
+      DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a local master browser.\n", workgroup_name));
+      goto done;
+    }
+  }
+  else
+  {
+    DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n",
+            name_type));
+    goto done;
+  }
+
+  send_backup_list_response(subrec, work, &dgram->source_name, 
+                            max_number_requested, token, p->ip, p->port);
+done:
+  END_PROFILE(get_backup_list);
+}
+
+/*******************************************************************
+  Process a reset browser state packet.
+
+  Diagnostic packet:
+  0x1 - Stop being a master browser and become a backup browser.
+  0x2 - Discard browse lists, stop being a master browser, try again.
+  0x4 - Stop being a master browser forever.
+         
+******************************************************************/
+
+void process_reset_browser(struct subnet_record *subrec,
+                                  struct packet_struct *p,char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int state = CVAL(buf,0);
+  struct subnet_record *sr;
+
+  START_PROFILE(reset_browser);
+  DEBUG(1,("process_reset_browser: received diagnostic browser reset \
+request from %s IP %s state=0x%X\n",
+             nmb_namestr(&dgram->source_name), inet_ntoa(p->ip), state));
+
+  /* Stop being a local master browser on all our broadcast subnets. */
+  if (state & 0x1)
+  {
+    for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr))
+    {
+      struct work_record *work;
+      for (work = sr->workgrouplist; work; work = work->next)
+      {
+        if (AM_LOCAL_MASTER_BROWSER(work))
+          unbecome_local_master_browser(sr, work, True);
+      }
+    }
+  }
+  
+  /* Discard our browse lists. */
+  if (state & 0x2)
+  {
+    /*
+     * Calling expire_workgroups_and_servers with a -1
+     * time causes all servers not marked with a PERMANENT_TTL
+     * on the workgroup lists to be discarded, and all 
+     * workgroups with empty server lists to be discarded.
+     * This means we keep our own server names and workgroup
+     * as these have a PERMANENT_TTL.
+     */
+
+    expire_workgroups_and_servers(-1);
+  }
+  
+  /* Request to stop browsing altogether. */
+  if (state & 0x4)
+    DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n"));
+
+  END_PROFILE(reset_browser);
+}
+
+/*******************************************************************
+  Process an announcement request packet.
+  We don't respond immediately, we just check it's a request for
+  our workgroup and then set the flag telling the announce code
+  in nmbd_sendannounce.c:announce_my_server_names that an 
+  announcement is needed soon.
+******************************************************************/
+
+void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  struct work_record *work;
+  char *workgroup_name = dgram->dest_name.name;
+  START_PROFILE(announce_request);
+  DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n",
+           nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+           nmb_namestr(&dgram->dest_name)));
+  
+  /* We only send announcement requests on our workgroup. */
+  if(strequal(workgroup_name, lp_workgroup()) == False)
+  {
+    DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n",
+           workgroup_name));
+    goto done;
+  }
+
+  if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL)
+  {
+    DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+            workgroup_name));
+    goto done;
+  }
+
+  work->needannounce = True;
+done:
+  END_PROFILE(lm_host_announce);
+}
+
+/*******************************************************************
+  Process a LanMan announcement request packet.
+  We don't respond immediately, we just check it's a request for
+  our workgroup and then set the flag telling that we have found
+  a LanMan client (DOS or OS/2) and that we will have to start
+  sending LanMan announcements (unless specifically disabled
+  through the "lm announce" parameter in smb.conf)
+******************************************************************/
+
+void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  char *workgroup_name = dgram->dest_name.name;
+
+  START_PROFILE(lm_announce_request);
+  DEBUG(3,("process_lm_announce_request: Announce request from %s IP %s to %s.\n",
+           nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+           nmb_namestr(&dgram->dest_name)));
+
+  /* We only send announcement requests on our workgroup. */
+  if(strequal(workgroup_name, lp_workgroup()) == False)
+  {
+    DEBUG(7,("process_lm_announce_request: Ignoring announce request for workgroup %s.\n",
+           workgroup_name));
+    goto done;
+  }
+
+  if(find_workgroup_on_subnet(subrec, workgroup_name) == NULL)
+  {
+    DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+            workgroup_name));
+    goto done;
+  }
+
+  found_lm_clients = True;
+done:
+  END_PROFILE(lm_host_announce);
+}
diff --git a/source4/nmbd/nmbd_incomingrequests.c b/source4/nmbd/nmbd_incomingrequests.c
new file mode 100644 (file)
index 0000000..916f776
--- /dev/null
@@ -0,0 +1,596 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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.
+   
+   This file contains all the code to process NetBIOS requests coming
+   in on port 137. It does not deal with the code needed to service
+   WINS server requests, but only broadcast and unicast requests.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Send a name release response.
+**************************************************************************/
+
+static void send_name_release_response(int rcode, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char rdata[6];
+
+  memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+  
+  reply_netbios_packet(p,                            /* Packet to reply to. */
+                       rcode,                        /* Result code. */
+                       NMB_REL,                      /* nmbd type code. */
+                       NMB_NAME_RELEASE_OPCODE,      /* opcode. */
+                       0,                            /* ttl. */
+                       rdata,                        /* data to send. */
+                       6);                           /* data length. */
+}
+
+/****************************************************************************
+Process a name release packet on a broadcast subnet.
+Ignore it if it's not one of our names.
+****************************************************************************/
+
+void process_name_release_request(struct subnet_record *subrec, 
+                                  struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct in_addr owner_ip;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  BOOL group = (nb_flags & NB_GROUP) ? True : False;
+  struct name_record *namerec;
+  int rcode = 0;
+  
+  putip((char *)&owner_ip,&nmb->additional->rdata[2]);  
+  
+  if(!bcast)
+  {
+    /* We should only get broadcast name release packets here.
+       Anyone trying to release unicast should be going to a WINS
+       server. If the code gets here, then either we are not a wins
+       server and they sent it anyway, or we are a WINS server and
+       the request was malformed. Either way, log an error here.
+       and send an error reply back.
+     */
+    DEBUG(0,("process_name_release_request: unicast name release request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name));      
+
+    send_name_release_response(FMT_ERR, p);
+    return;
+  }
+
+  DEBUG(3,("process_name_release_request: Name release on name %s, \
+subnet %s from owner IP %s\n",
+           nmb_namestr(&nmb->question.question_name),
+           subrec->subnet_name, inet_ntoa(owner_ip)));
+  
+  /* If someone is releasing a broadcast group name, just ignore it. */
+  if( group && !ismyip(owner_ip) )
+    return;
+
+  /*
+   * Code to work around a bug in FTP OnNet software NBT implementation.
+   * They do a broadcast name release for WORKGROUP<0> and WORKGROUP<1e>
+   * names and *don't set the group bit* !!!!!
+   */
+
+  if( !group && !ismyip(owner_ip) && strequal(question->name, lp_workgroup()) && 
+      ((question->name_type == 0x0) || (question->name_type == 0x1e)))
+  {
+    DEBUG(6,("process_name_release_request: FTP OnNet bug workaround. Ignoring \
+group release name %s from IP %s on subnet %s with no group bit set.\n",
+        nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name ));
+    return;
+  }
+
+  namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_ANY_NAME);
+
+  /* We only care about someone trying to release one of our names. */
+  if( namerec
+   && ( (namerec->data.source == SELF_NAME)
+     || (namerec->data.source == PERMANENT_NAME) ) )
+  {
+    rcode = ACT_ERR;
+    DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \
+on subnet %s being rejected as it is one of our names.\n", 
+          nmb_namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name));
+  }
+
+  if(rcode == 0)
+    return;
+
+  /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+  send_name_release_response(rcode, p);
+}
+
+/****************************************************************************
+Send a name registration response.
+**************************************************************************/
+
+static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char rdata[6];
+
+  memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+  
+  reply_netbios_packet(p,                             /* Packet to reply to. */
+                       rcode,                         /* Result code. */
+                       NMB_REG,                       /* nmbd type code. */
+                       NMB_NAME_REG_OPCODE,           /* opcode. */
+                       ttl,                           /* ttl. */
+                       rdata,                         /* data to send. */
+                       6);                            /* data length. */
+}
+
+/****************************************************************************
+Process a name refresh request on a broadcast subnet.
+**************************************************************************/
+     
+void process_name_refresh_request(struct subnet_record *subrec,
+                                  struct packet_struct *p)
+{    
+     
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  struct in_addr from_ip;
+  
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+  if(!bcast)
+  { 
+    /* We should only get broadcast name refresh packets here.
+       Anyone trying to refresh unicast should be going to a WINS
+       server. If the code gets here, then either we are not a wins
+       server and they sent it anyway, or we are a WINS server and
+       the request was malformed. Either way, log an error here.
+       and send an error reply back.
+     */
+    DEBUG(0,("process_name_refresh_request: unicast name registration request \
+received for name %s from IP %s on subnet %s.\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    DEBUG(0,("Error - should be sent to WINS server\n"));
+    
+    send_name_registration_response(FMT_ERR, 0, p);
+    return;
+  } 
+
+  /* Just log a message. We really don't care about broadcast name
+     refreshes. */
+     
+  DEBUG(3,("process_name_refresh_request: Name refresh for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+     
+}
+    
+/****************************************************************************
+Process a name registration request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_registration_request(struct subnet_record *subrec, 
+                                       struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  BOOL group = (nb_flags & NB_GROUP) ? True : False;
+  struct name_record *namerec = NULL;
+  int ttl = nmb->additional->ttl;
+  struct in_addr from_ip;
+  
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+  
+  if(!bcast)
+  {
+    /* We should only get broadcast name registration packets here.
+       Anyone trying to register unicast should be going to a WINS
+       server. If the code gets here, then either we are not a wins
+       server and they sent it anyway, or we are a WINS server and
+       the request was malformed. Either way, log an error here.
+       and send an error reply back.
+     */
+    DEBUG(0,("process_name_registration_request: unicast name registration request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));      
+
+    send_name_registration_response(FMT_ERR, 0, p);
+    return;
+  }
+
+  DEBUG(3,("process_name_registration_request: Name registration for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+  
+  /* See if the name already exists. */
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+  /* 
+   * If the name being registered exists and is a WINS_PROXY_NAME 
+   * then delete the WINS proxy name entry so we don't reply erroneously
+   * later to queries.
+   */
+
+  if((namerec != NULL) && (namerec->data.source == WINS_PROXY_NAME))
+  {
+    remove_name_from_namelist( subrec, namerec );
+    namerec = NULL;
+  }
+
+  if (!group)
+  {
+    /* Unique name. */
+
+    if( (namerec != NULL)
+     && ( (namerec->data.source == SELF_NAME)
+       || (namerec->data.source == PERMANENT_NAME)
+       || NAME_GROUP(namerec) ) )
+    {
+      /* No-one can register one of Samba's names, nor can they
+         register a name that's a group name as a unique name */
+
+      send_name_registration_response(ACT_ERR, 0, p);
+      return;
+    }
+    else if(namerec != NULL)
+    {
+      /* Update the namelist record with the new information. */
+      namerec->data.ip[0] = from_ip;
+      update_name_ttl(namerec, ttl);
+
+      DEBUG(3,("process_name_registration_request: Updated name record %s \
+with IP %s on subnet %s\n",nmb_namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
+      return;
+    }
+  }
+  else
+  {
+    /* Group name. */
+
+    if( (namerec != NULL)
+     && !NAME_GROUP(namerec)
+     && ( (namerec->data.source == SELF_NAME)
+       || (namerec->data.source == PERMANENT_NAME) ) )
+    {
+      /* Disallow group names when we have a unique name. */
+      send_name_registration_response(ACT_ERR, 0, p);  
+      return;  
+    }  
+  }
+}
+
+/****************************************************************************
+This is used to sort names for a name status into a sensible order.
+We put our own names first, then in alphabetical order.
+**************************************************************************/
+
+static int status_compare(char *n1,char *n2)
+{
+  int l1,l2,l3;
+
+  /* It's a bit tricky because the names are space padded */
+  for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ;
+  for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ;
+  l3 = strlen(lp_netbios_name());
+
+  if ((l1==l3) && strncmp(n1,lp_netbios_name(),l3) == 0 && 
+      (l2!=l3 || strncmp(n2,lp_netbios_name(),l3) != 0))
+    return -1;
+
+  if ((l2==l3) && strncmp(n2,lp_netbios_name(),l3) == 0 && 
+      (l1!=l3 || strncmp(n1,lp_netbios_name(),l3) != 0))
+    return 1;
+
+  return memcmp(n1,n2,18);
+}
+
+
+/****************************************************************************
+  Process a node status query
+  ****************************************************************************/
+
+void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char *qname   = nmb->question.question_name.name;
+  int ques_type = nmb->question.question_name.name_type;
+  char rdata[MAX_DGRAM_SIZE];
+  char *countptr, *buf, *bufend, *buf0;
+  int names_added,i;
+  struct name_record *namerec;
+
+  DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s.\n", nmb_namestr(&nmb->question.question_name), inet_ntoa(p->ip),
+          subrec->subnet_name));
+
+  if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name,
+                                    FIND_SELF_NAME)) == 0)
+  {
+    DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s - name not found.\n", nmb_namestr(&nmb->question.question_name),
+          inet_ntoa(p->ip), subrec->subnet_name));
+
+    return;
+  }
+  /* this is not an exact calculation. the 46 is for the stats buffer
+     and the 60 is to leave room for the header etc */
+  bufend = &rdata[MAX_DGRAM_SIZE] - (18 + 46 + 60);
+  countptr = buf = rdata;
+  buf += 1;
+  buf0 = buf;
+
+  names_added = 0;
+
+  namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+
+  while (buf < bufend) 
+  {
+    if( (namerec->data.source == SELF_NAME)
+     || (namerec->data.source == PERMANENT_NAME) )
+    {
+      int name_type = namerec->name.name_type;
+      
+      if (!strequal(namerec->name.name,"*") &&
+          !strequal(namerec->name.name,"__SAMBA__") &&
+          (name_type < 0x1b || name_type >= 0x20 || 
+           ques_type < 0x1b || ques_type >= 0x20 ||
+           strequal(qname, namerec->name.name)))
+      {
+        /* Start with the name. */
+        memset(buf,'\0',18);
+        slprintf(buf, 17, "%-15.15s",namerec->name.name);
+        strupper(buf);
+        
+        /* Put the name type and netbios flags in the buffer. */
+        buf[15] = name_type;
+        set_nb_flags( &buf[16],namerec->data.nb_flags );
+        buf[16] |= NB_ACTIVE; /* all our names are active */
+
+        buf += 18;
+
+        names_added++;
+      }
+    }
+
+    /* Remove duplicate names. */
+    if (names_added > 1) {
+           qsort( buf0, names_added, 18, QSORT_CAST status_compare );
+    }
+
+    for( i=1; i < names_added ; i++ )
+    {
+      if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) 
+      {
+        names_added--;
+        if (names_added == i)
+          break;
+        memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+        i--;
+      }
+    }
+
+    buf = buf0 + 18*names_added;
+
+    namerec = (struct name_record *)ubi_trNext( namerec );
+
+    if (!namerec)
+    {
+      /* End of the subnet specific name list. Now 
+         add the names on the unicast subnet . */
+      struct subnet_record *uni_subrec = unicast_subnet;
+
+      if (uni_subrec != subrec)
+      {
+        subrec = uni_subrec;
+        namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+      }
+    }
+    if (!namerec)
+      break;
+
+  }
+  
+  SCVAL(countptr,0,names_added);
+  
+  /* We don't send any stats as they could be used to attack
+     the protocol. */
+  memset(buf,'\0',46);
+  
+  buf += 46;
+  
+  /* Send a NODE STATUS RESPONSE */
+  reply_netbios_packet(p,                            /* Packet to reply to. */
+                       0,                            /* Result code. */
+                       NMB_STATUS,                   /* nmbd type code. */
+                       NMB_NAME_QUERY_OPCODE,        /* opcode. */
+                      0,                            /* ttl. */
+                       rdata,                        /* data to send. */
+                       PTR_DIFF(buf,rdata));         /* data length. */
+}
+
+
+/***************************************************************************
+Process a name query.
+
+For broadcast name queries:
+
+  - Only reply if the query is for one of YOUR names.
+  - NEVER send a negative response to a broadcast query.
+
+****************************************************************************/
+
+void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+       struct nmb_packet *nmb = &p->packet.nmb;
+       struct nmb_name *question = &nmb->question.question_name;
+       int name_type = question->name_type;
+       BOOL bcast = nmb->header.nm_flags.bcast;
+       int ttl=0;
+       int rcode = 0;
+       char *prdata = NULL;
+       char rdata[6];
+       BOOL success = False;
+       struct name_record *namerec = NULL;
+       int reply_data_len = 0;
+       int i;
+       
+       DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n", 
+                inet_ntoa(p->ip), subrec->subnet_name, nmb_namestr(question)));
+  
+       /* Look up the name in the cache - if the request is a broadcast request that
+          came from a subnet we don't know about then search all the broadcast subnets
+          for a match (as we don't know what interface the request came in on). */
+
+       if(subrec == remote_broadcast_subnet)
+               namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME);
+       else
+               namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+       /* Check if it is a name that expired */
+       if (namerec && 
+           ((namerec->data.death_time != PERMANENT_TTL) && 
+            (namerec->data.death_time < p->timestamp))) {
+               DEBUG(5,("process_name_query_request: expired name %s\n", nmb_namestr(&namerec->name)));
+               namerec = NULL;
+       }
+
+       if (namerec) {
+               /* 
+                * Always respond to unicast queries.
+                * Don't respond to broadcast queries unless the query is for
+                * a name we own, a Primary Domain Controller name, or a WINS_PROXY 
+                * name with type 0 or 0x20. WINS_PROXY names are only ever added
+                * into the namelist if we were configured as a WINS proxy.
+                */
+               
+               if (!bcast || 
+                   (bcast && ((name_type == 0x1b) ||
+                              (namerec->data.source == SELF_NAME) ||
+                              (namerec->data.source == PERMANENT_NAME) ||
+                              ((namerec->data.source == WINS_PROXY_NAME) &&
+                               ((name_type == 0) || (name_type == 0x20)))))) {
+                       /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY, 
+                          or it's a Domain Master type. */
+
+                       /*
+                        * If this is a WINS_PROXY_NAME, then ceck that none of the IP 
+                        * addresses we are returning is on the same broadcast subnet 
+                        * as the requesting packet. If it is then don't reply as the 
+                        * actual machine will be replying also and we don't want two 
+                        * replies to a broadcast query.
+                        */
+                       
+                       if (namerec->data.source == WINS_PROXY_NAME) {
+                               for( i = 0; i < namerec->data.num_ips; i++) {
+                                       if (same_net(namerec->data.ip[i], subrec->myip, subrec->mask_ip)) {
+                                               DEBUG(5,("process_name_query_request: name %s is a WINS proxy name and is also on the same subnet (%s) as the requestor. Not replying.\n", 
+                                                        nmb_namestr(&namerec->name), subrec->subnet_name ));
+                                               return;
+                                       }
+                               }
+                       }
+
+                       ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+                               namerec->data.death_time - p->timestamp : lp_max_ttl();
+
+                       /* Copy all known ip addresses into the return data. */
+                       /* Optimise for the common case of one IP address so 
+                          we don't need a malloc. */
+
+                       if (namerec->data.num_ips == 1) {
+                               prdata = rdata;
+                       } else {
+                               if ((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) {
+                                       DEBUG(0,("process_name_query_request: malloc fail !\n"));
+                                       return;
+                               }
+                       }
+
+                       for (i = 0; i < namerec->data.num_ips; i++) {
+                               set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+                               putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+                       }
+
+                       sort_query_replies(prdata, i, p->ip);
+                       
+                       reply_data_len = namerec->data.num_ips * 6;
+                       success = True;
+               }
+       }
+
+       /*
+        * If a machine is broadcasting a name lookup request and we have lp_wins_proxy()
+        * set we should initiate a WINS query here. On success we add the resolved name 
+        * into our namelist with a type of WINS_PROXY_NAME and then reply to the query.
+        */
+       
+       if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() && 
+          bcast && (subrec != remote_broadcast_subnet)) {
+               make_wins_proxy_name_query_request( subrec, p, question );
+               return;
+       }
+
+       if (!success && bcast) {
+               if(prdata != rdata)
+                       SAFE_FREE(prdata);
+               return; /* Never reply with a negative response to broadcasts. */
+       }
+
+       /* 
+        * Final check. From observation, if a unicast packet is sent
+        * to a non-WINS server with the recursion desired bit set
+        * then never send a negative response.
+        */
+       
+       if(!success && !bcast && nmb->header.nm_flags.recursion_desired) {
+               if(prdata != rdata)
+                       SAFE_FREE(prdata);
+               return;
+       }
+
+       if (success) {
+               rcode = 0;
+               DEBUG(3,("OK\n"));
+       } else {
+               rcode = NAM_ERR;
+               DEBUG(3,("UNKNOWN\n"));      
+       }
+
+       /* See rfc1002.txt 4.2.13. */
+
+       reply_netbios_packet(p,                              /* Packet to reply to. */
+                            rcode,                          /* Result code. */
+                            NMB_QUERY,                      /* nmbd type code. */
+                            NMB_NAME_QUERY_OPCODE,          /* opcode. */
+                            ttl,                            /* ttl. */
+                            prdata,                         /* data to send. */
+                            reply_data_len);                /* data length. */
+       
+       if(prdata != rdata)
+               SAFE_FREE(prdata);
+}
diff --git a/source4/nmbd/nmbd_lmhosts.c b/source4/nmbd/nmbd_lmhosts.c
new file mode 100644 (file)
index 0000000..c47384c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Jeremy Allison 1994-1998
+
+   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.
+
+   Revision History:
+
+   Handle lmhosts file reading.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Load a lmhosts file.
+****************************************************************************/
+void load_lmhosts_file(char *fname)
+{  
+  char *name;
+  int name_type;
+  struct in_addr ipaddr;
+  XFILE *fp = startlmhosts( fname );
+  TALLOC_CTX *mem_ctx;
+
+  if (!fp) {
+    DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n",
+             fname, strerror(errno)));
+    return;
+  }
+  mem_ctx = talloc_init("load_lmhosts_files");
+  if (!mem_ctx) {
+    DEBUG(2,("load_lmhosts_file: No memory to open lmhosts file %s. Error was %s\n",
+             fname, strerror(errno)));
+    return;
+  }
+  while (getlmhostsent(mem_ctx, fp, name, &name_type, &ipaddr) )
+  {
+    struct subnet_record *subrec = NULL;
+    enum name_source source = LMHOSTS_NAME;
+
+    /* We find a relevent subnet to put this entry on, then add it. */
+    /* Go through all the broadcast subnets and see if the mask matches. */
+    for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+    {
+      if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip))
+        break;
+    }
+  
+    /* If none match add the name to the remote_broadcast_subnet. */
+    if(subrec == NULL)
+      subrec = remote_broadcast_subnet;
+
+    if(name_type == -1)
+    {
+      /* Add the (0) and (0x20) names directly into the namelist for this subnet. */
+      (void)add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+      (void)add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+    }
+    else
+    {
+      /* Add the given name type to the subnet namelist. */
+      (void)add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+    }
+  }
+   
+  endlmhosts(fp);
+}
+
+/****************************************************************************
+  Find a name read from the lmhosts file. We secretly check the names on
+  the remote_broadcast_subnet as if the name was added to a regular broadcast
+  subnet it will be found by normal name query processing.
+****************************************************************************/
+
+BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp)
+{
+  struct name_record *namerec;
+
+  *namerecp = NULL;
+
+  if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, 
+                                 FIND_ANY_NAME))==NULL)
+    return False;
+
+  if(!NAME_IS_ACTIVE(namerec) || (namerec->data.source != LMHOSTS_NAME))
+    return False;
+
+  *namerecp = namerec;
+  return True;
+}
diff --git a/source4/nmbd/nmbd_logonnames.c b/source4/nmbd/nmbd_logonnames.c
new file mode 100644 (file)
index 0000000..40edc68
--- /dev/null
@@ -0,0 +1,172 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+  Fail to become a Logon server on a subnet.
+  ****************************************************************************/
+static void become_logon_server_fail(struct subnet_record *subrec,
+                                      struct response_record *rrec,
+                                      struct nmb_name *fail_name)
+{
+  struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name);
+  struct server_record *servrec;
+
+  if(!work)
+  {
+    DEBUG(0,("become_logon_server_fail: Error - cannot find \
+workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name));
+    return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), fail_name->name, subrec->subnet_name));
+    work->log_state = LOGON_NONE;
+    return;
+  }
+
+  /* Set the state back to LOGON_NONE. */
+  work->log_state = LOGON_NONE;
+
+  servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL;
+
+  DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+       work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+
+}
+
+/****************************************************************************
+  Become a Logon server on a subnet.
+  ****************************************************************************/
+
+static void become_logon_server_success(struct subnet_record *subrec,
+                                        struct userdata_struct *userdata,
+                                        struct nmb_name *registered_name,
+                                        uint16 nb_flags,
+                                        int ttl, struct in_addr registered_ip)
+{
+  struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name);
+  struct server_record *servrec;
+
+  if(!work)
+  {
+    DEBUG(0,("become_logon_server_success: Error - cannot find \
+workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name));
+    return;
+  }
+
+  if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL)
+  {
+    DEBUG(0,("become_logon_server_success: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+       lp_netbios_name(), registered_name->name, subrec->subnet_name));
+    work->log_state = LOGON_NONE;
+    return;
+  }
+
+  /* Set the state in the workgroup structure. */
+  work->log_state = LOGON_SRV; /* Become domain master. */
+
+  /* Update our server status. */
+  servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER);
+  /* To allow Win95 policies to load we need to set type domain
+     controller.
+   */
+  servrec->serv.type |= SV_TYPE_DOMAIN_CTRL;
+
+  /* Tell the namelist writer to write out a change. */
+  subrec->work_changed = True;
+
+  /*
+   * Add the WORKGROUP<1C> name to the UNICAST subnet with the IP address
+   * for this subnet so we will respond to queries on this name.
+   */
+  {
+         struct nmb_name nmbname;
+         make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+         insert_permanent_name_into_unicast(subrec, &nmbname, 0x1c);
+  }
+
+  DEBUG(0,("become_logon_server_success: Samba is now a logon server \
+for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+}
+
+/*******************************************************************
+  Become a logon server by attempting to register the WORKGROUP<1c>
+  group name.
+******************************************************************/
+
+static void become_logon_server(struct subnet_record *subrec,
+                                struct work_record *work)
+{
+  DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \
+on subnet %s\n", work->work_group,subrec->subnet_name));
+
+  DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n",
+          work->work_group));
+  work->log_state = LOGON_WAIT;
+
+  register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP,
+                become_logon_server_success,
+                become_logon_server_fail, NULL);
+}
+
+/*****************************************************************************
+  Add the internet group <1c> logon names by unicast and broadcast.
+  ****************************************************************************/
+void add_logon_names(void)
+{
+  struct subnet_record *subrec;
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+    if (work && (work->log_state == LOGON_NONE))
+    {
+      struct nmb_name nmbname;
+      make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+
+      if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL)
+      {
+        if( DEBUGLVL( 0 ) )
+        {
+          dbgtext( "add_domain_logon_names:\n" );
+          dbgtext( "Attempting to become logon server " );
+          dbgtext( "for workgroup %s ", lp_workgroup() );
+          dbgtext( "on subnet %s\n", subrec->subnet_name );
+        }
+        become_logon_server(subrec, work);
+      }
+    }
+  }
+}
diff --git a/source4/nmbd/nmbd_mynames.c b/source4/nmbd/nmbd_mynames.c
new file mode 100644 (file)
index 0000000..dd66821
--- /dev/null
@@ -0,0 +1,228 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail funtion when registering my netbios names.
+  **************************************************************************/
+
+static void my_name_register_failed(struct subnet_record *subrec,
+                              struct response_record *rrec, struct nmb_name *nmbname)
+{
+  DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n",
+            nmb_namestr(nmbname), subrec->subnet_name));
+}
+
+
+/****************************************************************************
+  Add my workgroup and my given names to one subnet
+  Also add the magic Samba names.
+  **************************************************************************/
+void register_my_workgroup_one_subnet(struct subnet_record *subrec)
+{
+       int i;
+
+       struct work_record *work;
+
+       /* Create the workgroup on the subnet. */
+       if((work = create_workgroup_on_subnet(subrec, lp_workgroup(), 
+                                             PERMANENT_TTL)) == NULL) {
+               DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \
+Exiting.\n", lp_workgroup(), subrec->subnet_name));
+               return;
+       }
+
+       /* Each subnet entry, except for the wins_server_subnet has
+           the magic Samba names. */
+       add_samba_names_to_subnet(subrec);
+
+       /* Register all our names including aliases. */
+       for (i=0; my_netbios_names(i); i++) {
+               register_name(subrec, my_netbios_names(i),0x20,samba_nb_type,
+                             NULL,
+                             my_name_register_failed, NULL);
+               register_name(subrec, my_netbios_names(i),0x03,samba_nb_type,
+                             NULL,
+                             my_name_register_failed, NULL);
+               register_name(subrec, my_netbios_names(i),0x00,samba_nb_type,
+                             NULL,
+                             my_name_register_failed, NULL);
+       }
+       
+       /* Initiate election processing, register the workgroup names etc. */
+       initiate_myworkgroup_startup(subrec, work);
+}
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+static void insert_refresh_name_into_unicast( struct subnet_record *subrec,
+                                                struct nmb_name *nmbname, uint16 nb_type )
+{
+  struct name_record *namerec;
+
+  if (!we_are_a_wins_client()) {
+    insert_permanent_name_into_unicast(subrec, nmbname, nb_type);
+    return;
+  }
+
+  if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL)
+  {
+    /* The name needs to be created on the unicast subnet. */
+    (void)add_name_to_subnet( unicast_subnet, nmbname->name,
+                              nmbname->name_type, nb_type,
+                              MIN(lp_max_ttl(), MAX_REFRESH_TIME), SELF_NAME, 1, &subrec->myip);
+  }
+  else
+  {
+    /* The name already exists on the unicast subnet. Add our local
+       IP for the given broadcast subnet to the name. */
+    add_ip_to_name_record( namerec, subrec->myip);
+  }
+}
+
+/****************************************************************************
+  Add my workgroup and my given names to the subnet lists.
+  Also add the magic Samba names.
+  **************************************************************************/
+
+BOOL register_my_workgroup_and_names(void)
+{
+  struct subnet_record *subrec;
+  int i;
+
+  for(subrec = FIRST_SUBNET; 
+      subrec; 
+      subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+         register_my_workgroup_one_subnet(subrec);
+  }
+
+  /* We still need to add the magic Samba
+     names and the netbios names to the unicast subnet directly. This is
+     to allow unicast node status requests and queries to still work
+     in a broadcast only environment. */
+
+  add_samba_names_to_subnet(unicast_subnet);
+
+  for (i=0; my_netbios_names(i); i++)
+  {
+    for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+    {
+      /*
+       * Ensure all the IP addresses are added if we are multihomed.
+       */
+      struct nmb_name nmbname;
+
+      make_nmb_name(&nmbname, my_netbios_names(i),0x20);
+      insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+      make_nmb_name(&nmbname, my_netbios_names(i),0x3);
+      insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+      make_nmb_name(&nmbname, my_netbios_names(i),0x0);
+      insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+    }
+  }
+
+  /*
+   * Add the WORKGROUP<0> and WORKGROUP<1e> group names to the unicast subnet
+   * also for the same reasons.
+   */
+
+  for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    /*
+     * Ensure all the IP addresses are added if we are multihomed.
+     */
+    struct nmb_name nmbname;
+
+    make_nmb_name(&nmbname, lp_workgroup(), 0x0);
+    insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+
+    make_nmb_name(&nmbname, lp_workgroup(), 0x1e);
+    insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+  }
+
+  /*
+   * We need to add the Samba names to the remote broadcast subnet,
+   * as NT 4.x does directed broadcast requests to the *<0x0> name.
+   */
+  add_samba_names_to_subnet(remote_broadcast_subnet);
+
+  return True;
+}
+
+/****************************************************************************
+  Remove all the names we registered.
+**************************************************************************/
+void release_wins_names(void)
+{
+       struct subnet_record *subrec = unicast_subnet;
+       struct name_record *namerec, *nextnamerec;
+
+       for (namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+            namerec;
+            namerec = nextnamerec) {
+               nextnamerec = (struct name_record *)ubi_trNext( namerec );
+               if( (namerec->data.source == SELF_NAME)
+                   && !NAME_IS_DEREGISTERING(namerec) )
+                       release_name( subrec, namerec, standard_success_release,
+                                     NULL, NULL);
+       }
+}
+
+/*******************************************************************
+  Refresh our registered names with WINS
+  ******************************************************************/
+void refresh_my_names(time_t t)
+{
+       struct name_record *namerec;
+
+       if (wins_srv_count() < 1) return;
+
+       for (namerec = (struct name_record *)ubi_trFirst(unicast_subnet->namelist);
+            namerec;
+            namerec = (struct name_record *)ubi_trNext(namerec)) {
+               /* Each SELF name has an individual time to be refreshed. */
+               if ((namerec->data.source == SELF_NAME) &&
+                   (namerec->data.refresh_time < t) &&
+                   (namerec->data.death_time != PERMANENT_TTL)) {
+                       /* We cheat here and pretend the refresh is going to be
+                          successful & update the refresh times. This stops
+                          multiple refresh calls being done. We actually
+                          deal with refresh failure in the fail_fn.
+                       */
+                       if (!is_refresh_already_queued(unicast_subnet, namerec)) {
+                               wins_refresh_name(namerec);
+                       }
+                       namerec->data.death_time = t + lp_max_ttl();
+                       namerec->data.refresh_time = t + MIN(lp_max_ttl()/2, MAX_REFRESH_TIME);
+               }
+       }
+}
diff --git a/source4/nmbd/nmbd_namelistdb.c b/source4/nmbd/nmbd_namelistdb.c
new file mode 100644 (file)
index 0000000..932d926
--- /dev/null
@@ -0,0 +1,624 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+uint16 samba_nb_type = 0; /* samba's NetBIOS name type */
+
+
+/* ************************************************************************** **
+ * Set Samba's NetBIOS name type.
+ * ************************************************************************** **
+ */
+void set_samba_nb_type(void)
+  {
+  if( lp_wins_support() || wins_srv_count() )
+    samba_nb_type = NB_HFLAG;               /* samba is a 'hybrid' node type. */
+  else
+    samba_nb_type = NB_BFLAG;           /* samba is broadcast-only node type. */
+  } /* set_samba_nb_type */
+
+/* ************************************************************************** **
+ * Convert a NetBIOS name to upper case.
+ * ************************************************************************** **
+ */
+static void upcase_name( struct nmb_name *target, struct nmb_name *source )
+  {
+  int i;
+
+  if( NULL != source )
+    (void)memcpy( target, source, sizeof( struct nmb_name ) );
+
+  strupper( target->name );
+  strupper( target->scope );
+
+  /* fudge... We're using a byte-by-byte compare, so we must be sure that
+   * unused space doesn't have garbage in it.
+   */
+  for( i = strlen( target->name ); i < sizeof( target->name ); i++ )
+    target->name[i] = '\0';
+  for( i = strlen( target->scope ); i < sizeof( target->scope ); i++ )
+    target->scope[i] = '\0';
+  } /* upcase_name */
+
+/* ************************************************************************** **
+ * Add a new or overwrite an existing namelist entry.
+ * ************************************************************************** **
+ */
+static void update_name_in_namelist( struct subnet_record *subrec,
+                              struct name_record   *namerec )
+  {
+  struct name_record *oldrec = NULL;
+
+  (void)ubi_trInsert( subrec->namelist, namerec, &(namerec->name), &oldrec );
+  if( oldrec )
+    {
+    SAFE_FREE( oldrec->data.ip );
+    SAFE_FREE( oldrec );
+    }
+  } /* update_name_in_namelist */
+
+/* ************************************************************************** **
+ * Remove a name from the namelist.
+ * ************************************************************************** **
+ */
+void remove_name_from_namelist( struct subnet_record *subrec, 
+                                struct name_record   *namerec )
+  {
+  (void)ubi_trRemove( subrec->namelist, namerec );
+
+  SAFE_FREE(namerec->data.ip);
+
+  ZERO_STRUCTP(namerec);
+  SAFE_FREE(namerec);
+
+  subrec->namelist_changed = True;
+  } /* remove_name_from_namelist */
+
+/* ************************************************************************** **
+ * Find a name in a subnet.
+ * ************************************************************************** **
+ */
+struct name_record *find_name_on_subnet( struct subnet_record *subrec,
+                                         struct nmb_name      *nmbname,
+                                         BOOL                  self_only )
+  {
+  struct nmb_name     uc_name[1];
+  struct name_record *name_ret;
+
+  upcase_name( uc_name, nmbname );
+  name_ret = (struct name_record *)ubi_trFind( subrec->namelist, uc_name );
+  if( name_ret )
+    {
+    /* Self names only - these include permanent names. */
+    if( self_only
+     && (name_ret->data.source != SELF_NAME)
+     && (name_ret->data.source != PERMANENT_NAME) )
+      {
+      DEBUG( 9, 
+             ( "find_name_on_subnet: on subnet %s - self name %s NOT FOUND\n",
+               subrec->subnet_name, nmb_namestr(nmbname) ) );
+      return( NULL );
+      }
+    DEBUG( 9, ("find_name_on_subnet: on subnet %s - found name %s source=%d\n",
+               subrec->subnet_name, nmb_namestr(nmbname), name_ret->data.source) );
+    return( name_ret );
+    }
+  DEBUG( 9, 
+         ( "find_name_on_subnet: on subnet %s - name %s NOT FOUND\n",
+           subrec->subnet_name, nmb_namestr(nmbname) ) );
+  return( NULL );
+  } /* find_name_on_subnet */
+
+/* ************************************************************************** **
+ * Find a name over all known broadcast subnets.
+ * ************************************************************************** **
+ */
+struct name_record *find_name_for_remote_broadcast_subnet(
+                                                   struct nmb_name *nmbname,
+                                                   BOOL             self_only )
+  {
+  struct subnet_record *subrec;
+  struct name_record   *namerec = NULL;
+
+  for( subrec = FIRST_SUBNET;
+       subrec;
+       subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
+    {
+    if( NULL != (namerec = find_name_on_subnet(subrec, nmbname, self_only)) )
+      break;
+    }
+
+  return( namerec );
+  } /* find_name_for_remote_broadcast_subnet */
+  
+/* ************************************************************************** **
+ * Update the ttl of an entry in a subnet name list.
+ * ************************************************************************** **
+ */
+void update_name_ttl( struct name_record *namerec, int ttl )
+{
+  time_t time_now = time(NULL);
+
+  if( namerec->data.death_time != PERMANENT_TTL )
+    namerec->data.death_time = time_now + ttl;
+
+  namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+  namerec->subnet->namelist_changed = True;
+} /* update_name_ttl */
+
+/* ************************************************************************** **
+ * Add an entry to a subnet name list.
+ * ************************************************************************** **
+ */
+struct name_record *add_name_to_subnet( struct subnet_record *subrec,
+                                        const char           *name,
+                                        int                   type,
+                                        uint16                nb_flags,
+                                        int                   ttl,
+                                        enum name_source      source,
+                                        int                   num_ips,
+                                        struct in_addr       *iplist)
+{
+  struct name_record *namerec;
+  time_t time_now = time(NULL);
+
+  namerec = (struct name_record *)malloc( sizeof(*namerec) );
+  if( NULL == namerec )
+  {
+    DEBUG( 0, ( "add_name_to_subnet: malloc fail.\n" ) );
+    return( NULL );
+  }
+
+  memset( (char *)namerec, '\0', sizeof(*namerec) );
+  namerec->data.ip = (struct in_addr *)malloc( sizeof(struct in_addr) 
+                                               * num_ips );
+  if( NULL == namerec->data.ip )
+  {
+     DEBUG( 0, ( "add_name_to_subnet: malloc fail when creating ip_flgs.\n" ) );
+
+     ZERO_STRUCTP(namerec);
+     SAFE_FREE(namerec);
+     return NULL;
+  }
+
+  namerec->subnet = subrec;
+
+  make_nmb_name(&namerec->name, name, type);
+  upcase_name(&namerec->name, NULL );
+
+  /* Enter the name as active. */
+  namerec->data.nb_flags = nb_flags | NB_ACTIVE;
+  namerec->data.wins_flags = WINS_ACTIVE;
+
+  /* If it's our primary name, flag it as so. */
+  if( strequal( my_netbios_names(0), name ) )
+    namerec->data.nb_flags |= NB_PERM;
+
+  /* Copy the IPs. */
+  namerec->data.num_ips = num_ips;
+  memcpy( (namerec->data.ip), iplist, num_ips * sizeof(struct in_addr) );
+
+  /* Data source. */
+  namerec->data.source = source;
+
+  /* Setup the death_time and refresh_time. */
+  if( ttl == PERMANENT_TTL )
+    namerec->data.death_time = PERMANENT_TTL;
+  else
+    namerec->data.death_time = time_now + ttl;
+
+  namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+  /* Now add the record to the name list. */    
+  update_name_in_namelist( subrec, namerec );
+
+  DEBUG( 3, ( "add_name_to_subnet: Added netbios name %s with first IP %s \
+ttl=%d nb_flags=%2x to subnet %s\n",
+           nmb_namestr( &namerec->name ),
+            inet_ntoa( *iplist ),
+            ttl,
+            (unsigned int)nb_flags,
+            subrec->subnet_name ) );
+
+  subrec->namelist_changed = True;
+
+  return(namerec);
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register 
+ succeeds. By definition this is a SELF_NAME (or we wouldn't be registering
+ it).
+ ******************************************************************/
+
+void standard_success_register(struct subnet_record *subrec, 
+                             struct userdata_struct *userdata,
+                             struct nmb_name *nmbname, uint16 nb_flags, int ttl,
+                             struct in_addr registered_ip)
+{
+  struct name_record *namerec;
+
+  namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+  if( NULL == namerec )
+    (void)add_name_to_subnet( subrec, nmbname->name, nmbname->name_type,
+                              nb_flags, ttl, SELF_NAME, 1, &registered_ip );
+  else
+    update_name_ttl( namerec, ttl );
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register 
+ fails. Note that this is only ever called on a broadcast subnet with
+ one IP address per name. This is why it can just delete the name 
+ without enumerating the IP adresses. JRA.
+ ******************************************************************/
+
+void standard_fail_register( struct subnet_record   *subrec,
+                             struct response_record *rrec,
+                             struct nmb_name        *nmbname )
+{
+  struct name_record *namerec;
+
+  namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+
+  DEBUG( 0, ( "standard_fail_register: Failed to register/refresh name %s \
+on subnet %s\n",
+            nmb_namestr(nmbname), subrec->subnet_name) );
+
+  /* Remove the name from the subnet. */
+  if( namerec )
+    remove_name_from_namelist(subrec, namerec);
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+static void remove_nth_ip_in_record( struct name_record *namerec, int ind)
+{
+  if( ind != namerec->data.num_ips )
+    memmove( (char *)(&namerec->data.ip[ind]),
+             (char *)(&namerec->data.ip[ind+1]), 
+             ( namerec->data.num_ips - ind - 1) * sizeof(struct in_addr) );
+
+  namerec->data.num_ips--;
+  namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to check if an IP address exists in a name record.
+ ******************************************************************/
+
+BOOL find_ip_in_name_record( struct name_record *namerec, struct in_addr ip )
+{
+  int i;
+
+  for(i = 0; i < namerec->data.num_ips; i++)
+    if(ip_equal( namerec->data.ip[i], ip))
+      return True;
+
+  return False;
+}
+
+/*******************************************************************
+ Utility function to add an IP address to a name record.
+ ******************************************************************/
+
+void add_ip_to_name_record( struct name_record *namerec, struct in_addr new_ip )
+{
+  struct in_addr *new_list;
+
+  /* Don't add one we already have. */
+  if( find_ip_in_name_record( namerec, new_ip ) )
+    return;
+  
+  new_list = (struct in_addr *)malloc( (namerec->data.num_ips + 1)
+                                       * sizeof(struct in_addr) );
+  if( NULL == new_list )
+  {
+    DEBUG(0,("add_ip_to_name_record: Malloc fail !\n"));
+    return;
+  }
+
+  memcpy( (char *)new_list,
+          (char *)namerec->data.ip,
+          namerec->data.num_ips * sizeof(struct in_addr) );
+  new_list[namerec->data.num_ips] = new_ip;
+
+  SAFE_FREE(namerec->data.ip);
+  namerec->data.ip = new_list;
+  namerec->data.num_ips += 1;
+
+  namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+void remove_ip_from_name_record( struct name_record *namerec,
+                                 struct in_addr      remove_ip )
+{
+  /* Try and find the requested ip address - remove it. */
+  int i;
+  int orig_num = namerec->data.num_ips;
+
+  for(i = 0; i < orig_num; i++)
+    if( ip_equal( remove_ip, namerec->data.ip[i]) )
+    {
+      remove_nth_ip_in_record( namerec, i);
+      break;
+    }
+}
+
+/*******************************************************************
+ Utility function that release_name callers can plug into as the
+ success function when a name release is successful. Used to save
+ duplication of success_function code.
+ ******************************************************************/
+
+void standard_success_release( struct subnet_record   *subrec,
+                               struct userdata_struct *userdata,
+                               struct nmb_name        *nmbname,
+                               struct in_addr          released_ip )
+{
+  struct name_record *namerec;
+
+  namerec = find_name_on_subnet( subrec, nmbname, FIND_ANY_NAME );
+
+  if( namerec == NULL )
+  {
+    DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. Name was not found on subnet.\n",
+                nmb_namestr(nmbname),
+                inet_ntoa(released_ip),
+                subrec->subnet_name) );
+    return;
+  }
+  else
+  {
+    int orig_num = namerec->data.num_ips;
+
+    remove_ip_from_name_record( namerec, released_ip );
+
+    if( namerec->data.num_ips == orig_num )
+      DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. This ip is not known for this name.\n",
+                nmb_namestr(nmbname),
+                inet_ntoa(released_ip),
+                subrec->subnet_name ) );
+  }
+
+  if( namerec->data.num_ips == 0 )
+    remove_name_from_namelist( subrec, namerec );
+}
+
+/*******************************************************************
+  Expires old names in a subnet namelist.
+  ******************************************************************/
+
+void expire_names_on_subnet(struct subnet_record *subrec, time_t t)
+{
+  struct name_record *namerec;
+  struct name_record *next_namerec;
+
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = next_namerec )
+  {
+    next_namerec = (struct name_record *)ubi_trNext( namerec );
+    if( (namerec->data.death_time != PERMANENT_TTL)
+     && (namerec->data.death_time < t) )
+    {
+      if( namerec->data.source == SELF_NAME )
+      {
+        DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF \
+name %s\n", 
+                    subrec->subnet_name, nmb_namestr(&namerec->name) ) );
+        namerec->data.death_time += 300;
+        namerec->subnet->namelist_changed = True;
+        continue;
+      }
+      DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n",
+                 subrec->subnet_name, nmb_namestr(&namerec->name)));
+  
+      remove_name_from_namelist( subrec, namerec );
+    }
+  }
+}
+
+/*******************************************************************
+  Expires old names in all subnet namelists.
+  ******************************************************************/
+
+void expire_names(time_t t)
+{
+  struct subnet_record *subrec;
+
+  for( subrec = FIRST_SUBNET;
+       subrec;
+       subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) )
+  {
+    expire_names_on_subnet( subrec, t );
+  }
+}
+
+/****************************************************************************
+  Add the magic samba names, useful for finding samba servers.
+  These go directly into the name list for a particular subnet,
+  without going through the normal registration process.
+  When adding them to the unicast subnet, add them as a list of
+  all broadcast subnet IP addresses.
+**************************************************************************/
+
+void add_samba_names_to_subnet( struct subnet_record *subrec )
+{
+  struct in_addr *iplist = &subrec->myip;
+  int num_ips = 1;
+
+  /* These names are added permanently (ttl of zero) and will NOT be
+     refreshed.  */
+
+  if( (subrec == unicast_subnet)
+   || (subrec == wins_server_subnet)
+   || (subrec == remote_broadcast_subnet) )
+  {
+    struct subnet_record *bcast_subrecs;
+    int i;
+    /* Create an IP list containing all our known subnets. */
+
+    num_ips = iface_count();
+    iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) );
+    if( NULL == iplist )
+    {
+      DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n"));
+      return;
+    }
+
+    for( bcast_subrecs = FIRST_SUBNET, i = 0;
+         bcast_subrecs; 
+         bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++ )
+      iplist[i] = bcast_subrecs->myip;
+
+  }
+
+  (void)add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL,
+                           PERMANENT_NAME, num_ips, iplist);
+  (void)add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL,
+                           PERMANENT_NAME, num_ips, iplist);
+  (void)add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL,
+                           PERMANENT_NAME, num_ips, iplist);
+  (void)add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL,
+                           PERMANENT_NAME, num_ips, iplist);
+
+  if(iplist != &subrec->myip)
+    SAFE_FREE(iplist);
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+static void dump_subnet_namelist( struct subnet_record *subrec, XFILE *fp)
+{
+  struct name_record *namerec;
+  const char *src_type;
+  struct tm *tm;
+  int i;
+
+  x_fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name);
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
+  {
+    x_fprintf(fp,"\tName = %s\t", nmb_namestr(&namerec->name));
+    switch(namerec->data.source)
+    {
+      case LMHOSTS_NAME:
+        src_type = "LMHOSTS_NAME";
+        break;
+      case WINS_PROXY_NAME:
+        src_type = "WINS_PROXY_NAME";
+        break;
+      case REGISTER_NAME:
+        src_type = "REGISTER_NAME";
+        break;
+      case SELF_NAME:
+        src_type = "SELF_NAME";
+        break;
+      case DNS_NAME:
+        src_type = "DNS_NAME";
+        break;
+      case DNSFAIL_NAME:
+        src_type = "DNSFAIL_NAME";
+        break;
+      case PERMANENT_NAME:
+        src_type = "PERMANENT_NAME";
+        break;
+      default:
+        src_type = "unknown!";
+        break;
+    }
+    x_fprintf(fp,"Source = %s\nb_flags = %x\t", src_type, namerec->data.nb_flags);
+
+    if(namerec->data.death_time != PERMANENT_TTL)
+    {
+      tm = LocalTime(&namerec->data.death_time);
+      x_fprintf(fp, "death_time = %s\t", asctime(tm));
+    }
+    else
+      x_fprintf(fp, "death_time = PERMANENT\t");
+
+    if(namerec->data.refresh_time != PERMANENT_TTL)
+    {
+      tm = LocalTime(&namerec->data.refresh_time);
+      x_fprintf(fp, "refresh_time = %s\n", asctime(tm));
+    }
+    else
+      x_fprintf(fp, "refresh_time = PERMANENT\n");
+
+    x_fprintf(fp, "\t\tnumber of IPS = %d", namerec->data.num_ips);
+    for(i = 0; i < namerec->data.num_ips; i++)
+      x_fprintf(fp, "\t%s", inet_ntoa(namerec->data.ip[i]));
+
+    x_fprintf(fp, "\n\n");
+  }
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+void dump_all_namelists(void)
+{
+  XFILE *fp; 
+  struct subnet_record *subrec;
+
+  fp = x_fopen(lock_path("namelist.debug"),O_WRONLY|O_CREAT|O_TRUNC, 0644);
+     
+  if (!fp)
+  { 
+    DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n",
+              "namelist.debug",strerror(errno)));
+    return;
+  }
+      
+  for( subrec = FIRST_SUBNET;
+       subrec;
+       subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) )
+    dump_subnet_namelist( subrec, fp );
+
+  if( !we_are_a_wins_client() )
+    dump_subnet_namelist( unicast_subnet, fp );
+
+  if( remote_broadcast_subnet->namelist != NULL )
+    dump_subnet_namelist( remote_broadcast_subnet, fp );
+
+  if( wins_server_subnet != NULL )
+    dump_subnet_namelist( wins_server_subnet, fp );
+  x_fclose( fp );
+}
diff --git a/source4/nmbd/nmbd_namequery.c b/source4/nmbd/nmbd_namequery.c
new file mode 100644 (file)
index 0000000..8995e9a
--- /dev/null
@@ -0,0 +1,304 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/****************************************************************************
+ Deal with a response packet when querying a name.
+****************************************************************************/
+
+static void query_name_response( struct subnet_record   *subrec,
+                                 struct response_record *rrec,
+                                 struct packet_struct   *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  BOOL success = False;
+  struct nmb_name *question_name = 
+                           &rrec->packet->packet.nmb.question.question_name;
+  struct in_addr answer_ip;
+
+  zero_ip(&answer_ip);
+
+  /* Ensure we don't retry the query but leave the response record cleanup
+     to the timeout code. We may get more answer responses in which case
+     we should mark the name in conflict.. */
+  rrec->repeat_count = 0;
+
+  if(rrec->num_msgs == 1)
+  {
+    /* This is the first response. */
+
+    if(nmb->header.opcode == NMB_WACK_OPCODE)
+    {
+      /* WINS server is telling us to wait. Pretend we didn't get
+         the response but don't send out any more query requests. */
+
+      if( DEBUGLVL( 5 ) )
+        {
+        dbgtext( "query_name_response: " );
+        dbgtext( "WACK from WINS server %s ", inet_ntoa(p->ip) );
+        dbgtext( "in querying name %s ", nmb_namestr(question_name) );
+        dbgtext( "on subnet %s.\n", subrec->subnet_name );
+        }
+  
+      rrec->repeat_count = 0;
+      /* How long we should wait for. */
+      rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+      rrec->num_msgs--;
+      return;
+    }
+    else if(nmb->header.rcode != 0)
+    {
+      success = False;
+
+      if( DEBUGLVL( 5 ) )
+        {
+        dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+        dbgtext( "- negative response from IP %s ", inet_ntoa(p->ip) );
+        dbgtext( "for name %s. ", nmb_namestr(question_name) );
+        dbgtext( "Error code was %d.\n", nmb->header.rcode );
+        }
+    }
+    else
+    {
+      if (!nmb->answers)
+      {
+        dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+       dbgtext( "IP %s ", inet_ntoa(p->ip) );
+       dbgtext( "returned a success response with no answer\n" );
+       return;
+      }
+
+      success = True;
+
+      putip((char *)&answer_ip,&nmb->answers->rdata[2]);
+      if( DEBUGLVL( 5 ) )
+        {
+        dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+        dbgtext( "- positive response from IP %s ", inet_ntoa(p->ip) );
+        dbgtext( "for name %s.  ", nmb_namestr(question_name) );
+        dbgtext( "IP of that name is %s\n", inet_ntoa(answer_ip) );
+        }
+
+      /* Interestingly, we could add these names to our namelists, and
+         change nmbd to a model that checked its own name cache first,
+         before sending out a query. This is a task for another day, though.
+       */
+    }
+  }
+  else if( rrec->num_msgs > 1)
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      if (nmb->answers)
+        putip( (char *)&answer_ip, &nmb->answers->rdata[2] );
+      dbgtext( "query_name_response: " );
+      dbgtext( "Multiple (%d) responses ", rrec->num_msgs );
+      dbgtext( "received for a query on subnet %s ", subrec->subnet_name );
+      dbgtext( "for name %s.\nThis response ", nmb_namestr(question_name) );
+      dbgtext( "was from IP %s, reporting ", inet_ntoa(p->ip) );
+      dbgtext( "an IP address of %s.\n", inet_ntoa(answer_ip) );
+      }
+
+    /* We have already called the success or fail function, so we
+       don't call again here. Leave the response record around in
+       case we get more responses. */
+
+    return; 
+  }
+  
+  if(success && rrec->success_fn)
+    (*(query_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers);
+  else if( rrec->fail_fn)
+    (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode);
+
+}
+
+/****************************************************************************
+ Deal with a timeout when querying a name.
+****************************************************************************/
+
+static void query_name_timeout_response(struct subnet_record *subrec,
+                       struct response_record *rrec)
+{
+  struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+  /* We can only fail here, never succeed. */
+  BOOL failed = True;
+  struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+  if(rrec->num_msgs != 0)
+  {
+    /* We got at least one response, and have called the success/fail
+       function already. */
+
+    failed = False; 
+  }
+
+  if(failed)
+  {
+    if( DEBUGLVL( 5 ) )
+      {
+      dbgtext( "query_name_timeout_response: No response to " );
+      dbgtext( "query for name %s ", nmb_namestr(question_name) );
+      dbgtext( "on subnet %s.\n", subrec->subnet_name );
+      }
+    if(rrec->fail_fn)
+      (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, 0);
+  }
+
+  remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Lookup a name on our local namelists. We check the lmhosts file first. If the
+ name is not there we look for the name on the given subnet.
+****************************************************************************/
+
+static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname,
+                                  struct name_record **namerecp) 
+{
+  struct name_record *namerec;
+
+  *namerecp = NULL;
+
+  if(find_name_in_lmhosts(nmbname, namerecp))
+    return True;
+  
+  if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL)
+    return False;
+
+  if( NAME_IS_ACTIVE(namerec)
+   && ( (namerec->data.source == SELF_NAME)
+     || (namerec->data.source == LMHOSTS_NAME) ) )
+  {
+    *namerecp = namerec;
+    return True;
+  } 
+  return False;
+}
+
+/****************************************************************************
+ Try and query for a name.
+****************************************************************************/
+
+BOOL query_name(struct subnet_record *subrec, char *name, int type,
+                   query_name_success_function success_fn,
+                   query_name_fail_function fail_fn, 
+                   struct userdata_struct *userdata)
+{
+  struct nmb_name nmbname;
+  struct name_record *namerec;
+
+  make_nmb_name(&nmbname, name, type);
+
+  /*
+   * We need to check our local namelists first.
+   * It may be an magic name, lmhosts name or just
+   * a name we have registered.
+   */
+
+  if(query_local_namelists(subrec, &nmbname, &namerec) == True)
+  {
+    struct res_rec rrec;
+    int i;
+
+    memset((char *)&rrec, '\0', sizeof(struct res_rec));
+
+    /* Fake up the needed res_rec just in case it's used. */
+    rrec.rr_name = nmbname;
+    rrec.rr_type = RR_TYPE_NB;
+    rrec.rr_class = RR_CLASS_IN;
+    rrec.ttl = PERMANENT_TTL;
+    rrec.rdlength = namerec->data.num_ips * 6;
+    if(rrec.rdlength > MAX_DGRAM_SIZE)
+    {
+      if( DEBUGLVL( 0 ) )
+        {
+        dbgtext( "query_name: nmbd internal error - " );
+        dbgtext( "there are %d ip addresses ", namerec->data.num_ips );
+        dbgtext( "for name %s.\n", nmb_namestr(&nmbname) );
+        }
+      return False;
+    }
+
+    for( i = 0; i < namerec->data.num_ips; i++)
+    {
+      set_nb_flags( &rrec.rdata[i*6], namerec->data.nb_flags );
+      putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->data.ip[i]);
+    }
+
+    /* Call the success function directly. */
+    if(success_fn)
+      (*(query_name_success_function)success_fn)(subrec, userdata, &nmbname, namerec->data.ip[0], &rrec);
+    return False;
+  }
+
+  if(queue_query_name( subrec,
+        query_name_response,
+        query_name_timeout_response,
+        success_fn,
+        fail_fn,
+        userdata,
+        &nmbname) == NULL)
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "query_name: Failed to send packet " );
+      dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+      }
+    return True;
+  }
+  return False;
+}
+
+/****************************************************************************
+ Try and query for a name from nmbd acting as a WINS server.
+****************************************************************************/
+
+BOOL query_name_from_wins_server(struct in_addr ip_to, 
+                   char *name, int type,
+                   query_name_success_function success_fn,
+                   query_name_fail_function fail_fn, 
+                   struct userdata_struct *userdata)
+{
+  struct nmb_name nmbname;
+
+  make_nmb_name(&nmbname, name, type);
+
+  if(queue_query_name_from_wins_server( ip_to,
+        query_name_response,
+        query_name_timeout_response,
+        success_fn,
+        fail_fn,
+        userdata,
+        &nmbname) == NULL)
+  {
+    if( DEBUGLVL( 0 ) )
+      {
+      dbgtext( "query_name_from_wins_server: Failed to send packet " );
+      dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+      }
+    return True;
+  }
+  return False;
+}
diff --git a/source4/nmbd/nmbd_nameregister.c b/source4/nmbd/nmbd_nameregister.c
new file mode 100644 (file)
index 0000000..7bf2584
--- /dev/null
@@ -0,0 +1,522 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/* forward declarations */
+static void wins_next_registration(struct response_record *rrec);
+
+
+/****************************************************************************
+ Deal with a response packet when registering one of our names.
+****************************************************************************/
+
+static void register_name_response(struct subnet_record *subrec,
+                       struct response_record *rrec, struct packet_struct *p)
+{
+       /* 
+        * If we are registering broadcast, then getting a response is an
+        * error - we do not have the name. If we are registering unicast,
+        * then we expect to get a response.
+        */
+
+       struct nmb_packet *nmb = &p->packet.nmb;
+       BOOL bcast = nmb->header.nm_flags.bcast;
+       BOOL success = True;
+       struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+       struct nmb_name *answer_name = &nmb->answers->rr_name;
+       struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+       int ttl = 0;
+       uint16 nb_flags = 0;
+       struct in_addr register_ip;
+       fstring reg_name;
+       
+       putip(&register_ip,&sent_nmb->additional->rdata[2]);
+       fstrcpy(reg_name, inet_ntoa(register_ip));
+       
+       if (subrec == unicast_subnet) {
+               /* we know that this wins server is definately alive - for the moment! */
+               wins_srv_alive(rrec->packet->ip, register_ip);
+       }
+
+       /* Sanity check. Ensure that the answer name in the incoming packet is the
+          same as the requested name in the outgoing packet. */
+
+       if(!question_name || !answer_name) {
+               DEBUG(0,("register_name_response: malformed response (%s is NULL).\n",
+                        question_name ? "question_name" : "answer_name" ));
+               return;
+       }
+
+       if(!nmb_name_equal(question_name, answer_name)) {
+               DEBUG(0,("register_name_response: Answer name %s differs from question name %s.\n", 
+                        nmb_namestr(answer_name), nmb_namestr(question_name)));
+               return;
+       }
+
+       if(bcast) {
+               /*
+                * Special hack to cope with old Samba nmbd's.
+                * Earlier versions of Samba (up to 1.9.16p11) respond 
+                * to a broadcast name registration of WORKGROUP<1b> when 
+                * they should not. Hence, until these versions are gone, 
+                * we should treat such errors as success for this particular
+                * case only. jallison@whistle.com.
+                */
+               
+#if 1 /* OLD_SAMBA_SERVER_HACK */
+               if((nmb->header.rcode == ACT_ERR) && strequal(lp_workgroup(), answer_name->name) &&
+                  (answer_name->name_type == 0x1b)) {
+                       /* Pretend we did not get this. */
+                       rrec->num_msgs--;
+
+                       DEBUG(5,("register_name_response: Ignoring broadcast response to registration of name %s due to old Samba server bug.\n", 
+                                nmb_namestr(answer_name)));
+                       return;
+               }
+#endif /* OLD_SAMBA_SERVER_HACK */
+
+               /* Someone else has the name. Log the problem. */
+               DEBUG(1,("register_name_response: Failed to register name %s IP %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n", 
+                        nmb_namestr(answer_name), 
+                        reg_name,
+                        subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
+               success = False;
+       } else {
+               /* Unicast - check to see if the response allows us to have the name. */
+               if (nmb->header.opcode == NMB_WACK_OPCODE) {
+                       /* WINS server is telling us to wait. Pretend we didn't get
+                          the response but don't send out any more register requests. */
+
+                       DEBUG(5,("register_name_response: WACK from WINS server %s in registering name %s IP %s\n", 
+                                inet_ntoa(p->ip), nmb_namestr(answer_name), reg_name));
+
+                       rrec->repeat_count = 0;
+                       /* How long we should wait for. */
+                       rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+                       rrec->num_msgs--;
+                       return;
+               } else if (nmb->header.rcode != 0) {
+                       /* Error code - we didn't get the name. */
+                       success = False;
+
+                       DEBUG(0,("register_name_response: %sserver at IP %s rejected our name registration of %s IP %s with error code %d.\n", 
+                                subrec==unicast_subnet?"WINS ":"",
+                                inet_ntoa(p->ip), 
+                                nmb_namestr(answer_name), 
+                                reg_name,
+                                nmb->header.rcode));
+               } else {
+                       success = True;
+                       /* Get the data we need to pass to the success function. */
+                       nb_flags = get_nb_flags(nmb->answers->rdata);
+                       ttl = nmb->answers->ttl;
+
+                       /* send off a registration for the next IP, if any */
+                       wins_next_registration(rrec);
+               }
+       } 
+
+       DEBUG(5,("register_name_response: %s in registering %sname %s IP %s with %s.\n",
+                success ? "success" : "failure", 
+                subrec==unicast_subnet?"WINS ":"",
+                nmb_namestr(answer_name), 
+                reg_name,
+                inet_ntoa(rrec->packet->ip)));
+
+       if(success) {
+               /* Enter the registered name into the subnet name database before calling
+                  the success function. */
+               standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+               if( rrec->success_fn)
+                       (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+       } else {
+               if( rrec->fail_fn)
+                       (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+               /* Remove the name. */
+               standard_fail_register( subrec, rrec, question_name);
+       }
+
+       /* Ensure we don't retry. */
+       remove_response_record(subrec, rrec);
+}
+
+
+/****************************************************************************
+ Deal with a timeout of a WINS registration request
+****************************************************************************/
+static void wins_registration_timeout(struct subnet_record *subrec,
+                                     struct response_record *rrec)
+{
+       struct userdata_struct *userdata = rrec->userdata;
+       struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+       struct nmb_name *nmbname = &sent_nmb->question.question_name;
+       struct in_addr register_ip;
+       fstring src_addr;
+
+       putip(&register_ip,&sent_nmb->additional->rdata[2]);
+
+       fstrcpy(src_addr, inet_ntoa(register_ip));
+
+       DEBUG(2,("wins_registration_timeout: WINS server %s timed out registering IP %s\n", 
+                inet_ntoa(rrec->packet->ip), src_addr));
+
+       /* mark it temporarily dead for this source address */
+       wins_srv_died(rrec->packet->ip, register_ip);
+
+       /* if we have some userdata then use that to work out what
+          wins server to try next */
+       if (userdata) {
+               const char *tag = (const char *)userdata->data;
+
+               /* try the next wins server in our failover list for
+                  this tag */
+               rrec->packet->ip = wins_srv_ip_tag(tag, register_ip);
+       }
+
+       /* if we have run out of wins servers for this tag then they
+          must all have timed out. We treat this as *success*, not
+          failure, and go into our standard name refresh mode. This
+          copes with all the wins servers being down */
+       if (wins_srv_is_dead(rrec->packet->ip, register_ip)) {
+               uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+               int ttl = sent_nmb->additional->ttl;
+
+               standard_success_register(subrec, userdata, nmbname, nb_flags, ttl, register_ip);
+               if(rrec->success_fn) {
+                       (*(register_name_success_function)rrec->success_fn)(subrec, 
+                                                                           rrec->userdata, 
+                                                                           nmbname, 
+                                                                           nb_flags, 
+                                                                           ttl, 
+                                                                           register_ip);
+               }
+
+               /* send off a registration for the next IP, if any */
+               wins_next_registration(rrec);
+
+               /* don't need to send this packet any more */
+               remove_response_record(subrec, rrec);
+               return;
+       }
+       
+       /* we will be moving to the next WINS server for this group,
+          send it immediately */
+       rrec->repeat_count = 2;
+       rrec->repeat_time = time(NULL) + 1;
+       rrec->in_expiration_processing = False;
+
+       DEBUG(6,("Retrying register of name %s IP %s with WINS server %s\n",
+                nmb_namestr(nmbname), src_addr, inet_ntoa(rrec->packet->ip)));
+
+       /* notice that we don't remove the response record. This keeps
+          us trying to register with each of our failover wins servers */
+}
+
+
+/****************************************************************************
+ Deal with a timeout when registering one of our names.
+****************************************************************************/
+
+static void register_name_timeout_response(struct subnet_record *subrec,
+                                          struct response_record *rrec)
+{
+       /*
+        * If we are registering unicast, then NOT getting a response is an
+        * error - we do not have the name. If we are registering broadcast,
+        * then we don't expect to get a response.
+        */
+
+       struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+       BOOL bcast = sent_nmb->header.nm_flags.bcast;
+       BOOL success = False;
+       struct nmb_name *question_name = &sent_nmb->question.question_name;
+       uint16 nb_flags = 0;
+       int ttl = 0;
+       struct in_addr registered_ip;
+       
+       if (bcast) {
+               if(rrec->num_msgs == 0) {
+                       /* Not receiving a message is success for broadcast registration. */
+                       success = True; 
+
+                       /* Pull the success values from the original request packet. */
+                       nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+                       ttl = sent_nmb->additional->ttl;
+                       putip(&registered_ip,&sent_nmb->additional->rdata[2]);
+               }
+       } else {
+               /* wins timeouts are special */
+               wins_registration_timeout(subrec, rrec);
+               return;
+       }
+
+       DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n",
+                success ? "success" : "failure", nmb_namestr(question_name), subrec->subnet_name));
+       if(success) {
+               /* Enter the registered name into the subnet name database before calling
+                  the success function. */
+               standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+               if( rrec->success_fn)
+                       (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+       } else {
+               if( rrec->fail_fn)
+                       (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+               /* Remove the name. */
+               standard_fail_register( subrec, rrec, question_name);
+       }
+
+       /* Ensure we don't retry. */
+       remove_response_record(subrec, rrec);
+}
+
+
+/****************************************************************************
+initiate one multi-homed name registration packet
+****************************************************************************/
+static void multihomed_register_one(struct nmb_name *nmbname,
+                                   uint16 nb_flags,
+                                   register_name_success_function success_fn,
+                                   register_name_fail_function fail_fn,
+                                   struct in_addr ip,
+                                   const char *tag)
+{
+       struct userdata_struct *userdata;
+       struct in_addr wins_ip = wins_srv_ip_tag(tag, ip);
+       fstring ip_str;
+
+       userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+       if (!userdata) {
+               DEBUG(0,("Failed to allocate userdata structure!\n"));
+               return;
+       }
+       ZERO_STRUCTP(userdata);
+       userdata->userdata_len = strlen(tag) + 1;
+       strlcpy(userdata->data, tag, userdata->userdata_len);   
+
+       fstrcpy(ip_str, inet_ntoa(ip));
+
+       DEBUG(6,("Registering name %s IP %s with WINS server %s using tag '%s'\n",
+                nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+       if (queue_register_multihomed_name(unicast_subnet,
+                                          register_name_response,
+                                          register_name_timeout_response,
+                                          success_fn,
+                                          fail_fn,
+                                          userdata,
+                                          nmbname,
+                                          nb_flags,
+                                          ip,
+                                          wins_ip) == NULL) {
+               DEBUG(0,("multihomed_register_one: Failed to send packet trying to register name %s IP %s\n", 
+                        nmb_namestr(nmbname), inet_ntoa(ip)));         
+       }
+
+       free(userdata);
+}
+
+
+/****************************************************************************
+we have finished the registration of one IP and need to see if we have
+any more IPs left to register with this group of wins server for this name
+****************************************************************************/
+static void wins_next_registration(struct response_record *rrec)
+{
+       struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+       struct nmb_name *nmbname = &sent_nmb->question.question_name;
+       uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+       struct userdata_struct *userdata = rrec->userdata;
+       const char *tag;
+       struct in_addr last_ip;
+       struct subnet_record *subrec;
+
+       putip(&last_ip,&sent_nmb->additional->rdata[2]);
+
+       if (!userdata) {
+               /* it wasn't multi-homed */
+               return;
+       }
+
+       tag = (const char *)userdata->data;
+
+       for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+               if (ip_equal(last_ip, subrec->myip)) {
+                       subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec);
+                       break;
+               }
+       }
+
+       if (!subrec) {
+               /* no more to do! */
+               return;
+       }
+
+       switch (sent_nmb->header.opcode) {
+       case NMB_NAME_MULTIHOMED_REG_OPCODE:
+               multihomed_register_one(nmbname, nb_flags, NULL, NULL, subrec->myip, tag);
+               break;
+       case NMB_NAME_REFRESH_OPCODE_8:
+               queue_wins_refresh(nmbname, 
+                                  register_name_response,
+                                  register_name_timeout_response,
+                                  nb_flags, subrec->myip, tag);
+               break;
+       }
+}
+
+/****************************************************************************
+ Try and register one of our names on the unicast subnet - multihomed.
+****************************************************************************/
+static void multihomed_register_name(struct nmb_name *nmbname, uint16 nb_flags,
+                                    register_name_success_function success_fn,
+                                    register_name_fail_function fail_fn)
+{
+       /*
+         If we are adding a group name, we just send multiple
+         register name packets to the WINS server (this is an
+         internet group name.
+
+         If we are adding a unique name, We need first to add 
+         our names to the unicast subnet namelist. This is 
+         because when a WINS server receives a multihomed 
+         registration request, the first thing it does is to 
+         send a name query to the registering machine, to see 
+         if it has put the name in it's local namelist.
+         We need the name there so the query response code in
+         nmbd_incomingrequests.c will find it.
+
+         We are adding this name prematurely (we don't really
+         have it yet), but as this is on the unicast subnet
+         only we will get away with this (only the WINS server
+         will ever query names from us on this subnet).
+       */
+       int num_ips=0;
+       int i, t;
+       struct subnet_record *subrec;
+       char **wins_tags;
+       struct in_addr *ip_list;
+
+       for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
+               num_ips++;
+       
+       if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) {
+               DEBUG(0,("multihomed_register_name: malloc fail !\n"));
+               return;
+       }
+
+       for (subrec = FIRST_SUBNET, i = 0; 
+            subrec;
+            subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ ) {
+               ip_list[i] = subrec->myip;
+       }
+
+       add_name_to_subnet(unicast_subnet, nmbname->name, nmbname->name_type,
+                          nb_flags, lp_max_ttl(), SELF_NAME,
+                          num_ips, ip_list);
+
+       /* get the list of wins tags - we try to register for each of them */
+       wins_tags = wins_srv_tags();
+
+       /* Now try and register the name for each wins tag.  Note that
+          at this point we only register our first IP with each wins
+          group. We will register the rest from
+          wins_next_registration() when we get the reply for this
+          one. That follows the way W2K does things (tridge)
+       */
+       for (t=0; wins_tags && wins_tags[t]; t++) {
+               multihomed_register_one(nmbname, nb_flags,
+                                       success_fn, fail_fn,
+                                       ip_list[0],
+                                       wins_tags[t]);
+       }
+
+       wins_srv_tags_free(wins_tags);
+       
+       SAFE_FREE(ip_list);
+}
+
+
+/****************************************************************************
+ Try and register one of our names.
+****************************************************************************/
+void register_name(struct subnet_record *subrec,
+                   const char *name, int type, uint16 nb_flags,
+                   register_name_success_function success_fn,
+                   register_name_fail_function fail_fn,
+                   struct userdata_struct *userdata)
+{
+       struct nmb_name nmbname;
+       
+       make_nmb_name(&nmbname, name, type);
+
+       /* Always set the NB_ACTIVE flag on the name we are
+          registering. Doesn't make sense without it.
+       */
+       
+       nb_flags |= NB_ACTIVE;
+       
+       if (subrec == unicast_subnet) {
+               /* we now always do multi-homed registration if we are
+                  registering to a WINS server. This copes much
+                  better with complex WINS setups */
+               multihomed_register_name(&nmbname, nb_flags,
+                                        success_fn, fail_fn);
+               return;
+       }
+       
+       if (queue_register_name(subrec,
+                               register_name_response,
+                               register_name_timeout_response,
+                               success_fn,
+                               fail_fn,
+                               userdata,
+                               &nmbname,
+                               nb_flags) == NULL) {
+               DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
+                        nmb_namestr(&nmbname)));
+       }
+}
+
+
+/****************************************************************************
+ Try and refresh one of our names. This is *only* called for WINS refresh
+****************************************************************************/
+void wins_refresh_name(struct name_record *namerec)
+{
+       int t;
+       char **wins_tags;
+
+       /* get the list of wins tags - we try to refresh for each of them */
+       wins_tags = wins_srv_tags();
+
+       for (t=0; wins_tags && wins_tags[t]; t++) {
+               queue_wins_refresh(&namerec->name, 
+                                  register_name_response,
+                                  register_name_timeout_response,
+                                  namerec->data.nb_flags,
+                                  namerec->data.ip[0], wins_tags[t]);
+       }
+
+       wins_srv_tags_free(wins_tags);
+}
diff --git a/source4/nmbd/nmbd_namerelease.c b/source4/nmbd/nmbd_namerelease.c
new file mode 100644 (file)
index 0000000..0611ca9
--- /dev/null
@@ -0,0 +1,222 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/****************************************************************************
+ Deal with a response packet when releasing one of our names.
+****************************************************************************/
+
+static void release_name_response(struct subnet_record *subrec,
+                                 struct response_record *rrec, struct packet_struct *p)
+{
+       /* 
+        * If we are releasing broadcast, then getting a response is an
+        * error. If we are releasing unicast, then we expect to get a response.
+        */
+       struct nmb_packet *nmb = &p->packet.nmb;
+       BOOL bcast = nmb->header.nm_flags.bcast;
+       BOOL success = True;
+       struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+       struct nmb_name *answer_name = &nmb->answers->rr_name;
+       struct in_addr released_ip;
+
+       /* Sanity check. Ensure that the answer name in the incoming packet is the
+          same as the requested name in the outgoing packet. */
+       if (!nmb_name_equal(question_name, answer_name)) {
+               DEBUG(0,("release_name_response: Answer name %s differs from question name %s.\n", 
+                        nmb_namestr(answer_name), nmb_namestr(question_name)));
+               return;
+       }
+
+       if (bcast) {
+               /* Someone sent a response to a bcast release? ignore it. */
+               return;
+       }
+
+       /* Unicast - check to see if the response allows us to release the name. */
+       if (nmb->header.rcode != 0) {
+               /* Error code - we were told not to release the name ! What now ! */
+               success = False;
+
+               DEBUG(0,("release_name_response: WINS server at IP %s rejected our \
+name release of name %s with error code %d.\n", 
+                        inet_ntoa(p->ip), 
+                        nmb_namestr(answer_name), nmb->header.rcode));
+       } else if (nmb->header.opcode == NMB_WACK_OPCODE) {
+               /* WINS server is telling us to wait. Pretend we didn't get
+                  the response but don't send out any more release requests. */
+
+               DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \
+name %s on subnet %s.\n", 
+                        inet_ntoa(p->ip), nmb_namestr(answer_name), subrec->subnet_name));
+
+               rrec->repeat_count = 0;
+               /* How long we should wait for. */
+               rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+               rrec->num_msgs--;
+               return;
+       }
+
+       DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n",
+                success ? "success" : "failure", nmb_namestr(answer_name), subrec->subnet_name));
+       if (success) {
+               putip((char*)&released_ip ,&nmb->answers->rdata[2]);
+
+               if(rrec->success_fn)
+                       (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip);
+               standard_success_release( subrec, rrec->userdata, answer_name, released_ip);
+       } else {
+               /* We have no standard_fail_release - maybe we should add one ? */
+               if (rrec->fail_fn) {
+                       (*(release_name_fail_function)rrec->fail_fn)(subrec, rrec, answer_name);
+               }
+       }
+
+       remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when releasing one of our names.
+****************************************************************************/
+
+static void release_name_timeout_response(struct subnet_record *subrec,
+                                         struct response_record *rrec)
+{
+       /* a release is *always* considered to be successful when it
+          times out. This doesn't cause problems as if a WINS server
+          doesn't respond and someone else wants the name then the
+          normal WACK/name query from the WINS server will cope */
+       struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+       BOOL bcast = sent_nmb->header.nm_flags.bcast;
+       struct nmb_name *question_name = &sent_nmb->question.question_name;
+       struct in_addr released_ip;
+
+       /* Get the ip address we were trying to release. */
+       putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]);
+
+       if (!bcast) {
+               /* mark the WINS server temporarily dead */
+               wins_srv_died(rrec->packet->ip, released_ip);
+       }
+
+       DEBUG(5,("release_name_timeout_response: success in releasing name %s on subnet %s.\n",
+                nmb_namestr(question_name), subrec->subnet_name));
+
+       if (rrec->success_fn) {
+               (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip);
+       }
+
+       standard_success_release( subrec, rrec->userdata, question_name, released_ip);
+       remove_response_record(subrec, rrec);
+}
+
+
+/*
+  when releasing a name with WINS we need to send the release to each of
+  the WINS groups
+*/
+static void wins_release_name(struct name_record *namerec,
+                             release_name_success_function success_fn,
+                             release_name_fail_function fail_fn,
+                             struct userdata_struct *userdata)                       
+{
+       int t, i;
+       char **wins_tags;
+
+       /* get the list of wins tags - we try to release for each of them */
+       wins_tags = wins_srv_tags();
+
+       for (t=0;wins_tags && wins_tags[t]; t++) {
+               for (i = 0; i < namerec->data.num_ips; i++) {
+                       struct in_addr wins_ip = wins_srv_ip_tag(wins_tags[t], namerec->data.ip[i]);
+
+                       BOOL last_one = ((i==namerec->data.num_ips - 1) && !wins_tags[t+1]);
+                       if (queue_release_name(unicast_subnet,
+                                              release_name_response,
+                                              release_name_timeout_response,
+                                              last_one?success_fn : NULL,
+                                              last_one? fail_fn : NULL,
+                                              last_one? userdata : NULL,
+                                              &namerec->name,
+                                              namerec->data.nb_flags,
+                                              namerec->data.ip[i],
+                                              wins_ip) == NULL) {
+                               DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+                                        nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+                       }
+               }
+       }
+
+       wins_srv_tags_free(wins_tags);
+}
+
+
+/****************************************************************************
+ Try and release one of our names.
+****************************************************************************/
+
+void release_name(struct subnet_record *subrec, struct name_record *namerec,
+                 release_name_success_function success_fn,
+                 release_name_fail_function fail_fn,
+                 struct userdata_struct *userdata)
+{
+       int i;
+
+       /* Ensure it's a SELF name, and in the ACTIVE state. */
+       if ((namerec->data.source != SELF_NAME) || !NAME_IS_ACTIVE(namerec)) {
+               DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n",
+                        nmb_namestr(&namerec->name), subrec->subnet_name, namerec->data.source)); 
+               return;
+       }
+
+       /* Set the name into the deregistering state. */
+       namerec->data.nb_flags |= NB_DEREG;
+
+       /* wins releases are a bit different */
+       if (subrec == unicast_subnet) {
+               wins_release_name(namerec, success_fn, fail_fn, userdata);
+               return;
+       }
+
+       /*  
+        * Go through and release the name for all known ip addresses.
+        * Only call the success/fail function on the last one (it should
+        * only be done once).
+        */
+       for (i = 0; i < namerec->data.num_ips; i++) {
+               if (queue_release_name(subrec,
+                                      release_name_response,
+                                      release_name_timeout_response,
+                                      (i == (namerec->data.num_ips - 1)) ? success_fn : NULL,
+                                      (i == (namerec->data.num_ips - 1)) ? fail_fn : NULL,
+                                      (i == (namerec->data.num_ips - 1)) ? userdata : NULL,
+                                      &namerec->name,
+                                      namerec->data.nb_flags,
+                                      namerec->data.ip[i],
+                                      subrec->bcast_ip) == NULL) {
+                       DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+                                nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+               }
+       }
+}
diff --git a/source4/nmbd/nmbd_nodestatus.c b/source4/nmbd/nmbd_nodestatus.c
new file mode 100644 (file)
index 0000000..993e4d9
--- /dev/null
@@ -0,0 +1,94 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+/****************************************************************************
+ Deal with a successful node status response.
+****************************************************************************/
+static void node_status_response(struct subnet_record *subrec,
+                       struct response_record *rrec, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+  struct nmb_name *answer_name = &nmb->answers->rr_name;
+
+  /* Sanity check. Ensure that the answer name in the incoming packet is the
+     same as the requested name in the outgoing packet. */
+
+  if(!nmb_name_equal(question_name, answer_name))
+  {
+    DEBUG(0,("node_status_response: Answer name %s differs from question \
+name %s.\n", nmb_namestr(answer_name), nmb_namestr(question_name)));
+    return;
+  }
+
+  DEBUG(5,("node_status_response: response from name %s on subnet %s.\n",
+        nmb_namestr(answer_name), subrec->subnet_name));
+
+  /* Just send the whole answer resource record for the success function
+     to parse. */
+  if(rrec->success_fn)
+    (*(node_status_success_function)rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip);
+
+  /* Ensure we don't retry. */
+  remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when requesting a node status.
+****************************************************************************/
+static void node_status_timeout_response(struct subnet_record *subrec,
+                       struct response_record *rrec)
+{
+  struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+  struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+  DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n",
+           nmb_namestr(question_name), subrec->subnet_name));
+
+  if( rrec->fail_fn)
+    (*rrec->fail_fn)(subrec, rrec);
+
+  /* Ensure we don't retry. */
+  remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and do a node status to a name - given the name & IP address.
+****************************************************************************/
+BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname,
+                 struct in_addr send_ip, node_status_success_function success_fn, 
+                 node_status_fail_function fail_fn, struct userdata_struct *userdata)
+{
+  if(queue_node_status( subrec, 
+              node_status_response, node_status_timeout_response,
+              success_fn, fail_fn, userdata, nmbname, send_ip)==NULL)
+  {
+    DEBUG(0,("node_status: Failed to send packet trying to get node status for \
+name %s, IP address %s\n", nmb_namestr(nmbname), inet_ntoa(send_ip)));
+    return True;
+  } 
+  return False;
+}
diff --git a/source4/nmbd/nmbd_packets.c b/source4/nmbd/nmbd_packets.c
new file mode 100644 (file)
index 0000000..6ee1381
--- /dev/null
@@ -0,0 +1,2013 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+extern int num_response_packets;
+
+extern struct in_addr loopback_ip;
+
+static void queue_packet(struct packet_struct *packet);
+
+BOOL rescan_listen_set = False;
+
+
+/*******************************************************************
+  The global packet linked-list. Incoming entries are 
+  added to the end of this list. It is supposed to remain fairly 
+  short so we won't bother with an end pointer.
+******************************************************************/
+
+static struct packet_struct *packet_queue = NULL;
+
+/***************************************************************************
+Utility function to find the specific fd to send a packet out on.
+**************************************************************************/
+
+static int find_subnet_fd_for_address( struct in_addr local_ip )
+{
+  struct subnet_record *subrec;
+
+  for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+    if(ip_equal(local_ip, subrec->myip))
+      return subrec->nmb_sock;
+
+  return ClientNMB;
+}
+
+/***************************************************************************
+Utility function to find the specific fd to send a mailslot packet out on.
+**************************************************************************/
+
+static int find_subnet_mailslot_fd_for_address( struct in_addr local_ip )
+{
+  struct subnet_record *subrec;
+
+  for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+    if(ip_equal(local_ip, subrec->myip))
+      return subrec->dgram_sock;
+
+  return ClientDGRAM;
+}
+
+/***************************************************************************
+Get/Set problematic nb_flags as network byte order 16 bit int.
+**************************************************************************/
+
+uint16 get_nb_flags(char *buf)
+{
+  return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
+}
+
+void set_nb_flags(char *buf, uint16 nb_flags)
+{
+  *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF);
+  *buf = '\0';
+}
+
+/***************************************************************************
+Dumps out the browse packet data.
+**************************************************************************/
+
+static void debug_browse_data(char *outbuf, int len)
+{
+  int i,j;
+
+  DEBUG( 4, ( "debug_browse_data():\n" ) );
+  for (i = 0; i < len; i+= 16)
+  {
+    DEBUGADD( 4, ( "%3x char ", i ) );
+
+    for (j = 0; j < 16; j++)
+    {
+      unsigned char x;
+      if (i+j >= len)
+        break;
+
+      x = outbuf[i+j];
+      if (x < 32 || x > 127) 
+        x = '.';
+           
+      DEBUGADD( 4, ( "%c", x ) );
+    }
+
+    DEBUGADD( 4, ( "%*s hex", 16-j, "" ) );
+
+    for (j = 0; j < 16; j++)
+    {
+      if (i+j >= len) 
+        break;
+      DEBUGADD( 4, ( " %02x", (unsigned char)outbuf[i+j] ) );
+    }
+
+    DEBUGADD( 4, ("\n") );
+  }
+}
+
+/***************************************************************************
+  Generates the unique transaction identifier
+**************************************************************************/
+
+static uint16 name_trn_id=0;
+
+static uint16 generate_name_trn_id(void)
+{
+
+  if (!name_trn_id)
+  {
+    name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100);
+  }
+  name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+  return name_trn_id;
+}
+
+/***************************************************************************
+ Either loops back or sends out a completed NetBIOS packet.
+**************************************************************************/
+
+static BOOL send_netbios_packet(struct packet_struct *p)
+{
+  BOOL loopback_this_packet = False;
+
+  /* Check if we are sending to or from ourselves as a WINS server. */
+  if(ismyip(p->ip) && (p->port == global_nmb_port))
+    loopback_this_packet = True;
+
+  if(loopback_this_packet)
+  {
+    struct packet_struct *lo_packet = NULL;
+    DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n"));
+    if((lo_packet = copy_packet(p)) == NULL)
+      return False;
+    queue_packet(lo_packet);
+  }
+  else if (!send_packet(p))
+  {
+    DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n",
+                         inet_ntoa(p->ip),p->port));
+    return False;
+  }
+  
+  return True;
+} 
+
+/***************************************************************************
+ Sets up the common elements of an outgoing NetBIOS packet.
+
+ Note: do not attempt to rationalise whether rec_des should be set or not
+ in a particular situation. Just follow rfc_1002 or look at examples from WinXX.
+ It does NOT follow the rule that requests to the wins server always have
+ rec_des true. See for example name releases and refreshes
+**************************************************************************/
+
+static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
+                                                            BOOL bcast, BOOL rec_des,
+                                                            struct in_addr to_ip)
+{
+  struct packet_struct *packet = NULL;
+  struct nmb_packet *nmb = NULL;
+
+  /* Allocate the packet_struct we will return. */
+  if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL)
+  {
+    DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n"));
+    return NULL;
+  }
+    
+  memset((char *)packet,'\0',sizeof(*packet));
+
+  nmb = &packet->packet.nmb;
+
+  nmb->header.name_trn_id = generate_name_trn_id();
+  nmb->header.response = False;
+  nmb->header.nm_flags.recursion_desired = rec_des;
+  nmb->header.nm_flags.recursion_available = False;
+  nmb->header.nm_flags.trunc = False;
+  nmb->header.nm_flags.authoritative = False;
+  nmb->header.nm_flags.bcast = bcast;
+  
+  nmb->header.rcode = 0;
+  nmb->header.qdcount = 1;
+  nmb->header.ancount = 0;
+  nmb->header.nscount = 0;
+
+  nmb->question.question_name = *nmbname;
+  nmb->question.question_type = QUESTION_TYPE_NB_QUERY;
+  nmb->question.question_class = QUESTION_CLASS_IN;
+
+  packet->ip = to_ip;
+  packet->port = NMB_PORT;
+  packet->fd = ClientNMB;
+  packet->timestamp = time(NULL);
+  packet->packet_type = NMB_PACKET;
+  packet->locked = False;
+  
+  return packet; /* Caller must free. */
+}
+
+/***************************************************************************
+ Sets up the common elements of register, refresh or release packet.
+**************************************************************************/
+
+static BOOL create_and_init_additional_record(struct packet_struct *packet,
+                                                     uint16 nb_flags,
+                                                     struct in_addr *register_ip)
+{
+       struct nmb_packet *nmb = &packet->packet.nmb;
+
+       if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) {
+               DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n"));
+               return False;
+       }
+
+       memset((char *)nmb->additional,'\0',sizeof(struct res_rec));
+
+       nmb->additional->rr_name  = nmb->question.question_name;
+       nmb->additional->rr_type  = RR_TYPE_NB;
+       nmb->additional->rr_class = RR_CLASS_IN;
+       
+       /* See RFC 1002, sections 5.1.1.1, 5.1.1.2 and 5.1.1.3 */
+       if (nmb->header.nm_flags.bcast)
+               nmb->additional->ttl = PERMANENT_TTL;
+       else
+               nmb->additional->ttl = lp_max_ttl();
+       
+       nmb->additional->rdlength = 6;
+       
+       set_nb_flags(nmb->additional->rdata,nb_flags);
+       
+       /* Set the address for the name we are registering. */
+       putip(&nmb->additional->rdata[2], register_ip);
+       
+       /* 
+          it turns out that Jeremys code was correct, we are supposed
+          to send registrations from the IP we are registering. The
+          trick is what to do on timeouts! When we send on a
+          non-routable IP then the reply will timeout, and we should
+          treat this as success, not failure. That means we go into
+          our standard refresh cycle for that name which copes nicely
+          with disconnected networks.
+       */
+       packet->fd = find_subnet_fd_for_address(*register_ip);
+
+       return True;
+}
+
+/***************************************************************************
+ Sends out a name query.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet( struct packet_struct *packet)
+{
+  struct nmb_packet *nmb = NULL;
+
+  nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+  nmb->header.arcount = 0;
+
+  nmb->header.nm_flags.recursion_desired = True;
+
+  DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n",
+          nmb_namestr(&nmb->question.question_name), 
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+  return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name query - from a WINS server. 
+**************************************************************************/
+
+static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+{   
+  struct nmb_packet *nmb = NULL;
+  
+  nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+  nmb->header.arcount = 0;
+    
+  nmb->header.nm_flags.recursion_desired = False;
+  
+  DEBUG(4,("initiate_name_query_packet_from_wins_server: sending query for name %s (bcast=%s) to IP %s\n",
+           nmb_namestr(&nmb->question.question_name),
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+    
+  return send_netbios_packet( packet );
+} 
+
+/***************************************************************************
+ Sends out a name register.
+**************************************************************************/
+
+static BOOL initiate_name_register_packet( struct packet_struct *packet,
+                                    uint16 nb_flags, struct in_addr *register_ip)
+{
+  struct nmb_packet *nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_REG_OPCODE;
+  nmb->header.arcount = 1;
+
+  nmb->header.nm_flags.recursion_desired = True;
+
+  if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+    return False;
+
+  DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n",
+          nmb_namestr(&nmb->additional->rr_name),
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+  return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a multihomed name register.
+**************************************************************************/
+
+static BOOL initiate_multihomed_name_register_packet(struct packet_struct *packet,
+                                                    uint16 nb_flags, struct in_addr *register_ip)
+{
+       struct nmb_packet *nmb = &packet->packet.nmb;
+       fstring second_ip_buf;
+
+       fstrcpy(second_ip_buf, inet_ntoa(packet->ip));
+
+       nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE;
+       nmb->header.arcount = 1;
+
+       nmb->header.nm_flags.recursion_desired = True;
+       
+       if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+               return False;
+       
+       DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \
+for name %s IP %s (bcast=%s) to IP %s\n",
+                nmb_namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
+                BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf ));
+
+       return send_netbios_packet( packet );
+} 
+
+/***************************************************************************
+ Sends out a name refresh.
+**************************************************************************/
+
+static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+                                   uint16 nb_flags, struct in_addr *refresh_ip)
+{
+  struct nmb_packet *nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8;
+  nmb->header.arcount = 1;
+
+  nmb->header.nm_flags.recursion_desired = False;
+
+  if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False)
+    return False;
+
+  DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n",
+          nmb_namestr(&nmb->additional->rr_name),
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+  return send_netbios_packet( packet );
+} 
+
+/***************************************************************************
+ Sends out a name release.
+**************************************************************************/
+
+static BOOL initiate_name_release_packet( struct packet_struct *packet,
+                                   uint16 nb_flags, struct in_addr *release_ip)
+{
+  struct nmb_packet *nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_RELEASE_OPCODE;
+  nmb->header.arcount = 1;
+
+  nmb->header.nm_flags.recursion_desired = False;
+
+  if(create_and_init_additional_record(packet, nb_flags, release_ip) == False)
+    return False;
+
+  DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n",
+          nmb_namestr(&nmb->additional->rr_name),
+           BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+  return send_netbios_packet( packet );
+} 
+
+/***************************************************************************
+ Sends out a node status.
+**************************************************************************/
+
+static BOOL initiate_node_status_packet( struct packet_struct *packet )
+{
+  struct nmb_packet *nmb = &packet->packet.nmb;
+
+  nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+  nmb->header.arcount = 0;
+
+  nmb->header.nm_flags.recursion_desired = False;
+
+  nmb->question.question_type = QUESTION_TYPE_NB_STATUS;
+
+  DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n",
+          nmb_namestr(&nmb->question.question_name),
+           inet_ntoa(packet->ip)));
+
+  return send_netbios_packet( packet );
+}
+
+/****************************************************************************
+  Simplification functions for queuing standard packets.
+  These should be the only publicly callable functions for sending
+  out packets.
+****************************************************************************/
+
+/****************************************************************************
+ Assertion - we should never be sending nmbd packets on the remote
+ broadcast subnet.
+****************************************************************************/
+
+static BOOL assert_check_subnet(struct subnet_record *subrec)
+{
+  if( subrec == remote_broadcast_subnet)
+  {
+    DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
+This is a bug.\n"));
+    return True;
+  }
+  return False;
+}
+
+/****************************************************************************
+ Queue a register name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_register_name( struct subnet_record *subrec,
+                          response_function resp_fn,
+                          timeout_response_function timeout_fn,
+                          register_name_success_function success_fn,
+                          register_name_fail_function fail_fn,
+                          struct userdata_struct *userdata,
+                          struct nmb_name *nmbname,
+                          uint16 nb_flags)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+
+  if(assert_check_subnet(subrec))
+    return NULL;
+
+  /* note that all name registration requests have RD set (rfc1002 -
+     section 4.2.2 */
+  if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), True,
+                                         subrec->bcast_ip)) == NULL)
+    return NULL;
+
+  if(initiate_name_register_packet( p, nb_flags, 
+                                    iface_ip(subrec->bcast_ip)) == False)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  if((rrec = make_response_record(subrec,           /* subnet record. */
+           p,                     /* packet we sent. */
+           resp_fn,               /* function to call on response. */
+           timeout_fn,            /* function to call on timeout. */
+           (success_function)success_fn,            /* function to call on operation success. */
+           (fail_function)fail_fn,               /* function to call on operation fail. */
+           userdata)) == NULL)  
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  return rrec;
+}
+
+
+/****************************************************************************
+ Queue a refresh name packet to the broadcast address of a subnet.
+****************************************************************************/
+void queue_wins_refresh(struct nmb_name *nmbname,
+                       response_function resp_fn,
+                       timeout_response_function timeout_fn,
+                       uint16 nb_flags,
+                       struct in_addr refresh_ip,
+                       const char *tag)
+{
+       struct packet_struct *p;
+       struct response_record *rrec;
+       struct in_addr wins_ip;
+       struct userdata_struct *userdata;
+       fstring ip_str;
+
+       wins_ip = wins_srv_ip_tag(tag, refresh_ip);
+
+       if ((p = create_and_init_netbios_packet(nmbname, False, False, wins_ip)) == NULL) {
+               return;
+       }
+
+       if (!initiate_name_refresh_packet(p, nb_flags, &refresh_ip)) {
+               p->locked = False;
+               free_packet(p);
+               return;
+       }
+
+       fstrcpy(ip_str, inet_ntoa(refresh_ip));
+
+       DEBUG(6,("Refreshing name %s IP %s with WINS server %s using tag '%s'\n",
+                nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+       userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+       if (!userdata) {
+               DEBUG(0,("Failed to allocate userdata structure!\n"));
+               return;
+       }
+       ZERO_STRUCTP(userdata);
+       userdata->userdata_len = strlen(tag) + 1;
+       strlcpy(userdata->data, tag, userdata->userdata_len);   
+
+       if ((rrec = make_response_record(unicast_subnet,
+                                        p,
+                                        resp_fn, timeout_fn,
+                                        NULL,
+                                        NULL,
+                                        userdata)) == NULL) {
+               p->locked = False;
+               free_packet(p);
+               return;
+       }
+
+       free(userdata);
+
+       /* we don't want to repeat refresh packets */
+       rrec->repeat_count = 0;
+}
+
+
+/****************************************************************************
+ Queue a multihomed register name packet to a given WINS server IP
+****************************************************************************/
+
+struct response_record *queue_register_multihomed_name( struct subnet_record *subrec,
+                                                       response_function resp_fn,
+                                                       timeout_response_function timeout_fn,
+                                                       register_name_success_function success_fn,
+                                                       register_name_fail_function fail_fn,
+                                                       struct userdata_struct *userdata,
+                                                       struct nmb_name *nmbname,
+                                                       uint16 nb_flags,
+                                                       struct in_addr register_ip,
+                                                       struct in_addr wins_ip)
+{
+       struct packet_struct *p;
+       struct response_record *rrec;
+       BOOL ret;
+       
+       /* Sanity check. */
+       if(subrec != unicast_subnet) {
+               DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+               return NULL;
+       }
+
+       if(assert_check_subnet(subrec))
+               return NULL;
+     
+       if ((p = create_and_init_netbios_packet(nmbname, False, True, wins_ip)) == NULL)
+               return NULL;
+
+       if (nb_flags & NB_GROUP)
+               ret = initiate_name_register_packet( p, nb_flags, &register_ip);
+       else
+               ret = initiate_multihomed_name_register_packet(p, nb_flags, &register_ip);
+
+       if (ret == False) {  
+               p->locked = False;
+               free_packet(p);
+               return NULL;
+       }  
+  
+       if ((rrec = make_response_record(subrec,    /* subnet record. */
+                                        p,                     /* packet we sent. */
+                                        resp_fn,               /* function to call on response. */
+                                        timeout_fn,            /* function to call on timeout. */
+                                        (success_function)success_fn, /* function to call on operation success. */
+                                        (fail_function)fail_fn,       /* function to call on operation fail. */
+                                        userdata)) == NULL) {  
+               p->locked = False;
+               free_packet(p);
+               return NULL;
+       }  
+       
+       return rrec;
+}
+
+/****************************************************************************
+ Queue a release name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_release_name( struct subnet_record *subrec,
+                                           response_function resp_fn,
+                                           timeout_response_function timeout_fn,
+                                           release_name_success_function success_fn,
+                                           release_name_fail_function fail_fn,
+                                           struct userdata_struct *userdata,
+                                           struct nmb_name *nmbname,
+                                           uint16 nb_flags,
+                                           struct in_addr release_ip,
+                                           struct in_addr dest_ip)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+
+  if(assert_check_subnet(subrec))
+    return NULL;
+
+  if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), False,
+                                         dest_ip)) == NULL)
+    return NULL;
+
+  if(initiate_name_release_packet( p, nb_flags, &release_ip) == False)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  if((rrec = make_response_record(subrec,           /* subnet record. */
+                    p,                     /* packet we sent. */
+                    resp_fn,               /* function to call on response. */
+                    timeout_fn,            /* function to call on timeout. */
+                    (success_function)success_fn,            /* function to call on operation success. */
+                    (fail_function)fail_fn,               /* function to call on operation fail. */
+                    userdata)) == NULL)  
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  /*
+   * For a broadcast release packet, only send once.
+   * This will cause us to remove the name asap. JRA.
+   */
+
+  if (subrec != unicast_subnet) {
+         rrec->repeat_count = 0;
+         rrec->repeat_time = 0;
+  }
+
+  return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to the broadcast address of a subnet.
+****************************************************************************/
+struct response_record *queue_query_name( struct subnet_record *subrec,
+                          response_function resp_fn,
+                          timeout_response_function timeout_fn,
+                          query_name_success_function success_fn,
+                          query_name_fail_function fail_fn,
+                          struct userdata_struct *userdata,
+                          struct nmb_name *nmbname)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+  struct in_addr to_ip;
+
+  if(assert_check_subnet(subrec))
+    return NULL;
+
+  to_ip = subrec->bcast_ip;
+  
+  /* queries to the WINS server turn up here as queries to IP 0.0.0.0 
+     These need to be handled a bit differently */
+  if (subrec->type == UNICAST_SUBNET && is_zero_ip(to_ip)) {
+         /* what we really need to do is loop over each of our wins
+          * servers and wins server tags here, but that just doesn't
+          * fit our architecture at the moment (userdata may already
+          * be used when we get here). For now we just query the first
+          * active wins server on the first tag. */
+         char **tags = wins_srv_tags();
+         if (!tags) {
+                 return NULL;
+         }
+         to_ip = wins_srv_ip_tag(tags[0], to_ip);
+         wins_srv_tags_free(tags);
+  }
+
+  if(( p = create_and_init_netbios_packet(nmbname, 
+                                         (subrec != unicast_subnet), 
+                                         (subrec == unicast_subnet), 
+                                         to_ip)) == NULL)
+    return NULL;
+
+  if(lp_bind_interfaces_only()) {
+    int i;
+
+    DEBUG(10,("queue_query_name: bind_interfaces_only is set, looking for suitable source IP\n"));
+    for(i = 0; i < iface_count(); i++) {
+      struct in_addr *ifip = iface_n_ip(i);
+
+      if(ifip == NULL) {
+        DEBUG(0,("queue_query_name: interface %d has NULL IP address !\n", i));
+        continue;
+      }
+
+      if (ip_equal(*ifip,loopback_ip)) {
+        DEBUG(5,("queue_query_name: ignoring loopback interface (%d)\n", i));
+        continue;
+      }
+
+      DEBUG(10,("queue_query_name: using source IP %s\n",inet_ntoa(*ifip)));
+      p->fd = find_subnet_fd_for_address( *ifip );
+      break;
+    }
+  }
+
+  if(initiate_name_query_packet( p ) == False) {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  if((rrec = make_response_record(subrec,           /* subnet record. */
+               p,                     /* packet we sent. */
+               resp_fn,               /* function to call on response. */
+               timeout_fn,            /* function to call on timeout. */
+               (success_function)success_fn,            /* function to call on operation success. */
+               (fail_function)fail_fn,               /* function to call on operation fail. */
+               userdata)) == NULL)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to a given address from the WINS subnet.
+****************************************************************************/
+struct response_record *queue_query_name_from_wins_server( struct in_addr to_ip,
+                          response_function resp_fn,
+                          timeout_response_function timeout_fn,
+                          query_name_success_function success_fn,
+                          query_name_fail_function fail_fn,
+                          struct userdata_struct *userdata,
+                          struct nmb_name *nmbname)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+
+  if ((p = create_and_init_netbios_packet(nmbname, False, False, to_ip)) == NULL)
+    return NULL;
+
+  if(initiate_name_query_packet_from_wins_server( p ) == False)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  if((rrec = make_response_record(wins_server_subnet,           /* subnet record. */
+               p,                     /* packet we sent. */
+               resp_fn,               /* function to call on response. */
+               timeout_fn,            /* function to call on timeout. */
+               (success_function)success_fn,            /* function to call on operation success. */
+               (fail_function)fail_fn,               /* function to call on operation fail. */
+               userdata)) == NULL)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  return rrec;
+}
+
+/****************************************************************************
+ Queue a node status packet to a given name and address.
+****************************************************************************/
+struct response_record *queue_node_status( struct subnet_record *subrec,
+                          response_function resp_fn,
+                          timeout_response_function timeout_fn,
+                          node_status_success_function success_fn,
+                          node_status_fail_function fail_fn,
+                          struct userdata_struct *userdata,
+                          struct nmb_name *nmbname,
+                          struct in_addr send_ip)
+{
+  struct packet_struct *p;
+  struct response_record *rrec;
+
+  /* Sanity check. */
+  if(subrec != unicast_subnet)
+  {
+    DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+    return NULL;
+  }
+
+  if(assert_check_subnet(subrec))
+    return NULL;
+
+  if(( p = create_and_init_netbios_packet(nmbname, False, False,
+                                         send_ip)) == NULL)
+    return NULL;
+
+  if(initiate_node_status_packet(p) == False)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  } 
+
+  if((rrec = make_response_record(subrec,           /* subnet record. */
+                   p,                     /* packet we sent. */
+                   resp_fn,               /* function to call on response. */
+                   timeout_fn,            /* function to call on timeout. */
+                   (success_function)success_fn,            /* function to call on operation success. */
+                   (fail_function)fail_fn,               /* function to call on operation fail. */
+                   userdata)) == NULL)
+  {
+    p->locked = False;
+    free_packet(p);
+    return NULL;
+  }
+
+  return rrec;
+}
+
+/****************************************************************************
+  Reply to a netbios name packet.  see rfc1002.txt
+****************************************************************************/
+
+void reply_netbios_packet(struct packet_struct *orig_packet,
+                          int rcode, enum netbios_reply_type_code rcv_code, int opcode,
+                          int ttl, char *data,int len)
+{
+  struct packet_struct packet;
+  struct nmb_packet *nmb = NULL;
+  struct res_rec answers;
+  struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
+  BOOL loopback_this_packet = False;
+  const char *packet_type = "unknown";
+  
+  /* Check if we are sending to or from ourselves. */
+  if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
+    loopback_this_packet = True;
+  
+  nmb = &packet.packet.nmb;
+
+  /* Do a partial copy of the packet. We clear the locked flag and
+     the resource record pointers. */
+  packet = *orig_packet;   /* Full structure copy. */
+  packet.locked = False;
+  nmb->answers = NULL;
+  nmb->nsrecs = NULL;
+  nmb->additional = NULL;
+
+  switch (rcv_code)
+  {
+    case NMB_STATUS:
+    {
+      packet_type = "nmb_status";
+      nmb->header.nm_flags.recursion_desired = False;
+      nmb->header.nm_flags.recursion_available = False;
+      break;
+    }
+    case NMB_QUERY:
+    {
+      packet_type = "nmb_query";
+      nmb->header.nm_flags.recursion_desired = True;
+      nmb->header.nm_flags.recursion_available = True;
+      break;
+    }
+    case NMB_REG:
+    case NMB_REG_REFRESH:
+    {
+      packet_type = "nmb_reg";
+      nmb->header.nm_flags.recursion_desired = True;
+      nmb->header.nm_flags.recursion_available = True;
+      break;
+    }
+    case NMB_REL:
+    {
+      packet_type = "nmb_rel";
+      nmb->header.nm_flags.recursion_desired = False;
+      nmb->header.nm_flags.recursion_available = False;
+      break;
+    }
+    case NMB_WAIT_ACK:
+    {
+      packet_type = "nmb_wack";
+      nmb->header.nm_flags.recursion_desired = False;
+      nmb->header.nm_flags.recursion_available = False;
+      break;
+    }
+    case WINS_REG:
+    {
+      packet_type = "wins_reg";
+      nmb->header.nm_flags.recursion_desired = True;
+      nmb->header.nm_flags.recursion_available = True;
+      break;
+    }
+    case WINS_QUERY:
+    {
+      packet_type = "wins_query";
+      nmb->header.nm_flags.recursion_desired = True;
+      nmb->header.nm_flags.recursion_available = True;
+      break;
+    }
+
+    default:
+    {
+      DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n",
+                   packet_type, nmb_namestr(&orig_nmb->question.question_name),
+                    inet_ntoa(packet.ip)));
+
+      return;
+    }
+  }
+
+  DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \
+for id %hu\n",
+          packet_type, nmb_namestr(&orig_nmb->question.question_name),
+           inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
+
+  nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
+  nmb->header.opcode = opcode;
+  nmb->header.response = True;
+  nmb->header.nm_flags.bcast = False;
+  nmb->header.nm_flags.trunc = False;
+  nmb->header.nm_flags.authoritative = True;
+  
+  nmb->header.rcode = rcode;
+  nmb->header.qdcount = 0;
+  nmb->header.ancount = 1;
+  nmb->header.nscount = 0;
+  nmb->header.arcount = 0;
+  
+  memset((char*)&nmb->question,'\0',sizeof(nmb->question));
+  
+  nmb->answers = &answers;
+  memset((char*)nmb->answers,'\0',sizeof(*nmb->answers));
+  
+  nmb->answers->rr_name  = orig_nmb->question.question_name;
+  nmb->answers->rr_type  = orig_nmb->question.question_type;
+  nmb->answers->rr_class = orig_nmb->question.question_class;
+  nmb->answers->ttl      = ttl;
+  
+  if (data && len)
+  {
+    nmb->answers->rdlength = len;
+    memcpy(nmb->answers->rdata, data, len);
+  }
+  
+  packet.packet_type = NMB_PACKET;
+  /* Ensure we send out on the same fd that the original
+     packet came in on to give the correct source IP address. */
+  packet.fd = orig_packet->fd;
+  packet.timestamp = time(NULL);
+
+  debug_nmb_packet(&packet);
+  
+  if(loopback_this_packet)
+  {
+    struct packet_struct *lo_packet;
+    DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n"));
+    if((lo_packet = copy_packet(&packet)) == NULL)
+      return;
+    queue_packet(lo_packet);
+  }
+  else if (!send_packet(&packet)) 
+  {
+    DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n",
+                 inet_ntoa(packet.ip),packet.port));
+  }
+}
+
+/*******************************************************************
+  Queue a packet into a packet queue
+******************************************************************/
+static void queue_packet(struct packet_struct *packet)
+{
+  struct packet_struct *p;
+
+  if (!packet_queue) 
+  {
+    packet->prev = NULL;
+    packet->next = NULL;
+    packet_queue = packet;
+    return;
+  }
+  
+  /* find the bottom */
+  for (p=packet_queue;p->next;p=p->next) 
+    ;
+
+  p->next = packet;
+  packet->next = NULL;
+  packet->prev = p;
+}
+
+/****************************************************************************
+ Try and find a matching subnet record for a datagram port 138 packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p)
+{
+  struct subnet_record *subrec;
+
+  /* Go through all the broadcast subnets and see if the mask matches. */
+  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+      return subrec;
+  }
+
+  /* If the subnet record is the remote announce broadcast subnet,
+     hack it here to be the first subnet. This is really gross and
+     is needed due to people turning on port 137/138 broadcast
+     forwarding on their routers. May fire and brimstone rain
+     down upon them...
+   */
+
+  return FIRST_SUBNET;
+}
+
+/****************************************************************************
+Dispatch a browse frame from port 138 to the correct processing function.
+****************************************************************************/
+static void process_browse_packet(struct packet_struct *p, char *buf,int len)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int command = CVAL(buf,0);
+  struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+
+  /* Drop the packet if it's a different NetBIOS scope, or
+     the source is from one of our names. */
+
+  if (!strequal(dgram->dest_name.scope, lp_netbios_scope()))
+  {
+    DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, lp_netbios_scope()));
+    return;
+  }
+
+  if (is_myname(dgram->source_name.name))
+  {
+    DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+    return;
+  }
+
+  switch (command)
+  {
+    case ANN_HostAnnouncement:
+    {
+      debug_browse_data(buf, len);
+      process_host_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_DomainAnnouncement:
+    {
+      debug_browse_data(buf, len);
+      process_workgroup_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_LocalMasterAnnouncement:
+    {
+      debug_browse_data(buf, len);
+      process_local_master_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_AnnouncementRequest:
+    {
+      debug_browse_data(buf, len);
+      process_announce_request(subrec, p, buf+1);
+      break;
+    }
+    case ANN_Election:
+    {
+      debug_browse_data(buf, len);
+      process_election(subrec, p, buf+1);
+      break;
+    }
+    case ANN_GetBackupListReq:
+    {
+      debug_browse_data(buf, len);
+      process_get_backup_list_request(subrec, p, buf+1);
+      break;
+    }
+    case ANN_GetBackupListResp:
+    {
+      debug_browse_data(buf, len);
+      /* We never send ANN_GetBackupListReq so we
+         should never get these. */
+      DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \
+packet from %s IP %s\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip)));
+      break;
+    }
+    case ANN_ResetBrowserState:
+    {
+      debug_browse_data(buf, len);
+      process_reset_browser(subrec, p, buf+1);
+      break;
+    }
+    case ANN_MasterAnnouncement:
+    {
+      /* Master browser datagrams must be processed
+         on the unicast subnet. */
+      subrec = unicast_subnet;
+
+      debug_browse_data(buf, len);
+      process_master_browser_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_BecomeBackup:
+    {
+      /* 
+       * We don't currently implement this. Log it just in case.
+       */
+      debug_browse_data(buf, len);
+      DEBUG(10,("process_browse_packet: On subnet %s ignoring browse packet \
+command ANN_BecomeBackup from %s IP %s to %s\n",
+            subrec->subnet_name, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+      break;
+    }
+    default:
+    {
+      debug_browse_data(buf, len);
+      DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n", 
+            subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+    }
+  } 
+}
+
+/****************************************************************************
+ Dispatch a LanMan browse frame from port 138 to the correct processing function.
+****************************************************************************/
+static void process_lanman_packet(struct packet_struct *p, char *buf,int len)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  int command = SVAL(buf,0);
+  struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+
+  /* Drop the packet if it's a different NetBIOS scope, or
+     the source is from one of our names. */
+
+  if (!strequal(dgram->dest_name.scope, lp_netbios_scope()))
+  {
+    DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, lp_netbios_scope()));
+    return;
+  }
+
+  if (is_myname(dgram->source_name.name))
+  {
+    DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+    return;
+  }
+
+  switch (command)
+  {
+    case ANN_HostAnnouncement:
+    {
+      debug_browse_data(buf, len);
+      process_lm_host_announce(subrec, p, buf+1);
+      break;
+    }
+    case ANN_AnnouncementRequest:
+    {
+      process_lm_announce_request(subrec, p, buf+1);
+      break;
+    }
+    default:
+    {
+      DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n",
+            subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+            inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+    }
+  }
+}
+
+/****************************************************************************
+  Determine if a packet is for us on port 138. Note that to have any chance of
+  being efficient we need to drop as many packets as possible at this
+  stage as subsequent processing is expensive. 
+****************************************************************************/
+
+static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+{
+  struct subnet_record *subrec = NULL;
+
+  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+      break;
+  }
+
+  if(subrec == NULL)
+    subrec = unicast_subnet;
+
+  return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL);
+}
+
+/****************************************************************************
+  Process udp 138 datagrams
+****************************************************************************/
+static void process_dgram(struct packet_struct *p)
+{
+  char *buf;
+  char *buf2;
+  int len;
+  struct dgram_packet *dgram = &p->packet.dgram;
+
+  /* If we aren't listening to the destination name then ignore the packet */
+  if (!listening(p,&dgram->dest_name))
+  {
+         unexpected_packet(p);
+         DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n",
+                  nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip)));
+         return;
+  }
+
+  if (dgram->header.msg_type != 0x10 &&
+      dgram->header.msg_type != 0x11 &&
+      dgram->header.msg_type != 0x12) 
+  {
+         unexpected_packet(p);
+         /* Don't process error packets etc yet */
+         DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \
+an error packet of type %x\n",
+                  nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
+         return;
+  }
+
+  buf = &dgram->data[0];
+  buf -= 4; /* XXXX for the pseudo tcp length - 
+              someday I need to get rid of this */
+
+  if (CVAL(buf,smb_com) != SMBtrans)
+    return;
+
+  len = SVAL(buf,smb_vwv11);
+  buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
+
+  if (len <= 0)
+    return;
+
+  if (buf2 + len > buf + sizeof(dgram->data)) {
+    DEBUG(2,("process_dgram: datagram from %s to %s IP %s for %s len=%d too long.\n",
+               nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+               inet_ntoa(p->ip), smb_buf(buf),len));
+       len = (buf + sizeof(dgram->data)) - buf;
+  }
+
+  DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n",
+          nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+          inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len));
+
+  /* Datagram packet received for the browser mailslot */
+  if (strequal(smb_buf(buf),BROWSE_MAILSLOT))
+  {
+    process_browse_packet(p,buf2,len);
+    return;
+  }
+
+  /* Datagram packet received for the LAN Manager mailslot */
+  if (strequal(smb_buf(buf),LANMAN_MAILSLOT)) {
+    process_lanman_packet(p,buf2,len);
+    return;
+  }
+
+  /* Datagram packet received for the domain logon mailslot */
+  if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT))
+  {
+    process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT);
+    return;
+  }
+
+  /* Datagram packet received for the NT domain logon mailslot */
+  if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT))
+  {
+    process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT);
+    return;
+  }
+
+  unexpected_packet(p);
+}
+
+/****************************************************************************
+  Validate a response nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+{
+  BOOL ignore = False;
+
+  switch (nmb->header.opcode) 
+  {
+    case NMB_NAME_REG_OPCODE:
+    case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+    case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+      if (nmb->header.ancount == 0)
+      {
+        DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. "));
+        ignore = True;
+      }
+      break;
+
+    case NMB_NAME_QUERY_OPCODE:
+      if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1))
+      {
+        DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. "));
+        ignore = True;
+      }
+      break;
+    case NMB_NAME_RELEASE_OPCODE:
+      if (nmb->header.ancount == 0)
+      {
+        DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. "));
+        ignore = True;
+      }
+      break;
+    case NMB_WACK_OPCODE:
+      /* Check WACK response here. */
+      if (nmb->header.ancount != 1)
+      {
+        DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. "));
+        ignore = True;
+      }
+      break;
+    default:
+      DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n",
+        nmb->header.opcode));
+      return True;
+  }
+
+  if(ignore)
+    DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode));
+
+  return ignore;
+}
+/****************************************************************************
+  Validate a request nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_packet( struct nmb_packet *nmb )
+{
+  BOOL ignore = False;
+
+  switch (nmb->header.opcode) 
+  {
+    case NMB_NAME_REG_OPCODE:
+    case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+    case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+    case NMB_NAME_MULTIHOMED_REG_OPCODE:
+      if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+      {
+        DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. "));
+        ignore = True;
+      }
+      break;
+
+    case NMB_NAME_QUERY_OPCODE:
+      if ((nmb->header.qdcount == 0) || 
+         ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) &&
+         (nmb->question.question_type != QUESTION_TYPE_NB_STATUS)))
+      {
+        DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. "));
+        ignore = True;
+      }
+      break;
+
+    case NMB_NAME_RELEASE_OPCODE:
+      if (nmb->header.qdcount==0 || nmb->header.arcount==0)
+      {
+        DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. "));
+        ignore = True;
+      }
+      break;
+    default:
+      DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n",
+        nmb->header.opcode));
+      return True;
+  }
+
+  if(ignore)
+    DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode));
+
+  return ignore;
+}
+
+/****************************************************************************
+  Find a subnet (and potentially a response record) for a packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p,
+                                                         struct response_record **pprrec)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct response_record *rrec = NULL;
+  struct subnet_record *subrec = NULL;
+
+  if(pprrec != NULL)
+    *pprrec = NULL;
+
+  if(nmb->header.response)
+  {
+    /* It's a response packet. Find a record for it or it's an error. */
+
+    rrec = find_response_record( &subrec, nmb->header.name_trn_id);
+    if(rrec == NULL)
+    {
+      DEBUG(3,("find_subnet_for_nmb_packet: response record not found for response id %hu\n",
+               nmb->header.name_trn_id));
+      unexpected_packet(p);
+      return NULL;
+    }
+
+    if(subrec == NULL)
+    {
+      DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n",
+               nmb->header.name_trn_id));
+      return NULL;
+    }
+
+    if(pprrec != NULL)
+      *pprrec = rrec;
+    return subrec;
+  }
+
+  /* Try and see what subnet this packet belongs to. */
+
+  /* WINS server ? */
+  if(packet_is_for_wins_server(p))
+    return wins_server_subnet;
+
+  /* If it wasn't a broadcast packet then send to the UNICAST subnet. */
+  if(nmb->header.nm_flags.bcast == False)
+    return unicast_subnet;
+
+  /* Go through all the broadcast subnets and see if the mask matches. */
+  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+      return subrec;
+  }
+
+  /* If none match it must have been a directed broadcast - assign
+     the remote_broadcast_subnet. */
+  return remote_broadcast_subnet;
+}
+
+/****************************************************************************
+  Process a nmb request packet - validate the packet and route it.
+****************************************************************************/
+
+static void process_nmb_request(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct subnet_record *subrec = NULL;
+
+  debug_nmb_packet(p);
+
+  /* Ensure we have a good packet. */
+  if(validate_nmb_packet(nmb))
+    return;
+
+  /* Allocate a subnet to this packet - if we cannot - fail. */
+  if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL)
+    return;
+
+  switch (nmb->header.opcode) 
+  {
+    case NMB_NAME_REG_OPCODE:
+      if(subrec == wins_server_subnet)
+        wins_process_name_registration_request(subrec, p);
+      else
+        process_name_registration_request(subrec, p);
+      break;
+
+    case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+    case NMB_NAME_REFRESH_OPCODE_9:
+      if(subrec == wins_server_subnet)
+        wins_process_name_refresh_request(subrec, p);
+      else
+        process_name_refresh_request(subrec, p);
+      break;
+
+    case NMB_NAME_MULTIHOMED_REG_OPCODE:
+      if(subrec == wins_server_subnet)
+        wins_process_multihomed_name_registration_request(subrec, p);
+      else
+      {
+        DEBUG(0,("process_nmb_request: Multihomed registration request must be \
+directed at a WINS server.\n"));
+      }
+      break;
+
+    case NMB_NAME_QUERY_OPCODE:
+      switch (nmb->question.question_type)
+      {
+        case QUESTION_TYPE_NB_QUERY:
+        {
+          if(subrec == wins_server_subnet)
+            wins_process_name_query_request(subrec, p);
+          else
+            process_name_query_request(subrec, p);
+          break;
+        }
+        case QUESTION_TYPE_NB_STATUS:
+        {
+          if(subrec == wins_server_subnet)
+          {
+            DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \
+not allowed.\n"));
+            break;
+          }
+          else
+            process_node_status_request(subrec, p);
+          break;
+        }
+      }
+      break;
+      
+    case NMB_NAME_RELEASE_OPCODE:
+      if(subrec == wins_server_subnet)
+        wins_process_name_release_request(subrec, p);
+      else
+        process_name_release_request(subrec, p);
+      break;
+  }
+}
+
+/****************************************************************************
+  Process a nmb response packet - validate the packet and route it.
+  to either the WINS server or a normal response.
+****************************************************************************/
+
+static void process_nmb_response(struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct subnet_record *subrec = NULL;
+  struct response_record *rrec = NULL;
+
+  debug_nmb_packet(p);
+
+  if(validate_nmb_response_packet(nmb))
+    return;
+
+  if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL)
+    return;
+
+  if(rrec == NULL)
+  {
+    DEBUG(0,("process_nmb_response: response packet received but no response record \
+found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
+    return;
+  }
+
+  /* Increment the number of responses received for this record. */
+  rrec->num_msgs++;
+  /* Ensure we don't re-send the request. */
+  rrec->repeat_count = 0;
+  
+  /* Call the response received function for this packet. */
+  (*rrec->resp_fn)(subrec, rrec, p);
+}
+
+
+/*******************************************************************
+  Run elements off the packet queue till its empty
+******************************************************************/
+
+void run_packet_queue(void)
+{
+  struct packet_struct *p;
+
+  while ((p = packet_queue))
+  {
+    packet_queue = p->next;
+    if (packet_queue)
+      packet_queue->prev = NULL;
+    p->next = p->prev = NULL;
+
+    switch (p->packet_type)
+    {
+      case NMB_PACKET:
+        if(p->packet.nmb.header.response)
+          process_nmb_response(p);
+        else
+          process_nmb_request(p);
+        break;
+
+      case DGRAM_PACKET:
+        process_dgram(p);
+        break;
+    }
+    free_packet(p);
+  }
+} 
+
+/*******************************************************************
+ Retransmit or timeout elements from all the outgoing subnet response
+ record queues. NOTE that this code must also check the WINS server
+ subnet for response records to timeout as the WINS server code
+ can send requests to check if a client still owns a name.
+ (Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>).
+******************************************************************/
+
+void retransmit_or_expire_response_records(time_t t)
+{
+  struct subnet_record *subrec;
+
+  for (subrec = FIRST_SUBNET; subrec; 
+               subrec = get_next_subnet_maybe_unicast_or_wins_server(subrec))
+  {
+    struct response_record *rrec, *nextrrec;
+
+    for (rrec = subrec->responselist; rrec; rrec = nextrrec)
+    {
+      nextrrec = rrec->next;
+   
+      if (rrec->repeat_time <= t) 
+      {
+        if (rrec->repeat_count > 0)
+        {
+          /* Resend while we have a non-zero repeat_count. */
+          if(!send_packet(rrec->packet))
+          {
+            DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \
+to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), 
+                          subrec->subnet_name));
+          }
+          rrec->repeat_time = t + rrec->repeat_interval;
+          rrec->repeat_count--;
+        }
+        else
+        {
+          DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \
+on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), 
+                 subrec->subnet_name));
+
+          /*
+           * Check the flag in this record to prevent recursion if we end
+           * up in this function again via the timeout function call.
+           */
+
+          if(!rrec->in_expiration_processing)
+          {
+
+            /*
+             * Set the recursion protection flag in this record.
+             */
+
+            rrec->in_expiration_processing = True;
+
+            /* Call the timeout function. This will deal with removing the
+               timed out packet. */
+            if(rrec->timeout_fn)
+              (*rrec->timeout_fn)(subrec, rrec);
+            else
+            {
+              /* We must remove the record ourself if there is
+                 no timeout function. */
+              remove_response_record(subrec, rrec);
+            }
+          } /* !rrec->in_expitation_processing */
+        } /* rrec->repeat_count > 0 */
+      } /* rrec->repeat_time <= t */
+    } /* end for rrec */
+  } /* end for subnet */
+}
+
+/****************************************************************************
+  Create an fd_set containing all the sockets in the subnet structures,
+  plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number)
+{
+  int *sock_array = NULL;
+  struct subnet_record *subrec = NULL;
+  int count = 0;
+  int num = 0;
+  fd_set *pset = (fd_set *)malloc(sizeof(fd_set));
+
+  if(pset == NULL)
+  {
+    DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+    return True;
+  }
+
+  /* Check that we can add all the fd's we need. */
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+    count++;
+
+  if((count*2) + 2 > FD_SETSIZE)
+  {
+    DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
+only use %d.\n", (count*2) + 2, FD_SETSIZE));
+    return True;
+  }
+
+  if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL)
+  {
+    DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n"));
+    return True;
+  }
+
+  FD_ZERO(pset);
+
+  /* Add in the broadcast socket on 137. */
+  FD_SET(ClientNMB,pset);
+  sock_array[num++] = ClientNMB;
+
+  /* Add in the 137 sockets on all the interfaces. */
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    FD_SET(subrec->nmb_sock,pset);
+    sock_array[num++] = subrec->nmb_sock;
+  }
+
+  /* Add in the broadcast socket on 138. */
+  FD_SET(ClientDGRAM,pset);
+  sock_array[num++] = ClientDGRAM;
+
+  /* Add in the 138 sockets on all the interfaces. */
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    FD_SET(subrec->dgram_sock,pset);
+    sock_array[num++] = subrec->dgram_sock;
+  }
+
+  *listen_number = (count*2) + 2;
+
+  SAFE_FREE(*ppset);
+  SAFE_FREE(*psock_array);
+
+  *ppset = pset;
+  *psock_array = sock_array;
+  return False;
+}
+
+/****************************************************************************
+  Listens for NMB or DGRAM packets, and queues them.
+  return True if the socket is dead
+***************************************************************************/
+
+BOOL listen_for_packets(BOOL run_election)
+{
+  static fd_set *listen_set = NULL;
+  static int listen_number = 0;
+  static int *sock_array = NULL;
+  int i;
+
+  fd_set fds;
+  int selrtn;
+  struct timeval timeout;
+#ifndef SYNC_DNS
+  int dns_fd;
+#endif
+
+  if(listen_set == NULL || rescan_listen_set)
+  {
+    if(create_listen_fdset(&listen_set, &sock_array, &listen_number))
+    {
+      DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+      return True;
+    }
+    rescan_listen_set = False;
+  }
+
+  memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+#ifndef SYNC_DNS
+  dns_fd = asyncdns_fd();
+  if (dns_fd != -1) {
+         FD_SET(dns_fd, &fds);
+  }
+#endif
+
+
+  /* 
+   * During elections and when expecting a netbios response packet we
+   * need to send election packets at tighter intervals.
+   * Ideally it needs to be the interval (in ms) between time now and
+   * the time we are expecting the next netbios packet.
+   */
+
+  timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP;
+  timeout.tv_usec = 0;
+
+  /* Prepare for the select - allow certain signals. */
+
+  BlockSignals(False, SIGTERM);
+
+  selrtn = sys_select(FD_SETSIZE,&fds,NULL,NULL,&timeout);
+
+  /* We can only take signals when we are in the select - block them again here. */
+
+  BlockSignals(True, SIGTERM);
+
+  if(selrtn == -1) {
+         return False;
+  }
+
+#ifndef SYNC_DNS
+  if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) {
+         run_dns_queue();
+  }
+#endif
+
+  for(i = 0; i < listen_number; i++) {
+         if (i < (listen_number/2)) {
+                 /* Processing a 137 socket. */
+                 if (FD_ISSET(sock_array[i],&fds)) {
+                         struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET);
+                         if (packet) {
+                                 /*
+                                  * If we got a packet on the broadcast socket and interfaces
+                                  * only is set then check it came from one of our local nets. 
+                                  */
+                                 if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) && 
+                                    (!is_local_net(packet->ip))) {
+                                         DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n",
+                                                  inet_ntoa(packet->ip),packet->port));          
+                                         free_packet(packet);
+                                 } else if ((ip_equal(loopback_ip, packet->ip) || 
+                                             ismyip(packet->ip)) && packet->port == global_nmb_port &&
+                                            packet->packet.nmb.header.nm_flags.bcast) {
+                                         DEBUG(7,("discarding own bcast packet from %s:%d\n",
+                                                  inet_ntoa(packet->ip),packet->port));          
+                                         free_packet(packet);
+                                 } else {
+                                         /* Save the file descriptor this packet came in on. */
+                                         packet->fd = sock_array[i];
+                                         queue_packet(packet);
+                                 }
+                         }
+                 }
+         } else {
+                 /* Processing a 138 socket. */
+                 if (FD_ISSET(sock_array[i],&fds)) {
+                         struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET);
+                         if (packet) {
+                                 /*
+                                  * If we got a packet on the broadcast socket and interfaces
+                                  * only is set then check it came from one of our local nets. 
+                                  */
+                                 if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) && 
+                                    (!is_local_net(packet->ip))) {
+                                         DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n",
+                                                  inet_ntoa(packet->ip),packet->port));          
+                                         free_packet(packet);
+                                 } else if ((ip_equal(loopback_ip, packet->ip) || 
+                                             ismyip(packet->ip)) && packet->port == DGRAM_PORT) {
+                                         DEBUG(7,("discarding own dgram packet from %s:%d\n",
+                                                  inet_ntoa(packet->ip),packet->port));          
+                                         free_packet(packet);
+                                 } else {
+                                         /* Save the file descriptor this packet came in on. */
+                                         packet->fd = sock_array[i];
+                                         queue_packet(packet);
+                                 }
+                         }
+                 }
+         } /* end processing 138 socket. */
+  } /* end for */
+  return False;
+}
+
+/****************************************************************************
+  Construct and send a netbios DGRAM.
+**************************************************************************/
+BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf,int len,
+                   const char *srcname, int src_type,
+                   const char *dstname, int dest_type,
+                   struct in_addr dest_ip,struct in_addr src_ip,
+                  int dest_port)
+{
+  BOOL loopback_this_packet = False;
+  struct packet_struct p;
+  struct dgram_packet *dgram = &p.packet.dgram;
+  char *ptr,*p2;
+  char tmp[4];
+
+  memset((char *)&p,'\0',sizeof(p));
+
+  if(ismyip(dest_ip) && (dest_port == DGRAM_PORT)) /* Only if to DGRAM_PORT */
+    loopback_this_packet = True;
+
+  /* generate_name_trn_id(); */ /* Not used, so gone, RJS */
+
+  /* DIRECT GROUP or UNIQUE datagram. */
+  dgram->header.msg_type = unique ? 0x10 : 0x11; 
+  dgram->header.flags.node_type = M_NODE;
+  dgram->header.flags.first = True;
+  dgram->header.flags.more = False;
+  dgram->header.dgm_id = generate_name_trn_id();
+  dgram->header.source_ip = src_ip;
+  dgram->header.source_port = DGRAM_PORT;
+  dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+  dgram->header.packet_offset = 0;
+  
+  make_nmb_name(&dgram->source_name,srcname,src_type);
+  make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+  ptr = &dgram->data[0];
+
+  /* Setup the smb part. */
+  ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+  memcpy(tmp,ptr,4);
+  set_message(ptr,17,23 + len,True);
+  memcpy(ptr,tmp,4);
+
+  SCVAL(ptr,smb_com,SMBtrans);
+  SSVAL(ptr,smb_vwv1,len);
+  SSVAL(ptr,smb_vwv11,len);
+  SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+  SSVAL(ptr,smb_vwv13,3);
+  SSVAL(ptr,smb_vwv14,1);
+  SSVAL(ptr,smb_vwv15,1);
+  SSVAL(ptr,smb_vwv16,2);
+  p2 = smb_buf(ptr);
+  pstrcpy(p2,mailslot);
+  p2 = skip_string(p2,1);
+
+  memcpy(p2,buf,len);
+  p2 += len;
+
+  dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+  p.ip = dest_ip;
+  p.port = dest_port;
+  p.fd = find_subnet_mailslot_fd_for_address( src_ip );
+  p.timestamp = time(NULL);
+  p.packet_type = DGRAM_PACKET;
+
+  DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+                    nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+  DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+  debug_browse_data(buf, len);
+
+  if(loopback_this_packet)
+  {
+    struct packet_struct *lo_packet = NULL;
+    DEBUG(5,("send_mailslot: sending packet to ourselves.\n"));
+    if((lo_packet = copy_packet(&p)) == NULL)
+      return False;
+    queue_packet(lo_packet);
+    return True;
+  }
+  else
+    return(send_packet(&p));
+}
diff --git a/source4/nmbd/nmbd_processlogon.c b/source4/nmbd/nmbd_processlogon.c
new file mode 100644 (file)
index 0000000..1fcfd11
--- /dev/null
@@ -0,0 +1,480 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   Copyright (C) Jim McDonough 2002
+   Copyright (C) Anthony Liguori 2002
+   
+   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.
+   
+   Revision History:
+
+*/
+
+#include "includes.h"
+
+struct sam_database_info {
+        uint32 index;
+        uint32 serial_lo, serial_hi;
+        uint32 date_lo, date_hi;
+};
+
+/****************************************************************************
+Send a message to smbd to do a sam delta sync
+**************************************************************************/
+static void send_repl_message(uint32 low_serial)
+{
+        TDB_CONTEXT *tdb;
+
+        tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+                           TDB_DEFAULT, O_RDONLY, 0);
+
+        if (!tdb) {
+                DEBUG(3, ("send_repl_message(): failed to open connections "
+                          "database\n"));
+                return;
+        }
+
+        DEBUG(3, ("sending replication message, serial = 0x%04x\n", 
+                  low_serial));
+        
+        message_send_all(tdb, MSG_SMB_SAM_REPL, &low_serial,
+                         sizeof(low_serial), False, NULL);
+
+        tdb_close(tdb);
+}
+
+/****************************************************************************
+Process a domain logon packet
+**************************************************************************/
+
+void process_logon_packet(struct packet_struct *p, char *buf,int len, 
+                          const char *mailslot)
+{
+  struct dgram_packet *dgram = &p->packet.dgram;
+  pstring my_name;
+  fstring reply_name;
+  pstring outbuf;
+  int code;
+  uint16 token = 0;
+  uint32 ntversion = 0;
+  uint16 lmnttoken = 0;
+  uint16 lm20token = 0;
+  uint32 domainsidsize;
+  BOOL short_request = False;
+  char *getdc;
+  char *uniuser; /* Unicode user name. */
+  pstring ascuser;
+  char *unicomp; /* Unicode computer name. */
+
+  memset(outbuf, 0, sizeof(outbuf));
+
+  if (!lp_domain_logons())
+  {
+    DEBUG(3,("process_logon_packet: Logon packet received from IP %s and domain \
+logons are not enabled.\n", inet_ntoa(p->ip) ));
+    return;
+  }
+
+  pstrcpy(my_name, lp_netbios_name());
+
+  code = SVAL(buf,0);
+  DEBUG(1,("process_logon_packet: Logon from %s: code = 0x%x\n", inet_ntoa(p->ip), code));
+
+  switch (code)
+  {
+    case 0:    
+    {
+      char *q = buf + 2;
+      char *machine = q;
+      char *user = skip_string(machine,1);
+
+      getdc = skip_string(user,1);
+      q = skip_string(getdc,1);
+      token = SVAL(q,3);
+
+      fstrcpy(reply_name,my_name); 
+
+      DEBUG(3,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n",
+             machine,inet_ntoa(p->ip),user,token));
+
+      q = outbuf;
+      SSVAL(q, 0, 6);
+      q += 2;
+
+      fstrcpy(reply_name, "\\\\");
+      fstrcat(reply_name, my_name);
+      fstrcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */
+
+      SSVAL(q, 0, token);
+      q += 2;
+
+      dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+      send_mailslot(True, getdc, 
+                    outbuf,PTR_DIFF(q,outbuf),
+                   lp_netbios_name(), 0x0,
+                                       machine,
+                    dgram->source_name.name_type,
+                    p->ip, *iface_ip(p->ip), p->port);  
+      break;
+    }
+
+    case QUERYFORPDC:
+    {
+      char *q = buf + 2;
+      char *machine = q;
+
+      if (!lp_domain_master())
+      {  
+         /* We're not Primary Domain Controller -- ignore this */
+         return;
+      }
+
+      getdc = skip_string(machine,1);
+      q = skip_string(getdc,1);
+      q = ALIGN2(q, buf);
+
+      /* at this point we can work out if this is a W9X or NT style
+         request. Experiments show that the difference is wether the
+         packet ends here. For a W9X request we now end with a pair of
+         bytes (usually 0xFE 0xFF) whereas with NT we have two further
+         strings - the following is a simple way of detecting this */
+      if (len - PTR_DIFF(q, buf) <= 3) {
+             short_request = True;
+      } else {
+             unicomp = q;
+
+             /* A full length (NT style) request */
+             q = skip_unibuf(unicomp, PTR_DIFF(buf + len, unicomp));
+
+             if (len - PTR_DIFF(q, buf) > 8) {
+                                       /* with NT5 clients we can sometimes
+                                          get additional data - a length specificed string
+                                          containing the domain name, then 16 bytes of
+                                          data (no idea what it is) */
+                                       int dom_len = CVAL(q, 0);
+                                       q++;
+                                       if (dom_len != 0) {
+                                               q += dom_len + 1;
+                                       }
+                                       q += 16;
+             }
+             ntversion = IVAL(q, 0);
+             lmnttoken = SVAL(q, 4);
+             lm20token = SVAL(q, 6);
+      }
+
+      /* Construct reply. */
+      q = outbuf;
+      SSVAL(q, 0, QUERYFORPDC_R);
+      q += 2;
+
+      fstrcpy(reply_name,my_name);
+      fstrcpy(q, reply_name);
+      q = skip_string(q, 1); /* PDC name */
+
+      /* PDC and domain name */
+      if (!short_request)  /* Make a full reply */
+      {
+        q = ALIGN2(q, outbuf);
+
+        q += dos_PutUniCode(q, my_name, sizeof(pstring), True); /* PDC name */
+        q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); /* Domain name*/
+        SIVAL(q, 0, 1); /* our nt version */
+        SSVAL(q, 4, 0xffff); /* our lmnttoken */
+        SSVAL(q, 6, 0xffff); /* our lm20token */
+        q += 8;
+      }
+
+      /* RJS, 21-Feb-2000, we send a short reply if the request was short */
+
+      DEBUG(3,("process_logon_packet: GETDC request from %s at IP %s, \
+reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
+            machine,inet_ntoa(p->ip), reply_name, lp_workgroup(),
+            QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken,
+            (uint32)lm20token ));
+
+      dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+      send_mailslot(True, getdc,
+                  outbuf,PTR_DIFF(q,outbuf),
+                   lp_netbios_name(), 0x0,
+                  dgram->source_name.name,
+                  dgram->source_name.name_type,
+                  p->ip, *iface_ip(p->ip), p->port);  
+      return;
+    }
+
+    case SAMLOGON:
+    {
+      char *q = buf + 2;
+      fstring asccomp;
+
+      q += 2;
+      unicomp = q;
+      uniuser = skip_unibuf(unicomp, PTR_DIFF(buf+len, unicomp));
+      getdc = skip_unibuf(uniuser,PTR_DIFF(buf+len, uniuser));
+      q = skip_string(getdc,1);
+      q += 4; /* Account Control Bits - indicating username type */
+      domainsidsize = IVAL(q, 0);
+      q += 4;
+
+      DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d, len = %d\n", domainsidsize, len));
+
+      if (domainsidsize < (len - PTR_DIFF(q, buf)) && (domainsidsize != 0)) {
+             q += domainsidsize;
+             q = ALIGN4(q, buf);
+      }
+
+      DEBUG(3,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %d\n", len, PTR_DIFF(q, buf) ));
+
+      if (len - PTR_DIFF(q, buf) > 8) {
+             /* with NT5 clients we can sometimes
+                get additional data - a length specificed string
+                containing the domain name, then 16 bytes of
+                data (no idea what it is) */
+             int dom_len = CVAL(q, 0);
+             q++;
+             if (dom_len < (len - PTR_DIFF(q, buf)) && (dom_len != 0)) {
+                     q += dom_len + 1;
+             }
+             q += 16;
+      }
+
+      ntversion = IVAL(q, 0);
+      lmnttoken = SVAL(q, 4);
+      lm20token = SVAL(q, 6);
+      q += 8;
+
+      DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion));
+
+      /*
+       * we respond regadless of whether the machine is in our password 
+       * database. If it isn't then we let smbd send an appropriate error.
+       * Let's ignore the SID.
+       */
+      pull_ucs2_pstring(ascuser, uniuser);
+      pull_ucs2_fstring(asccomp, unicomp);
+      DEBUG(3,("process_logon_packet: SAMLOGON user %s\n", ascuser));
+
+      fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */
+      fstrcat(reply_name, my_name);
+
+      DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n",
+              asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(),
+              SAMLOGON_R ,lmnttoken));
+
+      /* Construct reply. */
+
+      q = outbuf;
+      /* we want the simple version unless we are an ADS PDC..which means  */
+      /* never, at least for now */
+      if ((ntversion < 11) || (SEC_ADS != lp_security()) || (ROLE_DOMAIN_PDC != lp_server_role())) {
+       if (SVAL(uniuser, 0) == 0) {
+         SSVAL(q, 0, SAMLOGON_UNK_R);  /* user unknown */
+       } else {
+         SSVAL(q, 0, SAMLOGON_R);
+       }
+
+       q += 2;
+
+       q += dos_PutUniCode(q, reply_name,sizeof(pstring), True);
+       q += dos_PutUniCode(q, ascuser, sizeof(pstring), True);
+       q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True);
+      } 
+#ifdef HAVE_ADS
+      else {
+       GUID domain_guid;
+       pstring domain;
+       char *hostname = NULL;
+       char *component, *dc, *q1;
+       uint8 size;
+
+       get_mydomname(domain);
+       hostname = get_myname();
+       
+       if (SVAL(uniuser, 0) == 0) {
+         SSVAL(q, 0, SAMLOGON_AD_UNK_R);       /* user unknown */
+       } else {
+         SSVAL(q, 0, SAMLOGON_AD_R);
+       }
+       q += 2;
+
+       SSVAL(q, 0, 0);
+       q += 2;
+       SIVAL(q, 0, ADS_PDC|ADS_GC|ADS_LDAP|ADS_DS|
+                   ADS_KDC|ADS_TIMESERV|ADS_CLOSEST|ADS_WRITABLE);
+       q += 4;
+
+       /* Push Domain GUID */
+       if (False == secrets_fetch_domain_guid(domain, &domain_guid)) {
+         DEBUG(2, ("Could not fetch DomainGUID for %s\n", domain));
+         return;
+       }
+       memcpy(q, &domain_guid, sizeof(domain_guid));
+       q += sizeof(domain_guid);
+
+       /* Push domain components */
+       dc = domain;
+       q1 = q;
+       while ((component = strtok(dc, "."))) {
+         dc = NULL;
+         size = push_ascii(&q[1], component, -1, 0);
+         SCVAL(q, 0, size);
+         q += (size + 1);
+       }
+       SCVAL(q, 0, 0); q++;
+       SSVAL(q, 0, 0x18c0); /* not sure what this is for, but  */
+       q += 2;              /* it must follow the domain name. */
+
+       /* Push dns host name */
+       size = push_ascii(&q[1], hostname, -1, 0);
+       SCVAL(q, 0, size);
+       q += (size + 1);
+       SSVAL(q, 0, 0x18c0); /* not sure what this is for, but  */
+       q += 2;              /* it must follow the domain name. */
+
+       /* Push NETBIOS of domain */
+       size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER);
+       SCVAL(q, 0, size);
+       q += (size + 1);
+       SCVAL(q, 0, 0); q++; /* is this a null terminator or empty field */
+       /* null terminator would not be needed because size is included */
+
+       /* Push NETBIOS of hostname */
+       size = push_ascii(&q[1], my_name, -1, 0);
+       SCVAL(q, 0, size);
+       q += (size + 1);
+       SCVAL(q, 0, 0); q++; /* null terminator or empty field? */
+
+       /* Push user account */
+       size = push_ascii(&q[1], ascuser, -1, 0);
+       SCVAL(q, 0, size);
+       q += (size + 1);
+
+       /* Push 'Default-First-Site-Name' */
+       size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0);
+       SCVAL(q, 0, size);
+       q += (size + 1);
+
+       SSVAL(q, 0, 0xc000); /* unknown */
+       SCVAL(q, 2, PTR_DIFF(q,q1));
+       SCVAL(q, 3, 0x10); /* unknown */
+       q += 4;
+
+       SIVAL(q, 0, 0x00000002); q += 4; /* unknown */
+       SIVAL(q, 0, (iface_ip(p->ip))->s_addr); q += 4;
+       SIVAL(q, 0, 0x00000000); q += 4; /* unknown */
+       SIVAL(q, 0, 0x00000000); q += 4; /* unknown */
+       if (hostname) free(hostname);
+      }        
+#endif
+
+      /* tell the client what version we are */
+      SIVAL(q, 0, ((ntversion < 11) || (SEC_ADS != lp_security())) ? 1 : 13); 
+      /* our ntversion */
+      SSVAL(q, 4, 0xffff); /* our lmnttoken */ 
+      SSVAL(q, 6, 0xffff); /* our lm20token */
+      q += 8;
+
+      dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+      send_mailslot(True, getdc,
+                   outbuf,PTR_DIFF(q,outbuf),
+                   lp_netbios_name(), 0x0,
+                   dgram->source_name.name,
+                   dgram->source_name.name_type,
+                   p->ip, *iface_ip(p->ip), p->port);  
+      break;
+    }
+
+    /* Announce change to UAS or SAM.  Send by the domain controller when a
+       replication event is required. */
+
+  case SAM_UAS_CHANGE: {
+          struct sam_database_info *db_info;
+          char *q = buf + 2;
+          int i, db_count;
+          uint32 low_serial;
+          
+          /* Header */
+          
+          low_serial = IVAL(q, 0); q += 4;     /* Low serial number */
+
+          q += 4;                   /* Date/time */
+          q += 4;                   /* Pulse */
+          q += 4;                   /* Random */
+          
+          /* Domain info */
+          
+          q = skip_string(q, 1);    /* PDC name */
+          q = skip_string(q, 1);    /* Domain name */
+          q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode PDC name */
+          q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode domain name */
+          
+          /* Database info */
+          
+          db_count = SVAL(q, 0); q += 2;
+          
+          db_info = (struct sam_database_info *)
+                  malloc(sizeof(struct sam_database_info) * db_count);
+
+          if (db_info == NULL) {
+                  DEBUG(3, ("out of memory allocating info for %d databases\n",
+                            db_count));
+                  return;
+          }
+          
+          for (i = 0; i < db_count; i++) {
+                  db_info[i].index = IVAL(q, 0);
+                  db_info[i].serial_lo = IVAL(q, 4);
+                  db_info[i].serial_hi = IVAL(q, 8);
+                  db_info[i].date_lo = IVAL(q, 12);
+                  db_info[i].date_hi = IVAL(q, 16);
+                  q += 20;
+          }
+
+          /* Domain SID */
+
+          q += IVAL(q, 0) + 4;  /* 4 byte length plus data */
+          
+          q += 2;               /* Alignment? */
+
+          /* Misc other info */
+
+          q += 4;               /* NT version (0x1) */
+          q += 2;               /* LMNT token (0xff) */
+          q += 2;               /* LM20 token (0xff) */
+
+          SAFE_FREE(db_info);        /* Not sure whether we need to do anything
+                                   useful with these */
+
+          /* Send message to smbd */
+
+          send_repl_message(low_serial);
+
+          break;
+  }
+
+    default:
+    {
+      DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code));
+      return;
+    }
+  }
+}
diff --git a/source4/nmbd/nmbd_responserecordsdb.c b/source4/nmbd/nmbd_responserecordsdb.c
new file mode 100644 (file)
index 0000000..7e8c802
--- /dev/null
@@ -0,0 +1,264 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios library routines
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern int ClientNMB;
+
+int num_response_packets = 0;
+
+/***************************************************************************
+  Add an expected response record into the list
+  **************************************************************************/
+
+static void add_response_record(struct subnet_record *subrec,
+                               struct response_record *rrec)
+{
+  struct response_record *rrec2;
+
+  num_response_packets++; /* count of total number of packets still around */
+
+  DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n",
+            rrec->response_id, subrec->subnet_name, num_response_packets));
+
+  if (!subrec->responselist)
+  {
+    subrec->responselist = rrec;
+    rrec->prev = NULL;
+    rrec->next = NULL;
+    return;
+  }
+  
+  for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next) 
+    ;
+  
+  rrec2->next = rrec;
+  rrec->next = NULL;
+  rrec->prev = rrec2;
+}
+
+/***************************************************************************
+  Remove an expected response record from the list
+  **************************************************************************/
+
+void remove_response_record(struct subnet_record *subrec,
+                               struct response_record *rrec)
+{
+  if (rrec->prev)
+    rrec->prev->next = rrec->next;
+  if (rrec->next)
+    rrec->next->prev = rrec->prev;
+
+  if (subrec->responselist == rrec) 
+    subrec->responselist = rrec->next; 
+
+  if(rrec->userdata)
+  {
+         if(rrec->userdata->free_fn) {
+                 (*rrec->userdata->free_fn)(rrec->userdata);
+         } else {
+                 ZERO_STRUCTP(rrec->userdata);
+                 SAFE_FREE(rrec->userdata);
+         }
+  }
+
+  /* Ensure we can delete. */
+  rrec->packet->locked = False;
+  free_packet(rrec->packet);
+
+  ZERO_STRUCTP(rrec);
+  SAFE_FREE(rrec);
+
+  num_response_packets--; /* count of total number of packets still around */
+}
+
+/****************************************************************************
+  Create a response record for an outgoing packet.
+  **************************************************************************/
+
+struct response_record *make_response_record( struct subnet_record *subrec,
+                                             struct packet_struct *p,
+                                             response_function resp_fn,
+                                             timeout_response_function timeout_fn,
+                                             success_function success_fn,
+                                             fail_function fail_fn,
+                                             struct userdata_struct *userdata)
+{
+  struct response_record *rrec;
+  struct nmb_packet *nmb = &p->packet.nmb;
+
+  if (!(rrec = (struct response_record *)malloc(sizeof(*rrec)))) 
+  {
+    DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n"));
+    return NULL;
+  }
+
+  memset((char *)rrec, '\0', sizeof(*rrec));
+
+  rrec->response_id = nmb->header.name_trn_id;
+
+  rrec->resp_fn = resp_fn;
+  rrec->timeout_fn = timeout_fn;
+  rrec->success_fn = success_fn;
+  rrec->fail_fn = fail_fn;
+
+  rrec->packet = p;
+
+  if(userdata)
+  {
+    /* Intelligent userdata. */
+    if(userdata->copy_fn)
+    {
+      if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL)
+      {
+        DEBUG(0,("make_response_queue_record: copy fail for userdata.\n"));
+       ZERO_STRUCTP(rrec);
+        SAFE_FREE(rrec);
+        return NULL;
+      }
+    }
+    else
+    {
+      /* Primitive userdata, do a memcpy. */
+      if((rrec->userdata = (struct userdata_struct *)
+           malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL)
+      {
+        DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n"));
+       ZERO_STRUCTP(rrec);
+        SAFE_FREE(rrec);
+        return NULL;
+      }
+      rrec->userdata->copy_fn = userdata->copy_fn;
+      rrec->userdata->free_fn = userdata->free_fn;
+      rrec->userdata->userdata_len = userdata->userdata_len;
+      memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len);
+    }
+  }
+  else
+    rrec->userdata = NULL;
+
+  rrec->num_msgs = 0;
+
+  if(!nmb->header.nm_flags.bcast)
+    rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */
+  else
+    rrec->repeat_interval = 1; /* XXXX should be in ms */
+  rrec->repeat_count = 3; /* 3 retries */
+  rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */
+
+  /* This packet is not being processed. */
+  rrec->in_expiration_processing = False;
+
+  /* Lock the packet so we won't lose it while it's on the list. */
+  p->locked = True;
+
+  add_response_record(subrec, rrec);
+
+  return rrec;
+}
+
+/****************************************************************************
+  Find a response in a subnet's name query response list. 
+  **************************************************************************/
+
+static struct response_record *find_response_record_on_subnet(
+                                struct subnet_record *subrec, uint16 id)
+{  
+  struct response_record *rrec = NULL;
+
+  for (rrec = subrec->responselist; rrec; rrec = rrec->next)
+  {
+    if (rrec->response_id == id) 
+    {
+      DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n",
+               id, subrec->subnet_name));
+      break;
+    }
+  }
+  return rrec;
+}
+
+/****************************************************************************
+  Find a response in any subnet's name query response list. 
+  **************************************************************************/
+
+struct response_record *find_response_record(struct subnet_record **ppsubrec,
+                               uint16 id)
+{  
+  struct response_record *rrec = NULL;
+
+  for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec); 
+                  (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec))
+  {
+    if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL)
+      return rrec;
+  }
+
+  /* There should never be response records on the remote_broadcast subnet.
+     Sanity check to ensure this is so. */
+  if(remote_broadcast_subnet->responselist != NULL)
+  {
+    DEBUG(0,("find_response_record: response record found on subnet %s. This should \
+never happen !\n", remote_broadcast_subnet->subnet_name));
+  }
+
+  /* Now check the WINS server subnet if it exists. */
+  if(wins_server_subnet != NULL)
+  {
+    *ppsubrec = wins_server_subnet;
+    if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL)
+      return rrec;
+  }
+
+  DEBUG(0,("find_response_record: response packet id %hu received with no \
+matching record.\n", id));
+  *ppsubrec = NULL;
+
+  return NULL;
+}
+
+/****************************************************************************
+  Check if a refresh is queued for a particular name on a particular subnet.
+  **************************************************************************/
+   
+BOOL is_refresh_already_queued(struct subnet_record *subrec, struct name_record *namerec)
+{  
+  struct response_record *rrec = NULL;
+   
+  for (rrec = subrec->responselist; rrec; rrec = rrec->next)
+  {
+    struct packet_struct *p = rrec->packet;
+    struct nmb_packet *nmb = &p->packet.nmb;
+
+    if((nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+       (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9))
+    {
+      /* Yes it's a queued refresh - check if the name is correct. */
+      if(nmb_name_equal(&nmb->question.question_name, &namerec->name))
+        return True;
+    }
+  }
+
+  return False;
+} 
diff --git a/source4/nmbd/nmbd_sendannounce.c b/source4/nmbd/nmbd_sendannounce.c
new file mode 100644 (file)
index 0000000..191a3b7
--- /dev/null
@@ -0,0 +1,607 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+
+   SMB Version handling
+   Copyright (C) John H Terpstra 1995-1998
+   
+   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"
+
+extern int  updatecount;
+extern BOOL found_lm_clients;
+
+/****************************************************************************
+ Send a browser reset packet.
+**************************************************************************/
+
+void send_browser_reset(int reset_type, const char *to_name, int to_type, struct in_addr to_ip)
+{
+  pstring outbuf;
+  char *p;
+
+  DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n",
+       reset_type, to_name, to_type, inet_ntoa(to_ip) ));
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf;
+  SCVAL(p,0,ANN_ResetBrowserState);
+  p++;
+  SCVAL(p,0,reset_type);
+  p++;
+
+  send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+                lp_netbios_name(), 0x0, to_name, to_type, to_ip, 
+               FIRST_SUBNET->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+  Broadcast a packet to the local net requesting that all servers in this
+  workgroup announce themselves to us.
+  **************************************************************************/
+
+void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work)
+{
+  pstring outbuf;
+  char *p;
+
+  work->needannounce = True;
+
+  DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \
+to subnet %s\n", work->work_group, subrec->subnet_name));
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf;
+  SCVAL(p,0,ANN_AnnouncementRequest);
+  p++;
+
+  SCVAL(p,0,work->token); /* (local) Unique workgroup token id. */
+  p++;
+  p +=  push_string(NULL, p+1, lp_netbios_name(), 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+  
+  send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+                lp_netbios_name(), 0x0, work->work_group,0x1e, subrec->bcast_ip, 
+               subrec->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+  Broadcast an announcement.
+  **************************************************************************/
+
+static void send_announcement(struct subnet_record *subrec, int announce_type,
+                              const char *from_name, const char *to_name, int to_type, struct in_addr to_ip,
+                              time_t announce_interval,
+                              const char *server_name, int server_type, const char *server_comment)
+{
+  pstring outbuf;
+  char *p;
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf+1;
+
+  SCVAL(outbuf,0,announce_type);
+
+  /* Announcement parameters. */
+  SCVAL(p,0,updatecount);
+  SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */
+
+  push_string(NULL, p+5, server_name, 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+  SCVAL(p,21,lp_major_announce_version()); /* Major version. */
+  SCVAL(p,22,lp_minor_announce_version()); /* Minor version. */
+
+  SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+  /* Browse version: got from NT/AS 4.00  - Value defined in smb.h (JHT). */
+  SSVAL(p,27,BROWSER_ELECTION_VERSION);
+  SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */
+
+  p += 31 + push_string(NULL, p+31, server_comment, -1, STR_ASCII|STR_TERMINATE);
+
+  send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+                from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+               DGRAM_PORT);
+}
+
+/****************************************************************************
+  Broadcast a LanMan announcement.
+**************************************************************************/
+
+static void send_lm_announcement(struct subnet_record *subrec, int announce_type,
+                              char *from_name, char *to_name, int to_type, struct in_addr to_ip,
+                              time_t announce_interval,
+                              char *server_name, int server_type, char *server_comment)
+{
+  pstring outbuf;
+  char *p=outbuf;
+
+  memset(outbuf,'\0',sizeof(outbuf));
+
+  SSVAL(p,0,announce_type);
+  SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+  SCVAL(p,6,lp_major_announce_version()); /* Major version. */
+  SCVAL(p,7,lp_minor_announce_version()); /* Minor version. */
+  SSVAL(p,8,announce_interval);            /* In seconds - according to spec. */
+
+  p += 10;
+  /*StrnCpy(p,server_name,15);
+  strupper(p);
+  p = skip_string(p,1);
+  pstrcpy(p,server_comment);
+  p = skip_string(p,1);*/
+  p += push_string(NULL, p, server_name, 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+  p += push_string(NULL, p, server_comment, sizeof(pstring)-15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+  send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+                from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+               DGRAM_PORT);
+}
+
+/****************************************************************************
+ We are a local master browser. Announce this to WORKGROUP<1e>.
+****************************************************************************/
+
+static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work,
+                                           struct server_record *servrec)
+{
+  /* Ensure we don't have the prohibited bit set. */
+  uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+  DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n",
+            type, lp_netbios_name(), subrec->subnet_name, work->work_group));
+
+  send_announcement(subrec, ANN_LocalMasterAnnouncement,
+                    lp_netbios_name(),                 /* From nbt name. */
+                    work->work_group, 0x1e,          /* To nbt name. */
+                    subrec->bcast_ip,                /* To ip. */
+                    work->announce_interval,         /* Time until next announce. */
+                    lp_netbios_name(),                 /* Name to announce. */
+                    type,                            /* Type field. */
+                    servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the workgroup WORKGROUP to MSBROWSE<01>.
+****************************************************************************/
+
+static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work)
+{
+  DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n",
+            subrec->subnet_name, work->work_group));
+
+  send_announcement(subrec, ANN_DomainAnnouncement,
+                    lp_netbios_name(),                 /* From nbt name. */
+                    MSBROWSE, 0x1,                   /* To nbt name. */
+                    subrec->bcast_ip,                /* To ip. */
+                    work->announce_interval,         /* Time until next announce. */
+                    work->work_group,                /* Name to announce. */
+                    SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT,  /* workgroup announce flags. */
+                    lp_netbios_name());                /* From name as comment. */
+}
+
+/****************************************************************************
+ Announce the given host to WORKGROUP<1d>.
+****************************************************************************/
+
+static void send_host_announcement(struct subnet_record *subrec, struct work_record *work,
+                                   struct server_record *servrec)
+{
+  /* Ensure we don't have the prohibited bits set. */
+  uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+  DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n",
+            type, servrec->serv.name, subrec->subnet_name, work->work_group));
+
+  send_announcement(subrec, ANN_HostAnnouncement,
+                    servrec->serv.name,              /* From nbt name. */
+                    work->work_group, 0x1d,          /* To nbt name. */
+                    subrec->bcast_ip,                /* To ip. */
+                    work->announce_interval,         /* Time until next announce. */
+                    servrec->serv.name,              /* Name to announce. */
+                    type,                            /* Type field. */
+                    servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the given LanMan host
+****************************************************************************/
+
+static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work,
+                                   struct server_record *servrec, int lm_interval)
+{
+  /* Ensure we don't have the prohibited bits set. */
+  uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+  DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n",
+            type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval));
+
+  send_lm_announcement(subrec, ANN_HostAnnouncement,
+                    servrec->serv.name,              /* From nbt name. */
+                    work->work_group, 0x00,          /* To nbt name. */
+                    subrec->bcast_ip,                /* To ip. */
+                    lm_interval,                     /* Time until next announce. */
+                    servrec->serv.name,              /* Name to announce. */
+                    type,                            /* Type field. */
+                    servrec->serv.comment);
+}
+
+/****************************************************************************
+  Announce a server record.
+  ****************************************************************************/
+
+static void announce_server(struct subnet_record *subrec, struct work_record *work,
+                     struct server_record *servrec)
+{
+  /* Only do domain announcements if we are a master and it's
+     our primary name we're being asked to announce. */
+
+  if (AM_LOCAL_MASTER_BROWSER(work) && strequal(lp_netbios_name(),servrec->serv.name))
+  {
+    send_local_master_announcement(subrec, work, servrec);
+    send_workgroup_announcement(subrec, work);
+  }
+  else
+  {
+    send_host_announcement(subrec, work, servrec);
+  }
+}
+
+/****************************************************************************
+  Go through all my registered names on all broadcast subnets and announce
+  them if the timeout requires it.
+  **************************************************************************/
+
+void announce_my_server_names(time_t t)
+{
+  struct subnet_record *subrec;
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+    if(work)
+    {
+      struct server_record *servrec;
+
+      if (work->needannounce)
+      {
+        /* Drop back to a max 3 minute announce. This is to prevent a
+           single lost packet from breaking things for too long. */
+
+        work->announce_interval = MIN(work->announce_interval,
+                                    CHECK_TIME_MIN_HOST_ANNCE*60);
+        work->lastannounce_time = t - (work->announce_interval+1);
+        work->needannounce = False;
+      }
+
+      /* Announce every minute at first then progress to every 12 mins */
+      if ((t - work->lastannounce_time) < work->announce_interval)
+        continue;
+
+      if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60))
+        work->announce_interval += 60;
+
+      work->lastannounce_time = t;
+
+      for (servrec = work->serverlist; servrec; servrec = servrec->next)
+      {
+        if (is_myname(servrec->serv.name))
+          announce_server(subrec, work, servrec);
+      }
+    } /* if work */
+  } /* for subrec */
+}
+
+/****************************************************************************
+  Go through all my registered names on all broadcast subnets and announce
+  them as a LanMan server if the timeout requires it.
+**************************************************************************/
+
+void announce_my_lm_server_names(time_t t)
+{
+  struct subnet_record *subrec;
+  static time_t last_lm_announce_time=0;
+  int announce_interval = lp_lm_interval();
+  int lm_announce = lp_lm_announce();
+
+  if ((announce_interval <= 0) || (lm_announce <= 0))
+  {
+    /* user absolutely does not want LM announcements to be sent. */
+    return;
+  }
+
+  if ((lm_announce >= 2) && (!found_lm_clients))
+  {
+    /* has been set to 2 (Auto) but no LM clients detected (yet). */
+    return;
+  }
+
+  /* Otherwise: must have been set to 1 (Yes), or LM clients *have*
+     been detected. */
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+    if(work)
+    {
+      struct server_record *servrec;
+
+      if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval ))
+        continue;
+
+      last_lm_announce_time = t;
+
+      for (servrec = work->serverlist; servrec; servrec = servrec->next)
+      {
+        if (is_myname(servrec->serv.name))
+          /* skipping equivalent of announce_server() */
+          send_lm_host_announcement(subrec, work, servrec, announce_interval);
+      }
+    } /* if work */
+  } /* for subrec */
+}
+
+/* Announce timer. Moved into global static so it can be reset
+   when a machine becomes a local master browser. */
+static time_t announce_timer_last=0;
+
+/****************************************************************************
+ Reset the announce_timer so that a local master browser announce will be done
+ immediately.
+ ****************************************************************************/
+
+void reset_announce_timer(void)
+{
+  announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
+}
+
+/****************************************************************************
+  Announce myself as a local master browser to a domain master browser.
+  **************************************************************************/
+
+void announce_myself_to_domain_master_browser(time_t t)
+{
+  struct subnet_record *subrec;
+  struct work_record *work;
+
+  if(!we_are_a_wins_client())
+  {
+    DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n"));
+    return;
+  }
+
+  if (!announce_timer_last)
+    announce_timer_last = t;
+
+  if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60))
+  {
+    DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n",
+             (int)t, (int)announce_timer_last, 
+             CHECK_TIME_MST_ANNOUNCE * 60 ));
+    return;
+  }
+
+  announce_timer_last = t;
+
+  /* Look over all our broadcast subnets to see if any of them
+     has the state set as local master browser. */
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      if (AM_LOCAL_MASTER_BROWSER(work))
+      {
+        DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+        /* Look in nmbd_browsersync.c for the rest of this code. */
+        announce_and_sync_with_domain_master_browser(subrec, work);
+      }
+    }
+  }
+}
+
+/****************************************************************************
+Announce all samba's server entries as 'gone'.
+This must *only* be called on shutdown.
+****************************************************************************/
+
+void announce_my_servers_removed(void)
+{
+  int announce_interval = lp_lm_interval();
+  int lm_announce = lp_lm_announce();
+  struct subnet_record *subrec; 
+
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+    for (work = subrec->workgrouplist; work; work = work->next)
+    {
+      struct server_record *servrec;
+
+      work->announce_interval = 0;
+      for (servrec = work->serverlist; servrec; servrec = servrec->next)
+      {
+        if (!is_myname(servrec->serv.name))
+          continue;
+        servrec->serv.type = 0;
+        if(AM_LOCAL_MASTER_BROWSER(work))
+          send_local_master_announcement(subrec, work, servrec);
+        send_host_announcement(subrec, work, servrec);
+
+
+        if ((announce_interval <= 0) || (lm_announce <= 0))
+        {
+          /* user absolutely does not want LM announcements to be sent. */
+          continue;
+        }
+
+        if ((lm_announce >= 2) && (!found_lm_clients))
+        {
+          /* has been set to 2 (Auto) but no LM clients detected (yet). */
+          continue;
+        }
+
+        /* 
+         * lm announce was set or we have seen lm announcements, so do
+         * a lm announcement of host removed.
+         */
+
+        send_lm_host_announcement(subrec, work, servrec, 0);
+      }
+    }
+  }
+}
+
+/****************************************************************************
+  Do all the "remote" announcements. These are used to put ourselves
+  on a remote browse list. They are done blind, no checking is done to
+  see if there is actually a local master browser at the other end.
+  **************************************************************************/
+
+void announce_remote(time_t t)
+{
+  char *s;
+  const char *ptr;
+  static time_t last_time = 0;
+  pstring s2;
+  struct in_addr addr;
+  char *comment;
+  int stype = lp_default_server_announce();
+
+  if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+    return;
+
+  last_time = t;
+
+  s = lp_remote_announce();
+  if (!*s)
+    return;
+
+  comment = string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH);
+
+  for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) 
+  {
+    /* The entries are of the form a.b.c.d/WORKGROUP with 
+       WORKGROUP being optional */
+    const char *wgroup;
+    char *pwgroup;
+    int i;
+
+    pwgroup = strchr_m(s2,'/');
+    if (pwgroup)
+      *pwgroup++ = 0;
+    if (!pwgroup || !*pwgroup)
+      wgroup = lp_workgroup();
+    else
+      wgroup = pwgroup;
+
+    addr = *interpret_addr2(s2);
+    
+    /* Announce all our names including aliases */
+    /* Give the ip address as the address of our first
+       broadcast subnet. */
+
+    for(i=0; my_netbios_names(i); i++) 
+    {
+      const char *name = my_netbios_names(i);
+
+      DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n",
+                 name, inet_ntoa(addr) ));
+
+      send_announcement(FIRST_SUBNET, ANN_HostAnnouncement,
+                    name,                      /* From nbt name. */
+                    wgroup, 0x1d,              /* To nbt name. */
+                    addr,                      /* To ip. */
+                    REMOTE_ANNOUNCE_INTERVAL,  /* Time until next announce. */
+                    name,                      /* Name to announce. */
+                    stype,                     /* Type field. */
+                    comment);
+    }
+  }
+}
+
+/****************************************************************************
+  Implement the 'remote browse sync' feature Andrew added.
+  These are used to put our browse lists into remote browse lists.
+  **************************************************************************/
+
+void browse_sync_remote(time_t t)
+{  
+  char *s;
+  const char *ptr;
+  static time_t last_time = 0; 
+  pstring s2;
+  struct in_addr addr;
+  struct work_record *work;
+  pstring outbuf;
+  char *p;
+  if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+    return;
+   
+  last_time = t;
+
+  s = lp_remote_browse_sync();
+  if (!*s)
+    return;
+
+  /*
+   * We only do this if we are the local master browser
+   * for our workgroup on the firsst subnet.
+   */
+
+  if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL)
+  {   
+    DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n",
+           lp_workgroup(), FIRST_SUBNET->subnet_name ));
+    return;
+  }   
+         
+  if(!AM_LOCAL_MASTER_BROWSER(work))
+  {
+    DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \
+for workgroup %s on subnet %s.\n", lp_workgroup(), FIRST_SUBNET->subnet_name ));
+    return;
+  } 
+
+  memset(outbuf,'\0',sizeof(outbuf));
+  p = outbuf;
+  SCVAL(p,0,ANN_MasterAnnouncement);
+  p++;
+
+  StrnCpy(p,lp_netbios_name(),15);
+  strupper(p);
+  p = skip_string(p,1);
+
+  for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) 
+  {
+    /* The entries are of the form a.b.c.d */
+    addr = *interpret_addr2(s2);
+
+    DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n",
+                 lp_netbios_name(), inet_ntoa(addr) ));
+
+    send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+          lp_netbios_name(), 0x0, "*", 0x0, addr, FIRST_SUBNET->myip, DGRAM_PORT);
+  }
+}
diff --git a/source4/nmbd/nmbd_serverlistdb.c b/source4/nmbd/nmbd_serverlistdb.c
new file mode 100644 (file)
index 0000000..ee0c021
--- /dev/null
@@ -0,0 +1,448 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern int ClientNMB;
+
+int updatecount = 0;
+
+/*******************************************************************
+  Remove all the servers in a work group.
+  ******************************************************************/
+
+void remove_all_servers(struct work_record *work)
+{
+  struct server_record *servrec;
+  struct server_record *nexts;
+
+  for (servrec = work->serverlist; servrec; servrec = nexts)
+  {
+    DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
+    nexts = servrec->next;
+
+    if (servrec->prev)
+      servrec->prev->next = servrec->next;
+    if (servrec->next)
+      servrec->next->prev = servrec->prev;
+
+    if (work->serverlist == servrec)
+      work->serverlist = servrec->next;
+
+    ZERO_STRUCTP(servrec);
+    SAFE_FREE(servrec);
+
+  }
+
+  work->subnet->work_changed = True;
+}
+
+/***************************************************************************
+  Add a server into the a workgroup serverlist.
+  **************************************************************************/
+
+static void add_server_to_workgroup(struct work_record *work,
+                             struct server_record *servrec)
+{
+  struct server_record *servrec2;
+
+  if (!work->serverlist)
+  {
+    work->serverlist = servrec;
+    servrec->prev = NULL;
+    servrec->next = NULL;
+    return;
+  }
+
+  for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
+    ;
+
+  servrec2->next = servrec;
+  servrec->next = NULL;
+  servrec->prev = servrec2;
+  work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+  Find a server in a server list.
+  **************************************************************************/
+
+struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
+{
+  struct server_record *ret;
+  
+  for (ret = work->serverlist; ret; ret = ret->next)
+  {
+    if (strequal(ret->serv.name,name))
+      return ret;
+  }
+  return NULL;
+}
+
+
+/****************************************************************************
+  Remove a server entry from this workgroup.
+  ****************************************************************************/
+
+void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
+{
+  if (servrec->prev)
+    servrec->prev->next = servrec->next;
+  if (servrec->next)
+    servrec->next->prev = servrec->prev;
+
+  if (work->serverlist == servrec) 
+    work->serverlist = servrec->next; 
+
+  ZERO_STRUCTP(servrec);
+  SAFE_FREE(servrec);
+  work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+  Create a server entry on this workgroup.
+  ****************************************************************************/
+
+struct server_record *create_server_on_workgroup(struct work_record *work,
+                                                 const char *name,int servertype, 
+                                                 int ttl, const char *comment)
+{
+  struct server_record *servrec;
+  
+  if (name[0] == '*')
+  {
+      DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
+             name));
+      return (NULL);
+  }
+  
+  if((servrec = find_server_in_workgroup(work, name)) != NULL)
+  {
+    DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
+workgroup %s. This is a bug.\n", name, work->work_group));
+    return NULL;
+  }
+  
+  if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL)
+  {
+    DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
+    return NULL;
+  }
+
+  memset((char *)servrec,'\0',sizeof(*servrec));
+  servrec->subnet = work->subnet;
+  StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1);
+  StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
+  strupper(servrec->serv.name);
+  servrec->serv.type  = servertype;
+
+  update_server_ttl(servrec, ttl);
+  
+  add_server_to_workgroup(work, servrec);
+      
+  DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
+workgroup %s.\n", name,servertype,comment, work->work_group));
+  work->subnet->work_changed = True;
+  return(servrec);
+}
+
+/*******************************************************************
+ Update the ttl field of a server record.
+*******************************************************************/
+
+void update_server_ttl(struct server_record *servrec, int ttl)
+{
+  if(ttl > lp_max_ttl())
+    ttl = lp_max_ttl();
+
+  if(is_myname(servrec->serv.name))
+    servrec->death_time = PERMANENT_TTL;
+  else
+    servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+  servrec->subnet->work_changed = True;
+}
+
+/*******************************************************************
+  Expire old servers in the serverlist. A time of -1 indicates 
+  everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
+  This should only be called from expire_workgroups_and_servers().
+  ******************************************************************/
+
+void expire_servers(struct work_record *work, time_t t)
+{
+  struct server_record *servrec;
+  struct server_record *nexts;
+  
+  for (servrec = work->serverlist; servrec; servrec = nexts)
+  {
+    nexts = servrec->next;
+
+    if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t)))
+    {
+      DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
+      remove_server_from_workgroup(work, servrec);
+      work->subnet->work_changed = True;
+    }
+  }
+}
+
+/*******************************************************************
+ Decide if we should write out a server record for this server.
+ We return zero if we should not. Check if we've already written
+ out this server record from an earlier subnet.
+******************************************************************/
+
+static uint32 write_this_server_name( struct subnet_record *subrec,
+                                      struct work_record *work,
+                                      struct server_record *servrec)
+{
+  struct subnet_record *ssub;
+  struct work_record *iwork;
+
+  /* Go through all the subnets we have already seen. */
+  for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) 
+  {
+    for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next)
+    {
+      if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL)
+      {
+        /*
+         * We have already written out this server record, don't
+         * do it again. This gives precedence to servers we have seen
+         * on the broadcast subnets over servers that may have been
+         * added via a sync on the unicast_subet.
+         *
+         * The correct way to do this is to have a serverlist file
+         * per subnet - this means changes to smbd as well. I may
+         * add this at a later date (JRA).
+         */
+
+        return 0;
+      }
+    }
+  }
+
+  return servrec->serv.type;
+}
+
+/*******************************************************************
+ Decide if we should write out a workgroup record for this workgroup.
+ We return zero if we should not. Don't write out lp_workgroup() (we've
+ already done it) and also don't write out a second workgroup record
+ on the unicast subnet that we've already written out on one of the
+ broadcast subnets.
+******************************************************************/
+
+static uint32 write_this_workgroup_name( struct subnet_record *subrec, 
+                                         struct work_record *work)
+{
+  struct subnet_record *ssub;
+
+  if(strequal(lp_workgroup(), work->work_group))
+    return 0;
+
+  /* This is a workgroup we have seen on a broadcast subnet. All
+     these have the same type. */
+
+  if(subrec != unicast_subnet)
+    return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+
+  for(ssub = FIRST_SUBNET; ssub;  ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub))
+  {
+    /* This is the unicast subnet so check if we've already written out
+       this subnet when we passed over the broadcast subnets. */
+
+    if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
+      return 0;
+  }
+
+  /* All workgroups on the unicast subnet (except our own, which we
+     have already written out) cannot be local. */
+
+  return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
+}
+
+/*******************************************************************
+  Write out the browse.dat file.
+  ******************************************************************/
+
+void write_browse_list_entry(XFILE *fp, const char *name, uint32 rec_type,
+               const char *local_master_browser_name, const char *description)
+{
+       fstring tmp;
+
+       slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
+       x_fprintf(fp, "%-25s ", tmp);
+       x_fprintf(fp, "%08x ", rec_type);
+       slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
+       x_fprintf(fp, "%-30s", tmp);
+       x_fprintf(fp, "\"%s\"\n", description);
+}
+
+void write_browse_list(time_t t, BOOL force_write)
+{   
+  struct subnet_record *subrec;
+  struct work_record *work;
+  struct server_record *servrec;
+  pstring fname,fnamenew;
+  uint32 stype;
+  int i;
+  XFILE *fp;
+  BOOL list_changed = force_write;
+  static time_t lasttime = 0;
+    
+  /* Always dump if we're being told to by a signal. */
+  if(force_write == False)
+  {
+    if (!lasttime)
+      lasttime = t;
+    if (t - lasttime < 5)
+      return;
+  }
+
+  lasttime = t;
+
+  dump_workgroups(force_write);
+  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+    if(subrec->work_changed)
+    {
+      list_changed = True;
+      break;
+    }
+  }
+
+  if(!list_changed)
+    return;
+
+  updatecount++;
+    
+  pstrcpy(fname,lp_lockdir());
+  trim_string(fname,NULL,"/");
+  pstrcat(fname,"/");
+  pstrcat(fname,SERVER_LIST);
+  pstrcpy(fnamenew,fname);
+  pstrcat(fnamenew,".");
+  fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+  if (!fp)
+  {
+    DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
+              fnamenew,strerror(errno)));
+    return;
+  } 
+  
+  /*
+   * Write out a record for our workgroup. Use the record from the first
+   * subnet.
+   */
+
+  if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL)
+  { 
+    DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
+             lp_workgroup()));
+    x_fclose(fp);
+    return;
+  }
+
+  write_browse_list_entry(fp, work->work_group,
+     SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
+     work->local_master_browser_name, work->work_group);
+
+  /* 
+   * We need to do something special for our own names.
+   * This is due to the fact that we may be a local master browser on
+   * one of our broadcast subnets, and a domain master on the unicast
+   * subnet. We iterate over the subnets and only write out the name
+   * once.
+   */
+
+  for (i=0; my_netbios_names(i); i++)
+  {
+    stype = 0;
+    for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+    {
+      if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
+        continue;
+      if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
+        continue;
+
+      stype |= servrec->serv.type;
+    }
+
+    /* Output server details, plus what workgroup they're in. */
+    write_browse_list_entry(fp, my_netbios_names(i), stype,
+        string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH), lp_workgroup());
+  }
+      
+  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) 
+  { 
+    subrec->work_changed = False;
+
+    for (work = subrec->workgrouplist; work ; work = work->next)
+    {
+      /* Write out a workgroup record for a workgroup. */
+      uint32 wg_type = write_this_workgroup_name( subrec, work);
+
+      if(wg_type)
+      {
+        write_browse_list_entry(fp, work->work_group, wg_type,
+                                work->local_master_browser_name,
+                                work->work_group);
+      }
+
+      /* Now write out any server records a workgroup may have. */
+
+      for (servrec = work->serverlist; servrec ; servrec = servrec->next)
+      {
+        uint32 serv_type;
+
+        /* We have already written our names here. */
+        if(is_myname(servrec->serv.name))
+          continue; 
+
+        serv_type = write_this_server_name(subrec, work, servrec);
+
+        if(serv_type)
+        {
+          /* Output server details, plus what workgroup they're in. */
+          write_browse_list_entry(fp, servrec->serv.name, serv_type,
+                                 servrec->serv.comment, work->work_group);
+        }
+      }
+    }
+  } 
+  
+  x_fclose(fp);
+  unlink(fname);
+  chmod(fnamenew,0644);
+  rename(fnamenew,fname);
+  DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+}
diff --git a/source4/nmbd/nmbd_subnetdb.c b/source4/nmbd/nmbd_subnetdb.c
new file mode 100644 (file)
index 0000000..6296826
--- /dev/null
@@ -0,0 +1,361 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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.
+   
+   Revision History:
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+/* This is the broadcast subnets database. */
+struct subnet_record *subnetlist = NULL;
+
+/* Extra subnets - keep these separate so enumeration code doesn't
+   run onto it by mistake. */
+
+struct subnet_record *unicast_subnet = NULL;
+struct subnet_record *remote_broadcast_subnet = NULL;
+struct subnet_record *wins_server_subnet = NULL;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/****************************************************************************
+  Add a subnet into the list.
+  **************************************************************************/
+
+static void add_subnet(struct subnet_record *subrec)
+{
+       DLIST_ADD(subnetlist, subrec);
+}
+
+/* ************************************************************************** **
+ * Comparison routine for ordering the splay-tree based namelists assoicated
+ * with each subnet record.
+ *
+ *  Input:  Item  - Pointer to the comparison key.
+ *          Node  - Pointer to a node the splay tree.
+ *
+ *  Output: The return value will be <0 , ==0, or >0 depending upon the
+ *          ordinal relationship of the two keys.
+ *
+ * ************************************************************************** **
+ */
+static int namelist_entry_compare( ubi_trItemPtr Item, ubi_trNodePtr Node )
+  {
+  struct name_record *NR = (struct name_record *)Node;
+
+  if( DEBUGLVL( 10 ) )
+    {
+    struct nmb_name *Iname = (struct nmb_name *)Item;
+
+    Debug1( "nmbd_subnetdb:namelist_entry_compare()\n" );
+    Debug1( "%d == memcmp( \"%s\", \"%s\", %d )\n",
+            memcmp( Item, &(NR->name), sizeof(struct nmb_name) ),
+            nmb_namestr(Iname), nmb_namestr(&NR->name), (int)sizeof(struct nmb_name) );
+    }
+
+  return( memcmp( Item, &(NR->name), sizeof(struct nmb_name) ) ); 
+  } /* namelist_entry_compare */
+
+
+/****************************************************************************
+stop listening on a subnet
+we don't free the record as we don't have proper reference counting for it
+yet and it may be in use by a response record
+  ****************************************************************************/
+void close_subnet(struct subnet_record *subrec)
+{
+       DLIST_REMOVE(subnetlist, subrec);
+
+       if (subrec->dgram_sock != -1) {
+               close(subrec->dgram_sock);
+               subrec->dgram_sock = -1;
+       }
+       if (subrec->nmb_sock != -1) {
+               close(subrec->nmb_sock);
+               subrec->nmb_sock = -1;
+       }
+}
+
+
+
+/****************************************************************************
+  Create a subnet entry.
+  ****************************************************************************/
+
+static struct subnet_record *make_subnet(const char *name, enum subnet_type type,
+                                        struct in_addr myip, struct in_addr bcast_ip, 
+                                        struct in_addr mask_ip)
+{
+  struct subnet_record *subrec = NULL;
+  int nmb_sock, dgram_sock;
+
+  /* Check if we are creating a non broadcast subnet - if so don't create
+     sockets.
+   */
+
+  if(type != NORMAL_SUBNET)
+  {
+    nmb_sock = -1;
+    dgram_sock = -1;
+  }
+  else
+  {
+    /*
+     * Attempt to open the sockets on port 137/138 for this interface
+     * and bind them.
+     * Fail the subnet creation if this fails.
+     */
+
+    if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr,True)) == -1)
+    {
+      if( DEBUGLVL( 0 ) )
+      {
+        Debug1( "nmbd_subnetdb:make_subnet()\n" );
+        Debug1( "  Failed to open nmb socket on interface %s ", inet_ntoa(myip) );
+        Debug1( "for port %d.  ", global_nmb_port );
+        Debug1( "Error was %s\n", strerror(errno) );
+      }
+      return NULL;
+    }
+
+    if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr,True)) == -1)
+    {
+      if( DEBUGLVL( 0 ) )
+      {
+        Debug1( "nmbd_subnetdb:make_subnet()\n" );
+        Debug1( "  Failed to open dgram socket on interface %s ", inet_ntoa(myip) );
+        Debug1( "for port %d.  ", DGRAM_PORT );
+        Debug1( "Error was %s\n", strerror(errno) );
+      }
+      return NULL;
+    }
+
+    /* Make sure we can broadcast from these sockets. */
+    set_socket_options(nmb_sock,"SO_BROADCAST");
+    set_socket_options(dgram_sock,"SO_BROADCAST");
+
+  }
+
+  subrec = (struct subnet_record *)malloc(sizeof(*subrec));
+  
+  if (!subrec) 
+  {
+    DEBUG(0,("make_subnet: malloc fail !\n"));
+    close(nmb_sock);
+    close(dgram_sock);
+    return(NULL);
+  }
+  
+  memset( (char *)subrec, '\0', sizeof(*subrec) );
+  (void)ubi_trInitTree( subrec->namelist,
+                        namelist_entry_compare,
+                        ubi_trOVERWRITE );
+
+  if((subrec->subnet_name = strdup(name)) == NULL)
+  {
+    DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
+    close(nmb_sock);
+    close(dgram_sock);
+    ZERO_STRUCTP(subrec);
+    SAFE_FREE(subrec);
+    return(NULL);
+  }
+
+  DEBUG(2, ("making subnet name:%s ", name ));
+  DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip)));
+  DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip)));
+  subrec->namelist_changed = False;
+  subrec->work_changed = False;
+  subrec->bcast_ip = bcast_ip;
+  subrec->mask_ip  = mask_ip;
+  subrec->myip = myip;
+  subrec->type = type;
+  subrec->nmb_sock = nmb_sock;
+  subrec->dgram_sock = dgram_sock;
+  
+  return subrec;
+}
+
+
+/****************************************************************************
+  Create a normal subnet
+**************************************************************************/
+struct subnet_record *make_normal_subnet(struct interface *iface)
+{
+       struct subnet_record *subrec;
+
+       subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET,
+                            iface->ip, iface->bcast, iface->nmask);
+       if (subrec) {
+               add_subnet(subrec);
+       }
+       return subrec;
+}
+
+
+/****************************************************************************
+  Create subnet entries.
+**************************************************************************/
+
+BOOL create_subnets(void)
+{    
+  int num_interfaces = iface_count();
+  int i;
+  struct in_addr unicast_ip, ipzero;
+  extern struct in_addr loopback_ip;
+
+  if(num_interfaces == 0) {
+         DEBUG(0,("create_subnets: No local interfaces !\n"));
+         DEBUG(0,("create_subnets: Waiting for an interface to appear ...\n"));
+         while (iface_count() == 0) {
+                 sleep(5);
+                 load_interfaces();
+         }
+  }
+
+  num_interfaces = iface_count();
+
+  /* 
+   * Create subnets from all the local interfaces and thread them onto
+   * the linked list. 
+   */
+
+  for (i = 0 ; i < num_interfaces; i++)
+  {
+    struct interface *iface = get_interface(i);
+
+    /*
+     * We don't want to add a loopback interface, in case
+     * someone has added 127.0.0.1 for smbd, nmbd needs to
+     * ignore it here. JRA.
+     */
+
+    if (ip_equal(iface->ip, loopback_ip)) {
+      DEBUG(2,("create_subnets: Ignoring loopback interface.\n" ));
+      continue;
+    }
+
+    if (!make_normal_subnet(iface)) return False;
+  }
+
+  if (lp_we_are_a_wins_server()) {
+         /* Pick the first interface ip address as the WINS server ip. */
+         unicast_ip = *iface_n_ip(0);
+  } else {
+         /* note that we do not set the wins server IP here. We just
+            set it at zero and let the wins registration code cope
+            with getting the IPs right for each packet */
+         zero_ip(&unicast_ip);
+  }
+
+  /*
+   * Create the unicast and remote broadcast subnets.
+   * Don't put these onto the linked list.
+   * The ip address of the unicast subnet is set to be
+   * the WINS server address, if it exists, or ipzero if not.
+   */
+
+  unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET, 
+                                 unicast_ip, unicast_ip, unicast_ip);
+
+  zero_ip(&ipzero);
+
+  remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET",
+                                         REMOTE_BROADCAST_SUBNET,
+                                         ipzero, ipzero, ipzero);
+
+  if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL))
+    return False;
+
+  /* 
+   * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on
+   * the linked list.
+   */
+
+  if (lp_we_are_a_wins_server())
+  {
+    if( (wins_server_subnet = make_subnet( "WINS_SERVER_SUBNET",
+                                           WINS_SERVER_SUBNET, 
+                                           ipzero, ipzero, ipzero )) == NULL )
+      return False;
+  }
+
+  return True;
+}
+
+/*******************************************************************
+Function to tell us if we can use the unicast subnet.
+******************************************************************/
+BOOL we_are_a_wins_client(void)
+{
+       if (wins_srv_count() > 0) {
+               return True;
+       }
+
+       return False;
+}
+
+/*******************************************************************
+Access function used by NEXT_SUBNET_INCLUDING_UNICAST
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec)
+{
+  if(subrec == unicast_subnet)
+    return NULL;
+  else if((subrec->next == NULL) && we_are_a_wins_client())
+    return unicast_subnet;
+  else
+    return subrec->next;
+}
+
+/*******************************************************************
+ Access function used by retransmit_or_expire_response_records() in
+ nmbd_packets.c. Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>
+ Needed when we need to enumerate all the broadcast, unicast and
+ WINS subnets.
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast_or_wins_server(struct subnet_record *subrec)
+{
+  if(subrec == unicast_subnet)
+  {
+    if(wins_server_subnet)
+      return wins_server_subnet;
+    else
+      return NULL;
+  }
+
+  if(wins_server_subnet && subrec == wins_server_subnet)
+    return NULL;
+
+  if((subrec->next == NULL) && we_are_a_wins_client())
+    return unicast_subnet;
+  else
+    return subrec->next;
+}
diff --git a/source4/nmbd/nmbd_synclists.c b/source4/nmbd/nmbd_synclists.c
new file mode 100644 (file)
index 0000000..b9952fb
--- /dev/null
@@ -0,0 +1,300 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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.
+   
+*/
+
+/* this file handles asynchronous browse synchronisation requests. The
+   requests are done by forking and putting the result in a file in the
+   locks directory. We do it this way because we don't want nmbd to be
+   blocked waiting for some server to respond on a TCP connection. This
+   also allows us to have more than 1 sync going at once (tridge) */
+
+#include "includes.h"
+
+struct sync_record {
+       struct sync_record *next, *prev;
+       fstring workgroup;
+       fstring server;
+       pstring fname;
+       struct in_addr ip;
+       pid_t pid;
+};
+
+/* a linked list of current sync connections */
+static struct sync_record *syncs;
+
+static XFILE *fp;
+
+/*******************************************************************
+  This is the NetServerEnum callback.
+  Note sname and comment are in UNIX codepage format.
+  ******************************************************************/
+static void callback(const char *sname, uint32 stype, 
+                     const char *comment, void *state)
+{
+       x_fprintf(fp,"\"%s\" %08X \"%s\"\n", sname, stype, comment);
+}
+
+/*******************************************************************
+  Synchronise browse lists with another browse server.
+  Log in on the remote server's SMB port to their IPC$ service,
+  do a NetServerEnum and record the results in fname
+******************************************************************/
+static void sync_child(char *name, int nm_type, 
+                      char *workgroup,
+                      struct in_addr ip, BOOL local, BOOL servers,
+                      char *fname)
+{
+       extern fstring local_machine;
+       fstring unix_workgroup;
+       static struct cli_state cli;
+       uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
+       struct nmb_name called, calling;
+
+       /* W2K DMB's return empty browse lists on port 445. Use 139.
+        * Patch from Andy Levine andyl@epicrealm.com.
+        */
+
+       if (!cli_initialise(&cli) || !cli_set_port(&cli, 139) || !cli_connect(&cli, name, &ip)) {
+               return;
+       }
+
+       make_nmb_name(&calling, local_machine, 0x0);
+       make_nmb_name(&called , name         , nm_type);
+
+       if (!cli_session_request(&cli, &calling, &called))
+       {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       if (!cli_negprot(&cli)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       if (!cli_session_setup(&cli, "", "", 1, "", 0, workgroup)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       /* All the cli_XX functions take UNIX character set. */
+       fstrcpy(unix_workgroup, cli.server_domain?cli.server_domain:workgroup);
+
+       /* Fetch a workgroup list. */
+       cli_NetServerEnum(&cli, unix_workgroup,
+                         local_type|SV_TYPE_DOMAIN_ENUM, 
+                         callback, NULL);
+       
+       /* Now fetch a server list. */
+       if (servers) {
+               fstrcpy(unix_workgroup, workgroup);
+               cli_NetServerEnum(&cli, unix_workgroup, 
+                                 local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
+                                 callback, NULL);
+       }
+       
+       cli_shutdown(&cli);
+}
+
+
+/*******************************************************************
+  initialise a browse sync with another browse server.  Log in on the
+  remote server's SMB port to their IPC$ service, do a NetServerEnum
+  and record the results
+******************************************************************/
+void sync_browse_lists(struct work_record *work,
+                      char *name, int nm_type, 
+                      struct in_addr ip, BOOL local, BOOL servers)
+{
+       struct sync_record *s;
+       static int counter;
+
+       START_PROFILE(sync_browse_lists);
+       /* Check we're not trying to sync with ourselves. This can
+          happen if we are a domain *and* a local master browser. */
+       if (ismyip(ip)) {
+done:
+               END_PROFILE(sync_browse_lists);
+               return;
+       }
+
+       s = (struct sync_record *)malloc(sizeof(*s));
+       if (!s) goto done;
+
+       ZERO_STRUCTP(s);
+       
+       fstrcpy(s->workgroup, work->work_group);
+       fstrcpy(s->server, name);
+       s->ip = ip;
+
+       slprintf(s->fname, sizeof(pstring)-1,
+                "%s/sync.%d", lp_lockdir(), counter++);
+       all_string_sub(s->fname,"//", "/", 0);
+       
+       DLIST_ADD(syncs, s);
+
+       /* the parent forks and returns, leaving the child to do the
+          actual sync and call END_PROFILE*/
+       CatchChild();
+       if ((s->pid = sys_fork())) return;
+
+       BlockSignals( False, SIGTERM );
+
+       DEBUG(2,("Initiating browse sync for %s to %s(%s)\n",
+                work->work_group, name, inet_ntoa(ip)));
+
+       fp = x_fopen(s->fname,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (!fp) {
+               END_PROFILE(sync_browse_lists);
+               _exit(1);       
+       }
+
+       sync_child(name, nm_type, work->work_group, ip, local, servers,
+                  s->fname);
+
+       x_fclose(fp);
+       END_PROFILE(sync_browse_lists);
+       _exit(0);
+}
+
+/**********************************************************************
+handle one line from a completed sync file
+ **********************************************************************/
+static void complete_one(struct sync_record *s, 
+                        char *sname, uint32 stype, char *comment)
+{
+       struct work_record *work;
+       struct server_record *servrec;
+
+       stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+       if (stype & SV_TYPE_DOMAIN_ENUM) {
+               /* See if we can find the workgroup on this subnet. */
+               if((work=find_workgroup_on_subnet(unicast_subnet, sname))) {
+                       /* We already know about this workgroup -
+                           update the ttl. */
+                       update_workgroup_ttl(work,lp_max_ttl());
+               } else {
+                       /* Create the workgroup on the subnet. */
+                       work = create_workgroup_on_subnet(unicast_subnet, 
+                                                         sname, lp_max_ttl());
+                       if (work) {
+                               /* remember who the master is */
+                               fstrcpy(work->local_master_browser_name, 
+                                       comment);
+                       }
+               }
+               return;
+       } 
+
+       work = find_workgroup_on_subnet(unicast_subnet, s->workgroup);
+       if (!work) {
+               DEBUG(3,("workgroup %s doesn't exist on unicast subnet?\n",
+                        s->workgroup));
+               return;
+       }
+
+       if ((servrec = find_server_in_workgroup( work, sname))) {
+               /* Check that this is not a locally known
+                  server - if so ignore the entry. */
+               if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) {
+                       /* We already know about this server - update
+                           the ttl. */
+                       update_server_ttl(servrec, lp_max_ttl());
+                       /* Update the type. */
+                       servrec->serv.type = stype;
+               }
+               return;
+       } 
+
+       /* Create the server in the workgroup. */ 
+       create_server_on_workgroup(work, sname,stype, lp_max_ttl(), comment);
+}
+               
+
+/**********************************************************************
+read the completed sync info
+ **********************************************************************/
+static void complete_sync(struct sync_record *s)
+{
+       XFILE *f;
+       fstring server, type_str;
+       unsigned type;
+       pstring comment;
+       pstring line;
+       const char *ptr;
+       int count=0;
+
+       f = x_fopen(s->fname,O_RDONLY, 0);
+
+       if (!f) return;
+       
+       while (!x_feof(f)) {
+               
+               if (!fgets_slash(line,sizeof(pstring),f)) continue;
+               
+               ptr = line;
+
+               if (!next_token(&ptr,server,NULL,sizeof(server)) ||
+                   !next_token(&ptr,type_str,NULL, sizeof(type_str)) ||
+                   !next_token(&ptr,comment,NULL, sizeof(comment))) {
+                       continue;
+               }
+
+               sscanf(type_str, "%X", &type);
+
+               complete_one(s, server, type, comment);
+
+               count++;
+       }
+
+       x_fclose(f);
+
+       unlink(s->fname);
+
+       DEBUG(2,("sync with %s(%s) for workgroup %s completed (%d records)\n",
+                s->server, inet_ntoa(s->ip), s->workgroup, count));
+}
+
+/**********************************************************************
+check for completion of any of the child processes
+ **********************************************************************/
+void sync_check_completion(void)
+{
+       struct sync_record *s, *next;
+
+       for (s=syncs;s;s=next) {
+               next = s->next;
+               if (!process_exists(s->pid)) {
+                       /* it has completed - grab the info */
+                       complete_sync(s);
+                       DLIST_REMOVE(syncs, s);
+                       ZERO_STRUCTP(s);
+                       SAFE_FREE(s);
+               }
+       }
+}
diff --git a/source4/nmbd/nmbd_winsproxy.c b/source4/nmbd/nmbd_winsproxy.c
new file mode 100644 (file)
index 0000000..2e65ebb
--- /dev/null
@@ -0,0 +1,221 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+
+   Copyright (C) Jeremy Allison 1994-1998
+
+   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"
+
+/****************************************************************************
+Function called when the name lookup succeeded.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_success( struct subnet_record *subrec,
+                        struct userdata_struct *userdata,
+                        struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec)
+{
+  struct packet_struct *original_packet;
+  struct subnet_record *orig_broadcast_subnet;
+  struct name_record *namerec;
+  uint16 nb_flags;
+  int num_ips;
+  int i;
+  int ttl = 3600; /* By default one hour in the cache. */
+  struct in_addr *iplist;
+
+  /* Extract the original packet and the original broadcast subnet from
+     the userdata. */
+
+  memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) );
+  memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)],
+          sizeof(struct packet_struct *) );
+
+  nb_flags = get_nb_flags( rrec->rdata );
+
+  num_ips = rrec->rdlength / 6;
+  if(num_ips == 0)
+  {
+    DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \
+returned for name %s.\n", nmb_namestr(nmbname) ));
+    return;
+  }
+
+  if(num_ips == 1)
+    iplist = &ip;
+  else
+  {
+    if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL)
+    {
+      DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n"));
+      return;
+    }
+
+    for(i = 0; i < num_ips; i++)
+      putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]);
+  }
+
+  /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */
+
+  if(rrec == PERMANENT_TTL)
+    ttl = lp_max_ttl();
+
+  namerec = add_name_to_subnet( orig_broadcast_subnet, nmbname->name,
+                                nmbname->name_type, nb_flags, ttl,
+                                WINS_PROXY_NAME, num_ips, iplist );
+
+  if(iplist != &ip)
+    SAFE_FREE(iplist);
+
+  /*
+   * Check that none of the IP addresses we are returning is on the
+   * same broadcast subnet as the original requesting packet. If it
+   * is then don't reply (although we still need to add the name
+   * to the cache) as the actual machine will be replying also
+   * and we don't want two replies to a broadcast query.
+   */
+
+  if(namerec && original_packet->packet.nmb.header.nm_flags.bcast)
+  {
+    for( i = 0; i < namerec->data.num_ips; i++)
+    {
+      if( same_net( namerec->data.ip[i],
+                    orig_broadcast_subnet->myip,
+                    orig_broadcast_subnet->mask_ip ) )
+      {
+        DEBUG( 5, ( "wins_proxy_name_query_request_success: name %s is a WINS \
+proxy name and is also on the same subnet (%s) as the requestor. \
+Not replying.\n",
+                    nmb_namestr(&namerec->name),
+                    orig_broadcast_subnet->subnet_name ) );
+        return;
+      }
+    }
+  }
+
+  /* Finally reply to the original name query. */
+  reply_netbios_packet(original_packet,                /* Packet to reply to. */
+                       0,                              /* Result code. */
+                       NMB_QUERY,                      /* nmbd type code. */
+                       NMB_NAME_QUERY_OPCODE,          /* opcode. */
+                       ttl,                            /* ttl. */
+                       rrec->rdata,                    /* data to send. */
+                       rrec->rdlength);                /* data length. */
+}
+
+/****************************************************************************
+Function called when the name lookup failed.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_fail(struct subnet_record *subrec,
+                                    struct response_record *rrec,
+                                    struct nmb_name *question_name, int fail_code)
+{
+  DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \
+of name %s.\n", fail_code, nmb_namestr(question_name) ));
+}
+
+/****************************************************************************
+Function to make a deep copy of the userdata we will need when the WINS
+proxy query returns.
+****************************************************************************/
+
+static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata)
+{
+  struct packet_struct *p, *copy_of_p;
+  struct userdata_struct *new_userdata = 
+        (struct userdata_struct *)malloc( userdata->userdata_len );
+
+  if(new_userdata == NULL)
+    return NULL;
+
+  new_userdata->copy_fn = userdata->copy_fn;
+  new_userdata->free_fn = userdata->free_fn;
+  new_userdata->userdata_len = userdata->userdata_len;
+
+  /* Copy the subnet_record pointer. */
+  memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) );
+
+  /* Extract the pointer to the packet struct */
+  memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+         sizeof(struct packet_struct *) );
+
+  /* Do a deep copy of the packet. */
+  if((copy_of_p = copy_packet(p)) == NULL)
+  {
+    SAFE_FREE(new_userdata);
+    return NULL;
+  }
+
+  /* Lock the copy. */
+  copy_of_p->locked = True;
+
+  memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)&copy_of_p,
+          sizeof(struct packet_struct *) );
+
+  return new_userdata;
+}
+
+/****************************************************************************
+Function to free the deep copy of the userdata we used when the WINS
+proxy query returned.
+****************************************************************************/
+
+static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata)
+{
+  struct packet_struct *p;
+
+  /* Extract the pointer to the packet struct */
+  memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+         sizeof(struct packet_struct *));
+
+  /* Unlock the packet. */
+  p->locked = False;
+
+  free_packet(p);
+  ZERO_STRUCTP(userdata);
+  SAFE_FREE(userdata);
+}
+
+/****************************************************************************
+ Make a WINS query on behalf of a broadcast client name query request.
+****************************************************************************/
+
+void make_wins_proxy_name_query_request( struct subnet_record *subrec, 
+                                         struct packet_struct *incoming_packet,
+                                         struct nmb_name *question_name)
+{
+  long *ud[(sizeof(struct userdata_struct) + sizeof(struct subrec *) + 
+          sizeof(struct packet_struct *))/sizeof(long *) + 1];
+  struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+  memset(ud, '\0', sizeof(ud));
+  userdata->copy_fn = wins_proxy_userdata_copy_fn;
+  userdata->free_fn = wins_proxy_userdata_free_fn;
+  userdata->userdata_len = sizeof(ud);
+  memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *));
+  memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet,
+          sizeof(struct packet_struct *));
+
+  /* Now use the unicast subnet to query the name with the WINS server. */
+  query_name( unicast_subnet, question_name->name, question_name->name_type,
+              wins_proxy_name_query_request_success,
+              wins_proxy_name_query_request_fail,
+              userdata);
+}
diff --git a/source4/nmbd/nmbd_winsserver.c b/source4/nmbd/nmbd_winsserver.c
new file mode 100644 (file)
index 0000000..4ef476f
--- /dev/null
@@ -0,0 +1,2032 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+
+   Copyright (C) Jeremy Allison 1994-1998
+
+   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"
+
+#define WINS_LIST "wins.tdb"
+#define WINS_VERSION 1
+
+/****************************************************************************
+change the wins owner address in the record.
+*****************************************************************************/
+static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip)
+{
+       if (namerec==NULL)
+               return;
+       namerec->data.wins_ip=wins_ip;
+}
+
+/****************************************************************************
+create the wins flags based on the nb flags and the input value.
+*****************************************************************************/
+static void update_wins_flag(struct name_record *namerec, int flags)
+{
+       if (namerec==NULL)
+               return;
+
+       namerec->data.wins_flags=0x0;
+
+       /* if it's a group, it can be a normal or a special one */
+       if (namerec->data.nb_flags & NB_GROUP) {
+               if (namerec->name.name_type==0x1C)
+                       namerec->data.wins_flags|=WINS_SGROUP;
+               else
+                       if (namerec->data.num_ips>1)
+                               namerec->data.wins_flags|=WINS_SGROUP;
+                       else
+                               namerec->data.wins_flags|=WINS_NGROUP;
+       } else {
+               /* can be unique or multi-homed */
+               if (namerec->data.num_ips>1)
+                       namerec->data.wins_flags|=WINS_MHOMED;
+               else
+                       namerec->data.wins_flags|=WINS_UNIQUE;
+       }
+
+       /* the node type are the same bits */
+       namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK;
+
+       /* the static bit is elsewhere */
+       if (namerec->data.death_time == PERMANENT_TTL)
+               namerec->data.wins_flags|=WINS_STATIC;
+
+       /* and add the given bits */
+       namerec->data.wins_flags|=flags;
+
+       DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n", 
+                namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags));
+
+}
+
+/****************************************************************************
+return the general ID value and increase it if requested
+*****************************************************************************/
+static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update)
+{
+       /*
+        * it's kept as a static here, to prevent people from messing
+        * with the value directly
+        */
+
+       static SMB_BIG_UINT general_id = 1;
+
+       DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id));
+       
+       *current_id = general_id;
+       
+       if (update)
+               general_id++;
+}
+
+/****************************************************************************
+possibly call the WINS hook external program when a WINS change is made
+*****************************************************************************/
+static void wins_hook(const char *operation, struct name_record *namerec, int ttl)
+{
+       pstring command;
+       char *cmd = lp_wins_hook();
+       char *p;
+       int i;
+
+       if (!cmd || !*cmd) return;
+
+       for (p=namerec->name.name; *p; p++) {
+               if (!(isalnum((int)*p) || strchr_m("._-",*p))) {
+                       DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
+                       return;
+               }
+       }
+       
+       p = command;
+       p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d", 
+                     cmd,
+                     operation, 
+                     namerec->name.name,
+                     namerec->name.name_type,
+                     ttl);
+
+       for (i=0;i<namerec->data.num_ips;i++) {
+               p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i]));
+       }
+
+       DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
+       smbrun(command, NULL);
+}
+
+
+/****************************************************************************
+Determine if this packet should be allocated to the WINS server.
+*****************************************************************************/
+
+BOOL packet_is_for_wins_server(struct packet_struct *packet)
+{
+  struct nmb_packet *nmb = &packet->packet.nmb;
+
+  /* Only unicast packets go to a WINS server. */
+  if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True))
+  {
+    DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
+    return False;
+  }
+
+  /* Check for node status requests. */
+  if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY)
+    return False;
+
+  switch(nmb->header.opcode)
+  { 
+    /*
+     * A WINS server issues WACKS, not receives them.
+     */
+    case NMB_WACK_OPCODE:
+      DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
+      return False;
+    /*
+     * A WINS server only processes registration and
+     * release requests, not responses.
+     */
+    case NMB_NAME_REG_OPCODE:
+    case NMB_NAME_MULTIHOMED_REG_OPCODE:
+    case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+    case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+      if(nmb->header.response)
+      {
+        DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
+        return False;
+      }
+      break;
+
+    case NMB_NAME_RELEASE_OPCODE:
+      if(nmb->header.response)
+      {
+        DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
+        return False;
+      }
+      break;
+
+    /*
+     * Only process unicast name queries with rd = 1.
+     */
+    case NMB_NAME_QUERY_OPCODE:
+      if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired)
+      {
+        DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
+        return False;
+      }
+      break;
+  }
+
+  return True;
+}
+
+/****************************************************************************
+Utility function to decide what ttl to give a register/refresh request.
+*****************************************************************************/
+
+static int get_ttl_from_packet(struct nmb_packet *nmb)
+{
+  int ttl = nmb->additional->ttl;
+
+  if(ttl < lp_min_wins_ttl() )
+    ttl = lp_min_wins_ttl();
+
+  if(ttl > lp_max_wins_ttl() )
+    ttl = lp_max_wins_ttl();
+
+  return ttl;
+}
+
+/****************************************************************************
+Load or create the WINS database.
+*****************************************************************************/
+
+BOOL initialise_wins(void)
+{
+       time_t time_now = time(NULL);
+       TDB_CONTEXT *tdb;
+       TDB_DATA kbuf, dbuf, newkey;
+       struct name_record *namerec = NULL;
+       struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+       DEBUG(2,("initialise_wins: started\n"));
+
+       if(!lp_we_are_a_wins_server())
+               return True;
+
+       add_samba_names_to_subnet(wins_server_subnet);
+
+       tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+       if (!tdb) {
+               DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+               return True;
+       }
+
+       if (tdb_fetch_int32(tdb, INFO_VERSION) != WINS_VERSION) {
+               DEBUG(0,("Discarding invalid wins.dat file\n"));
+               tdb_close(tdb);
+               return True;
+       }
+
+       for (kbuf = tdb_firstkey(tdb); 
+            kbuf.dptr; 
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               fstring name_type;
+               pstring name, ip_str;
+               char *p;
+               int type = 0;
+               int nb_flags;
+               int ttl;
+               unsigned int num_ips;
+               int high, low;
+               struct in_addr wins_ip;
+               struct in_addr *ip_list;
+               int wins_flags;
+               int len,i;
+
+               if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
+                       continue;
+                               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr)
+                       continue;
+
+               fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
+
+               pstrcpy(name, name_type);
+
+               if((p = strchr(name,'#')) != NULL) {
+                       *p = 0;
+                       sscanf(p+1,"%x",&type);
+               }
+
+               len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
+                               &nb_flags, &high, &low,
+                               ip_str, &ttl, &num_ips, &wins_flags);
+
+               wins_ip=*interpret_addr2(ip_str);
+
+               /* Don't reload replica records */
+               if (!ip_equal(wins_ip, our_fake_ip)) {
+                       SAFE_FREE(dbuf.dptr);
+                       continue;
+               }
+
+               /* Don't reload released or tombstoned records */
+               if ((wins_flags&WINS_STATE_MASK) != WINS_ACTIVE) {
+                       SAFE_FREE(dbuf.dptr);
+                       continue;
+               }
+
+               /* Allocate the space for the ip_list. */
+               if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) {
+                       SAFE_FREE(dbuf.dptr);
+                       DEBUG(0,("initialise_wins: Malloc fail !\n"));
+                       return False;
+               }
+
+               for (i = 0; i < num_ips; i++) {
+                       len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
+                       ip_list[i] = *interpret_addr2(ip_str);
+               }
+
+               /* add all entries that have 60 seconds or more to live */
+               if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+                       if(ttl != PERMANENT_TTL)
+                               ttl -= time_now;
+    
+                       DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+                                  name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+                       namerec=add_name_to_subnet( wins_server_subnet, name, type, nb_flags, 
+                                               ttl, REGISTER_NAME, num_ips, ip_list);
+                       if (namerec!=NULL) {
+                               update_wins_owner(namerec, wins_ip);
+                               update_wins_flag(namerec, wins_flags);
+                               /* we don't reload the ID, on startup we restart at 1 */
+                               get_global_id_and_update(&namerec->data.id, True);
+                       }
+
+               } else {
+                       DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+                                 name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+               }
+
+               SAFE_FREE(dbuf.dptr);
+               SAFE_FREE(ip_list);
+       }
+    
+       tdb_close(tdb);
+       DEBUG(2,("initialise_wins: done\n"));
+       return True;
+}
+
+/****************************************************************************
+Send a WINS WACK (Wait ACKnowledgement) response.
+**************************************************************************/
+
+static void send_wins_wack_response(int ttl, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  unsigned char rdata[2];
+
+  rdata[0] = rdata[1] = 0;
+
+  /* Taken from nmblib.c - we need to send back almost
+     identical bytes from the requesting packet header. */
+
+  rdata[0] = (nmb->header.opcode & 0xF) << 3;
+  if (nmb->header.nm_flags.authoritative &&
+      nmb->header.response) rdata[0] |= 0x4;
+  if (nmb->header.nm_flags.trunc) rdata[0] |= 0x2;
+  if (nmb->header.nm_flags.recursion_desired) rdata[0] |= 0x1;
+  if (nmb->header.nm_flags.recursion_available &&
+      nmb->header.response) rdata[1] |= 0x80;
+  if (nmb->header.nm_flags.bcast) rdata[1] |= 0x10;
+
+  reply_netbios_packet(p,                             /* Packet to reply to. */
+                       0,                             /* Result code. */
+                       NMB_WAIT_ACK,                  /* nmbd type code. */
+                       NMB_WACK_OPCODE,               /* opcode. */
+                       ttl,                           /* ttl. */
+                       (char *)rdata,                 /* data to send. */
+                       2);                            /* data length. */
+}
+
+/****************************************************************************
+Send a WINS name registration response.
+**************************************************************************/
+
+static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char rdata[6];
+
+  memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+  reply_netbios_packet(p,                             /* Packet to reply to. */
+                       rcode,                         /* Result code. */
+                       WINS_REG,                      /* nmbd type code. */
+                       NMB_NAME_REG_OPCODE,           /* opcode. */
+                       ttl,                           /* ttl. */
+                       rdata,                         /* data to send. */
+                       6);                            /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name refresh request to a WINS server.
+************************************************************************/
+
+void wins_process_name_refresh_request(struct subnet_record *subrec,
+                                            struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  BOOL group = (nb_flags & NB_GROUP) ? True : False;
+  struct name_record *namerec = NULL;
+  int ttl = get_ttl_from_packet(nmb);
+  struct in_addr from_ip;
+  struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+  if(bcast)
+  {
+    /*
+     * We should only get unicast name refresh packets here.
+     * Anyone trying to refresh broadcast should not be going to a WINS
+     * server. Log an error here.
+     */
+
+    DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+  /* 
+   * See if the name already exists.
+   */
+
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+  /*
+   * If this is a refresh request and the name doesn't exist then
+   * treat it like a registration request. This allows us to recover 
+   * from errors (tridge)
+   */
+
+  if(namerec == NULL)
+  {
+    DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \
+the name does not exist. Treating as registration.\n", nmb_namestr(question) ));
+    wins_process_name_registration_request(subrec,p);
+    return;
+  }
+
+  /*
+   * if the name is present but not active,
+   * simply remove it and treat the request
+   * as a registration
+   */
+  if (namerec != NULL && !WINS_STATE_ACTIVE(namerec))
+  {
+    DEBUG(5,("wins_process_name_refresh_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+    remove_name_from_namelist( subrec, namerec );
+    namerec = NULL;
+    wins_process_name_registration_request(subrec,p);
+    return;
+  }
+
+  /*
+   * Check that the group bits for the refreshing name and the
+   * name in our database match.
+   */
+
+  if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) )
+  {
+    DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \
+does not match group bit in WINS for this name.\n", nmb_namestr(question), group ? "True" : "False" ));
+    send_wins_name_registration_response(RFS_ERR, 0, p);
+    return;
+  }
+
+  /*
+   * For a unique name check that the person refreshing the name is one of the registered IP
+   * addresses. If not - fail the refresh. Do the same for group names with a type of 0x1c.
+   * Just return success for unique 0x1d refreshes. For normal group names update the ttl
+   * and return success.
+   */
+
+  if((!group || (group && (question->name_type == 0x1c))) && find_ip_in_name_record(namerec, from_ip ))
+  {
+    /*
+     * Update the ttl.
+     */
+    update_name_ttl(namerec, ttl);
+
+    /*
+     * if the record is a replica:
+     * we take ownership and update the version ID.
+     */
+    if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+       update_wins_owner(namerec, our_fake_ip);
+       get_global_id_and_update(&namerec->data.id, True);
+    }
+
+    send_wins_name_registration_response(0, ttl, p);
+    wins_hook("refresh", namerec, ttl);
+    return;
+  }
+  else if(group)
+  {
+    /* 
+     * Normal groups are all registered with an IP address of 255.255.255.255 
+     * so we can't search for the IP address.
+     */
+    update_name_ttl(namerec, ttl);
+    send_wins_name_registration_response(0, ttl, p);
+    return;
+  }
+  else if(!group && (question->name_type == 0x1d))
+  {
+    /*
+     * Special name type - just pretend the refresh succeeded.
+     */
+    send_wins_name_registration_response(0, ttl, p);
+    return;
+  }
+  else
+  {
+    /*
+     * Fail the refresh.
+     */
+
+    DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \
+is IP is not known to the name.\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+    send_wins_name_registration_response(RFS_ERR, 0, p);
+    return;
+  }
+}
+
+/***********************************************************************
+ Deal with a name registration request query success to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The success here is actually a failure as it means
+ the client we queried wants to keep the name, so we must return
+ a registration failure to the original requestor.
+************************************************************************/
+
+static void wins_register_query_success(struct subnet_record *subrec,
+                                             struct userdata_struct *userdata,
+                                             struct nmb_name *question_name,
+                                             struct in_addr ip,
+                                             struct res_rec *answers)
+{
+  struct packet_struct *orig_reg_packet;
+
+  memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+  DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
+name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
+
+  send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The failure here is actually a success as it means
+ the client we queried didn't want to keep the name, so we can remove
+ the old name record and then successfully add the new name.
+************************************************************************/
+
+static void wins_register_query_fail(struct subnet_record *subrec,
+                                          struct response_record *rrec,
+                                          struct nmb_name *question_name,
+                                          int rcode)
+{
+  struct userdata_struct *userdata = rrec->userdata;
+  struct packet_struct *orig_reg_packet;
+  struct name_record *namerec = NULL;
+
+  memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+  /*
+   * We want to just add the name, as we now know the original owner
+   * didn't want it. But we can't just do that as an arbitary
+   * amount of time may have taken place between the name query
+   * request and this timeout/error response. So we check that
+   * the name still exists and is in the same state - if so
+   * we remove it and call wins_process_name_registration_request()
+   * as we know it will do the right thing now.
+   */
+
+  namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+  if( (namerec != NULL)
+   && (namerec->data.source == REGISTER_NAME)
+   && ip_equal(rrec->packet->ip, *namerec->data.ip) )
+  {
+    remove_name_from_namelist( subrec, namerec);
+    namerec = NULL;
+  }
+
+  if(namerec == NULL)
+    wins_process_name_registration_request(subrec, orig_reg_packet);
+  else
+    DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
+querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) ));
+
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request to a WINS server.
+
+ Use the following pseudocode :
+
+ registering_group
+     |
+     |
+     +--------name exists
+     |                  |
+     |                  |
+     |                  +--- existing name is group
+     |                  |                      |
+     |                  |                      |
+     |                  |                      +--- add name (return).
+     |                  |
+     |                  |
+     |                  +--- exiting name is unique
+     |                                         |
+     |                                         |
+     |                                         +--- query existing owner (return).
+     |
+     |
+     +--------name doesn't exist
+                        |
+                        |
+                        +--- add name (return).
+
+ registering_unique
+     |
+     |
+     +--------name exists
+     |                  |
+     |                  |
+     |                  +--- existing name is group 
+     |                  |                      |
+     |                  |                      |
+     |                  |                      +--- fail add (return).
+     |                  | 
+     |                  |
+     |                  +--- exiting name is unique
+     |                                         |
+     |                                         |
+     |                                         +--- query existing owner (return).
+     |
+     |
+     +--------name doesn't exist
+                        |
+                        |
+                        +--- add name (return).
+
+ As can be seen from the above, the two cases may be collapsed onto each
+ other with the exception of the case where the name already exists and
+ is a group name. This case we handle with an if statement.
+************************************************************************/
+
+void wins_process_name_registration_request(struct subnet_record *subrec,
+                                            struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  int ttl = get_ttl_from_packet(nmb);
+  struct name_record *namerec = NULL;
+  struct in_addr from_ip;
+  BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
+  struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+  if(bcast)
+  {
+    /*
+     * We should only get unicast name registration packets here.
+     * Anyone trying to register broadcast should not be going to a WINS
+     * server. Log an error here.
+     */
+
+    DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
+IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+  /*
+   * See if the name already exists.
+   */
+
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+  /*
+   * if the record exists but NOT in active state,
+   * consider it dead.
+   */
+  if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec))
+  {
+    DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+    remove_name_from_namelist( subrec, namerec );
+    namerec = NULL;
+  }
+
+  /*
+   * Deal with the case where the name found was a dns entry.
+   * Remove it as we now have a NetBIOS client registering the
+   * name.
+   */
+
+  if( (namerec != NULL)
+   && ( (namerec->data.source == DNS_NAME)
+     || (namerec->data.source == DNSFAIL_NAME) ) )
+  {
+    DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+a dns lookup - removing it.\n", nmb_namestr(question) ));
+    remove_name_from_namelist( subrec, namerec );
+    namerec = NULL;
+  }
+
+  /*
+   * Reject if the name exists and is not a REGISTER_NAME.
+   * (ie. Don't allow any static names to be overwritten.
+   */
+
+  if((namerec != NULL) && (namerec->data.source != REGISTER_NAME))
+  {
+    DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+                nmb_namestr(question), namerec->data.source ));
+    send_wins_name_registration_response(RFS_ERR, 0, p);
+    return;
+  }
+
+  /*
+   * Special policy decisions based on MS documentation.
+   * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255.
+   * 2). All unique names ending in 0x1d are ignored, although a positive response is sent.
+   */
+
+  /*
+   * A group name is always added as the local broadcast address, except
+   * for group names ending in 0x1c.
+   * Group names with type 0x1c are registered with individual IP addresses.
+   */
+
+  if(registering_group_name && (question->name_type != 0x1c))
+    from_ip = *interpret_addr2("255.255.255.255");
+
+  /*
+   * Ignore all attempts to register a unique 0x1d name, although return success.
+   */
+
+  if(!registering_group_name && (question->name_type == 0x1d))
+  {
+    DEBUG(3,("wins_process_name_registration_request: Ignoring request \
+to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
+    send_wins_name_registration_response(0, ttl, p);
+    return;
+  }
+
+  /*
+   * Next two cases are the 'if statement' mentioned above.
+   */
+
+  if((namerec != NULL) && NAME_GROUP(namerec))
+  {
+    if(registering_group_name)
+    {
+      /*
+       * If we are adding a group name, the name exists and is also a group entry just add this
+       * IP address to it and update the ttl.
+       */
+
+      DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
+            inet_ntoa(from_ip), nmb_namestr(question) ));
+      /* 
+       * Check the ip address is not already in the group.
+       */
+      if(!find_ip_in_name_record(namerec, from_ip)) {
+        add_ip_to_name_record(namerec, from_ip);
+       /* we need to update the record for replication */
+        get_global_id_and_update(&namerec->data.id, True);
+
+       /*
+        * if the record is a replica, we must change
+        * the wins owner to us to make the replication updates
+        * it on the other wins servers.
+        * And when the partner will receive this record,
+        * it will update its own record.
+        */
+
+       update_wins_owner(namerec, our_fake_ip);
+
+      }
+      update_name_ttl(namerec, ttl);
+      send_wins_name_registration_response(0, ttl, p);
+      return;
+    }
+    else
+    {
+      /*
+       * If we are adding a unique name, the name exists in the WINS db 
+       * and is a group name then reject the registration.
+       *
+       * explanation: groups have a higher priority than unique names.
+       */
+
+      DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+      send_wins_name_registration_response(RFS_ERR, 0, p);
+      return;
+    } 
+  }
+
+  /*
+   * From here on down we know that if the name exists in the WINS db it is
+   * a unique name, not a group name.
+   */
+
+  /* 
+   * If the name exists and is one of our names then check the
+   * registering IP address. If it's not one of ours then automatically
+   * reject without doing the query - we know we will reject it.
+   */
+
+  if((namerec != NULL) && (is_myname(namerec->name.name)) )
+  {
+    if(!ismyip(from_ip))
+    {
+      DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+      send_wins_name_registration_response(RFS_ERR, 0, p);
+      return;
+    }
+    else
+    {
+      /*
+       * It's one of our names and one of our IP's - update the ttl.
+       */
+      update_name_ttl(namerec, ttl);
+      send_wins_name_registration_response(0, ttl, p);
+      wins_hook("refresh", namerec, ttl);
+      return;
+    }
+  }
+
+  /*
+   * If the name exists and it is a unique registration and the registering IP 
+   * is the same as the (single) already registered IP then just update the ttl.
+   *
+   * But not if the record is an active replica. IF it's a replica, it means it can be
+   * the same client which has moved and not yet expired. So we don't update
+   * the ttl in this case and go beyond to do a WACK and query the old client
+   */
+
+  if( !registering_group_name
+   && (namerec != NULL)
+   && (namerec->data.num_ips == 1)
+   && ip_equal( namerec->data.ip[0], from_ip )
+   && ip_equal(namerec->data.wins_ip, our_fake_ip) )
+  {
+    update_name_ttl( namerec, ttl );
+    send_wins_name_registration_response( 0, ttl, p );
+    wins_hook("refresh", namerec, ttl);
+    return;
+  }
+
+  /*
+   * Finally if the name exists do a query to the registering machine 
+   * to see if they still claim to have the name.
+   */
+
+  if( namerec != NULL )
+  {
+    long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+    struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+    /*
+     * First send a WACK to the registering machine.
+     */
+
+    send_wins_wack_response(60, p);
+
+    /*
+     * When the reply comes back we need the original packet.
+     * Lock this so it won't be freed and then put it into
+     * the userdata structure.
+     */
+
+    p->locked = True;
+
+    userdata = (struct userdata_struct *)ud;
+
+    userdata->copy_fn = NULL;
+    userdata->free_fn = NULL;
+    userdata->userdata_len = sizeof(struct packet_struct *);
+    memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+    /*
+     * Use the new call to send a query directly to an IP address.
+     * This sends the query directly to the IP address, and ensures
+     * the recursion desired flag is not set (you were right Luke :-).
+     * This function should *only* be called from the WINS server
+     * code. JRA.
+     */
+
+    query_name_from_wins_server( *namerec->data.ip,
+                                  question->name,
+                                  question->name_type, 
+                                  wins_register_query_success,
+                                  wins_register_query_fail,
+                                  userdata );
+    return;
+  }
+
+  /*
+   * Name did not exist - add it.
+   */
+
+  (void)add_name_to_subnet( subrec, question->name, question->name_type,
+                            nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+  if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+       get_global_id_and_update(&namerec->data.id, True);
+       update_wins_owner(namerec, our_fake_ip);
+       update_wins_flag(namerec, WINS_ACTIVE);
+       wins_hook("add", namerec, ttl);
+  }
+
+  send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with a mutihomed name query success to the machine that
+ requested the multihomed name registration.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_success(struct subnet_record *subrec,
+                                             struct userdata_struct *userdata,
+                                             struct nmb_name *question_name,
+                                             struct in_addr ip,
+                                             struct res_rec *answers)
+{
+  struct packet_struct *orig_reg_packet;
+  struct nmb_packet *nmb;
+  struct name_record *namerec = NULL;
+  struct in_addr from_ip;
+  int ttl;
+  struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+  memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+  nmb = &orig_reg_packet->packet.nmb;
+
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+  ttl = get_ttl_from_packet(nmb);
+
+  /*
+   * We want to just add the new IP, as we now know the requesting
+   * machine claims to own it. But we can't just do that as an arbitary
+   * amount of time may have taken place between the name query
+   * request and this response. So we check that
+   * the name still exists and is in the same state - if so
+   * we just add the extra IP and update the ttl.
+   */
+
+  namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+  if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) )
+  {
+    DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
+a subsequent IP address.\n", nmb_namestr(question_name) ));
+    send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+    orig_reg_packet->locked = False;
+    free_packet(orig_reg_packet);
+
+    return;
+  }
+
+  if(!find_ip_in_name_record(namerec, from_ip))
+    add_ip_to_name_record(namerec, from_ip);
+
+  get_global_id_and_update(&namerec->data.id, True);
+  update_wins_owner(namerec, our_fake_ip);
+  update_wins_flag(namerec, WINS_ACTIVE);
+  update_name_ttl(namerec, ttl);
+  send_wins_name_registration_response(0, ttl, orig_reg_packet);
+  wins_hook("add", namerec, ttl);
+
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
+                                          struct response_record *rrec,
+                                          struct nmb_name *question_name,
+                                          int rcode)
+{
+  struct userdata_struct *userdata = rrec->userdata;
+  struct packet_struct *orig_reg_packet;
+
+  memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+  DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
+  send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+  orig_reg_packet->locked = False;
+  free_packet(orig_reg_packet);
+  return;
+}
+
+/***********************************************************************
+ Deal with a multihomed name registration request to a WINS server.
+ These cannot be group name registrations.
+***********************************************************************/
+
+void wins_process_multihomed_name_registration_request( struct subnet_record *subrec,
+                                                        struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  int ttl = get_ttl_from_packet(nmb);
+  struct name_record *namerec = NULL;
+  struct in_addr from_ip;
+  BOOL group = (nb_flags & NB_GROUP) ? True : False;
+  struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+  if(bcast)
+  {
+    /*
+     * We should only get unicast name registration packets here.
+     * Anyone trying to register broadcast should not be going to a WINS
+     * server. Log an error here.
+     */
+
+    DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    return;
+  }
+
+  /*
+   * Only unique names should be registered multihomed.
+   */
+
+  if(group)
+  {
+    DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
+received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    return;
+  }
+
+  DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+  /*
+   * Deal with policy regarding 0x1d names.
+   */
+
+  if(question->name_type == 0x1d)
+  {
+    DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
+to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+    send_wins_name_registration_response(0, ttl, p);  
+    return;
+  }
+
+  /*
+   * See if the name already exists.
+   */
+
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+  /*
+   * if the record exists but NOT in active state,
+   * consider it dead.
+   */
+  if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+         DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question)));
+         remove_name_from_namelist(subrec, namerec);
+         namerec = NULL;
+  }
+  
+  /*
+   * Deal with the case where the name found was a dns entry.
+   * Remove it as we now have a NetBIOS client registering the
+   * name.
+   */
+
+  if( (namerec != NULL)
+   && ( (namerec->data.source == DNS_NAME)
+     || (namerec->data.source == DNSFAIL_NAME) ) )
+  {
+    DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", nmb_namestr(question) ));
+    remove_name_from_namelist( subrec, namerec);
+    namerec = NULL;
+  }
+
+  /*
+   * Reject if the name exists and is not a REGISTER_NAME.
+   * (ie. Don't allow any static names to be overwritten.
+   */
+
+  if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) )
+  {
+    DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+    nmb_namestr(question), namerec->data.source ));
+    send_wins_name_registration_response(RFS_ERR, 0, p);
+    return;
+  }
+
+  /*
+   * Reject if the name exists and is a GROUP name and is active.
+   */
+
+  if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec))
+  {
+    DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+    send_wins_name_registration_response(RFS_ERR, 0, p);
+    return;
+  } 
+
+  /*
+   * From here on down we know that if the name exists in the WINS db it is
+   * a unique name, not a group name.
+   */
+
+  /*
+   * If the name exists and is one of our names then check the
+   * registering IP address. If it's not one of ours then automatically
+   * reject without doing the query - we know we will reject it.
+   */
+
+  if((namerec != NULL) && (is_myname(namerec->name.name)) )
+  {
+    if(!ismyip(from_ip))
+    {
+      DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+      send_wins_name_registration_response(RFS_ERR, 0, p);
+      return;
+    }
+    else
+    {
+      /*
+       * It's one of our names and one of our IP's. Ensure the IP is in the record and
+       *  update the ttl. Update the version ID to force replication.
+       */
+           if(!find_ip_in_name_record(namerec, from_ip)) {
+                   get_global_id_and_update(&namerec->data.id, True);
+                   update_wins_owner(namerec, our_fake_ip);
+                   update_wins_flag(namerec, WINS_ACTIVE);
+
+                   add_ip_to_name_record(namerec, from_ip);
+                   wins_hook("add", namerec, ttl);
+           } else {
+                   wins_hook("refresh", namerec, ttl);
+           }
+
+           update_name_ttl(namerec, ttl);
+           send_wins_name_registration_response(0, ttl, p);
+           return;
+    }
+  }
+
+  /*
+   * If the name exists and is active, check if the IP address is already registered
+   * to that name. If so then update the ttl and reply success.
+   */
+
+  if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec))
+  {
+    update_name_ttl(namerec, ttl);
+    /*
+     * If it's a replica, we need to become the wins owner
+     * to force the replication
+     */
+    if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+      get_global_id_and_update(&namerec->data.id, True);
+      update_wins_owner(namerec, our_fake_ip);
+      update_wins_flag(namerec, WINS_ACTIVE);
+    }
+    
+    send_wins_name_registration_response(0, ttl, p);
+    wins_hook("refresh", namerec, ttl);
+    return;
+  }
+
+  /*
+   * If the name exists do a query to the owner
+   * to see if they still want the name.
+   */
+
+  if(namerec != NULL)
+  {
+    long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+    struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+    /*
+     * First send a WACK to the registering machine.
+     */
+
+    send_wins_wack_response(60, p);
+
+    /*
+     * When the reply comes back we need the original packet.
+     * Lock this so it won't be freed and then put it into
+     * the userdata structure.
+     */
+
+    p->locked = True;
+
+    userdata = (struct userdata_struct *)ud;
+
+    userdata->copy_fn = NULL;
+    userdata->free_fn = NULL;
+    userdata->userdata_len = sizeof(struct packet_struct *);
+    memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+    /* 
+     * Use the new call to send a query directly to an IP address.
+     * This sends the query directly to the IP address, and ensures
+     * the recursion desired flag is not set (you were right Luke :-).
+     * This function should *only* be called from the WINS server
+     * code. JRA.
+     *
+     * Note that this packet is sent to the current owner of the name,
+     * not the person who sent the packet 
+     */
+
+    query_name_from_wins_server( namerec->data.ip[0],
+                                 question->name,
+                                 question->name_type, 
+                                 wins_multihomed_register_query_success,
+                                 wins_multihomed_register_query_fail,
+                                 userdata );
+
+    return;
+  }
+
+  /*
+   * Name did not exist - add it.
+   */
+
+  (void)add_name_to_subnet( subrec, question->name, question->name_type,
+                            nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+  if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+         get_global_id_and_update(&namerec->data.id, True);
+         update_wins_owner(namerec, our_fake_ip);
+         update_wins_flag(namerec, WINS_ACTIVE);
+         wins_hook("add", namerec, ttl);
+  }
+
+  send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with the special name query for *<1b>.
+***********************************************************************/
+   
+static void process_wins_dmb_query_request(struct subnet_record *subrec,  
+                                           struct packet_struct *p)
+{  
+  struct name_record *namerec = NULL;
+  char *prdata;
+  int num_ips;
+
+  /*
+   * Go through all the ACTIVE names in the WINS db looking for those
+   * ending in <1b>. Use this to calculate the number of IP
+   * addresses we need to return.
+   */
+
+  num_ips = 0;
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
+  {
+    if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b )
+      num_ips += namerec->data.num_ips;
+  }
+
+  if(num_ips == 0)
+  {
+    /*
+     * There are no 0x1b names registered. Return name query fail.
+     */
+    send_wins_name_query_response(NAM_ERR, p, NULL);
+    return;
+  }
+
+  if((prdata = (char *)malloc( num_ips * 6 )) == NULL)
+  {
+    DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
+    return;
+  }
+
+  /*
+   * Go through all the names again in the WINS db looking for those
+   * ending in <1b>. Add their IP addresses into the list we will
+   * return.
+   */ 
+
+  num_ips = 0;
+  for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+       namerec;
+       namerec = (struct name_record *)ubi_trNext( namerec ) )
+  {
+    if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b)
+    {
+      int i;
+      for(i = 0; i < namerec->data.num_ips; i++)
+      {
+        set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
+        putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
+        num_ips++;
+      }
+    }
+  }
+
+  /*
+   * Send back the reply containing the IP list.
+   */
+
+  reply_netbios_packet(p,                             /* Packet to reply to. */
+                       0,                             /* Result code. */
+                       WINS_QUERY,                    /* nmbd type code. */
+                       NMB_NAME_QUERY_OPCODE,         /* opcode. */
+                       lp_min_wins_ttl(),             /* ttl. */
+                       prdata,                        /* data to send. */
+                       num_ips*6);                    /* data length. */
+
+  SAFE_FREE(prdata);
+}
+
+/****************************************************************************
+Send a WINS name query response.
+**************************************************************************/
+
+void send_wins_name_query_response(int rcode, struct packet_struct *p, 
+                                          struct name_record *namerec)
+{
+  char rdata[6];
+  char *prdata = rdata;
+  int reply_data_len = 0;
+  int ttl = 0;
+  int i;
+
+  memset(rdata,'\0',6);
+
+  if(rcode == 0)
+  {
+    ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+             namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
+
+    /* Copy all known ip addresses into the return data. */
+    /* Optimise for the common case of one IP address so
+       we don't need a malloc. */
+
+    if( namerec->data.num_ips == 1 )
+      prdata = rdata;
+    else
+    {
+      if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL)
+      {
+        DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
+        return;
+      }
+    }
+
+    for(i = 0; i < namerec->data.num_ips; i++)
+    {
+      set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+      putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+    }
+
+    sort_query_replies(prdata, i, p->ip);
+
+    reply_data_len = namerec->data.num_ips * 6;
+  }
+
+  reply_netbios_packet(p,                             /* Packet to reply to. */
+                       rcode,                         /* Result code. */
+                       WINS_QUERY,                    /* nmbd type code. */
+                       NMB_NAME_QUERY_OPCODE,         /* opcode. */
+                       ttl,                           /* ttl. */
+                       prdata,                        /* data to send. */
+                       reply_data_len);               /* data length. */
+
+  if(prdata != rdata)
+    SAFE_FREE(prdata);
+}
+
+/***********************************************************************
+ Deal with a name query.
+***********************************************************************/
+
+void wins_process_name_query_request(struct subnet_record *subrec, 
+                                     struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  struct name_record *namerec = NULL;
+
+  DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n", 
+            nmb_namestr(question), inet_ntoa(p->ip) ));
+
+  /*
+   * Special name code. If the queried name is *<1b> then search
+   * the entire WINS database and return a list of all the IP addresses
+   * registered to any <1b> name. This is to allow domain master browsers
+   * to discover other domains that may not have a presence on their subnet.
+   */
+
+  if(strequal( question->name, "*") && (question->name_type == 0x1b))
+  {
+    process_wins_dmb_query_request( subrec, p);
+    return;
+  }
+
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+  if(namerec != NULL)
+  {
+    /*
+     * If the name is not anymore in active state then reply not found.
+     * it's fair even if we keep it in the cache for days.
+     */
+    if (!WINS_STATE_ACTIVE(namerec))
+    {
+      DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+             nmb_namestr(question) ));
+      send_wins_name_query_response(NAM_ERR, p, namerec);
+      return;
+    }
+    /* 
+     * If it's a DNSFAIL_NAME then reply name not found.
+     */
+
+    if( namerec->data.source == DNSFAIL_NAME )
+    {
+      DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
+             nmb_namestr(question) ));
+      send_wins_name_query_response(NAM_ERR, p, namerec);
+      return;
+    }
+
+    /*
+     * If the name has expired then reply name not found.
+     */
+
+    if( (namerec->data.death_time != PERMANENT_TTL)
+     && (namerec->data.death_time < p->timestamp) )
+    {
+      DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+                nmb_namestr(question) ));
+      send_wins_name_query_response(NAM_ERR, p, namerec);
+      return;
+    }
+
+    DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
+           nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
+
+    send_wins_name_query_response(0, p, namerec);
+    return;
+  }
+
+  /* 
+   * Name not found in WINS - try a dns query if it's a 0x20 name.
+   */
+
+  if(lp_dns_proxy() && 
+     ((question->name_type == 0x20) || question->name_type == 0))
+  {
+
+    DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
+              nmb_namestr(question) ));
+
+    queue_dns_query(p, question, &namerec);
+    return;
+  }
+
+  /*
+   * Name not found - return error.
+   */
+
+  send_wins_name_query_response(NAM_ERR, p, NULL);
+}
+
+/****************************************************************************
+Send a WINS name release response.
+**************************************************************************/
+
+static void send_wins_name_release_response(int rcode, struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  char rdata[6];
+
+  memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+  reply_netbios_packet(p,                            /* Packet to reply to. */
+                       rcode,                        /* Result code. */
+                       NMB_REL,                      /* nmbd type code. */
+                       NMB_NAME_RELEASE_OPCODE,      /* opcode. */
+                       0,                            /* ttl. */
+                       rdata,                        /* data to send. */
+                       6);                           /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name release.
+***********************************************************************/
+
+void wins_process_name_release_request(struct subnet_record *subrec,
+                                       struct packet_struct *p)
+{
+  struct nmb_packet *nmb = &p->packet.nmb;
+  struct nmb_name *question = &nmb->question.question_name;
+  BOOL bcast = nmb->header.nm_flags.bcast;
+  uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+  struct name_record *namerec = NULL;
+  struct in_addr from_ip;
+  BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+  putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+  if(bcast)
+  {
+    /*
+     * We should only get unicast name registration packets here.
+     * Anyone trying to register broadcast should not be going to a WINS
+     * server. Log an error here.
+     */
+
+    DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+          nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+    return;
+  }
+  
+  DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
+IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+    
+  /*
+   * Deal with policy regarding 0x1d names.
+   */
+
+  if(!releasing_group_name && (question->name_type == 0x1d))
+  {
+    DEBUG(3,("wins_process_name_release_request: Ignoring request \
+to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+    send_wins_name_release_response(0, p);
+    return;
+  }
+
+  /*
+   * See if the name already exists.
+   */
+    
+  namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+  if( (namerec == NULL)
+   || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) )
+  {
+    send_wins_name_release_response(NAM_ERR, p);
+    return;
+  }
+
+  /* 
+   * Check that the sending machine has permission to release this name.
+   * If it's a group name not ending in 0x1c then just say yes and let
+   * the group time out.
+   */
+
+  if(releasing_group_name && (question->name_type != 0x1c))
+  {
+    send_wins_name_release_response(0, p);
+    return;
+  }
+
+  /* 
+   * Check that the releasing node is on the list of IP addresses
+   * for this name. Disallow the release if not.
+   */
+
+  if(!find_ip_in_name_record(namerec, from_ip))
+  {
+    DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as IP %s is not one of the known IP's for this name.\n",
+           nmb_namestr(question), inet_ntoa(from_ip) ));
+    send_wins_name_release_response(NAM_ERR, p);
+    return;
+  }    
+
+  /*
+   * Check if the record is active. IF it's already released
+   * or tombstoned, refuse the release.
+   */
+  if (!WINS_STATE_ACTIVE(namerec)) {
+    DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as this record is not anymore active.\n",
+           nmb_namestr(question) ));
+    send_wins_name_release_response(NAM_ERR, p);
+    return;
+  }    
+
+  /*
+   * Check if the record is a 0x1c group
+   * and has more then one ip
+   * remove only this address.
+   */
+
+  if(releasing_group_name &&
+               (question->name_type == 0x1c) &&
+               (namerec->data.num_ips > 1)) {
+       remove_ip_from_name_record(namerec, from_ip);
+       DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n",
+                       inet_ntoa(from_ip),nmb_namestr(question)));
+       send_wins_name_release_response(0, p);
+       return;
+  }
+
+  /* 
+   * Send a release response.
+   * Flag the name as released and update the ttl
+   */
+
+  send_wins_name_release_response(0, p);
+  
+  namerec->data.wins_flags |= WINS_RELEASED;
+  update_name_ttl(namerec, EXTINCTION_INTERVAL);
+
+  wins_hook("delete", namerec, 0);
+}
+
+/*******************************************************************
+ WINS time dependent processing.
+******************************************************************/
+
+void initiate_wins_processing(time_t t)
+{
+       static time_t lasttime = 0;
+       struct name_record *namerec;
+       struct name_record *next_namerec;
+       struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+       if (!lasttime)
+               lasttime = t;
+       if (t - lasttime < 20)
+               return;
+
+       lasttime = t;
+
+       if(!lp_we_are_a_wins_server())
+               return;
+
+       for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+            namerec;
+            namerec = next_namerec ) {
+               next_namerec = (struct name_record *)ubi_trNext( namerec );
+
+               if( (namerec->data.death_time != PERMANENT_TTL)
+                    && (namerec->data.death_time < t) ) {
+
+                       if( namerec->data.source == SELF_NAME ) {
+                               DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n", 
+                                          wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) );
+                               namerec->data.death_time += 300;
+                               namerec->subnet->namelist_changed = True;
+                               continue;
+                       }
+
+                       /* handle records, samba is the wins owner */
+                       if (ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+                               switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+                                       case WINS_ACTIVE:
+                                               namerec->data.wins_flags&=~WINS_STATE_MASK;
+                                               namerec->data.wins_flags|=WINS_RELEASED;
+                                               namerec->data.death_time = t + EXTINCTION_INTERVAL;
+                                               DEBUG(3,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name)));
+                                               break;
+                                       case WINS_RELEASED:
+                                               namerec->data.wins_flags&=~WINS_STATE_MASK;
+                                               namerec->data.wins_flags|=WINS_TOMBSTONED;
+                                               namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+                                               get_global_id_and_update(&namerec->data.id, True);
+                                               DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+                                               break;
+                                       case WINS_TOMBSTONED:
+                                               DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+                                               remove_name_from_namelist( wins_server_subnet, namerec );
+                                               break;
+                               }
+                       } else {
+                               switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+                                       case WINS_ACTIVE:
+                                               /* that's not as MS says it should be */
+                                               namerec->data.wins_flags&=~WINS_STATE_MASK;
+                                               namerec->data.wins_flags|=WINS_TOMBSTONED;
+                                               namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+                                               DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+                                       case WINS_TOMBSTONED:
+                                               DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+                                               remove_name_from_namelist( wins_server_subnet, namerec );
+                                               break;
+                                       case WINS_RELEASED:
+                                               DEBUG(0,("initiate_wins_processing: %s is in released state and\
+we are not the wins owner !\n", nmb_namestr(&namerec->name)));
+                                               break;
+                               }
+                       }
+
+               }
+       }
+
+       if(wins_server_subnet->namelist_changed)
+               wins_write_database(True);
+
+       wins_server_subnet->namelist_changed = False;
+}
+
+/*******************************************************************
+ Write out the current WINS database.
+******************************************************************/
+void wins_write_database(BOOL background)
+{
+       struct name_record *namerec;
+       pstring fname, fnamenew;
+       TDB_CONTEXT *tdb;
+       TDB_DATA kbuf, dbuf;
+       pstring key, buf;
+       int len;
+       int num_record=0;
+       SMB_BIG_UINT id;
+
+       if(!lp_we_are_a_wins_server())
+               return;
+
+       /* we will do the writing in a child process to ensure that the parent
+         doesn't block while this is done */
+       if (background) {
+               CatchChild();
+               if (sys_fork()) {
+                       return;
+               }
+       }
+
+       slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
+       all_string_sub(fname,"//", "/", 0);
+       slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
+
+       tdb = tdb_open_log(fnamenew, 0, TDB_DEFAULT, O_RDWR|O_CREAT|O_TRUNC, 0644);
+       if (!tdb) {
+               DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+               if (background)
+                       _exit(0);
+               return;
+       }
+
+       DEBUG(3,("wins_write_database: Dump of WINS name list.\n"));
+
+       tdb_store_int32(tdb, INFO_VERSION, WINS_VERSION);
+
+       for (namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+            namerec;
+            namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+
+               int i;
+               struct tm *tm;
+
+               DEBUGADD(3,("%-19s ", nmb_namestr(&namerec->name) ));
+
+               if( namerec->data.death_time != PERMANENT_TTL ) {
+                       char *ts, *nl;
+
+                       tm = LocalTime(&namerec->data.death_time);
+                       ts = asctime(tm);
+                       nl = strrchr_m( ts, '\n' );
+                       if( NULL != nl )
+                               *nl = '\0';
+
+                       DEBUGADD(3,("TTL = %s  ", ts ));
+               } else
+                       DEBUGADD(3,("TTL = PERMANENT                 "));
+
+               for (i = 0; i < namerec->data.num_ips; i++)
+                       DEBUGADD(0,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
+
+               DEBUGADD(3,("0x%2x 0x%2x %15s\n", namerec->data.nb_flags, namerec->data.wins_flags, inet_ntoa(namerec->data.wins_ip)));
+
+               if( namerec->data.source == REGISTER_NAME ) {
+               
+                       /* store the type in the key to make the name unique */
+                       slprintf(key, sizeof(key), "%s%s#%02x", ENTRY_PREFIX, namerec->name.name, namerec->name.name_type);
+
+                       len = tdb_pack(buf, sizeof(buf), "dddfddd",
+                                       (int)namerec->data.nb_flags,
+                                       (int)(namerec->data.id>>32),
+                                       (int)(namerec->data.id&0xffffffff),
+                                       inet_ntoa(namerec->data.wins_ip),
+                                       (int)namerec->data.death_time, 
+                                       namerec->data.num_ips,
+                                       namerec->data.wins_flags);
+
+                       for (i = 0; i < namerec->data.num_ips; i++)
+                               len += tdb_pack(buf+len, sizeof(buf)-len, "f", inet_ntoa(namerec->data.ip[i]));
+                       
+                       kbuf.dsize = strlen(key)+1;
+                       kbuf.dptr = key;
+                       dbuf.dsize = len;
+                       dbuf.dptr = buf;
+                       if (tdb_store(tdb, kbuf, dbuf, TDB_INSERT) != 0) return;
+
+                       num_record++;
+               }
+       }
+
+       /* store the number of records */
+       tdb_store_int32(tdb, INFO_COUNT, num_record);
+
+       /* get and store the last used ID */
+       get_global_id_and_update(&id, False);
+       tdb_store_int32(tdb, INFO_ID_HIGH, id>>32);
+       tdb_store_int32(tdb, INFO_ID_LOW, id&0xffffffff);
+
+       tdb_close(tdb);
+
+       chmod(fnamenew,0644);
+       unlink(fname);
+       rename(fnamenew,fname);
+
+       if (background)
+               _exit(0);
+}
+
+/****************************************************************************
+process a internal Samba message receiving a wins record
+***************************************************************************/
+void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len)
+{
+       WINS_RECORD *record;
+       struct name_record *namerec = NULL;
+       struct name_record *new_namerec = NULL;
+       struct nmb_name question;
+       BOOL overwrite=False;
+       struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+       int i;
+
+       if (buf==NULL)
+               return;
+       
+       record=(WINS_RECORD *)buf;
+       
+       ZERO_STRUCT(question);
+       memcpy(question.name, record->name, 16);
+       question.name_type=record->type;
+
+       namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME);
+
+       /* record doesn't exist, add it */
+       if (namerec == NULL) {
+               DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n", 
+                         record->name, record->type, inet_ntoa(record->wins_ip)));
+
+               new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags, 
+                                               EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+               if (new_namerec!=NULL) {
+                               update_wins_owner(new_namerec, record->wins_ip);
+                               update_wins_flag(new_namerec, record->wins_flags);
+                               new_namerec->data.id=record->id;
+
+                               wins_server_subnet->namelist_changed = True;
+                       }
+       }
+
+       /* check if we have a conflict */
+       if (namerec != NULL) {
+               /* both records are UNIQUE */
+               if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) {
+
+                       /* the database record is a replica */
+                       if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+                               if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) {
+                                       if (ip_equal(namerec->data.wins_ip, record->wins_ip))
+                                               overwrite=True;
+                               } else
+                                       overwrite=True;
+                       } else {
+                       /* we are the wins owner of the database record */
+                               /* the 2 records have the same IP address */
+                               if (ip_equal(namerec->data.ip[0], record->ip[0])) {
+                                       if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED)
+                                               get_global_id_and_update(&namerec->data.id, True);
+                                       else
+                                               overwrite=True;
+                               
+                               } else {
+                               /* the 2 records have different IP address */
+                                       if (namerec->data.wins_flags&WINS_ACTIVE) {
+                                               if (record->wins_flags&WINS_TOMBSTONED)
+                                                       get_global_id_and_update(&namerec->data.id, True);
+                                               if (record->wins_flags&WINS_ACTIVE)
+                                                       /* send conflict challenge to the replica node */
+                                                       ;
+                                       } else
+                                               overwrite=True;
+                               }
+
+                       }
+               }
+               
+               /* the replica is a standard group */
+               if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) {
+                       /* if the database record is unique and active force a name release */
+                       if (namerec->data.wins_flags&WINS_UNIQUE)
+                               /* send a release name to the unique node */
+                               ;
+                       overwrite=True;
+               
+               }
+       
+               /* the replica is a special group */
+               if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) {
+                       if (namerec->data.wins_flags&WINS_ACTIVE) {
+                               for (i=0; i<record->num_ips; i++)
+                                       if(!find_ip_in_name_record(namerec, record->ip[i]))
+                                               add_ip_to_name_record(namerec, record->ip[i]);
+                       }
+                       else
+                               overwrite=True;
+               }
+               
+               /* the replica is a multihomed host */
+               
+               /* I'm giving up on multi homed. Too much complex to understand */
+               
+               if (record->wins_flags&WINS_MHOMED) {
+                       if (! (namerec->data.wins_flags&WINS_ACTIVE)) {
+                               if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP))
+                                       overwrite=True;
+                       }
+                       else {
+                               if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+                                       overwrite=True;
+                               
+                               if (ip_equal(namerec->data.wins_ip, our_fake_ip))
+                                       if (namerec->data.wins_flags&WINS_UNIQUE)
+                                               get_global_id_and_update(&namerec->data.id, True);
+                               
+                       }
+                       
+                       if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE)
+                               if (namerec->data.wins_flags&WINS_UNIQUE ||
+                                   namerec->data.wins_flags&WINS_MHOMED)
+                                       if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+                                               overwrite=True;
+                               
+               }
+
+               if (overwrite == False)
+                       DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n", 
+                                 record->name, record->type, inet_ntoa(record->wins_ip)));
+               else {
+                       DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n", 
+                                 record->name, record->type, inet_ntoa(record->wins_ip)));
+
+                       /* remove the old record and add a new one */
+                       remove_name_from_namelist( wins_server_subnet, namerec );
+                       new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags, 
+                                               EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+                       if (new_namerec!=NULL) {
+                               update_wins_owner(new_namerec, record->wins_ip);
+                               update_wins_flag(new_namerec, record->wins_flags);
+                               new_namerec->data.id=record->id;
+
+                               wins_server_subnet->namelist_changed = True;
+                       }
+
+                       wins_server_subnet->namelist_changed = True;
+               }
+
+       }
+}
+
+
+
+
+
+
+
+
diff --git a/source4/nmbd/nmbd_workgroupdb.c b/source4/nmbd/nmbd_workgroupdb.c
new file mode 100644 (file)
index 0000000..3e177bc
--- /dev/null
@@ -0,0 +1,342 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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"
+
+extern int ClientNMB;
+
+extern uint16 samba_nb_type;
+
+int workgroup_count = 0; /* unique index key: one for each workgroup */
+
+/****************************************************************************
+  Add a workgroup into the list.
+  **************************************************************************/
+
+static void add_workgroup(struct subnet_record *subrec, struct work_record *work)
+{
+       work->subnet = subrec;
+       DLIST_ADD(subrec->workgrouplist, work);
+       subrec->work_changed = True;
+}
+
+/****************************************************************************
+  Create an empty workgroup.
+  **************************************************************************/
+
+static struct work_record *create_workgroup(const char *name, int ttl)
+{
+  struct work_record *work;
+  struct subnet_record *subrec;
+  int t = -1;
+  
+  if((work = (struct work_record *)malloc(sizeof(*work))) == NULL)
+  {
+    DEBUG(0,("create_workgroup: malloc fail !\n"));
+    return NULL;
+  }
+  memset((char *)work, '\0', sizeof(*work));
+  StrnCpy(work->work_group,name,sizeof(work->work_group)-1);
+  work->serverlist = NULL;
+  
+  work->RunningElection = False;
+  work->ElectionCount = 0;
+  work->announce_interval = 0;
+  work->needelection = False;
+  work->needannounce = True;
+  work->lastannounce_time = time(NULL);
+  work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+  work->dom_state = DOMAIN_NONE;
+  work->log_state = LOGON_NONE;
+  
+  work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+  /* Make sure all token representations of workgroups are unique. */
+  
+  for (subrec = FIRST_SUBNET; subrec && (t == -1); 
+           subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+    struct work_record *w;
+    for (w = subrec->workgrouplist; w && t == -1; w = w->next)
+    {
+      if (strequal(w->work_group, work->work_group))
+        t = w->token;
+    }
+  }
+  
+  if (t == -1)
+    work->token = ++workgroup_count;
+  else
+    work->token = t;
+  
+  /* No known local master browser as yet. */
+  *work->local_master_browser_name = '\0';
+
+  /* No known domain master browser as yet. */
+  *work->dmb_name.name = '\0';
+  zero_ip(&work->dmb_addr);
+
+  /* WfWg  uses 01040b01 */
+  /* Win95 uses 01041501 */
+  /* NTAS  uses ???????? */
+  work->ElectionCriterion  = (MAINTAIN_LIST)|(BROWSER_ELECTION_VERSION<<8); 
+  work->ElectionCriterion |= (lp_os_level() << 24);
+  if (lp_domain_master())
+    work->ElectionCriterion |= 0x80;
+  
+  return work;
+}
+
+/*******************************************************************
+  Remove a workgroup.
+  ******************************************************************/
+
+static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec, 
+                                     struct work_record *work)
+{
+  struct work_record *ret_work = NULL;
+  
+  DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group));
+  
+  ret_work = work->next;
+
+  remove_all_servers(work);
+  
+  if (!work->serverlist)
+  {
+    if (work->prev)
+      work->prev->next = work->next;
+    if (work->next)
+      work->next->prev = work->prev;
+  
+    if (subrec->workgrouplist == work)
+      subrec->workgrouplist = work->next; 
+  
+    ZERO_STRUCTP(work);
+    SAFE_FREE(work);
+  }
+  
+  subrec->work_changed = True;
+
+  return ret_work;
+}
+
+
+/****************************************************************************
+  Find a workgroup in the workgroup list of a subnet.
+  **************************************************************************/
+
+struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec, 
+                                             const char *name)
+{
+  struct work_record *ret;
+  
+  DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ",
+            name, subrec->subnet_name));
+  
+  for (ret = subrec->workgrouplist; ret; ret = ret->next)
+  {
+    if (!strcmp(ret->work_group,name))
+    {
+      DEBUGADD(4, ("found.\n"));
+      return(ret);
+    }
+  }
+  DEBUGADD(4, ("not found.\n"));
+  return NULL;
+}
+
+/****************************************************************************
+  Create a workgroup in the workgroup list of the subnet.
+  **************************************************************************/
+
+struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec,
+                                               const char *name, int ttl)
+{
+  struct work_record *work = NULL;
+
+  DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n",
+           name, subrec->subnet_name));
+  
+  if ((work = create_workgroup(name, ttl)))
+  {
+    add_workgroup(subrec, work);
+
+    subrec->work_changed = True;
+
+    return(work);
+  }
+
+  return NULL;
+}
+
+/****************************************************************************
+  Update a workgroup ttl.
+  **************************************************************************/
+
+void update_workgroup_ttl(struct work_record *work, int ttl)
+{
+  if(work->death_time != PERMANENT_TTL)
+    work->death_time = time(NULL)+(ttl*3);
+  work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Fail function called if we cannot register the WORKGROUP<0> and
+ WORKGROUP<1e> names on the net.
+**************************************************************************/
+     
+static void fail_register(struct subnet_record *subrec, struct response_record *rrec,
+                          struct nmb_name *nmbname)
+{  
+  DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n",
+            nmb_namestr(nmbname), subrec->subnet_name));
+}  
+
+/****************************************************************************
+ If the workgroup is our primary workgroup, add the required names to it.
+**************************************************************************/
+
+void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work)
+{
+  int i;
+
+  if(!strequal(lp_workgroup(), work->work_group))
+    return;
+
+  /* If this is a broadcast subnet then start elections on it
+     if we are so configured. */
+
+  if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) &&
+      (subrec != wins_server_subnet) && lp_preferred_master() &&
+      lp_local_master())
+  {
+    DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+    work->needelection = True;
+    work->ElectionCriterion |= (1<<3);
+  }  
+  
+  /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */
+
+  register_name(subrec,lp_workgroup(),0x0,samba_nb_type|NB_GROUP,
+                NULL,
+                fail_register,NULL);
+     
+  register_name(subrec,lp_workgroup(),0x1e,samba_nb_type|NB_GROUP,
+                NULL,
+                fail_register,NULL);
+     
+  for( i = 0; my_netbios_names(i); i++)
+  {
+    const char *name = my_netbios_names(i);
+    int stype = lp_default_server_announce() | (lp_local_master() ?
+                        SV_TYPE_POTENTIAL_BROWSER : 0 );
+   
+    if(!strequal(lp_netbios_name(), name))
+        stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|
+                   SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
+   
+    create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY,
+                              PERMANENT_TTL, 
+                              string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+    DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \
+on subnet %s\n", name, subrec->subnet_name));
+  }
+} 
+
+/****************************************************************************
+  Dump a copy of the workgroup database into the log file.
+  **************************************************************************/
+
+void dump_workgroups(BOOL force_write)
+{
+  struct subnet_record *subrec;
+  int debuglevel = force_write ? 0 : 4;
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+    if (subrec->workgrouplist)
+    {
+      struct work_record *work;
+
+      if( DEBUGLVL( debuglevel ) )
+      {
+        dbgtext( "dump_workgroups()\n " );
+        dbgtext( "dump workgroup on subnet %15s: ", subrec->subnet_name );
+        dbgtext( "netmask=%15s:\n", inet_ntoa(subrec->mask_ip) );
+      }
+         
+      for (work = subrec->workgrouplist; work; work = work->next)
+      {
+        DEBUGADD( debuglevel, ( "\t%s(%d) current master browser = %s\n",
+                                work->work_group,
+                                work->token, 
+                               *work->local_master_browser_name
+                              ? work->local_master_browser_name : "UNKNOWN" ) );
+        if (work->serverlist)
+        {
+          struct server_record *servrec;                 
+          for (servrec = work->serverlist; servrec; servrec = servrec->next)
+          {
+            DEBUGADD( debuglevel, ( "\t\t%s %8x (%s)\n",
+                                    servrec->serv.name,
+                                    servrec->serv.type,
+                                    servrec->serv.comment ) );
+          }
+        }
+      }
+    }
+  }
+}
+
+/****************************************************************************
+  Expire any dead servers on all workgroups. If the workgroup has expired
+  remove it.
+  **************************************************************************/
+
+void expire_workgroups_and_servers(time_t t)
+{
+  struct subnet_record *subrec;
+   
+  for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
+  {
+    struct work_record *work;
+    struct work_record *nextwork;
+
+    for (work = subrec->workgrouplist; work; work = nextwork)
+    {
+      nextwork = work->next;
+      expire_servers(work, t);
+
+      if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) && 
+                     ((t == -1) || (work->death_time < t)))
+      {
+        DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n",
+                  work->work_group));
+        remove_workgroup_from_subnet(subrec, work);
+      }
+    }
+  }
+}
diff --git a/source4/nsswitch/.cvsignore b/source4/nsswitch/.cvsignore
new file mode 100644 (file)
index 0000000..658d50a
--- /dev/null
@@ -0,0 +1,4 @@
+*.po
+*.po32
+diffs
+winbindd_proto.h
diff --git a/source4/nsswitch/README b/source4/nsswitch/README
new file mode 100644 (file)
index 0000000..9f0c581
--- /dev/null
@@ -0,0 +1,13 @@
+This extension provides a "wins" module for NSS on glibc2/Linux.  This
+allows you to use a WINS entry in /etc/nsswitch.conf for hostname
+resolution, allowing you to resolve netbios names via start unix
+gethostbyname() calls. The end result is that you can use netbios
+names as host names in unix apps.
+
+1) run configure
+2) run "make nsswitch"
+3) cp nsswitch/libnss_wins.so /lib/libnss_wins.so.2
+4) add a wins entry to the hosts line in /etc/nsswitch.conf
+5) use it
+
+tridge@linuxcare.com
diff --git a/source4/nsswitch/hp_nss_common.h b/source4/nsswitch/hp_nss_common.h
new file mode 100644 (file)
index 0000000..5bd5374
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _HP_NSS_COMMON_H
+#define _HP_NSS_COMMON_H
+/*
+   Unix SMB/CIFS implementation.
+   Donated by HP to enable Winbindd to build on HPUX 11.x.
+   Copyright (C) Jeremy Allison 2002.
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   This library 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
+   Library General Public License for more details.
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.
+*/
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+typedef enum {
+       NSS_SUCCESS,
+       NSS_NOTFOUND,
+       NSS_UNAVAIL,
+       NSS_TRYAGAIN
+} nss_status_t;
+struct nss_backend;
+typedef nss_status_t (*nss_backend_op_t)(struct nss_backend *, void *args);
+struct nss_backend {
+       nss_backend_op_t *ops;
+       int n_ops;
+};
+typedef struct nss_backend nss_backend_t;
+typedef int nss_dbop_t;
+
+#endif /* _HP_NSS_COMMON_H */
diff --git a/source4/nsswitch/hp_nss_dbdefs.h b/source4/nsswitch/hp_nss_dbdefs.h
new file mode 100644 (file)
index 0000000..bd24772
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef _HP_NSS_DBDEFS_H
+#define _HP_NSS_DBDEFS_H
+
+/*
+   Unix SMB/CIFS implementation.
+   Donated by HP to enable Winbindd to build on HPUX 11.x.
+   Copyright (C) Jeremy Allison 2002.
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   This library 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
+   Library General Public License for more details.
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.
+*/
+
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+#ifndef NSS_INCLUDE_UNSAFE
+#define NSS_INCLUDE_UNSAFE      1       /* Build old, MT-unsafe interfaces, */
+#endif  /* NSS_INCLUDE_UNSAFE */
+enum nss_netgr_argn {
+       NSS_NETGR_MACHINE,
+       NSS_NETGR_USER,
+       NSS_NETGR_DOMAIN,
+       NSS_NETGR_N
+};
+enum nss_netgr_status {
+       NSS_NETGR_FOUND,
+       NSS_NETGR_NO,
+       NSS_NETGR_NOMEM
+};
+typedef unsigned nss_innetgr_argc;
+typedef char **nss_innetgr_argv;
+struct nss_innetgr_1arg {
+       nss_innetgr_argc argc;
+       nss_innetgr_argv argv;
+};
+typedef struct {
+       void *result;        /* "result" parameter to getXbyY_r() */
+       char *buffer;        /* "buffer"     "             "      */
+       int buflen;         /* "buflen"     "             "      */
+} nss_XbyY_buf_t;
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+union nss_XbyY_key {
+       uid_t uid;
+       gid_t gid;
+       const char *name;
+       int number;
+       struct {
+               long net;
+               int type;
+       } netaddr;
+       struct {
+               const char *addr;
+               int len;
+               int type;
+       } hostaddr;
+       struct {
+               union {
+                       const char *name;
+                       int port;
+               } serv;
+               const char *proto;
+       } serv;
+       void *ether;
+};
+typedef struct nss_XbyY_args {
+       nss_XbyY_buf_t  buf;
+       int stayopen;
+       /*
+        * Support for setXXXent(stayopen)
+        * Used only in hosts, protocols,
+        * networks, rpc, and services.
+        */
+       int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen);
+       union nss_XbyY_key key;
+       void *returnval;
+       int erange;
+       int h_errno;
+       nss_status_t status;
+} nss_XbyY_args_t;
+#endif /* _NSS_DBDEFS_H */
diff --git a/source4/nsswitch/nss.h b/source4/nsswitch/nss.h
new file mode 100644 (file)
index 0000000..d83a5e2
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+/* 
+   Unix SMB/CIFS implementation.
+
+   a common place to work out how to define NSS_STATUS on various
+   platforms
+
+   Copyright (C) Tim Potter 2000
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#ifdef HAVE_NSS_COMMON_H
+
+/* Sun Solaris */
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS     NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND    NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL     NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN    NSS_TRYAGAIN
+
+#elif HAVE_NSS_H
+
+/* GNU */
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#elif HAVE_NS_API_H
+
+/* SGI IRIX */
+
+/* following required to prevent warnings of double definition
+ * of datum from ns_api.h
+*/
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+  NSS_STATUS_SUCCESS=NS_SUCCESS,
+  NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+  NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+  NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#define NSD_MEM_STATIC 0
+#define NSD_MEM_VOLATILE 1
+#define NSD_MEM_DYNAMIC 2
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+/* HP-UX 11 */
+
+#include "nsswitch/hp_nss_common.h"
+#include "nsswitch/hp_nss_dbdefs.h"
+#include <nsswitch.h>
+
+#ifndef _HAVE_TYPEDEF_NSS_STATUS
+#define _HAVE_TYPEDEF_NSS_STATUS
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS     NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND    NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL     NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN    NSS_TRYAGAIN
+#endif /* HPUX */
+
+#else /* Nothing's defined. Neither gnu nor sun nor hp */
+
+typedef enum
+{
+  NSS_STATUS_SUCCESS=0,
+  NSS_STATUS_NOTFOUND=1,
+  NSS_STATUS_UNAVAIL=2,
+  NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSWITCH_NSS_H */
diff --git a/source4/nsswitch/pam_winbind.c b/source4/nsswitch/pam_winbind.c
new file mode 100644 (file)
index 0000000..123f670
--- /dev/null
@@ -0,0 +1,754 @@
+/* pam_winbind module
+
+   Copyright Andrew Tridgell <tridge@samba.org> 2000
+   Copyright Tim Potter <tpot@samba.org> 2000
+   Copyright Andrew Bartlett <abartlet@samba.org> 2002
+
+   largely based on pam_userdb by Christian Gafton <gafton@redhat.com> 
+   also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
+   (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES       3
+
+/* some syslogging */
+static void _pam_log(int err, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+       openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+       vsyslog(err, format, args);
+       va_end(args);
+       closelog();
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+       int ctrl;
+       /* step through arguments */
+       for (ctrl = 0; argc-- > 0; ++argv) {
+
+               /* generic options */
+               
+               if (!strcmp(*argv,"debug"))
+                       ctrl |= WINBIND_DEBUG_ARG;
+               else if (!strcasecmp(*argv, "use_authtok"))
+                       ctrl |= WINBIND_USE_AUTHTOK_ARG;
+               else if (!strcasecmp(*argv, "use_first_pass"))
+                       ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+               else if (!strcasecmp(*argv, "try_first_pass"))
+                       ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+               else if (!strcasecmp(*argv, "unknown_ok"))
+                       ctrl |= WINBIND_UNKNOWN_OK_ARG;
+               else {
+                       _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+               }
+       }
+       
+       return ctrl;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(pam_handle_t *pamh, int nargs,
+                   struct pam_message **message,
+                   struct pam_response **response)
+{
+    int retval;
+    struct pam_conv *conv;
+
+    retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
+    if (retval == PAM_SUCCESS) {
+       retval = conv->conv(nargs, (const struct pam_message **)message,
+                           response, conv->appdata_ptr);
+    }
+       
+    return retval; /* propagate error status */
+}
+
+
+static int _make_remark(pam_handle_t * pamh, int type, const char *text)
+{
+       int retval = PAM_SUCCESS;
+
+       struct pam_message *pmsg[1], msg[1];
+       struct pam_response *resp;
+       
+       pmsg[0] = &msg[0];
+       msg[0].msg = text;
+       msg[0].msg_style = type;
+       
+       resp = NULL;
+       retval = converse(pamh, 1, pmsg, &resp);
+       
+       if (resp) {
+               _pam_drop_reply(resp, 1);
+       }
+       return retval;
+}
+
+static int pam_winbind_request(enum winbindd_cmd req_type,
+                              struct winbindd_request *request,
+                              struct winbindd_response *response)
+{
+
+       /* Fill in request and send down pipe */
+       init_request(request, req_type);
+       
+       if (write_sock(request, sizeof(*request)) == -1) {
+               _pam_log(LOG_ERR, "write to socket failed!");
+               close_sock();
+               return PAM_SERVICE_ERR;
+       }
+       
+       /* Wait for reply */
+       if (read_reply(response) == -1) {
+               _pam_log(LOG_ERR, "read from socket failed!");
+               close_sock();
+               return PAM_SERVICE_ERR;
+       }
+
+       /* We are done with the socket - close it and avoid mischeif */
+       close_sock();
+
+       /* Copy reply data from socket */
+       if (response->result != WINBINDD_OK) {
+               if (response->data.auth.pam_error != PAM_SUCCESS) {
+                       _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s", 
+                                response->data.auth.error_string,
+                                response->data.auth.pam_error,
+                                response->data.auth.nt_status_string);
+                       return response->data.auth.pam_error;
+               } else {
+                       _pam_log(LOG_ERR, "request failed, but PAM error 0!");
+                       return PAM_SERVICE_ERR;
+               }
+       }
+       
+       return PAM_SUCCESS;
+}
+
+static int pam_winbind_request_log(enum winbindd_cmd req_type,
+                              struct winbindd_request *request,
+                              struct winbindd_response *response,
+                                  int ctrl,
+                                  const char *user)
+{
+       int retval;
+
+        retval = pam_winbind_request(req_type, request, response);
+
+       switch (retval) {
+       case PAM_AUTH_ERR:
+               /* incorrect password */
+               _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", user);
+               return retval;
+       case PAM_ACCT_EXPIRED:
+               /* account expired */
+               _pam_log(LOG_WARNING, "user `%s' account expired", user);
+               return retval;
+       case PAM_AUTHTOK_EXPIRED:
+               /* password expired */
+               _pam_log(LOG_WARNING, "user `%s' password expired", user);
+               return retval;
+       case PAM_NEW_AUTHTOK_REQD:
+               /* password expired */
+               _pam_log(LOG_WARNING, "user `%s' new password required", user);
+               return retval;
+       case PAM_USER_UNKNOWN:
+               /* the user does not exist */
+               if (ctrl & WINBIND_DEBUG_ARG)
+                       _pam_log(LOG_NOTICE, "user `%s' not found",
+                                user);
+               if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
+                       return PAM_IGNORE;
+               }        
+               return retval;
+       case PAM_SUCCESS:
+               if (req_type == WINBINDD_PAM_AUTH) {
+                       /* Otherwise, the authentication looked good */
+                       _pam_log(LOG_NOTICE, "user '%s' granted acces", user);
+               } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
+                       /* Otherwise, the authentication looked good */
+                       _pam_log(LOG_NOTICE, "user '%s' password changed", user);
+               } else { 
+                       /* Otherwise, the authentication looked good */
+                       _pam_log(LOG_NOTICE, "user '%s' OK", user);
+               }
+               return retval;
+       default:
+               /* we don't know anything about this return value */
+               _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+                        retval, user);
+               return retval;
+       }
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(const char *user, const char *pass, int ctrl)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+
+       strncpy(request.data.auth.user, user, 
+                sizeof(request.data.auth.user)-1);
+
+       strncpy(request.data.auth.pass, pass, 
+                sizeof(request.data.auth.pass)-1);
+       
+       
+        return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(const char *user, const char *oldpass,
+                                     const char *newpass, int ctrl)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+
+        if (request.data.chauthtok.user == NULL) return -2;
+
+       strncpy(request.data.chauthtok.user, user, 
+                sizeof(request.data.chauthtok.user) - 1);
+
+        if (oldpass != NULL) {
+            strncpy(request.data.chauthtok.oldpass, oldpass, 
+                    sizeof(request.data.chauthtok.oldpass) - 1);
+        } else {
+            request.data.chauthtok.oldpass[0] = '\0';
+        }
+       
+        if (newpass != NULL) {
+            strncpy(request.data.chauthtok.newpass, newpass, 
+                    sizeof(request.data.chauthtok.newpass) - 1);
+        } else {
+            request.data.chauthtok.newpass[0] = '\0';
+        }
+       
+        return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ *      1  = User not found
+ *      0  = OK
+ *     -1  = System error
+ */
+static int valid_user(const char *user)
+{
+       if (getpwnam(user)) return 0;
+       return 1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+    _pam_overwrite(xx);
+    _pam_drop(xx);
+    return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(pam_handle_t * pamh
+                                 ,unsigned int ctrl
+                                 ,const char *comment
+                                 ,const char *prompt1
+                                 ,const char *prompt2
+                                 ,const char **pass)
+{
+       int authtok_flag;
+       int retval;
+       const char *item;
+       char *token;
+
+       /*
+        * make sure nothing inappropriate gets returned
+        */
+
+       *pass = token = NULL;
+
+       /*
+        * which authentication token are we getting?
+        */
+
+       authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+       /*
+        * should we obtain the password from a PAM item ?
+        */
+
+       if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+               retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+               if (retval != PAM_SUCCESS) {
+                       /* very strange. */
+                       _pam_log(LOG_ALERT, 
+                                "pam_get_item returned error to unix-read-password"
+                           );
+                       return retval;
+               } else if (item != NULL) {      /* we have a password! */
+                       *pass = item;
+                       item = NULL;
+                       return PAM_SUCCESS;
+               } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+                       return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
+               } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+                          && off(WINBIND__OLD_PASSWORD, ctrl)) {
+                       return PAM_AUTHTOK_RECOVER_ERR;
+               }
+       }
+       /*
+        * getting here implies we will have to get the password from the
+        * user directly.
+        */
+
+       {
+               struct pam_message msg[3], *pmsg[3];
+               struct pam_response *resp;
+               int i, replies;
+
+               /* prepare to converse */
+
+               if (comment != NULL) {
+                       pmsg[0] = &msg[0];
+                       msg[0].msg_style = PAM_TEXT_INFO;
+                       msg[0].msg = comment;
+                       i = 1;
+               } else {
+                       i = 0;
+               }
+
+               pmsg[i] = &msg[i];
+               msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+               msg[i++].msg = prompt1;
+               replies = 1;
+
+               if (prompt2 != NULL) {
+                       pmsg[i] = &msg[i];
+                       msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+                       msg[i++].msg = prompt2;
+                       ++replies;
+               }
+               /* so call the conversation expecting i responses */
+               resp = NULL;
+               retval = converse(pamh, i, pmsg, &resp);
+
+               if (resp != NULL) {
+
+                       /* interpret the response */
+
+                       if (retval == PAM_SUCCESS) {    /* a good conversation */
+
+                               token = x_strdup(resp[i - replies].resp);
+                               if (token != NULL) {
+                                       if (replies == 2) {
+
+                                               /* verify that password entered correctly */
+                                               if (!resp[i - 1].resp
+                                                   || strcmp(token, resp[i - 1].resp)) {
+                                                       _pam_delete(token);     /* mistyped */
+                                                       retval = PAM_AUTHTOK_RECOVER_ERR;
+                                                       _make_remark(pamh                                                                   ,PAM_ERROR_MSG, MISTYPED_PASS);
+                                               }
+                                       }
+                               } else {
+                                       _pam_log(LOG_NOTICE
+                                                ,"could not recover authentication token");
+                               }
+
+                       }
+                       /*
+                        * tidy up the conversation (resp_retcode) is ignored
+                        * -- what is it for anyway? AGM
+                        */
+
+                       _pam_drop_reply(resp, i);
+
+               } else {
+                       retval = (retval == PAM_SUCCESS)
+                           ? PAM_AUTHTOK_RECOVER_ERR : retval;
+               }
+       }
+
+       if (retval != PAM_SUCCESS) {
+               if (on(WINBIND_DEBUG_ARG, ctrl))
+                       _pam_log(LOG_DEBUG,
+                                "unable to obtain a password");
+               return retval;
+       }
+       /* 'token' is the entered password */
+
+       /* we store this password as an item */
+       
+       retval = pam_set_item(pamh, authtok_flag, token);
+       _pam_delete(token);     /* clean it up */
+       if (retval != PAM_SUCCESS
+           || (retval = pam_get_item(pamh, authtok_flag
+                                     ,(const void **) &item))
+           != PAM_SUCCESS) {
+               
+               _pam_log(LOG_CRIT, "error manipulating password");
+               return retval;
+               
+       }
+
+       *pass = item;
+       item = NULL;            /* break link to password */
+
+       return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+                       int argc, const char **argv)
+{
+     const char *username;
+     const char *password;
+     int retval = PAM_AUTH_ERR;
+    
+     /* parse arguments */
+     int ctrl = _pam_parse(argc, argv);
+
+     /* Get the username */
+     retval = pam_get_user(pamh, &username, NULL);
+     if ((retval != PAM_SUCCESS) || (!username)) {
+        if (ctrl & WINBIND_DEBUG_ARG)
+            _pam_log(LOG_DEBUG,"can not get the username");
+        return PAM_SERVICE_ERR;
+     }
+     
+     retval = _winbind_read_password(pamh, ctrl, NULL, 
+                                    "Password: ", NULL,
+                                    &password);
+     
+     if (retval != PAM_SUCCESS) {
+        _pam_log(LOG_ERR, "Could not retrieve user's password");
+        return PAM_AUTHTOK_ERR;
+     }
+     
+     if (ctrl & WINBIND_DEBUG_ARG) {
+
+            /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+        _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+                 username, password);
+#else
+        _pam_log(LOG_INFO, "Verify user `%s'", username);
+#endif
+     }
+
+     /* Now use the username to look up password */
+     return winbind_auth_request(username, password, ctrl);
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+                  int argc, const char **argv)
+{
+    return PAM_SUCCESS;
+}
+
+/*
+ * Account management. We want to verify that the account exists 
+ * before returning PAM_SUCCESS
+ */
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+                  int argc, const char **argv)
+{
+    const char *username;
+    int retval = PAM_USER_UNKNOWN;
+
+    /* parse arguments */
+    int ctrl = _pam_parse(argc, argv);
+
+    /* Get the username */
+    retval = pam_get_user(pamh, &username, NULL);
+    if ((retval != PAM_SUCCESS) || (!username)) {
+       if (ctrl & WINBIND_DEBUG_ARG)
+           _pam_log(LOG_DEBUG,"can not get the username");
+       return PAM_SERVICE_ERR;
+    }
+
+    /* Verify the username */
+    retval = valid_user(username);
+    switch (retval) {
+       case -1:
+           /* some sort of system error. The log was already printed */
+           return PAM_SERVICE_ERR;
+       case 1:
+           /* the user does not exist */
+           if (ctrl & WINBIND_DEBUG_ARG)
+               _pam_log(LOG_NOTICE, "user `%s' not found",
+                        username);
+           if (ctrl & WINBIND_UNKNOWN_OK_ARG)
+               return PAM_IGNORE;
+           return PAM_USER_UNKNOWN;
+       case 0:
+           /* Otherwise, the authentication looked good */
+           _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
+           return PAM_SUCCESS;
+       default:
+           /* we don't know anything about this return value */
+           _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+                    retval, username);
+           return PAM_SERVICE_ERR;
+    }
+    
+    /* should not be reached */
+    return PAM_IGNORE;
+}
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+                int argc, const char **argv)
+{
+        /* parse arguments */
+        int ctrl = _pam_parse(argc, argv);
+        if (ctrl & WINBIND_DEBUG_ARG)
+              _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
+        return PAM_SUCCESS;
+}
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+                int argc, const char **argv)
+{
+        /* parse arguments */
+        int ctrl = _pam_parse(argc, argv);
+        if (ctrl & WINBIND_DEBUG_ARG)
+              _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
+        return PAM_SUCCESS;
+}
+
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+                               int argc, const char **argv)
+{
+       unsigned int lctrl;
+       int retval;
+       unsigned int ctrl = _pam_parse(argc, argv);
+
+       /* <DO NOT free() THESE> */
+       const char *user;
+       char *pass_old, *pass_new;
+       /* </DO NOT free() THESE> */
+
+       char *Announce;
+       
+       int retry = 0;
+
+       /*
+        * First get the name of a user
+        */
+       retval = pam_get_user(pamh, &user, "Username: ");
+       if (retval == PAM_SUCCESS) {
+               if (user == NULL) {
+                       _pam_log(LOG_ERR, "username was NULL!");
+                       return PAM_USER_UNKNOWN;
+               }
+               if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
+                       _pam_log(LOG_DEBUG, "username [%s] obtained",
+                                user);
+       } else {
+               if (on(WINBIND_DEBUG_ARG, ctrl))
+                       _pam_log(LOG_DEBUG,
+                                "password - could not identify user");
+               return retval;
+       }
+
+       /*
+        * obtain and verify the current password (OLDAUTHTOK) for
+        * the user.
+        */
+
+       if (flags & PAM_PRELIM_CHECK) {
+               
+               /* instruct user what is happening */
+#define greeting "Changing password for "
+               Announce = (char *) malloc(sizeof(greeting) + strlen(user));
+               if (Announce == NULL) {
+               _pam_log(LOG_CRIT, 
+                        "password - out of memory");
+               return PAM_BUF_ERR;
+               }
+               (void) strcpy(Announce, greeting);
+               (void) strcpy(Announce + sizeof(greeting) - 1, user);
+#undef greeting
+               
+               lctrl = ctrl | WINBIND__OLD_PASSWORD;
+               retval = _winbind_read_password(pamh, lctrl
+                                               ,Announce
+                                               ,"(current) NT password: "
+                                               ,NULL
+                                               ,(const char **) &pass_old);
+               free(Announce);
+               
+               if (retval != PAM_SUCCESS) {
+                       _pam_log(LOG_NOTICE
+                                ,"password - (old) token not obtained");
+                       return retval;
+               }
+               /* verify that this is the password for this user */
+               
+               retval = winbind_auth_request(user, pass_old, ctrl);
+               
+               if (retval != PAM_ACCT_EXPIRED 
+                   && retval != PAM_AUTHTOK_EXPIRED
+                   && retval != PAM_NEW_AUTHTOK_REQD 
+                   && retval != PAM_SUCCESS) {
+                       pass_old = NULL;
+                       return retval;
+               }
+               
+               retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+               pass_old = NULL;
+               if (retval != PAM_SUCCESS) {
+                       _pam_log(LOG_CRIT, 
+                                "failed to set PAM_OLDAUTHTOK");
+               }
+       } else if (flags & PAM_UPDATE_AUTHTOK) {
+       
+               /*
+                * obtain the proposed password
+                */
+               
+               /*
+                * get the old token back. 
+                */
+               
+               retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+                                     ,(const void **) &pass_old);
+               
+               if (retval != PAM_SUCCESS) {
+                       _pam_log(LOG_NOTICE, "user not authenticated");
+                       return retval;
+               }
+               
+               lctrl = ctrl;
+               
+               if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+                       ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
+               }
+               retry = 0;
+               retval = PAM_AUTHTOK_ERR;
+               while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+                       /*
+                        * use_authtok is to force the use of a previously entered
+                        * password -- needed for pluggable password strength checking
+                        */
+                       
+                       retval = _winbind_read_password(pamh, lctrl
+                                                       ,NULL
+                                                       ,"Enter new NT password: "
+                                                       ,"Retype new NT password: "
+                                                       ,(const char **) &pass_new);
+                       
+                       if (retval != PAM_SUCCESS) {
+                               if (on(WINBIND_DEBUG_ARG, ctrl)) {
+                                       _pam_log(LOG_ALERT
+                                                ,"password - new password not obtained");
+                               }
+                               pass_old = NULL;/* tidy up */
+                               return retval;
+                       }
+
+                       /*
+                        * At this point we know who the user is and what they
+                        * propose as their new password. Verify that the new
+                        * password is acceptable.
+                        */
+                       
+                       if (pass_new[0] == '\0') {/* "\0" password = NULL */
+                               pass_new = NULL;
+                       }
+               }
+               
+               /*
+                * By reaching here we have approved the passwords and must now
+                * rebuild the password database file.
+                */
+
+               retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
+               _pam_overwrite(pass_new);
+               _pam_overwrite(pass_old);
+               pass_old = pass_new = NULL;
+       } else {
+               retval = PAM_SERVICE_ERR;
+       }
+       
+       return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+     MODULE_NAME,
+     pam_sm_authenticate,
+     pam_sm_setcred,
+     pam_sm_acct_mgmt,
+     pam_sm_open_session,
+     pam_sm_close_session,
+     pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
+ * Copyright (c) Tim Potter       <tpot@samba.org>     2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/source4/nsswitch/pam_winbind.h b/source4/nsswitch/pam_winbind.h
new file mode 100644 (file)
index 0000000..fae635d
--- /dev/null
@@ -0,0 +1,93 @@
+/* pam_winbind header file 
+   (Solaris needs some macros from Linux for common PAM code)
+
+   Shirish Kalele 2000
+*/
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <config.h>
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+
+#if defined(SUNOS5) || defined(SUNOS4) || defined(HPUX)
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#include <security/pam_appl.h> 
+
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#ifdef HAVE_SECURITY_PAM_MODULES_H
+#include <security/pam_modules.h>
+#endif
+
+#ifdef HAVE_SECURITY__PAM_MACROS_H
+#include <security/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do {                                              \
+    int reply_i;                                  \
+                                                  \
+    for (reply_i=0; reply_i<replies; ++reply_i) { \
+        if (reply[reply_i].resp) {                \
+            _pam_overwrite(reply[reply_i].resp);  \
+            free(reply[reply_i].resp);            \
+        }                                         \
+    }                                             \
+    if (reply)                                    \
+        free(reply);                              \
+} while (0)
+
+#define _pam_overwrite(x)        \
+do {                             \
+     register char *__xx__;      \
+     if ((__xx__=(x)))           \
+          while (*__xx__)        \
+               *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define  x_strdup(s)  ( (s) ? strdup(s):NULL )     
+#endif
+
+#define WINBIND_DEBUG_ARG (1<<0)
+#define WINBIND_USE_AUTHTOK_ARG (1<<1)
+#define WINBIND_UNKNOWN_OK_ARG (1<<2)
+#define WINBIND_TRY_FIRST_PASS_ARG (1<<3)
+#define WINBIND_USE_FIRST_PASS_ARG (1<<4)
+#define WINBIND__OLD_PASSWORD (1<<5)
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#include "winbind_client.h"
diff --git a/source4/nsswitch/wb_client.c b/source4/nsswitch/wb_client.c
new file mode 100644 (file)
index 0000000..62c9686
--- /dev/null
@@ -0,0 +1,307 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   winbind client code
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Andrew Tridgell 2000
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#include "includes.h"
+#include "nsswitch/nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern DOM_SID global_sid_NULL;                        /* NULL sid */
+
+NSS_STATUS winbindd_request(int req_type,
+                                 struct winbindd_request *request,
+                                 struct winbindd_response *response);
+
+/* Call winbindd to convert a name to a sid */
+
+BOOL winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid, 
+                         enum SID_NAME_USE *name_type)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       NSS_STATUS result;
+       
+       if (!sid || !name_type)
+               return False;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.name.dom_name, dom_name);
+       fstrcpy(request.data.name.name, name);
+
+       if ((result = winbindd_request(WINBINDD_LOOKUPNAME, &request, 
+                                      &response)) == NSS_STATUS_SUCCESS) {
+               if (!string_to_sid(sid, response.data.sid.sid))
+                       return False;
+               *name_type = (enum SID_NAME_USE)response.data.sid.type;
+       }
+
+       return result == NSS_STATUS_SUCCESS;
+}
+
+/* Call winbindd to convert sid to name */
+
+BOOL winbind_lookup_sid(const DOM_SID *sid, 
+                       fstring dom_name, fstring name, 
+                        enum SID_NAME_USE *name_type)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       NSS_STATUS result;
+       fstring sid_str;
+       
+       /* Initialise request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       sid_to_string(sid_str, sid);
+       fstrcpy(request.data.sid, sid_str);
+       
+       /* Make request */
+
+       result = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+
+       /* Copy out result */
+
+       if (result == NSS_STATUS_SUCCESS) {
+               fstrcpy(dom_name, response.data.name.dom_name);
+               fstrcpy(name, response.data.name.name);
+               *name_type = (enum SID_NAME_USE)response.data.name.type;
+
+               DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n", 
+                           sid_str, dom_name, name));
+       }
+
+       return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to uid */
+
+BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int result;
+       fstring sid_str;
+
+       if (!puid)
+               return False;
+
+       /* Initialise request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       sid_to_string(sid_str, sid);
+       fstrcpy(request.data.sid, sid_str);
+       
+       /* Make request */
+
+       result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+       /* Copy out result */
+
+       if (result == NSS_STATUS_SUCCESS) {
+               *puid = response.data.uid;
+       }
+
+       return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int result;
+
+       if (!sid)
+               return False;
+
+       /* Initialise request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.uid = uid;
+
+       /* Make request */
+
+       result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+
+       /* Copy out result */
+
+       if (result == NSS_STATUS_SUCCESS) {
+               if (!string_to_sid(sid, response.data.sid.sid))
+                       return False;
+       } else {
+               sid_copy(sid, &global_sid_NULL);
+       }
+
+       return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid */
+
+BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int result;
+       fstring sid_str;
+
+       if (!pgid)
+               return False;
+
+       /* Initialise request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       sid_to_string(sid_str, sid);
+       fstrcpy(request.data.sid, sid_str);
+       
+       /* Make request */
+
+       result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+       /* Copy out result */
+
+       if (result == NSS_STATUS_SUCCESS) {
+               *pgid = response.data.gid;
+       }
+
+       return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert gid to sid */
+
+BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int result;
+
+       if (!sid)
+               return False;
+
+       /* Initialise request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.gid = gid;
+
+       /* Make request */
+
+       result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+
+       /* Copy out result */
+
+       if (result == NSS_STATUS_SUCCESS) {
+               if (!string_to_sid(sid, response.data.sid.sid))
+                       return False;
+       } else {
+               sid_copy(sid, &global_sid_NULL);
+       }
+
+       return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Fetch the list of groups a user is a member of from winbindd.  This is
+   used by winbind_getgroups. */
+
+static int wb_getgroups(const char *user, gid_t **groups)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int result;
+
+       /* Call winbindd */
+
+       fstrcpy(request.data.username, user);
+
+       ZERO_STRUCT(response);
+
+       result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+       if (result == NSS_STATUS_SUCCESS) {
+               
+               /* Return group list.  Don't forget to free the group list
+                  when finished. */
+
+               *groups = (gid_t *)response.extra_data;
+               return response.data.num_entries;
+       }
+
+       return -1;
+}
+
+/* Return a list of groups the user is a member of.  This function is
+   useful for large systems where inverting the group database would be too
+   time consuming.  If size is zero, list is not modified and the total
+   number of groups for the user is returned. */
+
+int winbind_getgroups(const char *user, int size, gid_t *list)
+{
+       gid_t *groups = NULL;
+       int result, i;
+
+       /*
+        * Don't do the lookup if the name has no separator _and_ we are not in
+        * 'winbind use default domain' mode.
+        */
+
+       if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
+               return -1;
+
+       /* Fetch list of groups */
+
+       result = wb_getgroups(user, &groups);
+
+       if (size == 0)
+               goto done;
+
+       if (result > size) {
+               result = -1;
+               errno = EINVAL; /* This is what getgroups() does */
+               goto done;
+       }
+
+       /* Copy list of groups across */
+
+       for (i = 0; i < result; i++) {
+               list[i] = groups[i];
+       }
+
+ done:
+       SAFE_FREE(groups);
+       return result;
+}
diff --git a/source4/nsswitch/wb_common.c b/source4/nsswitch/wb_common.c
new file mode 100644 (file)
index 0000000..89c751a
--- /dev/null
@@ -0,0 +1,433 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   winbind client common code
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) Andrew Bartlett 2002
+   
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#include "winbind_client.h"
+
+/* Global variables.  These are effectively the client state information */
+
+int winbindd_fd = -1;           /* fd for winbindd socket */
+
+/* Free a response structure */
+
+void free_response(struct winbindd_response *response)
+{
+       /* Free any allocated extra_data */
+
+       if (response)
+               SAFE_FREE(response->extra_data);
+}
+
+/* Initialise a request structure */
+
+void init_request(struct winbindd_request *request, int request_type)
+{
+       request->length = sizeof(struct winbindd_request);
+
+       request->cmd = (enum winbindd_cmd)request_type;
+       request->pid = getpid();
+
+}
+
+/* Initialise a response structure */
+
+void init_response(struct winbindd_response *response)
+{
+       /* Initialise return value */
+
+       response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+void close_sock(void)
+{
+       if (winbindd_fd != -1) {
+               close(winbindd_fd);
+               winbindd_fd = -1;
+       }
+}
+
+/* Make sure socket handle isn't stdin, stdout or stderr */
+#define RECURSION_LIMIT 3
+
+static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */) 
+{
+       int new_fd;
+       if (fd >= 0 && fd <= 2) {
+#ifdef F_DUPFD 
+               if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
+                       return -1;
+               }
+               /* Parinoia */
+               if (new_fd < 3) {
+                       close(new_fd);
+                       return -1;
+               }
+               close(fd);
+               return new_fd;
+#else
+               if (limit <= 0)
+                       return -1;
+               
+               new_fd = dup(fd);
+               if (new_fd == -1) 
+                       return -1;
+
+               /* use the program stack to hold our list of FDs to close */
+               new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
+               close(fd);
+               return new_fd;
+#endif
+       }
+       return fd;
+}
+
+static int make_safe_fd(int fd) 
+{
+       int result, flags;
+       int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
+       if (new_fd == -1) {
+               close(fd);
+               return -1;
+       }
+       /* Socket should be closed on exec() */
+       
+#ifdef FD_CLOEXEC
+       result = flags = fcntl(new_fd, F_GETFD, 0);
+       if (flags >= 0) {
+               flags |= FD_CLOEXEC;
+               result = fcntl( new_fd, F_SETFD, flags );
+       }
+       if (result < 0) {
+               close(new_fd);
+               return -1;
+       }
+#endif
+       return new_fd;
+}
+
+/* Connect to winbindd socket */
+
+int winbind_open_pipe_sock(void)
+{
+#ifdef HAVE_UNIXSOCKET
+       struct sockaddr_un sunaddr;
+       static pid_t our_pid;
+       struct stat st;
+       pstring path;
+       int fd;
+       
+       if (our_pid != getpid()) {
+               close_sock();
+               our_pid = getpid();
+       }
+       
+       if (winbindd_fd != -1) {
+               return winbindd_fd;
+       }
+       
+       /* Check permissions on unix socket directory */
+       
+       if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
+               return -1;
+       }
+       
+       if (!S_ISDIR(st.st_mode) || 
+           (st.st_uid != 0 && st.st_uid != geteuid())) {
+               return -1;
+       }
+       
+       /* Connect to socket */
+       
+       strncpy(path, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
+       path[sizeof(path) - 1] = '\0';
+       
+       strncat(path, "/", sizeof(path) - 1);
+       path[sizeof(path) - 1] = '\0';
+       
+       strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
+       path[sizeof(path) - 1] = '\0';
+       
+       ZERO_STRUCT(sunaddr);
+       sunaddr.sun_family = AF_UNIX;
+       strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
+       
+       /* If socket file doesn't exist, don't bother trying to connect
+          with retry.  This is an attempt to make the system usable when
+          the winbindd daemon is not running. */
+
+       if (lstat(path, &st) == -1) {
+               return -1;
+       }
+       
+       /* Check permissions on unix socket file */
+       
+       if (!S_ISSOCK(st.st_mode) || 
+           (st.st_uid != 0 && st.st_uid != geteuid())) {
+               return -1;
+       }
+       
+       /* Connect to socket */
+       
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+               return -1;
+       }
+
+       if ((winbindd_fd = make_safe_fd( fd)) == -1) {
+               return winbindd_fd;
+       }
+       
+       if (connect(winbindd_fd, (struct sockaddr *)&sunaddr, 
+                   sizeof(sunaddr)) == -1) {
+               close_sock();
+               return -1;
+       }
+        
+       return winbindd_fd;
+#else
+       return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+int write_sock(void *buffer, int count)
+{
+       int result, nwritten;
+       
+       /* Open connection to winbind daemon */
+       
+ restart:
+       
+       if (winbind_open_pipe_sock() == -1) {
+               return -1;
+       }
+       
+       /* Write data to socket */
+       
+       nwritten = 0;
+       
+       while(nwritten < count) {
+               struct timeval tv;
+               fd_set r_fds;
+               
+               /* Catch pipe close on other end by checking if a read()
+                  call would not block by calling select(). */
+
+               FD_ZERO(&r_fds);
+               FD_SET(winbindd_fd, &r_fds);
+               ZERO_STRUCT(tv);
+               
+               if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
+                       close_sock();
+                       return -1;                   /* Select error */
+               }
+               
+               /* Write should be OK if fd not available for reading */
+               
+               if (!FD_ISSET(winbindd_fd, &r_fds)) {
+                       
+                       /* Do the write */
+                       
+                       result = write(winbindd_fd,
+                                      (char *)buffer + nwritten, 
+                                      count - nwritten);
+                       
+                       if ((result == -1) || (result == 0)) {
+                               
+                               /* Write failed */
+                               
+                               close_sock();
+                               return -1;
+                       }
+                       
+                       nwritten += result;
+                       
+               } else {
+                       
+                       /* Pipe has closed on remote end */
+                       
+                       close_sock();
+                       goto restart;
+               }
+       }
+       
+       return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+static int read_sock(void *buffer, int count)
+{
+       int result = 0, nread = 0;
+
+       /* Read data from socket */
+       
+       while(nread < count) {
+               
+               result = read(winbindd_fd, (char *)buffer + nread, 
+                             count - nread);
+               
+               if ((result == -1) || (result == 0)) {
+                       
+                       /* Read failed.  I think the only useful thing we
+                          can do here is just return -1 and fail since the
+                          transaction has failed half way through. */
+                       
+                       close_sock();
+                       return -1;
+               }
+               
+               nread += result;
+       }
+       
+       return result;
+}
+
+/* Read reply */
+
+int read_reply(struct winbindd_response *response)
+{
+       int result1, result2 = 0;
+
+       if (!response) {
+               return -1;
+       }
+       
+       /* Read fixed length response */
+       
+       if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
+           == -1) {
+               
+               return -1;
+       }
+       
+       /* We actually send the pointer value of the extra_data field from
+          the server.  This has no meaning in the client's address space
+          so we clear it out. */
+
+       response->extra_data = NULL;
+
+       /* Read variable length response */
+       
+       if (response->length > sizeof(struct winbindd_response)) {
+               int extra_data_len = response->length - 
+                       sizeof(struct winbindd_response);
+               
+               /* Mallocate memory for extra data */
+               
+               if (!(response->extra_data = malloc(extra_data_len))) {
+                       return -1;
+               }
+               
+               if ((result2 = read_sock(response->extra_data, extra_data_len))
+                   == -1) {
+                       free_response(response);
+                       return -1;
+               }
+       }
+       
+       /* Return total amount of data read */
+       
+       return result1 + result2;
+}
+
+/* 
+ * send simple types of requests 
+ */
+
+NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
+{
+       struct winbindd_request lrequest;
+
+       /* Check for our tricky environment variable */
+
+       if (getenv(WINBINDD_DONT_ENV)) {
+               return NSS_STATUS_NOTFOUND;
+       }
+
+       if (!request) {
+               ZERO_STRUCT(lrequest);
+               request = &lrequest;
+       }
+       
+       /* Fill in request and send down pipe */
+
+       init_request(request, req_type);
+       
+       if (write_sock(request, sizeof(*request)) == -1) {
+               return NSS_STATUS_UNAVAIL;
+       }
+       
+       return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Get results from winbindd request
+ */
+
+NSS_STATUS winbindd_get_response(struct winbindd_response *response)
+{
+       struct winbindd_response lresponse;
+
+       if (!response) {
+               ZERO_STRUCT(lresponse);
+               response = &lresponse;
+       }
+
+       init_response(response);
+
+       /* Wait for reply */
+       if (read_reply(response) == -1) {
+               return NSS_STATUS_UNAVAIL;
+       }
+
+       /* Throw away extra data if client didn't request it */
+       if (response == &lresponse) {
+               free_response(response);
+       }
+
+       /* Copy reply data from socket */
+       if (response->result != WINBINDD_OK) {
+               return NSS_STATUS_NOTFOUND;
+       }
+       
+       return NSS_STATUS_SUCCESS;
+}
+
+/* Handle simple types of requests */
+
+NSS_STATUS winbindd_request(int req_type, 
+                           struct winbindd_request *request,
+                           struct winbindd_response *response)
+{
+       NSS_STATUS status;
+
+       status = winbindd_send_request(req_type, request);
+       if (status != NSS_STATUS_SUCCESS) 
+               return(status);
+       return winbindd_get_response(response);
+}
diff --git a/source4/nsswitch/wbinfo.c b/source4/nsswitch/wbinfo.c
new file mode 100644 (file)
index 0000000..68dc178
--- /dev/null
@@ -0,0 +1,891 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind status program.
+
+   Copyright (C) Tim Potter      2000-2002
+   Copyright (C) Andrew Bartlett 2002
+   
+   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 "winbindd.h"
+#include "debug.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern int winbindd_fd;
+
+static char winbind_separator(void)
+{
+       struct winbindd_response response;
+       static BOOL got_sep;
+       static char sep;
+
+       if (got_sep)
+               return sep;
+
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+           NSS_STATUS_SUCCESS) {
+               d_printf("could not obtain winbind separator!\n");
+               /* HACK: (this module should not call lp_ funtions) */
+               return *lp_winbind_separator();
+       }
+
+       sep = response.data.info.winbind_separator;
+       got_sep = True;
+
+       if (!sep) {
+               d_printf("winbind separator was NULL!\n");
+               /* HACK: (this module should not call lp_ funtions) */
+               sep = *lp_winbind_separator();
+       }
+       
+       return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+       struct winbindd_response response;
+       static fstring winbind_domain;
+
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+           NSS_STATUS_SUCCESS) {
+               d_printf("could not obtain winbind domain name!\n");
+               
+               /* HACK: (this module should not call lp_ funtions) */
+               return lp_workgroup();
+       }
+
+       fstrcpy(winbind_domain, response.data.domain_name);
+
+       return winbind_domain;
+
+}
+
+/* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
+   form DOMAIN/user into a domain and a user */
+
+static BOOL parse_wbinfo_domain_user(const char *domuser, fstring domain, 
+                                    fstring user)
+{
+
+       char *p = strchr(domuser,winbind_separator());
+
+       if (!p) {
+               fstrcpy(user, domuser);
+               fstrcpy(domain, get_winbind_domain());
+               return True;
+       }
+        
+       fstrcpy(user, p+1);
+       fstrcpy(domain, domuser);
+       domain[PTR_DIFF(p, domuser)] = 0;
+       strupper(domain);
+
+       return True;
+}
+
+/* List groups a user is a member of */
+
+static BOOL wbinfo_get_usergroups(char *user)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       NSS_STATUS result;
+       int i;
+       
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       fstrcpy(request.data.username, user);
+
+       result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+       if (result != NSS_STATUS_SUCCESS)
+               return False;
+
+       for (i = 0; i < response.data.num_entries; i++)
+               d_printf("%d\n", (int)((gid_t *)response.extra_data)[i]);
+
+       SAFE_FREE(response.extra_data);
+
+       return True;
+}
+
+/* Convert NetBIOS name to IP */
+
+static BOOL wbinfo_wins_byname(char *name)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       fstrcpy(request.data.winsreq, name);
+
+       if (winbindd_request(WINBINDD_WINS_BYNAME, &request, &response) !=
+           NSS_STATUS_SUCCESS) {
+               return False;
+       }
+
+       /* Display response */
+
+       printf("%s\n", response.data.winsresp);
+
+       return True;
+}
+
+/* Convert IP to NetBIOS name */
+
+static BOOL wbinfo_wins_byip(char *ip)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       fstrcpy(request.data.winsreq, ip);
+
+       if (winbindd_request(WINBINDD_WINS_BYIP, &request, &response) !=
+           NSS_STATUS_SUCCESS) {
+               return False;
+       }
+
+       /* Display response */
+
+       printf("%s\n", response.data.winsresp);
+
+       return True;
+}
+
+/* List trusted domains */
+
+static BOOL wbinfo_list_domains(void)
+{
+       struct winbindd_response response;
+       fstring name;
+
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       if (response.extra_data) {
+               const char *extra_data = (char *)response.extra_data;
+
+               while(next_token(&extra_data, name, ",", sizeof(fstring)))
+                       d_printf("%s\n", name);
+
+               SAFE_FREE(response.extra_data);
+       }
+
+       return True;
+}
+
+
+/* show sequence numbers */
+static BOOL wbinfo_show_sequence(void)
+{
+       struct winbindd_response response;
+
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       if (winbindd_request(WINBINDD_SHOW_SEQUENCE, NULL, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       if (response.extra_data) {
+               char *extra_data = (char *)response.extra_data;
+               d_printf("%s", extra_data);
+               SAFE_FREE(response.extra_data);
+       }
+
+       return True;
+}
+
+/* Check trust account password */
+
+static BOOL wbinfo_check_secret(void)
+{
+        struct winbindd_response response;
+        NSS_STATUS result;
+
+        ZERO_STRUCT(response);
+
+        result = winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response);
+               
+       d_printf("checking the trust secret via RPC calls %s\n", 
+                (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+       if (result != NSS_STATUS_SUCCESS)       
+               d_printf("error code was %s (0x%x)\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status);
+       
+       return result == NSS_STATUS_SUCCESS;    
+}
+
+/* Convert uid to sid */
+
+static BOOL wbinfo_uid_to_sid(uid_t uid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       request.data.uid = uid;
+
+       if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%s\n", response.data.sid.sid);
+
+       return True;
+}
+
+/* Convert gid to sid */
+
+static BOOL wbinfo_gid_to_sid(gid_t gid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       request.data.gid = gid;
+
+       if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%s\n", response.data.sid.sid);
+
+       return True;
+}
+
+/* Convert sid to uid */
+
+static BOOL wbinfo_sid_to_uid(char *sid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%d\n", (int)response.data.uid);
+
+       return True;
+}
+
+static BOOL wbinfo_sid_to_gid(char *sid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send request */
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%d\n", (int)response.data.gid);
+
+       return True;
+}
+
+/* Convert sid to string */
+
+static BOOL wbinfo_lookupsid(char *sid)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%s%c%s %d\n", response.data.name.dom_name, 
+                winbind_separator(), response.data.name.name, 
+                response.data.name.type);
+
+       return True;
+}
+
+/* Convert string to sid */
+
+static BOOL wbinfo_lookupname(char *name)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       parse_wbinfo_domain_user(name, request.data.name.dom_name, 
+                                request.data.name.name);
+
+       if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Display response */
+
+       d_printf("%s %d\n", response.data.sid.sid, response.data.sid.type);
+
+       return True;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL wbinfo_auth(char *username)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+        char *p;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+        p = strchr(username, '%');
+
+        if (p) {
+                *p = 0;
+                fstrcpy(request.data.auth.user, username);
+                fstrcpy(request.data.auth.pass, p + 1);
+                *p = '%';
+        } else
+                fstrcpy(request.data.auth.user, username);
+
+       result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+       /* Display response */
+
+        d_printf("plaintext password authentication %s\n", 
+               (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+       if (response.data.auth.nt_status)
+               d_printf("error code was %s (0x%x)\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status);
+
+        return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL wbinfo_auth_crap(char *username)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+        fstring name_user;
+        fstring name_domain;
+        fstring pass;
+        char *p;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+        p = strchr(username, '%');
+
+        if (p) {
+                *p = 0;
+                fstrcpy(pass, p + 1);
+       }
+               
+       parse_wbinfo_domain_user(username, name_domain, name_user);
+
+       fstrcpy(request.data.auth_crap.user, name_user);
+
+       fstrcpy(request.data.auth_crap.domain, name_domain);
+
+       generate_random_buffer(request.data.auth_crap.chal, 8, False);
+        
+        SMBencrypt(pass, request.data.auth_crap.chal, 
+                   (uchar *)request.data.auth_crap.lm_resp);
+        SMBNTencrypt(pass, request.data.auth_crap.chal,
+                     (uchar *)request.data.auth_crap.nt_resp);
+
+        request.data.auth_crap.lm_resp_len = 24;
+        request.data.auth_crap.nt_resp_len = 24;
+
+       result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+       /* Display response */
+
+        d_printf("challenge/response password authentication %s\n", 
+               (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+       if (response.data.auth.nt_status)
+               d_printf("error code was %s (0x%x)\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status);
+
+        return result == NSS_STATUS_SUCCESS;
+}
+
+/* Print domain users */
+
+static BOOL print_domain_users(void)
+{
+       struct winbindd_response response;
+       const char *extra_data;
+       fstring name;
+
+       /* Send request to winbind daemon */
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Look through extra data */
+
+       if (!response.extra_data)
+               return False;
+
+       extra_data = (const char *)response.extra_data;
+
+       while(next_token(&extra_data, name, ",", sizeof(fstring)))
+               d_printf("%s\n", name);
+       
+       SAFE_FREE(response.extra_data);
+
+       return True;
+}
+
+/* Print domain groups */
+
+static BOOL print_domain_groups(void)
+{
+       struct winbindd_response response;
+       const char *extra_data;
+       fstring name;
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response) !=
+           NSS_STATUS_SUCCESS)
+               return False;
+
+       /* Look through extra data */
+
+       if (!response.extra_data)
+               return False;
+
+       extra_data = (const char *)response.extra_data;
+
+       while(next_token(&extra_data, name, ",", sizeof(fstring)))
+               d_printf("%s\n", name);
+
+       SAFE_FREE(response.extra_data);
+       
+       return True;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static BOOL wbinfo_set_auth_user(char *username)
+{
+       char *password;
+       fstring user, domain;
+
+       /* Separate into user and password */
+
+       parse_wbinfo_domain_user(username, domain, user);
+
+       password = strchr(user, '%');
+
+       if (password) {
+               *password = 0;
+               password++;
+       } else
+               password = "";
+
+       /* Store or remove DOMAIN\username%password in secrets.tdb */
+
+       secrets_init();
+
+       if (user[0]) {
+
+               if (!secrets_store(SECRETS_AUTH_USER, user,
+                                  strlen(user) + 1)) {
+                       d_fprintf(stderr, "error storing username\n");
+                       return False;
+               }
+
+               /* We always have a domain name added by the
+                  parse_wbinfo_domain_user() function. */
+
+               if (!secrets_store(SECRETS_AUTH_DOMAIN, domain,
+                                  strlen(domain) + 1)) {
+                       d_fprintf(stderr, "error storing domain name\n");
+                       return False;
+               }
+
+       } else {
+               secrets_delete(SECRETS_AUTH_USER);
+               secrets_delete(SECRETS_AUTH_DOMAIN);
+       }
+
+       if (password[0]) {
+
+               if (!secrets_store(SECRETS_AUTH_PASSWORD, password,
+                                  strlen(password) + 1)) {
+                       d_fprintf(stderr, "error storing password\n");
+                       return False;
+               }
+
+       } else
+               secrets_delete(SECRETS_AUTH_PASSWORD);
+
+       return True;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+       char *user, *domain, *password;
+
+       /* Lift data from secrets file */
+
+       secrets_init();
+
+       user = secrets_fetch(SECRETS_AUTH_USER, NULL);
+       domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+       password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+       if (!user && !domain && !password) {
+               d_printf("No authorised user configured\n");
+               return;
+       }
+
+       /* Pretty print authorised user info */
+
+       d_printf("%s%s%s%s%s\n", domain ? domain : "", domain ? "\\" : "",
+                user, password ? "%" : "", password ? password : "");
+
+       SAFE_FREE(user);
+       SAFE_FREE(domain);
+       SAFE_FREE(password);
+}
+
+static BOOL wbinfo_ping(void)
+{
+        NSS_STATUS result;
+
+       result = winbindd_request(WINBINDD_PING, NULL, NULL);
+
+       /* Display response */
+
+        d_printf("'ping' to winbindd %s on fd %d\n", 
+               (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", winbindd_fd);
+
+        return result == NSS_STATUS_SUCCESS;
+}
+
+/* Main program */
+
+enum {
+       OPT_SET_AUTH_USER = 1000,
+       OPT_GET_AUTH_USER,
+       OPT_SEQUENCE
+};
+
+int main(int argc, char **argv)
+{
+       int opt;
+
+       poptContext pc;
+       static char *string_arg;
+       static int int_arg;
+       BOOL got_command = False;
+       int result = 1;
+
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+
+               /* longName, shortName, argInfo, argPtr, value, descrip, 
+                  argDesc */
+
+               { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users"},
+               { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups" },
+               { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP (WINS)", "NETBIOS-NAME" },
+               { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name (WINS)", "IP" },
+               { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" },
+               { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" },
+               { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" },
+               { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
+               { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
+               { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
+               { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
+               { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+               { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "show sequence numbers of all domains" },
+               { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+               { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+               { "set-auth-user", 'A', POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" },
+               { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
+               { "ping", 'p', POPT_ARG_NONE, 0, 'p', "'ping' winbindd to see if it is alive" },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { 0, 0, 0, 0 }
+       };
+
+       /* Samba client initialisation */
+
+       if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+               d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+                       dyn_CONFIGFILE, strerror(errno));
+               exit(1);
+       }
+
+       if (!init_names())
+               return 1;
+
+       load_interfaces();
+
+       /* Parse options */
+
+       pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0);
+
+       /* Parse command line options */
+
+       if (argc == 1) {
+               poptPrintHelp(pc, stderr, 0);
+               return 1;
+       }
+
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               if (got_command) {
+                       d_fprintf(stderr, "No more than one command may be specified at once.\n");
+                       exit(1);
+               }
+               got_command = True;
+       }
+
+       poptFreeContext(pc);
+
+       pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'u':
+                       if (!print_domain_users()) {
+                               d_printf("Error looking up domain users\n");
+                               goto done;
+                       }
+                       break;
+               case 'g':
+                       if (!print_domain_groups()) {
+                               d_printf("Error looking up domain groups\n");
+                               goto done;
+                       }
+                       break;
+               case 's':
+                       if (!wbinfo_lookupsid(string_arg)) {
+                               d_printf("Could not lookup sid %s\n", string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'n':
+                       if (!wbinfo_lookupname(string_arg)) {
+                               d_printf("Could not lookup name %s\n", string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'N':
+                       if (!wbinfo_wins_byname(string_arg)) {
+                               d_printf("Could not lookup WINS by name %s\n", string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'I':
+                       if (!wbinfo_wins_byip(string_arg)) {
+                               d_printf("Could not lookup WINS by IP %s\n", string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'U':
+                       if (!wbinfo_uid_to_sid(int_arg)) {
+                               d_printf("Could not convert uid %d to sid\n", int_arg);
+                               goto done;
+                       }
+                       break;
+               case 'G':
+                       if (!wbinfo_gid_to_sid(int_arg)) {
+                               d_printf("Could not convert gid %d to sid\n",
+                                      int_arg);
+                               goto done;
+                       }
+                       break;
+               case 'S':
+                       if (!wbinfo_sid_to_uid(string_arg)) {
+                               d_printf("Could not convert sid %s to uid\n",
+                                      string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'Y':
+                       if (!wbinfo_sid_to_gid(string_arg)) {
+                               d_printf("Could not convert sid %s to gid\n",
+                                      string_arg);
+                               goto done;
+                       }
+                       break;
+               case 't':
+                       if (!wbinfo_check_secret()) {
+                               d_printf("Could not check secret\n");
+                               goto done;
+                       }
+                       break;
+               case 'm':
+                       if (!wbinfo_list_domains()) {
+                               d_printf("Could not list trusted domains\n");
+                               goto done;
+                       }
+                       break;
+               case OPT_SEQUENCE:
+                       if (!wbinfo_show_sequence()) {
+                               d_printf("Could not show sequence numbers\n");
+                               goto done;
+                       }
+                       break;
+               case 'r':
+                       if (!wbinfo_get_usergroups(string_arg)) {
+                               d_printf("Could not get groups for user %s\n", 
+                                      string_arg);
+                               goto done;
+                       }
+                       break;
+               case 'a': {
+                                         BOOL got_error = False;
+
+                                         if (!wbinfo_auth(string_arg)) {
+                                                 d_printf("Could not authenticate user %s with "
+                                                                  "plaintext password\n", string_arg);
+                                                 got_error = True;
+                                         }
+
+                                         if (!wbinfo_auth_crap(string_arg)) {
+                                                 d_printf("Could not authenticate user %s with "
+                                                                  "challenge/response\n", string_arg);
+                                                 got_error = True;
+                                         }
+
+                                         if (got_error)
+                                                 goto done;
+                                         break;
+                                 }
+               case 'p': {
+                                         if (!wbinfo_ping()) {
+                                                 d_printf("could not ping winbindd!\n");
+                                                 goto done;
+                                         }
+                                         break;
+                                 }
+               case OPT_SET_AUTH_USER:
+                       wbinfo_set_auth_user(string_arg);
+                       break;
+               case OPT_GET_AUTH_USER:
+                       wbinfo_get_auth_user();
+                       break;
+               default:
+                       d_fprintf(stderr, "Invalid option\n");
+                       poptPrintHelp(pc, stderr, 0);
+                       goto done;
+               }
+       }
+
+       result = 0;
+
+       /* Exit code */
+
+ done:
+       poptFreeContext(pc);
+       return result;
+}
diff --git a/source4/nsswitch/winbind_client.h b/source4/nsswitch/winbind_client.h
new file mode 100644 (file)
index 0000000..4de2d57
--- /dev/null
@@ -0,0 +1,16 @@
+#include "winbind_nss_config.h"
+#include "winbindd_nss.h"
+
+void init_request(struct winbindd_request *req,int rq_type);
+NSS_STATUS winbindd_send_request(int req_type,
+                                struct winbindd_request *request);
+NSS_STATUS winbindd_get_response(struct winbindd_response *response);
+NSS_STATUS winbindd_request(int req_type, 
+                           struct winbindd_request *request,
+                           struct winbindd_response *response);
+int winbind_open_pipe_sock(void);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+void close_sock(void);
+void free_response(struct winbindd_response *response);
+
diff --git a/source4/nsswitch/winbind_nss.c b/source4/nsswitch/winbind_nss.c
new file mode 100644 (file)
index 0000000..0b4c0ce
--- /dev/null
@@ -0,0 +1,1341 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Windows NT Domain nsswitch module
+
+   Copyright (C) Tim Potter 2000
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#include "winbind_client.h"
+
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+
+#ifdef HAVE_NS_API_H
+/* IRIX version */
+
+static int send_next_request(nsd_file_t *, struct winbindd_request *);
+static int do_list(int state, nsd_file_t *rq);
+
+static nsd_file_t *current_rq = NULL;
+static int current_winbind_xid = 0;
+static int next_winbind_xid = 0;
+
+typedef struct winbind_xid {
+       int                     xid;
+       nsd_file_t              *rq;
+       struct winbindd_request *request;
+       struct winbind_xid      *next;
+} winbind_xid_t;
+
+static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
+
+static int
+winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
+{
+       winbind_xid_t *new;
+
+       nsd_logprintf(NSD_LOG_LOW,
+               "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
+               xid, rq, request);
+       new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
+       if (!new) {
+               nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
+               return NSD_ERROR;
+       }
+
+       new->xid = xid;
+       new->rq = rq;
+       new->request = request;
+       new->next = winbind_xids;
+       winbind_xids = new;
+
+       return NSD_CONTINUE;
+}
+
+/*
+** This routine will look down the xid list and return the request
+** associated with an xid.  We remove the record if it is found.
+*/
+nsd_file_t *
+winbind_xid_lookup(int xid, struct winbindd_request **requestp)
+{
+        winbind_xid_t **last, *dx;
+        nsd_file_t *result=0;
+
+        for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
+            last = &dx->next, dx = dx->next);
+        if (dx) {
+                *last = dx->next;
+                result = dx->rq;
+               *requestp = dx->request;
+                SAFE_FREE(dx);
+        }
+       nsd_logprintf(NSD_LOG_LOW,
+               "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
+               xid, result, dx->request);
+
+        return result;
+}
+
+static int
+winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+       nsd_file_t *rq;
+       struct winbindd_request *request;
+
+       nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
+       rq = to->t_file;
+       *rqp = rq;
+       nsd_timeout_remove(rq);
+       request = to->t_clientdata;
+       return(send_next_request(rq, request));
+}
+
+static void
+dequeue_request()
+{
+       nsd_file_t *rq;
+       struct winbindd_request *request;
+
+       /*
+        * Check for queued requests
+        */
+       if (winbind_xids) {
+           nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
+                       current_winbind_xid);
+           rq = winbind_xid_lookup(current_winbind_xid++, &request);
+           /* cause a timeout on the queued request so we can send it */
+           nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
+       }
+}
+
+static int
+do_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+       if (winbind_xids == NULL) {
+               /*
+                * No outstanding requests.
+                * Send off the request to winbindd
+                */
+               nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
+               return(send_next_request(rq, request));
+       } else {
+               /*
+                * Just queue it up for now - previous callout or timout
+                * will start it up
+                */
+               nsd_logprintf(NSD_LOG_MIN,
+                       "lookup (winbind): queue request xid = %d\n",
+                       next_winbind_xid);
+               return(winbind_xid_new(next_winbind_xid++, rq, request));
+       }
+}
+
+static int 
+winbind_callback(nsd_file_t **rqp, int fd)
+{
+       struct winbindd_response response;
+       struct winbindd_pw *pw = &response.data.pw;
+       struct winbindd_gr *gr = &response.data.gr;
+       nsd_file_t *rq;
+       NSS_STATUS status;
+       fstring result;
+       char *members;
+       int i, maxlen;
+
+       dequeue_request();
+
+       nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
+
+       rq = current_rq;
+       *rqp = rq;
+
+       nsd_timeout_remove(rq);
+       nsd_callback_remove(fd);
+
+       ZERO_STRUCT(response);
+       status = winbindd_get_response(&response);
+
+       if (status != NSS_STATUS_SUCCESS) {
+               /* free any extra data area in response structure */
+               free_response(&response);
+               nsd_logprintf(NSD_LOG_MIN, 
+                       "callback (winbind) returning not found, status = %d\n",
+                       status);
+               rq->f_status = NS_NOTFOUND;
+               return NSD_NEXT;
+       }
+
+       maxlen = sizeof(result) - 1;
+
+       switch ((int)rq->f_cmd_data) {
+           case WINBINDD_WINS_BYNAME:
+           case WINBINDD_WINS_BYIP:
+               snprintf(result,maxlen,"%s\n",response.data.winsresp);
+               break;
+           case WINBINDD_GETPWUID:
+           case WINBINDD_GETPWNAM:
+               snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
+                       pw->pw_name,
+                       pw->pw_passwd,
+                       pw->pw_uid,
+                       pw->pw_gid,
+                       pw->pw_gecos,
+                       pw->pw_dir,
+                       pw->pw_shell);
+               break;
+           case WINBINDD_GETGRNAM:
+           case WINBINDD_GETGRGID:
+               if (gr->num_gr_mem && response.extra_data)
+                       members = response.extra_data;
+               else
+                       members = "";
+               snprintf(result,maxlen,"%s:%s:%d:%s\n",
+                       gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
+               break;
+           case WINBINDD_SETGRENT:
+           case WINBINDD_SETPWENT:
+               nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
+               free_response(&response);
+               return(do_list(1,rq));
+           case WINBINDD_GETGRENT:
+           case WINBINDD_GETGRLST:
+               nsd_logprintf(NSD_LOG_MIN, 
+                       "callback (winbind) - %d GETGRENT responses\n",
+                       response.data.num_entries);
+               if (response.data.num_entries) {
+                   gr = (struct winbindd_gr *)response.extra_data;
+                   if (! gr ) {
+                       nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
+                       free_response(&response);
+                       return NSD_ERROR;
+                   }
+                   members = (char *)response.extra_data + 
+                               (response.data.num_entries * sizeof(struct winbindd_gr));
+                   for (i = 0; i < response.data.num_entries; i++) {
+                       snprintf(result,maxlen,"%s:%s:%d:%s\n",
+                               gr->gr_name, gr->gr_passwd, gr->gr_gid, 
+                               &members[gr->gr_mem_ofs]);
+                       nsd_logprintf(NSD_LOG_MIN, "     GETGRENT %s\n",result);
+                       nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+                       gr++;
+                   }
+               }
+               i = response.data.num_entries;
+               free_response(&response);
+               if (i < MAX_GETPWENT_USERS)
+                   return(do_list(2,rq));
+               else
+                   return(do_list(1,rq));
+           case WINBINDD_GETPWENT:
+               nsd_logprintf(NSD_LOG_MIN, 
+                       "callback (winbind) - %d GETPWENT responses\n",
+                       response.data.num_entries);
+               if (response.data.num_entries) {
+                   pw = (struct winbindd_pw *)response.extra_data;
+                   if (! pw ) {
+                       nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
+                       free_response(&response);
+                       return NSD_ERROR;
+                   }
+                   for (i = 0; i < response.data.num_entries; i++) {
+                       snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
+                               pw->pw_name,
+                               pw->pw_passwd,
+                               pw->pw_uid,
+                               pw->pw_gid,
+                               pw->pw_gecos,
+                               pw->pw_dir,
+                               pw->pw_shell);
+                       nsd_logprintf(NSD_LOG_MIN, "     GETPWENT %s\n",result);
+                       nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+                       pw++;
+                   }
+               }
+               i = response.data.num_entries;
+               free_response(&response);
+               if (i < MAX_GETPWENT_USERS)
+                   return(do_list(2,rq));
+               else
+                   return(do_list(1,rq));
+           case WINBINDD_ENDGRENT:
+           case WINBINDD_ENDPWENT:
+               nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
+               nsd_append_element(rq,NS_SUCCESS,"\n",1);
+               free_response(&response);
+               return NSD_NEXT;
+           default:
+               free_response(&response);
+               nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
+               return NSD_NEXT;
+       }
+       nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
+       /* free any extra data area in response structure */
+       free_response(&response);
+       nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
+       return NSD_OK;
+}
+
+static int 
+winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+       nsd_file_t *rq;
+
+       dequeue_request();
+
+       nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
+
+       rq = to->t_file;
+       *rqp = rq;
+
+       /* Remove the callback and timeout */
+       nsd_callback_remove(winbindd_fd);
+       nsd_timeout_remove(rq);
+
+       rq->f_status = NS_NOTFOUND;
+       return NSD_NEXT;
+}
+
+static int
+send_next_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+       NSS_STATUS status;
+       long timeout;
+
+       timeout = 1000;
+
+       nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
+                       rq->f_cmd_data, timeout);
+       status = winbindd_send_request((int)rq->f_cmd_data,request);
+       SAFE_FREE(request);
+
+       if (status != NSS_STATUS_SUCCESS) {
+               nsd_logprintf(NSD_LOG_MIN, 
+                       "send_next_request (winbind) error status = %d\n",status);
+               rq->f_status = status;
+               return NSD_NEXT;
+       }
+
+       current_rq = rq;
+
+       /*
+        * Set up callback and timeouts
+        */
+       nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
+       nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
+       nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
+       return NSD_CONTINUE;
+}
+
+int init(void)
+{
+       nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
+       return(NSD_OK);
+}
+
+int lookup(nsd_file_t *rq)
+{
+       char *map;
+       char *key;
+       struct winbindd_request *request;
+
+       nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
+       if (! rq)
+               return NSD_ERROR;
+
+       map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+       key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+       if (! map || ! key) {
+               nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
+               rq->f_status = NS_BADREQ;
+               return NSD_ERROR;
+       }
+
+       nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
+
+       request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+       if (! request) {
+               nsd_logprintf(NSD_LOG_RESOURCE,
+                       "lookup (winbind): failed malloc\n");
+               return NSD_ERROR;
+       }
+
+       if (strcasecmp(map,"passwd.byuid") == 0) {
+           request->data.uid = atoi(key);
+           rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
+       } else if (strcasecmp(map,"passwd.byname") == 0) {
+           strncpy(request->data.username, key, 
+               sizeof(request->data.username) - 1);
+           request->data.username[sizeof(request->data.username) - 1] = '\0';
+           rq->f_cmd_data = (void *)WINBINDD_GETPWNAM; 
+       } else if (strcasecmp(map,"group.byname") == 0) {
+           strncpy(request->data.groupname, key, 
+               sizeof(request->data.groupname) - 1);
+           request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
+           rq->f_cmd_data = (void *)WINBINDD_GETGRNAM; 
+       } else if (strcasecmp(map,"group.bygid") == 0) {
+           request->data.gid = atoi(key);
+           rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
+       } else if (strcasecmp(map,"hosts.byname") == 0) {
+           strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+           request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+           rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
+       } else if (strcasecmp(map,"hosts.byaddr") == 0) {
+           strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+           request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+           rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
+       } else {
+               /*
+                * Don't understand this map - just return not found
+                */
+               nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
+               SAFE_FREE(request);
+               rq->f_status = NS_NOTFOUND;
+               return NSD_NEXT;
+       }
+
+       return(do_request(rq, request));
+}
+
+int list(nsd_file_t *rq)
+{
+       char *map;
+
+       nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
+       if (! rq)
+               return NSD_ERROR;
+
+       map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+       if (! map ) {
+               nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
+               rq->f_status = NS_BADREQ;
+               return NSD_ERROR;
+       }
+
+       nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
+
+       return (do_list(0,rq));
+}
+
+static int
+do_list(int state, nsd_file_t *rq)
+{
+       char *map;
+       struct winbindd_request *request;
+
+       nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
+
+       map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+       request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+       if (! request) {
+               nsd_logprintf(NSD_LOG_RESOURCE,
+                       "do_list (winbind): failed malloc\n");
+               return NSD_ERROR;
+       }
+
+       if (strcasecmp(map,"passwd.byname") == 0) {
+           switch (state) {
+               case 0:
+                   rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
+                   break;
+               case 1:
+                   request->data.num_entries = MAX_GETPWENT_USERS;
+                   rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
+                   break;
+               case 2:
+                   rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
+                   break;
+               default:
+                   nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+                   SAFE_FREE(request);
+                   rq->f_status = NS_NOTFOUND;
+                   return NSD_NEXT;
+           }
+       } else if (strcasecmp(map,"group.byname") == 0) {
+           switch (state) {
+               case 0:
+                   rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
+                   break;
+               case 1:
+                   request->data.num_entries = MAX_GETGRENT_USERS;
+                   rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
+                   break;
+               case 2:
+                   rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
+                   break;
+               default:
+                   nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+                   SAFE_FREE(request);
+                   rq->f_status = NS_NOTFOUND;
+                   return NSD_NEXT;
+           }
+       } else {
+               /*
+                * Don't understand this map - just return not found
+                */
+               nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
+               SAFE_FREE(request);
+               rq->f_status = NS_NOTFOUND;
+               return NSD_NEXT;
+       }
+
+       return(do_request(rq, request));
+}
+
+#else
+
+/* Allocate some space from the nss static buffer.  The buffer and buflen
+   are the pointers passed in by the C library to the _nss_ntdom_*
+   functions. */
+
+static char *get_static(char **buffer, int *buflen, int len)
+{
+       char *result;
+
+       /* Error check.  We return false if things aren't set up right, or
+          there isn't enough buffer space left. */
+       
+       if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+               return NULL;
+       }
+
+       /* Return an index into the static buffer */
+
+       result = *buffer;
+       *buffer += len;
+       *buflen -= len;
+
+       return result;
+}
+
+/* I've copied the strtok() replacement function next_token() from
+   lib/util_str.c as I really don't want to have to link in any other
+   objects if I can possibly avoid it. */
+
+BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
+{
+       char *s;
+       BOOL quoted;
+       size_t len=1;
+
+       if (!ptr) return(False);
+
+       s = *ptr;
+
+       /* default to simple separators */
+       if (!sep) sep = " \t\n\r";
+
+       /* find the first non sep char */
+       while (*s && strchr(sep,*s)) s++;
+       
+       /* nothing left? */
+       if (! *s) return(False);
+       
+       /* copy over the token */
+       for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
+               if (*s == '\"') {
+                       quoted = !quoted;
+               } else {
+                       len++;
+                       *buff++ = *s;
+               }
+       }
+       
+       *ptr = (*s) ? s+1 : s;  
+       *buff = 0;
+       
+       return(True);
+}
+
+
+/* Fill a pwent structure from a winbindd_response structure.  We use
+   the static data passed to us by libc to put strings and stuff in.
+   Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_pwent(struct passwd *result,
+                                 struct winbindd_pw *pw,
+                                 char **buffer, size_t *buflen)
+{
+       /* User name */
+
+       if ((result->pw_name = 
+            get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->pw_name, pw->pw_name);
+
+       /* Password */
+
+       if ((result->pw_passwd = 
+            get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->pw_passwd, pw->pw_passwd);
+        
+       /* [ug]id */
+
+       result->pw_uid = pw->pw_uid;
+       result->pw_gid = pw->pw_gid;
+
+       /* GECOS */
+
+       if ((result->pw_gecos = 
+            get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->pw_gecos, pw->pw_gecos);
+       
+       /* Home directory */
+       
+       if ((result->pw_dir = 
+            get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->pw_dir, pw->pw_dir);
+
+       /* Logon shell */
+       
+       if ((result->pw_shell = 
+            get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
+               
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->pw_shell, pw->pw_shell);
+
+       /* The struct passwd for Solaris has some extra fields which must
+          be initialised or nscd crashes. */
+
+#if HAVE_PASSWD_PW_COMMENT
+       result->pw_comment = "";
+#endif
+
+#if HAVE_PASSWD_PW_AGE
+       result->pw_age = "";
+#endif
+
+       return NSS_STATUS_SUCCESS;
+}
+
+/* Fill a grent structure from a winbindd_response structure.  We use
+   the static data passed to us by libc to put strings and stuff in.
+   Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
+                     char *gr_mem, char **buffer, size_t *buflen)
+{
+       fstring name;
+       int i;
+       char *tst;
+
+       /* Group name */
+
+       if ((result->gr_name =
+            get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->gr_name, gr->gr_name);
+
+       /* Password */
+
+       if ((result->gr_passwd =
+            get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
+
+               /* Out of memory */
+               
+               return NSS_STATUS_TRYAGAIN;
+       }
+
+       strcpy(result->gr_passwd, gr->gr_passwd);
+
+       /* gid */
+
+       result->gr_gid = gr->gr_gid;
+
+       /* Group membership */
+
+       if ((gr->num_gr_mem < 0) || !gr_mem) {
+               gr->num_gr_mem = 0;
+       }
+
+       /* this next value is a pointer to a pointer so let's align it */
+
+       /* Calculate number of extra bytes needed to align on pointer size boundry */
+       if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
+               i = sizeof(char*) - i;
+       
+       if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) * 
+                                sizeof(char *)+i))) == NULL) {
+
+               /* Out of memory */
+
+               return NSS_STATUS_TRYAGAIN;
+       }
+       result->gr_mem = (char **)(tst + i);
+
+       if (gr->num_gr_mem == 0) {
+
+               /* Group is empty */
+
+               *(result->gr_mem) = NULL;
+               return NSS_STATUS_SUCCESS;
+       }
+
+       /* Start looking at extra data */
+
+       i = 0;
+
+       while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
+        
+               /* Allocate space for member */
+        
+               if (((result->gr_mem)[i] = 
+                    get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+            
+                       /* Out of memory */
+            
+                       return NSS_STATUS_TRYAGAIN;
+               }        
+        
+               strcpy((result->gr_mem)[i], name);
+               i++;
+       }
+
+       /* Terminate list */
+
+       (result->gr_mem)[i] = NULL;
+
+       return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * NSS user functions
+ */
+
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache;                 /* Current index into pwd cache */
+static int num_pw_cache;                 /* Current size of pwd cache */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+NSS_STATUS
+_nss_winbind_setpwent(void)
+{
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+       if (num_pw_cache > 0) {
+               ndx_pw_cache = num_pw_cache = 0;
+               free_response(&getpwent_response);
+       }
+
+       return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
+}
+
+/* Close ntdom password database "file pointer" */
+
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+       if (num_pw_cache > 0) {
+               ndx_pw_cache = num_pw_cache = 0;
+               free_response(&getpwent_response);
+       }
+
+       return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+NSS_STATUS
+_nss_winbind_getpwent_r(struct passwd *result, char *buffer, 
+                       size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       struct winbindd_request request;
+       static int called_again;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+       /* Return an entry from the cache if we have one, or if we are
+          called again because we exceeded our static buffer.  */
+
+       if ((ndx_pw_cache < num_pw_cache) || called_again) {
+               goto return_result;
+       }
+
+       /* Else call winbindd to get a bunch of entries */
+       
+       if (num_pw_cache > 0) {
+               free_response(&getpwent_response);
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(getpwent_response);
+
+       request.data.num_entries = MAX_GETPWENT_USERS;
+
+       ret = winbindd_request(WINBINDD_GETPWENT, &request, 
+                              &getpwent_response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               struct winbindd_pw *pw_cache;
+
+               /* Fill cache */
+
+               ndx_pw_cache = 0;
+               num_pw_cache = getpwent_response.data.num_entries;
+
+               /* Return a result */
+
+       return_result:
+
+               pw_cache = getpwent_response.extra_data;
+
+               /* Check data is valid */
+
+               if (pw_cache == NULL) {
+                       return NSS_STATUS_NOTFOUND;
+               }
+
+               ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+                                &buffer, &buflen);
+               
+               /* Out of memory - try again */
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       called_again = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               *errnop = errno = 0;
+               called_again = False;
+               ndx_pw_cache++;
+
+               /* If we've finished with this lot of results free cache */
+
+               if (ndx_pw_cache == num_pw_cache) {
+                       ndx_pw_cache = num_pw_cache = 0;
+                       free_response(&getpwent_response);
+               }
+       }
+
+       return ret;
+}
+
+/* Return passwd struct from uid */
+
+NSS_STATUS
+_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+                       size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       static struct winbindd_response response;
+       struct winbindd_request request;
+       static int keep_response=0;
+
+       /* If our static buffer needs to be expanded we are called again */
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(response);
+               ZERO_STRUCT(request);
+
+               request.data.uid = uid;
+
+               ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_pwent(result, &response.data.pw, 
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = errno = 0;
+       }
+
+       free_response(&response);
+       return ret;
+}
+
+/* Return passwd struct from username */
+
+NSS_STATUS
+_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+                       size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       static struct winbindd_response response;
+       struct winbindd_request request;
+       static int keep_response;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
+#endif
+
+       /* If our static buffer needs to be expanded we are called again */
+
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(response);
+               ZERO_STRUCT(request);
+
+               strncpy(request.data.username, name, 
+                       sizeof(request.data.username) - 1);
+               request.data.username
+                       [sizeof(request.data.username) - 1] = '\0';
+
+               ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_pwent(result, &response.data.pw, &buffer,
+                                        &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = errno = 0;
+       }
+
+       free_response(&response);
+       return ret;
+}
+
+/*
+ * NSS group functions
+ */
+
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache;                 /* Current index into grp cache */
+static int num_gr_cache;                 /* Current size of grp cache */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+NSS_STATUS
+_nss_winbind_setgrent(void)
+{
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+       if (num_gr_cache > 0) {
+               ndx_gr_cache = num_gr_cache = 0;
+               free_response(&getgrent_response);
+       }
+
+       return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
+}
+
+/* Close "file pointer" for ntdom group database */
+
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+       if (num_gr_cache > 0) {
+               ndx_gr_cache = num_gr_cache = 0;
+               free_response(&getgrent_response);
+       }
+
+       return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
+
+/* Get next entry from ntdom group database */
+
+static NSS_STATUS
+winbind_getgrent(enum winbindd_cmd cmd,
+                struct group *result,
+                char *buffer, size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       static struct winbindd_request request;
+       static int called_again;
+       
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+       /* Return an entry from the cache if we have one, or if we are
+          called again because we exceeded our static buffer.  */
+
+       if ((ndx_gr_cache < num_gr_cache) || called_again) {
+               goto return_result;
+       }
+
+       /* Else call winbindd to get a bunch of entries */
+       
+       if (num_gr_cache > 0) {
+               free_response(&getgrent_response);
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(getgrent_response);
+
+       request.data.num_entries = MAX_GETGRENT_USERS;
+
+       ret = winbindd_request(cmd, &request, 
+                              &getgrent_response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               struct winbindd_gr *gr_cache;
+               int mem_ofs;
+
+               /* Fill cache */
+
+               ndx_gr_cache = 0;
+               num_gr_cache = getgrent_response.data.num_entries;
+
+               /* Return a result */
+
+       return_result:
+
+               gr_cache = getgrent_response.extra_data;
+
+               /* Check data is valid */
+
+               if (gr_cache == NULL) {
+                       return NSS_STATUS_NOTFOUND;
+               }
+
+               /* Fill group membership.  The offset into the extra data
+                  for the group membership is the reported offset plus the
+                  size of all the winbindd_gr records returned. */
+
+               mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+                       num_gr_cache * sizeof(struct winbindd_gr);
+
+               ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+                                ((char *)getgrent_response.extra_data)+mem_ofs,
+                                &buffer, &buflen);
+               
+               /* Out of memory - try again */
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       called_again = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               *errnop = 0;
+               called_again = False;
+               ndx_gr_cache++;
+
+               /* If we've finished with this lot of results free cache */
+
+               if (ndx_gr_cache == num_gr_cache) {
+                       ndx_gr_cache = num_gr_cache = 0;
+                       free_response(&getgrent_response);
+               }
+       }
+
+       return ret;
+}
+
+
+NSS_STATUS
+_nss_winbind_getgrent_r(struct group *result,
+                       char *buffer, size_t buflen, int *errnop)
+{
+       return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
+}
+
+NSS_STATUS
+_nss_winbind_getgrlst_r(struct group *result,
+                       char *buffer, size_t buflen, int *errnop)
+{
+       return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+NSS_STATUS
+_nss_winbind_getgrnam_r(const char *name,
+                       struct group *result, char *buffer,
+                       size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       static struct winbindd_response response;
+       struct winbindd_request request;
+       static int keep_response;
+       
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
+
+       /* If our static buffer needs to be expanded we are called again */
+       
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(request);
+               ZERO_STRUCT(response);
+
+               strncpy(request.data.groupname, name, 
+                       sizeof(request.data.groupname));
+               request.data.groupname
+                       [sizeof(request.data.groupname) - 1] = '\0';
+
+               ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_grent(result, &response.data.gr, 
+                                        response.extra_data,
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+               
+               /* We've been called again */
+               
+               ret = fill_grent(result, &response.data.gr, 
+                                response.extra_data, &buffer, &buflen);
+               
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = 0;
+       }
+
+       free_response(&response);
+       return ret;
+}
+
+/* Return group struct from gid */
+
+NSS_STATUS
+_nss_winbind_getgrgid_r(gid_t gid,
+                       struct group *result, char *buffer,
+                       size_t buflen, int *errnop)
+{
+       NSS_STATUS ret;
+       static struct winbindd_response response;
+       struct winbindd_request request;
+       static int keep_response;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+       /* If our static buffer needs to be expanded we are called again */
+
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(request);
+               ZERO_STRUCT(response);
+
+               request.data.gid = gid;
+
+               ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+
+                       ret = fill_grent(result, &response.data.gr, 
+                                        response.extra_data, 
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_grent(result, &response.data.gr, 
+                                response.extra_data, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = 0;
+       }
+
+       free_response(&response);
+       return ret;
+}
+
+/* Initialise supplementary groups */
+
+NSS_STATUS
+_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+                           long int *size, gid_t **groups, long int limit,
+                           int *errnop)
+{
+       NSS_STATUS ret;
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int i;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+               user, group);
+#endif
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       strncpy(request.data.username, user,
+               sizeof(request.data.username) - 1);
+
+       ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               int num_gids = response.data.num_entries;
+               gid_t *gid_list = (gid_t *)response.extra_data;
+
+               /* Copy group list to client */
+
+               for (i = 0; i < num_gids; i++) {
+
+                       /* Skip primary group */
+
+                       if (gid_list[i] == group) continue;
+
+                       /* Add to buffer */
+
+                       if (*start == *size && limit <= 0) {
+                               (*groups) = realloc(
+                                       (*groups), (2 * (*size) + 1) * sizeof(**groups));
+                               if (! *groups) goto done;
+                               *size = 2 * (*size) + 1;
+                       }
+
+                       if (*start == *size) goto done;
+
+                       (*groups)[*start] = gid_list[i];
+                       *start += 1;
+
+                       /* Filled buffer? */
+
+                       if (*start == limit) goto done;
+               }
+       }
+       
+       /* Back to your regularly scheduled programming */
+
+ done:
+       return ret;
+}
+
+#endif
diff --git a/source4/nsswitch/winbind_nss_config.h b/source4/nsswitch/winbind_nss_config.h
new file mode 100644 (file)
index 0000000..2faaa30
--- /dev/null
@@ -0,0 +1,165 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) Tim Potter 2000
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include "nsswitch/nss.h"
+
+/* Declarations for functions in winbind_nss.c
+   needed in winbind_nss_solaris.c (solaris wrapper to nss) */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd* result, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t, struct passwd*, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char* name, struct passwd* result,
+                                  char* buffer, size_t buflen, int* errnop);
+
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group* result, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name,
+                                  struct group *result, char *buffer,
+                                  size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid,
+                                  struct group *result, char *buffer,
+                                  size_t buflen, int *errnop);
+
+/* I'm trying really hard not to include anything from smb.h with the
+   result of some silly looking redeclaration of structures. */
+
+#ifndef _PSTRING
+#define _PSTRING
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+#endif
+
+#ifndef _BOOL
+#define _BOOL                  /* So we don't typedef BOOL again in vfs.h */
+#define False (0)
+#define True (1)
+#define Auto (2)
+typedef int BOOL;
+#endif
+
+#if !defined(uint32)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#endif
+#endif
+
+#if !defined(uint16)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+/* zero a structure */
+#ifndef ZERO_STRUCT
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#endif
+
+/* zero a structure given a pointer to the structure */
+#ifndef ZERO_STRUCTP
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+#endif
+
+/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK S_IFIFO
+#endif
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode)  ((mode & S_IFSOCK) == S_IFSOCK)
+#endif
+
+#endif
diff --git a/source4/nsswitch/winbind_nss_solaris.c b/source4/nsswitch/winbind_nss_solaris.c
new file mode 100644 (file)
index 0000000..f3bd05b
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+  Solaris NSS wrapper for winbind 
+  - Shirish Kalele 2000
+  
+  Based on Luke Howard's ldap_nss module for Solaris 
+  */
+
+/*
+  Copyright (C) 1997-2003 Luke Howard.
+  This file is part of the nss_ldap library.
+
+  The nss_ldap library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  The nss_ldap library 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
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with the nss_ldap library; see the file COPYING.LIB.  If not,
+  write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include "includes.h"
+#include <syslog.h>
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+#include "winbind_nss_config.h"
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX) 
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#define make_pwent_str(dest, src)                                      \
+{                                                                      \
+  if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL)       \
+    {                                                                  \
+      *errnop = ERANGE;                                                        \
+      NSS_DEBUG("ERANGE error");                                       \
+      return NSS_STATUS_TRYAGAIN;                                      \
+    }                                                                  \
+  strcpy(dest, src);                                                   \
+}
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+       NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+       return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+       NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+       return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+       NSS_STATUS ret;
+       char* buffer = NSS_ARGS(args)->buf.buffer;
+       int buflen = NSS_ARGS(args)->buf.buflen;
+       struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+       int* errnop = &NSS_ARGS(args)->erange;
+       char logmsg[80];
+
+       ret = _nss_winbind_getpwent_r(result, buffer, 
+                                     buflen, errnop);
+
+       if(ret == NSS_STATUS_SUCCESS)
+               {
+                       snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+                                result->pw_name);
+                       NSS_DEBUG(logmsg);
+                       NSS_ARGS(args)->returnval = (void*) result;
+               } else {
+                       snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+                       NSS_DEBUG(logmsg);
+               }
+    
+       return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+       NSS_STATUS ret;
+       struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+       NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+       ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+                                               result,
+                                               NSS_ARGS(args)->buf.buffer,
+                                               NSS_ARGS(args)->buf.buflen,
+                                               &NSS_ARGS(args)->erange);
+       if(ret == NSS_STATUS_SUCCESS)
+               NSS_ARGS(args)->returnval = (void*) result;
+  
+       return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+       NSS_STATUS ret;
+       struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+  
+       NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+       ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+                                      result,
+                                      NSS_ARGS(args)->buf.buffer,
+                                      NSS_ARGS(args)->buf.buflen,
+                                      &NSS_ARGS(args)->erange);
+       if(ret == NSS_STATUS_SUCCESS)
+               NSS_ARGS(args)->returnval = (void*) result;
+  
+       return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+       SAFE_FREE(be);
+       NSS_DEBUG("_nss_winbind_passwd_destr");
+       return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+       _nss_winbind_passwd_destr,
+       _nss_winbind_endpwent_solwrap,          /* NSS_DBOP_ENDENT */
+       _nss_winbind_setpwent_solwrap,          /* NSS_DBOP_SETENT */
+       _nss_winbind_getpwent_solwrap,          /* NSS_DBOP_GETENT */
+       _nss_winbind_getpwnam_solwrap,          /* NSS_DBOP_PASSWD_BYNAME */
+       _nss_winbind_getpwuid_solwrap           /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+                           const char* src_name,
+                           const char* cfg_args)
+{
+       nss_backend_t *be;
+  
+       if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+               return NULL;
+
+       be->ops = passwd_ops;
+       be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+       NSS_DEBUG("Initialized nss_winbind passwd backend");
+       return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+       NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+       return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+       NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+       return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+       NSS_STATUS ret;
+       char* buffer = NSS_ARGS(args)->buf.buffer;
+       int buflen = NSS_ARGS(args)->buf.buflen;
+       struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+       int* errnop = &NSS_ARGS(args)->erange;
+       char logmsg[80];
+
+       ret = _nss_winbind_getgrent_r(result, buffer, 
+                                     buflen, errnop);
+
+       if(ret == NSS_STATUS_SUCCESS)
+               {
+                       snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+                       NSS_DEBUG(logmsg);
+                       NSS_ARGS(args)->returnval = (void*) result;
+               } else {
+                       snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+                       NSS_DEBUG(logmsg);
+               }
+
+       return ret;
+       
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+       NSS_STATUS ret;
+       struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+       NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+       ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+                                     result,
+                                     NSS_ARGS(args)->buf.buffer,
+                                     NSS_ARGS(args)->buf.buflen,
+                                     &NSS_ARGS(args)->erange);
+
+       if(ret == NSS_STATUS_SUCCESS)
+               NSS_ARGS(args)->returnval = (void*) result;
+  
+       return ret;
+}
+  
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+       NSS_STATUS ret;
+       struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+       NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+       ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+                                      result,
+                                      NSS_ARGS(args)->buf.buffer,
+                                      NSS_ARGS(args)->buf.buflen,
+                                      &NSS_ARGS(args)->erange);
+
+       if(ret == NSS_STATUS_SUCCESS)
+               NSS_ARGS(args)->returnval = (void*) result;
+
+       return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+       NSS_DEBUG("_nss_winbind_getgroupsbymember");
+       return NSS_STATUS_NOTFOUND;
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+       SAFE_FREE(be);
+       NSS_DEBUG("_nss_winbind_group_destr");
+       return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] = 
+{
+       _nss_winbind_group_destr,
+       _nss_winbind_endgrent_solwrap,
+       _nss_winbind_setgrent_solwrap,
+       _nss_winbind_getgrent_solwrap,
+       _nss_winbind_getgrnam_solwrap,
+       _nss_winbind_getgrgid_solwrap,
+       _nss_winbind_getgroupsbymember_solwrap
+}; 
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+                          const char* src_name,
+                          const char* cfg_args)
+{
+       nss_backend_t* be;
+
+       if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+               return NULL;
+
+       be->ops = group_ops;
+       be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+  
+       NSS_DEBUG("Initialized nss_winbind group backend");
+       return be;
+}
+
+#endif /* SUN_NSS */
+
+
diff --git a/source4/nsswitch/winbindd.c b/source4/nsswitch/winbindd.c
new file mode 100644 (file)
index 0000000..ad37768
--- /dev/null
@@ -0,0 +1,951 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) by Tim Potter 2000-2002
+   Copyright (C) Andrew Tridgell 2002
+   
+   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 "winbindd.h"
+
+BOOL opt_nocache = False;
+BOOL opt_dual_daemon = False;
+
+/* Reload configuration */
+
+static BOOL reload_services_file(BOOL test)
+{
+       BOOL ret;
+       pstring logfile;
+
+       if (lp_loaded()) {
+               pstring fname;
+
+               pstrcpy(fname,lp_configfile());
+               if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+                       pstrcpy(dyn_CONFIGFILE,fname);
+                       test = False;
+               }
+       }
+
+       snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+       lp_set_logfile(logfile);
+
+       reopen_logs();
+       ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+       snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+       lp_set_logfile(logfile);
+
+       reopen_logs();
+       load_interfaces();
+
+       return(ret);
+}
+
+/*******************************************************************
+ Print out all talloc memory info.
+********************************************************************/
+
+void return_all_talloc_info(int msg_type, pid_t src_pid, void *buf, size_t len)
+{
+       TALLOC_CTX *ctx = talloc_init("info context");
+       char *info = NULL;
+
+       if (!ctx)
+               return;
+
+       info = talloc_describe_all(ctx);
+       if (info)
+               DEBUG(10,(info));
+       message_send_pid(src_pid, MSG_TALLOC_USAGE, info, info ? strlen(info) + 1: 0, True);
+       talloc_destroy(ctx);
+}
+
+#if DUMP_CORE
+
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+       char *p;
+       pstring dname;
+       pstrcpy( dname, lp_logfile() );
+       if ((p=strrchr(dname,'/')))
+               *p=0;
+       pstrcat( dname, "/corefiles" );
+       mkdir( dname, 0700 );
+       sys_chown( dname, getuid(), getgid() );
+       chmod( dname, 0700 );
+       if ( chdir(dname) )
+               return( False );
+       umask( ~(0700) );
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+       {
+               struct rlimit rlp;
+               getrlimit( RLIMIT_CORE, &rlp );
+               rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+               setrlimit( RLIMIT_CORE, &rlp );
+               getrlimit( RLIMIT_CORE, &rlp );
+               DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+       }
+#endif
+#endif
+       DEBUG(0,("Dumping core in %s\n",dname));
+       abort();
+       return( True );
+} /* dump_core */
+#endif
+
+/**************************************************************************** **
+ Handle a fault..
+ **************************************************************************** */
+
+static void fault_quit(void)
+{
+#if DUMP_CORE
+       dump_core();
+#endif
+}
+
+static void winbindd_status(void)
+{
+       struct winbindd_cli_state *tmp;
+
+       DEBUG(0, ("winbindd status:\n"));
+
+       /* Print client state information */
+       
+       DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients()));
+       
+       if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
+               DEBUG(2, ("\tclient list:\n"));
+               for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
+                       DEBUG(2, ("\t\tpid %d, sock %d, rbl %d, wbl %d\n",
+                                 tmp->pid, tmp->sock, tmp->read_buf_len, 
+                                 tmp->write_buf_len));
+               }
+       }
+}
+
+/* Print winbindd status to log file */
+
+static void print_winbindd_status(void)
+{
+       winbindd_status();
+       winbindd_idmap_status();
+       winbindd_cm_status();
+}
+
+/* Flush client cache */
+
+static void flush_caches(void)
+{
+       /* Clear cached user and group enumation info */        
+       wcache_flush_cache();
+}
+
+/* Handle the signal by unlinking socket and exiting */
+
+static void terminate(void)
+{
+       pstring path;
+
+       winbindd_idmap_close();
+       
+       /* Remove socket file */
+       snprintf(path, sizeof(path), "%s/%s", 
+                WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+       unlink(path);
+       exit(0);
+}
+
+static BOOL do_sigterm;
+
+static void termination_handler(int signum)
+{
+       do_sigterm = True;
+       sys_select_signal();
+}
+
+static BOOL do_sigusr2;
+
+static void sigusr2_handler(int signum)
+{
+       do_sigusr2 = True;
+       sys_select_signal();
+}
+
+static BOOL do_sighup;
+
+static void sighup_handler(int signum)
+{
+       do_sighup = True;
+       sys_select_signal();
+}
+
+struct dispatch_table {
+       enum winbindd_cmd cmd;
+       enum winbindd_result (*fn)(struct winbindd_cli_state *state);
+       const char *winbindd_cmd_name;
+};
+
+static struct dispatch_table dispatch_table[] = {
+       
+       /* User functions */
+
+       { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" },
+       { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" },
+
+       { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
+       { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
+       { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
+
+       { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
+
+       /* Group functions */
+
+       { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" },
+       { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" },
+       { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
+       { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
+       { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
+       { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" },
+
+       /* PAM auth functions */
+
+       { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
+       { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
+       { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+
+       /* Enumeration functions */
+
+       { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" },
+       { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" },
+       { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
+       { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" },
+
+       /* SID related functions */
+
+       { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" },
+       { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" },
+
+       /* Lookup related functions */
+
+       { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" },
+       { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
+       { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
+       { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
+
+       /* Miscellaneous */
+
+       { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
+       { WINBINDD_PING, winbindd_ping, "PING" },
+       { WINBINDD_INFO, winbindd_info, "INFO" },
+       { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
+       { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
+       { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+
+       /* WINS functions */
+
+       { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
+       { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
+
+       /* End of list */
+
+       { WINBINDD_NUM_CMDS, NULL, "NONE" }
+};
+
+static void process_request(struct winbindd_cli_state *state)
+{
+       struct dispatch_table *table = dispatch_table;
+
+       /* Free response data - we may be interrupted and receive another
+          command before being able to send this data off. */
+
+       SAFE_FREE(state->response.extra_data);  
+
+       ZERO_STRUCT(state->response);
+
+       state->response.result = WINBINDD_ERROR;
+       state->response.length = sizeof(struct winbindd_response);
+
+       /* Process command */
+
+       for (table = dispatch_table; table->fn; table++) {
+               if (state->request.cmd == table->cmd) {
+                       DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
+                       state->response.result = table->fn(state);
+                       break;
+               }
+       }
+
+       if (!table->fn)
+               DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd ));
+
+       /* In case extra data pointer is NULL */
+
+       if (!state->response.extra_data)
+               state->response.length = sizeof(struct winbindd_response);
+}
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int listen_sock)
+{
+       struct sockaddr_un sunaddr;
+       struct winbindd_cli_state *state;
+       socklen_t len;
+       int sock;
+       
+       /* Accept connection */
+       
+       len = sizeof(sunaddr);
+
+       do {
+               sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len);
+       } while (sock == -1 && errno == EINTR);
+
+       if (sock == -1)
+               return;
+       
+       DEBUG(6,("accepted socket %d\n", sock));
+       
+       /* Create new connection structure */
+       
+       if ((state = (struct winbindd_cli_state *) 
+             malloc(sizeof(*state))) == NULL)
+               return;
+       
+       ZERO_STRUCTP(state);
+       state->sock = sock;
+
+       state->last_access = time(NULL);        
+
+       /* Add to connection list */
+       
+       winbindd_add_client(state);
+}
+
+/* Remove a client connection from client connection list */
+
+static void remove_client(struct winbindd_cli_state *state)
+{
+       /* It's a dead client - hold a funeral */
+       
+       if (state != NULL) {
+               
+               /* Close socket */
+               
+               close(state->sock);
+               
+               /* Free any getent state */
+               
+               free_getent_state(state->getpwent_state);
+               free_getent_state(state->getgrent_state);
+               
+               /* We may have some extra data that was not freed if the
+                  client was killed unexpectedly */
+
+               SAFE_FREE(state->response.extra_data);
+               
+               /* Remove from list and free */
+               
+               winbindd_remove_client(state);
+               SAFE_FREE(state);
+       }
+}
+
+
+/* Shutdown client connection which has been idle for the longest time */
+
+static BOOL remove_idle_client(void)
+{
+       struct winbindd_cli_state *state, *remove_state = NULL;
+       time_t last_access = 0;
+       int nidle = 0;
+
+       for (state = winbindd_client_list(); state; state = state->next) {
+               if (state->read_buf_len == 0 && state->write_buf_len == 0 &&
+                               !state->getpwent_state && !state->getgrent_state) {
+                       nidle++;
+                       if (!last_access || state->last_access < last_access) {
+                               last_access = state->last_access;
+                               remove_state = state;
+                       }
+               }
+       }
+
+       if (remove_state) {
+               DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
+                       nidle, remove_state->sock, (unsigned int)remove_state->pid));
+               remove_client(remove_state);
+               return True;
+       }
+
+       return False;
+}
+
+/* Process a complete received packet from a client */
+
+void winbind_process_packet(struct winbindd_cli_state *state)
+{
+       /* Process request */
+       
+       /* Ensure null termination of entire request */
+       state->request.null_term = '\0';
+
+       state->pid = state->request.pid;
+       
+       process_request(state);
+
+       /* Update client state */
+       
+       state->read_buf_len = 0;
+       state->write_buf_len = sizeof(struct winbindd_response);
+
+       /* we might need to send it to the dual daemon */
+       if (opt_dual_daemon) {
+               dual_send_request(state);
+       }
+}
+
+/* Read some data from a client connection */
+
+void winbind_client_read(struct winbindd_cli_state *state)
+{
+       int n;
+    
+       /* Read data */
+
+       n = sys_read(state->sock, state->read_buf_len + 
+                (char *)&state->request, 
+                sizeof(state->request) - state->read_buf_len);
+       
+       DEBUG(10,("client_read: read %d bytes. Need %d more for a full request.\n", n, sizeof(state->request) - n - state->read_buf_len ));
+
+       /* Read failed, kill client */
+       
+       if (n == -1 || n == 0) {
+               DEBUG(5,("read failed on sock %d, pid %d: %s\n",
+                        state->sock, state->pid, 
+                        (n == -1) ? strerror(errno) : "EOF"));
+               
+               state->finished = True;
+               return;
+       }
+       
+       /* Update client state */
+       
+       state->read_buf_len += n;
+       state->last_access = time(NULL);
+}
+
+/* Write some data to a client connection */
+
+static void client_write(struct winbindd_cli_state *state)
+{
+       char *data;
+       int num_written;
+       
+       /* Write some data */
+       
+       if (!state->write_extra_data) {
+
+               /* Write response structure */
+               
+               data = (char *)&state->response + sizeof(state->response) - 
+                       state->write_buf_len;
+
+       } else {
+
+               /* Write extra data */
+               
+               data = (char *)state->response.extra_data + 
+                       state->response.length - 
+                       sizeof(struct winbindd_response) - 
+                       state->write_buf_len;
+       }
+       
+       num_written = sys_write(state->sock, data, state->write_buf_len);
+       
+       DEBUG(10,("client_write: wrote %d bytes.\n", num_written ));
+
+       /* Write failed, kill cilent */
+       
+       if (num_written == -1 || num_written == 0) {
+               
+               DEBUG(3,("write failed on sock %d, pid %d: %s\n",
+                        state->sock, state->pid, 
+                        (num_written == -1) ? strerror(errno) : "EOF"));
+               
+               state->finished = True;
+
+               SAFE_FREE(state->response.extra_data);
+
+               return;
+       }
+       
+       /* Update client state */
+       
+       state->write_buf_len -= num_written;
+       state->last_access = time(NULL);
+
+       /* Have we written all data? */
+       
+       if (state->write_buf_len == 0) {
+               
+               /* Take care of extra data */
+               
+               if (state->write_extra_data) {
+
+                       SAFE_FREE(state->response.extra_data);
+
+                       state->write_extra_data = False;
+
+                       DEBUG(10,("client_write: client_write: complete response written.\n"));
+
+               } else if (state->response.length > 
+                          sizeof(struct winbindd_response)) {
+                       
+                       /* Start writing extra data */
+
+                       state->write_buf_len = 
+                               state->response.length -
+                               sizeof(struct winbindd_response);
+                       
+                       DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len));
+
+                       state->write_extra_data = True;
+               }
+       }
+}
+
+/* Process incoming clients on listen_sock.  We use a tricky non-blocking,
+   non-forking, non-threaded model which allows us to handle many
+   simultaneous connections while remaining impervious to many denial of
+   service attacks. */
+
+static void process_loop(void)
+{
+       /* We'll be doing this a lot */
+
+       while (1) {
+               struct winbindd_cli_state *state;
+               fd_set r_fds, w_fds;
+               int maxfd, listen_sock, selret;
+               struct timeval timeout;
+
+               /* Handle messages */
+
+               message_dispatch();
+
+               /* rescan the trusted domains list. This must be done
+                  regularly to cope with transitive trusts */
+               rescan_trusted_domains(False);
+
+               /* Free up temporary memory */
+
+               lp_talloc_free();
+               main_loop_talloc_free();
+
+               /* Initialise fd lists for select() */
+
+               listen_sock = open_winbindd_socket();
+
+               if (listen_sock == -1) {
+                       perror("open_winbind_socket");
+                       exit(1);
+               }
+
+               maxfd = listen_sock;
+
+               FD_ZERO(&r_fds);
+               FD_ZERO(&w_fds);
+               FD_SET(listen_sock, &r_fds);
+
+               timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+               timeout.tv_usec = 0;
+
+               if (opt_dual_daemon) {
+                       maxfd = dual_select_setup(&w_fds, maxfd);
+               }
+
+               /* Set up client readers and writers */
+
+               state = winbindd_client_list();
+
+               while (state) {
+
+                       /* Dispose of client connection if it is marked as 
+                          finished */ 
+
+                       if (state->finished) {
+                               struct winbindd_cli_state *next = state->next;
+
+                               remove_client(state);
+                               state = next;
+                               continue;
+                       }
+
+                       /* Select requires we know the highest fd used */
+
+                       if (state->sock > maxfd)
+                               maxfd = state->sock;
+
+                       /* Add fd for reading */
+
+                       if (state->read_buf_len != sizeof(state->request))
+                               FD_SET(state->sock, &r_fds);
+
+                       /* Add fd for writing */
+
+                       if (state->write_buf_len)
+                               FD_SET(state->sock, &w_fds);
+
+                       state = state->next;
+               }
+
+               /* Call select */
+        
+               selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+               if (selret == 0)
+                       continue;
+
+               if ((selret == -1 && errno != EINTR) || selret == 0) {
+
+                       /* Select error, something is badly wrong */
+
+                       perror("select");
+                       exit(1);
+               }
+
+               /* Create a new connection if listen_sock readable */
+
+               if (selret > 0) {
+
+                       if (opt_dual_daemon) {
+                               dual_select(&w_fds);
+                       }
+
+                       if (FD_ISSET(listen_sock, &r_fds)) {
+                               while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+                                       DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+                                               WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+                                       if (!remove_idle_client()) {
+                                               DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+                                                       WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+                                               break;
+                                       }
+                               }
+                               new_connection(listen_sock);
+                       }
+            
+                       /* Process activity on client connections */
+            
+                       for (state = winbindd_client_list(); state; 
+                            state = state->next) {
+                
+                               /* Data available for reading */
+                
+                               if (FD_ISSET(state->sock, &r_fds)) {
+                    
+                                       /* Read data */
+                    
+                                       winbind_client_read(state);
+
+                                       /* 
+                                        * If we have the start of a
+                                        * packet, then check the
+                                        * length field to make sure
+                                        * the client's not talking
+                                        * Mock Swedish.
+                                        */
+
+                                       if (state->read_buf_len >= sizeof(uint32)
+                                           && *(uint32 *) &state->request != sizeof(state->request)) {
+                                               DEBUG(0,("process_loop: Invalid request size from pid %d: %d bytes sent, should be %d\n",
+                                                               state->request.pid, *(uint32 *) &state->request, sizeof(state->request)));
+
+                                               remove_client(state);
+                                               break;
+                                       }
+
+                                       /* A request packet might be 
+                                          complete */
+                    
+                                       if (state->read_buf_len == 
+                                           sizeof(state->request)) {
+                                               winbind_process_packet(state);
+                                       }
+                               }
+                
+                               /* Data available for writing */
+                
+                               if (FD_ISSET(state->sock, &w_fds))
+                                       client_write(state);
+                       }
+               }
+
+#if 0
+               winbindd_check_cache_size(time(NULL));
+#endif
+
+               /* Check signal handling things */
+
+               if (do_sigterm)
+                       terminate();
+
+               if (do_sighup) {
+
+                       DEBUG(3, ("got SIGHUP\n"));
+                        /* Flush various caches */
+
+                       flush_caches();
+                       reload_services_file(True);
+                       do_sighup = False;
+               }
+
+               if (do_sigusr2) {
+                       print_winbindd_status();
+                       do_sigusr2 = False;
+               }
+       }
+}
+
+
+/*
+  these are split out from the main winbindd for use by the background daemon
+ */
+BOOL winbind_setup_common(void)
+{
+       load_interfaces();
+
+       if (!secrets_init()) {
+
+               DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n"));
+               return False;
+       }
+
+       namecache_enable();     /* Enable netbios namecache */
+
+       /* Check winbindd parameters are valid */
+
+       ZERO_STRUCT(server_state);
+
+       if (!winbindd_param_init())
+               return False;
+
+       /* Winbind daemon initialisation */
+
+       if (!winbindd_idmap_init())
+               return False;
+
+       /* Unblock all signals we are interested in as they may have been
+          blocked by the parent process. */
+
+       BlockSignals(False, SIGINT);
+       BlockSignals(False, SIGQUIT);
+       BlockSignals(False, SIGTERM);
+       BlockSignals(False, SIGUSR1);
+       BlockSignals(False, SIGUSR2);
+       BlockSignals(False, SIGHUP);
+
+       /* Setup signal handlers */
+       
+       CatchSignal(SIGINT, termination_handler);      /* Exit on these sigs */
+       CatchSignal(SIGQUIT, termination_handler);
+       CatchSignal(SIGTERM, termination_handler);
+
+       CatchSignal(SIGPIPE, SIG_IGN);                 /* Ignore sigpipe */
+
+       CatchSignal(SIGUSR2, sigusr2_handler);         /* Debugging sigs */
+       CatchSignal(SIGHUP, sighup_handler);
+
+       return True;
+}
+
+
+/* Main function */
+
+struct winbindd_state server_state;   /* Server state information */
+
+
+static void usage(void)
+{
+       printf("Usage: winbindd [options]\n");
+        printf("\t-F                daemon in foreground mode\n");
+        printf("\t-S                log to stdout\n");
+       printf("\t-i                interactive mode\n");
+       printf("\t-B                dual daemon mode\n");
+       printf("\t-n                disable cacheing\n");
+       printf("\t-d level          set debug level\n");
+       printf("\t-s configfile     choose smb.conf location\n");
+       printf("\t-h                show this help message\n");
+}
+
+ int main(int argc, char **argv)
+{
+       extern BOOL AllowDebugChange;
+       pstring logfile;
+       BOOL interactive = False;
+       BOOL Fork = True;
+       BOOL log_stdout = False;
+       int opt;
+
+       /* glibc (?) likes to print "User defined signal 1" and exit if a
+          SIGUSR[12] is received before a handler is installed */
+
+       CatchSignal(SIGUSR1, SIG_IGN);
+       CatchSignal(SIGUSR2, SIG_IGN);
+
+       fault_setup((void (*)(void *))fault_quit );
+
+       snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+       lp_set_logfile(logfile);
+
+       /* Initialise for running in non-root mode */
+
+       sec_init();
+
+       /* Set environment variable so we don't recursively call ourselves.
+          This may also be useful interactively. */
+
+       setenv(WINBINDD_DONT_ENV, "1", 1);
+
+       /* Initialise samba/rpc client stuff */
+
+       while ((opt = getopt(argc, argv, "FSid:s:nhB")) != EOF) {
+               switch (opt) {
+
+               case 'F':
+                       Fork = False;
+                       break;
+               case 'S':
+                       log_stdout = True;
+                       break;
+                       /* Don't become a daemon */
+               case 'i':
+                       interactive = True;
+                       log_stdout = True;
+                       Fork = False;
+                       break;
+
+                       /* dual daemon system */
+               case 'B':
+                       opt_dual_daemon = True;
+                       break;
+
+                       /* disable cacheing */
+               case 'n':
+                       opt_nocache = True;
+                       break;
+
+                       /* Run with specified debug level */
+               case 'd':
+                       DEBUGLEVEL = atoi(optarg);
+                       AllowDebugChange = False;
+                       break;
+
+                       /* Load a different smb.conf file */
+               case 's':
+                       pstrcpy(dyn_CONFIGFILE,optarg);
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(0);
+
+               default:
+                       printf("Unknown option %c\n", (char)opt);
+                       exit(1);
+               }
+       }
+
+       if (log_stdout && Fork) {
+               printf("Can't log to stdout (-S) unless daemon is in foreground +(-F) or interactive (-i)\n");
+               usage();
+               exit(1);
+       }
+
+       snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+       lp_set_logfile(logfile);
+       setup_logging("winbindd", log_stdout);
+       reopen_logs();
+
+       DEBUG(1, ("winbindd version %s started.\n", VERSION ) );
+       DEBUGADD( 1, ( "Copyright The Samba Team 2000-2001\n" ) );
+
+       if (!reload_services_file(False)) {
+               DEBUG(0, ("error opening config file\n"));
+               exit(1);
+       }
+
+       /* Setup names. */
+
+       if (!init_names())
+               exit(1);
+
+       if (!interactive) {
+               become_daemon(Fork);
+               pidfile_create("winbindd");
+       }
+
+
+#if HAVE_SETPGID
+       /*
+        * If we're interactive we want to set our own process group for
+        * signal management.
+        */
+       if (interactive)
+               setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+       if (!winbind_setup_common()) {
+               return 1;
+       }
+
+       if (opt_dual_daemon) {
+               do_dual_daemon();
+       }
+
+       /* Initialise messaging system */
+
+       if (!message_init()) {
+               DEBUG(0, ("unable to initialise messaging system\n"));
+               exit(1);
+       }
+
+       register_msg_pool_usage();
+       message_register(MSG_REQ_TALLOC_USAGE, return_all_talloc_info);
+
+       /* Loop waiting for requests */
+
+       process_loop();
+
+       trustdom_cache_shutdown();
+       uni_group_cache_shutdown();
+       return 0;
+}
diff --git a/source4/nsswitch/winbindd.h b/source4/nsswitch/winbindd.h
new file mode 100644 (file)
index 0000000..42ef209
--- /dev/null
@@ -0,0 +1,230 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Anthony Liguori 2003
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#ifndef _WINBINDD_H
+#define _WINBINDD_H
+
+#include "includes.h"
+#include "nterr.h"
+
+#include "winbindd_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Client state structure */
+
+struct winbindd_cli_state {
+       struct winbindd_cli_state *prev, *next;   /* Linked list pointers */
+       int sock;                                 /* Open socket from client */
+       pid_t pid;                                /* pid of client */
+       int read_buf_len, write_buf_len;          /* Indexes in request/response */
+       BOOL finished;                            /* Can delete from list */
+       BOOL write_extra_data;                    /* Write extra_data field */
+       time_t last_access;                       /* Time of last access (read or write) */
+       struct winbindd_request request;          /* Request from client */
+       struct winbindd_response response;        /* Respose to client */
+       struct getent_state *getpwent_state;      /* State for getpwent() */
+       struct getent_state *getgrent_state;      /* State for getgrent() */
+};
+
+/* State between get{pw,gr}ent() calls */
+
+struct getent_state {
+       struct getent_state *prev, *next;
+       void *sam_entries;
+       uint32 sam_entry_index, num_sam_entries;
+       BOOL got_sam_entries;
+       fstring domain_name;
+};
+
+/* Storage for cached getpwent() user entries */
+
+struct getpwent_user {
+       fstring name;                        /* Account name */
+       fstring gecos;                       /* User information */
+       DOM_SID user_sid;                    /* NT user and primary group SIDs */
+       DOM_SID group_sid;
+};
+
+/* Server state structure */
+
+struct winbindd_state {
+
+       /* User and group id pool */
+
+       uid_t uid_low, uid_high;               /* Range of uids to allocate */
+       gid_t gid_low, gid_high;               /* Range of gids to allocate */
+};
+
+extern struct winbindd_state server_state;  /* Server information */
+
+typedef struct {
+       char *acct_name;
+       char *full_name;
+       DOM_SID *user_sid;                    /* NT user and primary group SIDs */
+       DOM_SID *group_sid;
+} WINBIND_USERINFO;
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+       fstring name;                          /* Domain name */        
+       fstring alt_name;                      /* alt Domain name (if any) */
+       DOM_SID sid;                           /* SID for this domain */
+       BOOL native_mode;                      /* is this a win2k domain in native mode ? */
+
+       /* Lookup methods for this domain (LDAP or RPC) */
+
+       struct winbindd_methods *methods;
+
+        /* Private data for the backends (used for connection cache) */
+
+       void *private; 
+
+       /* Sequence number stuff */
+
+       time_t last_seq_check;
+       uint32 sequence_number;
+
+       /* Linked list info */
+
+       struct winbindd_domain *prev, *next;
+};
+
+/* per-domain methods. This is how LDAP vs RPC is selected
+ */
+struct winbindd_methods {
+       /* does this backend provide a consistent view of the data? (ie. is the primary group
+          always correct) */
+       BOOL consistent;
+
+       /* get a list of users, returning a WINBIND_USERINFO for each one */
+       NTSTATUS (*query_user_list)(struct winbindd_domain *domain,
+                                  TALLOC_CTX *mem_ctx,
+                                  uint32 *num_entries, 
+                                  WINBIND_USERINFO **info);
+
+       /* get a list of domain groups */
+       NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32 *num_entries, 
+                                   struct acct_info **info);
+
+       /* get a list of domain local groups */
+       NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32 *num_entries, 
+                                   struct acct_info **info);
+                                   
+       /* convert one user or group name to a sid */
+       NTSTATUS (*name_to_sid)(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               const char *name,
+                               DOM_SID *sid,
+                               enum SID_NAME_USE *type);
+
+       /* convert a sid to a user or group name */
+       NTSTATUS (*sid_to_name)(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               DOM_SID *sid,
+                               char **name,
+                               enum SID_NAME_USE *type);
+
+       /* lookup user info for a given SID */
+       NTSTATUS (*query_user)(struct winbindd_domain *domain, 
+                              TALLOC_CTX *mem_ctx, 
+                              DOM_SID *user_sid,
+                              WINBIND_USERINFO *user_info);
+
+       /* lookup all groups that a user is a member of. The backend
+          can also choose to lookup by username or rid for this
+          function */
+       NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+                                     TALLOC_CTX *mem_ctx,
+                                     DOM_SID *user_sid,
+                                     uint32 *num_groups, DOM_SID ***user_gids);
+
+       /* find all members of the group with the specified group_rid */
+       NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain,
+                                   TALLOC_CTX *mem_ctx,
+                                   DOM_SID *group_sid,
+                                   uint32 *num_names, 
+                                   DOM_SID ***sid_mem, char ***names, 
+                                   uint32 **name_types);
+
+       /* return the current global sequence number */
+       NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq);
+
+       /* enumerate trusted domains */
+       NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32 *num_domains,
+                                   char ***names,
+                                   char ***alt_names,
+                                   DOM_SID **dom_sids);
+
+       /* find the domain sid */
+       NTSTATUS (*domain_sid)(struct winbindd_domain *domain,
+                              DOM_SID *sid);
+
+       /* setup the list of alternate names for the domain, if any */
+       NTSTATUS (*alternate_name)(struct winbindd_domain *domain);
+};
+
+/* Used to glue a policy handle and cli_state together */
+
+typedef struct {
+       struct cli_state *cli;
+       POLICY_HND pol;
+} CLI_POLICY_HND;
+
+/* Filled out by IDMAP backends */
+struct idmap_methods {
+  /* Called when backend is first loaded */
+  BOOL (*init)(void);
+
+  BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid);
+  BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid);
+
+  BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid);
+  BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid);
+
+  /* Called when backend is unloaded */
+  BOOL (*close)(void);
+  /* Called to dump backend status */
+  void (*status)(void);
+};
+
+#include "winbindd_proto.h"
+
+#include "rpc_parse.h"
+#include "rpc_client.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define WINBINDD_RESCAN_FREQ 300
+
+#define DOM_SEQUENCE_NONE ((uint32)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source4/nsswitch/winbindd_ads.c b/source4/nsswitch/winbindd_ads.c
new file mode 100644 (file)
index 0000000..de3757a
--- /dev/null
@@ -0,0 +1,837 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind ADS backend functions
+
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+   
+   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 "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* the realm of our primary LDAP server */
+static char *primary_realm;
+
+
+/*
+  return our ads connections structure for a domain. We keep the connection
+  open to make things faster
+*/
+static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+
+       if (domain->private) {
+               return (ADS_STRUCT *)domain->private;
+       }
+
+       /* we don't want this to affect the users ccache */
+       setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
+
+       ads = ads_init(domain->alt_name, domain->name, NULL);
+       if (!ads) {
+               DEBUG(1,("ads_init for domain %s failed\n", domain->name));
+               return NULL;
+       }
+
+       /* the machine acct password might have change - fetch it every time */
+       SAFE_FREE(ads->auth.password);
+       ads->auth.password = secrets_fetch_machine_password();
+
+       if (primary_realm) {
+               SAFE_FREE(ads->auth.realm);
+               ads->auth.realm = strdup(primary_realm);
+       }
+
+       status = ads_connect(ads);
+       if (!ADS_ERR_OK(status) || !ads->config.realm) {
+               extern struct winbindd_methods msrpc_methods;
+               DEBUG(1,("ads_connect for domain %s failed: %s\n", 
+                        domain->name, ads_errstr(status)));
+               ads_destroy(&ads);
+
+               /* if we get ECONNREFUSED then it might be a NT4
+                   server, fall back to MSRPC */
+               if (status.error_type == ADS_ERROR_SYSTEM &&
+                   status.err.rc == ECONNREFUSED) {
+                       DEBUG(1,("Trying MSRPC methods\n"));
+                       domain->methods = &msrpc_methods;
+               }
+               return NULL;
+       }
+
+       /* remember our primary realm for trusted domain support */
+       if (!primary_realm) {
+               primary_realm = strdup(ads->config.realm);
+       }
+
+       domain->private = (void *)ads;
+       return ads;
+}
+
+
+/* Query display info for a realm. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+                              TALLOC_CTX *mem_ctx,
+                              uint32 *num_entries, 
+                              WINBIND_USERINFO **info)
+{
+       ADS_STRUCT *ads = NULL;
+       const char *attrs[] = {"userPrincipalName",
+                              "sAMAccountName",
+                              "name", "objectSid", "primaryGroupID", 
+                              "sAMAccountType", NULL};
+       int i, count;
+       ADS_STATUS rc;
+       void *res = NULL;
+       void *msg = NULL;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+       *num_entries = 0;
+
+       DEBUG(3,("ads: query_user_list\n"));
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+               goto done;
+       }
+
+       count = ads_count_replies(ads, res);
+       if (count == 0) {
+               DEBUG(1,("query_user_list: No users found\n"));
+               goto done;
+       }
+
+       (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+       if (!*info) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       i = 0;
+
+       for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+               char *name, *gecos;
+               DOM_SID sid;
+               DOM_SID *sid2;
+               DOM_SID *group_sid;
+               uint32 group;
+               uint32 atype;
+
+               if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
+                   ads_atype_map(atype) != SID_NAME_USER) {
+                       DEBUG(1,("Not a user account? atype=0x%x\n", atype));
+                       continue;
+               }
+
+               name = ads_pull_username(ads, mem_ctx, msg);
+               gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+               if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+                       DEBUG(1,("No sid for %s !?\n", name));
+                       continue;
+               }
+               if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
+                       DEBUG(1,("No primary group for %s !?\n", name));
+                       continue;
+               }
+
+               sid2 = talloc(mem_ctx, sizeof(*sid2));
+               if (!sid2) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               sid_copy(sid2, &sid);
+
+               group_sid = rid_to_talloced_sid(domain, mem_ctx, group);
+
+               (*info)[i].acct_name = name;
+               (*info)[i].full_name = gecos;
+               (*info)[i].user_sid = sid2;
+               (*info)[i].group_sid = group_sid;
+               i++;
+       }
+
+       (*num_entries) = i;
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
+
+done:
+       if (res) ads_msgfree(ads, res);
+
+       return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       ADS_STRUCT *ads = NULL;
+       const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+                              "name", "objectSid", 
+                              "sAMAccountType", NULL};
+       int i, count;
+       ADS_STATUS rc;
+       void *res = NULL;
+       void *msg = NULL;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       uint32 group_flags;
+
+       *num_entries = 0;
+
+       DEBUG(3,("ads: enum_dom_groups\n"));
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
+               goto done;
+       }
+
+       count = ads_count_replies(ads, res);
+       if (count == 0) {
+               DEBUG(1,("enum_dom_groups: No groups found\n"));
+               goto done;
+       }
+
+       (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+       if (!*info) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       i = 0;
+       
+       group_flags = ATYPE_GLOBAL_GROUP;
+       if ( domain->native_mode )
+               group_flags |= ATYPE_LOCAL_GROUP;
+
+       for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+               char *name, *gecos;
+               DOM_SID sid;
+               uint32 rid;
+               uint32 account_type;
+
+               if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) ) 
+                       continue; 
+                       
+               name = ads_pull_username(ads, mem_ctx, msg);
+               gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+               if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+                       DEBUG(1,("No sid for %s !?\n", name));
+                       continue;
+               }
+
+               if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
+                       DEBUG(1,("No rid for %s !?\n", name));
+                       continue;
+               }
+
+               fstrcpy((*info)[i].acct_name, name);
+               fstrcpy((*info)[i].acct_desc, gecos);
+               (*info)[i].rid = rid;
+               i++;
+       }
+
+       (*num_entries) = i;
+
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
+
+done:
+       if (res) ads_msgfree(ads, res);
+
+       return status;
+}
+
+/* list all domain local groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       /*
+        * This is a stub function only as we returned the domain 
+        * ocal groups in enum_dom_groups() if the domain->native field
+        * was true.  This is a simple performance optimization when
+        * using LDAP.
+        *
+        * if we ever need to enumerate domain local groups separately, 
+        * then this the optimization in enum_dom_groups() will need 
+        * to be split out
+        */
+       *num_entries = 0;
+       
+       return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           const char *name,
+                           DOM_SID *sid,
+                           enum SID_NAME_USE *type)
+{
+       ADS_STRUCT *ads;
+
+       DEBUG(3,("ads: name_to_sid\n"));
+
+       ads = ads_cached_connection(domain);
+       if (!ads) 
+               return NT_STATUS_UNSUCCESSFUL;
+
+       return ads_name_to_sid(ads, name, sid, type);
+}
+
+/* convert a sid to a user or group name */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           DOM_SID *sid,
+                           char **name,
+                           enum SID_NAME_USE *type)
+{
+       ADS_STRUCT *ads = NULL;
+       DEBUG(3,("ads: sid_to_name\n"));
+       ads = ads_cached_connection(domain);
+       if (!ads) 
+               return NT_STATUS_UNSUCCESSFUL;
+
+       return ads_sid_to_name(ads, mem_ctx, sid, name, type);
+}
+
+
+/* convert a DN to a name, SID and name type 
+   this might become a major speed bottleneck if groups have
+   lots of users, in which case we could cache the results
+*/
+static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+                     const char *dn,
+                     char **name, uint32 *name_type, DOM_SID *sid)
+{
+       char *exp;
+       void *res = NULL;
+       const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+                              "objectSid", "sAMAccountType", NULL};
+       ADS_STATUS rc;
+       uint32 atype;
+       char *escaped_dn = escape_ldap_string_alloc(dn);
+
+       if (!escaped_dn) {
+               return False;
+       }
+
+       asprintf(&exp, "(distinguishedName=%s)", dn);
+       rc = ads_search_retry(ads, &res, exp, attrs);
+       SAFE_FREE(exp);
+       SAFE_FREE(escaped_dn);
+
+       if (!ADS_ERR_OK(rc)) {
+               goto failed;
+       }
+
+       (*name) = ads_pull_username(ads, mem_ctx, res);
+
+       if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
+               goto failed;
+       }
+       (*name_type) = ads_atype_map(atype);
+
+       if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+               goto failed;
+       }
+
+       if (res) ads_msgfree(ads, res);
+       return True;
+
+failed:
+       if (res) ads_msgfree(ads, res);
+       return False;
+}
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain, 
+                          TALLOC_CTX *mem_ctx, 
+                          DOM_SID *sid, 
+                          WINBIND_USERINFO *info)
+{
+       ADS_STRUCT *ads = NULL;
+       const char *attrs[] = {"userPrincipalName", 
+                              "sAMAccountName",
+                              "name", 
+                              "primaryGroupID", NULL};
+       ADS_STATUS rc;
+       int count;
+       void *msg = NULL;
+       char *exp;
+       char *sidstr;
+       uint32 group_rid;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID *sid2;
+       fstring sid_string;
+
+       DEBUG(3,("ads: query_user\n"));
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       sidstr = sid_binstring(sid);
+       asprintf(&exp, "(objectSid=%s)", sidstr);
+       rc = ads_search_retry(ads, &msg, exp, attrs);
+       free(exp);
+       free(sidstr);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+               goto done;
+       }
+
+       count = ads_count_replies(ads, msg);
+       if (count != 1) {
+               DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid)));
+               goto done;
+       }
+
+       info->acct_name = ads_pull_username(ads, mem_ctx, msg);
+       info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
+
+       if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
+               DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid)));
+               goto done;
+       }
+       
+       sid2 = talloc(mem_ctx, sizeof(*sid2));
+       if (!sid2) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+       sid_copy(sid2, sid);
+       
+       info->user_sid = sid2;
+
+       info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid);
+
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads query_user gave %s\n", info->acct_name));
+done:
+       if (msg) ads_msgfree(ads, msg);
+
+       return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+   tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
+                                     TALLOC_CTX *mem_ctx,
+                                     const char *user_dn, 
+                                     DOM_SID *primary_group,
+                                     uint32 *num_groups, DOM_SID ***user_gids)
+{
+       ADS_STATUS rc;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       int count;
+       void *res = NULL;
+       void *msg = NULL;
+       char *exp;
+       ADS_STRUCT *ads;
+       const char *group_attrs[] = {"objectSid", NULL};
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       /* buggy server, no tokenGroups.  Instead lookup what groups this user
+          is a member of by DN search on member*/
+       if (asprintf(&exp, "(&(member=%s)(objectClass=group))", user_dn) == -1) {
+               DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       rc = ads_search_retry(ads, &res, exp, group_attrs);
+       free(exp);
+       
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+               return ads_ntstatus(rc);
+       }
+       
+       count = ads_count_replies(ads, res);
+       if (count == 0) {
+               DEBUG(5,("lookup_usergroups: No supp groups found\n"));
+               
+               status = ads_ntstatus(rc);
+               goto done;
+       }
+       
+       (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+       (*user_gids)[0] = primary_group;
+       
+       *num_groups = 1;
+       
+       for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+               DOM_SID group_sid;
+               
+               if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+                       DEBUG(1,("No sid for this group ?!?\n"));
+                       continue;
+               }
+               
+               if (sid_equal(&group_sid, primary_group)) continue;
+               
+               (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+               if (!(*user_gids)[*num_groups]) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               sid_copy((*user_gids)[*num_groups], &group_sid);
+
+               (*num_groups)++;
+                       
+       }
+
+       status = NT_STATUS_OK;
+
+       DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
+done:
+       if (res) ads_msgfree(ads, res);
+       if (msg) ads_msgfree(ads, msg);
+
+       return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+                                 TALLOC_CTX *mem_ctx,
+                                 DOM_SID *sid, 
+                                 uint32 *num_groups, DOM_SID ***user_gids)
+{
+       ADS_STRUCT *ads = NULL;
+       const char *attrs[] = {"distinguishedName", NULL};
+       const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
+       ADS_STATUS rc;
+       int count;
+       void *msg = NULL;
+       char *exp;
+       char *user_dn;
+       DOM_SID *sids;
+       int i;
+       DOM_SID *primary_group;
+       uint32 primary_group_rid;
+       char *sidstr;
+       fstring sid_string;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+       DEBUG(3,("ads: lookup_usergroups\n"));
+       *num_groups = 0;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       if (!(sidstr = sid_binstring(sid))) {
+               DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, sid)));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+       if (asprintf(&exp, "(objectSid=%s)", sidstr) == -1) {
+               free(sidstr);
+               DEBUG(1,("lookup_usergroups(sid=%s) asprintf failed!\n", sid_to_string(sid_string, sid)));
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       rc = ads_search_retry(ads, &msg, exp, attrs);
+       free(exp);
+       free(sidstr);
+
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("lookup_usergroups(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+               goto done;
+       }
+
+       user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
+       if (!user_dn) {
+               DEBUG(1,("lookup_usergroups(sid=%s) ads_search did not return a a distinguishedName!\n", sid_to_string(sid_string, sid)));
+               if (msg) ads_msgfree(ads, msg);
+               goto done;
+       }
+
+       if (msg) ads_msgfree(ads, msg);
+
+       rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+               goto done;
+       }
+
+       if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+               DEBUG(1,("%s: No primary group for sid=%s !?\n", domain->name, sid_to_string(sid_string, sid)));
+               goto done;
+       }
+
+       primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid);
+
+       count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+       if (msg) ads_msgfree(ads, msg);
+
+       /* there must always be at least one group in the token, 
+          unless we are talking to a buggy Win2k server */
+       if (count == 0) {
+               return lookup_usergroups_alt(domain, mem_ctx, user_dn, 
+                                            primary_group,
+                                            num_groups, user_gids);
+       }
+
+       (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+       (*user_gids)[0] = primary_group;
+       
+       *num_groups = 1;
+       
+       for (i=0;i<count;i++) {
+               if (sid_equal(&sids[i], primary_group)) continue;
+               
+               (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+               if (!(*user_gids)[*num_groups]) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               sid_copy((*user_gids)[*num_groups], &sids[i]);
+               (*num_groups)++;
+       }
+
+       status = NT_STATUS_OK;
+       DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid)));
+done:
+       return status;
+}
+
+/*
+  find the members of a group, given a group rid and domain
+ */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               DOM_SID *group_sid, uint32 *num_names, 
+                               DOM_SID ***sid_mem, char ***names, 
+                               uint32 **name_types)
+{
+       ADS_STATUS rc;
+       int count;
+       void *res=NULL;
+       ADS_STRUCT *ads = NULL;
+       char *exp;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       char *sidstr;
+       const char *attrs[] = {"member", NULL};
+       char **members;
+       int i, num_members;
+       fstring sid_string;
+
+       *num_names = 0;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) goto done;
+
+       sidstr = sid_binstring(group_sid);
+
+       /* search for all members of the group */
+       asprintf(&exp, "(objectSid=%s)",sidstr);
+       rc = ads_search_retry(ads, &res, exp, attrs);
+       free(exp);
+       free(sidstr);
+
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+               goto done;
+       }
+
+       count = ads_count_replies(ads, res);
+       if (count == 0) {
+               status = NT_STATUS_OK;
+               goto done;
+       }
+
+       members = ads_pull_strings(ads, mem_ctx, res, "member");
+       if (!members) {
+               /* no members? ok ... */
+               status = NT_STATUS_OK;
+               goto done;
+       }
+
+       /* now we need to turn a list of members into rids, names and name types 
+          the problem is that the members are in the form of distinguised names
+       */
+       for (i=0;members[i];i++) /* noop */ ;
+       num_members = i;
+
+       (*sid_mem) = talloc_zero(mem_ctx, sizeof(**sid_mem) * num_members);
+       (*name_types) = talloc_zero(mem_ctx, sizeof(**name_types) * num_members);
+       (*names) = talloc_zero(mem_ctx, sizeof(**names) * num_members);
+
+       for (i=0;i<num_members;i++) {
+               uint32 name_type;
+               char *name;
+               DOM_SID sid;
+
+               if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
+                   (*names)[*num_names] = name;
+                   (*name_types)[*num_names] = name_type;
+                   (*sid_mem)[*num_names] = talloc(mem_ctx, sizeof(***sid_mem));
+                   if (!(*sid_mem)[*num_names]) {
+                           status = NT_STATUS_NO_MEMORY;
+                           goto done;
+                   }
+                   sid_copy((*sid_mem)[*num_names], &sid);
+                   (*num_names)++;
+               }
+       }       
+
+       status = NT_STATUS_OK;
+       DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
+done:
+       if (res) ads_msgfree(ads, res);
+
+       return status;
+}
+
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS rc;
+
+       *seq = DOM_SEQUENCE_NONE;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+       rc = ads_USN(ads, seq);
+       if (!ADS_ERR_OK(rc)) {
+               /* its a dead connection */
+               ads_destroy(&ads);
+               domain->private = NULL;
+       }
+       return ads_ntstatus(rc);
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_domains,
+                               char ***names,
+                               char ***alt_names,
+                               DOM_SID **dom_sids)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+
+       *num_domains = 0;
+       *names = NULL;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+       rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
+
+       return ads_ntstatus(rc);
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+       rc = ads_domain_sid(ads, sid);
+
+       if (!ADS_ERR_OK(rc)) {
+               /* its a dead connection */
+               ads_destroy(&ads);
+               domain->private = NULL;
+       }
+
+       return ads_ntstatus(rc);
+}
+
+
+/* find alternate names list for the domain - for ADS this is the
+   netbios name */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       TALLOC_CTX *ctx;
+       char *workgroup;
+
+       ads = ads_cached_connection(domain);
+       if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+       if (!(ctx = talloc_init("alternate_name"))) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       rc = ads_workgroup_name(ads, ctx, &workgroup);
+
+       if (ADS_ERR_OK(rc)) {
+               fstrcpy(domain->name, workgroup);
+               fstrcpy(domain->alt_name, ads->config.realm);
+               strupper(domain->alt_name);
+               strupper(domain->name);
+       }
+
+       talloc_destroy(ctx);
+
+       return ads_ntstatus(rc);        
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods ads_methods = {
+       True,
+       query_user_list,
+       enum_dom_groups,
+       enum_local_groups,
+       name_to_sid,
+       sid_to_name,
+       query_user,
+       lookup_usergroups,
+       lookup_groupmem,
+       sequence_number,
+       trusted_domains,
+       domain_sid,
+       alternate_name
+};
+
+#endif
diff --git a/source4/nsswitch/winbindd_cache.c b/source4/nsswitch/winbindd_cache.c
new file mode 100644 (file)
index 0000000..5fb59e7
--- /dev/null
@@ -0,0 +1,1016 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind cache backend functions
+
+   Copyright (C) Andrew Tridgell 2001
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct winbind_cache {
+       struct winbindd_methods *backend;
+       TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+       NTSTATUS status;
+       uint32 sequence_number;
+       uint8 *data;
+       uint32 len, ofs;
+};
+
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+       extern BOOL opt_nocache;
+
+       if (!wcache) return;
+       if (wcache->tdb) {
+               tdb_close(wcache->tdb);
+               wcache->tdb = NULL;
+       }
+       if (opt_nocache) return;
+
+       wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
+                                  TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+       if (!wcache->tdb) {
+               DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+       }
+}
+
+void winbindd_check_cache_size(time_t t)
+{
+       static time_t last_check_time;
+       struct stat st;
+
+       if (last_check_time == (time_t)0)
+               last_check_time = t;
+
+       if (t - last_check_time < 60 && t - last_check_time > 0)
+               return;
+
+       if (wcache == NULL || wcache->tdb == NULL) {
+               DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
+               return;
+       }
+
+       if (fstat(wcache->tdb->fd, &st) == -1) {
+               DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+               return;
+       }
+
+       if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
+               DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
+                       (unsigned long)st.st_size,
+                       (unsigned long)WINBINDD_MAX_CACHE_SIZE));
+               wcache_flush_cache();
+       }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+       extern struct winbindd_methods msrpc_methods;
+       struct winbind_cache *ret = wcache;
+
+       if (ret) return ret;
+       
+       ret = smb_xmalloc(sizeof(*ret));
+       ZERO_STRUCTP(ret);
+       switch (lp_security()) {
+#ifdef HAVE_ADS
+       case SEC_ADS: {
+               extern struct winbindd_methods ads_methods;
+               ret->backend = &ads_methods;
+               break;
+       }
+#endif
+       default:
+               ret->backend = &msrpc_methods;
+       }
+
+       wcache = ret;
+       wcache_flush_cache();
+
+       return ret;
+}
+
+/*
+  free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+       if (!centry) return;
+       SAFE_FREE(centry->data);
+       free(centry);
+}
+
+
+/*
+  pull a uint32 from a cache entry 
+*/
+static uint32 centry_uint32(struct cache_entry *centry)
+{
+       uint32 ret;
+       if (centry->len - centry->ofs < 4) {
+               DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
+                        centry->len - centry->ofs));
+               smb_panic("centry_uint32");
+       }
+       ret = IVAL(centry->data, centry->ofs);
+       centry->ofs += 4;
+       return ret;
+}
+
+/*
+  pull a uint8 from a cache entry 
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
+{
+       uint8 ret;
+       if (centry->len - centry->ofs < 1) {
+               DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
+                        centry->len - centry->ofs));
+               smb_panic("centry_uint32");
+       }
+       ret = CVAL(centry->data, centry->ofs);
+       centry->ofs += 1;
+       return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+   talloc context 
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+       uint32 len;
+       char *ret;
+
+       len = centry_uint8(centry);
+
+       if (len == 0xFF) {
+               /* a deliberate NULL string */
+               return NULL;
+       }
+
+       if (centry->len - centry->ofs < len) {
+               DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
+                        len, centry->len - centry->ofs));
+               smb_panic("centry_string");
+       }
+
+       ret = talloc(mem_ctx, len+1);
+       if (!ret) {
+               smb_panic("centry_string out of memory\n");
+       }
+       memcpy(ret,centry->data + centry->ofs, len);
+       ret[len] = 0;
+       centry->ofs += len;
+       return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+   talloc context 
+*/
+static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+       DOM_SID *sid;
+       char *sid_string;
+       sid = talloc(mem_ctx, sizeof(*sid));
+       if (!sid) return NULL;
+       
+       sid_string = centry_string(centry, mem_ctx);
+       if (!string_to_sid(sid, sid_string)) {
+               return NULL;
+       }
+       return sid;
+}
+
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
+{
+       if (!wcache->tdb) return False;
+       return (domain->sequence_number == DOM_SEQUENCE_NONE);
+}
+
+
+/*
+  refresh the domain sequence number. If force is True
+  then always refresh it, no matter how recently we fetched it
+*/
+static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
+{
+       NTSTATUS status;
+       unsigned time_diff;
+       unsigned cache_time = lp_winbind_cache_time();
+
+       /* trying to reconnect is expensive, don't do it too often */
+       if (domain->sequence_number == DOM_SEQUENCE_NONE) {
+               cache_time *= 8;
+       }
+
+       time_diff = time(NULL) - domain->last_seq_check;
+
+       /* see if we have to refetch the domain sequence number */
+       if (!force && (time_diff < cache_time)) {
+               return;
+       }
+
+       status = wcache->backend->sequence_number(domain, &domain->sequence_number);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               domain->sequence_number = DOM_SEQUENCE_NONE;
+       }
+
+       domain->last_seq_check = time(NULL);
+}
+
+/*
+  decide if a cache entry has expired
+*/
+static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry)
+{
+       /* if the server is OK and our cache entry came from when it was down then
+          the entry is invalid */
+       if (domain->sequence_number != DOM_SEQUENCE_NONE && 
+           centry->sequence_number == DOM_SEQUENCE_NONE) {
+               return True;
+       }
+
+       /* if the server is down or the cache entry is not older than the
+          current sequence number then it is OK */
+       if (wcache_server_down(domain) || 
+           centry->sequence_number == domain->sequence_number) {
+               return False;
+       }
+
+       /* it's expired */
+       return True;
+}
+
+/*
+  fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+  number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
+                                       struct winbindd_domain *domain,
+                                       const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
+                                       struct winbindd_domain *domain,
+                                       const char *format, ...)
+{
+       va_list ap;
+       char *kstr;
+       TDB_DATA data;
+       struct cache_entry *centry;
+       TDB_DATA key;
+
+       refresh_sequence_number(domain, False);
+
+       va_start(ap, format);
+       smb_xvasprintf(&kstr, format, ap);
+       va_end(ap);
+       
+       key.dptr = kstr;
+       key.dsize = strlen(kstr);
+       data = tdb_fetch(wcache->tdb, key);
+       free(kstr);
+       if (!data.dptr) {
+               /* a cache miss */
+               return NULL;
+       }
+
+       centry = smb_xmalloc(sizeof(*centry));
+       centry->data = data.dptr;
+       centry->len = data.dsize;
+       centry->ofs = 0;
+
+       if (centry->len < 8) {
+               /* huh? corrupt cache? */
+               centry_free(centry);
+               return NULL;
+       }
+       
+       centry->status = NT_STATUS(centry_uint32(centry));
+       centry->sequence_number = centry_uint32(centry);
+
+       if (centry_expired(domain, centry)) {
+               extern BOOL opt_dual_daemon;
+
+               if (opt_dual_daemon) {
+                       extern BOOL background_process;
+                       background_process = True;
+               } else {
+                       centry_free(centry);
+                       return NULL;
+               }
+       }
+
+       return centry;
+}
+
+/*
+  make sure we have at least len bytes available in a centry 
+*/
+static void centry_expand(struct cache_entry *centry, uint32 len)
+{
+       uint8 *p;
+       if (centry->len - centry->ofs >= len) return;
+       centry->len *= 2;
+       p = realloc(centry->data, centry->len);
+       if (!p) {
+               DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
+               smb_panic("out of memory in centry_expand");
+       }
+       centry->data = p;
+}
+
+/*
+  push a uint32 into a centry 
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
+{
+       centry_expand(centry, 4);
+       SIVAL(centry->data, centry->ofs, v);
+       centry->ofs += 4;
+}
+
+/*
+  push a uint8 into a centry 
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
+{
+       centry_expand(centry, 1);
+       SCVAL(centry->data, centry->ofs, v);
+       centry->ofs += 1;
+}
+
+/* 
+   push a string into a centry 
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+       int len;
+
+       if (!s) {
+               /* null strings are marked as len 0xFFFF */
+               centry_put_uint8(centry, 0xFF);
+               return;
+       }
+
+       len = strlen(s);
+       /* can't handle more than 254 char strings. Truncating is probably best */
+       if (len > 254) len = 254;
+       centry_put_uint8(centry, len);
+       centry_expand(centry, len);
+       memcpy(centry->data + centry->ofs, s, len);
+       centry->ofs += len;
+}
+
+static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
+{
+       int len;
+       fstring sid_string;
+       centry_put_string(centry, sid_to_string(sid_string, sid));
+}
+
+/*
+  start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+{
+       struct cache_entry *centry;
+
+       if (!wcache->tdb) return NULL;
+
+       centry = smb_xmalloc(sizeof(*centry));
+
+       centry->len = 8192; /* reasonable default */
+       centry->data = smb_xmalloc(centry->len);
+       centry->ofs = 0;
+       centry->sequence_number = domain->sequence_number;
+       centry_put_uint32(centry, NT_STATUS_V(status));
+       centry_put_uint32(centry, centry->sequence_number);
+       return centry;
+}
+
+/*
+  finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+       va_list ap;
+       char *kstr;
+       TDB_DATA key, data;
+
+       va_start(ap, format);
+       smb_xvasprintf(&kstr, format, ap);
+       va_end(ap);
+
+       key.dptr = kstr;
+       key.dsize = strlen(kstr);
+       data.dptr = centry->data;
+       data.dsize = centry->ofs;
+
+       tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+       free(kstr);
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
+                                   NTSTATUS status, 
+                                   const char *name, DOM_SID *sid, 
+                                   enum SID_NAME_USE type)
+{
+       struct cache_entry *centry;
+       uint32 len;
+       fstring uname;
+       fstring sid_string;
+
+       centry = centry_start(domain, status);
+       if (!centry) return;
+       centry_put_sid(centry, sid);
+       fstrcpy(uname, name);
+       strupper(uname);
+       centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
+       centry_free(centry);
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
+                                   DOM_SID *sid, const char *name, enum SID_NAME_USE type)
+{
+       struct cache_entry *centry;
+       fstring sid_string;
+
+       centry = centry_start(domain, status);
+       if (!centry) return;
+       if (NT_STATUS_IS_OK(status)) {
+               centry_put_uint32(centry, type);
+               centry_put_string(centry, name);
+       }
+       centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
+       centry_free(centry);
+}
+
+
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+{
+       struct cache_entry *centry;
+       fstring sid_string;
+
+       centry = centry_start(domain, status);
+       if (!centry) return;
+       centry_put_string(centry, info->acct_name);
+       centry_put_string(centry, info->full_name);
+       centry_put_sid(centry, info->user_sid);
+       centry_put_sid(centry, info->group_sid);
+       centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
+       centry_free(centry);
+}
+
+
+/* Query display info. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               WINBIND_USERINFO **info)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+       if (!centry) goto do_query;
+
+       *num_entries = centry_uint32(centry);
+       
+       if (*num_entries == 0) goto do_cached;
+
+       (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+       if (! (*info)) smb_panic("query_user_list out of memory");
+       for (i=0; i<(*num_entries); i++) {
+               (*info)[i].acct_name = centry_string(centry, mem_ctx);
+               (*info)[i].full_name = centry_string(centry, mem_ctx);
+               (*info)[i].user_sid = centry_sid(centry, mem_ctx);
+               (*info)[i].group_sid = centry_sid(centry, mem_ctx);
+       }
+
+do_cached:     
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       *num_entries = 0;
+       *info = NULL;
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+
+       status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       centry = centry_start(domain, status);
+       if (!centry) goto skip_save;
+       centry_put_uint32(centry, *num_entries);
+       for (i=0; i<(*num_entries); i++) {
+               centry_put_string(centry, (*info)[i].acct_name);
+               centry_put_string(centry, (*info)[i].full_name);
+               centry_put_sid(centry, (*info)[i].user_sid);
+               centry_put_sid(centry, (*info)[i].group_sid);
+               if (cache->backend->consistent) {
+                       /* when the backend is consistent we can pre-prime some mappings */
+                       wcache_save_name_to_sid(domain, NT_STATUS_OK, 
+                                               (*info)[i].acct_name, 
+                                               (*info)[i].user_sid,
+                                               SID_NAME_USER);
+                       wcache_save_sid_to_name(domain, NT_STATUS_OK, 
+                                               (*info)[i].user_sid,
+                                               (*info)[i].acct_name, 
+                                               SID_NAME_USER);
+                       wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
+               }
+       }       
+       centry_end(centry, "UL/%s", domain->name);
+       centry_free(centry);
+
+skip_save:
+       return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+       if (!centry) goto do_query;
+
+       *num_entries = centry_uint32(centry);
+       
+       if (*num_entries == 0) goto do_cached;
+
+       (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+       if (! (*info)) smb_panic("enum_dom_groups out of memory");
+       for (i=0; i<(*num_entries); i++) {
+               fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+               fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+               (*info)[i].rid = centry_uint32(centry);
+       }
+
+do_cached:     
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       *num_entries = 0;
+       *info = NULL;
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+
+       status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       centry = centry_start(domain, status);
+       if (!centry) goto skip_save;
+       centry_put_uint32(centry, *num_entries);
+       for (i=0; i<(*num_entries); i++) {
+               centry_put_string(centry, (*info)[i].acct_name);
+               centry_put_string(centry, (*info)[i].acct_desc);
+               centry_put_uint32(centry, (*info)[i].rid);
+       }       
+       centry_end(centry, "GL/%s/domain", domain->name);
+       centry_free(centry);
+
+skip_save:
+       return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+       if (!centry) goto do_query;
+
+       *num_entries = centry_uint32(centry);
+       
+       if (*num_entries == 0) goto do_cached;
+
+       (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+       if (! (*info)) smb_panic("enum_dom_groups out of memory");
+       for (i=0; i<(*num_entries); i++) {
+               fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+               fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+               (*info)[i].rid = centry_uint32(centry);
+       }
+
+do_cached:     
+
+       /* If we are returning cached data and the domain controller
+          is down then we don't know whether the data is up to date
+          or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
+          indicate this. */
+
+       if (wcache_server_down(domain)) {
+               DEBUG(10, ("query_user_list: returning cached user list and server was down\n"));
+               status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+       } else
+               status = centry->status;
+
+       centry_free(centry);
+       return status;
+
+do_query:
+       *num_entries = 0;
+       *info = NULL;
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+
+       status = cache->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       centry = centry_start(domain, status);
+       if (!centry) goto skip_save;
+       centry_put_uint32(centry, *num_entries);
+       for (i=0; i<(*num_entries); i++) {
+               centry_put_string(centry, (*info)[i].acct_name);
+               centry_put_string(centry, (*info)[i].acct_desc);
+               centry_put_uint32(centry, (*info)[i].rid);
+       }
+       centry_end(centry, "GL/%s/local", domain->name);
+       centry_free(centry);
+
+skip_save:
+       return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           const char *name,
+                           DOM_SID *sid,
+                           enum SID_NAME_USE *type)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       fstring uname;
+       DOM_SID *sid2;
+
+       if (!cache->tdb) goto do_query;
+
+       fstrcpy(uname, name);
+       strupper(uname);
+       centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
+       if (!centry) goto do_query;
+       *type = centry_uint32(centry);
+       sid2 = centry_sid(centry, mem_ctx);
+       if (!sid2) {
+               ZERO_STRUCTP(sid);
+       } else {
+               sid_copy(sid, sid2);
+       }
+
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       ZERO_STRUCTP(sid);
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+       status = cache->backend->name_to_sid(domain, mem_ctx, name, sid, type);
+
+       /* and save it */
+       wcache_save_name_to_sid(domain, status, name, sid, *type);
+
+       /* We can't save the sid to name mapping as we don't know the
+          correct case of the name without looking it up */
+
+       return status;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+   given */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           DOM_SID *sid,
+                           char **name,
+                           enum SID_NAME_USE *type)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       fstring sid_string;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
+       if (!centry) goto do_query;
+       if (NT_STATUS_IS_OK(centry->status)) {
+               *type = centry_uint32(centry);
+               *name = centry_string(centry, mem_ctx);
+       }
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       *name = NULL;
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+       status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       wcache_save_sid_to_name(domain, status, sid, *name, *type);
+       wcache_save_name_to_sid(domain, status, *name, sid, *type);
+
+       return status;
+}
+
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain, 
+                          TALLOC_CTX *mem_ctx, 
+                          DOM_SID *user_sid, 
+                          WINBIND_USERINFO *info)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       fstring sid_string;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "U/%s", sid_to_string(sid_string, user_sid));
+       if (!centry) goto do_query;
+
+       info->acct_name = centry_string(centry, mem_ctx);
+       info->full_name = centry_string(centry, mem_ctx);
+       info->user_sid = centry_sid(centry, mem_ctx);
+       info->group_sid = centry_sid(centry, mem_ctx);
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       ZERO_STRUCTP(info);
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+       
+       status = cache->backend->query_user(domain, mem_ctx, user_sid, info);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       wcache_save_user(domain, status, info);
+
+       return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+                                 TALLOC_CTX *mem_ctx,
+                                 DOM_SID *user_sid, 
+                                 uint32 *num_groups, DOM_SID ***user_gids)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
+       if (!centry) goto do_query;
+
+       *num_groups = centry_uint32(centry);
+       
+       if (*num_groups == 0) goto do_cached;
+
+       (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
+       if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
+       for (i=0; i<(*num_groups); i++) {
+               (*user_gids)[i] = centry_sid(centry, mem_ctx);
+       }
+
+do_cached:     
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       (*num_groups) = 0;
+       (*user_gids) = NULL;
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+       status = cache->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       centry = centry_start(domain, status);
+       if (!centry) goto skip_save;
+       centry_put_uint32(centry, *num_groups);
+       for (i=0; i<(*num_groups); i++) {
+               centry_put_sid(centry, (*user_gids)[i]);
+       }       
+       centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
+       centry_free(centry);
+
+skip_save:
+       return status;
+}
+
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               DOM_SID *group_sid, uint32 *num_names, 
+                               DOM_SID ***sid_mem, char ***names, 
+                               uint32 **name_types)
+{
+       struct winbind_cache *cache = get_cache(domain);
+       struct cache_entry *centry = NULL;
+       NTSTATUS status;
+       unsigned int i;
+       fstring sid_string;
+
+       if (!cache->tdb) goto do_query;
+
+       centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
+       if (!centry) goto do_query;
+
+       *num_names = centry_uint32(centry);
+       
+       if (*num_names == 0) goto do_cached;
+
+       (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
+       (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
+       (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
+
+       if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
+               smb_panic("lookup_groupmem out of memory");
+       }
+
+       for (i=0; i<(*num_names); i++) {
+               (*sid_mem)[i] = centry_sid(centry, mem_ctx);
+               (*names)[i] = centry_string(centry, mem_ctx);
+               (*name_types)[i] = centry_uint32(centry);
+       }
+
+do_cached:     
+       status = centry->status;
+       centry_free(centry);
+       return status;
+
+do_query:
+       (*num_names) = 0;
+       (*sid_mem) = NULL;
+       (*names) = NULL;
+       (*name_types) = NULL;
+       
+
+       if (wcache_server_down(domain)) {
+               return NT_STATUS_SERVER_DISABLED;
+       }
+       status = cache->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
+                                                sid_mem, names, name_types);
+
+       /* and save it */
+       refresh_sequence_number(domain, True);
+       centry = centry_start(domain, status);
+       if (!centry) goto skip_save;
+       centry_put_uint32(centry, *num_names);
+       for (i=0; i<(*num_names); i++) {
+               centry_put_sid(centry, (*sid_mem)[i]);
+               centry_put_string(centry, (*names)[i]);
+               centry_put_uint32(centry, (*name_types)[i]);
+       }       
+       centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
+       centry_free(centry);
+
+skip_save:
+       return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+       refresh_sequence_number(domain, False);
+
+       *seq = domain->sequence_number;
+
+       return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_domains,
+                               char ***names,
+                               char ***alt_names,
+                               DOM_SID **dom_sids)
+{
+       struct winbind_cache *cache = get_cache(domain);
+
+       /* we don't cache this call */
+       return cache->backend->trusted_domains(domain, mem_ctx, num_domains, 
+                                              names, alt_names, dom_sids);
+}
+
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+       struct winbind_cache *cache = get_cache(domain);
+
+       /* we don't cache this call */
+       return cache->backend->domain_sid(domain, sid);
+}
+
+/* find the alternate names for the domain, if any */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+       struct winbind_cache *cache = get_cache(domain);
+
+       /* we don't cache this call */
+       return cache->backend->alternate_name(domain);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+       True,
+       query_user_list,
+       enum_dom_groups,
+       enum_local_groups,
+       name_to_sid,
+       sid_to_name,
+       query_user,
+       lookup_usergroups,
+       lookup_groupmem,
+       sequence_number,
+       trusted_domains,
+       domain_sid,
+       alternate_name
+};
diff --git a/source4/nsswitch/winbindd_cm.c b/source4/nsswitch/winbindd_cm.c
new file mode 100644 (file)
index 0000000..0748a1c
--- /dev/null
@@ -0,0 +1,954 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon connection manager
+
+   Copyright (C) Tim Potter 2001
+   Copyright (C) Andrew Bartlett 2002
+   
+   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.
+*/
+
+/*
+   We need to manage connections to domain controllers without having to
+   mess up the main winbindd code with other issues.  The aim of the
+   connection manager is to:
+  
+       - make connections to domain controllers and cache them
+       - re-establish connections when networks or servers go down
+       - centralise the policy on connection timeouts, domain controller
+        selection etc
+       - manage re-entrancy for when winbindd becomes able to handle
+        multiple outstanding rpc requests
+  
+   Why not have connection management as part of the rpc layer like tng?
+   Good question.  This code may morph into libsmb/rpc_cache.c or something
+   like that but at the moment it's simply staying as part of winbind. I
+   think the TNG architecture of forcing every user of the rpc layer to use
+   the connection caching system is a bad idea.         It should be an optional
+   method of using the routines.
+
+   The TNG design is quite good but I disagree with some aspects of the
+   implementation. -tpot
+
+ */
+
+/*
+   TODO:
+
+     - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
+       moved down into another function.
+
+     - There needs to be a utility function in libsmb/namequery.c that does
+       cm_get_dc_name() 
+
+     - Take care when destroying cli_structs as they can be shared between
+       various sam handles.
+
+ */
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Global list of connections. Initially a DLIST but can become a hash
+   table or whatever later. */
+
+struct winbindd_cm_conn {
+       struct winbindd_cm_conn *prev, *next;
+       fstring domain;
+       fstring controller;
+       fstring pipe_name;
+       size_t mutex_ref_count;
+       struct cli_state *cli;
+       POLICY_HND pol;
+};
+
+static struct winbindd_cm_conn *cm_conns = NULL;
+
+/* Get a domain controller name.  Cache positive and negative lookups so we
+   don't go to the network too often when something is badly broken. */
+
+#define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
+
+struct get_dc_name_cache {
+       fstring domain_name;
+       fstring srv_name;
+       time_t lookup_time;
+       struct get_dc_name_cache *prev, *next;
+};
+
+/*
+  find the DC for a domain using methods appropriate for a ADS domain
+*/
+static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
+{
+       ADS_STRUCT *ads;
+       const char *realm = domain;
+
+       if (strcasecmp(realm, lp_workgroup()) == 0)
+               realm = lp_realm();
+
+       ads = ads_init(realm, domain, NULL);
+       if (!ads)
+               return False;
+
+       /* we don't need to bind, just connect */
+       ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+       DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+       /* a full ads_connect() is actually overkill, as we don't srictly need
+          to do the SASL auth in order to get the info we need, but libads
+          doesn't offer a better way right now */
+       ads_connect(ads);
+#endif
+
+       if (!ads->config.realm)
+               return False;
+
+       fstrcpy(srv_name, ads->config.ldap_server_name);
+       strupper(srv_name);
+       *dc_ip = ads->ldap_ip;
+       ads_destroy(&ads);
+       
+       DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
+                srv_name, inet_ntoa(*dc_ip)));
+       
+       return True;
+}
+
+
+
+static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
+{
+       static struct get_dc_name_cache *get_dc_name_cache;
+       struct get_dc_name_cache *dcc;
+       struct in_addr dc_ip;
+       BOOL ret;
+
+       /* Check the cache for previous lookups */
+
+       for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
+
+               if (!strequal(domain, dcc->domain_name))
+                       continue; /* Not our domain */
+
+               if ((time(NULL) - dcc->lookup_time) > 
+                   GET_DC_NAME_CACHE_TIMEOUT) {
+
+                       /* Cache entry has expired, delete it */
+
+                       DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
+
+                       DLIST_REMOVE(get_dc_name_cache, dcc);
+                       SAFE_FREE(dcc);
+
+                       break;
+               }
+
+               /* Return a positive or negative lookup for this domain */
+
+               if (dcc->srv_name[0]) {
+                       DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
+                       fstrcpy(srv_name, dcc->srv_name);
+                       return True;
+               } else {
+                       DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
+                       return False;
+               }
+       }
+
+       /* Add cache entry for this lookup. */
+
+       DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
+
+       if (!(dcc = (struct get_dc_name_cache *) 
+             malloc(sizeof(struct get_dc_name_cache))))
+               return False;
+
+       ZERO_STRUCTP(dcc);
+
+       fstrcpy(dcc->domain_name, domain);
+       dcc->lookup_time = time(NULL);
+
+       DLIST_ADD(get_dc_name_cache, dcc);
+
+       zero_ip(&dc_ip);
+
+       ret = False;
+       if (lp_security() == SEC_ADS)
+               ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
+
+       if (!ret) {
+               /* fall back on rpc methods if the ADS methods fail */
+               ret = rpc_find_dc(domain, srv_name, &dc_ip);
+       }
+
+       if (!ret)
+               return False;
+
+       /* We have a name so make the cache entry positive now */
+       fstrcpy(dcc->srv_name, srv_name);
+
+       DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+                 inet_ntoa(dc_ip), domain));
+
+       *ip_out = dc_ip;
+
+       return True;
+}
+
+/* Choose between anonymous or authenticated connections.  We need to use
+   an authenticated connection if DCs have the RestrictAnonymous registry
+   entry set > 0, or the "Additional restrictions for anonymous
+   connections" set in the win2k Local Security Policy. 
+   
+   Caller to free() result in domain, username, password
+*/
+
+static void cm_get_ipc_userpass(char **username, char **domain, char **password)
+{
+       *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+       *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+       *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+       
+       if (*username && **username) {
+
+               if (!*domain || !**domain)
+                       *domain = smb_xstrdup(lp_workgroup());
+               
+               if (!*password || !**password)
+                       *password = smb_xstrdup("");
+
+               DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
+                         *domain, *username));
+
+       } else {
+               DEBUG(3, ("IPC$ connections done anonymously\n"));
+               *username = smb_xstrdup("");
+               *domain = smb_xstrdup("");
+               *password = smb_xstrdup("");
+       }
+}
+
+/* Open a new smb pipe connection to a DC on a given domain.  Cache
+   negative creation attempts so we don't try and connect to broken
+   machines too often. */
+
+#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
+
+struct failed_connection_cache {
+       fstring domain_name;
+       fstring controller;
+       time_t lookup_time;
+       NTSTATUS nt_status;
+       struct failed_connection_cache *prev, *next;
+};
+
+static struct failed_connection_cache *failed_connection_cache;
+
+/* Add an entry to the failed conneciton cache */
+
+static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, 
+                                       NTSTATUS result) 
+{
+       struct failed_connection_cache *fcc;
+
+       SMB_ASSERT(!NT_STATUS_IS_OK(result));
+
+       /* Check we already aren't in the cache */
+
+       for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+               if (strequal(fcc->domain_name, new_conn->domain)) {
+                       DEBUG(10, ("domain %s already tried and failed\n",
+                                  fcc->domain_name));
+                       return;
+               }
+       }
+
+       /* Create negative lookup cache entry for this domain and controller */
+
+       if (!(fcc = (struct failed_connection_cache *)
+             malloc(sizeof(struct failed_connection_cache)))) {
+               DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
+               return;
+       }
+       
+       ZERO_STRUCTP(fcc);
+       
+       fstrcpy(fcc->domain_name, new_conn->domain);
+       fstrcpy(fcc->controller, new_conn->controller);
+       fcc->lookup_time = time(NULL);
+       fcc->nt_status = result;
+       
+       DLIST_ADD(failed_connection_cache, fcc);
+}
+       
+/* Open a connction to the remote server, cache failures for 30 seconds */
+
+static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
+                              struct winbindd_cm_conn *new_conn, BOOL keep_mutex)
+{
+       struct failed_connection_cache *fcc;
+       NTSTATUS result;
+       char *ipc_username, *ipc_domain, *ipc_password;
+       struct in_addr dc_ip;
+       int i;
+       BOOL retry = True;
+       BOOL got_mutex = False;
+
+       ZERO_STRUCT(dc_ip);
+
+       fstrcpy(new_conn->domain, domain);
+       fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
+       
+       /* Look for a domain controller for this domain.  Negative results
+          are cached so don't bother applying the caching for this
+          function just yet.  */
+
+       if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
+               result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+               add_failed_connection_entry(new_conn, result);
+               return result;
+       }
+               
+       /* Return false if we have tried to look up this domain and netbios
+          name before and failed. */
+
+       for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+               
+               if (!(strequal(domain, fcc->domain_name) &&
+                     strequal(new_conn->controller, fcc->controller)))
+                       continue; /* Not our domain */
+
+               if ((time(NULL) - fcc->lookup_time) > 
+                   FAILED_CONNECTION_CACHE_TIMEOUT) {
+
+                       /* Cache entry has expired, delete it */
+
+                       DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
+
+                       DLIST_REMOVE(failed_connection_cache, fcc);
+                       free(fcc);
+
+                       break;
+               }
+
+               /* The timeout hasn't expired yet so return false */
+
+               DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
+
+               result = fcc->nt_status;
+               SMB_ASSERT(!NT_STATUS_IS_OK(result));
+               return result;
+       }
+
+       /* Initialise SMB connection */
+
+       cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+
+       DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
+             new_conn->controller, lp_netbios_name(), ipc_domain, ipc_username));
+
+       for (i = 0; retry && (i < 3); i++) {
+               
+               if (!secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME, &new_conn->mutex_ref_count)) {
+                       DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+                       result = NT_STATUS_POSSIBLE_DEADLOCK;
+                       continue;
+               }
+
+               got_mutex = True;
+
+               result = cli_full_connection(&new_conn->cli, lp_netbios_name(), new_conn->controller, 
+                       &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
+                       ipc_password, 0, &retry);
+
+               if (NT_STATUS_IS_OK(result))
+                       break;
+
+               secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+               got_mutex = False;
+       }
+
+       SAFE_FREE(ipc_username);
+       SAFE_FREE(ipc_domain);
+       SAFE_FREE(ipc_password);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               if (got_mutex)
+                       secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+               add_failed_connection_entry(new_conn, result);
+               return result;
+       }
+       
+       if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
+               result = NT_STATUS_PIPE_NOT_AVAILABLE;
+               /* 
+                * only cache a failure if we are not trying to open the 
+                * **win2k** specific lsarpc UUID.  This could be an NT PDC 
+                * and therefore a failure is normal.  This should probably
+                * be abstracted to a check for 2k specific pipes and wondering
+                * if the PDC is an NT4 box.   but since there is only one 2k 
+                * specific UUID right now, i'm not going to bother.  --jerry
+                */
+               if (got_mutex)
+                       secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+               if ( !is_win2k_pipe(pipe_index) )
+                       add_failed_connection_entry(new_conn, result);
+               cli_shutdown(new_conn->cli);
+               return result;
+       }
+
+       if ((got_mutex) && !keep_mutex)
+               secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+       return NT_STATUS_OK;
+}
+
+/* Return true if a connection is still alive */
+
+static BOOL connection_ok(struct winbindd_cm_conn *conn)
+{
+       if (!conn) {
+               smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
+               return False;
+       }
+
+       if (!conn->cli) {
+               DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
+                         conn->controller, conn->domain, conn->pipe_name));
+               smb_panic("connection_ok: conn->cli was null!");
+               return False;
+       }
+
+       if (!conn->cli->initialised) {
+               DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
+                         conn->controller, conn->domain, conn->pipe_name));
+               smb_panic("connection_ok: conn->cli->initialised is False!");
+               return False;
+       }
+
+       if (conn->cli->fd == -1) {
+               DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
+                         conn->controller, conn->domain, conn->pipe_name));
+               return False;
+       }
+       
+       return True;
+}
+
+/* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
+
+static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
+               struct winbindd_cm_conn **conn_out, BOOL keep_mutex) 
+{
+       struct winbindd_cm_conn *conn, conn_temp;
+       NTSTATUS result;
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) && 
+                   strequal(conn->pipe_name, pipe_name)) {
+                       if (!connection_ok(conn)) {
+                               if (conn->cli)
+                                       cli_shutdown(conn->cli);
+                               ZERO_STRUCT(conn_temp);
+                               conn_temp.next = conn->next;
+                               DLIST_REMOVE(cm_conns, conn);
+                               SAFE_FREE(conn);
+                               conn = &conn_temp;  /* Just to keep the loop moving */
+                       } else {
+                               if (keep_mutex) {
+                                       if (!secrets_named_mutex(conn->controller,
+                                                       WINBIND_SERVER_MUTEX_WAIT_TIME, &conn->mutex_ref_count))
+                                               DEBUG(0,("get_connection_from_cache: mutex grab failed for %s\n",
+                                                       conn->controller));
+                               }
+                               break;
+                       }
+               }
+       }
+       
+       if (!conn) {
+               if (!(conn = malloc(sizeof(*conn))))
+                       return NT_STATUS_NO_MEMORY;
+               
+               ZERO_STRUCTP(conn);
+               
+               if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn, keep_mutex))) {
+                       DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
+                                 domain, pipe_name, nt_errstr(result)));
+                       SAFE_FREE(conn);
+                       return result;
+               }
+               DLIST_ADD(cm_conns, conn);              
+       }
+       
+       *conn_out = conn;
+       return NT_STATUS_OK;
+}
+
+
+/**********************************************************************************
+**********************************************************************************/
+
+BOOL cm_check_for_native_mode_win2k( const char *domain )
+{
+       NTSTATUS                result;
+       struct winbindd_cm_conn conn;
+       DS_DOMINFO_CTR          ctr;
+       BOOL                    ret = False;
+       
+       ZERO_STRUCT( conn );
+       ZERO_STRUCT( ctr );
+       
+       
+       if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn, False)) ) {
+               DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
+                         domain, nt_errstr(result)));
+               return False;
+       }
+       
+       if ( conn.cli ) {
+               if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
+                               conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
+                       ret = False;
+                       goto done;
+               }
+       }
+                               
+       if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
+                       && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
+               ret = True;
+
+done:
+       if ( conn.cli )
+               cli_shutdown( conn.cli );
+       
+       return ret;
+}
+
+
+
+/* Return a LSA policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
+{
+       struct winbindd_cm_conn *conn;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       NTSTATUS result;
+       static CLI_POLICY_HND hnd;
+
+       /* Look for existing connections */
+
+       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
+               return NULL;
+
+       /* This *shitty* code needs scrapping ! JRA */
+       if (policy_handle_is_valid(&conn->pol)) {
+               hnd.pol = conn->pol;
+               hnd.cli = conn->cli;
+               return &hnd;
+       }
+       
+       result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
+                                    des_access, &conn->pol);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               /* Hit the cache code again.  This cleans out the old connection and gets a new one */
+               if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
+                               return NULL;
+
+                       result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
+                                                    des_access, &conn->pol);
+               }
+
+               if (!NT_STATUS_IS_OK(result)) {
+                       cli_shutdown(conn->cli);
+                       DLIST_REMOVE(cm_conns, conn);
+                       SAFE_FREE(conn);
+                       return NULL;
+               }
+       }       
+
+       hnd.pol = conn->pol;
+       hnd.cli = conn->cli;
+
+       return &hnd;
+}
+
+/* Return a SAM policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_sam_handle(char *domain)
+{ 
+       struct winbindd_cm_conn *conn;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       NTSTATUS result;
+       static CLI_POLICY_HND hnd;
+
+       /* Look for existing connections */
+
+       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
+               return NULL;
+       
+       /* This *shitty* code needs scrapping ! JRA */
+       if (policy_handle_is_valid(&conn->pol)) {
+               hnd.pol = conn->pol;
+               hnd.cli = conn->cli;
+               return &hnd;
+       }
+       result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+                                 des_access, &conn->pol);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               /* Hit the cache code again.  This cleans out the old connection and gets a new one */
+               if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
+                               return NULL;
+
+                       result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+                                                 des_access, &conn->pol);
+               }
+
+               if (!NT_STATUS_IS_OK(result)) {
+                       cli_shutdown(conn->cli);
+                       DLIST_REMOVE(cm_conns, conn);
+                       SAFE_FREE(conn);
+                       return NULL;
+               }
+       }       
+
+       hnd.pol = conn->pol;
+       hnd.cli = conn->cli;
+
+       return &hnd;
+}
+
+#if 0  /* This code now *well* out of date */
+
+/* Return a SAM domain policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
+{
+       struct winbindd_cm_conn *conn, *basic_conn = NULL;
+       static CLI_POLICY_HND hnd;
+       NTSTATUS result;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+       /* Look for existing connections */
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
+
+                       if (!connection_ok(conn)) {
+                               /* Shutdown cli?  Free conn?  Allow retry of DC? */
+                               DLIST_REMOVE(cm_conns, conn);
+                               return NULL;
+                       }
+
+                       goto ok;
+               }
+       }
+
+       /* Create a basic handle to open a domain handle from */
+
+       if (!cm_get_sam_handle(domain))
+               return False;
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
+                       basic_conn = conn;
+       }
+       
+       if (!(conn = (struct winbindd_cm_conn *)
+             malloc(sizeof(struct winbindd_cm_conn))))
+               return NULL;
+       
+       ZERO_STRUCTP(conn);
+
+       fstrcpy(conn->domain, basic_conn->domain);
+       fstrcpy(conn->controller, basic_conn->controller);
+       fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+
+       conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
+       conn->cli = basic_conn->cli;
+
+       result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
+                                     &basic_conn->pol, des_access, 
+                                     domain_sid, &conn->pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               return NULL;
+
+       /* Add to list */
+
+       DLIST_ADD(cm_conns, conn);
+
+ ok:
+       hnd.pol = conn->pol;
+       hnd.cli = conn->cli;
+
+       return &hnd;
+}
+
+/* Return a SAM policy handle on a domain user */
+
+CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
+                                      uint32 user_rid)
+{
+       struct winbindd_cm_conn *conn, *basic_conn = NULL;
+       static CLI_POLICY_HND hnd;
+       NTSTATUS result;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+       /* Look for existing connections */
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
+                   conn->pipe_data.samr.rid == user_rid) {
+
+                       if (!connection_ok(conn)) {
+                               /* Shutdown cli?  Free conn?  Allow retry of DC? */
+                               DLIST_REMOVE(cm_conns, conn);
+                               return NULL;
+                       }
+               
+                       goto ok;
+               }
+       }
+
+       /* Create a domain handle to open a user handle from */
+
+       if (!cm_get_sam_dom_handle(domain, domain_sid))
+               return NULL;
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
+                       basic_conn = conn;
+       }
+       
+       if (!basic_conn) {
+               DEBUG(0, ("No domain sam handle was created!\n"));
+               return NULL;
+       }
+
+       if (!(conn = (struct winbindd_cm_conn *)
+             malloc(sizeof(struct winbindd_cm_conn))))
+               return NULL;
+       
+       ZERO_STRUCTP(conn);
+
+       fstrcpy(conn->domain, basic_conn->domain);
+       fstrcpy(conn->controller, basic_conn->controller);
+       fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+       
+       conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
+       conn->cli = basic_conn->cli;
+       conn->pipe_data.samr.rid = user_rid;
+
+       result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
+                                   &basic_conn->pol, des_access, user_rid,
+                                   &conn->pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               return NULL;
+
+       /* Add to list */
+
+       DLIST_ADD(cm_conns, conn);
+
+ ok:
+       hnd.pol = conn->pol;
+       hnd.cli = conn->cli;
+
+       return &hnd;
+}
+
+/* Return a SAM policy handle on a domain group */
+
+CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
+                                       uint32 group_rid)
+{
+       struct winbindd_cm_conn *conn, *basic_conn = NULL;
+       static CLI_POLICY_HND hnd;
+       NTSTATUS result;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+       /* Look for existing connections */
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
+                   conn->pipe_data.samr.rid == group_rid) {
+
+                       if (!connection_ok(conn)) {
+                               /* Shutdown cli?  Free conn?  Allow retry of DC? */
+                               DLIST_REMOVE(cm_conns, conn);
+                               return NULL;
+                       }
+               
+                       goto ok;
+               }
+       }
+
+       /* Create a domain handle to open a user handle from */
+
+       if (!cm_get_sam_dom_handle(domain, domain_sid))
+               return NULL;
+
+       for (conn = cm_conns; conn; conn = conn->next) {
+               if (strequal(conn->domain, domain) &&
+                   strequal(conn->pipe_name, PIPE_SAMR) &&
+                   conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
+                       basic_conn = conn;
+       }
+       
+       if (!basic_conn) {
+               DEBUG(0, ("No domain sam handle was created!\n"));
+               return NULL;
+       }
+
+       if (!(conn = (struct winbindd_cm_conn *)
+             malloc(sizeof(struct winbindd_cm_conn))))
+               return NULL;
+       
+       ZERO_STRUCTP(conn);
+
+       fstrcpy(conn->domain, basic_conn->domain);
+       fstrcpy(conn->controller, basic_conn->controller);
+       fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+       
+       conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
+       conn->cli = basic_conn->cli;
+       conn->pipe_data.samr.rid = group_rid;
+
+       result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
+                                   &basic_conn->pol, des_access, group_rid,
+                                   &conn->pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               return NULL;
+
+       /* Add to list */
+
+       DLIST_ADD(cm_conns, conn);
+
+ ok:
+       hnd.pol = conn->pol;
+       hnd.cli = conn->cli;
+
+       return &hnd;
+}
+
+#endif
+
+/* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
+   netlogon pipe as no handle is returned. */
+
+NTSTATUS cm_get_netlogon_cli(const char *domain, const unsigned char *trust_passwd,
+                            struct cli_state **cli)
+{
+       NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+       struct winbindd_cm_conn *conn;
+       uint32 neg_flags = 0x000001ff;
+
+       if (!cli)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       /* Open an initial conection - keep the mutex. */
+
+       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
+               return result;
+       
+       result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
+
+       if (conn->mutex_ref_count)
+               secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(0, ("error connecting to domain password server: %s\n",
+                         nt_errstr(result)));
+               
+               /* Hit the cache code again.  This cleans out the old connection and gets a new one */
+               if (conn->cli->fd == -1) {
+
+                       if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
+                               return result;
+                       
+                       /* Try again */
+                       result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
+
+                       if (conn->mutex_ref_count)
+                               secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
+               }
+               
+               if (!NT_STATUS_IS_OK(result)) {
+                       cli_shutdown(conn->cli);
+                       DLIST_REMOVE(cm_conns, conn);
+                       SAFE_FREE(conn);
+                       return result;
+               }
+       }
+
+       *cli = conn->cli;
+
+       return result;
+}
+
+/* Dump the current connection status */
+
+static void dump_conn_list(void)
+{
+       struct winbindd_cm_conn *con;
+
+       DEBUG(0, ("\tDomain          Controller      Pipe\n"));
+
+       for(con = cm_conns; con; con = con->next) {
+               char *msg;
+
+               /* Display pipe info */
+               
+               if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
+                       DEBUG(0, ("Error: not enough memory!\n"));
+               } else {
+                       DEBUG(0, ("%s\n", msg));
+                       SAFE_FREE(msg);
+               }
+       }
+}
+
+void winbindd_cm_status(void)
+{
+       /* List open connections */
+
+       DEBUG(0, ("winbindd connection manager status:\n"));
+
+       if (cm_conns)
+               dump_conn_list();
+       else
+               DEBUG(0, ("\tNo active connections\n"));
+}
diff --git a/source4/nsswitch/winbindd_dual.c b/source4/nsswitch/winbindd_dual.c
new file mode 100644 (file)
index 0000000..207757b
--- /dev/null
@@ -0,0 +1,210 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind background daemon
+
+   Copyright (C) Andrew Tridgell 2002
+   
+   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.
+*/
+
+/*
+  the idea of the optional dual daemon mode is ot prevent slow domain
+  responses from clagging up the rest of the system. When in dual
+  daemon mode winbindd always responds to requests from cache if the
+  request is in cache, and if the cached answer is stale then it asks
+  the "dual daemon" to update the cache for that request
+
+ */
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern BOOL opt_dual_daemon;
+BOOL background_process = False;
+int dual_daemon_pipe = -1;
+
+
+/* a list of requests ready to be sent to the dual daemon */
+struct dual_list {
+       struct dual_list *next;
+       char *data;
+       int length;
+       int offset;
+};
+
+static struct dual_list *dual_list;
+static struct dual_list *dual_list_end;
+
+/*
+  setup a select() including the dual daemon pipe
+ */
+int dual_select_setup(fd_set *fds, int maxfd)
+{
+       if (dual_daemon_pipe == -1 ||
+           !dual_list) {
+               return maxfd;
+       }
+
+       FD_SET(dual_daemon_pipe, fds);
+       if (dual_daemon_pipe > maxfd) {
+               maxfd = dual_daemon_pipe;
+       }
+       return maxfd;
+}
+
+
+/*
+  a hook called from the main winbindd select() loop to handle writes
+  to the dual daemon pipe 
+*/
+void dual_select(fd_set *fds)
+{
+       int n;
+
+       if (dual_daemon_pipe == -1 ||
+           !dual_list ||
+           !FD_ISSET(dual_daemon_pipe, fds)) {
+               return;
+       }
+
+       n = sys_write(dual_daemon_pipe, 
+                 &dual_list->data[dual_list->offset],
+                 dual_list->length - dual_list->offset);
+
+       if (n <= 0) {
+               /* the pipe is dead! fall back to normal operation */
+               dual_daemon_pipe = -1;
+               return;
+       }
+
+       dual_list->offset += n;
+
+       if (dual_list->offset == dual_list->length) {
+               struct dual_list *next;
+               next = dual_list->next;
+               free(dual_list->data);
+               free(dual_list);
+               dual_list = next;
+               if (!dual_list) {
+                       dual_list_end = NULL;
+               }
+       }
+}
+
+/* 
+   send a request to the background daemon 
+   this is called for stale cached entries
+*/
+void dual_send_request(struct winbindd_cli_state *state)
+{
+       struct dual_list *list;
+
+       if (!background_process) return;
+
+       list = malloc(sizeof(*list));
+       if (!list) return;
+
+       list->next = NULL;
+       list->data = memdup(&state->request, sizeof(state->request));
+       list->length = sizeof(state->request);
+       list->offset = 0;
+       
+       if (!dual_list_end) {
+               dual_list = list;
+               dual_list_end = list;
+       } else {
+               dual_list_end->next = list;
+               dual_list_end = list;
+       }
+
+       background_process = False;
+}
+
+
+/* 
+the main dual daemon 
+*/
+void do_dual_daemon(void)
+{
+       int fdpair[2];
+       struct winbindd_cli_state state;
+       
+       if (pipe(fdpair) != 0) {
+               return;
+       }
+
+       ZERO_STRUCT(state);
+       state.pid = getpid();
+
+       dual_daemon_pipe = fdpair[1];
+       state.sock = fdpair[0];
+
+       if (fork() != 0) {
+               close(fdpair[0]);
+               return;
+       }
+       close(fdpair[1]);
+       
+       if (!winbind_setup_common()) 
+               _exit(0);
+
+       dual_daemon_pipe = -1;
+       opt_dual_daemon = False;
+
+       while (1) {
+               /* free up any talloc memory */
+               lp_talloc_free();
+               main_loop_talloc_free();
+
+               /* fetch a request from the main daemon */
+               winbind_client_read(&state);
+
+               if (state.finished) {
+                       /* we lost contact with our parent */
+                       exit(0);
+               }
+
+               /* process full rquests */
+               if (state.read_buf_len == sizeof(state.request)) {
+                       DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
+
+                       /* special handling for the stateful requests */
+                       switch (state.request.cmd) {
+                       case WINBINDD_GETPWENT:
+                               winbindd_setpwent(&state);
+                               break;
+                               
+                       case WINBINDD_GETGRENT:
+                       case WINBINDD_GETGRLST:
+                               winbindd_setgrent(&state);
+                               break;
+                       default:
+                               break;
+                       }
+
+                       winbind_process_packet(&state);
+                       SAFE_FREE(state.response.extra_data);
+
+                       free_getent_state(state.getpwent_state);
+                       free_getent_state(state.getgrent_state);
+                       state.getpwent_state = NULL;
+                       state.getgrent_state = NULL;
+               }
+       }
+}
+
diff --git a/source4/nsswitch/winbindd_group.c b/source4/nsswitch/winbindd_group.c
new file mode 100644 (file)
index 0000000..d06db59
--- /dev/null
@@ -0,0 +1,896 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Jeremy Allison 2001.
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/***************************************************************
+ Empty static struct for negative caching.
+****************************************************************/
+
+/* Fill a grent structure from various other information */
+
+static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 
+                      const char *gr_name, gid_t unix_gid)
+{
+       fstring full_group_name;
+       /* Fill in uid/gid */
+       fill_domain_username(full_group_name, dom_name, gr_name);
+
+       gr->gr_gid = unix_gid;
+    
+       /* Group name and password */
+    
+       safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
+       safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+
+       return True;
+}
+
+/* Fill in the group membership field of a NT group given by group_sid */
+
+static BOOL fill_grent_mem(struct winbindd_domain *domain,
+                          DOM_SID *group_sid, 
+                          enum SID_NAME_USE group_name_type, 
+                          int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+       DOM_SID **sid_mem = NULL;
+       uint32 num_names = 0;
+       uint32 *name_types = NULL;
+       unsigned int buf_len, buf_ndx, i;
+       char **names = NULL, *buf;
+       BOOL result = False;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+       fstring sid_string;
+
+       if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
+               return False;
+
+       /* Initialise group membership information */
+       
+       DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
+
+       *num_gr_mem = 0;
+       
+       if (group_name_type != SID_NAME_DOM_GRP) {
+               DEBUG(1, ("SID %s in domain %s isn't a domain group\n", 
+                         sid_to_string(sid_string, group_sid), domain->name));
+                goto done;
+       }
+
+       /* Lookup group members */
+       status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 
+                                                 &sid_mem, &names, &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 
+                         sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
+
+               goto done;
+       }
+
+       DEBUG(10, ("looked up %d names\n", num_names));
+
+       if (DEBUGLEVEL >= 10) {
+               for (i = 0; i < num_names; i++)
+                       DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
+                                  name_types[i]));
+       }
+
+       /* Add members to list */
+
+       buf = NULL;
+       buf_len = buf_ndx = 0;
+
+ again:
+
+       for (i = 0; i < num_names; i++) {
+               char *the_name;
+               fstring name;
+               int len;
+                       
+               the_name = names[i];
+
+               DEBUG(10, ("processing name %s\n", the_name));
+
+               /* FIXME: need to cope with groups within groups.  These
+                   occur in Universal groups on a Windows 2000 native mode
+                   server. */
+
+               if (name_types[i] != SID_NAME_USER) {
+                       DEBUG(3, ("name %s isn't a domain user\n", the_name));
+                       continue;
+               }
+
+               /* Don't bother with machine accounts */
+               
+               if (the_name[strlen(the_name) - 1] == '$') {
+                       DEBUG(10, ("%s is machine account\n", the_name));
+                       continue;
+               }
+
+               /* Append domain name */
+
+               fill_domain_username(name, domain->name, the_name);
+
+               len = strlen(name);
+               
+               /* Add to list or calculate buffer length */
+
+               if (!buf) {
+                       buf_len += len + 1; /* List is comma separated */
+                       (*num_gr_mem)++;
+                       DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
+               } else {
+                       DEBUG(10, ("appending %s at ndx %d\n", name, len));
+                       safe_strcpy(&buf[buf_ndx], name, len);
+                       buf_ndx += len;
+                       buf[buf_ndx] = ',';
+                       buf_ndx++;
+               }
+       }
+
+       /* Allocate buffer */
+
+       if (!buf && buf_len != 0) {
+               if (!(buf = malloc(buf_len))) {
+                       DEBUG(1, ("out of memory\n"));
+                       result = False;
+                       goto done;
+               }
+               memset(buf, 0, buf_len);
+               goto again;
+       }
+
+       if (buf && buf_ndx > 0) {
+               buf[buf_ndx - 1] = '\0';
+       }
+
+       *gr_mem = buf;
+       *gr_mem_len = buf_len;
+
+       DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 
+                  buf_len, *num_gr_mem ? buf : "NULL")); 
+       result = True;
+
+done:
+
+       talloc_destroy(mem_ctx);
+       
+       DEBUG(10, ("fill_grent_mem returning %d\n", result));
+
+       return result;
+}
+
+/* Return a group structure from a group name */
+
+enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
+{
+       DOM_SID group_sid;
+       struct winbindd_domain *domain;
+       enum SID_NAME_USE name_type;
+       fstring name_domain, name_group;
+       char *tmp, *gr_mem;
+       gid_t gid;
+       int gr_mem_len;
+       
+       /* Ensure null termination */
+       state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
+
+       DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
+                 state->request.data.groupname));
+
+       /* Parse domain and groupname */
+       
+       memset(name_group, 0, sizeof(fstring));
+
+       tmp = state->request.data.groupname;
+       if (!parse_domain_user(tmp, name_domain, name_group))
+               return WINBINDD_ERROR;
+
+       /* Get info for the domain */
+
+       if ((domain = find_domain_from_name(name_domain)) == NULL) {
+               DEBUG(0, ("could not get domain sid for domain %s\n",
+                         name_domain));
+               return WINBINDD_ERROR;
+       }
+
+       /* Get rid and name type from name */
+        
+       if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid, 
+                                        &name_type)) {
+               DEBUG(1, ("group %s in domain %s does not exist\n", 
+                         name_group, name_domain));
+               return WINBINDD_ERROR;
+       }
+
+       if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) {
+               DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
+                         name_group, name_type));
+               return WINBINDD_ERROR;
+       }
+
+       if (!winbindd_idmap_get_gid_from_sid(&group_sid, &gid)) {
+               DEBUG(1, ("error converting unix gid to sid\n"));
+               return WINBINDD_ERROR;
+       }
+
+       if (!fill_grent(&state->response.data.gr, name_domain,
+                       name_group, gid) ||
+           !fill_grent_mem(domain, &group_sid, name_type,
+                           &state->response.data.gr.num_gr_mem,
+                           &gr_mem, &gr_mem_len)) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Group membership lives at start of extra data */
+
+       state->response.data.gr.gr_mem_ofs = 0;
+
+       state->response.length += gr_mem_len;
+       state->response.extra_data = gr_mem;
+
+       return WINBINDD_OK;
+}
+
+/* Return a group structure from a gid number */
+
+enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *domain;
+       DOM_SID group_sid;
+       enum SID_NAME_USE name_type;
+       fstring dom_name;
+       fstring group_name;
+       int gr_mem_len;
+       char *gr_mem;
+
+       DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid, 
+                 state->request.data.gid));
+
+       /* Bug out if the gid isn't in the winbind range */
+
+       if ((state->request.data.gid < server_state.gid_low) ||
+           (state->request.data.gid > server_state.gid_high))
+               return WINBINDD_ERROR;
+
+       /* Get rid from gid */
+
+       if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &group_sid)) {
+               DEBUG(1, ("could not convert gid %d to rid\n", 
+                         state->request.data.gid));
+               return WINBINDD_ERROR;
+       }
+
+       /* Get name from sid */
+
+       if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
+               DEBUG(1, ("could not lookup sid\n"));
+               return WINBINDD_ERROR;
+       }
+
+       if (!((name_type == SID_NAME_ALIAS) || 
+             (name_type == SID_NAME_DOM_GRP))) {
+               DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 
+                         group_name, name_type));
+               return WINBINDD_ERROR;
+       }
+
+       /* Fill in group structure */
+
+       domain = find_domain_from_sid(&group_sid);
+
+       if (!domain) {
+               DEBUG(1,("Can't find domain from sid\n"));
+               return WINBINDD_ERROR;
+       }
+
+       if (!fill_grent(&state->response.data.gr, dom_name, group_name, 
+                       state->request.data.gid) ||
+           !fill_grent_mem(domain, &group_sid, name_type,
+                           &state->response.data.gr.num_gr_mem,
+                           &gr_mem, &gr_mem_len))
+               return WINBINDD_ERROR;
+
+       /* Group membership lives at start of extra data */
+
+       state->response.data.gr.gr_mem_ofs = 0;
+
+       state->response.length += gr_mem_len;
+       state->response.extra_data = gr_mem;
+
+       return WINBINDD_OK;
+}
+
+/*
+ * set/get/endgrent functions
+ */
+
+/* "Rewind" file pointer for group database enumeration */
+
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *domain;
+
+       DEBUG(3, ("[%5d]: setgrent\n", state->pid));
+
+       /* Check user has enabled this */
+
+       if (!lp_winbind_enum_groups())
+               return WINBINDD_ERROR;
+
+       /* Free old static data if it exists */
+       
+       if (state->getgrent_state != NULL) {
+               free_getent_state(state->getgrent_state);
+               state->getgrent_state = NULL;
+       }
+       
+       /* Create sam pipes for each domain we know about */
+       
+       for (domain = domain_list(); domain != NULL; domain = domain->next) {
+               struct getent_state *domain_state;
+               
+               /* Create a state record for this domain */
+               
+               if ((domain_state = (struct getent_state *)
+                    malloc(sizeof(struct getent_state))) == NULL) {
+                       DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
+                       return WINBINDD_ERROR;
+               }
+               
+               ZERO_STRUCTP(domain_state);
+               
+               fstrcpy(domain_state->domain_name, domain->name);
+
+               /* Add to list of open domains */
+               
+               DLIST_ADD(state->getgrent_state, domain_state);
+       }
+       
+       return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom group database */
+
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+{
+       DEBUG(3, ("[%5d]: endgrent\n", state->pid));
+
+       free_getent_state(state->getgrent_state);
+       state->getgrent_state = NULL;
+       
+       return WINBINDD_OK;
+}
+
+/* Get the list of domain groups and domain aliases for a domain.  We fill in
+   the sam_entries and num_sam_entries fields with domain group information.  
+   The dispinfo_ndx field is incremented to the index of the next group to 
+   fetch. Return True if some groups were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_group_entries(struct getent_state *ent)
+{
+       NTSTATUS status;
+       uint32 num_entries;
+       struct acct_info *name_list = NULL, *tmp_name_list = NULL;
+       TALLOC_CTX *mem_ctx;
+       BOOL result = False;
+       struct acct_info *sam_grp_entries = NULL;
+       struct winbindd_domain *domain;
+        
+       if (ent->got_sam_entries)
+               return False;
+
+       if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
+                                         ent->domain_name))) {
+               DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 
+               return False;
+       }
+               
+       /* Free any existing group info */
+
+       SAFE_FREE(ent->sam_entries);
+       ent->num_sam_entries = 0;
+       ent->got_sam_entries = True;
+
+       /* Enumerate domain groups */
+
+       num_entries = 0;
+
+       if (!(domain = find_domain_from_name(ent->domain_name))) {
+               DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
+               goto done;
+       }
+
+       /* always get the domain global groups */
+
+       status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+       
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
+               result = False;
+               goto done;
+       }
+
+       /* Copy entries into return buffer */
+
+       if (num_entries) {
+               if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
+                       DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 
+                               num_entries));
+                       result = False;
+                       goto done;
+               }
+               memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
+       }
+       
+       ent->num_sam_entries = num_entries;
+       
+       /* get the domain local groups if we are a member of 
+          a native win2k domain */
+          
+       if ( domain->native_mode && domain->methods->enum_local_groups )
+       {
+               DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
+               
+               status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+               
+               if ( !NT_STATUS_IS_OK(status) ) { 
+                       DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
+                       num_entries = 0;
+               }
+               else
+                       DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
+               
+               /* Copy entries into return buffer */
+
+               if ( num_entries ) {
+                       if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
+                       {
+                               DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 
+                                       num_entries));
+                               result = False;
+                               SAFE_FREE( name_list );
+                               goto done;
+                       }
+                       
+                       name_list = tmp_name_list;
+                               
+                       memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 
+                               num_entries * sizeof(struct acct_info) );
+               }
+       
+               ent->num_sam_entries += num_entries;
+       }
+       
+               
+       /* Fill in remaining fields */
+
+       ent->sam_entries = name_list;
+       ent->sam_entry_index = 0;
+
+       result = (ent->num_sam_entries > 0);
+
+ done:
+       talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+/* Fetch next group entry from ntdom database */
+
+#define MAX_GETGRENT_GROUPS 500
+
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+       struct getent_state *ent;
+       struct winbindd_gr *group_list = NULL;
+       int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
+       char *new_extra_data, *gr_mem_list = NULL;
+
+       DEBUG(3, ("[%5d]: getgrent\n", state->pid));
+
+       /* Check user has enabled this */
+
+       if (!lp_winbind_enum_groups())
+               return WINBINDD_ERROR;
+
+       num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
+
+       if ((state->response.extra_data = 
+            malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
+               return WINBINDD_ERROR;
+
+       state->response.data.num_entries = 0;
+
+       group_list = (struct winbindd_gr *)state->response.extra_data;
+
+       if (!(ent = state->getgrent_state))
+               return WINBINDD_ERROR;
+
+       /* Start sending back groups */
+
+       for (i = 0; i < num_groups; i++) {
+               struct acct_info *name_list = NULL;
+               fstring domain_group_name;
+               uint32 result;
+               gid_t group_gid;
+               int gr_mem_len;
+               char *gr_mem, *new_gr_mem_list;
+               DOM_SID group_sid;
+               struct winbindd_domain *domain;
+                               
+               /* Do we need to fetch another chunk of groups? */
+
+       tryagain:
+
+               DEBUG(10, ("entry_index = %d, num_entries = %d\n",
+                          ent->sam_entry_index, ent->num_sam_entries));
+
+               if (ent->num_sam_entries == ent->sam_entry_index) {
+
+                       while(ent && !get_sam_group_entries(ent)) {
+                               struct getent_state *next_ent;
+
+                               DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 
+
+                               /* Free state information for this domain */
+
+                               SAFE_FREE(ent->sam_entries);
+
+                               next_ent = ent->next;
+                               DLIST_REMOVE(state->getgrent_state, ent);
+                               
+                               SAFE_FREE(ent);
+                               ent = next_ent;
+                       }
+
+                       /* No more domains */
+
+                       if (!ent) 
+                                break;
+               }
+               
+               name_list = ent->sam_entries;
+               
+               if (!(domain = 
+                     find_domain_from_name(ent->domain_name))) {
+                       DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
+                       result = False;
+                       goto done;
+               }
+
+               /* Lookup group info */
+               
+               sid_copy(&group_sid, &domain->sid);
+               sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
+
+               if (!winbindd_idmap_get_gid_from_sid(
+                           &group_sid,
+                           &group_gid)) {
+                       
+                       DEBUG(1, ("could not look up gid for group %s\n", 
+                                 name_list[ent->sam_entry_index].acct_name));
+                       
+                       ent->sam_entry_index++;
+                       goto tryagain;
+               }
+
+               DEBUG(10, ("got gid %d for group %x\n", group_gid,
+                          name_list[ent->sam_entry_index].rid));
+               
+               /* Fill in group entry */
+
+               fill_domain_username(domain_group_name, ent->domain_name, 
+                        name_list[ent->sam_entry_index].acct_name);
+
+               result = fill_grent(&group_list[group_list_ndx], 
+                                   ent->domain_name,
+                                   name_list[ent->sam_entry_index].acct_name,
+                                   group_gid);
+
+               /* Fill in group membership entry */
+
+               if (result) {
+                       DOM_SID member_sid;
+                       group_list[group_list_ndx].num_gr_mem = 0;
+                       gr_mem = NULL;
+                       gr_mem_len = 0;
+                       
+                       /* Get group membership */                      
+                       if (state->request.cmd == WINBINDD_GETGRLST) {
+                               result = True;
+                       } else {
+                               sid_copy(&member_sid, &domain->sid);
+                               sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
+                               result = fill_grent_mem(
+                                       domain,
+                                       &member_sid,
+                                       SID_NAME_DOM_GRP,
+                                       &group_list[group_list_ndx].num_gr_mem, 
+                                       &gr_mem, &gr_mem_len);
+                       }
+               }
+
+               if (result) {
+                       /* Append to group membership list */
+                       new_gr_mem_list = Realloc(
+                               gr_mem_list,
+                               gr_mem_list_len + gr_mem_len);
+
+                       if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
+                               DEBUG(0, ("out of memory\n"));
+                               SAFE_FREE(gr_mem_list);
+                               gr_mem_list_len = 0;
+                               break;
+                       }
+
+                       DEBUG(10, ("list_len = %d, mem_len = %d\n",
+                                  gr_mem_list_len, gr_mem_len));
+
+                       gr_mem_list = new_gr_mem_list;
+
+                       memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
+                              gr_mem_len);
+
+                       SAFE_FREE(gr_mem);
+
+                       group_list[group_list_ndx].gr_mem_ofs = 
+                               gr_mem_list_len;
+
+                       gr_mem_list_len += gr_mem_len;
+               }
+
+               ent->sam_entry_index++;
+               
+               /* Add group to return list */
+               
+               if (result) {
+
+                       DEBUG(10, ("adding group num_entries = %d\n",
+                                  state->response.data.num_entries));
+
+                       group_list_ndx++;
+                       state->response.data.num_entries++;
+                       
+                       state->response.length +=
+                               sizeof(struct winbindd_gr);
+                       
+               } else {
+                       DEBUG(0, ("could not lookup domain group %s\n", 
+                                 domain_group_name));
+               }
+       }
+
+       /* Copy the list of group memberships to the end of the extra data */
+
+       if (group_list_ndx == 0)
+               goto done;
+
+       new_extra_data = Realloc(
+               state->response.extra_data,
+               group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
+
+       if (!new_extra_data) {
+               DEBUG(0, ("out of memory\n"));
+               group_list_ndx = 0;
+               SAFE_FREE(state->response.extra_data);
+               SAFE_FREE(gr_mem_list);
+
+               return WINBINDD_ERROR;
+       }
+
+       state->response.extra_data = new_extra_data;
+
+       memcpy(&((char *)state->response.extra_data)
+              [group_list_ndx * sizeof(struct winbindd_gr)], 
+              gr_mem_list, gr_mem_list_len);
+
+               SAFE_FREE(gr_mem_list);
+
+       state->response.length += gr_mem_list_len;
+
+       DEBUG(10, ("returning %d groups, length = %d\n",
+                  group_list_ndx, gr_mem_list_len));
+
+       /* Out of domains */
+
+ done:
+
+       return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain groups without mapping to unix ids */
+
+enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
+{
+       uint32 total_entries = 0;
+       struct winbindd_domain *domain;
+       char *extra_data = NULL;
+       char *ted = NULL;
+       unsigned int extra_data_len = 0, i;
+
+       DEBUG(3, ("[%5d]: list groups\n", state->pid));
+
+       /* Enumerate over trusted domains */
+
+       for (domain = domain_list(); domain; domain = domain->next) {
+               struct getent_state groups;
+
+               ZERO_STRUCT(groups);
+
+               /* Get list of sam groups */
+               ZERO_STRUCT(groups);
+               fstrcpy(groups.domain_name, domain->name);
+
+               get_sam_group_entries(&groups);
+                       
+               if (groups.num_sam_entries == 0) {
+                       /* this domain is empty or in an error state */
+                       continue;
+               }
+
+               /* keep track the of the total number of groups seen so 
+                  far over all domains */
+               total_entries += groups.num_sam_entries;
+               
+               /* Allocate some memory for extra data.  Note that we limit
+                  account names to sizeof(fstring) = 128 characters.  */               
+                ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+               if (!ted) {
+                       DEBUG(0,("failed to enlarge buffer!\n"));
+                       SAFE_FREE(extra_data);
+                       return WINBINDD_ERROR;
+               } else
+                       extra_data = ted;
+
+               /* Pack group list into extra data fields */
+               for (i = 0; i < groups.num_sam_entries; i++) {
+                       char *group_name = ((struct acct_info *)
+                                           groups.sam_entries)[i].acct_name; 
+                       fstring name;
+
+                       fill_domain_username(name, domain->name, group_name);
+                       /* Append to extra data */                      
+                       memcpy(&extra_data[extra_data_len], name, 
+                               strlen(name));
+                       extra_data_len += strlen(name);
+                       extra_data[extra_data_len++] = ',';
+               }
+
+               free(groups.sam_entries);
+       }
+
+       /* Assign extra_data fields in response structure */
+       if (extra_data) {
+               extra_data[extra_data_len - 1] = '\0';
+               state->response.extra_data = extra_data;
+               state->response.length += extra_data_len;
+       }
+
+       /* No domains may have responded but that's still OK so don't
+          return an error. */
+
+       return WINBINDD_OK;
+}
+
+/* Get user supplementary groups.  This is much quicker than trying to
+   invert the groups database. */
+
+enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
+{
+       fstring name_domain, name_user;
+       DOM_SID user_sid;
+       enum SID_NAME_USE name_type;
+       uint32 num_groups, num_gids;
+       NTSTATUS status;
+       DOM_SID **user_gids;
+       struct winbindd_domain *domain;
+       enum winbindd_result result = WINBINDD_ERROR;
+       gid_t *gid_list;
+       unsigned int i;
+       TALLOC_CTX *mem_ctx;
+       
+       /* Ensure null termination */
+       state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+       DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
+                 state->request.data.username));
+
+       if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
+                                         state->request.data.username)))
+               return WINBINDD_ERROR;
+
+       /* Parse domain and username */
+
+       if (!parse_domain_user(state->request.data.username, name_domain, 
+                         name_user))
+               goto done;
+
+       /* Get info for the domain */
+       
+       if ((domain = find_domain_from_name(name_domain)) == NULL) {
+               DEBUG(0, ("could not find domain entry for domain %s\n", 
+                         name_domain));
+               goto done;
+       }
+
+       /* Get rid and name type from name.  The following costs 1 packet */
+
+       if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, 
+                                        &name_type)) {
+               DEBUG(1, ("user '%s' does not exist\n", name_user));
+               goto done;
+       }
+
+       if (name_type != SID_NAME_USER) {
+               DEBUG(1, ("name '%s' is not a user name: %d\n", 
+                         name_user, name_type));
+               goto done;
+       }
+
+       status = domain->methods->lookup_usergroups(domain, mem_ctx, 
+                                                   &user_sid, &num_groups, 
+                                                   &user_gids);
+       if (!NT_STATUS_IS_OK(status)) goto done;
+
+       /* Copy data back to client */
+
+       num_gids = 0;
+       gid_list = malloc(sizeof(gid_t) * num_groups);
+
+       if (state->response.extra_data)
+               goto done;
+
+       for (i = 0; i < num_groups; i++) {
+               if (!winbindd_idmap_get_gid_from_sid(
+                           user_gids[i], 
+                           &gid_list[num_gids])) {
+                       fstring sid_string;
+
+                       DEBUG(1, ("unable to convert group sid %s to gid\n", 
+                                 sid_to_string(sid_string, user_gids[i])));
+                       continue;
+               }
+                       
+               num_gids++;
+       }
+
+       state->response.data.num_entries = num_gids;
+       state->response.extra_data = gid_list;
+       state->response.length += num_gids * sizeof(gid_t);
+
+       result = WINBINDD_OK;
+
+ done:
+
+       talloc_destroy(mem_ctx);
+
+       return result;
+}
diff --git a/source4/nsswitch/winbindd_idmap.c b/source4/nsswitch/winbindd_idmap.c
new file mode 100644 (file)
index 0000000..de547cd
--- /dev/null
@@ -0,0 +1,196 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Winbind ID Mapping
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Anthony Liguori <aliguor@us.ibm.com>  2003
+
+   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 "winbindd.h"
+
+static struct {
+  const char *name;
+  /* Function to create a member of the idmap_methods list */
+  BOOL (*reg_meth)(struct idmap_methods **methods);
+  struct idmap_methods *methods;
+} builtin_idmap_functions[] = {
+  { "tdb", winbind_idmap_reg_tdb, NULL },
+  /*  { "ldap", winbind_idmap_reg_ldap, NULL },*/
+  { NULL, NULL, NULL }
+};
+
+/* singleton pattern: uberlazy evaluation */
+static struct idmap_methods *impl;
+
+static struct idmap_methods *get_impl(const char *name)
+{
+  int i = 0;
+  struct idmap_methods *ret = NULL;
+
+  while (builtin_idmap_functions[i].name && 
+         strcmp(builtin_idmap_functions[i].name, name)) {
+    i++;
+  }
+
+  if (builtin_idmap_functions[i].name) {
+    if (!builtin_idmap_functions[i].methods) {
+      builtin_idmap_functions[i].reg_meth(&builtin_idmap_functions[i].methods);
+    }
+
+    ret = builtin_idmap_functions[i].methods;
+  }
+
+  return ret;
+}
+
+/* Initialize backend */
+BOOL winbindd_idmap_init(void)
+{
+  BOOL ret = False;
+
+  DEBUG(3, ("winbindd_idmap_init: using '%s' as backend\n", 
+            lp_idmap_backend()));
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+    if (!impl) {
+      DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+                lp_idmap_backend()));
+    }
+  }
+
+  if (impl) {
+    ret = impl->init();
+  }
+
+  DEBUG(3, ("winbind_idmap_init: returning %s\n", ret ? "true" : "false"));
+
+  return ret;
+}
+
+/* Get UID from SID */
+BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid)
+{
+  BOOL ret = False;
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+    if (!impl) {
+      DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+                lp_idmap_backend()));
+    }
+  }
+
+  if (impl) {
+    ret = impl->get_uid_from_sid(sid, uid);
+  }
+
+  return ret;
+}
+
+/* Get GID from SID */
+BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid)
+{
+  BOOL ret = False;
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+    if (!impl) {
+      DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+                lp_idmap_backend()));
+    }
+  }
+
+  if (impl) {
+    ret = impl->get_gid_from_sid(sid, gid);
+  }
+
+  return ret;
+}
+
+/* Get SID from UID */
+BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid)
+{
+  BOOL ret = False;
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+    if (!impl) {
+      DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+                lp_idmap_backend()));
+    }
+  }
+
+  if (impl) {
+    ret = impl->get_sid_from_uid(uid, sid);
+  }
+
+  return ret;
+}
+
+/* Get SID from GID */
+BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid)
+{
+  BOOL ret = False;
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+  }
+
+  if (impl) {
+    ret = impl->get_sid_from_gid(gid, sid);
+  } else {
+    DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+              lp_idmap_backend()));
+  }
+
+  return ret;
+}
+
+/* Close backend */
+BOOL winbindd_idmap_close(void)
+{
+  BOOL ret = False;
+
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+  }
+
+  if (impl) {
+    ret = impl->close();
+  } else {
+    DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+              lp_idmap_backend()));
+  }
+
+  return ret;
+}
+
+/* Dump backend status */
+void winbindd_idmap_status(void)
+{
+  if (!impl) {
+    impl = get_impl(lp_idmap_backend());
+  }
+
+  if (impl) {
+    impl->status();
+  } else {
+    DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+              lp_idmap_backend()));
+  }
+}
+
diff --git a/source4/nsswitch/winbindd_idmap_tdb.c b/source4/nsswitch/winbindd_idmap_tdb.c
new file mode 100644 (file)
index 0000000..911b3b4
--- /dev/null
@@ -0,0 +1,441 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - user related function
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Anthony Liguori 2003
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* High water mark keys */
+#define HWM_GROUP  "GROUP HWM"
+#define HWM_USER   "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+/* Globals */
+static TDB_CONTEXT *idmap_tdb;
+
+/* convert one record to the new format */
+static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
+                         void *ignored)
+{
+       struct winbindd_domain *domain;
+       char *p;
+       DOM_SID sid;
+       uint32 rid;
+       fstring keystr;
+       fstring dom_name;
+       TDB_DATA key2;
+
+       p = strchr(key.dptr, '/');
+       if (!p)
+               return 0;
+
+       *p = 0;
+       fstrcpy(dom_name, key.dptr);
+       *p++ = '/';
+
+       domain = find_domain_from_name(dom_name);
+       if (!domain) {
+               /* We must delete the old record. */
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
+                      dom_name));
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : deleting record %s\n",
+                      key.dptr));
+               tdb_delete(idmap_tdb, key);
+               return 0;
+       }
+
+       rid = atoi(p);
+
+       sid_copy(&sid, &domain->sid);
+       sid_append_rid(&sid, rid);
+
+       sid_to_string(keystr, &sid);
+       key2.dptr = keystr;
+       key2.dsize = strlen(keystr) + 1;
+
+       if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
+               /* not good! */
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : Unable to update record %s\n",
+                      key2.dptr));
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
+               return -1;
+       }
+
+       if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
+               /* not good! */
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : Unable to update record %s\n",
+                      data.dptr));
+               DEBUG(0,
+                     ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
+               return -1;
+       }
+
+       tdb_delete(idmap_tdb, key);
+
+       return 0;
+}
+
+/*****************************************************************************
+ Convert the idmap database from an older version.
+*****************************************************************************/
+static BOOL tdb_idmap_convert(const char *idmap_name)
+{
+       int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+       BOOL bigendianheader =
+           (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
+
+       if (vers == IDMAP_VERSION)
+               return True;
+
+       if (((vers == -1) && bigendianheader)
+           || (IREV(vers) == IDMAP_VERSION)) {
+               /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
+               /*
+                * high and low records were created on a
+                * big endian machine and will need byte-reversing.
+                */
+
+               int32 wm;
+
+               wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
+
+               if (wm != -1) {
+                       wm = IREV(wm);
+               } else
+                       wm = server_state.uid_low;
+
+               if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
+                       DEBUG(0,
+                             ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
+                       return False;
+               }
+
+               wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
+               if (wm != -1) {
+                       wm = IREV(wm);
+               } else
+                       wm = server_state.gid_low;
+
+               if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
+                       DEBUG(0,
+                             ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+                       return False;
+               }
+       }
+
+       /* the old format stored as DOMAIN/rid - now we store the SID direct */
+       tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
+
+       if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
+           -1) {
+               DEBUG(0,
+                     ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/* Allocate either a user or group id from the pool */
+static BOOL tdb_allocate_id(uid_t * id, BOOL isgroup)
+{
+       int hwm;
+
+       /* Get current high water mark */
+       if ((hwm = tdb_fetch_int32(idmap_tdb,
+                                  isgroup ? HWM_GROUP : HWM_USER)) ==
+           -1) {
+               return False;
+       }
+
+       /* Return next available uid in list */
+       if ((isgroup && (hwm > server_state.gid_high)) ||
+           (!isgroup && (hwm > server_state.uid_high))) {
+               DEBUG(0,
+                     ("winbind %sid range full!\n", isgroup ? "g" : "u"));
+               return False;
+       }
+
+       if (id) {
+               *id = hwm;
+       }
+
+       hwm++;
+
+       /* Store new high water mark */
+       tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
+
+       return True;
+}
+
+/* Get a sid from an id */
+static BOOL tdb_get_sid_from_id(int id, DOM_SID * sid, BOOL isgroup)
+{
+       TDB_DATA key, data;
+       fstring keystr;
+       BOOL result = False;
+
+       slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID",
+                id);
+
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       data = tdb_fetch(idmap_tdb, key);
+
+       if (data.dptr) {
+               result = string_to_sid(sid, data.dptr);
+               SAFE_FREE(data.dptr);
+       }
+
+       return result;
+}
+
+/* Get an id from a sid */
+static BOOL tdb_get_id_from_sid(DOM_SID * sid, uid_t * id, BOOL isgroup)
+{
+       TDB_DATA data, key;
+       fstring keystr;
+       BOOL result = False;
+
+       /* Check if sid is present in database */
+       sid_to_string(keystr, sid);
+
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       data = tdb_fetch(idmap_tdb, key);
+
+       if (data.dptr) {
+               fstring scanstr;
+               int the_id;
+
+               /* Parse and return existing uid */
+               fstrcpy(scanstr, isgroup ? "GID" : "UID");
+               fstrcat(scanstr, " %d");
+
+               if (sscanf(data.dptr, scanstr, &the_id) == 1) {
+                       /* Store uid */
+                       if (id) {
+                               *id = the_id;
+                       }
+
+                       result = True;
+               }
+
+               SAFE_FREE(data.dptr);
+       } else {
+
+               /* Allocate a new id for this sid */
+               if (id && tdb_allocate_id(id, isgroup)) {
+                       fstring keystr2;
+
+                       /* Store new id */
+                       slprintf(keystr2, sizeof(keystr2), "%s %d",
+                                isgroup ? "GID" : "UID", *id);
+
+                       data.dptr = keystr2;
+                       data.dsize = strlen(keystr2) + 1;
+
+                       tdb_store(idmap_tdb, key, data, TDB_REPLACE);
+                       tdb_store(idmap_tdb, data, key, TDB_REPLACE);
+
+                       result = True;
+               }
+       }
+
+       return result;
+}
+
+/*****************************************************************************
+ Initialise idmap database. 
+*****************************************************************************/
+static BOOL tdb_idmap_init(void)
+{
+       /* Open tdb cache */
+       if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0,
+                                      TDB_DEFAULT, O_RDWR | O_CREAT,
+                                      0600))) {
+               DEBUG(0,
+                     ("winbindd_idmap_init: Unable to open idmap database\n"));
+               return False;
+       }
+
+       /* possibly convert from an earlier version */
+       if (!tdb_idmap_convert(lock_path("winbindd_idmap.tdb"))) {
+               DEBUG(0,
+                     ("winbindd_idmap_init: Unable to open idmap database\n"));
+               return False;
+       }
+
+       /* Create high water marks for group and user id */
+       if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
+               if (tdb_store_int32
+                   (idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
+                       DEBUG(0,
+                             ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
+                       return False;
+               }
+       }
+
+       if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
+               if (tdb_store_int32
+                   (idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
+                       DEBUG(0,
+                             ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
+                       return False;
+               }
+       }
+
+       return True;
+}
+
+/* Get a sid from a uid */
+static BOOL tdb_get_sid_from_uid(uid_t uid, DOM_SID * sid)
+{
+       return tdb_get_sid_from_id((int) uid, sid, False);
+}
+
+/* Get a sid from a gid */
+static BOOL tdb_get_sid_from_gid(gid_t gid, DOM_SID * sid)
+{
+       return tdb_get_sid_from_id((int) gid, sid, True);
+}
+
+/* Get a uid from a sid */
+static BOOL tdb_get_uid_from_sid(DOM_SID * sid, uid_t * uid)
+{
+       return tdb_get_id_from_sid(sid, uid, False);
+}
+
+/* Get a gid from a group sid */
+static BOOL tdb_get_gid_from_sid(DOM_SID * sid, gid_t * gid)
+{
+       return tdb_get_id_from_sid(sid, gid, True);
+}
+
+/* Close the tdb */
+static BOOL tdb_idmap_close(void)
+{
+       if (idmap_tdb)
+               return (tdb_close(idmap_tdb) == 0);
+       return True;
+}
+
+
+/* Dump status information to log file.  Display different stuff based on
+   the debug level:
+
+   Debug Level        Information Displayed
+   =================================================================
+   0                  Percentage of [ug]id range allocated
+   0                  High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+static void tdb_idmap_status(void)
+{
+       int user_hwm, group_hwm;
+
+       DEBUG(0, ("winbindd idmap status:\n"));
+
+       /* Get current high water marks */
+
+       if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+               DEBUG(DUMP_INFO,
+                     ("\tCould not get userid high water mark!\n"));
+       }
+
+       if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+               DEBUG(DUMP_INFO,
+                     ("\tCould not get groupid high water mark!\n"));
+       }
+
+       /* Display next ids to allocate */
+
+       if (user_hwm != -1) {
+               DEBUG(DUMP_INFO,
+                     ("\tNext userid to allocate is %d\n", user_hwm));
+       }
+
+       if (group_hwm != -1) {
+               DEBUG(DUMP_INFO,
+                     ("\tNext groupid to allocate is %d\n", group_hwm));
+       }
+
+       /* Display percentage of id range already allocated. */
+
+       if (user_hwm != -1) {
+               int num_users = user_hwm - server_state.uid_low;
+               int total_users =
+                   server_state.uid_high - server_state.uid_low;
+
+               DEBUG(DUMP_INFO,
+                     ("\tUser id range is %d%% full (%d of %d)\n",
+                      num_users * 100 / total_users, num_users,
+                      total_users));
+       }
+
+       if (group_hwm != -1) {
+               int num_groups = group_hwm - server_state.gid_low;
+               int total_groups =
+                   server_state.gid_high - server_state.gid_low;
+
+               DEBUG(DUMP_INFO,
+                     ("\tGroup id range is %d%% full (%d of %d)\n",
+                      num_groups * 100 / total_groups, num_groups,
+                      total_groups));
+       }
+
+       /* Display complete mapping of users and groups to rids */
+}
+
+struct idmap_methods tdb_idmap_methods = {
+       tdb_idmap_init,
+
+       tdb_get_sid_from_uid,
+       tdb_get_sid_from_gid,
+
+       tdb_get_uid_from_sid,
+       tdb_get_gid_from_sid,
+
+       tdb_idmap_close,
+
+       tdb_idmap_status
+};
+
+BOOL winbind_idmap_reg_tdb(struct idmap_methods **meth)
+{
+       *meth = &tdb_idmap_methods;
+
+       return True;
+}
diff --git a/source4/nsswitch/winbindd_misc.c b/source4/nsswitch/winbindd_misc.c
new file mode 100644 (file)
index 0000000..b85cd05
--- /dev/null
@@ -0,0 +1,235 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - miscellaneous other functions
+
+   Copyright (C) Tim Potter      2000
+   Copyright (C) Andrew Bartlett 2002
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Check the machine account password is valid */
+
+enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uchar trust_passwd[16];
+        int num_retries = 0;
+        struct cli_state *cli;
+       DEBUG(3, ("[%5d]: check machine account\n", state->pid));
+
+       /* Get trust account password */
+
+ again:
+       if (!secrets_fetch_trust_account_password(
+                   lp_workgroup(), trust_passwd, NULL)) {
+               result = NT_STATUS_INTERNAL_ERROR;
+               goto done;
+       }
+
+        /* This call does a cli_nt_setup_creds() which implicitly checks
+           the trust account password. */
+
+       /* Don't shut this down - it belongs to the connection cache code */
+        result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+                goto done;
+        }
+
+        /* There is a race condition between fetching the trust account
+           password and the periodic machine password change.  So it's 
+          possible that the trust account password has been changed on us.  
+          We are returned NT_STATUS_ACCESS_DENIED if this happens. */
+
+#define MAX_RETRIES 8
+
+        if ((num_retries < MAX_RETRIES) && 
+            NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+                num_retries++;
+                goto again;
+        }
+
+       /* Pass back result code - zero for success, other values for
+          specific failures. */
+
+       DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ?  
+                  "good" : "bad"));
+
+ done:
+       state->response.data.auth.nt_status = NT_STATUS_V(result);
+       fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+       fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+       state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n", 
+                                               state->response.data.auth.nt_status_string));
+
+       return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state
+                                                  *state)
+{
+       struct winbindd_domain *domain;
+       int total_entries = 0, extra_data_len = 0;
+       char *ted, *extra_data = NULL;
+
+       DEBUG(3, ("[%5d]: list trusted domains\n", state->pid));
+
+       /* We need to refresh the trusted domain list as the domains may
+          have changed since we last looked.  There may be a sequence
+          number or something we should use but I haven't found it yet. */
+
+       if (!init_domain_list()) {
+               DEBUG(1, ("winbindd_list_trusted_domains: could not "
+                         "refresh trusted domain list\n"));
+               return WINBINDD_ERROR;
+       }
+
+       for(domain = domain_list(); domain; domain = domain->next) {
+
+               /* Skip own domain */
+
+               if (strequal(domain->name, lp_workgroup())) continue;
+
+               /* Add domain to list */
+
+               total_entries++;
+               ted = Realloc(extra_data, sizeof(fstring) * 
+                              total_entries);
+
+               if (!ted) {
+                       DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n"));
+                       SAFE_FREE(extra_data);
+                       return WINBINDD_ERROR;
+               } else 
+                        extra_data = ted;
+
+               memcpy(&extra_data[extra_data_len], domain->name,
+                      strlen(domain->name));
+
+               extra_data_len  += strlen(domain->name);
+               extra_data[extra_data_len++] = ',';
+       }
+
+       if (extra_data) {
+               if (extra_data_len > 1) 
+                        extra_data[extra_data_len - 1] = '\0';
+               state->response.extra_data = extra_data;
+               state->response.length += extra_data_len;
+       }
+
+       return WINBINDD_OK;
+}
+
+
+enum winbindd_result winbindd_show_sequence(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *domain;
+       char *extra_data = NULL;
+
+       DEBUG(3, ("[%5d]: show sequence\n", state->pid));
+
+       extra_data = strdup("");
+
+       /* this makes for a very simple data format, and is easily parsable as well
+          if that is ever needed */
+       for (domain = domain_list(); domain; domain = domain->next) {
+               char *s;
+
+               domain->methods->sequence_number(domain, &domain->sequence_number);
+               
+               if (DOM_SEQUENCE_NONE == (unsigned)domain->sequence_number) {
+                       asprintf(&s,"%s%s : DISCONNECTED\n", extra_data, 
+                                domain->name);
+               } else {
+                       asprintf(&s,"%s%s : %u\n", extra_data, 
+                                domain->name, (unsigned)domain->sequence_number);
+               }
+               free(extra_data);
+               extra_data = s;
+       }
+
+       state->response.extra_data = extra_data;
+       /* must add one to length to copy the 0 for string termination */
+       state->response.length += strlen(extra_data) + 1;
+
+       return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_ping(struct winbindd_cli_state
+                                                  *state)
+{
+       DEBUG(3, ("[%5d]: ping\n", state->pid));
+
+       return WINBINDD_OK;
+}
+
+/* List various tidbits of information */
+
+enum winbindd_result winbindd_info(struct winbindd_cli_state *state)
+{
+
+       DEBUG(3, ("[%5d]: request misc info\n", state->pid));
+
+       state->response.data.info.winbind_separator = *lp_winbind_separator();
+       fstrcpy(state->response.data.info.samba_version, VERSION);
+
+       return WINBINDD_OK;
+}
+
+/* Tell the client the current interface version */
+
+enum winbindd_result winbindd_interface_version(struct winbindd_cli_state *state)
+{
+
+       DEBUG(3, ("[%5d]: request interface version\n", state->pid));
+       
+       state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+
+       return WINBINDD_OK;
+}
+
+/* What domain are we a member of? */
+
+enum winbindd_result winbindd_domain_name(struct winbindd_cli_state *state)
+{
+
+       DEBUG(3, ("[%5d]: request domain name\n", state->pid));
+       
+       fstrcpy(state->response.data.domain_name, lp_workgroup());
+
+       return WINBINDD_OK;
+}
+
+/* What's my name again? */
+
+enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state)
+{
+
+       DEBUG(3, ("[%5d]: request netbios name\n", state->pid));
+       
+       fstrcpy(state->response.data.netbios_name, lp_netbios_name());
+
+       return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/winbindd_nss.h b/source4/nsswitch/winbindd_nss.h
new file mode 100644 (file)
index 0000000..2c87a77
--- /dev/null
@@ -0,0 +1,242 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) Tim Potter 2000
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+   
+   This library 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
+   Library General Public License for more details.
+   
+   You should have received a copy of the GNU Library General Public
+   License along with this library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA  02111-1307, USA.   
+*/
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
+#endif
+
+#ifndef _WINBINDD_NTDOM_H
+#define _WINBINDD_NTDOM_H
+
+#define WINBINDD_SOCKET_NAME "pipe"            /* Name of PF_UNIX socket */
+#define WINBINDD_SOCKET_DIR  "/tmp/.winbindd"  /* Name of PF_UNIX dir */
+
+#define WINBINDD_DOMAIN_ENV  "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV    "_NO_WINBINDD"
+
+/* Update this when you change the interface.  */
+
+#define WINBIND_INTERFACE_VERSION 7
+
+/* Socket commands */
+
+enum winbindd_cmd {
+
+       WINBINDD_INTERFACE_VERSION,    /* Always a well known value */
+
+       /* Get users and groups */
+
+       WINBINDD_GETPWNAM,
+       WINBINDD_GETPWUID,
+       WINBINDD_GETGRNAM,
+       WINBINDD_GETGRGID,
+       WINBINDD_GETGROUPS,
+
+       /* Enumerate users and groups */
+
+       WINBINDD_SETPWENT,
+       WINBINDD_ENDPWENT,
+       WINBINDD_GETPWENT,
+       WINBINDD_SETGRENT,
+       WINBINDD_ENDGRENT,
+       WINBINDD_GETGRENT,
+
+       /* PAM authenticate and password change */
+
+       WINBINDD_PAM_AUTH,
+       WINBINDD_PAM_AUTH_CRAP,
+       WINBINDD_PAM_CHAUTHTOK,
+
+       /* List various things */
+
+       WINBINDD_LIST_USERS,         /* List w/o rid->id mapping */
+       WINBINDD_LIST_GROUPS,        /* Ditto */
+       WINBINDD_LIST_TRUSTDOM,
+
+       /* SID conversion */
+
+       WINBINDD_LOOKUPSID,
+       WINBINDD_LOOKUPNAME,
+
+       /* Lookup functions */
+
+       WINBINDD_SID_TO_UID,       
+       WINBINDD_SID_TO_GID,
+       WINBINDD_UID_TO_SID,
+       WINBINDD_GID_TO_SID,
+
+       /* Miscellaneous other stuff */
+
+       WINBINDD_CHECK_MACHACC,     /* Check machine account pw works */
+       WINBINDD_PING,              /* Just tell me winbind is running */
+       WINBINDD_INFO,              /* Various bit of info.  Currently just tidbits */
+       WINBINDD_DOMAIN_NAME,       /* The domain this winbind server is a member of (lp_workgroup()) */
+
+       WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */
+
+       /* WINS commands */
+
+       WINBINDD_WINS_BYIP,
+       WINBINDD_WINS_BYNAME,
+
+       /* this is like GETGRENT but gives an empty group list */
+       WINBINDD_GETGRLST,
+
+       WINBINDD_NETBIOS_NAME,       /* The netbios name of the server */
+       /* Placeholder for end of cmd list */
+
+       WINBINDD_NUM_CMDS
+};
+
+#define WINBIND_PAM_INFO3_NDR  0x0001
+#define WINBIND_PAM_INFO3_TEXT 0x0002
+#define WINBIND_PAM_NTKEY      0x0004
+#define WINBIND_PAM_LMKEY      0x0008
+#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010
+
+/* Winbind request structure */
+
+struct winbindd_request {
+       uint32 length;
+       enum winbindd_cmd cmd;   /* Winbindd command to execute */
+       pid_t pid;               /* pid of calling process */
+
+       union {
+               fstring winsreq;     /* WINS request */
+               fstring username;    /* getpwnam */
+               fstring groupname;   /* getgrnam */
+               uid_t uid;           /* getpwuid, uid_to_sid */
+               gid_t gid;           /* getgrgid, gid_to_sid */
+               struct {
+                       /* We deliberatedly don't split into domain/user to
+                           avoid having the client know what the separator
+                           character is. */    
+                       fstring user;
+                       fstring pass;
+               } auth;              /* pam_winbind auth module */
+                struct {
+                        unsigned char chal[8];
+                        fstring user;
+                        fstring domain;
+                        fstring lm_resp;
+                        uint16 lm_resp_len;
+                        fstring nt_resp;
+                        uint16 nt_resp_len;
+                       fstring workstation;
+                       uint32 flags;
+                } auth_crap;
+                struct {
+                    fstring user;
+                    fstring oldpass;
+                    fstring newpass;
+                } chauthtok;         /* pam_winbind passwd module */
+               fstring sid;         /* lookupsid, sid_to_[ug]id */
+               struct {
+                       fstring dom_name;       /* lookupname */
+                       fstring name;       
+               } name;
+               uint32 num_entries;  /* getpwent, getgrent */
+       } data;
+       char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+       WINBINDD_ERROR,
+       WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+struct winbindd_response {
+    
+       /* Header information */
+
+       uint32 length;                        /* Length of response */
+       enum winbindd_result result;          /* Result code */
+
+       /* Fixed length return data */
+       
+       union {
+               int interface_version;  /* Try to ensure this is always in the same spot... */
+               
+               fstring winsresp;               /* WINS response */
+
+               /* getpwnam, getpwuid */
+               
+               struct winbindd_pw {
+                       fstring pw_name;
+                       fstring pw_passwd;
+                       uid_t pw_uid;
+                       gid_t pw_gid;
+                       fstring pw_gecos;
+                       fstring pw_dir;
+                       fstring pw_shell;
+               } pw;
+
+               /* getgrnam, getgrgid */
+
+               struct winbindd_gr {
+                       fstring gr_name;
+                       fstring gr_passwd;
+                       gid_t gr_gid;
+                       int num_gr_mem;
+                       int gr_mem_ofs;   /* offset to group membership */
+               } gr;
+
+               uint32 num_entries; /* getpwent, getgrent */
+               struct winbindd_sid {
+                       fstring sid;        /* lookupname, [ug]id_to_sid */
+                       int type;
+               } sid;
+               struct winbindd_name {
+                       fstring dom_name;       /* lookupsid */
+                       fstring name;       
+                       int type;
+               } name;
+               uid_t uid;          /* sid_to_uid */
+               gid_t gid;          /* sid_to_gid */
+               struct winbindd_info {
+                       char winbind_separator;
+                       fstring samba_version;
+               } info;
+               fstring domain_name;
+               fstring netbios_name;
+
+               struct auth_reply {
+                       uint32 nt_status;
+                       fstring nt_status_string;
+                       fstring error_string;
+                       int pam_error;
+                       char nt_session_key[16];
+                       char first_8_lm_hash[8];
+               } auth;
+       } data;
+
+       /* Variable length return data */
+
+       void *extra_data;               /* getgrnam, getgrgid, getgrent */
+};
+
+#endif
diff --git a/source4/nsswitch/winbindd_pam.c b/source4/nsswitch/winbindd_pam.c
new file mode 100644 (file)
index 0000000..8a0326b
--- /dev/null
@@ -0,0 +1,368 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - pam auth funcions
+
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) Tim Potter 2001
+   Copyright (C) Andrew Bartlett 2001-2002
+   
+   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 "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, 
+                                   struct winbindd_cli_state *state, 
+                                   NET_USER_INFO_3 *info3) 
+{
+       prs_struct ps;
+       uint32 size;
+       if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+               prs_mem_free(&ps);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       size = prs_data_size(&ps);
+       state->response.extra_data = malloc(size);
+       if (!state->response.extra_data) {
+               prs_mem_free(&ps);
+               return NT_STATUS_NO_MEMORY;
+       }
+       prs_copy_all_data_out(state->response.extra_data, &ps);
+       state->response.length += size;
+       prs_mem_free(&ps);
+       return NT_STATUS_OK;
+}
+
+/* Return a password structure from a username.  */
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) 
+{
+       NTSTATUS result;
+       fstring name_domain, name_user;
+       unsigned char trust_passwd[16];
+       time_t last_change_time;
+        uint32 smb_uid_low;
+        NET_USER_INFO_3 info3;
+        struct cli_state *cli = NULL;
+       uchar chal[8];
+       TALLOC_CTX *mem_ctx = NULL;
+       DATA_BLOB lm_resp;
+       DATA_BLOB nt_resp;
+
+       /* Ensure null termination */
+       state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
+       DEBUG(3, ("[%5d]: pam auth %s\n", state->pid,
+                 state->request.data.auth.user));
+
+       if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) {
+               DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n"));
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       /* Parse domain and username */
+       
+       if (!parse_domain_user(state->request.data.auth.user, name_domain, 
+                              name_user)) {
+               DEBUG(5,("no domain separator (%s) in username (%s) - failing auth\n", lp_winbind_separator(), state->request.data.auth.user));
+               result = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       {
+               unsigned char local_lm_response[24];
+               unsigned char local_nt_response[24];
+               
+               generate_random_buffer(chal, 8, False);
+               SMBencrypt(state->request.data.auth.pass, chal, local_lm_response);
+               
+               SMBNTencrypt(state->request.data.auth.pass, chal, local_nt_response);
+
+               lm_resp = data_blob_talloc(mem_ctx, local_lm_response, sizeof(local_lm_response));
+               nt_resp = data_blob_talloc(mem_ctx, local_nt_response, sizeof(local_nt_response));
+       }
+       
+       /*
+        * Get the machine account password for our primary domain
+        */
+
+       if (!secrets_fetch_trust_account_password(
+                lp_workgroup(), trust_passwd, &last_change_time)) {
+               DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
+                          "password for domain %s\n", lp_workgroup()));
+               result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               goto done;
+       }
+
+       /* We really don't care what LUID we give the user. */
+
+       generate_random_buffer( (unsigned char *)&smb_uid_low, 4, False);
+
+       ZERO_STRUCT(info3);
+       
+       /* Don't shut this down - it belongs to the connection cache code */
+        result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+                goto done;
+        }
+
+       result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+                                               name_user, name_domain, 
+                                               lp_netbios_name(), chal, 
+                                               lm_resp, nt_resp, 
+                                               &info3);
+        
+       uni_group_cache_store_netlogon(mem_ctx, &info3);
+done:
+
+       state->response.data.auth.nt_status = NT_STATUS_V(result);
+       fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+       fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+       state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", 
+             state->request.data.auth.user, 
+             state->response.data.auth.nt_status_string,
+             state->response.data.auth.pam_error));          
+
+       if (mem_ctx) 
+               talloc_destroy(mem_ctx);
+       
+       return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+       
+/* Challenge Response Authentication Protocol */
+
+enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) 
+{
+       NTSTATUS result;
+       unsigned char trust_passwd[16];
+       time_t last_change_time;
+        NET_USER_INFO_3 info3;
+        struct cli_state *cli = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
+       char *user = NULL;
+       const char *domain = NULL;
+       const char *contact_domain;
+       const char *workstation;
+
+       DATA_BLOB lm_resp, nt_resp;
+
+       /* Ensure null termination */
+       state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]='\0';
+
+       if (!(mem_ctx = talloc_init("winbind pam auth crap for (utf8) %s", state->request.data.auth_crap.user))) {
+               DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+        if (pull_utf8_talloc(mem_ctx, &user, state->request.data.auth_crap.user) == (size_t)-1) {
+               DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+       }
+
+       if (*state->request.data.auth_crap.domain) {
+               char *dom = NULL;
+               if (pull_utf8_talloc(mem_ctx, &dom, state->request.data.auth_crap.domain) == (size_t)-1) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+               }
+               domain = dom;
+       } else if (lp_winbind_use_default_domain()) {
+               domain = lp_workgroup();
+       } else {
+               DEBUG(5,("no domain specified with username (%s) - failing auth\n", 
+                        user));
+               result = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid,
+                 domain, user));
+
+       if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) {
+               contact_domain = domain;
+       } else {
+               contact_domain = lp_workgroup();
+       }
+
+       if (*state->request.data.auth_crap.workstation) {
+               char *wrk = NULL;
+               if (pull_utf8_talloc(mem_ctx, &wrk, state->request.data.auth_crap.workstation) == (size_t)-1) {
+                       DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+               }
+               workstation = wrk;
+       } else {
+               workstation = lp_netbios_name();
+       }
+
+       if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
+               || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
+               DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", 
+                         state->request.data.auth_crap.lm_resp_len, 
+                         state->request.data.auth_crap.nt_resp_len));
+               result = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len);
+       nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len);
+       
+       /*
+        * Get the machine account password for the domain to contact.
+        * This is either our own domain for a workstation, or possibly
+        * any domain for a PDC with trusted domains.
+        */
+
+       if (!secrets_fetch_trust_account_password (
+                contact_domain, trust_passwd, &last_change_time)) {
+               DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
+                          "password for domain %s\n", contact_domain));
+               result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               goto done;
+       }
+
+       ZERO_STRUCT(info3);
+
+       /* Don't shut this down - it belongs to the connection cache code */
+        result = cm_get_netlogon_cli(contact_domain, trust_passwd, &cli);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", nt_errstr(result)));
+                goto done;
+        }
+
+       result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+                                               user, domain,
+                                               workstation, state->request.data.auth_crap.chal, 
+                                               lm_resp, nt_resp, 
+                                               &info3);
+        
+       if (NT_STATUS_IS_OK(result)) {
+               uni_group_cache_store_netlogon(mem_ctx, &info3);
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) {
+                       result = append_info3_as_ndr(mem_ctx, state, &info3);
+               }
+
+#if 0
+               /* we don't currently do this stuff right */
+               /* Doing an assert in a daemon is going to be a pretty bad 
+                   idea. - tpot */
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) {
+                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); 
+                       memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+               }
+               if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) {
+                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); 
+                       memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+               }
+#endif
+       }
+
+done:
+
+       state->response.data.auth.nt_status = NT_STATUS_V(result);
+       push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result));
+       push_utf8_fstring(state->response.data.auth.error_string, nt_errstr(result));
+       state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
+             ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n", 
+              domain,
+              user,
+              state->response.data.auth.nt_status_string,
+              state->response.data.auth.pam_error));         
+
+       if (mem_ctx) 
+               talloc_destroy(mem_ctx);
+       
+       return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* Change a user password */
+
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state)
+{
+       NTSTATUS result;
+       char *oldpass, *newpass;
+       fstring domain, user;
+       CLI_POLICY_HND *hnd;
+
+       DEBUG(3, ("[%5d]: pam chauthtok %s\n", state->pid,
+               state->request.data.chauthtok.user));
+
+       /* Setup crap */
+
+       if (state == NULL)
+               return WINBINDD_ERROR;
+
+       if (!parse_domain_user(state->request.data.chauthtok.user, domain, 
+                              user)) {
+               result = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       /* Change password */
+
+       oldpass = state->request.data.chauthtok.oldpass;
+       newpass = state->request.data.chauthtok.newpass;
+
+       /* Get sam handle */
+
+       if (!(hnd = cm_get_sam_handle(domain))) {
+               DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+               result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+               goto done;
+       }
+
+       if (!cli_oem_change_password(hnd->cli, user, newpass, oldpass)) {
+               DEBUG(1, ("password change failed for user %s/%s\n", domain, 
+                         user));
+               result = NT_STATUS_WRONG_PASSWORD;
+       } else {
+               result = NT_STATUS_OK;
+       }
+
+done:    
+       state->response.data.auth.nt_status = NT_STATUS_V(result);
+       fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+       fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+       state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+       DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, 
+             ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n", 
+              domain,
+              user,
+              state->response.data.auth.nt_status_string,
+              state->response.data.auth.pam_error));         
+
+       return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
diff --git a/source4/nsswitch/winbindd_rpc.c b/source4/nsswitch/winbindd_rpc.c
new file mode 100644 (file)
index 0000000..9989f27
--- /dev/null
@@ -0,0 +1,776 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind rpc backend functions
+
+   Copyright (C) Tim Potter 2000-2001,2003
+   Copyright (C) Andrew Tridgell 2001
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+/* Query display info for a domain.  This returns enough information plus a
+   bit extra to give an overview of domain users for the User Manager
+   application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+                              TALLOC_CTX *mem_ctx,
+                              uint32 *num_entries, 
+                              WINBIND_USERINFO **info)
+{
+       CLI_POLICY_HND *hnd;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND dom_pol;
+       BOOL got_dom_pol = False;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       unsigned int i, start_idx, retry;
+
+       DEBUG(3,("rpc: query_user_list\n"));
+
+       *num_entries = 0;
+       *info = NULL;
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       goto done;
+
+               /* Get domain handle */
+
+               result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+                                               des_access, &domain->sid, &dom_pol);
+
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_dom_pol = True;
+
+       i = start_idx = 0;
+       do {
+               TALLOC_CTX *ctx2;
+               char **dom_users;
+               uint32 num_dom_users, *dom_rids, j, size = 0xffff;
+               uint16 acb_mask = ACB_NORMAL;
+
+               if (!(ctx2 = talloc_init("winbindd enum_users"))) {
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }               
+
+               result = cli_samr_enum_dom_users(
+                       hnd->cli, ctx2, &dom_pol, &start_idx, acb_mask,
+                       size, &dom_users, &dom_rids, &num_dom_users);
+
+               *num_entries += num_dom_users;
+
+               *info = talloc_realloc(
+                       mem_ctx, *info, 
+                       (*num_entries) * sizeof(WINBIND_USERINFO));
+
+               if (!(*info)) {
+                       result = NT_STATUS_NO_MEMORY;
+                       talloc_destroy(ctx2);
+                       goto done;
+               }
+
+               for (j = 0; j < num_dom_users; i++, j++) {
+                       (*info)[i].acct_name = 
+                               talloc_strdup(mem_ctx, dom_users[j]);
+                       (*info)[i].full_name = talloc_strdup(mem_ctx, "");
+                       (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, dom_rids[j]);
+                       /* For the moment we set the primary group for
+                          every user to be the Domain Users group.
+                          There are serious problems with determining
+                          the actual primary group for large domains.
+                          This should really be made into a 'winbind
+                          force group' smb.conf parameter or
+                          something like that. */
+                       (*info)[i].group_sid 
+                               = rid_to_talloced_sid(domain, 
+                                                     mem_ctx, 
+                                                     DOMAIN_GROUP_RID_USERS);
+               }
+
+               talloc_destroy(ctx2);
+
+       } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+
+       if (got_dom_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       CLI_POLICY_HND *hnd;
+       POLICY_HND dom_pol;
+       NTSTATUS status;
+       uint32 start = 0;
+       int retry;
+
+       *num_entries = 0;
+       *info = NULL;
+
+       DEBUG(3,("rpc: enum_dom_groups\n"));
+
+       retry = 0;
+       do {
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       return NT_STATUS_UNSUCCESSFUL;
+
+               status = cli_samr_open_domain(hnd->cli, mem_ctx,
+                                             &hnd->pol, des_access, &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (!NT_STATUS_IS_OK(status))
+               return status;
+
+       do {
+               struct acct_info *info2 = NULL;
+               uint32 count = 0;
+               TALLOC_CTX *mem_ctx2;
+
+               mem_ctx2 = talloc_init("enum_dom_groups[rpc]");
+
+               /* start is updated by this call. */
+               status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol,
+                                                 &start,
+                                                 0xFFFF, /* buffer size? */
+                                                 &info2, &count);
+
+               if (!NT_STATUS_IS_OK(status) && 
+                   !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+                       talloc_destroy(mem_ctx2);
+                       break;
+               }
+
+               (*info) = talloc_realloc(mem_ctx, *info, 
+                                        sizeof(**info) * ((*num_entries) + count));
+               if (! *info) {
+                       talloc_destroy(mem_ctx2);
+                       cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+               (*num_entries) += count;
+               talloc_destroy(mem_ctx2);
+       } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+       cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       return status;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_entries, 
+                               struct acct_info **info)
+{
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       CLI_POLICY_HND *hnd;
+       POLICY_HND dom_pol;
+       NTSTATUS result;
+       int retry;
+
+       *num_entries = 0;
+       *info = NULL;
+
+       retry = 0;
+       do {
+               if ( !(hnd = cm_get_sam_handle(domain->name)) )
+                       return NT_STATUS_UNSUCCESSFUL;
+
+               result = cli_samr_open_domain( hnd->cli, mem_ctx, &hnd->pol, 
+                                               des_access, &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if ( !NT_STATUS_IS_OK(result))
+               return result;
+
+       do {
+               struct acct_info *info2 = NULL;
+               uint32 count = 0, start = *num_entries;
+               TALLOC_CTX *mem_ctx2;
+
+               mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]");
+
+               result = cli_samr_enum_als_groups( hnd->cli, mem_ctx2, &dom_pol,
+                                         &start, 0xFFFF, &info2, &count);
+                                         
+               if ( !NT_STATUS_IS_OK(result) 
+                       && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) ) 
+               {
+                       talloc_destroy(mem_ctx2);
+                       break;
+               }
+
+               (*info) = talloc_realloc(mem_ctx, *info, 
+                                        sizeof(**info) * ((*num_entries) + count));
+               if (! *info) {
+                       talloc_destroy(mem_ctx2);
+                       cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+               (*num_entries) += count;
+               talloc_destroy(mem_ctx2);
+       } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+       cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           const char *name,
+                           DOM_SID *sid,
+                           enum SID_NAME_USE *type)
+{
+       CLI_POLICY_HND *hnd;
+       NTSTATUS status;
+       DOM_SID *sids = NULL;
+       uint32 *types = NULL;
+       const char *full_name;
+       int retry;
+
+       DEBUG(3,("rpc: name_to_sid name=%s\n", name));
+
+       full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain->name, name);
+       
+       if (!full_name) {
+               DEBUG(0, ("talloc_asprintf failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       retry = 0;
+       do {
+               if (!(hnd = cm_get_lsa_handle(domain->name))) {
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+        
+               status = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1, 
+                                             &full_name, &sids, &types);
+       } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+        
+       /* Return rid and type if lookup successful */
+
+       if (NT_STATUS_IS_OK(status)) {
+               sid_copy(sid, &sids[0]);
+               *type = types[0];
+       }
+
+       return status;
+}
+
+/*
+  convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+                           TALLOC_CTX *mem_ctx,
+                           DOM_SID *sid,
+                           char **name,
+                           enum SID_NAME_USE *type)
+{
+       CLI_POLICY_HND *hnd;
+       char **domains;
+       char **names;
+       uint32 *types;
+       NTSTATUS status;
+       int retry;
+
+       DEBUG(3,("rpc: sid_to_name\n"));
+
+       retry = 0;
+       do {
+               if (!(hnd = cm_get_lsa_handle(domain->name)))
+                       return NT_STATUS_UNSUCCESSFUL;
+        
+               status = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+                                            1, sid, &domains, &names, &types);
+       } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (NT_STATUS_IS_OK(status)) {
+               *type = types[0];
+               *name = names[0];
+               DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+               /* Paranoia */
+               if (strcasecmp(domain->name, domains[0]) != 0) {
+                       DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain->name, domains[0]));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+       return status;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain, 
+                          TALLOC_CTX *mem_ctx, 
+                          DOM_SID *user_sid, 
+                          WINBIND_USERINFO *user_info)
+{
+       CLI_POLICY_HND *hnd;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND dom_pol, user_pol;
+       BOOL got_dom_pol = False, got_user_pol = False;
+       SAM_USERINFO_CTR *ctr;
+       int retry;
+       fstring sid_string;
+       uint32 user_rid;
+
+       DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid)));
+       if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+               goto done;
+       }
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       goto done;
+
+               /* Get domain handle */
+
+               result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+                                             SEC_RIGHTS_MAXIMUM_ALLOWED, 
+                                             &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_dom_pol = True;
+
+       /* Get user handle */
+       result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+                                   SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_user_pol = True;
+
+       /* Get user info */
+       result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol, 
+                                        0x15, &ctr);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+       got_user_pol = False;
+
+       user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid);
+       user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid);
+       user_info->acct_name = unistr2_tdup(mem_ctx, 
+                                           &ctr->info.id21->uni_user_name);
+       user_info->full_name = unistr2_tdup(mem_ctx, 
+                                           &ctr->info.id21->uni_full_name);
+
+ done:
+       /* Clean up policy handles */
+       if (got_user_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+       if (got_dom_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       return result;
+}                                   
+
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+                                 TALLOC_CTX *mem_ctx,
+                                 DOM_SID *user_sid,
+                                 uint32 *num_groups, DOM_SID ***user_gids)
+{
+       CLI_POLICY_HND *hnd;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND dom_pol, user_pol;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       BOOL got_dom_pol = False, got_user_pol = False;
+       DOM_GID *user_groups;
+       unsigned int i;
+       unsigned int retry;
+       fstring sid_string;
+       uint32 user_rid;
+
+       DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid)));
+
+       *num_groups = 0;
+
+       /* First try cached universal groups from logon */
+       *user_gids = uni_group_cache_fetch(&domain->sid, user_sid, mem_ctx, num_groups);
+       if((*num_groups > 0) && *user_gids) {
+               return NT_STATUS_OK;
+       } else {
+           *user_gids = NULL;
+           *num_groups = 0;
+       }
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       goto done;
+
+               /* Get domain handle */
+               result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+                                             des_access, &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_dom_pol = True;
+
+
+       if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+               goto done;
+       }
+
+       /* Get user handle */
+       result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+                                       des_access, user_rid, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_user_pol = True;
+
+       /* Query user rids */
+       result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol, 
+                                          num_groups, &user_groups);
+
+       if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0)
+               goto done;
+
+       (*user_gids) = talloc(mem_ctx, sizeof(uint32) * (*num_groups));
+       if (!(*user_gids)) {
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       for (i=0;i<(*num_groups);i++) {
+               (*user_gids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid);
+       }
+       
+ done:
+       /* Clean up policy handles */
+       if (got_user_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+       if (got_dom_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       return result;
+}
+
+
+/* Lookup group membership given a rid.   */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               DOM_SID *group_sid, uint32 *num_names, 
+                               DOM_SID ***sid_mem, char ***names, 
+                               uint32 **name_types)
+{
+        CLI_POLICY_HND *hnd;
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        uint32 i, total_names = 0;
+        POLICY_HND dom_pol, group_pol;
+        uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+        BOOL got_dom_pol = False, got_group_pol = False;
+       uint32 *rid_mem = NULL;
+       uint32 group_rid;
+       int retry;
+       unsigned int j;
+       fstring sid_string;
+
+       DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid)));
+
+       if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) {
+               goto done;
+       }
+
+       *num_names = 0;
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       goto done;
+
+               /* Get domain handle */
+
+               result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+                               des_access, &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+        if (!NT_STATUS_IS_OK(result))
+                goto done;
+
+        got_dom_pol = True;
+
+        /* Get group handle */
+
+        result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol,
+                                     des_access, group_rid, &group_pol);
+
+        if (!NT_STATUS_IS_OK(result))
+                goto done;
+
+        got_group_pol = True;
+
+        /* Step #1: Get a list of user rids that are the members of the
+           group. */
+
+        result = cli_samr_query_groupmem(hnd->cli, mem_ctx,
+                                         &group_pol, num_names, &rid_mem,
+                                         name_types);
+
+        if (!NT_STATUS_IS_OK(result))
+                goto done;
+
+        /* Step #2: Convert list of rids into list of usernames.  Do this
+           in bunches of ~1000 to avoid crashing NT4.  It looks like there
+           is a buffer overflow or something like that lurking around
+           somewhere. */
+
+#define MAX_LOOKUP_RIDS 900
+
+        *names = talloc_zero(mem_ctx, *num_names * sizeof(char *));
+        *name_types = talloc_zero(mem_ctx, *num_names * sizeof(uint32));
+        *sid_mem = talloc_zero(mem_ctx, *num_names * sizeof(DOM_SID *));
+
+       for (j=0;j<(*num_names);j++) {
+               (*sid_mem)[j] = rid_to_talloced_sid(domain, mem_ctx, (rid_mem)[j]);
+       }
+       
+       if (!*names || !*name_types) {
+               result = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+        for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
+                int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
+                uint32 tmp_num_names = 0;
+                char **tmp_names = NULL;
+                uint32 *tmp_types = NULL;
+
+                /* Lookup a chunk of rids */
+
+                result = cli_samr_lookup_rids(hnd->cli, mem_ctx,
+                                              &dom_pol, 1000, /* flags */
+                                              num_lookup_rids,
+                                              &rid_mem[i],
+                                              &tmp_num_names,
+                                              &tmp_names, &tmp_types);
+
+                if (!NT_STATUS_IS_OK(result))
+                        goto done;
+
+                /* Copy result into array.  The talloc system will take
+                   care of freeing the temporary arrays later on. */
+
+                memcpy(&(*names)[i], tmp_names, sizeof(char *) * 
+                       tmp_num_names);
+
+                memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) *
+                       tmp_num_names);
+               
+                total_names += tmp_num_names;
+        }
+
+        *num_names = total_names;
+
+ done:
+        if (got_group_pol)
+                cli_samr_close(hnd->cli, mem_ctx, &group_pol);
+
+        if (got_dom_pol)
+                cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+        return result;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+       TALLOC_CTX *mem_ctx;
+       CLI_POLICY_HND *hnd;
+       SAM_UNK_CTR ctr;
+       uint16 switch_value = 2;
+       NTSTATUS result;
+       uint32 seqnum = DOM_SEQUENCE_NONE;
+       POLICY_HND dom_pol;
+       BOOL got_dom_pol = False;
+       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       int retry;
+
+       DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name));
+
+       *seq = DOM_SEQUENCE_NONE;
+
+       if (!(mem_ctx = talloc_init("sequence_number[rpc]")))
+               return NT_STATUS_NO_MEMORY;
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+               if (!(hnd = cm_get_sam_handle(domain->name)))
+                       goto done;
+
+               /* Get domain handle */
+               result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, 
+                                     des_access, &domain->sid, &dom_pol);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_dom_pol = True;
+
+       /* Query domain info */
+
+       result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol,
+                                        switch_value, &ctr);
+
+       if (NT_STATUS_IS_OK(result)) {
+               seqnum = ctr.info.inf2.seq_num;
+               DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)seqnum ));
+       } else {
+               DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
+                       (unsigned)seqnum, domain->name ));
+       }
+
+  done:
+
+       if (got_dom_pol)
+               cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+       talloc_destroy(mem_ctx);
+
+       *seq = seqnum;
+
+       return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+                               TALLOC_CTX *mem_ctx,
+                               uint32 *num_domains,
+                               char ***names,
+                               char ***alt_names,
+                               DOM_SID **dom_sids)
+{
+       CLI_POLICY_HND *hnd;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 enum_ctx = 0;
+       int retry;
+
+       DEBUG(3,("rpc: trusted_domains\n"));
+
+       *num_domains = 0;
+       *alt_names = NULL;
+
+       retry = 0;
+       do {
+               if (!(hnd = cm_get_lsa_handle(lp_workgroup())))
+                       goto done;
+
+               result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx,
+                                               &hnd->pol, &enum_ctx,
+                                               num_domains, names, dom_sids);
+       } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&  hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+       return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *mem_ctx;
+       CLI_POLICY_HND *hnd;
+       fstring level5_dom;
+       int retry;
+
+       DEBUG(3,("rpc: domain_sid\n"));
+
+       if (!(mem_ctx = talloc_init("domain_sid[rpc]")))
+               return NT_STATUS_NO_MEMORY;
+
+       retry = 0;
+       do {
+               /* Get sam handle */
+               if (!(hnd = cm_get_lsa_handle(domain->name)))
+                       goto done;
+
+               status = cli_lsa_query_info_policy(hnd->cli, mem_ctx,
+                                          &hnd->pol, 0x05, level5_dom, sid);
+       } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) &&  hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+       talloc_destroy(mem_ctx);
+       return status;
+}
+
+/* find alternate names list for the domain - none for rpc */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+       return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods msrpc_methods = {
+       False,
+       query_user_list,
+       enum_dom_groups,
+       enum_local_groups,
+       name_to_sid,
+       sid_to_name,
+       query_user,
+       lookup_usergroups,
+       lookup_groupmem,
+       sequence_number,
+       trusted_domains,
+       domain_sid,
+       alternate_name
+};
diff --git a/source4/nsswitch/winbindd_sid.c b/source4/nsswitch/winbindd_sid.c
new file mode 100644 (file)
index 0000000..6ab2eaa
--- /dev/null
@@ -0,0 +1,235 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - sid related functions
+
+   Copyright (C) Tim Potter 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Convert a string  */
+
+enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state)
+{
+       extern DOM_SID global_sid_Builtin;
+       enum SID_NAME_USE type;
+       DOM_SID sid, tmp_sid;
+       uint32 rid;
+       fstring name;
+       fstring dom_name;
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+       DEBUG(3, ("[%5d]: lookupsid %s\n", state->pid, 
+                 state->request.data.sid));
+
+       /* Lookup sid from PDC using lsa_lookup_sids() */
+
+       if (!string_to_sid(&sid, state->request.data.sid)) {
+               DEBUG(5, ("%s not a SID\n", state->request.data.sid));
+               return WINBINDD_ERROR;
+       }
+
+       /* Don't look up BUILTIN sids */
+
+       sid_copy(&tmp_sid, &sid);
+       sid_split_rid(&tmp_sid, &rid);
+
+       if (sid_equal(&tmp_sid, &global_sid_Builtin)) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Lookup the sid */
+
+       if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) {
+               return WINBINDD_ERROR;
+       }
+
+       fstrcpy(state->response.data.name.dom_name, dom_name);
+       fstrcpy(state->response.data.name.name, name);
+
+       state->response.data.name.type = type;
+
+       return WINBINDD_OK;
+}
+
+
+/**
+ * Look up the SID for a qualified name.  
+ **/
+enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state)
+{
+       enum SID_NAME_USE type;
+       fstring sid_str;
+       char *name_domain, *name_user;
+       DOM_SID sid;
+       struct winbindd_domain *domain;
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
+       DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid,
+                 state->request.data.name.dom_name, 
+                 lp_winbind_separator(),
+                 state->request.data.name.name));
+
+       name_domain = state->request.data.name.dom_name;
+       name_user = state->request.data.name.name;
+
+       if ((domain = find_domain_from_name(name_domain)) == NULL) {
+               DEBUG(0, ("could not find domain entry for domain %s\n", 
+                         name_domain));
+               return WINBINDD_ERROR;
+       }
+
+       /* Lookup name from PDC using lsa_lookup_names() */
+       if (!winbindd_lookup_sid_by_name(domain, name_user, &sid, &type)) {
+               return WINBINDD_ERROR;
+       }
+
+       sid_to_string(sid_str, &sid);
+       fstrcpy(state->response.data.sid.sid, sid_str);
+       state->response.data.sid.type = type;
+
+       return WINBINDD_OK;
+}
+
+/* Convert a sid to a uid.  We assume we only have one rid attached to the
+   sid. */
+
+enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state)
+{
+       DOM_SID sid;
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+       DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid,
+                 state->request.data.sid));
+
+       /* Split sid into domain sid and user rid */
+       if (!string_to_sid(&sid, state->request.data.sid)) {
+               DEBUG(1, ("Could not get convert sid %s from string\n",
+                         state->request.data.sid));
+               return WINBINDD_ERROR;
+       }
+
+       /* Find uid for this sid and return it */
+       if (!winbindd_idmap_get_uid_from_sid(&sid, &state->response.data.uid)) {
+               DEBUG(1, ("Could not get uid for sid %s\n",
+                         state->request.data.sid));
+               return WINBINDD_ERROR;
+       }
+
+       return WINBINDD_OK;
+}
+
+/* Convert a sid to a gid.  We assume we only have one rid attached to the
+   sid.*/
+
+enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state)
+{
+       DOM_SID sid;
+
+       /* Ensure null termination */
+       state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+       DEBUG(3, ("[%5d]: sid to gid %s\n", state->pid, 
+                 state->request.data.sid));
+
+       if (!string_to_sid(&sid, state->request.data.sid)) {
+               DEBUG(1, ("Could not cvt string to sid %s\n",
+                         state->request.data.sid));
+               return WINBINDD_ERROR;
+       }
+
+       /* Find gid for this sid and return it */
+       if (!winbindd_idmap_get_gid_from_sid(&sid, &state->response.data.gid)) {
+               DEBUG(1, ("Could not get gid for sid %s\n",
+                         state->request.data.sid));
+               return WINBINDD_ERROR;
+       }
+
+       return WINBINDD_OK;
+}
+
+/* Convert a uid to a sid */
+
+enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state)
+{
+       DOM_SID sid;
+
+       /* Bug out if the uid isn't in the winbind range */
+
+       if ((state->request.data.uid < server_state.uid_low ) ||
+           (state->request.data.uid > server_state.uid_high)) {
+               return WINBINDD_ERROR;
+       }
+
+       DEBUG(3, ("[%5d]: uid to sid %d\n", state->pid, 
+                 state->request.data.uid));
+
+       /* Lookup rid for this uid */
+       if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid, &sid)) {
+               DEBUG(1, ("Could not convert uid %d to rid\n",
+                         state->request.data.uid));
+               return WINBINDD_ERROR;
+       }
+
+       sid_to_string(state->response.data.sid.sid, &sid);
+       state->response.data.sid.type = SID_NAME_USER;
+
+       return WINBINDD_OK;
+}
+
+/* Convert a gid to a sid */
+
+enum winbindd_result winbindd_gid_to_sid(struct winbindd_cli_state *state)
+{
+       DOM_SID sid;
+
+       /* Bug out if the gid isn't in the winbind range */
+
+       if ((state->request.data.gid < server_state.gid_low) ||
+           (state->request.data.gid > server_state.gid_high)) {
+               return WINBINDD_ERROR;
+       }
+
+       DEBUG(3, ("[%5d]: gid to sid %d\n", state->pid,
+                 state->request.data.gid));
+
+       /* Lookup sid for this uid */
+       if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &sid)) {
+               DEBUG(1, ("Could not convert gid %d to sid\n",
+                         state->request.data.gid));
+               return WINBINDD_ERROR;
+       }
+
+       /* Construct sid and return it */
+       sid_to_string(state->response.data.sid.sid, &sid);
+       state->response.data.sid.type = SID_NAME_DOM_GRP;
+
+       return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/winbindd_user.c b/source4/nsswitch/winbindd_user.c
new file mode 100644 (file)
index 0000000..ee05543
--- /dev/null
@@ -0,0 +1,607 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - user related functions
+
+   Copyright (C) Tim Potter 2000
+   Copyright (C) Jeremy Allison 2001.
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Fill a pwent structure with information we have obtained */
+
+static BOOL winbindd_fill_pwent(char *dom_name, char *user_name, 
+                               DOM_SID *user_sid, DOM_SID *group_sid,
+                               char *full_name, struct winbindd_pw *pw)
+{
+       extern userdom_struct current_user_info;
+       fstring output_username;
+       pstring homedir;
+       fstring sid_string;
+       
+       if (!pw || !dom_name || !user_name)
+               return False;
+       
+       /* Resolve the uid number */
+       
+       if (!winbindd_idmap_get_uid_from_sid(user_sid, 
+                                            &pw->pw_uid)) {
+               DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
+               return False;
+       }
+       
+       /* Resolve the gid number */   
+       
+       if (!winbindd_idmap_get_gid_from_sid(group_sid, 
+                                            &pw->pw_gid)) {
+               DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
+               return False;
+       }
+
+       /* Username */
+
+       fill_domain_username(output_username, dom_name, user_name); 
+
+       safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
+       
+       /* Full name (gecos) */
+       
+       safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
+
+       /* Home directory and shell - use template config parameters.  The
+          defaults are /tmp for the home directory and /bin/false for
+          shell. */
+       
+       /* The substitution of %U and %D in the 'template homedir' is done
+          by lp_string() calling standard_sub_basic(). */
+
+       fstrcpy(current_user_info.smb_name, user_name);
+       sub_set_smb_name(user_name);
+       fstrcpy(current_user_info.domain, dom_name);
+
+       pstrcpy(homedir, lp_template_homedir());
+       
+       safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
+       
+       safe_strcpy(pw->pw_shell, lp_template_shell(), 
+                   sizeof(pw->pw_shell) - 1);
+       
+       /* Password - set to "x" as we can't generate anything useful here.
+          Authentication can be done using the pam_winbind module. */
+
+       safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+       
+       return True;
+}
+
+/* Return a password structure from a username.  */
+
+enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state) 
+{
+       WINBIND_USERINFO user_info;
+       DOM_SID user_sid;
+       NTSTATUS status;
+       fstring name_domain, name_user;
+       enum SID_NAME_USE name_type;
+       struct winbindd_domain *domain;
+       TALLOC_CTX *mem_ctx;
+       
+       /* Ensure null termination */
+       state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+       DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
+                 state->request.data.username));
+       
+       /* Parse domain and username */
+
+       if (!parse_domain_user(state->request.data.username, name_domain, 
+                              name_user))
+               return WINBINDD_ERROR;
+       
+       if ((domain = find_domain_from_name(name_domain)) == NULL) {
+               DEBUG(5, ("no such domain: %s\n", name_domain));
+               return WINBINDD_ERROR;
+       }
+       
+       /* Get rid and name type from name */
+
+       if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, &name_type)) {
+               DEBUG(1, ("user '%s' does not exist\n", name_user));
+               return WINBINDD_ERROR;
+       }
+
+       if (name_type != SID_NAME_USER) {
+               DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, 
+                         name_type));
+               return WINBINDD_ERROR;
+       }
+       
+       /* Get some user info.  Split the user rid from the sid obtained
+          from the winbind_lookup_by_name() call and use it in a
+          winbind_lookup_userinfo() */
+    
+       if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])", 
+                                         name_domain, name_user))) {
+               DEBUG(1, ("out of memory\n"));
+               return WINBINDD_ERROR;
+       }
+
+       status = domain->methods->query_user(domain, mem_ctx, &user_sid, 
+                                            &user_info);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n", 
+                         name_domain, name_user));
+               talloc_destroy(mem_ctx);
+               return WINBINDD_ERROR;
+       }
+    
+       /* Now take all this information and fill in a passwd structure */      
+       if (!winbindd_fill_pwent(name_domain, name_user, 
+                                user_info.user_sid, user_info.group_sid, 
+                                user_info.full_name,
+                                &state->response.data.pw)) {
+               talloc_destroy(mem_ctx);
+               return WINBINDD_ERROR;
+       }
+
+       talloc_destroy(mem_ctx);
+       
+       return WINBINDD_OK;
+}       
+
+/* Return a password structure given a uid number */
+
+enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
+{
+       DOM_SID user_sid;
+       struct winbindd_domain *domain;
+       fstring dom_name;
+       fstring user_name;
+       enum SID_NAME_USE name_type;
+       WINBIND_USERINFO user_info;
+       gid_t gid;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+       
+       /* Bug out if the uid isn't in the winbind range */
+
+       if ((state->request.data.uid < server_state.uid_low ) ||
+           (state->request.data.uid > server_state.uid_high))
+               return WINBINDD_ERROR;
+
+       DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid, 
+                 state->request.data.uid));
+       
+       /* Get rid from uid */
+
+       if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid, 
+                                            &user_sid)) {
+               DEBUG(1, ("could not convert uid %d to SID\n", 
+                         state->request.data.uid));
+               return WINBINDD_ERROR;
+       }
+       
+       /* Get name and name type from rid */
+
+       if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
+               fstring temp;
+               
+               sid_to_string(temp, &user_sid);
+               DEBUG(1, ("could not lookup sid %s\n", temp));
+               return WINBINDD_ERROR;
+       }
+       
+       domain = find_domain_from_sid(&user_sid);
+
+       if (!domain) {
+               DEBUG(1,("Can't find domain from sid\n"));
+               return WINBINDD_ERROR;
+       }
+
+       /* Get some user info */
+       
+       if (!(mem_ctx = talloc_init("winbind_getpwuid(%d)",
+                                         state->request.data.uid))) {
+
+               DEBUG(1, ("out of memory\n"));
+               return WINBINDD_ERROR;
+       }
+
+       status = domain->methods->query_user(domain, mem_ctx, &user_sid, 
+                                            &user_info);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("error getting user info for user '%s'\n", 
+                         user_name));
+               talloc_destroy(mem_ctx);
+               return WINBINDD_ERROR;
+       }
+       
+       /* Resolve gid number */
+
+       if (!winbindd_idmap_get_gid_from_sid(user_info.group_sid, &gid)) {
+               DEBUG(1, ("error getting group id for user %s\n", user_name));
+               talloc_destroy(mem_ctx);
+               return WINBINDD_ERROR;
+       }
+
+       /* Fill in password structure */
+
+       if (!winbindd_fill_pwent(domain->name, user_name, user_info.user_sid, 
+                                user_info.group_sid,
+                                user_info.full_name, &state->response.data.pw)) {
+               talloc_destroy(mem_ctx);
+               return WINBINDD_ERROR;
+       }
+       
+       talloc_destroy(mem_ctx);
+
+       return WINBINDD_OK;
+}
+
+/*
+ * set/get/endpwent functions
+ */
+
+/* Rewind file pointer for ntdom passwd database */
+
+enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *domain;
+        
+       DEBUG(3, ("[%5d]: setpwent\n", state->pid));
+        
+       /* Check user has enabled this */
+        
+       if (!lp_winbind_enum_users())
+               return WINBINDD_ERROR;
+
+       /* Free old static data if it exists */
+        
+       if (state->getpwent_state != NULL) {
+               free_getent_state(state->getpwent_state);
+               state->getpwent_state = NULL;
+       }
+        
+       /* Create sam pipes for each domain we know about */
+        
+       for(domain = domain_list(); domain != NULL; domain = domain->next) {
+               struct getent_state *domain_state;
+                
+               /* Create a state record for this domain */
+                
+               if ((domain_state = (struct getent_state *)
+                    malloc(sizeof(struct getent_state))) == NULL)
+                       return WINBINDD_ERROR;
+                
+               ZERO_STRUCTP(domain_state);
+
+               fstrcpy(domain_state->domain_name, domain->name);
+
+               /* Add to list of open domains */
+                
+               DLIST_ADD(state->getpwent_state, domain_state);
+       }
+        
+       return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom passwd database */
+
+enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
+{
+       DEBUG(3, ("[%5d]: endpwent\n", state->pid));
+
+       free_getent_state(state->getpwent_state);    
+       state->getpwent_state = NULL;
+        
+       return WINBINDD_OK;
+}
+
+/* Get partial list of domain users for a domain.  We fill in the sam_entries,
+   and num_sam_entries fields with domain user information.  The dispinfo_ndx
+   field is incremented to the index of the next user to fetch.  Return True if
+   some users were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_user_entries(struct getent_state *ent)
+{
+       NTSTATUS status;
+       uint32 num_entries;
+       WINBIND_USERINFO *info;
+       struct getpwent_user *name_list = NULL;
+       BOOL result = False;
+       TALLOC_CTX *mem_ctx;
+       struct winbindd_domain *domain;
+       struct winbindd_methods *methods;
+       unsigned int i;
+
+       if (ent->num_sam_entries)
+               return False;
+
+       if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)",
+                                   ent->domain_name)))
+               return False;
+
+       if (!(domain = find_domain_from_name(ent->domain_name))) {
+               DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
+                         ent->domain_name));
+               return False;
+       }
+
+       methods = domain->methods;
+
+       /* Free any existing user info */
+
+       SAFE_FREE(ent->sam_entries);
+       ent->num_sam_entries = 0;
+       
+       /* Call query_user_list to get a list of usernames and user rids */
+
+       num_entries = 0;
+
+       status = methods->query_user_list(domain, mem_ctx, &num_entries, 
+                                         &info);
+               
+       if (num_entries) {
+               struct getpwent_user *tnl;
+               
+               tnl = (struct getpwent_user *)Realloc(name_list, 
+                                                     sizeof(struct getpwent_user) *
+                                                     (ent->num_sam_entries + 
+                                                      num_entries));
+               
+               if (!tnl) {
+                       DEBUG(0,("get_sam_user_entries realloc failed.\n"));
+                       SAFE_FREE(name_list);
+                       goto done;
+               } else
+                       name_list = tnl;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               /* Store account name and gecos */
+               if (!info[i].acct_name) {
+                       fstrcpy(name_list[ent->num_sam_entries + i].name, "");
+               } else {
+                       fstrcpy(name_list[ent->num_sam_entries + i].name, 
+                               info[i].acct_name); 
+               }
+               if (!info[i].full_name) {
+                       fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
+               } else {
+                       fstrcpy(name_list[ent->num_sam_entries + i].gecos, 
+                               info[i].full_name); 
+               }
+               
+               /* User and group ids */
+               sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid);
+               sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid);
+       }
+               
+       ent->num_sam_entries += num_entries;
+       
+       /* Fill in remaining fields */
+       
+       ent->sam_entries = name_list;
+       ent->sam_entry_index = 0;
+       result = ent->num_sam_entries > 0;
+
+ done:
+
+       talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+/* Fetch next passwd entry from ntdom database */
+
+#define MAX_GETPWENT_USERS 500
+
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
+{
+       struct getent_state *ent;
+       struct winbindd_pw *user_list;
+       int num_users, user_list_ndx = 0, i;
+
+       DEBUG(3, ("[%5d]: getpwent\n", state->pid));
+
+       /* Check user has enabled this */
+
+       if (!lp_winbind_enum_users())
+               return WINBINDD_ERROR;
+
+       /* Allocate space for returning a chunk of users */
+
+       num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
+       
+       if ((state->response.extra_data = 
+            malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
+               return WINBINDD_ERROR;
+
+       memset(state->response.extra_data, 0, num_users * 
+              sizeof(struct winbindd_pw));
+
+       user_list = (struct winbindd_pw *)state->response.extra_data;
+       
+       if (!(ent = state->getpwent_state))
+               return WINBINDD_ERROR;
+
+       /* Start sending back users */
+
+       for (i = 0; i < num_users; i++) {
+               struct getpwent_user *name_list = NULL;
+               fstring domain_user_name;
+               uint32 result;
+
+               /* Do we need to fetch another chunk of users? */
+
+               if (ent->num_sam_entries == ent->sam_entry_index) {
+
+                       while(ent && !get_sam_user_entries(ent)) {
+                               struct getent_state *next_ent;
+
+                               /* Free state information for this domain */
+
+                               SAFE_FREE(ent->sam_entries);
+
+                               next_ent = ent->next;
+                               DLIST_REMOVE(state->getpwent_state, ent);
+
+                               SAFE_FREE(ent);
+                               ent = next_ent;
+                       }
+                       /* No more domains */
+
+                       if (!ent) 
+                               break;
+               }
+
+               name_list = ent->sam_entries;
+
+               /* Skip machine accounts */
+
+               if (name_list[ent->sam_entry_index].
+                   name[strlen(name_list[ent->sam_entry_index].name) - 1] 
+                   == '$') {
+                       ent->sam_entry_index++;
+                       continue;
+               }
+
+               /* Lookup user info */
+               
+               result = winbindd_fill_pwent(
+                       ent->domain_name, 
+                       name_list[ent->sam_entry_index].name,
+                       &name_list[ent->sam_entry_index].user_sid,
+                       &name_list[ent->sam_entry_index].group_sid,
+                       name_list[ent->sam_entry_index].gecos,
+                       &user_list[user_list_ndx]);
+               
+               ent->sam_entry_index++;
+               
+               /* Add user to return list */
+               
+               if (result) {
+                               
+                       user_list_ndx++;
+                       state->response.data.num_entries++;
+                       state->response.length += 
+                               sizeof(struct winbindd_pw);
+
+               } else
+                       DEBUG(1, ("could not lookup domain user %s\n",
+                                 domain_user_name));
+       }
+
+       /* Out of domains */
+
+       return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain users without mapping to unix ids */
+
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *domain;
+       WINBIND_USERINFO *info;
+       uint32 num_entries = 0, total_entries = 0;
+       char *ted, *extra_data = NULL;
+       int extra_data_len = 0;
+       TALLOC_CTX *mem_ctx;
+       enum winbindd_result rv = WINBINDD_ERROR;
+
+       DEBUG(3, ("[%5d]: list users\n", state->pid));
+
+       if (!(mem_ctx = talloc_init("winbindd_list_users")))
+               return WINBINDD_ERROR;
+
+       /* Enumerate over trusted domains */
+
+       for (domain = domain_list(); domain; domain = domain->next) {
+               NTSTATUS status;
+               struct winbindd_methods *methods;
+               unsigned int i;
+
+               methods = domain->methods;
+
+               /* Query display info */
+               status = methods->query_user_list(domain, mem_ctx, 
+                                                 &num_entries, &info);
+
+               if (num_entries == 0)
+                       continue;
+
+               /* Allocate some memory for extra data */
+               total_entries += num_entries;
+                       
+               ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+                       
+               if (!ted) {
+                       DEBUG(0,("failed to enlarge buffer!\n"));
+                       SAFE_FREE(extra_data);
+                       goto done;
+               } else 
+                       extra_data = ted;
+                       
+               /* Pack user list into extra data fields */
+                       
+               for (i = 0; i < num_entries; i++) {
+                       fstring acct_name, name;
+                       
+                       if (!info[i].acct_name) {
+                               fstrcpy(acct_name, "");
+                       } else {
+                               fstrcpy(acct_name, info[i].acct_name);
+                       }
+                       
+                       fill_domain_username(name, domain->name, acct_name);
+                       
+                               /* Append to extra data */
+                       memcpy(&extra_data[extra_data_len], name, 
+                              strlen(name));
+                       extra_data_len += strlen(name);
+                       extra_data[extra_data_len++] = ',';
+               }   
+        }
+
+       /* Assign extra_data fields in response structure */
+
+       if (extra_data) {
+               extra_data[extra_data_len - 1] = '\0';
+               state->response.extra_data = extra_data;
+               state->response.length += extra_data_len;
+       }
+
+       /* No domains responded but that's still OK so don't return an
+          error. */
+
+       rv = WINBINDD_OK;
+
+ done:
+
+       talloc_destroy(mem_ctx);
+
+       return rv;
+}
diff --git a/source4/nsswitch/winbindd_util.c b/source4/nsswitch/winbindd_util.c
new file mode 100644 (file)
index 0000000..7ccf032
--- /dev/null
@@ -0,0 +1,553 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon for ntdom nss module
+
+   Copyright (C) Tim Potter 2000-2001
+   Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/**
+ * @file winbindd_util.c
+ *
+ * Winbind daemon for NT domain authentication nss module.
+ **/
+
+
+/**
+ * Used to clobber name fields that have an undefined value.
+ *
+ * Correct code should never look at a field that has this value.
+ **/
+
+static const fstring name_deadbeef = "<deadbeef>";
+
+/* The list of trusted domains.  Note that the list can be deleted and
+   recreated using the init_domain_list() function so pointers to
+   individual winbindd_domain structures cannot be made.  Keep a copy of
+   the domain name instead. */
+
+static struct winbindd_domain *_domain_list;
+
+struct winbindd_domain *domain_list(void)
+{
+       /* Initialise list */
+
+       if (!_domain_list)
+               init_domain_list();
+
+       return _domain_list;
+}
+
+/* Free all entries in the trusted domain list */
+
+void free_domain_list(void)
+{
+       struct winbindd_domain *domain = _domain_list;
+
+       while(domain) {
+               struct winbindd_domain *next = domain->next;
+               
+               DLIST_REMOVE(_domain_list, domain);
+               SAFE_FREE(domain);
+               domain = next;
+       }
+}
+
+
+/* Add a trusted domain to our list of domains */
+static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
+                                                 struct winbindd_methods *methods,
+                                                 DOM_SID *sid)
+{
+       struct winbindd_domain *domain;
+        
+       /* We can't call domain_list() as this function is called from
+          init_domain_list() and we'll get stuck in a loop. */
+       for (domain = _domain_list; domain; domain = domain->next) {
+               if (strcasecmp(domain_name, domain->name) == 0 ||
+                   strcasecmp(domain_name, domain->alt_name) == 0) {
+                       return domain;
+               }
+               if (alt_name && *alt_name) {
+                       if (strcasecmp(alt_name, domain->name) == 0 ||
+                           strcasecmp(alt_name, domain->alt_name) == 0) {
+                               return domain;
+                       }
+               }
+       }
+        
+       /* Create new domain entry */
+
+       if ((domain = (struct winbindd_domain *)
+            malloc(sizeof(*domain))) == NULL)
+               return NULL;
+
+       /* Fill in fields */
+        
+       ZERO_STRUCTP(domain);
+
+       /* prioritise the short name */
+       if (strchr_m(domain_name, '.') && alt_name && *alt_name) {
+               fstrcpy(domain->name, alt_name);
+               fstrcpy(domain->alt_name, domain_name);
+       } else {
+       fstrcpy(domain->name, domain_name);
+               if (alt_name) {
+                       fstrcpy(domain->alt_name, alt_name);
+               }
+       }
+
+       domain->methods = methods;
+       domain->sequence_number = DOM_SEQUENCE_NONE;
+       domain->last_seq_check = 0;
+       if (sid) {
+               sid_copy(&domain->sid, sid);
+       }
+       
+       /* see if this is a native mode win2k domain, but only for our own domain */
+          
+       if ( strequal( lp_workgroup(), domain_name) )   {
+               domain->native_mode = cm_check_for_native_mode_win2k( domain_name );
+               DEBUG(3,("add_trusted_domain: %s is a %s mode domain\n", domain_name,
+                                       domain->native_mode ? "native" : "mixed" ));
+       }       
+
+       /* Link to domain list */
+       DLIST_ADD(_domain_list, domain);
+        
+       DEBUG(1,("Added domain %s %s %s\n", 
+                domain->name, domain->alt_name,
+                sid?sid_string_static(&domain->sid):""));
+        
+       return domain;
+}
+
+
+/*
+  rescan our domains looking for new trusted domains
+ */
+void rescan_trusted_domains(BOOL force)
+{
+       struct winbindd_domain *domain;
+       TALLOC_CTX *mem_ctx;
+       static time_t last_scan;
+       time_t t = time(NULL);
+
+       /* trusted domains might be disabled */
+       if (!lp_allow_trusted_domains()) {
+               return;
+       }
+
+       /* Only rescan every few minutes but force if necessary */
+
+       if (((unsigned)(t - last_scan) < WINBINDD_RESCAN_FREQ) && !force)
+               return;
+
+       last_scan = t;
+
+       DEBUG(1, ("scanning trusted domain list\n"));
+
+       if (!(mem_ctx = talloc_init("init_domain_list")))
+               return;
+
+       for (domain = _domain_list; domain; domain = domain->next) {
+               NTSTATUS result;
+               char **names;
+               char **alt_names;
+               int num_domains = 0;
+               DOM_SID *dom_sids;
+               int i;
+
+               result = domain->methods->trusted_domains(domain, mem_ctx, &num_domains,
+                                                         &names, &alt_names, &dom_sids);
+               if (!NT_STATUS_IS_OK(result)) {
+                       continue;
+               }
+
+               /* Add each domain to the trusted domain list. Each domain inherits
+                  the access methods of its parent */
+               for(i = 0; i < num_domains; i++) {
+                       DEBUG(10,("Found domain %s\n", names[i]));
+                       add_trusted_domain(names[i], alt_names?alt_names[i]:NULL,
+                                          domain->methods, &dom_sids[i]);
+                       
+                       /* store trusted domain in the cache */
+                       trustdom_cache_store(mem_ctx, names[i], alt_names ? alt_names[i] : NULL,
+                                            &dom_sids[i], t + WINBINDD_RESCAN_FREQ);
+               }
+       }
+
+       talloc_destroy(mem_ctx);
+}
+
+/* Look up global info for the winbind daemon */
+BOOL init_domain_list(void)
+{
+       extern struct winbindd_methods cache_methods;
+       struct winbindd_domain *domain;
+
+       /* Free existing list */
+       free_domain_list();
+
+       /* Add ourselves as the first entry */
+       domain = add_trusted_domain(lp_workgroup(), NULL, &cache_methods, NULL);
+       if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) {
+               DEBUG(1, ("Could not fetch sid for our domain %s\n",
+                         domain->name));
+               return False;
+       }
+
+       /* get any alternate name for the primary domain */
+       cache_methods.alternate_name(domain);
+
+       /* do an initial scan for trusted domains */
+       rescan_trusted_domains(True);
+
+       return True;
+}
+
+/* Given a domain name, return the struct winbindd domain info for it 
+   if it is actually working. */
+
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+       struct winbindd_domain *domain;
+
+       /* Search through list */
+
+       for (domain = domain_list(); domain != NULL; domain = domain->next) {
+               if (strequal(domain_name, domain->name) ||
+                   (domain->alt_name[0] && strequal(domain_name, domain->alt_name)))
+                       return domain;
+       }
+
+       /* Not found */
+
+       return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid(DOM_SID *sid)
+{
+       struct winbindd_domain *domain;
+
+       /* Search through list */
+
+       for (domain = domain_list(); domain != NULL; domain = domain->next) {
+               if (sid_compare_domain(sid, &domain->sid) == 0)
+                       return domain;
+       }
+
+       /* Not found */
+
+       return NULL;
+}
+
+/* Lookup a sid in a domain from a name */
+
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain, 
+                                const char *name, DOM_SID *sid, 
+                                enum SID_NAME_USE *type)
+{
+       NTSTATUS result;
+        TALLOC_CTX *mem_ctx;
+       /* Don't bother with machine accounts */
+
+       if (name[strlen(name) - 1] == '$')
+               return False;
+
+       mem_ctx = talloc_init("lookup_sid_by_name for %s\n", name);
+       if (!mem_ctx) 
+               return False;
+        
+       /* Lookup name */
+       result = domain->methods->name_to_sid(domain, mem_ctx, name, sid, type);
+
+       talloc_destroy(mem_ctx);
+        
+       /* Return rid and type if lookup successful */
+       if (!NT_STATUS_IS_OK(result)) {
+               *type = SID_NAME_UNKNOWN;
+       }
+
+       return NT_STATUS_IS_OK(result);
+}
+
+/**
+ * @brief Lookup a name in a domain from a sid.
+ *
+ * @param sid Security ID you want to look up.
+ *
+ * @param name On success, set to the name corresponding to @p sid.
+ * 
+ * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
+ * 
+ * @param type On success, contains the type of name: alias, group or
+ * user.
+ *
+ * @retval True if the name exists, in which case @p name and @p type
+ * are set, otherwise False.
+ **/
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid,
+                                fstring dom_name,
+                                fstring name,
+                                enum SID_NAME_USE *type)
+{
+       char *names;
+       NTSTATUS result;
+       TALLOC_CTX *mem_ctx;
+       BOOL rv = False;
+       struct winbindd_domain *domain;
+
+       domain = find_domain_from_sid(sid);
+
+       if (!domain) {
+               DEBUG(1,("Can't find domain from sid\n"));
+               return False;
+       }
+
+       /* Lookup name */
+
+       if (!(mem_ctx = talloc_init("winbindd_lookup_name_by_sid")))
+               return False;
+        
+       result = domain->methods->sid_to_name(domain, mem_ctx, sid, &names, type);
+
+       /* Return name and type if successful */
+        
+       if ((rv = NT_STATUS_IS_OK(result))) {
+               fstrcpy(dom_name, domain->name);
+               fstrcpy(name, names);
+       } else {
+               *type = SID_NAME_UNKNOWN;
+               fstrcpy(name, name_deadbeef);
+       }
+        
+       talloc_destroy(mem_ctx);
+
+       return rv;
+}
+
+
+/* Free state information held for {set,get,end}{pw,gr}ent() functions */
+
+void free_getent_state(struct getent_state *state)
+{
+       struct getent_state *temp;
+
+       /* Iterate over state list */
+
+       temp = state;
+
+       while(temp != NULL) {
+               struct getent_state *next;
+
+               /* Free sam entries then list entry */
+
+               SAFE_FREE(state->sam_entries);
+               DLIST_REMOVE(state, state);
+               next = temp->next;
+
+               SAFE_FREE(temp);
+               temp = next;
+       }
+}
+
+/* Parse winbindd related parameters */
+
+BOOL winbindd_param_init(void)
+{
+       /* Parse winbind uid and winbind_gid parameters */
+
+       if (!lp_winbind_uid(&server_state.uid_low, &server_state.uid_high)) {
+               DEBUG(0, ("winbind uid range missing or invalid\n"));
+               return False;
+       }
+       
+       if (!lp_winbind_gid(&server_state.gid_low, &server_state.gid_high)) {
+               DEBUG(0, ("winbind gid range missing or invalid\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/* Check if a domain is present in a comma-separated list of domains */
+
+BOOL check_domain_env(char *domain_env, char *domain)
+{
+       fstring name;
+       const char *tmp = domain_env;
+
+       while(next_token(&tmp, name, ",", sizeof(fstring))) {
+               if (strequal(name, domain))
+                       return True;
+       }
+
+       return False;
+}
+
+/* Parse a string of the form DOMAIN/user into a domain and a user */
+
+BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
+{
+       char *p = strchr(domuser,*lp_winbind_separator());
+
+       if (!(p || lp_winbind_use_default_domain()))
+               return False;
+       
+       if(!p && lp_winbind_use_default_domain()) {
+               fstrcpy(user, domuser);
+               fstrcpy(domain, lp_workgroup());
+       } else {
+               fstrcpy(user, p+1);
+               fstrcpy(domain, domuser);
+               domain[PTR_DIFF(p, domuser)] = 0;
+       }
+       strupper(domain);
+       return True;
+}
+
+/*
+    Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
+    'winbind separator' options.
+    This means:
+       - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
+       lp_workgroup
+        
+*/
+void fill_domain_username(fstring name, const char *domain, const char *user)
+{
+       if(lp_winbind_use_default_domain() &&
+           !strcmp(lp_workgroup(), domain)) {
+               strlcpy(name, user, sizeof(fstring));
+       } else {
+               slprintf(name, sizeof(fstring) - 1, "%s%s%s",
+                        domain, lp_winbind_separator(),
+                        user);
+       }
+}
+
+/*
+ * Winbindd socket accessor functions
+ */
+
+/* Open the winbindd socket */
+
+static int _winbindd_socket = -1;
+
+int open_winbindd_socket(void)
+{
+       if (_winbindd_socket == -1) {
+               _winbindd_socket = create_pipe_sock(
+                       WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
+               DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
+                          _winbindd_socket));
+       }
+
+       return _winbindd_socket;
+}
+
+/* Close the winbindd socket */
+
+void close_winbindd_socket(void)
+{
+       if (_winbindd_socket != -1) {
+               DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+                          _winbindd_socket));
+               close(_winbindd_socket);
+               _winbindd_socket = -1;
+       }
+}
+
+/*
+ * Client list accessor functions
+ */
+
+static struct winbindd_cli_state *_client_list;
+static int _num_clients;
+
+/* Return list of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list(void)
+{
+       return _client_list;
+}
+
+/* Add a connection to the list */
+
+void winbindd_add_client(struct winbindd_cli_state *cli)
+{
+       DLIST_ADD(_client_list, cli);
+       _num_clients++;
+}
+
+/* Remove a client from the list */
+
+void winbindd_remove_client(struct winbindd_cli_state *cli)
+{
+       DLIST_REMOVE(_client_list, cli);
+       _num_clients--;
+}
+
+/* Close all open clients */
+
+void winbindd_kill_all_clients(void)
+{
+       struct winbindd_cli_state *cl = winbindd_client_list();
+
+       DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
+
+       while (cl) {
+               struct winbindd_cli_state *next;
+               
+               next = cl->next;
+               winbindd_remove_client(cl);
+               cl = next;
+       }
+}
+
+/* Return number of open clients */
+
+int winbindd_num_clients(void)
+{
+       return _num_clients;
+}
+
+/* Help with RID -> SID conversion */
+
+DOM_SID *rid_to_talloced_sid(struct winbindd_domain *domain,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32 rid) 
+{
+       DOM_SID *sid;
+       sid = talloc(mem_ctx, sizeof(*sid));
+       if (!sid) {
+               smb_panic("rid_to_to_talloced_sid: talloc for DOM_SID failed!\n");
+       }
+       sid_copy(sid, &domain->sid);
+       sid_append_rid(sid, rid);
+       return sid;
+}
+       
diff --git a/source4/nsswitch/winbindd_wins.c b/source4/nsswitch/winbindd_wins.c
new file mode 100644 (file)
index 0000000..8ddd5dc
--- /dev/null
@@ -0,0 +1,210 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind daemon - WINS related functions
+
+   Copyright (C) Andrew Tridgell 1999
+   Copyright (C) Herb Lewis 2002
+   
+   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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+       struct sockaddr_in sock;
+       int val=1;
+       int res;
+
+       memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+       sock.sin_len = sizeof(sock);
+#endif
+       sock.sin_port = 0;
+       sock.sin_family = AF_INET;
+       sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+       res = socket(AF_INET, SOCK_DGRAM, 0);
+       if (res == -1)
+               return -1;
+
+       setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+       setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+       /* now we've got a socket - we need to bind it */
+
+       if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+               close(res);
+               return(-1);
+       }
+
+       set_socket_options(res,"SO_BROADCAST");
+
+       return res;
+}
+
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+       int fd;
+       struct in_addr  ip;
+       struct nmb_name nname;
+       struct node_status *status;
+
+       fd = wins_lookup_open_socket_in();
+       if (fd == -1)
+               return NULL;
+
+       make_nmb_name(&nname, "*", 0);
+       ip = *interpret_addr2(addr);
+       status = node_status_query(fd,&nname,ip, count);
+
+       close(fd);
+       return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+       int fd;
+       struct in_addr *ret = NULL;
+       int j, flags = 0;
+
+       *count = 0;
+
+       /* always try with wins first */
+       if (resolve_wins(name,0x20,&ret,count)) {
+               return ret;
+       }
+
+       fd = wins_lookup_open_socket_in();
+       if (fd == -1) {
+               return NULL;
+       }
+
+       /* uggh, we have to broadcast to each interface in turn */
+       for (j=iface_count() - 1;
+            j >= 0;
+            j--) {
+               struct in_addr *bcast = iface_n_bcast(j);
+               ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+               if (ret) break;
+       }
+
+       close(fd);
+       return ret;
+}
+
+/* Get hostname from IP  */
+
+enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state)
+{
+       fstring response;
+       int i, count, maxlen, size;
+       struct node_status *status;
+
+       /* Ensure null termination */
+       state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+       DEBUG(3, ("[%5d]: wins_byip %s\n", state->pid,
+               state->request.data.winsreq));
+
+       *response = '\0';
+       maxlen = sizeof(response) - 1;
+
+       if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){
+           size = strlen(state->request.data.winsreq);
+           if (size > maxlen) {
+               SAFE_FREE(status);
+               return WINBINDD_ERROR;
+           }
+           safe_strcat(response,state->request.data.winsreq,maxlen);
+           safe_strcat(response,"\t",maxlen);
+           for (i = 0; i < count; i++) {
+               /* ignore group names */
+               if (status[i].flags & 0x80) continue;
+               if (status[i].type == 0x20) {
+                       size = sizeof(status[i].name) + strlen(response);
+                       if (size > maxlen) {
+                           SAFE_FREE(status);
+                           return WINBINDD_ERROR;
+                       }
+                       safe_strcat(response, status[i].name, maxlen);
+                       safe_strcat(response, " ", maxlen);
+               }
+           }
+           /* make last character a newline */
+           response[strlen(response)-1] = '\n';
+           SAFE_FREE(status);
+       }
+       fstrcpy(state->response.data.winsresp,response);
+       return WINBINDD_OK;
+}
+
+/* Get IP from hostname */
+
+enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state)
+{
+       struct in_addr *ip_list;
+       int i, count, maxlen, size;
+       fstring response;
+       char * addr;
+
+       /* Ensure null termination */
+       state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+       DEBUG(3, ("[%5d]: wins_byname %s\n", state->pid,
+               state->request.data.winsreq));
+
+       *response = '\0';
+       maxlen = sizeof(response) - 1;
+
+       if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){
+               for (i = count; i ; i--) {
+                   addr = inet_ntoa(ip_list[i-1]);
+                   size = strlen(addr);
+                   if (size > maxlen) {
+                       SAFE_FREE(ip_list);
+                       return WINBINDD_ERROR;
+                   }
+                   if (i != 0) {
+                       /* Clear out the newline character */
+                       response[strlen(response)-1] = ' '; 
+                   }
+                   safe_strcat(response,addr,maxlen);
+                   safe_strcat(response,"\t",maxlen);
+               }
+               size = strlen(state->request.data.winsreq) + strlen(response);
+               if (size > maxlen) {
+                   SAFE_FREE(ip_list);
+                   return WINBINDD_ERROR;
+               }   
+               safe_strcat(response,state->request.data.winsreq,maxlen);
+               safe_strcat(response,"\n",maxlen);
+               SAFE_FREE(ip_list);
+       } else
+               return WINBINDD_ERROR;
+
+       fstrcpy(state->response.data.winsresp,response);
+
+       return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/wins.c b/source4/nsswitch/wins.c
new file mode 100644 (file)
index 0000000..187748d
--- /dev/null
@@ -0,0 +1,322 @@
+/* 
+   Unix SMB/CIFS implementation.
+   a WINS nsswitch module 
+   Copyright (C) Andrew Tridgell 1999
+   
+   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"
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+static int initialised;
+
+extern BOOL AllowDebugChange;
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+       struct sockaddr_in sock;
+       int val=1;
+       int res;
+
+       memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+       sock.sin_len = sizeof(sock);
+#endif
+       sock.sin_port = 0;
+       sock.sin_family = AF_INET;
+       sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+       res = socket(AF_INET, SOCK_DGRAM, 0);
+       if (res == -1)
+               return -1;
+
+       setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+       setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+       /* now we've got a socket - we need to bind it */
+
+       if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+               close(res);
+               return(-1);
+       }
+
+       set_socket_options(res,"SO_BROADCAST");
+
+       return res;
+}
+
+
+static void nss_wins_init(void)
+{
+       initialised = 1;
+       DEBUGLEVEL = 0;
+       AllowDebugChange = False;
+
+       TimeInit();
+       setup_logging("nss_wins",False);
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+}
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+       int fd;
+       struct in_addr  ip;
+       struct nmb_name nname;
+       struct node_status *status;
+
+       if (!initialised) {
+               nss_wins_init();
+       }
+
+       fd = wins_lookup_open_socket_in();
+       if (fd == -1)
+               return NULL;
+
+       make_nmb_name(&nname, "*", 0);
+       ip = *interpret_addr2(addr);
+       status = node_status_query(fd,&nname,ip, count);
+
+       close(fd);
+       return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+       int fd = -1;
+       struct in_addr *ret = NULL;
+       struct in_addr  p;
+       int j, flags = 0;
+
+       if (!initialised) {
+               nss_wins_init();
+       }
+
+       *count = 0;
+
+       /* always try with wins first */
+       if (resolve_wins(name,0x20,&ret,count)) {
+               return ret;
+       }
+
+       fd = wins_lookup_open_socket_in();
+       if (fd == -1) {
+               return NULL;
+       }
+
+       /* uggh, we have to broadcast to each interface in turn */
+       for (j=iface_count() - 1;j >= 0;j--) {
+               struct in_addr *bcast = iface_n_bcast(j);
+               ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+               if (ret) break;
+       }
+
+out:
+       close(fd);
+       return ret;
+}
+
+
+#ifdef HAVE_NS_API_H
+/* IRIX version */
+
+int init(void)
+{
+       nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+       nss_wins_init();
+       return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+       char *map;
+       char *key;
+       char *addr;
+       struct in_addr *ip_list;
+       struct node_status *status;
+       int i, count, len, size;
+       char response[1024];
+       BOOL found = False;
+
+       nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+       if (! rq) 
+               return NSD_ERROR;
+
+       map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+       if (! map) {
+               rq->f_status = NS_FATAL;
+               return NSD_ERROR;
+       }
+
+       key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+       if (! key || ! *key) {
+               rq->f_status = NS_FATAL;
+               return NSD_ERROR;
+       }
+
+       response[0] = '\0';
+       len = sizeof(response) - 2;
+
+       /* 
+        * response needs to be a string of the following format
+        * ip_address[ ip_address]*\tname[ alias]*
+        */
+       if (strcasecmp(map,"hosts.byaddr") == 0) {
+               if ( status = lookup_byaddr_backend(key, &count)) {
+                   size = strlen(key) + 1;
+                   if (size > len) {
+                       free(status);
+                       return NSD_ERROR;
+                   }
+                   len -= size;
+                   strncat(response,key,size);
+                   strncat(response,"\t",1);
+                   for (i = 0; i < count; i++) {
+                       /* ignore group names */
+                       if (status[i].flags & 0x80) continue;
+                       if (status[i].type == 0x20) {
+                               size = sizeof(status[i].name) + 1;
+                               if (size > len) {
+                                   free(status);
+                                   return NSD_ERROR;
+                               }
+                               len -= size;
+                               strncat(response, status[i].name, size);
+                               strncat(response, " ", 1);
+                               found = True;
+                       }
+                   }
+                   response[strlen(response)-1] = '\n';
+                   free(status);
+               }
+       } else if (strcasecmp(map,"hosts.byname") == 0) {
+           if (ip_list = lookup_byname_backend(key, &count)) {
+               for (i = count; i ; i--) {
+                   addr = inet_ntoa(ip_list[i-1]);
+                   size = strlen(addr) + 1;
+                   if (size > len) {
+                       free(ip_list);
+                       return NSD_ERROR;
+                   }
+                   len -= size;
+                   if (i != 0)
+                       response[strlen(response)-1] = ' ';
+                   strncat(response,addr,size);
+                   strncat(response,"\t",1);
+               }
+               size = strlen(key) + 1;
+               if (size > len) {
+                   free(ip_list);
+                   return NSD_ERROR;
+               }   
+               strncat(response,key,size);
+               strncat(response,"\n",1);
+               found = True;
+               free(ip_list);
+           }
+       }
+
+       if (found) {
+           nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+           nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+           return NSD_OK;
+       }
+       nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+       rq->f_status = NS_NOTFOUND;
+       return NSD_NEXT;
+}
+
+#else
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+  **************************************************************************/
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *name, struct hostent *he,
+                         char *buffer, size_t buflen, int *errnop,
+                         int *h_errnop)
+{
+       char **host_addresses;
+       struct in_addr *ip_list;
+       int i, count;
+       size_t namelen = strlen(name) + 1;
+               
+       memset(he, '\0', sizeof(*he));
+
+       ip_list = lookup_byname_backend(name, &count);
+       if (!ip_list) {
+               return NSS_STATUS_NOTFOUND;
+       }
+
+       if (buflen < namelen + (2*count+1)*INADDRSZ) {
+               /* no ENOMEM error type?! */
+               return NSS_STATUS_NOTFOUND;
+       }
+
+
+       host_addresses = (char **)buffer;
+       he->h_addr_list = host_addresses;
+       host_addresses[count] = NULL;
+       buffer += (count + 1) * INADDRSZ;
+       buflen += (count + 1) * INADDRSZ;
+       he->h_addrtype = AF_INET;
+       he->h_length = INADDRSZ;
+
+       for (i=0;i<count;i++) {
+               memcpy(buffer, &ip_list[i].s_addr, INADDRSZ);
+               *host_addresses = buffer;
+               buffer += INADDRSZ;
+               buflen -= INADDRSZ;
+               host_addresses++;
+       }
+
+       if (ip_list)
+               free(ip_list);
+
+       memcpy(buffer, name, namelen);
+       he->h_name = buffer;
+
+       return NSS_STATUS_SUCCESS;
+}
+
+
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+                               char *buffer, size_t buflen, int *errnop,
+                               int *h_errnop)
+{
+       if(af!=AF_INET) {
+               *h_errnop = NO_DATA;
+               *errnop = EAFNOSUPPORT;
+               return NSS_STATUS_UNAVAIL;
+       }
+
+       return _nss_wins_gethostbyname_r(name,he,buffer,buflen,errnop,h_errnop);
+}
+#endif
diff --git a/source4/ntvfs/README b/source4/ntvfs/README
new file mode 100644 (file)
index 0000000..c86c9a0
--- /dev/null
@@ -0,0 +1,26 @@
+This is the base of the new NTVFS subsystem for Samba. The model for
+NTVFS backends is quite different than for the older style VFS
+backends, in particular:
+
+- the NTVFS backends receive windows style file names, although they
+  are in the unix charset (usually UTF8). This means the backend is
+  responsible for mapping windows filename conventions to unix
+  filename conventions if necessary
+
+- the NTVFS backends are responsible for changing effective UID before
+  calling any OS local filesystem operations (if needed). The
+  become_*() functions are provided to make this easier.
+
+- the NTVFS backends are responsible for resolving DFS paths
+
+- each NTVFS backend handles either disk, printer or IPC$ shares,
+  rather than one backend handling all types
+
+- the entry points of the NTVFS backends correspond closely with basic
+  SMB operations, wheres the old VFS was modelled directly on the
+  POSIX filesystem interface.
+
+- the NTVFS backends are responsible for all semantic mappings, such
+  as mapping dos file attributes, ACLs, file ownership and file times
+
+
diff --git a/source4/ntvfs/cifs/README b/source4/ntvfs/cifs/README
new file mode 100644 (file)
index 0000000..a43ad09
--- /dev/null
@@ -0,0 +1,5 @@
+This is the 'CIFS on CIFS' backend for Samba. It provides a NTVFS
+backend that talks to a remote CIFS server. The primary aim of this
+backend is for debugging and development, although some poeple may
+find it useful as a CIFS gateway.
+
diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c
new file mode 100644 (file)
index 0000000..9a17336
--- /dev/null
@@ -0,0 +1,723 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   CIFS-on-CIFS NTVFS filesystem backend
+
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+   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.
+*/
+/*
+  this implements a CIFS->CIFS NTVFS filesystem backend. 
+  
+*/
+
+#include "includes.h"
+
+/* this is stored in ntvfs_private */
+struct cvfs_private {
+       struct cli_tree *tree;
+       struct cli_transport *transport;
+       struct tcon_context *conn;
+       const char *map_calls;
+};
+
+
+/* a structure used to pass information to an async handler */
+struct async_info {
+       struct request_context *req;
+       void *parms;
+};
+
+/*
+  an idle function to cope with messages from the smbd client while 
+  waiting for a reply from the server
+  this function won't be needed once all of the cifs backend
+  and the core of smbd is converted to use async calls
+*/
+static void idle_func(struct cli_transport *transport, void *p_private)
+{
+       struct cvfs_private *private = p_private;
+       if (socket_pending(private->conn->smb->socket.fd)) {
+               smbd_process_async(private->conn->smb);
+       }
+}
+
+/*
+  a handler for oplock break events from the server - these need to be passed
+  along to the client
+ */
+static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *p_private)
+{
+       struct cvfs_private *private = p_private;
+       
+       DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum));
+       return req_send_oplock_break(private->conn, fnum, level);
+}
+
+/*
+  a handler for read events on a connection to a backend server
+*/
+static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags)
+{
+       struct tcon_context *conn = fde->private;
+       struct cvfs_private *private = conn->ntvfs_private;
+
+       DEBUG(5,("cifs_socket_handler event on fd %d\n", fde->fd));
+
+       if (!cli_request_receive_next(private->transport)) {
+               /* the connection to our server is dead */
+               close_cnum(conn);
+       }
+}
+
+/*
+  connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS cvfs_connect(struct request_context *req, const char *sharename)
+{
+       struct tcon_context *conn = req->conn;
+       NTSTATUS status;
+       struct cvfs_private *private;
+       char *map_calls;
+       struct fd_event fde;
+       const char *host, *user, *pass, *domain, *remote_share;
+
+       /* Here we need to determine which server to connect to.
+        * For now we use parametric options, type cifs.
+        * Later we will use security=server and auth_server.c.
+        */
+       host = lp_parm_string(req->conn->service, "cifs", "server");
+       user = lp_parm_string(req->conn->service, "cifs", "user");
+       pass = lp_parm_string(req->conn->service, "cifs", "password");
+       domain = lp_parm_string(req->conn->service, "cifs", "domain");
+       remote_share = lp_parm_string(req->conn->service, "cifs", "share");
+       if (!remote_share) {
+               remote_share = sharename;
+       }
+
+       if (!host || !user || !pass || !domain) {
+               DEBUG(1,("CIFS backend: You must supply server, user, password and domain\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       private = talloc(req->conn->mem_ctx, sizeof(struct cvfs_private));
+       if (!private) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       ZERO_STRUCTP(private);
+
+       req->conn->ntvfs_private = (void *)private;
+
+       status = cli_tree_full_connection(&private->tree, 
+                                         "vfs_cifs",
+                                         host,
+                                         0,
+                                         remote_share, "?????",
+                                         user, domain,
+                                         pass);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       private->transport = private->tree->session->transport;
+       private->tree->session->pid = SVAL(req->in.hdr, HDR_PID);
+       private->conn = req->conn;
+
+       conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS");
+       conn->dev_type = talloc_strdup(conn->mem_ctx, "A:");
+       
+       map_calls = lp_parm_string(req->conn->service, "cifs", "map calls");
+       if (map_calls) {
+               private->map_calls = talloc_strdup(conn->mem_ctx, map_calls);
+       }
+
+       /* if we are mapping trans2, then we need to not give a trans2
+          pointer in the operations structure */
+       if (private->map_calls && in_list("trans2", private->map_calls, True)) {
+               conn->ntvfs_ops->trans2 = NULL;
+       }         
+
+       /* we need to tell the event loop that we wish to receive read events
+          on our SMB connection to the server */
+       fde.fd = private->transport->socket->fd;
+       fde.flags = EVENT_FD_READ;
+       fde.private = req->conn;
+       fde.handler = cifs_socket_handler;
+
+       event_add_fd(conn->smb->events, &fde);
+
+       /* we need to receive oplock break requests from the server */
+       cli_oplock_handler(private->transport, oplock_handler, private);
+       cli_transport_idle_handler(private->transport, idle_func, 100, private);
+
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS cvfs_disconnect(struct tcon_context *conn)
+{
+       struct cvfs_private *private = conn->ntvfs_private;
+
+       event_remove_fd_all(conn->smb->events, private->transport->socket->fd);
+       smb_tree_disconnect(private->tree);
+       cli_tree_close(private->tree);
+
+       return NT_STATUS_OK;
+}
+
+/*
+  a handler for simple async replies
+  this handler can only be used for functions that don't return any
+  parameters (those that just return a status code)
+ */
+static void async_simple(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = cli_request_simple_recv(c_req);
+       req->async.send_fn(req);
+}
+
+
+/* save some typing for the simple functions */
+#define ASYNC_RECV_TAIL(io, async_fn) do { \
+       if (!c_req) return NT_STATUS_UNSUCCESSFUL; \
+       { \
+               struct async_info *async = c_req->async.private; \
+               async = talloc(req->mem_ctx, sizeof(*async)); \
+               if (!async) return NT_STATUS_NO_MEMORY; \
+               async->parms = io; \
+               async->req = req; \
+               c_req->async.private = async; \
+       } \
+       c_req->async.fn = async_fn; \
+       req->control_flags |= REQ_CONTROL_ASYNC; \
+       return NT_STATUS_OK; \
+} while (0)
+
+#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple)
+
+/*
+  delete a file - the dirtype specifies the file types to include in the search. 
+  The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS cvfs_unlink(struct request_context *req, struct smb_unlink *unl)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       /* see if the front end will allow us to perform this
+          function asynchronously.  */
+       if (!req->async.send_fn) {
+               return smb_raw_unlink(private->tree, unl);
+       }
+
+       c_req = smb_raw_unlink_send(private->tree, unl);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  a handler for async ioctl replies
+ */
+static void async_ioctl(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_ioctl_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  ioctl interface
+*/
+static NTSTATUS cvfs_ioctl(struct request_context *req, struct smb_ioctl *io)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       /* see if the front end will allow us to perform this
+          function asynchronously.  */
+       if (!req->async.send_fn) {
+               return smb_raw_ioctl(private->tree, req->mem_ctx, io);
+       }
+
+       c_req = smb_raw_ioctl_send(private->tree, io);
+
+       ASYNC_RECV_TAIL(io, async_ioctl);
+}
+
+/*
+  check if a directory exists
+*/
+static NTSTATUS cvfs_chkpath(struct request_context *req, struct smb_chkpath *cp)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_chkpath(private->tree, cp);
+       }
+
+       c_req = smb_raw_chkpath_send(private->tree, cp);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  a handler for async qpathinfo replies
+ */
+static void async_qpathinfo(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_pathinfo_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  return info on a pathname
+*/
+static NTSTATUS cvfs_qpathinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_pathinfo(private->tree, req->mem_ctx, info);
+       }
+
+       c_req = smb_raw_pathinfo_send(private->tree, info);
+
+       ASYNC_RECV_TAIL(info, async_qpathinfo);
+}
+
+/*
+  a handler for async qfileinfo replies
+ */
+static void async_qfileinfo(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_fileinfo_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  query info on a open file
+*/
+static NTSTATUS cvfs_qfileinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_fileinfo(private->tree, req->mem_ctx, info);
+       }
+
+       c_req = smb_raw_fileinfo_send(private->tree, info);
+
+       ASYNC_RECV_TAIL(info, async_qfileinfo);
+}
+
+
+/*
+  set info on a pathname
+*/
+static NTSTATUS cvfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_setpathinfo(private->tree, st);
+       }
+
+       c_req = smb_raw_setpathinfo_send(private->tree, st);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+  a handler for async open replies
+ */
+static void async_open(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_open_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  open a file
+*/
+static NTSTATUS cvfs_open(struct request_context *req, union smb_open *io)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (private->map_calls && in_list("open", private->map_calls, True) &&
+           io->generic.level != RAW_OPEN_GENERIC) {
+               return ntvfs_map_open(req, io);
+       }
+
+       if (!req->async.send_fn) {
+               return smb_raw_open(private->tree, req->mem_ctx, io);
+       }
+
+       c_req = smb_raw_open_send(private->tree, io);
+
+       ASYNC_RECV_TAIL(io, async_open);
+}
+
+/*
+  create a directory
+*/
+static NTSTATUS cvfs_mkdir(struct request_context *req, union smb_mkdir *md)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_mkdir(private->tree, md);
+       }
+
+       c_req = smb_raw_mkdir_send(private->tree, md);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  remove a directory
+*/
+static NTSTATUS cvfs_rmdir(struct request_context *req, struct smb_rmdir *rd)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_rmdir(private->tree, rd);
+       }
+       c_req = smb_raw_rmdir_send(private->tree, rd);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  rename a set of files
+*/
+static NTSTATUS cvfs_rename(struct request_context *req, struct smb_rename *ren)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_rename(private->tree, ren);
+       }
+
+       c_req = smb_raw_rename_send(private->tree, ren);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  copy a set of files
+*/
+static NTSTATUS cvfs_copy(struct request_context *req, struct smb_copy *cp)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  a handler for async read replies
+ */
+static void async_read(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_read_recv(c_req, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  read from a file
+*/
+static NTSTATUS cvfs_read(struct request_context *req, union smb_read *rd)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_read(private->tree, rd);
+       }
+
+       c_req = smb_raw_read_send(private->tree, rd);
+
+       ASYNC_RECV_TAIL(rd, async_read);
+}
+
+/*
+  a handler for async write replies
+ */
+static void async_write(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_write_recv(c_req, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  write to a file
+*/
+static NTSTATUS cvfs_write(struct request_context *req, union smb_write *wr)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_write(private->tree, wr);
+       }
+
+       c_req = smb_raw_write_send(private->tree, wr);
+
+       ASYNC_RECV_TAIL(wr, async_write);
+}
+
+/*
+  seek in a file
+*/
+static NTSTATUS cvfs_seek(struct request_context *req, struct smb_seek *io)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  flush a file
+*/
+static NTSTATUS cvfs_flush(struct request_context *req, struct smb_flush *io)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  close a file
+*/
+static NTSTATUS cvfs_close(struct request_context *req, union smb_close *io)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_close(private->tree, io);
+       }
+
+       c_req = smb_raw_close_send(private->tree, io);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  exit - closing files?
+*/
+static NTSTATUS cvfs_exit(struct request_context *req)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  lock a byte range
+*/
+static NTSTATUS cvfs_lock(struct request_context *req, union smb_lock *lck)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_lock(private->tree, lck);
+       }
+
+       c_req = smb_raw_lock_send(private->tree, lck);
+       SIMPLE_ASYNC_TAIL;
+}
+
+/*
+  set info on a open file
+*/
+static NTSTATUS cvfs_setfileinfo(struct request_context *req, 
+                                union smb_setfileinfo *info)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_setfileinfo(private->tree, info);
+       }
+       c_req = smb_raw_setfileinfo_send(private->tree, info);
+
+       SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+  a handler for async fsinfo replies
+ */
+static void async_fsinfo(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_fsinfo_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/*
+  return filesystem space info
+*/
+static NTSTATUS cvfs_fsinfo(struct request_context *req, union smb_fsinfo *fs)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_fsinfo(private->tree, req->mem_ctx, fs);
+       }
+
+       c_req = smb_raw_fsinfo_send(private->tree, req->mem_ctx, fs);
+
+       ASYNC_RECV_TAIL(fs, async_fsinfo);
+}
+
+/*
+  return print queue info
+*/
+static NTSTATUS cvfs_lpq(struct request_context *req, union smb_lpq *lpq)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/* 
+   list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS cvfs_search_first(struct request_context *req, union smb_search_first *io, 
+                                 void *search_private, 
+                                 BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+
+       return smb_raw_search_first(private->tree, req->mem_ctx, io, search_private, callback);
+}
+
+/* continue a search */
+static NTSTATUS cvfs_search_next(struct request_context *req, union smb_search_next *io, 
+                                void *search_private, 
+                                BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+
+       return smb_raw_search_next(private->tree, req->mem_ctx, io, search_private, callback);
+}
+
+/* close a search */
+static NTSTATUS cvfs_search_close(struct request_context *req, union smb_search_close *io)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+
+       return smb_raw_search_close(private->tree, io);
+}
+
+/*
+  a handler for async trans2 replies
+ */
+static void async_trans2(struct cli_request *c_req)
+{
+       struct async_info *async = c_req->async.private;
+       struct request_context *req = async->req;
+       req->async.status = smb_raw_trans2_recv(c_req, req->mem_ctx, async->parms);
+       req->async.send_fn(req);
+}
+
+/* raw trans2 */
+static NTSTATUS cvfs_trans2(struct request_context *req, struct smb_trans2 *trans2)
+{
+       struct cvfs_private *private = req->conn->ntvfs_private;
+       struct cli_request *c_req;
+
+       if (!req->async.send_fn) {
+               return smb_raw_trans2(private->tree, req->mem_ctx, trans2);
+       }
+
+       c_req = smb_raw_trans2_send(private->tree, trans2);
+
+       ASYNC_RECV_TAIL(trans2, async_trans2);
+}
+
+/*
+  initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL cifs_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = cvfs_connect;
+       ops.disconnect = cvfs_disconnect;
+       ops.unlink = cvfs_unlink;
+       ops.chkpath = cvfs_chkpath;
+       ops.qpathinfo = cvfs_qpathinfo;
+       ops.setpathinfo = cvfs_setpathinfo;
+       ops.open = cvfs_open;
+       ops.mkdir = cvfs_mkdir;
+       ops.rmdir = cvfs_rmdir;
+       ops.rename = cvfs_rename;
+       ops.copy = cvfs_copy;
+       ops.ioctl = cvfs_ioctl;
+       ops.read = cvfs_read;
+       ops.write = cvfs_write;
+       ops.seek = cvfs_seek;
+       ops.flush = cvfs_flush; 
+       ops.close = cvfs_close;
+       ops.exit = cvfs_exit;
+       ops.lock = cvfs_lock;
+       ops.setfileinfo = cvfs_setfileinfo;
+       ops.qfileinfo = cvfs_qfileinfo;
+       ops.fsinfo = cvfs_fsinfo;
+       ops.lpq = cvfs_lpq;
+       ops.search_first = cvfs_search_first;
+       ops.search_next = cvfs_search_next;
+       ops.search_close = cvfs_search_close;
+
+       /* only define this one for trans2 testing */
+       ops.trans2 = cvfs_trans2;
+
+       /* register ourselves with the NTVFS subsystem. We register under the name 'cifs'. */
+
+       ret = ntvfs_register("cifs", NTVFS_DISK, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register CIFS backend!\n"));
+               return False;
+       }
+       
+       return True;
+}
diff --git a/source4/ntvfs/ipc/README b/source4/ntvfs/ipc/README
new file mode 100644 (file)
index 0000000..059a714
--- /dev/null
@@ -0,0 +1,5 @@
+This is the IPC$ backend for Samba. NTVFS operations that are made on
+IPC$ shares are directed here by default. Most file operations 
+are not supported on IPC$ shares.
+
+
diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c
new file mode 100644 (file)
index 0000000..fe310d1
--- /dev/null
@@ -0,0 +1,295 @@
+/* 
+   Unix SMB/CIFS implementation.
+   default IPC$ NTVFS backend
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements the IPC$ backend, called by the NTVFS subsystem to
+  handle requests on IPC$ shares
+*/
+
+
+#include "includes.h"
+
+/*
+  connect to a share - always works 
+*/
+static NTSTATUS ipc_connect(struct request_context *req, const char *sharename)
+{
+       struct tcon_context *conn = req->conn;
+
+       conn->fs_type = talloc_strdup(conn->mem_ctx, "IPC");
+       conn->dev_type = talloc_strdup(conn->mem_ctx, "IPC");
+
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS ipc_disconnect(struct tcon_context *tcon)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  delete a file
+*/
+static NTSTATUS ipc_unlink(struct request_context *req, struct smb_unlink *unl)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+  ioctl interface - we don't do any
+*/
+static NTSTATUS ipc_ioctl(struct request_context *req, struct smb_ioctl *io)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  check if a directory exists
+*/
+static NTSTATUS ipc_chkpath(struct request_context *req, struct smb_chkpath *cp)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  return info on a pathname
+*/
+static NTSTATUS ipc_qpathinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  set info on a pathname
+*/
+static NTSTATUS ipc_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  open a file
+*/
+static NTSTATUS ipc_open(struct request_context *req, union smb_open *oi)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  create a directory
+*/
+static NTSTATUS ipc_mkdir(struct request_context *req, union smb_mkdir *md)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  remove a directory
+*/
+static NTSTATUS ipc_rmdir(struct request_context *req, struct smb_rmdir *rd)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  rename a set of files
+*/
+static NTSTATUS ipc_rename(struct request_context *req, struct smb_rename *ren)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  copy a set of files
+*/
+static NTSTATUS ipc_copy(struct request_context *req, struct smb_copy *cp)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  read from a file
+*/
+static NTSTATUS ipc_read(struct request_context *req, union smb_read *rd)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  write to a file
+*/
+static NTSTATUS ipc_write(struct request_context *req, union smb_write *wr)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  seek in a file
+*/
+static NTSTATUS ipc_seek(struct request_context *req, struct smb_seek *io)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  flush a file
+*/
+static NTSTATUS ipc_flush(struct request_context *req, struct smb_flush *io)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  close a file
+*/
+static NTSTATUS ipc_close(struct request_context *req, union smb_close *io)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  exit - closing files?
+*/
+static NTSTATUS ipc_exit(struct request_context *req)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  lock a byte range
+*/
+static NTSTATUS ipc_lock(struct request_context *req, union smb_lock *lck)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  set info on a open file
+*/
+static NTSTATUS ipc_setfileinfo(struct request_context *req, union smb_setfileinfo *info)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  query info on a open file
+*/
+static NTSTATUS ipc_qfileinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+  return filesystem info
+*/
+static NTSTATUS ipc_fsinfo(struct request_context *req, union smb_fsinfo *fs)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+  return print queue info
+*/
+static NTSTATUS ipc_lpq(struct request_context *req, union smb_lpq *lpq)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/* 
+   list files in a directory matching a wildcard pattern
+*/
+NTSTATUS ipc_search_first(struct request_context *req, union smb_search_first *io,
+                         void *search_private, 
+                         BOOL (*callback)(void *, union smb_search_data *))
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/* 
+   continue listing files in a directory 
+*/
+NTSTATUS ipc_search_next(struct request_context *req, union smb_search_next *io,
+                        void *search_private, 
+                        BOOL (*callback)(void *, union smb_search_data *))
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+/* 
+   end listing files in a directory 
+*/
+NTSTATUS ipc_search_close(struct request_context *req, union smb_search_close *io)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+  initialialise the IPC backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL ipc_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = ipc_connect;
+       ops.disconnect = ipc_disconnect;
+       ops.unlink = ipc_unlink;
+       ops.chkpath = ipc_chkpath;
+       ops.qpathinfo = ipc_qpathinfo;
+       ops.setpathinfo = ipc_setpathinfo;
+       ops.open = ipc_open;
+       ops.mkdir = ipc_mkdir;
+       ops.rmdir = ipc_rmdir;
+       ops.rename = ipc_rename;
+       ops.copy = ipc_copy;
+       ops.ioctl = ipc_ioctl;
+       ops.read = ipc_read;
+       ops.write = ipc_write;
+       ops.seek = ipc_seek;
+       ops.flush = ipc_flush;  
+       ops.close = ipc_close;
+       ops.exit = ipc_exit;
+       ops.lock = ipc_lock;
+       ops.setfileinfo = ipc_setfileinfo;
+       ops.qfileinfo = ipc_qfileinfo;
+       ops.fsinfo = ipc_fsinfo;
+       ops.lpq = ipc_lpq;
+       ops.search_first = ipc_search_first;
+       ops.search_next = ipc_search_next;
+       ops.search_close = ipc_search_close;
+
+       /* register ourselves with the NTVFS subsystem. */
+       ret = ntvfs_register("ipc", NTVFS_IPC, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register IPC backend!\n"));
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c
new file mode 100644 (file)
index 0000000..57cdb97
--- /dev/null
@@ -0,0 +1,145 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NTVFS base code
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements the core code for all NTVFS modules. Backends register themselves here.
+*/
+
+#include "includes.h"
+
+
+/* the list of currently registered NTVFS backends, note that there
+ * can be more than one backend with the same name, as long as they
+ * have different typesx */
+static struct {
+       const char *name;
+       enum ntvfs_type type;
+       struct ntvfs_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+  register a NTVFS backend. 
+
+  The 'name' can be later used by other backends to find the operations
+  structure for this backend.  
+
+  The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops)
+{
+       if (ntvfs_backend_byname(name, type) != NULL) {
+               /* its already registered! */
+               DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", 
+                        name, (int)type));
+               return False;
+       }
+
+       backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1));
+       if (!backends) {
+               smb_panic("out of memory in ntvfs_register");
+       }
+
+       backends[num_backends].name = smb_xstrdup(name);
+       backends[num_backends].type = type;
+       backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops));
+
+       num_backends++;
+
+       return True;
+}
+
+
+/*
+  return the operations structure for a named backend of the specified type
+*/
+struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type)
+{
+       int i;
+
+       for (i=0;i<num_backends;i++) {
+               if (backends[i].type == type && 
+                   strcmp(backends[i].name, name) == 0) {
+                       return backends[i].ops;
+               }
+       }
+
+       return NULL;
+}
+
+
+/*
+  return the NTVFS interface version, and the size of some critical types
+  This can be used by backends to either detect compilation errors, or provide
+  multiple implementations for different smbd compilation options in one module
+*/
+int ntvfs_interface_version(struct ntvfs_critical_sizes *sizes)
+{
+       sizes->sizeof_ntvfs_ops = sizeof(struct ntvfs_ops);
+       sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T);
+       sizes->sizeof_tcon_context = sizeof(struct tcon_context);
+       sizes->sizeof_request_context = sizeof(struct request_context);
+
+       return NTVFS_INTERFACE_VERSION;
+}
+
+
+/*
+  initialise the NTVFS subsystem
+*/
+BOOL ntvfs_init(void)
+{
+       /* initialise our 3 basic backends. These are assumed to be
+        * present and are always built in */
+       if (!posix_vfs_init() ||
+           !ipc_vfs_init() ||
+           !print_vfs_init()) {
+               return False;
+       }
+       /* initialize optional backends, e.g. CIFS. We allow failures here. */
+       cifs_vfs_init();
+
+#if WITH_NTVFS_STFS
+       tank_vfs_init();
+#endif
+
+       DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION));
+       return True;
+}
+
+
+/*
+  initialise a connection structure to point at a NTVFS backend
+*/
+NTSTATUS ntvfs_init_connection(struct request_context *req)
+{
+       const char *handler = lp_ntvfs_handler(req->conn->service);
+       
+       if (strequal(handler, "default"))
+               handler = "ipc";
+
+       req->conn->ntvfs_ops = ntvfs_backend_byname(handler, req->conn->type);
+
+       if (!req->conn->ntvfs_ops) {
+               DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n", handler, req->conn->type));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/ntvfs_dfs.c b/source4/ntvfs/ntvfs_dfs.c
new file mode 100644 (file)
index 0000000..7acd1f7
--- /dev/null
@@ -0,0 +1,117 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NTVFS base code
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements the core code for all NTVFS modules. Backends register themselves here.
+*/
+
+#include "includes.h"
+
+
+/* the list of currently registered NTVFS backends, note that there
+ * can be more than one backend with the same name, as long as they
+ * have different typesx */
+static struct {
+       const char *name;
+       enum ntvfs_type type;
+       struct ntvfs_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+  register a NTVFS backend. 
+
+  The 'name' can be later used by other backends to find the operations
+  structure for this backend.  
+
+  The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops)
+{
+       if (ntvfs_backend_byname(name, type) != NULL) {
+               /* its already registered! */
+               DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", 
+                        name, (int)type));
+               return False;
+       }
+
+       backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1));
+       if (!backends) {
+               smb_panic("out of memory in ntvfs_register");
+       }
+
+       backends[num_backends].name = smb_xstrdup(name);
+       backends[num_backends].type = type;
+       backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops));
+
+       num_backends++;
+
+       return True;
+}
+
+
+/*
+  return the operations structure for a named backend of the specified type
+*/
+struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type)
+{
+       int i;
+
+       for (i=0;i<num_backends;i++) {
+               if (backends[i].type == type && 
+                   strcmp(backends[i].name, name) == 0) {
+                       return backends[i].ops;
+               }
+       }
+
+       return NULL;
+}
+
+
+/*
+  return the NTVFS interface version, and the size of some critical types
+  This can be used by backends to either detect compilation errors, or provide
+  multiple implementations for different smbd compilation options in one module
+*/
+int ntvfs_interface_version(struct ntvfs_critical_sizes *sizes)
+{
+       sizes->sizeof_ntvfs_ops = sizeof(struct ntvfs_ops);
+       sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T);
+       sizes->sizeof_tcon_context = sizeof(struct tcon_context);
+
+       return NTVFS_INTERFACE_VERSION;
+}
+
+
+/*
+  initialise the NTVFS subsystem
+*/
+BOOL ntvfs_init(void)
+{
+       /* initialise our 3 basic backends. These are assumed to be
+        * present and are always built in */
+       if (!posix_vfs_init() ||
+           !ipc_vfs_init() ||
+           !print_vfs_init()) {
+               return False;
+       }
+
+       DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION));
+       return True;
+}
diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c
new file mode 100644 (file)
index 0000000..def448a
--- /dev/null
@@ -0,0 +1,544 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   NTVFS generic level mapping code
+
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements mappings between info levels for NTVFS backend calls
+
+  the idea is that each of these functions implements one of the NTVFS
+  backend calls in terms of the 'generic' call. All backends that use
+  these functions must supply the generic call, but can if it wants to
+  also implement other levels if the need arises
+
+  this allows backend writers to only implement one varient of each
+  call unless they need fine grained control of the calls.
+*/
+
+#include "includes.h"
+
+/*
+  see if a filename ends in EXE COM DLL or SYM. This is needed for the DENY_DOS mapping for OpenX
+*/
+static BOOL is_exe_file(const char *fname)
+{
+       char *p;
+       p = strrchr(fname, '.');
+       if (!p) {
+               return False;
+       }
+       p++;
+       if (strcasecmp(p, "EXE") == 0 ||
+           strcasecmp(p, "COM") == 0 ||
+           strcasecmp(p, "DLL") == 0 ||
+           strcasecmp(p, "SYM") == 0) {
+               return True;
+       }
+       return False;
+}
+
+
+/* 
+   NTVFS open generic to any mapper
+*/
+NTSTATUS ntvfs_map_open(struct request_context *req, union smb_open *io)
+{
+       NTSTATUS status;
+       union smb_open io2;
+
+       if (io->generic.level == RAW_OPEN_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       switch (io->generic.level) {
+       case RAW_OPEN_OPENX:
+               ZERO_STRUCT(io2.generic.in);
+               io2.generic.level = RAW_OPEN_GENERIC;
+               if (io->openx.in.flags & OPENX_FLAGS_REQUEST_OPLOCK) {
+                       io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
+               }
+               if (io->openx.in.flags & OPENX_FLAGS_REQUEST_BATCH_OPLOCK) {
+                       io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+               }
+
+               switch (io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK) {
+               case OPENX_MODE_ACCESS_READ:
+                       io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ;
+                       break;
+               case OPENX_MODE_ACCESS_WRITE:
+                       io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE;
+                       break;
+               case OPENX_MODE_ACCESS_RDWR:
+               case OPENX_MODE_ACCESS_FCB:
+                       io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
+                       break;
+               }
+
+               switch (io->openx.in.open_mode & OPENX_MODE_DENY_MASK) {
+               case OPENX_MODE_DENY_READ:
+                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE;
+                       break;
+               case OPENX_MODE_DENY_WRITE:
+                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+                       break;
+               case OPENX_MODE_DENY_ALL:
+                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                       break;
+               case OPENX_MODE_DENY_NONE:
+                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+                       break;
+               case OPENX_MODE_DENY_DOS:
+                       /* DENY_DOS is quite strange - it depends on the filename! */
+                       if (is_exe_file(io->openx.in.fname)) {
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+                       } else {
+                               if ((io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK) == 
+                                   OPENX_MODE_ACCESS_READ) {
+                                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+                               } else {
+                                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                               }
+                       }
+                       break;
+               case OPENX_MODE_DENY_FCB:
+                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                       break;
+               }
+
+               switch (io->openx.in.open_func) {
+               case (OPENX_OPEN_FUNC_FAIL):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+                       break;
+               case (OPENX_OPEN_FUNC_OPEN):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN;
+                       break;
+               case (OPENX_OPEN_FUNC_TRUNC):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
+                       break;
+               case (OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+                       break;
+               case (OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+                       break;
+               case (OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE):
+                       io2.generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+                       break;                  
+               }
+               io2.generic.in.alloc_size = io->openx.in.size;
+               io2.generic.in.file_attr = io->openx.in.file_attrs;
+               io2.generic.in.fname = io->openx.in.fname;
+
+               status = req->conn->ntvfs_ops->open(req, &io2);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               
+               ZERO_STRUCT(io->openx.out);
+               io->openx.out.fnum = io2.generic.out.fnum;
+               io->openx.out.attrib = io2.generic.out.attrib;
+               io->openx.out.write_time = nt_time_to_unix(&io2.generic.out.write_time);
+               io->openx.out.size = io2.generic.out.size;
+               
+               return NT_STATUS_OK;
+
+
+       case RAW_OPEN_OPEN:
+               ZERO_STRUCT(io2.generic.in);
+               io2.generic.level = RAW_OPEN_GENERIC;
+               io2.generic.in.file_attr = io->open.in.search_attrs;
+               io2.generic.in.fname = io->open.in.fname;
+               io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN;
+               DEBUG(9,("ntvfs_map_open(OPEN): mapping flags=0x%x\n",
+                       io->open.in.flags));
+               switch (io->open.in.flags & OPEN_FLAGS_MODE_MASK) {
+                       case OPEN_FLAGS_OPEN_READ:
+                               io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ;
+                               io->open.out.rmode = DOS_OPEN_RDONLY;
+                               break;
+                       case OPEN_FLAGS_OPEN_WRITE:
+                               io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE;
+                               io->open.out.rmode = DOS_OPEN_WRONLY;
+                               break;
+                       case OPEN_FLAGS_OPEN_RDWR:
+                       case 0xf: /* FCB mode */
+                               io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
+                               io->open.out.rmode = DOS_OPEN_RDWR; /* assume we got r/w */
+                               break;
+                       default:
+                               DEBUG(2,("ntvfs_map_open(OPEN): invalid mode 0x%x\n",
+                                       io->open.in.flags & OPEN_FLAGS_MODE_MASK));
+                               return NT_STATUS_INVALID_PARAMETER;
+               }
+               
+               switch(io->open.in.flags & OPEN_FLAGS_DENY_MASK) {
+                       case OPEN_FLAGS_DENY_DOS:
+                               /* DENY_DOS is quite strange - it depends on the filename! */
+                               /* REWRITE: is this necessary for OPEN? */
+                               if (is_exe_file(io->open.in.fname)) {
+                                       io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+                               } else {
+                                       if ((io->open.in.flags & OPEN_FLAGS_MODE_MASK) == 
+                                           OPEN_FLAGS_OPEN_READ) {
+                                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+                                       } else {
+                                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                                       }
+                               }
+                               break;
+                       case OPEN_FLAGS_DENY_ALL:
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                               break;
+                       case OPEN_FLAGS_DENY_WRITE:
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+                               break;
+                       case OPEN_FLAGS_DENY_READ:
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE;
+                               break;
+                       case OPEN_FLAGS_DENY_NONE:
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE |
+                                               NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE;
+                               break;
+                       case 0x70: /* FCB mode */
+                               io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+                               break;
+                       default:
+                               DEBUG(2,("ntvfs_map_open(OPEN): invalid DENY 0x%x\n",
+                                       io->open.in.flags & OPEN_FLAGS_DENY_MASK));
+                               return NT_STATUS_INVALID_PARAMETER;
+               }
+               DEBUG(9,("ntvfs_map_open(OPEN): mapped flags=0x%x to access_mask=0x%x and share_access=0x%x\n",
+                       io->open.in.flags, io2.generic.in.access_mask, io2.generic.in.share_access));
+
+               status = req->conn->ntvfs_ops->open(req, &io2);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               
+               ZERO_STRUCT(io->openx.out);
+               io->open.out.fnum = io2.generic.out.fnum;
+               io->open.out.attrib = io2.generic.out.attrib;
+               io->open.out.write_time = nt_time_to_unix(&io2.generic.out.write_time);
+               io->open.out.size = io2.generic.out.size;
+               io->open.out.rmode = DOS_OPEN_RDWR;
+               
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/* 
+   NTVFS fsinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_fsinfo(struct request_context *req, union smb_fsinfo *fs)
+{
+       NTSTATUS status;
+       union smb_fsinfo fs2;
+
+       if (fs->generic.level == RAW_QFS_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* ask the backend for the generic info */
+       fs2.generic.level = RAW_QFS_GENERIC;
+
+       status = req->conn->ntvfs_ops->fsinfo(req, &fs2);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* and convert it to the required level */
+       switch (fs->generic.level) {
+       case RAW_QFS_GENERIC:
+               return NT_STATUS_INVALID_LEVEL;
+
+       case RAW_QFS_DSKATTR: {
+               /* map from generic to DSKATTR */
+               unsigned bpunit = 64;
+
+               /* we need to scale the sizes to fit */
+               for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
+                       if (fs2.generic.out.blocks_total * (double)fs2.generic.out.block_size < bpunit * 512 * 65535.0) {
+                               break;
+                       }
+               }
+
+               fs->dskattr.out.blocks_per_unit = bpunit;
+               fs->dskattr.out.block_size = 512;
+               fs->dskattr.out.units_total = 
+                       (fs2.generic.out.blocks_total * (double)fs2.generic.out.block_size) / (bpunit * 512);
+               fs->dskattr.out.units_free  = 
+                       (fs2.generic.out.blocks_free  * (double)fs2.generic.out.block_size) / (bpunit * 512);
+
+               /* we must return a maximum of 2G to old DOS systems, or they get very confused */
+               if (bpunit > 64 && req->smb->negotiate.protocol <= PROTOCOL_LANMAN2) {
+                       fs->dskattr.out.blocks_per_unit = 64;
+                       fs->dskattr.out.units_total = 0xFFFF;
+                       fs->dskattr.out.units_free = 0xFFFF;
+               }
+               return NT_STATUS_OK;
+       }
+
+       case RAW_QFS_ALLOCATION:
+               fs->allocation.out.fs_id = fs2.generic.out.fs_id;
+               fs->allocation.out.total_alloc_units = fs2.generic.out.blocks_total;
+               fs->allocation.out.avail_alloc_units = fs2.generic.out.blocks_free;
+               fs->allocation.out.sectors_per_unit = 1;
+               fs->allocation.out.bytes_per_sector = fs2.generic.out.block_size;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_VOLUME:
+               fs->volume.out.serial_number = fs2.generic.out.serial_number;
+               fs->volume.out.volume_name.s = fs2.generic.out.volume_name;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_VOLUME_INFO:
+       case RAW_QFS_VOLUME_INFORMATION:
+               fs->volume_info.out.create_time = fs2.generic.out.create_time;
+               fs->volume_info.out.serial_number = fs2.generic.out.serial_number;
+               fs->volume_info.out.volume_name.s = fs2.generic.out.volume_name;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_SIZE_INFO:
+       case RAW_QFS_SIZE_INFORMATION:
+               fs->size_info.out.total_alloc_units = fs2.generic.out.blocks_total;
+               fs->size_info.out.avail_alloc_units = fs2.generic.out.blocks_free;
+               fs->size_info.out.sectors_per_unit = 1;
+               fs->size_info.out.bytes_per_sector = fs2.generic.out.block_size;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_DEVICE_INFO:
+       case RAW_QFS_DEVICE_INFORMATION:
+               fs->device_info.out.device_type = fs2.generic.out.device_type;
+               fs->device_info.out.characteristics = fs2.generic.out.device_characteristics;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_ATTRIBUTE_INFO:
+       case RAW_QFS_ATTRIBUTE_INFORMATION:
+               fs->attribute_info.out.fs_attr = fs2.generic.out.fs_attr;
+               fs->attribute_info.out.max_file_component_length = fs2.generic.out.max_file_component_length;
+               fs->attribute_info.out.fs_type.s = fs2.generic.out.fs_type;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_QUOTA_INFORMATION:
+               ZERO_STRUCT(fs->quota_information.out.unknown);
+               fs->quota_information.out.quota_soft = fs2.generic.out.quota_soft;
+               fs->quota_information.out.quota_hard = fs2.generic.out.quota_hard;
+               fs->quota_information.out.quota_flags = fs2.generic.out.quota_flags;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_FULL_SIZE_INFORMATION:
+               fs->full_size_information.out.total_alloc_units = fs2.generic.out.blocks_total;
+               fs->full_size_information.out.call_avail_alloc_units = fs2.generic.out.blocks_free;
+               fs->full_size_information.out.actual_avail_alloc_units = fs2.generic.out.blocks_free;
+               fs->full_size_information.out.sectors_per_unit = 1;
+               fs->full_size_information.out.bytes_per_sector = fs2.generic.out.block_size;
+               return NT_STATUS_OK;
+
+       case RAW_QFS_OBJECTID_INFORMATION:
+               fs->objectid_information.out.guid = fs2.generic.out.guid;
+               ZERO_STRUCT(fs->objectid_information.out.unknown);
+               return NT_STATUS_OK;
+       }
+
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/* 
+   NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, union smb_fileinfo *info2)
+{
+       /* and convert it to the required level using results in info2 */
+       switch (info->generic.level) {
+               case RAW_FILEINFO_GENERIC:
+               return NT_STATUS_INVALID_LEVEL;
+       case RAW_FILEINFO_GETATTR:
+               info->getattr.out.attrib = info2->generic.out.attrib & 0x3f;
+               info->getattr.out.size = info2->generic.out.size;
+               info->getattr.out.write_time = nt_time_to_unix(&info2->generic.out.write_time);
+               return NT_STATUS_OK;
+               
+       case RAW_FILEINFO_GETATTRE:
+               info->getattre.out.attrib = info2->generic.out.attrib;
+               info->getattre.out.size = info2->generic.out.size;
+               info->getattre.out.write_time = nt_time_to_unix(&info2->generic.out.write_time);
+               info->getattre.out.create_time = nt_time_to_unix(&info2->generic.out.create_time);
+               info->getattre.out.access_time = nt_time_to_unix(&info2->generic.out.access_time);
+               info->getattre.out.alloc_size = info2->generic.out.alloc_size;
+               return NT_STATUS_OK;
+               
+       case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+               info->network_open_information.out.create_time = info2->generic.out.create_time;
+               info->network_open_information.out.access_time = info2->generic.out.access_time;
+               info->network_open_information.out.write_time =  info2->generic.out.write_time;
+               info->network_open_information.out.change_time = info2->generic.out.change_time;
+               info->network_open_information.out.alloc_size = info2->generic.out.alloc_size;
+               info->network_open_information.out.size = info2->generic.out.size;
+               info->network_open_information.out.attrib = info2->generic.out.attrib;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALL_INFO:
+       case RAW_FILEINFO_ALL_INFORMATION:
+               info->all_info.out.create_time = info2->generic.out.create_time;
+               info->all_info.out.access_time = info2->generic.out.access_time;
+               info->all_info.out.write_time =  info2->generic.out.write_time;
+               info->all_info.out.change_time = info2->generic.out.change_time;
+               info->all_info.out.attrib = info2->generic.out.attrib;
+               info->all_info.out.alloc_size = info2->generic.out.alloc_size;
+               info->all_info.out.size = info2->generic.out.size;
+               info->all_info.out.nlink = info2->generic.out.nlink;
+               info->all_info.out.delete_pending = info2->generic.out.delete_pending;
+               info->all_info.out.directory = info2->generic.out.directory;
+               info->all_info.out.ea_size = info2->generic.out.ea_size;
+               info->all_info.out.fname.s = info2->generic.out.fname.s;
+               info->all_info.out.fname.private_length = info2->generic.out.fname.private_length;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_BASIC_INFO:
+       case RAW_FILEINFO_BASIC_INFORMATION:
+               info->basic_info.out.create_time = info2->generic.out.create_time;
+               info->basic_info.out.access_time = info2->generic.out.access_time;
+               info->basic_info.out.write_time = info2->generic.out.write_time;
+               info->basic_info.out.change_time = info2->generic.out.change_time;
+               info->basic_info.out.attrib = info2->generic.out.attrib;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STANDARD:
+               info->standard.out.create_time = nt_time_to_unix(&info2->generic.out.create_time);
+               info->standard.out.access_time = nt_time_to_unix(&info2->generic.out.access_time);
+               info->standard.out.write_time = nt_time_to_unix(&info2->generic.out.write_time);
+               info->standard.out.size = info2->generic.out.size;
+               info->standard.out.alloc_size = info2->generic.out.alloc_size;
+               info->standard.out.attrib = info2->generic.out.attrib;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_SIZE:
+               info->ea_size.out.create_time = nt_time_to_unix(&info2->generic.out.create_time);
+               info->ea_size.out.access_time = nt_time_to_unix(&info2->generic.out.access_time);
+               info->ea_size.out.write_time = nt_time_to_unix(&info2->generic.out.write_time);
+               info->ea_size.out.size = info2->generic.out.size;
+               info->ea_size.out.alloc_size = info2->generic.out.alloc_size;
+               info->ea_size.out.attrib = info2->generic.out.attrib;
+               info->ea_size.out.ea_size = info2->generic.out.ea_size;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STANDARD_INFO:
+       case RAW_FILEINFO_STANDARD_INFORMATION:
+               info->standard_info.out.alloc_size = info2->generic.out.alloc_size;
+               info->standard_info.out.size = info2->generic.out.size;
+               info->standard_info.out.nlink = info2->generic.out.nlink;
+               info->standard_info.out.delete_pending = info2->generic.out.delete_pending;
+               info->standard_info.out.directory = info2->generic.out.directory;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_INTERNAL_INFORMATION:
+               info->internal_information.out.device = info2->generic.out.device;
+               info->internal_information.out.inode = info2->generic.out.inode;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_INFO:
+       case RAW_FILEINFO_EA_INFORMATION:
+               info->ea_info.out.ea_size = info2->generic.out.ea_size;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+               info->attribute_tag_information.out.attrib = info2->generic.out.attrib;
+               info->attribute_tag_information.out.reparse_tag = info2->generic.out.reparse_tag;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STREAM_INFO:
+       case RAW_FILEINFO_STREAM_INFORMATION:
+               /* setup a single data stream */
+               info->stream_info.out.num_streams = info2->generic.out.num_streams;
+               info->stream_info.out.streams = talloc(req->mem_ctx, sizeof(info2->stream_info.out.streams[0]));
+               if (!info->stream_info.out.streams) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               info->stream_info.out.streams[0].size = info2->generic.out.streams[0].size;
+               info->stream_info.out.streams[0].alloc_size = info2->generic.out.streams[0].alloc_size;
+               info->stream_info.out.streams[0].stream_name.s = info2->generic.out.streams[0].stream_name.s;
+               info->stream_info.out.streams[0].stream_name.private_length = info->generic.out.streams[0].stream_name.private_length;
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_NAME_INFO:
+       case RAW_FILEINFO_NAME_INFORMATION:
+               info->name_info.out.fname.s = info2->generic.out.fname.s;
+               info->name_info.out.fname.private_length = info2->generic.out.fname.private_length;
+               return NT_STATUS_OK;
+               
+       case RAW_FILEINFO_ALT_NAME_INFO:
+       case RAW_FILEINFO_ALT_NAME_INFORMATION:
+               info->alt_name_info.out.fname.s = info2->generic.out.alt_fname.s;
+               info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length;
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/* 
+   NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qfileinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       NTSTATUS status;
+       union smb_fileinfo info2;
+
+       if (info->generic.level == RAW_FILEINFO_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* ask the backend for the generic info */
+       info2.generic.level = RAW_FILEINFO_GENERIC;
+       info2.generic.in.fnum = info->generic.in.fnum;
+
+       status = req->conn->ntvfs_ops->qfileinfo(req, &info2);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       return ntvfs_map_fileinfo(req, info, &info2);
+}
+
+/* 
+   NTVFS pathinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qpathinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       NTSTATUS status;
+       union smb_fileinfo info2;
+
+       if (info->generic.level == RAW_FILEINFO_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* ask the backend for the generic info */
+       info2.generic.level = RAW_FILEINFO_GENERIC;
+       info2.generic.in.fname = info->generic.in.fname;
+
+       status = req->conn->ntvfs_ops->qpathinfo(req, &info2);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+       return ntvfs_map_fileinfo(req, info, &info2);
+}
diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c
new file mode 100644 (file)
index 0000000..4036fb0
--- /dev/null
@@ -0,0 +1,26 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NTVFS utility code
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements common utility functions that many NTVFS backends may wish to use
+*/
+
+#include "includes.h"
+
+
diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c
new file mode 100644 (file)
index 0000000..b27e749
--- /dev/null
@@ -0,0 +1,151 @@
+/* 
+   Unix SMB/CIFS implementation.
+   POSIX NTVFS backend
+   Copyright (C) Andrew Tridgell 1992-2003
+   Copyright (C) Andrew Bartlett      2001
+
+   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.
+*/
+/*
+  this implements most of the POSIX NTVFS backend
+  This is the default backend
+*/
+
+#include "includes.h"
+
+/*
+  connect to a share - used when a tree_connect operation comes
+  in. For a disk based backend we needs to ensure that the base
+  directory exists (tho it doesn't need to be accessible by the user,
+  that comes later)
+*/
+static NTSTATUS pvfs_connect(struct ntvfs_context *ctx, const char *sharename)
+{
+       struct stat st;
+       struct connection_struct *conn = ctx->conn;
+       NTSTATUS status;
+
+       /* the directory must exist */
+       if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+               DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", 
+                        conn->connectpath, lp_servicename(SNUM(conn))));
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+
+       /* Initialise old VFS function pointers */
+       if (!smbd_vfs_init(conn)) {
+               DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+
+       /* become the user for the rest */
+       status = ntvfs_change_to_user(ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* the posix backend can do preexec */
+       status = ntvfs_connect_preexec(ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* Invoke the old POSIX VFS make connection hook */
+       if (conn->vfs_ops.connect && 
+           conn->vfs_ops.connect(conn, lp_servicename(snum), user) < 0) {
+                       DEBUG(0,("make_connection: POSIX VFS make connection failed!\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+
+
+       /*
+        * Print out the 'connected as' stuff here as we need
+        * to know the effective uid and gid we will be using
+        * (at least initially).
+        */
+       if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
+               dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
+               dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
+               dbgtext( "initially as user %s ", user );
+               dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
+               dbgtext( "(pid %d)\n", (int)sys_getpid() );
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS pvfs_disconnect(struct ntvfs_context *ctx)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  delete a file - the dirtype specifies the file types to include in the search. 
+  The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS pvfs_unlink(struct ntvfs_context *ctx, const char *name, uint16 dirtype)
+{
+       NTSTATUS status;
+
+       if (ntvfs_dfs_redirect(ctx, name)) {
+               return NT_STATUS_PATH_NOT_COVERED;
+       }
+       
+       status = unlink_internals(ctx->conn, dirtype, name);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       ntvfs_run_change_notify_queue();
+       
+       return NT_STATUS_OK;
+}
+
+
+
+
+
+
+
+/*
+  initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL posix_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = pvfs_connect;
+       ops.disconnect = pvfs_disconnect;
+       ops.unlink = pvfs_unlink;
+
+       /* register ourselves with the NTVFS subsystem. We register under the name 'default'
+          as we wish to be the default backend */
+       ret = ntvfs_register("default", NTVFS_DISK, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register POSIX backend!\n"));
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/ntvfs/print/README b/source4/ntvfs/print/README
new file mode 100644 (file)
index 0000000..441c82d
--- /dev/null
@@ -0,0 +1,3 @@
+This is the print NTVFS backend for Samba. NTVFS operations that are
+made on print shares are directed here by default. Most directory
+operations and many file operations are not supported on print shares.
diff --git a/source4/ntvfs/print/vfs_print.c b/source4/ntvfs/print/vfs_print.c
new file mode 100644 (file)
index 0000000..2563f0a
--- /dev/null
@@ -0,0 +1,104 @@
+/* 
+   Unix SMB/CIFS implementation.
+   default print NTVFS backend
+   Copyright (C) Andrew Tridgell  2003
+
+   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.
+*/
+/*
+  this implements the print backend, called by the NTVFS subsystem to
+  handle requests on printing shares
+*/
+
+#include "includes.h"
+
+/*
+  connect to a share - used when a tree_connect operation comes
+  in. For printing shares this should check that the spool directory
+  is available
+*/
+static NTSTATUS print_connect(struct request_context *req, const char *sharename)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS print_disconnect(struct tcon_context *conn)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  lots of operations are not allowed on printing shares - mostly return NT_STATUS_ACCESS_DENIED
+*/
+static NTSTATUS print_unlink(struct request_context *req, struct smb_unlink *unl)
+{
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+  ioctl - used for job query
+*/
+static NTSTATUS print_ioctl(struct request_context *req, struct smb_ioctl *io)
+{
+       char *p;
+
+       if (io->in.request == IOCTL_QUERY_JOB_INFO) {
+               /* a request for the print job id of an open print job */
+               io->out.blob = data_blob_talloc(req->mem_ctx, NULL, 32);
+
+               memset(io->out.blob.data, 0, io->out.blob.length);
+
+               p = io->out.blob.data;
+               SSVAL(p,0, 1 /* REWRITE: fsp->rap_print_jobid */);
+               push_string(NULL, p+2, lp_netbios_name(), 15, STR_TERMINATE|STR_ASCII);
+               push_string(NULL, p+18, lp_servicename(req->conn->service), 13, STR_TERMINATE|STR_ASCII);
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_PARAMETER;
+}
+
+
+/*
+  initialialise the print backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL print_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = print_connect;
+       ops.disconnect = print_disconnect;
+       ops.unlink = print_unlink;
+       ops.ioctl = print_ioctl;
+
+       /* register ourselves with the NTVFS subsystem. We register under the name 'default'
+          as we wish to be the default backend */
+       ret = ntvfs_register("default", NTVFS_PRINT, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register PRINT backend!\n"));
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/ntvfs/reference/ref.h b/source4/ntvfs/reference/ref.h
new file mode 100644 (file)
index 0000000..00eab76
--- /dev/null
@@ -0,0 +1,36 @@
+
+struct rvfs_private {
+       /* the meta-data database */
+       TDB_CONTEXT *tdb;
+
+       /* the base directory */
+       char *connectpath;
+
+       /* a linked list of open searches */
+       struct search_state *search;
+
+       /* next available search handle */
+       uint16 next_search_handle;
+};
+
+struct rvfs_dir {
+       uint_t count;
+       char *unix_dir;
+       struct {
+               char *name;
+       } *files;
+};
+
+struct search_state {
+       struct search_state *next, *prev;
+       TALLOC_CTX *mem_ctx;
+       uint16 handle;
+       uint_t current_index;
+       struct rvfs_dir *dir;
+};
+
+
+struct ref_struct {
+       NTTIME mtime, ctime, atime, wtime;
+       char *;
+};
diff --git a/source4/ntvfs/reference/ref_util.c b/source4/ntvfs/reference/ref_util.c
new file mode 100644 (file)
index 0000000..8234944
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   simple NTVFS filesystem backend
+
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  utility functions for simple backend
+*/
+
+#include "includes.h"
+#include "svfs.h"
+
+/*
+  convert a windows path to a unix path - don't do any manging or case sensitive handling
+*/
+char *svfs_unix_path(struct request_context *req, const char *name)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+       char *ret;
+
+       if (*name != '\\') {
+               ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name);
+       } else {
+               ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name);
+       }
+       all_string_sub(ret, "\\", "/", 0);
+
+       strlower(ret);
+
+       return ret;
+}
+
+
+/*
+  read a directory and find all matching file names and stat info
+  returned names are separate unix and DOS names. The returned names
+  are relative to the directory
+*/
+struct svfs_dir *svfs_list(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern)
+{
+       char *unix_path;
+       char *p, *mask;
+       struct svfs_dir *dir;
+       DIR *odir;
+       struct dirent *dent;
+       uint_t allocated = 0;
+       char *low_mask;
+
+       unix_path = svfs_unix_path(req, pattern);
+       if (!unix_path) { return NULL; }
+
+       dir = talloc(mem_ctx, sizeof(struct svfs_dir));
+       if (!dir) { return NULL; }
+
+       dir->count = 0;
+       dir->files = 0;
+
+       /* find the base directory */
+       p = strrchr(unix_path, '/');
+       if (!p) { return NULL; }
+
+       dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
+       if (!dir->unix_dir) { return NULL; }
+
+       /* the wildcard pattern is the last part */
+       mask = p+1;
+
+       low_mask = talloc_strdup(mem_ctx, mask);
+       if (!low_mask) { return NULL; }
+       strlower(low_mask);
+
+       odir = opendir(dir->unix_dir);
+       if (!odir) { return NULL; }
+
+       while ((dent = readdir(odir))) {
+               uint_t i = dir->count;
+               char *full_name;
+               char *low_name;
+
+               low_name = talloc_strdup(mem_ctx, dent->d_name);
+               if (!low_name) { continue; }
+               strlower(low_name);
+
+               /* check it matches the wildcard pattern */
+               if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) {
+                       continue;
+               }
+               
+               if (dir->count >= allocated) {
+                       allocated = (allocated + 100) * 1.2;
+                       dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0]));
+                       if (!dir->files) { 
+                               closedir(odir);
+                               return NULL;
+                       }
+               }
+
+               dir->files[i].name = low_name;
+               if (!dir->files[i].name) { continue; }
+
+               asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name);
+               if (!full_name) { continue; }
+
+               if (stat(full_name, &dir->files[i].st) == 0) { 
+                       dir->count++;
+               }
+
+               free(full_name); 
+       }
+
+       closedir(odir);
+
+       return dir;
+}
+
+
+/*
+  convert a unix stat struct to a dos attrib
+*/
+uint32 svfs_file_attrib(struct stat *st)
+{
+       if (S_ISDIR(st->st_mode)) {
+               return FILE_ATTRIBUTE_DIRECTORY;
+       }
+       return 0;
+}
diff --git a/source4/ntvfs/reference/vfs_ref.c b/source4/ntvfs/reference/vfs_ref.c
new file mode 100644 (file)
index 0000000..b4fa2e6
--- /dev/null
@@ -0,0 +1,843 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   simple NTVFS filesystem backend
+
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements a very simple NTVFS filesystem backend. 
+  
+  this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
+  minimal work to give a working backend.
+*/
+
+#include "includes.h"
+#include "svfs.h"
+
+/*
+  connect to a share - used when a tree_connect operation comes
+  in. For a disk based backend we needs to ensure that the base
+  directory exists (tho it doesn't need to be accessible by the user,
+  that comes later)
+*/
+static NTSTATUS svfs_connect(struct request_context *req, const char *sharename)
+{
+       struct stat st;
+       struct tcon_context *conn = req->conn;
+       struct svfs_private *private;
+
+       conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private));
+
+       private = conn->ntvfs_private;
+
+       private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service));
+
+       /* the directory must exist */
+       if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+               DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", 
+                        private->connectpath, sharename));
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+
+       conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS");
+       conn->dev_type = talloc_strdup(conn->mem_ctx, "A:");
+
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS svfs_disconnect(struct request_context *req)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  delete a file - the dirtype specifies the file types to include in the search. 
+  The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS svfs_unlink(struct request_context *req, struct smb_unlink *unl)
+{
+       char *unix_path;
+
+       unix_path = svfs_unix_path(req, unl->in.pattern);
+
+       /* ignoring wildcards ... */
+       if (unlink(unix_path) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  ioctl interface - we don't do any
+*/
+static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io)
+{
+       return NT_STATUS_INVALID_PARAMETER;
+}
+
+/*
+  check if a directory exists
+*/
+static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp)
+{
+       char *unix_path;
+       struct stat st;
+
+       unix_path = svfs_unix_path(req, cp->in.path);
+
+       if (stat(unix_path, &st) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (!S_ISDIR(st.st_mode)) {
+               return NT_STATUS_NOT_A_DIRECTORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  approximately map a struct stat to a fileinfo struct
+*/
+static NTSTATUS map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st)
+{
+       switch (info->generic.level) {
+       case SMB_FILEINFO_NETWORK_OPEN_INFORMATION:
+               unix_to_nt_time(&info->netopen.out.create_time, st->st_ctime);
+               unix_to_nt_time(&info->netopen.out.access_time, st->st_atime);
+               unix_to_nt_time(&info->netopen.out.write_time,  st->st_mtime);
+               unix_to_nt_time(&info->netopen.out.change_time, st->st_mtime);
+               info->netopen.out.alloc_size = st->st_size;
+               info->netopen.out.size = st->st_size;
+               info->netopen.out.attrib = svfs_file_attrib(st);
+               info->netopen.out.unknown = 0;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_ALL_INFO:
+               unix_to_nt_time(&info->all_info.out.create_time, st->st_ctime);
+               unix_to_nt_time(&info->all_info.out.access_time, st->st_atime);
+               unix_to_nt_time(&info->all_info.out.write_time,  st->st_mtime);
+               unix_to_nt_time(&info->all_info.out.change_time, st->st_mtime);
+               info->all_info.out.attrib = svfs_file_attrib(st);
+               info->all_info.out.alloc_size = st->st_size;
+               info->all_info.out.size = st->st_size;
+               info->all_info.out.nlink = st->st_nlink;
+               info->all_info.out.delete_pending = 0;
+               info->all_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+               info->all_info.out.index_number = st->st_ino;
+               info->all_info.out.ea_size = 0;
+               info->all_info.out.access_flags = 0xd01BF; /* what is this!? */
+               info->all_info.out.current_offset = 0;
+               info->all_info.out.open_mode = 0; /* what do do put here!? */
+               info->all_info.out.alignment_requirement = 0; /* what do do put here!? */
+               info->all_info.out.fname = talloc_strdup(req->mem_ctx, "TODO - STORE FILENAME");
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_BASIC:
+               unix_to_nt_time(&info->basic.out.create_time, st->st_ctime);
+               unix_to_nt_time(&info->basic.out.access_time, st->st_atime);
+               unix_to_nt_time(&info->basic.out.write_time,  st->st_mtime);
+               unix_to_nt_time(&info->basic.out.change_time, st->st_mtime);
+               info->basic.out.attrib = svfs_file_attrib(st);
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_INFO_STANDARD:
+               info->info_standard.out.create_time = st->st_ctime;
+               info->info_standard.out.access_time = st->st_atime;
+               info->info_standard.out.write_time = st->st_mtime;
+               info->info_standard.out.size = st->st_size;
+               info->info_standard.out.alloc_size = st->st_size;
+               info->info_standard.out.attrib = svfs_file_attrib(st);
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_INFO_STANDARD_EA:
+               info->info_standard_ea.out.create_time = st->st_ctime;
+               info->info_standard_ea.out.access_time = st->st_atime;
+               info->info_standard_ea.out.write_time = st->st_mtime;
+               info->info_standard_ea.out.size = st->st_size;
+               info->info_standard_ea.out.alloc_size = st->st_size;
+               info->info_standard_ea.out.attrib = svfs_file_attrib(st);
+               info->info_standard_ea.out.ea_size = 0;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_STANDARD_INFO:
+               info->standard_info.out.alloc_size = st->st_size;
+               info->standard_info.out.size = st->st_size;
+               info->standard_info.out.nlink = st->st_nlink;
+               info->standard_info.out.delete_pending = 0;
+               info->standard_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+               info->standard_info.out.unknown = 0;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_INTERNAL:
+               info->internal.out.device = st->st_dev;
+               info->internal.out.device = st->st_ino;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_EA:
+               info->ea.out.unknown = 0;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_ATTRIB_TAGINFO:
+               info->tag.out.attrib = svfs_file_attrib(st);
+               info->tag.out.reparse_tag = 0;
+               return NT_STATUS_OK;
+
+       case SMB_FILEINFO_STREAM:
+               /* setup a single data stream */
+               info->stream.out.num_streams = 1;
+               info->stream.out.streams = talloc(req->mem_ctx, sizeof(info->stream.out.streams[0]));
+               if (!info->stream.out.streams) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               info->stream.out.streams[0].size = st->st_size;
+               info->stream.out.streams[0].alloc_size = st->st_size;
+               info->stream.out.streams[0].stream_name = talloc_strdup(req->mem_ctx,"::$DATA");
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+  return info on a pathname
+*/
+static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       char *unix_path;
+       struct stat st;
+
+       unix_path = svfs_unix_path(req, info->basic.in.fname);
+
+       if (stat(unix_path, &st) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return map_fileinfo(req, info, &st);
+}
+
+/*
+  query info on a open file
+*/
+static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       struct stat st;
+
+       if (fstat(info->generic.in.fnum, &st) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return map_fileinfo(req, info, &st);
+}
+
+
+/*
+  set info on a pathname
+*/
+static NTSTATUS svfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  open a file
+*/
+static NTSTATUS svfs_open(struct request_context *req, union smb_open *io)
+{
+       char *unix_path;
+       struct stat st;
+       int fd, flags;
+       
+       if (io->generic.level != SMB_OPEN_GENERIC) {
+               return ntvfs_map_open(req, io);
+       }
+
+       unix_path = svfs_unix_path(req, io->ntcreatex.in.fname);
+
+       switch (io->generic.in.open_disposition) {
+       case FILE_SUPERSEDE:
+               flags = O_RDWR | O_CREAT | O_TRUNC;
+               break;
+       case FILE_OPEN:
+               flags = O_RDWR;
+               break;
+       case FILE_CREATE:
+               flags = O_RDWR | O_CREAT | O_EXCL;
+               break;
+       case FILE_OPEN_IF:
+               flags = O_RDWR | O_CREAT;
+               break;
+       case FILE_OVERWRITE:
+               flags = O_RDWR;
+               break;
+       case FILE_OVERWRITE_IF:
+               flags = O_RDWR | O_CREAT | O_TRUNC;
+               break;
+       default:
+               flags = O_RDWR;
+               break;
+       }
+
+       if (io->generic.in.create_options & FILE_DIRECTORY_FILE) {
+               flags = O_RDONLY | O_DIRECTORY;
+               switch (io->generic.in.open_disposition) {
+               case FILE_CREATE:
+                       if (mkdir(unix_path, 0755) == -1) {
+                               return map_nt_error_from_unix(errno);
+                       }
+                       break;
+               case FILE_OPEN_IF:
+                       if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
+                               return map_nt_error_from_unix(errno);
+                       }
+                       break;
+               }
+       }
+
+       fd = open(unix_path, flags, 0644);
+       if (fd == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               return map_nt_error_from_unix(errno);
+       }
+
+       ZERO_STRUCT(io->generic.out);
+       
+       unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
+       unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
+       unix_to_nt_time(&io->generic.out.write_time,  st.st_mtime);
+       unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
+       io->generic.out.fnum = fd;
+       io->generic.out.alloc_size = st.st_size;
+       io->generic.out.size = st.st_size;
+       io->generic.out.file_attr = svfs_file_attrib(&st);
+       io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
+
+       return NT_STATUS_OK;
+}
+
+/*
+  create a directory
+*/
+static NTSTATUS svfs_mkdir(struct request_context *req, union smb_mkdir *md)
+{
+       char *unix_path;
+
+       if (md->generic.level != RAW_MKDIR_MKDIR) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       unix_path = svfs_unix_path(req, md->mkdir.in.path);
+
+       if (mkdir(unix_path, 0777) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  remove a directory
+*/
+static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd)
+{
+       char *unix_path;
+
+       unix_path = svfs_unix_path(req, rd->in.path);
+
+       if (rmdir(unix_path) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  rename a set of files
+*/
+static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren)
+{
+       char *unix_path1, *unix_path2;
+
+       unix_path1 = svfs_unix_path(req, ren->in.pattern1);
+       unix_path2 = svfs_unix_path(req, ren->in.pattern2);
+
+       if (rename(unix_path1, unix_path2) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*
+  copy a set of files
+*/
+static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  read from a file
+*/
+static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd)
+{
+       ssize_t ret;
+
+       if (rd->generic.level != SMB_READ_READX) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       ret = pread(rd->readx.in.fnum, 
+                   rd->readx.out.data, 
+                   rd->readx.in.maxcnt,
+                   rd->readx.in.offset);
+       if (ret == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       rd->readx.out.nread = ret;
+       rd->readx.out.remaining = 0; /* should fill this in? */
+
+       return NT_STATUS_OK;
+}
+
+/*
+  write to a file
+*/
+static NTSTATUS svfs_write(struct request_context *req, union smb_write *wr)
+{
+       ssize_t ret;
+
+       switch (wr->generic.level) {
+       case SMB_WRITE_WRITEX:
+               ret = pwrite(wr->writex.in.fnum, 
+                            wr->writex.in.data, 
+                            wr->writex.in.count,
+                            wr->writex.in.offset);
+               if (ret == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+               
+               wr->writex.out.nwritten = ret;
+               wr->writex.out.remaining = 0; /* should fill this in? */
+
+               return NT_STATUS_OK;
+
+       case SMB_WRITE_WRITE:
+               if (wr->write.in.count == 0) {
+                       /* a truncate! */
+                       ret = ftruncate(wr->write.in.fnum, wr->write.in.offset);
+               } else {
+                       ret = pwrite(wr->write.in.fnum, 
+                                    wr->write.in.data, 
+                                    wr->write.in.count,
+                                    wr->write.in.offset);
+               }
+               if (ret == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+               
+               wr->write.out.nwritten = ret;
+
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  seek in a file
+*/
+static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  flush a file
+*/
+static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io)
+{
+       fsync(io->in.fnum);
+       return NT_STATUS_OK;
+}
+
+/*
+  close a file
+*/
+static NTSTATUS svfs_close(struct request_context *req, union smb_close *io)
+{
+       if (io->generic.level != SMB_CLOSE_CLOSE) {
+               /* we need a mapping function */
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       if (close(io->close.in.fnum) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  exit - closing files?
+*/
+static NTSTATUS svfs_exit(struct request_context *req)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  lock a byte range
+*/
+static NTSTATUS svfs_lock(struct request_context *req, union smb_lock *lck)
+{
+       DEBUG(0,("REWRITE: not doing byte range locking!\n"));
+       return NT_STATUS_OK;
+}
+
+/*
+  set info on a open file
+*/
+static NTSTATUS svfs_setfileinfo(struct request_context *req, 
+                                union smb_setfileinfo *info)
+{
+       DEBUG(0,("REWRITE: svfs_setfileinfo: not doing setfileinfo level %d\n", 
+                info->generic.level));
+       switch (info->generic.level) {
+       case SMB_SETFILEINFO_BASIC:
+               info->basic.out.ea_error_offset = 0;
+               break;
+       case SMB_SETFILEINFO_END_OF_FILE:
+               if (ftruncate(info->eof.in.fnum, info->eof.in.size) != 0) {
+                       return map_nt_error_from_unix(errno);
+               }
+               info->eof.out.ea_error_offset = 0;
+               break;
+       case SMB_SETFILEINFO_ALLOCATION:
+               info->eof.out.ea_error_offset = 0;
+               break;
+       }
+       return NT_STATUS_OK;
+}
+
+
+/*
+  return filesystem space info
+*/
+static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+
+       if (fs->generic.level != SMB_FSINFO_GENERIC) {
+               return ntvfs_map_fsinfo(req, fs);
+       }
+
+       if (sys_fsusage(private->connectpath, 
+                       &fs->generic.out.blocks_free, 
+                       &fs->generic.out.blocks_total) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       fs->generic.out.block_size = 512;
+
+       return NT_STATUS_OK;
+}
+
+/*
+  return filesystem attribute info
+*/
+static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs)
+{
+       struct stat st;
+       struct svfs_private *private = req->conn->ntvfs_private;
+
+       if (fs->generic.level != SMB_FSATTR_GENERIC) {
+               return ntvfs_map_fsattr(req, fs);
+       }
+
+       if (stat(private->connectpath, &st) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+       fs->generic.out.fs_attr = 
+               FILE_CASE_PRESERVED_NAMES | 
+               FILE_CASE_SENSITIVE_SEARCH | 
+               FILE_PERSISTENT_ACLS;
+       fs->generic.out.max_file_component_length = 255;
+       fs->generic.out.serial_number = 1;
+       fs->generic.out.fs_type = talloc_strdup(req->mem_ctx, "NTFS");
+       fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, 
+                                                   lp_servicename(req->conn->service));
+
+       return NT_STATUS_OK;
+}
+
+/*
+  return print queue info
+*/
+static NTSTATUS svfs_lpq(struct request_context *req, union smb_lpq *lpq)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/* 
+   list files in a directory matching a wildcard pattern
+*/
+NTSTATUS svfs_search_first(struct request_context *req, union smb_search_first *io, 
+                          void *search_private, 
+                          BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct svfs_dir *dir;
+       int i;
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+       union smb_search_data file;
+       TALLOC_CTX *mem_ctx;
+       uint_t max_count;
+
+       if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       mem_ctx = talloc_init("svfs_search");
+
+       search = talloc_zero(mem_ctx, sizeof(struct search_state));
+       if (!search) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       max_count = io->t2ffirst.in.max_count;
+
+       dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern);
+       if (!dir) {
+               talloc_destroy_pool(mem_ctx);
+               return NT_STATUS_FOOBAR;
+       }
+
+       search->mem_ctx = mem_ctx;
+       search->handle = private->next_search_handle;
+       search->dir = dir;
+
+       if (dir->count < max_count) {
+               max_count = dir->count;
+       }
+
+       for (i=0; i < max_count;i++) {
+               ZERO_STRUCT(file);
+               unix_to_nt_time(&file.both.create_time, dir->files[i].st.st_ctime);
+               unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime);
+               unix_to_nt_time(&file.both.write_time,  dir->files[i].st.st_mtime);
+               unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime);
+               file.both.name = dir->files[i].name;
+               file.both.short_name = dir->files[i].name;
+               file.both.size = dir->files[i].st.st_size;
+               file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st);
+
+               if (!callback(search_private, &file)) {
+                       break;
+               }
+       }
+
+       search->current_index = i;
+
+       io->t2ffirst.out.count = i;
+       io->t2ffirst.out.handle = search->handle;
+       io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+       /* work out if we are going to keep the search state */
+       if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+           ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+               talloc_destroy(search->mem_ctx);
+       } else {
+               private->next_search_handle++;
+               DLIST_ADD(private->search, search);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* continue a search */
+NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, 
+                          void *search_private, 
+                          BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct svfs_dir *dir;
+       int i;
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+       union smb_search_data file;
+       uint_t max_count;
+
+       if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       for (search=private->search; search; search = search->next) {
+               if (search->handle == io->t2fnext.in.handle) break;
+       }
+       
+       if (!search) {
+               /* we didn't find the search handle */
+               return NT_STATUS_FOOBAR;
+       }
+
+       dir = search->dir;
+
+       /* the client might be asking for something other than just continuing
+          with the search */
+       if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
+           (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
+           io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+               /* look backwards first */
+               for (i=search->current_index; i > 0; i--) {
+                       if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+                               search->current_index = i;
+                               goto found;
+                       }
+               }
+
+               /* then look forwards */
+               for (i=search->current_index+1; i <= dir->count; i++) {
+                       if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+                               search->current_index = i;
+                               goto found;
+                       }
+               }
+       }
+
+found: 
+       max_count = search->current_index + io->t2fnext.in.max_count;
+
+       if (max_count > dir->count) {
+               max_count = dir->count;
+       }
+
+       for (i = search->current_index; i < max_count;i++) {
+               ZERO_STRUCT(file);
+               unix_to_nt_time(&file.both.create_time, dir->files[i].st.st_ctime);
+               unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime);
+               unix_to_nt_time(&file.both.write_time,  dir->files[i].st.st_mtime);
+               unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime);
+               file.both.name = dir->files[i].name;
+               file.both.short_name = dir->files[i].name;
+               file.both.size = dir->files[i].st.st_size;
+               file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st);
+
+               if (!callback(search_private, &file)) {
+                       break;
+               }
+       }
+
+       io->t2fnext.out.count = i - search->current_index;
+       io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+       search->current_index = i;
+
+       /* work out if we are going to keep the search state */
+       if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+           ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+               DLIST_REMOVE(private->search, search);
+               talloc_destroy(search->mem_ctx);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* close a search */
+NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+
+       for (search=private->search; search; search = search->next) {
+               if (search->handle == io->findclose.in.handle) break;
+       }
+       
+       if (!search) {
+               /* we didn't find the search handle */
+               return NT_STATUS_FOOBAR;
+       }
+
+       DLIST_REMOVE(private->search, search);
+       talloc_destroy(search->mem_ctx);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL posix_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = svfs_connect;
+       ops.disconnect = svfs_disconnect;
+       ops.unlink = svfs_unlink;
+       ops.chkpath = svfs_chkpath;
+       ops.qpathinfo = svfs_qpathinfo;
+       ops.setpathinfo = svfs_setpathinfo;
+       ops.open = svfs_open;
+       ops.mkdir = svfs_mkdir;
+       ops.rmdir = svfs_rmdir;
+       ops.rename = svfs_rename;
+       ops.copy = svfs_copy;
+       ops.ioctl = svfs_ioctl;
+       ops.read = svfs_read;
+       ops.write = svfs_write;
+       ops.seek = svfs_seek;
+       ops.flush = svfs_flush; 
+       ops.close = svfs_close;
+       ops.exit = svfs_exit;
+       ops.lock = svfs_lock;
+       ops.setfileinfo = svfs_setfileinfo;
+       ops.qfileinfo = svfs_qfileinfo;
+       ops.fsinfo = svfs_fsinfo;
+       ops.fsattr = svfs_fsattr;
+       ops.lpq = svfs_lpq;
+       ops.search_first = svfs_search_first;
+       ops.search_next = svfs_search_next;
+       ops.search_close = svfs_search_close;
+
+       /* register ourselves with the NTVFS subsystem. We register under the name 'default'
+          as we wish to be the default backend */
+       ret = ntvfs_register("simple", NTVFS_DISK, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register POSIX backend!\n"));
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/ntvfs/simple/svfs.h b/source4/ntvfs/simple/svfs.h
new file mode 100644 (file)
index 0000000..e74fd07
--- /dev/null
@@ -0,0 +1,28 @@
+
+struct svfs_private {
+       /* the base directory */
+       char *connectpath;
+
+       /* a linked list of open searches */
+       struct search_state *search;
+
+       /* next available search handle */
+       uint16 next_search_handle;
+};
+
+struct svfs_dir {
+       uint_t count;
+       char *unix_dir;
+       struct {
+               char *name;
+               struct stat st;
+       } *files;
+};
+
+struct search_state {
+       struct search_state *next, *prev;
+       TALLOC_CTX *mem_ctx;
+       uint16 handle;
+       uint_t current_index;
+       struct svfs_dir *dir;
+};
diff --git a/source4/ntvfs/simple/svfs_util.c b/source4/ntvfs/simple/svfs_util.c
new file mode 100644 (file)
index 0000000..a8a88cf
--- /dev/null
@@ -0,0 +1,162 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   simple NTVFS filesystem backend
+
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  utility functions for simple backend
+*/
+
+#include "includes.h"
+#include "svfs.h"
+
+/*
+  convert a windows path to a unix path - don't do any manging or case sensitive handling
+*/
+char *svfs_unix_path(struct request_context *req, const char *name)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+       char *ret;
+
+       if (*name != '\\') {
+               ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name);
+       } else {
+               ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name);
+       }
+       all_string_sub(ret, "\\", "/", 0);
+
+       strlower(ret);
+
+       return ret;
+}
+
+
+/*
+  read a directory and find all matching file names and stat info
+  returned names are separate unix and DOS names. The returned names
+  are relative to the directory
+*/
+struct svfs_dir *svfs_list(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern)
+{
+       char *unix_path;
+       char *p, *mask;
+       struct svfs_dir *dir;
+       DIR *odir;
+       struct dirent *dent;
+       uint_t allocated = 0;
+       char *low_mask;
+
+       unix_path = svfs_unix_path(req, pattern);
+       if (!unix_path) { return NULL; }
+
+       dir = talloc(mem_ctx, sizeof(struct svfs_dir));
+       if (!dir) { return NULL; }
+
+       dir->count = 0;
+       dir->files = 0;
+
+       /* find the base directory */
+       p = strrchr(unix_path, '/');
+       if (!p) { return NULL; }
+
+       dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
+       if (!dir->unix_dir) { return NULL; }
+
+       /* the wildcard pattern is the last part */
+       mask = p+1;
+
+       low_mask = talloc_strdup(mem_ctx, mask);
+       if (!low_mask) { return NULL; }
+       strlower(low_mask);
+
+       odir = opendir(dir->unix_dir);
+       if (!odir) { return NULL; }
+
+       while ((dent = readdir(odir))) {
+               uint_t i = dir->count;
+               char *full_name;
+               char *low_name;
+
+               low_name = talloc_strdup(mem_ctx, dent->d_name);
+               if (!low_name) { continue; }
+               strlower(low_name);
+
+               /* check it matches the wildcard pattern */
+               if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) {
+                       continue;
+               }
+               
+               if (dir->count >= allocated) {
+                       allocated = (allocated + 100) * 1.2;
+                       dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0]));
+                       if (!dir->files) { 
+                               closedir(odir);
+                               return NULL;
+                       }
+               }
+
+               dir->files[i].name = low_name;
+               if (!dir->files[i].name) { continue; }
+
+               asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name);
+               if (!full_name) { continue; }
+
+               if (stat(full_name, &dir->files[i].st) == 0) { 
+                       dir->count++;
+               }
+
+               free(full_name); 
+       }
+
+       closedir(odir);
+
+       return dir;
+}
+
+
+/*******************************************************************
+set the time on a file via file descriptor
+*******************************************************************/
+int svfs_file_utime(int fd, struct utimbuf *times)
+{
+       char *fd_path = NULL;
+       int ret;
+
+       asprintf(&fd_path, "/proc/self/%d", fd);
+       if (!fd_path) {
+               errno = ENOMEM;
+               return -1;
+       }
+       
+       ret = utime(fd_path, times);
+       free(fd_path);
+       return ret;
+}
+
+
+/*
+  map a unix file attrib to a DOS attribute
+*/
+uint16 svfs_unix_to_dos_attrib(mode_t mode)
+{
+       uint16 ret = 0;
+       if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
+       if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
+       return ret;
+}
diff --git a/source4/ntvfs/simple/vfs_simple.c b/source4/ntvfs/simple/vfs_simple.c
new file mode 100644 (file)
index 0000000..caf7732
--- /dev/null
@@ -0,0 +1,848 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   simple NTVFS filesystem backend
+
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+  this implements a very simple NTVFS filesystem backend. 
+  
+  this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
+  minimal work to give a working backend.
+*/
+
+#include "includes.h"
+#include "svfs.h"
+
+/*
+  connect to a share - used when a tree_connect operation comes
+  in. For a disk based backend we needs to ensure that the base
+  directory exists (tho it doesn't need to be accessible by the user,
+  that comes later)
+*/
+static NTSTATUS svfs_connect(struct request_context *req, const char *sharename)
+{
+       struct stat st;
+       struct tcon_context *conn = req->conn;
+       struct svfs_private *private;
+
+       conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private));
+
+       private = conn->ntvfs_private;
+
+       private->next_search_handle = 0;
+       private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service));
+
+       /* the directory must exist */
+       if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+               DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", 
+                        private->connectpath, sharename));
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+
+       conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS");
+       conn->dev_type = talloc_strdup(conn->mem_ctx, "A:");
+
+       return NT_STATUS_OK;
+}
+
+/*
+  disconnect from a share
+*/
+static NTSTATUS svfs_disconnect(struct tcon_context *req)
+{
+       return NT_STATUS_OK;
+}
+
+/*
+  delete a file - the dirtype specifies the file types to include in the search. 
+  The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS svfs_unlink(struct request_context *req, struct smb_unlink *unl)
+{
+       char *unix_path;
+
+       unix_path = svfs_unix_path(req, unl->in.pattern);
+
+       /* ignoring wildcards ... */
+       if (unlink(unix_path) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  ioctl interface - we don't do any
+*/
+static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io)
+{
+       return NT_STATUS_INVALID_PARAMETER;
+}
+
+/*
+  check if a directory exists
+*/
+static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp)
+{
+       char *unix_path;
+       struct stat st;
+
+       unix_path = svfs_unix_path(req, cp->in.path);
+
+       if (stat(unix_path, &st) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (!S_ISDIR(st.st_mode)) {
+               return NT_STATUS_NOT_A_DIRECTORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  approximately map a struct stat to a generic fileinfo struct
+*/
+static NTSTATUS svfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st)
+{
+       unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
+       unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
+       unix_to_nt_time(&info->generic.out.write_time,  st->st_mtime);
+       unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
+       info->generic.out.alloc_size = st->st_size;
+       info->generic.out.size = st->st_size;
+       info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
+       info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
+       info->generic.out.nlink = st->st_nlink;
+       info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+       info->generic.out.device = st->st_dev;
+       info->generic.out.inode = st->st_ino;
+       /* REWRITE: TODO stuff in here */
+       info->generic.out.delete_pending = 0;
+       info->generic.out.ea_size = 0;
+       info->generic.out.num_eas = 0;
+       info->generic.out.fname.s = talloc_strdup(req->mem_ctx, "TODO - STORE FILENAME");
+       info->generic.out.alt_fname.s = talloc_strdup(req->mem_ctx, "TODO - STORE ALT_FN");
+       info->generic.out.ex_attrib = 0;
+       info->generic.out.compressed_size = 0;
+       info->generic.out.format = 0;
+       info->generic.out.unit_shift = 0;
+       info->generic.out.chunk_shift = 0;
+       info->generic.out.cluster_shift = 0;
+       
+       info->generic.out.access_flags = 0;
+       info->generic.out.position = 0;
+       info->generic.out.mode = 0;
+       info->generic.out.alignment_requirement = 0;
+       info->generic.out.reparse_tag = 0;
+       info->generic.out.num_streams = 0;
+       /* setup a single data stream */
+       info->generic.out.num_streams = 1;
+       info->generic.out.streams = talloc(req->mem_ctx, sizeof(info->stream_info.out.streams[0]));
+       if (!info->generic.out.streams) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       info->generic.out.streams[0].size = st->st_size;
+       info->generic.out.streams[0].alloc_size = st->st_size;
+       info->generic.out.streams[0].stream_name.s = talloc_strdup(req->mem_ctx,"::$DATA");
+       /* REWRITE: end */
+
+       return NT_STATUS_OK;
+}
+
+/*
+  return info on a pathname
+*/
+static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       char *unix_path;
+       struct stat st;
+
+       DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.fname, info->generic.level));
+       if (info->generic.level != RAW_FILEINFO_GENERIC) {
+               return ntvfs_map_qpathinfo(req, info);
+       }
+       
+       unix_path = svfs_unix_path(req, info->generic.in.fname);
+       DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
+       if (stat(unix_path, &st) == -1) {
+               DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
+               if (errno == 0)
+                       errno = ENOENT;
+               return map_nt_error_from_unix(errno);
+       }
+       DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
+       return svfs_map_fileinfo(req, info, &st);
+}
+
+/*
+  query info on a open file
+*/
+static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info)
+{
+       struct stat st;
+
+       if (info->generic.level != RAW_FILEINFO_GENERIC) {
+               return ntvfs_map_qfileinfo(req, info);
+       }
+       
+       if (fstat(info->generic.in.fnum, &st) == -1) {
+               if (errno == 0)
+                       errno = ENOENT;
+               return map_nt_error_from_unix(errno);
+       }
+
+       return svfs_map_fileinfo(req, info, &st);
+}
+
+
+/*
+  open a file
+*/
+static NTSTATUS svfs_open(struct request_context *req, union smb_open *io)
+{
+       char *unix_path;
+       struct stat st;
+       int fd, flags;
+       
+       if (io->generic.level != RAW_OPEN_GENERIC) {
+               return ntvfs_map_open(req, io);
+       }
+
+       unix_path = svfs_unix_path(req, io->ntcreatex.in.fname);
+
+       switch (io->generic.in.open_disposition) {
+       case NTCREATEX_DISP_SUPERSEDE:
+               flags = O_RDWR | O_CREAT | O_TRUNC;
+               break;
+       case NTCREATEX_DISP_OPEN:
+               flags = O_RDWR;
+               break;
+       case NTCREATEX_DISP_CREATE:
+               flags = O_RDWR | O_CREAT | O_EXCL;
+               break;
+       case NTCREATEX_DISP_OPEN_IF:
+               flags = O_RDWR | O_CREAT;
+               break;
+       case NTCREATEX_DISP_OVERWRITE:
+               flags = O_RDWR;
+               break;
+       case NTCREATEX_DISP_OVERWRITE_IF:
+               flags = O_RDWR | O_CREAT | O_TRUNC;
+               break;
+       default:
+               flags = O_RDWR;
+               break;
+       }
+
+       if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
+               flags = O_RDONLY | O_DIRECTORY;
+               switch (io->generic.in.open_disposition) {
+               case NTCREATEX_DISP_CREATE:
+                       if (mkdir(unix_path, 0755) == -1) {
+                               DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+                               return map_nt_error_from_unix(errno);
+                       }
+                       break;
+               case NTCREATEX_DISP_OPEN_IF:
+                       if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
+                               DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+                               return map_nt_error_from_unix(errno);
+                       }
+                       break;
+               }
+       }
+       DEBUG(9,("svfs_open: file %s flags=0x%x\n", unix_path, flags));
+       fd = open(unix_path, flags, 0644);
+       DEBUG(9,("svfs_open: fd=%d errno=%d\n", fd, errno));
+       if (fd == -1) {
+               if (errno == 0)
+                       errno = ENOENT;
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (fstat(fd, &st) == -1) {
+               DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
+               if (errno == 0)
+                       errno = ENOENT;
+               close(fd);
+               return map_nt_error_from_unix(errno);
+       }
+
+       ZERO_STRUCT(io->generic.out);
+       
+       unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
+       unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
+       unix_to_nt_time(&io->generic.out.write_time,  st.st_mtime);
+       unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
+       io->generic.out.fnum = fd;
+       io->generic.out.alloc_size = st.st_size;
+       io->generic.out.size = st.st_size;
+       io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
+       io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
+
+       return NT_STATUS_OK;
+}
+
+/*
+  create a directory
+*/
+static NTSTATUS svfs_mkdir(struct request_context *req, union smb_mkdir *md)
+{
+       char *unix_path;
+
+       if (md->generic.level != RAW_MKDIR_MKDIR) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       unix_path = svfs_unix_path(req, md->mkdir.in.path);
+
+       if (mkdir(unix_path, 0777) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  remove a directory
+*/
+static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd)
+{
+       char *unix_path;
+
+       unix_path = svfs_unix_path(req, rd->in.path);
+
+       if (rmdir(unix_path) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  rename a set of files
+*/
+static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren)
+{
+       char *unix_path1, *unix_path2;
+
+       unix_path1 = svfs_unix_path(req, ren->in.pattern1);
+       unix_path2 = svfs_unix_path(req, ren->in.pattern2);
+
+       if (rename(unix_path1, unix_path2) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*
+  copy a set of files
+*/
+static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  read from a file
+*/
+static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd)
+{
+       ssize_t ret;
+
+       if (rd->generic.level != RAW_READ_READX) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       ret = pread(rd->readx.in.fnum, 
+                   rd->readx.out.data, 
+                   rd->readx.in.maxcnt,
+                   rd->readx.in.offset);
+       if (ret == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       rd->readx.out.nread = ret;
+       rd->readx.out.remaining = 0; /* should fill this in? */
+       rd->readx.out.compaction_mode = 0; 
+
+       return NT_STATUS_OK;
+}
+
+/*
+  write to a file
+*/
+static NTSTATUS svfs_write(struct request_context *req, union smb_write *wr)
+{
+       ssize_t ret;
+
+       switch (wr->generic.level) {
+       case RAW_WRITE_WRITEX:
+               ret = pwrite(wr->writex.in.fnum, 
+                            wr->writex.in.data, 
+                            wr->writex.in.count,
+                            wr->writex.in.offset);
+               if (ret == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+               
+               wr->writex.out.nwritten = ret;
+               wr->writex.out.remaining = 0; /* should fill this in? */
+
+               return NT_STATUS_OK;
+
+       case RAW_WRITE_WRITE:
+               if (wr->write.in.count == 0) {
+                       /* a truncate! */
+                       ret = ftruncate(wr->write.in.fnum, wr->write.in.offset);
+               } else {
+                       ret = pwrite(wr->write.in.fnum, 
+                                    wr->write.in.data, 
+                                    wr->write.in.count,
+                                    wr->write.in.offset);
+               }
+               if (ret == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+               
+               wr->write.out.nwritten = ret;
+
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  seek in a file
+*/
+static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  flush a file
+*/
+static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io)
+{
+       fsync(io->in.fnum);
+       return NT_STATUS_OK;
+}
+
+/*
+  close a file
+*/
+static NTSTATUS svfs_close(struct request_context *req, union smb_close *io)
+{
+       if (io->generic.level != RAW_CLOSE_CLOSE) {
+               /* we need a mapping function */
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       if (close(io->close.in.fnum) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+  exit - closing files?
+*/
+static NTSTATUS svfs_exit(struct request_context *req)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  lock a byte range
+*/
+static NTSTATUS svfs_lock(struct request_context *req, union smb_lock *lck)
+{
+       DEBUG(0,("REWRITE: not doing byte range locking!\n"));
+       return NT_STATUS_OK;
+}
+
+/*
+  set info on a pathname
+*/
+static NTSTATUS svfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+  set info on a open file
+*/
+static NTSTATUS svfs_setfileinfo(struct request_context *req, 
+                                union smb_setfileinfo *info)
+{
+       struct utimbuf unix_times;
+       int fd;
+               
+       switch (info->generic.level) {
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               if (ftruncate(info->end_of_file_info.file.fnum, 
+                             info->end_of_file_info.in.size) != 0) {
+                       return map_nt_error_from_unix(errno);
+               }
+               break;
+       case RAW_SFILEINFO_SETATTRE:
+               unix_times.actime = info->setattre.in.access_time;
+               unix_times.modtime = info->setattre.in.write_time;
+               fd = info->setattre.file.fnum;
+       
+               if (unix_times.actime == 0 && unix_times.modtime == 0) {
+                       break;
+               } 
+
+               /* set modify time = to access time if modify time was 0 */
+               if (unix_times.actime != 0 && unix_times.modtime == 0) {
+                       unix_times.modtime = unix_times.actime;
+               }
+
+               /* Set the date on this file */
+               if (svfs_file_utime(fd, &unix_times) != 0) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+               break;
+       }
+       return NT_STATUS_OK;
+}
+
+
+/*
+  return filesystem space info
+*/
+static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct stat st;
+
+       if (fs->generic.level != RAW_QFS_GENERIC) {
+               return ntvfs_map_fsinfo(req, fs);
+       }
+
+       if (sys_fsusage(private->connectpath, 
+                       &fs->generic.out.blocks_free, 
+                       &fs->generic.out.blocks_total) == -1) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       fs->generic.out.block_size = 512;
+
+       if (stat(private->connectpath, &st) != 0) {
+               return NT_STATUS_DISK_CORRUPT_ERROR;
+       }
+       
+       fs->generic.out.fs_id = st.st_ino;
+       unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+       fs->generic.out.serial_number = st.st_ino;
+       fs->generic.out.fs_attr = 0;
+       fs->generic.out.max_file_component_length = 255;
+       fs->generic.out.device_type = 0;
+       fs->generic.out.device_characteristics = 0;
+       fs->generic.out.quota_soft = 0;
+       fs->generic.out.quota_hard = 0;
+       fs->generic.out.quota_flags = 0;
+       fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, lp_servicename(req->conn->service));
+       fs->generic.out.fs_type = req->conn->fs_type;
+
+       return NT_STATUS_OK;
+}
+
+#if 0
+/*
+  return filesystem attribute info
+*/
+static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs)
+{
+       struct stat st;
+       struct svfs_private *private = req->conn->ntvfs_private;
+
+       if (fs->generic.level != RAW_FSATTR_GENERIC) {
+               return ntvfs_map_fsattr(req, fs);
+       }
+
+       if (stat(private->connectpath, &st) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+       fs->generic.out.fs_attr = 
+               FILE_CASE_PRESERVED_NAMES | 
+               FILE_CASE_SENSITIVE_SEARCH | 
+               FILE_PERSISTENT_ACLS;
+       fs->generic.out.max_file_component_length = 255;
+       fs->generic.out.serial_number = 1;
+       fs->generic.out.fs_type = talloc_strdup(req->mem_ctx, "NTFS");
+       fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, 
+                                                   lp_servicename(req->conn->service));
+
+       return NT_STATUS_OK;
+}
+#endif
+
+/*
+  return print queue info
+*/
+static NTSTATUS svfs_lpq(struct request_context *req, union smb_lpq *lpq)
+{
+       return NT_STATUS_NOT_SUPPORTED;
+}
+
+/* 
+   list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS svfs_search_first(struct request_context *req, union smb_search_first *io, 
+                                 void *search_private, 
+                                 BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct svfs_dir *dir;
+       int i;
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+       union smb_search_data file;
+       TALLOC_CTX *mem_ctx;
+       uint_t max_count;
+
+       if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       mem_ctx = talloc_init("svfs_search");
+
+       search = talloc_zero(mem_ctx, sizeof(struct search_state));
+       if (!search) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       max_count = io->t2ffirst.in.max_count;
+
+       dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern);
+       if (!dir) {
+               talloc_destroy_pool(mem_ctx);
+               return NT_STATUS_FOOBAR;
+       }
+
+       search->mem_ctx = mem_ctx;
+       search->handle = private->next_search_handle;
+       search->dir = dir;
+
+       if (dir->count < max_count) {
+               max_count = dir->count;
+       }
+
+       for (i=0; i < max_count;i++) {
+               ZERO_STRUCT(file);
+               unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+               unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+               unix_to_nt_time(&file.both_directory_info.write_time,  dir->files[i].st.st_mtime);
+               unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+               file.both_directory_info.name.s = dir->files[i].name;
+               file.both_directory_info.short_name.s = dir->files[i].name;
+               file.both_directory_info.size = dir->files[i].st.st_size;
+               file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+               if (!callback(search_private, &file)) {
+                       break;
+               }
+       }
+
+       search->current_index = i;
+
+       io->t2ffirst.out.count = i;
+       io->t2ffirst.out.handle = search->handle;
+       io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+       /* work out if we are going to keep the search state */
+       if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+           ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+               talloc_destroy(search->mem_ctx);
+       } else {
+               private->next_search_handle++;
+               DLIST_ADD(private->search, search);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, 
+                                void *search_private, 
+                                BOOL (*callback)(void *, union smb_search_data *))
+{
+       struct svfs_dir *dir;
+       int i;
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+       union smb_search_data file;
+       uint_t max_count;
+
+       if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       for (search=private->search; search; search = search->next) {
+               if (search->handle == io->t2fnext.in.handle) break;
+       }
+       
+       if (!search) {
+               /* we didn't find the search handle */
+               return NT_STATUS_FOOBAR;
+       }
+
+       dir = search->dir;
+
+       /* the client might be asking for something other than just continuing
+          with the search */
+       if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
+           (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
+           io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+               /* look backwards first */
+               for (i=search->current_index; i > 0; i--) {
+                       if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+                               search->current_index = i;
+                               goto found;
+                       }
+               }
+
+               /* then look forwards */
+               for (i=search->current_index+1; i <= dir->count; i++) {
+                       if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+                               search->current_index = i;
+                               goto found;
+                       }
+               }
+       }
+
+found: 
+       max_count = search->current_index + io->t2fnext.in.max_count;
+
+       if (max_count > dir->count) {
+               max_count = dir->count;
+       }
+
+       for (i = search->current_index; i < max_count;i++) {
+               ZERO_STRUCT(file);
+               unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+               unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+               unix_to_nt_time(&file.both_directory_info.write_time,  dir->files[i].st.st_mtime);
+               unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+               file.both_directory_info.name.s = dir->files[i].name;
+               file.both_directory_info.short_name.s = dir->files[i].name;
+               file.both_directory_info.size = dir->files[i].st.st_size;
+               file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+               if (!callback(search_private, &file)) {
+                       break;
+               }
+       }
+
+       io->t2fnext.out.count = i - search->current_index;
+       io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+       search->current_index = i;
+
+       /* work out if we are going to keep the search state */
+       if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+           ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+               DLIST_REMOVE(private->search, search);
+               talloc_destroy(search->mem_ctx);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* close a search */
+static NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io)
+{
+       struct svfs_private *private = req->conn->ntvfs_private;
+       struct search_state *search;
+
+       for (search=private->search; search; search = search->next) {
+               if (search->handle == io->findclose.in.handle) break;
+       }
+       
+       if (!search) {
+               /* we didn't find the search handle */
+               return NT_STATUS_FOOBAR;
+       }
+
+       DLIST_REMOVE(private->search, search);
+       talloc_destroy(search->mem_ctx);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+BOOL posix_vfs_init(void)
+{
+       BOOL ret;
+       struct ntvfs_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.connect = svfs_connect;
+       ops.disconnect = svfs_disconnect;
+       ops.unlink = svfs_unlink;
+       ops.chkpath = svfs_chkpath;
+       ops.qpathinfo = svfs_qpathinfo;
+       ops.setpathinfo = svfs_setpathinfo;
+       ops.open = svfs_open;
+       ops.mkdir = svfs_mkdir;
+       ops.rmdir = svfs_rmdir;
+       ops.rename = svfs_rename;
+       ops.copy = svfs_copy;
+       ops.ioctl = svfs_ioctl;
+       ops.read = svfs_read;
+       ops.write = svfs_write;
+       ops.seek = svfs_seek;
+       ops.flush = svfs_flush; 
+       ops.close = svfs_close;
+       ops.exit = svfs_exit;
+       ops.lock = svfs_lock;
+       ops.setfileinfo = svfs_setfileinfo;
+       ops.qfileinfo = svfs_qfileinfo;
+       ops.fsinfo = svfs_fsinfo;
+       ops.lpq = svfs_lpq;
+       ops.search_first = svfs_search_first;
+       ops.search_next = svfs_search_next;
+       ops.search_close = svfs_search_close;
+
+       /* register ourselves with the NTVFS subsystem. We register under the name 'default'
+          as we wish to be the default backend */
+       ret = ntvfs_register("simple", NTVFS_DISK, &ops);
+
+       if (!ret) {
+               DEBUG(0,("Failed to register POSIX backend!\n"));
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/pam_smbpass/CHANGELOG b/source4/pam_smbpass/CHANGELOG
new file mode 100644 (file)
index 0000000..96ef784
--- /dev/null
@@ -0,0 +1,31 @@
+version 0.7.5  25 Mar 2001
+   - Use Samba 2.2.0 (alpha) as the target codebase, since it doesn't look
+     like Samba will be offering shared libraries in the near future.
+   - added a Makefile and support scripts to make the build process easier.
+   - imported some Solaris fixes that I've been sitting on.
+
+version 0.7.4  20 Jan 2000
+   - added a 'migrate' option to the authentication code which makes no
+     effort to authenticate the user, or even to ask for a password, but
+     it can be useful for filling in an SMB password db.
+
+version 0.7.3  19 Jan 2000
+   - updated to use the SAMBA_TNG Samba branch, allowing us to dynamically
+     link against Luke's new shared libs (libsamba, libsmb).
+
+version 0.7.2  20 Jul 1999
+   - miscellaneous bugfixes.  Cleanup of legacy pam_pwdb code.
+   - fixed return value of pam_sm_setcred function.
+   - fix to autoconf support
+   - clarified some of the messages being logged
+
+version 0.6,  15 Jul 1999
+   - updated to use the new Samba (2.0) password database API.
+   - added autoconf support. May now theoretically compile on more
+     platforms than PAM itself does.
+   - added support for account management functions (i.e., disabled
+     accounts)
+
+version 0.5,  4 Apr 1998
+   - added support for hashed passwords as input.  Now capable of serving
+     as an authentication agent for encrypted network transactions.
diff --git a/source4/pam_smbpass/INSTALL b/source4/pam_smbpass/INSTALL
new file mode 100644 (file)
index 0000000..ae2ba02
--- /dev/null
@@ -0,0 +1,64 @@
+
+Because pam_smbpass is derived from the Samba smbpasswd utility, recent
+versions of pam_smbpass require a copy of the Samba source code to be
+available on the build system.  Version 0.7.5 has been tested against
+Samba 2.2.0-alpha3, and this is the recommended version of Samba to use
+for building pam_smbpass.  This only affects /building/ pam_smbpass; you
+can still run any version of the Samba server that you want, although
+clearly it saves some disk space to have only one copy of the source
+code on your system (Samba 2.2.0-alpha3 takes roughly 32MB of disk space
+to build pam_smbpass).
+
+Version 0.7.5 features a new build system to make it easier to build
+pam_smbpass.
+
+
+Using the new build system
+==========================
+
+If you don't have a copy of the Samba source code on your machine, and you
+don't have a preferred Samba version (or mirror site), you can build
+pam_smbpass by just typing 'make'.
+
+If you want to use a version other than 2.2.0-alpha3, or you want to
+download the source code from a faster Samba mirror (see
+<http://us1.samba.org/samba/> for a list of mirror sites), please download
+the source code and unpack it before running make.  The build scripts will
+attempt to autodetect your Samba source directory, and if it can't be
+found automatically, you will be given the opportunity to specify an
+alternate directory for the Samba sources.
+
+Feedback is welcome if you try (or succeed!) to build pam_smbpass with
+other versions of Samba.
+
+
+Options to 'make'
+=================
+
+By default, pam_smbpass will configure the Samba build tree with the
+options
+
+    --with-fhs --with-privatedir=/etc --with-configdir=/etc
+
+This will configure pam_smbpass to look for the smbpasswd file as
+/etc/smbpasswd (or /etc/smbpasswd.tdb), and the smb.conf file as
+/etc/smb.conf.  You can override these options by setting CONFIGOPTS when
+calling make.  E.g., if you have your smb.conf file in /usr/etc and your
+smbpasswd file in /usr/etc/private, you might run
+
+    make CONFIGOPTS="--with-privatedir=/usr/etc/private --with-configdir=/usr/etc"
+
+For a complete list of available configuration options, see
+'./samba/configure --help'
+
+
+Installing the module
+=====================
+
+If all goes well in the build process, the file pam_smbpass.so will be
+created in the current directory.  Simply install the module into your
+system's PAM module directory:
+
+       install -m 755 -s bin/pam_smbpass.so /lib/security
+
+and you're all set.
diff --git a/source4/pam_smbpass/README b/source4/pam_smbpass/README
new file mode 100644 (file)
index 0000000..cf208a9
--- /dev/null
@@ -0,0 +1,66 @@
+25 Mar 2001
+
+pam_smbpass is a PAM module which can be used on conforming systems to
+keep the smbpasswd (Samba password) database in sync with the unix
+password file. PAM (Pluggable Authentication Modules) is an API supported
+under some Unices, such as Solaris, HPUX and Linux, that provides a
+generic interface to authentication mechanisms.
+
+For more information on PAM, see http://ftp.kernel.org/pub/linux/libs/pam/
+
+This module authenticates a local smbpasswd user database.  If you require
+support for authenticating against a remote SMB server, or if you're
+concerned about the presence of suid root binaries on your system, it is
+recommended that you use one of the other two following modules
+
+  pam_smb - http://www.csn.ul.ie/~airlied/pam_smb/
+    authenticates against any remote SMB server
+
+  pam_ntdom - ftp://ftp.samba.org/pub/samba/pam_ntdom/
+    authenticates against an NT or Samba domain controller
+
+Options recognized by this module are as follows:
+
+       debug           -       log more debugging info
+       audit           -       like debug, but also logs unknown usernames
+       use_first_pass  -       don't prompt the user for passwords;
+                               take them from PAM_ items instead
+       try_first_pass  -       try to get the password from a previous
+                               PAM module, fall back to prompting the user
+       use_authtok     -       like try_first_pass, but *fail* if the new
+                               PAM_AUTHTOK has not been previously set.
+                               (intended for stacking password modules only)
+       not_set_pass    -       don't make passwords used by this module
+                               available to other modules.
+       nodelay         -       don't insert ~1 second delays on authentication
+                               failure.
+       nullok          -       null passwords are allowed.
+       nonull          -       null passwords are not allowed. Used to
+                               override the Samba configuration.
+       migrate         -       only meaningful in an "auth" context;
+                               used to update smbpasswd file with a
+                               password used for successful authentication.
+       smbconf=<file>  -       specify an alternate path to the smb.conf
+                               file.
+
+See the samples/ directory for example PAM configurations using this
+module.
+
+Thanks go to the following people:
+
+* Andrew Morgan <morgan@transmeta.com>, for providing the Linux-PAM
+framework, without which none of this would have happened
+
+* Christian Gafton <gafton@redhat.com> and Andrew Morgan again, for the
+pam_pwdb module upon which pam_smbpass was originally based
+
+* Luke Leighton <lkcl@switchboard.net> for being receptive to the idea,
+and for the occasional good-natured complaint about the project's status
+that keep me working on it :)
+
+* and of course, all the other members of the Samba team 
+<http://www.samba.org/samba/team.html>, for creating a great product 
+and for giving this project a purpose
+
+---------------------
+Stephen Langasek <vorlon@netexpress.net>
diff --git a/source4/pam_smbpass/TODO b/source4/pam_smbpass/TODO
new file mode 100644 (file)
index 0000000..20cf4fb
--- /dev/null
@@ -0,0 +1,7 @@
+This is a tentative TODO file which will probably get much longer before
+it gets much shorter.
+
+- Recognizing (and overriding) debug options in the smb.conf file
+- Support for 'name=value' parameters in the PAM config
+- Compliant handling of unrecognized PAM parameters (i.e., fail on error)
+- 
diff --git a/source4/pam_smbpass/general.h b/source4/pam_smbpass/general.h
new file mode 100644 (file)
index 0000000..4f13d60
--- /dev/null
@@ -0,0 +1,130 @@
+#ifndef LINUX
+/* This is only needed by modules in the Sun implementation. */
+#include <security/pam_appl.h>
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR  
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+     const char *token;
+     unsigned int mask;            /* shall assume 32 bits of flags */
+     unsigned int flag;
+} SMB_Ctrls;
+
+#ifndef False
+#define False (0)
+#endif
+
+#ifndef True
+#define True (1)
+#endif
+
+/* macro to determine if a given flag is on */
+#define on(x,ctrl)  (smb_args[x].flag & ctrl)
+
+/* macro to determine that a given flag is NOT on */
+#define off(x,ctrl) (!on(x,ctrl))
+
+/* macro to turn on/off a ctrl flag manually */
+#define set(x,ctrl)   (ctrl = ((ctrl)&smb_args[x].mask)|smb_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(smb_args[x].flag))
+
+#ifndef __linux__
+#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n)
+#endif
+
+/* the generic mask */
+#define _ALL_ON_  (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/*
+ * These are the options supported by the smb password module, very
+ * similar to the pwdb options
+ */
+
+#define SMB__OLD_PASSWD                 0      /* internal */
+#define SMB__VERIFY_PASSWD      1      /* internal */
+
+#define SMB_AUDIT               2      /* print more things than debug..
+                                          some information may be sensitive */
+#define SMB_USE_FIRST_PASS      3
+#define SMB_TRY_FIRST_PASS      4
+#define SMB_NOT_SET_PASS        5      /* don't set the AUTHTOK items */
+
+#define SMB__NONULL             6      /* internal */
+#define SMB__QUIET              7      /* internal */
+#define SMB_USE_AUTHTOK                 8      /* insist on reading PAM_AUTHTOK */
+#define SMB__NULLOK             9      /* Null token ok */
+#define SMB_DEBUG              10      /* send more info to syslog(3) */
+#define SMB_NODELAY            11      /* admin does not want a fail-delay */
+#define SMB_MIGRATE            12      /* Does no authentication, just
+                                          updates the smb database. */
+#define SMB_CONF_FILE          13      /* Alternate location of smb.conf */
+
+#define SMB_CTRLS_             14      /* number of ctrl arguments defined */
+
+static const SMB_Ctrls smb_args[SMB_CTRLS_] = {
+/* symbol                 token name          ctrl mask      ctrl       *
+ * ------------------     ------------------  -------------- ---------- */
+
+/* SMB__OLD_PASSWD */   {  NULL,            _ALL_ON_,              01 },
+/* SMB__VERIFY_PASSWD */ {  NULL,            _ALL_ON_,              02 },
+/* SMB_AUDIT */                 { "audit",          _ALL_ON_,              04 },
+/* SMB_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(030),       010 },
+/* SMB_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(030),       020 },
+/* SMB_NOT_SET_PASS */  { "not_set_pass",   _ALL_ON_,             040 },
+/* SMB__NONULL */       {  "nonull",        _ALL_ON_,            0100 },
+/* SMB__QUIET */        {  NULL,            _ALL_ON_,            0200 },
+/* SMB_USE_AUTHTOK */   { "use_authtok",    _ALL_ON_,            0400 },
+/* SMB__NULLOK */       { "nullok",         _ALL_ON_^(0100),        0 },
+/* SMB_DEBUG */                 { "debug",          _ALL_ON_,           01000 },
+/* SMB_NODELAY */       { "nodelay",        _ALL_ON_,           02000 },
+/* SMB_MIGRATE */       { "migrate",        _ALL_ON_^(0100),    04000 },
+/* SMB_CONF_FILE */     { "smbconf=",       _ALL_ON_,               0 },
+};
+
+#define SMB_DEFAULTS  (smb_args[SMB__NONULL].flag)
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX                    "-SMB-FAIL-"
+#define SMB_MAX_RETRIES                        3
+
+struct _pam_failed_auth {
+    char *user;                 /* user that's failed to be authenticated */
+    int id;                     /* uid of requested user */
+    char *agent;                /* attempt from user with name */
+    int count;                  /* number of failures so far */
+};
+
+/*
+ * General use functions go here 
+ */
+
+/* from support.c */
+int make_remark(pam_handle_t *, unsigned int, int, const char *);
diff --git a/source4/pam_smbpass/pam_smb_acct.c b/source4/pam_smbpass/pam_smb_acct.c
new file mode 100644 (file)
index 0000000..0803ef8
--- /dev/null
@@ -0,0 +1,113 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_ACCT
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+
+/*
+ * pam_sm_acct_mgmt() verifies whether or not the account is disabled.
+ *
+ */
+
+int pam_sm_acct_mgmt( pam_handle_t *pamh, int flags,
+                      int argc, const char **argv )
+{
+    unsigned int ctrl;
+    int retval;
+
+    const char *name;
+    SAM_ACCOUNT *sampass = NULL;
+
+    extern BOOL in_client;
+
+    /* Samba initialization. */
+    setup_logging( "pam_smbpass", False );
+    in_client = True;
+
+    ctrl = set_ctrl( flags, argc, argv );
+
+    /* get the username */
+
+    retval = pam_get_user( pamh, &name, "Username: " );
+    if (retval != PAM_SUCCESS) {
+        if (on( SMB_DEBUG, ctrl )) {
+           _log_err( LOG_DEBUG, "acct: could not identify user" );
+        }
+        return retval;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "acct: username [%s] obtained", name );
+    }
+
+    if (!initialize_password_db(True)) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        return PAM_AUTHINFO_UNAVAIL;
+    }
+
+    /* Get the user's record. */
+    pdb_init_sam(&sampass);
+    pdb_getsampwnam(sampass, name );
+
+    if (!sampass)
+        return PAM_USER_UNKNOWN;
+
+    if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+        if (on( SMB_DEBUG, ctrl )) {
+            _log_err( LOG_DEBUG
+                      , "acct: account %s is administratively disabled", name );
+        }
+        make_remark( pamh, ctrl, PAM_ERROR_MSG
+                     , "Your account has been disabled; "
+                       "please see your system administrator." );
+
+        return PAM_ACCT_EXPIRED;
+    }
+
+    /* TODO: support for expired passwords. */
+
+    return PAM_SUCCESS;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_acct_modstruct = {
+     "pam_smbpass",
+     NULL,
+     NULL,
+     pam_sm_acct_mgmt,
+     NULL,
+     NULL,
+     NULL
+};
+#endif
+
diff --git a/source4/pam_smbpass/pam_smb_auth.c b/source4/pam_smbpass/pam_smb_auth.c
new file mode 100644 (file)
index 0000000..e5cc12e
--- /dev/null
@@ -0,0 +1,249 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_AUTH
+
+#include "includes.h"
+#include "debug.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+#define AUTH_RETURN                                            \
+do {                                                           \
+       if(ret_data) {                                          \
+               *ret_data = retval;                             \
+               pam_set_data( pamh, "smb_setcred_return"        \
+                             , (void *) ret_data, NULL );      \
+       }                                                       \
+       return retval;                                          \
+} while (0)
+
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+                         const char *name, SAM_ACCOUNT *sampass, BOOL exist);
+
+
+/*
+ * pam_sm_authenticate() authenticates users against the samba password file.
+ *
+ *     First, obtain the password from the user. Then use a
+ *      routine in 'support.c' to authenticate the user.
+ */
+
+#define _SMB_AUTHTOK  "-SMB-PASS"
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+                        int argc, const char **argv)
+{
+    unsigned int ctrl;
+    int retval, *ret_data = NULL;
+    SAM_ACCOUNT *sampass = NULL;
+    extern BOOL in_client;
+    const char *name;
+    BOOL found;
+
+    /* Points to memory managed by the PAM library. Do not free. */
+    char *p = NULL;
+
+
+    /* Samba initialization. */
+    setup_logging("pam_smbpass",False);
+    in_client = True;
+
+    ctrl = set_ctrl(flags, argc, argv);
+
+    /* Get a few bytes so we can pass our return value to
+       pam_sm_setcred(). */
+    ret_data = malloc(sizeof(int));
+
+    /* get the username */
+    retval = pam_get_user( pamh, &name, "Username: " );
+    if ( retval != PAM_SUCCESS ) {
+        if (on( SMB_DEBUG, ctrl )) {
+           _log_err(LOG_DEBUG, "auth: could not identify user");
+        }
+        AUTH_RETURN;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "username [%s] obtained", name );
+    }
+
+    if (!initialize_password_db(True)) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        retval = PAM_AUTHINFO_UNAVAIL;
+        AUTH_RETURN;
+    }
+
+    pdb_init_sam(&sampass);
+    
+    found = pdb_getsampwnam( sampass, name );
+
+    if (on( SMB_MIGRATE, ctrl )) {
+       retval = _smb_add_user(pamh, ctrl, name, sampass, found);
+       pdb_free_sam(&sampass);
+       AUTH_RETURN;
+    }
+
+    if (!found) {
+        _log_err(LOG_ALERT, "Failed to find entry for user %s.", name);
+        retval = PAM_USER_UNKNOWN;
+       pdb_free_sam(&sampass);
+       sampass = NULL;
+        AUTH_RETURN;
+    }
+   
+    /* if this user does not have a password... */
+
+    if (_smb_blankpasswd( ctrl, sampass )) {
+        pdb_free_sam(&sampass);
+        retval = PAM_SUCCESS;
+        AUTH_RETURN;
+    }
+
+    /* get this user's authentication token */
+
+    retval = _smb_read_password(pamh, ctrl, NULL, "Password: ", NULL, _SMB_AUTHTOK, &p);
+    if (retval != PAM_SUCCESS ) {
+       _log_err(LOG_CRIT, "auth: no password provided for [%s]"
+                , name);
+        pdb_free_sam(&sampass);
+        AUTH_RETURN;
+    }
+
+    /* verify the password of this user */
+
+    retval = _smb_verify_password( pamh, sampass, p, ctrl );
+    pdb_free_sam(&sampass);
+    p = NULL;
+    AUTH_RETURN;
+}
+
+/*
+ * This function is for setting samba credentials.  If anyone comes up
+ * with any credentials they think should be set, let me know.
+ */
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+                   int argc, const char **argv)
+{
+    int retval, *pretval = NULL;
+
+    retval = PAM_SUCCESS;
+
+    pam_get_data(pamh, "smb_setcred_return", (const void **) &pretval);
+    if(pretval) {
+       retval = *pretval;
+       SAFE_FREE(pretval);
+    }
+    pam_set_data(pamh, "smb_setcred_return", NULL, NULL);
+
+    return retval;
+}
+
+
+/* Helper function for adding a user to the db. */
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+                         const char *name, SAM_ACCOUNT *sampass, BOOL exist)
+{
+    pstring err_str;
+    pstring msg_str;
+    const char *pass = NULL;
+    int retval;
+
+    err_str[0] = '\0';
+    msg_str[0] = '\0';
+
+    /* Get the authtok; if we don't have one, silently fail. */
+    retval = pam_get_item( pamh, PAM_AUTHTOK, (const void **) &pass );
+
+    if (retval != PAM_SUCCESS) {
+       _log_err( LOG_ALERT
+                 , "pam_get_item returned error to pam_sm_authenticate" );
+       return PAM_AUTHTOK_RECOVER_ERR;
+    } else if (pass == NULL) {
+       return PAM_AUTHTOK_RECOVER_ERR;
+    }
+
+    /* Add the user to the db if they aren't already there. */
+   if (!exist) {
+       retval = local_password_change( name, LOCAL_ADD_USER,
+                                        pass, err_str,
+                                        sizeof(err_str),
+                                        msg_str, sizeof(msg_str) );
+       if (!retval && *err_str)
+       {
+           err_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+       }
+       else if (*msg_str)
+       {
+           msg_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+       }
+       pass = NULL;
+
+       return PAM_IGNORE;
+   }
+   else {
+    /* Change the user's password IFF it's null. */
+    if ((pdb_get_lanman_passwd(sampass) == NULL) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+    {
+       retval = local_password_change( name, 0, pass, err_str, sizeof(err_str),
+                                        msg_str, sizeof(msg_str) );
+       if (!retval && *err_str)
+       {
+           err_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+       }
+       else if (*msg_str)
+       {
+           msg_str[PSTRING_LEN-1] = '\0';
+           make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+       }
+    }
+   }
+    
+    pass = NULL;
+
+    return PAM_IGNORE;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_auth_modstruct = {
+     "pam_smbpass",
+     pam_sm_authenticate,
+     pam_sm_setcred,
+     NULL,
+     NULL,
+     NULL,
+     NULL
+};
+#endif
+
diff --git a/source4/pam_smbpass/pam_smb_passwd.c b/source4/pam_smbpass/pam_smb_passwd.c
new file mode 100644 (file)
index 0000000..91eae3c
--- /dev/null
@@ -0,0 +1,330 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_PASSWORD
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif  /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h" 
+
+#include "support.h"
+
+int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user,  const char *pass_new )
+{
+ int           retval;
+ pstring       err_str;
+ pstring       msg_str;
+
+    err_str[0] = '\0';
+    msg_str[0] = '\0';
+
+    retval = local_password_change( user, 0, pass_new, err_str, sizeof(err_str),
+                                   msg_str, sizeof(msg_str) );
+
+    if (!retval) {
+        if (*err_str) {
+            err_str[PSTRING_LEN-1] = '\0';
+            make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+        }
+
+        /* FIXME: what value is appropriate here? */
+        retval = PAM_AUTHTOK_ERR;
+    } else {
+        if (*msg_str) {
+            msg_str[PSTRING_LEN-1] = '\0';
+            make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+        }
+        retval = PAM_SUCCESS;
+    }
+
+    return retval;      
+
+}
+
+
+/* data tokens */
+
+#define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
+#define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
+
+/*
+ * FUNCTION: pam_sm_chauthtok()
+ *
+ * This function is called twice by the PAM library, once with
+ * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
+ * Linux-PAM, these two passes generally involve first checking the old
+ * token and then changing the token.  This is what we do here.
+ *
+ * Having obtained a new password. The function updates the
+ * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
+ */
+
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+                     int argc, const char **argv)
+{
+    unsigned int ctrl;
+    int retval;
+
+    extern BOOL in_client;
+
+    SAM_ACCOUNT *sampass = NULL;
+    const char *user;
+    char *pass_old;
+    char *pass_new;
+
+    NTSTATUS nt_status;
+
+    /* Samba initialization. */
+    setup_logging( "pam_smbpass", False );
+    in_client = True;
+
+    ctrl = set_ctrl(flags, argc, argv);
+
+    /*
+     * First get the name of a user.  No need to do anything if we can't
+     * determine this.
+     */
+
+    retval = pam_get_user( pamh, &user, "Username: " );
+    if (retval != PAM_SUCCESS) {
+        if (on( SMB_DEBUG, ctrl )) {
+            _log_err( LOG_DEBUG, "password: could not identify user" );
+        }
+        return retval;
+    }
+    if (on( SMB_DEBUG, ctrl )) {
+        _log_err( LOG_DEBUG, "username [%s] obtained", user );
+    }
+
+    if (!initialize_password_db(True)) {
+        _log_err( LOG_ALERT, "Cannot access samba password database" );
+        return PAM_AUTHINFO_UNAVAIL;
+    }
+
+    /* obtain user record */
+    if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+           return nt_status_to_pam(nt_status);
+    }
+
+    if (!pdb_getsampwnam(sampass,user)) {
+        _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
+        return PAM_USER_UNKNOWN;
+    }
+
+    if (flags & PAM_PRELIM_CHECK) {
+        /*
+         * obtain and verify the current password (OLDAUTHTOK) for
+         * the user.
+         */
+
+        char *Announce;
+
+        if (_smb_blankpasswd( ctrl, sampass )) {
+
+            pdb_free_sam(&sampass);
+            return PAM_SUCCESS;
+        }
+
+       /* Password change by root, or for an expired token, doesn't
+           require authentication.  Is this a good choice? */
+        if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+
+            /* tell user what is happening */
+#define greeting "Changing password for "
+            Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+            if (Announce == NULL) {
+                _log_err(LOG_CRIT, "password: out of memory");
+                pdb_free_sam(&sampass);
+                return PAM_BUF_ERR;
+            }
+            strncpy( Announce, greeting, sizeof(greeting) );
+            strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
+#undef greeting
+
+            set( SMB__OLD_PASSWD, ctrl );
+            retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
+                                         NULL, _SMB_OLD_AUTHTOK, &pass_old );
+            SAFE_FREE( Announce );
+
+            if (retval != PAM_SUCCESS) {
+                _log_err( LOG_NOTICE
+                          , "password - (old) token not obtained" );
+                pdb_free_sam(&sampass);
+                return retval;
+            }
+
+            /* verify that this is the password for this user */
+
+            retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
+
+        } else {
+           pass_old = NULL;
+            retval = PAM_SUCCESS;           /* root doesn't have to */
+        }
+
+        pass_old = NULL;
+        pdb_free_sam(&sampass);
+        return retval;
+
+    } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+#if 0
+        /* We used to return when this flag was set, but that breaks
+           password synchronization when /other/ tokens are expired.  For
+           now, we change the password whenever we're asked. SRL */
+        if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
+            pdb_free_sam(&sampass);
+            return PAM_SUCCESS;
+        }
+#endif
+        /*
+         * obtain the proposed password
+         */
+
+        /*
+         * get the old token back. NULL was ok only if root [at this
+         * point we assume that this has already been enforced on a
+         * previous call to this function].
+         */
+
+        if (off( SMB_NOT_SET_PASS, ctrl )) {
+            retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
+                                   (const void **)&pass_old );
+        } else {
+            retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
+                                   (const void **)&pass_old );
+            if (retval == PAM_NO_MODULE_DATA) {
+               pass_old = NULL;
+                retval = PAM_SUCCESS;
+            }
+        }
+
+        if (retval != PAM_SUCCESS) {
+            _log_err( LOG_NOTICE, "password: user not authenticated" );
+            pdb_free_sam(&sampass);
+            return retval;
+        }
+
+        /*
+         * use_authtok is to force the use of a previously entered
+         * password -- needed for pluggable password strength checking
+        * or other module stacking
+         */
+
+        if (on( SMB_USE_AUTHTOK, ctrl )) {
+            set( SMB_USE_FIRST_PASS, ctrl );
+        }
+
+        retval = _smb_read_password( pamh, ctrl
+                                      , NULL
+                                      , "Enter new SMB password: "
+                                      , "Retype new SMB password: "
+                                      , _SMB_NEW_AUTHTOK
+                                      , &pass_new );
+
+        if (retval != PAM_SUCCESS) {
+            if (on( SMB_DEBUG, ctrl )) {
+                _log_err( LOG_ALERT
+                          , "password: new password not obtained" );
+            }
+            pass_old = NULL;                               /* tidy up */
+            pdb_free_sam(&sampass);
+            return retval;
+        }
+
+        /*
+         * At this point we know who the user is and what they
+         * propose as their new password. Verify that the new
+         * password is acceptable.
+         */ 
+
+        if (pass_new[0] == '\0') {     /* "\0" password = NULL */
+            pass_new = NULL;
+        }
+
+        retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+        if (retval != PAM_SUCCESS) {
+            _log_err(LOG_NOTICE, "new password not acceptable");
+            pass_new = pass_old = NULL;               /* tidy up */
+            pdb_free_sam(&sampass);
+            return retval;
+        }
+
+        /*
+         * By reaching here we have approved the passwords and must now
+         * rebuild the smb password file.
+         */
+
+        /* update the password database */
+
+        retval = smb_update_db(pamh, ctrl, user, pass_new);
+        if (retval == PAM_SUCCESS) {
+            /* password updated */
+            _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)"
+                      , user, pdb_get_uid(sampass), uidtoname( getuid() )
+                      , getuid() );
+        } else {
+            _log_err( LOG_ERR, "password change failed for user %s"
+                      , user );
+        }
+
+        pass_old = pass_new = NULL;
+       if (sampass) {
+               pdb_free_sam(&sampass);
+               sampass = NULL;
+       }
+
+    } else {            /* something has broken with the library */
+
+        _log_err( LOG_ALERT, "password received unknown request" );
+        retval = PAM_ABORT;
+
+    }
+    
+    if (sampass) {
+       pdb_free_sam(&sampass);
+       sampass = NULL;
+    }
+
+    pdb_free_sam(&sampass);
+    return retval;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_passwd_modstruct = {
+     "pam_smbpass",
+     NULL,
+     NULL,
+     NULL,
+     NULL,
+     NULL,
+     pam_sm_chauthtok
+};
+#endif
+
diff --git a/source4/pam_smbpass/samples/README b/source4/pam_smbpass/samples/README
new file mode 100644 (file)
index 0000000..d776033
--- /dev/null
@@ -0,0 +1,3 @@
+This directory contains example configurations demonstrating various uses
+of pam_smbpass.  These examples use Linux-style /etc/pam.d syntax, and
+must be modified for use on Solaris systems.
diff --git a/source4/pam_smbpass/samples/kdc-pdc b/source4/pam_smbpass/samples/kdc-pdc
new file mode 100644 (file)
index 0000000..70f1998
--- /dev/null
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# kdc-pdc
+# 
+# A sample PAM configuration that shows pam_smbpass used together with
+# pam_krb5.  This could be useful on a Samba PDC that is also a member of
+# a Kerberos realm.
+
+auth       requisite        pam_nologin.so
+auth       requisite        pam_krb5.so
+auth       optional         pam_smbpass.so migrate
+account    required         pam_krb5.so
+password   requisite        pam_cracklib.so retry=3
+password   optional         pam_smbpass.so nullok use_authtok try_first_pass
+password   required         pam_krb5.so use_authtok try_first_pass
+session    required         pam_krb5.so
diff --git a/source4/pam_smbpass/samples/password-mature b/source4/pam_smbpass/samples/password-mature
new file mode 100644 (file)
index 0000000..6d73e09
--- /dev/null
@@ -0,0 +1,14 @@
+#%PAM-1.0
+# password-mature
+#
+# A sample PAM configuration for a 'mature' smbpasswd installation.
+# private/smbpasswd is fully populated, and we consider it an error if
+# the smbpasswd doesn't exist or doesn't match the Unix password.
+
+auth       requisite        pam_nologin.so
+auth       required         pam_unix.so
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   required         pam_smbpass.so use_authtok use_first_pass
+session    required         pam_unix.so
diff --git a/source4/pam_smbpass/samples/password-migration b/source4/pam_smbpass/samples/password-migration
new file mode 100644 (file)
index 0000000..305cb53
--- /dev/null
@@ -0,0 +1,18 @@
+#%PAM-1.0
+# password-migration
+#
+# A sample PAM configuration that shows the use of pam_smbpass to migrate
+# from plaintext to encrypted passwords for Samba.  Unlike other methods,
+# this can be used for users who have never connected to Samba shares:
+# password migration takes place when users ftp in, login using ssh, pop
+# their mail, etc.
+
+auth       requisite        pam_nologin.so
+# pam_smbpass is called IFF pam_unix succeeds.
+auth       requisite        pam_unix.so
+auth       optional         pam_smbpass.so migrate
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   optional         pam_smbpass.so nullok use_authtok try_first_pass
+session    required         pam_unix.so
diff --git a/source4/pam_smbpass/samples/password-sync b/source4/pam_smbpass/samples/password-sync
new file mode 100644 (file)
index 0000000..0a950dd
--- /dev/null
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# password-sync
+#
+# A sample PAM configuration that shows the use of pam_smbpass to make
+# sure private/smbpasswd is kept in sync when /etc/passwd (/etc/shadow)
+# is changed.  Useful when an expired password might be changed by an
+# application (such as ssh).
+
+auth       requisite        pam_nologin.so
+auth       required         pam_unix.so
+account    required         pam_unix.so
+password   requisite        pam_cracklib.so retry=3
+password   requisite        pam_unix.so shadow md5 use_authtok try_first_pass
+password   required         pam_smbpass.so nullok use_authtok try_first_pass
+session    required         pam_unix.so
diff --git a/source4/pam_smbpass/support.c b/source4/pam_smbpass/support.c
new file mode 100644 (file)
index 0000000..11de306
--- /dev/null
@@ -0,0 +1,637 @@
+       /* Unix NT password database implementation, version 0.6.
+        *
+        * 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 "general.h"
+
+       #include "support.h"
+
+
+       #define _pam_overwrite(x)        \
+       do {                             \
+            register char *__xx__;      \
+            if ((__xx__=(x)))           \
+                 while (*__xx__)        \
+                      *__xx__++ = '\0'; \
+       } while (0)
+
+       /*
+        * Don't just free it, forget it too.
+        */
+
+       #define _pam_drop(X) \
+       do {                 \
+           if (X) {         \
+               free(X);     \
+               X=NULL;      \
+           }                \
+       } while (0)
+
+       #define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+       do {                                              \
+           int reply_i;                                  \
+                                                         \
+           for (reply_i=0; reply_i<replies; ++reply_i) { \
+               if (reply[reply_i].resp) {                \
+                   _pam_overwrite(reply[reply_i].resp);  \
+                   free(reply[reply_i].resp);            \
+               }                                         \
+           }                                             \
+           if (reply)                                    \
+               free(reply);                              \
+       } while (0)
+
+
+       int converse(pam_handle_t *, int, int, struct pam_message **,
+                                struct pam_response **);
+       int make_remark(pam_handle_t *, unsigned int, int, const char *);
+       void _cleanup(pam_handle_t *, void *, int);
+       char *_pam_delete(register char *);
+
+       /* default configuration file location */
+
+       char *servicesf = dyn_CONFIGFILE;
+
+       /* syslogging function for errors and other information */
+
+       void _log_err( int err, const char *format, ... )
+       {
+           va_list args;
+
+           va_start( args, format );
+           openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
+           vsyslog( err, format, args );
+           va_end( args );
+           closelog();
+       }
+
+       /* this is a front-end for module-application conversations */
+
+       int converse( pam_handle_t * pamh, int ctrl, int nargs
+                     , struct pam_message **message
+                     , struct pam_response **response )
+       {
+               int retval;
+               struct pam_conv *conv;
+
+               retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+               if (retval == PAM_SUCCESS) {
+
+                       retval = conv->conv(nargs, (const struct pam_message **) message
+                                                               ,response, conv->appdata_ptr);
+
+                       if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
+                               _log_err(LOG_DEBUG, "conversation failure [%s]"
+                                                ,pam_strerror(pamh, retval));
+                       }
+               } else {
+                       _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+                                        ,pam_strerror(pamh, retval));
+               }
+
+               return retval;                          /* propagate error status */
+       }
+
+       int make_remark( pam_handle_t * pamh, unsigned int ctrl
+                        , int type, const char *text )
+       {
+               if (off(SMB__QUIET, ctrl)) {
+                       struct pam_message *pmsg[1], msg[1];
+                       struct pam_response *resp;
+
+                       pmsg[0] = &msg[0];
+                       msg[0].msg = text;
+                       msg[0].msg_style = type;
+                       resp = NULL;
+
+                       return converse(pamh, ctrl, 1, pmsg, &resp);
+               }
+               return PAM_SUCCESS;
+       }
+
+
+       /* set the control flags for the SMB module. */
+
+int set_ctrl( int flags, int argc, const char **argv )
+{
+    int i = 0;
+    const char *service_file = dyn_CONFIGFILE;
+    unsigned int ctrl;
+
+    ctrl = SMB_DEFAULTS;       /* the default selection of options */
+
+    /* set some flags manually */
+
+    /* A good, sane default (matches Samba's behavior). */
+    set( SMB__NONULL, ctrl );
+
+    /* initialize service file location */
+    service_file=servicesf;
+
+    if (flags & PAM_SILENT) {
+        set( SMB__QUIET, ctrl );
+    }
+
+    /* Run through the arguments once, looking for an alternate smb config
+       file location */
+    while (i < argc) {
+       int j;
+
+       for (j = 0; j < SMB_CTRLS_; ++j) {
+           if (smb_args[j].token
+               && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
+           {
+               break;
+           }
+       }
+
+       if (j == SMB_CONF_FILE) {
+           service_file = argv[i] + 8;
+       }
+       i++;
+    }
+
+    /* Read some options from the Samba config. Can be overridden by
+       the PAM config. */
+    if(lp_load(service_file,True,False,False) == False) {
+       _log_err( LOG_ERR, "Error loading service file %s", service_file );
+    }
+
+    secrets_init();
+
+    if (lp_null_passwords()) {
+        set( SMB__NULLOK, ctrl );
+    }
+
+    /* now parse the rest of the arguments to this module */
+
+    while (argc-- > 0) {
+        int j;
+
+        for (j = 0; j < SMB_CTRLS_; ++j) {
+            if (smb_args[j].token
+               && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
+            {
+                break;
+            }
+        }
+
+        if (j >= SMB_CTRLS_) {
+            _log_err( LOG_ERR, "unrecognized option [%s]", *argv );
+        } else {
+            ctrl &= smb_args[j].mask;  /* for turning things off */
+            ctrl |= smb_args[j].flag;  /* for turning things on  */
+        }
+
+        ++argv;                                /* step to next argument */
+    }
+
+    /* auditing is a more sensitive version of debug */
+
+    if (on( SMB_AUDIT, ctrl )) {
+        set( SMB_DEBUG, ctrl );
+    }
+    /* return the set of flags */
+
+    return ctrl;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+
+char * _pam_delete( register char *xx )
+{
+    _pam_overwrite( xx );
+    _pam_drop( xx );
+    return NULL;
+}
+
+void _cleanup( pam_handle_t * pamh, void *x, int error_status )
+{
+    x = _pam_delete( (char *) x );
+}
+
+/* JHT
+ *
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ *
+ */
+char * smbpXstrDup( const char *x )
+{
+    register char *new = NULL;
+
+    if (x != NULL) {
+        register int i;
+
+        for (i = 0; x[i]; ++i); /* length of string */
+        if ((new = malloc(++i)) == NULL) {
+            i = 0;
+            _log_err( LOG_CRIT, "out of memory in smbpXstrDup" );
+        } else {
+            while (i-- > 0) {
+                new[i] = x[i];
+            }
+        }
+        x = NULL;
+    }
+    return new;                        /* return the duplicate or NULL on error */
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions                                   *
+ * ************************************************************** */
+
+void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
+{
+    int quiet;
+    const char *service = NULL;
+    struct _pam_failed_auth *failure;
+
+#ifdef PAM_DATA_SILENT
+    quiet = err & PAM_DATA_SILENT;     /* should we log something? */
+#else
+    quiet = 0;
+#endif
+#ifdef PAM_DATA_REPLACE
+    err &= PAM_DATA_REPLACE;   /* are we just replacing data? */
+#endif
+    failure = (struct _pam_failed_auth *) fl;
+
+    if (failure != NULL) {
+
+#ifdef PAM_DATA_SILENT
+        if (!quiet && !err) {  /* under advisement from Sun,may go away */
+#else
+        if (!quiet) {  /* under advisement from Sun,may go away */
+#endif
+
+            /* log the number of authentication failures */
+            if (failure->count != 0) {
+                pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
+                _log_err( LOG_NOTICE
+                          , "%d authentication %s "
+                            "from %s for service %s as %s(%d)"
+                          , failure->count
+                          , failure->count == 1 ? "failure" : "failures"
+                          , failure->agent
+                          , service == NULL ? "**unknown**" : service 
+                          , failure->user, failure->id );
+                if (failure->count > SMB_MAX_RETRIES) {
+                    _log_err( LOG_ALERT
+                              , "service(%s) ignoring max retries; %d > %d"
+                              , service == NULL ? "**unknown**" : service
+                              , failure->count
+                              , SMB_MAX_RETRIES );
+                }
+            }
+        }
+        _pam_delete( failure->agent ); /* tidy up */
+        _pam_delete( failure->user );  /* tidy up */
+       SAFE_FREE( failure );
+    }
+}
+
+int _smb_verify_password( pam_handle_t * pamh, SAM_ACCOUNT *sampass,
+                         const char *p, unsigned int ctrl )
+{
+    uchar hash_pass[16];
+    uchar lm_pw[16];
+    uchar nt_pw[16];
+    int retval = PAM_AUTH_ERR;
+    char *data_name;
+    const char *name;
+
+    if (!sampass)
+        return PAM_ABORT;
+
+    name = pdb_get_username(sampass);
+
+#ifdef HAVE_PAM_FAIL_DELAY
+    if (off( SMB_NODELAY, ctrl )) {
+        (void) pam_fail_delay( pamh, 1000000 );        /* 1 sec delay for on failure */
+    }
+#endif
+
+    if (!pdb_get_lanman_passwd(sampass))
+    {
+        _log_err( LOG_DEBUG, "user %s has null SMB password"
+                  , name );
+
+        if (off( SMB__NONULL, ctrl )
+            && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+        { /* this means we've succeeded */
+            return PAM_SUCCESS;
+        } else {
+            const char *service;
+
+            pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+            _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , pdb_get_uid(sampass) );
+            return PAM_AUTH_ERR;
+        }
+    }
+
+    data_name = (char *) malloc( sizeof(FAIL_PREFIX) + strlen( name ));
+    if (data_name == NULL) {
+        _log_err( LOG_CRIT, "no memory for data-name" );
+    }
+    strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
+    strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
+
+    /*
+     * The password we were given wasn't an encrypted password, or it
+     * didn't match the one we have.  We encrypt the password now and try
+     * again.
+     */
+
+    nt_lm_owf_gen(p, nt_pw, lm_pw);
+
+    /* the moment of truth -- do we agree with the password? */
+
+    if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
+
+        retval = PAM_SUCCESS;
+        if (data_name) {               /* reset failures */
+            pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+        }
+    } else {
+
+        const char *service;
+
+        pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+
+        if (data_name != NULL) {
+            struct _pam_failed_auth *new = NULL;
+            const struct _pam_failed_auth *old = NULL;
+
+            /* get a failure recorder */
+
+            new = (struct _pam_failed_auth *)
+                      malloc( sizeof(struct _pam_failed_auth) );
+
+            if (new != NULL) {
+
+                /* any previous failures for this user ? */
+                pam_get_data(pamh, data_name, (const void **) &old);
+
+                if (old != NULL) {
+                    new->count = old->count + 1;
+                    if (new->count >= SMB_MAX_RETRIES) {
+                        retval = PAM_MAXTRIES;
+                    }
+                } else {
+                    _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , pdb_get_uid(sampass) );
+                    new->count = 1;
+                }
+                new->user = smbpXstrDup( name );
+                new->id = pdb_get_uid(sampass);
+                new->agent = smbpXstrDup( uidtoname( getuid() ) );
+                pam_set_data( pamh, data_name, new, _cleanup_failures );
+
+            } else {
+                _log_err( LOG_CRIT, "no memory for failure recorder" );
+                _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , pdb_get_uid(sampass) );
+            }
+        } else {
+            _log_err( LOG_NOTICE
+                      , "failed auth request by %s for service %s as %s(%d)"
+                      , uidtoname( getuid() )
+                      , service ? service : "**unknown**", name
+                      , pdb_get_uid(sampass) );
+            retval = PAM_AUTH_ERR;
+        }
+    }
+
+    _pam_delete( data_name );
+    
+    return retval;
+}
+
+
+/*
+ * _smb_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int _smb_blankpasswd( unsigned int ctrl, SAM_ACCOUNT *sampass )
+{
+       int retval;
+
+       /*
+        * This function does not have to be too smart if something goes
+        * wrong, return FALSE and let this case to be treated somewhere
+        * else (CG)
+        */
+
+       if (on( SMB__NONULL, ctrl ))
+               return 0;               /* will fail but don't let on yet */
+
+       if (pdb_get_lanman_passwd(sampass) == NULL)
+               retval = 1;
+       else
+               retval = 0;
+
+       return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
+                        const char *comment, const char *prompt1,
+                        const char *prompt2, const char *data_name, char **pass )
+{
+    int authtok_flag;
+    int retval;
+    char *item = NULL;
+    char *token;
+
+    struct pam_message msg[3], *pmsg[3];
+    struct pam_response *resp;
+    int i, expect;
+
+
+    /* make sure nothing inappropriate gets returned */
+
+    *pass = token = NULL;
+
+    /* which authentication token are we getting? */
+
+    authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+    /* should we obtain the password from a PAM item ? */
+
+    if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
+        retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
+        if (retval != PAM_SUCCESS) {
+            /* very strange. */
+            _log_err( LOG_ALERT
+                      , "pam_get_item returned error to smb_read_password" );
+            return retval;
+        } else if (item != NULL) {     /* we have a password! */
+            *pass = item;
+            item = NULL;
+            return PAM_SUCCESS;
+        } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
+            return PAM_AUTHTOK_RECOVER_ERR;            /* didn't work */
+        } else if (on( SMB_USE_AUTHTOK, ctrl )
+                   && off( SMB__OLD_PASSWD, ctrl ))
+        {
+            return PAM_AUTHTOK_RECOVER_ERR;
+        }
+    }
+
+    /*
+     * getting here implies we will have to get the password from the
+     * user directly.
+     */
+
+    /* prepare to converse */
+    if (comment != NULL && off(SMB__QUIET, ctrl)) {
+        pmsg[0] = &msg[0];
+        msg[0].msg_style = PAM_TEXT_INFO;
+        msg[0].msg = comment;
+        i = 1;
+    } else {
+        i = 0;
+    }
+
+    pmsg[i] = &msg[i];
+    msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+    msg[i++].msg = prompt1;
+
+    if (prompt2 != NULL) {
+        pmsg[i] = &msg[i];
+        msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+        msg[i++].msg = prompt2;
+        expect = 2;
+    } else
+        expect = 1;
+
+    resp = NULL;
+
+    retval = converse( pamh, ctrl, i, pmsg, &resp );
+
+    if (resp != NULL) {
+        int j = comment ? 1 : 0;
+        /* interpret the response */
+
+        if (retval == PAM_SUCCESS) {   /* a good conversation */
+
+            token = smbpXstrDup(resp[j++].resp);
+            if (token != NULL) {
+                if (expect == 2) {
+                    /* verify that password entered correctly */
+                    if (!resp[j].resp || strcmp( token, resp[j].resp )) {
+                        _pam_delete( token );
+                        retval = PAM_AUTHTOK_RECOVER_ERR;
+                        make_remark( pamh, ctrl, PAM_ERROR_MSG
+                                     , MISTYPED_PASS );
+                    }
+                }
+            } else {
+                _log_err(LOG_NOTICE, "could not recover authentication token");
+            }
+        }
+
+        /* tidy up */
+        _pam_drop_reply( resp, expect );
+
+    } else {
+        retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
+    }
+
+    if (retval != PAM_SUCCESS) {
+        if (on( SMB_DEBUG, ctrl ))
+            _log_err( LOG_DEBUG, "unable to obtain a password" );
+        return retval;
+    }
+    /* 'token' is the entered password */
+
+    if (off( SMB_NOT_SET_PASS, ctrl )) {
+
+        /* we store this password as an item */
+
+        retval = pam_set_item( pamh, authtok_flag, (const void *)token );
+        _pam_delete( token );          /* clean it up */
+        if (retval != PAM_SUCCESS
+            || (retval = pam_get_item( pamh, authtok_flag
+                            ,(const void **)&item )) != PAM_SUCCESS)
+        {
+            _log_err( LOG_CRIT, "error manipulating password" );
+            return retval;
+        }
+    } else {
+        /*
+         * then store it as data specific to this module. pam_end()
+         * will arrange to clean it up.
+         */
+
+        retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
+        if (retval != PAM_SUCCESS
+            || (retval = pam_get_data( pamh, data_name, (const void **)&item ))
+                             != PAM_SUCCESS)
+        {
+            _log_err( LOG_CRIT, "error manipulating password data [%s]"
+                      , pam_strerror( pamh, retval ));
+            _pam_delete( token );
+            item = NULL;
+            return retval;
+        }
+        token = NULL;                  /* break link to password */
+    }
+
+    *pass = item;
+    item = NULL;                       /* break link to password */
+
+    return PAM_SUCCESS;
+}
+
+int _pam_smb_approve_pass(pam_handle_t * pamh,
+               unsigned int ctrl,
+               const char *pass_old,
+               const char *pass_new )
+{
+
+    /* Further checks should be handled through module stacking. -SRL */
+    if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
+    {
+       if (on(SMB_DEBUG, ctrl)) {
+           _log_err( LOG_DEBUG,
+                     "passwd: bad authentication token (null or unchanged)" );
+       }
+       make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+                               "No password supplied" : "Password unchanged" );
+       return PAM_AUTHTOK_ERR;
+    }
+
+    return PAM_SUCCESS;
+}
diff --git a/source4/pam_smbpass/support.h b/source4/pam_smbpass/support.h
new file mode 100644 (file)
index 0000000..a13a2d0
--- /dev/null
@@ -0,0 +1,50 @@
+/* syslogging function for errors and other information */
+extern void _log_err(int, const char *, ...);
+
+/* set the control flags for the UNIX module. */
+extern int set_ctrl(int, int, const char **);
+
+/* generic function for freeing pam data segments */
+extern void _cleanup(pam_handle_t *, void *, int);
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+extern char *smbpXstrDup(const char *);
+
+/* ************************************************************** *
+ * Useful non-trivial functions                                   *
+ * ************************************************************** */
+
+extern void _cleanup_failures(pam_handle_t *, void *, int);
+
+/* compare 2 strings */
+extern BOOL strequal(const char *, const char *);
+
+extern struct smb_passwd *
+_my_get_smbpwnam(FILE *, const char *, BOOL *, BOOL *, long *);
+
+extern int _smb_verify_password( pam_handle_t *pamh , SAM_ACCOUNT *sampass, 
+       const char *p, unsigned int ctrl );
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+extern int _smb_get_user(pam_handle_t *, unsigned int,
+                        const char *, const char **);
+
+/* _smb_blankpasswd() is a quick check for a blank password */
+
+extern int _smb_blankpasswd(unsigned int, SAM_ACCOUNT *);
+
+
+/* obtain a password from the user */
+extern int _smb_read_password( pam_handle_t *, unsigned int, const char*,
+                               const char *, const char *, const char *, char **);
+
+extern int _pam_smb_approve_pass(pam_handle_t *, unsigned int, const char *,
+                                const char *);
diff --git a/source4/param/.cvsignore b/source4/param/.cvsignore
new file mode 100644 (file)
index 0000000..76e2d7f
--- /dev/null
@@ -0,0 +1,3 @@
+*.po32
+*.po
+
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
new file mode 100644 (file)
index 0000000..89ba792
--- /dev/null
@@ -0,0 +1,4136 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Parameter loading functions
+   Copyright (C) Karl Auer 1993-1998
+
+   Largely re-written by Andrew Tridgell, September 1994
+
+   Copyright (C) Simo Sorce 2001
+   Copyright (C) Alexander Bokovoy 2002
+   Copyright (C) Stefan (metze) Metzmacher 2002
+   Copyright (C) Anthony Liguori 2003
+   Copyright (C) James Myers 2003 <myersjj@samba.org>
+   
+   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.
+*/
+
+/*
+ *  Load parameters.
+ *
+ *  This module provides suitable callback functions for the params
+ *  module. It builds the internal table of service details which is
+ *  then used by the rest of the server.
+ *
+ * To add a parameter:
+ *
+ * 1) add it to the global or service structure definition
+ * 2) add it to the parm_table
+ * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
+ * 4) If it's a global then initialise it in init_globals. If a local
+ *    (ie. service) parameter then initialise it in the sDefault structure
+ *  
+ *
+ * Notes:
+ *   The configuration file is processed sequentially for speed. It is NOT
+ *   accessed randomly as happens in 'real' Windows. For this reason, there
+ *   is a fair bit of sequence-dependent code here - ie., code which assumes
+ *   that certain things happen before others. In particular, the code which
+ *   happens at the boundary between sections is delicately poised, so be
+ *   careful!
+ *
+ */
+
+#include "includes.h"
+
+BOOL in_client = False;                /* Not in the client by default */
+static BOOL bLoaded = False;
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+#ifndef PRINTERS_NAME
+#define PRINTERS_NAME "printers"
+#endif
+
+#ifndef HOMES_NAME
+#define HOMES_NAME "homes"
+#endif
+
+/* some helpful bits */
+#define LP_SNUM_OK(i) (((i) >= 0) && ((i) < iNumServices) && ServicePtrs[(i)]->valid)
+#define VALID(i) ServicePtrs[i]->valid
+
+static BOOL do_parameter(const char *, const char *);
+
+static BOOL defaults_saved = False;
+
+struct param_opt {
+       struct param_opt *prev, *next;
+       char *key;
+       char *value;
+       int flags;
+};
+
+/* 
+ * This structure describes global (ie., server-wide) parameters.
+ */
+typedef struct
+{
+       char *smb_ports;
+       char *dos_charset;
+       char *unix_charset;
+       char *display_charset;
+       char *szPrintcapname;
+       char *szEnumPortsCommand;
+       char *szAddPrinterCommand;
+       char *szDeletePrinterCommand;
+       char *szOs2DriverMap;
+       char *szLockDir;
+       char *szPidDir;
+       char *szRootdir;
+       char *szDefaultService;
+       char *szDfree;
+       char *szMsgCommand;
+       char *szHostsEquiv;
+       char *szServerString;
+       char *szAutoServices;
+       char *szPasswdProgram;
+       char *szPasswdChat;
+       char *szLogFile;
+       char *szConfigFile;
+       char *szSMBPasswdFile;
+       char *szPrivateDir;
+       char **szPassdbBackend;
+       char **szSamBackend;
+       char **szPreloadModules;
+       char *szPasswordServer;
+       char *szSocketOptions;
+       char *szRealm;
+       char *szADSserver;
+       char *szUsernameMap;
+       char *szLogonScript;
+       char *szLogonPath;
+       char *szLogonDrive;
+       char *szLogonHome;
+       char **szWINSservers;
+       char **szInterfaces;
+       char *szRemoteAnnounce;
+       char *szRemoteBrowseSync;
+       char *szSocketAddress;
+       char *szNISHomeMapName;
+       char *szAnnounceVersion;        /* This is initialised in init_globals */
+       char *szWorkgroup;
+       char *szNetbiosName;
+       char **szNetbiosAliases;
+       char *szNetbiosScope;
+       char *szDomainOtherSIDs;
+       char *szNameResolveOrder;
+       char *szPanicAction;
+       char *szAddUserScript;
+       char *szDelUserScript;
+       char *szAddGroupScript;
+       char *szDelGroupScript;
+       char *szAddUserToGroupScript;
+       char *szDelUserFromGroupScript;
+       char *szSetPrimaryGroupScript;
+       char *szAddMachineScript;
+       char *szShutdownScript;
+       char *szAbortShutdownScript;
+       char *szWINSHook;
+       char *szWINSPartners;
+#ifdef WITH_UTMP
+       char *szUtmpDir;
+       char *szWtmpDir;
+       BOOL bUtmp;
+#endif
+       char *szSourceEnv;
+       char *szWinbindUID;
+       char *szWinbindGID;
+       char *szNonUnixAccountRange;
+       int AlgorithmicRidBase;
+       char *szTemplateHomedir;
+       char *szTemplateShell;
+       char *szWinbindSeparator;
+       BOOL bWinbindEnumUsers;
+       BOOL bWinbindEnumGroups;
+       BOOL bWinbindUseDefaultDomain;
+       char *szIDMapBackend;
+       char *szAddShareCommand;
+       char *szChangeShareCommand;
+       char *szDeleteShareCommand;
+       char *szGuestaccount;
+       char *szManglingMethod;
+       int mangle_prefix;
+       int max_log_size;
+       int keepalive;
+       int mangled_stack;
+       int max_xmit;
+       int max_mux;
+       int max_open_files;
+       int pwordlevel;
+       int unamelevel;
+       int deadtime;
+       int maxprotocol;
+       int minprotocol;
+       int security;
+       char **AuthMethods;
+       BOOL paranoid_server_security;
+       int maxdisksize;
+       int lpqcachetime;
+       int iMaxSmbdProcesses;
+       BOOL bDisableSpoolss;
+       int iTotalPrintJobs;
+       int syslog;
+       int os_level;
+       int enhanced_browsing;
+       int time_offset;
+       int max_ttl;
+       int max_wins_ttl;
+       int min_wins_ttl;
+       int ReadSize;
+       int lm_announce;
+       int lm_interval;
+       int announce_as;        /* This is initialised in init_globals */
+       int machine_password_timeout;
+       int change_notify_timeout;
+       int stat_cache_size;
+       int map_to_guest;
+       int min_passwd_length;
+       int oplock_break_wait_time;
+       int winbind_cache_time;
+       int iLockSpinCount;
+       int iLockSpinTime;
+       char *szLdapMachineSuffix;
+       char *szLdapUserSuffix;
+#ifdef WITH_LDAP_SAMCONFIG
+       int ldap_port;
+       char *szLdapServer;
+#endif
+       char *socket_options;
+       int ldap_ssl;
+       char *szLdapSuffix;
+       char *szLdapFilter;
+       char *szLdapAdminDn;
+       BOOL ldap_trust_ids;
+       char *szAclCompat;
+       int ldap_passwd_sync; 
+       BOOL bMsAddPrinterWizard;
+       BOOL bDNSproxy;
+       BOOL bWINSsupport;
+       BOOL bWINSproxy;
+       BOOL bLocalMaster;
+       BOOL bPreferredMaster;
+       BOOL bDomainMaster;
+       BOOL bDomainLogons;
+       BOOL bEncryptPasswords;
+       BOOL bUpdateEncrypt;
+       BOOL bStripDot;
+       BOOL bNullPasswords;
+       BOOL bObeyPamRestrictions;
+       BOOL bLoadPrinters;
+       BOOL bLargeReadwrite;
+       BOOL bReadRaw;
+       BOOL bWriteRaw;
+       BOOL bReadPrediction;
+       BOOL bReadbmpx;
+       BOOL bSyslogOnly;
+       BOOL bBrowseList;
+       BOOL bNISHomeMap;
+       BOOL bTimeServer;
+       BOOL bBindInterfacesOnly;
+       BOOL bPamPasswordChange;
+       BOOL bUnixPasswdSync;
+       BOOL bPasswdChatDebug;
+       BOOL bTimestampLogs;
+       BOOL bNTSmbSupport;
+       BOOL bNTPipeSupport;
+       BOOL bNTStatusSupport;
+       BOOL bStatCache;
+       BOOL bKernelOplocks;
+       BOOL bAllowTrustedDomains;
+       BOOL bLanmanAuth;
+       BOOL bNTLMAuth;
+       BOOL bUseSpnego;
+       BOOL bClientLanManAuth;
+       BOOL bClientNTLMv2Auth;
+       BOOL bClientUseSpnego;
+       BOOL bDebugHiresTimestamp;
+       BOOL bDebugPid;
+       BOOL bDebugUid;
+       BOOL bHostMSDfs;
+       BOOL bHideLocalUsers;
+       BOOL bUnicode;
+       BOOL bUseMmap;
+       BOOL bHostnameLookups;
+       BOOL bUnixExtensions;
+       BOOL bDisableNetbios;
+       BOOL bKernelChangeNotify;
+       int restrict_anonymous;
+       int name_cache_timeout;
+       BOOL client_signing;
+       struct param_opt *param_opt;
+}
+global;
+
+static global Globals;
+
+/* 
+ * This structure describes a single service. 
+ */
+typedef struct
+{
+       BOOL valid;
+       BOOL autoloaded;
+       char *szService;
+       char *szPath;
+       char *szUsername;
+       char **szInvalidUsers;
+       char **szValidUsers;
+       char **szAdminUsers;
+       char *szCopy;
+       char *szInclude;
+       char *szPreExec;
+       char *szPostExec;
+       char *szRootPreExec;
+       char *szRootPostExec;
+       char *szPrintcommand;
+       char *szLpqcommand;
+       char *szLprmcommand;
+       char *szLppausecommand;
+       char *szLpresumecommand;
+       char *szQueuepausecommand;
+       char *szQueueresumecommand;
+       char *szPrintername;
+       char *szDontdescend;
+       char **szHostsallow;
+       char **szHostsdeny;
+       char *szMagicScript;
+       char *szMagicOutput;
+       char *szMangledMap;
+       char *szVetoFiles;
+       char *szHideFiles;
+       char *szVetoOplockFiles;
+       char *comment;
+       char *force_user;
+       char *force_group;
+       char **readlist;
+       char **writelist;
+       char **printer_admin;
+       char *volume;
+       char *fstype;
+       char *szVfsObjectFile;
+       char *szVfsOptions;
+       char *szVfsPath;
+       char *szMSDfsProxy;
+       char *ntvfs_handler;
+       int iMinPrintSpace;
+       int iMaxPrintJobs;
+       int iMaxReportedPrintJobs;
+       int iWriteCacheSize;
+       int iCreate_mask;
+       int iCreate_force_mode;
+       int iSecurity_mask;
+       int iSecurity_force_mode;
+       int iDir_mask;
+       int iDir_force_mode;
+       int iDir_Security_mask;
+       int iDir_Security_force_mode;
+       int iMaxConnections;
+       int iDefaultCase;
+       int iPrinting;
+       int iOplockContentionLimit;
+       int iCSCPolicy;
+       int iBlock_size;
+       BOOL bPreexecClose;
+       BOOL bRootpreexecClose;
+       BOOL bCaseSensitive;
+       BOOL bCasePreserve;
+       BOOL bShortCasePreserve;
+       BOOL bCaseMangle;
+       BOOL bHideDotFiles;
+       BOOL bHideSpecialFiles;
+       BOOL bHideUnReadable;
+       BOOL bHideUnWriteableFiles;
+       BOOL bBrowseable;
+       BOOL bAvailable;
+       BOOL bRead_only;
+       BOOL bNo_set_dir;
+       BOOL bGuest_only;
+       BOOL bGuest_ok;
+       BOOL bPrint_ok;
+       BOOL bMap_system;
+       BOOL bMap_hidden;
+       BOOL bMap_archive;
+       BOOL bLocking;
+       BOOL bStrictLocking;
+       BOOL bPosixLocking;
+       BOOL bShareModes;
+       BOOL bOpLocks;
+       BOOL bLevel2OpLocks;
+       BOOL bOnlyUser;
+       BOOL bMangledNames;
+       BOOL bWidelinks;
+       BOOL bSymlinks;
+       BOOL bSyncAlways;
+       BOOL bStrictAllocate;
+       BOOL bStrictSync;
+       char magic_char;
+       BOOL *copymap;
+       BOOL bDeleteReadonly;
+       BOOL bFakeOplocks;
+       BOOL bDeleteVetoFiles;
+       BOOL bDosFilemode;
+       BOOL bDosFiletimes;
+       BOOL bDosFiletimeResolution;
+       BOOL bFakeDirCreateTimes;
+       BOOL bBlockingLocks;
+       BOOL bInheritPerms;
+       BOOL bInheritACLS;
+       BOOL bMSDfsRoot;
+       BOOL bUseClientDriver;
+       BOOL bDefaultDevmode;
+       BOOL bNTAclSupport;
+       BOOL bUseSendfile;
+       BOOL bProfileAcls;
+       struct param_opt *param_opt;
+
+       char dummy[3];          /* for alignment */
+}
+service;
+
+
+/* This is a default service used to prime a services structure */
+static service sDefault = {
+       True,                   /* valid */
+       False,                  /* not autoloaded */
+       NULL,                   /* szService */
+       NULL,                   /* szPath */
+       NULL,                   /* szUsername */
+       NULL,                   /* szInvalidUsers */
+       NULL,                   /* szValidUsers */
+       NULL,                   /* szAdminUsers */
+       NULL,                   /* szCopy */
+       NULL,                   /* szInclude */
+       NULL,                   /* szPreExec */
+       NULL,                   /* szPostExec */
+       NULL,                   /* szRootPreExec */
+       NULL,                   /* szRootPostExec */
+       NULL,                   /* szPrintcommand */
+       NULL,                   /* szLpqcommand */
+       NULL,                   /* szLprmcommand */
+       NULL,                   /* szLppausecommand */
+       NULL,                   /* szLpresumecommand */
+       NULL,                   /* szQueuepausecommand */
+       NULL,                   /* szQueueresumecommand */
+       NULL,                   /* szPrintername */
+       NULL,                   /* szDontdescend */
+       NULL,                   /* szHostsallow */
+       NULL,                   /* szHostsdeny */
+       NULL,                   /* szMagicScript */
+       NULL,                   /* szMagicOutput */
+       NULL,                   /* szMangledMap */
+       NULL,                   /* szVetoFiles */
+       NULL,                   /* szHideFiles */
+       NULL,                   /* szVetoOplockFiles */
+       NULL,                   /* comment */
+       NULL,                   /* force user */
+       NULL,                   /* force group */
+       NULL,                   /* readlist */
+       NULL,                   /* writelist */
+       NULL,                   /* printer admin */
+       NULL,                   /* volume */
+       NULL,                   /* fstype */
+       NULL,                   /* vfs object */
+       NULL,                   /* vfs options */
+       NULL,                   /* vfs path */
+       NULL,                   /* szMSDfsProxy */
+       NULL,                   /* ntvfs_handler */
+       0,                      /* iMinPrintSpace */
+       1000,                   /* iMaxPrintJobs */
+       0,                      /* iMaxReportedPrintJobs */
+       0,                      /* iWriteCacheSize */
+       0744,                   /* iCreate_mask */
+       0000,                   /* iCreate_force_mode */
+       0777,                   /* iSecurity_mask */
+       0,                      /* iSecurity_force_mode */
+       0755,                   /* iDir_mask */
+       0000,                   /* iDir_force_mode */
+       0777,                   /* iDir_Security_mask */
+       0,                      /* iDir_Security_force_mode */
+       0,                      /* iMaxConnections */
+       CASE_LOWER,             /* iDefaultCase */
+       DEFAULT_PRINTING,       /* iPrinting */
+       2,                      /* iOplockContentionLimit */
+       0,                      /* iCSCPolicy */
+       1024,           /* iBlock_size */
+       False,                  /* bPreexecClose */
+       False,                  /* bRootpreexecClose */
+       False,                  /* case sensitive */
+       True,                   /* case preserve */
+       True,                   /* short case preserve */
+       False,                  /* case mangle */
+       True,                   /* bHideDotFiles */
+       False,                  /* bHideSpecialFiles */
+       False,                  /* bHideUnReadable */
+       False,                  /* bHideUnWriteableFiles */
+       True,                   /* bBrowseable */
+       True,                   /* bAvailable */
+       True,                   /* bRead_only */
+       True,                   /* bNo_set_dir */
+       False,                  /* bGuest_only */
+       False,                  /* bGuest_ok */
+       False,                  /* bPrint_ok */
+       False,                  /* bMap_system */
+       False,                  /* bMap_hidden */
+       True,                   /* bMap_archive */
+       True,                   /* bLocking */
+       True,                   /* bStrictLocking */
+       True,                   /* bPosixLocking */
+       True,                   /* bShareModes */
+       True,                   /* bOpLocks */
+       True,                   /* bLevel2OpLocks */
+       False,                  /* bOnlyUser */
+       True,                   /* bMangledNames */
+       True,                   /* bWidelinks */
+       True,                   /* bSymlinks */
+       False,                  /* bSyncAlways */
+       False,                  /* bStrictAllocate */
+       False,                  /* bStrictSync */
+       '~',                    /* magic char */
+       NULL,                   /* copymap */
+       False,                  /* bDeleteReadonly */
+       False,                  /* bFakeOplocks */
+       False,                  /* bDeleteVetoFiles */
+       False,                  /* bDosFilemode */
+       False,                  /* bDosFiletimes */
+       False,                  /* bDosFiletimeResolution */
+       False,                  /* bFakeDirCreateTimes */
+       True,                   /* bBlockingLocks */
+       False,                  /* bInheritPerms */
+       False,                  /* bInheritACLS */
+       False,                  /* bMSDfsRoot */
+       False,                  /* bUseClientDriver */
+       False,                  /* bDefaultDevmode */
+       True,                   /* bNTAclSupport */
+       False,                  /* bUseSendfile */
+       False,                  /* bProfileAcls */
+       
+       NULL,                   /* Parametric options */
+
+       ""                      /* dummy */
+};
+
+/* local variables */
+static service **ServicePtrs = NULL;
+static int iNumServices = 0;
+static int iServiceIndex = 0;
+static BOOL bInGlobalSection = True;
+static BOOL bGlobalOnly = False;
+static int server_role;
+static int default_server_announce;
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+/* prototypes for the special type handlers */
+static BOOL handle_include(const char *pszParmValue, char **ptr);
+static BOOL handle_copy(const char *pszParmValue, char **ptr);
+static BOOL handle_vfs_object(const char *pszParmValue, char **ptr);
+static BOOL handle_source_env(const char *pszParmValue, char **ptr);
+static BOOL handle_winbind_uid(const char *pszParmValue, char **ptr);
+static BOOL handle_winbind_gid(const char *pszParmValue, char **ptr);
+static BOOL handle_non_unix_account_range(const char *pszParmValue, char **ptr);
+
+static BOOL handle_ldap_machine_suffix ( const char *pszParmValue, char **ptr );
+static BOOL handle_ldap_user_suffix ( const char *pszParmValue, char **ptr );
+static BOOL handle_ldap_suffix ( const char *pszParmValue, char **ptr );
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr);
+
+static void set_server_role(void);
+static void set_default_server_announce_type(void);
+
+static const struct enum_list enum_protocol[] = {
+       {PROTOCOL_NT1, "NT1"},
+       {PROTOCOL_LANMAN2, "LANMAN2"},
+       {PROTOCOL_LANMAN1, "LANMAN1"},
+       {PROTOCOL_CORE, "CORE"},
+       {PROTOCOL_COREPLUS, "COREPLUS"},
+       {PROTOCOL_COREPLUS, "CORE+"},
+       {-1, NULL}
+};
+
+static const struct enum_list enum_security[] = {
+       {SEC_SHARE, "SHARE"},
+       {SEC_USER, "USER"},
+       {SEC_SERVER, "SERVER"},
+       {SEC_DOMAIN, "DOMAIN"},
+#ifdef HAVE_ADS
+       {SEC_ADS, "ADS"},
+#endif
+       {-1, NULL}
+};
+
+static const struct enum_list enum_printing[] = {
+       {PRINT_SYSV, "sysv"},
+       {PRINT_AIX, "aix"},
+       {PRINT_HPUX, "hpux"},
+       {PRINT_BSD, "bsd"},
+       {PRINT_QNX, "qnx"},
+       {PRINT_PLP, "plp"},
+       {PRINT_LPRNG, "lprng"},
+       {PRINT_SOFTQ, "softq"},
+       {PRINT_CUPS, "cups"},
+       {PRINT_LPRNT, "nt"},
+       {PRINT_LPROS2, "os2"},
+#ifdef DEVELOPER
+       {PRINT_TEST, "test"},
+       {PRINT_VLP, "vlp"},
+#endif /* DEVELOPER */
+       {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_ssl[] = {
+#ifdef WITH_LDAP_SAMCONFIG
+       {LDAP_SSL_ON, "Yes"},
+       {LDAP_SSL_ON, "yes"},
+       {LDAP_SSL_ON, "on"},
+       {LDAP_SSL_ON, "On"},
+#endif
+       {LDAP_SSL_OFF, "no"},
+       {LDAP_SSL_OFF, "No"},
+       {LDAP_SSL_OFF, "off"},
+       {LDAP_SSL_OFF, "Off"},
+       {LDAP_SSL_START_TLS, "start tls"},
+       {LDAP_SSL_START_TLS, "Start_tls"},
+       {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_passwd_sync[] = {
+       {LDAP_PASSWD_SYNC_ON, "Yes"},
+       {LDAP_PASSWD_SYNC_ON, "yes"},
+       {LDAP_PASSWD_SYNC_ON, "on"},
+       {LDAP_PASSWD_SYNC_ON, "On"},
+       {LDAP_PASSWD_SYNC_OFF, "no"},
+       {LDAP_PASSWD_SYNC_OFF, "No"},
+       {LDAP_PASSWD_SYNC_OFF, "off"},
+       {LDAP_PASSWD_SYNC_OFF, "Off"},
+#ifdef LDAP_EXOP_X_MODIFY_PASSWD       
+       {LDAP_PASSWD_SYNC_ONLY, "Only"},
+       {LDAP_PASSWD_SYNC_ONLY, "only"},
+#endif /* LDAP_EXOP_X_MODIFY_PASSWD */ 
+       {-1, NULL}
+};
+
+/* Types of machine we can announce as. */
+#define ANNOUNCE_AS_NT_SERVER 1
+#define ANNOUNCE_AS_WIN95 2
+#define ANNOUNCE_AS_WFW 3
+#define ANNOUNCE_AS_NT_WORKSTATION 4
+
+static const struct enum_list enum_announce_as[] = {
+       {ANNOUNCE_AS_NT_SERVER, "NT"},
+       {ANNOUNCE_AS_NT_SERVER, "NT Server"},
+       {ANNOUNCE_AS_NT_WORKSTATION, "NT Workstation"},
+       {ANNOUNCE_AS_WIN95, "win95"},
+       {ANNOUNCE_AS_WFW, "WfW"},
+       {-1, NULL}
+};
+
+static const struct enum_list enum_case[] = {
+       {CASE_LOWER, "lower"},
+       {CASE_UPPER, "upper"},
+       {-1, NULL}
+};
+
+static const struct enum_list enum_bool_auto[] = {
+       {False, "No"},
+       {False, "False"},
+       {False, "0"},
+       {True, "Yes"},
+       {True, "True"},
+       {True, "1"},
+       {Auto, "Auto"},
+       {-1, NULL}
+};
+
+/* Client-side offline caching policy types */
+#define CSC_POLICY_MANUAL 0
+#define CSC_POLICY_DOCUMENTS 1
+#define CSC_POLICY_PROGRAMS 2
+#define CSC_POLICY_DISABLE 3
+
+static const struct enum_list enum_csc_policy[] = {
+       {CSC_POLICY_MANUAL, "manual"},
+       {CSC_POLICY_DOCUMENTS, "documents"},
+       {CSC_POLICY_PROGRAMS, "programs"},
+       {CSC_POLICY_DISABLE, "disable"},
+       {-1, NULL}
+};
+
+/* 
+   Do you want session setups at user level security with a invalid
+   password to be rejected or allowed in as guest? WinNT rejects them
+   but it can be a pain as it means "net view" needs to use a password
+
+   You have 3 choices in the setting of map_to_guest:
+
+   "Never" means session setups with an invalid password
+   are rejected. This is the default.
+
+   "Bad User" means session setups with an invalid password
+   are rejected, unless the username does not exist, in which case it
+   is treated as a guest login
+
+   "Bad Password" means session setups with an invalid password
+   are treated as a guest login
+
+   Note that map_to_guest only has an effect in user or server
+   level security.
+*/
+
+static const struct enum_list enum_map_to_guest[] = {
+       {NEVER_MAP_TO_GUEST, "Never"},
+       {MAP_TO_GUEST_ON_BAD_USER, "Bad User"},
+       {MAP_TO_GUEST_ON_BAD_PASSWORD, "Bad Password"},
+       {-1, NULL}
+};
+
+/* Note: We do not initialise the defaults union - it is not allowed in ANSI C
+ *
+ * Note: We have a flag called FLAG_DEVELOPER but is not used at this time, it
+ * is implied in current control logic. This may change at some later time. A
+ * flag value of 0 means - show as development option only.
+ *
+ * The FLAG_HIDE is explicit. Paramters set this way do NOT appear in any edit
+ * screen in SWAT. This is used to exclude parameters as well as to squash all
+ * parameters that have been duplicated by pseudonyms.
+ */
+static struct parm_struct parm_table[] = {
+       {"Base Options", P_SEP, P_SEPARATOR},
+
+       {"dos charset", P_STRING, P_GLOBAL, &Globals.dos_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"unix charset", P_STRING, P_GLOBAL, &Globals.unix_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"display charset", P_STRING, P_GLOBAL, &Globals.display_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"path", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"directory", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_HIDE},
+       {"workgroup", P_USTRING, P_GLOBAL, &Globals.szWorkgroup, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"realm", P_USTRING, P_GLOBAL, &Globals.szRealm, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"ADS server", P_STRING, P_GLOBAL, &Globals.szADSserver, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"netbios name", P_USTRING, P_GLOBAL, &Globals.szNetbiosName, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"netbios aliases", P_LIST, P_GLOBAL, &Globals.szNetbiosAliases, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"netbios scope", P_USTRING, P_GLOBAL, &Globals.szNetbiosScope, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"server string", P_STRING, P_GLOBAL, &Globals.szServerString, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED  | FLAG_DEVELOPER},
+       {"interfaces", P_LIST, P_GLOBAL, &Globals.szInterfaces, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"bind interfaces only", P_BOOL, P_GLOBAL, &Globals.bBindInterfacesOnly, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"ntvfs handler", P_STRING, P_LOCAL, &sDefault.ntvfs_handler, NULL, NULL, FLAG_ADVANCED},
+
+       {"Security Options", P_SEP, P_SEPARATOR},
+       
+       {"security", P_ENUM, P_GLOBAL, &Globals.security, NULL, enum_security, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"auth methods", P_LIST, P_GLOBAL, &Globals.AuthMethods, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"encrypt passwords", P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"update encrypted", P_BOOL, P_GLOBAL, &Globals.bUpdateEncrypt, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"allow trusted domains", P_BOOL, P_GLOBAL, &Globals.bAllowTrustedDomains, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIDMapBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"min passwd length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"min password length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"map to guest", P_ENUM, P_GLOBAL, &Globals.map_to_guest, NULL, enum_map_to_guest, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"obey pam restrictions", P_BOOL, P_GLOBAL, &Globals.bObeyPamRestrictions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"private dir", P_STRING, P_GLOBAL, &Globals.szPrivateDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"passdb backend", P_LIST, P_GLOBAL, &Globals.szPassdbBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"sam backend", P_LIST, P_GLOBAL, &Globals.szSamBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"non unix account range", P_STRING, P_GLOBAL, &Globals.szNonUnixAccountRange, handle_non_unix_account_range, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"algorithmic rid base", P_INTEGER, P_GLOBAL, &Globals.AlgorithmicRidBase, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"root dir", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"root", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE | FLAG_DEVELOPER},
+       {"guest account", P_STRING, P_GLOBAL, &Globals.szGuestaccount, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"pam password change", P_BOOL, P_GLOBAL, &Globals.bPamPasswordChange, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"passwd program", P_STRING, P_GLOBAL, &Globals.szPasswdProgram, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"passwd chat debug", P_BOOL, P_GLOBAL, &Globals.bPasswdChatDebug, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER | FLAG_DEVELOPER},
+       {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"username level", P_INTEGER, P_GLOBAL, &Globals.unamelevel, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"unix password sync", P_BOOL, P_GLOBAL, &Globals.bUnixPasswdSync, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"restrict anonymous", P_INTEGER, P_GLOBAL, &Globals.restrict_anonymous, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"lanman auth", P_BOOL, P_GLOBAL, &Globals.bLanmanAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ntlm auth", P_BOOL, P_GLOBAL, &Globals.bNTLMAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"client NTLMv2 auth", P_BOOL, P_GLOBAL, &Globals.bClientNTLMv2Auth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"client lanman auth", P_BOOL, P_GLOBAL, &Globals.bClientLanManAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"user", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+       {"users", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+       
+       {"invalid users", P_LIST, P_LOCAL, &sDefault.szInvalidUsers, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"valid users", P_LIST, P_LOCAL, &sDefault.szValidUsers, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"admin users", P_LIST, P_LOCAL, &sDefault.szAdminUsers, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"read list", P_LIST, P_LOCAL, &sDefault.readlist, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"write list", P_LIST, P_LOCAL, &sDefault.writelist, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"printer admin", P_LIST, P_LOCAL, &sDefault.printer_admin, NULL, NULL, FLAG_GLOBAL | FLAG_PRINT},
+       {"force user", P_STRING, P_LOCAL, &sDefault.force_user, NULL, NULL, FLAG_SHARE},
+       {"force group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_SHARE},
+       {"group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"read only", P_BOOL, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE},
+       {"write ok", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+       {"writeable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+       {"writable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+       
+       {"create mask", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_GLOBAL},
+       {"force create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"security mask", P_OCTAL, P_LOCAL, &sDefault.iSecurity_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"force security mode", P_OCTAL, P_LOCAL, &sDefault.iSecurity_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"directory mask", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_GLOBAL},
+       {"force directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"directory security mask", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"force directory security mode", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE},
+       {"inherit permissions", P_BOOL, P_LOCAL, &sDefault.bInheritPerms, NULL, NULL, FLAG_SHARE},
+       {"inherit acls", P_BOOL, P_LOCAL, &sDefault.bInheritACLS, NULL, NULL, FLAG_SHARE},
+       {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_SHARE},
+       {"only guest", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_HIDE},
+
+       {"guest ok", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"public", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_HIDE},
+       
+       {"only user", P_BOOL, P_LOCAL, &sDefault.bOnlyUser, NULL, NULL, FLAG_SHARE},
+       {"hosts allow", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"allow hosts", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_HIDE},
+       {"hosts deny", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"deny hosts", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_HIDE},
+       {"preload modules", P_LIST, P_GLOBAL, &Globals.szPreloadModules, NULL, NULL, FLAG_BASIC | FLAG_GLOBAL},
+
+       {"Logging Options", P_SEP, P_SEPARATOR},
+
+       {"log level", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"debuglevel", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL, NULL, FLAG_HIDE},
+       {"syslog", P_INTEGER, P_GLOBAL, &Globals.syslog, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"syslog only", P_BOOL, P_GLOBAL, &Globals.bSyslogOnly, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"log file", P_STRING, P_GLOBAL, &Globals.szLogFile, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"max log size", P_INTEGER, P_GLOBAL, &Globals.max_log_size, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"timestamp logs", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"debug timestamp", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_DEVELOPER},
+       {"debug hires timestamp", P_BOOL, P_GLOBAL, &Globals.bDebugHiresTimestamp, NULL, NULL, FLAG_DEVELOPER},
+       {"debug pid", P_BOOL, P_GLOBAL, &Globals.bDebugPid, NULL, NULL, FLAG_DEVELOPER},
+       {"debug uid", P_BOOL, P_GLOBAL, &Globals.bDebugUid, NULL, NULL, FLAG_DEVELOPER},
+       
+       {"Protocol Options", P_SEP, P_SEPARATOR},
+       
+       {"smb ports", P_STRING, P_GLOBAL, &Globals.smb_ports, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"large readwrite", P_BOOL, P_GLOBAL, &Globals.bLargeReadwrite, NULL, NULL, FLAG_DEVELOPER},
+       {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_DEVELOPER},
+       {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_DEVELOPER},
+       {"unicode", P_BOOL, P_GLOBAL, &Globals.bUnicode, NULL, NULL, FLAG_DEVELOPER},
+       {"read bmpx", P_BOOL, P_GLOBAL, &Globals.bReadbmpx, NULL, NULL, FLAG_DEVELOPER},
+       {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_DEVELOPER},
+       {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_DEVELOPER},
+       {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"acl compatibility", P_STRING, P_GLOBAL, &Globals.szAclCompat, handle_acl_compatibility, NULL, FLAG_SHARE | FLAG_GLOBAL | FLAG_ADVANCED},
+       {"nt acl support", P_BOOL,  P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD},
+       {"nt pipe support", P_BOOL, P_GLOBAL, &Globals.bNTPipeSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"nt status support", P_BOOL, P_GLOBAL, &Globals.bNTStatusSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"profile acls", P_BOOL,  P_LOCAL, &sDefault.bProfileAcls, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE  | FLAG_ADVANCED | FLAG_WIZARD},
+       
+       {"announce version", P_STRING, P_GLOBAL, &Globals.szAnnounceVersion, NULL, NULL, FLAG_DEVELOPER},
+       {"announce as", P_ENUM, P_GLOBAL, &Globals.announce_as, NULL, enum_announce_as, FLAG_DEVELOPER},
+       {"max mux", P_INTEGER, P_GLOBAL, &Globals.max_mux, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"max xmit", P_INTEGER, P_GLOBAL, &Globals.max_xmit, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"name resolve order", P_STRING, P_GLOBAL, &Globals.szNameResolveOrder, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, 
+       {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"time server", P_BOOL, P_GLOBAL, &Globals.bTimeServer, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_DEVELOPER},
+       {"client signing", P_BOOL, P_GLOBAL, &Globals.client_signing, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"client use spnego", P_BOOL, P_GLOBAL, &Globals.bClientUseSpnego, NULL, NULL, FLAG_DEVELOPER},
+
+       {"Tuning Options", P_SEP, P_SEPARATOR},
+       
+       {"block size", P_INTEGER, P_LOCAL, &sDefault.iBlock_size, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"change notify timeout", P_INTEGER, P_GLOBAL, &Globals.change_notify_timeout, NULL, NULL, FLAG_DEVELOPER},
+       {"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL, NULL, FLAG_DEVELOPER},
+       {"keepalive", P_INTEGER, P_GLOBAL, &Globals.keepalive, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"kernel change notify", P_BOOL, P_GLOBAL, &Globals.bKernelChangeNotify, NULL, NULL, FLAG_DEVELOPER},
+       
+       {"lpq cache time", P_INTEGER, P_GLOBAL, &Globals.lpqcachetime, NULL, NULL, FLAG_DEVELOPER},
+       {"max smbd processes", P_INTEGER, P_GLOBAL, &Globals.iMaxSmbdProcesses, NULL, NULL, FLAG_DEVELOPER},
+       {"max connections", P_INTEGER, P_LOCAL, &sDefault.iMaxConnections, NULL, NULL, FLAG_SHARE},
+       {"paranoid server security", P_BOOL, P_GLOBAL, &Globals.paranoid_server_security, NULL, NULL, FLAG_DEVELOPER},
+       {"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL, NULL, FLAG_DEVELOPER},
+       {"max open files", P_INTEGER, P_GLOBAL, &Globals.max_open_files, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL, NULL, FLAG_PRINT},
+       {"read size", P_INTEGER, P_GLOBAL, &Globals.ReadSize, NULL, NULL, FLAG_DEVELOPER},
+       
+       {"socket options", P_STRING, P_GLOBAL, &Globals.socket_options, NULL, NULL, FLAG_DEVELOPER},
+       {"stat cache size", P_INTEGER, P_GLOBAL, &Globals.stat_cache_size, NULL, NULL, FLAG_DEVELOPER},
+       {"strict allocate", P_BOOL, P_LOCAL, &sDefault.bStrictAllocate, NULL, NULL, FLAG_SHARE},
+       {"strict sync", P_BOOL, P_LOCAL, &sDefault.bStrictSync, NULL, NULL, FLAG_SHARE},
+       {"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL, NULL, FLAG_SHARE},
+       {"use mmap", P_BOOL, P_GLOBAL, &Globals.bUseMmap, NULL, NULL, FLAG_DEVELOPER},
+       {"use sendfile", P_BOOL, P_LOCAL, &sDefault.bUseSendfile, NULL, NULL, FLAG_SHARE},
+       {"hostname lookups", P_BOOL, P_GLOBAL, &Globals.bHostnameLookups, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"write cache size", P_INTEGER, P_LOCAL, &sDefault.iWriteCacheSize, NULL, NULL, FLAG_SHARE},
+
+       {"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {"Printing Options", P_SEP, P_SEPARATOR},
+       
+       {"total print jobs", P_INTEGER, P_GLOBAL, &Globals.iTotalPrintJobs, NULL, NULL, FLAG_PRINT},
+       {"max reported print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxReportedPrintJobs, NULL, NULL, FLAG_PRINT},
+       {"max print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxPrintJobs, NULL, NULL, FLAG_PRINT},
+       {"load printers", P_BOOL, P_GLOBAL, &Globals.bLoadPrinters, NULL, NULL, FLAG_PRINT},
+       {"printcap name", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_PRINT | FLAG_DEVELOPER},
+       {"printcap", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_HIDE},
+       {"printable", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_PRINT},
+       {"print ok", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_HIDE},
+       {"printing", P_ENUM, P_LOCAL, &sDefault.iPrinting, NULL, enum_printing, FLAG_PRINT | FLAG_GLOBAL},
+       {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"disable spoolss", P_BOOL, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"lpq command", P_STRING, P_LOCAL, &sDefault.szLpqcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"lprm command", P_STRING, P_LOCAL, &sDefault.szLprmcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"lppause command", P_STRING, P_LOCAL, &sDefault.szLppausecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"lpresume command", P_STRING, P_LOCAL, &sDefault.szLpresumecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"queuepause command", P_STRING, P_LOCAL, &sDefault.szQueuepausecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+       {"queueresume command", P_STRING, P_LOCAL, &sDefault.szQueueresumecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL},
+
+       {"enumports command", P_STRING, P_GLOBAL, &Globals.szEnumPortsCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"addprinter command", P_STRING, P_GLOBAL, &Globals.szAddPrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"deleteprinter command", P_STRING, P_GLOBAL, &Globals.szDeletePrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"show add printer wizard", P_BOOL, P_GLOBAL, &Globals.bMsAddPrinterWizard, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"os2 driver map", P_STRING, P_GLOBAL, &Globals.szOs2DriverMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"printer name", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_PRINT},
+       {"printer", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_HIDE},
+       {"use client driver", P_BOOL, P_LOCAL, &sDefault.bUseClientDriver, NULL, NULL, FLAG_PRINT},
+       {"default devmode", P_BOOL, P_LOCAL, &sDefault.bDefaultDevmode, NULL, NULL, FLAG_PRINT},
+
+       {"Filename Handling", P_SEP, P_SEPARATOR},
+       {"strip dot", P_BOOL, P_GLOBAL, &Globals.bStripDot, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"mangling method", P_STRING, P_GLOBAL, &Globals.szManglingMethod, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"mangle prefix", P_INTEGER, P_GLOBAL, &Globals.mangle_prefix, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"mangled stack", P_INTEGER, P_GLOBAL, &Globals.mangled_stack, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"default case", P_ENUM, P_LOCAL, &sDefault.iDefaultCase, NULL, enum_case, FLAG_SHARE},
+       {"case sensitive", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"casesignames", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_HIDE},
+       {"preserve case", P_BOOL, P_LOCAL, &sDefault.bCasePreserve, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"short preserve case", P_BOOL, P_LOCAL, &sDefault.bShortCasePreserve, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"mangle case", P_BOOL, P_LOCAL, &sDefault.bCaseMangle, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"mangling char", P_CHAR, P_LOCAL, &sDefault.magic_char, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"hide dot files", P_BOOL, P_LOCAL, &sDefault.bHideDotFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"hide special files", P_BOOL, P_LOCAL, &sDefault.bHideSpecialFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"hide unreadable", P_BOOL, P_LOCAL, &sDefault.bHideUnReadable, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"hide unwriteable files", P_BOOL, P_LOCAL, &sDefault.bHideUnWriteableFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"delete veto files", P_BOOL, P_LOCAL, &sDefault.bDeleteVetoFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"veto files", P_STRING, P_LOCAL, &sDefault.szVetoFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL },
+       {"hide files", P_STRING, P_LOCAL, &sDefault.szHideFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL },
+       {"veto oplock files", P_STRING, P_LOCAL, &sDefault.szVetoOplockFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL },
+       {"map system", P_BOOL, P_LOCAL, &sDefault.bMap_system, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"map hidden", P_BOOL, P_LOCAL, &sDefault.bMap_hidden, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"map archive", P_BOOL, P_LOCAL, &sDefault.bMap_archive, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"mangled names", P_BOOL, P_LOCAL, &sDefault.bMangledNames, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"mangled map", P_STRING, P_LOCAL, &sDefault.szMangledMap, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"stat cache", P_BOOL, P_GLOBAL, &Globals.bStatCache, NULL, NULL, FLAG_DEVELOPER},
+
+       {"Domain Options", P_SEP, P_SEPARATOR},
+       
+       {"machine password timeout", P_INTEGER, P_GLOBAL, &Globals.machine_password_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+
+       {"Logon Options", P_SEP, P_SEPARATOR},
+
+       {"add user script", P_STRING, P_GLOBAL, &Globals.szAddUserScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"delete user script", P_STRING, P_GLOBAL, &Globals.szDelUserScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"add group script", P_STRING, P_GLOBAL, &Globals.szAddGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"delete group script", P_STRING, P_GLOBAL, &Globals.szDelGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"add user to group script", P_STRING, P_GLOBAL, &Globals.szAddUserToGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"delete user from group script", P_STRING, P_GLOBAL, &Globals.szDelUserFromGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"set primary group script", P_STRING, P_GLOBAL, &Globals.szSetPrimaryGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"add machine script", P_STRING, P_GLOBAL, &Globals.szAddMachineScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"shutdown script", P_STRING, P_GLOBAL, &Globals.szShutdownScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"abort shutdown script", P_STRING, P_GLOBAL, &Globals.szAbortShutdownScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {"logon script", P_STRING, P_GLOBAL, &Globals.szLogonScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"logon path", P_STRING, P_GLOBAL, &Globals.szLogonPath, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"logon drive", P_STRING, P_GLOBAL, &Globals.szLogonDrive, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"logon home", P_STRING, P_GLOBAL, &Globals.szLogonHome, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"domain logons", P_BOOL, P_GLOBAL, &Globals.bDomainLogons, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {"Browse Options", P_SEP, P_SEPARATOR},
+       
+       {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"lm announce", P_ENUM, P_GLOBAL, &Globals.lm_announce, NULL, enum_bool_auto, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"lm interval", P_INTEGER, P_GLOBAL, &Globals.lm_interval, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"preferred master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"prefered master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_HIDE},
+       {"local master", P_BOOL, P_GLOBAL, &Globals.bLocalMaster, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"domain master", P_ENUM, P_GLOBAL, &Globals.bDomainMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"browse list", P_BOOL, P_GLOBAL, &Globals.bBrowseList, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"browseable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER},
+       {"browsable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_HIDE},
+       {"enhanced browsing", P_BOOL, P_GLOBAL, &Globals.enhanced_browsing, NULL, NULL, FLAG_DEVELOPER | FLAG_ADVANCED},
+
+       {"WINS Options", P_SEP, P_SEPARATOR},
+       {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"wins server", P_LIST, P_GLOBAL, &Globals.szWINSservers, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+       {"wins hook", P_STRING, P_GLOBAL, &Globals.szWINSHook, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"wins partners", P_STRING, P_GLOBAL, &Globals.szWINSPartners, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER},
+
+       {"Locking Options", P_SEP, P_SEPARATOR},
+       
+       {"blocking locks", P_BOOL, P_LOCAL, &sDefault.bBlockingLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"csc policy", P_ENUM, P_LOCAL, &sDefault.iCSCPolicy, NULL, enum_csc_policy, FLAG_SHARE | FLAG_GLOBAL},
+       {"fake oplocks", P_BOOL, P_LOCAL, &sDefault.bFakeOplocks, NULL, NULL, FLAG_SHARE},
+       {"kernel oplocks", P_BOOL, P_GLOBAL, &Globals.bKernelOplocks, NULL, NULL, FLAG_GLOBAL},
+       {"locking", P_BOOL, P_LOCAL, &sDefault.bLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"lock spin count", P_INTEGER, P_GLOBAL, &Globals.iLockSpinCount, NULL, NULL, FLAG_GLOBAL},
+       {"lock spin time", P_INTEGER, P_GLOBAL, &Globals.iLockSpinTime, NULL, NULL, FLAG_GLOBAL},
+       
+       {"oplocks", P_BOOL, P_LOCAL, &sDefault.bOpLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"level2 oplocks", P_BOOL, P_LOCAL, &sDefault.bLevel2OpLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"oplock break wait time", P_INTEGER, P_GLOBAL, &Globals.oplock_break_wait_time, NULL, NULL, FLAG_GLOBAL},
+       {"oplock contention limit", P_INTEGER, P_LOCAL, &sDefault.iOplockContentionLimit, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"posix locking", P_BOOL, P_LOCAL, &sDefault.bPosixLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"strict locking", P_BOOL, P_LOCAL, &sDefault.bStrictLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"share modes", P_BOOL, P_LOCAL,  &sDefault.bShareModes, NULL, NULL, FLAG_SHARE|FLAG_GLOBAL},
+
+       {"Ldap Options", P_SEP, P_SEPARATOR},
+       
+#ifdef WITH_LDAP_SAMCONFIG
+       {"ldap server", P_STRING, P_GLOBAL, &Globals.szLdapServer, NULL, NULL, 0},
+       {"ldap port", P_INTEGER, P_GLOBAL, &Globals.ldap_port, NULL, NULL, 0}, 
+#endif
+       {"ldap suffix", P_STRING, P_GLOBAL, &Globals.szLdapSuffix, handle_ldap_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap machine suffix", P_STRING, P_GLOBAL, &Globals.szLdapMachineSuffix, handle_ldap_machine_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap user suffix", P_STRING, P_GLOBAL, &Globals.szLdapUserSuffix, handle_ldap_user_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap filter", P_STRING, P_GLOBAL, &Globals.szLdapFilter, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap admin dn", P_STRING, P_GLOBAL, &Globals.szLdapAdminDn, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap ssl", P_ENUM, P_GLOBAL, &Globals.ldap_ssl, NULL, enum_ldap_ssl, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap passwd sync", P_ENUM, P_GLOBAL, &Globals.ldap_passwd_sync, NULL, enum_ldap_passwd_sync, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"ldap trust ids", P_BOOL, P_GLOBAL, &Globals.ldap_trust_ids, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {"Miscellaneous Options", P_SEP, P_SEPARATOR},
+       {"add share command", P_STRING, P_GLOBAL, &Globals.szAddShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"change share command", P_STRING, P_GLOBAL, &Globals.szChangeShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"delete share command", P_STRING, P_GLOBAL, &Globals.szDeleteShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE},
+       {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_HIDE}, 
+       {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, 
+#ifdef WITH_UTMP
+       {"utmp directory", P_STRING, P_GLOBAL, &Globals.szUtmpDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"wtmp directory", P_STRING, P_GLOBAL, &Globals.szWtmpDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"utmp",          P_BOOL, P_GLOBAL, &Globals.bUtmp, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+#endif
+       
+       {"default service", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"default", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL,  FLAG_DEVELOPER},
+       {"message command", P_STRING, P_GLOBAL, &Globals.szMsgCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"dfree command", P_STRING, P_GLOBAL, &Globals.szDfree, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"remote announce", P_STRING, P_GLOBAL, &Globals.szRemoteAnnounce, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"remote browse sync", P_STRING, P_GLOBAL, &Globals.szRemoteBrowseSync, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"socket address", P_STRING, P_GLOBAL, &Globals.szSocketAddress, NULL, NULL, FLAG_DEVELOPER},
+       {"homedir map", P_STRING, P_GLOBAL, &Globals.szNISHomeMapName, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"time offset", P_INTEGER, P_GLOBAL, &Globals.time_offset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"NIS homedir", P_BOOL, P_GLOBAL, &Globals.bNISHomeMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"-valid", P_BOOL, P_LOCAL, &sDefault.valid, NULL, NULL, FLAG_HIDE},
+       
+       {"copy", P_STRING, P_LOCAL, &sDefault.szCopy, handle_copy, NULL, FLAG_HIDE},
+       {"include", P_STRING, P_LOCAL, &sDefault.szInclude, handle_include, NULL, FLAG_HIDE},
+       {"exec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT},
+       {"preexec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       
+       {"preexec close", P_BOOL, P_LOCAL, &sDefault.bPreexecClose, NULL, NULL, FLAG_SHARE},
+       {"postexec", P_STRING, P_LOCAL, &sDefault.szPostExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT},
+       {"root preexec", P_STRING, P_LOCAL, &sDefault.szRootPreExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT},
+       {"root preexec close", P_BOOL, P_LOCAL, &sDefault.bRootpreexecClose, NULL, NULL, FLAG_SHARE},
+       {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT},
+       {"available", P_BOOL, P_LOCAL, &sDefault.bAvailable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+       {"volume", P_STRING, P_LOCAL, &sDefault.volume, NULL, NULL, FLAG_SHARE },
+       {"fstype", P_STRING, P_LOCAL, &sDefault.fstype, NULL, NULL, FLAG_SHARE},
+       {"set directory", P_BOOLREV, P_LOCAL, &sDefault.bNo_set_dir, NULL, NULL, FLAG_SHARE},
+       {"source environment", P_STRING, P_GLOBAL, &Globals.szSourceEnv, handle_source_env, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"wide links", P_BOOL, P_LOCAL, &sDefault.bWidelinks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"follow symlinks", P_BOOL, P_LOCAL, &sDefault.bSymlinks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"dont descend", P_STRING, P_LOCAL, &sDefault.szDontdescend, NULL, NULL, FLAG_SHARE},
+       {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL, NULL, FLAG_SHARE},
+       {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL, NULL, FLAG_SHARE},
+       {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"dos filemode", P_BOOL, P_LOCAL, &sDefault.bDosFilemode, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"dos filetimes", P_BOOL, P_LOCAL, &sDefault.bDosFiletimes, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"dos filetime resolution", P_BOOL, P_LOCAL, &sDefault.bDosFiletimeResolution, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+
+       {"fake directory create times", P_BOOL, P_LOCAL, &sDefault.bFakeDirCreateTimes, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL},
+       {"panic action", P_STRING, P_GLOBAL, &Globals.szPanicAction, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"hide local users", P_BOOL, P_GLOBAL, &Globals.bHideLocalUsers, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {"VFS module options", P_SEP, P_SEPARATOR},
+       
+       {"vfs object", P_STRING, P_LOCAL, &sDefault.szVfsObjectFile, handle_vfs_object, NULL, FLAG_SHARE},
+       {"vfs options", P_STRING, P_LOCAL, &sDefault.szVfsOptions, NULL, NULL, FLAG_SHARE},
+       {"vfs path", P_STRING, P_LOCAL, &sDefault.szVfsPath, NULL, NULL, FLAG_SHARE},
+
+       
+       {"msdfs root", P_BOOL, P_LOCAL, &sDefault.bMSDfsRoot, NULL, NULL, FLAG_SHARE},
+       {"msdfs proxy", P_STRING, P_LOCAL, &sDefault.szMSDfsProxy, NULL, NULL, FLAG_SHARE},
+       {"host msdfs", P_BOOL, P_GLOBAL, &Globals.bHostMSDfs, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"Winbind options", P_SEP, P_SEPARATOR},
+
+       {"winbind uid", P_STRING, P_GLOBAL, &Globals.szWinbindUID, handle_winbind_uid, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind gid", P_STRING, P_GLOBAL, &Globals.szWinbindGID, handle_winbind_gid, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"template homedir", P_STRING, P_GLOBAL, &Globals.szTemplateHomedir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"template shell", P_STRING, P_GLOBAL, &Globals.szTemplateShell, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind separator", P_STRING, P_GLOBAL, &Globals.szWinbindSeparator, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind cache time", P_INTEGER, P_GLOBAL, &Globals.winbind_cache_time, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind enum users", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumUsers, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind enum groups", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumGroups, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+       {"winbind use default domain", P_BOOL, P_GLOBAL, &Globals.bWinbindUseDefaultDomain, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+
+       {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0}
+};
+
+/***************************************************************************
+ Initialise the sDefault parameter structure for the printer values.
+***************************************************************************/
+
+static void init_printer_values(void)
+{
+       /* choose defaults depending on the type of printing */
+       switch (sDefault.iPrinting) {
+               case PRINT_BSD:
+               case PRINT_AIX:
+               case PRINT_LPRNT:
+               case PRINT_LPROS2:
+                       string_set(&sDefault.szLpqcommand, "lpq -P'%p'");
+                       string_set(&sDefault.szLprmcommand, "lprm -P'%p' %j");
+                       string_set(&sDefault.szPrintcommand,
+                                  "lpr -r -P'%p' %s");
+                       break;
+
+               case PRINT_LPRNG:
+               case PRINT_PLP:
+                       string_set(&sDefault.szLpqcommand, "lpq -P'%p'");
+                       string_set(&sDefault.szLprmcommand, "lprm -P'%p' %j");
+                       string_set(&sDefault.szPrintcommand,
+                                  "lpr -r -P'%p' %s");
+                       string_set(&sDefault.szQueuepausecommand,
+                                  "lpc stop '%p'");
+                       string_set(&sDefault.szQueueresumecommand,
+                                  "lpc start '%p'");
+                       string_set(&sDefault.szLppausecommand,
+                                  "lpc hold '%p' %j");
+                       string_set(&sDefault.szLpresumecommand,
+                                  "lpc release '%p' %j");
+                       break;
+
+               case PRINT_CUPS:
+#ifdef HAVE_CUPS
+                       string_set(&sDefault.szLpqcommand, "");
+                       string_set(&sDefault.szLprmcommand, "");
+                       string_set(&sDefault.szPrintcommand, "");
+                       string_set(&sDefault.szLppausecommand, "");
+                       string_set(&sDefault.szLpresumecommand, "");
+                       string_set(&sDefault.szQueuepausecommand, "");
+                       string_set(&sDefault.szQueueresumecommand, "");
+
+                       string_set(&Globals.szPrintcapname, "cups");
+#else
+                       string_set(&sDefault.szLpqcommand,
+                                  "/usr/bin/lpstat -o '%p'");
+                       string_set(&sDefault.szLprmcommand,
+                                  "/usr/bin/cancel '%p-%j'");
+                       string_set(&sDefault.szPrintcommand,
+                                  "/usr/bin/lp -d '%p' %s; rm %s");
+                       string_set(&sDefault.szLppausecommand,
+                                  "lp -i '%p-%j' -H hold");
+                       string_set(&sDefault.szLpresumecommand,
+                                  "lp -i '%p-%j' -H resume");
+                       string_set(&sDefault.szQueuepausecommand,
+                                  "/usr/bin/disable '%p'");
+                       string_set(&sDefault.szQueueresumecommand,
+                                  "/usr/bin/enable '%p'");
+                       string_set(&Globals.szPrintcapname, "lpstat");
+#endif /* HAVE_CUPS */
+                       break;
+
+               case PRINT_SYSV:
+               case PRINT_HPUX:
+                       string_set(&sDefault.szLpqcommand, "lpstat -o%p");
+                       string_set(&sDefault.szLprmcommand, "cancel %p-%j");
+                       string_set(&sDefault.szPrintcommand,
+                                  "lp -c -d%p %s; rm %s");
+                       string_set(&sDefault.szQueuepausecommand,
+                                  "disable %p");
+                       string_set(&sDefault.szQueueresumecommand,
+                                  "enable %p");
+#ifndef HPUX
+                       string_set(&sDefault.szLppausecommand,
+                                  "lp -i %p-%j -H hold");
+                       string_set(&sDefault.szLpresumecommand,
+                                  "lp -i %p-%j -H resume");
+#endif /* HPUX */
+                       break;
+
+               case PRINT_QNX:
+                       string_set(&sDefault.szLpqcommand, "lpq -P%p");
+                       string_set(&sDefault.szLprmcommand, "lprm -P%p %j");
+                       string_set(&sDefault.szPrintcommand, "lp -r -P%p %s");
+                       break;
+
+               case PRINT_SOFTQ:
+                       string_set(&sDefault.szLpqcommand, "qstat -l -d%p");
+                       string_set(&sDefault.szLprmcommand,
+                                  "qstat -s -j%j -c");
+                       string_set(&sDefault.szPrintcommand,
+                                  "lp -d%p -s %s; rm %s");
+                       string_set(&sDefault.szLppausecommand,
+                                  "qstat -s -j%j -h");
+                       string_set(&sDefault.szLpresumecommand,
+                                  "qstat -s -j%j -r");
+                       break;
+#ifdef DEVELOPER
+       case PRINT_TEST:
+       case PRINT_VLP:
+               string_set(&sDefault.szPrintcommand, "vlp print %p %s");
+               string_set(&sDefault.szLpqcommand, "vlp lpq %p");
+               string_set(&sDefault.szLprmcommand, "vlp lprm %p %j");
+               string_set(&sDefault.szLppausecommand, "vlp lppause %p %j");
+               string_set(&sDefault.szLpresumecommand, "vlp lpresum %p %j");
+               string_set(&sDefault.szQueuepausecommand, "vlp queuepause %p");
+               string_set(&sDefault.szQueueresumecommand, "vlp queueresume %p");
+               break;
+#endif /* DEVELOPER */
+
+       }
+}
+
+
+/***************************************************************************
+ Initialise the global parameter structure.
+***************************************************************************/
+static void init_globals(void)
+{
+       pstring s;
+       int i;
+
+       DEBUG(3, ("Initialising global parameters\n"));
+
+       for (i = 0; parm_table[i].label; i++) {
+               if ((parm_table[i].type == P_STRING ||
+                    parm_table[i].type == P_USTRING) &&
+                   parm_table[i].ptr &&
+                   !(parm_table[i].flags & FLAG_CMDLINE)) {
+                       string_set(parm_table[i].ptr, "");
+               }
+       }
+
+       /* options that can be set on the command line must be initialised via
+          the slower do_parameter() to ensure that FLAG_CMDLINE is obeyed */
+       do_parameter("socket options", DEFAULT_SOCKET_OPTIONS);
+       do_parameter("workgroup", DEFAULT_WORKGROUP);
+       do_parameter("netbios name", get_myname());
+       do_parameter("max protocol", "NT1");
+       do_parameter("name resolve order", "lmhosts wins host bcast");
+
+       init_printer_values();
+
+       string_set(&sDefault.fstype, FSTYPE_STRING);
+       string_set(&sDefault.ntvfs_handler, "default");
+
+       string_set(&Globals.szSMBPasswdFile, dyn_SMB_PASSWD_FILE);
+       string_set(&Globals.szPrivateDir, dyn_PRIVATE_DIR);
+
+       /* use the new 'hash2' method by default, with a prefix of 1 */
+       string_set(&Globals.szManglingMethod, "hash2");
+       Globals.mangle_prefix = 1;
+
+       string_set(&Globals.szGuestaccount, GUEST_ACCOUNT);
+
+       /* using UTF8 by default allows us to support all chars */
+       string_set(&Globals.unix_charset, "UTF8");
+
+       /* Use codepage 850 as a default for the dos character set */
+       string_set(&Globals.dos_charset, "CP850");
+
+       /*
+        * Allow the default PASSWD_CHAT to be overridden in local.h.
+        */
+       string_set(&Globals.szPasswdChat, DEFAULT_PASSWD_CHAT);
+       
+       string_set(&Globals.szPasswdProgram, "");
+       string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
+       string_set(&Globals.szPidDir, dyn_PIDDIR);
+       string_set(&Globals.szLockDir, dyn_LOCKDIR);
+       string_set(&Globals.szSocketAddress, "0.0.0.0");
+       pstrcpy(s, "Samba ");
+       pstrcat(s, SAMBA_VERSION);
+       string_set(&Globals.szServerString, s);
+       slprintf(s, sizeof(s) - 1, "%d.%d", DEFAULT_MAJOR_VERSION,
+                DEFAULT_MINOR_VERSION);
+       string_set(&Globals.szAnnounceVersion, s);
+
+       string_set(&Globals.szLogonDrive, "");
+       /* %N is the NIS auto.home server if -DAUTOHOME is used, else same as %L */
+       string_set(&Globals.szLogonHome, "\\\\%N\\%U");
+       string_set(&Globals.szLogonPath, "\\\\%N\\%U\\profile");
+
+       string_set(&Globals.szPasswordServer, "*");
+
+       Globals.AlgorithmicRidBase = BASE_RID;
+
+       Globals.bLoadPrinters = True;
+       Globals.mangled_stack = 50;
+       /* Was 65535 (0xFFFF). 0x4101 matches W2K and causes major speed improvements... */
+       /* Discovered by 2 days of pain by Don McCall @ HP :-). */
+       Globals.max_xmit = 0x4104;
+       Globals.max_mux = 50;   /* This is *needed* for profile support. */
+       Globals.lpqcachetime = 10;
+       Globals.bDisableSpoolss = False;
+       Globals.iMaxSmbdProcesses = 0;/* no limit specified */
+       Globals.iTotalPrintJobs = 0;  /* no limit specified */
+       Globals.pwordlevel = 0;
+       Globals.unamelevel = 0;
+       Globals.deadtime = 0;
+       Globals.bLargeReadwrite = True;
+       Globals.max_log_size = 5000;
+       Globals.max_open_files = MAX_OPEN_FILES;
+       Globals.minprotocol = PROTOCOL_CORE;
+       Globals.security = SEC_USER;
+       Globals.paranoid_server_security = True;
+       Globals.bEncryptPasswords = True;
+       Globals.bUpdateEncrypt = False;
+       Globals.bReadRaw = True;
+       Globals.bWriteRaw = True;
+       Globals.bReadPrediction = False;
+       Globals.bReadbmpx = False;
+       Globals.bNullPasswords = False;
+       Globals.bObeyPamRestrictions = False;
+       Globals.bStripDot = False;
+       Globals.syslog = 1;
+       Globals.bSyslogOnly = False;
+       Globals.bTimestampLogs = True;
+       Globals.bDebugHiresTimestamp = False;
+       Globals.bDebugPid = False;
+       Globals.bDebugUid = False;
+       Globals.max_ttl = 60 * 60 * 24 * 3;     /* 3 days default. */
+       Globals.max_wins_ttl = 60 * 60 * 24 * 6;        /* 6 days default. */
+       Globals.min_wins_ttl = 60 * 60 * 6;     /* 6 hours default. */
+       Globals.machine_password_timeout = 60 * 60 * 24 * 7;    /* 7 days default. */
+       Globals.change_notify_timeout = 60;     /* 1 minute default. */
+       Globals.bKernelChangeNotify = True;     /* On if we have it. */
+       Globals.ReadSize = 16 * 1024;
+       Globals.lm_announce = 2;        /* = Auto: send only if LM clients found */
+       Globals.lm_interval = 60;
+       Globals.stat_cache_size = 50;   /* Number of stat translations we'll keep */
+       Globals.announce_as = ANNOUNCE_AS_NT_SERVER;
+#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
+       Globals.bNISHomeMap = False;
+#ifdef WITH_NISPLUS_HOME
+       string_set(&Globals.szNISHomeMapName, "auto_home.org_dir");
+#else
+       string_set(&Globals.szNISHomeMapName, "auto.home");
+#endif
+#endif
+       Globals.bTimeServer = False;
+       Globals.bBindInterfacesOnly = False;
+       Globals.bUnixPasswdSync = False;
+       Globals.bPamPasswordChange = False;
+       Globals.bPasswdChatDebug = False;
+       Globals.bUnicode = True;        /* Do unicode on the wire by default */
+       Globals.bNTPipeSupport = True;  /* Do NT pipes by default. */
+       Globals.bNTStatusSupport = True; /* Use NT status by default. */
+       Globals.bStatCache = True;      /* use stat cache by default */
+       Globals.restrict_anonymous = 0;
+       Globals.bClientLanManAuth = True;       /* Do use the LanMan hash if it is available */
+       Globals.bLanmanAuth = True;     /* Do use the LanMan hash if it is available */
+       Globals.bNTLMAuth = True;       /* Do use NTLMv1 if it is available (otherwise NTLMv2) */
+       
+       Globals.map_to_guest = 0;       /* By Default, "Never" */
+       Globals.min_passwd_length = MINPASSWDLENGTH;    /* By Default, 5. */
+       Globals.oplock_break_wait_time = 0;     /* By Default, 0 msecs. */
+       Globals.enhanced_browsing = True; 
+       Globals.iLockSpinCount = 3; /* Try 2 times. */
+       Globals.iLockSpinTime = 10; /* usec. */
+#ifdef MMAP_BLACKLIST
+       Globals.bUseMmap = False;
+#else
+       Globals.bUseMmap = True;
+#endif
+       Globals.bUnixExtensions = False;
+
+       /* hostname lookups can be very expensive and are broken on
+          a large number of sites (tridge) */
+       Globals.bHostnameLookups = False;
+
+#ifdef WITH_LDAP_SAMCONFIG
+       string_set(&Globals.szLdapServer, "localhost");
+       Globals.ldap_port = 636;
+       Globals.szPassdbBackend = str_list_make("ldapsam guest", NULL);
+#else
+       Globals.szPassdbBackend = str_list_make("smbpasswd guest", NULL);
+#endif /* WITH_LDAP_SAMCONFIG */
+
+       string_set(&Globals.szLdapSuffix, "");
+       string_set(&Globals.szLdapMachineSuffix, "");
+       string_set(&Globals.szLdapUserSuffix, "");
+
+       string_set(&Globals.szLdapFilter, "(&(uid=%u)(objectclass=sambaAccount))");
+       string_set(&Globals.szLdapAdminDn, "");
+       Globals.ldap_ssl = LDAP_SSL_ON;
+       Globals.ldap_passwd_sync = LDAP_PASSWD_SYNC_OFF;
+
+/* these parameters are set to defaults that are more appropriate
+   for the increasing samba install base:
+
+   as a member of the workgroup, that will possibly become a
+   _local_ master browser (lm = True).  this is opposed to a forced
+   local master browser startup (pm = True).
+
+   doesn't provide WINS server service by default (wsupp = False),
+   and doesn't provide domain master browser services by default, either.
+
+*/
+
+       Globals.bMsAddPrinterWizard = True;
+       Globals.bPreferredMaster = Auto;        /* depending on bDomainMaster */
+       Globals.os_level = 20;
+       Globals.bLocalMaster = True;
+       Globals.bDomainMaster = Auto;   /* depending on bDomainLogons */
+       Globals.bDomainLogons = False;
+       Globals.bBrowseList = True;
+       Globals.bWINSsupport = False;
+       Globals.bWINSproxy = False;
+
+       Globals.bDNSproxy = True;
+
+       /* this just means to use them if they exist */
+       Globals.bKernelOplocks = True;
+
+       Globals.bAllowTrustedDomains = True;
+
+       string_set(&Globals.szTemplateShell, "/bin/false");
+       string_set(&Globals.szTemplateHomedir, "/home/%D/%U");
+       string_set(&Globals.szWinbindSeparator, "\\");
+       string_set(&Globals.szAclCompat, "");
+
+       Globals.winbind_cache_time = 15;
+       Globals.bWinbindEnumUsers = True;
+       Globals.bWinbindEnumGroups = True;
+       Globals.bWinbindUseDefaultDomain = False;
+
+       string_set(&Globals.szIDMapBackend, "tdb");
+
+       Globals.name_cache_timeout = 660; /* In seconds */
+
+       Globals.bUseSpnego = True;
+       Globals.bClientUseSpnego = True;
+
+       string_set(&Globals.smb_ports, SMB_PORTS);
+}
+
+static TALLOC_CTX *lp_talloc;
+
+/******************************************************************* a
+ Free up temporary memory - called from the main loop.
+********************************************************************/
+
+void lp_talloc_free(void)
+{
+       if (!lp_talloc)
+               return;
+       talloc_destroy(lp_talloc);
+       lp_talloc = NULL;
+}
+
+/*******************************************************************
+ Convenience routine to grab string parameters into temporary memory
+ and run standard_sub_basic on them. The buffers can be written to by
+ callers without affecting the source string.
+********************************************************************/
+
+static char *lp_string(const char *s)
+{
+       size_t len = s ? strlen(s) : 0;
+       char *ret;
+
+       /* The follow debug is useful for tracking down memory problems
+          especially if you have an inner loop that is calling a lp_*()
+          function that returns a string.  Perhaps this debug should be
+          present all the time? */
+
+#if 0
+       DEBUG(10, ("lp_string(%s)\n", s));
+#endif
+#if 0  /* until REWRITE done to make thread-safe */
+       if (!lp_talloc)
+               lp_talloc = talloc_init("lp_talloc");
+
+       ret = (char *)talloc(lp_talloc, len + 100);     /* leave room for substitution */
+
+       if (!ret)
+               return NULL;
+
+       if (!s)
+               *ret = 0;
+       else
+               StrnCpy(ret, s, len);
+
+       if (trim_string(ret, "\"", "\"")) {
+               if (strchr(ret,'"') != NULL)
+                       StrnCpy(ret, s, len);
+       }
+
+       standard_sub_basic(ret,len+100);
+       return (ret);
+#endif
+       return s;
+}
+
+/*
+   In this section all the functions that are used to access the 
+   parameters from the rest of the program are defined 
+*/
+
+#define FN_GLOBAL_STRING(fn_name,ptr) \
+ char *fn_name(void) {return(lp_string(*(char **)(ptr) ? *(char **)(ptr) : ""));}
+#define FN_GLOBAL_CONST_STRING(fn_name,ptr) \
+ const char *fn_name(void) {return(*(const char **)(ptr) ? *(const char **)(ptr) : "");}
+#define FN_GLOBAL_LIST(fn_name,ptr) \
+ const char **fn_name(void) {return(*(const char ***)(ptr));}
+#define FN_GLOBAL_BOOL(fn_name,ptr) \
+ BOOL fn_name(void) {return(*(BOOL *)(ptr));}
+#define FN_GLOBAL_CHAR(fn_name,ptr) \
+ char fn_name(void) {return(*(char *)(ptr));}
+#define FN_GLOBAL_INTEGER(fn_name,ptr) \
+ int fn_name(void) {return(*(int *)(ptr));}
+
+#define FN_LOCAL_STRING(fn_name,val) \
+ char *fn_name(int i) {return(lp_string((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val));}
+#define FN_LOCAL_CONST_STRING(fn_name,val) \
+ const char *fn_name(int i) {return (const char *)((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_LIST(fn_name,val) \
+ const char **fn_name(int i) {return(const char **)(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_BOOL(fn_name,val) \
+ BOOL fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_CHAR(fn_name,val) \
+ char fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_INTEGER(fn_name,val) \
+ int fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+
+FN_GLOBAL_STRING(lp_smb_ports, &Globals.smb_ports)
+FN_GLOBAL_STRING(lp_dos_charset, &Globals.dos_charset)
+FN_GLOBAL_STRING(lp_unix_charset, &Globals.unix_charset)
+FN_GLOBAL_STRING(lp_display_charset, &Globals.display_charset)
+FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
+FN_GLOBAL_STRING(lp_configfile, &Globals.szConfigFile)
+FN_GLOBAL_STRING(lp_smb_passwd_file, &Globals.szSMBPasswdFile)
+FN_GLOBAL_STRING(lp_private_dir, &Globals.szPrivateDir)
+FN_GLOBAL_STRING(lp_serverstring, &Globals.szServerString)
+FN_GLOBAL_STRING(lp_printcapname, &Globals.szPrintcapname)
+FN_GLOBAL_STRING(lp_enumports_cmd, &Globals.szEnumPortsCommand)
+FN_GLOBAL_STRING(lp_addprinter_cmd, &Globals.szAddPrinterCommand)
+FN_GLOBAL_STRING(lp_deleteprinter_cmd, &Globals.szDeletePrinterCommand)
+FN_GLOBAL_STRING(lp_os2_driver_map, &Globals.szOs2DriverMap)
+FN_GLOBAL_STRING(lp_lockdir, &Globals.szLockDir)
+FN_GLOBAL_STRING(lp_piddir, &Globals.szPidDir)
+FN_GLOBAL_STRING(lp_mangling_method, &Globals.szManglingMethod)
+FN_GLOBAL_INTEGER(lp_mangle_prefix, &Globals.mangle_prefix)
+#ifdef WITH_UTMP
+FN_GLOBAL_STRING(lp_utmpdir, &Globals.szUtmpDir)
+FN_GLOBAL_STRING(lp_wtmpdir, &Globals.szWtmpDir)
+FN_GLOBAL_BOOL(lp_utmp, &Globals.bUtmp)
+#endif
+FN_GLOBAL_STRING(lp_rootdir, &Globals.szRootdir)
+FN_GLOBAL_STRING(lp_source_environment, &Globals.szSourceEnv)
+FN_GLOBAL_STRING(lp_defaultservice, &Globals.szDefaultService)
+FN_GLOBAL_STRING(lp_msg_command, &Globals.szMsgCommand)
+FN_GLOBAL_STRING(lp_dfree_command, &Globals.szDfree)
+FN_GLOBAL_STRING(lp_hosts_equiv, &Globals.szHostsEquiv)
+FN_GLOBAL_STRING(lp_auto_services, &Globals.szAutoServices)
+FN_GLOBAL_STRING(lp_passwd_program, &Globals.szPasswdProgram)
+FN_GLOBAL_STRING(lp_passwd_chat, &Globals.szPasswdChat)
+FN_GLOBAL_STRING(lp_passwordserver, &Globals.szPasswordServer)
+FN_GLOBAL_STRING(lp_name_resolve_order, &Globals.szNameResolveOrder)
+FN_GLOBAL_STRING(lp_realm, &Globals.szRealm)
+FN_GLOBAL_STRING(lp_ads_server, &Globals.szADSserver)
+FN_GLOBAL_STRING(lp_username_map, &Globals.szUsernameMap)
+FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options)
+FN_GLOBAL_STRING(lp_workgroup, &Globals.szWorkgroup)
+FN_GLOBAL_STRING(lp_netbios_name, &Globals.szNetbiosName)
+FN_GLOBAL_STRING(lp_netbios_scope, &Globals.szNetbiosScope)
+FN_GLOBAL_CONST_STRING(lp_logon_script, &Globals.szLogonScript)
+FN_GLOBAL_CONST_STRING(lp_logon_path, &Globals.szLogonPath)
+FN_GLOBAL_CONST_STRING(lp_logon_drive, &Globals.szLogonDrive)
+FN_GLOBAL_CONST_STRING(lp_logon_home, &Globals.szLogonHome)
+FN_GLOBAL_STRING(lp_remote_announce, &Globals.szRemoteAnnounce)
+FN_GLOBAL_STRING(lp_remote_browse_sync, &Globals.szRemoteBrowseSync)
+FN_GLOBAL_LIST(lp_wins_server_list, &Globals.szWINSservers)
+FN_GLOBAL_LIST(lp_interfaces, &Globals.szInterfaces)
+FN_GLOBAL_STRING(lp_socket_address, &Globals.szSocketAddress)
+FN_GLOBAL_STRING(lp_nis_home_map_name, &Globals.szNISHomeMapName)
+static FN_GLOBAL_STRING(lp_announce_version, &Globals.szAnnounceVersion)
+FN_GLOBAL_LIST(lp_netbios_aliases, &Globals.szNetbiosAliases)
+FN_GLOBAL_LIST(lp_passdb_backend, &Globals.szPassdbBackend)
+FN_GLOBAL_LIST(lp_sam_backend, &Globals.szSamBackend)
+FN_GLOBAL_LIST(lp_preload_modules, &Globals.szPreloadModules)
+FN_GLOBAL_STRING(lp_panic_action, &Globals.szPanicAction)
+FN_GLOBAL_STRING(lp_adduser_script, &Globals.szAddUserScript)
+FN_GLOBAL_STRING(lp_deluser_script, &Globals.szDelUserScript)
+
+FN_GLOBAL_CONST_STRING(lp_guestaccount, &Globals.szGuestaccount)
+FN_GLOBAL_STRING(lp_addgroup_script, &Globals.szAddGroupScript)
+FN_GLOBAL_STRING(lp_delgroup_script, &Globals.szDelGroupScript)
+FN_GLOBAL_STRING(lp_addusertogroup_script, &Globals.szAddUserToGroupScript)
+FN_GLOBAL_STRING(lp_deluserfromgroup_script, &Globals.szDelUserFromGroupScript)
+FN_GLOBAL_STRING(lp_setprimarygroup_script, &Globals.szSetPrimaryGroupScript)
+
+FN_GLOBAL_STRING(lp_addmachine_script, &Globals.szAddMachineScript)
+
+FN_GLOBAL_STRING(lp_shutdown_script, &Globals.szShutdownScript)
+FN_GLOBAL_STRING(lp_abort_shutdown_script, &Globals.szAbortShutdownScript)
+
+FN_GLOBAL_STRING(lp_wins_hook, &Globals.szWINSHook)
+FN_GLOBAL_STRING(lp_wins_partners, &Globals.szWINSPartners)
+FN_GLOBAL_STRING(lp_template_homedir, &Globals.szTemplateHomedir)
+FN_GLOBAL_STRING(lp_template_shell, &Globals.szTemplateShell)
+FN_GLOBAL_CONST_STRING(lp_winbind_separator, &Globals.szWinbindSeparator)
+FN_GLOBAL_STRING(lp_acl_compatibility, &Globals.szAclCompat)
+FN_GLOBAL_BOOL(lp_winbind_enum_users, &Globals.bWinbindEnumUsers)
+FN_GLOBAL_BOOL(lp_winbind_enum_groups, &Globals.bWinbindEnumGroups)
+FN_GLOBAL_BOOL(lp_winbind_use_default_domain, &Globals.bWinbindUseDefaultDomain)
+FN_GLOBAL_STRING(lp_idmap_backend, &Globals.szIDMapBackend)
+
+#ifdef WITH_LDAP_SAMCONFIG
+FN_GLOBAL_STRING(lp_ldap_server, &Globals.szLdapServer)
+FN_GLOBAL_INTEGER(lp_ldap_port, &Globals.ldap_port)
+#endif
+FN_GLOBAL_STRING(lp_ldap_suffix, &Globals.szLdapSuffix)
+FN_GLOBAL_STRING(lp_ldap_machine_suffix, &Globals.szLdapMachineSuffix)
+FN_GLOBAL_STRING(lp_ldap_user_suffix, &Globals.szLdapUserSuffix)
+FN_GLOBAL_STRING(lp_ldap_filter, &Globals.szLdapFilter)
+FN_GLOBAL_STRING(lp_ldap_admin_dn, &Globals.szLdapAdminDn)
+FN_GLOBAL_INTEGER(lp_ldap_ssl, &Globals.ldap_ssl)
+FN_GLOBAL_INTEGER(lp_ldap_passwd_sync, &Globals.ldap_passwd_sync)
+FN_GLOBAL_BOOL(lp_ldap_trust_ids, &Globals.ldap_trust_ids)
+FN_GLOBAL_STRING(lp_add_share_cmd, &Globals.szAddShareCommand)
+FN_GLOBAL_STRING(lp_change_share_cmd, &Globals.szChangeShareCommand)
+FN_GLOBAL_STRING(lp_delete_share_cmd, &Globals.szDeleteShareCommand)
+
+FN_GLOBAL_BOOL(lp_disable_netbios, &Globals.bDisableNetbios)
+FN_GLOBAL_BOOL(lp_ms_add_printer_wizard, &Globals.bMsAddPrinterWizard)
+FN_GLOBAL_BOOL(lp_dns_proxy, &Globals.bDNSproxy)
+FN_GLOBAL_BOOL(lp_wins_support, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_we_are_a_wins_server, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_wins_proxy, &Globals.bWINSproxy)
+FN_GLOBAL_BOOL(lp_local_master, &Globals.bLocalMaster)
+FN_GLOBAL_BOOL(lp_domain_logons, &Globals.bDomainLogons)
+FN_GLOBAL_BOOL(lp_load_printers, &Globals.bLoadPrinters)
+FN_GLOBAL_BOOL(lp_readprediction, &Globals.bReadPrediction)
+FN_GLOBAL_BOOL(lp_readbmpx, &Globals.bReadbmpx)
+FN_GLOBAL_BOOL(lp_readraw, &Globals.bReadRaw)
+FN_GLOBAL_BOOL(lp_large_readwrite, &Globals.bLargeReadwrite)
+FN_GLOBAL_BOOL(lp_writeraw, &Globals.bWriteRaw)
+FN_GLOBAL_BOOL(lp_null_passwords, &Globals.bNullPasswords)
+FN_GLOBAL_BOOL(lp_obey_pam_restrictions, &Globals.bObeyPamRestrictions)
+FN_GLOBAL_BOOL(lp_strip_dot, &Globals.bStripDot)
+FN_GLOBAL_BOOL(lp_encrypted_passwords, &Globals.bEncryptPasswords)
+FN_GLOBAL_BOOL(lp_update_encrypted, &Globals.bUpdateEncrypt)
+FN_GLOBAL_BOOL(lp_syslog_only, &Globals.bSyslogOnly)
+FN_GLOBAL_BOOL(lp_timestamp_logs, &Globals.bTimestampLogs)
+FN_GLOBAL_BOOL(lp_debug_hires_timestamp, &Globals.bDebugHiresTimestamp)
+FN_GLOBAL_BOOL(lp_debug_pid, &Globals.bDebugPid)
+FN_GLOBAL_BOOL(lp_debug_uid, &Globals.bDebugUid)
+FN_GLOBAL_BOOL(lp_browse_list, &Globals.bBrowseList)
+FN_GLOBAL_BOOL(lp_nis_home_map, &Globals.bNISHomeMap)
+static FN_GLOBAL_BOOL(lp_time_server, &Globals.bTimeServer)
+FN_GLOBAL_BOOL(lp_bind_interfaces_only, &Globals.bBindInterfacesOnly)
+FN_GLOBAL_BOOL(lp_pam_password_change, &Globals.bPamPasswordChange)
+FN_GLOBAL_BOOL(lp_unix_password_sync, &Globals.bUnixPasswdSync)
+FN_GLOBAL_BOOL(lp_passwd_chat_debug, &Globals.bPasswdChatDebug)
+FN_GLOBAL_BOOL(lp_unicode, &Globals.bUnicode)
+FN_GLOBAL_BOOL(lp_nt_pipe_support, &Globals.bNTPipeSupport)
+FN_GLOBAL_BOOL(lp_nt_status_support, &Globals.bNTStatusSupport)
+FN_GLOBAL_BOOL(lp_stat_cache, &Globals.bStatCache)
+FN_GLOBAL_BOOL(lp_allow_trusted_domains, &Globals.bAllowTrustedDomains)
+FN_GLOBAL_INTEGER(lp_restrict_anonymous, &Globals.restrict_anonymous)
+FN_GLOBAL_BOOL(lp_lanman_auth, &Globals.bLanmanAuth)
+FN_GLOBAL_BOOL(lp_ntlm_auth, &Globals.bNTLMAuth)
+FN_GLOBAL_BOOL(lp_client_lanman_auth, &Globals.bClientLanManAuth)
+FN_GLOBAL_BOOL(lp_client_ntlmv2_auth, &Globals.bClientNTLMv2Auth)
+FN_GLOBAL_BOOL(lp_host_msdfs, &Globals.bHostMSDfs)
+FN_GLOBAL_BOOL(lp_kernel_oplocks, &Globals.bKernelOplocks)
+FN_GLOBAL_BOOL(lp_enhanced_browsing, &Globals.enhanced_browsing)
+FN_GLOBAL_BOOL(lp_use_mmap, &Globals.bUseMmap)
+FN_GLOBAL_BOOL(lp_unix_extensions, &Globals.bUnixExtensions)
+FN_GLOBAL_BOOL(lp_use_spnego, &Globals.bUseSpnego)
+FN_GLOBAL_BOOL(lp_client_use_spnego, &Globals.bClientUseSpnego)
+FN_GLOBAL_BOOL(lp_hostname_lookups, &Globals.bHostnameLookups)
+FN_GLOBAL_BOOL(lp_kernel_change_notify, &Globals.bKernelChangeNotify)
+FN_GLOBAL_INTEGER(lp_os_level, &Globals.os_level)
+FN_GLOBAL_INTEGER(lp_max_ttl, &Globals.max_ttl)
+FN_GLOBAL_INTEGER(lp_max_wins_ttl, &Globals.max_wins_ttl)
+FN_GLOBAL_INTEGER(lp_min_wins_ttl, &Globals.min_wins_ttl)
+FN_GLOBAL_INTEGER(lp_max_log_size, &Globals.max_log_size)
+FN_GLOBAL_INTEGER(lp_max_open_files, &Globals.max_open_files)
+FN_GLOBAL_INTEGER(lp_maxxmit, &Globals.max_xmit)
+FN_GLOBAL_INTEGER(lp_time_offset, &Globals.time_offset)
+FN_GLOBAL_INTEGER(lp_maxmux, &Globals.max_mux)
+FN_GLOBAL_INTEGER(lp_passwordlevel, &Globals.pwordlevel)
+FN_GLOBAL_INTEGER(lp_usernamelevel, &Globals.unamelevel)
+FN_GLOBAL_INTEGER(lp_readsize, &Globals.ReadSize)
+FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime)
+FN_GLOBAL_INTEGER(lp_keepalive, &Globals.keepalive)
+FN_GLOBAL_INTEGER(lp_maxprotocol, &Globals.maxprotocol)
+FN_GLOBAL_INTEGER(lp_minprotocol, &Globals.minprotocol)
+FN_GLOBAL_INTEGER(lp_security, &Globals.security)
+FN_GLOBAL_LIST(lp_auth_methods, &Globals.AuthMethods)
+FN_GLOBAL_BOOL(lp_paranoid_server_security, &Globals.paranoid_server_security)
+FN_GLOBAL_INTEGER(lp_maxdisksize, &Globals.maxdisksize)
+FN_GLOBAL_INTEGER(lp_lpqcachetime, &Globals.lpqcachetime)
+FN_GLOBAL_INTEGER(lp_max_smbd_processes, &Globals.iMaxSmbdProcesses)
+FN_GLOBAL_INTEGER(lp_disable_spoolss, &Globals.bDisableSpoolss)
+FN_GLOBAL_INTEGER(lp_totalprintjobs, &Globals.iTotalPrintJobs)
+FN_GLOBAL_INTEGER(lp_syslog, &Globals.syslog)
+static FN_GLOBAL_INTEGER(lp_announce_as, &Globals.announce_as)
+FN_GLOBAL_INTEGER(lp_lm_announce, &Globals.lm_announce)
+FN_GLOBAL_INTEGER(lp_lm_interval, &Globals.lm_interval)
+FN_GLOBAL_INTEGER(lp_machine_password_timeout, &Globals.machine_password_timeout)
+FN_GLOBAL_INTEGER(lp_change_notify_timeout, &Globals.change_notify_timeout)
+FN_GLOBAL_INTEGER(lp_stat_cache_size, &Globals.stat_cache_size)
+FN_GLOBAL_INTEGER(lp_map_to_guest, &Globals.map_to_guest)
+FN_GLOBAL_INTEGER(lp_min_passwd_length, &Globals.min_passwd_length)
+FN_GLOBAL_INTEGER(lp_oplock_break_wait_time, &Globals.oplock_break_wait_time)
+FN_GLOBAL_INTEGER(lp_lock_spin_count, &Globals.iLockSpinCount)
+FN_GLOBAL_INTEGER(lp_lock_sleep_time, &Globals.iLockSpinTime)
+FN_LOCAL_STRING(lp_preexec, szPreExec)
+FN_LOCAL_STRING(lp_postexec, szPostExec)
+FN_LOCAL_STRING(lp_rootpreexec, szRootPreExec)
+FN_LOCAL_STRING(lp_rootpostexec, szRootPostExec)
+FN_LOCAL_STRING(lp_servicename, szService)
+FN_LOCAL_CONST_STRING(lp_const_servicename, szService)
+FN_LOCAL_STRING(lp_pathname, szPath)
+FN_LOCAL_STRING(lp_dontdescend, szDontdescend)
+FN_LOCAL_STRING(lp_username, szUsername)
+FN_LOCAL_LIST(lp_invalid_users, szInvalidUsers)
+FN_LOCAL_LIST(lp_valid_users, szValidUsers)
+FN_LOCAL_LIST(lp_admin_users, szAdminUsers)
+FN_LOCAL_STRING(lp_printcommand, szPrintcommand)
+FN_LOCAL_STRING(lp_lpqcommand, szLpqcommand)
+FN_LOCAL_STRING(lp_lprmcommand, szLprmcommand)
+FN_LOCAL_STRING(lp_lppausecommand, szLppausecommand)
+FN_LOCAL_STRING(lp_lpresumecommand, szLpresumecommand)
+FN_LOCAL_STRING(lp_queuepausecommand, szQueuepausecommand)
+FN_LOCAL_STRING(lp_queueresumecommand, szQueueresumecommand)
+static FN_LOCAL_STRING(_lp_printername, szPrintername)
+FN_LOCAL_LIST(lp_hostsallow, szHostsallow)
+FN_LOCAL_LIST(lp_hostsdeny, szHostsdeny)
+FN_LOCAL_STRING(lp_magicscript, szMagicScript)
+FN_LOCAL_STRING(lp_magicoutput, szMagicOutput)
+FN_LOCAL_STRING(lp_comment, comment)
+FN_LOCAL_STRING(lp_force_user, force_user)
+FN_LOCAL_STRING(lp_force_group, force_group)
+FN_LOCAL_LIST(lp_readlist, readlist)
+FN_LOCAL_LIST(lp_writelist, writelist)
+FN_LOCAL_LIST(lp_printer_admin, printer_admin)
+FN_LOCAL_STRING(lp_fstype, fstype)
+FN_LOCAL_STRING(lp_vfsobj, szVfsObjectFile)
+FN_LOCAL_STRING(lp_vfs_options, szVfsOptions)
+FN_LOCAL_STRING(lp_vfs_path, szVfsPath)
+FN_LOCAL_STRING(lp_msdfs_proxy, szMSDfsProxy)
+static FN_LOCAL_STRING(lp_volume, volume)
+FN_LOCAL_STRING(lp_mangled_map, szMangledMap)
+FN_LOCAL_STRING(lp_veto_files, szVetoFiles)
+FN_LOCAL_STRING(lp_hide_files, szHideFiles)
+FN_LOCAL_STRING(lp_veto_oplocks, szVetoOplockFiles)
+FN_LOCAL_STRING(lp_ntvfs_handler, ntvfs_handler)
+FN_LOCAL_BOOL(lp_msdfs_root, bMSDfsRoot)
+FN_LOCAL_BOOL(lp_autoloaded, autoloaded)
+FN_LOCAL_BOOL(lp_preexec_close, bPreexecClose)
+FN_LOCAL_BOOL(lp_rootpreexec_close, bRootpreexecClose)
+FN_LOCAL_BOOL(lp_casesensitive, bCaseSensitive)
+FN_LOCAL_BOOL(lp_preservecase, bCasePreserve)
+FN_LOCAL_BOOL(lp_shortpreservecase, bShortCasePreserve)
+FN_LOCAL_BOOL(lp_casemangle, bCaseMangle)
+FN_LOCAL_BOOL(lp_hide_dot_files, bHideDotFiles)
+FN_LOCAL_BOOL(lp_hide_special_files, bHideSpecialFiles)
+FN_LOCAL_BOOL(lp_hideunreadable, bHideUnReadable)
+FN_LOCAL_BOOL(lp_hideunwriteable_files, bHideUnWriteableFiles)
+FN_LOCAL_BOOL(lp_browseable, bBrowseable)
+FN_LOCAL_BOOL(lp_readonly, bRead_only)
+FN_LOCAL_BOOL(lp_no_set_dir, bNo_set_dir)
+FN_LOCAL_BOOL(lp_guest_ok, bGuest_ok)
+FN_LOCAL_BOOL(lp_guest_only, bGuest_only)
+FN_LOCAL_BOOL(lp_print_ok, bPrint_ok)
+FN_LOCAL_BOOL(lp_map_hidden, bMap_hidden)
+FN_LOCAL_BOOL(lp_map_archive, bMap_archive)
+FN_LOCAL_BOOL(lp_locking, bLocking)
+FN_LOCAL_BOOL(lp_strict_locking, bStrictLocking)
+FN_LOCAL_BOOL(lp_posix_locking, bPosixLocking)
+FN_LOCAL_BOOL(lp_share_modes, bShareModes)
+FN_LOCAL_BOOL(lp_oplocks, bOpLocks)
+FN_LOCAL_BOOL(lp_level2_oplocks, bLevel2OpLocks)
+FN_LOCAL_BOOL(lp_onlyuser, bOnlyUser)
+FN_LOCAL_BOOL(lp_manglednames, bMangledNames)
+FN_LOCAL_BOOL(lp_widelinks, bWidelinks)
+FN_LOCAL_BOOL(lp_symlinks, bSymlinks)
+FN_LOCAL_BOOL(lp_syncalways, bSyncAlways)
+FN_LOCAL_BOOL(lp_strict_allocate, bStrictAllocate)
+FN_LOCAL_BOOL(lp_strict_sync, bStrictSync)
+FN_LOCAL_BOOL(lp_map_system, bMap_system)
+FN_LOCAL_BOOL(lp_delete_readonly, bDeleteReadonly)
+FN_LOCAL_BOOL(lp_fake_oplocks, bFakeOplocks)
+FN_LOCAL_BOOL(lp_recursive_veto_delete, bDeleteVetoFiles)
+FN_LOCAL_BOOL(lp_dos_filemode, bDosFilemode)
+FN_LOCAL_BOOL(lp_dos_filetimes, bDosFiletimes)
+FN_LOCAL_BOOL(lp_dos_filetime_resolution, bDosFiletimeResolution)
+FN_LOCAL_BOOL(lp_fake_dir_create_times, bFakeDirCreateTimes)
+FN_LOCAL_BOOL(lp_blocking_locks, bBlockingLocks)
+FN_LOCAL_BOOL(lp_inherit_perms, bInheritPerms)
+FN_LOCAL_BOOL(lp_inherit_acls, bInheritACLS)
+FN_LOCAL_BOOL(lp_use_client_driver, bUseClientDriver)
+FN_LOCAL_BOOL(lp_default_devmode, bDefaultDevmode)
+FN_LOCAL_BOOL(lp_nt_acl_support, bNTAclSupport)
+FN_LOCAL_BOOL(lp_use_sendfile, bUseSendfile)
+FN_LOCAL_BOOL(lp_profile_acls, bProfileAcls)
+FN_LOCAL_INTEGER(lp_create_mask, iCreate_mask)
+FN_LOCAL_INTEGER(lp_force_create_mode, iCreate_force_mode)
+FN_LOCAL_INTEGER(lp_security_mask, iSecurity_mask)
+FN_LOCAL_INTEGER(lp_force_security_mode, iSecurity_force_mode)
+FN_LOCAL_INTEGER(lp_dir_mask, iDir_mask)
+FN_LOCAL_INTEGER(lp_force_dir_mode, iDir_force_mode)
+FN_LOCAL_INTEGER(lp_dir_security_mask, iDir_Security_mask)
+FN_LOCAL_INTEGER(lp_force_dir_security_mode, iDir_Security_force_mode)
+FN_LOCAL_INTEGER(lp_max_connections, iMaxConnections)
+FN_LOCAL_INTEGER(lp_defaultcase, iDefaultCase)
+FN_LOCAL_INTEGER(lp_minprintspace, iMinPrintSpace)
+FN_LOCAL_INTEGER(lp_printing, iPrinting)
+FN_LOCAL_INTEGER(lp_max_reported_jobs, iMaxReportedPrintJobs)
+FN_LOCAL_INTEGER(lp_oplock_contention_limit, iOplockContentionLimit)
+FN_LOCAL_INTEGER(lp_csc_policy, iCSCPolicy)
+FN_LOCAL_INTEGER(lp_write_cache_size, iWriteCacheSize)
+FN_LOCAL_INTEGER(lp_block_size, iBlock_size)
+FN_LOCAL_CHAR(lp_magicchar, magic_char)
+FN_GLOBAL_INTEGER(lp_winbind_cache_time, &Globals.winbind_cache_time)
+FN_GLOBAL_BOOL(lp_hide_local_users, &Globals.bHideLocalUsers)
+FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase)
+FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout)
+FN_GLOBAL_BOOL(lp_client_signing, &Globals.client_signing)
+
+/* local prototypes */
+
+static int map_parameter(const char *pszParmName);
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue);
+static int getservicebyname(const char *pszServiceName,
+                           service * pserviceDest);
+static void copy_service(service * pserviceDest,
+                        service * pserviceSource, BOOL *pcopymapDest);
+static BOOL service_ok(int iService);
+static BOOL do_section(const char *pszSectionName);
+static void init_copymap(service * pservice);
+
+/* This is a helper function for parametrical options support. */
+/* It returns a pointer to parametrical option value if it exists or NULL otherwise */
+/* Actual parametrical functions are quite simple */
+static const char *get_parametrics(int lookup_service, const char *type, const char *option)
+{
+       char *vfskey;
+        struct param_opt *data;
+       
+       if (lookup_service >= iNumServices) return NULL;
+       
+       data = (lookup_service < 0) ? 
+               Globals.param_opt : ServicePtrs[lookup_service]->param_opt;
+    
+       asprintf(&vfskey, "%s:%s", type, option);
+       strlower(vfskey);
+
+       while (data) {
+               if (strcmp(data->key, vfskey) == 0) {
+                       free(vfskey);
+                       return data->value;
+               }
+               data = data->next;
+       }
+
+       if (lookup_service >= 0) {
+               /* Try to fetch the same option but from globals */
+               /* but only if we are not already working with Globals */
+               data = Globals.param_opt;
+               while (data) {
+                       if (strcmp(data->key, vfskey) == 0) {
+                               free(vfskey);
+                               return data->value;
+                       }
+                       data = data->next;
+               }
+       }
+
+       free(vfskey);
+       
+       return NULL;
+}
+
+
+/*******************************************************************
+convenience routine to return int parameters.
+********************************************************************/
+static int lp_int(const char *s)
+{
+
+       if (!s) {
+               DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+               return (-1);
+       }
+
+       return atoi(s); 
+}
+
+/*******************************************************************
+convenience routine to return unsigned long parameters.
+********************************************************************/
+static int lp_ulong(const char *s)
+{
+
+       if (!s) {
+               DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+               return (-1);
+       }
+
+       return strtoul(s, NULL, 10);
+}
+
+/*******************************************************************
+convenience routine to return boolean parameters.
+********************************************************************/
+static BOOL lp_bool(const char *s)
+{
+       BOOL ret = False;
+
+       if (!s) {
+               DEBUG(0,("lp_bool(%s): is called with NULL!\n",s));
+               return False;
+       }
+       
+       if (!set_boolean(&ret,s)) {
+               DEBUG(0,("lp_bool(%s): value is not boolean!\n",s));
+               return False;
+       }
+
+       return ret;
+}
+
+/*******************************************************************
+convenience routine to return enum parameters.
+********************************************************************/
+static int lp_enum(const char *s,const struct enum_list *_enum)
+{
+       int i;
+
+       if (!s || !_enum) {
+               DEBUG(0,("lp_enum(%s,enum): is called with NULL!\n",s));
+               return False;
+       }
+       
+       for (i=0; _enum[i].name; i++) {
+               if (strcasecmp(_enum[i].name,s)==0)
+                       return _enum[i].value;
+       }
+
+       DEBUG(0,("lp_enum(%s,enum): value is not in enum_list!\n",s));
+       return (-1);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+/* Returned value is allocated in 'lp_talloc' context */
+
+char *lp_parm_string(int lookup_service, const char *type, const char *option)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return lp_string(value);
+
+       return NULL;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+/* Returned value is allocated in 'lp_talloc' context */
+
+char **lp_parm_string_list(int lookup_service, const char *type, const char *option,
+                          const char *separator)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return str_list_make(value, separator);
+
+       return NULL;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+int lp_parm_int(int lookup_service, const char *type, const char *option)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return lp_int(value);
+
+       return (-1);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+unsigned long lp_parm_ulong(int lookup_service, const char *type, const char *option)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return lp_ulong(value);
+
+       return (0);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+BOOL lp_parm_bool(int lookup_service, const char *type, const char *option, BOOL default_v)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return lp_bool(value);
+
+       return default_v;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+int lp_parm_enum(int lookup_service, const char *type, const char *option,
+                const struct enum_list *_enum)
+{
+       const char *value = get_parametrics(lookup_service, type, option);
+       
+       if (value)
+               return lp_enum(value, _enum);
+
+       return (-1);
+}
+
+
+/***************************************************************************
+ Initialise a service to the defaults.
+***************************************************************************/
+
+static void init_service(service * pservice)
+{
+       memset((char *)pservice, '\0', sizeof(service));
+       copy_service(pservice, &sDefault, NULL);
+}
+
+/***************************************************************************
+ Free the dynamically allocated parts of a service struct.
+***************************************************************************/
+
+static void free_service(service *pservice)
+{
+       int i;
+        struct param_opt *data, *pdata;
+       if (!pservice)
+               return;
+
+       if (pservice->szService)
+               DEBUG(5, ("free_service: Freeing service %s\n",
+                      pservice->szService));
+
+       string_free(&pservice->szService);
+       SAFE_FREE(pservice->copymap);
+
+       for (i = 0; parm_table[i].label; i++) {
+               if ((parm_table[i].type == P_STRING ||
+                    parm_table[i].type == P_USTRING) &&
+                   parm_table[i].class == P_LOCAL)
+                       string_free((char **)
+                                   (((char *)pservice) +
+                                    PTR_DIFF(parm_table[i].ptr, &sDefault)));
+               else if (parm_table[i].type == P_LIST &&
+                        parm_table[i].class == P_LOCAL)
+                            str_list_free((char ***)
+                                           (((char *)pservice) +
+                                            PTR_DIFF(parm_table[i].ptr, &sDefault)));
+       }
+                               
+       DEBUG(5,("Freeing parametrics:\n"));
+       data = pservice->param_opt;
+       while (data) {
+               DEBUG(5,("[%s = %s]\n", data->key, data->value));
+               string_free(&data->key);
+               string_free(&data->value);
+               pdata = data->next;
+               SAFE_FREE(data);
+               data = pdata;
+       }
+
+       ZERO_STRUCTP(pservice);
+}
+
+/***************************************************************************
+ Add a new service to the services array initialising it with the given 
+ service. 
+***************************************************************************/
+
+static int add_a_service(const service *pservice, const char *name)
+{
+       int i;
+       service tservice;
+       int num_to_alloc = iNumServices + 1;
+       struct param_opt *data, *pdata;
+
+       tservice = *pservice;
+
+       /* it might already exist */
+       if (name) {
+               i = getservicebyname(name, NULL);
+               if (i >= 0) {
+                       /* Clean all parametric options for service */
+                       /* They will be added during parsing again */
+                       data = ServicePtrs[i]->param_opt;
+                       while (data) {
+                               string_free(&data->key);
+                               string_free(&data->value);
+                               pdata = data->next;
+                               SAFE_FREE(data);
+                               data = pdata;
+                       }
+                       ServicePtrs[i]->param_opt = NULL;
+                       return (i);
+               }
+       }
+
+       /* find an invalid one */
+       for (i = 0; i < iNumServices; i++)
+               if (!ServicePtrs[i]->valid)
+                       break;
+
+       /* if not, then create one */
+       if (i == iNumServices) {
+               service **tsp;
+               
+               tsp = (service **) Realloc(ServicePtrs,
+                                          sizeof(service *) *
+                                          num_to_alloc);
+                                          
+               if (!tsp) {
+                       DEBUG(0,("add_a_service: failed to enlarge ServicePtrs!\n"));
+                       return (-1);
+               }
+               else {
+                       ServicePtrs = tsp;
+                       ServicePtrs[iNumServices] =
+                               (service *) malloc(sizeof(service));
+               }
+               if (!ServicePtrs[iNumServices]) {
+                       DEBUG(0,("add_a_service: out of memory!\n"));
+                       return (-1);
+               }
+
+               iNumServices++;
+       } else
+               free_service(ServicePtrs[i]);
+
+       ServicePtrs[i]->valid = True;
+
+       init_service(ServicePtrs[i]);
+       copy_service(ServicePtrs[i], &tservice, NULL);
+       if (name)
+               string_set(&ServicePtrs[i]->szService, name);
+       return (i);
+}
+
+/***************************************************************************
+ Add a new home service, with the specified home directory, defaults coming 
+ from service ifrom.
+***************************************************************************/
+
+BOOL lp_add_home(const char *pszHomename, int iDefaultService, 
+                const char *user, const char *pszHomedir)
+{
+       int i;
+       pstring newHomedir;
+
+       i = add_a_service(ServicePtrs[iDefaultService], pszHomename);
+
+       if (i < 0)
+               return (False);
+
+       if (!(*(ServicePtrs[iDefaultService]->szPath))
+           || strequal(ServicePtrs[iDefaultService]->szPath, lp_pathname(-1))) {
+               pstrcpy(newHomedir, pszHomedir);
+       } else {
+               pstrcpy(newHomedir, lp_pathname(iDefaultService));
+               string_sub(newHomedir,"%H", pszHomedir, sizeof(newHomedir)); 
+       }
+
+       string_set(&ServicePtrs[i]->szPath, newHomedir);
+
+       if (!(*(ServicePtrs[i]->comment))) {
+               pstring comment;
+               slprintf(comment, sizeof(comment) - 1,
+                        "Home directory of %s", user);
+               string_set(&ServicePtrs[i]->comment, comment);
+       }
+       ServicePtrs[i]->bAvailable = sDefault.bAvailable;
+       ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+       DEBUG(3, ("adding home's share [%s] for user '%s' at '%s'\n", pszHomename, 
+              user, newHomedir));
+       
+       return (True);
+}
+
+/***************************************************************************
+ Add a new service, based on an old one.
+***************************************************************************/
+
+int lp_add_service(const char *pszService, int iDefaultService)
+{
+       return (add_a_service(ServicePtrs[iDefaultService], pszService));
+}
+
+/***************************************************************************
+ Add the IPC service.
+***************************************************************************/
+
+static BOOL lp_add_ipc(const char *ipc_name, BOOL guest_ok)
+{
+       pstring comment;
+       int i = add_a_service(&sDefault, ipc_name);
+
+       if (i < 0)
+               return (False);
+
+       slprintf(comment, sizeof(comment) - 1,
+                "IPC Service (%s)", Globals.szServerString);
+
+       string_set(&ServicePtrs[i]->szPath, tmpdir());
+       string_set(&ServicePtrs[i]->szUsername, "");
+       string_set(&ServicePtrs[i]->comment, comment);
+       string_set(&ServicePtrs[i]->fstype, "IPC");
+       ServicePtrs[i]->iMaxConnections = 0;
+       ServicePtrs[i]->bAvailable = True;
+       ServicePtrs[i]->bRead_only = True;
+       ServicePtrs[i]->bGuest_only = False;
+       ServicePtrs[i]->bGuest_ok = guest_ok;
+       ServicePtrs[i]->bPrint_ok = False;
+       ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+       DEBUG(3, ("adding IPC service\n"));
+
+       return (True);
+}
+
+/***************************************************************************
+ Add a new printer service, with defaults coming from service iFrom.
+***************************************************************************/
+
+BOOL lp_add_printer(const char *pszPrintername, int iDefaultService)
+{
+       const char *comment = "From Printcap";
+       int i = add_a_service(ServicePtrs[iDefaultService], pszPrintername);
+
+       if (i < 0)
+               return (False);
+
+       /* note that we do NOT default the availability flag to True - */
+       /* we take it from the default service passed. This allows all */
+       /* dynamic printers to be disabled by disabling the [printers] */
+       /* entry (if/when the 'available' keyword is implemented!).    */
+
+       /* the printer name is set to the service name. */
+       string_set(&ServicePtrs[i]->szPrintername, pszPrintername);
+       string_set(&ServicePtrs[i]->comment, comment);
+       ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+       /* Printers cannot be read_only. */
+       ServicePtrs[i]->bRead_only = False;
+       /* No share modes on printer services. */
+       ServicePtrs[i]->bShareModes = False;
+       /* No oplocks on printer services. */
+       ServicePtrs[i]->bOpLocks = False;
+       /* Printer services must be printable. */
+       ServicePtrs[i]->bPrint_ok = True;
+
+       DEBUG(3, ("adding printer service %s\n", pszPrintername));
+
+       update_server_announce_as_printserver();
+
+       return (True);
+}
+
+/***************************************************************************
+ Map a parameter's string representation to something we can use. 
+ Returns False if the parameter string is not recognised, else TRUE.
+***************************************************************************/
+
+static int map_parameter(const char *pszParmName)
+{
+       int iIndex;
+
+       if (*pszParmName == '-')
+               return (-1);
+
+       for (iIndex = 0; parm_table[iIndex].label; iIndex++)
+               if (strwicmp(parm_table[iIndex].label, pszParmName) == 0)
+                       return (iIndex);
+
+       /* Warn only if it isn't parametric option */
+       if (strchr(pszParmName, ':') == NULL)
+               DEBUG(0, ("Unknown parameter encountered: \"%s\"\n", pszParmName));
+       /* We do return 'fail' for parametric options as well because they are
+          stored in different storage
+        */
+       return (-1);
+}
+
+/***************************************************************************
+ Set a boolean variable from the text value stored in the passed string.
+ Returns True in success, False if the passed string does not correctly 
+ represent a boolean.
+***************************************************************************/
+
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue)
+{
+       BOOL bRetval;
+
+       bRetval = True;
+       if (strwicmp(pszParmValue, "yes") == 0 ||
+           strwicmp(pszParmValue, "true") == 0 ||
+           strwicmp(pszParmValue, "1") == 0)
+               *pb = True;
+       else if (strwicmp(pszParmValue, "no") == 0 ||
+                   strwicmp(pszParmValue, "False") == 0 ||
+                   strwicmp(pszParmValue, "0") == 0)
+               *pb = False;
+       else {
+               DEBUG(0,
+                     ("ERROR: Badly formed boolean in configuration file: \"%s\".\n",
+                      pszParmValue));
+               bRetval = False;
+       }
+       return (bRetval);
+}
+
+/***************************************************************************
+Find a service by name. Otherwise works like get_service.
+***************************************************************************/
+
+static int getservicebyname(const char *pszServiceName, service * pserviceDest)
+{
+       int iService;
+
+       for (iService = iNumServices - 1; iService >= 0; iService--)
+               if (VALID(iService) &&
+                   strwicmp(ServicePtrs[iService]->szService, pszServiceName) == 0) {
+                       if (pserviceDest != NULL)
+                               copy_service(pserviceDest, ServicePtrs[iService], NULL);
+                       break;
+               }
+
+       return (iService);
+}
+
+/***************************************************************************
+ Copy a service structure to another.
+ If pcopymapDest is NULL then copy all fields
+***************************************************************************/
+
+static void copy_service(service * pserviceDest, service * pserviceSource, BOOL *pcopymapDest)
+{
+       int i;
+       BOOL bcopyall = (pcopymapDest == NULL);
+       struct param_opt *data, *pdata, *paramo;
+       BOOL not_added;
+
+       for (i = 0; parm_table[i].label; i++)
+               if (parm_table[i].ptr && parm_table[i].class == P_LOCAL &&
+                   (bcopyall || pcopymapDest[i])) {
+                       void *def_ptr = parm_table[i].ptr;
+                       void *src_ptr =
+                               ((char *)pserviceSource) + PTR_DIFF(def_ptr,
+                                                                   &sDefault);
+                       void *dest_ptr =
+                               ((char *)pserviceDest) + PTR_DIFF(def_ptr,
+                                                                 &sDefault);
+
+                       switch (parm_table[i].type) {
+                               case P_BOOL:
+                               case P_BOOLREV:
+                                       *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
+                                       break;
+
+                               case P_INTEGER:
+                               case P_ENUM:
+                               case P_OCTAL:
+                                       *(int *)dest_ptr = *(int *)src_ptr;
+                                       break;
+
+                               case P_CHAR:
+                                       *(char *)dest_ptr = *(char *)src_ptr;
+                                       break;
+
+                               case P_STRING:
+                                       string_set(dest_ptr,
+                                                  *(char **)src_ptr);
+                                       break;
+
+                               case P_USTRING:
+                                       string_set(dest_ptr,
+                                                  *(char **)src_ptr);
+                                       strupper(*(char **)dest_ptr);
+                                       break;
+                               case P_LIST:
+                                       str_list_copy((char ***)dest_ptr, *(const char ***)src_ptr);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+
+       if (bcopyall) {
+               init_copymap(pserviceDest);
+               if (pserviceSource->copymap)
+                       memcpy((void *)pserviceDest->copymap,
+                              (void *)pserviceSource->copymap,
+                              sizeof(BOOL) * NUMPARAMETERS);
+       }
+       
+       data = pserviceSource->param_opt;
+       while (data) {
+               not_added = True;
+               pdata = pserviceDest->param_opt;
+               /* Traverse destination */
+               while (pdata) {
+                       /* If we already have same option, override it */
+                       if (strcmp(pdata->key, data->key) == 0) {
+                               string_free(&pdata->value);
+                               pdata->value = strdup(data->value);
+                               not_added = False;
+                               break;
+                       }
+                       pdata = pdata->next;
+               }
+               if (not_added) {
+                       paramo = smb_xmalloc(sizeof(*paramo));
+                       paramo->key = strdup(data->key);
+                       paramo->value = strdup(data->value);
+                       DLIST_ADD(pserviceDest->param_opt, paramo);
+               }
+               data = data->next;
+       }
+}
+
+/***************************************************************************
+Check a service for consistency. Return False if the service is in any way
+incomplete or faulty, else True.
+***************************************************************************/
+
+static BOOL service_ok(int iService)
+{
+       BOOL bRetval;
+
+       bRetval = True;
+       if (ServicePtrs[iService]->szService[0] == '\0') {
+               DEBUG(0, ("The following message indicates an internal error:\n"));
+               DEBUG(0, ("No service name in service entry.\n"));
+               bRetval = False;
+       }
+
+       /* The [printers] entry MUST be printable. I'm all for flexibility, but */
+       /* I can't see why you'd want a non-printable printer service...        */
+       if (strwicmp(ServicePtrs[iService]->szService, PRINTERS_NAME) == 0) {
+               if (!ServicePtrs[iService]->bPrint_ok) {
+                       DEBUG(0, ("WARNING: [%s] service MUST be printable!\n",
+                              ServicePtrs[iService]->szService));
+                       ServicePtrs[iService]->bPrint_ok = True;
+               }
+               /* [printers] service must also be non-browsable. */
+               if (ServicePtrs[iService]->bBrowseable)
+                       ServicePtrs[iService]->bBrowseable = False;
+       }
+
+       if (ServicePtrs[iService]->szPath[0] == '\0' &&
+           strwicmp(ServicePtrs[iService]->szService, HOMES_NAME) != 0) {
+               DEBUG(0, ("No path in service %s - using %s\n",
+                      ServicePtrs[iService]->szService, tmpdir()));
+               string_set(&ServicePtrs[iService]->szPath, tmpdir());
+       }
+
+       /* If a service is flagged unavailable, log the fact at level 0. */
+       if (!ServicePtrs[iService]->bAvailable)
+               DEBUG(1, ("NOTE: Service %s is flagged unavailable.\n",
+                         ServicePtrs[iService]->szService));
+
+       return (bRetval);
+}
+
+static struct file_lists {
+       struct file_lists *next;
+       char *name;
+       char *subfname;
+       time_t modtime;
+} *file_lists = NULL;
+
+/*******************************************************************
+ Keep a linked list of all config files so we know when one has changed 
+ it's date and needs to be reloaded.
+********************************************************************/
+
+static void add_to_file_list(const char *fname, const char *subfname)
+{
+       struct file_lists *f = file_lists;
+
+       while (f) {
+               if (f->name && !strcmp(f->name, fname))
+                       break;
+               f = f->next;
+       }
+
+       if (!f) {
+               f = (struct file_lists *)malloc(sizeof(file_lists[0]));
+               if (!f)
+                       return;
+               f->next = file_lists;
+               f->name = strdup(fname);
+               if (!f->name) {
+                       SAFE_FREE(f);
+                       return;
+               }
+               f->subfname = strdup(subfname);
+               if (!f->subfname) {
+                       SAFE_FREE(f);
+                       return;
+               }
+               file_lists = f;
+               f->modtime = file_modtime(subfname);
+       } else {
+               time_t t = file_modtime(subfname);
+               if (t)
+                       f->modtime = t;
+       }
+}
+
+/*******************************************************************
+ Check if a config file has changed date.
+********************************************************************/
+
+BOOL lp_file_list_changed(void)
+{
+       struct file_lists *f = file_lists;
+       DEBUG(6, ("lp_file_list_changed()\n"));
+
+       while (f) {
+               pstring n2;
+               time_t mod_time;
+
+               pstrcpy(n2, f->name);
+               standard_sub_basic(n2,sizeof(n2));
+
+               DEBUGADD(6, ("file %s -> %s  last mod_time: %s\n",
+                            f->name, n2, ctime(&f->modtime)));
+
+               mod_time = file_modtime(n2);
+
+               if (mod_time && ((f->modtime != mod_time) || (f->subfname == NULL) || (strcmp(n2, f->subfname) != 0))) {
+                       DEBUGADD(6,
+                                ("file %s modified: %s\n", n2,
+                                 ctime(&mod_time)));
+                       f->modtime = mod_time;
+                       SAFE_FREE(f->subfname);
+                       f->subfname = strdup(n2);
+                       return (True);
+               }
+               f = f->next;
+       }
+       return (False);
+}
+
+
+/***************************************************************************
+ Do the work of sourcing in environment variable/value pairs.
+***************************************************************************/
+
+static BOOL source_env(char **lines)
+{
+       char *varval;
+       size_t len;
+       int i;
+       char *p;
+
+       for (i = 0; lines[i]; i++) {
+               char *line = lines[i];
+
+               if ((len = strlen(line)) == 0)
+                       continue;
+
+               if (line[len - 1] == '\n')
+                       line[--len] = '\0';
+
+               if ((varval = malloc(len + 1)) == NULL) {
+                       DEBUG(0, ("source_env: Not enough memory!\n"));
+                       return (False);
+               }
+
+               DEBUG(4, ("source_env: Adding to environment: %s\n", line));
+               strncpy(varval, line, len);
+               varval[len] = '\0';
+
+               p = strchr_m(line, (int)'=');
+               if (p == NULL) {
+                       DEBUG(4, ("source_env: missing '=': %s\n", line));
+                       continue;
+               }
+
+               if (putenv(varval)) {
+                       DEBUG(0, ("source_env: Failed to put environment variable %s\n",
+                              varval));
+                       continue;
+               }
+
+               *p = '\0';
+               p++;
+               DEBUG(4, ("source_env: getting var %s = %s\n", line, getenv(line)));
+       }
+
+       DEBUG(4, ("source_env: returning successfully\n"));
+       return (True);
+}
+
+/***************************************************************************
+ Handle the source environment operation.
+***************************************************************************/
+
+static BOOL handle_source_env(const char *pszParmValue, char **ptr)
+{
+       pstring fname;
+       char *p = fname;
+       BOOL result;
+       char **lines;
+
+       pstrcpy(fname, pszParmValue);
+
+       standard_sub_basic(fname,sizeof(fname));
+
+       string_set(ptr, pszParmValue);
+
+       DEBUG(4, ("handle_source_env: checking env type\n"));
+
+       /*
+        * Filename starting with '|' means popen and read from stdin.
+        */
+
+       if (*p == '|')
+               lines = file_lines_pload(p + 1, NULL);
+       else
+               lines = file_lines_load(fname, NULL);
+
+       if (!lines) {
+               DEBUG(0, ("handle_source_env: Failed to open file %s, Error was %s\n",
+                      fname, strerror(errno)));
+               return (False);
+       }
+
+       result = source_env(lines);
+       file_lines_free(lines);
+
+       return (result);
+}
+
+/***************************************************************************
+ Handle the interpretation of the vfs object parameter.
+*************************************************************************/
+
+static BOOL handle_vfs_object(const char *pszParmValue, char **ptr)
+{
+       /* Set string value */
+
+       string_set(ptr, pszParmValue);
+
+       /* Do any other initialisation required for vfs.  Note that
+          anything done here may have linking repercussions in nmbd. */
+
+       return True;
+}
+
+/***************************************************************************
+ Handle the include operation.
+***************************************************************************/
+
+static BOOL handle_include(const char *pszParmValue, char **ptr)
+{
+       pstring fname;
+       pstrcpy(fname, pszParmValue);
+
+       standard_sub_basic(fname,sizeof(fname));
+
+       add_to_file_list(pszParmValue, fname);
+
+       string_set(ptr, fname);
+
+       if (file_exist(fname, NULL))
+               return (pm_process(fname, do_section, do_parameter));
+
+       DEBUG(2, ("Can't find include file %s\n", fname));
+
+       return (False);
+}
+
+/***************************************************************************
+ Handle the interpretation of the copy parameter.
+***************************************************************************/
+
+static BOOL handle_copy(const char *pszParmValue, char **ptr)
+{
+       BOOL bRetval;
+       int iTemp;
+       service serviceTemp;
+
+       string_set(ptr, pszParmValue);
+
+       init_service(&serviceTemp);
+
+       bRetval = False;
+
+       DEBUG(3, ("Copying service from service %s\n", pszParmValue));
+
+       if ((iTemp = getservicebyname(pszParmValue, &serviceTemp)) >= 0) {
+               if (iTemp == iServiceIndex) {
+                       DEBUG(0, ("Can't copy service %s - unable to copy self!\n", pszParmValue));
+               } else {
+                       copy_service(ServicePtrs[iServiceIndex],
+                                    &serviceTemp,
+                                    ServicePtrs[iServiceIndex]->copymap);
+                       bRetval = True;
+               }
+       } else {
+               DEBUG(0, ("Unable to copy service - source not found: %s\n", pszParmValue));
+               bRetval = False;
+       }
+
+       free_service(&serviceTemp);
+       return (bRetval);
+}
+
+/***************************************************************************
+ Handle winbind/non unix account uid and gid allocation parameters.  The format of these
+ parameters is:
+
+ [global]
+
+        winbind uid = 1000-1999
+        winbind gid = 700-899
+
+ We only do simple parsing checks here.  The strings are parsed into useful
+ structures in the winbind daemon code.
+
+***************************************************************************/
+
+/* Some lp_ routines to return winbind [ug]id information */
+
+static uid_t winbind_uid_low, winbind_uid_high;
+static gid_t winbind_gid_low, winbind_gid_high;
+static uint32 non_unix_account_low, non_unix_account_high;
+
+BOOL lp_winbind_uid(uid_t *low, uid_t *high)
+{
+        if (winbind_uid_low == 0 || winbind_uid_high == 0)
+                return False;
+
+        if (low)
+                *low = winbind_uid_low;
+
+        if (high)
+                *high = winbind_uid_high;
+
+        return True;
+}
+
+BOOL lp_winbind_gid(gid_t *low, gid_t *high)
+{
+        if (winbind_gid_low == 0 || winbind_gid_high == 0)
+                return False;
+
+        if (low)
+                *low = winbind_gid_low;
+
+        if (high)
+                *high = winbind_gid_high;
+
+        return True;
+}
+
+BOOL lp_non_unix_account_range(uint32 *low, uint32 *high)
+{
+        if (non_unix_account_low == 0 || non_unix_account_high == 0)
+                return False;
+
+        if (low)
+                *low = non_unix_account_low;
+
+        if (high)
+                *high = non_unix_account_high;
+
+        return True;
+}
+
+/* Do some simple checks on "winbind [ug]id" parameter values */
+
+static BOOL handle_winbind_uid(const char *pszParmValue, char **ptr)
+{
+       uint32 low, high;
+
+       if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+               return False;
+
+       /* Parse OK */
+
+       string_set(ptr, pszParmValue);
+
+        winbind_uid_low = low;
+        winbind_uid_high = high;
+
+       return True;
+}
+
+static BOOL handle_winbind_gid(const char *pszParmValue, char **ptr)
+{
+       uint32 low, high;
+
+       if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+               return False;
+
+       /* Parse OK */
+
+       string_set(ptr, pszParmValue);
+
+        winbind_gid_low = low;
+        winbind_gid_high = high;
+
+       return True;
+}
+
+/***************************************************************************
+ Do some simple checks on "non unix account range" parameter values.
+***************************************************************************/
+
+static BOOL handle_non_unix_account_range(const char *pszParmValue, char **ptr)
+{
+       uint32 low, high;
+
+       if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+               return False;
+
+       /* Parse OK */
+
+       string_set(ptr, pszParmValue);
+
+        non_unix_account_low = low;
+        non_unix_account_high = high;
+
+       return True;
+}
+
+/***************************************************************************
+ Handle the ldap machine suffix option.
+***************************************************************************/
+
+static BOOL handle_ldap_machine_suffix( const char *pszParmValue, char **ptr)
+{
+       pstring suffix;
+       
+       pstrcpy(suffix, pszParmValue);
+
+       if (! *Globals.szLdapSuffix ) {
+               string_set( ptr, suffix );
+               return True;
+       }
+
+       if (! strstr(suffix, Globals.szLdapSuffix) ) {
+               if ( *pszParmValue )
+                       pstrcat(suffix, ",");
+               pstrcat(suffix, Globals.szLdapSuffix);
+       }
+       string_set( ptr, suffix );
+       return True;
+}
+
+/***************************************************************************
+ Handle the ldap user suffix option.
+***************************************************************************/
+
+static BOOL handle_ldap_user_suffix( const char *pszParmValue, char **ptr)
+{
+       pstring suffix;
+       
+       pstrcpy(suffix, pszParmValue);
+
+       if (! *Globals.szLdapSuffix ) {
+               string_set( ptr, suffix );
+               return True;
+       }
+       
+       if (! strstr(suffix, Globals.szLdapSuffix) ) {
+               if ( *pszParmValue )
+                       pstrcat(suffix, ",");
+               pstrcat(suffix, Globals.szLdapSuffix);
+       }
+       string_set( ptr, suffix );
+       return True;
+}
+
+/***************************************************************************
+ Handle setting ldap suffix and determines whether ldap machine suffix needs
+ to be set as well.
+***************************************************************************/
+
+static BOOL handle_ldap_suffix( const char *pszParmValue, char **ptr)
+{
+       pstring suffix;
+       pstring user_suffix;
+       pstring machine_suffix; 
+                 
+       pstrcpy(suffix, pszParmValue);
+
+       if (! *Globals.szLdapMachineSuffix )
+               string_set(&Globals.szLdapMachineSuffix, suffix);
+       if (! *Globals.szLdapUserSuffix ) 
+               string_set(&Globals.szLdapUserSuffix, suffix);
+         
+       if (! strstr(Globals.szLdapMachineSuffix, suffix)) {
+               pstrcpy(machine_suffix, Globals.szLdapMachineSuffix);
+               if ( *Globals.szLdapMachineSuffix )
+                       pstrcat(machine_suffix, ",");
+               pstrcat(machine_suffix, suffix);
+               string_set(&Globals.szLdapMachineSuffix, machine_suffix);       
+       }
+
+       if (! strstr(Globals.szLdapUserSuffix, suffix)) {
+               pstrcpy(user_suffix, Globals.szLdapUserSuffix);
+               if ( *Globals.szLdapUserSuffix )
+                       pstrcat(user_suffix, ",");
+               pstrcat(user_suffix, suffix);   
+               string_set(&Globals.szLdapUserSuffix, user_suffix);
+       } 
+
+       string_set(ptr, suffix); 
+
+       return True;
+}
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr)
+{
+       if (strequal(pszParmValue, "auto"))
+               string_set(ptr, "");
+       else if (strequal(pszParmValue, "winnt"))
+               string_set(ptr, "winnt");
+       else if (strequal(pszParmValue, "win2k"))
+               string_set(ptr, "win2k");
+       else
+               return False;
+
+       return True;
+}
+/***************************************************************************
+ Initialise a copymap.
+***************************************************************************/
+
+static void init_copymap(service * pservice)
+{
+       int i;
+       SAFE_FREE(pservice->copymap);
+       pservice->copymap = (BOOL *)malloc(sizeof(BOOL) * NUMPARAMETERS);
+       if (!pservice->copymap)
+               DEBUG(0,
+                     ("Couldn't allocate copymap!! (size %d)\n",
+                      (int)NUMPARAMETERS));
+       else
+               for (i = 0; i < NUMPARAMETERS; i++)
+                       pservice->copymap[i] = True;
+}
+
+/***************************************************************************
+ Return the local pointer to a parameter given the service number and the 
+ pointer into the default structure.
+***************************************************************************/
+
+void *lp_local_ptr(int snum, void *ptr)
+{
+       return (void *)(((char *)ServicePtrs[snum]) + PTR_DIFF(ptr, &sDefault));
+}
+
+
+/***************************************************************************
+ Process a parametric option
+***************************************************************************/
+static BOOL lp_do_parameter_parametric(int snum, const char *pszParmName, const char *pszParmValue, int flags)
+{
+       struct param_opt *paramo, *data;
+       char *name;
+
+       name = strdup(pszParmName);
+       if (!name) return False;
+
+       string_sub(name, " ", "", 0);
+       strlower(name);
+
+       if (snum < 0) {
+               data = Globals.param_opt;
+       } else {
+               data = ServicePtrs[snum]->param_opt;
+       }
+
+       /* Traverse destination */
+       for (paramo=data; paramo; paramo=paramo->next) {
+               /* If we already have the option set, override it unless
+                  it was a command line option and the new one isn't */
+               if (strcmp(paramo->key, name) == 0) {
+                       if ((paramo->flags & FLAG_CMDLINE) &&
+                           !(flags & FLAG_CMDLINE)) {
+                               return True;
+                       }
+
+                       free(paramo->value);
+                       paramo->value = strdup(pszParmValue);
+                       paramo->flags = flags;
+                       free(name);
+                       return True;
+               }
+       }
+
+       paramo = smb_xmalloc(sizeof(*paramo));
+       paramo->key = strdup(name);
+       paramo->value = strdup(pszParmValue);
+       paramo->flags = flags;
+       if (snum < 0) {
+               DLIST_ADD(Globals.param_opt, paramo);
+       } else {
+               DLIST_ADD(ServicePtrs[snum]->param_opt, paramo);
+       }
+
+       free(name);
+       
+       return True;
+}
+
+/***************************************************************************
+ Process a parameter for a particular service number. If snum < 0
+ then assume we are in the globals.
+***************************************************************************/
+BOOL lp_do_parameter(int snum, const char *pszParmName, const char *pszParmValue)
+{
+       int parmnum, i;
+       void *parm_ptr = NULL;  /* where we are going to store the result */
+       void *def_ptr = NULL;
+
+       parmnum = map_parameter(pszParmName);
+
+       if (parmnum < 0) {
+               if (strchr(pszParmName, ':')) {
+                       return lp_do_parameter_parametric(snum, pszParmName, pszParmValue, 0);
+               }
+               DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+               return (True);
+       }
+
+       if (parm_table[parmnum].flags & FLAG_DEPRECATED) {
+               DEBUG(1, ("WARNING: The \"%s\" option is deprecated\n",
+                         pszParmName));
+       }
+
+       /* if the flag has been set on the command line, then don't allow override,
+          but don't report an error */
+       if (parm_table[parmnum].flags & FLAG_CMDLINE) {
+               return True;
+       }
+
+       def_ptr = parm_table[parmnum].ptr;
+
+       /* we might point at a service, the default service or a global */
+       if (snum < 0) {
+               parm_ptr = def_ptr;
+       } else {
+               if (parm_table[parmnum].class == P_GLOBAL) {
+                       DEBUG(0,
+                             ("Global parameter %s found in service section!\n",
+                              pszParmName));
+                       return (True);
+               }
+               parm_ptr =
+                       ((char *)ServicePtrs[snum]) + PTR_DIFF(def_ptr,
+                                                           &sDefault);
+       }
+
+       if (snum >= 0) {
+               if (!ServicePtrs[snum]->copymap)
+                       init_copymap(ServicePtrs[snum]);
+
+               /* this handles the aliases - set the copymap for other entries with
+                  the same data pointer */
+               for (i = 0; parm_table[i].label; i++)
+                       if (parm_table[i].ptr == parm_table[parmnum].ptr)
+                               ServicePtrs[snum]->copymap[i] = False;
+       }
+
+       /* if it is a special case then go ahead */
+       if (parm_table[parmnum].special) {
+               parm_table[parmnum].special(pszParmValue, (char **)parm_ptr);
+               return (True);
+       }
+
+       /* now switch on the type of variable it is */
+       switch (parm_table[parmnum].type)
+       {
+               case P_BOOL:
+                       set_boolean(parm_ptr, pszParmValue);
+                       break;
+
+               case P_BOOLREV:
+                       set_boolean(parm_ptr, pszParmValue);
+                       *(BOOL *)parm_ptr = !*(BOOL *)parm_ptr;
+                       break;
+
+               case P_INTEGER:
+                       *(int *)parm_ptr = atoi(pszParmValue);
+                       break;
+
+               case P_CHAR:
+                       *(char *)parm_ptr = *pszParmValue;
+                       break;
+
+               case P_OCTAL:
+                       sscanf(pszParmValue, "%o", (int *)parm_ptr);
+                       break;
+
+               case P_LIST:
+                       *(char ***)parm_ptr = str_list_make(pszParmValue, NULL);
+                       break;
+
+               case P_STRING:
+                       string_set(parm_ptr, pszParmValue);
+                       break;
+
+               case P_USTRING:
+                       string_set(parm_ptr, pszParmValue);
+                       strupper(*(char **)parm_ptr);
+                       break;
+
+               case P_ENUM:
+                       for (i = 0; parm_table[parmnum].enum_list[i].name; i++) {
+                               if (strequal
+                                   (pszParmValue,
+                                    parm_table[parmnum].enum_list[i].name)) {
+                                       *(int *)parm_ptr =
+                                               parm_table[parmnum].
+                                               enum_list[i].value;
+                                       break;
+                               }
+                       }
+                       break;
+               case P_SEP:
+                       break;
+       }
+
+       return (True);
+}
+
+/***************************************************************************
+ Process a parameter.
+***************************************************************************/
+
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue)
+{
+       if (!bInGlobalSection && bGlobalOnly)
+               return (True);
+
+       DEBUGADD(4, ("doing parameter %s = %s\n", pszParmName, pszParmValue));
+
+       return (lp_do_parameter(bInGlobalSection ? -2 : iServiceIndex,
+                               pszParmName, pszParmValue));
+}
+
+
+/*
+  set a parameter from the commandline - this is called from command line parameter
+  parsing code. It sets the parameter then marks the parameter as unable to be modified
+  by smb.conf processing
+*/
+BOOL lp_set_cmdline(const char *pszParmName, const char *pszParmValue)
+{
+       int parmnum = map_parameter(pszParmName);
+
+       if (parmnum < 0 && strchr(pszParmName, ':')) {
+               /* set a parametric option */
+               return lp_do_parameter_parametric(-1, pszParmName, pszParmValue, FLAG_CMDLINE);
+       }
+
+       /* reset the CMDLINE flag in case this has been called before */
+       parm_table[parmnum].flags &= ~FLAG_CMDLINE;
+
+       if (!lp_do_parameter(-2, pszParmName, pszParmValue)) {
+               return False;
+       }
+
+       parm_table[parmnum].flags |= FLAG_CMDLINE;
+       return True;
+}
+
+/***************************************************************************
+ Print a parameter of the specified type.
+***************************************************************************/
+
+static void print_parameter(struct parm_struct *p, void *ptr, FILE * f)
+{
+       int i;
+       switch (p->type)
+       {
+               case P_ENUM:
+                       for (i = 0; p->enum_list[i].name; i++) {
+                               if (*(int *)ptr == p->enum_list[i].value) {
+                                       fprintf(f, "%s",
+                                               p->enum_list[i].name);
+                                       break;
+                               }
+                       }
+                       break;
+
+               case P_BOOL:
+                       fprintf(f, "%s", BOOLSTR(*(BOOL *)ptr));
+                       break;
+
+               case P_BOOLREV:
+                       fprintf(f, "%s", BOOLSTR(!*(BOOL *)ptr));
+                       break;
+
+               case P_INTEGER:
+                       fprintf(f, "%d", *(int *)ptr);
+                       break;
+
+               case P_CHAR:
+                       fprintf(f, "%c", *(char *)ptr);
+                       break;
+
+               case P_OCTAL:
+                       fprintf(f, "%s", octal_string(*(int *)ptr));
+                       break;
+
+               case P_LIST:
+                       if ((char ***)ptr && *(char ***)ptr) {
+                               char **list = *(char ***)ptr;
+                               
+                               for (; *list; list++)
+                                       fprintf(f, "%s%s", *list,
+                                               ((*(list+1))?", ":""));
+                       }
+                       break;
+
+               case P_STRING:
+               case P_USTRING:
+                       if (*(char **)ptr) {
+                               fprintf(f, "%s", *(char **)ptr);
+                       }
+                       break;
+               case P_SEP:
+                       break;
+       }
+}
+
+/***************************************************************************
+ Check if two parameters are equal.
+***************************************************************************/
+
+static BOOL equal_parameter(parm_type type, void *ptr1, void *ptr2)
+{
+       switch (type) {
+               case P_BOOL:
+               case P_BOOLREV:
+                       return (*((BOOL *)ptr1) == *((BOOL *)ptr2));
+
+               case P_INTEGER:
+               case P_ENUM:
+               case P_OCTAL:
+                       return (*((int *)ptr1) == *((int *)ptr2));
+
+               case P_CHAR:
+                       return (*((char *)ptr1) == *((char *)ptr2));
+               
+               case P_LIST:
+                       return str_list_compare(*(char ***)ptr1, *(char ***)ptr2);
+
+               case P_STRING:
+               case P_USTRING:
+               {
+                       char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2;
+                       if (p1 && !*p1)
+                               p1 = NULL;
+                       if (p2 && !*p2)
+                               p2 = NULL;
+                       return (p1 == p2 || strequal(p1, p2));
+               }
+               case P_SEP:
+                       break;
+       }
+       return (False);
+}
+
+/***************************************************************************
+ Process a new section (service). At this stage all sections are services.
+ Later we'll have special sections that permit server parameters to be set.
+ Returns True on success, False on failure. 
+***************************************************************************/
+
+static BOOL do_section(const char *pszSectionName)
+{
+       BOOL bRetval;
+       BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) ||
+                        (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
+       bRetval = False;
+
+       /* if we've just struck a global section, note the fact. */
+       bInGlobalSection = isglobal;
+
+       /* check for multiple global sections */
+       if (bInGlobalSection) {
+               DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName));
+               return (True);
+       }
+
+       if (!bInGlobalSection && bGlobalOnly)
+               return (True);
+
+       /* if we have a current service, tidy it up before moving on */
+       bRetval = True;
+
+       if (iServiceIndex >= 0)
+               bRetval = service_ok(iServiceIndex);
+
+       /* if all is still well, move to the next record in the services array */
+       if (bRetval) {
+               /* We put this here to avoid an odd message order if messages are */
+               /* issued by the post-processing of a previous section. */
+               DEBUG(2, ("Processing section \"[%s]\"\n", pszSectionName));
+
+               if ((iServiceIndex = add_a_service(&sDefault, pszSectionName))
+                   < 0) {
+                       DEBUG(0, ("Failed to add a new service\n"));
+                       return (False);
+               }
+       }
+
+       return (bRetval);
+}
+
+
+/***************************************************************************
+ Determine if a partcular base parameter is currentl set to the default value.
+***************************************************************************/
+
+static BOOL is_default(int i)
+{
+       if (!defaults_saved)
+               return False;
+       switch (parm_table[i].type) {
+               case P_LIST:
+                       return str_list_compare (parm_table[i].def.lvalue, 
+                                               *(char ***)parm_table[i].ptr);
+               case P_STRING:
+               case P_USTRING:
+                       return strequal(parm_table[i].def.svalue,
+                                       *(char **)parm_table[i].ptr);
+               case P_BOOL:
+               case P_BOOLREV:
+                       return parm_table[i].def.bvalue ==
+                               *(BOOL *)parm_table[i].ptr;
+               case P_CHAR:
+                       return parm_table[i].def.cvalue ==
+                               *(char *)parm_table[i].ptr;
+               case P_INTEGER:
+               case P_OCTAL:
+               case P_ENUM:
+                       return parm_table[i].def.ivalue ==
+                               *(int *)parm_table[i].ptr;
+               case P_SEP:
+                       break;
+       }
+       return False;
+}
+
+/***************************************************************************
+Display the contents of the global structure.
+***************************************************************************/
+
+static void dump_globals(FILE *f)
+{
+       int i;
+       struct param_opt *data;
+       
+       fprintf(f, "# Global parameters\n[global]\n");
+
+       for (i = 0; parm_table[i].label; i++)
+               if (parm_table[i].class == P_GLOBAL &&
+                   parm_table[i].ptr &&
+                   (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+                       if (defaults_saved && is_default(i))
+                               continue;
+                       fprintf(f, "\t%s = ", parm_table[i].label);
+                       print_parameter(&parm_table[i], parm_table[i].ptr, f);
+                       fprintf(f, "\n");
+       }
+       if (Globals.param_opt != NULL) {
+               data = Globals.param_opt;
+               while(data) {
+                       fprintf(f, "\t%s = %s\n", data->key, data->value);
+                       data = data->next;
+               }
+        }
+
+}
+
+/***************************************************************************
+ Return True if a local parameter is currently set to the global default.
+***************************************************************************/
+
+BOOL lp_is_default(int snum, struct parm_struct *parm)
+{
+       int pdiff = PTR_DIFF(parm->ptr, &sDefault);
+
+       return equal_parameter(parm->type,
+                              ((char *)ServicePtrs[snum]) + pdiff,
+                              ((char *)&sDefault) + pdiff);
+}
+
+/***************************************************************************
+ Display the contents of a single services record.
+***************************************************************************/
+
+static void dump_a_service(service * pService, FILE * f)
+{
+       int i;
+       struct param_opt *data;
+       
+       if (pService != &sDefault)
+               fprintf(f, "\n[%s]\n", pService->szService);
+
+       for (i = 0; parm_table[i].label; i++)
+               if (parm_table[i].class == P_LOCAL &&
+                   parm_table[i].ptr &&
+                   (*parm_table[i].label != '-') &&
+                   (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+                       int pdiff = PTR_DIFF(parm_table[i].ptr, &sDefault);
+
+                       if (pService == &sDefault) {
+                               if (defaults_saved && is_default(i))
+                                       continue;
+                       } else {
+                               if (equal_parameter(parm_table[i].type,
+                                                   ((char *)pService) +
+                                                   pdiff,
+                                                   ((char *)&sDefault) +
+                                                   pdiff))
+                                       continue;
+                       }
+
+                       fprintf(f, "\t%s = ", parm_table[i].label);
+                       print_parameter(&parm_table[i],
+                                       ((char *)pService) + pdiff, f);
+                       fprintf(f, "\n");
+       }
+       if (pService->param_opt != NULL) {
+               data = pService->param_opt;
+               while(data) {
+                       fprintf(f, "\t%s = %s\n", data->key, data->value);
+                       data = data->next;
+               }
+        }
+}
+
+
+/***************************************************************************
+ Return info about the next service  in a service. snum==-1 gives the globals.
+ Return NULL when out of parameters.
+***************************************************************************/
+
+struct parm_struct *lp_next_parameter(int snum, int *i, int allparameters)
+{
+       if (snum == -1) {
+               /* do the globals */
+               for (; parm_table[*i].label; (*i)++) {
+                       if (parm_table[*i].class == P_SEPARATOR)
+                               return &parm_table[(*i)++];
+
+                       if (!parm_table[*i].ptr
+                           || (*parm_table[*i].label == '-'))
+                               continue;
+
+                       if ((*i) > 0
+                           && (parm_table[*i].ptr ==
+                               parm_table[(*i) - 1].ptr))
+                               continue;
+
+                       return &parm_table[(*i)++];
+               }
+       } else {
+               service *pService = ServicePtrs[snum];
+
+               for (; parm_table[*i].label; (*i)++) {
+                       if (parm_table[*i].class == P_SEPARATOR)
+                               return &parm_table[(*i)++];
+
+                       if (parm_table[*i].class == P_LOCAL &&
+                           parm_table[*i].ptr &&
+                           (*parm_table[*i].label != '-') &&
+                           ((*i) == 0 ||
+                            (parm_table[*i].ptr !=
+                             parm_table[(*i) - 1].ptr)))
+                       {
+                               int pdiff =
+                                       PTR_DIFF(parm_table[*i].ptr,
+                                                &sDefault);
+
+                               if (allparameters ||
+                                   !equal_parameter(parm_table[*i].type,
+                                                    ((char *)pService) +
+                                                    pdiff,
+                                                    ((char *)&sDefault) +
+                                                    pdiff))
+                               {
+                                       return &parm_table[(*i)++];
+                               }
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+#if 0
+/***************************************************************************
+ Display the contents of a single copy structure.
+***************************************************************************/
+static void dump_copy_map(BOOL *pcopymap)
+{
+       int i;
+       if (!pcopymap)
+               return;
+
+       printf("\n\tNon-Copied parameters:\n");
+
+       for (i = 0; parm_table[i].label; i++)
+               if (parm_table[i].class == P_LOCAL &&
+                   parm_table[i].ptr && !pcopymap[i] &&
+                   (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr)))
+               {
+                       printf("\t\t%s\n", parm_table[i].label);
+               }
+}
+#endif
+
+/***************************************************************************
+ Return TRUE if the passed service number is within range.
+***************************************************************************/
+
+BOOL lp_snum_ok(int iService)
+{
+       return (LP_SNUM_OK(iService) && ServicePtrs[iService]->bAvailable);
+}
+
+/***************************************************************************
+ Auto-load some home services.
+***************************************************************************/
+
+static void lp_add_auto_services(char *str)
+{
+       char *s;
+       char *p;
+       int homes;
+
+       if (!str)
+               return;
+
+       s = strdup(str);
+       if (!s)
+               return;
+
+       homes = lp_servicenumber(HOMES_NAME);
+
+       for (p = strtok(s, LIST_SEP); p; p = strtok(NULL, LIST_SEP)) {
+               char *home = get_user_home_dir(p);
+
+               if (lp_servicenumber(p) >= 0)
+                       continue;
+
+               if (home && homes >= 0)
+                       lp_add_home(p, homes, p, home);
+       }
+       SAFE_FREE(s);
+}
+
+/***************************************************************************
+ Auto-load one printer.
+***************************************************************************/
+
+void lp_add_one_printer(char *name, char *comment)
+{
+       int printers = lp_servicenumber(PRINTERS_NAME);
+       int i;
+
+       if (lp_servicenumber(name) < 0) {
+               lp_add_printer(name, printers);
+               if ((i = lp_servicenumber(name)) >= 0) {
+                       string_set(&ServicePtrs[i]->comment, comment);
+                       ServicePtrs[i]->autoloaded = True;
+               }
+       }
+}
+
+/***************************************************************************
+ Announce ourselves as a print server.
+***************************************************************************/
+
+void update_server_announce_as_printserver(void)
+{
+       default_server_announce |= SV_TYPE_PRINTQ_SERVER;       
+}
+
+/***************************************************************************
+ Have we loaded a services file yet?
+***************************************************************************/
+
+BOOL lp_loaded(void)
+{
+       return (bLoaded);
+}
+
+/***************************************************************************
+ Unload unused services.
+***************************************************************************/
+
+void lp_killunused(struct server_context *smb, BOOL (*snumused) (struct server_context *, int))
+{
+       int i;
+       for (i = 0; i < iNumServices; i++) {
+               if (!VALID(i))
+                       continue;
+
+               if (!snumused || !snumused(smb, i)) {
+                       ServicePtrs[i]->valid = False;
+                       free_service(ServicePtrs[i]);
+               }
+       }
+}
+
+/***************************************************************************
+ Unload a service.
+***************************************************************************/
+
+void lp_killservice(int iServiceIn)
+{
+       if (VALID(iServiceIn)) {
+               ServicePtrs[iServiceIn]->valid = False;
+               free_service(ServicePtrs[iServiceIn]);
+       }
+}
+
+/***************************************************************************
+ Save the curent values of all global and sDefault parameters into the 
+ defaults union. This allows swat and testparm to show only the
+ changed (ie. non-default) parameters.
+***************************************************************************/
+
+static void lp_save_defaults(void)
+{
+       int i;
+       for (i = 0; parm_table[i].label; i++) {
+               if (i > 0 && parm_table[i].ptr == parm_table[i - 1].ptr)
+                       continue;
+               switch (parm_table[i].type) {
+                       case P_LIST:
+                               str_list_copy(&(parm_table[i].def.lvalue),
+                                           *(const char ***)parm_table[i].ptr);
+                               break;
+                       case P_STRING:
+                       case P_USTRING:
+                               if (parm_table[i].ptr) {
+                                       parm_table[i].def.svalue = strdup(*(char **)parm_table[i].ptr);
+                               } else {
+                                       parm_table[i].def.svalue = NULL;
+                               }
+                               break;
+                       case P_BOOL:
+                       case P_BOOLREV:
+                               parm_table[i].def.bvalue =
+                                       *(BOOL *)parm_table[i].ptr;
+                               break;
+                       case P_CHAR:
+                               parm_table[i].def.cvalue =
+                                       *(char *)parm_table[i].ptr;
+                               break;
+                       case P_INTEGER:
+                       case P_OCTAL:
+                       case P_ENUM:
+                               parm_table[i].def.ivalue =
+                                       *(int *)parm_table[i].ptr;
+                               break;
+                       case P_SEP:
+                               break;
+               }
+       }
+       defaults_saved = True;
+}
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_server_role(void)
+{
+       server_role = ROLE_STANDALONE;
+
+       switch (lp_security()) {
+               case SEC_SHARE:
+                       if (lp_domain_logons())
+                               DEBUG(0, ("Server's Role (logon server) conflicts with share-level security\n"));
+                       break;
+               case SEC_SERVER:
+               case SEC_DOMAIN:
+               case SEC_ADS:
+                       if (lp_domain_logons()) {
+                               server_role = ROLE_DOMAIN_PDC;
+                               break;
+                       }
+                       server_role = ROLE_DOMAIN_MEMBER;
+                       break;
+               case SEC_USER:
+                       if (lp_domain_logons()) {
+
+                               if (Globals.bDomainMaster) /* auto or yes */ 
+                                       server_role = ROLE_DOMAIN_PDC;
+                               else
+                                       server_role = ROLE_DOMAIN_BDC;
+                       }
+                       break;
+               default:
+                       DEBUG(0, ("Server's Role undefined due to unknown security mode\n"));
+                       break;
+       }
+
+       DEBUG(10, ("set_server_role: role = "));
+
+       switch(server_role) {
+       case ROLE_STANDALONE:
+               DEBUGADD(10, ("ROLE_STANDALONE\n"));
+               break;
+       case ROLE_DOMAIN_MEMBER:
+               DEBUGADD(10, ("ROLE_DOMAIN_MEMBER\n"));
+               break;
+       case ROLE_DOMAIN_BDC:
+               DEBUGADD(10, ("ROLE_DOMAIN_BDC\n"));
+               break;
+       case ROLE_DOMAIN_PDC:
+               DEBUGADD(10, ("ROLE_DOMAIN_PDC\n"));
+               break;
+       }
+}
+
+/***************************************************************************
+ Load the services array from the services file. Return True on success, 
+ False on failure.
+***************************************************************************/
+
+BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
+            BOOL add_ipc)
+{
+       pstring n2;
+       BOOL bRetval;
+       struct param_opt *data;
+
+       pstrcpy(n2, pszFname);
+       standard_sub_basic(n2,sizeof(n2));
+
+       add_to_file_list(pszFname, n2);
+
+       bRetval = False;
+
+       DEBUG(0, ("lp_load: refreshing parameters from %s\n", pszFname));
+       
+       bInGlobalSection = True;
+       bGlobalOnly = global_only;
+
+       init_globals();
+
+       if (save_defaults)
+       {
+               lp_save_defaults();
+       }
+
+       if (Globals.param_opt != NULL) {
+               struct param_opt *next;
+               for (data=Globals.param_opt; data; data=next) {
+                       next = data->next;
+                       if (data->flags & FLAG_CMDLINE) continue;
+                       free(data->key);
+                       free(data->value);
+                       DLIST_REMOVE(Globals.param_opt, data);
+                       free(data);
+               }
+       }
+       
+       /* We get sections first, so have to start 'behind' to make up */
+       iServiceIndex = -1;
+       bRetval = pm_process(n2, do_section, do_parameter);
+
+       /* finish up the last section */
+       DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval)));
+       if (bRetval)
+               if (iServiceIndex >= 0)
+                       bRetval = service_ok(iServiceIndex);
+
+       lp_add_auto_services(lp_auto_services());
+
+       if (add_ipc) {
+               /* When 'restrict anonymous = 2' guest connections to ipc$
+                  are denied */
+               lp_add_ipc("IPC$", (lp_restrict_anonymous() < 2));
+               lp_add_ipc("ADMIN$", False);
+       }
+
+       set_server_role();
+       set_default_server_announce_type();
+
+       bLoaded = True;
+
+       /* Now we check bWINSsupport and set szWINSserver to 127.0.0.1 */
+       /* if bWINSsupport is true and we are in the client            */
+       if (in_client && Globals.bWINSsupport) {
+               lp_do_parameter(-1, "wins server", "127.0.0.1");
+       }
+
+       init_iconv();
+
+       return (bRetval);
+}
+
+/***************************************************************************
+ Reset the max number of services.
+***************************************************************************/
+
+void lp_resetnumservices(void)
+{
+       iNumServices = 0;
+}
+
+/***************************************************************************
+ Return the max number of services.
+***************************************************************************/
+
+int lp_numservices(void)
+{
+       return (iNumServices);
+}
+
+/***************************************************************************
+Display the contents of the services array in human-readable form.
+***************************************************************************/
+
+void lp_dump(FILE *f, BOOL show_defaults, int maxtoprint)
+{
+       int iService;
+
+       if (show_defaults)
+               defaults_saved = False;
+
+       dump_globals(f);
+
+       dump_a_service(&sDefault, f);
+
+       for (iService = 0; iService < maxtoprint; iService++)
+               lp_dump_one(f, show_defaults, iService);
+}
+
+/***************************************************************************
+Display the contents of one service in human-readable form.
+***************************************************************************/
+
+void lp_dump_one(FILE * f, BOOL show_defaults, int snum)
+{
+       if (VALID(snum)) {
+               if (ServicePtrs[snum]->szService[0] == '\0')
+                       return;
+               dump_a_service(ServicePtrs[snum], f);
+       }
+}
+
+/***************************************************************************
+Return the number of the service with the given name, or -1 if it doesn't
+exist. Note that this is a DIFFERENT ANIMAL from the internal function
+getservicebyname()! This works ONLY if all services have been loaded, and
+does not copy the found service.
+***************************************************************************/
+
+int lp_servicenumber(const char *pszServiceName)
+{
+       int iService;
+        fstring serviceName;
+       for (iService = iNumServices - 1; iService >= 0; iService--) {
+               if (VALID(iService) && ServicePtrs[iService]->szService) {
+                       /*
+                        * The substitution here is used to support %U is
+                        * service names
+                        */
+                       fstrcpy(serviceName, ServicePtrs[iService]->szService);
+                       standard_sub_basic(serviceName,sizeof(serviceName));
+                       if (strequal(serviceName, pszServiceName))
+                               break;
+               }
+       }
+
+       if (iService < 0)
+               DEBUG(7,("lp_servicenumber: couldn't find %s\n", pszServiceName));
+
+       return (iService);
+}
+
+/*******************************************************************
+ A useful volume label function. 
+********************************************************************/
+char *volume_label(int snum)
+{
+       char *ret = lp_volume(snum);
+       if (!*ret)
+               return lp_servicename(snum);
+       return (ret);
+}
+
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_default_server_announce_type(void)
+{
+       default_server_announce = 0;
+       default_server_announce |= SV_TYPE_WORKSTATION;
+       default_server_announce |= SV_TYPE_SERVER;
+       default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+       switch (lp_announce_as()) {
+               case ANNOUNCE_AS_NT_SERVER:
+                       default_server_announce |= SV_TYPE_SERVER_NT;
+                       /* fall through... */
+               case ANNOUNCE_AS_NT_WORKSTATION:
+                       default_server_announce |= SV_TYPE_NT;
+                       break;
+               case ANNOUNCE_AS_WIN95:
+                       default_server_announce |= SV_TYPE_WIN95_PLUS;
+                       break;
+               case ANNOUNCE_AS_WFW:
+                       default_server_announce |= SV_TYPE_WFW;
+                       break;
+               default:
+                       break;
+       }
+
+       switch (lp_server_role()) {
+               case ROLE_DOMAIN_MEMBER:
+                       default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+                       break;
+               case ROLE_DOMAIN_PDC:
+                       default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+                       break;
+               case ROLE_DOMAIN_BDC:
+                       default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+                       break;
+               case ROLE_STANDALONE:
+               default:
+                       break;
+       }
+       if (lp_time_server())
+               default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+       if (lp_host_msdfs())
+               default_server_announce |= SV_TYPE_DFS_SERVER;
+}
+
+/***********************************************************
+ returns role of Samba server
+************************************************************/
+
+int lp_server_role(void)
+{
+       return server_role;
+}
+
+/***********************************************************
+ If we are PDC then prefer us as DMB
+************************************************************/
+
+BOOL lp_domain_master(void)
+{
+       if (Globals.bDomainMaster == Auto)
+               return (lp_server_role() == ROLE_DOMAIN_PDC);
+
+       return Globals.bDomainMaster;
+}
+
+/***********************************************************
+ If we are DMB then prefer us as LMB
+************************************************************/
+
+BOOL lp_preferred_master(void)
+{
+       if (Globals.bPreferredMaster == Auto)
+               return (lp_local_master() && lp_domain_master());
+
+       return Globals.bPreferredMaster;
+}
+
+/*******************************************************************
+ Remove a service.
+********************************************************************/
+
+void lp_remove_service(int snum)
+{
+       ServicePtrs[snum]->valid = False;
+}
+
+/*******************************************************************
+ Copy a service.
+********************************************************************/
+
+void lp_copy_service(int snum, const char *new_name)
+{
+       char *oldname = lp_servicename(snum);
+       do_section(new_name);
+       if (snum >= 0) {
+               snum = lp_servicenumber(new_name);
+               if (snum >= 0)
+                       lp_do_parameter(snum, "copy", oldname);
+       }
+}
+
+
+/*******************************************************************
+ Get the default server type we will announce as via nmbd.
+********************************************************************/
+
+int lp_default_server_announce(void)
+{
+       return default_server_announce;
+}
+
+/*******************************************************************
+ Split the announce version into major and minor numbers.
+********************************************************************/
+
+int lp_major_announce_version(void)
+{
+       static BOOL got_major = False;
+       static int major_version = DEFAULT_MAJOR_VERSION;
+       char *vers;
+       char *p;
+
+       if (got_major)
+               return major_version;
+
+       got_major = True;
+       if ((vers = lp_announce_version()) == NULL)
+               return major_version;
+
+       if ((p = strchr_m(vers, '.')) == 0)
+               return major_version;
+
+       *p = '\0';
+       major_version = atoi(vers);
+       return major_version;
+}
+
+int lp_minor_announce_version(void)
+{
+       static BOOL got_minor = False;
+       static int minor_version = DEFAULT_MINOR_VERSION;
+       char *vers;
+       char *p;
+
+       if (got_minor)
+               return minor_version;
+
+       got_minor = True;
+       if ((vers = lp_announce_version()) == NULL)
+               return minor_version;
+
+       if ((p = strchr_m(vers, '.')) == 0)
+               return minor_version;
+
+       p++;
+       minor_version = atoi(p);
+       return minor_version;
+}
+
+const char *lp_printername(int snum)
+{
+       const char *ret = _lp_printername(snum);
+       if (ret == NULL || (ret != NULL && *ret == '\0'))
+               ret = lp_const_servicename(snum);
+
+       return ret;
+}
+
+
+/*******************************************************************
+ Return the max print jobs per queue.
+********************************************************************/
+
+int lp_maxprintjobs(int snum)
+{
+       int maxjobs = LP_SNUM_OK(snum) ? ServicePtrs[snum]->iMaxPrintJobs : sDefault.iMaxPrintJobs;
+       if (maxjobs <= 0 || maxjobs >= PRINT_MAX_JOBID)
+               maxjobs = PRINT_MAX_JOBID - 1;
+
+       return maxjobs;
+}
diff --git a/source4/param/params.c b/source4/param/params.c
new file mode 100644 (file)
index 0000000..892e547
--- /dev/null
@@ -0,0 +1,599 @@
+/* -------------------------------------------------------------------------- **
+ * Microsoft Network Services for Unix, AKA., Andrew Tridgell's SAMBA.
+ *
+ * This module Copyright (C) 1990-1998 Karl Auer
+ *
+ * Rewritten almost completely by Christopher R. Hertel
+ * at the University of Minnesota, September, 1997.
+ * This module Copyright (C) 1997-1998 by the University of Minnesota
+ * -------------------------------------------------------------------------- **
+ *
+ * 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.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Module name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ *  This module performs lexical analysis and initial parsing of a
+ *  Windows-like parameter file.  It recognizes and handles four token
+ *  types:  section-name, parameter-name, parameter-value, and
+ *  end-of-file.  Comments and line continuation are handled
+ *  internally.
+ *
+ *  The entry point to the module is function pm_process().  This
+ *  function opens the source file, calls the Parse() function to parse
+ *  the input, and then closes the file when either the EOF is reached
+ *  or a fatal error is encountered.
+ *
+ *  A sample parameter file might look like this:
+ *
+ *  [section one]
+ *  parameter one = value string
+ *  parameter two = another value
+ *  [section two]
+ *  new parameter = some value or t'other
+ *
+ *  The parameter file is divided into sections by section headers:
+ *  section names enclosed in square brackets (eg. [section one]).
+ *  Each section contains parameter lines, each of which consist of a
+ *  parameter name and value delimited by an equal sign.  Roughly, the
+ *  syntax is:
+ *
+ *    <file>            :==  { <section> } EOF
+ *
+ *    <section>         :==  <section header> { <parameter line> }
+ *
+ *    <section header>  :==  '[' NAME ']'
+ *
+ *    <parameter line>  :==  NAME '=' VALUE '\n'
+ *
+ *  Blank lines and comment lines are ignored.  Comment lines are lines
+ *  beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ *  All whitespace in section names and parameter names is compressed
+ *  to single spaces.  Leading and trailing whitespace is stipped from
+ *  both names and values.
+ *
+ *  Only the first equals sign in a parameter line is significant.
+ *  Parameter values may contain equals signs, square brackets and
+ *  semicolons.  Internal whitespace is retained in parameter values,
+ *  with the exception of the '\r' character, which is stripped for
+ *  historic reasons.  Parameter names may not start with a left square
+ *  bracket, an equal sign, a pound sign, or a semicolon, because these
+ *  are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ *  DEBUGLEVEL  - The ubiquitous DEBUGLEVEL.  This determines which DEBUG()
+ *                messages will be produced.
+ *  bufr        - pointer to a global buffer.  This is probably a kludge,
+ *                but it was the nicest kludge I could think of (for now).
+ *  bSize       - The size of the global buffer <bufr>.
+ */
+
+static char *bufr  = NULL;
+static int   bSize = 0;
+
+/* we can't use FILE* due to the 256 fd limit - use this cheap hack
+   instead */
+typedef struct {
+       char *buf;
+       char *p;
+       size_t size;
+} myFILE;
+
+static int mygetc(myFILE *f)
+{
+       if (f->p >= f->buf+f->size) return EOF;
+        /* be sure to return chars >127 as positive values */
+       return (int)( *(f->p++) & 0x00FF );
+}
+
+static void myfile_close(myFILE *f)
+{
+       if (!f) return;
+       SAFE_FREE(f->buf);
+       SAFE_FREE(f);
+}
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( myFILE *InFile )
+  /* ------------------------------------------------------------------------ **
+   * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+   * character, or newline, or EOF.
+   *
+   *  Input:  InFile  - Input source.
+   *
+   *  Output: The next non-whitespace character in the input stream.
+   *
+   *  Notes:  Because the config files use a line-oriented grammar, we
+   *          explicitly exclude the newline character from the list of
+   *          whitespace characters.
+   *        - Note that both EOF (-1) and the nul character ('\0') are
+   *          considered end-of-file markers.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int c;
+
+  for( c = mygetc( InFile ); isspace( c ) && ('\n' != c); c = mygetc( InFile ) )
+    ;
+  return( c );
+  } /* EatWhitespace */
+
+static int EatComment( myFILE *InFile )
+  /* ------------------------------------------------------------------------ **
+   * Scan to the end of a comment.
+   *
+   *  Input:  InFile  - Input source.
+   *
+   *  Output: The character that marks the end of the comment.  Normally,
+   *          this will be a newline, but it *might* be an EOF.
+   *
+   *  Notes:  Because the config files use a line-oriented grammar, we
+   *          explicitly exclude the newline character from the list of
+   *          whitespace characters.
+   *        - Note that both EOF (-1) and the nul character ('\0') are
+   *          considered end-of-file markers.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int c;
+
+  for( c = mygetc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = mygetc( InFile ) )
+    ;
+  return( c );
+  } /* EatComment */
+
+/*****************************************************************************
+ * Scan backards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ *  Input:  line  - A pointer to a buffer containing the string to be
+ *                  scanned.
+ *          pos   - This is taken to be the offset of the end of the
+ *                  string.  This position is *not* scanned.
+ *
+ *  Output: The offset of the '\\' character if it was found, or -1 to
+ *          indicate that it was not.
+ *
+ *****************************************************************************/
+
+static int Continuation(char *line, int pos )
+{
+       pos--;
+       while( (pos >= 0) && isspace((int)line[pos]))
+               pos--;
+
+       return (((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+}
+
+
+static BOOL Section( myFILE *InFile, BOOL (*sfunc)(const char *) )
+  /* ------------------------------------------------------------------------ **
+   * Scan a section name, and pass the name to function sfunc().
+   *
+   *  Input:  InFile  - Input source.
+   *          sfunc   - Pointer to the function to be called if the section
+   *                    name is successfully read.
+   *
+   *  Output: True if the section name was read and True was returned from
+   *          <sfunc>.  False if <sfunc> failed or if a lexical error was
+   *          encountered.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int   c;
+  int   i;
+  int   end;
+  const char *func  = "params.c:Section() -";
+
+  i = 0;      /* <i> is the offset of the next free byte in bufr[] and  */
+  end = 0;    /* <end> is the current "end of string" offset.  In most  */
+              /* cases these will be the same, but if the last          */
+              /* character written to bufr[] is a space, then <end>     */
+              /* will be one less than <i>.                             */
+
+  c = EatWhitespace( InFile );    /* We've already got the '['.  Scan */
+                                  /* past initial white space.        */
+
+  while( (EOF != c) && (c > 0) )
+    {
+
+    /* Check that the buffer is big enough for the next character. */
+    if( i > (bSize - 2) )
+      {
+      char *tb;
+      
+      tb = Realloc( bufr, bSize +BUFR_INC );
+      if( NULL == tb )
+        {
+        DEBUG(0, ("%s Memory re-allocation failure.", func) );
+        return( False );
+        }
+      bufr = tb;
+      bSize += BUFR_INC;
+      }
+
+    /* Handle a single character. */
+    switch( c )
+      {
+      case ']':                       /* Found the closing bracket.         */
+        bufr[end] = '\0';
+        if( 0 == end )                  /* Don't allow an empty name.       */
+          {
+          DEBUG(0, ("%s Empty section name in configuration file.\n", func ));
+          return( False );
+          }
+        if( !sfunc(bufr) )            /* Got a valid name.  Deal with it. */
+          return( False );
+        (void)EatComment( InFile );     /* Finish off the line.             */
+        return( True );
+
+      case '\n':                      /* Got newline before closing ']'.    */
+        i = Continuation( bufr, i );    /* Check for line continuation.     */
+        if( i < 0 )
+          {
+          bufr[end] = '\0';
+          DEBUG(0, ("%s Badly formed line in configuration file: %s\n",
+                   func, bufr ));
+          return( False );
+          }
+        end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+        c = mygetc( InFile );             /* Continue with next line.         */
+        break;
+
+      default:                        /* All else are a valid name chars.   */
+        if( isspace( c ) )              /* One space per whitespace region. */
+          {
+          bufr[end] = ' ';
+          i = end + 1;
+          c = EatWhitespace( InFile );
+          }
+        else                            /* All others copy verbatim.        */
+          {
+          bufr[i++] = c;
+          end = i;
+          c = mygetc( InFile );
+          }
+      }
+    }
+
+  /* We arrive here if we've met the EOF before the closing bracket. */
+  DEBUG(0, ("%s Unexpected EOF in the configuration file: %s\n", func, bufr ));
+  return( False );
+  } /* Section */
+
+static BOOL Parameter( myFILE *InFile, BOOL (*pfunc)(const char *, const char *), int c )
+  /* ------------------------------------------------------------------------ **
+   * Scan a parameter name and value, and pass these two fields to pfunc().
+   *
+   *  Input:  InFile  - The input source.
+   *          pfunc   - A pointer to the function that will be called to
+   *                    process the parameter, once it has been scanned.
+   *          c       - The first character of the parameter name, which
+   *                    would have been read by Parse().  Unlike a comment
+   *                    line or a section header, there is no lead-in
+   *                    character that can be discarded.
+   *
+   *  Output: True if the parameter name and value were scanned and processed
+   *          successfully, else False.
+   *
+   *  Notes:  This function is in two parts.  The first loop scans the
+   *          parameter name.  Internal whitespace is compressed, and an
+   *          equal sign (=) terminates the token.  Leading and trailing
+   *          whitespace is discarded.  The second loop scans the parameter
+   *          value.  When both have been successfully identified, they are
+   *          passed to pfunc() for processing.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int   i       = 0;    /* Position within bufr. */
+  int   end     = 0;    /* bufr[end] is current end-of-string. */
+  int   vstart  = 0;    /* Starting position of the parameter value. */
+  const char *func    = "params.c:Parameter() -";
+
+  /* Read the parameter name. */
+  while( 0 == vstart )  /* Loop until we've found the start of the value. */
+    {
+
+    if( i > (bSize - 2) )       /* Ensure there's space for next char.    */
+      {
+      char *tb;
+      
+      tb = Realloc( bufr, bSize + BUFR_INC );
+      if( NULL == tb )
+        {
+        DEBUG(0, ("%s Memory re-allocation failure.", func) );
+        return( False );
+        }
+      bufr = tb;
+      bSize += BUFR_INC;
+      }
+
+    switch( c )
+      {
+      case '=':                 /* Equal sign marks end of param name. */
+        if( 0 == end )              /* Don't allow an empty name.      */
+          {
+          DEBUG(0, ("%s Invalid parameter name in config. file.\n", func ));
+          return( False );
+          }
+        bufr[end++] = '\0';         /* Mark end of string & advance.   */
+        i       = end;              /* New string starts here.         */
+        vstart  = end;              /* New string is parameter value.  */
+        bufr[i] = '\0';             /* New string is nul, for now.     */
+        break;
+
+      case '\n':                /* Find continuation char, else error. */
+        i = Continuation( bufr, i );
+        if( i < 0 )
+          {
+          bufr[end] = '\0';
+          DEBUG(1,("%s Ignoring badly formed line in configuration file: %s\n",
+                   func, bufr ));
+          return( True );
+          }
+        end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+        c = mygetc( InFile );       /* Read past eoln.                   */
+        break;
+
+      case '\0':                /* Shouldn't have EOF within param name. */
+      case EOF:
+        bufr[i] = '\0';
+        DEBUG(1,("%s Unexpected end-of-file at: %s\n", func, bufr ));
+        return( True );
+
+      default:
+        if( isspace( c ) )     /* One ' ' per whitespace region.       */
+          {
+          bufr[end] = ' ';
+          i = end + 1;
+          c = EatWhitespace( InFile );
+          }
+        else                   /* All others verbatim.                 */
+          {
+          bufr[i++] = c;
+          end = i;
+          c = mygetc( InFile );
+          }
+      }
+    }
+
+  /* Now parse the value. */
+  c = EatWhitespace( InFile );  /* Again, trim leading whitespace. */
+  while( (EOF !=c) && (c > 0) )
+    {
+
+    if( i > (bSize - 2) )       /* Make sure there's enough room. */
+      {
+      char *tb;
+      
+      tb = Realloc( bufr, bSize + BUFR_INC );
+      if( NULL == tb )
+        {
+        DEBUG(0, ("%s Memory re-allocation failure.", func) );
+        return( False );
+        }
+      bufr = tb;
+      bSize += BUFR_INC;
+      }
+
+    switch( c )
+      {
+      case '\r':              /* Explicitly remove '\r' because the older */
+        c = mygetc( InFile );   /* version called fgets_slash() which also  */
+        break;                /* removes them.                            */
+
+      case '\n':              /* Marks end of value unless there's a '\'. */
+        i = Continuation( bufr, i );
+        if( i < 0 )
+          c = 0;
+        else
+          {
+          for( end = i; (end >= 0) && isspace((int)bufr[end]); end-- )
+            ;
+          c = mygetc( InFile );
+          }
+        break;
+
+      default:               /* All others verbatim.  Note that spaces do */
+        bufr[i++] = c;       /* not advance <end>.  This allows trimming  */
+        if( !isspace( c ) )  /* of whitespace at the end of the line.     */
+          end = i;
+        c = mygetc( InFile );
+        break;
+      }
+    }
+  bufr[end] = '\0';          /* End of value. */
+
+  return( pfunc( bufr, &bufr[vstart] ) );   /* Pass name & value to pfunc().  */
+  } /* Parameter */
+
+static BOOL Parse( myFILE *InFile,
+                   BOOL (*sfunc)(const char *),
+                   BOOL (*pfunc)(const char *, const char *) )
+  /* ------------------------------------------------------------------------ **
+   * Scan & parse the input.
+   *
+   *  Input:  InFile  - Input source.
+   *          sfunc   - Function to be called when a section name is scanned.
+   *                    See Section().
+   *          pfunc   - Function to be called when a parameter is scanned.
+   *                    See Parameter().
+   *
+   *  Output: True if the file was successfully scanned, else False.
+   *
+   *  Notes:  The input can be viewed in terms of 'lines'.  There are four
+   *          types of lines:
+   *            Blank      - May contain whitespace, otherwise empty.
+   *            Comment    - First non-whitespace character is a ';' or '#'.
+   *                         The remainder of the line is ignored.
+   *            Section    - First non-whitespace character is a '['.
+   *            Parameter  - The default case.
+   * 
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int    c;
+
+  c = EatWhitespace( InFile );
+  while( (EOF != c) && (c > 0) )
+    {
+    switch( c )
+      {
+      case '\n':                        /* Blank line. */
+        c = EatWhitespace( InFile );
+        break;
+
+      case ';':                         /* Comment line. */
+      case '#':
+        c = EatComment( InFile );
+        break;
+
+      case '[':                         /* Section Header. */
+        if( !Section( InFile, sfunc ) )
+          return( False );
+        c = EatWhitespace( InFile );
+        break;
+
+      case '\\':                        /* Bogus backslash. */
+        c = EatWhitespace( InFile );
+        break;
+
+      default:                          /* Parameter line. */
+        if( !Parameter( InFile, pfunc, c ) )
+          return( False );
+        c = EatWhitespace( InFile );
+        break;
+      }
+    }
+  return( True );
+  } /* Parse */
+
+static myFILE *OpenConfFile( const char *FileName )
+  /* ------------------------------------------------------------------------ **
+   * Open a configuration file.
+   *
+   *  Input:  FileName  - The pathname of the config file to be opened.
+   *
+   *  Output: A pointer of type (char **) to the lines of the file
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  const char *func = "params.c:OpenConfFile() -";
+  extern BOOL in_client;
+  int lvl = in_client?1:0;
+  myFILE *ret;
+
+  ret = (myFILE *)malloc(sizeof(*ret));
+  if (!ret) return NULL;
+
+  ret->buf = file_load(FileName, &ret->size);
+  if( NULL == ret->buf )
+    {
+    DEBUG( lvl,
+      ("%s Unable to open configuration file \"%s\":\n\t%s\n",
+      func, FileName, strerror(errno)) );
+    SAFE_FREE(ret);
+    return NULL;
+    }
+
+  ret->p = ret->buf;
+  return( ret );
+  } /* OpenConfFile */
+
+BOOL pm_process( const char *FileName,
+                 BOOL (*sfunc)(const char *),
+                 BOOL (*pfunc)(const char *, const char *) )
+  /* ------------------------------------------------------------------------ **
+   * Process the named parameter file.
+   *
+   *  Input:  FileName  - The pathname of the parameter file to be opened.
+   *          sfunc     - A pointer to a function that will be called when
+   *                      a section name is discovered.
+   *          pfunc     - A pointer to a function that will be called when
+   *                      a parameter name and value are discovered.
+   *
+   *  Output: TRUE if the file was successfully parsed, else FALSE.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int   result;
+  myFILE *InFile;
+  const char *func = "params.c:pm_process() -";
+
+  InFile = OpenConfFile( FileName );          /* Open the config file. */
+  if( NULL == InFile )
+    return( False );
+
+  DEBUG( 3, ("%s Processing configuration file \"%s\"\n", func, FileName) );
+
+  if( NULL != bufr )                          /* If we already have a buffer */
+    result = Parse( InFile, sfunc, pfunc );   /* (recursive call), then just */
+                                              /* use it.                     */
+
+  else                                        /* If we don't have a buffer   */
+    {                                         /* allocate one, then parse,   */
+    bSize = BUFR_INC;                         /* then free.                  */
+    bufr = (char *)malloc( bSize );
+    if( NULL == bufr )
+      {
+      DEBUG(0,("%s memory allocation failure.\n", func));
+      myfile_close(InFile);
+      return( False );
+      }
+    result = Parse( InFile, sfunc, pfunc );
+    SAFE_FREE( bufr );
+    bufr  = NULL;
+    bSize = 0;
+    }
+
+  myfile_close(InFile);
+
+  if( !result )                               /* Generic failure. */
+    {
+    DEBUG(0,("%s Failed.  Error returned from params.c:parse().\n", func));
+    return( False );
+    }
+
+  return( True );                             /* Generic success. */
+  } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
diff --git a/source4/passdb/.cvsignore b/source4/passdb/.cvsignore
new file mode 100644 (file)
index 0000000..5f2a5c4
--- /dev/null
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source4/passdb/machine_sid.c b/source4/passdb/machine_sid.c
new file mode 100644 (file)
index 0000000..f19b4a0
--- /dev/null
@@ -0,0 +1,192 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Jeremy Allison                1996-2002
+   Copyright (C) Andrew Tridgell               2002
+   Copyright (C) Gerald (Jerry) Carter         2000
+   Copyright (C) Stefan (metze) Metzmacher     2002
+      
+   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"
+
+/* NOTE! the global_sam_sid is the SID of our local SAM. This is only
+   equal to the domain SID when we are a DC, otherwise its our
+   workstation SID */
+static DOM_SID *global_sam_sid=NULL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/****************************************************************************
+ Read a SID from a file. This is for compatibility with the old MACHINE.SID
+ style of SID storage
+****************************************************************************/
+static BOOL read_sid_from_file(const char *fname, DOM_SID *sid)
+{
+       char **lines;
+       int numlines;
+       BOOL ret;
+
+       lines = file_lines_load(fname, &numlines);
+       
+       if (!lines || numlines < 1) {
+               if (lines) file_lines_free(lines);
+               return False;
+       }
+       
+       ret = string_to_sid(sid, lines[0]);
+       file_lines_free(lines);
+       return ret;
+}
+
+/*
+  generate a random sid - used to build our own sid if we don't have one
+*/
+static void generate_random_sid(DOM_SID *sid)
+{
+       int i;
+       uchar raw_sid_data[12];
+
+       memset((char *)sid, '\0', sizeof(*sid));
+       sid->sid_rev_num = 1;
+       sid->id_auth[5] = 5;
+       sid->num_auths = 0;
+       sid->sub_auths[sid->num_auths++] = 21;
+
+       generate_random_buffer(raw_sid_data, 12, True);
+       for (i = 0; i < 3; i++)
+               sid->sub_auths[sid->num_auths++] = IVAL(raw_sid_data, i*4);
+}
+
+/****************************************************************************
+ Generate the global machine sid.
+****************************************************************************/
+
+static BOOL pdb_generate_sam_sid(void)
+{
+       char *fname = NULL;
+       BOOL is_dc = False;
+
+       if(global_sam_sid==NULL)
+               if(!(global_sam_sid=(DOM_SID *)malloc(sizeof(DOM_SID))))
+                       return False;
+                       
+       generate_wellknown_sids();
+
+       switch (lp_server_role()) {
+       case ROLE_DOMAIN_PDC:
+       case ROLE_DOMAIN_BDC:
+               is_dc = True;
+               break;
+       default:
+               is_dc = False;
+               break;
+       }
+
+       if (secrets_fetch_domain_sid(lp_netbios_name(), global_sam_sid)) {
+               DOM_SID domain_sid;
+
+               /* We got our sid. If not a pdc/bdc, we're done. */
+               if (!is_dc)
+                       return True;
+
+               if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+
+                       /* No domain sid and we're a pdc/bdc. Store it */
+
+                       if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) {
+                               DEBUG(0,("pdb_generate_sam_sid: Can't store domain SID as a pdc/bdc.\n"));
+                               return False;
+                       }
+                       return True;
+               }
+
+               if (!sid_equal(&domain_sid, global_sam_sid)) {
+
+                       /* Domain name sid doesn't match global sam sid. Re-store global sam sid as domain sid. */
+
+                       DEBUG(0,("pdb_generate_sam_sid: Mismatched SIDs as a pdc/bdc.\n"));
+                       if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) {
+                               DEBUG(0,("pdb_generate_sam_sid: Can't re-store domain SID as a pdc/bdc.\n"));
+                               return False;
+                       }
+                       return True;
+               }
+
+               return True;
+               
+       }
+
+       /* check for an old MACHINE.SID file for backwards compatibility */
+       asprintf(&fname, "%s/MACHINE.SID", lp_private_dir());
+
+       if (read_sid_from_file(fname, global_sam_sid)) {
+               /* remember it for future reference and unlink the old MACHINE.SID */
+               if (!secrets_store_domain_sid(lp_netbios_name(), global_sam_sid)) {
+                       DEBUG(0,("pdb_generate_sam_sid: Failed to store SID from file-1.\n"));
+                       SAFE_FREE(fname);
+                       return False;
+               }
+               unlink(fname);
+               if (is_dc) {
+                       if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) {
+                               DEBUG(0,("pdb_generate_sam_sid: Failed to store domain SID from file-2.\n"));
+                               SAFE_FREE(fname);
+                               return False;
+                       }
+               }
+
+               /* Stored the old sid from MACHINE.SID successfully.*/
+               SAFE_FREE(fname);
+               return True;
+       }
+
+       SAFE_FREE(fname);
+
+       /* we don't have the SID in secrets.tdb, we will need to
+           generate one and save it */
+       generate_random_sid(global_sam_sid);
+
+       if (!secrets_store_domain_sid(lp_netbios_name(), global_sam_sid)) {
+               DEBUG(0,("pdb_generate_sam_sid: Failed to store generated machine SID-3, nb name=%s.\n", lp_netbios_name()));
+               return False;
+       }
+       if (is_dc) {
+               if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) {
+                       DEBUG(0,("pdb_generate_sam_sid: Failed to store generated domain SID-4, wg name=%s.\n", lp_workgroup()));
+                       return False;
+               }
+       }
+
+       return True;
+}   
+
+/* return our global_sam_sid */
+DOM_SID *get_global_sam_sid(void)
+{
+       if (global_sam_sid != NULL)
+               return global_sam_sid;
+       
+       /* memory for global_sam_sid is allocated in 
+          pdb_generate_sam_sid() as needed */
+
+       if (!pdb_generate_sam_sid())
+               global_sam_sid=NULL;    
+       
+       return global_sam_sid;
+}
+
diff --git a/source4/passdb/passdb.c b/source4/passdb/passdb.c
new file mode 100644 (file)
index 0000000..39e2d4c
--- /dev/null
@@ -0,0 +1,1167 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Jeremy Allison                1996-2001
+   Copyright (C) Luke Kenneth Casson Leighton  1996-1998
+   Copyright (C) Gerald (Jerry) Carter         2000-2001
+   Copyright (C) Andrew Bartlett               2001-2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/*
+ * This is set on startup - it defines the SID for this
+ * machine, and therefore the SAM database for which it is
+ * responsible.
+ */
+
+/************************************************************
+ Fill the SAM_ACCOUNT with default values.
+ ***********************************************************/
+
+static void pdb_fill_default_sam(SAM_ACCOUNT *user)
+{
+       ZERO_STRUCT(user->private); /* Don't touch the talloc context */
+
+       /* no initial methods */
+       user->methods = NULL;
+
+        /* Don't change these timestamp settings without a good reason.
+           They are important for NT member server compatibility. */
+
+       user->private.uid = user->private.gid       = -1;
+
+       user->private.logon_time            = (time_t)0;
+       user->private.pass_last_set_time    = (time_t)0;
+       user->private.pass_can_change_time  = (time_t)0;
+       user->private.logoff_time           = 
+       user->private.kickoff_time          = 
+       user->private.pass_must_change_time = get_time_t_max();
+       user->private.unknown_3 = 0x00ffffff;   /* don't know */
+       user->private.logon_divs = 168;         /* hours per week */
+       user->private.hours_len = 21;           /* 21 times 8 bits = 168 */
+       memset(user->private.hours, 0xff, user->private.hours_len); /* available at all hours */
+       user->private.unknown_5 = 0x00000000; /* don't know */
+       user->private.unknown_6 = 0x000004ec; /* don't know */
+
+       /* Some parts of samba strlen their pdb_get...() returns, 
+          so this keeps the interface unchanged for now. */
+          
+       user->private.username = "";
+       user->private.domain = "";
+       user->private.nt_username = "";
+       user->private.full_name = "";
+       user->private.home_dir = "";
+       user->private.logon_script = "";
+       user->private.profile_path = "";
+       user->private.acct_desc = "";
+       user->private.workstations = "";
+       user->private.unknown_str = "";
+       user->private.munged_dial = "";
+
+       user->private.plaintext_pw = NULL;
+
+}      
+
+static void destroy_pdb_talloc(SAM_ACCOUNT **user) 
+{
+       if (*user) {
+               data_blob_clear_free(&((*user)->private.lm_pw));
+               data_blob_clear_free(&((*user)->private.nt_pw));
+
+               if((*user)->private.plaintext_pw!=NULL)
+                       memset((*user)->private.plaintext_pw,'\0',strlen((*user)->private.plaintext_pw));
+               talloc_destroy((*user)->mem_ctx);
+               *user = NULL;
+       }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a struct sam_passwd on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS pdb_init_sam_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT **user)
+{
+       if (*user != NULL) {
+               DEBUG(0,("pdb_init_sam_talloc: SAM_ACCOUNT was non NULL\n"));
+#if 0
+               smb_panic("non-NULL pointer passed to pdb_init_sam\n");
+#endif
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!mem_ctx) {
+               DEBUG(0,("pdb_init_sam_talloc: mem_ctx was NULL!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       *user=(SAM_ACCOUNT *)talloc(mem_ctx, sizeof(SAM_ACCOUNT));
+
+       if (*user==NULL) {
+               DEBUG(0,("pdb_init_sam_talloc: error while allocating memory\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*user)->mem_ctx = mem_ctx;
+
+       (*user)->free_fn = NULL;
+
+       pdb_fill_default_sam(*user);
+       
+       return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam(SAM_ACCOUNT **user)
+{
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS nt_status;
+       
+       mem_ctx = talloc_init("passdb internal SAM_ACCOUNT allocation");
+
+       if (!mem_ctx) {
+               DEBUG(0,("pdb_init_sam: error while doing talloc_init()\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, user))) {
+               talloc_destroy(mem_ctx);
+               return nt_status;
+       }
+       
+       (*user)->free_fn = destroy_pdb_talloc;
+
+       return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_fill_sam_pw(SAM_ACCOUNT *sam_account, const struct passwd *pwd)
+{
+       GROUP_MAP map;
+
+       const char *guest_account = lp_guestaccount();
+       if (!(guest_account && *guest_account)) {
+               DEBUG(1, ("NULL guest account!?!?\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!pwd) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       pdb_fill_default_sam(sam_account);
+
+       pdb_set_username(sam_account, pwd->pw_name, PDB_SET);
+       pdb_set_fullname(sam_account, pwd->pw_gecos, PDB_SET);
+
+       pdb_set_unix_homedir(sam_account, pwd->pw_dir, PDB_SET);
+
+       pdb_set_domain (sam_account, lp_workgroup(), PDB_DEFAULT);
+
+       pdb_set_uid(sam_account, pwd->pw_uid, PDB_SET);
+       pdb_set_gid(sam_account, pwd->pw_gid, PDB_SET);
+       
+       /* When we get a proper uid -> SID and SID -> uid allocation
+          mechinism, we should call it here.  
+          
+          We can't just set this to 0 or allow it only to be filled
+          in when added to the backend, becouse the user's SID 
+          may already be in security descriptors etc.
+          
+          -- abartlet 11-May-02
+       */
+
+
+       /* Ensure this *must* be set right */
+       if (strcmp(pwd->pw_name, guest_account) == 0) {
+               if (!pdb_set_user_sid_from_rid(sam_account, DOMAIN_USER_RID_GUEST, PDB_DEFAULT)) {
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               if (!pdb_set_group_sid_from_rid(sam_account, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT)) {
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       } else {
+
+               if (!pdb_set_user_sid_from_rid(sam_account, 
+                                              fallback_pdb_uid_to_user_rid(pwd->pw_uid), PDB_SET)) {
+                       DEBUG(0,("Can't set User SID from RID!\n"));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               
+               /* call the mapping code here */
+               if(pdb_getgrgid(&map, pwd->pw_gid, MAPPING_WITHOUT_PRIV)) {
+                       if (!pdb_set_group_sid(sam_account,&map.sid, PDB_SET)){
+                               DEBUG(0,("Can't set Group SID!\n"));
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
+               } 
+               else {
+                       if (!pdb_set_group_sid_from_rid(sam_account,pdb_gid_to_group_rid(pwd->pw_gid), PDB_SET)) {
+                               DEBUG(0,("Can't set Group SID\n"));
+                               return NT_STATUS_INVALID_PARAMETER;
+                       }
+               }
+       }
+
+       /* check if this is a user account or a machine account */
+       if (pwd->pw_name[strlen(pwd->pw_name)-1] != '$')
+       {
+               pdb_set_profile_path(sam_account, 
+                                    talloc_sub_specified((sam_account)->mem_ctx, 
+                                                           lp_logon_path(), 
+                                                           pwd->pw_name, lp_netbios_name(), 
+                                                           pwd->pw_uid, pwd->pw_gid), 
+                                    PDB_DEFAULT);
+               
+               pdb_set_homedir(sam_account, 
+                               talloc_sub_specified((sam_account)->mem_ctx, 
+                                                      lp_logon_home(),
+                                                      pwd->pw_name, lp_netbios_name(), 
+                                                      pwd->pw_uid, pwd->pw_gid),
+                               PDB_DEFAULT);
+               
+               pdb_set_dir_drive(sam_account, 
+                                 talloc_sub_specified((sam_account)->mem_ctx, 
+                                                        lp_logon_drive(),
+                                                        pwd->pw_name, lp_netbios_name(), 
+                                                        pwd->pw_uid, pwd->pw_gid),
+                                 PDB_DEFAULT);
+               
+               pdb_set_logon_script(sam_account, 
+                                    talloc_sub_specified((sam_account)->mem_ctx, 
+                                                           lp_logon_script(),
+                                                           pwd->pw_name, lp_netbios_name(), 
+                                                           pwd->pw_uid, pwd->pw_gid), 
+                                    PDB_DEFAULT);
+               if (!pdb_set_acct_ctrl(sam_account, ACB_NORMAL, PDB_DEFAULT)) {
+                       DEBUG(1, ("Failed to set 'normal account' flags for user %s.\n", pwd->pw_name));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       } else {
+               if (!pdb_set_acct_ctrl(sam_account, ACB_WSTRUST, PDB_DEFAULT)) {
+                       DEBUG(1, ("Failed to set 'trusted workstation account' flags for user %s.\n", pwd->pw_name));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+       return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam_pw(SAM_ACCOUNT **new_sam_acct, const struct passwd *pwd)
+{
+       NTSTATUS nt_status;
+
+       if (!pwd) {
+               new_sam_acct = NULL;
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(new_sam_acct))) {
+               new_sam_acct = NULL;
+               return nt_status;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*new_sam_acct, pwd))) {
+               pdb_free_sam(new_sam_acct);
+               new_sam_acct = NULL;
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/**
+ * Free the contets of the SAM_ACCOUNT, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from 
+ * memory.
+ *
+ * @param user SAM_ACCOUNT to free members of.
+ **/
+
+static void pdb_free_sam_contents(SAM_ACCOUNT *user)
+{
+
+       /* Kill off sensitive data.  Free()ed by the
+          talloc mechinism */
+
+       data_blob_clear_free(&(user->private.lm_pw));
+       data_blob_clear_free(&(user->private.nt_pw));
+       if (user->private.plaintext_pw!=NULL)
+               memset(user->private.plaintext_pw,'\0',strlen(user->private.plaintext_pw));
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS pdb_reset_sam(SAM_ACCOUNT *user)
+{
+       if (user == NULL) {
+               DEBUG(0,("pdb_reset_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+               smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       pdb_free_sam_contents(user);
+
+       pdb_fill_default_sam(user);
+
+       return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT and the member pointers.
+ ***********************************************************/
+
+NTSTATUS pdb_free_sam(SAM_ACCOUNT **user)
+{
+       if (*user == NULL) {
+               DEBUG(0,("pdb_free_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+               smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       pdb_free_sam_contents(*user);
+       
+       if ((*user)->free_fn) {
+               (*user)->free_fn(user);
+       }
+
+       return NT_STATUS_OK;    
+}
+
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+       static fstring acct_str;
+       size_t i = 0;
+
+       acct_str[i++] = '[';
+
+       if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+       if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+       if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+       if (acct_ctrl & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
+       if (acct_ctrl & ACB_NORMAL   ) acct_str[i++] = 'U';
+       if (acct_ctrl & ACB_MNS      ) acct_str[i++] = 'M';
+       if (acct_ctrl & ACB_WSTRUST  ) acct_str[i++] = 'W';
+       if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+       if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+       if (acct_ctrl & ACB_PWNOEXP  ) acct_str[i++] = 'X';
+       if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+       for ( ; i < length - 2 ; i++ )
+               acct_str[i] = ' ';
+
+       i = length - 2;
+       acct_str[i++] = ']';
+       acct_str[i++] = '\0';
+
+       return acct_str;
+}     
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 pdb_decode_acct_ctrl(const char *p)
+{
+       uint16 acct_ctrl = 0;
+       BOOL finished = False;
+
+       /*
+        * Check if the account type bits have been encoded after the
+        * NT password (in the form [NDHTUWSLXI]).
+        */
+
+       if (*p != '[')
+               return 0;
+
+       for (p++; *p && !finished; p++) {
+               switch (*p) {
+                       case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+                       case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+                       case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+                       case 'T': { acct_ctrl |= ACB_TEMPDUP  ; break; /* 'T'emp account. */ } 
+                       case 'U': { acct_ctrl |= ACB_NORMAL   ; break; /* 'U'ser account (normal). */ } 
+                       case 'M': { acct_ctrl |= ACB_MNS      ; break; /* 'M'NS logon user account. What is this ? */ } 
+                       case 'W': { acct_ctrl |= ACB_WSTRUST  ; break; /* 'W'orkstation account. */ } 
+                       case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ } 
+                       case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } 
+                       case 'X': { acct_ctrl |= ACB_PWNOEXP  ; break; /* No 'X'piry on password */ } 
+                       case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+            case ' ': { break; }
+                       case ':':
+                       case '\n':
+                       case '\0': 
+                       case ']':
+                       default:  { finished = True; }
+               }
+       }
+
+       return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void pdb_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+       if (pwd != NULL) {
+               int i;
+               for (i = 0; i < 16; i++)
+                       slprintf(&p[i*2], 3, "%02X", pwd[i]);
+       } else {
+               if (acct_ctrl & ACB_PWNOTREQ)
+                       safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+               else
+                       safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+       }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL pdb_gethexpwd(const char *p, unsigned char *pwd)
+{
+       int i;
+       unsigned char   lonybble, hinybble;
+       const char      *hexchars = "0123456789ABCDEF";
+       char           *p1, *p2;
+       
+       if (!p)
+               return (False);
+       
+       for (i = 0; i < 32; i += 2) {
+               hinybble = toupper(p[i]);
+               lonybble = toupper(p[i + 1]);
+
+               p1 = strchr(hexchars, hinybble);
+               p2 = strchr(hexchars, lonybble);
+
+               if (!p1 || !p2)
+                       return (False);
+
+               hinybble = PTR_DIFF(p1, hexchars);
+               lonybble = PTR_DIFF(p2, hexchars);
+
+               pwd[i / 2] = (hinybble << 4) | lonybble;
+       }
+       return (True);
+}
+
+/*******************************************************************
+ Converts NT user RID to a UNIX uid.
+ ********************************************************************/
+
+static int algorithmic_rid_base(void)
+{
+       static int rid_offset = 0;
+
+       if (rid_offset != 0)
+               return rid_offset;
+
+       rid_offset = lp_algorithmic_rid_base();
+
+       if (rid_offset < BASE_RID) {  
+               /* Try to prevent admin foot-shooting, we can't put algorithmic
+                  rids below 1000, that's the 'well known RIDs' on NT */
+               DEBUG(0, ("'algorithmic rid base' must be equal to or above %ld\n", BASE_RID));
+               rid_offset = BASE_RID;
+       }
+       if (rid_offset & 1) {
+               DEBUG(0, ("algorithmic rid base must be even\n"));
+               rid_offset += 1;
+       }
+       return rid_offset;
+}
+
+
+uid_t fallback_pdb_user_rid_to_uid(uint32 user_rid)
+{
+       int rid_offset = algorithmic_rid_base();
+       return (uid_t)(((user_rid & (~USER_RID_TYPE))- rid_offset)/RID_MULTIPLIER);
+}
+
+
+/*******************************************************************
+ converts UNIX uid to an NT User RID.
+ ********************************************************************/
+
+uint32 fallback_pdb_uid_to_user_rid(uid_t uid)
+{
+       int rid_offset = algorithmic_rid_base();
+       return (((((uint32)uid)*RID_MULTIPLIER) + rid_offset) | USER_RID_TYPE);
+}
+
+/*******************************************************************
+ Converts NT group RID to a UNIX gid.
+ ********************************************************************/
+
+gid_t pdb_group_rid_to_gid(uint32 group_rid)
+{
+       int rid_offset = algorithmic_rid_base();
+       return (gid_t)(((group_rid & (~GROUP_RID_TYPE))- rid_offset)/RID_MULTIPLIER);
+}
+
+/*******************************************************************
+ converts NT Group RID to a UNIX uid.
+ warning: you must not call that function only
+ you must do a call to the group mapping first.
+ there is not anymore a direct link between the gid and the rid.
+ ********************************************************************/
+
+uint32 pdb_gid_to_group_rid(gid_t gid)
+{
+       int rid_offset = algorithmic_rid_base();
+       return (((((uint32)gid)*RID_MULTIPLIER) + rid_offset) | GROUP_RID_TYPE);
+}
+
+/*******************************************************************
+ Decides if a RID is a well known RID.
+ ********************************************************************/
+
+static BOOL pdb_rid_is_well_known(uint32 rid)
+{
+       /* Not using rid_offset here, becouse this is the actual
+          NT fixed value (1000) */
+
+       return (rid < BASE_RID);
+}
+
+/*******************************************************************
+ Decides if a RID is a user or group RID.
+ ********************************************************************/
+
+BOOL pdb_rid_is_user(uint32 rid)
+{
+  /* lkcl i understand that NT attaches an enumeration to a RID
+   * such that it can be identified as either a user, group etc
+   * type.  there are 5 such categories, and they are documented.
+   */
+       /* However, they are not in the RID, just somthing you can query
+          seperatly.  Sorry luke :-) */
+
+   if(pdb_rid_is_well_known(rid)) {
+      /*
+       * The only well known user RIDs are DOMAIN_USER_RID_ADMIN
+       * and DOMAIN_USER_RID_GUEST.
+       */
+     if(rid == DOMAIN_USER_RID_ADMIN || rid == DOMAIN_USER_RID_GUEST)
+       return True;
+   } else if((rid & RID_TYPE_MASK) == USER_RID_TYPE) {
+     return True;
+   }
+   return False;
+}
+
+/*******************************************************************
+ Convert a rid into a name. Used in the lookup SID rpc.
+ ********************************************************************/
+
+BOOL local_lookup_sid(DOM_SID *sid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+       uint32 rid;
+       SAM_ACCOUNT *sam_account = NULL;
+       GROUP_MAP map;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("local_lookup_sid");
+       if (!mem_ctx) {
+               DEBUG(0,("local_sid_to_gid: No memory\n"));
+               return False;
+       }
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid)){
+               DEBUG(0,("local_sid_to_gid: sid_peek_check_rid return False! SID: %s\n",
+                       sid_string_talloc(mem_ctx, &map.sid)));
+               return False;
+       }
+       talloc_destroy(mem_ctx);        
+       *psid_name_use = SID_NAME_UNKNOWN;
+       
+       DEBUG(5,("local_lookup_sid: looking up RID %u.\n", (unsigned int)rid));
+       
+       if (rid == DOMAIN_USER_RID_ADMIN) {
+               const char **admin_list = lp_admin_users(-1);
+               *psid_name_use = SID_NAME_USER;
+               if (admin_list) {
+                       const char *p = *admin_list;
+                       if(!next_token(&p, name, NULL, sizeof(fstring)))
+                               fstrcpy(name, "Administrator");
+               } else {
+                       fstrcpy(name, "Administrator");
+               }
+               return True;
+       }
+
+       /*
+        * Don't try to convert the rid to a name if 
+        * running in appliance mode
+        */
+
+       if (lp_hide_local_users())
+               return False;
+               
+       if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+               return False;
+       }
+               
+       /* This now does the 'generic' mapping in pdb_unix */
+       /* 'guest' is also handled there */
+       if (pdb_getsampwsid(sam_account, sid)) {
+               fstrcpy(name, pdb_get_username(sam_account));
+               *psid_name_use = SID_NAME_USER;
+
+               pdb_free_sam(&sam_account);
+                       
+               return True;
+       }
+
+       pdb_free_sam(&sam_account);
+               
+       if (pdb_getgrsid(&map, *sid, MAPPING_WITHOUT_PRIV)) {
+               if (map.gid!=(gid_t)-1) {
+                       DEBUG(5,("local_lookup_sid: mapped group %s to gid %u\n", map.nt_name, (unsigned int)map.gid));
+               } else {
+                       DEBUG(5,("local_lookup_sid: mapped group %s to no unix gid.  Returning name.\n", map.nt_name));
+               }
+
+               fstrcpy(name, map.nt_name);
+               *psid_name_use = map.sid_name_use;
+               return True;
+       }
+
+       if (pdb_rid_is_user(rid)) {
+               uid_t uid;
+
+               DEBUG(5, ("assuming RID %u is a user\n", (unsigned)rid));
+
+                       uid = fallback_pdb_user_rid_to_uid(rid);
+               slprintf(name, sizeof(fstring)-1, "unix_user.%u", (unsigned int)uid);   
+
+               return False;  /* Indicates that this user was 'not mapped' */
+       } else {
+               gid_t gid;
+               struct group *gr; 
+                       
+               DEBUG(5, ("assuming RID %u is a group\n", (unsigned)rid));
+
+               gid = pdb_group_rid_to_gid(rid);
+               gr = getgrgid(gid);
+                       
+               *psid_name_use = SID_NAME_ALIAS;
+                       
+               DEBUG(5,("local_lookup_sid: looking up gid %u %s\n", (unsigned int)gid,
+                        gr ? "succeeded" : "failed" ));
+                       
+               if(!gr) {
+                       slprintf(name, sizeof(fstring)-1, "unix_group.%u", (unsigned int)gid);
+                       return False; /* Indicates that this group was 'not mapped' */
+               }
+                       
+               fstrcpy( name, gr->gr_name);
+                       
+               DEBUG(5,("local_lookup_sid: found group %s for rid %u\n", name,
+                        (unsigned int)rid ));
+               return True;   
+       }
+}
+
+/*******************************************************************
+ Convert a name into a SID. Used in the lookup name rpc.
+ ********************************************************************/
+
+BOOL local_lookup_name(const char *c_user, DOM_SID *psid, enum SID_NAME_USE *psid_name_use)
+{
+       extern DOM_SID global_sid_World_Domain;
+       DOM_SID local_sid;
+       fstring user;
+       SAM_ACCOUNT *sam_account = NULL;
+       struct group *grp;
+       GROUP_MAP map;
+               
+       *psid_name_use = SID_NAME_UNKNOWN;
+
+       /*
+        * user may be quoted a const string, and map_username and
+        * friends can modify it. Make a modifiable copy. JRA.
+        */
+
+       fstrcpy(user, c_user);
+
+       sid_copy(&local_sid, get_global_sam_sid());
+
+       /*
+        * Special case for MACHINE\Everyone. Map to the world_sid.
+        */
+
+       if(strequal(user, "Everyone")) {
+               sid_copy( psid, &global_sid_World_Domain);
+               sid_append_rid(psid, 0);
+               *psid_name_use = SID_NAME_ALIAS;
+               return True;
+       }
+
+       /* 
+        * Don't lookup local unix users if running in appliance mode
+        */
+       if (lp_hide_local_users()) 
+               return False;
+
+       if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+               return False;
+       }
+       
+       if (pdb_getsampwnam(sam_account, user)) {
+               sid_copy(psid, pdb_get_user_sid(sam_account));
+               *psid_name_use = SID_NAME_USER;
+               
+               pdb_free_sam(&sam_account);
+               return True;
+       }
+
+       pdb_free_sam(&sam_account);
+
+       /*
+        * Maybe it was a group ?
+        */
+
+       /* check if it's a mapped group */
+       if (pdb_getgrnam(&map, user, MAPPING_WITHOUT_PRIV)) {
+               /* yes it's a mapped group */
+               sid_copy(&local_sid, &map.sid);
+               *psid_name_use = map.sid_name_use;
+       } else {
+               /* it's not a mapped group */
+               grp = getgrnam(user);
+               if(!grp)
+                       return False;
+               
+               /* 
+                *check if it's mapped, if it is reply it doesn't exist
+                *
+                * that's to prevent this case:
+                *
+                * unix group ug is mapped to nt group ng
+                * someone does a lookup on ug
+                * we must not reply as it doesn't "exist" anymore
+                * for NT. For NT only ng exists.
+                * JFM, 30/11/2001
+                */
+               
+               if (pdb_getgrgid(&map, grp->gr_gid, MAPPING_WITHOUT_PRIV)){
+                       return False;
+               }
+               
+               sid_append_rid( &local_sid, pdb_gid_to_group_rid(grp->gr_gid));
+               *psid_name_use = SID_NAME_ALIAS;
+       }
+
+       sid_copy( psid, &local_sid);
+
+       return True;
+}
+
+/****************************************************************************
+ Convert a uid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+       struct passwd *pass;
+       SAM_ACCOUNT *sam_user = NULL;
+       fstring str; /* sid string buffer */
+
+       sid_copy(psid, get_global_sam_sid());
+
+       if((pass = getpwuid_alloc(uid))) {
+
+               if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) {
+                       passwd_free(&pass);
+                       return NULL;
+               }
+               
+               if (pdb_getsampwnam(sam_user, pass->pw_name)) {
+                       sid_copy(psid, pdb_get_user_sid(sam_user));
+               } else {
+                       sid_append_rid(psid, fallback_pdb_uid_to_user_rid(uid));
+               }
+
+               DEBUG(10,("local_uid_to_sid: uid %u -> SID (%s) (%s).\n", 
+                         (unsigned)uid, sid_to_string( str, psid),
+                         pass->pw_name ));
+
+               passwd_free(&pass);
+               pdb_free_sam(&sam_user);
+       
+       } else {
+               sid_append_rid(psid, fallback_pdb_uid_to_user_rid(uid));
+
+               DEBUG(10,("local_uid_to_sid: uid %u -> SID (%s) (unknown user).\n", 
+                         (unsigned)uid, sid_to_string( str, psid)));
+       }
+
+       return psid;
+}
+
+/****************************************************************************
+ Convert a SID to uid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_uid(uid_t *puid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+       fstring str;
+       SAM_ACCOUNT *sam_user = NULL;
+
+       *name_type = SID_NAME_UNKNOWN;
+
+       if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user)))
+               return False;
+       
+       if (pdb_getsampwsid(sam_user, psid)) {
+               
+               if (!IS_SAM_SET(sam_user,PDB_UID)&&!IS_SAM_CHANGED(sam_user,PDB_UID)) {
+                       pdb_free_sam(&sam_user);
+                       return False;
+               }
+
+               *puid = pdb_get_uid(sam_user);
+                       
+               DEBUG(10,("local_sid_to_uid: SID %s -> uid (%u) (%s).\n", sid_to_string( str, psid),
+                         (unsigned int)*puid, pdb_get_username(sam_user)));
+               pdb_free_sam(&sam_user);
+       } else {
+
+               DOM_SID dom_sid;
+               uint32 rid;
+               GROUP_MAP map;
+
+               pdb_free_sam(&sam_user);  
+
+               if (pdb_getgrsid(&map, *psid, MAPPING_WITHOUT_PRIV)) {
+                       DEBUG(3, ("local_sid_to_uid: SID '%s' is a group, not a user... \n", sid_to_string(str, psid)));
+                       /* It's a group, not a user... */
+                       return False;
+               }
+
+               sid_copy(&dom_sid, psid);
+               if (!sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
+                       DEBUG(3, ("sid_peek_rid failed - sid '%s' is not in our domain\n", sid_to_string(str, psid)));
+                       return False;
+               }
+
+               if (!pdb_rid_is_user(rid)) {
+                       DEBUG(3, ("local_sid_to_uid: sid '%s' cannot be mapped to a uid algorithmicly becouse it is a group\n", sid_to_string(str, psid)));
+                       return False;
+               }
+               
+               *puid = fallback_pdb_user_rid_to_uid(rid);
+               
+               DEBUG(5,("local_sid_to_uid: SID %s algorithmicly mapped to %ld mapped becouse SID was not found in passdb.\n", 
+                        sid_to_string(str, psid), (signed long int)(*puid)));
+       }
+
+       *name_type = SID_NAME_USER;
+
+       return True;
+}
+
+/****************************************************************************
+ Convert a gid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+       GROUP_MAP map;
+
+       sid_copy(psid, get_global_sam_sid());
+       
+       if (pdb_getgrgid(&map, gid, MAPPING_WITHOUT_PRIV)) {
+               sid_copy(psid, &map.sid);
+       } 
+       else {
+               sid_append_rid(psid, pdb_gid_to_group_rid(gid));
+       }
+
+       return psid;
+}
+
+/****************************************************************************
+ Convert a SID to gid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_gid(gid_t *pgid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+       fstring str;
+       GROUP_MAP map;
+
+       *name_type = SID_NAME_UNKNOWN;
+
+       /*
+        * We can only convert to a gid if this is our local
+        * Domain SID (ie. we are the controling authority).
+        *
+        * Or in the Builtin SID too. JFM, 11/30/2001
+        */
+
+       if (pdb_getgrsid(&map, *psid, MAPPING_WITHOUT_PRIV)) {
+               
+               /* the SID is in the mapping table but not mapped */
+               if (map.gid==(gid_t)-1)
+                       return False;
+
+               *pgid = map.gid;
+               *name_type = map.sid_name_use;
+               DEBUG(10,("local_sid_to_gid: mapped SID %s (%s) -> gid (%u).\n", 
+                         sid_to_string( str, psid),
+                         map.nt_name, (unsigned int)*pgid));
+
+       } else {
+               uint32 rid;
+               SAM_ACCOUNT *sam_user = NULL;
+               if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user)))
+                       return False;
+               
+               if (pdb_getsampwsid(sam_user, psid)) {
+                       return False;
+                       pdb_free_sam(&sam_user);
+               }
+
+               pdb_free_sam(&sam_user);
+
+               if (!sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
+                       DEBUG(3, ("sid_peek_rid failed - sid '%s' is not in our domain\n", sid_to_string(str, psid)));
+                       return False;
+               }
+
+               if (pdb_rid_is_user(rid))
+                       return False;
+               
+               *pgid = pdb_group_rid_to_gid(rid);
+               *name_type = SID_NAME_ALIAS;
+               DEBUG(10,("local_sid_to_gid: SID %s -> gid (%u).\n", sid_to_string( str, psid),
+                         (unsigned int)*pgid));
+       }
+       
+       return True;
+}
+
+/*************************************************************
+ Change a password entry in the local smbpasswd file.
+
+It is currently being called by SWAT and by smbpasswd.
+ --jerry
+ *************************************************************/
+
+BOOL local_password_change(const char *user_name, int local_flags,
+                          const char *new_passwd, 
+                          char *err_str, size_t err_str_len,
+                          char *msg_str, size_t msg_str_len)
+{
+       struct passwd  *pwd = NULL;
+       SAM_ACCOUNT     *sam_pass=NULL;
+       uint16 other_acb;
+
+       *err_str = '\0';
+       *msg_str = '\0';
+
+       /* Get the smb passwd entry for this user */
+       pdb_init_sam(&sam_pass);
+       if(!pdb_getsampwnam(sam_pass, user_name)) {
+               pdb_free_sam(&sam_pass);
+               
+               if (local_flags & LOCAL_ADD_USER) {
+                       pwd = getpwnam_alloc(user_name);
+               } else if (local_flags & LOCAL_DELETE_USER) {
+                       /* Might not exist in /etc/passwd */
+               } else {
+                       slprintf(err_str, err_str_len-1,"Failed to find entry for user %s.\n", user_name);
+                       return False;
+               }
+               
+               if (pwd) {
+                       /* Local user found, so init from this */
+                       if (!NT_STATUS_IS_OK(pdb_init_sam_pw(&sam_pass, pwd))){
+                               slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name);
+                               passwd_free(&pwd);
+                               return False;
+                       }
+               
+                       passwd_free(&pwd);
+               } else {
+                       if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_pass))){
+                               slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name);
+                               return False;
+                       }
+
+                       if (!pdb_set_username(sam_pass, user_name, PDB_CHANGED)) {
+                               slprintf(err_str, err_str_len - 1, "Failed to set username for user %s.\n", user_name);
+                               pdb_free_sam(&sam_pass);
+                               return False;
+                       }
+               }
+       } else {
+               /* the entry already existed */
+               local_flags &= ~LOCAL_ADD_USER;
+       }
+
+       /* the 'other' acb bits not being changed here */
+       other_acb =  (pdb_get_acct_ctrl(sam_pass) & (!(ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST|ACB_NORMAL)));
+       if (local_flags & LOCAL_TRUST_ACCOUNT) {
+               if (!pdb_set_acct_ctrl(sam_pass, ACB_WSTRUST | other_acb, PDB_CHANGED) ) {
+                       slprintf(err_str, err_str_len - 1, "Failed to set 'trusted workstation account' flags for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+               if (!pdb_set_acct_ctrl(sam_pass, ACB_DOMTRUST | other_acb, PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len - 1, "Failed to set 'domain trust account' flags for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       } else {
+               if (!pdb_set_acct_ctrl(sam_pass, ACB_NORMAL | other_acb, PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len - 1, "Failed to set 'normal account' flags for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       }
+
+       /*
+        * We are root - just write the new password
+        * and the valid last change time.
+        */
+
+       if (local_flags & LOCAL_DISABLE_USER) {
+               if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_DISABLED, PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len-1, "Failed to set 'disabled' flag for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       } else if (local_flags & LOCAL_ENABLE_USER) {
+               if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       }
+       
+       if (local_flags & LOCAL_SET_NO_PASSWORD) {
+               if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_PWNOTREQ, PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len-1, "Failed to set 'no password required' flag for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       } else if (local_flags & LOCAL_SET_PASSWORD) {
+               /*
+                * If we're dealing with setting a completely empty user account
+                * ie. One with a password of 'XXXX', but not set disabled (like
+                * an account created from scratch) then if the old password was
+                * 'XX's then getsmbpwent will have set the ACB_DISABLED flag.
+                * We remove that as we're giving this user their first password
+                * and the decision hasn't really been made to disable them (ie.
+                * don't create them disabled). JRA.
+                */
+               if ((pdb_get_lanman_passwd(sam_pass)==NULL) && (pdb_get_acct_ctrl(sam_pass)&ACB_DISABLED)) {
+                       if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+                               slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+                               pdb_free_sam(&sam_pass);
+                               return False;
+                       }
+               }
+               if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_PWNOTREQ), PDB_CHANGED)) {
+                       slprintf(err_str, err_str_len-1, "Failed to unset 'no password required' flag for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+               
+               if (!pdb_set_plaintext_passwd (sam_pass, new_passwd)) {
+                       slprintf(err_str, err_str_len-1, "Failed to set password for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       }       
+
+       if (local_flags & LOCAL_ADD_USER) {
+               if (pdb_add_sam_account(sam_pass)) {
+                       slprintf(msg_str, msg_str_len-1, "Added user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return True;
+               } else {
+                       slprintf(err_str, err_str_len-1, "Failed to add entry for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+       } else if (local_flags & LOCAL_DELETE_USER) {
+               if (!pdb_delete_sam_account(sam_pass)) {
+                       slprintf(err_str,err_str_len-1, "Failed to delete entry for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+               slprintf(msg_str, msg_str_len-1, "Deleted user %s.\n", user_name);
+       } else {
+               if(!pdb_update_sam_account(sam_pass)) {
+                       slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n", user_name);
+                       pdb_free_sam(&sam_pass);
+                       return False;
+               }
+               if(local_flags & LOCAL_DISABLE_USER)
+                       slprintf(msg_str, msg_str_len-1, "Disabled user %s.\n", user_name);
+               else if (local_flags & LOCAL_ENABLE_USER)
+                       slprintf(msg_str, msg_str_len-1, "Enabled user %s.\n", user_name);
+               else if (local_flags & LOCAL_SET_NO_PASSWORD)
+                       slprintf(msg_str, msg_str_len-1, "User %s password set to none.\n", user_name);
+       }
+
+       pdb_free_sam(&sam_pass);
+       return True;
+}
diff --git a/source4/passdb/pdb_compat.c b/source4/passdb/pdb_compat.c
new file mode 100644 (file)
index 0000000..0bd003f
--- /dev/null
@@ -0,0 +1,115 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_ACCOUNT access routines
+   Copyright (C) Jeremy Allison                1996-2001
+   Copyright (C) Luke Kenneth Casson Leighton  1996-1998
+   Copyright (C) Gerald (Jerry) Carter         2000-2001
+   Copyright (C) Andrew Bartlett               2001-2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+uint32 pdb_get_user_rid (const SAM_ACCOUNT *sampass)
+{
+       uint32 u_rid;
+
+       if (sampass)
+               if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_user_sid(sampass),&u_rid))
+                       return u_rid;
+       
+       return (0);
+}
+
+uint32 pdb_get_group_rid (const SAM_ACCOUNT *sampass)
+{
+       uint32 g_rid;
+
+       if (sampass)
+               if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_group_sid(sampass),&g_rid))
+                       return g_rid;
+       return (0);
+}
+
+BOOL pdb_set_user_sid_from_rid (SAM_ACCOUNT *sampass, uint32 rid, enum pdb_value_state flag)
+{
+       DOM_SID u_sid;
+       const DOM_SID *global_sam_sid;
+       TALLOC_CTX *mem_ctx;
+       
+       if (!sampass)
+               return False;
+
+       if (!(global_sam_sid = get_global_sam_sid())) {
+               DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+               return False;
+       }
+
+       sid_copy(&u_sid, global_sam_sid);
+
+       if (!sid_append_rid(&u_sid, rid))
+               return False;
+
+       if (!pdb_set_user_sid(sampass, &u_sid, flag))
+               return False;
+       mem_ctx = talloc_init("pdb_set_user_sid_from_rid");
+       if (!mem_ctx) {
+               DEBUG(1, ("pdb_set_user_sid_from_rid: No memory\n"));
+               return False;
+       }
+       DEBUG(10, ("pdb_set_user_sid_from_rid:\n\tsetting user sid %s from rid %d\n", 
+                   sid_string_talloc(mem_ctx, &u_sid),rid));
+       talloc_destroy(mem_ctx);
+       return True;
+}
+
+BOOL pdb_set_group_sid_from_rid (SAM_ACCOUNT *sampass, uint32 grid, enum pdb_value_state flag)
+{
+       DOM_SID g_sid;
+       const DOM_SID *global_sam_sid;
+       TALLOC_CTX *mem_ctx;
+
+       if (!sampass)
+               return False;
+       
+       if (!(global_sam_sid = get_global_sam_sid())) {
+               DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+               return False;
+       }
+
+       sid_copy(&g_sid, global_sam_sid);
+       
+       if (!sid_append_rid(&g_sid, grid))
+               return False;
+
+       if (!pdb_set_group_sid(sampass, &g_sid, flag))
+               return False;
+
+       mem_ctx = talloc_init("pdb_set_user_sid_from_rid");
+       if (!mem_ctx) {
+               DEBUG(1, ("pdb_set_user_sid_from_rid: No memory\n"));
+               return False;
+       }
+       DEBUG(10, ("pdb_set_group_sid_from_rid:\n\tsetting group sid %s from rid %d\n", 
+                   sid_string_talloc(mem_ctx, &g_sid), grid));
+       talloc_destroy(mem_ctx);
+       return True;
+}
+
diff --git a/source4/passdb/pdb_get_set.c b/source4/passdb/pdb_get_set.c
new file mode 100644 (file)
index 0000000..27aa592
--- /dev/null
@@ -0,0 +1,1123 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_ACCOUNT access routines
+   Copyright (C) Jeremy Allison                1996-2001
+   Copyright (C) Luke Kenneth Casson Leighton  1996-1998
+   Copyright (C) Gerald (Jerry) Carter         2000-2001
+   Copyright (C) Andrew Bartlett               2001-2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/**
+ * @todo Redefine this to NULL, but this changes the API becouse
+ *       much of samba assumes that the pdb_get...() funtions 
+ *       return pstrings.  (ie not null-pointers).
+ *       See also pdb_fill_default_sam().
+ */
+
+#define PDB_NOT_QUITE_NULL ""
+
+/*********************************************************************
+ Collection of get...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+uint16 pdb_get_acct_ctrl (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.acct_ctrl);
+       else
+               return (ACB_DISABLED);
+}
+
+time_t pdb_get_logon_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.logon_time);
+       else
+               return (0);
+}
+
+time_t pdb_get_logoff_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.logoff_time);
+       else
+               return (-1);
+}
+
+time_t pdb_get_kickoff_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.kickoff_time);
+       else
+               return (-1);
+}
+
+time_t pdb_get_pass_last_set_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.pass_last_set_time);
+       else
+               return (-1);
+}
+
+time_t pdb_get_pass_can_change_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.pass_can_change_time);
+       else
+               return (-1);
+}
+
+time_t pdb_get_pass_must_change_time (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.pass_must_change_time);
+       else
+               return (-1);
+}
+
+uint16 pdb_get_logon_divs (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.logon_divs);
+       else
+               return (-1);
+}
+
+uint32 pdb_get_hours_len (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.hours_len);
+       else
+               return (-1);
+}
+
+const uint8* pdb_get_hours (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.hours);
+       else
+               return (NULL);
+}
+
+const uint8* pdb_get_nt_passwd (const SAM_ACCOUNT *sampass)
+{
+       if (sampass) {
+               SMB_ASSERT((!sampass->private.nt_pw.data) 
+                          || sampass->private.nt_pw.length == NT_HASH_LEN);
+               return ((uint8*)sampass->private.nt_pw.data);
+       }
+       else
+               return (NULL);
+}
+
+const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass)
+{
+       if (sampass) {
+               SMB_ASSERT((!sampass->private.lm_pw.data) 
+                          || sampass->private.lm_pw.length == LM_HASH_LEN);
+               return ((uint8*)sampass->private.lm_pw.data);
+       }
+       else
+               return (NULL);
+}
+
+/* Return the plaintext password if known.  Most of the time
+   it isn't, so don't assume anything magic about this function.
+   
+   Used to pass the plaintext to passdb backends that might 
+   want to store more than just the NTLM hashes.
+*/
+const char* pdb_get_plaintext_passwd (const SAM_ACCOUNT *sampass)
+{
+       if (sampass) {
+               return (sampass->private.plaintext_pw);
+       }
+       else
+               return (NULL);
+}
+const DOM_SID *pdb_get_user_sid(const SAM_ACCOUNT *sampass)
+{
+       if (sampass) 
+               return &sampass->private.user_sid;
+       else
+               return (NULL);
+}
+
+const DOM_SID *pdb_get_group_sid(const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return &sampass->private.group_sid;
+       else    
+               return (NULL);
+}      
+
+/**
+ * Get flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @return the flags indicating the members initialised in the struct.
+ **/
+enum pdb_value_state pdb_get_init_flags (const SAM_ACCOUNT *sampass, enum pdb_elements element)
+{
+       enum pdb_value_state ret = PDB_DEFAULT;
+       
+        if (!sampass || !sampass->private.change_flags || !sampass->private.set_flags)
+               return ret;
+               
+        if (bitmap_query(sampass->private.set_flags, element)) {
+               DEBUG(10, ("element %d: SET\n", element)); 
+               ret = PDB_SET;
+       }
+               
+        if (bitmap_query(sampass->private.change_flags, element)) {
+               DEBUG(10, ("element %d: CHANGED\n", element)); 
+               ret = PDB_CHANGED;
+       }
+
+       if (ret == PDB_DEFAULT) {
+               DEBUG(10, ("element %d: DEFAULT\n", element)); 
+       }
+
+        return ret;
+}
+
+uid_t pdb_get_uid (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.uid);
+       else
+               return (-1);
+}
+
+gid_t pdb_get_gid (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.gid);
+       else
+               return (-1);
+}
+
+const char* pdb_get_username (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.username);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_domain (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.domain);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_nt_username (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.nt_username);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_fullname (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.full_name);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_homedir (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.home_dir);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_unix_homedir (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.unix_home_dir);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_dir_drive (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.dir_drive);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_logon_script (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.logon_script);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_profile_path (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.profile_path);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_acct_desc (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.acct_desc);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_workstations (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.workstations);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_unknown_str (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.unknown_str);
+       else
+               return (NULL);
+}
+
+const char* pdb_get_munged_dial (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.munged_dial);
+       else
+               return (NULL);
+}
+
+uint32 pdb_get_unknown_3 (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.unknown_3);
+       else
+               return (-1);
+}
+
+uint32 pdb_get_unknown_5 (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.unknown_5);
+       else
+               return (-1);
+}
+
+uint32 pdb_get_unknown_6 (const SAM_ACCOUNT *sampass)
+{
+       if (sampass)
+               return (sampass->private.unknown_6);
+       else
+               return (-1);
+}
+
+/*********************************************************************
+ Collection of set...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+BOOL pdb_set_acct_ctrl (SAM_ACCOUNT *sampass, uint16 acct_ctrl, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+               
+       sampass->private.acct_ctrl = acct_ctrl;
+
+       return pdb_set_init_flags(sampass, PDB_ACCTCTRL, flag);
+}
+
+BOOL pdb_set_logon_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.logon_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_LOGONTIME, flag);
+}
+
+BOOL pdb_set_logoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.logoff_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_LOGOFFTIME, flag);
+}
+
+BOOL pdb_set_kickoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.kickoff_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_KICKOFFTIME, flag);
+}
+
+BOOL pdb_set_pass_can_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.pass_can_change_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_CANCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_must_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.pass_must_change_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_MUSTCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_last_set_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.pass_last_set_time = mytime;
+
+       return pdb_set_init_flags(sampass, PDB_PASSLASTSET, flag);
+}
+
+BOOL pdb_set_hours_len (SAM_ACCOUNT *sampass, uint32 len, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.hours_len = len;
+
+       return pdb_set_init_flags(sampass, PDB_HOURSLEN, flag);
+}
+
+BOOL pdb_set_logon_divs (SAM_ACCOUNT *sampass, uint16 hours, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.logon_divs = hours;
+
+       return pdb_set_init_flags(sampass, PDB_LOGONDIVS, flag);
+}
+
+/**
+ * Set flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @param flag The *new* flag to be set.  Old flags preserved
+ *             this flag is only added.  
+ **/
+BOOL pdb_set_init_flags (SAM_ACCOUNT *sampass, enum pdb_elements element, enum pdb_value_state value_flag)
+{
+        if (!sampass || !sampass->mem_ctx)
+                return False;
+
+        if (!sampass->private.set_flags) {
+               if ((sampass->private.set_flags = 
+                       bitmap_talloc(sampass->mem_ctx, 
+                                       PDB_COUNT))==NULL) {
+                       DEBUG(0,("bitmap_talloc failed\n"));
+                       return False;
+               }
+        }
+        if (!sampass->private.change_flags) {
+               if ((sampass->private.change_flags = 
+                       bitmap_talloc(sampass->mem_ctx, 
+                                       PDB_COUNT))==NULL) {
+                       DEBUG(0,("bitmap_talloc failed\n"));
+                       return False;
+               }
+        }
+        
+        switch(value_flag) {
+               case PDB_CHANGED:
+                       if (!bitmap_set(sampass->private.change_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+                               return False;
+                       }
+                       if (!bitmap_set(sampass->private.set_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in set_falgs.\n",element));
+                               return False;
+                       }
+                       DEBUG(10, ("element %d -> now CHANGED\n", element)); 
+                       break;
+               case PDB_SET:
+                       if (!bitmap_clear(sampass->private.change_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+                               return False;
+                       }
+                       if (!bitmap_set(sampass->private.set_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in set_falgs.\n",element));
+                               return False;
+                       }
+                       DEBUG(10, ("element %d -> now SET\n", element)); 
+                       break;
+               case PDB_DEFAULT:
+               default:
+                       if (!bitmap_clear(sampass->private.change_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+                               return False;
+                       }
+                       if (!bitmap_clear(sampass->private.set_flags, element)) {
+                               DEBUG(0,("Can't set flag: %d in set_falgs.\n",element));
+                               return False;
+                       }
+                       DEBUG(10, ("element %d -> now DEFAULT\n", element)); 
+                       break;
+       }
+
+        return True;
+}
+
+BOOL pdb_set_uid (SAM_ACCOUNT *sampass, const uid_t uid, enum pdb_value_state flag)
+{      
+       if (!sampass)
+               return False;
+       
+       DEBUG(10, ("pdb_set_uid: setting uid %d, was %d\n", 
+                  (int)uid, (int)sampass->private.uid));
+       sampass->private.uid = uid;
+       
+       return pdb_set_init_flags(sampass, PDB_UID, flag);
+}
+
+BOOL pdb_set_gid (SAM_ACCOUNT *sampass, const gid_t gid, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+               
+       DEBUG(10, ("pdb_set_gid: setting gid %d, was %d\n", 
+                  (int)gid, (int)sampass->private.gid));
+       sampass->private.gid = gid; 
+
+       return pdb_set_init_flags(sampass, PDB_GID, flag);
+}
+
+BOOL pdb_set_user_sid (SAM_ACCOUNT *sampass, DOM_SID *u_sid, enum pdb_value_state flag)
+{
+       TALLOC_CTX *mem_ctx;
+       if (!sampass || !u_sid)
+               return False;
+       
+       sid_copy(&sampass->private.user_sid, u_sid);
+
+       mem_ctx = talloc_init("pdb_set_group_sid");
+       if (!mem_ctx) return False;
+       DEBUG(10, ("pdb_set_user_sid: setting user sid %s\n", 
+                   sid_string_talloc(mem_ctx, &sampass->private.user_sid)));
+
+       talloc_destroy(mem_ctx);
+       return pdb_set_init_flags(sampass, PDB_USERSID, flag);
+}
+
+BOOL pdb_set_user_sid_from_string (SAM_ACCOUNT *sampass, fstring u_sid, enum pdb_value_state flag)
+{
+       DOM_SID new_sid;
+       
+       if (!sampass || !u_sid)
+               return False;
+
+       DEBUG(10, ("pdb_set_user_sid_from_string: setting user sid %s\n",
+                  u_sid));
+
+       if (!string_to_sid(&new_sid, u_sid)) { 
+               DEBUG(1, ("pdb_set_user_sid_from_string: %s isn't a valid SID!\n", u_sid));
+               return False;
+       }
+        
+       if (!pdb_set_user_sid(sampass, &new_sid, flag)) {
+               DEBUG(1, ("pdb_set_user_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", u_sid));
+               return False;
+       }
+
+       return True;
+}
+
+BOOL pdb_set_group_sid (SAM_ACCOUNT *sampass, DOM_SID *g_sid, enum pdb_value_state flag)
+{
+       TALLOC_CTX *mem_ctx;
+       if (!sampass || !g_sid)
+               return False;
+
+       sid_copy(&sampass->private.group_sid, g_sid);
+       
+       mem_ctx = talloc_init("pdb_set_group_sid");
+       if (!mem_ctx) return False;
+       DEBUG(10, ("pdb_set_group_sid: setting group sid %s\n", 
+                   sid_string_talloc(mem_ctx, &sampass->private.group_sid)));
+       talloc_destroy(mem_ctx);
+       return pdb_set_init_flags(sampass, PDB_GROUPSID, flag);
+}
+
+BOOL pdb_set_group_sid_from_string (SAM_ACCOUNT *sampass, fstring g_sid, enum pdb_value_state flag)
+{
+       DOM_SID new_sid;
+       if (!sampass || !g_sid)
+               return False;
+
+       DEBUG(10, ("pdb_set_group_sid_from_string: setting group sid %s\n",
+                  g_sid));
+
+       if (!string_to_sid(&new_sid, g_sid)) { 
+               DEBUG(1, ("pdb_set_group_sid_from_string: %s isn't a valid SID!\n", g_sid));
+               return False;
+       }
+        
+       if (!pdb_set_group_sid(sampass, &new_sid, flag)) {
+               DEBUG(1, ("pdb_set_group_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", g_sid));
+               return False;
+       }
+       return True;
+}
+
+/*********************************************************************
+ Set the user's UNIX name.
+ ********************************************************************/
+
+BOOL pdb_set_username(SAM_ACCOUNT *sampass, const char *username, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+       if (username) { 
+               DEBUG(10, ("pdb_set_username: setting username %s, was %s\n", username,
+                       (sampass->private.username)?(sampass->private.username):"NULL"));
+
+               sampass->private.username = talloc_strdup(sampass->mem_ctx, username);
+
+               if (!sampass->private.username) {
+                       DEBUG(0, ("pdb_set_username: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.username = PDB_NOT_QUITE_NULL;
+       }
+       
+       return pdb_set_init_flags(sampass, PDB_USERNAME, flag);
+}
+
+/*********************************************************************
+ Set the domain name.
+ ********************************************************************/
+
+BOOL pdb_set_domain(SAM_ACCOUNT *sampass, const char *domain, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (domain) { 
+               DEBUG(10, ("pdb_set_domain: setting domain %s, was %s\n", domain,
+                       (sampass->private.domain)?(sampass->private.domain):"NULL"));
+
+               sampass->private.domain = talloc_strdup(sampass->mem_ctx, domain);
+
+               if (!sampass->private.domain) {
+                       DEBUG(0, ("pdb_set_domain: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.domain = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_DOMAIN, flag);
+}
+
+/*********************************************************************
+ Set the user's NT name.
+ ********************************************************************/
+
+BOOL pdb_set_nt_username(SAM_ACCOUNT *sampass, const char *nt_username, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (nt_username) { 
+               DEBUG(10, ("pdb_set_nt_username: setting nt username %s, was %s\n", nt_username,
+                       (sampass->private.nt_username)?(sampass->private.nt_username):"NULL"));
+               sampass->private.nt_username = talloc_strdup(sampass->mem_ctx, nt_username);
+               
+               if (!sampass->private.nt_username) {
+                       DEBUG(0, ("pdb_set_nt_username: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.nt_username = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_NTUSERNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's full name.
+ ********************************************************************/
+
+BOOL pdb_set_fullname(SAM_ACCOUNT *sampass, const char *full_name, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (full_name) { 
+               DEBUG(10, ("pdb_set_full_name: setting full name %s, was %s\n", full_name,
+                       (sampass->private.full_name)?(sampass->private.full_name):"NULL"));
+       
+               sampass->private.full_name = talloc_strdup(sampass->mem_ctx, full_name);
+
+               if (!sampass->private.full_name) {
+                       DEBUG(0, ("pdb_set_fullname: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.full_name = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_FULLNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's logon script.
+ ********************************************************************/
+
+BOOL pdb_set_logon_script(SAM_ACCOUNT *sampass, const char *logon_script, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (logon_script) { 
+               DEBUG(10, ("pdb_set_logon_script: setting logon script %s, was %s\n", logon_script,
+                       (sampass->private.logon_script)?(sampass->private.logon_script):"NULL"));
+               sampass->private.logon_script = talloc_strdup(sampass->mem_ctx, logon_script);
+
+               if (!sampass->private.logon_script) {
+                       DEBUG(0, ("pdb_set_logon_script: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.logon_script = PDB_NOT_QUITE_NULL;
+       }
+       
+       return pdb_set_init_flags(sampass, PDB_LOGONSCRIPT, flag);
+}
+
+/*********************************************************************
+ Set the user's profile path.
+ ********************************************************************/
+
+BOOL pdb_set_profile_path (SAM_ACCOUNT *sampass, const char *profile_path, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (profile_path) { 
+               DEBUG(10, ("pdb_set_profile_path: setting profile path %s, was %s\n", profile_path,
+                       (sampass->private.profile_path)?(sampass->private.profile_path):"NULL"));
+               sampass->private.profile_path = talloc_strdup(sampass->mem_ctx, profile_path);
+               
+               if (!sampass->private.profile_path) {
+                       DEBUG(0, ("pdb_set_profile_path: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.profile_path = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_PROFILE, flag);
+}
+
+/*********************************************************************
+ Set the user's directory drive.
+ ********************************************************************/
+
+BOOL pdb_set_dir_drive (SAM_ACCOUNT *sampass, const char *dir_drive, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (dir_drive) { 
+               DEBUG(10, ("pdb_set_dir_drive: setting dir drive %s, was %s\n", dir_drive,
+                       (sampass->private.dir_drive)?(sampass->private.dir_drive):"NULL"));
+               sampass->private.dir_drive = talloc_strdup(sampass->mem_ctx, dir_drive);
+               
+               if (!sampass->private.dir_drive) {
+                       DEBUG(0, ("pdb_set_dir_drive: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.dir_drive = PDB_NOT_QUITE_NULL;
+       }
+       
+       return pdb_set_init_flags(sampass, PDB_DRIVE, flag);
+}
+
+/*********************************************************************
+ Set the user's home directory.
+ ********************************************************************/
+
+BOOL pdb_set_homedir (SAM_ACCOUNT *sampass, const char *home_dir, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (home_dir) { 
+               DEBUG(10, ("pdb_set_homedir: setting home dir %s, was %s\n", home_dir,
+                       (sampass->private.home_dir)?(sampass->private.home_dir):"NULL"));
+               sampass->private.home_dir = talloc_strdup(sampass->mem_ctx, home_dir);
+               
+               if (!sampass->private.home_dir) {
+                       DEBUG(0, ("pdb_set_home_dir: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.home_dir = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_SMBHOME, flag);
+}
+
+/*********************************************************************
+ Set the user's unix home directory.
+ ********************************************************************/
+
+BOOL pdb_set_unix_homedir (SAM_ACCOUNT *sampass, const char *unix_home_dir, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (unix_home_dir) { 
+               DEBUG(10, ("pdb_set_unix_homedir: setting home dir %s, was %s\n", unix_home_dir,
+                       (sampass->private.unix_home_dir)?(sampass->private.unix_home_dir):"NULL"));
+               sampass->private.unix_home_dir = talloc_strdup(sampass->mem_ctx, 
+                                                         unix_home_dir);
+               
+               if (!sampass->private.unix_home_dir) {
+                       DEBUG(0, ("pdb_set_unix_home_dir: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.unix_home_dir = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_UNIXHOMEDIR, flag);
+}
+
+/*********************************************************************
+ Set the user's account description.
+ ********************************************************************/
+
+BOOL pdb_set_acct_desc (SAM_ACCOUNT *sampass, const char *acct_desc, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (acct_desc) { 
+               sampass->private.acct_desc = talloc_strdup(sampass->mem_ctx, acct_desc);
+
+               if (!sampass->private.acct_desc) {
+                       DEBUG(0, ("pdb_set_acct_desc: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.acct_desc = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_ACCTDESC, flag);
+}
+
+/*********************************************************************
+ Set the user's workstation allowed list.
+ ********************************************************************/
+
+BOOL pdb_set_workstations (SAM_ACCOUNT *sampass, const char *workstations, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (workstations) { 
+               DEBUG(10, ("pdb_set_workstations: setting workstations %s, was %s\n", workstations,
+                       (sampass->private.workstations)?(sampass->private.workstations):"NULL"));
+               sampass->private.workstations = talloc_strdup(sampass->mem_ctx, workstations);
+
+               if (!sampass->private.workstations) {
+                       DEBUG(0, ("pdb_set_workstations: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.workstations = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_WORKSTATIONS, flag);
+}
+
+/*********************************************************************
+ Set the user's 'unknown_str', whatever the heck this actually is...
+ ********************************************************************/
+
+BOOL pdb_set_unknown_str (SAM_ACCOUNT *sampass, const char *unknown_str, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (unknown_str) { 
+               sampass->private.unknown_str = talloc_strdup(sampass->mem_ctx, unknown_str);
+               
+               if (!sampass->private.unknown_str) {
+                       DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.unknown_str = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_UNKNOWNSTR, flag);
+}
+
+/*********************************************************************
+ Set the user's dial string.
+ ********************************************************************/
+
+BOOL pdb_set_munged_dial (SAM_ACCOUNT *sampass, const char *munged_dial, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (munged_dial) { 
+               sampass->private.munged_dial = talloc_strdup(sampass->mem_ctx, munged_dial);
+               
+               if (!sampass->private.munged_dial) {
+                       DEBUG(0, ("pdb_set_munged_dial: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.munged_dial = PDB_NOT_QUITE_NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_MUNGEDDIAL, flag);
+}
+
+/*********************************************************************
+ Set the user's NT hash.
+ ********************************************************************/
+
+BOOL pdb_set_nt_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[NT_HASH_LEN], enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       data_blob_clear_free(&sampass->private.nt_pw);
+       
+       sampass->private.nt_pw = data_blob(pwd, NT_HASH_LEN);
+
+       return pdb_set_init_flags(sampass, PDB_NTPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's LM hash.
+ ********************************************************************/
+
+BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[LM_HASH_LEN], enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       data_blob_clear_free(&sampass->private.lm_pw);
+       
+       sampass->private.lm_pw = data_blob(pwd, LM_HASH_LEN);
+
+       return pdb_set_init_flags(sampass, PDB_LMPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's plaintext password only (base procedure, see helper
+ below)
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_pw_only (SAM_ACCOUNT *sampass, const char *password, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (password) { 
+               if (sampass->private.plaintext_pw!=NULL) 
+                       memset(sampass->private.plaintext_pw,'\0',strlen(sampass->private.plaintext_pw)+1);
+
+               sampass->private.plaintext_pw = talloc_strdup(sampass->mem_ctx, password);
+               
+               if (!sampass->private.plaintext_pw) {
+                       DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+                       return False;
+               }
+
+       } else {
+               sampass->private.plaintext_pw = NULL;
+       }
+
+       return pdb_set_init_flags(sampass, PDB_PLAINTEXT_PW, flag);
+}
+
+BOOL pdb_set_unknown_3 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.unknown_3 = unkn;
+       
+       return pdb_set_init_flags(sampass, PDB_UNKNOWN3, flag);
+}
+
+BOOL pdb_set_unknown_5 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.unknown_5 = unkn;
+
+       return pdb_set_init_flags(sampass, PDB_UNKNOWN5, flag);
+}
+
+BOOL pdb_set_unknown_6 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       sampass->private.unknown_6 = unkn;
+
+       return pdb_set_init_flags(sampass, PDB_UNKNOWN6, flag);
+}
+
+BOOL pdb_set_hours (SAM_ACCOUNT *sampass, const uint8 *hours, enum pdb_value_state flag)
+{
+       if (!sampass)
+               return False;
+
+       if (!hours) {
+               memset ((char *)sampass->private.hours, 0, MAX_HOURS_LEN);
+               return True;
+       }
+       
+       memcpy (sampass->private.hours, hours, MAX_HOURS_LEN);
+
+       return pdb_set_init_flags(sampass, PDB_HOURS, flag);
+}
+
+
+/* Helpful interfaces to the above */
+
+/*********************************************************************
+ Sets the last changed times and must change times for a normal
+ password change.
+ ********************************************************************/
+
+BOOL pdb_set_pass_changed_now (SAM_ACCOUNT *sampass)
+{
+       uint32 expire;
+
+       if (!sampass)
+               return False;
+       
+       if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
+               return False;
+
+       if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire) 
+           || (expire==(uint32)-1)) {
+               if (!pdb_set_pass_must_change_time (sampass, get_time_t_max(), PDB_CHANGED))
+                       return False;
+       } else {
+               if (!pdb_set_pass_must_change_time (sampass, 
+                                                   pdb_get_pass_last_set_time(sampass)
+                                                   + expire, PDB_CHANGED))
+                       return False;
+       }
+       
+       return True;
+}
+
+/*********************************************************************
+ Set the user's PLAINTEXT password.  Used as an interface to the above.
+ Also sets the last change time to NOW.
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
+{
+       uchar new_lanman_p16[16];
+       uchar new_nt_p16[16];
+
+       if (!sampass || !plaintext)
+               return False;
+       
+       nt_lm_owf_gen (plaintext, new_nt_p16, new_lanman_p16);
+
+       if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED)) 
+               return False;
+
+       if (!pdb_set_lanman_passwd (sampass, new_lanman_p16, PDB_CHANGED)) 
+               return False;
+
+       if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED)) 
+               return False;
+
+       if (!pdb_set_pass_changed_now (sampass))
+               return False;
+
+       return True;
+}
diff --git a/source4/passdb/pdb_guest.c b/source4/passdb/pdb_guest.c
new file mode 100644 (file)
index 0000000..3f0f06d
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 'Guest' password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Copyright (C) Andrew Bartlett 2003
+ * 
+ * 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"
+
+/******************************************************************
+  Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS guestsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+       NTSTATUS nt_status;
+       struct passwd *pass;
+       const char *guest_account = lp_guestaccount();
+       if (!(guest_account && *guest_account)) {
+               DEBUG(1, ("NULL guest account!?!?\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!methods) {
+               DEBUG(0,("invalid methods\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       if (!sname) {
+               DEBUG(0,("invalid name specified"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!strequal(guest_account, sname)) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+               
+       pass = getpwnam_alloc(guest_account);
+
+       nt_status = pdb_fill_sam_pw(user, pass);
+
+       passwd_free(&pass);
+       return nt_status;
+}
+
+
+/***************************************************************************
+  Search by rid
+ **************************************************************************/
+
+static NTSTATUS guestsam_getsampwrid (struct pdb_methods *methods, 
+                                SAM_ACCOUNT *user, uint32 rid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct passwd *pass = NULL;
+       const char *guest_account = lp_guestaccount();
+       if (!(guest_account && *guest_account)) {
+               DEBUG(1, ("NULL guest account!?!?\n"));
+               return nt_status;
+       }
+
+       if (!methods) {
+               DEBUG(0,("invalid methods\n"));
+               return nt_status;
+       }
+       
+       if (rid == DOMAIN_USER_RID_GUEST) {
+               pass = getpwnam_alloc(guest_account);
+               if (!pass) {
+                       DEBUG(1, ("guest account %s does not seem to exist...\n", guest_account));
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+       } else {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       nt_status = pdb_fill_sam_pw(user, pass);
+       passwd_free(&pass);
+
+       return nt_status;
+}
+
+static NTSTATUS guestsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+       uint32 rid;
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+               return NT_STATUS_NO_SUCH_USER;
+       return guestsam_getsampwrid(my_methods, user, rid);
+}
+
+NTSTATUS pdb_init_guestsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       
+       if (!pdb_context) {
+               DEBUG(0, ("invalid pdb_context specified\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+       
+       (*pdb_method)->name = "guestsam";
+       
+       (*pdb_method)->getsampwnam = guestsam_getsampwnam;
+       (*pdb_method)->getsampwsid = guestsam_getsampwsid;
+       
+       /* There's not very much to initialise here */
+       return NT_STATUS_OK;
+}
diff --git a/source4/passdb/pdb_interface.c b/source4/passdb/pdb_interface.c
new file mode 100644 (file)
index 0000000..48a039b
--- /dev/null
@@ -0,0 +1,855 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Jelmer Vernooij                       2002
+
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/** List of various built-in passdb modules */
+static const struct {
+    const char *name;
+    /* Function to create a member of the pdb_methods list */
+    pdb_init_function init;
+} builtin_pdb_init_functions[] = {
+       { "smbpasswd", pdb_init_smbpasswd },
+       { "smbpasswd_nua", pdb_init_smbpasswd_nua },
+       { "tdbsam", pdb_init_tdbsam },
+       { "tdbsam_nua", pdb_init_tdbsam_nua },
+       { "ldapsam", pdb_init_ldapsam },
+       { "ldapsam_nua", pdb_init_ldapsam_nua },
+       { "unixsam", pdb_init_unixsam },
+       { "guest", pdb_init_guestsam },
+       { "nisplussam", pdb_init_nisplussam },
+       { NULL, NULL}
+};
+
+static struct pdb_init_function_entry *backends;
+static void lazy_initialize_passdb(void);
+
+static void lazy_initialize_passdb()
+{
+       int i;
+       static BOOL initialised = False;
+       
+       if(!initialised) {
+               initialised = True;
+
+               for(i = 0; builtin_pdb_init_functions[i].name; i++) {
+                       smb_register_passdb(builtin_pdb_init_functions[i].name, builtin_pdb_init_functions[i].init, PASSDB_INTERFACE_VERSION);
+               }
+       }
+}
+
+BOOL smb_register_passdb(const char *name, pdb_init_function init, int version) 
+{
+       struct pdb_init_function_entry *entry = backends;
+
+       if(version != PASSDB_INTERFACE_VERSION)
+               return False;
+
+       DEBUG(5,("Attempting to register passdb backend %s\n", name));
+
+       /* Check for duplicates */
+       while(entry) { 
+               if(strcasecmp(name, entry->name) == 0) { 
+                       DEBUG(0,("There already is a passdb backend registered with the name %s!\n", name));
+                       return False;
+               }
+               entry = entry->next;
+       }
+
+       entry = smb_xmalloc(sizeof(struct pdb_init_function_entry));
+       entry->name = name;
+       entry->init = init;
+
+       DLIST_ADD(backends, entry);
+       DEBUG(5,("Successfully added passdb backend '%s'\n", name));
+       return True;
+}
+
+struct pdb_init_function_entry *pdb_find_backend_entry(const char *name)
+{
+       struct pdb_init_function_entry *entry = backends;
+
+       while(entry) {
+               if (strequal(entry->name, name)) return entry;
+               entry = entry->next;
+       }
+
+       return NULL;
+}
+
+static NTSTATUS context_setsampwent(struct pdb_context *context, BOOL update)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if (!context) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       context->pwent_methods = context->pdb_methods;
+
+       if (!context->pwent_methods) {
+               /* No passdbs at all */
+               return ret;
+       }
+
+       while (NT_STATUS_IS_ERR(ret = context->pwent_methods->setsampwent(context->pwent_methods, update))) {
+               context->pwent_methods = context->pwent_methods->next;
+               if (context->pwent_methods == NULL) 
+                       return NT_STATUS_UNSUCCESSFUL;
+       }
+       return ret;
+}
+
+static void context_endsampwent(struct pdb_context *context)
+{
+       if ((!context)){
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return;
+       }
+
+       if (context->pwent_methods && context->pwent_methods->endsampwent)
+               context->pwent_methods->endsampwent(context->pwent_methods);
+
+       /* So we won't get strange data when calling getsampwent now */
+       context->pwent_methods = NULL;
+}
+
+static NTSTATUS context_getsampwent(struct pdb_context *context, SAM_ACCOUNT *user)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pwent_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       /* Loop until we find something useful */
+       while (NT_STATUS_IS_ERR(ret = context->pwent_methods->getsampwent(context->pwent_methods, user))) {
+
+               context->pwent_methods->endsampwent(context->pwent_methods);
+
+               context->pwent_methods = context->pwent_methods->next;
+
+               /* All methods are checked now. There are no more entries */
+               if (context->pwent_methods == NULL)
+                       return ret;
+       
+               context->pwent_methods->setsampwent(context->pwent_methods, False);
+       }
+       user->methods = context->pwent_methods;
+       return ret;
+}
+
+static NTSTATUS context_getsampwnam(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const char *username)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *curmethods;
+       if ((!context)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       curmethods = context->pdb_methods;
+       while (curmethods){
+               if (NT_STATUS_IS_OK(ret = curmethods->getsampwnam(curmethods, sam_acct, username))) {
+                       sam_acct->methods = curmethods;
+                       return ret;
+               }
+               curmethods = curmethods->next;
+       }
+
+       return ret;
+}
+
+static NTSTATUS context_getsampwsid(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *curmethods;
+       if ((!context)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       
+       curmethods = context->pdb_methods;
+
+       while (curmethods){
+               if (NT_STATUS_IS_OK(ret = curmethods->getsampwsid(curmethods, sam_acct, sid))) {
+                       sam_acct->methods = curmethods;
+                       return ret;
+               }
+               curmethods = curmethods->next;
+       }
+
+       return ret;
+}
+
+static NTSTATUS context_add_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       /** @todo  This is where a 're-read on add' should be done */
+       /* We now add a new account to the first database listed. 
+        * Should we? */
+
+       return context->pdb_methods->add_sam_account(context->pdb_methods, sam_acct);
+}
+
+static NTSTATUS context_update_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if (!context) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       if (!sam_acct || !sam_acct->methods){
+               DEBUG(0, ("invalid sam_acct specified\n"));
+               return ret;
+       }
+
+       /** @todo  This is where a 're-read on update' should be done */
+
+       return sam_acct->methods->update_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_delete_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *pdb_selected;
+       if (!context) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       if (!sam_acct->methods){
+               pdb_selected = context->pdb_methods;
+               /* There's no passdb backend specified for this account.
+                * Try to delete it in every passdb available 
+                * Needed to delete accounts in smbpasswd that are not
+                * in /etc/passwd.
+                */
+               while (pdb_selected){
+                       if (NT_STATUS_IS_OK(ret = pdb_selected->delete_sam_account(pdb_selected, sam_acct))) {
+                               return ret;
+                       }
+                       pdb_selected = pdb_selected->next;
+               }
+               return ret;
+       }
+
+       if (!sam_acct->methods->delete_sam_account){
+               DEBUG(0,("invalid sam_acct->methods->delete_sam_account\n"));
+               return ret;
+       }
+       
+       return sam_acct->methods->delete_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_getgrsid(struct pdb_context *context,
+                                GROUP_MAP *map, DOM_SID sid, BOOL with_priv)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *curmethods;
+       if ((!context)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       curmethods = context->pdb_methods;
+       while (curmethods){
+               ret = curmethods->getgrsid(curmethods, map, sid, with_priv);
+               if (NT_STATUS_IS_OK(ret)) {
+                       map->methods = curmethods;
+                       return ret;
+               }
+               curmethods = curmethods->next;
+       }
+
+       return ret;
+}
+
+static NTSTATUS context_getgrgid(struct pdb_context *context,
+                                GROUP_MAP *map, gid_t gid, BOOL with_priv)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *curmethods;
+       if ((!context)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       curmethods = context->pdb_methods;
+       while (curmethods){
+               ret = curmethods->getgrgid(curmethods, map, gid, with_priv);
+               if (NT_STATUS_IS_OK(ret)) {
+                       map->methods = curmethods;
+                       return ret;
+               }
+               curmethods = curmethods->next;
+       }
+
+       return ret;
+}
+
+static NTSTATUS context_getgrnam(struct pdb_context *context,
+                                GROUP_MAP *map, char *name, BOOL with_priv)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       struct pdb_methods *curmethods;
+       if ((!context)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+       curmethods = context->pdb_methods;
+       while (curmethods){
+               ret = curmethods->getgrnam(curmethods, map, name, with_priv);
+               if (NT_STATUS_IS_OK(ret)) {
+                       map->methods = curmethods;
+                       return ret;
+               }
+               curmethods = curmethods->next;
+       }
+
+       return ret;
+}
+
+static NTSTATUS context_add_group_mapping_entry(struct pdb_context *context,
+                                               GROUP_MAP *map)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       return context->pdb_methods->add_group_mapping_entry(context->pdb_methods,
+                                                            map);
+}
+
+static NTSTATUS context_update_group_mapping_entry(struct pdb_context *context,
+                                                  GROUP_MAP *map)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       return context->
+               pdb_methods->update_group_mapping_entry(context->pdb_methods, map);
+}
+
+static NTSTATUS context_delete_group_mapping_entry(struct pdb_context *context,
+                                                  DOM_SID sid)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       return context->
+               pdb_methods->delete_group_mapping_entry(context->pdb_methods, sid);
+}
+
+static NTSTATUS context_enum_group_mapping(struct pdb_context *context,
+                                          enum SID_NAME_USE sid_name_use,
+                                          GROUP_MAP **rmap, int *num_entries,
+                                          BOOL unix_only, BOOL with_priv)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       if ((!context) || (!context->pdb_methods)) {
+               DEBUG(0, ("invalid pdb_context specified!\n"));
+               return ret;
+       }
+
+       return context->pdb_methods->enum_group_mapping(context->pdb_methods,
+                                                       sid_name_use, rmap,
+                                                       num_entries, unix_only,
+                                                       with_priv);
+}
+
+/******************************************************************
+  Free and cleanup a pdb context, any associated data and anything
+  that the attached modules might have associated.
+ *******************************************************************/
+
+static void free_pdb_context(struct pdb_context **context)
+{
+       struct pdb_methods *pdb_selected = (*context)->pdb_methods;
+
+       while (pdb_selected){
+               if(pdb_selected->free_private_data)
+                       pdb_selected->free_private_data(&(pdb_selected->private_data));
+               pdb_selected = pdb_selected->next;
+       }
+
+       talloc_destroy((*context)->mem_ctx);
+       *context = NULL;
+}
+
+/******************************************************************
+  Make a pdb_methods from scratch
+ *******************************************************************/
+
+static NTSTATUS make_pdb_methods_name(struct pdb_methods **methods, struct pdb_context *context, const char *selected)
+{
+       char *module_name = smb_xstrdup(selected);
+       char *module_location = NULL, *p;
+       struct pdb_init_function_entry *entry;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       lazy_initialize_passdb();
+
+       p = strchr(module_name, ':');
+
+       if (p) {
+               *p = 0;
+               module_location = p+1;
+               trim_string(module_location, " ", " ");
+       }
+
+       trim_string(module_name, " ", " ");
+
+
+       DEBUG(5,("Attempting to find an passdb backend to match %s (%s)\n", selected, module_name));
+
+       entry = pdb_find_backend_entry(module_name);
+       
+       /* Try to find a module that contains this module */
+       if(!entry) { 
+               smb_probe_module("passdb", module_name);
+               entry = pdb_find_backend_entry(module_name);
+       }
+       
+       /* No such backend found */
+       if(!entry) { 
+               SAFE_FREE(module_name);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       DEBUG(5,("Found pdb backend %s\n", module_name));
+       nt_status = entry->init(context, methods, module_location);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(5,("pdb backend %s has a valid init\n", selected));
+       } else {
+               DEBUG(0,("pdb backend %s did not correctly init (error was %s)\n", selected, nt_errstr(nt_status)));
+       }
+       SAFE_FREE(module_name);
+       return nt_status;
+}
+
+/******************************************************************
+  Make a pdb_context from scratch.
+ *******************************************************************/
+
+static NTSTATUS make_pdb_context(struct pdb_context **context) 
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("pdb_context internal allocation context");
+
+       if (!mem_ctx) {
+               DEBUG(0, ("make_pdb_context: talloc init failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }               
+
+       *context = talloc(mem_ctx, sizeof(**context));
+       if (!*context) {
+               DEBUG(0, ("make_pdb_context: talloc failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*context);
+
+       (*context)->mem_ctx = mem_ctx;
+
+       (*context)->pdb_setsampwent = context_setsampwent;
+       (*context)->pdb_endsampwent = context_endsampwent;
+       (*context)->pdb_getsampwent = context_getsampwent;
+       (*context)->pdb_getsampwnam = context_getsampwnam;
+       (*context)->pdb_getsampwsid = context_getsampwsid;
+       (*context)->pdb_add_sam_account = context_add_sam_account;
+       (*context)->pdb_update_sam_account = context_update_sam_account;
+       (*context)->pdb_delete_sam_account = context_delete_sam_account;
+       (*context)->pdb_getgrsid = context_getgrsid;
+       (*context)->pdb_getgrgid = context_getgrgid;
+       (*context)->pdb_getgrnam = context_getgrnam;
+       (*context)->pdb_add_group_mapping_entry = context_add_group_mapping_entry;
+       (*context)->pdb_update_group_mapping_entry = context_update_group_mapping_entry;
+       (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry;
+       (*context)->pdb_enum_group_mapping = context_enum_group_mapping;
+
+       (*context)->free_fn = free_pdb_context;
+
+       return NT_STATUS_OK;
+}
+
+
+/******************************************************************
+  Make a pdb_context, given an array of strings
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **selected) 
+{
+       int i = 0;
+       struct pdb_methods *curmethods, *tmpmethods;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) {
+               return nt_status;
+       }
+
+       while (selected[i]){
+               /* Try to initialise pdb */
+               DEBUG(5,("Trying to load: %s\n", selected[i]));
+               if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods_name(&curmethods, *context, selected[i]))) {
+                       DEBUG(1, ("Loading %s failed!\n", selected[i]));
+                       free_pdb_context(context);
+                       return nt_status;
+               }
+               curmethods->parent = *context;
+               DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
+               i++;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+  Make a pdb_context, given a text string.
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_string(struct pdb_context **context, const char *selected) 
+{
+       NTSTATUS ret;
+       char **newsel = str_list_make(selected, NULL);
+       ret = make_pdb_context_list(context, (const char **)newsel);
+       str_list_free(&newsel);
+       return ret;
+}
+
+/******************************************************************
+ Return an already initialised pdb_context, to facilitate backward 
+ compatibility (see functions below).
+*******************************************************************/
+
+static struct pdb_context *pdb_get_static_context(BOOL reload) 
+{
+       static struct pdb_context *pdb_context = NULL;
+
+       if ((pdb_context) && (reload)) {
+               pdb_context->free_fn(&pdb_context);
+               if (NT_STATUS_IS_ERR(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+                       return NULL;
+               }
+       }
+
+       if (!pdb_context) {
+               if (NT_STATUS_IS_ERR(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+                       return NULL;
+               }
+       }
+
+       return pdb_context;
+}
+
+/******************************************************************
+ Backward compatibility functions for the original passdb interface
+*******************************************************************/
+
+BOOL pdb_setsampwent(BOOL update) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_setsampwent(pdb_context, update));
+}
+
+void pdb_endsampwent(void) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return;
+       }
+
+       pdb_context->pdb_endsampwent(pdb_context);
+}
+
+BOOL pdb_getsampwent(SAM_ACCOUNT *user) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_getsampwent(pdb_context, user));
+}
+
+BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, const char *username) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_getsampwnam(pdb_context, sam_acct, username));
+}
+
+BOOL pdb_getsampwsid(SAM_ACCOUNT *sam_acct, const DOM_SID *sid) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_getsampwsid(pdb_context, sam_acct, sid));
+}
+
+BOOL pdb_add_sam_account(SAM_ACCOUNT *sam_acct) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_add_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_update_sam_account(SAM_ACCOUNT *sam_acct) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_update_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_delete_sam_account(SAM_ACCOUNT *sam_acct) 
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->pdb_delete_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_getgrsid(GROUP_MAP *map, DOM_SID sid, BOOL with_priv)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_getgrsid(pdb_context, map, sid, with_priv));
+}
+
+BOOL pdb_getgrgid(GROUP_MAP *map, gid_t gid, BOOL with_priv)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_getgrgid(pdb_context, map, gid, with_priv));
+}
+
+BOOL pdb_getgrnam(GROUP_MAP *map, char *name, BOOL with_priv)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_getgrnam(pdb_context, map, name, with_priv));
+}
+
+BOOL pdb_add_group_mapping_entry(GROUP_MAP *map)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_add_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_update_group_mapping_entry(GROUP_MAP *map)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_update_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_delete_group_mapping_entry(DOM_SID sid)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_delete_group_mapping_entry(pdb_context, sid));
+}
+
+BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+                           int *num_entries, BOOL unix_only, BOOL with_priv)
+{
+       struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+       if (!pdb_context) {
+               return False;
+       }
+
+       return NT_STATUS_IS_OK(pdb_context->
+                              pdb_enum_group_mapping(pdb_context, sid_name_use,
+                                                     rmap, num_entries, unix_only,
+                                                     with_priv));
+}
+
+/***************************************************************
+  Initialize the static context (at smbd startup etc). 
+
+  If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_password_db(BOOL reload)
+{      
+       return (pdb_get_static_context(reload) != NULL);
+}
+
+
+/***************************************************************************
+  Default implementations of some functions.
+ ****************************************************************************/
+
+static NTSTATUS pdb_default_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+       return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+       return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+       DEBUG(0,("this backend (%s) should not be listed as the first passdb backend! You can't add users to it.\n", methods->name));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *pwd)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *user)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static void pdb_default_endsampwent(struct pdb_methods *methods)
+{
+       return; /* NT_STATUS_NOT_IMPLEMENTED; */
+}
+
+NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods) 
+{
+       *methods = talloc(mem_ctx, sizeof(struct pdb_methods));
+
+       if (!*methods) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*methods);
+
+       (*methods)->setsampwent = pdb_default_setsampwent;
+       (*methods)->endsampwent = pdb_default_endsampwent;
+       (*methods)->getsampwent = pdb_default_getsampwent;
+       (*methods)->getsampwnam = pdb_default_getsampwnam;
+       (*methods)->getsampwsid = pdb_default_getsampwsid;
+       (*methods)->add_sam_account = pdb_default_add_sam_account;
+       (*methods)->update_sam_account = pdb_default_update_sam_account;
+       (*methods)->delete_sam_account = pdb_default_delete_sam_account;
+
+       (*methods)->getgrsid = pdb_default_getgrsid;
+       (*methods)->getgrgid = pdb_default_getgrgid;
+       (*methods)->getgrnam = pdb_default_getgrnam;
+       (*methods)->add_group_mapping_entry = pdb_default_add_group_mapping_entry;
+       (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry;
+       (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry;
+       (*methods)->enum_group_mapping = pdb_default_enum_group_mapping;
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/passdb/pdb_ldap.c b/source4/passdb/pdb_ldap.c
new file mode 100644 (file)
index 0000000..0136a33
--- /dev/null
@@ -0,0 +1,2089 @@
+/* 
+   Unix SMB/CIFS implementation.
+   LDAP protocol helper functions for SAMBA
+   Copyright (C) Jean François Micouleau       1998
+   Copyright (C) Gerald Carter                 2001
+   Copyright (C) Shahms King                   2001
+   Copyright (C) Andrew Bartlett               2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+    
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#ifdef HAVE_LDAP
+/* TODO:
+*  persistent connections: if using NSS LDAP, many connections are made
+*      however, using only one within Samba would be nice
+*  
+*  Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK
+*
+*  Other LDAP based login attributes: accountExpires, etc.
+*  (should be the domain of Samba proper, but the sam_password/SAM_ACCOUNT
+*  structures don't have fields for some of these attributes)
+*
+*  SSL is done, but can't get the certificate based authentication to work
+*  against on my test platform (Linux 2.4, OpenLDAP 2.x)
+*/
+
+/* NOTE: this will NOT work against an Active Directory server
+*  due to the fact that the two password fields cannot be retrieved
+*  from a server; recommend using security = domain in this situation
+*  and/or winbind
+*/
+
+#include <lber.h>
+#include <ldap.h>
+
+#ifndef SAM_ACCOUNT
+#define SAM_ACCOUNT struct sam_passwd
+#endif
+
+struct ldapsam_privates {
+
+       /* Former statics */
+       LDAP *ldap_struct;
+       LDAPMessage *result;
+       LDAPMessage *entry;
+       int index;
+       
+       time_t last_ping;
+       /* retrive-once info */
+       const char *uri;
+       
+       BOOL permit_non_unix_accounts;
+       
+       uint32 low_nua_rid; 
+       uint32 high_nua_rid; 
+
+       char *bind_dn;
+       char *bind_secret;
+};
+
+#define LDAPSAM_DONT_PING_TIME 10      /* ping only all 10 seconds */
+
+static struct ldapsam_privates *static_ldap_state;
+
+static uint32 ldapsam_get_next_available_nua_rid(struct ldapsam_privates *ldap_state);
+
+/*******************************************************************
+ find the ldap password
+******************************************************************/
+static BOOL fetch_ldapsam_pw(char **dn, char** pw)
+{
+       char *key = NULL;
+       size_t size;
+       
+       *dn = smb_xstrdup(lp_ldap_admin_dn());
+       
+       if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
+               SAFE_FREE(*dn);
+               DEBUG(0, ("fetch_ldapsam_pw: asprintf failed!\n"));
+       }
+       
+       *pw=secrets_fetch(key, &size);
+       if (!size) {
+               /* Upgrade 2.2 style entry */
+               char *p;
+               char* old_style_key = strdup(*dn);
+               char *data;
+               fstring old_style_pw;
+               
+               if (!old_style_key) {
+                       DEBUG(0, ("fetch_ldapsam_pw: strdup failed!\n"));
+                       return False;
+               }
+
+               for (p=old_style_key; *p; p++)
+                       if (*p == ',') *p = '/';
+       
+               data=secrets_fetch(old_style_key, &size);
+               if (!size && size < sizeof(old_style_pw)) {
+                       DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
+                       SAFE_FREE(old_style_key);
+                       SAFE_FREE(*dn);
+                       return False;
+               }
+
+               strncpy(old_style_pw, data, size);
+               old_style_pw[size] = 0;
+
+               SAFE_FREE(data);
+
+               if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
+                       DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
+                       SAFE_FREE(old_style_key);
+                       SAFE_FREE(*dn);
+                       return False;                   
+               }
+               if (!secrets_delete(old_style_key)) {
+                       DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
+               }
+
+               SAFE_FREE(old_style_key);
+
+               *pw = smb_xstrdup(old_style_pw);                
+       }
+       
+       return True;
+}
+
+static const char *attr[] = {"uid", "pwdLastSet", "logonTime",
+                            "logoffTime", "kickoffTime", "cn",
+                            "pwdCanChange", "pwdMustChange",
+                            "displayName", "homeDrive",
+                            "smbHome", "scriptPath",
+                            "profilePath", "description",
+                            "userWorkstations", "rid",
+                            "primaryGroupID", "lmPassword",
+                            "ntPassword", "acctFlags",
+                            "domain", "objectClass", 
+                            "uidNumber", "gidNumber", 
+                            "homeDirectory", NULL };
+
+/*******************************************************************
+ open a connection to the ldap server.
+******************************************************************/
+static int ldapsam_open_connection (struct ldapsam_privates *ldap_state, LDAP ** ldap_struct)
+{
+       int rc = LDAP_SUCCESS;
+       int version;
+       BOOL ldap_v3 = False;
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+       DEBUG(10, ("ldapsam_open_connection: %s\n", ldap_state->uri));
+       
+       if ((rc = ldap_initialize(ldap_struct, ldap_state->uri)) != LDAP_SUCCESS) {
+               DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
+               return rc;
+       }
+       
+#else 
+
+       /* Parse the string manually */
+
+       {
+               int port = 0;
+               fstring protocol;
+               fstring host;
+               const char *p = ldap_state->uri; 
+               SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
+               
+               /* skip leading "URL:" (if any) */
+               if ( strncasecmp( p, "URL:", 4 ) == 0 ) {
+                       p += 4;
+               }
+               
+               sscanf(p, "%10[^:]://%254s[^:]:%d", protocol, host, &port);
+               
+               if (port == 0) {
+                       if (strequal(protocol, "ldap")) {
+                               port = LDAP_PORT;
+                       } else if (strequal(protocol, "ldaps")) {
+                               port = LDAPS_PORT;
+                       } else {
+                               DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
+                       }
+               }
+               
+               if ((*ldap_struct = ldap_init(host, port)) == NULL)     {
+                       DEBUG(0, ("ldap_init failed !\n"));
+                       return LDAP_OPERATIONS_ERROR;
+               }
+               
+               if (strequal(protocol, "ldaps")) {
+#ifdef LDAP_OPT_X_TLS
+                       int tls = LDAP_OPT_X_TLS_HARD;
+                       if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS)
+                       {
+                               DEBUG(0, ("Failed to setup a TLS session\n"));
+                       }
+                       
+                       DEBUG(3,("LDAPS option set...!\n"));
+#else
+                       DEBUG(0,("ldapsam_open_connection: Secure connection not supported by LDAP client libraries!\n"));
+                       return LDAP_OPERATIONS_ERROR;
+#endif
+               }
+       }
+#endif
+
+       if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS)
+       {
+               if (version != LDAP_VERSION3)
+               {
+                       version = LDAP_VERSION3;
+                       if (ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
+                               ldap_v3 = True;
+                       }
+               } else {
+                       ldap_v3 = True;
+               }
+       }
+
+       if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
+#ifdef LDAP_OPT_X_TLS
+               if (ldap_v3) {
+                       if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS)
+                       {
+                               DEBUG(0,("Failed to issue the StartTLS instruction: %s\n",
+                                        ldap_err2string(rc)));
+                               return rc;
+                       }
+                       DEBUG (3, ("StartTLS issued: using a TLS connection\n"));
+               } else {
+                       
+                       DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
+                       return LDAP_OPERATIONS_ERROR;
+               }
+#else
+               DEBUG(0,("ldapsam_open_connection: StartTLS not supported by LDAP client libraries!\n"));
+               return LDAP_OPERATIONS_ERROR;
+#endif
+       }
+
+       DEBUG(2, ("ldapsam_open_connection: connection opened\n"));
+       return rc;
+}
+
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+static int rebindproc_with_state  (LDAP * ld, char **whop, char **credp, 
+                                  int *methodp, int freeit, void *arg)
+{
+       struct ldapsam_privates *ldap_state = arg;
+       
+       /** @TODO Should we be doing something to check what servers we rebind to?
+           Could we get a referral to a machine that we don't want to give our
+           username and password to? */
+       
+       if (freeit) {
+               SAFE_FREE(*whop);
+               memset(*credp, '\0', strlen(*credp));
+               SAFE_FREE(*credp);
+       } else {
+               DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n", 
+                         ldap_state->bind_dn));
+
+               *whop = strdup(ldap_state->bind_dn);
+               if (!*whop) {
+                       return LDAP_NO_MEMORY;
+               }
+               *credp = strdup(ldap_state->bind_secret);
+               if (!*credp) {
+                       SAFE_FREE(*whop);
+                       return LDAP_NO_MEMORY;
+               }
+               *methodp = LDAP_AUTH_SIMPLE;
+       }
+       return 0;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+ and actually does the connection.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+static int rebindproc_connect_with_state (LDAP *ldap_struct, 
+                                         LDAP_CONST char *url, 
+                                         ber_tag_t request,
+                                         ber_int_t msgid, void *arg)
+{
+       struct ldapsam_privates *ldap_state = arg;
+       int rc;
+       DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n", 
+                ldap_state->bind_dn));
+       
+       /** @TODO Should we be doing something to check what servers we rebind to?
+           Could we get a referral to a machine that we don't want to give our
+           username and password to? */
+
+       rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret);
+       
+       return rc;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ Add a rebind function for authenticated referrals
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
+                      int *method, int freeit )
+{
+       return rebindproc_with_state(ldap_struct, whop, credp,
+                                  method, freeit, static_ldap_state);
+       
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ this also does the connection, but no void*.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
+                              ber_int_t msgid)
+{
+       return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid, 
+                                            static_ldap_state);
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ connect to the ldap server under system privilege.
+******************************************************************/
+static int ldapsam_connect_system(struct ldapsam_privates *ldap_state, LDAP * ldap_struct)
+{
+       int rc;
+       char *ldap_dn;
+       char *ldap_secret;
+
+       /* The rebind proc needs this *HACK*.  We are not multithreaded, so
+          this will work, but it's not nice. */
+       static_ldap_state = ldap_state;
+
+       /* get the password */
+       if (!fetch_ldapsam_pw(&ldap_dn, &ldap_secret))
+       {
+               DEBUG(0, ("ldap_connect_system: Failed to retrieve password from secrets.tdb\n"));
+               return LDAP_INVALID_CREDENTIALS;
+       }
+
+       ldap_state->bind_dn = ldap_dn;
+       ldap_state->bind_secret = ldap_secret;
+
+       /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite 
+          (OpenLDAP) doesnt' seem to support it */
+          
+       DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n",
+                 ldap_state->uri, ldap_dn));
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2    
+       ldap_set_rebind_proc(ldap_struct, &rebindproc_connect); 
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3    
+       ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state);  
+# endif
+#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+# if LDAP_SET_REBIND_PROC_ARGS == 2    
+       ldap_set_rebind_proc(ldap_struct, &rebindproc); 
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3    
+       ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state);  
+# endif
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+       rc = ldap_simple_bind_s(ldap_struct, ldap_dn, ldap_secret);
+
+       if (rc != LDAP_SUCCESS) {
+               char *ld_error;
+               ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+                               &ld_error);
+               DEBUG(0,
+                     ("failed to bind to server with dn= %s Error: %s\n\t%s\n",
+                              ldap_dn, ldap_err2string(rc),
+                              ld_error));
+               free(ld_error);
+               return rc;
+       }
+       
+       DEBUG(2, ("ldap_connect_system: succesful connection to the LDAP server\n"));
+       return rc;
+}
+
+/**********************************************************************
+Connect to LDAP server 
+*********************************************************************/
+static int ldapsam_open(struct ldapsam_privates *ldap_state)
+{
+       int rc;
+       SMB_ASSERT(ldap_state);
+               
+#ifndef NO_LDAP_SECURITY
+       if (geteuid() != 0) {
+               DEBUG(0, ("ldapsam_open: cannot access LDAP when not root..\n"));
+               return  LDAP_INSUFFICIENT_ACCESS;
+       }
+#endif
+
+       if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + LDAPSAM_DONT_PING_TIME) < time(NULL))) {
+               struct sockaddr_un addr;
+               socklen_t len;
+               int sd;
+               if (ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_DESC, &sd) == 0 &&
+                   getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
+                       /* the other end has died. reopen. */
+                       ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+                       ldap_state->ldap_struct = NULL;
+                       ldap_state->last_ping = (time_t)0;
+               } else {
+                       ldap_state->last_ping = time(NULL);
+               } 
+       }
+
+       if (ldap_state->ldap_struct != NULL) {
+               DEBUG(5,("ldapsam_open: allready connected to the LDAP server\n"));
+               return LDAP_SUCCESS;
+       }
+
+       if ((rc = ldapsam_open_connection(ldap_state, &ldap_state->ldap_struct))) {
+               return rc;
+       }
+
+       if ((rc = ldapsam_connect_system(ldap_state, ldap_state->ldap_struct))) {
+               ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+               ldap_state->ldap_struct = NULL;
+               return rc;
+       }
+
+
+       ldap_state->last_ping = time(NULL);
+       DEBUG(4,("The LDAP server is succesful connected\n"));
+
+       return LDAP_SUCCESS;
+}
+
+/**********************************************************************
+Disconnect from LDAP server 
+*********************************************************************/
+static NTSTATUS ldapsam_close(struct ldapsam_privates *ldap_state)
+{
+       if (!ldap_state)
+               return NT_STATUS_INVALID_PARAMETER;
+               
+       if (ldap_state->ldap_struct != NULL) {
+               ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+               ldap_state->ldap_struct = NULL;
+       }
+       
+       DEBUG(5,("The connection to the LDAP server was closed\n"));
+       /* maybe free the results here --metze */
+       
+       return NT_STATUS_OK;
+}
+
+static int ldapsam_retry_open(struct ldapsam_privates *ldap_state, int *attempts)
+{
+       int rc;
+
+       SMB_ASSERT(ldap_state && attempts);
+               
+       if (*attempts != 0) {
+               /* we retry after 0.5, 2, 4.5, 8, 12.5, 18, 24.5 seconds */
+               msleep((((*attempts)*(*attempts))/2)*1000);
+       }
+       (*attempts)++;
+
+       if ((rc = ldapsam_open(ldap_state))) {
+               DEBUG(0,("Connection to LDAP Server failed for the %d try!\n",*attempts));
+               return rc;
+       } 
+       
+       return LDAP_SUCCESS;            
+}
+
+
+static int ldapsam_search(struct ldapsam_privates *ldap_state, 
+                         const char *base, int scope, const char *filter, 
+                         const char *attrs[], int attrsonly, 
+                         LDAPMessage **res)
+{
+       int             rc = LDAP_SERVER_DOWN;
+       int             attempts = 0;
+       
+       SMB_ASSERT(ldap_state);
+
+       while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
+               
+               if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+                       continue;
+               
+               rc = ldap_search_s(ldap_state->ldap_struct, base, scope, 
+                                  filter, attrs, attrsonly, res);
+       }
+       
+       if (rc == LDAP_SERVER_DOWN) {
+               DEBUG(0,("ldapsam_seacrh: LDAP server is down!\n"));
+               ldapsam_close(ldap_state);      
+       }
+       
+       return rc;
+}
+
+static int ldapsam_modify(struct ldapsam_privates *ldap_state, char *dn, LDAPMod *attrs[])
+{
+       int             rc = LDAP_SERVER_DOWN;
+       int             attempts = 0;
+       
+       if (!ldap_state)
+               return (-1);
+
+       while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
+               
+               if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+                       continue;
+               
+               rc = ldap_modify_s(ldap_state->ldap_struct, dn, attrs);
+       }
+       
+       if (rc == LDAP_SERVER_DOWN) {
+               DEBUG(0,("ldapsam_modify: LDAP server is down!\n"));
+               ldapsam_close(ldap_state);      
+       }
+       
+       return rc;
+}
+
+static int ldapsam_add(struct ldapsam_privates *ldap_state, const char *dn, LDAPMod *attrs[])
+{
+       int             rc = LDAP_SERVER_DOWN;
+       int             attempts = 0;
+       
+       if (!ldap_state)
+               return (-1);
+
+       while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
+               
+               if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+                       continue;
+               
+               rc = ldap_add_s(ldap_state->ldap_struct, dn, attrs);
+       }
+       
+       if (rc == LDAP_SERVER_DOWN) {
+               DEBUG(0,("ldapsam_add: LDAP server is down!\n"));
+               ldapsam_close(ldap_state);      
+       }
+               
+       return rc;
+}
+
+static int ldapsam_delete(struct ldapsam_privates *ldap_state, char *dn)
+{
+       int             rc = LDAP_SERVER_DOWN;
+       int             attempts = 0;
+       
+       if (!ldap_state)
+               return (-1);
+
+       while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
+               
+               if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+                       continue;
+               
+               rc = ldap_delete_s(ldap_state->ldap_struct, dn);
+       }
+       
+       if (rc == LDAP_SERVER_DOWN) {
+               DEBUG(0,("ldapsam_delete: LDAP server is down!\n"));
+               ldapsam_close(ldap_state);      
+       }
+               
+       return rc;
+}
+
+static int ldapsam_extended_operation(struct ldapsam_privates *ldap_state, LDAP_CONST char *reqoid, struct berval *reqdata, LDAPControl **serverctrls, LDAPControl **clientctrls, char **retoidp, struct berval **retdatap)
+{
+       int             rc = LDAP_SERVER_DOWN;
+       int             attempts = 0;
+       
+       if (!ldap_state)
+               return (-1);
+
+       while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) {
+               
+               if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+                       continue;
+               
+               rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid, reqdata, serverctrls, clientctrls, retoidp, retdatap);
+       }
+       
+       if (rc == LDAP_SERVER_DOWN) {
+               DEBUG(0,("ldapsam_extended_operation: LDAP server is down!\n"));
+               ldapsam_close(ldap_state);      
+       }
+               
+       return rc;
+}
+
+/*******************************************************************
+ run the search by name.
+******************************************************************/
+static int ldapsam_search_one_user (struct ldapsam_privates *ldap_state, const char *filter, LDAPMessage ** result)
+{
+       int scope = LDAP_SCOPE_SUBTREE;
+       int rc;
+
+       DEBUG(2, ("ldapsam_search_one_user: searching for:[%s]\n", filter));
+
+       rc = ldapsam_search(ldap_state, lp_ldap_suffix (), scope, filter, attr, 0, result);
+
+       if (rc != LDAP_SUCCESS) {
+               DEBUG(0,("ldapsam_search_one_user: Problem during the LDAP search: %s\n", 
+                       ldap_err2string (rc)));
+               DEBUG(3,("ldapsam_search_one_user: Query was: %s, %s\n", lp_ldap_suffix(), 
+                       filter));
+       }
+       
+       return rc;
+}
+
+/*******************************************************************
+ run the search by name.
+******************************************************************/
+static int ldapsam_search_one_user_by_name (struct ldapsam_privates *ldap_state, const char *user,
+                            LDAPMessage ** result)
+{
+       pstring filter;
+       char *escape_user = escape_ldap_string_alloc(user);
+
+       if (!escape_user) {
+               return LDAP_NO_MEMORY;
+       }
+
+       /*
+        * in the filter expression, replace %u with the real name
+        * so in ldap filter, %u MUST exist :-)
+        */
+       pstrcpy(filter, lp_ldap_filter());
+
+       /* 
+        * have to use this here because $ is filtered out
+          * in pstring_sub
+        */
+       
+
+       all_string_sub(filter, "%u", escape_user, sizeof(pstring));
+       SAFE_FREE(escape_user);
+
+       return ldapsam_search_one_user(ldap_state, filter, result);
+}
+
+/*******************************************************************
+ run the search by uid.
+******************************************************************/
+static int ldapsam_search_one_user_by_uid(struct ldapsam_privates *ldap_state, 
+                                         int uid,
+                                         LDAPMessage ** result)
+{
+       struct passwd *user;
+       pstring filter;
+       char *escape_user;
+
+       /* Get the username from the system and look that up in the LDAP */
+       
+       if ((user = getpwuid_alloc(uid)) == NULL) {
+               DEBUG(3,("ldapsam_search_one_user_by_uid: Failed to locate uid [%d]\n", uid));
+               return LDAP_NO_SUCH_OBJECT;
+       }
+       
+       pstrcpy(filter, lp_ldap_filter());
+       
+       escape_user = escape_ldap_string_alloc(user->pw_name);
+       if (!escape_user) {
+               passwd_free(&user);
+               return LDAP_NO_MEMORY;
+       }
+
+       all_string_sub(filter, "%u", escape_user, sizeof(pstring));
+
+       passwd_free(&user);
+       SAFE_FREE(escape_user);
+
+       return ldapsam_search_one_user(ldap_state, filter, result);
+}
+
+/*******************************************************************
+ run the search by rid.
+******************************************************************/
+static int ldapsam_search_one_user_by_rid (struct ldapsam_privates *ldap_state, 
+                                          uint32 rid,
+                                          LDAPMessage ** result)
+{
+       pstring filter;
+       int rc;
+
+       /* check if the user rid exsists, if not, try searching on the uid */
+       
+       snprintf(filter, sizeof(filter) - 1, "rid=%i", rid);
+       rc = ldapsam_search_one_user(ldap_state, filter, result);
+       
+       if (rc != LDAP_SUCCESS)
+               rc = ldapsam_search_one_user_by_uid(ldap_state,
+                                                   fallback_pdb_user_rid_to_uid(rid), 
+                                                   result);
+
+       return rc;
+}
+
+/*******************************************************************
+search an attribute and return the first value found.
+******************************************************************/
+static BOOL get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
+                                 const char *attribute, pstring value)
+{
+       char **values;
+
+       if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) {
+               value = NULL;
+               DEBUG (10, ("get_single_attribute: [%s] = [<does not exist>]\n", attribute));
+               
+               return False;
+       }
+       
+       pstrcpy(value, values[0]);
+       ldap_value_free(values);
+#ifdef DEBUG_PASSWORDS
+       DEBUG (100, ("get_single_attribute: [%s] = [%s]\n", attribute, value));
+#endif 
+       return True;
+}
+
+/************************************************************************
+Routine to manage the LDAPMod structure array
+manage memory used by the array, by each struct, and values
+
+************************************************************************/
+static void make_a_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value)
+{
+       LDAPMod **mods;
+       int i;
+       int j;
+
+       mods = *modlist;
+
+       if (attribute == NULL || *attribute == '\0')
+               return;
+
+       if (value == NULL || *value == '\0')
+               return;
+
+       if (mods == NULL) 
+       {
+               mods = (LDAPMod **) malloc(sizeof(LDAPMod *));
+               if (mods == NULL)
+               {
+                       DEBUG(0, ("make_a_mod: out of memory!\n"));
+                       return;
+               }
+               mods[0] = NULL;
+       }
+
+       for (i = 0; mods[i] != NULL; ++i) {
+               if (mods[i]->mod_op == modop && !strcasecmp(mods[i]->mod_type, attribute))
+                       break;
+       }
+
+       if (mods[i] == NULL)
+       {
+               mods = (LDAPMod **) Realloc (mods, (i + 2) * sizeof (LDAPMod *));
+               if (mods == NULL)
+               {
+                       DEBUG(0, ("make_a_mod: out of memory!\n"));
+                       return;
+               }
+               mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
+               if (mods[i] == NULL)
+               {
+                       DEBUG(0, ("make_a_mod: out of memory!\n"));
+                       return;
+               }
+               mods[i]->mod_op = modop;
+               mods[i]->mod_values = NULL;
+               mods[i]->mod_type = strdup(attribute);
+               mods[i + 1] = NULL;
+       }
+
+       if (value != NULL)
+       {
+               j = 0;
+               if (mods[i]->mod_values != NULL) {
+                       for (; mods[i]->mod_values[j] != NULL; j++);
+               }
+               mods[i]->mod_values = (char **)Realloc(mods[i]->mod_values,
+                                              (j + 2) * sizeof (char *));
+                                              
+               if (mods[i]->mod_values == NULL) {
+                       DEBUG (0, ("make_a_mod: Memory allocation failure!\n"));
+                       return;
+               }
+               mods[i]->mod_values[j] = strdup(value);
+               mods[i]->mod_values[j + 1] = NULL;
+       }
+       *modlist = mods;
+}
+
+/* New Interface is being implemented here */
+
+/**********************************************************************
+Initialize SAM_ACCOUNT from an LDAP query (unix attributes only)
+*********************************************************************/
+static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state, 
+                               SAM_ACCOUNT * sampass,
+                               LDAPMessage * entry)
+{
+       pstring  homedir;
+       pstring  temp;
+       uid_t uid;
+       gid_t gid;
+       char **ldap_values;
+       char **values;
+
+       if ((ldap_values = ldap_get_values (ldap_state->ldap_struct, entry, "objectClass")) == NULL) {
+               DEBUG (1, ("get_unix_attributes: no objectClass! \n"));
+               return False;
+       }
+
+       for (values=ldap_values;*values;values++) {
+               if (strcasecmp(*values, "posixAccount") == 0) {
+                       break;
+               }
+       }
+       
+       if (!*values) { /*end of array, no posixAccount */
+               DEBUG(10, ("user does not have posixAcccount attributes\n"));
+               ldap_value_free(ldap_values);
+               return False;
+       }
+       ldap_value_free(ldap_values);
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "homeDirectory", homedir)) 
+               return False;
+       
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "uidNumber", temp))
+               return False;
+       
+       uid = (uid_t)atol(temp);
+       
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "gidNumber", temp))
+               return False;
+       
+       gid = (gid_t)atol(temp);
+
+       pdb_set_unix_homedir(sampass, homedir, PDB_SET);
+       pdb_set_uid(sampass, uid, PDB_SET);
+       pdb_set_gid(sampass, gid, PDB_SET);
+       
+       DEBUG(10, ("user has posixAcccount attributes\n"));
+       return True;
+}
+
+
+/**********************************************************************
+Initialize SAM_ACCOUNT from an LDAP query
+(Based on init_sam_from_buffer in pdb_tdb.c)
+*********************************************************************/
+static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state, 
+                               SAM_ACCOUNT * sampass,
+                               LDAPMessage * entry)
+{
+       time_t  logon_time,
+                       logoff_time,
+                       kickoff_time,
+                       pass_last_set_time, 
+                       pass_can_change_time, 
+                       pass_must_change_time;
+       pstring         username, 
+                       domain,
+                       nt_username,
+                       fullname,
+                       homedir,
+                       dir_drive,
+                       logon_script,
+                       profile_path,
+                       acct_desc,
+                       munged_dial,
+                       workstations;
+       struct passwd   *pw;
+       uint32          user_rid, 
+                       group_rid;
+       uint8           smblmpwd[LM_HASH_LEN],
+                       smbntpwd[NT_HASH_LEN];
+       uint16          acct_ctrl = 0, 
+                       logon_divs;
+       uint32 hours_len;
+       uint8           hours[MAX_HOURS_LEN];
+       pstring temp;
+       uid_t           uid = -1;
+       gid_t           gid = getegid();
+
+
+       /*
+        * do a little initialization
+        */
+       username[0]     = '\0';
+       domain[0]       = '\0';
+       nt_username[0]  = '\0';
+       fullname[0]     = '\0';
+       homedir[0]      = '\0';
+       dir_drive[0]    = '\0';
+       logon_script[0] = '\0';
+       profile_path[0] = '\0';
+       acct_desc[0]    = '\0';
+       munged_dial[0]  = '\0';
+       workstations[0] = '\0';
+        
+
+       if (sampass == NULL || ldap_state == NULL || entry == NULL) {
+               DEBUG(0, ("init_sam_from_ldap: NULL parameters found!\n"));
+               return False;
+       }
+
+       if (ldap_state->ldap_struct == NULL) {
+               DEBUG(0, ("init_sam_from_ldap: ldap_state->ldap_struct is NULL!\n"));
+               return False;
+       }
+       
+       get_single_attribute(ldap_state->ldap_struct, entry, "uid", username);
+       DEBUG(2, ("Entry found for user: %s\n", username));
+
+       pstrcpy(nt_username, username);
+
+       pstrcpy(domain, lp_workgroup());
+       
+       pdb_set_username(sampass, username, PDB_SET);
+
+       pdb_set_domain(sampass, domain, PDB_DEFAULT);
+       pdb_set_nt_username(sampass, nt_username, PDB_SET);
+
+       get_single_attribute(ldap_state->ldap_struct, entry, "rid", temp);
+       user_rid = (uint32)atol(temp);
+
+       pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "primaryGroupID", temp)) {
+               group_rid = 0;
+       } else {
+               group_rid = (uint32)atol(temp);
+               pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+       }
+
+
+       /* 
+        * If so configured, try and get the values from LDAP 
+        */
+
+       if (!lp_ldap_trust_ids() || (!get_unix_attributes(ldap_state, sampass, entry))) {
+               
+               /* 
+                * Otherwise just ask the system getpw() calls.
+                */
+       
+               pw = getpwnam_alloc(username);
+               if (pw == NULL) {
+                       if (! ldap_state->permit_non_unix_accounts) {
+                               DEBUG (2,("init_sam_from_ldap: User [%s] does not exist via system getpwnam!\n", username));
+                               return False;
+                       }
+               } else {
+                       uid = pw->pw_uid;
+                       pdb_set_uid(sampass, uid, PDB_SET);
+                       gid = pw->pw_gid;
+                       pdb_set_gid(sampass, gid, PDB_SET);
+                       
+                       pdb_set_unix_homedir(sampass, pw->pw_dir, PDB_SET);
+
+                       passwd_free(&pw);
+               }
+       }
+
+       if (group_rid == 0 && pdb_get_init_flags(sampass,PDB_GID) != PDB_DEFAULT) {
+               GROUP_MAP map;
+               gid = pdb_get_gid(sampass);
+               /* call the mapping code here */
+               if(pdb_getgrgid(&map, gid, MAPPING_WITHOUT_PRIV)) {
+                       pdb_set_group_sid(sampass, &map.sid, PDB_SET);
+               } 
+               else {
+                       pdb_set_group_sid_from_rid(sampass, pdb_gid_to_group_rid(gid), PDB_SET);
+               }
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdLastSet", temp)) {
+               /* leave as default */
+       } else {
+               pass_last_set_time = (time_t) atol(temp);
+               pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "logonTime", temp)) {
+               /* leave as default */
+       } else {
+               logon_time = (time_t) atol(temp);
+               pdb_set_logon_time(sampass, logon_time, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "logoffTime", temp)) {
+               /* leave as default */
+       } else {
+               logoff_time = (time_t) atol(temp);
+               pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "kickoffTime", temp)) {
+               /* leave as default */
+       } else {
+               kickoff_time = (time_t) atol(temp);
+               pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdCanChange", temp)) {
+               /* leave as default */
+       } else {
+               pass_can_change_time = (time_t) atol(temp);
+               pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdMustChange", temp)) {
+               /* leave as default */
+       } else {
+               pass_must_change_time = (time_t) atol(temp);
+               pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+       }
+
+       /* recommend that 'gecos' and 'displayName' should refer to the same
+        * attribute OID.  userFullName depreciated, only used by Samba
+        * primary rules of LDAP: don't make a new attribute when one is already defined
+        * that fits your needs; using cn then displayName rather than 'userFullName'
+        */
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "cn", fullname)) {
+               if (!get_single_attribute(ldap_state->ldap_struct, entry, "displayName", fullname)) {
+                       /* leave as default */
+               } else {
+                       pdb_set_fullname(sampass, fullname, PDB_SET);
+               }
+       } else {
+               pdb_set_fullname(sampass, fullname, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "homeDrive", dir_drive)) {
+               pdb_set_dir_drive(sampass, talloc_sub_specified(sampass->mem_ctx, 
+                                                                 lp_logon_drive(),
+                                                                 username, domain, 
+                                                                 uid, gid),
+                                 PDB_DEFAULT);
+       } else {
+               pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "smbHome", homedir)) {
+               pdb_set_homedir(sampass, talloc_sub_specified(sampass->mem_ctx, 
+                                                                 lp_logon_home(),
+                                                                 username, domain, 
+                                                                 uid, gid), 
+                                 PDB_DEFAULT);
+       } else {
+               pdb_set_homedir(sampass, homedir, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "scriptPath", logon_script)) {
+               pdb_set_logon_script(sampass, talloc_sub_specified(sampass->mem_ctx, 
+                                                                    lp_logon_script(),
+                                                                    username, domain, 
+                                                                    uid, gid), 
+                                    PDB_DEFAULT);
+       } else {
+               pdb_set_logon_script(sampass, logon_script, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "profilePath", profile_path)) {
+               pdb_set_profile_path(sampass, talloc_sub_specified(sampass->mem_ctx, 
+                                                                    lp_logon_path(),
+                                                                    username, domain, 
+                                                                    uid, gid), 
+                                    PDB_DEFAULT);
+       } else {
+               pdb_set_profile_path(sampass, profile_path, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "description", acct_desc)) {
+               /* leave as default */
+       } else {
+               pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+       }
+
+       if (!get_single_attribute(ldap_state->ldap_struct, entry, "userWorkstations", workstations)) {
+               /* leave as default */;
+       } else {
+               pdb_set_workstations(sampass, workstations, PDB_SET);
+       }
+
+       /* FIXME: hours stuff should be cleaner */
+       
+       logon_divs = 168;
+       hours_len = 21;
+       memset(hours, 0xff, hours_len);
+
+       if (!get_single_attribute (ldap_state->ldap_struct, entry, "lmPassword", temp)) {
+               /* leave as default */
+       } else {
+               pdb_gethexpwd(temp, smblmpwd);
+               memset((char *)temp, '\0', strlen(temp)+1);
+               if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET))
+                       return False;
+               ZERO_STRUCT(smblmpwd);
+       }
+
+       if (!get_single_attribute (ldap_state->ldap_struct, entry, "ntPassword", temp)) {
+               /* leave as default */
+       } else {
+               pdb_gethexpwd(temp, smbntpwd);
+               memset((char *)temp, '\0', strlen(temp)+1);
+               if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET))
+                       return False;
+               ZERO_STRUCT(smbntpwd);
+       }
+
+       if (!get_single_attribute (ldap_state->ldap_struct, entry, "acctFlags", temp)) {
+               acct_ctrl |= ACB_NORMAL;
+       } else {
+               acct_ctrl = pdb_decode_acct_ctrl(temp);
+
+               if (acct_ctrl == 0)
+                       acct_ctrl |= ACB_NORMAL;
+
+               pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+       }
+
+       pdb_set_hours_len(sampass, hours_len, PDB_SET);
+       pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+
+       pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+       
+       /* pdb_set_unknown_3(sampass, unknown3, PDB_SET); */
+       /* pdb_set_unknown_5(sampass, unknown5, PDB_SET); */
+       /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
+
+       pdb_set_hours(sampass, hours, PDB_SET);
+
+       return True;
+}
+
+static BOOL need_ldap_mod(BOOL pdb_add, const SAM_ACCOUNT * sampass, enum pdb_elements element) {
+       if (pdb_add) {
+               return (!IS_SAM_DEFAULT(sampass, element));
+       } else {
+               return IS_SAM_CHANGED(sampass, element);
+       }
+}
+
+/**********************************************************************
+Initialize SAM_ACCOUNT from an LDAP query
+(Based on init_buffer_from_sam in pdb_tdb.c)
+*********************************************************************/
+static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state, 
+                               LDAPMod *** mods, int ldap_op, 
+                               BOOL pdb_add,
+                               const SAM_ACCOUNT * sampass)
+{
+       pstring temp;
+       uint32 rid;
+
+       if (mods == NULL || sampass == NULL) {
+               DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
+               return False;
+       }
+
+       *mods = NULL;
+
+       /* 
+        * took out adding "objectclass: sambaAccount"
+        * do this on a per-mod basis
+        */
+       if (need_ldap_mod(pdb_add, sampass, PDB_USERNAME)) {
+               make_a_mod(mods, ldap_op, "uid", pdb_get_username(sampass));
+               DEBUG(2, ("Setting entry for user: %s\n", pdb_get_username(sampass)));
+       }
+       
+       if ((rid = pdb_get_user_rid(sampass))!=0 ) {
+               if (need_ldap_mod(pdb_add, sampass, PDB_USERSID)) {             
+                       slprintf(temp, sizeof(temp) - 1, "%i", rid);
+                       make_a_mod(mods, ldap_op, "rid", temp);
+               }
+       } else if (!IS_SAM_DEFAULT(sampass, PDB_UID)) {
+               rid = fallback_pdb_uid_to_user_rid(pdb_get_uid(sampass));
+               slprintf(temp, sizeof(temp) - 1, "%i", rid);
+               make_a_mod(mods, ldap_op, "rid", temp);
+       } else if (ldap_state->permit_non_unix_accounts) {
+               rid = ldapsam_get_next_available_nua_rid(ldap_state);
+               if (rid == 0) {
+                       DEBUG(0, ("NO user RID specified on account %s, and findining next available NUA RID failed, cannot store!\n", pdb_get_username(sampass)));
+                       return False;
+               }
+               slprintf(temp, sizeof(temp) - 1, "%i", rid);
+               make_a_mod(mods, ldap_op, "rid", temp);
+       } else {
+               DEBUG(0, ("NO user RID specified on account %s, cannot store!\n", pdb_get_username(sampass)));
+               return False;
+       }
+
+
+
+       if ((rid = pdb_get_group_rid(sampass))!=0 ) {
+               if (need_ldap_mod(pdb_add, sampass, PDB_GROUPSID)) {            
+                       slprintf(temp, sizeof(temp) - 1, "%i", rid);
+                       make_a_mod(mods, ldap_op, "primaryGroupID", temp);
+               }
+       } else if (!IS_SAM_DEFAULT(sampass, PDB_GID)) {
+               rid = pdb_gid_to_group_rid(pdb_get_gid(sampass));
+               slprintf(temp, sizeof(temp) - 1, "%i", rid);
+               make_a_mod(mods, ldap_op, "primaryGroupID", temp);
+       } else if (ldap_state->permit_non_unix_accounts) {
+               rid = DOMAIN_GROUP_RID_USERS;
+               slprintf(temp, sizeof(temp) - 1, "%i", rid);
+               make_a_mod(mods, ldap_op, "primaryGroupID", temp);
+       } else {
+               DEBUG(0, ("NO group RID specified on account %s, cannot store!\n", pdb_get_username(sampass)));
+               return False;
+       }
+
+
+       /* displayName, cn, and gecos should all be the same
+        *  most easily accomplished by giving them the same OID
+        *  gecos isn't set here b/c it should be handled by the 
+        *  add-user script
+        */
+       if (need_ldap_mod(pdb_add, sampass, PDB_FULLNAME)) {
+               make_a_mod(mods, ldap_op, "displayName", pdb_get_fullname(sampass));
+               make_a_mod(mods, ldap_op, "cn", pdb_get_fullname(sampass));
+       }
+       if (need_ldap_mod(pdb_add, sampass, PDB_ACCTDESC)) {    
+               make_a_mod(mods, ldap_op, "description", pdb_get_acct_desc(sampass));
+       }
+       if (need_ldap_mod(pdb_add, sampass, PDB_WORKSTATIONS)) {        
+               make_a_mod(mods, ldap_op, "userWorkstations", pdb_get_workstations(sampass));
+       }
+       /*
+        * Only updates fields which have been set (not defaults from smb.conf)
+        */
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_SMBHOME)) {
+               make_a_mod(mods, ldap_op, "smbHome", pdb_get_homedir(sampass));
+       }
+                       
+       if (need_ldap_mod(pdb_add, sampass, PDB_DRIVE)) {
+               make_a_mod(mods, ldap_op, "homeDrive", pdb_get_dir_drive(sampass));
+       }
+       
+       if (need_ldap_mod(pdb_add, sampass, PDB_LOGONSCRIPT)) {
+               make_a_mod(mods, ldap_op, "scriptPath", pdb_get_logon_script(sampass));
+       }
+       
+       if (need_ldap_mod(pdb_add, sampass, PDB_PROFILE))
+               make_a_mod(mods, ldap_op, "profilePath", pdb_get_profile_path(sampass));
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_LOGONTIME)) {
+               slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass));
+               make_a_mod(mods, ldap_op, "logonTime", temp);
+       }
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_LOGOFFTIME)) {
+               slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass));
+               make_a_mod(mods, ldap_op, "logoffTime", temp);
+       }
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_KICKOFFTIME)) {
+               slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass));
+               make_a_mod(mods, ldap_op, "kickoffTime", temp);
+       }
+
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_CANCHANGETIME)) {
+               slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass));
+               make_a_mod(mods, ldap_op, "pwdCanChange", temp);
+       }
+
+       if (need_ldap_mod(pdb_add, sampass, PDB_MUSTCHANGETIME)) {
+               slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass));
+               make_a_mod(mods, ldap_op, "pwdMustChange", temp);
+       }
+
+       if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))||
+               (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
+
+               if (need_ldap_mod(pdb_add, sampass, PDB_LMPASSWD)) {
+                       pdb_sethexpwd (temp, pdb_get_lanman_passwd(sampass), pdb_get_acct_ctrl(sampass));
+                       make_a_mod (mods, ldap_op, "lmPassword", temp);
+               }
+               
+               if (need_ldap_mod(pdb_add, sampass, PDB_NTPASSWD)) {
+                       pdb_sethexpwd (temp, pdb_get_nt_passwd(sampass), pdb_get_acct_ctrl(sampass));
+                       make_a_mod (mods, ldap_op, "ntPassword", temp);
+               }
+               
+               if (need_ldap_mod(pdb_add, sampass, PDB_PASSLASTSET)) {
+                       slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
+                       make_a_mod(mods, ldap_op, "pwdLastSet", temp);
+               }
+       }
+
+       /* FIXME: Hours stuff goes in LDAP  */
+       if (need_ldap_mod(pdb_add, sampass, PDB_ACCTCTRL)) {
+               make_a_mod (mods, ldap_op, "acctFlags", pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass),
+                       NEW_PW_FORMAT_SPACE_PADDED_LEN));
+       }
+       
+       return True;
+}
+
+
+/**********************************************************************
+Connect to LDAP server and find the next available RID.
+*********************************************************************/
+static uint32 check_nua_rid_is_avail(struct ldapsam_privates *ldap_state, uint32 top_rid) 
+{
+       LDAPMessage *result;
+       uint32 final_rid = (top_rid & (~USER_RID_TYPE)) + RID_MULTIPLIER;
+       if (top_rid == 0) {
+               return 0;
+       }
+       
+       if (final_rid < ldap_state->low_nua_rid || final_rid > ldap_state->high_nua_rid) {
+               return 0;
+       }
+
+       if (ldapsam_search_one_user_by_rid(ldap_state, final_rid, &result) != LDAP_SUCCESS) {
+               DEBUG(0, ("Cannot allocate NUA RID %d (0x%x), as the confirmation search failed!\n", final_rid, final_rid));
+               return 0;
+       }
+
+       if (ldap_count_entries(ldap_state->ldap_struct, result) != 0) {
+               DEBUG(0, ("Cannot allocate NUA RID %d (0x%x), as the RID is already in use!!\n", final_rid, final_rid));
+               ldap_msgfree(result);
+               return 0;
+       }
+
+       DEBUG(5, ("NUA RID %d (0x%x), declared valid\n", final_rid, final_rid));
+       ldap_msgfree(result);
+       return final_rid;
+}
+
+/**********************************************************************
+Extract the RID from an LDAP entry
+*********************************************************************/
+static uint32 entry_to_user_rid(struct ldapsam_privates *ldap_state, LDAPMessage *entry) {
+       uint32 rid;
+       SAM_ACCOUNT *user = NULL;
+       if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+               return 0;
+       }
+
+       if (init_sam_from_ldap(ldap_state, user, entry)) {
+               rid = pdb_get_user_rid(user);
+       } else {
+               rid =0;
+       }
+       pdb_free_sam(&user);
+       if (rid >= ldap_state->low_nua_rid && rid <= ldap_state->high_nua_rid) {
+               return rid;
+       }
+       return 0;
+}
+
+
+/**********************************************************************
+Connect to LDAP server and find the next available RID.
+*********************************************************************/
+static uint32 search_top_nua_rid(struct ldapsam_privates *ldap_state)
+{
+       int rc;
+       pstring filter;
+       LDAPMessage *result;
+       LDAPMessage *entry;
+       char *final_filter = NULL;
+       uint32 top_rid = 0;
+       uint32 count;
+       uint32 rid;
+
+       pstrcpy(filter, lp_ldap_filter());
+       all_string_sub(filter, "%u", "*", sizeof(pstring));
+
+#if 0
+       asprintf(&final_filter, "(&(%s)(&(rid>=%d)(rid<=%d)))", filter, ldap_state->low_nua_rid, ldap_state->high_nua_rid);
+#else 
+       final_filter = strdup(filter);
+#endif 
+       DEBUG(2, ("ldapsam_get_next_available_nua_rid: searching for:[%s]\n", final_filter));
+
+       rc = ldapsam_search(ldap_state, lp_ldap_suffix(),
+                          LDAP_SCOPE_SUBTREE, final_filter, attr, 0,
+                          &result);
+
+       if (rc != LDAP_SUCCESS) {
+               DEBUG(3, ("LDAP search failed! cannot find base for NUA RIDs: %s\n", ldap_err2string(rc)));
+               DEBUGADD(3, ("Query was: %s, %s\n", lp_ldap_suffix(), final_filter));
+
+               free(final_filter);
+               result = NULL;
+               return 0;
+       }
+       
+       count = ldap_count_entries(ldap_state->ldap_struct, result);
+       DEBUG(2, ("search_top_nua_rid: %d entries in the base!\n", count));
+       
+       if (count == 0) {
+               DEBUG(3, ("LDAP search returned no records, assuming no non-unix-accounts present!: %s\n", ldap_err2string(rc)));
+               DEBUGADD(3, ("Query was: %s, %s\n", lp_ldap_suffix(), final_filter));
+               free(final_filter);
+               ldap_msgfree(result);
+               result = NULL;
+               return ldap_state->low_nua_rid;
+       }
+       
+       free(final_filter);
+       entry = ldap_first_entry(ldap_state->ldap_struct,result);
+
+       top_rid = entry_to_user_rid(ldap_state, entry);
+
+       while ((entry = ldap_next_entry(ldap_state->ldap_struct, entry))) {
+
+               rid = entry_to_user_rid(ldap_state, entry);
+               if (rid > top_rid) {
+                       top_rid = rid;
+               }
+       }
+
+       ldap_msgfree(result);
+
+       if (top_rid < ldap_state->low_nua_rid) 
+               top_rid = ldap_state->low_nua_rid;
+
+       return top_rid;
+}
+
+/**********************************************************************
+Connect to LDAP server and find the next available RID.
+*********************************************************************/
+static uint32 ldapsam_get_next_available_nua_rid(struct ldapsam_privates *ldap_state) {
+       uint32 next_nua_rid;
+       uint32 top_nua_rid;
+
+       top_nua_rid = search_top_nua_rid(ldap_state);
+
+       next_nua_rid = check_nua_rid_is_avail(ldap_state, 
+                                             top_nua_rid);
+       
+       return next_nua_rid;
+}
+
+/**********************************************************************
+Connect to LDAP server for password enumeration
+*********************************************************************/
+static NTSTATUS ldapsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       int rc;
+       pstring filter;
+
+       pstrcpy(filter, lp_ldap_filter());
+       all_string_sub(filter, "%u", "*", sizeof(pstring));
+
+       rc = ldapsam_search(ldap_state, lp_ldap_suffix(),
+                          LDAP_SCOPE_SUBTREE, filter, attr, 0,
+                          &ldap_state->result);
+
+       if (rc != LDAP_SUCCESS) {
+               DEBUG(0, ("LDAP search failed: %s\n", ldap_err2string(rc)));
+               DEBUG(3, ("Query was: %s, %s\n", lp_ldap_suffix(), filter));
+               ldap_msgfree(ldap_state->result);
+               ldap_state->result = NULL;
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+               ldap_count_entries(ldap_state->ldap_struct,
+               ldap_state->result)));
+
+       ldap_state->entry = ldap_first_entry(ldap_state->ldap_struct,
+                                ldap_state->result);
+       ldap_state->index = 0;
+
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+End enumeration of the LDAP password list 
+*********************************************************************/
+static void ldapsam_endsampwent(struct pdb_methods *my_methods)
+{
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       if (ldap_state->result) {
+               ldap_msgfree(ldap_state->result);
+               ldap_state->result = NULL;
+       }
+}
+
+/**********************************************************************
+Get the next entry in the LDAP password database 
+*********************************************************************/
+static NTSTATUS ldapsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       BOOL bret = False;
+
+       /* The rebind proc needs this *HACK*.  We are not multithreaded, so
+          this will work, but it's not nice. */
+       static_ldap_state = ldap_state;
+
+       while (!bret) {
+               if (!ldap_state->entry)
+                       return ret;
+               
+               ldap_state->index++;
+               bret = init_sam_from_ldap(ldap_state, user, ldap_state->entry);
+               
+               ldap_state->entry = ldap_next_entry(ldap_state->ldap_struct,
+                                           ldap_state->entry); 
+       }
+
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Get SAM_ACCOUNT entry from LDAP by username 
+*********************************************************************/
+static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       LDAPMessage *result;
+       LDAPMessage *entry;
+       int count;
+       
+       if (ldapsam_search_one_user_by_name(ldap_state, sname, &result) != LDAP_SUCCESS) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       count = ldap_count_entries(ldap_state->ldap_struct, result);
+       
+       if (count < 1) {
+               DEBUG(4,
+                     ("We don't find this user [%s] count=%d\n", sname,
+                      count));
+               return NT_STATUS_NO_SUCH_USER;
+       } else if (count > 1) {
+               DEBUG(1,
+                     ("Duplicate entries for this user [%s] Failing. count=%d\n", sname,
+                      count));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       entry = ldap_first_entry(ldap_state->ldap_struct, result);
+       if (entry) {
+               if (!init_sam_from_ldap(ldap_state, user, entry)) {
+                       DEBUG(1,("ldapsam_getsampwnam: init_sam_from_ldap failed for user '%s'!\n", sname));
+                       ldap_msgfree(result);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+               ldap_msgfree(result);
+               ret = NT_STATUS_OK;
+       } else {
+               ldap_msgfree(result);
+       }
+       return ret;
+}
+
+/**********************************************************************
+Get SAM_ACCOUNT entry from LDAP by rid 
+*********************************************************************/
+static NTSTATUS ldapsam_getsampwrid(struct pdb_methods *my_methods, SAM_ACCOUNT *user, uint32 rid)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct ldapsam_privates *ldap_state = 
+               (struct ldapsam_privates *)my_methods->private_data;
+       LDAPMessage *result;
+       LDAPMessage *entry;
+       int count;
+
+       if (ldapsam_search_one_user_by_rid(ldap_state, rid, &result) != LDAP_SUCCESS) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       count = ldap_count_entries(ldap_state->ldap_struct, result);
+               
+       if (count < 1) {
+               DEBUG(4,
+                     ("We don't find this rid [%i] count=%d\n", rid,
+                      count));
+               return NT_STATUS_NO_SUCH_USER;
+       } else if (count > 1) {
+               DEBUG(1,
+                     ("More than one user with rid [%i]. Failing. count=%d\n", rid,
+                      count));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       entry = ldap_first_entry(ldap_state->ldap_struct, result);
+       if (entry) {
+               if (!init_sam_from_ldap(ldap_state, user, entry)) {
+                       DEBUG(1,("ldapsam_getsampwrid: init_sam_from_ldap failed!\n"));
+                       ldap_msgfree(result);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+               ldap_msgfree(result);
+               ret = NT_STATUS_OK;
+       } else {
+               ldap_msgfree(result);
+       }
+       return ret;
+}
+
+static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+       uint32 rid;
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+               return NT_STATUS_NO_SUCH_USER;
+       return ldapsam_getsampwrid(my_methods, user, rid);
+}      
+
+/********************************************************************
+Do the actual modification - also change a plaittext passord if 
+it it set.
+**********************************************************************/
+
+static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods, 
+                                    SAM_ACCOUNT *newpwd, char *dn,
+                                    LDAPMod **mods, int ldap_op, BOOL pdb_add)
+{
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       int rc;
+       
+       if (!my_methods || !newpwd || !dn) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       if (!mods) {
+               DEBUG(5,("mods is empty: nothing to modify\n"));
+               /* may be password change below however */
+       } else {
+               switch(ldap_op)
+               {
+                       case LDAP_MOD_ADD: 
+                               make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "account");
+                               rc = ldapsam_add(ldap_state, dn, mods);
+                               break;
+                       case LDAP_MOD_REPLACE: 
+                               rc = ldapsam_modify(ldap_state, dn ,mods);
+                               break;
+                       default:        
+                               DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op));
+                               return NT_STATUS_UNSUCCESSFUL;
+               }
+               
+               if (rc!=LDAP_SUCCESS) {
+                       char *ld_error;
+                       ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+                                       &ld_error);
+                       DEBUG(1,
+                             ("failed to %s user dn= %s with: %s\n\t%s\n",
+                              ldap_op == LDAP_MOD_ADD ? "add" : "modify",
+                              dn, ldap_err2string(rc),
+                              ld_error));
+                       free(ld_error);
+                       return NT_STATUS_UNSUCCESSFUL;
+               }  
+       }
+       
+#ifdef LDAP_EXOP_X_MODIFY_PASSWD
+       if (!(pdb_get_acct_ctrl(newpwd)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))&&
+               (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_OFF)&&
+               need_ldap_mod(pdb_add, newpwd, PDB_PLAINTEXT_PW)&&
+               (pdb_get_plaintext_passwd(newpwd)!=NULL)) {
+               BerElement *ber;
+               struct berval *bv;
+               char *retoid;
+               struct berval *retdata;
+
+               if ((ber = ber_alloc_t(LBER_USE_DER))==NULL) {
+                       DEBUG(0,("ber_alloc_t returns NULL\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               ber_printf (ber, "{");
+               ber_printf (ber, "ts", LDAP_TAG_EXOP_X_MODIFY_PASSWD_ID,dn);
+               ber_printf (ber, "ts", LDAP_TAG_EXOP_X_MODIFY_PASSWD_NEW, pdb_get_plaintext_passwd(newpwd));
+               ber_printf (ber, "N}");
+
+               if ((rc = ber_flatten (ber, &bv))<0) {
+                       DEBUG(0,("ber_flatten returns a value <0\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               
+               ber_free(ber,1);
+
+               if ((rc = ldapsam_extended_operation(ldap_state, LDAP_EXOP_X_MODIFY_PASSWD,
+                                                   bv, NULL, NULL, &retoid, &retdata))!=LDAP_SUCCESS) {
+                       DEBUG(0,("LDAP Password could not be changed for user %s: %s\n",
+                               pdb_get_username(newpwd),ldap_err2string(rc)));
+               } else {
+                       DEBUG(3,("LDAP Password changed for user %s\n",pdb_get_username(newpwd)));
+    
+                       ber_bvfree(retdata);
+                       ber_memfree(retoid);
+               }
+               ber_bvfree(bv);
+       }
+#else
+       DEBUG(10,("LDAP PASSWORD SYNC is not supported!\n"));
+#endif /* LDAP_EXOP_X_MODIFY_PASSWD */
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Delete entry from LDAP for username 
+*********************************************************************/
+static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * sam_acct)
+{
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       const char *sname;
+       int rc;
+       char *dn;
+       LDAPMessage *entry;
+       LDAPMessage *result;
+
+       if (!sam_acct) {
+               DEBUG(0, ("sam_acct was NULL!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       sname = pdb_get_username(sam_acct);
+
+       DEBUG (3, ("Deleting user %s from LDAP.\n", sname));
+
+       rc = ldapsam_search_one_user_by_name(ldap_state, sname, &result);
+       if (rc != LDAP_SUCCESS) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       if (ldap_count_entries (ldap_state->ldap_struct, result) == 0) {
+               DEBUG (0, ("User doesn't exit!\n"));
+               ldap_msgfree (result);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       entry = ldap_first_entry (ldap_state->ldap_struct, result);
+       dn = ldap_get_dn (ldap_state->ldap_struct, entry);
+       ldap_msgfree(result);
+       
+       rc = ldapsam_delete(ldap_state, dn);
+
+       ldap_memfree (dn);
+       if (rc != LDAP_SUCCESS) {
+               char *ld_error;
+               ldap_get_option (ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+               DEBUG (0,("failed to delete user with uid = %s with: %s\n\t%s\n",
+                       sname, ldap_err2string (rc), ld_error));
+               free (ld_error);
+               return NT_STATUS_CANNOT_DELETE;
+       }
+
+       DEBUG (2,("successfully deleted uid = %s from the LDAP database\n", sname));
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Update SAM_ACCOUNT 
+*********************************************************************/
+static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       int rc;
+       char *dn;
+       LDAPMessage *result;
+       LDAPMessage *entry;
+       LDAPMod **mods;
+
+       if (!init_ldap_from_sam(ldap_state, &mods, LDAP_MOD_REPLACE, False, newpwd)) {
+               DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       if (mods == NULL) {
+               DEBUG(4,("mods is empty: nothing to update for user: %s\n",pdb_get_username(newpwd)));
+               return NT_STATUS_OK;
+       }
+       
+       rc = ldapsam_search_one_user_by_name(ldap_state, pdb_get_username(newpwd), &result);
+       if (rc != LDAP_SUCCESS) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (ldap_count_entries(ldap_state->ldap_struct, result) == 0) {
+               DEBUG(0, ("No user to modify!\n"));
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       entry = ldap_first_entry(ldap_state->ldap_struct, result);
+       dn = ldap_get_dn(ldap_state->ldap_struct, entry);
+        ldap_msgfree(result);
+       
+       ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,LDAP_MOD_REPLACE, False);
+       if (NT_STATUS_IS_ERR(ret)) {
+               DEBUG(0,("failed to modify user with uid = %s\n",
+                                       pdb_get_username(newpwd)));
+               ldap_mods_free(mods,1);
+               return ret;
+       }
+
+
+       DEBUG(2,
+             ("successfully modified uid = %s in the LDAP database\n",
+              pdb_get_username(newpwd)));
+       ldap_mods_free(mods, 1);
+       return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Add SAM_ACCOUNT to LDAP 
+*********************************************************************/
+static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+       int rc;
+       pstring filter;
+       LDAPMessage *result = NULL;
+       pstring dn;
+       LDAPMod **mods = NULL;
+       int             ldap_op;
+       uint32          num_result;
+       
+       const char *username = pdb_get_username(newpwd);
+       if (!username || !*username) {
+               DEBUG(0, ("Cannot add user without a username!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       rc = ldapsam_search_one_user_by_name (ldap_state, username, &result);
+       if (rc != LDAP_SUCCESS) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (ldap_count_entries(ldap_state->ldap_struct, result) != 0) {
+               DEBUG(0,("User '%s' already in the base, with samba properties\n", 
+                        username));
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       ldap_msgfree(result);
+
+       slprintf (filter, sizeof (filter) - 1, "uid=%s", username);
+       rc = ldapsam_search_one_user(ldap_state, filter, &result);
+       if (rc != LDAP_SUCCESS) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       num_result = ldap_count_entries(ldap_state->ldap_struct, result);
+       
+       if (num_result > 1) {
+               DEBUG (0, ("More than one user with that uid exists: bailing out!\n"));
+               ldap_msgfree(result);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* Check if we need to update an existing entry */
+       if (num_result == 1) {
+               char *tmp;
+               LDAPMessage *entry;
+               
+               DEBUG(3,("User exists without samba properties: adding them\n"));
+               ldap_op = LDAP_MOD_REPLACE;
+               entry = ldap_first_entry (ldap_state->ldap_struct, result);
+               tmp = ldap_get_dn (ldap_state->ldap_struct, entry);
+               slprintf (dn, sizeof (dn) - 1, "%s", tmp);
+               ldap_memfree (tmp);
+       } else {
+               /* Check if we need to add an entry */
+               DEBUG(3,("Adding new user\n"));
+               ldap_op = LDAP_MOD_ADD;
+               if (username[strlen(username)-1] == '$') {
+                        slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_machine_suffix ());
+                } else {
+                        slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_user_suffix ());
+                }
+       }
+
+       ldap_msgfree(result);
+
+       if (!init_ldap_from_sam(ldap_state, &mods, ldap_op, True, newpwd)) {
+               DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n"));
+               ldap_mods_free(mods, 1);
+               return NT_STATUS_UNSUCCESSFUL;          
+       }
+       
+       if (mods == NULL) {
+               DEBUG(0,("mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "sambaAccount");
+
+       ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,ldap_op, True);
+       if (NT_STATUS_IS_ERR(ret)) {
+               DEBUG(0,("failed to modify/add user with uid = %s (dn = %s)\n",
+                        pdb_get_username(newpwd),dn));
+               ldap_mods_free(mods,1);
+               return ret;
+       }
+
+       DEBUG(2,("added: uid = %s in the LDAP database\n", pdb_get_username(newpwd)));
+       ldap_mods_free(mods, 1);
+       return NT_STATUS_OK;
+}
+
+static void free_private_data(void **vp) 
+{
+       struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp;
+
+       ldapsam_close(*ldap_state);
+
+       if ((*ldap_state)->bind_secret) {
+               memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret));
+       }
+
+       ldapsam_close(*ldap_state);
+               
+       SAFE_FREE((*ldap_state)->bind_dn);
+       SAFE_FREE((*ldap_state)->bind_secret);
+
+       *ldap_state = NULL;
+
+       /* No need to free any further, as it is talloc()ed */
+}
+
+NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct ldapsam_privates *ldap_state;
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "ldapsam";
+
+       (*pdb_method)->setsampwent = ldapsam_setsampwent;
+       (*pdb_method)->endsampwent = ldapsam_endsampwent;
+       (*pdb_method)->getsampwent = ldapsam_getsampwent;
+       (*pdb_method)->getsampwnam = ldapsam_getsampwnam;
+       (*pdb_method)->getsampwsid = ldapsam_getsampwsid;
+       (*pdb_method)->add_sam_account = ldapsam_add_sam_account;
+       (*pdb_method)->update_sam_account = ldapsam_update_sam_account;
+       (*pdb_method)->delete_sam_account = ldapsam_delete_sam_account;
+
+       /* TODO: Setup private data and free */
+
+       ldap_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct ldapsam_privates));
+
+       if (!ldap_state) {
+               DEBUG(0, ("talloc() failed for ldapsam private_data!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (location) {
+               ldap_state->uri = talloc_strdup(pdb_context->mem_ctx, location);
+#ifdef WITH_LDAP_SAMCONFIG
+       } else {
+               int ldap_port = lp_ldap_port();
+                       
+               /* remap default port if not using SSL (ie clear or TLS) */
+               if ( (lp_ldap_ssl() != LDAP_SSL_ON) && (ldap_port == 636) ) {
+                       ldap_port = 389;
+               }
+
+               ldap_state->uri = talloc_asprintf(pdb_context->mem_ctx, "%s://%s:%d", lp_ldap_ssl() == LDAP_SSL_ON ? "ldaps" : "ldap", lp_ldap_server(), ldap_port);
+               if (!ldap_state->uri) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+#else
+       } else {
+               ldap_state->uri = "ldap://localhost";
+#endif
+       }
+
+       (*pdb_method)->private_data = ldap_state;
+
+       (*pdb_method)->free_private_data = free_private_data;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_init_ldapsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct ldapsam_privates *ldap_state;
+       uint32 low_nua_uid, high_nua_uid;
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "ldapsam_nua";
+
+       ldap_state = (*pdb_method)->private_data;
+       
+       ldap_state->permit_non_unix_accounts = True;
+
+       if (!lp_non_unix_account_range(&low_nua_uid, &high_nua_uid)) {
+               DEBUG(0, ("cannot use ldapsam_nua without 'non unix account range' in smb.conf!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       ldap_state->low_nua_rid=fallback_pdb_uid_to_user_rid(low_nua_uid);
+
+       ldap_state->high_nua_rid=fallback_pdb_uid_to_user_rid(high_nua_uid);
+
+       return NT_STATUS_OK;
+}
+
+
+#else
+
+NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       DEBUG(0, ("ldap not detected at configure time, ldapsam not availalble!\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_init_ldapsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       DEBUG(0, ("ldap not dectected at configure time, ldapsam_nua not available!\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+#endif
diff --git a/source4/passdb/pdb_nisplus.c b/source4/passdb/pdb_nisplus.c
new file mode 100644 (file)
index 0000000..0a42c36
--- /dev/null
@@ -0,0 +1,1565 @@
+
+/*
+ * NIS+ Passdb Backend
+ * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
+ * Copyright (C) Benny Holmgren 1998 <bigfoot@astrakan.hgs.se> 
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998.
+ * Copyright (C) Toomas Soome <tsoome@ut.ee> 2001
+ * Copyright (C) Jelmer Vernooij 2002
+ * 
+ * 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"
+
+#ifdef WITH_NISPLUS_SAM
+
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+
+/*
+ * The following lines are needed due to buggy include files
+ * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
+ * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
+ * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
+ * an enum in /usr/include/rpcsvc/nis.h.
+ */
+
+
+#if defined(GROUP)
+#undef GROUP
+#endif
+
+#if defined(GROUP_OBJ)
+#undef GROUP_OBJ
+#endif
+
+#endif
+
+#include <rpcsvc/nis.h>
+
+/***************************************************************
+
+ the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are:
+
+               name=S,nogw=r 
+               uid=S,nogw=r 
+               user_rid=S,nogw=r
+               smb_grpid=,nw+r
+               group_rid=,nw+r
+               acb=,nw+r
+
+               lmpwd=C,nw=,g=r,o=rm 
+               ntpwd=C,nw=,g=r,o=rm 
+
+               logon_t=,nw+r 
+               logoff_t=,nw+r 
+               kick_t=,nw+r 
+               pwdlset_t=,nw+r 
+               pwdlchg_t=,nw+r 
+               pwdmchg_t=,nw+r 
+                               
+               full_name=,nw+r 
+               home_dir=,nw+r 
+               dir_drive=,nw+r 
+               logon_script=,nw+r 
+               profile_path=,nw+r 
+               acct_desc=,nw+r 
+               workstations=,nw+r 
+                                  
+               hours=,nw+r 
+
+****************************************************************/
+
+#define NPF_NAME          0
+#define NPF_UID           1
+#define NPF_USER_RID      2
+#define NPF_SMB_GRPID     3
+#define NPF_GROUP_RID     4
+#define NPF_ACB           5
+#define NPF_LMPWD         6
+#define NPF_NTPWD         7
+#define NPF_LOGON_T       8
+#define NPF_LOGOFF_T      9
+#define NPF_KICK_T        10
+#define NPF_PWDLSET_T     11
+#define NPF_PWDCCHG_T     12
+#define NPF_PWDMCHG_T     13
+#define NPF_FULL_NAME     14
+#define NPF_HOME_DIR      15
+#define NPF_DIR_DRIVE     16
+#define NPF_LOGON_SCRIPT  17
+#define NPF_PROFILE_PATH  18
+#define NPF_ACCT_DESC     19
+#define NPF_WORKSTATIONS  20
+#define NPF_HOURS         21
+
+struct nisplus_private_info {
+       nis_result *result;
+       int enum_entry;
+       char *location;
+};
+
+static char *make_nisname_from_user_rid (uint32 rid, char *pfile);
+static char *make_nisname_from_name (const char *user_name, char *pfile);
+static void get_single_attribute (const nis_object * new_obj, int col,
+                                 char *val, int len);;
+static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf,
+                                      const nis_object * obj);
+static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf,
+                                    const nis_result * result);;
+static void set_single_attribute (nis_object * new_obj, int col,
+                                 const char *val, int len, int flags);
+static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass,
+                               nis_object * old);
+static nis_result *nisp_get_nis_list (const char *nisname,
+                                     unsigned int flags);
+
+/***************************************************************
+ Start enumeration of the passwd list.
+****************************************************************/
+
+static NTSTATUS nisplussam_setsampwent (struct pdb_methods *methods, BOOL update)
+{
+       struct nisplus_private_info *private =
+               (struct nisplus_private_info *) methods->private_data;
+
+       char *sp;
+       pstring pfiletmp;
+
+       if ((sp = strrchr (private->location, '/')))
+               safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1);
+       else
+               safe_strcpy (pfiletmp, p, sizeof (pfiletmp) - 1);
+       safe_strcat (pfiletmp, ".org_dir",
+                    sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+
+       pdb_endsampwent ();     /* just in case */
+       global_nisp_ent->result = nisp_get_nis_list (pfiletmp, 0);
+       global_nisp_ent->enum_entry = 0;
+       if (global_nisp_ent->result != NULL) 
+               return NT_STATUS_UNSUCCESSFUL;
+       else
+               return NT_STATUS_OK;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+****************************************************************/
+
+static void nisplussam_endsampwent (struct pdb_methods *methods)
+{
+       struct nisplus_private_info *global_nisp_ent =
+               (struct nisplus_private_info *) methods->private_data;
+       if (global_nisp_ent->result)
+               nis_freeresult (global_nisp_ent->result);
+       global_nisp_ent->result = NULL;
+       global_nisp_ent->enum_entry = 0;
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+*****************************************************************/
+
+static NTSTATUS nisplussam_getsampwent (struct pdb_methods *methods,
+                                   SAM_ACCOUNT * user)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct nisplus_private_info *global_nisp_ent =
+               (struct nisplus_private_info *) methods->private_data;
+       int enum_entry = (int) (global_nisp_ent->enum_entry);
+       nis_result *result = global_nisp_ent->result;
+
+       if (user == NULL) {
+               DEBUG (0, ("SAM_ACCOUNT is NULL.\n"));
+               return nt_status;
+       }
+
+       if (result == NULL || enum_entry < 0 || enum_entry >= (NIS_RES_NUMOBJ (result) - 1)) {
+               return nt_status;
+       }
+
+       if (!make_sam_from_nisp_object(user, &NIS_RES_OBJECT (result)[enum_entry])) {
+               DEBUG (0, ("Bad SAM_ACCOUNT entry returned from NIS+!\n"));
+               return nt_status;
+       }
+       (int) (global_nisp_ent->enum_entry)++;
+
+       return nt_status;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+******************************************************************/
+
+static NTSTATUS nisplussam_getsampwnam (struct pdb_methods *methods,
+                                   SAM_ACCOUNT * user, const char *sname)
+{
+       /* Static buffers we will return. */
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       nis_result *result = NULL;
+       pstring nisname;
+       BOOL ret;
+       struct nisplus_private_info *private =
+               (struct nisplus_private_info *) methods->private_data;
+
+       if (!private->location || !(*private->location)) {
+               DEBUG (0, ("No SMB password file set\n"));
+               return nt_status;
+       }
+       if (strrchr (private->location, '/'))
+               private->location = strrchr (private->location, '/') + 1;
+
+       slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
+                 sname, private->location);
+       DEBUG (10, ("search by nisname: %s\n", nisname));
+
+       /* Search the table. */
+
+       if (!(result = nisp_get_nis_list (nisname, 0))) {
+               return nt_status;
+       }
+
+       ret = make_sam_from_nisresult (user, result);
+       nis_freeresult (result);
+
+       if (ret) nt_status = NT_STATUS_OK;
+
+       return nt_status;
+}
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS nisplussam_getsampwrid (struct pdb_methods *methods,
+                                   SAM_ACCOUNT * user, uint32 rid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       nis_result *result;
+       char *nisname;
+       BOOL ret;
+       char *sp;
+       pstring pfiletmp;
+       struct nisplus_private_info *private =
+               (struct nisplus_private_info *) methods->private_data;
+
+       if (!private->location || !(*private->location)) {
+               DEBUG (0, ("no SMB password file set\n"));
+               return nt_status;
+       }
+
+       if ((sp = strrchr (private->location, '/')))
+               safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1);
+       else
+               safe_strcpy (pfiletmp, private->location, sizeof (pfiletmp) - 1);
+       safe_strcat (pfiletmp, ".org_dir",
+                    sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+
+       nisname = make_nisname_from_user_rid (rid, pfiletmp);
+
+       DEBUG (10, ("search by rid: %s\n", nisname));
+
+       /* Search the table. */
+
+       if (!(result = nisp_get_nis_list (nisname, 0))) {
+               return nt_status;
+       }
+
+       ret = make_sam_from_nisresult (user, result);
+       nis_freeresult (result);
+
+       if (ret) nt_status = NT_STATUS_OK;
+
+       return nt_status;
+}
+
+static NTSTATUS nisplussam_getsampwsid (struct pdb_methods *methods,
+                                   SAM_ACCOUNT * user, const DOM_SID * sid)
+{
+       uint32 rid;
+
+       if (!sid_peek_check_rid (get_global_sam_sid (), sid, &rid))
+               return NT_STATUS_UNSUCCESSFUL;
+       return nisplussam_getsampwrid (methods, user, rid);
+}
+
+
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS nisplussam_delete_sam_account (struct pdb_methods *methods,
+                                               SAM_ACCOUNT * user)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       const char *sname;
+       pstring nisname;
+       nis_result *result, *delresult;
+       nis_object *obj;
+       struct nisplus_private_info *private =
+               (struct nisplus_private_info *) methods->private_data;
+
+       if (!user) {
+               DEBUG (0, ("no SAM_ACCOUNT specified!\n"));
+               return nt_status;
+       }
+
+       sname = pdb_get_username (user);
+
+       if (!private->location || !(*private->location)) {
+               DEBUG (0, ("no SMB password file set\n"));
+               return nt_status;
+       }
+
+       if (strrchr (private->location, '/'))
+               private->location = strrchr (private->location, '/') + 1;
+
+       slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
+                 sname, private->location);
+
+       /* Search the table. */
+
+       if (!(result = nisp_get_nis_list (nisname,
+                                         MASTER_ONLY | FOLLOW_LINKS |
+                                         FOLLOW_PATH | EXPAND_NAME |
+                                         HARD_LOOKUP))) {
+               return nt_status;
+       }
+
+       if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
+               /* User not found. */
+               DEBUG (0, ("user not found in NIS+\n"));
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       obj = NIS_RES_OBJECT (result);
+       slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s", sname,
+                 obj->zo_name, obj->zo_domain);
+
+       DEBUG (10, ("removing name: %s\n", nisname));
+       delresult = nis_remove_entry (nisname, obj,
+                                     MASTER_ONLY | REM_MULTIPLE | ALL_RESULTS
+                                     | FOLLOW_PATH | EXPAND_NAME |
+                                     HARD_LOOKUP);
+
+       nis_freeresult (result);
+
+       if (delresult->status != NIS_SUCCESS) {
+               DEBUG (0, ("NIS+ table update failed: %s %s\n",
+                          nisname, nis_sperrno (delresult->status)));
+               nis_freeresult (delresult);
+               return nt_status;
+       }
+       nis_freeresult (delresult);
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Modifies an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS nisplussam_update_sam_account (struct pdb_methods *methods,
+                                          SAM_ACCOUNT * newpwd)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       nis_result *result, *addresult;
+       nis_object *obj;
+       nis_object new_obj;
+       entry_col *ecol;
+       int ta_maxcol;
+       struct nisplus_private_info *private =
+               (struct nisplus_private_info *) methods->private_data;
+       pstring nisname;
+
+       if (!private->location || !(*private->location)) {
+               DEBUG (0, ("no SMB password file set\n"));
+               return nt_status;
+       }
+       if (strrchr (private->location, '/'))
+               private->location = strrchr (private->location, '/') + 1;
+
+       slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
+                 pdb_get_username (newpwd), private->location);
+
+       DEBUG (10, ("search by name: %s\n", nisname));
+
+       /* Search the table. */
+
+       if (!
+           (result =
+            nisp_get_nis_list (nisname,
+                               MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
+                               EXPAND_NAME | HARD_LOOKUP))) {
+               return ne_status;
+       }
+
+       if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
+               /* User not found. */
+               DEBUG (0, ("user not found in NIS+\n"));
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       obj = NIS_RES_OBJECT (result);
+       DEBUG (6, ("entry found in %s\n", obj->zo_domain));
+
+       /* we must create new stub object with EN_MODIFIED flag.
+          this is because obj from result is going to be freed and
+          we do not want to break it or cause memory leaks or corruption.
+        */
+
+       memmove ((char *) &new_obj, obj, sizeof (new_obj));
+       ta_maxcol = obj->TA_data.ta_maxcol;
+
+       if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) {
+               DEBUG (0, ("memory allocation failure\n"));
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       memmove ((char *) ecol, obj->EN_data.en_cols.en_cols_val,
+                ta_maxcol * sizeof (entry_col));
+       new_obj.EN_data.en_cols.en_cols_val = ecol;
+       new_obj.EN_data.en_cols.en_cols_len = ta_maxcol;
+
+       if (init_nisp_from_sam (&new_obj, newpwd, obj) == True) {
+               slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s",
+                         pdb_get_username (newpwd), private->location, obj->zo_domain);
+
+               DEBUG (10, ("NIS+ table update: %s\n", nisname));
+               addresult =
+                       nis_modify_entry (nisname, &new_obj,
+                                         MOD_SAMEOBJ | FOLLOW_PATH |
+                                         EXPAND_NAME | HARD_LOOKUP);
+
+               if (addresult->status != NIS_SUCCESS) {
+                       DEBUG (0, ("NIS+ table update failed: %s %s\n",
+                                  nisname, nis_sperrno (addresult->status)));
+                       nis_freeresult (addresult);
+                       nis_freeresult (result);
+                       free (ecol);
+                       return nt_status;
+               }
+
+               DEBUG (6, ("password changed\n"));
+               nis_freeresult (addresult);
+       } else {
+               DEBUG (6, ("nothing to change!\n"));
+       }
+
+       free (ecol);
+       nis_freeresult (result);
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS nisplussam_add_sam_account (struct pdb_methods *methods,
+                                       SAM_ACCOUNT * newpwd)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       int local_user = 0;
+       char *pfile;
+       pstring pfiletmp;
+       char *nisname;
+       nis_result *result = NULL, *tblresult = NULL;
+       nis_object new_obj;
+       entry_col *ecol;
+       int ta_maxcol;
+
+       /*
+        * 1. find user domain.
+        *   a. try nis search in passwd.org_dir - if found use domain from result.
+        *   b. try getpwnam. this may be needed if user is defined
+        *      in /etc/passwd file (or elsewere) and not in passwd.org_dir.
+        *      if found, use host default domain.
+        *   c. exit with False - no such user.
+        *
+        * 2. add user
+        *   a. find smbpasswd table
+        *      search pfile in user domain if not found, try host default
+        *      domain. 
+        *   b. smbpasswd domain is found, fill data and add entry.
+        *
+        * pfile should contain ONLY table name, org_dir will be concated.
+        * so, at first we will clear path prefix from pfile, and
+        * then we will use pfiletmp as playground to put together full
+        * nisname string.
+        * such approach will make it possible to specify samba private dir
+        * AND still use NIS+ table. as all domain related data is normally
+        * stored in org_dir.DOMAIN, this should be ok do do.
+        */
+
+       pfile = private->location;
+       if (strrchr (pfile, '/'))
+               pfile = strrchr (pfile, '/') + 1;
+
+       /*
+        * Check if user is already there.
+        */
+       safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
+       safe_strcat (pfiletmp, ".org_dir",
+                    sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+
+       if (pdb_get_username (newpwd) != NULL) {
+               nisname = make_nisname_from_name (pdb_get_username (newpwd),
+                                                 pfiletmp);
+       } else {
+               return nt_status;
+       }
+
+       if (!
+           (result =
+            nisp_get_nis_list (nisname,
+                               MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
+                               EXPAND_NAME | HARD_LOOKUP))) {
+               return nt_status;
+       }
+       if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) {
+               DEBUG (3, ("nis_list failure: %s: %s\n",
+                          nisname, nis_sperrno (result->status)));
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ (result) > 0) {
+               DEBUG (3, ("User already exists in NIS+ password db: %s\n",
+                          pfile));
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       nis_freeresult (result);        /* no such user, free results */
+
+       /*
+        * check for user in unix password database. we need this to get
+        * domain, where smbpasswd entry should be stored.
+        */
+
+       nisname = make_nisname_from_name (pdb_get_username (newpwd),
+                                         "passwd.org_dir");
+
+       result = nisp_get_nis_list (nisname,
+                                   MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
+                                   EXPAND_NAME | HARD_LOOKUP);
+
+       if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
+               struct passwd *passwd;
+
+               DEBUG (3, ("nis_list failure: %s: %s\n",
+                          nisname, nis_sperrno (result->status)));
+               nis_freeresult (result);
+
+               if (!(passwd = getpwnam_alloc (pdb_get_username (newpwd)))) {
+                       /* no such user in system! */
+                       return nt_status;
+               }
+               passwd_free (&passwd);
+
+               /* 
+                * user is defined, but not in passwd.org_dir.
+                */
+               local_user = 1;
+       } else {
+               safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
+               safe_strcat (pfiletmp, ".",
+                            sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+               safe_strcat (pfiletmp, NIS_RES_OBJECT (result)->zo_domain,
+                            sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+               nis_freeresult (result);        /* not needed any more */
+
+               tblresult = nisp_get_nis_list (pfiletmp,
+                                              MASTER_ONLY | FOLLOW_LINKS |
+                                              FOLLOW_PATH | EXPAND_NAME |
+                                              HARD_LOOKUP);
+       }
+
+       if (local_user || tblresult->status != NIS_SUCCESS) {
+               /*
+                * no user domain or
+                * smbpasswd table not found in user domain, fallback to
+                * default domain.
+                */
+               if (!local_user)        /* free previous failed search result */
+                       nis_freeresult (tblresult);
+
+               safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
+               safe_strcat (pfiletmp, ".org_dir",
+                            sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+               tblresult = nis_lookup (pfiletmp, MASTER_ONLY | FOLLOW_LINKS |
+                                       FOLLOW_PATH | EXPAND_NAME |
+                                       HARD_LOOKUP);
+               if (tblresult->status != NIS_SUCCESS) {
+                       /* still nothing. bail out */
+                       nis_freeresult (tblresult);
+                       DEBUG (3, ("nis_lookup failure: %s\n",
+                                  nis_sperrno (tblresult->status)));
+                       return nt_status;
+               }
+               /* we need full name for nis_add_entry() */
+               safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
+               safe_strcat (pfiletmp, ".",
+                            sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+               safe_strcat (pfiletmp, NIS_RES_OBJECT (tblresult)->zo_domain,
+                            sizeof (pfiletmp) - strlen (pfiletmp) - 1);
+       }
+
+       memset ((char *) &new_obj, 0, sizeof (new_obj));
+       /* fill entry headers */
+       /* we do not free these. */
+       new_obj.zo_name = NIS_RES_OBJECT (tblresult)->zo_name;
+       new_obj.zo_owner = NIS_RES_OBJECT (tblresult)->zo_owner;
+       new_obj.zo_group = NIS_RES_OBJECT (tblresult)->zo_group;
+       new_obj.zo_domain = NIS_RES_OBJECT (tblresult)->zo_domain;
+       /* uints */
+       new_obj.zo_access = NIS_RES_OBJECT (tblresult)->zo_access;
+       new_obj.zo_ttl = NIS_RES_OBJECT (tblresult)->zo_ttl;
+
+       new_obj.zo_data.zo_type = ENTRY_OBJ;
+       new_obj.EN_data.en_type = NIS_RES_OBJECT (tblresult)->TA_data.ta_type;
+
+       ta_maxcol = NIS_RES_OBJECT (tblresult)->TA_data.ta_maxcol;
+
+       if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) {
+               DEBUG (0, ("memory allocation failure\n"));
+               nis_freeresult (tblresult);
+               return nt_status;
+       }
+
+       memset ((char *) ecol, 0, ta_maxcol * sizeof (entry_col));
+       new_obj.EN_data.en_cols.en_cols_val = ecol;
+       new_obj.EN_data.en_cols.en_cols_len = ta_maxcol;
+
+       init_nisp_from_sam (&new_obj, newpwd, NULL);
+
+       DEBUG (10, ("add NIS+ entry: %s\n", nisname));
+       result = nis_add_entry (pfiletmp, &new_obj, 0);
+
+       free (ecol);            /* free allocated entry space */
+
+       if (result->status != NIS_SUCCESS) {
+               DEBUG (3, ("NIS+ table update failed: %s,%s\n",
+                          nisname, nis_sperrno (result->status)));
+               nis_freeresult (tblresult);
+               nis_freeresult (result);
+               return nt_status;
+       }
+
+       nis_freeresult (tblresult);
+       nis_freeresult (result);
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************
+ make_nisname_from_user_rid
+ ****************************************************************/
+static char *make_nisname_from_user_rid (uint32 rid, char *pfile)
+{
+       static pstring nisname;
+
+       safe_strcpy (nisname, "[user_rid=", sizeof (nisname) - 1);
+       slprintf (nisname, sizeof (nisname) - 1, "%s%d", nisname, rid);
+       safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1);
+       safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1);
+
+       return nisname;
+}
+
+/***************************************************************
+ make_nisname_from_name
+ ****************************************************************/
+static char *make_nisname_from_name (const char *user_name, char *pfile)
+{
+       static pstring nisname;
+
+       safe_strcpy (nisname, "[name=", sizeof (nisname) - 1);
+       safe_strcat (nisname, user_name,
+                    sizeof (nisname) - strlen (nisname) - 1);
+       safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1);
+       safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1);
+
+       return nisname;
+}
+
+/*************************************************************************
+ gets a NIS+ attribute
+ *************************************************************************/
+static void get_single_attribute (const nis_object * new_obj, int col,
+                                 char *val, int len)
+{
+       int entry_len;
+
+       if (new_obj == NULL || val == NULL)
+               return;
+
+       entry_len = ENTRY_LEN (new_obj, col);
+       if (len > entry_len) {
+               len = entry_len;
+       }
+
+       safe_strcpy (val, ENTRY_VAL (new_obj, col), len - 1);
+}
+
+/************************************************************************
+ makes a struct sam_passwd from a NIS+ object.
+ ************************************************************************/
+static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf,
+                                      const nis_object * obj)
+{
+       char *ptr;
+       pstring full_name;      /* this must be translated to dos code page */
+       pstring acct_desc;      /* this must be translated to dos code page */
+       pstring home_dir;       /* set default value from smb.conf for user */
+       pstring home_drive;     /* set default value from smb.conf for user */
+       pstring logon_script;   /* set default value from smb.conf for user */
+       pstring profile_path;   /* set default value from smb.conf for user */
+       pstring hours;
+       int hours_len;
+       unsigned char smbpwd[16];
+       unsigned char smbntpwd[16];
+
+
+       /*
+        * time values. note: this code assumes 32bit time_t!
+        */
+
+       /* Don't change these timestamp settings without a good reason.  They are
+          important for NT member server compatibility. */
+
+       pdb_set_logon_time (pw_buf, (time_t) 0, PDB_DEFAULT);
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGON_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "LNT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_logon_time (pw_buf,
+                                           (time_t) strtol (ptr, NULL, 16),
+                                           PDB_SET);
+               }
+       }
+
+       pdb_set_logoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT);
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGOFF_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "LOT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_logoff_time (pw_buf,
+                                            (time_t) strtol (ptr, NULL, 16),
+                                            PDB_SET);
+               }
+       }
+
+       pdb_set_kickoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT);
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_KICK_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "KOT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_kickoff_time (pw_buf,
+                                             (time_t) strtol (ptr, NULL, 16),
+                                             PDB_SET);
+               }
+       }
+
+       pdb_set_pass_last_set_time (pw_buf, (time_t) 0, PDB_DEFAULT);
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDLSET_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "LCT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_pass_last_set_time (pw_buf,
+                                                   (time_t) strtol (ptr,
+                                                                    NULL,
+                                                                    16),
+                                                    PDB_SET);
+               }
+       }
+
+       pdb_set_pass_can_change_time (pw_buf, (time_t) 0, PDB_DEFAULT);
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDCCHG_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "CCT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_pass_can_change_time (pw_buf,
+                                                     (time_t) strtol (ptr,
+                                                                      NULL,
+                                                                      16),
+                                                     PDB_SET);
+               }
+       }
+
+       pdb_set_pass_must_change_time (pw_buf, get_time_t_max (), PDB_DEFAULT); /* Password never expires. */
+       ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDMCHG_T);
+       if (ptr && *ptr && (StrnCaseCmp (ptr, "MCT-", 4) == 0)) {
+               int i;
+
+               ptr += 4;
+               for (i = 0; i < 8; i++) {
+                       if (ptr[i] == '\0' || !isxdigit (ptr[i]))
+                               break;
+               }
+               if (i == 8) {
+                       pdb_set_pass_must_change_time (pw_buf,
+                                                      (time_t) strtol (ptr,
+                                                                       NULL,
+                                                                       16),
+                                                      PDB_SET);
+               }
+       }
+
+       /* string values */
+       pdb_set_username (pw_buf, ENTRY_VAL (obj, NPF_NAME), PDB_SET);
+       pdb_set_domain (pw_buf, lp_workgroup (), PDB_DEFAULT);
+       /* pdb_set_nt_username() -- cant set it here... */
+
+       get_single_attribute (obj, NPF_FULL_NAME, full_name,
+                             sizeof (pstring));
+#if 0
+       unix_to_dos (full_name, True);
+#endif
+       pdb_set_fullname (pw_buf, full_name, PDB_SET);
+
+       pdb_set_acct_ctrl (pw_buf, pdb_decode_acct_ctrl (ENTRY_VAL (obj,
+                                                                   NPF_ACB), PDB_SET));
+
+       get_single_attribute (obj, NPF_ACCT_DESC, acct_desc,
+                             sizeof (pstring));
+#if 0
+       unix_to_dos (acct_desc, True);
+#endif
+       pdb_set_acct_desc (pw_buf, acct_desc, PDB_SET);
+
+       pdb_set_workstations (pw_buf, ENTRY_VAL (obj, NPF_WORKSTATIONS), PDB_SET);
+       pdb_set_munged_dial (pw_buf, NULL, PDB_DEFAULT);
+
+       pdb_set_uid (pw_buf, atoi (ENTRY_VAL (obj, NPF_UID)), PDB_SET);
+       pdb_set_gid (pw_buf, atoi (ENTRY_VAL (obj, NPF_SMB_GRPID)), PDB_SET);
+       pdb_set_user_sid_from_rid (pw_buf,
+                                  atoi (ENTRY_VAL (obj, NPF_USER_RID)), PDB_SET);
+       pdb_set_group_sid_from_rid (pw_buf,
+                                   atoi (ENTRY_VAL (obj, NPF_GROUP_RID)), PDB_SET);
+
+       /* values, must exist for user */
+       if (!(pdb_get_acct_ctrl (pw_buf) & ACB_WSTRUST)) {
+
+               get_single_attribute (obj, NPF_HOME_DIR, home_dir,
+                                     sizeof (pstring));
+               if (!(home_dir && *home_dir)) {
+                       pstrcpy (home_dir, lp_logon_home ());
+                       pdb_set_homedir (pw_buf, home_dir, PDB_DEFAULT);
+               } else
+                       pdb_set_homedir (pw_buf, home_dir, PDB_SET);
+
+               get_single_attribute (obj, NPF_DIR_DRIVE, home_drive,
+                                     sizeof (pstring));
+               if (!(home_drive && *home_drive)) {
+                       pstrcpy (home_drive, lp_logon_drive ());
+                       pdb_set_dir_drive (pw_buf, home_drive, PDB_DEFAULT);
+               } else
+                       pdb_set_dir_drive (pw_buf, home_drive, PDB_SET);
+
+               get_single_attribute (obj, NPF_LOGON_SCRIPT, logon_script,
+                                     sizeof (pstring));
+               if (!(logon_script && *logon_script)) {
+                       pstrcpy (logon_script, lp_logon_script ());
+                       pdb_set_logon_script (pw_buf, logon_script, PDB_DEFAULT);
+               } else
+                       pdb_set_logon_script (pw_buf, logon_script, PDB_SET);
+
+               get_single_attribute (obj, NPF_PROFILE_PATH, profile_path,
+                                     sizeof (pstring));
+               if (!(profile_path && *profile_path)) {
+                       pstrcpy (profile_path, lp_logon_path ());
+                       pdb_set_profile_path (pw_buf, profile_path, PDB_DEFAULT);
+               } else
+                       pdb_set_profile_path (pw_buf, profile_path, PDB_SET);
+
+       } else {
+               /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
+               pdb_set_group_sid_from_rid (pw_buf, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+       }
+
+       /* Check the lanman password column. */
+       ptr = (char *) ENTRY_VAL (obj, NPF_LMPWD);
+       if (!pdb_set_lanman_passwd (pw_buf, NULL, PDB_DEFAULT))
+               return False;
+
+       if (!strncasecmp (ptr, "NO PASSWORD", 11)) {
+               pdb_set_acct_ctrl (pw_buf,
+                                  pdb_get_acct_ctrl (pw_buf) | ACB_PWNOTREQ, PDB_SET);
+       } else {
+               if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbpwd)) {
+                       DEBUG (0, ("malformed LM pwd entry: %s.\n",
+                                  pdb_get_username (pw_buf)));
+                       return False;
+               }
+               if (!pdb_set_lanman_passwd (pw_buf, smbpwd, PDB_SET))
+                       return False;
+       }
+
+       /* Check the NT password column. */
+       ptr = ENTRY_VAL (obj, NPF_NTPWD);
+       if (!pdb_set_nt_passwd (pw_buf, NULL, PDB_DEFAULT))
+               return False;
+
+       if (!(pdb_get_acct_ctrl (pw_buf) & ACB_PWNOTREQ) &&
+           strncasecmp (ptr, "NO PASSWORD", 11)) {
+               if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbntpwd)) {
+                       DEBUG (0, ("malformed NT pwd entry:\
+ uid = %d.\n", pdb_get_uid (pw_buf)));
+                       return False;
+               }
+               if (!pdb_set_nt_passwd (pw_buf, smbntpwd, PDB_SET))
+                       return False;
+       }
+
+       pdb_set_unknown_3 (pw_buf, 0xffffff, PDB_DEFAULT);      /* don't know */
+       pdb_set_logon_divs (pw_buf, 168, PDB_DEFAULT);  /* hours per week */
+
+       if ((hours_len = ENTRY_LEN (obj, NPF_HOURS)) == 21) {
+               memcpy (hours, ENTRY_VAL (obj, NPF_HOURS), hours_len);
+       } else {
+               hours_len = 21; /* 21 times 8 bits = 168 */
+               /* available at all hours */
+               memset (hours, 0xff, hours_len);
+       }
+       pdb_set_hours_len (pw_buf, hours_len, PDB_SET);
+       pdb_set_hours (pw_buf, hours, PDB_SET);
+
+       pdb_set_unknown_5 (pw_buf, 0x00020000, PDB_DEFAULT);    /* don't know */
+       pdb_set_unknown_6 (pw_buf, 0x000004ec, PDB_DEFAULT);    /* don't know */
+
+       return True;
+}
+
+/************************************************************************
+ makes a struct sam_passwd from a NIS+ result.
+ ************************************************************************/
+static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf,
+                                    const nis_result * result)
+{
+       if (pw_buf == NULL || result == NULL)
+               return False;
+
+       if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) {
+               DEBUG (0, ("NIS+ lookup failure: %s\n",
+                          nis_sperrno (result->status)));
+               return False;
+       }
+
+       /* User not found. */
+       if (NIS_RES_NUMOBJ (result) <= 0) {
+               DEBUG (10, ("user not found in NIS+\n"));
+               return False;
+       }
+
+       if (NIS_RES_NUMOBJ (result) > 1) {
+               DEBUG (10,
+                      ("WARNING: Multiple entries for user in NIS+ table!\n"));
+       }
+
+       /* Grab the first hit. */
+       return make_sam_from_nisp_object (pw_buf,
+                                         &NIS_RES_OBJECT (result)[0]);
+}
+
+/*************************************************************************
+ sets a NIS+ attribute
+ *************************************************************************/
+static void set_single_attribute (nis_object * new_obj, int col,
+                                 const char *val, int len, int flags)
+{
+       if (new_obj == NULL)
+               return;
+
+       ENTRY_VAL (new_obj, col) = val;
+       ENTRY_LEN (new_obj, col) = len + 1;
+
+       if (flags != 0) {
+               new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags;
+       }
+}
+
+/***************************************************************
+ copy or modify nis object. this object is used to add or update
+ nisplus table entry.
+ ****************************************************************/
+static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass,
+                               nis_object * old)
+{
+       /*
+        * Fill nis_object for entry add or update.
+        * if we are updateing, we have to find out differences and set
+        * EN_MODIFIED flag. also set need_to_modify to trigger
+        * nis_modify_entry() call in pdb_update_sam_account().
+        *
+        * TODO:
+        *   get data from SAM
+        *   if (modify) get data from nis_object, compare and store if
+        *               different + set EN_MODIFIED and need_to_modify
+        *   else
+        *               store
+        */
+       BOOL need_to_modify = False;
+       const char *name = pdb_get_username (sampass);  /* from SAM */
+
+       /* these must be static or allocate and free entry columns! */
+       static fstring uid;     /* from SAM */
+       static fstring user_rid;        /* from SAM */
+       static fstring gid;     /* from SAM */
+       static fstring group_rid;       /* from SAM */
+       char *acb;              /* from SAM */
+       static fstring smb_passwd;      /* from SAM */
+       static fstring smb_nt_passwd;   /* from SAM */
+       static fstring logon_t; /* from SAM */
+       static fstring logoff_t;        /* from SAM */
+       static fstring kickoff_t;       /* from SAM */
+       static fstring pwdlset_t;       /* from SAM */
+       static fstring pwdlchg_t;       /* from SAM */
+       static fstring pwdmchg_t;       /* from SAM */
+       static fstring full_name;       /* from SAM */
+       static fstring acct_desc;       /* from SAM */
+       static char empty[1];   /* just an empty string */
+
+       slprintf (uid, sizeof (uid) - 1, "%u", pdb_get_uid (sampass));
+       slprintf (user_rid, sizeof (user_rid) - 1, "%u",
+                 pdb_get_user_rid (sampass) ? pdb_get_user_rid (sampass) :
+                 fallback_pdb_uid_to_user_rid (pdb_get_uid (sampass)));
+       slprintf (gid, sizeof (gid) - 1, "%u", pdb_get_gid (sampass));
+
+       {
+               uint32 rid;
+               GROUP_MAP map;
+
+               rid = pdb_get_group_rid (sampass);
+
+               if (rid == 0) {
+                       if (pdb_getgrgid(&map, pdb_get_gid (sampass),
+                                        MAPPING_WITHOUT_PRIV)) {
+                               if (!sid_peek_check_rid
+                                   (get_global_sam_sid (), &map.sid, &rid))
+                                       return False;
+                       } else
+                               rid = pdb_gid_to_group_rid (pdb_get_gid
+                                                           (sampass));
+               }
+
+               slprintf (group_rid, sizeof (group_rid) - 1, "%u", rid);
+       }
+
+       acb = pdb_encode_acct_ctrl (pdb_get_acct_ctrl (sampass),
+                                   NEW_PW_FORMAT_SPACE_PADDED_LEN);
+       pdb_sethexpwd (smb_passwd, pdb_get_lanman_passwd (sampass),
+                      pdb_get_acct_ctrl (sampass));
+       pdb_sethexpwd (smb_nt_passwd, pdb_get_nt_passwd (sampass),
+                      pdb_get_acct_ctrl (sampass));
+       slprintf (logon_t, 13, "LNT-%08X",
+                 (uint32) pdb_get_logon_time (sampass));
+       slprintf (logoff_t, 13, "LOT-%08X",
+                 (uint32) pdb_get_logoff_time (sampass));
+       slprintf (kickoff_t, 13, "KOT-%08X",
+                 (uint32) pdb_get_kickoff_time (sampass));
+       slprintf (pwdlset_t, 13, "LCT-%08X",
+                 (uint32) pdb_get_pass_last_set_time (sampass));
+       slprintf (pwdlchg_t, 13, "CCT-%08X",
+                 (uint32) pdb_get_pass_can_change_time (sampass));
+       slprintf (pwdmchg_t, 13, "MCT-%08X",
+                 (uint32) pdb_get_pass_must_change_time (sampass));
+       safe_strcpy (full_name, pdb_get_fullname (sampass),
+                    sizeof (full_name) - 1);
+       safe_strcpy (acct_desc, pdb_get_acct_desc (sampass),
+                    sizeof (acct_desc) - 1);
+
+#if 0
+
+       /* Not sure what to do with these guys. -tpot */
+
+       dos_to_unix (full_name, True);
+       dos_to_unix (acct_desc, True);
+
+#endif
+
+       if (old) {
+               /* name */
+               if (strcmp (ENTRY_VAL (old, NPF_NAME), name)) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_NAME, name,
+                                             strlen (name), EN_MODIFIED);
+               }
+
+
+               /* uid */
+               if (pdb_get_uid (sampass) != -1) {
+                       if (!ENTRY_VAL (old, NPF_UID)
+                           || strcmp (ENTRY_VAL (old, NPF_UID), uid)) {
+                               need_to_modify = True;
+                               set_single_attribute (obj, NPF_UID, uid,
+                                                     strlen (uid),
+                                                     EN_MODIFIED);
+                       }
+               }
+
+               /* user_rid */
+               if (pdb_get_user_rid (sampass)) {
+                       if (!ENTRY_VAL (old, NPF_USER_RID) ||
+                           strcmp (ENTRY_VAL (old, NPF_USER_RID),
+                                   user_rid)) {
+                               need_to_modify = True;
+                               set_single_attribute (obj, NPF_USER_RID,
+                                                     user_rid,
+                                                     strlen (user_rid),
+                                                     EN_MODIFIED);
+                       }
+               }
+
+               /* smb_grpid */
+               if (pdb_get_gid (sampass) != -1) {
+                       if (!ENTRY_VAL (old, NPF_SMB_GRPID) ||
+                           strcmp (ENTRY_VAL (old, NPF_SMB_GRPID), gid)) {
+                               need_to_modify = True;
+                               set_single_attribute (obj, NPF_SMB_GRPID, gid,
+                                                     strlen (gid),
+                                                     EN_MODIFIED);
+                       }
+               }
+
+               /* group_rid */
+               if (pdb_get_group_rid (sampass)) {
+                       if (!ENTRY_VAL (old, NPF_GROUP_RID) ||
+                           strcmp (ENTRY_VAL (old, NPF_GROUP_RID),
+                                   group_rid)) {
+                               need_to_modify = True;
+                               set_single_attribute (obj, NPF_GROUP_RID,
+                                                     group_rid,
+                                                     strlen (group_rid),
+                                                     EN_MODIFIED);
+                       }
+               }
+
+               /* acb */
+               if (!ENTRY_VAL (old, NPF_ACB) ||
+                   strcmp (ENTRY_VAL (old, NPF_ACB), acb)) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_ACB, acb, strlen (acb),
+                                             EN_MODIFIED);
+               }
+
+               /* lmpwd */
+               if (!ENTRY_VAL (old, NPF_LMPWD) ||
+                   strcmp (ENTRY_VAL (old, NPF_LMPWD), smb_passwd)) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_LMPWD, smb_passwd,
+                                             strlen (smb_passwd),
+                                             EN_CRYPT | EN_MODIFIED);
+               }
+
+               /* ntpwd */
+               if (!ENTRY_VAL (old, NPF_NTPWD) ||
+                   strcmp (ENTRY_VAL (old, NPF_NTPWD), smb_nt_passwd)) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd,
+                                             strlen (smb_nt_passwd),
+                                             EN_CRYPT | EN_MODIFIED);
+               }
+
+               /* logon_t */
+               if (pdb_get_logon_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_LOGON_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_LOGON_T), logon_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_LOGON_T, logon_t,
+                                             strlen (logon_t), EN_MODIFIED);
+               }
+
+               /* logoff_t */
+               if (pdb_get_logoff_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_LOGOFF_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_LOGOFF_T), logoff_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_LOGOFF_T, logoff_t,
+                                             strlen (logoff_t), EN_MODIFIED);
+               }
+
+               /* kick_t */
+               if (pdb_get_kickoff_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_KICK_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_KICK_T), kickoff_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_KICK_T, kickoff_t,
+                                             strlen (kickoff_t),
+                                             EN_MODIFIED);
+               }
+
+               /* pwdlset_t */
+               if (pdb_get_pass_last_set_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_PWDLSET_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_PWDLSET_T), pwdlset_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t,
+                                             strlen (pwdlset_t),
+                                             EN_MODIFIED);
+               }
+
+               /* pwdlchg_t */
+               if (pdb_get_pass_can_change_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_PWDCCHG_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_PWDCCHG_T), pwdlchg_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t,
+                                             strlen (pwdlchg_t),
+                                             EN_MODIFIED);
+               }
+
+               /* pwdmchg_t */
+               if (pdb_get_pass_must_change_time (sampass) &&
+                   (!ENTRY_VAL (old, NPF_PWDMCHG_T) ||
+                    strcmp (ENTRY_VAL (old, NPF_PWDMCHG_T), pwdmchg_t))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t,
+                                             strlen (pwdmchg_t),
+                                             EN_MODIFIED);
+               }
+
+               /* full_name */
+               /* must support set, unset and change */
+               if ((pdb_get_fullname (sampass) &&
+                    !ENTRY_VAL (old, NPF_FULL_NAME)) ||
+                   (ENTRY_VAL (old, NPF_FULL_NAME) &&
+                    !pdb_get_fullname (sampass)) ||
+                   (ENTRY_VAL (old, NPF_FULL_NAME) &&
+                    pdb_get_fullname (sampass) &&
+                    strcmp (ENTRY_VAL (old, NPF_FULL_NAME), full_name))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_FULL_NAME, full_name,
+                                             strlen (full_name),
+                                             EN_MODIFIED);
+               }
+
+               /* home_dir */
+               /* must support set, unset and change */
+               if ((pdb_get_homedir (sampass) &&
+                    !ENTRY_VAL (old, NPF_HOME_DIR)) ||
+                   (ENTRY_VAL (old, NPF_HOME_DIR) &&
+                    !pdb_get_homedir (sampass)) ||
+                   (ENTRY_VAL (old, NPF_HOME_DIR) &&
+                    pdb_get_homedir (sampass) &&
+                    strcmp (ENTRY_VAL (old, NPF_HOME_DIR),
+                            pdb_get_homedir (sampass)))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_HOME_DIR,
+                                             pdb_get_homedir (sampass),
+                                             strlen (pdb_get_homedir
+                                                     (sampass)),
+                                             EN_MODIFIED);
+               }
+
+               /* dir_drive */
+               /* must support set, unset and change */
+               if ((pdb_get_dir_drive (sampass) &&
+                    !ENTRY_VAL (old, NPF_DIR_DRIVE)) ||
+                   (ENTRY_VAL (old, NPF_DIR_DRIVE) &&
+                    !pdb_get_dir_drive (sampass)) ||
+                   (ENTRY_VAL (old, NPF_DIR_DRIVE) &&
+                    pdb_get_dir_drive (sampass) &&
+                    strcmp (ENTRY_VAL (old, NPF_DIR_DRIVE),
+                            pdb_get_dir_drive (sampass)))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_DIR_DRIVE,
+                                             pdb_get_dir_drive (sampass),
+                                             strlen (pdb_get_dir_drive
+                                                     (sampass)),
+                                             EN_MODIFIED);
+               }
+
+               /* logon_script */
+               /* must support set, unset and change */
+               if (((pdb_get_logon_script (sampass) &&
+                     !ENTRY_VAL (old, NPF_LOGON_SCRIPT)) ||
+                    ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) &&
+                      (!pdb_get_logon_script (sampass)))) ||
+                    ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) &&
+                      pdb_get_logon_script (sampass) &&
+                      strcmp (ENTRY_VAL (old, NPF_LOGON_SCRIPT),
+                              pdb_get_logon_script (sampass)))))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_LOGON_SCRIPT,
+                                             pdb_get_logon_script (sampass),
+                                             strlen (pdb_get_logon_script
+                                                     (sampass)),
+                                             EN_MODIFIED);
+               }
+
+               /* profile_path */
+               /* must support set, unset and change */
+               if ((pdb_get_profile_path (sampass) &&
+                    !ENTRY_VAL (old, NPF_PROFILE_PATH)) ||
+                   (ENTRY_VAL (old, NPF_PROFILE_PATH) &&
+                    !pdb_get_profile_path (sampass)) ||
+                   (ENTRY_VAL (old, NPF_PROFILE_PATH) &&
+                    pdb_get_profile_path (sampass) &&
+                    strcmp (ENTRY_VAL (old, NPF_PROFILE_PATH),
+                            pdb_get_profile_path (sampass)))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_PROFILE_PATH,
+                                             pdb_get_profile_path (sampass),
+                                             strlen (pdb_get_profile_path
+                                                     (sampass)),
+                                             EN_MODIFIED);
+               }
+
+               /* acct_desc */
+               /* must support set, unset and change */
+               if ((pdb_get_acct_desc (sampass) &&
+                    !ENTRY_VAL (old, NPF_ACCT_DESC)) ||
+                   (ENTRY_VAL (old, NPF_ACCT_DESC) &&
+                    !pdb_get_acct_desc (sampass)) ||
+                   (ENTRY_VAL (old, NPF_ACCT_DESC) &&
+                    pdb_get_acct_desc (sampass) &&
+                    strcmp (ENTRY_VAL (old, NPF_ACCT_DESC), acct_desc))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_ACCT_DESC, acct_desc,
+                                             strlen (acct_desc),
+                                             EN_MODIFIED);
+               }
+
+               /* workstations */
+               /* must support set, unset and change */
+               if ((pdb_get_workstations (sampass) &&
+                    !ENTRY_VAL (old, NPF_WORKSTATIONS)) ||
+                   (ENTRY_VAL (old, NPF_WORKSTATIONS) &&
+                    !pdb_get_workstations (sampass)) ||
+                   (ENTRY_VAL (old, NPF_WORKSTATIONS) &&
+                    (pdb_get_workstations (sampass)) &&
+                    strcmp (ENTRY_VAL (old, NPF_WORKSTATIONS),
+                            pdb_get_workstations (sampass)))) {
+                       need_to_modify = True;
+                       set_single_attribute (obj, NPF_WORKSTATIONS,
+                                             pdb_get_workstations (sampass),
+                                             strlen (pdb_get_workstations
+                                                     (sampass)),
+                                             EN_MODIFIED);
+               }
+
+               /* hours */
+               if ((pdb_get_hours_len (sampass) !=
+                    ENTRY_LEN (old, NPF_HOURS))
+                   || memcmp (pdb_get_hours (sampass),
+                              ENTRY_VAL (old, NPF_HOURS), ENTRY_LEN (old,
+                                                                     NPF_HOURS)))
+               {
+                       need_to_modify = True;
+                       /* set_single_attribute will add 1 for len ... */
+                       set_single_attribute (obj, NPF_HOURS,
+                                             pdb_get_hours (sampass),
+                                             pdb_get_hours_len (sampass) - 1,
+                                             EN_MODIFIED);
+               }
+       } else {
+               const char *homedir, *dirdrive, *logon_script, *profile_path,
+                       *workstations;
+
+               *empty = '\0';  /* empty string */
+
+               set_single_attribute (obj, NPF_NAME, name, strlen (name), 0);
+               set_single_attribute (obj, NPF_UID, uid, strlen (uid), 0);
+               set_single_attribute (obj, NPF_USER_RID, user_rid,
+                                     strlen (user_rid), 0);
+               set_single_attribute (obj, NPF_SMB_GRPID, gid, strlen (gid),
+                                     0);
+               set_single_attribute (obj, NPF_GROUP_RID, group_rid,
+                                     strlen (group_rid), 0);
+               set_single_attribute (obj, NPF_ACB, acb, strlen (acb), 0);
+               set_single_attribute (obj, NPF_LMPWD, smb_passwd,
+                                     strlen (smb_passwd), EN_CRYPT);
+               set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd,
+                                     strlen (smb_nt_passwd), EN_CRYPT);
+               set_single_attribute (obj, NPF_LOGON_T, logon_t,
+                                     strlen (logon_t), 0);
+               set_single_attribute (obj, NPF_LOGOFF_T, logoff_t,
+                                     strlen (logoff_t), 0);
+               set_single_attribute (obj, NPF_KICK_T, kickoff_t,
+                                     strlen (kickoff_t), 0);
+               set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t,
+                                     strlen (pwdlset_t), 0);
+               set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t,
+                                     strlen (pwdlchg_t), 0);
+               set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t,
+                                     strlen (pwdmchg_t), 0);
+               set_single_attribute (obj, NPF_FULL_NAME,
+                                     full_name, strlen (full_name), 0);
+
+               if (!(homedir = pdb_get_homedir (sampass)))
+                       homedir = empty;
+
+               set_single_attribute (obj, NPF_HOME_DIR,
+                                     homedir, strlen (homedir), 0);
+
+               if (!(dirdrive = pdb_get_dir_drive (sampass)))
+                       dirdrive = empty;
+
+               set_single_attribute (obj, NPF_DIR_DRIVE,
+                                     dirdrive, strlen (dirdrive), 0);
+
+               if (!(logon_script = pdb_get_logon_script (sampass)))
+                       logon_script = empty;
+
+               set_single_attribute (obj, NPF_LOGON_SCRIPT,
+                                     logon_script, strlen (logon_script), 0);
+
+               if (!(profile_path = pdb_get_profile_path (sampass)))
+                       profile_path = empty;
+
+               set_single_attribute (obj, NPF_PROFILE_PATH,
+                                     profile_path, strlen (profile_path), 0);
+
+               set_single_attribute (obj, NPF_ACCT_DESC,
+                                     acct_desc, strlen (acct_desc), 0);
+
+               if (!(workstations = pdb_get_workstations (sampass)))
+                       workstations = empty;
+
+               set_single_attribute (obj, NPF_WORKSTATIONS,
+                                     workstations, strlen (workstations), 0);
+
+               /* set_single_attribute will add 1 for len ... */
+               set_single_attribute (obj, NPF_HOURS,
+                                     pdb_get_hours (sampass),
+                                     pdb_get_hours_len (sampass) - 1, 0);
+       }
+
+       return need_to_modify;
+}
+
+/***************************************************************
+ calls nis_list, returns results.
+ ****************************************************************/
+static nis_result *nisp_get_nis_list (const char *nisname, unsigned int flags)
+{
+       nis_result *result;
+       int i;
+
+       if (!flags)
+               flags = FOLLOW_LINKS | FOLLOW_PATH | EXPAND_NAME |
+                       HARD_LOOKUP;
+
+       for (i = 0; i < 2; i++) {
+               alarm (60);     /* hopefully ok for long searches */
+               result = nis_list (nisname, flags, NULL, NULL);
+
+               alarm (0);
+               CatchSignal (SIGALRM, SIGNAL_CAST SIG_DFL);
+
+               if (!(flags & MASTER_ONLY) && NIS_RES_NUMOBJ (result) <= 0) {
+                       /* nis replicas are not in sync perhaps?
+                        * this can happen, if account was just added.
+                        */
+                       DEBUG (10, ("will try master only\n"));
+                       nis_freeresult (result);
+                       flags |= MASTER_ONLY;
+               } else
+                       break;
+       }
+       return result;
+}
+
+static void free_private_data(void **vp)
+{
+       struct nisplus_private_info **private = (struct nisplus_private_info **)vp;
+
+       if ((*private)->result) {
+               nis_freeresult ((*private)->result);
+       }
+
+       free(*private);
+
+        /* No need to free any further, as it is talloc()ed */
+}
+
+NTSTATUS pdb_init_nisplussam (PDB_CONTEXT * pdb_context,
+                             PDB_METHODS ** pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct nisplus_private_info *private = malloc (sizeof (struct nisplus_private_info));
+
+       ZERO_STRUCT(private);
+       p->location = talloc_strdup(pdb_context->mem_ctx, location);
+
+       if (!NT_STATUS_IS_OK
+           (nt_status =
+            make_pdb_methods (pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "nisplussam";
+
+       /* Functions your pdb module doesn't provide should be set 
+        * to NULL */
+
+       (*pdb_method)->setsampwent = nisplussam_setsampwent;
+       (*pdb_method)->endsampwent = nisplussam_endsampwent;
+       (*pdb_method)->getsampwent = nisplussam_getsampwent;
+       (*pdb_method)->getsampwnam = nisplussam_getsampwnam;
+       (*pdb_method)->getsampwsid = nisplussam_getsampwsid;
+       (*pdb_method)->add_sam_account = nisplussam_add_sam_account;
+       (*pdb_method)->update_sam_account = nisplussam_update_sam_account;
+       (*pdb_method)->delete_sam_account = nisplussam_delete_sam_account;
+       (*pdb_method)->free_private_data = free_private_data;
+       (*pdb_method)->private_data = private;
+
+       return NT_STATUS_OK;
+}
+
+#else
+NTSTATUS pdb_init_nisplussam (PDB_CONTEXT * c, PDB_METHODS ** m,
+                             const char *l)
+{
+       DEBUG (0, ("nisplus sam not compiled in!\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+#endif /* WITH_NISPLUS_SAM */
diff --git a/source4/passdb/pdb_smbpasswd.c b/source4/passdb/pdb_smbpasswd.c
new file mode 100644 (file)
index 0000000..6f8c8a6
--- /dev/null
@@ -0,0 +1,1581 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998 
+ * Modified by Jeremy Allison 1995.
+ * Modified by Gerald (Jerry) Carter 2000-2001
+ * Modified by Andrew Bartlett 2002.
+ * 
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/* 
+   smb_passwd is analogous to sam_passwd used everywhere
+   else.  However, smb_passwd is limited to the information
+   stored by an smbpasswd entry 
+ */
+struct smb_passwd
+{
+        BOOL smb_userid_set;     /* this is actually the unix uid_t */
+        uint32 smb_userid;     /* this is actually the unix uid_t */
+        const char *smb_name;     /* username string */
+
+        const unsigned char *smb_passwd; /* Null if no password */
+        const unsigned char *smb_nt_passwd; /* Null if no password */
+
+        uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+        time_t pass_last_set_time;    /* password last set time */
+};
+
+struct smbpasswd_privates
+{
+       /* used for maintain locks on the smbpasswd file */
+       int     pw_file_lock_depth;
+       
+       /* Global File pointer */
+       FILE    *pw_file;
+       
+       /* formerly static variables */
+       struct smb_passwd pw_buf;
+       pstring  user_name;
+       unsigned char smbpwd[16];
+       unsigned char smbntpwd[16];
+
+       /* retrive-once info */
+       const char *smbpasswd_file;
+
+       BOOL permit_non_unix_accounts;
+
+       uint32 low_nua_userid; 
+       uint32 high_nua_userid; 
+
+};
+
+enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
+{
+  if (fd < 0)
+    return False;
+
+  if(*plock_depth == 0) {
+    if (!do_file_lock(fd, secs, type)) {
+      DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
+                 strerror(errno)));
+      return False;
+    }
+  }
+
+  (*plock_depth)++;
+
+  return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_unlock(int fd, int *plock_depth)
+{
+  BOOL ret=True;
+
+  if (fd == 0 || *plock_depth == 0) {
+         return True;
+  }
+
+  if(*plock_depth == 1)
+    ret = do_file_lock(fd, 5, F_UNLCK);
+
+  if (*plock_depth > 0)
+    (*plock_depth)--;
+
+  if(!ret)
+    DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
+                 strerror(errno)));
+  return ret;
+}
+
+
+/**************************************************************
+ Intialize a smb_passwd struct
+ *************************************************************/
+
+static void pdb_init_smb(struct smb_passwd *user)
+{
+       if (user == NULL) 
+               return;
+       ZERO_STRUCTP (user);
+       
+       user->pass_last_set_time = (time_t)0;
+}
+
+/***************************************************************
+ Internal fn to enumerate the smbpasswd list. Returns a void pointer
+ to ensure no modification outside this module. Checks for atomic
+ rename of smbpasswd file on update or create once the lock has
+ been granted to prevent race conditions. JRA.
+****************************************************************/
+
+static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
+{
+  FILE *fp = NULL;
+  const char *open_mode = NULL;
+  int race_loop = 0;
+  int lock_type = F_RDLCK;
+
+  if (!*pfile) {
+    DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
+    return (NULL);
+  }
+
+  switch(type) {
+  case PWF_READ:
+    open_mode = "rb";
+    lock_type = F_RDLCK;
+    break;
+  case PWF_UPDATE:
+    open_mode = "r+b";
+    lock_type = F_WRLCK;
+    break;
+  case PWF_CREATE:
+    /*
+     * Ensure atomic file creation.
+     */
+    {
+      int i, fd = -1;
+
+      for(i = 0; i < 5; i++) {
+        if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
+          break;
+        sys_usleep(200); /* Spin, spin... */
+      }
+      if(fd == -1) {
+        DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
+        return NULL;
+      }
+      close(fd);
+      open_mode = "r+b";
+      lock_type = F_WRLCK;
+      break;
+    }
+  }
+                      
+  for(race_loop = 0; race_loop < 5; race_loop++) {
+    DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
+
+    if((fp = sys_fopen(pfile, open_mode)) == NULL) {
+      DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) ));
+      return NULL;
+    }
+
+    if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
+      DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
+      fclose(fp);
+      return NULL;
+    }
+
+    /*
+     * Only check for replacement races on update or create.
+     * For read we don't mind if the data is one record out of date.
+     */
+
+    if(type == PWF_READ) {
+      break;
+    } else {
+      SMB_STRUCT_STAT sbuf1, sbuf2;
+
+      /*
+       * Avoid the potential race condition between the open and the lock
+       * by doing a stat on the filename and an fstat on the fd. If the
+       * two inodes differ then someone did a rename between the open and
+       * the lock. Back off and try the open again. Only do this 5 times to
+       * prevent infinate loops. JRA.
+       */
+
+      if (sys_stat(pfile,&sbuf1) != 0) {
+        DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
+        pw_file_unlock(fileno(fp), lock_depth);
+        fclose(fp);
+        return NULL;
+      }
+
+      if (sys_fstat(fileno(fp),&sbuf2) != 0) {
+        DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
+        pw_file_unlock(fileno(fp), lock_depth);
+        fclose(fp);
+        return NULL;
+      }
+
+      if( sbuf1.st_ino == sbuf2.st_ino) {
+        /* No race. */
+        break;
+      }
+
+      /*
+       * Race occurred - back off and try again...
+       */
+
+      pw_file_unlock(fileno(fp), lock_depth);
+      fclose(fp);
+    }
+  }
+
+  if(race_loop == 5) {
+    DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
+    return NULL;
+  }
+
+  /* Set a buffer to do more efficient reads */
+  setvbuf(fp, (char *)NULL, _IOFBF, 1024);
+
+  /* Make sure it is only rw by the owner */
+  if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
+    DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
+Error was %s\n.", pfile, strerror(errno) ));
+    pw_file_unlock(fileno(fp), lock_depth);
+    fclose(fp);
+    return NULL;
+  }
+
+  /* We have a lock on the file. */
+  return fp;
+}
+
+/***************************************************************
+ End enumeration of the smbpasswd list.
+****************************************************************/
+static void endsmbfilepwent(FILE *fp, int *lock_depth)
+{
+       if (!fp) {
+               return;
+       }
+
+       pw_file_unlock(fileno(fp), lock_depth);
+       fclose(fp);
+       DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
+}
+
+/*************************************************************************
+ Routine to return the next entry in the smbpasswd list.
+ *************************************************************************/
+
+static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
+{
+  /* Static buffers we will return. */
+  struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
+  char  *user_name = smbpasswd_state->user_name;
+  unsigned char *smbpwd = smbpasswd_state->smbpwd;
+  unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
+  char            linebuf[256];
+  unsigned char   c;
+  unsigned char  *p;
+  long            uidval;
+  size_t            linebuf_len;
+
+  if(fp == NULL) {
+    DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
+    return NULL;
+  }
+
+  pdb_init_smb(pw_buf);
+
+  pw_buf->acct_ctrl = ACB_NORMAL;  
+
+  /*
+   * Scan the file, a line at a time and check if the name matches.
+   */
+  while (!feof(fp)) {
+    linebuf[0] = '\0';
+
+    fgets(linebuf, 256, fp);
+    if (ferror(fp)) {
+      return NULL;
+    }
+
+    /*
+     * Check if the string is terminated with a newline - if not
+     * then we must keep reading and discard until we get one.
+     */
+    if ((linebuf_len = strlen(linebuf)) == 0)
+               continue;
+
+    if (linebuf[linebuf_len - 1] != '\n') {
+      c = '\0';
+      while (!ferror(fp) && !feof(fp)) {
+        c = fgetc(fp);
+        if (c == '\n')
+          break;
+      }
+    } else
+      linebuf[linebuf_len - 1] = '\0';
+
+#ifdef DEBUG_PASSWORD
+    DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
+#endif
+    if ((linebuf[0] == 0) && feof(fp)) {
+      DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
+      break;
+    }
+    /*
+     * The line we have should be of the form :-
+     * 
+     * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
+     * ignored....
+     * 
+     * or,
+     *
+     * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
+     *
+     * if Windows NT compatible passwords are also present.
+     * [Account type] is an ascii encoding of the type of account.
+     * LCT-(8 hex digits) is the time_t value of the last change time.
+     */
+
+    if (linebuf[0] == '#' || linebuf[0] == '\0') {
+      DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
+      continue;
+    }
+    p = (unsigned char *) strchr_m(linebuf, ':');
+    if (p == NULL) {
+      DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
+      continue;
+    }
+    /*
+     * As 256 is shorter than a pstring we don't need to check
+     * length here - if this ever changes....
+     */
+    SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
+
+    strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+    user_name[PTR_DIFF(p, linebuf)] = '\0';
+
+    /* Get smb uid. */
+
+    p++;               /* Go past ':' */
+
+    if(*p == '-') {
+      DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
+      continue;
+    }
+
+    if (!isdigit(*p)) {
+      DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
+      continue;
+    }
+
+    uidval = atoi((char *) p);
+
+    while (*p && isdigit(*p))
+      p++;
+
+    if (*p != ':') {
+      DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
+      continue;
+    }
+
+    pw_buf->smb_name = user_name;
+    pw_buf->smb_userid = uidval;
+
+    /*
+     * Now get the password value - this should be 32 hex digits
+     * which are the ascii representations of a 16 byte string.
+     * Get two at a time and put them into the password.
+     */
+
+    /* Skip the ':' */
+    p++;
+
+    if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+      DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
+      continue;
+    }
+
+    if (p[32] != ':') {
+      DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
+      continue;
+    }
+
+    if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
+      pw_buf->smb_passwd = NULL;
+      pw_buf->acct_ctrl |= ACB_PWNOTREQ;
+    } else {
+           if (*p == '*' || *p == 'X') {
+                   /* NULL LM password */
+                   pw_buf->smb_passwd = NULL;
+                   DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
+           } else if (pdb_gethexpwd((char *)p, smbpwd)) {
+                   pw_buf->smb_passwd = smbpwd;
+           } else {
+                   pw_buf->smb_passwd = NULL;
+                   DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
+           }
+    }
+
+    /* 
+     * Now check if the NT compatible password is
+     * available.
+     */
+    pw_buf->smb_nt_passwd = NULL;
+
+    p += 33; /* Move to the first character of the line after
+                the lanman password. */
+    if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
+      if (*p != '*' && *p != 'X') {
+        if(pdb_gethexpwd((char *)p,smbntpwd))
+          pw_buf->smb_nt_passwd = smbntpwd;
+      }
+      p += 33; /* Move to the first character of the line after
+                  the NT password. */
+    }
+
+    DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
+            user_name, uidval));
+
+    if (*p == '[')
+       {
+      unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
+      pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
+
+      /* Must have some account type set. */
+      if(pw_buf->acct_ctrl == 0)
+        pw_buf->acct_ctrl = ACB_NORMAL;
+
+      /* Now try and get the last change time. */
+      if(end_p)
+        p = end_p + 1;
+      if(*p == ':') {
+        p++;
+        if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
+          int i;
+          p += 4;
+          for(i = 0; i < 8; i++) {
+            if(p[i] == '\0' || !isxdigit(p[i]))
+              break;
+          }
+          if(i == 8) {
+            /*
+             * p points at 8 characters of hex digits - 
+             * read into a time_t as the seconds since
+             * 1970 that the password was last changed.
+             */
+            pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
+          }
+        }
+      }
+    } else {
+      /* 'Old' style file. Fake up based on user name. */
+      /*
+       * Currently trust accounts are kept in the same
+       * password file as 'normal accounts'. If this changes
+       * we will have to fix this code. JRA.
+       */
+      if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
+        pw_buf->acct_ctrl &= ~ACB_NORMAL;
+        pw_buf->acct_ctrl |= ACB_WSTRUST;
+      }
+    }
+
+    return pw_buf;
+  }
+
+  DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
+  return NULL;
+}
+
+/************************************************************************
+ Create a new smbpasswd entry - malloced space returned.
+*************************************************************************/
+
+static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
+{
+  int new_entry_length;
+  char *new_entry;
+  char *p;
+
+  new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
+
+  if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
+    DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
+    return NULL;
+  }
+
+  slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
+
+  p = new_entry+strlen(new_entry);
+  
+  pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
+
+  p+=strlen(p); *p = ':'; p++;
+
+  pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
+
+  p+=strlen(p); *p = ':'; p++;
+
+  /* Add the account encoding and the last change time. */
+  slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
+           pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
+           (uint32)newpwd->pass_last_set_time);
+
+  return new_entry;
+}
+
+/************************************************************************
+ Routine to add an entry to the smbpasswd file.
+*************************************************************************/
+
+static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
+{
+  const char *pfile = smbpasswd_state->smbpasswd_file;
+  struct smb_passwd *pwd = NULL;
+  FILE *fp = NULL;
+  int wr_len;
+  int fd;
+  size_t new_entry_length;
+  char *new_entry;
+  SMB_OFF_T offpos;
+  uint32 max_found_uid = 0;
+  /* Open the smbpassword file - for update. */
+  fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth));
+
+  if (fp == NULL && errno == ENOENT) {
+       /* Try again - create. */
+       fp = startsmbfilepwent(pfile, PWF_CREATE, &(smbpasswd_state->pw_file_lock_depth));
+  }
+
+  if (fp == NULL) {
+    DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
+    return False;
+  }
+
+  /*
+   * Scan the file, a line at a time and check if the name matches.
+   */
+
+  while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) 
+  {
+    if (strequal(newpwd->smb_name, pwd->smb_name)) 
+    {
+       DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       return False;
+    }
+    
+    /* Look for a free uid for use in non-unix accounts */
+    if (pwd->smb_userid > max_found_uid) {
+           max_found_uid = pwd->smb_userid;
+    }
+   }
+
+  /* Ok - entry doesn't exist. We can add it */
+
+  /* Account not in /etc/passwd hack!!! */
+  if (!newpwd->smb_userid_set) {
+         if (!smbpasswd_state->permit_non_unix_accounts) {
+                 DEBUG(0, ("add_smbfilepwd_entry: cannot add account %s without unix identity\n", newpwd->smb_name));
+                 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+                 return False;
+         }
+
+         if (max_found_uid < smbpasswd_state->low_nua_userid) {
+                 newpwd->smb_userid = smbpasswd_state->low_nua_userid;
+                 newpwd->smb_userid_set = True;
+         } else if (max_found_uid >= smbpasswd_state->high_nua_userid) {
+                 DEBUG(0, ("add_smbfilepwd_entry: cannot add machine %s, no uids are free! \n", newpwd->smb_name));
+                 endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+                 return False;           
+         } else {
+                 newpwd->smb_userid = max_found_uid + 1;
+                 newpwd->smb_userid_set = True;
+         }
+  }
+
+
+  /* Create a new smb passwd entry and set it to the given password. */
+  /* 
+   * The add user write needs to be atomic - so get the fd from 
+   * the fp and do a raw write() call.
+   */
+  fd = fileno(fp);
+
+  if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) 
+  {
+       DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       return False;
+  }
+
+  if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) 
+  {
+       DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       return False;
+  }
+
+  new_entry_length = strlen(new_entry);
+
+#ifdef DEBUG_PASSWORD
+  DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", 
+                            fd, new_entry_length, new_entry));
+#endif
+
+  if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) 
+  {
+       DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
+Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
+
+       /* Remove the entry we just wrote. */
+       if(sys_ftruncate(fd, offpos) == -1) 
+       {
+               DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
+Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
+               newpwd->smb_name, strerror(errno)));
+       }
+
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       free(new_entry);
+       return False;
+  }
+
+  free(new_entry);
+  endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+  return True;
+}
+
+/************************************************************************
+ Routine to search the smbpasswd file for an entry matching the username.
+ and then modify its password entry. We can't use the startsmbpwent()/
+ getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
+ in the actual file to decide how much room we have to write data.
+ override = False, normal
+ override = True, override XXXXXXXX'd out password or NO PASS
+************************************************************************/
+
+static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
+{
+  /* Static buffers we will return. */
+       pstring user_name;
+
+  char            linebuf[256];
+  char            readbuf[1024];
+  unsigned char   c;
+  fstring         ascii_p16;
+  fstring         encode_bits;
+  unsigned char  *p = NULL;
+  size_t            linebuf_len = 0;
+  FILE           *fp;
+  int             lockfd;
+  const char     *pfile = smbpasswd_state->smbpasswd_file;
+  BOOL found_entry = False;
+  BOOL got_pass_last_set_time = False;
+
+  SMB_OFF_T pwd_seekpos = 0;
+
+  int i;
+  int wr_len;
+  int fd;
+
+  if (!*pfile) {
+    DEBUG(0, ("No SMB password file set\n"));
+    return False;
+  }
+  DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
+
+  fp = sys_fopen(pfile, "r+");
+
+  if (fp == NULL) {
+    DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
+    return False;
+  }
+  /* Set a buffer to do more efficient reads */
+  setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+
+  lockfd = fileno(fp);
+
+  if (!pw_file_lock(lockfd, F_WRLCK, 5, &(smbpasswd_state->pw_file_lock_depth))) {
+    DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
+    fclose(fp);
+    return False;
+  }
+
+  /* Make sure it is only rw by the owner */
+  chmod(pfile, 0600);
+
+  /* We have a write lock on the file. */
+  /*
+   * Scan the file, a line at a time and check if the name matches.
+   */
+  while (!feof(fp)) {
+    pwd_seekpos = sys_ftell(fp);
+
+    linebuf[0] = '\0';
+
+    fgets(linebuf, sizeof(linebuf), fp);
+    if (ferror(fp)) {
+      pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+      fclose(fp);
+      return False;
+    }
+
+    /*
+     * Check if the string is terminated with a newline - if not
+     * then we must keep reading and discard until we get one.
+     */
+    linebuf_len = strlen(linebuf);
+    if (linebuf[linebuf_len - 1] != '\n') {
+      c = '\0';
+      while (!ferror(fp) && !feof(fp)) {
+        c = fgetc(fp);
+        if (c == '\n') {
+          break;
+        }
+      }
+    } else {
+      linebuf[linebuf_len - 1] = '\0';
+    }
+
+#ifdef DEBUG_PASSWORD
+    DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
+#endif
+
+    if ((linebuf[0] == 0) && feof(fp)) {
+      DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
+      break;
+    }
+
+    /*
+     * The line we have should be of the form :-
+     * 
+     * username:uid:[32hex bytes]:....other flags presently
+     * ignored....
+     * 
+     * or,
+     *
+     * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
+     *
+     * if Windows NT compatible passwords are also present.
+     */
+
+    if (linebuf[0] == '#' || linebuf[0] == '\0') {
+      DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
+      continue;
+    }
+
+    p = (unsigned char *) strchr_m(linebuf, ':');
+
+    if (p == NULL) {
+      DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
+      continue;
+    }
+
+    /*
+     * As 256 is shorter than a pstring we don't need to check
+     * length here - if this ever changes....
+     */
+
+    SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
+
+    strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+    user_name[PTR_DIFF(p, linebuf)] = '\0';
+    if (strequal(user_name, pwd->smb_name)) {
+      found_entry = True;
+      break;
+    }
+  }
+
+  if (!found_entry) {
+    pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+
+    DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
+             pwd->smb_name));
+    return False;
+  }
+
+  DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
+
+  /* User name matches - get uid and password */
+  p++;         /* Go past ':' */
+
+  if (!isdigit(*p)) {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
+    pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  while (*p && isdigit(*p))
+    p++;
+  if (*p != ':') {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
+    pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  /*
+   * Now get the password value - this should be 32 hex digits
+   * which are the ascii representations of a 16 byte string.
+   * Get two at a time and put them into the password.
+   */
+  p++;
+
+  /* Record exact password position */
+  pwd_seekpos += PTR_DIFF(p, linebuf);
+
+  if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return (False);
+  }
+
+  if (p[32] != ':') {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  /* Now check if the NT compatible password is
+     available. */
+  p += 33; /* Move to the first character of the line after
+              the lanman password. */
+  if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return (False);
+  }
+
+  if (p[32] != ':') {
+    DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  /* 
+   * Now check if the account info and the password last
+   * change time is available.
+   */
+  p += 33; /* Move to the first character of the line after
+              the NT password. */
+
+  if (*p == '[') {
+
+    i = 0;
+    encode_bits[i++] = *p++;
+    while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
+      encode_bits[i++] = *p++;
+
+    encode_bits[i++] = ']';
+    encode_bits[i++] = '\0';
+
+    if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
+      /*
+       * We are using a new format, space padded
+       * acct ctrl field. Encode the given acct ctrl
+       * bits into it.
+       */
+      fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+    } else {
+           DEBUG(0,("mod_smbfilepwd_entry:  Using old smbpasswd format.  This is no longer supported.!\n"));
+           DEBUG(0,("mod_smbfilepwd_entry:  No changes made, failing.!\n"));
+           return False;
+    }
+
+    /* Go past the ']' */
+    if(linebuf_len > PTR_DIFF(p, linebuf))
+      p++;
+
+    if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
+      p++;
+
+      /* We should be pointing at the LCT entry. */
+      if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
+
+        p += 4;
+        for(i = 0; i < 8; i++) {
+          if(p[i] == '\0' || !isxdigit(p[i]))
+            break;
+        }
+        if(i == 8) {
+          /*
+           * p points at 8 characters of hex digits -
+           * read into a time_t as the seconds since
+           * 1970 that the password was last changed.
+           */
+          got_pass_last_set_time = True;
+        } /* i == 8 */
+      } /* *p && StrnCaseCmp() */
+    } /* p == ':' */
+  } /* p == '[' */
+
+  /* Entry is correctly formed. */
+
+  /* Create the 32 byte representation of the new p16 */
+  pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
+
+  /* Add on the NT md4 hash */
+  ascii_p16[32] = ':';
+  wr_len = 66;
+  pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
+  ascii_p16[65] = ':';
+  ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
+
+  /* Add on the account info bits and the time of last
+     password change. */
+
+  if(got_pass_last_set_time) {
+    slprintf(&ascii_p16[strlen(ascii_p16)], 
+            sizeof(ascii_p16)-(strlen(ascii_p16)+1),
+            "%s:LCT-%08X:", 
+                     encode_bits, (uint32)pwd->pass_last_set_time );
+    wr_len = strlen(ascii_p16);
+  }
+
+#ifdef DEBUG_PASSWORD
+  DEBUG(100,("mod_smbfilepwd_entry: "));
+  dump_data(100, ascii_p16, wr_len);
+#endif
+
+  if(wr_len > sizeof(linebuf)) {
+    DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return (False);
+  }
+
+  /*
+   * Do an atomic write into the file at the position defined by
+   * seekpos.
+   */
+
+  /* The mod user write needs to be atomic - so get the fd from 
+     the fp and do a raw write() call.
+   */
+
+  fd = fileno(fp);
+
+  if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
+    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  /* Sanity check - ensure the areas we are writing are framed by ':' */
+  if (read(fd, linebuf, wr_len+1) != wr_len+1) {
+    DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
+    DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+  if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
+    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  if (write(fd, ascii_p16, wr_len) != wr_len) {
+    DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
+    pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+    fclose(fp);
+    return False;
+  }
+
+  pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+  fclose(fp);
+  return True;
+}
+
+/************************************************************************
+ Routine to delete an entry in the smbpasswd file by name.
+*************************************************************************/
+
+static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
+{
+       const char *pfile = smbpasswd_state->smbpasswd_file;
+  pstring pfile2;
+  struct smb_passwd *pwd = NULL;
+  FILE *fp = NULL;
+  FILE *fp_write = NULL;
+  int pfile2_lockdepth = 0;
+
+  slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)getpid() );
+
+  /*
+   * Open the smbpassword file - for update. It needs to be update
+   * as we need any other processes to wait until we have replaced
+   * it.
+   */
+
+  if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
+    DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+    return False;
+  }
+
+  /*
+   * Create the replacement password file.
+   */
+  if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
+    DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+    endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+    return False;
+  }
+
+  /*
+   * Scan the file, a line at a time and check if the name matches.
+   */
+
+  while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+    char *new_entry;
+    size_t new_entry_length;
+
+    if (strequal(name, pwd->smb_name)) {
+      DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
+      continue;
+    }
+
+    /*
+     * We need to copy the entry out into the second file.
+     */
+
+    if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) 
+    {
+       DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+       unlink(pfile2);
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       endsmbfilepwent(fp_write, &pfile2_lockdepth);
+       return False;
+    }
+
+    new_entry_length = strlen(new_entry);
+
+    if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) 
+    {
+       DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+       unlink(pfile2);
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       endsmbfilepwent(fp_write, &pfile2_lockdepth);
+       free(new_entry);
+       return False;
+    }
+
+    free(new_entry);
+  }
+
+  /*
+   * Ensure pfile2 is flushed before rename.
+   */
+
+  if(fflush(fp_write) != 0) 
+  {
+       DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+       endsmbfilepwent(fp_write,&pfile2_lockdepth);
+       return False;
+  }
+
+  /*
+   * Do an atomic rename - then release the locks.
+   */
+
+  if(rename(pfile2,pfile) != 0) {
+    unlink(pfile2);
+  }
+  
+  endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+  endsmbfilepwent(fp_write,&pfile2_lockdepth);
+  return True;
+}
+
+/*********************************************************************
+ Create a smb_passwd struct from a SAM_ACCOUNT.
+ We will not allocate any new memory.  The smb_passwd struct
+ should only stay around as long as the SAM_ACCOUNT does.
+ ********************************************************************/
+static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
+{
+       uid_t uid;
+
+       if (sampass == NULL) 
+               return False;
+
+       ZERO_STRUCTP(smb_pw);
+        if (!IS_SAM_UNIX_USER(sampass)) {
+               smb_pw->smb_userid_set = False;
+               DEBUG(5,("build_smb_pass: storing user without a UNIX uid or gid. \n"));
+       } else {
+               uint32 rid = pdb_get_user_rid(sampass);
+               smb_pw->smb_userid_set = True;
+               uid = pdb_get_uid(sampass);
+
+               /* If the user specified a RID, make sure its able to be both stored and retreived */
+               if (rid && rid != DOMAIN_USER_RID_GUEST && uid != fallback_pdb_user_rid_to_uid(rid)) {
+                       DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
+                       return False;
+               }
+
+               smb_pw->smb_userid=uid;
+        }
+
+       smb_pw->smb_name=(const char*)pdb_get_username(sampass);
+
+       smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
+       smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
+
+       smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
+       smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
+
+#if 0
+       /*
+        * ifdef'out by JFM on 11/29/2001.
+        * this assertion is no longer valid
+        * and I don't understand the goal 
+        * and doing the same thing with the group mapping code
+        * is hairy !
+        *
+        * We just have the RID, in which SID is it valid ?
+        * our domain SID ? well known SID ? local SID ?
+        */
+
+       if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) {
+               DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n"));
+               DEBUG(0,("build_sam_pass: %d %d %d. \n", *gid, pdb_group_rid_to_gid(pdb_get_group_rid(sampass)), pdb_get_group_rid(sampass)));
+               return False;
+       }
+#endif
+
+       return True;
+}      
+
+/*********************************************************************
+ Create a SAM_ACCOUNT from a smb_passwd struct
+ ********************************************************************/
+static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state, 
+                             SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
+{
+       struct passwd *pwfile;
+       
+       if (sam_pass==NULL) {
+               DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
+               return False;
+       }
+               
+       pwfile = getpwnam_alloc(pw_buf->smb_name);
+       if (pwfile == NULL) {
+               if ((smbpasswd_state->permit_non_unix_accounts) 
+                   && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) 
+                   && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) {
+
+                       pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid), PDB_SET);
+                       
+                       /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. 
+                          
+                       This was down the bottom for machines, but it looks pretty good as
+                       a general default for non-unix users. --abartlet 2002-01-08
+                       */
+                       pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS, PDB_SET); 
+                       pdb_set_username (sam_pass, pw_buf->smb_name, PDB_SET);
+                       pdb_set_domain (sam_pass, lp_workgroup(), PDB_DEFAULT);
+                       
+               } else {
+                       DEBUG(0,("build_sam_account: smbpasswd database is corrupt!  username %s with uid %u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
+                       return False;
+               }
+       } else {
+               if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) {
+                       return False;
+               }
+               
+               passwd_free(&pwfile);
+       }
+       
+       pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
+       pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);                  
+       pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
+       pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+       pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+       
+#if 0  /* JERRY */
+       /* the smbpasswd format doesn't have a must change time field, so
+          we can't get this right. The best we can do is to set this to 
+          some time in the future. 21 days seems as reasonable as any other value :) 
+       */
+       pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE, PDB_DEFAULT);
+#endif
+       return True;
+}
+
+/*****************************************************************
+ Functions to be implemented by the new passdb API 
+ ****************************************************************/
+static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
+{
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       
+       smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
+                                                      update ? PWF_UPDATE : PWF_READ, 
+                                                      &(smbpasswd_state->pw_file_lock_depth));
+                                  
+       /* did we fail?  Should we try to create it? */
+       if (!smbpasswd_state->pw_file && update && errno == ENOENT) 
+       {
+               FILE *fp;
+               /* slprintf(msg_str,msg_str_len-1,
+                  "smbpasswd file did not exist - attempting to create it.\n"); */
+               DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
+               fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
+               if (fp) 
+               {
+                       fprintf(fp, "# Samba SMB password file\n");
+                       fclose(fp);
+               }
+               
+               smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file, 
+                                                            update ? PWF_UPDATE : PWF_READ, 
+                                                            &(smbpasswd_state->pw_file_lock_depth));
+       }
+       
+       if (smbpasswd_state->pw_file != NULL)
+               return NT_STATUS_OK;
+       else
+               return NT_STATUS_UNSUCCESSFUL;  
+}
+
+static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
+{
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
+}
+/*****************************************************************
+ ****************************************************************/
+static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       struct smb_passwd *pw_buf=NULL;
+       BOOL done = False;
+       DEBUG(5,("pdb_getsampwent\n"));
+
+       if (user==NULL) {
+               DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
+#if 0
+               smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
+#endif
+               return nt_status;
+       }
+
+       while (!done)
+       {
+               /* do we have an entry? */
+               pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
+               if (pw_buf == NULL) 
+                       return nt_status;
+
+               /* build the SAM_ACCOUNT entry from the smb_passwd struct. 
+                  We loop in case the user in the pdb does not exist in 
+                  the local system password file */
+               if (build_sam_account(smbpasswd_state, user, pw_buf))
+                       done = True;
+       }
+
+       DEBUG(5,("getsampwent (smbpasswd): done\n"));
+
+       /* success */
+       return NT_STATUS_OK;
+}
+
+
+/****************************************************************
+ Search smbpasswd file by iterating over the entries.  Do not
+ call getpwnam() for unix account information until we have found
+ the correct entry
+ ***************************************************************/
+static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods, 
+                                 SAM_ACCOUNT *sam_acct, const char *username)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       struct smb_passwd *smb_pw;
+       void *fp = NULL;
+
+       DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
+
+       /* startsmbfilepwent() is used here as we don't want to lookup
+          the UNIX account in the local system password file until
+          we have a match.  */
+       fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+       if (fp == NULL) {
+               DEBUG(0, ("unable to open passdb database.\n"));
+               return nt_status;
+       }
+
+       while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
+               /* do nothing....another loop */ ;
+       
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+       /* did we locate the username in smbpasswd  */
+       if (smb_pw == NULL)
+               return nt_status;
+       
+       DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+       if (!sam_acct) {
+               DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
+#if 0
+               smb_panic("NULL pointer passed to pdb_getsampwnam\n");
+#endif
+               return nt_status;
+       }
+               
+       /* now build the SAM_ACCOUNT */
+       if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
+               return nt_status;
+
+       /* success */
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       struct smb_passwd *smb_pw;
+       void *fp = NULL;
+       fstring sid_str;
+       uint32 rid;
+       
+       DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
+
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       /* More special case 'guest account' hacks... */
+       if (rid == DOMAIN_USER_RID_GUEST) {
+               const char *guest_account = lp_guestaccount();
+               if (!(guest_account && *guest_account)) {
+                       DEBUG(1, ("Guest account not specfied!\n"));
+                       return nt_status;
+               }
+               return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
+       }
+
+       /* Open the sam password file - not for update. */
+       fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+       if (fp == NULL) {
+               DEBUG(0, ("unable to open passdb database.\n"));
+               return nt_status;
+       }
+
+       while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
+               /* do nothing */ ;
+
+       endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+       /* did we locate the username in smbpasswd  */
+       if (smb_pw == NULL)
+               return nt_status;
+       
+       DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+               
+       if (!sam_acct) {
+               DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
+#if 0
+               smb_panic("NULL pointer passed to pdb_getsampwrid\n");
+#endif
+               return nt_status;
+       }
+
+       /* now build the SAM_ACCOUNT */
+       if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
+               return nt_status;
+
+       /* build_sam_account might change the SID on us, if the name was for the guest account */
+       if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
+               fstring sid_string1, sid_string2;
+               DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
+                         sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       /* success */
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       struct smb_passwd smb_pw;
+       
+       /* convert the SAM_ACCOUNT */
+       if (!build_smb_pass(&smb_pw, sampass)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* add the entry */
+       if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+       struct smb_passwd smb_pw;
+       
+       /* convert the SAM_ACCOUNT */
+       if (!build_smb_pass(&smb_pw, sampass)) {
+               DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* update the entry */
+       if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+               DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+       struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+       const char *username = pdb_get_username(sampass);
+
+       if (del_smbfilepwd_entry(smbpasswd_state, username))
+               return NT_STATUS_OK;
+
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp) 
+{
+       struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
+       
+       endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
+       
+       *privates = NULL;
+       /* No need to free any further, as it is talloc()ed */
+}
+
+
+NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct smbpasswd_privates *privates;
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "smbpasswd";
+
+       (*pdb_method)->setsampwent = smbpasswd_setsampwent;
+       (*pdb_method)->endsampwent = smbpasswd_endsampwent;
+       (*pdb_method)->getsampwent = smbpasswd_getsampwent;
+       (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
+       (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
+       (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
+       (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
+       (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
+
+       /* Setup private data and free function */
+
+       privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
+
+       if (!privates) {
+               DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Store some config details */
+
+       if (location) {
+               privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
+       } else {
+               privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
+       }
+       
+       if (!privates->smbpasswd_file) {
+               DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*pdb_method)->private_data = privates;
+
+       (*pdb_method)->free_private_data = free_private_data;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_init_smbpasswd_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct smbpasswd_privates *privates;
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_smbpasswd(pdb_context, pdb_method, location))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "smbpasswd_nua";
+
+       privates = (*pdb_method)->private_data;
+       
+       privates->permit_non_unix_accounts = True;
+
+       if (!lp_non_unix_account_range(&privates->low_nua_userid, &privates->high_nua_userid)) {
+               DEBUG(0, ("cannot use smbpasswd_nua without 'non unix account range' in smb.conf!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/passdb/pdb_tdb.c b/source4/passdb/pdb_tdb.c
new file mode 100644 (file)
index 0000000..c48c956
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Simo Sorce 2000-2002
+ * Copyright (C) Gerald Carter 2000
+ * Copyright (C) Jeremy Allison 2001
+ * Copyright (C) Andrew Bartlett 2002
+ * 
+ * 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"
+
+#if 0 /* when made a module use this */
+
+static int tdbsam_debug_level = DBGC_ALL;
+#undef DBGC_CLASS
+#define DBGC_CLASS tdbsam_debug_level
+
+#else
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#endif
+
+#ifdef WITH_TDB_SAM
+
+#define PDB_VERSION            "20010830"
+#define PASSDB_FILE_NAME       "passdb.tdb"
+#define TDB_FORMAT_STRING      "ddddddBBBBBBBBBBBBddBBwdwdBdd"
+#define USERPREFIX             "USER_"
+#define RIDPREFIX              "RID_"
+
+struct tdbsam_privates {
+       TDB_CONTEXT     *passwd_tdb;
+       TDB_DATA        key;
+
+       /* retrive-once info */
+       const char *tdbsam_location;
+
+       BOOL permit_non_unix_accounts;
+
+       BOOL algorithmic_rids;
+
+       uint32 low_nua_rid; 
+       uint32 high_nua_rid;
+};
+
+/**********************************************************************
+ Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len
+ *********************************************************************/
+
+static BOOL init_sam_from_buffer (struct tdbsam_privates *tdb_state,
+                                 SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+
+       /* times are stored as 32bit integer
+          take care on system with 64bit wide time_t
+          --SSS */
+       uint32  logon_time,
+               logoff_time,
+               kickoff_time,
+               pass_last_set_time,
+               pass_can_change_time,
+               pass_must_change_time;
+       char *username;
+       char *domain;
+       char *nt_username;
+       char *dir_drive;
+       char *unknown_str;
+       char *munged_dial;
+       char *fullname;
+       char *homedir;
+       char *logon_script;
+       char *profile_path;
+       char *acct_desc;
+       char *workstations;
+       uint32  username_len, domain_len, nt_username_len,
+               dir_drive_len, unknown_str_len, munged_dial_len,
+               fullname_len, homedir_len, logon_script_len,
+               profile_path_len, acct_desc_len, workstations_len;
+               
+       uint32  user_rid, group_rid, unknown_3, hours_len, unknown_5, unknown_6;
+       uint16  acct_ctrl, logon_divs;
+       uint8   *hours;
+       static uint8    *lm_pw_ptr, *nt_pw_ptr;
+       uint32          len = 0;
+       uint32          lm_pw_len, nt_pw_len, hourslen;
+       BOOL ret = True;
+       struct passwd *pw;
+       uid_t uid = -1;
+       gid_t gid = -1; /* This is what standard sub advanced expects if no gid is known */
+       
+       if(sampass == NULL || buf == NULL) {
+               DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
+               return False;
+       }
+                                                                       
+       /* unpack the buffer into variables */
+       len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING,
+               &logon_time,
+               &logoff_time,
+               &kickoff_time,
+               &pass_last_set_time,
+               &pass_can_change_time,
+               &pass_must_change_time,
+               &username_len, &username,
+               &domain_len, &domain,
+               &nt_username_len, &nt_username,
+               &fullname_len, &fullname,
+               &homedir_len, &homedir,
+               &dir_drive_len, &dir_drive,
+               &logon_script_len, &logon_script,
+               &profile_path_len, &profile_path,
+               &acct_desc_len, &acct_desc,
+               &workstations_len, &workstations,
+               &unknown_str_len, &unknown_str,
+               &munged_dial_len, &munged_dial,
+               &user_rid,
+               &group_rid,
+               &lm_pw_len, &lm_pw_ptr,
+               &nt_pw_len, &nt_pw_ptr,
+               &acct_ctrl,
+               &unknown_3,
+               &logon_divs,
+               &hours_len,
+               &hourslen, &hours,
+               &unknown_5,
+               &unknown_6);
+               
+       if (len == -1)  {
+               ret = False;
+               goto done;
+       }
+
+       /* validate the account and fill in UNIX uid and gid. Standard
+        * getpwnam() is used instead of Get_Pwnam() as we do not need
+        * to try case permutations
+        */
+       if (!username || !(pw = getpwnam_alloc(username))) {
+               if (!(tdb_state->permit_non_unix_accounts)) {
+                       DEBUG(0,("tdbsam: getpwnam_alloc(%s) return NULL.  User does not exist!\n", username));
+                       ret = False;
+                       goto done;
+               }
+       }
+               
+       if (pw) {
+               uid = pw->pw_uid;
+               gid = pw->pw_gid;
+               
+               pdb_set_unix_homedir(sampass, pw->pw_dir, PDB_SET);
+
+               passwd_free(&pw);
+
+               pdb_set_uid(sampass, uid, PDB_SET);
+               pdb_set_gid(sampass, gid, PDB_SET);
+       }
+
+       pdb_set_logon_time(sampass, logon_time, PDB_SET);
+       pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+       pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+       pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+       pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+       pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+       pdb_set_username     (sampass, username, PDB_SET); 
+       pdb_set_domain       (sampass, domain, PDB_SET);
+       pdb_set_nt_username  (sampass, nt_username, PDB_SET);
+       pdb_set_fullname     (sampass, fullname, PDB_SET);
+
+       if (homedir) {
+               pdb_set_homedir(sampass, homedir, PDB_SET);
+       }
+       else {
+               pdb_set_homedir(sampass, 
+                               talloc_sub_specified(sampass->mem_ctx, 
+                                                      lp_logon_home(),
+                                                      username, domain, 
+                                                      uid, gid),
+                               PDB_DEFAULT);
+       }
+
+       if (dir_drive)  
+               pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+       else {
+               pdb_set_dir_drive(sampass, 
+                                 talloc_sub_specified(sampass->mem_ctx, 
+                                                        lp_logon_drive(),
+                                                        username, domain, 
+                                                        uid, gid),
+                                 PDB_DEFAULT);
+       }
+
+       if (logon_script) 
+               pdb_set_logon_script(sampass, logon_script, PDB_SET);
+       else {
+               pdb_set_logon_script(sampass, 
+                                    talloc_sub_specified(sampass->mem_ctx, 
+                                                           lp_logon_script(),
+                                                           username, domain, 
+                                                           uid, gid),
+                                 PDB_DEFAULT);
+       }
+       
+       if (profile_path) {     
+               pdb_set_profile_path(sampass, profile_path, PDB_SET);
+       } else {
+               pdb_set_profile_path(sampass, 
+                                    talloc_sub_specified(sampass->mem_ctx, 
+                                                           lp_logon_path(),
+                                                           username, domain, 
+                                                           uid, gid),
+                                    PDB_DEFAULT);
+       }
+
+       pdb_set_acct_desc    (sampass, acct_desc, PDB_SET);
+       pdb_set_workstations (sampass, workstations, PDB_SET);
+       pdb_set_munged_dial  (sampass, munged_dial, PDB_SET);
+
+       if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+               if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+                       ret = False;
+                       goto done;
+               }
+       }
+
+       if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+               if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+                       ret = False;
+                       goto done;
+               }
+       }
+
+       pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+       pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+       pdb_set_unknown_3(sampass, unknown_3, PDB_SET);
+       pdb_set_hours_len(sampass, hours_len, PDB_SET);
+       pdb_set_unknown_5(sampass, unknown_5, PDB_SET);
+       pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+       pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+       pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+       pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+       SAFE_FREE(username);
+       SAFE_FREE(domain);
+       SAFE_FREE(nt_username);
+       SAFE_FREE(fullname);
+       SAFE_FREE(homedir);
+       SAFE_FREE(dir_drive);
+       SAFE_FREE(logon_script);
+       SAFE_FREE(profile_path);
+       SAFE_FREE(acct_desc);
+       SAFE_FREE(workstations);
+       SAFE_FREE(munged_dial);
+
+       return ret;
+}
+
+/**********************************************************************
+ Intialize a BYTE buffer from a SAM_ACCOUNT struct
+ *********************************************************************/
+static uint32 init_buffer_from_sam (struct tdbsam_privates *tdb_state,
+                                   uint8 **buf, const SAM_ACCOUNT *sampass)
+{
+       size_t          len, buflen;
+
+       /* times are stored as 32bit integer
+          take care on system with 64bit wide time_t
+          --SSS */
+       uint32  logon_time,
+               logoff_time,
+               kickoff_time,
+               pass_last_set_time,
+               pass_can_change_time,
+               pass_must_change_time;
+
+       uint32  user_rid, group_rid;
+
+       const char *username;
+       const char *domain;
+       const char *nt_username;
+       const char *dir_drive;
+       const char *unknown_str;
+       const char *munged_dial;
+       const char *fullname;
+       const char *homedir;
+       const char *logon_script;
+       const char *profile_path;
+       const char *acct_desc;
+       const char *workstations;
+       uint32  username_len, domain_len, nt_username_len,
+               dir_drive_len, unknown_str_len, munged_dial_len,
+               fullname_len, homedir_len, logon_script_len,
+               profile_path_len, acct_desc_len, workstations_len;
+
+       const uint8 *lm_pw;
+       const uint8 *nt_pw;
+       uint32  lm_pw_len = 16;
+       uint32  nt_pw_len = 16;
+
+       /* do we have a valid SAM_ACCOUNT pointer? */
+       if (sampass == NULL) {
+               DEBUG(0, ("init_buffer_from_sam: SAM_ACCOUNT is NULL!\n"));
+               return -1;
+       }
+       
+       *buf = NULL;
+       buflen = 0;
+
+       logon_time = (uint32)pdb_get_logon_time(sampass);
+       logoff_time = (uint32)pdb_get_logoff_time(sampass);
+       kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
+       pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+       pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
+       pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
+
+       user_rid = pdb_get_user_rid(sampass);
+       group_rid = pdb_get_group_rid(sampass);
+
+       username = pdb_get_username(sampass);
+       if (username) username_len = strlen(username) +1;
+       else username_len = 0;
+
+       domain = pdb_get_domain(sampass);
+       if (domain) domain_len = strlen(domain) +1;
+       else domain_len = 0;
+
+       nt_username = pdb_get_nt_username(sampass);
+       if (nt_username) nt_username_len = strlen(nt_username) +1;
+       else nt_username_len = 0;
+
+       fullname = pdb_get_fullname(sampass);
+       if (fullname) fullname_len = strlen(fullname) +1;
+       else fullname_len = 0;
+
+       /*
+        * Only updates fields which have been set (not defaults from smb.conf)
+        */
+
+       if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE)) 
+         dir_drive = pdb_get_dir_drive(sampass);
+       else dir_drive = NULL;
+       if (dir_drive) dir_drive_len = strlen(dir_drive) +1;
+       else dir_drive_len = 0;
+
+       if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME)) homedir = pdb_get_homedir(sampass);
+       else homedir = NULL;
+       if (homedir) homedir_len = strlen(homedir) +1;
+       else homedir_len = 0;
+
+       if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT)) logon_script = pdb_get_logon_script(sampass);
+       else logon_script = NULL;
+       if (logon_script) logon_script_len = strlen(logon_script) +1;
+       else logon_script_len = 0;
+
+       if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE)) profile_path = pdb_get_profile_path(sampass);
+       else profile_path = NULL;
+       if (profile_path) profile_path_len = strlen(profile_path) +1;
+       else profile_path_len = 0;
+       
+       lm_pw = pdb_get_lanman_passwd(sampass);
+       if (!lm_pw) lm_pw_len = 0;
+       
+       nt_pw = pdb_get_nt_passwd(sampass);
+       if (!nt_pw) nt_pw_len = 0;
+               
+       acct_desc = pdb_get_acct_desc(sampass);
+       if (acct_desc) acct_desc_len = strlen(acct_desc) +1;
+       else acct_desc_len = 0;
+
+       workstations = pdb_get_workstations(sampass);
+       if (workstations) workstations_len = strlen(workstations) +1;
+       else workstations_len = 0;
+
+       unknown_str = NULL;
+       unknown_str_len = 0;
+
+       munged_dial = pdb_get_munged_dial(sampass);
+       if (munged_dial) munged_dial_len = strlen(munged_dial) +1;
+       else munged_dial_len = 0;       
+               
+       /* one time to get the size needed */
+       len = tdb_pack(NULL, 0,  TDB_FORMAT_STRING,
+               logon_time,
+               logoff_time,
+               kickoff_time,
+               pass_last_set_time,
+               pass_can_change_time,
+               pass_must_change_time,
+               username_len, username,
+               domain_len, domain,
+               nt_username_len, nt_username,
+               fullname_len, fullname,
+               homedir_len, homedir,
+               dir_drive_len, dir_drive,
+               logon_script_len, logon_script,
+               profile_path_len, profile_path,
+               acct_desc_len, acct_desc,
+               workstations_len, workstations,
+               unknown_str_len, unknown_str,
+               munged_dial_len, munged_dial,
+               user_rid,
+               group_rid,
+               lm_pw_len, lm_pw,
+               nt_pw_len, nt_pw,
+               pdb_get_acct_ctrl(sampass),
+               pdb_get_unknown_3(sampass),
+               pdb_get_logon_divs(sampass),
+               pdb_get_hours_len(sampass),
+               MAX_HOURS_LEN, pdb_get_hours(sampass),
+               pdb_get_unknown_5(sampass),
+               pdb_get_unknown_6(sampass));
+
+
+       /* malloc the space needed */
+       if ( (*buf=(uint8*)malloc(len)) == NULL) {
+               DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+               return (-1);
+       }
+       
+       /* now for the real call to tdb_pack() */
+       buflen = tdb_pack(*buf, len,  TDB_FORMAT_STRING,
+               logon_time,
+               logoff_time,
+               kickoff_time,
+               pass_last_set_time,
+               pass_can_change_time,
+               pass_must_change_time,
+               username_len, username,
+               domain_len, domain,
+               nt_username_len, nt_username,
+               fullname_len, fullname,
+               homedir_len, homedir,
+               dir_drive_len, dir_drive,
+               logon_script_len, logon_script,
+               profile_path_len, profile_path,
+               acct_desc_len, acct_desc,
+               workstations_len, workstations,
+               unknown_str_len, unknown_str,
+               munged_dial_len, munged_dial,
+               user_rid,
+               group_rid,
+               lm_pw_len, lm_pw,
+               nt_pw_len, nt_pw,
+               pdb_get_acct_ctrl(sampass),
+               pdb_get_unknown_3(sampass),
+               pdb_get_logon_divs(sampass),
+               pdb_get_hours_len(sampass),
+               MAX_HOURS_LEN, pdb_get_hours(sampass),
+               pdb_get_unknown_5(sampass),
+               pdb_get_unknown_6(sampass));
+       
+       
+       /* check to make sure we got it correct */
+       if (buflen != len) {
+               DEBUG(0, ("init_buffer_from_sam: somthing odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n", 
+                         buflen, len));  
+               /* error */
+               SAFE_FREE (*buf);
+               return (-1);
+       }
+
+       return (buflen);
+}
+
+/***************************************************************
+ Open the TDB passwd database for SAM account enumeration.
+****************************************************************/
+
+static NTSTATUS tdbsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       
+       /* Open tdb passwd */
+       if (!(tdb_state->passwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, update?(O_RDWR|O_CREAT):O_RDONLY, 0600)))
+       {
+               DEBUG(0, ("Unable to open/create TDB passwd\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       tdb_state->key = tdb_firstkey(tdb_state->passwd_tdb);
+
+       return NT_STATUS_OK;
+}
+
+static void close_tdb(struct tdbsam_privates *tdb_state) 
+{
+       if (tdb_state->passwd_tdb) {
+               tdb_close(tdb_state->passwd_tdb);
+               tdb_state->passwd_tdb = NULL;
+       }
+}
+
+/***************************************************************
+ End enumeration of the TDB passwd list.
+****************************************************************/
+
+static void tdbsam_endsampwent(struct pdb_methods *my_methods)
+{
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       close_tdb(tdb_state);
+       
+       DEBUG(7, ("endtdbpwent: closed sam database.\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the TDB (next in line)
+*****************************************************************/
+
+static NTSTATUS tdbsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       TDB_DATA        data;
+       const char *prefix = USERPREFIX;
+       int  prefixlen = strlen (prefix);
+
+
+       if (user==NULL) {
+               DEBUG(0,("pdb_get_sampwent: SAM_ACCOUNT is NULL.\n"));
+               return nt_status;
+       }
+
+       /* skip all non-USER entries (eg. RIDs) */
+       while ((tdb_state->key.dsize != 0) && (strncmp(tdb_state->key.dptr, prefix, prefixlen)))
+               /* increment to next in line */
+               tdb_state->key = tdb_nextkey(tdb_state->passwd_tdb, tdb_state->key);
+
+       /* do we have an valid iteration pointer? */
+       if(tdb_state->passwd_tdb == NULL) {
+               DEBUG(0,("pdb_get_sampwent: Bad TDB Context pointer.\n"));
+               return nt_status;
+       }
+
+       data = tdb_fetch(tdb_state->passwd_tdb, tdb_state->key);
+       if (!data.dptr) {
+               DEBUG(5,("pdb_getsampwent: database entry not found.\n"));
+               return nt_status;
+       }
+  
+       /* unpack the buffer */
+       if (!init_sam_from_buffer(tdb_state, user, data.dptr, data.dsize)) {
+               DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+               SAFE_FREE(data.dptr);
+               return nt_status;
+       }
+       SAFE_FREE(data.dptr);
+       
+       /* increment to next in line */
+       tdb_state->key = tdb_nextkey(tdb_state->passwd_tdb, tdb_state->key);
+
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Lookup a name in the SAM TDB
+******************************************************************/
+
+static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       TDB_CONTEXT     *pwd_tdb;
+       TDB_DATA        data, key;
+       fstring         keystr;
+       fstring         name;
+
+       if (user==NULL) {
+               DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+               return nt_status;
+       }
+
+       /* Data is stored in all lower-case */
+       unix_strlower(sname, -1, name, sizeof(name));
+
+       /* set search key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       /* open the accounts TDB */
+       if (!(pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDONLY, 0600))) {
+               DEBUG(0, ("pdb_getsampwnam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location));
+               return nt_status;
+       }
+
+       /* get the record */
+       data = tdb_fetch(pwd_tdb, key);
+       if (!data.dptr) {
+               DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+               DEBUGADD(5, (" Key: %s\n", keystr));
+               tdb_close(pwd_tdb);
+               return nt_status;
+       }
+  
+       /* unpack the buffer */
+       if (!init_sam_from_buffer(tdb_state, user, data.dptr, data.dsize)) {
+               DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+               SAFE_FREE(data.dptr);
+               tdb_close(pwd_tdb);
+               return nt_status;
+       }
+       SAFE_FREE(data.dptr);
+
+       /* no further use for database, close it now */
+       tdb_close(pwd_tdb);
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods, SAM_ACCOUNT *user, uint32 rid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       TDB_CONTEXT             *pwd_tdb;
+       TDB_DATA                data, key;
+       fstring                 keystr;
+       fstring                 name;
+       
+       if (user==NULL) {
+               DEBUG(0,("pdb_getsampwrid: SAM_ACCOUNT is NULL.\n"));
+               return nt_status;
+       }
+
+       /* set search key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+       key.dptr = keystr;
+       key.dsize = strlen (keystr) + 1;
+
+       /* open the accounts TDB */
+       if (!(pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDONLY, 0600))) {
+               DEBUG(0, ("pdb_getsampwrid: Unable to open TDB rid database!\n"));
+               return nt_status;
+       }
+
+       /* get the record */
+       data = tdb_fetch (pwd_tdb, key);
+       if (!data.dptr) {
+               DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+               tdb_close (pwd_tdb);
+               return nt_status;
+       }
+
+       fstrcpy (name, data.dptr);
+       SAFE_FREE(data.dptr);
+       
+       tdb_close (pwd_tdb);
+       
+       return tdbsam_getsampwnam (my_methods, user, name);
+}
+
+static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+       uint32 rid;
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+               return NT_STATUS_UNSUCCESSFUL;
+       return tdbsam_getsampwrid(my_methods, user, rid);
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_pass)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       TDB_CONTEXT     *pwd_tdb;
+       TDB_DATA        key;
+       fstring         keystr;
+       uint32          rid;
+       fstring         name;
+       
+       unix_strlower(pdb_get_username(sam_pass), -1, name, sizeof(name));
+       
+       /* open the TDB */
+       if (!(pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDWR, 0600))) {
+               DEBUG(0, ("Unable to open TDB passwd!"));
+               return nt_status;
+       }
+  
+       /* set the search key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+       key.dptr = keystr;
+       key.dsize = strlen (keystr) + 1;
+       
+       rid = pdb_get_user_rid(sam_pass);
+
+       /* it's outaa here!  8^) */
+       if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+               DEBUG(5, ("Error deleting entry from tdb passwd database!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+               tdb_close(pwd_tdb); 
+               return nt_status;
+       }       
+
+       /* delete also the RID key */
+
+       /* set the search key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+       key.dptr = keystr;
+       key.dsize = strlen (keystr) + 1;
+
+       /* it's outaa here!  8^) */
+       if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+               DEBUG(5, ("Error deleting entry from tdb rid database!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+               tdb_close(pwd_tdb); 
+               return nt_status;
+       }
+       
+       tdb_close(pwd_tdb);
+       
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Update the TDB SAM
+****************************************************************************/
+
+static BOOL tdb_update_sam(struct pdb_methods *my_methods, SAM_ACCOUNT* newpwd, int flag)
+{
+       struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+       TDB_CONTEXT     *pwd_tdb = NULL;
+       TDB_DATA        key, data;
+       uint8           *buf = NULL;
+       fstring         keystr;
+       fstring         name;
+       BOOL            ret = True;
+       uint32          user_rid;
+       BOOL            tdb_ret;
+
+       /* invalidate the existing TDB iterator if it is open */
+       if (tdb_state->passwd_tdb) {
+               tdb_close(tdb_state->passwd_tdb);
+               tdb_state->passwd_tdb = NULL;
+       }
+
+       /* open the account TDB passwd*/
+       pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
+       if (!pwd_tdb)
+       {
+               DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location));
+               return False;
+       }
+
+       /* if flag == TDB_INSERT then make up a new RID else throw an error. */
+       if (!(user_rid = pdb_get_user_rid(newpwd))) {
+               if (flag & TDB_INSERT) {
+                       if (IS_SAM_UNIX_USER(newpwd)) {
+                               if (tdb_state->algorithmic_rids) {
+                                       user_rid = fallback_pdb_uid_to_user_rid(pdb_get_uid(newpwd));
+                               } else {
+                                       user_rid = BASE_RID;
+                                       tdb_ret = tdb_change_uint32_atomic(pwd_tdb, "RID_COUNTER", &user_rid, RID_MULTIPLIER);
+                                       if (!tdb_ret) {
+                                               ret = False;
+                                               goto done;
+                                       }
+                               }
+                               pdb_set_user_sid_from_rid(newpwd, user_rid, PDB_CHANGED);
+                       } else {
+                               user_rid = tdb_state->low_nua_rid;
+                               tdb_ret = tdb_change_uint32_atomic(pwd_tdb, "NUA_RID_COUNTER", &user_rid, RID_MULTIPLIER);
+                               if (!tdb_ret) {
+                                       ret = False;
+                                       goto done;
+                               }
+                               if (user_rid > tdb_state->high_nua_rid) {
+                                       DEBUG(0, ("tdbsam: no NUA rids available, cannot add user %s!\n", pdb_get_username(newpwd)));
+                                       ret = False;
+                                       goto done;
+                               }
+                               pdb_set_user_sid_from_rid(newpwd, user_rid, PDB_CHANGED);
+                       }
+               } else {
+                       DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a RID\n",pdb_get_username(newpwd)));
+                       ret = False;
+                       goto done;
+               }
+       }
+
+       if (!pdb_get_group_rid(newpwd)) {
+               if (flag & TDB_INSERT) {
+                       if (!tdb_state->permit_non_unix_accounts) {
+                               DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",pdb_get_username(newpwd)));
+                               ret = False;
+                               goto done;
+                       } else {
+                               /* This seems like a good default choice for non-unix users */
+                               pdb_set_group_sid_from_rid(newpwd, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+                       }
+               } else {
+                       DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",pdb_get_username(newpwd)));
+                       ret = False;
+                       goto done;
+               }
+       }
+
+       /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */
+       if ((data.dsize=init_buffer_from_sam (tdb_state, &buf, newpwd)) == -1) {
+               DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n"));
+               ret = False;
+               goto done;
+       }
+       data.dptr = buf;
+
+       unix_strlower(pdb_get_username(newpwd), -1, name, sizeof(name));
+       
+       DEBUG(5, ("Storing %saccount %s with RID %d\n", flag == TDB_INSERT ? "(new) " : "", name, user_rid));
+
+       /* setup the USER index key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+       key.dptr = keystr;
+       key.dsize = strlen (keystr) + 1;
+
+       /* add the account */
+       if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+               DEBUG(0, ("Unable to modify passwd TDB!"));
+               DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+               DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+               ret = False;
+               goto done;
+       }
+       
+       /* setup RID data */
+       data.dsize = sizeof(fstring);
+       data.dptr = name;
+
+       /* setup the RID index key */
+       slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, user_rid);
+       key.dptr = keystr;
+       key.dsize = strlen (keystr) + 1;
+       
+       /* add the reference */
+       if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+               DEBUG(0, ("Unable to modify TDB passwd !"));
+               DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+               DEBUGADD(0, (" occured while storing the RID index (%s)\n", keystr));
+               ret = False;
+               goto done;
+       }
+
+done:  
+       /* cleanup */
+       tdb_close (pwd_tdb);
+       SAFE_FREE(buf);
+       
+       return (ret);   
+}
+
+/***************************************************************************
+ Modifies an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+       if (tdb_update_sam(my_methods, newpwd, TDB_MODIFY))
+               return NT_STATUS_OK;
+       else
+               return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+       if (tdb_update_sam(my_methods, newpwd, TDB_INSERT))
+               return NT_STATUS_OK;
+       else
+               return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp) 
+{
+       struct tdbsam_privates **tdb_state = (struct tdbsam_privates **)vp;
+       close_tdb(*tdb_state);
+       *tdb_state = NULL;
+
+       /* No need to free any further, as it is talloc()ed */
+}
+
+
+NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct tdbsam_privates *tdb_state;
+
+#if 0 /* when made a module use this */
+       tdbsam_debug_level = debug_add_class("tdbsam");
+       if(tdbsam_debug_level == -1) {
+               tdbsam_debug_level = DBGC_ALL;
+               DEBUG(0, ("tdbsam: Couldn't register custom debugging class!\n"));
+       }
+#endif
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "tdbsam";
+
+       (*pdb_method)->setsampwent = tdbsam_setsampwent;
+       (*pdb_method)->endsampwent = tdbsam_endsampwent;
+       (*pdb_method)->getsampwent = tdbsam_getsampwent;
+       (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
+       (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
+       (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
+       (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
+       (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
+
+       tdb_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct tdbsam_privates));
+
+       if (!tdb_state) {
+               DEBUG(0, ("talloc() failed for tdbsam private_data!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (location) {
+               tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, location);
+       } else {
+               pstring tdbfile;
+               get_private_directory(tdbfile);
+               pstrcat(tdbfile, "/");
+               pstrcat(tdbfile, PASSDB_FILE_NAME);
+               tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, tdbfile);
+       }
+
+       tdb_state->algorithmic_rids = True;
+
+       (*pdb_method)->private_data = tdb_state;
+
+       (*pdb_method)->free_private_data = free_private_data;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_init_tdbsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       struct tdbsam_privates *tdb_state;
+       uint32 low_nua_uid, high_nua_uid;
+
+       if (!NT_STATUS_IS_OK(nt_status = pdb_init_tdbsam(pdb_context, pdb_method, location))) {
+               return nt_status;
+       }
+
+       (*pdb_method)->name = "tdbsam_nua";
+
+       tdb_state = (*pdb_method)->private_data;
+
+       tdb_state->permit_non_unix_accounts = True;
+
+       if (!lp_non_unix_account_range(&low_nua_uid, &high_nua_uid)) {
+               DEBUG(0, ("cannot use tdbsam_nua without 'non unix account range' in smb.conf!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       tdb_state->low_nua_rid=fallback_pdb_uid_to_user_rid(low_nua_uid);
+
+       tdb_state->high_nua_rid=fallback_pdb_uid_to_user_rid(high_nua_uid);
+
+       return NT_STATUS_OK;
+}
+
+
+#else
+
+NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       DEBUG(0, ("tdbsam not compiled in!\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_init_tdbsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       DEBUG(0, ("tdbsam_nua not compiled in!\n"));
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+#endif
diff --git a/source4/passdb/pdb_unix.c b/source4/passdb/pdb_unix.c
new file mode 100644 (file)
index 0000000..b42843d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Unix password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * 
+ * 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"
+
+/******************************************************************
+  Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS unixsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+       struct passwd *pass;
+       if (!methods) {
+               DEBUG(0,("invalid methods\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       if (!sname) {
+               DEBUG(0,("invalid name specified"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       pass = Get_Pwnam(sname);
+
+       return pdb_fill_sam_pw(user, pass);
+}
+
+
+/***************************************************************************
+  Search by rid
+ **************************************************************************/
+
+static NTSTATUS unixsam_getsampwrid (struct pdb_methods *methods, 
+                                SAM_ACCOUNT *user, uint32 rid)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       struct passwd *pass = NULL;
+       const char *guest_account = lp_guestaccount();
+       if (!(guest_account && *guest_account)) {
+               DEBUG(1, ("NULL guest account!?!?\n"));
+               return nt_status;
+       }
+
+       if (!methods) {
+               DEBUG(0,("invalid methods\n"));
+               return nt_status;
+       }
+       
+       if (rid == DOMAIN_USER_RID_GUEST) {
+               pass = getpwnam_alloc(guest_account);
+               if (!pass) {
+                       DEBUG(1, ("guest account %s does not seem to exist...\n", guest_account));
+                       return nt_status;
+               }
+       } else if (pdb_rid_is_user(rid)) {
+               pass = getpwuid_alloc(fallback_pdb_user_rid_to_uid (rid));
+       }
+
+       if (pass == NULL) {
+               return nt_status;
+       }
+
+       nt_status = pdb_fill_sam_pw(user, pass);
+       passwd_free(&pass);
+
+       return nt_status;
+}
+
+static NTSTATUS unixsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+       uint32 rid;
+       if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+               return NT_STATUS_UNSUCCESSFUL;
+       return unixsam_getsampwrid(my_methods, user, rid);
+}
+
+NTSTATUS pdb_init_unixsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+       NTSTATUS nt_status;
+       
+       if (!pdb_context) {
+               DEBUG(0, ("invalid pdb_context specified\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+               return nt_status;
+       }
+       
+       (*pdb_method)->name = "unixsam";
+       (*pdb_method)->getsampwnam = unixsam_getsampwnam;
+       (*pdb_method)->getsampwsid = unixsam_getsampwsid;
+       
+       /* There's not very much to initialise here */
+       return NT_STATUS_OK;
+}
diff --git a/source4/passdb/privileges.c b/source4/passdb/privileges.c
new file mode 100644 (file)
index 0000000..c90bc47
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ *
+ * default privileges backend for passdb
+ *
+ * Copyright (C) Andrew Tridgell 2003
+ * 
+ * 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"
+
+/*
+  this is a local implementation of a privileges backend, with
+  privileges stored in a tdb. Most passdb implementations will
+  probably use this backend, although some (such as pdb_ldap) will
+  store the privileges in another manner.
+
+  The basic principle is that the backend should store a list of SIDs
+  associated with each right, where a right is a string name such as
+  'SeTakeOwnershipPrivilege'. The SIDs can be of any type, and do not
+  need to belong to the local domain.
+
+  The way this is used is that certain places in the code which
+  require access control will ask the privileges backend 'does this
+  user have the following privilege'. The 'user' will be a NT_TOKEN,
+  which is essentially just a list of SIDs. If any of those SIDs are
+  listed in the list of SIDs for that privilege then the answer will
+  be 'yes'. That will usually mean that the user gets unconditional
+  access to that functionality, regradless of any ACLs. In this way
+  privileges act in a similar fashion to unix setuid bits.
+*/
+
+/*
+  The terms 'right' and 'privilege' are used interchangably in this
+  file. This follows MSDN convention where the LSA calls are calls on
+  'rights', which really means privileges. My apologies for the
+  confusion.
+*/
+
+
+/* 15 seconds seems like an ample time for timeouts on the privileges db */
+#define LOCK_TIMEOUT 15
+
+
+/* the tdb handle for the privileges database */
+static TDB_CONTEXT *tdb;
+
+
+/* initialise the privilege database */
+BOOL privilege_init(void)
+{
+       TALLOC_CTX *mem_ctx;
+       
+       mem_ctx = talloc_init("privilege_init talloc");
+       if (!mem_ctx) {
+               DEBUG(0,("No memory to open privilege database\n"));
+               return False;
+       }
+       tdb = tdb_open_log(lock_path(mem_ctx, "privilege.tdb"), 0, TDB_DEFAULT, 
+                          O_RDWR|O_CREAT, 0600);
+       talloc_destroy(mem_ctx);
+       if (!tdb) {
+               DEBUG(0,("Failed to open privilege database\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/* 
+   lock the record for a particular privilege (write lock)
+*/
+static NTSTATUS privilege_lock_right(const char *right) 
+{
+       if (tdb_lock_bystring(tdb, right, LOCK_TIMEOUT) != 0) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       return NT_STATUS_OK;
+}
+
+/* 
+   unlock the record for a particular privilege (write lock)
+*/
+static void privilege_unlock_right(const char *right) 
+{
+       tdb_unlock_bystring(tdb, right);
+}
+
+
+/* 
+   return a list of SIDs that have a particular right
+*/
+NTSTATUS privilege_enum_account_with_right(const char *right, 
+                                          uint32 *count, 
+                                          DOM_SID **sids)
+{
+       TDB_DATA data;
+       char *p;
+       int i;
+
+       if (!tdb) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       data = tdb_fetch_by_string(tdb, right);
+       if (!data.dptr) {
+               *count = 0;
+               *sids = NULL;
+               return NT_STATUS_OK;
+       }
+
+       /* count them */
+       for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+               p += strlen(p) + 1;
+       }
+       *count = i;
+
+       /* allocate and parse */
+       *sids = malloc(sizeof(DOM_SID) * *count);
+       if (! *sids) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+               if (!string_to_sid(&(*sids)[i], p)) {
+                       free(data.dptr);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               p += strlen(p) + 1;
+       }
+       
+       free(data.dptr);
+
+       return NT_STATUS_OK;
+}
+
+/* 
+   set what accounts have a given right - this is an internal interface
+*/
+static NTSTATUS privilege_set_accounts_with_right(const char *right, 
+                                                 uint32 count, 
+                                                 DOM_SID *sids)
+{
+       TDB_DATA data;
+       char *p;
+       int i;
+
+       if (!tdb) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       /* allocate the maximum size that we might use */
+       data.dptr = malloc(count * ((MAXSUBAUTHS*11) + 30));
+       if (!data.dptr) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       p = data.dptr;
+
+       for (i=0;i<count;i++) {
+               sid_to_string(p, &sids[i]);
+               p += strlen(p) + 1;
+       }
+
+       data.dsize = PTR_DIFF(p, data.dptr);
+
+       if (tdb_store_by_string(tdb, right, data, TDB_REPLACE) != 0) {
+               free(data.dptr);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       free(data.dptr);
+       return NT_STATUS_OK;
+}
+
+
+/* 
+   add a SID to the list of SIDs for a right
+*/
+NTSTATUS privilege_add_account_right(const char *right, 
+                                    DOM_SID *sid)
+{
+       NTSTATUS status;
+       DOM_SID *current_sids;
+       uint32 current_count;
+       int i;
+
+       status = privilege_lock_right(right);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               privilege_unlock_right(right);
+               return status;
+       }       
+
+       /* maybe that SID is already listed? this is not an error */
+       for (i=0;i<current_count;i++) {
+               if (sid_equal(&current_sids[i], sid)) {
+                       privilege_unlock_right(right);
+                       free(current_sids);
+                       return NT_STATUS_OK;
+               }
+       }
+
+       /* add it in */
+       current_sids = Realloc(current_sids, sizeof(current_sids[0]) * (current_count+1));
+       if (!current_sids) {
+               privilege_unlock_right(right);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       sid_copy(&current_sids[current_count], sid);
+       current_count++;
+       
+       status = privilege_set_accounts_with_right(right, current_count, current_sids);
+
+       free(current_sids);
+       privilege_unlock_right(right);
+
+       return status;
+}
+
+
+/* 
+   remove a SID from the list of SIDs for a right
+*/
+NTSTATUS privilege_remove_account_right(const char *right, 
+                                       DOM_SID *sid)
+{
+       NTSTATUS status;
+       DOM_SID *current_sids;
+       uint32 current_count;
+       int i;
+
+       status = privilege_lock_right(right);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               privilege_unlock_right(right);
+               return status;
+       }       
+
+       for (i=0;i<current_count;i++) {
+               if (sid_equal(&current_sids[i], sid)) {
+                       /* found it - so remove it */
+                       if (current_count-i > 1) {
+                               memmove(&current_sids[i], &current_sids[i+1],
+                                       sizeof(current_sids[0]) * ((current_count-i)-1));
+                       }
+                       current_count--;
+                       status = privilege_set_accounts_with_right(right, 
+                                                                  current_count, 
+                                                                  current_sids);
+                       free(current_sids);
+                       privilege_unlock_right(right);
+                       return status;
+               }
+       }
+
+       /* removing a right that you don't have is not an error */
+       
+       safe_free(current_sids);
+       privilege_unlock_right(right);
+       return NT_STATUS_OK;
+}
+
+
+/*
+  an internal function for checking if a SID has a right
+*/
+static BOOL privilege_sid_has_right(DOM_SID *sid, const char *right)
+{
+       NTSTATUS status;
+       uint32 count;
+       DOM_SID *sids;
+       int i;
+
+       status = privilege_enum_account_with_right(right, &count, &sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+       for (i=0;i<count;i++) {
+               if (sid_equal(sid, &sids[i])) {
+                       free(sids);
+                       return True;
+               }
+       }       
+
+       safe_free(sids);
+       return False;
+}
+
+/* 
+   list the rights for an account. This involves traversing the database
+*/
+NTSTATUS privilege_enum_account_rights(DOM_SID *sid,
+                                      uint32 *count,
+                                      char ***rights)
+{
+       TDB_DATA key, nextkey;
+       char *right;
+
+       if (!tdb) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       *rights = NULL;
+       *count = 0;
+
+       for (key = tdb_firstkey(tdb); key.dptr; key = nextkey) {
+               nextkey = tdb_nextkey(tdb, key);
+
+               right = key.dptr;
+               
+               if (privilege_sid_has_right(sid, right)) {
+                       (*rights) = (char **)Realloc(*rights,sizeof(char *) * ((*count)+1));
+                       if (! *rights) {
+                               safe_free(nextkey.dptr);
+                               free(key.dptr);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       (*rights)[*count] = strdup(right);
+                       (*count)++;
+               }
+
+               free(key.dptr);
+       }
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/passdb/secrets.c b/source4/passdb/secrets.c
new file mode 100644 (file)
index 0000000..01eb82f
--- /dev/null
@@ -0,0 +1,612 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Copyright (C) Andrew Tridgell 1992-2001
+   Copyright (C) Andrew Bartlett      2002
+   Copyright (C) Rafal Szczesniak     2002
+   
+   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.
+*/
+
+/* the Samba secrets database stores any generated, private information
+   such as the local SID and machine trust password */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static TDB_CONTEXT *tdb;
+
+/* open up the secrets database */
+BOOL secrets_init(void)
+{
+       pstring fname;
+
+       if (tdb)
+               return True;
+
+       pstrcpy(fname, lp_private_dir());
+       pstrcat(fname,"/secrets.tdb");
+
+       tdb = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+       if (!tdb) {
+               DEBUG(0,("Failed to open %s\n", fname));
+               return False;
+       }
+       return True;
+}
+
+/* read a entry from the secrets database - the caller must free the result
+   if size is non-null then the size of the entry is put in there
+ */
+void *secrets_fetch(const char *key, size_t *size)
+{
+       TDB_DATA kbuf, dbuf;
+       secrets_init();
+       if (!tdb)
+               return NULL;
+       kbuf.dptr = strdup(key);
+       kbuf.dsize = strlen(key);
+       dbuf = tdb_fetch(tdb, kbuf);
+       if (size)
+               *size = dbuf.dsize;
+       free(kbuf.dptr);
+       return dbuf.dptr;
+}
+
+/* store a secrets entry 
+ */
+BOOL secrets_store(const char *key, const void *data, size_t size)
+{
+       TDB_DATA kbuf, dbuf;
+       int ret;
+
+       secrets_init();
+       if (!tdb)
+               return False;
+       kbuf.dptr = strdup(key);
+       kbuf.dsize = strlen(key);
+       dbuf.dptr = memdup(data, size);
+       dbuf.dsize = size;
+
+       ret = tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) == 0;
+
+       free(kbuf.dptr);
+       free(dbuf.dptr);
+
+       return ret == 0;
+}
+
+
+/* delete a secets database entry
+ */
+BOOL secrets_delete(const char *key)
+{
+       TDB_DATA kbuf;
+       int ret;
+
+       secrets_init();
+       if (!tdb)
+               return False;
+       kbuf.dptr = strdup(key);
+       kbuf.dsize = strlen(key);
+       ret = tdb_delete(tdb, kbuf);
+       free(kbuf.dptr);
+       return ret == 0;
+}
+
+BOOL secrets_store_domain_sid(const char *domain, const DOM_SID *sid)
+{
+       fstring key;
+
+       slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+       strupper(key);
+       return secrets_store(key, sid, sizeof(DOM_SID));
+}
+
+BOOL secrets_fetch_domain_sid(const char *domain, DOM_SID *sid)
+{
+       DOM_SID *dyn_sid;
+       fstring key;
+       size_t size;
+
+       slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+       strupper(key);
+       dyn_sid = (DOM_SID *)secrets_fetch(key, &size);
+
+       if (dyn_sid == NULL)
+               return False;
+
+       if (size != sizeof(DOM_SID))
+       { 
+               SAFE_FREE(dyn_sid);
+               return False;
+       }
+
+       *sid = *dyn_sid;
+       SAFE_FREE(dyn_sid);
+       return True;
+}
+
+BOOL secrets_store_domain_guid(const char *domain, GUID *guid)
+{
+       fstring key;
+
+       slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+       strupper(key);
+       return secrets_store(key, guid, sizeof(GUID));
+}
+
+BOOL secrets_fetch_domain_guid(const char *domain, GUID *guid)
+{
+       GUID *dyn_guid;
+       fstring key;
+       size_t size;
+       GUID new_guid;
+
+       slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+       strupper(key);
+       dyn_guid = (GUID *)secrets_fetch(key, &size);
+
+       DEBUG(6,("key is %s, size is %d\n", key, (int)size));
+
+       if ((NULL == dyn_guid) && (ROLE_DOMAIN_PDC == lp_server_role())) {
+               uuid_generate_random(&new_guid);
+               if (!secrets_store_domain_guid(domain, &new_guid))
+                       return False;
+               dyn_guid = (GUID *)secrets_fetch(key, &size);
+               if (dyn_guid == NULL)
+                       return False;
+       }
+
+       if (size != sizeof(GUID))
+       { 
+               SAFE_FREE(dyn_guid);
+               return False;
+       }
+
+       *guid = *dyn_guid;
+       SAFE_FREE(dyn_guid);
+       return True;
+}
+
+/**
+ * Form a key for fetching the machine trust account password
+ *
+ * @param domain domain name
+ *
+ * @return stored password's key
+ **/
+const char *trust_keystr(const char *domain)
+{
+       static fstring keystr;
+
+       slprintf(keystr,sizeof(keystr)-1,"%s/%s", 
+                SECRETS_MACHINE_ACCT_PASS, domain);
+       strupper(keystr);
+
+       return keystr;
+}
+
+/**
+ * Form a key for fetching a trusted domain password
+ *
+ * @param domain trusted domain name
+ *
+ * @return stored password's key
+ **/
+char *trustdom_keystr(const char *domain)
+{
+       static char* keystr;
+
+       asprintf(&keystr, "%s/%s", SECRETS_DOMTRUST_ACCT_PASS, domain);
+       strupper(keystr);
+               
+       return keystr;
+}
+
+/************************************************************************
+ Lock the trust password entry.
+************************************************************************/
+
+BOOL secrets_lock_trust_account_password(const char *domain, BOOL dolock)
+{
+       if (!tdb)
+               return False;
+
+       if (dolock)
+               return (tdb_lock_bystring(tdb, trust_keystr(domain),0) == 0);
+       else
+               tdb_unlock_bystring(tdb, trust_keystr(domain));
+       return True;
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file using
+ the above call.
+************************************************************************/
+
+BOOL secrets_fetch_trust_account_password(const char *domain, uint8 ret_pwd[16],
+                                         time_t *pass_last_set_time)
+{
+       struct machine_acct_pass *pass;
+       char *plaintext;
+       size_t size;
+
+       plaintext = secrets_fetch_machine_password();
+       if (plaintext) {
+               /* we have an ADS password - use that */
+               DEBUG(4,("Using ADS machine password\n"));
+               E_md4hash(plaintext, ret_pwd);
+               SAFE_FREE(plaintext);
+               pass_last_set_time = 0;
+               return True;
+       }
+
+       if (!(pass = secrets_fetch(trust_keystr(domain), &size))) {
+               DEBUG(5, ("secrets_fetch failed!\n"));
+               return False;
+       }
+       
+       if (size != sizeof(*pass)) {
+               DEBUG(0, ("secrets were of incorrect size!\n"));
+               return False;
+       }
+
+       if (pass_last_set_time) *pass_last_set_time = pass->mod_time;
+       memcpy(ret_pwd, pass->hash, 16);
+       SAFE_FREE(pass);
+       return True;
+}
+
+/************************************************************************
+ Routine to get account password to trusted domain
+************************************************************************/
+
+BOOL secrets_fetch_trusted_domain_password(const char *domain, char** pwd,
+                                          DOM_SID *sid, time_t *pass_last_set_time)
+{
+       struct trusted_dom_pass *pass;
+       size_t size;
+
+       /* fetching trusted domain password structure */
+       if (!(pass = secrets_fetch(trustdom_keystr(domain), &size))) {
+               DEBUG(5, ("secrets_fetch failed!\n"));
+               return False;
+       }
+
+       if (size != sizeof(*pass)) {
+               DEBUG(0, ("secrets were of incorrect size!\n"));
+               return False;
+       }
+
+       /* the trust's password */      
+       if (pwd) {
+               *pwd = strdup(pass->pass);
+               if (!*pwd) {
+                       return False;
+               }
+       }
+
+       /* last change time */
+       if (pass_last_set_time) *pass_last_set_time = pass->mod_time;
+
+       /* domain sid */
+       memcpy(&sid, &(pass->domain_sid), sizeof(sid));
+       
+       SAFE_FREE(pass);
+       
+       return True;
+}
+
+/************************************************************************
+ Routine to set the trust account password for a domain.
+************************************************************************/
+
+BOOL secrets_store_trust_account_password(const char *domain, uint8 new_pwd[16])
+{
+       struct machine_acct_pass pass;
+
+       pass.mod_time = time(NULL);
+       memcpy(pass.hash, new_pwd, 16);
+
+       return secrets_store(trust_keystr(domain), (void *)&pass, sizeof(pass));
+}
+
+/**
+ * Routine to set the password for trusted domain
+ *
+ * @param domain remote domain name
+ * @param pwd plain text password of trust relationship
+ * @param sid remote domain sid
+ *
+ * @return true if succeeded
+ **/
+
+BOOL secrets_store_trusted_domain_password(const char* domain, smb_ucs2_t *uni_dom_name,
+                                          size_t uni_name_len, const char* pwd,
+                                          DOM_SID sid)
+{
+       struct trusted_dom_pass pass;
+       ZERO_STRUCT(pass);
+
+       /* unicode domain name and its length */
+       if (!uni_dom_name)
+               return False;
+               
+       strncpy_w(pass.uni_name, uni_dom_name, sizeof(pass.uni_name) - 1);
+       pass.uni_name_len = uni_name_len;
+
+       /* last change time */
+       pass.mod_time = time(NULL);
+
+       /* password of the trust */
+       pass.pass_len = strlen(pwd);
+       fstrcpy(pass.pass, pwd);
+
+       /* domain sid */
+       memcpy(&(pass.domain_sid), &sid, sizeof(sid));
+
+       return secrets_store(trustdom_keystr(domain), (void *)&pass, sizeof(pass));
+}
+
+/************************************************************************
+ Routine to set the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+
+BOOL secrets_store_machine_password(const char *pass)
+{
+       char *key;
+       BOOL ret;
+       asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, lp_workgroup());
+       strupper(key);
+       ret = secrets_store(key, pass, strlen(pass)+1);
+       free(key);
+       return ret;
+}
+
+
+/************************************************************************
+ Routine to fetch the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+char *secrets_fetch_machine_password(void)
+{
+       char *key;
+       char *ret;
+       asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, lp_workgroup());
+       strupper(key);
+       ret = (char *)secrets_fetch(key, NULL);
+       free(key);
+       return ret;
+}
+
+
+
+/************************************************************************
+ Routine to delete the machine trust account password file for a domain.
+************************************************************************/
+
+BOOL trust_password_delete(const char *domain)
+{
+       return secrets_delete(trust_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the password for trusted domain
+************************************************************************/
+
+BOOL trusted_domain_password_delete(const char *domain)
+{
+       return secrets_delete(trustdom_keystr(domain));
+}
+
+
+BOOL secrets_store_ldap_pw(const char* dn, char* pw)
+{
+       char *key = NULL;
+       BOOL ret;
+       
+       if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, dn) < 0) {
+               DEBUG(0, ("secrets_store_ldap_pw: asprintf failed!\n"));
+               return False;
+       }
+               
+       ret = secrets_store(key, pw, strlen(pw)+1);
+       
+       SAFE_FREE(key);
+       return ret;
+}
+
+
+/**
+ * Get trusted domains info from secrets.tdb.
+ *
+ * The linked list is allocated on the supplied talloc context, caller gets to destroy
+ * when done.
+ *
+ * @param ctx Allocation context
+ * @param enum_ctx Starting index, eg. we can start fetching at third
+ *        or sixth trusted domain entry. Zero is the first index.
+ *        Value it is set to is the enum context for the next enumeration.
+ * @param num_domains Number of domain entries to fetch at one call
+ * @param domains Pointer to array of trusted domain structs to be filled up
+ *
+ * @return nt status code of rpc response
+ **/ 
+
+NTSTATUS secrets_get_trusted_domains(TALLOC_CTX* ctx, int* enum_ctx, unsigned int max_num_domains, int *num_domains, TRUSTDOM ***domains)
+{
+       TDB_LIST_NODE *keys, *k;
+       TRUSTDOM *dom = NULL;
+       char *pattern;
+       unsigned int start_idx;
+       uint32 idx = 0;
+       size_t size;
+       fstring dom_name;
+       struct trusted_dom_pass *pass;
+       NTSTATUS status;
+
+       if (!secrets_init()) return NT_STATUS_ACCESS_DENIED;
+
+       *num_domains = 0;
+       start_idx = *enum_ctx;
+
+       /* generate searching pattern */
+       if (!(pattern = talloc_asprintf(ctx, "%s/*", SECRETS_DOMTRUST_ACCT_PASS))) {
+               DEBUG(0, ("secrets_get_trusted_domains: talloc_asprintf() failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       DEBUG(5, ("secrets_get_trusted_domains: looking for %d domains, starting at index %d\n", 
+                 max_num_domains, *enum_ctx));
+
+       *domains = talloc_zero(ctx, sizeof(**domains)*max_num_domains);
+
+       /* fetching trusted domains' data and collecting them in a list */
+       keys = tdb_search_keys(tdb, pattern);
+
+       /* 
+        * if there's no keys returned ie. no trusted domain,
+        * return "no more entries" code
+        */
+       status = NT_STATUS_NO_MORE_ENTRIES;
+
+       /* searching for keys in sectrets db -- way to go ... */
+       for (k = keys; k; k = k->next) {
+               char *secrets_key;
+               
+               /* important: ensure null-termination of the key string */
+               secrets_key = strndup(k->node_key.dptr, k->node_key.dsize);
+               if (!secrets_key) {
+                       DEBUG(0, ("strndup failed!\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+                               
+               pass = secrets_fetch(secrets_key, &size);
+               
+               if (size != sizeof(*pass)) {
+                       DEBUG(2, ("Secrets record %s is invalid!\n", secrets_key));
+                       SAFE_FREE(pass);
+                       continue;
+               }
+               
+               pull_ucs2_fstring(dom_name, pass->uni_name);
+               DEBUG(18, ("Fetched secret record num %d.\nDomain name: %s, SID: %s\n",
+                          idx, dom_name, sid_string_talloc(ctx, &pass->domain_sid)));
+
+               SAFE_FREE(secrets_key);
+
+               if (idx >= start_idx && idx < start_idx + max_num_domains) {
+                       dom = talloc_zero(ctx, sizeof(*dom));
+                       if (!dom) {
+                               /* free returned tdb record */
+                               SAFE_FREE(pass);
+                               
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       
+                       /* copy domain sid */
+                       SMB_ASSERT(sizeof(dom->sid) == sizeof(pass->domain_sid));
+                       memcpy(&(dom->sid), &(pass->domain_sid), sizeof(dom->sid));
+                       
+                       /* copy unicode domain name */
+                       dom->name = talloc_strdup_w(ctx, pass->uni_name);
+                       
+                       (*domains)[idx - start_idx] = dom;
+                       
+                       DEBUG(18, ("Secret record is in required range.\n \
+                                  start_idx = %d, max_num_domains = %d. Added to returned array.\n",
+                                  start_idx, max_num_domains));
+
+                       *enum_ctx = idx + 1;
+                       (*num_domains)++;
+               
+                       /* set proper status code to return */
+                       if (k->next) {
+                               /* there are yet some entries to enumerate */
+                               status = STATUS_MORE_ENTRIES;
+                       } else {
+                               /* this is the last entry in the whole enumeration */
+                               status = NT_STATUS_OK;
+                       }
+               } else {
+                       DEBUG(18, ("Secret is outside the required range.\n \
+                                  start_idx = %d, max_num_domains = %d. Not added to returned array\n",
+                                  start_idx, max_num_domains));
+               }
+               
+               idx++;
+               
+               /* free returned tdb record */
+               SAFE_FREE(pass);
+       }
+       
+       DEBUG(5, ("secrets_get_trusted_domains: got %d domains\n", *num_domains));
+
+       /* free the results of searching the keys */
+       tdb_search_list_free(keys);
+
+       return status;
+}
+
+/*******************************************************************************
+ Lock the secrets tdb based on a string - this is used as a primitive form of mutex
+ between smbd instances.
+*******************************************************************************/
+
+BOOL secrets_named_mutex(const char *name, unsigned int timeout, size_t *p_ref_count)
+{
+       size_t ref_count = *p_ref_count;
+       int ret = 0;
+
+       if (!message_init())
+               return False;
+
+       if (ref_count == 0) {
+               ret = tdb_lock_bystring(tdb, name, timeout);
+               if (ret == 0)
+                       DEBUG(10,("secrets_named_mutex: got mutex for %s\n", name ));
+       }
+
+       if (ret == 0) {
+               *p_ref_count = ++ref_count;
+               DEBUG(10,("secrets_named_mutex: ref_count for mutex %s = %u\n", name, (unsigned int)ref_count ));
+       }
+       return (ret == 0);
+}
+
+/*******************************************************************************
+ Unlock a named mutex.
+*******************************************************************************/
+
+void secrets_named_mutex_release(const char *name, size_t *p_ref_count)
+{
+       size_t ref_count = *p_ref_count;
+
+       SMB_ASSERT(ref_count != 0);
+
+       if (ref_count == 1) {
+               tdb_unlock_bystring(tdb, name);
+               DEBUG(10,("secrets_named_mutex: released mutex for %s\n", name ));
+       }
+
+       *p_ref_count = --ref_count;
+       DEBUG(10,("secrets_named_mutex_release: ref_count for mutex %s = %u\n", name, (unsigned int)ref_count ));
+}
+
diff --git a/source4/passdb/util_sam_sid.c b/source4/passdb/util_sam_sid.c
new file mode 100644 (file)
index 0000000..e183868
--- /dev/null
@@ -0,0 +1,303 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+   Copyright (C) Jeremy Allison  1999
+   
+   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"
+
+#define MAX_SID_NAMES  7
+
+typedef struct _known_sid_users {
+       uint32 rid;
+       enum SID_NAME_USE sid_name_use;
+       const char *known_user_name;
+} known_sid_users;
+
+static struct sid_name_map_info
+{
+       DOM_SID *sid;
+       const char *name;
+       const known_sid_users *known_users;
+} sid_name_map[MAX_SID_NAMES];
+
+extern DOM_SID global_sid_Builtin;                             /* Local well-known domain */
+extern DOM_SID global_sid_World_Domain;                /* Everyone domain */
+extern DOM_SID global_sid_Creator_Owner_Domain;    /* Creator Owner domain */
+extern DOM_SID global_sid_NT_Authority;                /* NT Authority */
+
+
+static BOOL sid_name_map_initialized = False;
+/* static known_sid_users no_users[] = {{0, 0, NULL}}; */
+
+static const known_sid_users everyone_users[] = {
+       { 0, SID_NAME_WKN_GRP, "Everyone" },
+       {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users creator_owner_users[] = {
+       { 0, SID_NAME_WKN_GRP, "Creator Owner" },
+       { 1, SID_NAME_WKN_GRP, "Creator Group" },
+       {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users nt_authority_users[] = {
+       {  1, SID_NAME_ALIAS, "Dialup" },
+       {  2, SID_NAME_ALIAS, "Network"},
+       {  3, SID_NAME_ALIAS, "Batch"},
+       {  4, SID_NAME_ALIAS, "Interactive"},
+       {  6, SID_NAME_ALIAS, "Service"},
+       {  7, SID_NAME_ALIAS, "AnonymousLogon"},
+       {  8, SID_NAME_ALIAS, "Proxy"},
+       {  9, SID_NAME_ALIAS, "ServerLogon"},
+       { 11, SID_NAME_ALIAS, "Authenticated Users"},
+       { 18, SID_NAME_ALIAS, "SYSTEM"},
+       {  0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users builtin_groups[] = {
+       { BUILTIN_ALIAS_RID_ADMINS, SID_NAME_ALIAS, "Administrators" },
+       { BUILTIN_ALIAS_RID_USERS, SID_NAME_ALIAS, "Users" },
+       { BUILTIN_ALIAS_RID_GUESTS, SID_NAME_ALIAS, "Guests" },
+       { BUILTIN_ALIAS_RID_ACCOUNT_OPS, SID_NAME_ALIAS, "Account Operators" },
+       { BUILTIN_ALIAS_RID_SYSTEM_OPS, SID_NAME_ALIAS, "Server Operators" },
+       { BUILTIN_ALIAS_RID_PRINT_OPS, SID_NAME_ALIAS, "Print Operators" },
+       { BUILTIN_ALIAS_RID_BACKUP_OPS, SID_NAME_ALIAS, "Backup Operators" },
+       {  0, (enum SID_NAME_USE)0, NULL}};
+
+/**************************************************************************
+ Quick init function.
+*************************************************************************/
+
+static void init_sid_name_map (void)
+{
+       int i = 0;
+       
+       if (sid_name_map_initialized) return;
+
+       generate_wellknown_sids();
+
+       if ((lp_security() == SEC_USER) && lp_domain_logons()) {
+               sid_name_map[i].sid = get_global_sam_sid();
+               /* This is not lp_workgroup() for good reason:
+                  it must stay around longer than the lp_*() 
+                  strings do */
+               sid_name_map[i].name = strdup(lp_workgroup());
+               sid_name_map[i].known_users = NULL;
+               i++;
+               sid_name_map[i].sid = get_global_sam_sid();
+               sid_name_map[i].name = strdup(lp_netbios_name());
+               sid_name_map[i].known_users = NULL;
+               i++;
+       } else {
+               sid_name_map[i].sid = get_global_sam_sid();
+               sid_name_map[i].name = strdup(lp_netbios_name());
+               sid_name_map[i].known_users = NULL;
+               i++;
+       }
+
+       sid_name_map[i].sid = &global_sid_Builtin;
+       sid_name_map[i].name = "BUILTIN";
+       sid_name_map[i].known_users = &builtin_groups[0];
+       i++;
+       
+       sid_name_map[i].sid = &global_sid_World_Domain;
+       sid_name_map[i].name = "";
+       sid_name_map[i].known_users = &everyone_users[0];
+       i++;
+
+       sid_name_map[i].sid = &global_sid_Creator_Owner_Domain;
+       sid_name_map[i].name = "";
+       sid_name_map[i].known_users = &creator_owner_users[0];
+       i++;
+               
+       sid_name_map[i].sid = &global_sid_NT_Authority;
+       sid_name_map[i].name = "NT Authority";
+       sid_name_map[i].known_users = &nt_authority_users[0];
+       i++;
+               
+       /* End of array. */
+       sid_name_map[i].sid = NULL;
+       sid_name_map[i].name = NULL;
+       sid_name_map[i].known_users = NULL;
+       
+       sid_name_map_initialized = True;
+               
+       return;
+}
+
+/**************************************************************************
+ Turns a domain SID into a name, returned in the nt_domain argument.
+***************************************************************************/
+
+BOOL map_domain_sid_to_name(DOM_SID *sid, fstring nt_domain)
+{
+       fstring sid_str;
+       int i = 0;
+       
+       sid_to_string(sid_str, sid);
+
+       if (!sid_name_map_initialized) 
+               init_sid_name_map();
+
+       DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str));
+
+       if (nt_domain == NULL)
+               return False;
+
+       while (sid_name_map[i].sid != NULL) {
+               sid_to_string(sid_str, sid_name_map[i].sid);
+               DEBUG(5,("map_domain_sid_to_name: compare: %s\n", sid_str));
+               if (sid_equal(sid_name_map[i].sid, sid)) {              
+                       fstrcpy(nt_domain, sid_name_map[i].name);
+                       DEBUG(5,("map_domain_sid_to_name: found '%s'\n", nt_domain));
+                       return True;
+               }
+               i++;
+       }
+
+       DEBUG(5,("map_domain_sid_to_name: mapping for %s not found\n", sid_str));
+
+    return False;
+}
+
+/**************************************************************************
+ Looks up a known username from one of the known domains.
+***************************************************************************/
+
+BOOL lookup_known_rid(DOM_SID *sid, uint32 rid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+       int i = 0;
+       struct sid_name_map_info *psnm;
+
+       if (!sid_name_map_initialized) 
+               init_sid_name_map();
+
+       for(i = 0; sid_name_map[i].sid != NULL; i++) {
+               psnm = &sid_name_map[i];
+               if(sid_equal(psnm->sid, sid)) {
+                       int j;
+                       for(j = 0; psnm->known_users && psnm->known_users[j].known_user_name != NULL; j++) {
+                               if(rid == psnm->known_users[j].rid) {
+                                       DEBUG(5,("lookup_builtin_rid: rid = %u, domain = '%s', user = '%s'\n",
+                                               (unsigned int)rid, psnm->name, psnm->known_users[j].known_user_name ));
+                                       fstrcpy( name, psnm->known_users[j].known_user_name);
+                                       *psid_name_use = psnm->known_users[j].sid_name_use;
+                                       return True;
+                               }
+                       }
+               }
+       }
+
+       return False;
+}
+
+/**************************************************************************
+ Turns a domain name into a SID.
+ *** side-effect: if the domain name is NULL, it is set to our domain ***
+***************************************************************************/
+
+BOOL map_domain_name_to_sid(DOM_SID *sid, char *nt_domain)
+{
+       int i = 0;
+
+       if (nt_domain == NULL) {
+               DEBUG(5,("map_domain_name_to_sid: mapping NULL domain to our SID.\n"));
+               sid_copy(sid, get_global_sam_sid());
+               return True;
+       }
+
+       if (nt_domain[0] == 0) {
+               fstrcpy(nt_domain, lp_netbios_name());
+               DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n", nt_domain));
+               sid_copy(sid, get_global_sam_sid());
+               return True;
+       }
+
+       DEBUG(5,("map_domain_name_to_sid: %s\n", nt_domain));
+
+       if (!sid_name_map_initialized) 
+               init_sid_name_map();
+
+       while (sid_name_map[i].name != NULL) {
+               DEBUG(5,("map_domain_name_to_sid: compare: %s\n", sid_name_map[i].name));
+               if (strequal(sid_name_map[i].name, nt_domain)) {
+                       fstring sid_str;
+                       sid_copy(sid, sid_name_map[i].sid);
+                       sid_to_string(sid_str, sid_name_map[i].sid);
+                       DEBUG(5,("map_domain_name_to_sid: found %s\n", sid_str));
+                       return True;
+               }
+               i++;
+       }
+
+       DEBUG(0,("map_domain_name_to_sid: mapping to %s not found.\n", nt_domain));
+       return False;
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/  
+
+BOOL sid_check_is_domain(const DOM_SID *sid)
+{
+       return sid_equal(sid, get_global_sam_sid());
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/  
+
+BOOL sid_check_is_in_our_domain(const DOM_SID *sid)
+{
+       DOM_SID dom_sid;
+       uint32 rid;
+
+       sid_copy(&dom_sid, sid);
+       sid_split_rid(&dom_sid, &rid);
+       
+       return sid_equal(&dom_sid, get_global_sam_sid());
+}
+
+/**************************************************************************
+ Try and map a name to one of the well known SIDs.
+***************************************************************************/
+
+BOOL map_name_to_wellknown_sid(DOM_SID *sid, enum SID_NAME_USE *use, const char *name)
+{
+       int i, j;
+
+       if (!sid_name_map_initialized)
+               init_sid_name_map();
+
+       for (i=0; sid_name_map[i].sid != NULL; i++) {
+               const known_sid_users *users = sid_name_map[i].known_users;
+
+               if (users == NULL)
+                       continue;
+
+               for (j=0; users[j].known_user_name != NULL; j++) {
+                       if (strequal(users[j].known_user_name, name) == 0) {
+                               sid_copy(sid, sid_name_map[i].sid);
+                               sid_append_rid(sid, users[j].rid);
+                               *use = users[j].sid_name_use;
+                               return True;
+                       }
+               }
+       }
+
+       return False;
+}
diff --git a/source4/po/de.msg b/source4/po/de.msg
new file mode 100644 (file)
index 0000000..6a8da43
--- /dev/null
@@ -0,0 +1,1707 @@
+# German messages for international release of SWAT.
+# Copyright (C) 2001 Andreas Moroder
+
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-10-27 14:05+0100\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Andreas Moroder"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "ERRORE: Kann %s nicht öffnen\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "Hilfe"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "Setze Standardwerte"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "Verbunden als <b>%s</b><p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "Globals"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "Freigaben"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "Drucker"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "Status"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "Zeige Konfiguration"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "Passwortverwaltung"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "Aktuelle Konfiguration"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "Normale Ansich"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "Komplette Ansicht"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "Globale Variablen"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "Speichere Änderungen"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "Setze Werte zurück"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "Erweiterte Ansicht"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "Basis Ansicht"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "Parameter der Freigabe"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "Wähle Freigabe"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "Lösche Freigabe"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "Erstelle Freigabe"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "Änderung des Passworts im Demo modus nicht aktiv"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr " \"Benutzername\" muss angegeben werden \n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr " \"Altes Passwort\" muß angegeben werden \n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr " \"Remote Maschine\" muß angegeben werden \n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr " "Neues/Bestätige Passwort" muß angegeben werden \n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr " Das bestätigte Passwort stimmt nicht mit dem neuen Passwort überein\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " Das Passwort für '%s' wurde geändert. \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr " Das Passwort für '%s' wurde nicht geändert. \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "Verwaltung des Server Passwortes"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr " Benutzername : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr " Altes Passwort : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr " Neues Passwort : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr " Bestätige neues Passwort : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "Ändere Passwort"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "Füge Benutzer hinzu"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "Lösche Benutzer"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "Desaktiviere Benutzer"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "Aktiviere Benutzer"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "Client/Server Passwort Verwaltung"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " Remote Maschine : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "Drucker Parameter"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "Wichtige Hinweise:"
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Mit [*] gekennzeichnete Druckername in der Druckerauswahlliste"
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "wurde automatisch geladen von :"
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "Printcap Name"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "Der Versuch diese Drucker von SWAT aus zu löschen wird keine Auswirkung haben.\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "Wähle Drucker"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "Lösche Drucker"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "Ersteller Drucker"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr ""
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr ""
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr ""
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr ""
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr ""
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr ""
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr ""
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr ""
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr ""
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr ""
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr ""
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr ""
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr ""
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "Server Status"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "Automatische Aktualisierung"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "Aktualisierungsintervall: "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "Stop Aktualisierung"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "Version:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "aktiv"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "inaktiv"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "Stopp smbd"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "Start smbd"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "Neustart smbd"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr ""
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "Stopp nmbd"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "Start nmbd"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "Neustart nmbd"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "Aktive Verbindungen"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr ""
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "IP Adresse"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "Datum"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "Kill"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "Aktive Freigaben"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "Freigabe"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "Benutzer"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "Gruppe"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "Offene Dateien"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr ""
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "Basisoptionen"
+
+#: param/loadparm.c:643
+msgid "dos charset"
+msgstr "dos Charakterset"
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr "unix Charakterset"
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr "Anzeige Charakterset"
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr "Kommentar"
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr "Pfad"
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr "Verzeichnis"
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr "Arbeitsgruppe"
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr "netbios name"
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr "netbios aliase"
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr "netbios scope"
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr "server string"
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr "Schnittstellen"
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr "verwende nur definierte Schnittstellen"
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "Sicherheitsoptionen"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr "Sicherheit"
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr "Verschlüsselte Passwörter"
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr "update Verschlüsselte"
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr "Erlaube Vertrauneswürdige Domänen"
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr "Alternative Berechtigungen"
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr ""
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr "Min. Länge Passwort"
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr "Min. Länge Passwort"
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr "Map nach Gast"
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr "leere Passwörter"
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr "Folge pam Einschränkungen"
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr "Serverpasswort"
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr "smb passwd Datei"
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr "Privates Verzeichnis"
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr "Pfad passdb Modul"
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr ""
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr ""
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr ""
+
+#: param/loadparm.c:678
+msgid "pam password change"
+msgstr "pam Passwortänderung"
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr ""
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr ""
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr ""
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr "username map"
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr "Stufe Passwort "
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr "Stufe Benutzer"
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr "Synchronisiere unix Passwort "
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr "Beschränke anonymus"
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr "plaintext to smbpasswd"
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr "use rhosts"
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr "Benutzername"
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr "Benutzer"
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr "Benutzer"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr "Gast Account"
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr "Ungültige Benutzer"
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr "Gültige Benutzer"
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr "Administratoren"
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr ""
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr ""
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr "Druckerverwalter"
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr ""
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr ""
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr "Gruppe"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr "nur lesen"
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr "Schreiben zulassen"
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr "Beschreibbar"
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr "Beschreibbar"
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr "Erstellungsmaske"
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr "Erstellungsmodus"
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr "Erzwinge Erstellungsmodus"
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr ""
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr "Erzwinge Sicherheitsmodus"
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr "Verzeichnismaske"
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr "Verzeichnismodus"
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr "Erzwinge Verzeichnismodus"
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr "Verzeichnis Sicherheitsmaske"
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr "Erzwinge Verzeichnis Sicherheitsmodus"
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr "Vererbe Rechte"
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr "nur Gäste"
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr "nur Gäste"
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr "Gäste erlaubt"
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr "Öffentlich"
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr "Nur Benutzer"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr "Erlaube hosts"
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr "Erlaube hosts"
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr "verbiete hosts"
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr "verbiete hosts"
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "Secure Socket Layer Optionen"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr ""
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr ""
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr ""
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr ""
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr ""
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr ""
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr ""
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr ""
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr ""
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr "ssl bedarf eines Clientzertifikats"
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr "ssl bedarf eines Serverzertifikats"
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr ""
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr ""
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr ""
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr "Log Optionen"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr "Log Stufe"
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr "Debug Stufe"
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr ""
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr "nur syslog"
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr "Log Datei"
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr "max log Grösse"
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr ""
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr ""
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr ""
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr ""
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr ""
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr " Protokoll Optionen"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr "Protokoll"
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr "max Protokoll"
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr "min Protokoll"
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr ""
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr ""
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr ""
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr "nt smb Unterstützung"
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr "nt pipe Unterstützung"
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr "ntacl Unterstützung"
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr "Melde Version"
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr "Melde als"
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr ""
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr ""
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr "Reihenfolge Namensauflösung"
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr "max Paket"
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr "Paketgröße"
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr ""
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr ""
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr "wins ttl minimo"
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr "Zeitserver"
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "Optimierungsoptionen"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr ""
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr ""
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr ""
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr ""
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr "Max Anzahl smbd Prozesse"
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr "Max. Verbindungen"
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr "Paranoide Serversicherheit"
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr "Max. Festplattengröße"
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr "max Anzahl offener Dateien"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr ""
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr ""
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr "Socket Optionen"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr "Grösse stat cache"
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr ""
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr ""
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr ""
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr "verwende mmap"
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr "größe Schreibpuffer"
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "Druckoptionen"
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr "Druckaufträge insges."
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr "Druckaufträge max."
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr "lade Drucker"
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr "printcap name"
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr ""
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr "Bedruckbar"
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr "Druck ok"
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr ""
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr "Druck"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr "Druckbefehl"
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr "deaktiviere spoolss"
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr "lpq Befehl"
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr "lprm Befehl"
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr "lppause Befehl"
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr "lpresume Befehl"
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr "queuepause Befehl"
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr "queueresume Befehl"
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr "enumports Befehl"
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr "addprinter Befehl"
+
+#: param/loadparm.c:842
+msgid "deleteprinter command"
+msgstr "deleteprinter Befehl"
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr "Zeige Wizzard zum hinzufügen von Druckern"
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr ""
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr "Druckername"
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr "Drucker
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr "Verwende client Treiber"
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr "Druckertreiber"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr "Druckertreiber Datei"
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr "Pfad Druckertreiber"
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "Verwaltung Dateinamen"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr "Entferne den Punkt"
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr ""
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr ""
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr ""
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr ""
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr ""
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr ""
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr ""
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr ""
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr ""
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr ""
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr ""
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr "Verstecke Dateien"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr ""
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr ""
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr ""
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr ""
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr ""
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr ""
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr ""
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "Domänen Optionen"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr "Gruppe Domänenadministratoren"
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr "Domänen Gastgruppen"
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr ""
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr "Verfall Maschinenpasswort"
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "Login optionen"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr ""
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr ""
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr ""
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr ""
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr ""
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr ""
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr ""
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr ""
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr ""
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr ""
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr ""
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr ""
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr ""
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr ""
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "Browsing Optionen"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr "os Stufe"
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr ""
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr ""
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr "Bevorzugter master"
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr "Bevorzugter master"
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr "Lokaler master"
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr "Domänen master"
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr "browsing Liste"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr ""
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr ""
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr "Erweitertes browsing"
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "WINS Optionen"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr ""
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr ""
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr ""
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr ""
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr ""
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "Locking Optionen"
+
+#: param/loadparm.c:930
+msgid "blocking locks"
+msgstr ""
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr ""
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr ""
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr ""
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr ""
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr ""
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr ""
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr ""
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr ""
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr ""
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr ""
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "LDAP Optionen"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr ""
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr ""
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr ""
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr ""
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr ""
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr ""
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "Verschiedene Optionen"
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr ""
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr ""
+
+#: param/loadparm.c:957
+msgid "delete share command"
+msgstr ""
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr "Konfigurationsdatei"
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr "Lade im Voraus"
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr ""
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr "Lock Verzeichnis"
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr "Lock Verzeichnis"
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr "utmp Verzeichnis"
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr "wtmp Verzeichnis"
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr ""
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr ""
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr ""
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr "Message Befehl"
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr "dfree Befehl"
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr "remote announce"
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr ""
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr ""
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr ""
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr ""
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr ""
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr ""
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr "Kopie"
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr "include"
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr ""
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr ""
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr ""
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr ""
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr ""
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr ""
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr ""
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr "Verfügbar"
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr ""
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr "Typ Dateisystem"
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr ""
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr ""
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr "Folge symlinks"
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr "nicht hinabsteigen"
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr ""
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr ""
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr "Lösche nur-lesen"
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr ""
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr ""
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr ""
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr ""
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr ""
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr "verstecke lokale Benutzer"
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr "VFS Optionen"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+msgid "vfs options"
+msgstr ""
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr ""
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr ""
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr "Winbind Optionen"
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr ""
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr ""
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr ""
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr ""
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr ""
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
diff --git a/source4/po/en.msg b/source4/po/en.msg
new file mode 100644 (file)
index 0000000..2f78cb8
--- /dev/null
@@ -0,0 +1,1707 @@
+# English messages for international release of SWAT.
+# Copyright (C) 2001 Free Software Foundation, Inc.
+# TAKAHASHI Motonobu <monyo@samba.org>, 2001.
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-09-20 20:29+0900\n"
+"PO-Revision-Date: 2000-02-08 12:48+09:00\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.gr.jp>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr ""
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr ""
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr ""
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr ""
+
+#: web/swat.c:505
+msgid "Home"
+msgstr ""
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr ""
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr ""
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr ""
+
+#: web/swat.c:512
+msgid "Status"
+msgstr ""
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr ""
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr ""
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr ""
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr ""
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr ""
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr ""
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr ""
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr ""
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr ""
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr ""
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr ""
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr ""
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr ""
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr ""
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr ""
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr ""
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr ""
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr ""
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr ""
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr ""
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr ""
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr ""
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr ""
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr ""
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr ""
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr ""
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr ""
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr ""
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr ""
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr ""
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr ""
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr ""
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr ""
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr ""
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr ""
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr ""
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr ""
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr ""
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr ""
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr ""
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr ""
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr ""
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr ""
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr ""
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr ""
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr ""
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr ""
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr ""
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr ""
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr ""
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr ""
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr ""
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr ""
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr ""
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr ""
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr ""
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr ""
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr ""
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr ""
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr ""
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr ""
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr ""
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr ""
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr ""
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr ""
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr ""
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr ""
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr ""
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr ""
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr ""
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr ""
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr ""
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr ""
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr ""
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr ""
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr ""
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr ""
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr ""
+
+#: param/loadparm.c:643
+msgid "dos charset"
+msgstr ""
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr ""
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr ""
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr ""
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr ""
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr ""
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr ""
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr ""
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr ""
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr ""
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr ""
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr ""
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr ""
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr ""
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr ""
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr ""
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr ""
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr ""
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr ""
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr ""
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr ""
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr ""
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr ""
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr ""
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr ""
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr ""
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr ""
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr ""
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr ""
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr ""
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr ""
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr ""
+
+#: param/loadparm.c:678
+msgid "pam password change"
+msgstr ""
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr ""
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr ""
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr ""
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr ""
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr ""
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr ""
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr ""
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr ""
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr ""
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr ""
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr ""
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr ""
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr ""
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr ""
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr ""
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr ""
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr ""
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr ""
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr ""
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr ""
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr ""
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr ""
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr ""
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr ""
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr ""
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr ""
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr ""
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr ""
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr ""
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr ""
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr ""
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr ""
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr ""
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr ""
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr ""
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr ""
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr ""
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr ""
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr ""
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr ""
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr ""
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr ""
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr ""
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr ""
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr ""
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr ""
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr ""
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr ""
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr ""
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr ""
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr ""
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr ""
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr ""
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr ""
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr ""
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr ""
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr ""
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr ""
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr ""
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr ""
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr ""
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr ""
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr ""
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr ""
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr ""
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr ""
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr ""
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr ""
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr ""
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr ""
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr ""
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr ""
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr ""
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr ""
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr ""
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr ""
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr ""
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr ""
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr ""
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr ""
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr ""
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr ""
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr ""
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr ""
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr ""
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr ""
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr ""
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr ""
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr ""
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr ""
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr ""
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr ""
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr ""
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr ""
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr ""
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr ""
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr ""
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr ""
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr ""
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr ""
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr ""
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr ""
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr ""
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr ""
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr ""
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr ""
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr ""
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr ""
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr ""
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr ""
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr ""
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr ""
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr ""
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr ""
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr ""
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr ""
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr ""
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr ""
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr ""
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr ""
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr ""
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr ""
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr ""
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr ""
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr ""
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr ""
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr ""
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr ""
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr ""
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr ""
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr ""
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr ""
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr ""
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr ""
+
+#: param/loadparm.c:842
+msgid "deleteprinter command"
+msgstr ""
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr ""
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr ""
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr ""
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr ""
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr ""
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr ""
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr ""
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr ""
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr ""
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr ""
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr ""
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr ""
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr ""
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr ""
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr ""
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr ""
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr ""
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr ""
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr ""
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr ""
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr ""
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr ""
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr ""
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr ""
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr ""
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr ""
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr ""
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr ""
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr ""
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr ""
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr ""
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr ""
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr ""
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr ""
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr ""
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr ""
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr ""
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr ""
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr ""
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr ""
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr ""
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr ""
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr ""
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr ""
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr ""
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr ""
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr ""
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr ""
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr ""
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr ""
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr ""
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr ""
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr ""
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr ""
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr ""
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr ""
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr ""
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr ""
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr ""
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr ""
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr ""
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr ""
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr ""
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr ""
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr ""
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr ""
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr ""
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr ""
+
+#: param/loadparm.c:930
+msgid "blocking locks"
+msgstr ""
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr ""
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr ""
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr ""
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr ""
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr ""
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr ""
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr ""
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr ""
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr ""
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr ""
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr ""
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr ""
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr ""
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr ""
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr ""
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr ""
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr ""
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr ""
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr ""
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr ""
+
+#: param/loadparm.c:957
+msgid "delete share command"
+msgstr ""
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr ""
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr ""
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr ""
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr ""
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr ""
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr ""
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr ""
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr ""
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr ""
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr ""
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr ""
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr ""
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr ""
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr ""
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr ""
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr ""
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr ""
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr ""
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr ""
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr ""
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr ""
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr ""
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr ""
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr ""
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr ""
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr ""
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr ""
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr ""
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr ""
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr ""
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr ""
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr ""
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr ""
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr ""
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr ""
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr ""
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr ""
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr ""
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr ""
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr ""
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr ""
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr ""
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr ""
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr ""
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr ""
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+msgid "vfs options"
+msgstr ""
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr ""
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr ""
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr ""
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr ""
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr ""
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr ""
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr ""
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr ""
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
diff --git a/source4/po/fr.msg b/source4/po/fr.msg
new file mode 100644 (file)
index 0000000..493db65
--- /dev/null
@@ -0,0 +1,1709 @@
+# French messages for international release of SWAT.
+# Copyright (C) 2001 François Le Lay <fanch@tuxfamily.org>
+
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-09-20 14:05+0100\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: François Le Lay <fanch@tuxfamily.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "ERREUR: Impossible d'ouvrir %s\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "Aide"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "Définir par défaut"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "Connecté en tant que <b>%s</b><p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "Paramètres Généraux"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "Partages"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "Imprimantes"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "Statut"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "Voir Configuration"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "Gestion des mots de passe"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "Configuration Actuelle"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "Vue Normale"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "Vue Complète"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "Variables Globales"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "Sauver les modifications"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "Réinitialiser Valeurs"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "Vue Détaillée"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "Vue Basique"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "Paramètres de partage"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "Choisir un partage"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "Supprimer un partage"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "Créer un partage"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "changement de mot de passe en mode démo rejeté"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr "  Le champ \"Nom d'utilisateur\" doit être spécifié\n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr " Le champ  \"Ancien mot de passe\" doît être spécifié\n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr "  Le champ \"Machine Distante\" doît être spécifié\n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr " Les champs \"Nouveau mot de passe\" et  \"Confirmation du nouveau mot de passe\" doivent être spécifiés \n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr " Echec de la confirmation du nouveau mot de passe\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " Le mot de passe de '%s' a été modifié. \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The password for '%s' has NOT been changed. \n"
+msgstr " Le mot de passe de '%s' n'a PAS été modifié. \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "Gestion des mots de passe serveur"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr " Nom d'utilisateur : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr " Ancien mot de passe : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr " Nouveau mot de passe : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr " Confirmation du nouveau mot de passe : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "Modifier le mot de passe"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "Nouvel Utilisateur"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "Supprimer Utilisateur"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "Désactiver Utilisateur"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "Activer Utilisateur"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "Gestion des mots de passe Client/Serveur"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " Machine distante : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "Paramètres Imprimantes"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "Note Importante:"
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Les Noms d'imprimantes marqués du signe [*] dans le menu déroulant Choisir Imprimante"
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "désignent des imprimantes automatiquement chargées depuis le "
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "Nom Printcap"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "Essayer de supprimer ces imprimantes depuis SWAT n'aura aucun effet.\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "Choisir Imprimante"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "Supprimer Imprimante"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "Créer Imprimante"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr ""
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr ""
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr ""
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr ""
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr ""
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr ""
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr ""
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr ""
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr ""
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr ""
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr ""
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr ""
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr ""
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "Statut du Serveur"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "Rafraîchissement Automatique"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "Intervalle de rafraîchissement: "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "Stopper Rafraîchissement"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "version:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "actif"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "non actif"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "Stopper smbd"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "Lancer smbd"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "Relancer smbd"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr ""
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "Stopper nmbd"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "Lancer nmbd"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "Relancer nmbd"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "Connections Actives"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr ""
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "adresse IP"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "Date"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "Terminer"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "Partages Actifs"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "Partager"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "Utilisateur"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "Groupe"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "Fichiers Ouverts"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr "Fichier"
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "Options de base"
+
+#: param/loadparm.c:643
+msgid "dos charset"
+msgstr "caractères dos"
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr "caractères unix"
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr "caractères display"
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr "commentaire"
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr "chemin"
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr "répertoire"
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr "groupe de travail"
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr "nom netbios"
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr "alias netbios"
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr ""
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr ""
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr ""
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr "lier uniquement les interfaces"
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "Options de Sécurité"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr "sécurité"
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr "crypter les mots de passe"
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr "mise à jour cryptés"
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr "autoriser les domaines de confiance"
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr "permissions alternatives"
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr ""
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr "taille minimale des mots de passe"
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr "taille minimale des mots de passe"
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr "associer à un invité"
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr "mots de passe null"
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr "utiliser les restrictions pam"
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr "serveur de mots de passe"
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr "fichier passwd de smb"
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr "répertoire privé"
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr "chemin du module passdb"
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr "répertoire root"
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr "répertoire root"
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr ""
+
+#: param/loadparm.c:678
+msgid "pam password change"
+msgstr "modification de mot de passe pam"
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr "programme passwd"
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr ""
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr ""
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr ""
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr "niveau mot de passe"
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr "niveau nom d'utilisateur"
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr "synchroniser avec mots de passe unix"
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr "limiter anonyme"
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr "texte brut vers smbpasswd"
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr "utiliser rhosts"
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr "nom d'utilisateur"
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr "utilisateur"
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr "utilisateurs"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr "compte invité"
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr "utilisateurs non-valides"
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr "utilisateurs valides"
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr "utilisateurs administrateurs"
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr ""
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr ""
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr "administrateur d'imprimante"
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr "forcer utilisateur"
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr "forcer le groupe"
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr "groupe"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr "lecture seule"
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr "écriture ok"
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr "écriture possible"
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr "écriture possible"
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr ""
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr "mode création"
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr "forcer le mode création"
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr ""
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr "forcer le mode sécurité"
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr "masque de répertoire"
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr "mode répertoire"
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr "forcer le mode répertoire"
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr "masque de sécurité répertoire"
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr "forcer le mode sécurité répertoire"
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr "héritage de permissions"
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr "invité seulement"
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr "invité seulement"
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr "invité ok"
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr "public"
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr "utilisateur seulement"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr "autoriser hosts"
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr "autoriser hosts"
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr "refuser hosts"
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr "refuser hosts"
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "Options Secure Socket Layer"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr ""
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr ""
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr ""
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr ""
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr ""
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr "certificat ssl serveur"
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr "clé ssl serveur"
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr "certificat ssl client"
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr "clé ssl client"
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr "ssl requiert un certificat client"
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr "ssl requiert un certificat serveur"
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr "chiffres ssl"
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr "ssl version"
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr "compatibilité ssl"
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr "Options de Logging"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr "niveau log"
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr "niveau debug"
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr ""
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr "syslog seulement"
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr "fichier log"
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr "taille maxi de la log"
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr ""
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr ""
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr ""
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr ""
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr ""
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr "Options de Protocole"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr "protocole"
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr "protocole maxi"
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr "protocole mini"
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr ""
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr ""
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr ""
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr "support smb nt"
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr "support pipe nt"
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr "support acl nt"
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr "annoncer la version"
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr "annoncer comme"
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr "mux maxi"
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr "xmit maxi"
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr "ordre de résolution des noms"
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr "paquet maxi"
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr "taille de paquet"
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr "ttl maxi"
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr "wins ttl maxi"
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr "wins ttl mini"
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr ""
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "Options de réglage"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr ""
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr ""
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr ""
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr ""
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr "nombre maxi de processus smbd"
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr "connections maxi"
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr ""
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr "taille maxi de disque"
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr "nombre maxi de fichiers ouverts"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr "espace d'impression mini"
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr ""
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr "options de socket"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr "taille du cache stat"
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr "allocation stricte"
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr "synchronisation stricte"
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr "toujours synchroniser"
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr "utiliser mmap"
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr "taille du cache d'écriture"
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "Options d'impression"
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr "total jobs d'impression"
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr "max jobs d'impression"
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr "charger imprimantes"
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr "nom printcap"
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr ""
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr "imprimable"
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr "imprimante ok"
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr ""
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr "impression"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr "commande d'impression"
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr "désactiver spoolss"
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr "commande lpq"
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr "commande lprm"
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr "commande lppause"
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr "commande lpresume"
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr "commande queuepause"
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr "commande queueresume"
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr "commande enumports"
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr "commande addprinter"
+
+#: param/loadparm.c:842
+msgid "deleteprinter command"
+msgstr "commande deleteprinter"
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr "Voir l'assistant d'ajout d'imprimante"
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr ""
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr "nom d'imprimante"
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr "imprimante"
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr "utiliser le pilote client"
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr "pilote d'imprimante"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr "fichier de pilote d'imprimante"
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr "adresse du pilote d'imprimante"
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "Gestion des noms de fichier"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr ""
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr ""
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr "casse par défaut"
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr "sensible à la casse"
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr ""
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr "garder la casse"
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr ""
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr ""
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr ""
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr ""
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr "supprimer les fichiers veto"
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr "fichiers veto"
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr "cacher les fichiers"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr ""
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr "map système"
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr "map caché"
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr ""
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr ""
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr ""
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr "cache stat"
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "Options de Domaine"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr "groupe d'administration du domaine"
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr "groupe invité du domaine"
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr "map noms de groupe"
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr ""
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "Options de Logon"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr "ajouter script utilisateur"
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr "supprimer script utilisateur"
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr "ajouter script de groupe"
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr "supprimer script de groupe"
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr "ajouter un utilisateur à un script de groupe"
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr "supprimer un utilisateur d'un script de groupe"
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr "ajouter un script machine"
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr "script shutdown"
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr "annuler script shutdown"
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr "script de logon"
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr ""
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr ""
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr ""
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr ""
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "Options de Navigation"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr "niveau os"
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr "annonce lm"
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr "intervalle lm"
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr "master préféré"
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr "master préféré"
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr "master local"
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr "master de domaine"
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr "parcourir la liste"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr "navigable"
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr "navigable"
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr "navigation améliorée"
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "Options WINS"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr ""
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr ""
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr ""
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr ""
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr ""
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "Options de Verrouillage"
+
+#: param/loadparm.c:930
+msgid "blocking locks"
+msgstr "verrous bloquants"
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr ""
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr ""
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr "verrouillage"
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr ""
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr ""
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr ""
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr ""
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr "verrouillage posix"
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr "verrouillage strict"
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr "modes de partage"
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "Options Ldap"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr "serveur ldap"
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr "port ldap"
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr "suffixe ldap"
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr "filtre ldap"
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr ""
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr ""
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "Options Diverses"
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr "ajouter une commande de partage"
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr "modifier une commande de partage"
+
+#: param/loadparm.c:957
+msgid "delete share command"
+msgstr "supprimer une commande de partage"
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr "fichier de configuration"
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr "pré-chargement"
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr ""
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr ""
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr ""
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr ""
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr ""
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr ""
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr "service par défaut"
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr "par défaut"
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr "commande message"
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr "commande dfree"
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr "annonce distante"
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr "synchronisation de navigation distante"
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr "adresse de socket"
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr ""
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr ""
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr ""
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr ""
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr ""
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr ""
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr ""
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr ""
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr ""
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr ""
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr ""
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr ""
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr ""
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr "disponible"
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr ""
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr ""
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr ""
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr ""
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr ""
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr "ne pas descendre"
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr ""
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr ""
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr "supprimer lecture seule"
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr ""
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr ""
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr ""
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr ""
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr ""
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr "cacher les utilisateurs locaux"
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr "Options VFS"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+msgid "vfs options"
+msgstr ""
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr ""
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr ""
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr "Options Winbind"
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr ""
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr ""
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr ""
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr ""
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr ""
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
+
+
diff --git a/source4/po/it.msg b/source4/po/it.msg
new file mode 100644 (file)
index 0000000..708f216
--- /dev/null
@@ -0,0 +1,1707 @@
+# Italian messages for international release of SWAT.
+# Copyright (C) 2001 Simo Sorce <idra@samba.org>
+
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-09-20 14:05+0100\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Simo Sorce <idra@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "ERRORE: Impossibile aprire %s\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "Aiuto"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "Imposta Default"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "Connesso come <b>%s</b><p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "Globali"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "Condivisioni"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "Stampanti"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "Stato"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "Visualizza Configurazione"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "Gestione Password"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "Configurazione Attuale"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "Vista Normale"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "Vista Completa"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "Variabili Globali"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "Salva Modifiche"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "Resetta Valori"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "Vista Avanzata"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "Vista Semplice"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "Parametri Condivisioni"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "Scegli Condivisione"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "Cancella Condivisione"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "Crea Condivisione"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "cambio password in modalita' demo rigettata"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr " \"Nome Utente\" deve essere specificato \n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr " \"Vecchia Password\" deve essere specificato \n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr " \"Macchina Remota\" deve essere specificato \n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr " "Nuova/Conferma Password" devono essere specificati \n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr " la password di conferma non e' uguale alla nuova password\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " La password per '%s' e' stata cambiata. \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr " La password per '%s' non e' stata cambianta. \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "Gestione Password del Server"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr " Nome Utente : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr " Vecchia Password : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr " Nuova Password : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr " Conferma nuova Password : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "Cambia Password"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "Aggiungi Nuovo Utente"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "Cancella Utente"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "Disabilita Utente"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "Abilita Utente"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "Gestione Password Client/Server"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " Macchina Remota : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "Parametri Stampante"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "Nota Importante:"
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "nomi di stampante marcati con [*] nel riquadro a scomparsa Scegli Stampante"
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "sono stampanti caricate automaticamente da "
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "Nome Printcap"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "Il tentativo di cancellare queste stampanti da sWAT non avara' effetto.\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "Scegli Stampante"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "Cancella Stampante"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "Crea Stampante"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr ""
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr ""
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr ""
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr ""
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr ""
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr ""
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr ""
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr ""
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr ""
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr ""
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr ""
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr ""
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr ""
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "Stato del Server"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "Rinfresco Automatico"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "Intervallo Rinfresco: "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "Ferma Rinfresco"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "versione:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "attivo"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "non attivo"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "Ferma smbd"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "Lancia smbd"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "Rilancia smbd"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr ""
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "Ferma nmbd"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "Lancia nmbd"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "Rilancia nmbd"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "Connessioni Attive"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr ""
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "indirizzo IP"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "Data"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "Termina"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "Condivisioni Attive"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "Condivisione"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "Utente"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "Gruppo"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "File Aperti"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr ""
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "Opzioni Basilari"
+
+#: param/loadparm.c:643
+msgid "dos charset"
+msgstr "set caratteri dos"
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr "set caratteri unix"
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr "set caratteri display"
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr "commento"
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr "percorso"
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr ""
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr ""
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr "nome netbios"
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr "alias netbios"
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr "scope netbios"
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr "stringa server"
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr "interfacce"
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr "usa solo interfacce definite"
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "Opzioni di Sicurezza"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr "sicurezza"
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr "password cryptate"
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr "aggiorna criptate"
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr "permetti domini trusted"
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr "permessi alternativi"
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr ""
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr "minima lunghezza passwd"
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr "minima lunghezza password"
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr "mappa su ospite"
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr "password nulle"
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr "usa restrizioni pam"
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr ""
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr ""
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr "directory privata"
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr "percorso modulo passdb"
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr ""
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr ""
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr ""
+
+#: param/loadparm.c:678
+msgid "pam password change"
+msgstr ""
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr ""
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr ""
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr ""
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr "mappa nomi utenti"
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr "livello password"
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr "livello nome utente"
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr "sincronizza password unix"
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr "limita anonimo"
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr "da plaintext a smbpasswd"
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr "usa rhosts"
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr "nome utente"
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr "utente"
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr "utenti"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr "account ospite"
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr "utenti non validi"
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr "utenti validi"
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr "utenti amministratori"
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr ""
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr ""
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr "amministratore stmapnati"
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr "forza utente"
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr "forza gruppo"
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr "gruppo"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr "sola lettura"
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr "ok scrittura"
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr "scrivibile"
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr "scrivibile"
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr ""
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr ""
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr "forza create mode"
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr ""
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr "forza security mode"
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr ""
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr ""
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr "forza directory mode"
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr ""
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr "forza directory security mode"
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr "eredita permessi"
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr "solo ospite"
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr "solo ospite"
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr "ok ospite"
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr "pubblico"
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr "solo utente"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr "permetti hosts"
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr "permetti hosts"
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr "vieta hosts"
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr "vieta hosts"
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "Opzioni Secure Socket Layer"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr ""
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr ""
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr ""
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr ""
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr ""
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr ""
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr ""
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr ""
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr ""
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr "ssl richiede certificato client"
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr "ssl richiede certificato server"
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr ""
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr "ssl versione"
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr ""
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr "Opzioni di Log"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr "livello log"
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr "livello debug"
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr ""
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr "solo syslog"
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr "file di log"
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr "massima dimensione log"
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr ""
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr ""
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr ""
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr ""
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr ""
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr "Opzioni Protocollo"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr "protocollo"
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr "protocollo massimo"
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr "protocollo minimo"
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr ""
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr ""
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr ""
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr "supporto smb nt"
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr "supporto pipe nt"
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr "supporto acl nt"
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr "annuncia versione"
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr "annuncia come"
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr "mux massimo"
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr "xmit massimo"
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr "ordine risoluzione nomi"
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr "pacchetto massimo"
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr "dimensione pacchetto"
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr "ttl massimo"
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr "wins ttl massimo"
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr "wins ttl minimo"
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr ""
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "Opzioni Tuning"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr ""
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr ""
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr ""
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr ""
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr "massimo n. processi smbd"
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr "massimo n. connessioni"
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr ""
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr "massima dimensione disco"
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr "massimo n. file aperti"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr "spazio stampa minimo"
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr ""
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr "opzioni socket"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr "dimensione stat cache"
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr "allocazione stretta"
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr "sincronizzazione stretta"
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr "sincronizza sempre"
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr "usa mmap"
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr "dimensione write cache"
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "Opzioni di Stampa"
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr "job stampa totali"
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr "massimo n. job stampa"
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr "carica stampanti"
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr "nome printcap"
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr ""
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr "stmpabile"
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr "ok stampa"
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr ""
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr "stampa"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr "comando stampa"
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr "disabilita spoolss"
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr "comando lpq"
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr "comando lprm"
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr "comando lppause"
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr "comando lpresume"
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr "comando queuepause"
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr "comando queueresume"
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr "cmando enumports"
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr "comando addprinter"
+
+#: param/loadparm.c:842
+msgid "deleteprinter command"
+msgstr "comando deleteprinter"
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr "mostra wizard aggiungi stampanti"
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr "mappa driver os2"
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr "nome stampante"
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr "stampante"
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr "usa client driver"
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr "driver stampante"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr "file driver stampante"
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr "posizione driver stampante"
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "Gestione Nomi File"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr "togli il punto"
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr ""
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr ""
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr ""
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr ""
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr ""
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr ""
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr ""
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr ""
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr ""
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr ""
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr ""
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr "nascondi file"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr ""
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr "mappa system"
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr "mappa hidden"
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr "mappa archive"
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr ""
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr ""
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr ""
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "Opzioni Dominio"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr "gruppo amministratori dominio"
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr "gruppo ospiti dominio"
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr "mappa nome gruppo"
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr ""
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "Opzioni di Logon"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr ""
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr ""
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr ""
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr ""
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr ""
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr ""
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr ""
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr ""
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr ""
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr ""
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr ""
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr ""
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr ""
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr ""
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "Opzioni Browsing"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr "livello os"
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr "annuncio lm"
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr "intervallo lm"
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr "master preferito"
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr "master preferito"
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr "master locale"
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr "master dominio"
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr "lista browsing"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr ""
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr ""
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr ""
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "opzioni WINS"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr ""
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr ""
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr ""
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr ""
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr ""
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "Opzioni Locking"
+
+#: param/loadparm.c:930
+msgid "blocking locks"
+msgstr ""
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr ""
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr ""
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr ""
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr ""
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr ""
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr ""
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr ""
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr ""
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr ""
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr ""
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "Opzioni Ldap"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr ""
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr ""
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr ""
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr ""
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr ""
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr ""
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "Opzioni Generiche"
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr ""
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr ""
+
+#: param/loadparm.c:957
+msgid "delete share command"
+msgstr ""
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr "file configurazione"
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr "precarica"
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr ""
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr ""
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr ""
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr ""
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr ""
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr ""
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr ""
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr ""
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr "comando message"
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr "comando dfree"
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr "annuncio remoto"
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr ""
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr ""
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr ""
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr ""
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr ""
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr ""
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr "copia"
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr "includi"
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr "esegui"
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr "pre-esegui"
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr "pre-esegui e chiudi"
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr "post-esegui"
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr "root pre-esegui"
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr "root pre-esegui e chiudi"
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr "root post-esegui"
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr "disponibile"
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr ""
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr "tipo file system"
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr ""
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr ""
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr ""
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr "non discendere"
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr ""
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr ""
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr "cancella sola-lettura"
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr ""
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr ""
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr ""
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr ""
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr ""
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr "nascondi utenti locali"
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr "Opzioni VFS"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+msgid "vfs options"
+msgstr ""
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr ""
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr ""
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr "Opzioni Winbind"
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr ""
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr ""
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr ""
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr ""
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr ""
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
diff --git a/source4/po/ja.msg b/source4/po/ja.msg
new file mode 100644 (file)
index 0000000..e77f34e
--- /dev/null
@@ -0,0 +1,1821 @@
+# Japanese messages for international release of SWAT.
+# Copyright (C) 2001 Ryo Kawahara <rkawa@lbe.co.jp>, 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n-swatE VERSION\n"
+"POT-Creation-Date: 2001-09-20 20:29+0900\n"
+"PO-Revision-Date: 2000-04-03 17:55+09:00\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.gr.jp>\n"
+"Language-Team: Samba Team <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=Shift_JIS\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "%s \82ð\83I\81[\83v\83\93\82Å\82«\82Ü\82¹\82ñ\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "\90à\96¾"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "\8aù\92è\92l\82É\96ß\82·"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "<b>%s</b>\82Æ\82µ\82Ä\83\8d\83O\83C\83\93<p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "\83z\81[\83\80"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "\91S\91Ì\90Ý\92è"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "\8b¤\97L\90Ý\92è"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "\83v\83\8a\83\93\83^\90Ý\92è"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "\93®\8dì\8fó\8bµ"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "\90Ý\92è\95\\\8e¦"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "\83p\83X\83\8f\81[\83h\8aÇ\97\9d\81E\83\86\81[\83U\8aÇ\97\9d"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "\8c»\8dÝ\82Ì\90Ý\92è"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "\95W\8f\80\95\\8e¦"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "\8a®\91S\95\\8e¦"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "\91S\91Ì\90Ý\92è"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "\90Ý\92è\95Ï\8dX"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "\83\8a\83Z\83b\83g"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "\8fÚ\8d×\95\\8e¦"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "\95W\8f\80\95\\8e¦"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "\8b¤\97L\90Ý\92è"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "\8b¤\97L\91I\91ð"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "\8b¤\97L\8dí\8f\9c"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "\90V\8bK\8b¤\97L\8dì\90¬"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "\83f\83\82\81E\83\82\81[\83h\82Å\82Ì\83p\83X\83\8f\81[\83h\95Ï\8dX\82Í\82Å\82«\82Ü\82¹\82ñ\n"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr "\81u\83\86\81[\83U\96¼\81v\82ð\93ü\97Í\82­\82¾\82³\82¢\n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr "\81u\8b\8c\83p\83X\83\8f\81[\83h\81v\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢\n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr "\81u\83\8a\83\82\81[\83\83}\83V\83\93\81v\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢\n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr "\81u\90V\83p\83X\83\8f\81[\83h\81v\82ð\93ü\97Í\82µ\81A\8dÄ\93ü\97Í\82à\82µ\82Ä\82­\82¾\82³\82¢\n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr "\90V\83p\83X\83\8f\81[\83h\82Ì\8dÄ\93ü\97Í\82ª\8aÔ\88á\82Á\82Ä\82¢\82Ü\82·\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " '%s' \82Ì\83p\83X\83\8f\81[\83h\82Í\95Ï\8dX\82³\82ê\82Ü\82µ\82½ \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr " '%s' \82Ì\83p\83X\83\8f\81[\83h\82Í\95Ï\8dX\82³\82ê\82Ü\82¹\82ñ\82Å\82µ\82½ \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "\83\8d\81[\83J\83\8b \83}\83V\83\93\82Ì\83p\83X\83\8f\81[\83h\8aÇ\97\9d"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr "\83\86\81[\83U\96¼ : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr "\8b\8c\83p\83X\83\8f\81[\83h : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr "\90V\83p\83X\83\8f\81[\83h : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr "\90V\83p\83X\83\8f\81[\83h\8dÄ\93ü\97Í : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "\83p\83X\83\8f\81[\83h\95Ï\8dX"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "\90V\8bK\83\86\81[\83U\92Ç\89Á"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "\83\86\81[\83U\82ð\8dí\8f\9c\82·\82é"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "\8eg\97p\95s\89Â\82É\82·\82é"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "\8eg\97p\89Â\94\\82É\82·\82é"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "\83\8a\83\82\81[\83\83}\83V\83\93\82Ì\83p\83X\83\8f\81[\83h\8aÇ\97\9d"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " \83\8a\83\82\81[\83\83}\83V\83\93 : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "\83v\83\8a\83\93\83^\90Ý\92è [Printer]"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "*\92\8d:"
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "\96¼\91O\82Ì\90æ\93ª\82É [*] \82ª\82Â\82¢\82½\83v\83\8a\83\93\83^"
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "\82Í\81A"
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "printcap name \83p\83\89\83\81\81[\83^"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "\82©\82ç\8e©\93®\90Ý\92è\82³\82ê\82½\82à\82Ì\82Å\82·\82©\82ç\81A\8dí\8f\9c\82·\82é\82±\82Æ\82Í\82Å\82«\82Ü\82¹\82ñ\81B\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "\83v\83\8a\83\93\83^\91I\91ð"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "\83v\83\8a\83\93\83^\8dí\8f\9c"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "\83v\83\8a\83\93\83^\90V\8bK\8dì\90¬"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr "\8b\91\94Û\82È\82µ"
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr "\82·\82×\82Ä\8b\91\94Û"
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr "DOS\82ð\8b\91\94Û"
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr "\8eQ\8fÆ\82ð\8b\91\94Û"
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr "\8dX\90V\82ð\8b\91\94Û"
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr "\8eQ\8fÆ\82Ì\82Ý"
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr "\91}\93ü\82Ì\82Ý"
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr "\8dX\90V    "
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr "\90ê\97L+\83o\83b\83` "
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr "\90ê\97L        "
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr "\83o\83b\83`      "
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr "\83\8c\83x\83\8b_II   "
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr "\82È\82µ        "
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "\83T\81[\83o\81[\93®\8dì\8fó\8bµ"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "\8e©\93®\8dÄ\95\\8e¦"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "\8dÄ\95\\8e¦\8aÔ\8au(\95b): "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "\8e©\93®\95\\8e¦\92â\8e~"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "\83o\81[\83W\83\87\83\93:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr "\83t\83@\83C\83\8b\8b¤\97L\83f\81[\83\82\83\93(smbd):"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "\93®\8dì\92\86"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "\92â\8e~\92\86"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "smbd\92â\8e~"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "smbd\8bN\93®"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "smbd\8dÄ\8bN\93®"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr "\83l\81[\83\80 \83T\81[\83r\83\83f\81[\83\82\83\93(nmbd)"
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "nmbd\92â\8e~"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "nmbd\8bN\93®"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "nmbd\8dÄ\8bN\93®"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "\90Ú\91±\92\86\83N\83\89\83C\83A\83\93\83g"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr "\83v\83\8d\83Z\83XID"
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr "\83N\83\89\83C\83A\83\93\83g"
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "IP\83A\83h\83\8c\83X"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "\93ú\95t"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "\90Ø\92f"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "\90Ú\91±\92\86\8b¤\97L"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "\8b¤\97L\96¼"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "\83\86\81[\83U"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "\83O\83\8b\81[\83v"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "\8eg\97p\92\86\83t\83@\83C\83\8b"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr "\94r\91¼\83\82\81[\83h"
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr "\8eQ\8fÆ/\8dX\90V"
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr "\95Ö\8bX\83\8d\83b\83N(Oplock)"
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr "\83t\83@\83C\83\8b\96¼"
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "\8aî\96{\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:643
+#, fuzzy
+msgid "dos charset"
+msgstr "\97L\8cø\82È\95\8e\9a"
+
+#: param/loadparm.c:644
+#, fuzzy
+msgid "unix charset"
+msgstr "\97L\8cø\82È\95\8e\9a"
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr ""
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr "\83R\83\81\83\93\83g"
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr "\83p\83X"
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr "\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr "\83\8f\81[\83N\83O\83\8b\81[\83v"
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr "netbios \96¼"
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr "netbios \83G\83C\83\8a\83A\83X"
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr "netbios \83X\83R\81[\83v"
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr "\83T\81[\83o\95\8e\9a\97ñ"
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr "\83C\83\93\83^\81[\83t\83F\81[\83X"
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr "\82±\82Ì\83C\83\93\83^\81[\83t\83F\81[\83X\82Ì\82Ý\8eg\97p"
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "\83Z\83L\83\85\83\8a\83e\83\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr "\83Z\83L\83\85\83\8a\83e\83B"
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr "\83p\83X\83\8f\81[\83h\82ð\88Ã\8d\86\89»"
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr "\88Ã\8d\86\89»\83p\83X\83\8f\81[\83h\82É\8dX\90V"
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr "\90M\97\8a\82Å\82«\82é\83h\83\81\83C\83\93\82ð\8b\96\89Â"
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr "\91ã\8ds\83p\81[\83~\83b\83V\83\87\83\93"
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr "\93¯\93\99\82Ì\83z\83X\83g"
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr "\8dÅ\8f¬\83p\83X\83\8f\81[\83h\92·"
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr "\8dÅ\8f¬\83p\83X\83\8f\81[\83h\92·"
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr "\83Q\83X\83g\82É\83}\83b\83v"
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr "\8bó\83p\83X\83\8f\81[\83h"
+
+#: param/loadparm.c:669
+#, fuzzy
+msgid "obey pam restrictions"
+msgstr "\90æ\93Ç\82Ý"
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr "\83p\83X\83\8f\81[\83\83T\81[\83o"
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr "smb passwd \83t\83@\83C\83\8b"
+
+#: param/loadparm.c:672
+#, fuzzy
+msgid "private dir"
+msgstr "\83v\83\8a\83\93\83\83h\83\89\83C\83o"
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr ""
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr "\83\8b\81[\83\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr "\83\8b\81[\83\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr "\83\8b\81[\83g"
+
+#: param/loadparm.c:678
+#, fuzzy
+msgid "pam password change"
+msgstr "\8dÅ\8f¬\83p\83X\83\8f\81[\83h\92·"
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr "\83p\83X\83\8f\81[\83\83v\83\8d\83O\83\89\83\80"
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr "\83p\83X\83\8f\81[\83\83`\83\83\83b\83g"
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr "\83p\83X\83\8f\81[\83\83`\83\83\83b\83\83f\83o\83b\83O"
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr "\83\86\81[\83U\96¼\83}\83b\83v"
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr "\83p\83X\83\8f\81[\83\83\8c\83x\83\8b"
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr "\83\86\81[\83U\96¼\83\8c\83x\83\8b"
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr "unix \83p\83X\83\8f\81[\83h\82ð\93¯\8aú\82³\82¹\82é"
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr "\93½\96¼\83A\83N\83Z\83X\82Ì\90§\8cÀ"
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr ""
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr "rhosts \82ð\8eg\82¤"
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr "\83\86\81[\83U\96¼"
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr "\83\86\81[\83U"
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr "\83\86\81[\83U"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr "\83Q\83X\83\83A\83J\83E\83\93\83g"
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr "\96³\8cø\82È\83\86\81[\83U"
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr "\97L\8cø\82È\83\86\81[\83U"
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr "\8aÇ\97\9d\83\86\81[\83U"
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr "\93Ç\82Ý\8eæ\82è\83\8a\83X\83g"
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr "\8f\91\82«\8d\9e\82Ý\83\8a\83X\83g"
+
+#: param/loadparm.c:702
+#, fuzzy
+msgid "printer admin"
+msgstr "\83v\83\8a\83\93\83^\96¼"
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr "\8b­\90§\82·\82é\83\86\81[\83U"
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr "\8b­\90§\82·\82é\83O\83\8b\81[\83v"
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr "\83O\83\8b\81[\83v"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr "\93Ç\82Ý\8eæ\82è\82Ì\82Ý"
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr "\8f\91\82«\8d\9e\82Ý\89Â"
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr "\8f\91\82«\8d\9e\82Ý\89Â"
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr "\8f\91\82«\8d\9e\82Ý\89Â"
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr "\8dì\90¬\8e\9e\82É\83}\83X\83N"
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr "\8dì\90¬\8e\9e\82Ì\83\82\81[\83h"
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr "\8b­\90§\82·\82é\8dì\90¬\8e\9e\82Ì\83\82\81[\83h"
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr "\83Z\83L\83\85\83\8a\83e\83\83}\83X\83N"
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr "\8b­\90§\82·\82é\83Z\83L\83\85\83\8a\83e\83\83\82\81[\83h"
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr "\83f\83B\83\8c\83N\83g\83\8a \83}\83X\83N"
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr "\83f\83B\83\8c\83N\83g\83\8a \83\82\81[\83h"
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr "\8b­\90§\82·\82é\83f\83B\83\8c\83N\83g\83\8a \83\82\81[\83h"
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr "\83f\83B\83\8c\83N\83g\83\8a\82Ì\83Z\83L\83\85\83\8a\83e\83\83}\83X\83N"
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr "\8b­\90§\82·\82é\83f\83B\83\8c\83N\83g\83\8a\82Ì\83Z\83L\83\85\83\8a\83e\83\83\82\81[\83h"
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr "\83p\81[\83~\83b\83V\83\87\83\93\82ð\8cp\8f³"
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr "\83Q\83X\83g\82Ì\82Ý"
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr "\83Q\83X\83g\82Ì\82Ý"
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr "\83Q\83X\83g\89Â"
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr "\83p\83u\83\8a\83b\83N"
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr "\83\86\81[\83U\82Ì\82Ý"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr "\8b\96\89Â\82·\82é\83z\83X\83g"
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr "\8b\96\89Â\82·\82é\83z\83X\83g"
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr "\8b\91\94Û\82·\82é\83z\83X\83g"
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr "\8b\91\94Û\82·\82é\83z\83X\83g"
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "\83Z\83L\83\85\83\83\\83P\83b\83\83\8c\83C\83A\81\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr "ssl"
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr "ssl \83z\83X\83g"
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr "ssl \96¢\8eg\97p\83z\83X\83g"
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr "ssl CA \94F\8fØ\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr "ssl CA \94F\8fØ\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr "ssl \83T\81[\83o\94F\8fØ"
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr "ssl \83T\81[\83o\8c®"
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr "ssl \83N\83\89\83C\83A\83\93\83g\94F\8fØ"
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr "ssl \83N\83\89\83C\83A\83\93\83g\8c®"
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr "ssl \83N\83\89\83C\83A\83\93\83g\94F\8fØ\82ð\97v\8b\81"
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr "ssl \83T\81[\83o\94F\8fØ\82ð\97v\8b\81"
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr "ssl \88Ã\8d\86"
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr "ssl \83o\81[\83W\83\87\83\93"
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr "ssl \8cÝ\8a·\90«"
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr "\83\8d\83M\83\93\83\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr "\83\8d\83\83\8c\83x\83\8b"
+
+#: param/loadparm.c:756
+#, fuzzy
+msgid "debuglevel"
+msgstr "\83f\83o\83b\83O\83\8c\83x\83\8b"
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr "syslog"
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr "syslog \82Ì\82Ý"
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr "\83\8d\83\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr "\8dÅ\91å\83\8d\83\83T\83C\83Y"
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr "\83^\83C\83\80\83X\83^\83\93\83\83\8d\83O"
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr "\83f\83o\83b\83\83^\83C\83\80\83X\83^\83\93\83v"
+
+#: param/loadparm.c:764
+#, fuzzy
+msgid "debug hires timestamp"
+msgstr "\83f\83o\83b\83\83^\83C\83\80\83X\83^\83\93\83v"
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr "\83f\83o\83b\83O pid"
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr "\83f\83o\83b\83O uid"
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr "\83v\83\8d\83g\83R\83\8b \83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr "\83v\83\8d\83g\83R\83\8b"
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+#, fuzzy
+msgid "max protocol"
+msgstr "\83v\83\8d\83g\83R\83\8b"
+
+#: param/loadparm.c:773
+#, fuzzy
+msgid "min protocol"
+msgstr "\83v\83\8d\83g\83R\83\8b"
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr "bmpx \93Ç\82Ý\8fo\82µ"
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr "raw \93Ç\82Ý\8fo\82µ"
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr "raw \8f\91\82«\8d\9e\82Ý"
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr "nt smb \83T\83|\81[\83g"
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr "nt pipe \83T\83|\81[\83g"
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr "nt acl \83T\83|\81[\83g"
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr "\83A\83i\83E\83\93\83\83o\81[\83W\83\87\83\93"
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr "\83A\83i\83E\83\93\83X\82·\82é\8eí\97Þ"
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr "\8dÅ\91å mux"
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr "\8dÅ\91å xmit"
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr "\96¼\91O\89ð\8c\88\82Ì\8f\87\94Ô"
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr "\8dÅ\91å\83p\83P\83b\83g"
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr "\83p\83P\83b\83\83T\83C\83Y"
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr "\8dÅ\91å ttl"
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr "\8dÅ\91å wins ttl"
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr "\8dÅ\8f¬ wins ttl"
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr "\83^\83C\83\80 \83T\81[\83o"
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "\83`\83\85\81[\83j\83\93\83\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr "\8dX\90V\92Ê\92m\82Ì\8aÔ\8au"
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr "\90Ø\92f\82Ü\82Å\82Ì\8e\9e\8aÔ"
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr "getwd \83L\83\83\83b\83V\83\85"
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr "lpq \83L\83\83\83b\83V\83\85\8e\9e\8aÔ"
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr ""
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr "\8dÅ\91å\90Ú\91±\90\94"
+
+#: param/loadparm.c:805
+#, fuzzy
+msgid "paranoid server security"
+msgstr "\83\86\81[\83U\92Ç\89Á\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr "\8dÅ\91å\83f\83B\83X\83\83T\83C\83Y"
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr "\8dÅ\91å\83t\83@\83C\83\8b \83I\81[\83v\83\93\90\94"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr "\8dÅ\8f¬\88ó\8dü\83X\83y\81[\83X"
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr "\93Ç\82Ý\8eæ\82è\83T\83C\83Y"
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr "\83\\\83P\83b\83\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr "stat \83L\83\83\83b\83V\83\85 \83T\83C\83Y"
+
+#: param/loadparm.c:813
+#, fuzzy
+msgid "strict allocate"
+msgstr "\8cµ\96§\82È\83\8d\83b\83N"
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr "\8cµ\96§\82È sync"
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr "\8fí\82É sync"
+
+#: param/loadparm.c:816
+#, fuzzy
+msgid "use mmap"
+msgstr "\83\86\81[\83U\96¼\83}\83b\83v"
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr "\8f\91\82«\8d\9e\82Ý\83L\83\83\83b\83V\83\85 \83T\83C\83Y"
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "\88ó\8dü\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:822
+#, fuzzy
+msgid "total print jobs"
+msgstr "\83v\83\8a\83\93\83^\82ð\83\8d\81[\83h"
+
+#: param/loadparm.c:823
+#, fuzzy
+msgid "max print jobs"
+msgstr "\88ó\8dü\89Â"
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr "\83v\83\8a\83\93\83^\82ð\83\8d\81[\83h"
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr "printcap \96¼"
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr "printcap"
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr "\88ó\8dü\89Â"
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr "\88ó\8dü\89Â"
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr "\83|\83X\83g\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr "\88ó\8dü\95û\96@"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr "\88ó\8dü\83R\83}\83\93\83h"
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr ""
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr "lpq \83R\83}\83\93\83h"
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr "lprm \83R\83}\83\93\83h"
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr "lppause \83R\83}\83\93\83h"
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr "lpresume \83R\83}\83\93\83h"
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr "\83L\83\85\81[\92â\8e~\83R\83}\83\93\83h"
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr "\83L\83\85\81[\8dÄ\8aJ\83R\83}\83\93\83h"
+
+#: param/loadparm.c:840
+#, fuzzy
+msgid "enumports command"
+msgstr "\88ó\8dü\83R\83}\83\93\83h"
+
+#: param/loadparm.c:841
+#, fuzzy
+msgid "addprinter command"
+msgstr "\88ó\8dü\83R\83}\83\93\83h"
+
+#: param/loadparm.c:842
+#, fuzzy
+msgid "deleteprinter command"
+msgstr "\88ó\8dü\83R\83}\83\93\83h"
+
+#: param/loadparm.c:843
+#, fuzzy
+msgid "show add printer wizard"
+msgstr "\83v\83\8a\83\93\83^\82ð\83\8d\81[\83h"
+
+#: param/loadparm.c:844
+#, fuzzy
+msgid "os2 driver map"
+msgstr "\83z\81[\83\80\83f\83B\83\8c\83N\83g\83\8a \83}\83b\83v"
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr "\83v\83\8a\83\93\83^\96¼"
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr "\83v\83\8a\83\93\83^"
+
+#: param/loadparm.c:848
+#, fuzzy
+msgid "use client driver"
+msgstr "ssl \83N\83\89\83C\83A\83\93\83g\94F\8fØ"
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr "\83v\83\8a\83\93\83\83h\83\89\83C\83o"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr "\83v\83\8a\83\93\83\83h\83\89\83C\83\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr "\83v\83\8a\83\93\83\83h\83\89\83C\83o\82Ì\8fê\8f\8a"
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "\83t\83@\83C\83\8b\96¼\82Ì\8eæ\88µ"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr "\83h\83b\83g\82ð\8dí\82é"
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr "\96¼\91O\95Ï\8a·\97p\83X\83^\83b\83N"
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr "\8aù\92è\82Ì\95\8e\9a\82Ì\91å\8f¬"
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr "\91å/\8f¬\95\8e\9a\82Ì\8bæ\95Ê"
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr "\91å/\8f¬\95\8e\9a\82Ì\8bæ\95Ê"
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr "\95\8e\9a\82Ì\91å\8f¬\82ð\95Û\91¶"
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr "\92Z\8c`\8e®\82Å\95\8e\9a\82Ì\91å\8f¬\82ð\95Û\91¶"
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr "\91å/\8f¬\95\8e\9a\82Ì\95Ï\8a·"
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr "\95Ï\8a·\97p\95\8e\9a"
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr "\83h\83b\83g\83t\83@\83C\83\8b\82ð\89B\82·"
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr "\8b\91\94Û\83t\83@\83C\83\8b\82ð\8dí\8f\9c"
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr "\8b\91\94Û\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr "\89B\82µ\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr "oplock \82ð\8bÖ\8e~\82·\82é\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr "\83V\83X\83e\83\80\91®\90«\82É\83}\83b\83v"
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr "\89B\82µ\91®\90«\82É\83}\83b\83v"
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr "\83A\81[\83J\83C\83u\91®\90«\82É\83}\83b\83v"
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr "\95Ï\8a·\82µ\82½\96¼\91O\82Å\95\\8e¦"
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr "\95Ï\8a·\83}\83b\83v"
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr "stat \83L\83\83\83b\83V\83\85"
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "\83h\83\81\83C\83\93 \83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr "\83h\83\81\83C\83\93\8aÇ\97\9d\83O\83\8b\81[\83v"
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr "\83h\83\81\83C\83\93 \83Q\83X\83\83O\83\8b\81[\83v"
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr "\83O\83\8b\81[\83v\96¼\83}\83b\83v"
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr "\83}\83V\83\93 \83p\83X\83\8f\81[\83\83^\83C\83\80\83A\83E\83g"
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "\83\8d\83O\83I\83\93 \83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr "\83\86\81[\83U\92Ç\89Á\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr "\83\86\81[\83U\8dí\8f\9c\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:892
+#, fuzzy
+msgid "add group script"
+msgstr "\83\86\81[\83U\92Ç\89Á\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:893
+#, fuzzy
+msgid "delete group script"
+msgstr "\83\86\81[\83U\8dí\8f\9c\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:894
+#, fuzzy
+msgid "add user to group script"
+msgstr "\83\86\81[\83U\92Ç\89Á\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:895
+#, fuzzy
+msgid "delete user from group script"
+msgstr "\83\86\81[\83U\8dí\8f\9c\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:896
+#, fuzzy
+msgid "add machine script"
+msgstr "\83\86\81[\83U\92Ç\89Á\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:897
+#, fuzzy
+msgid "shutdown script"
+msgstr "\83\8d\83O\83I\83\93 \83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:898
+#, fuzzy
+msgid "abort shutdown script"
+msgstr "\83\8d\83O\83I\83\93 \83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr "\83\8d\83O\83I\83\93 \83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr "\83\8d\83O\83I\83\93 \83p\83X"
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr "\83\8d\83O\83I\83\93 \83h\83\89\83C\83u"
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr "\83\8d\83O\83I\83\93 \83z\81[\83\80"
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr "\83h\83\81\83C\83\93 \83\8d\83O\83I\83\93"
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "\83R\83\93\83s\83\85\81[\83^\88ê\97\97\95\\8e¦\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr "os \83\8c\83x\83\8b"
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr "lm \83A\83i\83E\83\93\83X"
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr "lm \8aÔ\8au"
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr "\97D\90æ\82·\82é\83}\83X\83^"
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr "\97D\90æ\82·\82é\83}\83X\83^"
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr "\83\8d\81[\83J\83\8b \83}\83X\83^"
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr "\83h\83\81\83C\83\93 \83}\83X\83^"
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr "\83u\83\89\83E\83\83\8a\83X\83g"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr "\83u\83\89\83E\83Y\89Â"
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr "\83u\83\89\83E\83Y\89Â"
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr ""
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "WINS\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr "dns \83v\83\8d\83L\83V"
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr "wins \83v\83\8d\83L\83V"
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr "wins \83T\81[\83o"
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr "wins \83T\83|\81[\83g"
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr "wins \83t\83b\83N"
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "\83\8d\83b\83L\83\93\83\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:930
+#, fuzzy
+msgid "blocking locks"
+msgstr "\83\8d\83b\83N"
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr "\8bU\91\95 oplock"
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr "\83J\81[\83l\83\8b oplock"
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr "\83\8d\83b\83N"
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr "\95Ö\8bX\93I\83\8d\83b\83N"
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr "level2 oplocks"
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr "oplock \92\86\92f\82Ì\91Ò\82¿\8e\9e\8aÔ"
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr "oplock \8b£\8d\87\82Ì\8cÀ\93x"
+
+#: param/loadparm.c:939
+#, fuzzy
+msgid "posix locking"
+msgstr "\8cµ\96§\82È\83\8d\83b\83N"
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr "\8cµ\96§\82È\83\8d\83b\83N"
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr "\8b¤\97L\83\82\81[\83h"
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "Ldap \83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr "ldap \83T\81[\83o"
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr "ldap \83|\81[\83g"
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr "lpad \83T\83t\83B\83b\83N\83X"
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr "ldap \83t\83B\83\8b\83^\81["
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr "ldap \83\8b\81[\83g"
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr "ldap \83\8b\81[\83\83p\83X\83\8f\81[\83h"
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "\82»\82Ì\91¼\82Ì\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:955
+#, fuzzy
+msgid "add share command"
+msgstr "dfree \83R\83}\83\93\83h"
+
+#: param/loadparm.c:956
+#, fuzzy
+msgid "change share command"
+msgstr "\83\81\83b\83Z\81[\83\83R\83}\83\93\83h"
+
+#: param/loadparm.c:957
+#, fuzzy
+msgid "delete share command"
+msgstr "\83\81\83b\83Z\81[\83\83R\83}\83\93\83h"
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr "\90Ý\92è\83t\83@\83C\83\8b"
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr "\83v\83\8a\83\8d\81[\83h"
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr "\8e©\93®\83T\81[\83r\83X"
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr "\83\8d\83b\83\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr "\83\8d\83b\83\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr "utmp \83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:966
+#, fuzzy
+msgid "wtmp directory"
+msgstr "utmp \83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:967
+#, fuzzy
+msgid "utmp"
+msgstr "utmp \83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr "\8aù\92è\83T\81[\83r\83X"
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr "\8aù\92è"
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr "\83\81\83b\83Z\81[\83\83R\83}\83\93\83h"
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr "dfree \83R\83}\83\93\83h"
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr "\83\8a\83\82\81[\83\83A\83i\83E\83\93\83X"
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr "\83\8a\83\82\81[\83g\82Ì\83u\83\89\83E\83Y\83\8a\83X\83g\82ð\93¯\8aú"
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr "\83\\\83P\83b\83\83A\83h\83\8c\83X"
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr "\83z\81[\83\80\83f\83B\83\8c\83N\83g\83\8a \83}\83b\83v"
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr "\8e\9e\8aÔ\83I\83t\83Z\83b\83g"
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr "NIS \83z\81[\83\80\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr "-valid"
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr "\83R\83s\81["
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr "\83C\83\93\83N\83\8b\81[\83h"
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr "\8eÀ\8ds"
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr "\90Ú\91±\8e\9e\82É\8eÀ\8ds"
+
+#: param/loadparm.c:987
+#, fuzzy
+msgid "preexec close"
+msgstr "\90Ú\91±\8e\9e\82É\8eÀ\8ds"
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr "\90Ø\92f\8e\9e\82É\8eÀ\8ds"
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr "\83\8b\81[\83g\82Å\90Ú\91±\8e\9e\8eÀ\8ds"
+
+#: param/loadparm.c:990
+#, fuzzy
+msgid "root preexec close"
+msgstr "\83\8b\81[\83g\82Å\90Ú\91±\8e\9e\8eÀ\8ds"
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr "\83\8b\81[\83g\82Å\90Ø\92f\8e\9e\8eÀ\8ds"
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr "\97\98\97p\89Â\94\"
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr "\83{\83\8a\83\85\81[\83\80"
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr "\83t\83@\83C\83\8b \83V\83X\83e\83\80 \83^\83C\83v"
+
+#: param/loadparm.c:995
+#, fuzzy
+msgid "set directory"
+msgstr "\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr "\8dL\82­\83\8a\83\93\83N"
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr "symlink \90æ\82ð\8eQ\8fÆ"
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr "\89º\82É\8d~\82è\82È\82¢\83f\83B\83\8c\83N\83g\83\8a"
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr "\83}\83W\83b\83\83X\83N\83\8a\83v\83g"
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr "\83}\83W\83b\83\8fo\97Í"
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr "\93Ç\82Ý\8eæ\82è\82Ì\82Ý\82Ì\83t\83@\83C\83\8b\82ð\8dí\8f\9c"
+
+#: param/loadparm.c:1003
+#, fuzzy
+msgid "dos filemode"
+msgstr "dos \82Ì\83t\83@\83C\83\8b\8e\9e\8d\8f"
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr "dos \82Ì\83t\83@\83C\83\8b\8e\9e\8d\8f"
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr "dos \82Ì\83t\83@\83C\83\8b\8e\9e\8d\8f\82Ì\95ª\89ð\94\"
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr "\8bU\82Ì\83f\83B\83\8c\83N\83g\83\8a\8dì\90¬\8e\9e\8d\8f"
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr "\83p\83j\83b\83\83A\83N\83V\83\87\83\93"
+
+#: param/loadparm.c:1009
+#, fuzzy
+msgid "hide local users"
+msgstr "\83\8d\81[\83J\83\8b \83}\83X\83^"
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr "VFS\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+#, fuzzy
+msgid "vfs options"
+msgstr "VFS\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1018
+#, fuzzy
+msgid "msdfs root"
+msgstr "ldap \83\8b\81[\83g"
+
+#: param/loadparm.c:1019
+#, fuzzy
+msgid "host msdfs"
+msgstr "\8b\91\94Û\82·\82é\83z\83X\83g"
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr "Winbind\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1023
+#, fuzzy
+msgid "winbind uid"
+msgstr "Winbind\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1024
+#, fuzzy
+msgid "winbind gid"
+msgstr "Winbind\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+#, fuzzy
+msgid "winbind separator"
+msgstr "Winbind\83I\83v\83V\83\87\83\93"
+
+#: param/loadparm.c:1028
+#, fuzzy
+msgid "winbind cache time"
+msgstr "lpq \83L\83\83\83b\83V\83\85\8e\9e\8aÔ"
+
+#: param/loadparm.c:1029
+#, fuzzy
+msgid "winbind enum users"
+msgstr "\96³\8cø\82È\83\86\81[\83U"
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
+
+#~ msgid "failed to open %s for writing\n"
+#~ msgstr "%s \82ð\8f\91\82«\8d\9e\82Ý\97p\82É\83I\81[\83v\83\93\82Å\82«\82Ü\82¹\82ñ\n"
+
+#~ msgid "Can't reload %s\n"
+#~ msgstr "%s \82ð\8dÄ\93Ç\82Ý\8d\9e\82Ý\82Å\82«\82Ü\82¹\82ñ\n"
+
+#~ msgid "Can't setup password database vectors.\n"
+#~ msgstr "\83p\83X\83\8f\81[\83h\81E\83f\81[\83^\83x\81[\83X\82ª\8c©\82Â\82¯\82ç\82ê\82Ü\82¹\82ñ\n"
+
+#~ msgid "You need to have status=yes in your smb config file\n"
+#~ msgstr "smb.conf \82Å status=yes \82ð\90Ý\92è\82µ\82Ä\82­\82¾\82³\82¢\n"
+
+#~ msgid "coding system"
+#~ msgstr "\83R\81[\83f\83B\83\93\83\83V\83X\83e\83\80"
+
+#~ msgid "client code page"
+#~ msgstr "\83N\83\89\83C\83A\83\93\83\83R\81[\83h\83y\81[\83W"
+
+#~ msgid "revalidate"
+#~ msgstr "\8dÄ\94F\8fØ"
+
+#~ msgid "status"
+#~ msgstr "\83X\83e\81[\83^\83X"
+
+#~ msgid "shared mem size"
+#~ msgstr "\8b¤\97L\83\81\83\82\83\8a \83T\83C\83Y"
+
+#~ msgid "character set"
+#~ msgstr "\95\8e\9a\83Z\83b\83g"
+
+#~ msgid "domain groups"
+#~ msgstr "\83h\83\81\83C\83\93 \83O\83\8b\81[\83v"
+
+#~ msgid "domain admin users"
+#~ msgstr "\83h\83\81\83C\83\93\8aÇ\97\9d\83\86\81[\83U"
+
+#~ msgid "domain guest users"
+#~ msgstr "\83h\83\81\83C\83\93 \83Q\83X\83\83\86\81[\83U"
+
+#~ msgid "ole locking compatibility"
+#~ msgstr "ole \83\8d\83b\83N\82Ì\8cÝ\8a·\90«"
+
+#~ msgid "smbrun"
+#~ msgstr "smbrun"
+
+#, fuzzy
+#~ msgid "wtmp dir"
+#~ msgstr "utmp \83f\83B\83\8c\83N\83g\83\8a"
+
+#~ msgid "unix realname"
+#~ msgstr "unix \82Ì\96{\96¼"
diff --git a/source4/po/pl.msg b/source4/po/pl.msg
new file mode 100644 (file)
index 0000000..c547a94
--- /dev/null
@@ -0,0 +1,1773 @@
+# Polish messages for international release of SWAT.
+# Copyright (C) 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+#
+#   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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-09-20 20:29+0900\n"
+"PO-Revision-Date: 2001-08-15 22:45+02:00\n"
+"Last-Translator: Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>\n"
+"Language-Team: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-2\n"
+"Content-Transfer-Encoding: \n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "B£¡D: Nie mo¿na otworzyæ %s\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "Pomoc"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "Ustaw domy¶lnie"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "Zalogowany jako <b>%s</b><p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "Strona domowa"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "Ustawienia globalne"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "Wspó³udzia³y"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "Drukarki"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "Status"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "Przejrzyj Konfiguracjê"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "Zarz±dzanie Has³ami"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "Bie¿±ca Konfiguracja"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "Normalny Widok"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "Pe³ny Widok"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "Zmienne Globalne"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "Potwierd¼ Zmiany"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "Zresetuj Warto¶ci"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "Widok Zaawansowany"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "Widok Podstawowy"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "Parametry Wspó³udzia³u"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "Wybierz Wspó³udzia³"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "Usuñ Wspó³udzia³"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "Utwórz Wspó³udzia³"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "zmiana has³a w trybie demo odrzucona\n"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr " Musisz podaæ \"Nazwê U¿ytkownika\" \n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr " Musisz podaæ \"Stare Has³o\" \n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr " Musisz podaæ \"Zdaln± Maszynê\" \n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr " Musisz podaæ \"Nowe Has³o, i ponownie wpisane Nowe Has³o\" \n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr " Ponownie wpisane has³o nie pasuje do nowego has³a\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " Has³o dla '%s' zosta³o zmienione. \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr " Has³o dla '%s' NIE zosta³o zmienione. \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "Zarz±dzanie Has³ami na Serwerze"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr " Nazwa U¿ytkownika : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr " Stare Has³o : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr " Nowe Has³o : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr " Ponownie wpisz Nowe Has³o : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "Zmieñ Has³o"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "Dodaj Nowego U¿ytkownika"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "Usuñ U¿ytkownika"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "Zablokuj U¿ytkownika"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "Odblokuj U¿ytkownika"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "Zarz±dzanie Has³ami Klient/Serwer"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " Zdalna Maszyna : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "Parametry Drukarki"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "Wa¿na Informacja:"
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Nazwy Drukarek zaznaczone [*] w rozwijanym polu Wybierz Drukarkê "
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "s± drukarkami automatycznie ³adowanymi z "
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "Nazwa Printcap"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "Próby usuniêcia tych drukarek ze SWAT nie przynios± efektu.\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "Wybierz Drukarkê"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "Usuñ Drukarkê"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "Utwórz Drukarkê"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr ""
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr ""
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr ""
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr ""
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr ""
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr ""
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr ""
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr ""
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr ""
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr ""
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr ""
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr ""
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr ""
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "Status Serwera"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "Automatyczne Od¶wie¿anie"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "Interwa³ Od¶wie¿ania: "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "Zatrzymaj Od¶wie¿anie"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "wersja:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr ""
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "dzia³a"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "nie dzia³a"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "Zatrzymaj smbd"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "Uruchom smbd"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "Zrestartuj smbd"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr ""
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "Zatrzymaj nmbd"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "Uruchom nmbd"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "Zrestartuj nmbd"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "Aktywne Po³±czenia"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr ""
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr "Klient"
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "adres IP"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "Data"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "Zatrzymaj"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "Aktywne Wspó³udzia³y"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "Wspó³udzia³"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "U¿ytkownik"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "Grupa"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "Otwarte Pliki"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr "Wspó³dzielenie"
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr ""
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr "Plik"
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "Bazowe Opcje"
+
+#: param/loadparm.c:643
+#, fuzzy
+msgid "dos charset"
+msgstr "Wybierz Wspó³udzia³"
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr ""
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr ""
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr ""
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr ""
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr ""
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr ""
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr ""
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr ""
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr ""
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr ""
+
+#: param/loadparm.c:654
+#, fuzzy
+msgid "interfaces"
+msgstr "Drukarki"
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr ""
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "Opcje Zabezpieczeñ"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr ""
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr ""
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr ""
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr ""
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr ""
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr ""
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr ""
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr ""
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr ""
+
+#: param/loadparm.c:668
+#, fuzzy
+msgid "null passwords"
+msgstr "Zmieñ Has³o"
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr ""
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr ""
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr ""
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr ""
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr ""
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr ""
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr ""
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr ""
+
+#: param/loadparm.c:678
+#, fuzzy
+msgid "pam password change"
+msgstr "Zarz±dzanie Has³ami"
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr ""
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr ""
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr ""
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr ""
+
+#: param/loadparm.c:683
+#, fuzzy
+msgid "password level"
+msgstr "Zarz±dzanie Has³ami"
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr ""
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr ""
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr ""
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr ""
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr ""
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr ""
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr ""
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr ""
+
+#: param/loadparm.c:693
+#, fuzzy
+msgid "user"
+msgstr "U¿ytkownik"
+
+#: param/loadparm.c:694
+#, fuzzy
+msgid "users"
+msgstr "U¿ytkownik"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr ""
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr ""
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr ""
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr ""
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr ""
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr ""
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr ""
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr ""
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr ""
+
+#: param/loadparm.c:705
+#, fuzzy
+msgid "group"
+msgstr "Grupa"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr ""
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr ""
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr ""
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr ""
+
+#: param/loadparm.c:712
+#, fuzzy
+msgid "create mask"
+msgstr "Utwórz Wspó³udzia³"
+
+#: param/loadparm.c:713
+#, fuzzy
+msgid "create mode"
+msgstr "Utwórz Wspó³udzia³"
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr ""
+
+#: param/loadparm.c:715
+#, fuzzy
+msgid "security mask"
+msgstr "Opcje Zabezpieczeñ"
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr ""
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr ""
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr ""
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr ""
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr ""
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr ""
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr ""
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr ""
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr ""
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr ""
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr ""
+
+#: param/loadparm.c:729
+#, fuzzy
+msgid "only user"
+msgstr "Odblokuj U¿ytkownika"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr ""
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr ""
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr ""
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr ""
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "Opcje SSL"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr ""
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr ""
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr ""
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr ""
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr ""
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr ""
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr ""
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr ""
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr ""
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr ""
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr ""
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr ""
+
+#: param/loadparm.c:750
+#, fuzzy
+msgid "ssl version"
+msgstr "wersja:"
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr ""
+
+#: param/loadparm.c:754
+#, fuzzy
+msgid "Logging Options"
+msgstr "Opcje Blokowania"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr ""
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr ""
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr ""
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr ""
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr ""
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr ""
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr ""
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr ""
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr ""
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr ""
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr ""
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr "Opcje Protoko³u"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr ""
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr ""
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr ""
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr ""
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr ""
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr ""
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr ""
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr ""
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr ""
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr ""
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr ""
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr ""
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr ""
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr ""
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr ""
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr ""
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr ""
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr ""
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr ""
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr ""
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr ""
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr ""
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "Opcje Dostrajaj±ce"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr ""
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr ""
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr ""
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr ""
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr ""
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr ""
+
+#: param/loadparm.c:804
+#, fuzzy
+msgid "max connections"
+msgstr "Aktywne Po³±czenia"
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr ""
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr ""
+
+#: param/loadparm.c:807
+#, fuzzy
+msgid "max open files"
+msgstr "Otwarte Pliki"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr ""
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr ""
+
+#: param/loadparm.c:811
+#, fuzzy
+msgid "socket options"
+msgstr "Bazowe Opcje"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr ""
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr ""
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr ""
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr ""
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr ""
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr ""
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr ""
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "Opcje Drukowania"
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr ""
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr ""
+
+#: param/loadparm.c:824
+#, fuzzy
+msgid "load printers"
+msgstr "Drukarki"
+
+#: param/loadparm.c:825
+#, fuzzy
+msgid "printcap name"
+msgstr "Nazwa Printcap"
+
+#: param/loadparm.c:826
+#, fuzzy
+msgid "printcap"
+msgstr "Nazwa Printcap"
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr ""
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr ""
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr ""
+
+#: param/loadparm.c:830
+#, fuzzy
+msgid "printing"
+msgstr "dzia³a"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr ""
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr ""
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr ""
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr ""
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr ""
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr ""
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr ""
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr ""
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr ""
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr ""
+
+#: param/loadparm.c:842
+#, fuzzy
+msgid "deleteprinter command"
+msgstr "Usuñ Drukarkê"
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr ""
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr ""
+
+#: param/loadparm.c:846
+#, fuzzy
+msgid "printer name"
+msgstr "Parametry Drukarki"
+
+#: param/loadparm.c:847
+#, fuzzy
+msgid "printer"
+msgstr "Drukarki"
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr ""
+
+#: param/loadparm.c:849
+#, fuzzy
+msgid "printer driver"
+msgstr "Parametry Drukarki"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr ""
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr ""
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "Obs³uga Nazw Plików"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr ""
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr ""
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr ""
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr ""
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr ""
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr ""
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr ""
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr ""
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr ""
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr ""
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr ""
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr ""
+
+#: param/loadparm.c:867
+#, fuzzy
+msgid "veto files"
+msgstr "Otwarte Pliki"
+
+#: param/loadparm.c:868
+#, fuzzy
+msgid "hide files"
+msgstr "Otwarte Pliki"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr ""
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr ""
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr ""
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr ""
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr ""
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr ""
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr ""
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "Opcje Domeny"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr ""
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr ""
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr ""
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr ""
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "Opcje Logowania"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr ""
+
+#: param/loadparm.c:891
+#, fuzzy
+msgid "delete user script"
+msgstr "Usuñ U¿ytkownika"
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr ""
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr ""
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr ""
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr ""
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr ""
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr ""
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr ""
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr ""
+
+#: param/loadparm.c:901
+#, fuzzy
+msgid "logon path"
+msgstr "Opcje Logowania"
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr ""
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr ""
+
+#: param/loadparm.c:904
+#, fuzzy
+msgid "domain logons"
+msgstr "Opcje Domeny"
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "Opcje Przegl±dania"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr ""
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr ""
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr ""
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr ""
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr ""
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr ""
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr ""
+
+#: param/loadparm.c:915
+#, fuzzy
+msgid "browse list"
+msgstr "Opcje Przegl±dania"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr ""
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr ""
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr ""
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "Opcje WINS"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr ""
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr ""
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr ""
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr ""
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr ""
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "Opcje Blokowania"
+
+#: param/loadparm.c:930
+#, fuzzy
+msgid "blocking locks"
+msgstr "Opcje Blokowania"
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr ""
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr ""
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr ""
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr ""
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr ""
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr ""
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr ""
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr ""
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr ""
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr ""
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "Opcje Ldap"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr ""
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr ""
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr ""
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr ""
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr ""
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr ""
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "Pozosta³e Opcje"
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr ""
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr ""
+
+#: param/loadparm.c:957
+#, fuzzy
+msgid "delete share command"
+msgstr "Usuñ Wspó³udzia³"
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr ""
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr ""
+
+#: param/loadparm.c:961
+#, fuzzy
+msgid "auto services"
+msgstr "Automatyczne Od¶wie¿anie"
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr ""
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr ""
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr ""
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr ""
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr ""
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr ""
+
+#: param/loadparm.c:971
+#, fuzzy
+msgid "default"
+msgstr "Ustaw domy¶lnie"
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr ""
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr ""
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr ""
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr ""
+
+#: param/loadparm.c:976
+#, fuzzy
+msgid "socket address"
+msgstr "adres IP"
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr ""
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr ""
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr ""
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr ""
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr ""
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr ""
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr ""
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr ""
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr ""
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr ""
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr ""
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr ""
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr ""
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr ""
+
+#: param/loadparm.c:993
+#, fuzzy
+msgid "volume"
+msgstr "Strona domowa"
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr ""
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr ""
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr ""
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr ""
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr ""
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr ""
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr ""
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr ""
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr ""
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr ""
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr ""
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr ""
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr ""
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr ""
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr ""
+
+#: param/loadparm.c:1012
+#, fuzzy
+msgid "VFS options"
+msgstr "Opcje WINS"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr ""
+
+#: param/loadparm.c:1015
+#, fuzzy
+msgid "vfs options"
+msgstr "Bazowe Opcje"
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr ""
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr ""
+
+#: param/loadparm.c:1021
+#, fuzzy
+msgid "Winbind options"
+msgstr "Opcje Drukowania"
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr ""
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr ""
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr ""
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr ""
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr ""
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr ""
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr ""
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr ""
+
+#~ msgid "failed to open %s for writing\n"
+#~ msgstr "nie uda³o siê otworzyæ %s do zapisu\n"
+
+#~ msgid "Can't reload %s\n"
+#~ msgstr "Nie mogê prze³adowaæ %s\n"
+
+#~ msgid "Can't setup password database vectors.\n"
+#~ msgstr "Nie mo¿na ustawiæ wektorów bazy hase³.\n"
+
+#~ msgid "You need to have status=yes in your smb config file\n"
+#~ msgstr "Musisz mieæ status=yes w swoim pliku konfiguracyjnym smb\n"
diff --git a/source4/po/tr.msg b/source4/po/tr.msg
new file mode 100644 (file)
index 0000000..6c2bc1f
--- /dev/null
@@ -0,0 +1,1723 @@
+# Swat Turkish Translation
+# Copyright (C) 2001 Deniz Akkus Kanca
+# Deniz Akkus Kanca <deniz@arayan.com>, 2001.
+#
+#   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.
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2001-09-20 20:29+0900\n"
+"PO-Revision-Date: 2001-09-20 22:51EEST\n"
+"Last-Translator: Deniz Akkus Kanca <deniz@arayan.com>\n"
+"Language-Team: Turkish <gnu-tr-u12a@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 0.9.1\n"
+
+#: web/swat.c:120
+#, c-format
+msgid "ERROR: Can't open %s\n"
+msgstr "HATA: %s açýlamadý\n"
+
+#.
+#. str = stripspace(parm->label);
+#. strlower (str); //monyo
+#. d_printf("<tr><td><A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\">%s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s</td><td>",
+#. str, _("Help"), parm->label);
+#.
+#: web/swat.c:211
+msgid "Help"
+msgstr "Yardým"
+
+#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263
+#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297
+msgid "Set Default"
+msgstr "Öntanýmlýya Ayarla"
+
+#: web/swat.c:502
+#, c-format
+msgid "Logged in as <b>%s</b><p>\n"
+msgstr "<b>%s</b> kimliði ile oturum açýlmýþ<p>\n"
+
+#: web/swat.c:505
+msgid "Home"
+msgstr "Ev"
+
+#: web/swat.c:507
+msgid "Globals"
+msgstr "Evrenseller"
+
+#: web/swat.c:508
+msgid "Shares"
+msgstr "Paylaþýmlar"
+
+#: web/swat.c:509
+msgid "Printers"
+msgstr "Yazýcýlar"
+
+#: web/swat.c:512
+msgid "Status"
+msgstr "Durum"
+
+#: web/swat.c:513
+msgid "View Config"
+msgstr "Ayarlara Gözat"
+
+#: web/swat.c:515
+msgid "Password Management"
+msgstr "Þifre Yönetimi"
+
+#: web/swat.c:539
+msgid "Current Config"
+msgstr "Þimdiki Ayarlar"
+
+#: web/swat.c:543
+msgid "Normal View"
+msgstr "Normal Görünüm"
+
+#: web/swat.c:545
+msgid "Full View"
+msgstr "Tam Görünüm"
+
+#: web/swat.c:561
+msgid "Global Variables"
+msgstr "Genel Deðiþkenler"
+
+#: web/swat.c:575 web/swat.c:671 web/swat.c:1014
+msgid "Commit Changes"
+msgstr "Deðiþiklikleri Kaydet"
+
+#: web/swat.c:579 web/swat.c:674 web/swat.c:1016
+msgid "Reset Values"
+msgstr "Deðerleri Ýlk Haline Getir"
+
+#: web/swat.c:581 web/swat.c:676 web/swat.c:1018
+msgid "Advanced View"
+msgstr "Geliþmiþ Görünüm"
+
+#: web/swat.c:583 web/swat.c:678 web/swat.c:1020
+msgid "Basic View"
+msgstr "Temel Görünüm"
+
+#: web/swat.c:613
+msgid "Share Parameters"
+msgstr "Paylaþým Parametreleri"
+
+#: web/swat.c:642
+msgid "Choose Share"
+msgstr "Paylaþým Seçin"
+
+#: web/swat.c:656
+msgid "Delete Share"
+msgstr "Paylaþým Kaldýr"
+
+#: web/swat.c:663
+msgid "Create Share"
+msgstr "Paylaþým Oluþtur"
+
+#: web/swat.c:708
+msgid "password change in demo mode rejected\n"
+msgstr "demo kipinde þifre deðiþikliði kabul edilmedi\n"
+
+#: web/swat.c:747
+msgid " Must specify \"User Name\" \n"
+msgstr " \"Kullanýcý Adý\" belirtilmeli \n"
+
+#: web/swat.c:763
+msgid " Must specify \"Old Password\" \n"
+msgstr " \"Eski Þifre\" belirtilmeli \n"
+
+#: web/swat.c:769
+msgid " Must specify \"Remote Machine\" \n"
+msgstr " \"Uzak Makina\" belirtilmeli \n"
+
+#: web/swat.c:776
+msgid " Must specify \"New, and Re-typed Passwords\" \n"
+msgstr " \"Yeni ve Tekrar Girilmiþ Þifreler\" belirtilmeli \n"
+
+#: web/swat.c:782
+msgid " Re-typed password didn't match new password\n"
+msgstr " Tekrar girilen þifre yeni þifre ile eþleþmedi\n"
+
+#: web/swat.c:812
+#, c-format
+msgid " The passwd for '%s' has been changed. \n"
+msgstr " '%s' için þifre deðiþtirildi. \n"
+
+#: web/swat.c:814
+#, c-format
+msgid " The passwd for '%s' has NOT been changed. \n"
+msgstr " '%s' için þifre DEÐÝÞTÝRÝLMEDÝ. \n"
+
+#: web/swat.c:838
+msgid "Server Password Management"
+msgstr "Sunucu Þifre Yönetimi"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: web/swat.c:847 web/swat.c:894
+msgid " User Name : "
+msgstr " Kullanýcý Adý : "
+
+#: web/swat.c:850 web/swat.c:896
+msgid " Old Password : "
+msgstr " Eski Þifre : "
+
+#: web/swat.c:853 web/swat.c:898
+msgid " New Password : "
+msgstr " Yeni Þifre : "
+
+#: web/swat.c:855 web/swat.c:900
+msgid " Re-type New Password : "
+msgstr " Yeni Þifre Tekrarý : "
+
+#: web/swat.c:863 web/swat.c:911
+msgid "Change Password"
+msgstr "Þifre Deðiþtir"
+
+#: web/swat.c:866
+msgid "Add New User"
+msgstr "Kull. Ekle"
+
+#: web/swat.c:868
+msgid "Delete User"
+msgstr "Kull. Sil"
+
+#: web/swat.c:870
+msgid "Disable User"
+msgstr "Kull. Etkisizleþtir"
+
+#: web/swat.c:872
+msgid "Enable User"
+msgstr "Kull. Etkinleþtir"
+
+#: web/swat.c:885
+msgid "Client/Server Password Management"
+msgstr "Ýstemci/Sunucu Þifre Yönetimi"
+
+#: web/swat.c:902
+msgid " Remote Machine : "
+msgstr " Uzak Makina : "
+
+#: web/swat.c:940
+msgid "Printer Parameters"
+msgstr "Yazýcý Bilgileri"
+
+#: web/swat.c:942
+msgid "Important Note:"
+msgstr "Önemli Not: "
+
+#: web/swat.c:943
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Yazýcý Seç kutusunda [*] ile iþaretlenmiþ yazýcý isimleri "
+
+#: web/swat.c:944
+msgid "are autoloaded printers from "
+msgstr "otomatik yüklenen yazýcýlar "
+
+#: web/swat.c:945
+msgid "Printcap Name"
+msgstr "Printcap Adý"
+
+#: web/swat.c:946
+msgid "Attempting to delete these printers from SWAT will have no effect.\n"
+msgstr "Bu yazýcýlarý SWAT'dan silmek etkisiz olacaktýr.\n"
+
+#: web/swat.c:980
+msgid "Choose Printer"
+msgstr "Yazýcý Seç"
+
+#: web/swat.c:999
+msgid "Delete Printer"
+msgstr "Yazýcý Sil"
+
+#: web/swat.c:1006
+msgid "Create Printer"
+msgstr "Yazýcý Oluþtur"
+
+#: web/statuspage.c:40
+msgid "DENY_NONE"
+msgstr "HERKESE_AÇIK"
+
+#: web/statuspage.c:41
+msgid "DENY_ALL   "
+msgstr "HERKESÝ_REDDET   "
+
+#: web/statuspage.c:42
+msgid "DENY_DOS   "
+msgstr "DOSU_REDDET   "
+
+#: web/statuspage.c:43
+msgid "DENY_READ  "
+msgstr "OKU_REDDET  "
+
+#: web/statuspage.c:44
+msgid "DENY_WRITE "
+msgstr "YAZ_REDDET "
+
+#: web/statuspage.c:50
+msgid "RDONLY     "
+msgstr "SALTOKUNUR    "
+
+#: web/statuspage.c:51
+msgid "WRONLY     "
+msgstr "SALTYAZILIR    "
+
+#: web/statuspage.c:52
+msgid "RDWR       "
+msgstr "O/Y        "
+
+#: web/statuspage.c:60
+msgid "EXCLUSIVE+BATCH "
+msgstr "ÞAHSÝ+TOPTAN "
+
+#: web/statuspage.c:62
+msgid "EXCLUSIVE       "
+msgstr "ÞAHSÝ         "
+
+#: web/statuspage.c:64
+msgid "BATCH           "
+msgstr "TOPTAN        "
+
+#: web/statuspage.c:66
+msgid "LEVEL_II        "
+msgstr "SEVÝYE_II        "
+
+#: web/statuspage.c:68
+msgid "NONE            "
+msgstr "HÝÇ             "
+
+#: web/statuspage.c:195
+msgid "Server Status"
+msgstr "Sunucu Durumu"
+
+#: web/statuspage.c:200
+msgid "Auto Refresh"
+msgstr "Oto Tazele"
+
+#: web/statuspage.c:201 web/statuspage.c:206
+msgid "Refresh Interval: "
+msgstr "Tazeleme Aralýðý: "
+
+#: web/statuspage.c:205
+msgid "Stop Refreshing"
+msgstr "Tazelemeyi Durdur"
+
+#: web/statuspage.c:220
+msgid "version:"
+msgstr "sürüm:"
+
+#: web/statuspage.c:223
+msgid "smbd:"
+msgstr "smbd:"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "running"
+msgstr "çalýþýyor"
+
+#: web/statuspage.c:223 web/statuspage.c:235
+msgid "not running"
+msgstr "çalýþmýyor"
+
+#: web/statuspage.c:226
+msgid "Stop smbd"
+msgstr "Smbd'yi durdur"
+
+#: web/statuspage.c:228
+msgid "Start smbd"
+msgstr "Smbd'yi çalýþtýr"
+
+#: web/statuspage.c:230
+msgid "Restart smbd"
+msgstr "Smbd'yi yeniden çalýþtýr"
+
+#: web/statuspage.c:235
+msgid "nmbd:"
+msgstr "nmbd:"
+
+#: web/statuspage.c:238
+msgid "Stop nmbd"
+msgstr "Nmbd'yi durdur"
+
+#: web/statuspage.c:240
+msgid "Start nmbd"
+msgstr "Nmbd'yi çalýþtýr"
+
+#: web/statuspage.c:242
+msgid "Restart nmbd"
+msgstr "Nmbd'yi yeniden çalýþtýr"
+
+#: web/statuspage.c:249
+msgid "Active Connections"
+msgstr "Aktif Baðlantýlar"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "PID"
+msgstr "PID"
+
+#: web/statuspage.c:251 web/statuspage.c:264
+msgid "Client"
+msgstr "Ýstemci"
+
+#: web/statuspage.c:251
+msgid "IP address"
+msgstr "IP numarasý"
+
+#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272
+msgid "Date"
+msgstr "Tarih"
+
+#: web/statuspage.c:253
+msgid "Kill"
+msgstr "Kapat"
+
+#: web/statuspage.c:261
+msgid "Active Shares"
+msgstr "Aktif Paylaþýmlar"
+
+#: web/statuspage.c:264
+msgid "Share"
+msgstr "Paylaþým"
+
+#: web/statuspage.c:264
+msgid "User"
+msgstr "Kullanýcý"
+
+#: web/statuspage.c:264
+msgid "Group"
+msgstr "Grup"
+
+#: web/statuspage.c:270
+msgid "Open Files"
+msgstr "Açýk Dosyalar"
+
+#: web/statuspage.c:272
+msgid "Sharing"
+msgstr "Paylaþýlýyor"
+
+#: web/statuspage.c:272
+msgid "R/W"
+msgstr "O/Y"
+
+#: web/statuspage.c:272
+msgid "Oplock"
+msgstr "Oplock"
+
+#: web/statuspage.c:272
+msgid "File"
+msgstr "Dosya"
+
+#: param/loadparm.c:641
+msgid "Base Options"
+msgstr "Temel Seçenekler"
+
+#: param/loadparm.c:643
+msgid "dos charset"
+msgstr "dos karakter kümesi"
+
+#: param/loadparm.c:644
+msgid "unix charset"
+msgstr "unix karakter kümesi"
+
+#: param/loadparm.c:645
+msgid "display charset"
+msgstr "karakter kümesini göster"
+
+#: param/loadparm.c:646
+msgid "comment"
+msgstr "açýklama"
+
+#: param/loadparm.c:647
+msgid "path"
+msgstr "yol"
+
+#: param/loadparm.c:648
+msgid "directory"
+msgstr "dizin"
+
+#: param/loadparm.c:649
+msgid "workgroup"
+msgstr "çalýþma grubu"
+
+#: param/loadparm.c:650
+msgid "netbios name"
+msgstr "netbios adý"
+
+#: param/loadparm.c:651
+msgid "netbios aliases"
+msgstr "netbios rumuzlarý"
+
+#: param/loadparm.c:652
+msgid "netbios scope"
+msgstr "netbios kapsamý"
+
+#: param/loadparm.c:653
+msgid "server string"
+msgstr "sunucu dizgesi"
+
+#: param/loadparm.c:654
+msgid "interfaces"
+msgstr "arayüzler"
+
+#: param/loadparm.c:655
+msgid "bind interfaces only"
+msgstr "yalnýzca arayüzleri baðla"
+
+#: param/loadparm.c:657
+msgid "Security Options"
+msgstr "Güvenlik Seçenekleri"
+
+#: param/loadparm.c:659
+msgid "security"
+msgstr "güvenlik"
+
+#: param/loadparm.c:660
+msgid "encrypt passwords"
+msgstr "þifreyi þifrele "
+
+#: param/loadparm.c:661
+msgid "update encrypted"
+msgstr "þifrelenmiþ güncelle"
+
+#: param/loadparm.c:662
+msgid "allow trusted domains"
+msgstr "güvenli alanlara izin ver"
+
+#: param/loadparm.c:663
+msgid "alternate permissions"
+msgstr "baþka izinler"
+
+#: param/loadparm.c:664
+msgid "hosts equiv"
+msgstr "hosts eþdeðerlisi"
+
+#: param/loadparm.c:665
+msgid "min passwd length"
+msgstr "en kýsa þifre uzunluðu"
+
+#: param/loadparm.c:666
+msgid "min password length"
+msgstr "en kýsa þifre uzunluðu"
+
+#: param/loadparm.c:667
+msgid "map to guest"
+msgstr "guest (misafir) kullanýcýya eþle"
+
+#: param/loadparm.c:668
+msgid "null passwords"
+msgstr "boþ þifreler"
+
+#: param/loadparm.c:669
+msgid "obey pam restrictions"
+msgstr "pam kýsýtlamalarýna uy"
+
+#: param/loadparm.c:670
+msgid "password server"
+msgstr "þifre sunucusu"
+
+#: param/loadparm.c:671
+msgid "smb passwd file"
+msgstr "smb þifre dosyasý"
+
+#: param/loadparm.c:672
+msgid "private dir"
+msgstr "özel dizin"
+
+#: param/loadparm.c:673
+msgid "passdb module path"
+msgstr "þifre veritabaný modül yolu"
+
+#: param/loadparm.c:674
+msgid "root directory"
+msgstr "kök dizin"
+
+#: param/loadparm.c:675
+msgid "root dir"
+msgstr "kök dizin"
+
+#: param/loadparm.c:676
+msgid "root"
+msgstr "kök"
+
+#: param/loadparm.c:678
+msgid "pam password change"
+msgstr "pam þifre deðiþikliði"
+
+#: param/loadparm.c:679
+msgid "passwd program"
+msgstr "þifre yazýlýmý"
+
+#: param/loadparm.c:680
+msgid "passwd chat"
+msgstr "þifre diyaloðu"
+
+#: param/loadparm.c:681
+msgid "passwd chat debug"
+msgstr "þifre diyalog hata ayýklamasý"
+
+#: param/loadparm.c:682
+msgid "username map"
+msgstr "kullanýcý adý eþlemesi"
+
+#: param/loadparm.c:683
+msgid "password level"
+msgstr "þifre seviyesi"
+
+#: param/loadparm.c:684
+msgid "username level"
+msgstr "kullanýcý kimliði seviyesi"
+
+#: param/loadparm.c:685
+msgid "unix password sync"
+msgstr "unix þifre senkronizasyonu"
+
+#: param/loadparm.c:686
+msgid "restrict anonymous"
+msgstr "anonim eriþimi kýsýtla"
+
+#: param/loadparm.c:687
+msgid "lanman auth"
+msgstr "lanman auth"
+
+#: param/loadparm.c:688
+msgid "ntlm auth"
+msgstr "ntlm auth"
+
+#: param/loadparm.c:689
+msgid "plaintext to smbpasswd"
+msgstr "düz metinden smbpasswd'e"
+
+#: param/loadparm.c:690
+msgid "use rhosts"
+msgstr "rhosts kullan"
+
+#: param/loadparm.c:692
+msgid "username"
+msgstr "kullanýcý adý"
+
+#: param/loadparm.c:693
+msgid "user"
+msgstr "kullanýcý"
+
+#: param/loadparm.c:694
+msgid "users"
+msgstr "kullanýcýlar"
+
+#: param/loadparm.c:696
+msgid "guest account"
+msgstr "misafir hesap"
+
+#: param/loadparm.c:697
+msgid "invalid users"
+msgstr "geçersiz kullanýcýlar"
+
+#: param/loadparm.c:698
+msgid "valid users"
+msgstr "geçerli kullanýcýlar"
+
+#: param/loadparm.c:699
+msgid "admin users"
+msgstr "yönetici kullanýcýlarý"
+
+#: param/loadparm.c:700
+msgid "read list"
+msgstr "okuma listesi"
+
+#: param/loadparm.c:701
+msgid "write list"
+msgstr "yazma listesi"
+
+#: param/loadparm.c:702
+msgid "printer admin"
+msgstr "yazýcý yönetimi"
+
+#: param/loadparm.c:703
+msgid "force user"
+msgstr "kullanýcýyý zorla"
+
+#: param/loadparm.c:704
+msgid "force group"
+msgstr "grubu zorla"
+
+#: param/loadparm.c:705
+msgid "group"
+msgstr "grup"
+
+#: param/loadparm.c:707
+msgid "read only"
+msgstr "salt okunur"
+
+#: param/loadparm.c:708
+msgid "write ok"
+msgstr "yazma tamam"
+
+#: param/loadparm.c:709
+msgid "writeable"
+msgstr "yazýlabilir"
+
+#: param/loadparm.c:710
+msgid "writable"
+msgstr "yazýlabilir"
+
+#: param/loadparm.c:712
+msgid "create mask"
+msgstr "oluþturma izinleri"
+
+#: param/loadparm.c:713
+msgid "create mode"
+msgstr "oluþturma kipi"
+
+#: param/loadparm.c:714
+msgid "force create mode"
+msgstr "oluþturma kipini zorla"
+
+#: param/loadparm.c:715
+msgid "security mask"
+msgstr "güvenlik izinleri"
+
+#: param/loadparm.c:716
+msgid "force security mode"
+msgstr "güvenlik kipini zorla"
+
+#: param/loadparm.c:717
+msgid "directory mask"
+msgstr "dizin izinleri"
+
+#: param/loadparm.c:718
+msgid "directory mode"
+msgstr "dizin kipi"
+
+#: param/loadparm.c:719
+msgid "force directory mode"
+msgstr "dizin kipini zorla"
+
+#: param/loadparm.c:720
+msgid "directory security mask"
+msgstr "dizin güvenlik izinleri"
+
+#: param/loadparm.c:721
+msgid "force directory security mode"
+msgstr "dizin güvenlik kipini zorla"
+
+#: param/loadparm.c:722
+msgid "inherit permissions"
+msgstr "izinleri ebeveynden al"
+
+#: param/loadparm.c:723
+msgid "guest only"
+msgstr "yalnýz misafir"
+
+#: param/loadparm.c:724
+msgid "only guest"
+msgstr "yalnýz misafir"
+
+#: param/loadparm.c:726
+msgid "guest ok"
+msgstr "misafir tamam"
+
+#: param/loadparm.c:727
+msgid "public"
+msgstr "genel"
+
+#: param/loadparm.c:729
+msgid "only user"
+msgstr "salt kullanýcý"
+
+#: param/loadparm.c:730
+msgid "hosts allow"
+msgstr "hosts izinli"
+
+#: param/loadparm.c:731
+msgid "allow hosts"
+msgstr "hosts izinli"
+
+#: param/loadparm.c:732
+msgid "hosts deny"
+msgstr "hosts izinsiz"
+
+#: param/loadparm.c:733
+msgid "deny hosts"
+msgstr "hosts izinsiz"
+
+#: param/loadparm.c:736
+msgid "Secure Socket Layer Options"
+msgstr "Güvenli Soket Katman Seçenekleri"
+
+#: param/loadparm.c:737
+msgid "ssl"
+msgstr "ssl"
+
+#: param/loadparm.c:739
+msgid "ssl hosts"
+msgstr "ssl hosts"
+
+#: param/loadparm.c:740
+msgid "ssl hosts resign"
+msgstr "ssl hosts istifa"
+
+#: param/loadparm.c:741
+msgid "ssl CA certDir"
+msgstr "ssl CA sertifika dizini"
+
+#: param/loadparm.c:742
+msgid "ssl CA certFile"
+msgstr "ssl CA sertifika dosyasý"
+
+#: param/loadparm.c:743
+msgid "ssl server cert"
+msgstr "ssl sunucu sertifikasý"
+
+#: param/loadparm.c:744
+msgid "ssl server key"
+msgstr "ssl sunucu anahtarý"
+
+#: param/loadparm.c:745
+msgid "ssl client cert"
+msgstr "ssl istemci sertifikasý"
+
+#: param/loadparm.c:746
+msgid "ssl client key"
+msgstr "ssl istemci anahtarý"
+
+#: param/loadparm.c:747
+msgid "ssl require clientcert"
+msgstr "ssl istemci sertifikasý iste"
+
+#: param/loadparm.c:748
+msgid "ssl require servercert"
+msgstr "ssl sunucu sertifikasý iste"
+
+#: param/loadparm.c:749
+msgid "ssl ciphers"
+msgstr "ssl þifreleri"
+
+#: param/loadparm.c:750
+msgid "ssl version"
+msgstr "ssl sürümü"
+
+#: param/loadparm.c:751
+msgid "ssl compatibility"
+msgstr "ssl uyumluluðu"
+
+#: param/loadparm.c:754
+msgid "Logging Options"
+msgstr "Günlük Kaydý Seçenekleri"
+
+#: param/loadparm.c:755
+msgid "log level"
+msgstr "günlük seviyesi"
+
+#: param/loadparm.c:756
+msgid "debuglevel"
+msgstr "hata ayýklama seviyesi"
+
+#: param/loadparm.c:757
+msgid "syslog"
+msgstr "sistem günlüðü"
+
+#: param/loadparm.c:758
+msgid "syslog only"
+msgstr "salt sistem günlüðü"
+
+#: param/loadparm.c:759
+msgid "log file"
+msgstr "günlük dosyasý"
+
+#: param/loadparm.c:761
+msgid "max log size"
+msgstr "maksimum günlük büyüklüðü"
+
+#: param/loadparm.c:762
+msgid "timestamp logs"
+msgstr "zaman damgasý günlükleri"
+
+#: param/loadparm.c:763
+msgid "debug timestamp"
+msgstr "hata ayýklama zaman damgasý"
+
+#: param/loadparm.c:764
+msgid "debug hires timestamp"
+msgstr "hata ayýklama yüksek çözünürlüklü zaman damgasý"
+
+#: param/loadparm.c:765
+msgid "debug pid"
+msgstr "hata ayýklama pid"
+
+#: param/loadparm.c:766
+msgid "debug uid"
+msgstr "hata ayýklama uid"
+
+#: param/loadparm.c:768
+msgid "Protocol Options"
+msgstr "Protokol Seçenekleri"
+
+#: param/loadparm.c:770
+msgid "protocol"
+msgstr "protokol"
+
+#: param/loadparm.c:771
+msgid "large readwrite"
+msgstr "büyük oku/yaz"
+
+#: param/loadparm.c:772
+msgid "max protocol"
+msgstr "max protokol"
+
+#: param/loadparm.c:773
+msgid "min protocol"
+msgstr "min protokol"
+
+#: param/loadparm.c:774
+msgid "unicode"
+msgstr "unicode"
+
+#: param/loadparm.c:775
+msgid "read bmpx"
+msgstr "bmpx oku"
+
+#: param/loadparm.c:776
+msgid "read raw"
+msgstr "ham oku"
+
+#: param/loadparm.c:777
+msgid "write raw"
+msgstr "ham yaz"
+
+#: param/loadparm.c:779
+msgid "nt smb support"
+msgstr "nt smb desteði"
+
+#: param/loadparm.c:780
+msgid "nt pipe support"
+msgstr "nt verihattý desteði"
+
+#: param/loadparm.c:781
+msgid "nt acl support"
+msgstr "nt acl desteði"
+
+#: param/loadparm.c:782
+msgid "announce version"
+msgstr "sürümü bildir"
+
+#: param/loadparm.c:783
+msgid "announce as"
+msgstr "bildir"
+
+#: param/loadparm.c:784
+msgid "max mux"
+msgstr "maksimum mux"
+
+#: param/loadparm.c:785
+msgid "max xmit"
+msgstr "maksimum xmit"
+
+#: param/loadparm.c:787
+msgid "name resolve order"
+msgstr "ad çözümleme sýrasý"
+
+#: param/loadparm.c:788
+msgid "max packet"
+msgstr "maksimum paket"
+
+#: param/loadparm.c:789
+msgid "packet size"
+msgstr "paket büyüklüðü"
+
+#: param/loadparm.c:790
+msgid "max ttl"
+msgstr "maksimum ttl"
+
+#: param/loadparm.c:791
+msgid "max wins ttl"
+msgstr "maksimum wins ttl"
+
+#: param/loadparm.c:792
+msgid "min wins ttl"
+msgstr "minimum wins ttl"
+
+#: param/loadparm.c:793
+msgid "time server"
+msgstr "zaman sunucusu"
+
+#: param/loadparm.c:795
+msgid "Tuning Options"
+msgstr "Ayar Seçenekleri"
+
+#: param/loadparm.c:797
+msgid "change notify timeout"
+msgstr "zamanaþýmý bildirmesini deðiþtir"
+
+#: param/loadparm.c:798
+msgid "deadtime"
+msgstr "ölüzaman"
+
+#: param/loadparm.c:799
+msgid "getwd cache"
+msgstr "getwd arabelleði"
+
+#: param/loadparm.c:800
+msgid "keepalive"
+msgstr "hayattatut"
+
+#: param/loadparm.c:802
+msgid "lpq cache time"
+msgstr "lpq arabellek zamaný"
+
+#: param/loadparm.c:803
+msgid "max smbd processes"
+msgstr "maksimum smbd süreci"
+
+#: param/loadparm.c:804
+msgid "max connections"
+msgstr "maksimum baðlantý"
+
+#: param/loadparm.c:805
+msgid "paranoid server security"
+msgstr "yüksek dereceli sunucu güvenliði"
+
+#: param/loadparm.c:806
+msgid "max disk size"
+msgstr "maksimum disk büyüklüðü"
+
+#: param/loadparm.c:807
+msgid "max open files"
+msgstr "maksimum açýk dosya"
+
+#: param/loadparm.c:808
+msgid "min print space"
+msgstr "minimum yazma alaný"
+
+#: param/loadparm.c:809
+msgid "read size"
+msgstr "okuma boyu"
+
+#: param/loadparm.c:811
+msgid "socket options"
+msgstr "soket seçenekleri"
+
+#: param/loadparm.c:812
+msgid "stat cache size"
+msgstr "durum arabelleði boyu"
+
+#: param/loadparm.c:813
+msgid "strict allocate"
+msgstr "sýký ayýrma"
+
+#: param/loadparm.c:814
+msgid "strict sync"
+msgstr "sýký senkronizasyon"
+
+#: param/loadparm.c:815
+msgid "sync always"
+msgstr "herzaman senkronize"
+
+#: param/loadparm.c:816
+msgid "use mmap"
+msgstr "bellek eþlemesi kullan"
+
+#: param/loadparm.c:817
+msgid "hostname lookups"
+msgstr "sunucu adý arama"
+
+#: param/loadparm.c:818
+msgid "write cache size"
+msgstr "yazma arabellek boyu"
+
+#: param/loadparm.c:820
+msgid "Printing Options"
+msgstr "Yazdýrma Seçenekleri"
+
+#: param/loadparm.c:822
+msgid "total print jobs"
+msgstr "toplam yazdýrma iþleri"
+
+#: param/loadparm.c:823
+msgid "max print jobs"
+msgstr "maksimum yazdýrma iþi"
+
+#: param/loadparm.c:824
+msgid "load printers"
+msgstr "yazýcýlarý yükle"
+
+#: param/loadparm.c:825
+msgid "printcap name"
+msgstr "printcap adý"
+
+#: param/loadparm.c:826
+msgid "printcap"
+msgstr "printcap"
+
+#: param/loadparm.c:827
+msgid "printable"
+msgstr "yazdýrýlabilir"
+
+#: param/loadparm.c:828
+msgid "print ok"
+msgstr "yazdýrma tamam"
+
+#: param/loadparm.c:829
+msgid "postscript"
+msgstr "postscript"
+
+#: param/loadparm.c:830
+msgid "printing"
+msgstr "yazdýrýyor"
+
+#: param/loadparm.c:831
+msgid "print command"
+msgstr "yazdýrma komutu"
+
+#: param/loadparm.c:832
+msgid "disable spoolss"
+msgstr "kuyruðu etkisizleþtir"
+
+#: param/loadparm.c:833
+msgid "lpq command"
+msgstr "lpq komutu"
+
+#: param/loadparm.c:834
+msgid "lprm command"
+msgstr "lprm komutu"
+
+#: param/loadparm.c:835
+msgid "lppause command"
+msgstr "lppause komutu"
+
+#: param/loadparm.c:836
+msgid "lpresume command"
+msgstr "lpresume komutu"
+
+#: param/loadparm.c:837
+msgid "queuepause command"
+msgstr "queuepause komutu"
+
+#: param/loadparm.c:838
+msgid "queueresume command"
+msgstr "queueresume komutu"
+
+#: param/loadparm.c:840
+msgid "enumports command"
+msgstr "port listele komutu"
+
+#: param/loadparm.c:841
+msgid "addprinter command"
+msgstr "yazýcý ekle komutu"
+
+#: param/loadparm.c:842
+msgid "deleteprinter command"
+msgstr "yazýcý sil komutu"
+
+#: param/loadparm.c:843
+msgid "show add printer wizard"
+msgstr "yazýcý ekleme sihirbazýný göster"
+
+#: param/loadparm.c:844
+msgid "os2 driver map"
+msgstr "os2 sürücü eþlemesi"
+
+#: param/loadparm.c:846
+msgid "printer name"
+msgstr "yazýcý adý"
+
+#: param/loadparm.c:847
+msgid "printer"
+msgstr "yazýcý"
+
+#: param/loadparm.c:848
+msgid "use client driver"
+msgstr "istemci sürücüsü kullan"
+
+#: param/loadparm.c:849
+msgid "printer driver"
+msgstr "yazýcý sürücüsü"
+
+#: param/loadparm.c:850
+msgid "printer driver file"
+msgstr "yazýcý sürücü dosyasý"
+
+#: param/loadparm.c:851
+msgid "printer driver location"
+msgstr "yazýcý sürücüsü yeri"
+
+#: param/loadparm.c:853
+msgid "Filename Handling"
+msgstr "Dosyaadý Ýþlenmesi"
+
+#: param/loadparm.c:854
+msgid "strip dot"
+msgstr "noktalarý bastýr"
+
+#: param/loadparm.c:856
+msgid "mangled stack"
+msgstr "karýþtýrýlmýþ yýðýt"
+
+#: param/loadparm.c:857
+msgid "default case"
+msgstr "öntanýmlý büyük/küçük harf"
+
+#: param/loadparm.c:858
+msgid "case sensitive"
+msgstr "büyük küçük harfe duyarlý"
+
+#: param/loadparm.c:859
+msgid "casesignames"
+msgstr "casesignames"
+
+#: param/loadparm.c:860
+msgid "preserve case"
+msgstr "büyük küçük harf ayrýmýný tut"
+
+#: param/loadparm.c:861
+msgid "short preserve case"
+msgstr "kýsa büyük küçük harf ayrýmýný tut"
+
+#: param/loadparm.c:862
+msgid "mangle case"
+msgstr "büyük küçük harf harmanla"
+
+#: param/loadparm.c:863
+msgid "mangling char"
+msgstr "karakter harmanlanýyor"
+
+#: param/loadparm.c:864
+msgid "hide dot files"
+msgstr "nokta ile baþlayan dosyalarý gizle"
+
+#: param/loadparm.c:865
+msgid "hide unreadable"
+msgstr "okunamazlarý sakla"
+
+#: param/loadparm.c:866
+msgid "delete veto files"
+msgstr "veto dosyalarýný sil"
+
+#: param/loadparm.c:867
+msgid "veto files"
+msgstr "veto dosyalarý"
+
+#: param/loadparm.c:868
+msgid "hide files"
+msgstr "dosyalarý gizle"
+
+#: param/loadparm.c:869
+msgid "veto oplock files"
+msgstr "veto oplock dosyalarý"
+
+#: param/loadparm.c:870
+msgid "map system"
+msgstr "sistemi eþle"
+
+#: param/loadparm.c:871
+msgid "map hidden"
+msgstr "gizlileri eþle"
+
+#: param/loadparm.c:872
+msgid "map archive"
+msgstr "arþivi eþle"
+
+#: param/loadparm.c:873
+msgid "mangled names"
+msgstr "harmanlanmýþ isimler"
+
+#: param/loadparm.c:874
+msgid "mangled map"
+msgstr "harmanlanmýþ eþleþme"
+
+#: param/loadparm.c:875
+msgid "stat cache"
+msgstr "durum arabelleði"
+
+#: param/loadparm.c:877
+msgid "Domain Options"
+msgstr "Alan Seçenekleri"
+
+#: param/loadparm.c:879
+msgid "domain admin group"
+msgstr "alan yönetici grubu"
+
+#: param/loadparm.c:880
+msgid "domain guest group"
+msgstr "alan misafir grubu"
+
+#: param/loadparm.c:883
+msgid "groupname map"
+msgstr "grup adý eþlemesi"
+
+#: param/loadparm.c:886
+msgid "machine password timeout"
+msgstr "makina þifresi zamanaþýmý"
+
+#: param/loadparm.c:888
+msgid "Logon Options"
+msgstr "Sistem Giriþ Seçenekleri"
+
+#: param/loadparm.c:890
+msgid "add user script"
+msgstr "kullanýcý ekleme betiði"
+
+#: param/loadparm.c:891
+msgid "delete user script"
+msgstr "kullanýcý silme betiði"
+
+#: param/loadparm.c:892
+msgid "add group script"
+msgstr "grup ekleme betiði"
+
+#: param/loadparm.c:893
+msgid "delete group script"
+msgstr "grup silme betiði"
+
+#: param/loadparm.c:894
+msgid "add user to group script"
+msgstr "gruba kullanýcý ekleme betiði"
+
+#: param/loadparm.c:895
+msgid "delete user from group script"
+msgstr "gruptan kullanýcý silme betiði"
+
+#: param/loadparm.c:896
+msgid "add machine script"
+msgstr "makina ekleme betiði"
+
+#: param/loadparm.c:897
+msgid "shutdown script"
+msgstr "sistem kapanýþ betiði"
+
+#: param/loadparm.c:898
+msgid "abort shutdown script"
+msgstr "sistem kapanýþ betiðini durdur"
+
+#: param/loadparm.c:900
+msgid "logon script"
+msgstr "sistem giriþ betiði"
+
+#: param/loadparm.c:901
+msgid "logon path"
+msgstr "sistem giriþ yolu"
+
+#: param/loadparm.c:902
+msgid "logon drive"
+msgstr "sistem giriþ aygýtý"
+
+#: param/loadparm.c:903
+msgid "logon home"
+msgstr "sistem giriþ kökü"
+
+#: param/loadparm.c:904
+msgid "domain logons"
+msgstr "alan giriþleri"
+
+#: param/loadparm.c:906
+msgid "Browse Options"
+msgstr "Gözatma Seçenekleri"
+
+#: param/loadparm.c:908
+msgid "os level"
+msgstr "iþletim sistem seviyesi"
+
+#: param/loadparm.c:909
+msgid "lm announce"
+msgstr "lm bildirimi"
+
+#: param/loadparm.c:910
+msgid "lm interval"
+msgstr "lm aralýðý"
+
+#: param/loadparm.c:911
+msgid "preferred master"
+msgstr "tercih edilen ana alan sunucusu"
+
+#: param/loadparm.c:912
+msgid "prefered master"
+msgstr "tercih edilen ana alan sunucusu"
+
+#: param/loadparm.c:913
+msgid "local master"
+msgstr "yerel alan sunucusu"
+
+#: param/loadparm.c:914
+msgid "domain master"
+msgstr "alan sunucusu"
+
+#: param/loadparm.c:915
+msgid "browse list"
+msgstr "gözatma listesi"
+
+#: param/loadparm.c:916
+msgid "browseable"
+msgstr "gözatýlabilir"
+
+#: param/loadparm.c:917
+msgid "browsable"
+msgstr "gözatýlabilir"
+
+#: param/loadparm.c:918
+msgid "enhanced browsing"
+msgstr "geliþkin gözatma"
+
+#: param/loadparm.c:920
+msgid "WINS Options"
+msgstr "WINS Seçenekleri"
+
+#: param/loadparm.c:921
+msgid "dns proxy"
+msgstr "dns proxy"
+
+#: param/loadparm.c:922
+msgid "wins proxy"
+msgstr "wins proxy"
+
+#: param/loadparm.c:924
+msgid "wins server"
+msgstr "wins sunucusu"
+
+#: param/loadparm.c:925
+msgid "wins support"
+msgstr "wins desteði"
+
+#: param/loadparm.c:926
+msgid "wins hook"
+msgstr "wins giriþi"
+
+#: param/loadparm.c:928
+msgid "Locking Options"
+msgstr "Kilitleme Seçenekleri"
+
+#: param/loadparm.c:930
+msgid "blocking locks"
+msgstr "engelleyen kilitler"
+
+#: param/loadparm.c:931
+msgid "fake oplocks"
+msgstr "sahte oplocklar"
+
+#: param/loadparm.c:932
+msgid "kernel oplocks"
+msgstr "çekirdek oplocklarý"
+
+#: param/loadparm.c:933
+msgid "locking"
+msgstr "kilitliyor"
+
+#: param/loadparm.c:935
+msgid "oplocks"
+msgstr "oplocklar"
+
+#: param/loadparm.c:936
+msgid "level2 oplocks"
+msgstr "Seviye 2 oplocklar"
+
+#: param/loadparm.c:937
+msgid "oplock break wait time"
+msgstr "oplock kýrma bekleme süresi"
+
+#: param/loadparm.c:938
+msgid "oplock contention limit"
+msgstr "oplock ihtilaf limiti"
+
+#: param/loadparm.c:939
+msgid "posix locking"
+msgstr "posix kilitlemesi"
+
+#: param/loadparm.c:940
+msgid "strict locking"
+msgstr "sýký kilitleme"
+
+#: param/loadparm.c:941
+msgid "share modes"
+msgstr "paylaþým kipleri"
+
+#: param/loadparm.c:944
+msgid "Ldap Options"
+msgstr "Ldap Seçenekleri"
+
+#: param/loadparm.c:946
+msgid "ldap server"
+msgstr "ldap sunucusu"
+
+#: param/loadparm.c:947
+msgid "ldap port"
+msgstr "ldap portu"
+
+#: param/loadparm.c:948
+msgid "ldap suffix"
+msgstr "ldap soneki"
+
+#: param/loadparm.c:949
+msgid "ldap filter"
+msgstr "ldap filtresi"
+
+#: param/loadparm.c:950
+msgid "ldap root"
+msgstr "ldap kökü"
+
+#: param/loadparm.c:951
+msgid "ldap root passwd"
+msgstr "ldap kök þifresi"
+
+#: param/loadparm.c:954
+msgid "Miscellaneous Options"
+msgstr "Diðer Seçenekler"
+
+#: param/loadparm.c:955
+msgid "add share command"
+msgstr "paylaþým ekle komutu"
+
+#: param/loadparm.c:956
+msgid "change share command"
+msgstr "paylaþým deðiþtir komutu"
+
+#: param/loadparm.c:957
+msgid "delete share command"
+msgstr "paylaþým sil komutu"
+
+#: param/loadparm.c:959
+msgid "config file"
+msgstr "ayar dosyasý"
+
+#: param/loadparm.c:960
+msgid "preload"
+msgstr "önyükle"
+
+#: param/loadparm.c:961
+msgid "auto services"
+msgstr "otomatik servisler"
+
+#: param/loadparm.c:962
+msgid "lock dir"
+msgstr "kilit dizini"
+
+#: param/loadparm.c:963
+msgid "lock directory"
+msgstr "kilit dizini"
+
+#: param/loadparm.c:965
+msgid "utmp directory"
+msgstr "utmp dizini"
+
+#: param/loadparm.c:966
+msgid "wtmp directory"
+msgstr "wtmp dizini"
+
+#: param/loadparm.c:967
+msgid "utmp"
+msgstr "utmp"
+
+#: param/loadparm.c:970
+msgid "default service"
+msgstr "öntanýmlý servis"
+
+#: param/loadparm.c:971
+msgid "default"
+msgstr "öntanýmlý"
+
+#: param/loadparm.c:972
+msgid "message command"
+msgstr "ileti komutu"
+
+#: param/loadparm.c:973
+msgid "dfree command"
+msgstr "dfree komutu"
+
+#: param/loadparm.c:974
+msgid "remote announce"
+msgstr "uzak bildirim"
+
+#: param/loadparm.c:975
+msgid "remote browse sync"
+msgstr "uzak gözatma senkronizasyonu"
+
+#: param/loadparm.c:976
+msgid "socket address"
+msgstr "soket adresi"
+
+#: param/loadparm.c:977
+msgid "homedir map"
+msgstr "evdizini eþlemesi"
+
+#: param/loadparm.c:978
+msgid "time offset"
+msgstr "zaman kaydýrmasý"
+
+#: param/loadparm.c:979
+msgid "NIS homedir"
+msgstr "NIS evdizini"
+
+#: param/loadparm.c:980
+msgid "-valid"
+msgstr "-geçerli"
+
+#: param/loadparm.c:982
+msgid "copy"
+msgstr "kopyala"
+
+#: param/loadparm.c:983
+msgid "include"
+msgstr "ekle"
+
+#: param/loadparm.c:984
+msgid "exec"
+msgstr "çalýþtýr"
+
+#: param/loadparm.c:985
+msgid "preexec"
+msgstr "preexec"
+
+#: param/loadparm.c:987
+msgid "preexec close"
+msgstr "preexec close"
+
+#: param/loadparm.c:988
+msgid "postexec"
+msgstr "postexec"
+
+#: param/loadparm.c:989
+msgid "root preexec"
+msgstr "root preexec"
+
+#: param/loadparm.c:990
+msgid "root preexec close"
+msgstr "root preexec close"
+
+#: param/loadparm.c:991
+msgid "root postexec"
+msgstr "root postexec"
+
+#: param/loadparm.c:992
+msgid "available"
+msgstr "mevcut"
+
+#: param/loadparm.c:993
+msgid "volume"
+msgstr "volume"
+
+#: param/loadparm.c:994
+msgid "fstype"
+msgstr "dosya sistem tipi"
+
+#: param/loadparm.c:995
+msgid "set directory"
+msgstr "dizini belirle"
+
+#: param/loadparm.c:996
+msgid "source environment"
+msgstr "source environment"
+
+#: param/loadparm.c:997
+msgid "wide links"
+msgstr "wide links"
+
+#: param/loadparm.c:998
+msgid "follow symlinks"
+msgstr "sembolik baðlarý izle"
+
+#: param/loadparm.c:999
+msgid "dont descend"
+msgstr "dont descend"
+
+#: param/loadparm.c:1000
+msgid "magic script"
+msgstr "magic script"
+
+#: param/loadparm.c:1001
+msgid "magic output"
+msgstr "magic output"
+
+#: param/loadparm.c:1002
+msgid "delete readonly"
+msgstr "salt okunurlarý sil"
+
+#: param/loadparm.c:1003
+msgid "dos filemode"
+msgstr "dos dosya kipi"
+
+#: param/loadparm.c:1004
+msgid "dos filetimes"
+msgstr "dos dosya zamanlarý"
+
+#: param/loadparm.c:1005
+msgid "dos filetime resolution"
+msgstr "dos dosya zamaný çözünürlüðü"
+
+#: param/loadparm.c:1007
+msgid "fake directory create times"
+msgstr "dizin oluþma zamanlarýný taklit et"
+
+#: param/loadparm.c:1008
+msgid "panic action"
+msgstr "panik iþlemi"
+
+#: param/loadparm.c:1009
+msgid "hide local users"
+msgstr "yerel kullanýcýlarý sakla"
+
+#: param/loadparm.c:1012
+msgid "VFS options"
+msgstr "VFS Seçenekleri"
+
+#: param/loadparm.c:1014
+msgid "vfs object"
+msgstr "vfs nesnesi"
+
+#: param/loadparm.c:1015
+msgid "vfs options"
+msgstr "vfs seçenekleri"
+
+#: param/loadparm.c:1018
+msgid "msdfs root"
+msgstr "msdfs kökü"
+
+#: param/loadparm.c:1019
+msgid "host msdfs"
+msgstr "host msdfs"
+
+#: param/loadparm.c:1021
+msgid "Winbind options"
+msgstr "Winbind seçenekleri"
+
+#: param/loadparm.c:1023
+msgid "winbind uid"
+msgstr "winbind uid"
+
+#: param/loadparm.c:1024
+msgid "winbind gid"
+msgstr "winbind gid"
+
+#: param/loadparm.c:1025
+msgid "template homedir"
+msgstr "örnek ev dizini"
+
+#: param/loadparm.c:1026
+msgid "template shell"
+msgstr "örnek kabuk"
+
+#: param/loadparm.c:1027
+msgid "winbind separator"
+msgstr "winbind ayracý"
+
+#: param/loadparm.c:1028
+msgid "winbind cache time"
+msgstr "winbind arabellek zamaný"
+
+#: param/loadparm.c:1029
+msgid "winbind enum users"
+msgstr "winbind kullanýcý listele"
+
+#: param/loadparm.c:1030
+msgid "winbind enum groups"
+msgstr "winbind grup listele"
+
diff --git a/source4/popt/.cvsignore b/source4/popt/.cvsignore
new file mode 100644 (file)
index 0000000..86b08b5
--- /dev/null
@@ -0,0 +1,8 @@
+ID
+Makefile
+config.cache
+config.h
+config.log
+config.status
+rsync
+zlib/dummy
diff --git a/source4/popt/CHANGES b/source4/popt/CHANGES
new file mode 100644 (file)
index 0000000..b6ab2aa
--- /dev/null
@@ -0,0 +1,43 @@
+1.3 ->
+       - heavy dose of const's
+       - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+       - added support for single -
+       - misc bug fixes
+       - portability improvements
+
+1.2.2 -> 1.2.3
+       - fixed memset() in help message generation (Dale Hawkins)
+       - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+       - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+       - fixed bug in chaind alias happens which seems to have only
+         affected --triggers in rpm
+       - added POPT_ARG_VAL
+       - popt.3 installed by default
+
+1.2 -> 1.2.1
+       - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+       - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+       - added popt.3 man page (Robert Lynch)
+       - don't use mmap anymore (its lack of portability isn't worth the
+         trouble)
+       - added test script
+       - added support for exec
+       - removed support for *_POPT_ALIASES env variable -- it was a bad
+         idea
+       - reorganized into multiple source files
+       - added automatic help generation, POPT_AUTOHELP
+       - added table callbacks
+       - added table inclusion
+       - updated man page for new features
+       - added test scripts
+
+1.0 -> 1.1
+       - moved to autoconf (Fred Fish)
+       - added STRERROR replacement (Norbert Warmuth)
+       - added const keywords (Bruce Perens)
diff --git a/source4/popt/COPYING b/source4/popt/COPYING
new file mode 100644 (file)
index 0000000..b4c7ca8
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright (c) 1998  Red Hat Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/source4/popt/README b/source4/popt/README
new file mode 100644 (file)
index 0000000..7fccc83
--- /dev/null
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+       1) popt is fully reentrant
+       2) popt can parse arbitrary argv[] style arrays while 
+          getopt(2) makes this quite difficult
+       3) popt allows users to alias command line arguments
+       4) popt provides convience functions for parsting strings
+          into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt. 
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/source4/popt/dummy.in b/source4/popt/dummy.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/popt/findme.c b/source4/popt/findme.c
new file mode 100644 (file)
index 0000000..f2ad05b
--- /dev/null
@@ -0,0 +1,46 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0) {
+    char * path = getenv("PATH");
+    char * pathbuf;
+    char * start, * chptr;
+    char * buf, *local = NULL;
+
+    /* If there is a / in the argv[0], it has to be an absolute
+       path */
+    if (strchr(argv0, '/'))
+       return xstrdup(argv0);
+
+    if (!path) return NULL;
+
+    local = start = pathbuf = malloc(strlen(path) + 1);
+    buf = malloc(strlen(path) + strlen(argv0) + 2);
+    strcpy(pathbuf, path);
+
+    chptr = NULL;
+    do {
+       if ((chptr = strchr(start, ':')))
+           *chptr = '\0';
+       sprintf(buf, "%s/%s", start, argv0);
+
+       if (!access(buf, X_OK)) {
+               if (local) free(local);
+               return buf;
+       }
+
+       if (chptr) 
+           start = chptr + 1;
+       else
+           start = NULL;
+    } while (start && *start);
+
+    free(buf);
+    if (local) free(local);
+
+    return NULL;
+}
diff --git a/source4/popt/findme.h b/source4/popt/findme.h
new file mode 100644 (file)
index 0000000..5e93963
--- /dev/null
@@ -0,0 +1,10 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+const char * findProgramPath(const char * argv0);
+
+#endif
diff --git a/source4/popt/popt.c b/source4/popt/popt.c
new file mode 100644 (file)
index 0000000..9fa8650
--- /dev/null
@@ -0,0 +1,782 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef HAVE_STRERROR
+static char * strerror(int errno) {
+    extern int sys_nerr;
+    extern char * sys_errlist[];
+
+    if ((0 <= errno) && (errno < sys_nerr))
+       return sys_errlist[errno];
+    else
+       return POPT_("unknown errno");
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+    if (con->execPath) xfree(con->execPath);
+    con->execPath = xstrdup(path);
+    con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+                           int post) {
+    const struct poptOption * opt = table;
+    poptCallbackType cb;
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           invokeCallbacks(con, opt->arg, post);
+       } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+                  ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+                   ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+           cb = (poptCallbackType)opt->arg;
+           cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+              NULL, NULL, opt->descrip);
+       }
+       opt++;
+    }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+                          const struct poptOption * options, int flags) {
+    poptContext con = malloc(sizeof(*con));
+
+    memset(con, 0, sizeof(*con));
+
+    con->os = con->optionStack;
+    con->os->argc = argc;
+    con->os->argv = argv;
+    con->os->argb = NULL;
+
+    if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+       con->os->next = 1;                      /* skip argv[0] */
+
+    con->leftovers = calloc( (argc + 1), sizeof(char *) );
+    con->options = options;
+    con->aliases = NULL;
+    con->numAliases = 0;
+    con->flags = flags;
+    con->execs = NULL;
+    con->numExecs = 0;
+    con->finalArgvAlloced = argc * 2;
+    con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+    con->execAbsolute = 1;
+    con->arg_strip = NULL;
+
+    if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+       con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+    if (name)
+       con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+    invokeCallbacks(con, con->options, 0);
+
+    return con;
+}
+
+static void cleanOSE(struct optionStackEntry *os)
+{
+    if (os->nextArg) {
+       xfree(os->nextArg);
+       os->nextArg = NULL;
+    }
+    if (os->argv) {
+       xfree(os->argv);
+       os->argv = NULL;
+    }
+    if (os->argb) {
+       PBM_FREE(os->argb);
+       os->argb = NULL;
+    }
+}
+
+void poptResetContext(poptContext con) {
+    int i;
+
+    while (con->os > con->optionStack) {
+       cleanOSE(con->os--);
+    }
+    if (con->os->argb) {
+       PBM_FREE(con->os->argb);
+       con->os->argb = NULL;
+    }
+    con->os->currAlias = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->nextArg = NULL;
+    con->os->next = 1;                 /* skip argv[0] */
+
+    con->numLeftovers = 0;
+    con->nextLeftover = 0;
+    con->restLeftover = 0;
+    con->doExec = NULL;
+
+    for (i = 0; i < con->finalArgvCount; i++) {
+       if (con->finalArgv[i]) {
+           xfree(con->finalArgv[i]);
+           con->finalArgv[i] = NULL;
+       }
+    }
+
+    con->finalArgvCount = 0;
+
+    if (con->arg_strip) {
+       PBM_FREE(con->arg_strip);
+       con->arg_strip = NULL;
+    }
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+    int i;
+
+    i = con->numExecs - 1;
+    if (longName) {
+       while (i >= 0 && (!con->execs[i].longName ||
+           strcmp(con->execs[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->execs[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if (con->flags & POPT_CONTEXT_NO_EXEC)
+       return 1;
+
+    if (con->doExec == NULL) {
+       con->doExec = con->execs + i;
+       return 1;
+    }
+
+    /* We already have an exec to do; remember this option for next
+       time 'round */
+    if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+       con->finalArgvAlloced += 10;
+       con->finalArgv = realloc(con->finalArgv,
+                       sizeof(*con->finalArgv) * con->finalArgvAlloced);
+    }
+
+    i = con->finalArgvCount++;
+    {  char *s  = malloc((longName ? strlen(longName) : 0) + 3);
+       if (longName)
+           sprintf(s, "--%s", longName);
+       else
+           sprintf(s, "-%c", shortName);
+       con->finalArgv[i] = s;
+    }
+
+    return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, const char * longName, char shortName,
+                      /*@keep@*/ const char * nextCharArg) {
+    int i;
+
+    if (con->os->currAlias && con->os->currAlias->longName && longName &&
+       !strcmp(con->os->currAlias->longName, longName))
+       return 0;
+    if (con->os->currAlias && shortName &&
+           shortName == con->os->currAlias->shortName)
+       return 0;
+
+    i = con->numAliases - 1;
+    if (longName) {
+       while (i >= 0 && (!con->aliases[i].longName ||
+           strcmp(con->aliases[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->aliases[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    if (nextCharArg && *nextCharArg)
+       con->os->nextCharArg = nextCharArg;
+
+    con->os++;
+    con->os->next = 0;
+    con->os->stuffed = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = con->aliases + i;
+    poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+               &con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+
+    return 1;
+}
+
+static void execCommand(poptContext con) {
+    const char ** argv;
+    int pos = 0;
+    const char * script = con->doExec->script;
+
+    argv = malloc(sizeof(*argv) *
+                       (6 + con->numLeftovers + con->finalArgvCount));
+
+    if (!con->execAbsolute && strchr(script, '/')) return;
+
+    if (!strchr(script, '/') && con->execPath) {
+       char *s = malloc(strlen(con->execPath) + strlen(script) + 2);
+       sprintf(s, "%s/%s", con->execPath, script);
+       argv[pos] = s;
+    } else {
+       argv[pos] = script;
+    }
+    pos++;
+
+    argv[pos] = findProgramPath(con->os->argv[0]);
+    if (argv[pos]) pos++;
+    argv[pos++] = ";";
+
+    memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+    pos += con->finalArgvCount;
+
+    if (con->numLeftovers) {
+       argv[pos++] = "--";
+       memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+       pos += con->numLeftovers;
+    }
+
+    argv[pos++] = NULL;
+
+#ifdef __hpux
+    setresuid(getuid(), getuid(),-1);
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX         sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+    setuid(getuid());
+#elif defined (HAVE_SETREUID)
+    setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#else
+    ; /* Can't drop privileges */
+#endif
+#endif
+
+    execvp(argv[0], (char *const *)argv);
+}
+
+/*@observer@*/ static const struct poptOption *
+findOption(const struct poptOption * table, const char * longName,
+    char shortName,
+    /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData,
+    int singleDash)
+{
+    const struct poptOption * opt = table;
+    const struct poptOption * opt2;
+    const struct poptOption * cb = NULL;
+
+    /* This happens when a single - is given */
+    if (singleDash && !shortName && !*longName)
+       shortName = '-';
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           opt2 = findOption(opt->arg, longName, shortName, callback,
+                             callbackData, singleDash);
+           if (opt2) {
+               if (*callback && !*callbackData)
+                   *callbackData = opt->descrip;
+               return opt2;
+           }
+       } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+           cb = opt;
+       } else if (longName && opt->longName &&
+                  (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+                  !strcmp(longName, opt->longName)) {
+           break;
+       } else if (shortName && shortName == opt->shortName) {
+           break;
+       }
+       opt++;
+    }
+
+    if (!opt->longName && !opt->shortName) return NULL;
+    *callbackData = NULL;
+    *callback = NULL;
+    if (cb) {
+       *callback = (poptCallbackType)cb->arg;
+       if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+           *callbackData = cb->descrip;
+    }
+
+    return opt;
+}
+
+static const char *findNextArg(poptContext con, unsigned argx, int delete)
+{
+    struct optionStackEntry * os = con->os;
+    const char * arg;
+
+    do {
+       int i;
+       arg = NULL;
+       while (os->next == os->argc && os > con->optionStack) os--;
+       if (os->next == os->argc && os == con->optionStack) break;
+       for (i = os->next; i < os->argc; i++) {
+           if (os->argb && PBM_ISSET(i, os->argb)) continue;
+           if (*os->argv[i] == '-') continue;
+           if (--argx > 0) continue;
+           arg = os->argv[i];
+           if (delete) {
+               if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+               PBM_SET(i, os->argb);
+           }
+           break;
+       }
+       if (os > con->optionStack) os--;
+    } while (arg == NULL);
+    return arg;
+}
+
+static /*@only@*/ const char * expandNextArg(poptContext con, const char * s)
+{
+    const char *a;
+    size_t alen;
+    char *t, *te;
+    size_t tn = strlen(s) + 1;
+    char c;
+
+    te = t = malloc(tn);;
+    while ((c = *s++) != '\0') {
+       switch (c) {
+#if 0  /* XXX can't do this */
+       case '\\':      /* escape */
+           c = *s++;
+           break;
+#endif
+       case '!':
+           if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+               break;
+           if ((a = findNextArg(con, 1, 1)) == NULL)
+               break;
+           s += 3;
+
+           alen = strlen(a);
+           tn += alen;
+           *te = '\0';
+           t = realloc(t, tn);
+           te = t + strlen(t);
+           strncpy(te, a, alen); te += alen;
+           continue;
+           /*@notreached@*/ break;
+       default:
+           break;
+       }
+       *te++ = c;
+    }
+    *te = '\0';
+    t = realloc(t, strlen(t)+1);       /* XXX memory leak, hard to plug */
+    return t;
+}
+
+static void poptStripArg(poptContext con, int which)
+{
+    if(con->arg_strip == NULL) {
+       con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+    }
+    PBM_SET(which, con->arg_strip);
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+    const struct poptOption * opt = NULL;
+    int done = 0;
+
+    /* looks a bit tricky to get rid of alloca properly in this fn */
+#if HAVE_ALLOCA_H
+#define ALLOCA(x) alloca(x)
+#else
+#define ALLOCA(x) malloc(x)
+#endif
+
+
+    while (!done) {
+       const char * origOptString = NULL;
+       poptCallbackType cb = NULL;
+       const void * cbData = NULL;
+       const char * longArg = NULL;
+       int canstrip = 0;
+
+       while (!con->os->nextCharArg && con->os->next == con->os->argc
+               && con->os > con->optionStack) {
+           cleanOSE(con->os--);
+       }
+       if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+           invokeCallbacks(con, con->options, 1);
+           if (con->doExec) execCommand(con);
+           return -1;
+       }
+
+       /* Process next long option */
+       if (!con->os->nextCharArg) {
+           char * localOptString, * optString;
+           int thisopt;
+
+           if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+               con->os->next++;
+               continue;
+           }
+           thisopt=con->os->next;
+           origOptString = con->os->argv[con->os->next++];
+
+           if (con->restLeftover || *origOptString != '-') {
+               con->leftovers[con->numLeftovers++] = origOptString;
+               if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+                   con->restLeftover = 1;
+               continue;
+           }
+
+           /* Make a copy we can hack at */
+           localOptString = optString =
+                       strcpy(ALLOCA(strlen(origOptString) + 1),
+                       origOptString);
+
+           if (!optString[0])
+               return POPT_ERROR_BADOPT;
+
+           if (optString[1] == '-' && !optString[2]) {
+               con->restLeftover = 1;
+               continue;
+           } else {
+               char *oe;
+               int singleDash;
+
+               optString++;
+               if (*optString == '-')
+                   singleDash = 0, optString++;
+               else
+                   singleDash = 1;
+
+               /* XXX aliases with arg substitution need "--alias=arg" */
+               if (handleAlias(con, optString, '\0', NULL))
+                   continue;
+               if (handleExec(con, optString, '\0'))
+                   continue;
+
+               /* Check for "--long=arg" option. */
+               for (oe = optString; *oe && *oe != '='; oe++)
+                   ;
+               if (*oe == '=') {
+                   *oe++ = '\0';
+                   /* XXX longArg is mapped back to persistent storage. */
+                   longArg = origOptString + (oe - localOptString);
+               }
+
+               opt = findOption(con->options, optString, '\0', &cb, &cbData,
+                                singleDash);
+               if (!opt && !singleDash)
+                   return POPT_ERROR_BADOPT;
+           }
+
+           if (!opt) {
+               con->os->nextCharArg = origOptString + 1;
+           } else {
+               if(con->os == con->optionStack &&
+                  opt->argInfo & POPT_ARGFLAG_STRIP) {
+                   canstrip = 1;
+                   poptStripArg(con, thisopt);
+               }
+           }
+       }
+
+       /* Process next short option */
+       if (con->os->nextCharArg) {
+           origOptString = con->os->nextCharArg;
+
+           con->os->nextCharArg = NULL;
+
+           if (handleAlias(con, NULL, *origOptString,
+                           origOptString + 1)) {
+               origOptString++;
+               continue;
+           }
+           if (handleExec(con, NULL, *origOptString))
+               continue;
+
+           opt = findOption(con->options, NULL, *origOptString, &cb,
+                            &cbData, 0);
+           if (!opt)
+               return POPT_ERROR_BADOPT;
+
+           origOptString++;
+           if (*origOptString)
+               con->os->nextCharArg = origOptString;
+       }
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+           *((int *)opt->arg) = 1;
+       } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+           if (opt->arg)
+               *((int *) opt->arg) = opt->val;
+       } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+           if (con->os->nextArg) {
+               xfree(con->os->nextArg);
+               con->os->nextArg = NULL;
+           }
+           if (longArg) {
+               con->os->nextArg = expandNextArg(con, longArg);
+           } else if (con->os->nextCharArg) {
+               con->os->nextArg = expandNextArg(con, con->os->nextCharArg);
+               con->os->nextCharArg = NULL;
+           } else {
+               while (con->os->next == con->os->argc &&
+                      con->os > con->optionStack) {
+                   cleanOSE(con->os--);
+               }
+               if (con->os->next == con->os->argc)
+                   return POPT_ERROR_NOARG;
+
+               /* make sure this isn't part of a short arg or the
+                   result of an alias expansion */
+               if(con->os == con->optionStack &&
+                  opt->argInfo & POPT_ARGFLAG_STRIP &&
+                  canstrip) {
+                   poptStripArg(con, con->os->next);
+               }
+               
+               con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]);
+           }
+
+           if (opt->arg) {
+               long aLong;
+               char *end;
+
+               switch (opt->argInfo & POPT_ARG_MASK) {
+                 case POPT_ARG_STRING:
+                   /* XXX memory leak, hard to plug */
+                   *((const char **) opt->arg) = xstrdup(con->os->nextArg);
+                   break;
+
+                 case POPT_ARG_INT:
+                 case POPT_ARG_LONG:
+                   aLong = strtol(con->os->nextArg, &end, 0);
+                   if (!(end && *end == '\0'))
+                       return POPT_ERROR_BADNUMBER;
+
+                   if (aLong == LONG_MIN || aLong == LONG_MAX)
+                       return POPT_ERROR_OVERFLOW;
+                   if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+                       *((long *) opt->arg) = aLong;
+                   } else {
+                       if (aLong > INT_MAX || aLong < INT_MIN)
+                           return POPT_ERROR_OVERFLOW;
+                       *((int *) opt->arg) = aLong;
+                   }
+                   break;
+
+                 default:
+                   fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+                     opt->argInfo & POPT_ARG_MASK);
+                   exit(EXIT_FAILURE);
+               }
+           }
+       }
+
+       if (cb)
+           cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+       else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+           done = 1;
+
+       if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+           con->finalArgvAlloced += 10;
+           con->finalArgv = realloc(con->finalArgv,
+                           sizeof(*con->finalArgv) * con->finalArgvAlloced);
+       }
+
+       {    char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+           if (opt->longName)
+               sprintf(s, "--%s", opt->longName);
+           else
+               sprintf(s, "-%c", opt->shortName);
+           con->finalArgv[con->finalArgvCount++] = s;
+       }
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
+                    && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) {
+           con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg);
+       }
+    }
+
+    return opt->val;
+}
+
+const char * poptGetOptArg(poptContext con) {
+    const char * ret = con->os->nextArg;
+    con->os->nextArg = NULL;
+    return ret;
+}
+
+const char * poptGetArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return con->leftovers[con->nextLeftover++];
+}
+
+const char * poptPeekArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return con->leftovers[con->nextLeftover];
+}
+
+const char ** poptGetArgs(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+
+    /* some apps like [like RPM ;-) ] need this NULL terminated */
+    con->leftovers[con->numLeftovers] = NULL;
+
+    return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+    int i;
+
+    poptResetContext(con);
+    if (con->os->argb) free(con->os->argb);
+
+    for (i = 0; i < con->numAliases; i++) {
+       if (con->aliases[i].longName) xfree(con->aliases[i].longName);
+       free(con->aliases[i].argv);
+    }
+
+    for (i = 0; i < con->numExecs; i++) {
+       if (con->execs[i].longName) xfree(con->execs[i].longName);
+       xfree(con->execs[i].script);
+    }
+    if (con->execs) xfree(con->execs);
+
+    free(con->leftovers);
+    free(con->finalArgv);
+    if (con->appName) xfree(con->appName);
+    if (con->aliases) free(con->aliases);
+    if (con->otherHelp) xfree(con->otherHelp);
+    if (con->execPath) xfree(con->execPath);
+    if (con->arg_strip) PBM_FREE(con->arg_strip);
+    
+    free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias,
+               /*@unused@*/ int flags)
+{
+    int aliasNum = con->numAliases++;
+    struct poptAlias * alias;
+
+    /* SunOS won't realloc(NULL, ...) */
+    if (!con->aliases)
+       con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+    else
+       con->aliases = realloc(con->aliases,
+                              sizeof(newAlias) * con->numAliases);
+    alias = con->aliases + aliasNum;
+
+    alias->longName = (newAlias.longName)
+       ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName)
+       : NULL;
+    alias->shortName = newAlias.shortName;
+    alias->argc = newAlias.argc;
+    alias->argv = newAlias.argv;
+
+    return 0;
+}
+
+const char * poptBadOption(poptContext con, int flags) {
+    struct optionStackEntry * os;
+
+    if (flags & POPT_BADOPTION_NOALIAS)
+       os = con->optionStack;
+    else
+       os = con->os;
+
+    return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+
+const char *poptStrerror(const int error) {
+    switch (error) {
+      case POPT_ERROR_NOARG:
+       return POPT_("missing argument");
+      case POPT_ERROR_BADOPT:
+       return POPT_("unknown option");
+      case POPT_ERROR_OPTSTOODEEP:
+       return POPT_("aliases nested too deeply");
+      case POPT_ERROR_BADQUOTE:
+       return POPT_("error in paramter quoting");
+      case POPT_ERROR_BADNUMBER:
+       return POPT_("invalid numeric value");
+      case POPT_ERROR_OVERFLOW:
+       return POPT_("number too large or too small");
+      case POPT_ERROR_ERRNO:
+       return strerror(errno);
+      default:
+       return POPT_("unknown error");
+    }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv) {
+    int argc;
+
+    if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    for (argc = 0; argv[argc]; argc++)
+       ;
+
+    con->os++;
+    con->os->next = 0;
+    con->os->nextArg = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->currAlias = NULL;
+    poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+    con->os->argb = NULL;
+    con->os->stuffed = 1;
+
+    return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+    return con->os->argv[0];
+}
+
+int poptStrippedArgv(poptContext con, int argc, char **argv)
+{
+    int i,j=1, numargs=argc;
+    
+    for(i=1; i<argc; i++) {
+       if(PBM_ISSET(i, con->arg_strip)) {
+           numargs--;
+       }
+    }
+    
+    for(i=1; i<argc; i++) {
+       if(PBM_ISSET(i, con->arg_strip)) {
+           continue;
+       } else {
+           if(j<numargs) {
+               argv[j++]=argv[i];
+           } else {
+               argv[j++]='\0';
+           }
+       }
+    }
+    
+    return(numargs);
+}
diff --git a/source4/popt/popt.h b/source4/popt/popt.h
new file mode 100644 (file)
index 0000000..c33ceda
--- /dev/null
@@ -0,0 +1,130 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>                     /* for FILE * */
+
+#define POPT_OPTION_DEPTH      10
+
+#define POPT_ARG_NONE          0
+#define POPT_ARG_STRING                1
+#define POPT_ARG_INT           2
+#define POPT_ARG_LONG          3
+#define POPT_ARG_INCLUDE_TABLE 4       /* arg points to table */
+#define POPT_ARG_CALLBACK      5       /* table-wide callback... must be
+                                          set first in table; arg points 
+                                          to callback, descrip points to 
+                                          callback data to pass */
+#define POPT_ARG_INTL_DOMAIN    6       /* set the translation domain
+                                          for this table and any
+                                          included tables; arg points
+                                          to the domain string */
+#define POPT_ARG_VAL           7       /* arg should take value val */
+#define POPT_ARG_MASK          0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH   0x80000000  /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000  /* don't show in help/usage */
+#define POPT_ARGFLAG_STRIP     0x20000000  /* strip this arg from argv (only applies to long args) */
+#define POPT_CBFLAG_PRE                0x80000000  /* call the callback before parse */
+#define POPT_CBFLAG_POST       0x40000000  /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA   0x20000000  /* use data from the include line,
+                                              not the subtable */
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER   -17
+#define POPT_ERROR_OVERFLOW    -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS  (1 << 0)  /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC   (1 << 0)  /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST        (1 << 1)  /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+    /*@observer@*/ /*@null@*/ const char * longName;   /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argInfo;
+    /*@shared@*/ /*@null@*/ void * arg;                /* depends on argInfo */
+    int val;                   /* 0 means don't return, just update flag */
+    /*@shared@*/ /*@null@*/ const char * descrip;      /* description for autohelp -- may be NULL */
+    /*@shared@*/ /*@null@*/ const char * argDescrip;   /* argument description for autohelp */
+};
+
+struct poptAlias {
+    /*@owned@*/ /*@null@*/ const char * longName;      /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argc;
+    /*@owned@*/ const char ** argv;            /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+                       0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE, 
+                         POPT_CALLBACK_REASON_POST,
+                         POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con, 
+                                enum poptCallbackReason reason,
+                                const struct poptOption * opt,
+                                const char * arg, const void * data);
+
+/*@only@*/ poptContext poptGetContext(/*@keep@*/ const char * name,
+               int argc, /*@keep@*/ const char ** argv,
+               /*@keep@*/ const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+/*@observer@*/ /*@null@*/ const char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+/*@observer@*/ /*@null@*/ const char * poptGetArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char * poptPeekArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+/*@observer@*/ const char * poptBadOption(poptContext con, int flags);
+void poptFreeContext( /*@only@*/ poptContext con);
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, const char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment 
+   vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+   the same as " and both may include \ quotes */
+int poptDupArgv(int argc, const char **argv,
+               /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+int poptParseArgvString(const char * s,
+               /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+/*@observer@*/ const char *poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+/*@observer@*/ const char * poptGetInvocationName(poptContext con);
+/* shuffles argv pointers to remove stripped args, returns new argc */
+int poptStrippedArgv(poptContext con, int argc, char **argv);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/source4/popt/poptconfig.c b/source4/popt/poptconfig.c
new file mode 100644 (file)
index 0000000..eb76941
--- /dev/null
@@ -0,0 +1,142 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+    int nameLength = strlen(con->appName);
+    char * opt;
+    struct poptAlias alias;
+    char * entryType;
+    char * longName = NULL;
+    char shortName = '\0';
+    
+    if (strncmp(line, con->appName, nameLength)) return;
+    line += nameLength;
+    if (!*line || !isspace(*line)) return;
+    while (*line && isspace(*line)) line++;
+    entryType = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+    opt = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+
+    if (opt[0] == '-' && opt[1] == '-')
+       longName = opt + 2;
+    else if (opt[0] == '-' && !opt[2])
+       shortName = opt[1];
+
+    if (!strcmp(entryType, "alias")) {
+       if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+       alias.longName = longName, alias.shortName = shortName;
+       poptAddAlias(con, alias, 0);
+    } else if (!strcmp(entryType, "exec")) {
+       con->execs = realloc(con->execs,
+                               sizeof(*con->execs) * (con->numExecs + 1));
+       if (longName)
+           con->execs[con->numExecs].longName = xstrdup(longName);
+       else
+           con->execs[con->numExecs].longName = NULL;
+
+       con->execs[con->numExecs].shortName = shortName;
+       con->execs[con->numExecs].script = xstrdup(line);
+       
+       con->numExecs++;
+    }
+}
+
+int poptReadConfigFile(poptContext con, const char * fn) {
+    char * file=NULL, * chptr, * end;
+    char * buf=NULL, * dst;
+    int fd, rc;
+    int fileLength;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+       if (errno == ENOENT)
+           return 0;
+       else 
+           return POPT_ERROR_ERRNO;
+    }
+
+    fileLength = lseek(fd, 0, SEEK_END);
+    (void) lseek(fd, 0, 0);
+
+    file = malloc(fileLength + 1);
+    if (read(fd, file, fileLength) != fileLength) {
+       rc = errno;
+       close(fd);
+       errno = rc;
+       if (file) free(file);
+       return POPT_ERROR_ERRNO;
+    }
+    close(fd);
+
+    dst = buf = malloc(fileLength + 1);
+
+    chptr = file;
+    end = (file + fileLength);
+    while (chptr < end) {
+       switch (*chptr) {
+         case '\n':
+           *dst = '\0';
+           dst = buf;
+           while (*dst && isspace(*dst)) dst++;
+           if (*dst && *dst != '#') {
+               configLine(con, dst);
+           }
+           chptr++;
+           break;
+         case '\\':
+           *dst++ = *chptr++;
+           if (chptr < end) {
+               if (*chptr == '\n') 
+                   dst--, chptr++;     
+                   /* \ at the end of a line does not insert a \n */
+               else
+                   *dst++ = *chptr++;
+           }
+           break;
+         default:
+           *dst++ = *chptr++;
+           break;
+       }
+    }
+
+    free(file);
+    free(buf);
+
+    return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) {
+    char * fn, * home;
+    int rc;
+
+    if (!con->appName) return 0;
+
+    rc = poptReadConfigFile(con, "/etc/popt");
+    if (rc) return rc;
+    if (getuid() != geteuid()) return 0;
+
+    if ((home = getenv("HOME"))) {
+       fn = malloc(strlen(home) + 20);
+       strcpy(fn, home);
+       strcat(fn, "/.popt");
+       rc = poptReadConfigFile(con, fn);
+       free(fn);
+       if (rc) return rc;
+    }
+
+    return 0;
+}
+
diff --git a/source4/popt/popthelp.c b/source4/popt/popthelp.c
new file mode 100644 (file)
index 0000000..6b790a6
--- /dev/null
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con,
+               /*@unused@*/ enum poptCallbackReason foo,
+               struct poptOption * key, 
+               /*@unused@*/ const char * arg, /*@unused@*/ void * data) {
+    if (key->shortName== '?')
+       poptPrintHelp(con, stdout, 0);
+    else
+       poptPrintUsage(con, stdout, 0);
+    exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+    { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+    { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+    { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+    { NULL, '\0', 0, NULL, 0, NULL, NULL }
+} ;
+
+
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(const struct poptOption *table)
+{
+  const struct poptOption *opt;
+
+  for(opt = table;
+      opt->longName || opt->shortName || opt->arg;
+      opt++) {
+    if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
+      return opt->arg;
+  }
+
+  return NULL;
+}
+
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt, const char *translation_domain)
+{
+    if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+    if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+       if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+    if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+    return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol, 
+                            const struct poptOption * opt,
+                            const char *translation_domain) {
+    int indentLength = maxLeftCol + 5;
+    int lineLength = 79 - indentLength;
+    const char * help = D_(translation_domain, opt->descrip);
+    int helpLength;
+    const char * ch;
+    char format[10];
+    char * left;
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+    left = malloc(maxLeftCol + 1);
+    *left = '\0';
+
+    if (opt->longName && opt->shortName)
+       sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+    else if (opt->shortName) 
+       sprintf(left, "-%c", opt->shortName);
+    else if (opt->longName)
+       sprintf(left, "--%s", opt->longName);
+    if (!*left) return ;
+    if (argDescrip) {
+       strcat(left, "=");
+       strcat(left, argDescrip);
+    }
+
+    if (help)
+       fprintf(f,"  %-*s   ", maxLeftCol, left);
+    else {
+       fprintf(f,"  %s\n", left); 
+       goto out;
+    }
+
+    helpLength = strlen(help);
+    while (helpLength > lineLength) {
+       ch = help + lineLength - 1;
+       while (ch > help && !isspace(*ch)) ch--;
+       if (ch == help) break;          /* give up */
+       while (ch > (help + 1) && isspace(*ch)) ch--;
+       ch++;
+
+       sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+       fprintf(f, format, help, " ");
+       help = ch;
+       while (isspace(*help) && *help) help++;
+       helpLength = strlen(help);
+    }
+
+    if (helpLength) fprintf(f, "%s\n", help);
+
+out:
+    free(left);
+}
+
+static int maxArgWidth(const struct poptOption * opt,
+                      const char * translation_domain) {
+    int max = 0;
+    int this;
+    const char * s;
+    
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           this = maxArgWidth(opt->arg, translation_domain);
+           if (this > max) max = this;
+       } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+           this = opt->shortName ? 2 : 0;
+           if (opt->longName) {
+               if (this) this += 2;
+               this += strlen(opt->longName) + 2;
+           }
+
+           s = getArgDescrip(opt, translation_domain);
+           if (s)
+               this += strlen(s) + 1;
+           if (this > max) max = this;
+       }
+
+       opt++;
+    }
+    
+    return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table, 
+                           int left,
+                           const char *translation_domain) {
+    const struct poptOption * opt;
+    const char *sub_transdom;
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->longName || opt->shortName) && 
+           !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+           singleOptionHelp(f, left, opt, translation_domain);
+       opt++;
+    }
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           sub_transdom = getTableTranslationDomain(opt->arg);
+           if(!sub_transdom)
+               sub_transdom = translation_domain;
+           
+           if (opt->descrip)
+               fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+           singleTableHelp(f, opt->arg, left, sub_transdom);
+       }
+       opt++;
+    }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+    int len = 6;
+    const char * fn;
+
+    fprintf(f, POPT_("Usage:"));
+    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+       fn = con->optionStack->argv[0];
+       if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+       fprintf(f, " %s", fn);
+       len += strlen(fn) + 1;
+    }
+
+    return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) {
+    int leftColWidth;
+
+    showHelpIntro(con, f);
+    if (con->otherHelp)
+       fprintf(f, " %s\n", con->otherHelp);
+    else
+       fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+    leftColWidth = maxArgWidth(con->options, NULL);
+    singleTableHelp(f, con->options, leftColWidth, NULL);
+}
+
+static int singleOptionUsage(FILE * f, int cursor, 
+                            const struct poptOption * opt,
+                            const char *translation_domain) {
+    int len = 3;
+    char shortStr[2] = { '\0', '\0' };
+    const char * item = shortStr;
+    const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+    if (opt->shortName) {
+       if (!(opt->argInfo & POPT_ARG_MASK)) 
+           return cursor;      /* we did these already */
+       len++;
+       *shortStr = opt->shortName;
+       shortStr[1] = '\0';
+    } else if (opt->longName) {
+       len += 1 + strlen(opt->longName);
+       item = opt->longName;
+    }
+
+    if (len == 3) return cursor;
+
+    if (argDescrip) 
+       len += strlen(argDescrip) + 1;
+
+    if ((cursor + len) > 79) {
+       fprintf(f, "\n       ");
+       cursor = 7;
+    } 
+
+    fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+           argDescrip ? (opt->shortName ? " " : "=") : "",
+           argDescrip ? argDescrip : "");
+
+    return cursor + len + 1;
+}
+
+static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
+                    const char *translation_domain) {
+    const struct poptOption * opt;
+    
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
+           translation_domain = (const char *)opt->arg;
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) 
+           cursor = singleTableUsage(f, cursor, opt->arg,
+                                     translation_domain);
+       else if ((opt->longName || opt->shortName) && 
+                !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+         cursor = singleOptionUsage(f, cursor, opt, translation_domain);
+
+       opt++;
+    }
+
+    return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f, 
+                           char * str) {
+    char s[300];               /* this is larger then the ascii set, so
+                                  it should do just fine */
+
+    s[0] = '\0';
+    if (str == NULL) {
+       memset(s, 0, sizeof(s));
+       str = s;
+    }
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+           str[strlen(str)] = opt->shortName;
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+           showShortOptions(opt->arg, f, str);
+
+       opt++;
+    } 
+
+    if (s != str || !*s)
+       return 0;
+
+    fprintf(f, " [-%s]", s);
+    return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) {
+    int cursor;
+
+    cursor = showHelpIntro(con, f);
+    cursor += showShortOptions(con->options, f, NULL);
+    singleTableUsage(f, cursor, con->options, NULL);
+
+    if (con->otherHelp) {
+       cursor += strlen(con->otherHelp) + 1;
+       if (cursor > 79) fprintf(f, "\n       ");
+       fprintf(f, " %s", con->otherHelp);
+    }
+
+    fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+    if (con->otherHelp) xfree(con->otherHelp);
+    con->otherHelp = xstrdup(text);
+}
diff --git a/source4/popt/poptint.h b/source4/popt/poptint.h
new file mode 100644 (file)
index 0000000..1847ffa
--- /dev/null
@@ -0,0 +1,71 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/* Bit mask macros. */
+typedef        unsigned int __pbm_bits;
+#define        __PBM_NBITS             (8 * sizeof (__pbm_bits))
+#define        __PBM_IX(d)             ((d) / __PBM_NBITS)
+#define __PBM_MASK(d)          ((__pbm_bits) 1 << ((d) % __PBM_NBITS))
+typedef struct {
+    __pbm_bits bits[1];
+} pbm_set;
+#define        __PBM_BITS(set) ((set)->bits)
+
+#define        PBM_ALLOC(d)    calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define        PBM_FREE(s)     free(s);
+#define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+    int argc;
+    /*@only@*/ const char ** argv;
+    /*@only@*/ pbm_set * argb;
+    int next;
+    /*@only@*/ const char * nextArg;
+    /*@keep@*/ const char * nextCharArg;
+    /*@dependent@*/ struct poptAlias * currAlias;
+    int stuffed;
+};
+
+struct execEntry {
+    const char * longName;
+    char shortName;
+    const char * script;
+};
+
+struct poptContext_s {
+    struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+    /*@dependent@*/ struct optionStackEntry * os;
+    /*@owned@*/ const char ** leftovers;
+    int numLeftovers;
+    int nextLeftover;
+    /*@keep@*/ const struct poptOption * options;
+    int restLeftover;
+    /*@only@*/ const char * appName;
+    /*@only@*/ struct poptAlias * aliases;
+    int numAliases;
+    int flags;
+    struct execEntry * execs;
+    int numExecs;
+    /*@only@*/ const char ** finalArgv;
+    int finalArgvCount;
+    int finalArgvAlloced;
+    /*@dependent@*/ struct execEntry * doExec;
+    /*@only@*/ const char * execPath;
+    int execAbsolute;
+    /*@only@*/ const char * otherHelp;
+    pbm_set * arg_strip;
+};
+
+#define        xfree(_a)       free((void *)_a)
+
+#define POPT_(foo) (foo)
+#define D_(dom, str) (str)
+#define N_(foo) (foo)
+
+#endif
diff --git a/source4/popt/poptparse.c b/source4/popt/poptparse.c
new file mode 100644 (file)
index 0000000..8f00769
--- /dev/null
@@ -0,0 +1,102 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+int poptDupArgv(int argc, const char **argv,
+               int * argcPtr, const char *** argvPtr)
+{
+    size_t nb = (argc + 1) * sizeof(*argv);
+    const char ** argv2;
+    char * dst;
+    int i;
+
+    for (i = 0; i < argc; i++) {
+       if (argv[i] == NULL)
+           return POPT_ERROR_NOARG;
+       nb += strlen(argv[i]) + 1;
+    }
+       
+    dst = malloc(nb);
+    argv2 = (void *) dst;
+    dst += (argc + 1) * sizeof(*argv);
+
+    for (i = 0; i < argc; i++) {
+       argv2[i] = dst;
+       dst += strlen(strcpy(dst, argv[i])) + 1;
+    }
+    argv2[argc] = NULL;
+
+    *argvPtr = argv2;
+    *argcPtr = argc;
+    return 0;
+}
+
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+    const char * src;
+    char quote = '\0';
+    int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+    const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+    int argc = 0;
+    int buflen = strlen(s) + 1;
+    char *buf0 = calloc(buflen, 1);
+    char *buf = buf0;
+
+    argv[argc] = buf;
+
+    for (src = s; *src; src++) {
+       if (quote == *src) {
+           quote = '\0';
+       } else if (quote) {
+           if (*src == '\\') {
+               src++;
+               if (!*src) {
+                   free(argv);
+                   free(buf0);
+                   return POPT_ERROR_BADQUOTE;
+               }
+               if (*src != quote) *buf++ = '\\';
+           }
+           *buf++ = *src;
+       } else if (isspace(*src)) {
+           if (*argv[argc]) {
+               buf++, argc++;
+               if (argc == argvAlloced) {
+                   argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+                   argv = realloc(argv, sizeof(*argv) * argvAlloced);
+               }
+               argv[argc] = buf;
+           }
+       } else switch (*src) {
+         case '"':
+         case '\'':
+           quote = *src;
+           break;
+         case '\\':
+           src++;
+           if (!*src) {
+               free(argv);
+               free(buf0);
+               return POPT_ERROR_BADQUOTE;
+           }
+           /*@fallthrough@*/
+         default:
+           *buf++ = *src;
+           break;
+       }
+    }
+
+    if (strlen(argv[argc])) {
+       argc++, buf++;
+    }
+
+    (void) poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+    free(argv);
+    free(buf0);
+    return 0;
+}
diff --git a/source4/popt/system.h b/source4/popt/system.h
new file mode 100644 (file)
index 0000000..059c045
--- /dev/null
@@ -0,0 +1,53 @@
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H 
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+/* AIX requires this to be the first thing in the file.  */ 
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+#pragma alloca
+#  else
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif
+#  endif
+# endif
+#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define alloca __builtin_alloca
+#endif
+
+/*@only@*/ char * xstrdup (const char *str);
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+#define        vmefail()       (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xstrdup(_str)   (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
+#else
+#define        xstrdup(_str)   strdup(_str)
+#endif  /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+
+#include "popt.h"
diff --git a/source4/printing/.cvsignore b/source4/printing/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/printing/load.c b/source4/printing/load.c
new file mode 100644 (file)
index 0000000..cd90cbb
--- /dev/null
@@ -0,0 +1,73 @@
+/* 
+   Unix SMB/CIFS implementation.
+   load printer lists
+   Copyright (C) Andrew Tridgell 1992-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"
+
+
+/***************************************************************************
+auto-load printer services
+***************************************************************************/
+void add_all_printers(void)
+{
+       int printers = lp_servicenumber(PRINTERS_NAME);
+
+       if (printers < 0) return;
+
+       pcap_printer_fn(lp_add_one_printer);
+}
+
+/***************************************************************************
+auto-load some homes and printer services
+***************************************************************************/
+static void add_auto_printers(void)
+{
+       const char *p;
+       int printers;
+       char *str = strdup(lp_auto_services());
+
+       if (!str) return;
+
+       printers = lp_servicenumber(PRINTERS_NAME);
+
+       if (printers < 0) {
+               SAFE_FREE(str);
+               return;
+       }
+       
+       for (p=strtok(str,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) {
+               if (lp_servicenumber(p) >= 0) continue;
+               
+               if (pcap_printername_ok(p,NULL)) {
+                       lp_add_printer(p,printers);
+               }
+       }
+
+    SAFE_FREE(str);
+}
+
+/***************************************************************************
+load automatic printer services
+***************************************************************************/
+void load_printers(void)
+{
+       add_auto_printers();
+       if (lp_load_printers())
+               add_all_printers();
+}
diff --git a/source4/printing/lpq_parse.c b/source4/printing/lpq_parse.c
new file mode 100644 (file)
index 0000000..4b91b8a
--- /dev/null
@@ -0,0 +1,1099 @@
+/* 
+   Unix SMB/CIFS implementation.
+   lpq parsing routines
+   Copyright (C) Andrew Tridgell 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"
+
+static const char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
+
+
+/*******************************************************************
+process time fields
+********************************************************************/
+static time_t EntryTime(fstring tok[], int ptr, int count, int minimum)
+{
+  time_t jobtime,jobtime1;
+
+  jobtime = time(NULL);                /* default case: take current time */
+  if (count >= minimum) {
+    struct tm *t;
+    int i, day, hour, min, sec;
+    char   *c;
+
+    for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
+    if (i<12) {
+      t = localtime(&jobtime);
+      day = atoi(tok[ptr+1]);
+      c=(char *)(tok[ptr+2]);
+      *(c+2)=0;
+      hour = atoi(c);
+      *(c+5)=0;
+      min = atoi(c+3);
+      if(*(c+6) != 0)sec = atoi(c+6);
+      else  sec=0;
+
+      if ((t->tm_mon < i)||
+         ((t->tm_mon == i)&&
+          ((t->tm_mday < day)||
+           ((t->tm_mday == day)&&
+            (t->tm_hour*60+t->tm_min < hour*60+min)))))
+       t->tm_year--;           /* last year's print job */
+
+      t->tm_mon = i;
+      t->tm_mday = day;
+      t->tm_hour = hour;
+      t->tm_min = min;
+      t->tm_sec = sec;
+      jobtime1 = mktime(t);
+      if (jobtime1 != (time_t)-1)
+       jobtime = jobtime1;
+    }
+  }
+  return jobtime;
+}
+
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under bsd
+
+Warning: no daemon present
+Rank   Owner      Job  Files                                 Total Size
+1st    tridge     148  README                                8096 bytes
+
+here is an example of lpq output under osf/1
+
+Warning: no daemon present
+Rank   Pri Owner      Job  Files                             Total Size
+1st    0   tridge     148  README                            8096 bytes
+
+
+<allan@umich.edu> June 30, 1998.
+Modified to handle file names with spaces, like the parse_lpq_lprng code
+further below.
+****************************************************************************/
+static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
+{
+#ifdef OSF1
+#define        RANKTOK 0
+#define        PRIOTOK 1
+#define        USERTOK 2
+#define        JOBTOK  3
+#define        FILETOK 4
+#define        TOTALTOK (count - 2)
+#define        NTOK    6
+#define        MAXTOK  128
+#else  /* OSF1 */
+#define        RANKTOK 0
+#define        USERTOK 1
+#define        JOBTOK  2
+#define        FILETOK 3
+#define        TOTALTOK (count - 2)
+#define        NTOK    5
+#define        MAXTOK  128
+#endif /* OSF1 */
+
+  char *tok[MAXTOK];
+  int  count = 0;
+  pstring line2;
+
+  pstrcpy(line2,line);
+
+#ifdef OSF1
+  {
+    size_t length;
+    length = strlen(line2);
+    if (line2[length-3] == ':')
+      return(False);
+  }
+#endif /* OSF1 */
+
+  /* FIXME: Use next_token rather than strtok! */
+  tok[0] = strtok(line2," \t");
+  count++;
+
+  while (((tok[count] = strtok(NULL," \t")) != NULL) && (count < MAXTOK)) {
+    count++;
+  }
+
+  /* we must get at least NTOK tokens */
+  if (count < NTOK)
+    return(False);
+
+  /* the Job and Total columns must be integer */
+  if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False);
+
+  buf->job = atoi(tok[JOBTOK]);
+  buf->size = atoi(tok[TOTALTOK]);
+  buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
+  buf->time = time(NULL);
+  StrnCpy(buf->fs_user,tok[USERTOK],sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file,tok[FILETOK],sizeof(buf->fs_file)-1);
+
+  if ((FILETOK + 1) != TOTALTOK) {
+    int i;
+
+    for (i = (FILETOK + 1); i < TOTALTOK; i++) {
+        /* FIXME: Using fstrcat rather than other means is a bit
+         * inefficient; this might be a problem for enormous queues with
+         * many fields. */
+         fstrcat(buf->fs_file, " ");
+         fstrcat(buf->fs_file, tok[i]);
+    }
+    /* Ensure null termination. */
+    fstrterminate(buf->fs_file);
+  }
+
+#ifdef PRIOTOK
+  buf->priority = atoi(tok[PRIOTOK]);
+#else
+  buf->priority = 1;
+#endif
+  return(True);
+}
+
+/*
+<magnus@hum.auc.dk>
+LPRng_time modifies the current date by inserting the hour and minute from
+the lpq output.  The lpq time looks like "23:15:07"
+
+<allan@umich.edu> June 30, 1998.
+Modified to work with the re-written parse_lpq_lprng routine.
+
+<J.P.M.v.Itegem@tue.nl> Dec 17,1999
+Modified to work with lprng 3.16
+With lprng 3.16 The lpq time looks like
+                       "23:15:07"
+                       "23:15:07.100"
+                       "1999-12-16-23:15:07"
+                       "1999-12-16-23:15:07.100"
+
+*/
+static time_t LPRng_time(char *time_string)
+{
+       time_t jobtime;
+       struct tm t;
+
+       jobtime = time(NULL);         /* default case: take current time */
+       t = *localtime(&jobtime);
+
+       if ( atoi(time_string) < 24 ){
+               t.tm_hour = atoi(time_string);
+               t.tm_min = atoi(time_string+3);
+               t.tm_sec = atoi(time_string+6);
+       } else {
+               t.tm_year = atoi(time_string)-1900;
+               t.tm_mon = atoi(time_string+5)-1;
+               t.tm_mday = atoi(time_string+8);
+               t.tm_hour = atoi(time_string+11);
+               t.tm_min = atoi(time_string+14);
+               t.tm_sec = atoi(time_string+17);
+       }    
+       jobtime = mktime(&t);
+
+       return jobtime;
+}
+
+
+/****************************************************************************
+  parse a lprng lpq line
+  <allan@umich.edu> June 30, 1998.
+  Re-wrote this to handle file names with spaces, multiple file names on one
+  lpq line, etc;
+****************************************************************************/
+static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
+{
+#define        LPRNG_RANKTOK   0
+#define        LPRNG_USERTOK   1
+#define        LPRNG_PRIOTOK   2
+#define        LPRNG_JOBTOK    3
+#define        LPRNG_FILETOK   4
+#define        LPRNG_TOTALTOK  (num_tok - 2)
+#define        LPRNG_TIMETOK   (num_tok - 1)
+#define        LPRNG_NTOK      7
+#define        LPRNG_MAXTOK    128 /* PFMA just to keep us from running away. */
+
+  fstring tokarr[LPRNG_MAXTOK];
+  const char *cptr;
+  char *ptr;
+  int  num_tok = 0;
+
+  cptr = line;
+  while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK))
+    num_tok++;
+
+  /* We must get at least LPRNG_NTOK tokens. */
+  if (num_tok < LPRNG_NTOK) {
+    return(False);
+  }
+
+  if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
+    return(False);
+  }
+
+  buf->job  = atoi(tokarr[LPRNG_JOBTOK]);
+  buf->size = atoi(tokarr[LPRNG_TOTALTOK]);
+
+  if (strequal(tokarr[LPRNG_RANKTOK],"active")) {
+    buf->status = LPQ_PRINTING;
+  } else if (strequal(tokarr[LPRNG_RANKTOK],"done")) {
+    buf->status = LPQ_PRINTED;
+  } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) {
+    buf->status = LPQ_QUEUED;
+  } else {
+    buf->status = LPQ_PAUSED;
+  }
+
+  buf->priority = *tokarr[LPRNG_PRIOTOK] -'A';
+
+  buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]);
+
+  StrnCpy(buf->fs_user,tokarr[LPRNG_USERTOK],sizeof(buf->fs_user)-1);
+
+  /* The '@hostname' prevents windows from displaying the printing icon
+   * for the current user on the taskbar.  Plop in a null.
+   */
+
+  if ((ptr = strchr_m(buf->fs_user,'@')) != NULL) {
+    *ptr = '\0';
+  }
+
+  StrnCpy(buf->fs_file,tokarr[LPRNG_FILETOK],sizeof(buf->fs_file)-1);
+
+  if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
+    int i;
+
+    for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
+      /* FIXME: Using fstrcat rather than other means is a bit
+       * inefficient; this might be a problem for enormous queues with
+       * many fields. */
+      fstrcat(buf->fs_file, " ");
+      fstrcat(buf->fs_file, tokarr[i]);
+    }
+    /* Ensure null termination. */
+    fstrterminate(buf->fs_file);
+  }
+
+  return(True);
+}
+
+
+
+/*******************************************************************
+parse lpq on an aix system
+
+Queue   Dev   Status    Job Files              User         PP %   Blks  Cp Rnk
+------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
+lazer   lazer READY
+lazer   lazer RUNNING   537 6297doc.A          kvintus@IE    0 10  2445   1   1
+              QUEUED    538 C.ps               root@IEDVB           124   1   2
+              QUEUED    539 E.ps               root@IEDVB            28   1   3
+              QUEUED    540 L.ps               root@IEDVB           172   1   4
+              QUEUED    541 P.ps               root@IEDVB            22   1   5
+********************************************************************/
+static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
+{
+  fstring tok[11];
+  int count=0;
+  const char *cline = line;
+
+  /* handle the case of "(standard input)" as a filename */
+  string_sub(line,"standard input","STDIN",0);
+  all_string_sub(line,"(","\"",0);
+  all_string_sub(line,")","\"",0);
+
+  for (count=0; 
+       count<10 && 
+              next_token(&cline,tok[count],NULL, sizeof(tok[count])); 
+       count++) ;
+
+  /* we must get 6 tokens */
+  if (count < 10)
+  {
+      if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
+      {
+          /* the 2nd and 5th columns must be integer */
+          if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False);
+          buf->size = atoi(tok[4]) * 1024;
+          /* if the fname contains a space then use STDIN */
+          if (strchr_m(tok[2],' '))
+            fstrcpy(tok[2],"STDIN");
+
+          /* only take the last part of the filename */
+          {
+            fstring tmp;
+            char *p = strrchr_m(tok[2],'/');
+            if (p)
+              {
+                fstrcpy(tmp,p+1);
+                fstrcpy(tok[2],tmp);
+              }
+          }
+
+
+          buf->job = atoi(tok[1]);
+          buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
+         buf->priority = 0;
+          buf->time = time(NULL);
+          StrnCpy(buf->fs_user,tok[3],sizeof(buf->fs_user)-1);
+          StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1);
+      }
+      else
+      {
+          DEBUG(6,("parse_lpq_aix count=%d\n", count));
+          return(False);
+      }
+  }
+  else
+  {
+      /* the 4th and 9th columns must be integer */
+      if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False);
+      buf->size = atoi(tok[8]) * 1024;
+      /* if the fname contains a space then use STDIN */
+      if (strchr_m(tok[4],' '))
+        fstrcpy(tok[4],"STDIN");
+
+      /* only take the last part of the filename */
+      {
+        fstring tmp;
+        char *p = strrchr_m(tok[4],'/');
+        if (p)
+          {
+            fstrcpy(tmp,p+1);
+            fstrcpy(tok[4],tmp);
+          }
+      }
+
+
+      buf->job = atoi(tok[3]);
+      buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
+      buf->priority = 0;
+      buf->time = time(NULL);
+      StrnCpy(buf->fs_user,tok[5],sizeof(buf->fs_user)-1);
+      StrnCpy(buf->fs_file,tok[4],sizeof(buf->fs_file)-1);
+  }
+
+
+  return(True);
+}
+
+
+/****************************************************************************
+parse a lpq line
+here is an example of lpq output under hpux; note there's no space after -o !
+$> lpstat -oljplus
+ljplus-2153         user           priority 0  Jan 19 08:14 on ljplus
+      util.c                                  125697 bytes
+      server.c                               110712 bytes
+ljplus-2154         user           priority 0  Jan 19 08:14 from client
+      (standard input)                          7551 bytes
+****************************************************************************/
+static BOOL parse_lpq_hpux(char *line, print_queue_struct *buf, BOOL first)
+{
+  /* must read two lines to process, therefore keep some values static */
+  static BOOL header_line_ok=False, base_prio_reset=False;
+  static fstring jobuser;
+  static int jobid;
+  static int jobprio;
+  static time_t jobtime;
+  static int jobstat=LPQ_QUEUED;
+  /* to store minimum priority to print, lpstat command should be invoked
+     with -p option first, to work */
+  static int base_prio;
+  int count;
+  char htab = '\011';  
+  const char *cline = line;
+  fstring tok[12];
+
+  /* If a line begins with a horizontal TAB, it is a subline type */
+  
+  if (line[0] == htab) { /* subline */
+    /* check if it contains the base priority */
+    if (!strncmp(line,"\tfence priority : ",18)) {
+       base_prio=atoi(&line[18]);
+       DEBUG(4, ("fence priority set at %d\n", base_prio));
+    }
+    if (!header_line_ok) return (False); /* incorrect header line */
+    /* handle the case of "(standard input)" as a filename */
+    string_sub(line,"standard input","STDIN",0);
+    all_string_sub(line,"(","\"",0);
+    all_string_sub(line,")","\"",0);
+    
+    for (count=0; count<2 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+    /* we must get 2 tokens */
+    if (count < 2) return(False);
+    
+    /* the 2nd column must be integer */
+    if (!isdigit((int)*tok[1])) return(False);
+    
+    /* if the fname contains a space then use STDIN */
+    if (strchr_m(tok[0],' '))
+      fstrcpy(tok[0],"STDIN");
+    
+    buf->size = atoi(tok[1]);
+    StrnCpy(buf->fs_file,tok[0],sizeof(buf->fs_file)-1);
+    
+    /* fill things from header line */
+    buf->time = jobtime;
+    buf->job = jobid;
+    buf->status = jobstat;
+    buf->priority = jobprio;
+    StrnCpy(buf->fs_user,jobuser,sizeof(buf->fs_user)-1);
+    
+    return(True);
+  }
+  else { /* header line */
+    header_line_ok=False; /* reset it */
+    if (first) {
+       if (!base_prio_reset) {
+         base_prio=0; /* reset it */
+         base_prio_reset=True;
+       }
+    }
+    else if (base_prio) base_prio_reset=False;
+    
+    /* handle the dash in the job id */
+    string_sub(line,"-"," ",0);
+    
+    for (count=0; count<12 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+      
+    /* we must get 8 tokens */
+    if (count < 8) return(False);
+    
+    /* first token must be printer name (cannot check ?) */
+    /* the 2nd, 5th & 7th column must be integer */
+    if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False);
+    jobid = atoi(tok[1]);
+    StrnCpy(jobuser,tok[2],sizeof(buf->fs_user)-1);
+    jobprio = atoi(tok[4]);
+    
+    /* process time */
+    jobtime=EntryTime(tok, 5, count, 8);
+    if (jobprio < base_prio) {
+       jobstat = LPQ_PAUSED;
+       DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
+    }
+    else {
+       jobstat = LPQ_QUEUED;
+       if ((count >8) && (((strequal(tok[8],"on")) ||
+                          ((strequal(tok[8],"from")) && 
+                           ((count > 10)&&(strequal(tok[10],"on")))))))
+        jobstat = LPQ_PRINTING;
+    }
+    
+    header_line_ok=True; /* information is correct */
+    return(False); /* need subline info to include into queuelist */
+  }
+}
+
+
+/****************************************************************************
+parse a lpstat line
+
+here is an example of "lpstat -o dcslw" output under sysv
+
+dcslw-896               tridge            4712   Dec 20 10:30:30 on dcslw
+dcslw-897               tridge            4712   Dec 20 10:30:30 being held
+
+****************************************************************************/
+static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
+{
+  fstring tok[9];
+  int count=0;
+  char *p;
+  const char *cline = line;
+
+  /* 
+   * Handle the dash in the job id, but make sure that we skip over
+   * the printer name in case we have a dash in that.
+   * Patch from Dom.Mitchell@palmerharvey.co.uk.
+   */
+
+  /*
+   * Move to the first space.
+   */
+  for (p = line ; !isspace(*p) && *p; p++)
+    ;
+
+  /*
+   * Back up until the last '-' character or
+   * start of line.
+   */
+  for (; (p >= line) && (*p != '-'); p--)
+    ;
+
+  if((p >= line) && (*p == '-'))
+    *p = ' ';
+
+  for (count=0; count<9 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++)
+    ;
+
+  /* we must get 7 tokens */
+  if (count < 7)
+    return(False);
+
+  /* the 2nd and 4th, 6th columns must be integer */
+  if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3]))
+    return(False);
+  if (!isdigit((int)*tok[5]))
+    return(False);
+
+  /* if the user contains a ! then trim the first part of it */  
+  if ((p=strchr_m(tok[2],'!'))) {
+      fstring tmp;
+      fstrcpy(tmp,p+1);
+      fstrcpy(tok[2],tmp);
+  }
+
+  buf->job = atoi(tok[1]);
+  buf->size = atoi(tok[3]);
+  if (count > 7 && strequal(tok[7],"on"))
+    buf->status = LPQ_PRINTING;
+  else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
+    buf->status = LPQ_PAUSED;
+  else
+    buf->status = LPQ_QUEUED;
+  buf->priority = 0;
+  buf->time = EntryTime(tok, 4, count, 7);
+  StrnCpy(buf->fs_user,tok[2],sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1);
+  return(True);
+}
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under qnx
+Spooler: /qnx/spooler, on node 1
+Printer: txt        (ready) 
+0000:     root [job #1    ]   active 1146 bytes        /etc/profile
+0001:     root [job #2    ]    ready 2378 bytes        /etc/install
+0002:     root [job #3    ]    ready 1146 bytes        -- standard input --
+****************************************************************************/
+static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
+{
+  fstring tok[7];
+  int count=0;
+  const char *cline;
+
+  DEBUG(4,("antes [%s]\n", line));
+
+  /* handle the case of "-- standard input --" as a filename */
+  string_sub(line,"standard input","STDIN",0);
+  DEBUG(4,("despues [%s]\n", line));
+  all_string_sub(line,"-- ","\"",0);
+  all_string_sub(line," --","\"",0);
+  DEBUG(4,("despues 1 [%s]\n", line));
+
+  string_sub(line,"[job #","",0);
+  string_sub(line,"]","",0);
+  DEBUG(4,("despues 2 [%s]\n", line));
+
+  for (count=0; count<7 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+  /* we must get 7 tokens */
+  if (count < 7)
+    return(False);
+
+  /* the 3rd and 5th columns must be integer */
+  if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False);
+
+  /* only take the last part of the filename */
+  {
+    fstring tmp;
+    char *p = strrchr_m(tok[6],'/');
+    if (p)
+      {
+       fstrcpy(tmp,p+1);
+       fstrcpy(tok[6],tmp);
+      }
+  }
+       
+
+  buf->job = atoi(tok[2]);
+  buf->size = atoi(tok[4]);
+  buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
+  buf->priority = 0;
+  buf->time = time(NULL);
+  StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1);
+  return(True);
+}
+
+
+/****************************************************************************
+  parse a lpq line for the plp printing system
+  Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
+
+redone by tridge. Here is a sample queue:
+
+Local  Printer 'lp2' (fjall):
+  Printing (started at Jun 15 13:33:58, attempt 1).
+    Rank Owner       Pr Opt  Job Host        Files           Size     Date
+  active tridge      X  -    6   fjall       /etc/hosts      739      Jun 15 13:33
+     3rd tridge      X  -    7   fjall       /etc/hosts      739      Jun 15 13:33
+
+****************************************************************************/
+static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
+{
+  fstring tok[11];
+  int count=0;
+  const char *cline = line;
+
+  /* handle the case of "(standard input)" as a filename */
+  string_sub(line,"stdin","STDIN",0);
+  all_string_sub(line,"(","\"",0);
+  all_string_sub(line,")","\"",0);
+  
+  for (count=0; count<11 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+  /* we must get 11 tokens */
+  if (count < 11)
+    return(False);
+
+  /* the first must be "active" or begin with an integer */
+  if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
+    return(False);
+
+  /* the 5th and 8th must be integer */
+  if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7])) 
+    return(False);
+
+  /* if the fname contains a space then use STDIN */
+  if (strchr_m(tok[6],' '))
+    fstrcpy(tok[6],"STDIN");
+
+  /* only take the last part of the filename */
+  {
+    fstring tmp;
+    char *p = strrchr_m(tok[6],'/');
+    if (p)
+      {
+        fstrcpy(tmp,p+1);
+        fstrcpy(tok[6],tmp);
+      }
+  }
+
+
+  buf->job = atoi(tok[4]);
+
+  buf->size = atoi(tok[7]);
+  if (strchr_m(tok[7],'K'))
+    buf->size *= 1024;
+  if (strchr_m(tok[7],'M'))
+    buf->size *= 1024*1024;
+
+  buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
+  buf->priority = 0;
+  buf->time = time(NULL);
+  StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1);
+  return(True);
+}
+
+/****************************************************************************
+parse a qstat line
+
+here is an example of "qstat -l -d qms" output under softq
+
+Queue qms: 2 jobs; daemon active (313); enabled; accepting;
+ job-ID   submission-time     pri     size owner      title 
+205980: H 98/03/09 13:04:05     0    15733 stephenf   chap1.ps
+206086:>  98/03/12 17:24:40     0      659 chris      -
+206087:   98/03/12 17:24:45     0     4876 chris      -
+Total:      21268 bytes in queue
+
+
+****************************************************************************/
+static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first)
+{
+  fstring tok[10];
+  int count=0;
+  const char *cline = line;
+
+  /* mung all the ":"s to spaces*/
+  string_sub(line,":"," ",0);
+  
+  for (count=0; count<10 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+  /* we must get 9 tokens */
+  if (count < 9)
+    return(False);
+
+  /* the 1st and 7th columns must be integer */
+  if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6]))  return(False);
+  /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be
+   * integer, else it's the 6th and 7th that must be
+   */
+  if (*tok[1] == 'H' || *tok[1] == '>')
+    {
+      if (!isdigit((int)*tok[7]))
+        return(False);
+      buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED;
+      count = 1;
+    }
+  else
+    {
+      if (!isdigit((int)*tok[5]))
+        return(False);
+      buf->status = LPQ_QUEUED;
+      count = 0;
+    }
+       
+
+  buf->job = atoi(tok[0]);
+  buf->size = atoi(tok[count+6]);
+  buf->priority = atoi(tok[count+5]);
+  StrnCpy(buf->fs_user,tok[count+7],sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file,tok[count+8],sizeof(buf->fs_file)-1);
+  buf->time = time(NULL);              /* default case: take current time */
+  {
+    time_t jobtime;
+    struct tm *t;
+
+    t = localtime(&buf->time);
+    t->tm_mday = atoi(tok[count+2]+6);
+    t->tm_mon  = atoi(tok[count+2]+3);
+    switch (*tok[count+2])
+    {
+    case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]); break;
+    default:                t->tm_year = atoi(tok[count+2]); break;
+    }
+
+    t->tm_hour = atoi(tok[count+3]);
+    t->tm_min = atoi(tok[count+4]);
+    t->tm_sec = atoi(tok[count+5]);
+    jobtime = mktime(t);
+    if (jobtime != (time_t)-1)
+      buf->time = jobtime; 
+  }
+
+  return(True);
+}
+
+/*******************************************************************
+parse lpq on an NT system
+
+                         Windows 2000 LPD Server
+                              Printer \\10.0.0.2\NP17PCL (Paused)
+
+Owner       Status         Jobname          Job-Id    Size   Pages  Priority
+----------------------------------------------------------------------------
+root (9.99. Printing  /usr/lib/rhs/rhs-pr      3       625      0      1
+root (9.99. Paused    /usr/lib/rhs/rhs-pr      4       625      0      1
+jmcd        Waiting   Re: Samba Open Sour     26     32476      1      1
+
+********************************************************************/
+static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPRNT_OWNSIZ 11
+#define LPRNT_STATSIZ 9
+#define LPRNT_JOBSIZ 19
+#define LPRNT_IDSIZ 6
+#define LPRNT_SIZSIZ 9
+  typedef struct 
+  {
+    char owner[LPRNT_OWNSIZ];
+    char space1;
+    char status[LPRNT_STATSIZ];
+    char space2;
+    char jobname[LPRNT_JOBSIZ];
+    char space3;
+    char jobid[LPRNT_IDSIZ];
+    char space4;
+    char size[LPRNT_SIZSIZ];
+    char terminator;
+  } nt_lpq_line;
+
+  nt_lpq_line parse_line;
+#define LPRNT_PRINTING "Printing"
+#define LPRNT_WAITING "Waiting"
+#define LPRNT_PAUSED "Paused"
+
+  memset(&parse_line, '\0', sizeof(parse_line));
+  strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+  if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+    return(False);
+
+  /* Just want the first word in the owner field - the username */
+  if (strchr_m(parse_line.owner, ' '))
+    *(strchr_m(parse_line.owner, ' ')) = '\0';
+  else
+    parse_line.space1 = '\0';
+
+  /* Make sure we have an owner */
+  if (!strlen(parse_line.owner))
+    return(False);
+
+  /* Make sure the status is valid */
+  parse_line.space2 = '\0';
+  trim_string(parse_line.status, NULL, " ");
+  if (!strequal(parse_line.status, LPRNT_PRINTING) &&
+      !strequal(parse_line.status, LPRNT_PAUSED) &&
+      !strequal(parse_line.status, LPRNT_WAITING))
+    return(False);
+  
+  parse_line.space3 = '\0';
+  trim_string(parse_line.jobname, NULL, " ");
+
+  buf->job = atoi(parse_line.jobid);
+  buf->priority = 0;
+  buf->size = atoi(parse_line.size);
+  buf->time = time(NULL);
+  StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1);
+  StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1);
+  if (strequal(parse_line.status, LPRNT_PRINTING))
+    buf->status = LPQ_PRINTING;
+  else if (strequal(parse_line.status, LPRNT_PAUSED))
+    buf->status = LPQ_PAUSED;
+  else
+    buf->status = LPQ_QUEUED;
+
+  return(True);
+}
+
+/*******************************************************************
+parse lpq on an OS2 system
+
+JobID  File Name          Rank      Size        Status          Comment       
+-----  ---------------    ------    --------    ------------    ------------  
+    3  Control                 1          68    Queued          root@psflinu  
+    4  /etc/motd               2       11666    Queued          root@psflinu  
+
+********************************************************************/
+static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPROS2_IDSIZ 5
+#define LPROS2_JOBSIZ 15
+#define LPROS2_SIZSIZ 8
+#define LPROS2_STATSIZ 12
+#define LPROS2_OWNSIZ 12
+  typedef struct 
+  {
+    char jobid[LPROS2_IDSIZ];
+    char space1[2];
+    char jobname[LPROS2_JOBSIZ];
+    char space2[14];
+    char size[LPROS2_SIZSIZ];
+    char space3[4];
+    char status[LPROS2_STATSIZ];
+    char space4[4];
+    char owner[LPROS2_OWNSIZ];
+    char terminator;
+  } os2_lpq_line;
+
+  os2_lpq_line parse_line;
+#define LPROS2_PRINTING "Printing"
+#define LPROS2_WAITING "Queued"
+#define LPROS2_PAUSED "Paused"
+
+  memset(&parse_line, '\0', sizeof(parse_line));
+  strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+  if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+    return(False);
+
+  /* Get the jobid */
+  buf->job = atoi(parse_line.jobid);
+
+  /* Get the job name */
+  parse_line.space2[0] = '\0';
+  trim_string(parse_line.jobname, NULL, " ");
+  StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1);
+
+  buf->priority = 0;
+  buf->size = atoi(parse_line.size);
+  buf->time = time(NULL);
+
+  /* Make sure we have an owner */
+  if (!strlen(parse_line.owner))
+    return(False);
+
+  /* Make sure we have a valid status */
+  parse_line.space4[0] = '\0';
+  trim_string(parse_line.status, NULL, " ");
+  if (!strequal(parse_line.status, LPROS2_PRINTING) &&
+      !strequal(parse_line.status, LPROS2_PAUSED) &&
+      !strequal(parse_line.status, LPROS2_WAITING))
+    return(False);
+
+  StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1);
+  if (strequal(parse_line.status, LPROS2_PRINTING))
+    buf->status = LPQ_PRINTING;
+  else if (strequal(parse_line.status, LPROS2_PAUSED))
+    buf->status = LPQ_PAUSED;
+  else
+    buf->status = LPQ_QUEUED;
+
+  return(True);
+}
+
+static const char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
+static const char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
+static const char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
+
+#ifdef DEVELOPER
+
+/****************************************************************************
+parse a vlp line
+****************************************************************************/
+static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
+{
+       int toknum = 0;
+       fstring tok;
+       const char *cline = line;
+
+       /* First line is printer status */
+
+       if (!isdigit(line[0])) return False;
+
+       /* Parse a print job entry */
+
+       while(next_token(&cline, tok, NULL, sizeof(fstring))) {
+               switch (toknum) {
+               case 0:
+                       buf->job = atoi(tok);
+                       break;
+               case 1:
+                       buf->size = atoi(tok);
+                       break;
+               case 2:
+                       buf->status = atoi(tok);
+                       break;
+               case 3:
+                       buf->time = atoi(tok);
+                       break;
+               case 4:
+                       fstrcpy(buf->fs_user, tok);
+                       break;
+               case 5:
+                       fstrcpy(buf->fs_file, tok);
+                       break;
+               }
+               toknum++;
+       }
+
+       return True;
+}
+
+#endif /* DEVELOPER */
+
+/****************************************************************************
+parse a lpq line. Choose printing style
+****************************************************************************/
+
+BOOL parse_lpq_entry(int snum,char *line,
+                    print_queue_struct *buf,
+                    print_status_struct *status,BOOL first)
+{
+  BOOL ret;
+
+  switch (lp_printing(snum))
+    {
+    case PRINT_SYSV:
+      ret = parse_lpq_sysv(line,buf,first);
+      break;
+    case PRINT_AIX:      
+      ret = parse_lpq_aix(line,buf,first);
+      break;
+    case PRINT_HPUX:
+      ret = parse_lpq_hpux(line,buf,first);
+      break;
+    case PRINT_QNX:
+      ret = parse_lpq_qnx(line,buf,first);
+      break;
+    case PRINT_LPRNG:
+      ret = parse_lpq_lprng(line,buf,first);
+      break;
+    case PRINT_PLP:
+      ret = parse_lpq_plp(line,buf,first);
+      break;
+    case PRINT_SOFTQ:
+      ret = parse_lpq_softq(line,buf,first);
+      break;
+    case PRINT_LPRNT:
+      ret = parse_lpq_nt(line,buf,first);
+      break;
+    case PRINT_LPROS2:
+      ret = parse_lpq_os2(line,buf,first);
+      break;
+#ifdef DEVELOPER
+    case PRINT_VLP:
+    case PRINT_TEST:
+           ret = parse_lpq_vlp(line,buf,first);
+           break;
+#endif /* DEVELOPER */
+    default:
+      ret = parse_lpq_bsd(line,buf,first);
+      break;
+    }
+
+  /* We don't want the newline in the status message. */
+  {
+    char *p = strchr_m(line,'\n');
+    if (p) *p = 0;
+  }
+
+  /* in the LPRNG case, we skip lines starting by a space.*/
+  if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
+  {
+       if (line[0]==' ')
+               return ret;
+  }
+
+
+  if (status && !ret)
+    {
+      /* a few simple checks to see if the line might be a
+         printer status line: 
+        handle them so that most severe condition is shown */
+      int i;
+      strlower(line);
+      
+      switch (status->status) {
+      case LPSTAT_OK:
+       for (i=0; stat0_strings[i]; i++)
+         if (strstr(line,stat0_strings[i])) {
+           StrnCpy(status->message,line,sizeof(status->message)-1);
+           status->status=LPSTAT_OK;
+           return ret;
+         }
+      case LPSTAT_STOPPED:
+       for (i=0; stat1_strings[i]; i++)
+         if (strstr(line,stat1_strings[i])) {
+           StrnCpy(status->message,line,sizeof(status->message)-1);
+           status->status=LPSTAT_STOPPED;
+           return ret;
+         }
+      case LPSTAT_ERROR:
+       for (i=0; stat2_strings[i]; i++)
+         if (strstr(line,stat2_strings[i])) {
+           StrnCpy(status->message,line,sizeof(status->message)-1);
+           status->status=LPSTAT_ERROR;
+           return ret;
+         }
+       break;
+      }
+    }
+
+  return(ret);
+}
diff --git a/source4/printing/notify.c b/source4/printing/notify.c
new file mode 100644 (file)
index 0000000..9e92e5d
--- /dev/null
@@ -0,0 +1,526 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.2
+   printing backend routines
+   Copyright (C) Tim Potter, 2002
+   Copyright (C) Gerald Carter,         2002
+   
+   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 "printing.h"
+
+static TALLOC_CTX *send_ctx;
+
+static BOOL create_send_ctx(void)
+{
+       if (!send_ctx)
+               send_ctx = talloc_init("print notify queue");
+
+       if (!send_ctx)
+               return False;
+
+       return True;
+}
+
+/****************************************************************************
+ Turn a queue name into a snum.
+****************************************************************************/
+
+int print_queue_snum(const char *qname)
+{
+       int snum = lp_servicenumber(qname);
+       if (snum == -1 || !lp_print_ok(snum))
+               return -1;
+       return snum;
+}
+
+/*******************************************************************
+ Used to decide if we need a short select timeout.
+*******************************************************************/
+
+BOOL print_notify_messages_pending(void)
+{
+       return (notify_queue_head != NULL);
+}
+
+/*******************************************************************
+ Flatten data into a message.
+*******************************************************************/
+
+static BOOL flatten_message(struct notify_queue *q)
+{
+       struct spoolss_notify_msg *msg = q->msg;
+       char *buf = NULL;
+       size_t buflen = 0, len;
+
+again:
+       len = 0;
+
+       /* Pack header */
+
+       len += tdb_pack(buf + len, buflen - len, "f", msg->printer);
+
+       len += tdb_pack(buf + len, buflen - len, "ddddd",
+                       msg->type, msg->field, msg->id, msg->len, msg->flags);
+
+       /* Pack data */
+
+       if (msg->len == 0)
+               len += tdb_pack(buf + len, buflen - len, "dd",
+                               msg->notify.value[0], msg->notify.value[1]);
+       else
+               len += tdb_pack(buf + len, buflen - len, "B",
+                               msg->len, msg->notify.data);
+
+       if (buflen != len) {
+               buf = talloc_realloc(send_ctx, buf, len);
+               if (!buf)
+                       return False;
+               buflen = len;
+               goto again;
+       }
+
+       q->buf = buf;
+       q->buflen = buflen;
+
+       return True;
+}
+
+/*******************************************************************
+ Send the batched messages - on a per-printer basis.
+*******************************************************************/
+
+static void print_notify_send_messages_to_printer(const char *printer, unsigned int timeout)
+{
+       char *buf;
+       struct notify_queue *pq, *pq_next;
+       size_t msg_count = 0, offset = 0;
+       size_t num_pids = 0;
+       size_t i;
+       pid_t *pid_list = NULL;
+
+       /* Count the space needed to send the messages. */
+       for (pq = notify_queue_head; pq; pq = pq->next) {
+               if (strequal(printer, pq->msg->printer)) {
+                       if (!flatten_message(pq)) {
+                               DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+                               talloc_destroy_pool(send_ctx);
+                               return;
+                       }
+                       offset += (pq->buflen + 4);
+                       msg_count++;
+               }       
+       }
+       offset += 4; /* For count. */
+
+       buf = talloc(send_ctx, offset);
+       if (!buf) {
+               DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+               talloc_destroy_pool(send_ctx);
+               return;
+       }
+
+       offset = 0;
+       SIVAL(buf,offset,msg_count);
+       offset += 4;
+       for (pq = notify_queue_head; pq; pq = pq_next) {
+               pq_next = pq->next;
+
+               if (strequal(printer, pq->msg->printer)) {
+                       SIVAL(buf,offset,pq->buflen);
+                       offset += 4;
+                       memcpy(buf + offset, pq->buf, pq->buflen);
+                       offset += pq->buflen;
+
+                       /* Remove from list. */
+                       DLIST_REMOVE(notify_queue_head, pq);
+               }
+       }
+
+       DEBUG(5, ("print_notify_send_messages_to_printer: sending %d print notify message%s to printer %s\n", 
+                 msg_count, msg_count != 1 ? "s" : "", printer));
+
+       /*
+        * Get the list of PID's to send to.
+        */
+
+       if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list))
+               return;
+
+       for (i = 0; i < num_pids; i++)
+               message_send_pid_with_timeout(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True, timeout);
+}
+
+/*******************************************************************
+ Actually send the batched messages.
+*******************************************************************/
+
+void print_notify_send_messages(unsigned int timeout)
+{
+       if (!print_notify_messages_pending())
+               return;
+
+       if (!create_send_ctx())
+               return;
+
+       while (print_notify_messages_pending())
+               print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout);
+
+       talloc_destroy_pool(send_ctx);
+}
+
+/**********************************************************************
+ deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX
+ *********************************************************************/
+static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from )
+{
+
+       if ( !to || !from )
+               return False;
+       
+       memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) );
+       
+       if ( from->len ) {
+               to->notify.data = talloc_memdup(send_ctx, from->notify.data, from->len );
+               if ( !to->notify.data ) {
+                       DEBUG(0,("copy_notify2_msg: talloc_memdup() of size [%d] failed!\n", from->len ));
+                       return False;
+               }
+       }
+       
+
+       return True;
+}
+
+/*******************************************************************
+ Batch up print notify messages.
+*******************************************************************/
+
+static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
+{
+       struct notify_queue *pnqueue, *tmp_ptr;
+
+       /*
+        * Ensure we only have one message unique to each name/type/field/id/flags
+        * tuple. There is no point in sending multiple messages that match
+        * as they will just cause flickering updates in the client.
+        */
+
+       for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) {
+               if (tmp_ptr->msg->type == msg->type &&
+                               tmp_ptr->msg->field == msg->field &&
+                               tmp_ptr->msg->id == msg->id &&
+                               tmp_ptr->msg->flags == msg->flags &&
+                               strequal(tmp_ptr->msg->printer, msg->printer)) {
+
+                       DEBUG(5, ("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for printer %s \
+in notify_queue\n", msg->type, msg->field, msg->printer));
+
+                       tmp_ptr->msg = msg;
+                       return;
+               }
+       }
+
+       /* Store the message on the pending queue. */
+
+       pnqueue = talloc(send_ctx, sizeof(*pnqueue));
+       if (!pnqueue) {
+               DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n"));
+               return;
+       }
+
+       /* allocate a new msg structure and copy the fields */
+       
+       if ( !(pnqueue->msg = (SPOOLSS_NOTIFY_MSG*)talloc(send_ctx, sizeof(SPOOLSS_NOTIFY_MSG))) ) {
+               DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%d] failed!\n", 
+                       sizeof(SPOOLSS_NOTIFY_MSG)));
+               return;
+       }
+       copy_notify2_msg(pnqueue->msg, msg);
+       pnqueue->buf = NULL;
+       pnqueue->buflen = 0;
+
+       DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \
+to notify_queue_head\n", msg->type, msg->field, msg->printer));
+
+       /*
+        * Note we add to the end of the list to ensure
+        * the messages are sent in the order they were received. JRA.
+        */
+
+       DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr);
+}
+
+static void send_notify_field_values(const char *printer_name, uint32 type,
+                                    uint32 field, uint32 id, uint32 value1, 
+                                    uint32 value2, uint32 flags)
+{
+       struct spoolss_notify_msg *msg;
+
+       if (lp_disable_spoolss())
+               return;
+
+       if (!create_send_ctx())
+               return;
+
+       msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+       if (!msg)
+               return;
+
+       ZERO_STRUCTP(msg);
+
+       fstrcpy(msg->printer, printer_name);
+       msg->type = type;
+       msg->field = field;
+       msg->id = id;
+       msg->notify.value[0] = value1;
+       msg->notify.value[1] = value2;
+       msg->flags = flags;
+
+       send_spoolss_notify2_msg(msg);
+}
+
+static void send_notify_field_buffer(const char *printer_name, uint32 type,
+                                    uint32 field, uint32 id, uint32 len,
+                                    char *buffer)
+{
+       struct spoolss_notify_msg *msg;
+
+       if (lp_disable_spoolss())
+               return;
+
+       if (!create_send_ctx())
+               return;
+
+       msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+       if (!msg)
+               return;
+
+       ZERO_STRUCTP(msg);
+
+       fstrcpy(msg->printer, printer_name);
+       msg->type = type;
+       msg->field = field;
+       msg->id = id;
+       msg->len = len;
+       msg->notify.data = buffer;
+
+       send_spoolss_notify2_msg(msg);
+}
+
+/* Send a message that the printer status has changed */
+
+void notify_printer_status_byname(const char *printer_name, uint32 status)
+{
+       /* Printer status stored in value1 */
+
+       send_notify_field_values(printer_name, PRINTER_NOTIFY_TYPE, 
+                                PRINTER_NOTIFY_STATUS, 0, 
+                                status, 0, 0);
+}
+
+void notify_printer_status(int snum, uint32 status)
+{
+       const char *printer_name = SERVICE(snum); 
+
+       if (printer_name)
+               notify_printer_status_byname(printer_name, status);
+}
+
+void notify_job_status_byname(const char *printer_name, uint32 jobid, uint32 status,
+                             uint32 flags)
+{
+       /* Job id stored in id field, status in value1 */
+
+       send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+                                JOB_NOTIFY_STATUS, jobid,
+                                status, 0, flags);
+}
+
+void notify_job_status(int snum, uint32 jobid, uint32 status)
+{
+       const char *printer_name = SERVICE(snum);
+
+       notify_job_status_byname(printer_name, jobid, status, 0);
+}
+
+void notify_job_total_bytes(int snum, uint32 jobid, uint32 size)
+{
+       const char *printer_name = SERVICE(snum);
+
+       /* Job id stored in id field, status in value1 */
+
+       send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+                                JOB_NOTIFY_TOTAL_BYTES, jobid,
+                                size, 0, 0);
+}
+
+void notify_job_total_pages(int snum, uint32 jobid, uint32 pages)
+{
+       const char *printer_name = SERVICE(snum);
+
+       /* Job id stored in id field, status in value1 */
+
+       send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+                                JOB_NOTIFY_TOTAL_PAGES, jobid,
+                                pages, 0, 0);
+}
+
+void notify_job_username(int snum, uint32 jobid, char *name)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME,
+               jobid, strlen(name) + 1, name);
+}
+
+void notify_job_name(int snum, uint32 jobid, char *name)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT,
+               jobid, strlen(name) + 1, name);
+}
+
+void notify_job_submitted(int snum, uint32 jobid, time_t submitted)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED,
+               jobid, sizeof(submitted), (char *)&submitted);
+}
+
+void notify_printer_driver(int snum, char *driver_name)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,
+               snum, strlen(driver_name) + 1, driver_name);
+}
+
+void notify_printer_comment(int snum, char *comment)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,
+               snum, strlen(comment) + 1, comment);
+}
+
+void notify_printer_sharename(int snum, char *share_name)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,
+               snum, strlen(share_name) + 1, share_name);
+}
+
+void notify_printer_port(int snum, char *port_name)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,
+               snum, strlen(port_name) + 1, port_name);
+}
+
+void notify_printer_location(int snum, char *location)
+{
+       const char *printer_name = SERVICE(snum);
+
+       send_notify_field_buffer(
+               printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,
+               snum, strlen(location) + 1, location);
+}
+
+void notify_printer_byname( char *printername, uint32 change, char *value )
+{
+       int snum = print_queue_snum(printername);
+       int type = PRINTER_NOTIFY_TYPE;
+       
+       if ( snum == -1 )
+               return;
+               
+       send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value );
+} 
+
+
+/****************************************************************************
+ Return a malloced list of pid_t's that are interested in getting update
+ messages on this print queue. Used in printing/notify to send the messages.
+****************************************************************************/
+
+BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list)
+{
+       struct tdb_print_db *pdb = NULL;
+       TDB_CONTEXT *tdb = NULL;
+       TDB_DATA data;
+       BOOL ret = True;
+       size_t i, num_pids, offset;
+       pid_t *pid_list;
+
+       *p_num_pids = 0;
+       *pp_pid_list = NULL;
+
+       pdb = get_print_db_byname(printername);
+       if (!pdb)
+               return False;
+       tdb = pdb->tdb;
+
+       if (tdb_read_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+               DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n",
+                                       printername));
+               if (pdb)
+                       release_print_db(pdb);
+               return False;
+       }
+
+       data = get_printer_notify_pid_list( tdb, printername, True );
+
+       if (!data.dptr) {
+               ret = True;
+               goto done;
+       }
+
+       num_pids = data.dsize / 8;
+
+       if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) {
+               ret = False;
+               goto done;
+       }
+
+       for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++)
+               pid_list[i] = (pid_t)IVAL(data.dptr, offset);
+
+       *pp_pid_list = pid_list;
+       *p_num_pids = num_pids;
+
+       ret = True;
+
+  done:
+
+       tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+       if (pdb)
+               release_print_db(pdb);
+       SAFE_FREE(data.dptr);
+       return ret;
+}
diff --git a/source4/printing/nt_printing.c b/source4/printing/nt_printing.c
new file mode 100644 (file)
index 0000000..017dade
--- /dev/null
@@ -0,0 +1,4887 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2000.
+ *  Copyright (C) Gerald Carter                2002-2003.
+ *
+ *  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"
+
+extern DOM_SID global_sid_World;
+
+static TDB_CONTEXT *tdb_forms; /* used for forms files */
+static TDB_CONTEXT *tdb_drivers; /* used for driver files */
+static TDB_CONTEXT *tdb_printers; /* used for printers files */
+
+#define FORMS_PREFIX "FORMS/"
+#define DRIVERS_PREFIX "DRIVERS/"
+#define DRIVER_INIT_PREFIX "DRIVER_INIT/"
+#define PRINTERS_PREFIX "PRINTERS/"
+#define SECDESC_PREFIX "SECDESC/"
+#define GLOBAL_C_SETPRINTER "GLOBALS/c_setprinter"
+#define NTDRIVERS_DATABASE_VERSION_1 1
+#define NTDRIVERS_DATABASE_VERSION_2 2
+#define NTDRIVERS_DATABASE_VERSION_3 3 /* little endian version of v2 */
+#define NTDRIVERS_DATABASE_VERSION NTDRIVERS_DATABASE_VERSION_3
+
+/* Map generic permissions to printer object specific permissions */
+
+GENERIC_MAPPING printer_generic_mapping = {
+       PRINTER_READ,
+       PRINTER_WRITE,
+       PRINTER_EXECUTE,
+       PRINTER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printer_std_mapping = {
+       PRINTER_READ,
+       PRINTER_WRITE,
+       PRINTER_EXECUTE,
+       PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+GENERIC_MAPPING printserver_generic_mapping = {
+       SERVER_READ,
+       SERVER_WRITE,
+       SERVER_EXECUTE,
+       SERVER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printserver_std_mapping = {
+       SERVER_READ,
+       SERVER_WRITE,
+       SERVER_EXECUTE,
+       SERVER_ALL_ACCESS
+};
+
+/* We need one default form to support our default printer. Msoft adds the
+forms it wants and in the ORDER it wants them (note: DEVMODE papersize is an
+array index). Letter is always first, so (for the current code) additions
+always put things in the correct order. */
+static const nt_forms_struct default_forms[] = {
+       {"Letter",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+       {"Letter Small",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+       {"Tabloid",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+       {"Ledger",0x1,0x696b8,0x44368,0x0,0x0,0x696b8,0x44368},
+       {"Legal",0x1,0x34b5c,0x56d10,0x0,0x0,0x34b5c,0x56d10},
+       {"Statement",0x1,0x221b4,0x34b5c,0x0,0x0,0x221b4,0x34b5c},
+       {"Executive",0x1,0x2cf56,0x411cc,0x0,0x0,0x2cf56,0x411cc},
+       {"A3",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+       {"A4",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+       {"A4 Small",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+       {"A5",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+       {"B4 (JIS)",0x1,0x3ebe8,0x58de0,0x0,0x0,0x3ebe8,0x58de0},
+       {"B5 (JIS)",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+       {"Folio",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+       {"Quarto",0x1,0x347d8,0x43238,0x0,0x0,0x347d8,0x43238},
+       {"10x14",0x1,0x3e030,0x56d10,0x0,0x0,0x3e030,0x56d10},
+       {"11x17",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+       {"Note",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+       {"Envelope #9",0x1,0x18079,0x37091,0x0,0x0,0x18079,0x37091},
+       {"Envelope #10",0x1,0x19947,0x3ae94,0x0,0x0,0x19947,0x3ae94},
+       {"Envelope #11",0x1,0x1be7c,0x40565,0x0,0x0,0x1be7c,0x40565},
+       {"Envelope #12",0x1,0x1d74a,0x44368,0x0,0x0,0x1d74a,0x44368},
+       {"Envelope #14",0x1,0x1f018,0x47504,0x0,0x0,0x1f018,0x47504},
+       {"C size sheet",0x1,0x696b8,0x886d0,0x0,0x0,0x696b8,0x886d0},
+       {"D size sheet",0x1,0x886d0,0xd2d70,0x0,0x0,0x886d0,0xd2d70},
+       {"E size sheet",0x1,0xd2d70,0x110da0,0x0,0x0,0xd2d70,0x110da0},
+       {"Envelope DL",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+       {"Envelope C5",0x1,0x278d0,0x37e88,0x0,0x0,0x278d0,0x37e88},
+       {"Envelope C3",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+       {"Envelope C4",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+       {"Envelope C6",0x1,0x1bd50,0x278d0,0x0,0x0,0x1bd50,0x278d0},
+       {"Envelope C65",0x1,0x1bd50,0x37e88,0x0,0x0,0x1bd50,0x37e88},
+       {"Envelope B4",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+       {"Envelope B5",0x1,0x2af80,0x3d090,0x0,0x0,0x2af80,0x3d090},
+       {"Envelope B6",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+       {"Envelope",0x1,0x1adb0,0x38270,0x0,0x0,0x1adb0,0x38270},
+       {"Envelope Monarch",0x1,0x18079,0x2e824,0x0,0x0,0x18079,0x2e824},
+       {"6 3/4 Envelope",0x1,0x167ab,0x284ec,0x0,0x0,0x167ab,0x284ec},
+       {"US Std Fanfold",0x1,0x5c3e1,0x44368,0x0,0x0,0x5c3e1,0x44368},
+       {"German Std Fanfold",0x1,0x34b5c,0x4a6a0,0x0,0x0,0x34b5c,0x4a6a0},
+       {"German Legal Fanfold",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+       {"B4 (ISO)",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+       {"Japanese Postcard",0x1,0x186a0,0x24220,0x0,0x0,0x186a0,0x24220},
+       {"9x11",0x1,0x37cf8,0x44368,0x0,0x0,0x37cf8,0x44368},
+       {"10x11",0x1,0x3e030,0x44368,0x0,0x0,0x3e030,0x44368},
+       {"15x11",0x1,0x5d048,0x44368,0x0,0x0,0x5d048,0x44368},
+       {"Envelope Invite",0x1,0x35b60,0x35b60,0x0,0x0,0x35b60,0x35b60},
+       {"Reserved48",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+       {"Reserved49",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+       {"Letter Extra",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+       {"Legal Extra",0x1,0x3ae94,0x5d048,0x0,0x0,0x3ae94,0x5d048},
+       {"Tabloid Extra",0x1,0x4a6a0,0x6f9f0,0x0,0x0,0x4a6a0,0x6f9f0},
+       {"A4 Extra",0x1,0x397c2,0x4eb16,0x0,0x0,0x397c2,0x4eb16},
+       {"Letter Transverse",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+       {"A4 Transverse",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+       {"Letter Extra Transverse",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+       {"Super A",0x1,0x376b8,0x56ea0,0x0,0x0,0x376b8,0x56ea0},
+       {"Super B",0x1,0x4a768,0x76e58,0x0,0x0,0x4a768,0x76e58},
+       {"Letter Plus",0x1,0x34b5c,0x4eb16,0x0,0x0,0x34b5c,0x4eb16},
+       {"A4 Plus",0x1,0x33450,0x50910,0x0,0x0,0x33450,0x50910},
+       {"A5 Transverse",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+       {"B5 (JIS) Transverse",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+       {"A3 Extra",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+       {"A5 Extra",0x1,0x2a7b0,0x395f8,0x0,0x0,0x2a7b0,0x395f8},
+       {"B5 (ISO) Extra",0x1,0x31128,0x43620,0x0,0x0,0x31128,0x43620},
+       {"A2",0x1,0x668a0,0x91050,0x0,0x0,0x668a0,0x91050},
+       {"A3 Transverse",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+       {"A3 Extra Transverse",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+       {"Japanese Double Postcard",0x1,0x30d40,0x24220,0x0,0x0,0x30d40,0x24220},
+       {"A6",0x1,0x19a28,0x24220,0x0,0x0,0x19a28,0x24220},
+       {"Japanese Envelope Kaku #2",0x1,0x3a980,0x510e0,0x0,0x0,0x3a980,0x510e0},
+       {"Japanese Envelope Kaku #3",0x1,0x34bc0,0x43a08,0x0,0x0,0x34bc0,0x43a08},
+       {"Japanese Envelope Chou #3",0x1,0x1d4c0,0x395f8,0x0,0x0,0x1d4c0,0x395f8},
+       {"Japanese Envelope Chou #4",0x1,0x15f90,0x320c8,0x0,0x0,0x15f90,0x320c8},
+       {"Letter Rotated",0x1,0x44368,0x34b5c,0x0,0x0,0x44368,0x34b5c},
+       {"A3 Rotated",0x1,0x668a0,0x48828,0x0,0x0,0x668a0,0x48828},
+       {"A4 Rotated",0x1,0x48828,0x33450,0x0,0x0,0x48828,0x33450},
+       {"A5 Rotated",0x1,0x33450,0x24220,0x0,0x0,0x33450,0x24220},
+       {"B4 (JIS) Rotated",0x1,0x58de0,0x3ebe8,0x0,0x0,0x58de0,0x3ebe8},
+       {"B5 (JIS) Rotated",0x1,0x3ebe8,0x2c6f0,0x0,0x0,0x3ebe8,0x2c6f0},
+       {"Japanese Postcard Rotated",0x1,0x24220,0x186a0,0x0,0x0,0x24220,0x186a0},
+       {"Double Japan Postcard Rotated",0x1,0x24220,0x30d40,0x0,0x0,0x24220,0x30d40},
+       {"A6 Rotated",0x1,0x24220,0x19a28,0x0,0x0,0x24220,0x19a28},
+       {"Japan Envelope Kaku #2 Rotated",0x1,0x510e0,0x3a980,0x0,0x0,0x510e0,0x3a980},
+       {"Japan Envelope Kaku #3 Rotated",0x1,0x43a08,0x34bc0,0x0,0x0,0x43a08, 0x34bc0},
+       {"Japan Envelope Chou #3 Rotated",0x1,0x395f8,0x1d4c0,0x0,0x0,0x395f8,0x1d4c0},
+       {"Japan Envelope Chou #4 Rotated",0x1,0x320c8,0x15f90,0x0,0x0,0x320c8,0x15f90},
+       {"B6 (JIS)",0x1,0x1f400,0x2c6f0,0x0,0x0,0x1f400,0x2c6f0},
+       {"B6 (JIS) Rotated",0x1,0x2c6f0,0x1f400,0x0,0x0,0x2c6f0,0x1f400},
+       {"12x11",0x1,0x4a724,0x443e1,0x0,0x0,0x4a724,0x443e1},
+       {"Japan Envelope You #4",0x1,0x19a28,0x395f8,0x0,0x0,0x19a28,0x395f8},
+       {"Japan Envelope You #4 Rotated",0x1,0x395f8,0x19a28,0x0,0x0,0x395f8,0x19a28},
+       {"PRC 16K",0x1,0x2de60,0x3f7a0,0x0,0x0,0x2de60,0x3f7a0},
+       {"PRC 32K",0x1,0x1fbd0,0x2cec0,0x0,0x0,0x1fbd0,0x2cec0},
+       {"PRC 32K(Big)",0x1,0x222e0,0x318f8,0x0,0x0,0x222e0,0x318f8},
+       {"PRC Envelope #1",0x1,0x18e70,0x28488,0x0,0x0,0x18e70,0x28488},
+       {"PRC Envelope #2",0x1,0x18e70,0x2af80,0x0,0x0,0x18e70,0x2af80},
+       {"PRC Envelope #3",0x1,0x1e848,0x2af80,0x0,0x0,0x1e848,0x2af80},
+       {"PRC Envelope #4",0x1,0x1adb0,0x32c80,0x0,0x0,0x1adb0,0x32c80},
+       {"PRC Envelope #5",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+       {"PRC Envelope #6",0x1,0x1d4c0,0x38270,0x0,0x0,0x1d4c0,0x38270},
+       {"PRC Envelope #7",0x1,0x27100,0x38270,0x0,0x0,0x27100,0x38270},
+       {"PRC Envelope #8",0x1,0x1d4c0,0x4b708,0x0,0x0,0x1d4c0,0x4b708},
+       {"PRC Envelope #9",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+       {"PRC Envelope #10",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+       {"PRC 16K Rotated",0x1,0x3f7a0,0x2de60,0x0,0x0,0x3f7a0,0x2de60},
+       {"PRC 32K Rotated",0x1,0x2cec0,0x1fbd0,0x0,0x0,0x2cec0,0x1fbd0},
+       {"PRC 32K(Big) Rotated",0x1,0x318f8,0x222e0,0x0,0x0,0x318f8,0x222e0},
+       {"PRC Envelope #1 Rotated",0x1,0x28488,0x18e70,0x0,0x0,0x28488,0x18e70},
+       {"PRC Envelope #2 Rotated",0x1,0x2af80,0x18e70,0x0,0x0,0x2af80,0x18e70},
+       {"PRC Envelope #3 Rotated",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+       {"PRC Envelope #4 Rotated",0x1,0x32c80,0x1adb0,0x0,0x0,0x32c80,0x1adb0},
+       {"PRC Envelope #5 Rotated",0x1,0x35b60,0x1adb0,0x0,0x0,0x35b60,0x1adb0},
+       {"PRC Envelope #6 Rotated",0x1,0x38270,0x1d4c0,0x0,0x0,0x38270,0x1d4c0},
+       {"PRC Envelope #7 Rotated",0x1,0x38270,0x27100,0x0,0x0,0x38270,0x27100},
+       {"PRC Envelope #8 Rotated",0x1,0x4b708,0x1d4c0,0x0,0x0,0x4b708,0x1d4c0},
+       {"PRC Envelope #9 Rotated",0x1,0x4f1a0,0x37e88,0x0,0x0,0x4f1a0,0x37e88},
+       {"PRC Envelope #10 Rotated",0x1,0x6fd10,0x4f1a0,0x0,0x0,0x6fd10,0x4f1a0}
+};
+
+static BOOL upgrade_to_version_3(void)
+{
+       TDB_DATA kbuf, newkey, dbuf;
+       DEBUG(0,("upgrade_to_version_3: upgrading print tdb's to version 3\n"));
+       for (kbuf = tdb_firstkey(tdb_drivers); kbuf.dptr;
+                       newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               dbuf = tdb_fetch(tdb_drivers, kbuf);
+
+               if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) == 0) {
+                       DEBUG(0,("upgrade_to_version_3:moving form\n"));
+                       if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to move form. Error (%s).\n", tdb_errorstr(tdb_forms)));
+                               return False;
+                       }
+                       if (tdb_delete(tdb_drivers, kbuf) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+                               return False;
+                       }
+               }
+               if (strncmp(kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) == 0) {
+                       DEBUG(0,("upgrade_to_version_3:moving printer\n"));
+                       if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to move printer. Error (%s)\n", tdb_errorstr(tdb_printers)));
+                               return False;
+                       }
+                       if (tdb_delete(tdb_drivers, kbuf) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+                               return False;
+                       }
+               }
+               if (strncmp(kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) {
+                       DEBUG(0,("upgrade_to_version_3:moving secdesc\n"));
+                       if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to move secdesc. Error (%s)\n", tdb_errorstr(tdb_printers)));
+                               return False;
+                       }
+                       if (tdb_delete(tdb_drivers, kbuf) != 0) {
+                               SAFE_FREE(dbuf.dptr);
+                               DEBUG(0,("upgrade_to_version_3: failed to delete secdesc. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+                               return False;
+                       }
+               }
+               SAFE_FREE(dbuf.dptr);
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Open the NT printing tdbs. Done once before fork().
+****************************************************************************/
+
+BOOL nt_printing_init(void)
+{
+       static pid_t local_pid;
+       const char *vstring = "INFO/version";
+
+       if (tdb_drivers && tdb_printers && tdb_forms && local_pid == sys_getpid())
+               return True;
+       if (tdb_drivers)
+               tdb_close(tdb_drivers);
+       tdb_drivers = tdb_open_log(lock_path("ntdrivers.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       if (!tdb_drivers) {
+               DEBUG(0,("nt_printing_init: Failed to open nt drivers database %s (%s)\n",
+                       lock_path("ntdrivers.tdb"), strerror(errno) ));
+               return False;
+       }
+       if (tdb_printers)
+               tdb_close(tdb_printers);
+       tdb_printers = tdb_open_log(lock_path("ntprinters.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       if (!tdb_printers) {
+               DEBUG(0,("nt_printing_init: Failed to open nt printers database %s (%s)\n",
+                       lock_path("ntprinters.tdb"), strerror(errno) ));
+               return False;
+       }
+       if (tdb_forms)
+               tdb_close(tdb_forms);
+       tdb_forms = tdb_open_log(lock_path("ntforms.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       if (!tdb_forms) {
+               DEBUG(0,("nt_printing_init: Failed to open nt forms database %s (%s)\n",
+                       lock_path("ntforms.tdb"), strerror(errno) ));
+               return False;
+       }
+       local_pid = sys_getpid();
+       /* handle a Samba upgrade */
+       tdb_lock_bystring(tdb_drivers, vstring, 0);
+       {
+               int32 vers_id;
+
+               /* Cope with byte-reversed older versions of the db. */
+               vers_id = tdb_fetch_int32(tdb_drivers, vstring);
+               if ((vers_id == NTDRIVERS_DATABASE_VERSION_2) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_2)) {
+                       /* Written on a bigendian machine with old fetch_int code. Save as le. */
+                       /* The only upgrade between V2 and V3 is to save the version in little-endian. */
+                       tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+                       vers_id = NTDRIVERS_DATABASE_VERSION;
+               }
+
+               if (vers_id != NTDRIVERS_DATABASE_VERSION) {
+
+                       if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) { 
+                               if (!upgrade_to_version_3())
+                                       return False;
+                       } else
+                               tdb_traverse(tdb_drivers, tdb_traverse_delete_fn, NULL);
+                        
+                       tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+               }
+       }
+       tdb_unlock_bystring(tdb_drivers, vstring);
+
+       update_c_setprinter(True);
+
+       /*
+        * register callback to handle updating printers as new
+        * drivers are installed
+        */
+
+       message_register( MSG_PRINTER_DRVUPGRADE, do_drv_upgrade_printer );
+
+       /*
+        * register callback to handle updating printer data
+        * when a driver is initialized
+        */
+
+       message_register( MSG_PRINTERDATA_INIT_RESET, reset_all_printerdata );
+
+
+       return True;
+}
+
+/*******************************************************************
+ tdb traversal function for counting printers.
+********************************************************************/
+
+static int traverse_counting_printers(TDB_CONTEXT *t, TDB_DATA key,
+                                      TDB_DATA data, void *context)
+{
+       int *printer_count = (int*)context;
+       if (memcmp(PRINTERS_PREFIX, key.dptr, sizeof(PRINTERS_PREFIX)-1) == 0) {
+               (*printer_count)++;
+               DEBUG(10,("traverse_counting_printers: printer = [%s]  printer_count = %d\n", key.dptr, *printer_count));
+       }
+       return 0;
+}
+/*******************************************************************
+ Update the spooler global c_setprinter. This variable is initialized
+ when the parent smbd starts with the number of existing printers. It
+ is monotonically increased by the current number of printers *after*
+ each add or delete printer RPC. Only Microsoft knows why... JRR020119
+********************************************************************/
+
+uint32 update_c_setprinter(BOOL initialize)
+{
+       int32 c_setprinter;
+       int32 printer_count = 0;
+       tdb_lock_bystring(tdb_printers, GLOBAL_C_SETPRINTER, 0);
+       /* Traverse the tdb, counting the printers */
+       tdb_traverse(tdb_printers, traverse_counting_printers, (void *)&printer_count);
+       /* If initializing, set c_setprinter to current printers count
+        * otherwise, bump it by the current printer count
+        */
+       if (!initialize)
+               c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER) + printer_count;
+       else
+               c_setprinter = printer_count;
+       DEBUG(10,("update_c_setprinter: c_setprinter = %u\n", (unsigned int)c_setprinter));
+       tdb_store_int32(tdb_printers, GLOBAL_C_SETPRINTER, c_setprinter);
+       tdb_unlock_bystring(tdb_printers, GLOBAL_C_SETPRINTER);
+       return (uint32)c_setprinter;
+}
+
+/*******************************************************************
+ Get the spooler global c_setprinter, accounting for initialization.
+********************************************************************/
+
+uint32 get_c_setprinter(void)
+{
+       int32 c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER);
+       if (c_setprinter == (int32)-1)
+               c_setprinter = update_c_setprinter(True);
+       DEBUG(10,("get_c_setprinter: c_setprinter = %d\n", c_setprinter));
+       return (uint32)c_setprinter;
+}
+
+/****************************************************************************
+ Get builtin form struct list.
+****************************************************************************/
+
+int get_builtin_ntforms(nt_forms_struct **list)
+{
+       *list = (nt_forms_struct *)memdup(&default_forms[0], sizeof(default_forms));
+       return sizeof(default_forms) / sizeof(default_forms[0]);
+}
+
+/****************************************************************************
+ get a builtin form struct
+****************************************************************************/
+
+BOOL get_a_builtin_ntform(UNISTR2 *uni_formname,nt_forms_struct *form)
+{
+       int i,count;
+       fstring form_name;
+       unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+       DEBUGADD(6,("Looking for builtin form %s \n", form_name));
+       count = sizeof(default_forms) / sizeof(default_forms[0]);
+       for (i=0;i<count;i++) {
+               if (strequal(form_name,default_forms[i].name)) {
+                       DEBUGADD(6,("Found builtin form %s \n", form_name));
+                       memcpy(form,&default_forms[i],sizeof(*form));
+                       break;
+               }
+       }
+
+       return (i !=count);
+}
+
+/****************************************************************************
+get a form struct list
+****************************************************************************/
+int get_ntforms(nt_forms_struct **list)
+{
+       TDB_DATA kbuf, newkey, dbuf;
+       nt_forms_struct *tl;
+       nt_forms_struct form;
+       int ret;
+       int i;
+       int n = 0;
+
+       for (kbuf = tdb_firstkey(tdb_forms);
+            kbuf.dptr;
+            newkey = tdb_nextkey(tdb_forms, kbuf), safe_free(kbuf.dptr), kbuf=newkey) 
+       {
+               if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0) 
+                       continue;
+               
+               dbuf = tdb_fetch(tdb_forms, kbuf);
+               if (!dbuf.dptr) 
+                       continue;
+
+               fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX));
+               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddd",
+                                &i, &form.flag, &form.width, &form.length, &form.left,
+                                &form.top, &form.right, &form.bottom);
+               SAFE_FREE(dbuf.dptr);
+               if (ret != dbuf.dsize) 
+                       continue;
+
+               tl = Realloc(*list, sizeof(nt_forms_struct)*(n+1));
+               if (!tl) {
+                       DEBUG(0,("get_ntforms: Realloc fail.\n"));
+                       return 0;
+               }
+               *list = tl;
+               (*list)[n] = form;
+               n++;
+       }
+       
+
+       return n;
+}
+
+/****************************************************************************
+write a form struct list
+****************************************************************************/
+int write_ntforms(nt_forms_struct **list, int number)
+{
+       pstring buf, key;
+       int len;
+       TDB_DATA kbuf,dbuf;
+       int i;
+
+       for (i=0;i<number;i++) {
+               /* save index, so list is rebuilt in correct order */
+               len = tdb_pack(buf, sizeof(buf), "dddddddd",
+                              i, (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+                              (*list)[i].left, (*list)[i].top, (*list)[i].right,
+                              (*list)[i].bottom);
+               if (len > sizeof(buf)) break;
+               slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[i].name);
+               kbuf.dsize = strlen(key)+1;
+               kbuf.dptr = key;
+               dbuf.dsize = len;
+               dbuf.dptr = buf;
+               if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) break;
+       }
+
+       return i;
+}
+
+/****************************************************************************
+add a form struct at the end of the list
+****************************************************************************/
+BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
+{
+       int n=0;
+       BOOL update;
+       fstring form_name;
+       nt_forms_struct *tl;
+
+       /*
+        * NT tries to add forms even when
+        * they are already in the base
+        * only update the values if already present
+        */
+
+       update=False;
+       
+       unistr2_to_ascii(form_name, &form->name, sizeof(form_name)-1);
+       for (n=0; n<*count; n++) {
+               if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+                       DEBUG(103, ("NT workaround, [%s] already exists\n", form_name));
+                       update=True;
+                       break;
+               }
+       }
+
+       if (update==False) {
+               if((tl=Realloc(*list, (n+1)*sizeof(nt_forms_struct))) == NULL) {
+                       DEBUG(0,("add_a_form: failed to enlarge forms list!\n"));
+                       return False;
+               }
+               *list = tl;
+               unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name)-1);
+               (*count)++;
+       }
+       
+       (*list)[n].flag=form->flags;
+       (*list)[n].width=form->size_x;
+       (*list)[n].length=form->size_y;
+       (*list)[n].left=form->left;
+       (*list)[n].top=form->top;
+       (*list)[n].right=form->right;
+       (*list)[n].bottom=form->bottom;
+
+       return True;
+}
+
+/****************************************************************************
+ Delete a named form struct.
+****************************************************************************/
+
+BOOL delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, WERROR *ret)
+{
+       pstring key;
+       TDB_DATA kbuf;
+       int n=0;
+       fstring form_name;
+
+       *ret = WERR_OK;
+
+       unistr2_to_ascii(form_name, del_name, sizeof(form_name)-1);
+
+       for (n=0; n<*count; n++) {
+               if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+                       DEBUG(103, ("delete_a_form, [%s] in list\n", form_name));
+                       break;
+               }
+       }
+
+       if (n == *count) {
+               DEBUG(10,("delete_a_form, [%s] not found\n", form_name));
+               *ret = WERR_INVALID_PARAM;
+               return False;
+       }
+
+       slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[n].name);
+       kbuf.dsize = strlen(key)+1;
+       kbuf.dptr = key;
+       if (tdb_delete(tdb_forms, kbuf) != 0) {
+               *ret = WERR_NOMEM;
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Update a form struct.
+****************************************************************************/
+
+void update_a_form(nt_forms_struct **list, const FORM *form, int count)
+{
+       int n=0;
+       fstring form_name;
+       unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1);
+
+       DEBUG(106, ("[%s]\n", form_name));
+       for (n=0; n<count; n++) {
+               DEBUGADD(106, ("n [%d]:[%s]\n", n, (*list)[n].name));
+               if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
+                       break;
+       }
+
+       if (n==count) return;
+
+       (*list)[n].flag=form->flags;
+       (*list)[n].width=form->size_x;
+       (*list)[n].length=form->size_y;
+       (*list)[n].left=form->left;
+       (*list)[n].top=form->top;
+       (*list)[n].right=form->right;
+       (*list)[n].bottom=form->bottom;
+}
+
+/****************************************************************************
+ Get the nt drivers list.
+ Traverse the database and look-up the matching names.
+****************************************************************************/
+int get_ntdrivers(fstring **list, const char *architecture, uint32 version)
+{
+       int total=0;
+       fstring short_archi;
+       fstring *fl;
+       pstring key;
+       TDB_DATA kbuf, newkey;
+
+       get_short_archi(short_archi, architecture);
+       slprintf(key, sizeof(key)-1, "%s%s/%d/", DRIVERS_PREFIX, short_archi, version);
+
+       for (kbuf = tdb_firstkey(tdb_drivers);
+            kbuf.dptr;
+            newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+               if (strncmp(kbuf.dptr, key, strlen(key)) != 0)
+                       continue;
+               
+               if((fl = Realloc(*list, sizeof(fstring)*(total+1))) == NULL) {
+                       DEBUG(0,("get_ntdrivers: failed to enlarge list!\n"));
+                       return -1;
+               }
+               else *list = fl;
+
+               fstrcpy((*list)[total], kbuf.dptr+strlen(key));
+               total++;
+       }
+
+       return(total);
+}
+
+/****************************************************************************
+ Function to do the mapping between the long architecture name and
+ the short one.
+****************************************************************************/
+BOOL get_short_archi(char *short_archi, const char *long_archi)
+{
+       struct table {
+               const char *long_archi;
+               const char *short_archi;
+       };
+       
+       struct table archi_table[]=
+       {
+               {"Windows 4.0",          "WIN40"    },
+               {"Windows NT x86",       "W32X86"   },
+               {"Windows NT R4000",     "W32MIPS"  },
+               {"Windows NT Alpha_AXP", "W32ALPHA" },
+               {"Windows NT PowerPC",   "W32PPC"   },
+               {NULL,                   ""         }
+       };
+       
+       int i=-1;
+
+       DEBUG(107,("Getting architecture dependant directory\n"));
+
+       if (long_archi == NULL) {
+               DEBUGADD(107,("Bad long_archi param.!\n"));
+               return False;
+       }
+
+       do {
+               i++;
+       } while ( (archi_table[i].long_archi!=NULL ) &&
+                 StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+       if (archi_table[i].long_archi==NULL) {
+               DEBUGADD(107,("Unknown architecture [%s] !\n", long_archi));
+               return False;
+       }
+
+       StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi));
+
+       DEBUGADD(108,("index: [%d]\n", i));
+       DEBUGADD(108,("long architecture: [%s]\n", long_archi));
+       DEBUGADD(108,("short architecture: [%s]\n", short_archi));
+       
+       return True;
+}
+
+/****************************************************************************
+ Version information in Microsoft files is held in a VS_VERSION_INFO structure.
+ There are two case to be covered here: PE (Portable Executable) and NE (New
+ Executable) files. Both files support the same INFO structure, but PE files
+ store the signature in unicode, and NE files store it as !unicode.
+ returns -1 on error, 1 on version info found, and 0 on no version info found.
+****************************************************************************/
+
+static int get_file_version(files_struct *fsp, char *fname,uint32 *major, uint32 *minor)
+{
+       int     i;
+       char    *buf;
+       ssize_t byte_count;
+
+       if ((buf=malloc(PE_HEADER_SIZE)) == NULL) {
+               DEBUG(0,("get_file_version: PE file [%s] PE Header malloc failed bytes = %d\n",
+                               fname, PE_HEADER_SIZE));
+               goto error_exit;
+       }
+
+       /* Note: DOS_HEADER_SIZE < malloc'ed PE_HEADER_SIZE */
+       if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) {
+               DEBUG(3,("get_file_version: File [%s] DOS header too short, bytes read = %d\n",
+                               fname, byte_count));
+               goto no_version_info;
+       }
+
+       /* Is this really a DOS header? */
+       if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+               DEBUG(6,("get_file_version: File [%s] bad DOS magic = 0x%x\n",
+                               fname, SVAL(buf,DOS_HEADER_MAGIC_OFFSET)));
+               goto no_version_info;
+       }
+
+       /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+       if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) {
+               DEBUG(3,("get_file_version: File [%s] too short, errno = %d\n",
+                               fname, errno));
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               goto no_version_info;
+       }
+
+       if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) {
+               DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %d\n",
+                               fname, byte_count));
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               goto no_version_info;
+       }
+
+       /* The header may be a PE (Portable Executable) or an NE (New Executable) */
+       if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+               int num_sections;
+               int section_table_bytes;
+               
+               if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) != PE_HEADER_MACHINE_I386) {
+                       DEBUG(3,("get_file_version: PE file [%s] wrong machine = 0x%x\n",
+                                       fname, SVAL(buf,PE_HEADER_MACHINE_OFFSET)));
+                       /* At this point, we assume the file is in error. It still could be somthing
+                        * else besides a PE file, but it unlikely at this point.
+                        */
+                       goto error_exit;
+               }
+
+               /* get the section table */
+               num_sections        = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS);
+               section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE;
+               SAFE_FREE(buf);
+               if ((buf=malloc(section_table_bytes)) == NULL) {
+                       DEBUG(0,("get_file_version: PE file [%s] section table malloc failed bytes = %d\n",
+                                       fname, section_table_bytes));
+                       goto error_exit;
+               }
+
+               if ((byte_count = vfs_read_data(fsp, buf, section_table_bytes)) < section_table_bytes) {
+                       DEBUG(3,("get_file_version: PE file [%s] Section header too short, bytes read = %d\n",
+                                       fname, byte_count));
+                       goto error_exit;
+               }
+
+               /* Iterate the section table looking for the resource section ".rsrc" */
+               for (i = 0; i < num_sections; i++) {
+                       int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE;
+
+                       if (strcmp(".rsrc", &buf[sec_offset+PE_HEADER_SECT_NAME_OFFSET]) == 0) {
+                               int section_pos   = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET);
+                               int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+                               SAFE_FREE(buf);
+                               if ((buf=malloc(section_bytes)) == NULL) {
+                                       DEBUG(0,("get_file_version: PE file [%s] version malloc failed bytes = %d\n",
+                                                       fname, section_bytes));
+                                       goto error_exit;
+                               }
+
+                               /* Seek to the start of the .rsrc section info */
+                               if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, section_pos, SEEK_SET) == (SMB_OFF_T)-1) {
+                                       DEBUG(3,("get_file_version: PE file [%s] too short for section info, errno = %d\n",
+                                                       fname, errno));
+                                       goto error_exit;
+                               }
+
+                               if ((byte_count = vfs_read_data(fsp, buf, section_bytes)) < section_bytes) {
+                                       DEBUG(3,("get_file_version: PE file [%s] .rsrc section too short, bytes read = %d\n",
+                                                       fname, byte_count));
+                                       goto error_exit;
+                               }
+
+                               for (i=0; i<section_bytes-VS_VERSION_INFO_UNICODE_SIZE; i++) {
+                                       /* Scan for 1st 3 unicoded bytes followed by word aligned magic value */
+                                       if (buf[i] == 'V' && buf[i+1] == '\0' && buf[i+2] == 'S') {
+                                               /* Align to next long address */
+                                               int pos = (i + sizeof(VS_SIGNATURE)*2 + 3) & 0xfffffffc;
+
+                                               if (IVAL(buf,pos) == VS_MAGIC_VALUE) {
+                                                       *major = IVAL(buf,pos+VS_MAJOR_OFFSET);
+                                                       *minor = IVAL(buf,pos+VS_MINOR_OFFSET);
+                                                       
+                                                       DEBUG(6,("get_file_version: PE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+                                                                         fname, *major, *minor,
+                                                                         (*major>>16)&0xffff, *major&0xffff,
+                                                                         (*minor>>16)&0xffff, *minor&0xffff));
+                                                       SAFE_FREE(buf);
+                                                       return 1;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               /* Version info not found, fall back to origin date/time */
+               DEBUG(10,("get_file_version: PE file [%s] has no version info\n", fname));
+               SAFE_FREE(buf);
+               return 0;
+
+       } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) {
+               if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) {
+                       DEBUG(3,("get_file_version: NE file [%s] wrong target OS = 0x%x\n",
+                                       fname, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET)));
+                       /* At this point, we assume the file is in error. It still could be somthing
+                        * else besides a NE file, but it unlikely at this point. */
+                       goto error_exit;
+               }
+
+               /* Allocate a bit more space to speed up things */
+               SAFE_FREE(buf);
+               if ((buf=malloc(VS_NE_BUF_SIZE)) == NULL) {
+                       DEBUG(0,("get_file_version: NE file [%s] malloc failed bytes  = %d\n",
+                                       fname, PE_HEADER_SIZE));
+                       goto error_exit;
+               }
+
+               /* This is a HACK! I got tired of trying to sort through the messy
+                * 'NE' file format. If anyone wants to clean this up please have at
+                * it, but this works. 'NE' files will eventually fade away. JRR */
+               while((byte_count = vfs_read_data(fsp, buf, VS_NE_BUF_SIZE)) > 0) {
+                       /* Cover case that should not occur in a well formed 'NE' .dll file */
+                       if (byte_count-VS_VERSION_INFO_SIZE <= 0) break;
+
+                       for(i=0; i<byte_count; i++) {
+                               /* Fast skip past data that can't possibly match */
+                               if (buf[i] != 'V') continue;
+
+                               /* Potential match data crosses buf boundry, move it to beginning
+                                * of buf, and fill the buf with as much as it will hold. */
+                               if (i>byte_count-VS_VERSION_INFO_SIZE) {
+                                       int bc;
+
+                                       memcpy(buf, &buf[i], byte_count-i);
+                                       if ((bc = vfs_read_data(fsp, &buf[byte_count-i], VS_NE_BUF_SIZE-
+                                                                  (byte_count-i))) < 0) {
+
+                                               DEBUG(0,("get_file_version: NE file [%s] Read error, errno=%d\n",
+                                                                fname, errno));
+                                               goto error_exit;
+                                       }
+
+                                       byte_count = bc + (byte_count - i);
+                                       if (byte_count<VS_VERSION_INFO_SIZE) break;
+
+                                       i = 0;
+                               }
+
+                               /* Check that the full signature string and the magic number that
+                                * follows exist (not a perfect solution, but the chances that this
+                                * occurs in code is, well, remote. Yes I know I'm comparing the 'V'
+                                * twice, as it is simpler to read the code. */
+                               if (strcmp(&buf[i], VS_SIGNATURE) == 0) {
+                                       /* Compute skip alignment to next long address */
+                                       int skip = -(fsp->conn->vfs_ops.lseek(fsp, fsp->fd, 0, SEEK_CUR) - (byte_count - i) +
+                                                                sizeof(VS_SIGNATURE)) & 3;
+                                       if (IVAL(buf,i+sizeof(VS_SIGNATURE)+skip) != 0xfeef04bd) continue;
+
+                                       *major = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MAJOR_OFFSET);
+                                       *minor = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MINOR_OFFSET);
+                                       DEBUG(6,("get_file_version: NE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+                                                         fname, *major, *minor,
+                                                         (*major>>16)&0xffff, *major&0xffff,
+                                                         (*minor>>16)&0xffff, *minor&0xffff));
+                                       SAFE_FREE(buf);
+                                       return 1;
+                               }
+                       }
+               }
+
+               /* Version info not found, fall back to origin date/time */
+               DEBUG(0,("get_file_version: NE file [%s] Version info not found\n", fname));
+               SAFE_FREE(buf);
+               return 0;
+
+       } else
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               DEBUG(3,("get_file_version: File [%s] unknown file format, signature = 0x%x\n",
+                               fname, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+
+       no_version_info:
+               SAFE_FREE(buf);
+               return 0;
+
+       error_exit:
+               SAFE_FREE(buf);
+               return -1;
+}
+
+/****************************************************************************
+Drivers for Microsoft systems contain multiple files. Often, multiple drivers
+share one or more files. During the MS installation process files are checked
+to insure that only a newer version of a shared file is installed over an
+older version. There are several possibilities for this comparison. If there
+is no previous version, the new one is newer (obviously). If either file is
+missing the version info structure, compare the creation date (on Unix use
+the modification date). Otherwise chose the numerically larger version number.
+****************************************************************************/
+
+static int file_version_is_newer(struct tcon_context *conn, fstring new_file, fstring old_file)
+{
+       BOOL   use_version = True;
+       pstring filepath;
+
+       uint32 new_major;
+       uint32 new_minor;
+       time_t new_create_time;
+
+       uint32 old_major;
+       uint32 old_minor;
+       time_t old_create_time;
+
+       int access_mode;
+       int action;
+       files_struct    *fsp = NULL;
+       SMB_STRUCT_STAT st;
+       SMB_STRUCT_STAT stat_buf;
+       BOOL bad_path;
+
+       ZERO_STRUCT(st);
+       ZERO_STRUCT(stat_buf);
+       new_create_time = (time_t)0;
+       old_create_time = (time_t)0;
+
+       /* Get file version info (if available) for previous file (if it exists) */
+       pstrcpy(filepath, old_file);
+
+       unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+       fsp = open_file_shared(conn, filepath, &stat_buf,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               /* Old file not found, so by definition new file is in fact newer */
+               DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n",
+                               filepath, errno));
+               return True;
+
+       } else {
+               int ret = get_file_version(fsp, old_file, &old_major, &old_minor);
+               if (ret == -1) goto error_exit;
+
+               if (!ret) {
+                       DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+                                        old_file));
+                       use_version = False;
+                       if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit;
+                       old_create_time = st.st_mtime;
+                       DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", old_create_time));
+               }
+       }
+       close_file(fsp, True);
+
+       /* Get file version info (if available) for new file */
+       pstrcpy(filepath, new_file);
+       unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+       fsp = open_file_shared(conn, filepath, &stat_buf,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               /* New file not found, this shouldn't occur if the caller did its job */
+               DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n",
+                               filepath, errno));
+               goto error_exit;
+
+       } else {
+               int ret = get_file_version(fsp, new_file, &new_major, &new_minor);
+               if (ret == -1) goto error_exit;
+
+               if (!ret) {
+                       DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+                                        new_file));
+                       use_version = False;
+                       if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit;
+                       new_create_time = st.st_mtime;
+                       DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", new_create_time));
+               }
+       }
+       close_file(fsp, True);
+
+       if (use_version && (new_major != old_major || new_minor != old_minor)) {
+               /* Compare versions and choose the larger version number */
+               if (new_major > old_major ||
+                       (new_major == old_major && new_minor > old_minor)) {
+                       
+                       DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+                       return True;
+               }
+               else {
+                       DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+                       return False;
+               }
+
+       } else {
+               /* Compare modification time/dates and choose the newest time/date */
+               if (new_create_time > old_create_time) {
+                       DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+                       return True;
+               }
+               else {
+                       DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+                       return False;
+               }
+       }
+
+       error_exit:
+               if(fsp)
+                       close_file(fsp, True);
+               return -1;
+}
+
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32 get_correct_cversion(fstring architecture, fstring driverpath_in,
+                                  struct current_user *user, WERROR *perr)
+{
+       int               cversion;
+       int               access_mode;
+       int               action;
+       NTSTATUS          nt_status;
+       pstring           driverpath;
+       DATA_BLOB         null_pw;
+       files_struct      *fsp = NULL;
+       BOOL              bad_path;
+       SMB_STRUCT_STAT   st;
+       struct tcon_context *conn;
+
+       ZERO_STRUCT(st);
+
+       *perr = WERR_INVALID_PARAM;
+
+       /* If architecture is Windows 95/98/ME, the version is always 0. */
+       if (strcmp(architecture, "WIN40") == 0) {
+               DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+               *perr = WERR_OK;
+               return 0;
+       }
+
+       /*
+        * Connect to the print$ share under the same account as the user connected
+        * to the rpc pipe. Note we must still be root to do this.
+        */
+
+       /* Null password is ok - we are already an authenticated user... */
+       null_pw = data_blob(NULL, 0);
+       become_root();
+       conn = make_connection_with_chdir("print$", null_pw, "A:", user->vuid, &nt_status);
+       unbecome_root();
+
+       if (conn == NULL) {
+               DEBUG(0,("get_correct_cversion: Unable to connect\n"));
+               *perr = ntstatus_to_werror(nt_status);
+               return -1;
+       }
+
+       /* We are temporarily becoming the connection user. */
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("get_correct_cversion: Can't become user!\n"));
+               *perr = WERR_ACCESS_DENIED;
+               return -1;
+       }
+
+       /* Open the driver file (Portable Executable format) and determine the
+        * deriver the cversion. */
+       slprintf(driverpath, sizeof(driverpath)-1, "%s/%s", architecture, driverpath_in);
+
+       unix_convert(driverpath,conn,NULL,&bad_path,&st);
+
+       fsp = open_file_shared(conn, driverpath, &st,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
+                               driverpath, errno));
+               *perr = WERR_ACCESS_DENIED;
+               goto error_exit;
+       }
+       else {
+               uint32 major;
+               uint32 minor;
+               int    ret = get_file_version(fsp, driverpath, &major, &minor);
+               if (ret == -1) goto error_exit;
+
+               if (!ret) {
+                       DEBUG(6,("get_correct_cversion: Version info not found [%s]\n", driverpath));
+                       goto error_exit;
+               }
+
+               /*
+                * This is a Microsoft'ism. See references in MSDN to VER_FILEVERSION
+                * for more details. Version in this case is not just the version of the 
+                * file, but the version in the sense of kernal mode (2) vs. user mode
+                * (3) drivers. Other bits of the version fields are the version info. 
+                * JRR 010716
+               */
+               cversion = major & 0x0000ffff;
+               switch (cversion) {
+                       case 2: /* WinNT drivers */
+                       case 3: /* Win2K drivers */
+                               break;
+                       
+                       default:
+                               DEBUG(6,("get_correct_cversion: cversion invalid [%s]  cversion = %d\n", 
+                                       driverpath, cversion));
+                               goto error_exit;
+               }
+
+               DEBUG(10,("get_correct_cversion: Version info found [%s]  major = 0x%x  minor = 0x%x\n",
+                                 driverpath, major, minor));
+       }
+
+    DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+                       driverpath, cversion));
+
+       close_file(fsp, True);
+       close_cnum(conn, user->vuid);
+       unbecome_user();
+       *perr = WERR_OK;
+       return cversion;
+
+
+  error_exit:
+
+       if(fsp)
+               close_file(fsp, True);
+
+       close_cnum(conn, user->vuid);
+       unbecome_user();
+       return -1;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver,
+                                                                                        struct current_user *user)
+{
+       fstring architecture;
+       fstring new_name;
+       char *p;
+       int i;
+       WERROR err;
+
+       /* clean up the driver name.
+        * we can get .\driver.dll
+        * or worse c:\windows\system\driver.dll !
+        */
+       /* using an intermediate string to not have overlaping memcpy()'s */
+       if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->driverpath, new_name);
+       }
+
+       if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->datafile, new_name);
+       }
+
+       if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->configfile, new_name);
+       }
+
+       if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->helpfile, new_name);
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+                               fstrcpy(new_name, p+1);
+                               fstrcpy(driver->dependentfiles[i], new_name);
+                       }
+               }
+       }
+
+       get_short_archi(architecture, driver->environment);
+       
+       /* jfm:7/16/2000 the client always sends the cversion=0.
+        * The server should check which version the driver is by reading
+        * the PE header of driver->driverpath.
+        *
+        * For Windows 95/98 the version is 0 (so the value sent is correct)
+        * For Windows NT (the architecture doesn't matter)
+        *      NT 3.1: cversion=0
+        *      NT 3.5/3.51: cversion=1
+        *      NT 4: cversion=2
+        *      NT2K: cversion=3
+        */
+       if ((driver->cversion = get_correct_cversion( architecture,
+                                                                       driver->driverpath, user, &err)) == -1)
+               return err;
+
+       return WERR_OK;
+}
+       
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver, struct current_user *user)
+{
+       fstring architecture;
+       fstring new_name;
+       char *p;
+       int i;
+       WERROR err;
+
+       /* clean up the driver name.
+        * we can get .\driver.dll
+        * or worse c:\windows\system\driver.dll !
+        */
+       /* using an intermediate string to not have overlaping memcpy()'s */
+       if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->driverpath, new_name);
+       }
+
+       if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->datafile, new_name);
+       }
+
+       if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->configfile, new_name);
+       }
+
+       if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->helpfile, new_name);
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+                               fstrcpy(new_name, p+1);
+                               fstrcpy(driver->dependentfiles[i], new_name);
+                       }
+               }
+       }
+
+       get_short_archi(architecture, driver->environment);
+
+       /* jfm:7/16/2000 the client always sends the cversion=0.
+        * The server should check which version the driver is by reading
+        * the PE header of driver->driverpath.
+        *
+        * For Windows 95/98 the version is 0 (so the value sent is correct)
+        * For Windows NT (the architecture doesn't matter)
+        *      NT 3.1: cversion=0
+        *      NT 3.5/3.51: cversion=1
+        *      NT 4: cversion=2
+        *      NT2K: cversion=3
+        */
+       if ((driver->version = get_correct_cversion(architecture, driver->driverpath, user, &err)) == -1)
+               return err;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+WERROR clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
+                                                         uint32 level, struct current_user *user)
+{
+       switch (level) {
+               case 3:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+                       driver=driver_abstract.info_3;
+                       return clean_up_driver_struct_level_3(driver, user);
+               }
+               case 6:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver;
+                       driver=driver_abstract.info_6;
+                       return clean_up_driver_struct_level_6(driver, user);
+               }
+               default:
+                       return WERR_INVALID_PARAM;
+       }
+}
+
+/****************************************************************************
+ This function sucks and should be replaced. JRA.
+****************************************************************************/
+
+static void convert_level_6_to_level3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *dst, NT_PRINTER_DRIVER_INFO_LEVEL_6 *src)
+{
+    dst->cversion  = src->version;
+
+    fstrcpy( dst->name, src->name);
+    fstrcpy( dst->environment, src->environment);
+    fstrcpy( dst->driverpath, src->driverpath);
+    fstrcpy( dst->datafile, src->datafile);
+    fstrcpy( dst->configfile, src->configfile);
+    fstrcpy( dst->helpfile, src->helpfile);
+    fstrcpy( dst->monitorname, src->monitorname);
+    fstrcpy( dst->defaultdatatype, src->defaultdatatype);
+    dst->dependentfiles = src->dependentfiles;
+}
+
+#if 0 /* Debugging function */
+
+static char* ffmt(unsigned char *c){
+       int i;
+       static char ffmt_str[17];
+
+       for (i=0; i<16; i++) {
+               if ((c[i] < ' ') || (c[i] > '~'))
+                       ffmt_str[i]='.';
+               else
+                       ffmt_str[i]=c[i];
+       }
+    ffmt_str[16]='\0';
+       return ffmt_str;
+}
+
+#endif
+
+/****************************************************************************
+****************************************************************************/
+BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level, 
+                                 struct current_user *user, WERROR *perr)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 converted_driver;
+       fstring architecture;
+       pstring new_dir;
+       pstring old_name;
+       pstring new_name;
+       DATA_BLOB null_pw;
+       struct tcon_context *conn;
+       NTSTATUS nt_status;
+       pstring inbuf;
+       pstring outbuf;
+       int ver = 0;
+       int i;
+
+       memset(inbuf, '\0', sizeof(inbuf));
+       memset(outbuf, '\0', sizeof(outbuf));
+       *perr = WERR_OK;
+
+       if (level==3)
+               driver=driver_abstract.info_3;
+       else if (level==6) {
+               convert_level_6_to_level3(&converted_driver, driver_abstract.info_6);
+               driver = &converted_driver;
+       } else {
+               DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)level ));
+               return False;
+       }
+
+       get_short_archi(architecture, driver->environment);
+
+       /*
+        * Connect to the print$ share under the same account as the user connected to the rpc pipe.
+        * Note we must be root to do this.
+        */
+
+       become_root();
+       null_pw = data_blob(NULL, 0);
+       conn = make_connection_with_chdir("print$", null_pw, "A:", user->vuid, &nt_status);
+       unbecome_root();
+
+       if (conn == NULL) {
+               DEBUG(0,("move_driver_to_download_area: Unable to connect\n"));
+               *perr = ntstatus_to_werror(nt_status);
+               return False;
+       }
+
+       /*
+        * Save who we are - we are temporarily becoming the connection user.
+        */
+
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("move_driver_to_download_area: Can't become user!\n"));
+               return False;
+       }
+
+       /*
+        * make the directories version and version\driver_name
+        * under the architecture directory.
+        */
+       DEBUG(5,("Creating first directory\n"));
+       slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion);
+       mkdir_internal(conn, new_dir);
+
+       /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
+        * listed for this driver which has already been moved, skip it (note:
+        * drivers may list the same file name several times. Then check if the
+        * file already exists in archi\cversion\, if so, check that the version
+        * info (or time stamps if version info is unavailable) is newer (or the
+        * date is later). If it is, move it to archi\cversion\filexxx.yyy.
+        * Otherwise, delete the file.
+        *
+        * If a file is not moved to archi\cversion\ because of an error, all the
+        * rest of the 'unmoved' driver files are removed from archi\. If one or
+        * more of the driver's files was already moved to archi\cversion\, it
+        * potentially leaves the driver in a partially updated state. Version
+        * trauma will most likely occur if an client attempts to use any printer
+        * bound to the driver. Perhaps a rewrite to make sure the moves can be
+        * done is appropriate... later JRR
+        */
+
+       DEBUG(5,("Moving files now !\n"));
+
+       if (driver->driverpath && strlen(driver->driverpath)) {
+               slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->driverpath);      
+               slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->driverpath);   
+               if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                       NTSTATUS status;
+                       status = rename_internals(conn, new_name, old_name, True);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                               new_name, old_name));
+                               *perr = ntstatus_to_werror(status);
+                               unlink_internals(conn, 0, new_name);
+                               ver = -1;
+                       }
+               }
+               else
+                       unlink_internals(conn, 0, new_name);
+       }
+
+       if (driver->datafile && strlen(driver->datafile)) {
+               if (!strequal(driver->datafile, driver->driverpath)) {
+                       slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->datafile);        
+                       slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->datafile);     
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               NTSTATUS status;
+                               status = rename_internals(conn, new_name, old_name, True);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = ntstatus_to_werror(status);
+                                       unlink_internals(conn, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, 0, new_name);
+               }
+       }
+
+       if (driver->configfile && strlen(driver->configfile)) {
+               if (!strequal(driver->configfile, driver->driverpath) &&
+                       !strequal(driver->configfile, driver->datafile)) {
+                       slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->configfile);      
+                       slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->configfile);   
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               NTSTATUS status;
+                               status = rename_internals(conn, new_name, old_name, True);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = ntstatus_to_werror(status);
+                                       unlink_internals(conn, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, 0, new_name);
+               }
+       }
+
+       if (driver->helpfile && strlen(driver->helpfile)) {
+               if (!strequal(driver->helpfile, driver->driverpath) &&
+                       !strequal(driver->helpfile, driver->datafile) &&
+                       !strequal(driver->helpfile, driver->configfile)) {
+                       slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->helpfile);        
+                       slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->helpfile);     
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               NTSTATUS status;
+                               status = rename_internals(conn, new_name, old_name, True);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = ntstatus_to_werror(status);
+                                       unlink_internals(conn, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, 0, new_name);
+               }
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if (!strequal(driver->dependentfiles[i], driver->driverpath) &&
+                               !strequal(driver->dependentfiles[i], driver->datafile) &&
+                               !strequal(driver->dependentfiles[i], driver->configfile) &&
+                               !strequal(driver->dependentfiles[i], driver->helpfile)) {
+                               int j;
+                               for (j=0; j < i; j++) {
+                                       if (strequal(driver->dependentfiles[i], driver->dependentfiles[j])) {
+                                               goto NextDriver;
+                                       }
+                               }
+
+                               slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->dependentfiles[i]);       
+                               slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->dependentfiles[i]);    
+                               if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                                       NTSTATUS status;
+                                       status = rename_internals(conn, new_name, old_name, True);
+                                       if (!NT_STATUS_IS_OK(status)) {
+                                               DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                               new_name, old_name));
+                                               *perr = ntstatus_to_werror(status);
+                                               unlink_internals(conn, 0, new_name);
+                                               ver = -1;
+                                       }
+                               }
+                               else
+                                       unlink_internals(conn, 0, new_name);
+                       }
+               NextDriver: ;
+               }
+       }
+
+       close_cnum(conn, user->vuid);
+       unbecome_user();
+
+       return ver == -1 ? False : True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+{
+       int len, buflen;
+       fstring architecture;
+       pstring directory;
+       fstring temp_name;
+       pstring key;
+       char *buf;
+       int i, ret;
+       TDB_DATA kbuf, dbuf;
+
+       get_short_archi(architecture, driver->environment);
+
+       /* The names are relative. We store them in the form: \print$\arch\version\driver.xxx
+        * \\server is added in the rpc server layer.
+        * It does make sense to NOT store the server's name in the printer TDB.
+        */
+
+       slprintf(directory, sizeof(directory)-1, "\\print$\\%s\\%d\\", architecture, driver->cversion);
+
+       /* .inf files do not always list a file for each of the four standard files. 
+        * Don't prepend a path to a null filename, or client claims:
+        *   "The server on which the printer resides does not have a suitable 
+        *   <printer driver name> printer driver installed. Click OK if you 
+        *   wish to install the driver on your local machine."
+        */
+       if (strlen(driver->driverpath)) {
+               fstrcpy(temp_name, driver->driverpath);
+               slprintf(driver->driverpath, sizeof(driver->driverpath)-1, "%s%s", directory, temp_name);
+       }
+
+       if (strlen(driver->datafile)) {
+               fstrcpy(temp_name, driver->datafile);
+               slprintf(driver->datafile, sizeof(driver->datafile)-1, "%s%s", directory, temp_name);
+       }
+
+       if (strlen(driver->configfile)) {
+               fstrcpy(temp_name, driver->configfile);
+               slprintf(driver->configfile, sizeof(driver->configfile)-1, "%s%s", directory, temp_name);
+       }
+
+       if (strlen(driver->helpfile)) {
+               fstrcpy(temp_name, driver->helpfile);
+               slprintf(driver->helpfile, sizeof(driver->helpfile)-1, "%s%s", directory, temp_name);
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       fstrcpy(temp_name, driver->dependentfiles[i]);
+                       slprintf(driver->dependentfiles[i], sizeof(driver->dependentfiles[i])-1, "%s%s", directory, temp_name);
+               }
+       }
+
+       slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name);
+
+       DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key ));
+
+       buf = NULL;
+       len = buflen = 0;
+
+ again:
+       len = 0;
+       len += tdb_pack(buf+len, buflen-len, "dffffffff",
+                       driver->cversion,
+                       driver->name,
+                       driver->environment,
+                       driver->driverpath,
+                       driver->datafile,
+                       driver->configfile,
+                       driver->helpfile,
+                       driver->monitorname,
+                       driver->defaultdatatype);
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       len += tdb_pack(buf+len, buflen-len, "f",
+                                       driver->dependentfiles[i]);
+               }
+       }
+
+       if (len != buflen) {
+               char *tb;
+
+               tb = (char *)Realloc(buf, len);
+               if (!tb) {
+                       DEBUG(0,("add_a_printer_driver_3: failed to enlarge buffer\n!"));
+                       ret = -1;
+                       goto done;
+               }
+               else buf = tb;
+               buflen = len;
+               goto again;
+       }
+
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
+       
+       ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+       if (ret)
+               DEBUG(0,("add_a_printer_driver_3: Adding driver with key %s failed.\n", key ));
+
+       SAFE_FREE(buf);
+       return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 info3;
+
+       ZERO_STRUCT(info3);
+       info3.cversion = driver->version;
+       fstrcpy(info3.name,driver->name);
+       fstrcpy(info3.environment,driver->environment);
+       fstrcpy(info3.driverpath,driver->driverpath);
+       fstrcpy(info3.datafile,driver->datafile);
+       fstrcpy(info3.configfile,driver->configfile);
+       fstrcpy(info3.helpfile,driver->helpfile);
+       fstrcpy(info3.monitorname,driver->monitorname);
+       fstrcpy(info3.defaultdatatype,driver->defaultdatatype);
+       info3.dependentfiles = driver->dependentfiles;
+
+       return add_a_printer_driver_3(&info3);
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3_default(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, const char *driver, const char *arch)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 info;
+
+       ZERO_STRUCT(info);
+
+       fstrcpy(info.name, driver);
+       fstrcpy(info.defaultdatatype, "RAW");
+       
+       fstrcpy(info.driverpath, "");
+       fstrcpy(info.datafile, "");
+       fstrcpy(info.configfile, "");
+       fstrcpy(info.helpfile, "");
+
+       if ((info.dependentfiles=(fstring *)malloc(2*sizeof(fstring))) == NULL)
+               return WERR_NOMEM;
+
+       memset(info.dependentfiles, '\0', 2*sizeof(fstring));
+       fstrcpy(info.dependentfiles[0], "");
+
+       *info_ptr = memdup(&info, sizeof(info));
+       
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring drivername, const char *arch, uint32 version)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 driver;
+       TDB_DATA kbuf, dbuf;
+       fstring architecture;
+       int len = 0;
+       int i;
+       pstring key;
+
+       ZERO_STRUCT(driver);
+
+       get_short_archi(architecture, arch);
+
+       DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, drivername));
+
+       slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, version, drivername);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       
+       dbuf = tdb_fetch(tdb_drivers, kbuf);
+       if (!dbuf.dptr) 
+               return WERR_UNKNOWN_PRINTER_DRIVER;
+
+       len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff",
+                         &driver.cversion,
+                         driver.name,
+                         driver.environment,
+                         driver.driverpath,
+                         driver.datafile,
+                         driver.configfile,
+                         driver.helpfile,
+                         driver.monitorname,
+                         driver.defaultdatatype);
+
+       i=0;
+       while (len < dbuf.dsize) {
+               fstring *tddfs;
+
+               tddfs = (fstring *)Realloc(driver.dependentfiles,
+                                                        sizeof(fstring)*(i+2));
+               if (tddfs == NULL) {
+                       DEBUG(0,("get_a_printer_driver_3: failed to enlarge buffer!\n"));
+                       break;
+               }
+               else driver.dependentfiles = tddfs;
+
+               len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f",
+                                 &driver.dependentfiles[i]);
+               i++;
+       }
+       
+       if (driver.dependentfiles != NULL)
+               fstrcpy(driver.dependentfiles[i], "");
+
+       SAFE_FREE(dbuf.dptr);
+
+       if (len != dbuf.dsize) {
+               SAFE_FREE(driver.dependentfiles);
+
+               return get_a_printer_driver_3_default(info_ptr, drivername, arch);
+       }
+
+       *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver));
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+
+static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+       uint32 result;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+       int i;
+       
+       DEBUG(20,("Dumping printer driver at level [%d]\n", level));
+       
+       switch (level)
+       {
+               case 3:
+               {
+                       if (driver.info_3 == NULL)
+                               result=5;
+                       else {
+                               info3=driver.info_3;
+                       
+                               DEBUGADD(20,("version:[%d]\n",         info3->cversion));
+                               DEBUGADD(20,("name:[%s]\n",            info3->name));
+                               DEBUGADD(20,("environment:[%s]\n",     info3->environment));
+                               DEBUGADD(20,("driverpath:[%s]\n",      info3->driverpath));
+                               DEBUGADD(20,("datafile:[%s]\n",        info3->datafile));
+                               DEBUGADD(20,("configfile:[%s]\n",      info3->configfile));
+                               DEBUGADD(20,("helpfile:[%s]\n",        info3->helpfile));
+                               DEBUGADD(20,("monitorname:[%s]\n",     info3->monitorname));
+                               DEBUGADD(20,("defaultdatatype:[%s]\n", info3->defaultdatatype));
+                               
+                               for (i=0; info3->dependentfiles &&
+                                         *info3->dependentfiles[i]; i++) {
+                                       DEBUGADD(20,("dependentfile:[%s]\n",
+                                                     info3->dependentfiles[i]));
+                               }
+                               result=0;
+                       }
+                       break;
+               }
+               default:
+                       DEBUGADD(20,("dump_a_printer_driver: Level %u not implemented\n", (unsigned int)level));
+                       result=1;
+                       break;
+       }
+       
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen)
+{
+       int len = 0;
+
+       len += tdb_pack(buf+len, buflen-len, "p", nt_devmode);
+
+       if (!nt_devmode)
+               return len;
+
+       len += tdb_pack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+                       nt_devmode->devicename,
+                       nt_devmode->formname,
+
+                       nt_devmode->specversion,
+                       nt_devmode->driverversion,
+                       nt_devmode->size,
+                       nt_devmode->driverextra,
+                       nt_devmode->orientation,
+                       nt_devmode->papersize,
+                       nt_devmode->paperlength,
+                       nt_devmode->paperwidth,
+                       nt_devmode->scale,
+                       nt_devmode->copies,
+                       nt_devmode->defaultsource,
+                       nt_devmode->printquality,
+                       nt_devmode->color,
+                       nt_devmode->duplex,
+                       nt_devmode->yresolution,
+                       nt_devmode->ttoption,
+                       nt_devmode->collate,
+                       nt_devmode->logpixels,
+                       
+                       nt_devmode->fields,
+                       nt_devmode->bitsperpel,
+                       nt_devmode->pelswidth,
+                       nt_devmode->pelsheight,
+                       nt_devmode->displayflags,
+                       nt_devmode->displayfrequency,
+                       nt_devmode->icmmethod,
+                       nt_devmode->icmintent,
+                       nt_devmode->mediatype,
+                       nt_devmode->dithertype,
+                       nt_devmode->reserved1,
+                       nt_devmode->reserved2,
+                       nt_devmode->panningwidth,
+                       nt_devmode->panningheight,
+                       nt_devmode->private);
+
+       
+       if (nt_devmode->private) {
+               len += tdb_pack(buf+len, buflen-len, "B",
+                               nt_devmode->driverextra,
+                               nt_devmode->private);
+       }
+
+       DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname));
+
+       return len;
+}
+
+/****************************************************************************
+ Pack all values in all printer keys
+ ***************************************************************************/
+static int pack_values(NT_PRINTER_DATA *data, char *buf, int buflen)
+{
+       int             len = 0;
+       int             i, j;
+       REGISTRY_VALUE  *val;
+       REGVAL_CTR      *val_ctr;
+       pstring         path;
+       int             num_values;
+
+       if ( !data )
+               return 0;
+
+       /* loop over all keys */
+               
+       for ( i=0; i<data->num_keys; i++ ) {    
+               val_ctr = &data->keys[i].values;
+               num_values = regval_ctr_numvals( val_ctr );
+               
+               /* loop over all values */
+               
+               for ( j=0; j<num_values; j++ ) {
+                       /* pathname should be stored as <key>\<value> */
+                       
+                       val = regval_ctr_specific_value( val_ctr, j );
+                       pstrcpy( path, data->keys[i].name );
+                       pstrcat( path, "\\" );
+                       pstrcat( path, regval_name(val) );
+                       
+                       len += tdb_pack(buf+len, buflen-len, "pPdB",
+                                       val,
+                                       path,
+                                       regval_type(val),
+                                       regval_size(val),
+                                       regval_data_p(val) );
+               }
+       
+       }
+
+       /* terminator */
+       
+       len += tdb_pack(buf+len, buflen-len, "p", NULL);
+
+       return len;
+}
+
+
+/****************************************************************************
+ Delete a printer - this just deletes the printer info file, any open
+ handles are not affected.
+****************************************************************************/
+
+uint32 del_a_printer(char *sharename)
+{
+       pstring key;
+       TDB_DATA kbuf;
+
+       slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+       kbuf.dptr=key;
+       kbuf.dsize=strlen(key)+1;
+
+       tdb_delete(tdb_printers, kbuf);
+       return 0;
+}
+
+/* FIXME!!!  Reorder so this forward declaration is not necessary --jerry */
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **, const char* sharename);
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **);
+/****************************************************************************
+****************************************************************************/
+static WERROR update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+       pstring key;
+       char *buf;
+       int buflen, len;
+       WERROR ret;
+       TDB_DATA kbuf, dbuf;
+       
+       /*
+        * in addprinter: no servername and the printer is the name
+        * in setprinter: servername is \\server
+        *                and printer is \\server\\printer
+        *
+        * Samba manages only local printers.
+        * we currently don't support things like path=\\other_server\printer
+        */
+
+       if (info->servername[0]!='\0') {
+               trim_string(info->printername, info->servername, NULL);
+               trim_string(info->printername, "\\", NULL);
+               info->servername[0]='\0';
+       }
+
+       /*
+        * JFM: one day I'll forget.
+        * below that's info->portname because that's the SAMBA sharename
+        * and I made NT 'thinks' it's the portname
+        * the info->sharename is the thing you can name when you add a printer
+        * that's the short-name when you create shared printer for 95/98
+        * So I've made a limitation in SAMBA: you can only have 1 printer model
+        * behind a SAMBA share.
+        */
+
+       buf = NULL;
+       buflen = 0;
+
+ again:        
+       len = 0;
+       len += tdb_pack(buf+len, buflen-len, "dddddddddddfffffPfffff",
+                       info->attributes,
+                       info->priority,
+                       info->default_priority,
+                       info->starttime,
+                       info->untiltime,
+                       info->status,
+                       info->cjobs,
+                       info->averageppm,
+                       info->changeid,
+                       info->c_setprinter,
+                       info->setuptime,
+                       info->servername,
+                       info->printername,
+                       info->sharename,
+                       info->portname,
+                       info->drivername,
+                       info->comment,
+                       info->location,
+                       info->sepfile,
+                       info->printprocessor,
+                       info->datatype,
+                       info->parameters);
+
+       len += pack_devicemode(info->devmode, buf+len, buflen-len);
+       
+       len += pack_values( &info->data, buf+len, buflen-len );
+
+       if (buflen != len) {
+               char *tb;
+
+               tb = (char *)Realloc(buf, len);
+               if (!tb) {
+                       DEBUG(0,("update_a_printer_2: failed to enlarge buffer!\n"));
+                       ret = WERR_NOMEM;
+                       goto done;
+               }
+               else buf = tb;
+               buflen = len;
+               goto again;
+       }
+       
+
+       slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, info->sharename);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
+
+       ret = (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) == 0? WERR_OK : WERR_NOMEM);
+
+done:
+       if (!W_ERROR_IS_OK(ret))
+               DEBUG(8, ("error updating printer to tdb on disk\n"));
+
+       SAFE_FREE(buf);
+
+       DEBUG(8,("packed printer [%s] with driver [%s] portname=[%s] len=%d\n",
+                info->sharename, info->drivername, info->portname, len));
+
+       return ret;
+}
+
+
+/****************************************************************************
+ Malloc and return an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
+{
+
+       char adevice[32];
+       NT_DEVICEMODE *nt_devmode = (NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE));
+
+       if (nt_devmode == NULL) {
+               DEBUG(0,("construct_nt_devicemode: malloc fail.\n"));
+               return NULL;
+       }
+
+       ZERO_STRUCTP(nt_devmode);
+
+       safe_strcpy(adevice, default_devicename, sizeof(adevice));
+       fstrcpy(nt_devmode->devicename, adevice);       
+       
+       fstrcpy(nt_devmode->formname, "Letter");
+
+       nt_devmode->specversion      = 0x0401;
+       nt_devmode->driverversion    = 0x0400;
+       nt_devmode->size             = 0x00DC;
+       nt_devmode->driverextra      = 0x0000;
+       nt_devmode->fields           = FORMNAME | TTOPTION | PRINTQUALITY |
+                                      DEFAULTSOURCE | COPIES | SCALE |
+                                      PAPERSIZE | ORIENTATION;
+       nt_devmode->orientation      = 1;
+       nt_devmode->papersize        = PAPER_LETTER;
+       nt_devmode->paperlength      = 0;
+       nt_devmode->paperwidth       = 0;
+       nt_devmode->scale            = 0x64;
+       nt_devmode->copies           = 1;
+       nt_devmode->defaultsource    = BIN_FORMSOURCE;
+       nt_devmode->printquality     = RES_HIGH;           /* 0x0258 */
+       nt_devmode->color            = COLOR_MONOCHROME;
+       nt_devmode->duplex           = DUP_SIMPLEX;
+       nt_devmode->yresolution      = 0;
+       nt_devmode->ttoption         = TT_SUBDEV;
+       nt_devmode->collate          = COLLATE_FALSE;
+       nt_devmode->icmmethod        = 0;
+       nt_devmode->icmintent        = 0;
+       nt_devmode->mediatype        = 0;
+       nt_devmode->dithertype       = 0;
+
+       /* non utilisés par un driver d'imprimante */
+       nt_devmode->logpixels        = 0;
+       nt_devmode->bitsperpel       = 0;
+       nt_devmode->pelswidth        = 0;
+       nt_devmode->pelsheight       = 0;
+       nt_devmode->displayflags     = 0;
+       nt_devmode->displayfrequency = 0;
+       nt_devmode->reserved1        = 0;
+       nt_devmode->reserved2        = 0;
+       nt_devmode->panningwidth     = 0;
+       nt_devmode->panningheight    = 0;
+       
+       nt_devmode->private = NULL;
+       return nt_devmode;
+}
+
+/****************************************************************************
+ Deepcopy an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *dup_nt_devicemode(NT_DEVICEMODE *nt_devicemode)
+{
+       NT_DEVICEMODE *new_nt_devicemode = NULL;
+
+       if ( !nt_devicemode )
+               return NULL;
+
+       if ((new_nt_devicemode = (NT_DEVICEMODE *)memdup(nt_devicemode, sizeof(NT_DEVICEMODE))) == NULL) {
+               DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+               return NULL;
+       }
+
+       new_nt_devicemode->private = NULL;
+       if (nt_devicemode->private != NULL) {
+               if ((new_nt_devicemode->private = memdup(nt_devicemode->private, nt_devicemode->driverextra)) == NULL) {
+                       SAFE_FREE(new_nt_devicemode);
+                       DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+                       return NULL;
+        }
+       }
+
+       return new_nt_devicemode;
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_DEVICEMODE.
+****************************************************************************/
+
+void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
+{
+       NT_DEVICEMODE *nt_devmode = *devmode_ptr;
+
+       if(nt_devmode == NULL)
+               return;
+
+       DEBUG(106,("free_nt_devicemode: deleting DEVMODE\n"));
+
+       SAFE_FREE(nt_devmode->private);
+       SAFE_FREE(*devmode_ptr);
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_PRINTER_INFO_LEVEL_2.
+****************************************************************************/
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
+{
+       NT_PRINTER_INFO_LEVEL_2 *info = *info_ptr;
+       NT_PRINTER_DATA         *data;
+       int                     i;
+
+       if ( !info )
+               return;
+
+       DEBUG(106,("free_nt_printer_info_level_2: deleting info\n"));
+
+       free_nt_devicemode(&info->devmode);
+
+       /* clean up all registry keys */
+       
+       data = &info->data;
+       for ( i=0; i<data->num_keys; i++ ) {
+               SAFE_FREE( data->keys[i].name );
+               regval_ctr_destroy( &data->keys[i].values );
+       }
+       SAFE_FREE( data->keys );
+
+       /* finally the top level structure */
+       
+       SAFE_FREE( *info_ptr );
+}
+
+
+/****************************************************************************
+****************************************************************************/
+int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen)
+{
+       int len = 0;
+       int extra_len = 0;
+       NT_DEVICEMODE devmode;
+
+       ZERO_STRUCT(devmode);
+
+       len += tdb_unpack(buf+len, buflen-len, "p", nt_devmode);
+
+       if (!*nt_devmode) return len;
+
+       len += tdb_unpack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+                         devmode.devicename,
+                         devmode.formname,
+
+                         &devmode.specversion,
+                         &devmode.driverversion,
+                         &devmode.size,
+                         &devmode.driverextra,
+                         &devmode.orientation,
+                         &devmode.papersize,
+                         &devmode.paperlength,
+                         &devmode.paperwidth,
+                         &devmode.scale,
+                         &devmode.copies,
+                         &devmode.defaultsource,
+                         &devmode.printquality,
+                         &devmode.color,
+                         &devmode.duplex,
+                         &devmode.yresolution,
+                         &devmode.ttoption,
+                         &devmode.collate,
+                         &devmode.logpixels,
+                       
+                         &devmode.fields,
+                         &devmode.bitsperpel,
+                         &devmode.pelswidth,
+                         &devmode.pelsheight,
+                         &devmode.displayflags,
+                         &devmode.displayfrequency,
+                         &devmode.icmmethod,
+                         &devmode.icmintent,
+                         &devmode.mediatype,
+                         &devmode.dithertype,
+                         &devmode.reserved1,
+                         &devmode.reserved2,
+                         &devmode.panningwidth,
+                         &devmode.panningheight,
+                         &devmode.private);
+       
+       if (devmode.private) {
+               /* the len in tdb_unpack is an int value and
+                * devmode.driverextra is only a short
+                */
+               len += tdb_unpack(buf+len, buflen-len, "B", &extra_len, &devmode.private);
+               devmode.driverextra=(uint16)extra_len;
+               
+               /* check to catch an invalid TDB entry so we don't segfault */
+               if (devmode.driverextra == 0) {
+                       devmode.private = NULL;
+               }
+       }
+
+       *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode));
+
+       DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname));
+       if (devmode.private)
+               DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra));
+
+       return len;
+}
+
+/****************************************************************************
+ Allocate and initialize a new slot.
+***************************************************************************/
+static int add_new_printer_key( NT_PRINTER_DATA *data, const char *name )
+{
+       NT_PRINTER_KEY  *d;
+       int             key_index;
+       
+       if ( !data || !name )
+               return -1;
+       
+       /* allocate another slot in the NT_PRINTER_KEY array */
+       
+       d = Realloc( data->keys, sizeof(NT_PRINTER_KEY)*(data->num_keys+1) );
+       if ( d )
+               data->keys = d;
+       
+       key_index = data->num_keys;
+       
+       /* initialze new key */
+       
+       data->num_keys++;
+       data->keys[key_index].name = strdup( name );
+       
+       ZERO_STRUCTP( &data->keys[key_index].values );
+       
+       regval_ctr_init( &data->keys[key_index].values );
+       
+       DEBUG(10,("add_new_printer_key: Inserted new data key [%s]\n", name ));
+       
+       return key_index;
+}
+
+/****************************************************************************
+ search for a registry key name in the existing printer data
+ ***************************************************************************/
+int lookup_printerkey( NT_PRINTER_DATA *data, const char *name )
+{
+       int             key_index = -1;
+       int             i;
+       
+       if ( !data || !name )
+               return -1;
+
+       DEBUG(12,("lookup_printerkey: Looking for [%s]\n", name));
+
+       /* loop over all existing keys */
+       
+       for ( i=0; i<data->num_keys; i++ ) {
+               if ( strequal(data->keys[i].name, name) ) {
+                       DEBUG(12,("lookup_printerkey: Found [%s]!\n", name));
+                       key_index = i;
+                       break;
+               
+               }
+       }
+       
+       return key_index;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+uint32 get_printer_subkeys( NT_PRINTER_DATA *data, const char* key, fstring **subkeys )
+{
+       int     i, j;
+       int     key_len;
+       int     num_subkeys = 0;
+       char    *p;
+       fstring *ptr, *subkeys_ptr = NULL;
+       fstring subkeyname;
+       
+       if ( !data )
+               return 0;
+               
+       for ( i=0; i<data->num_keys; i++ ) {
+               if ( StrnCaseCmp(data->keys[i].name, key, strlen(key)) == 0 ) {
+                       /* match sure it is a subkey and not the key itself */
+                       
+                       key_len = strlen( key );
+                       if ( strlen(data->keys[i].name) == key_len )
+                               continue;
+                       
+                       /* get subkey path */
+
+                       p = data->keys[i].name + key_len;
+                       if ( *p == '\\' )
+                               p++;
+                       fstrcpy( subkeyname, p );
+                       if ( (p = strchr( subkeyname, '\\' )) )
+                               *p = '\0';
+                       
+                       /* don't add a key more than once */
+                       
+                       for ( j=0; j<num_subkeys; j++ ) {
+                               if ( strequal( subkeys_ptr[j], subkeyname ) )
+                                       break;
+                       }
+                       
+                       if ( j != num_subkeys )
+                               continue;
+
+                       /* found a match, so allocate space and copy the name */
+                       
+                       if ( !(ptr = Realloc( subkeys_ptr, (num_subkeys+2)*sizeof(fstring))) ) {
+                               DEBUG(0,("get_printer_subkeys: Realloc failed for [%d] entries!\n", 
+                                       num_subkeys+1));
+                               SAFE_FREE( subkeys );
+                               return 0;
+                       }
+                       
+                       subkeys_ptr = ptr;
+                       fstrcpy( subkeys_ptr[num_subkeys], subkeyname );
+                       num_subkeys++;
+               }
+               
+       }
+       
+       /* tag of the end */
+       
+       if (num_subkeys)
+               fstrcpy(subkeys_ptr[num_subkeys], "" );
+       
+       *subkeys = subkeys_ptr;
+
+       return num_subkeys;
+}
+
+static void map_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name, 
+                           const char *sz)
+{
+       smb_ucs2_t conv_str[1024];
+       size_t str_size;
+
+       regval_ctr_delvalue(ctr, val_name);
+       str_size = push_ucs2(NULL, conv_str, sz, sizeof(conv_str),
+                            STR_TERMINATE | STR_NOALIGN);
+       regval_ctr_addvalue(ctr, val_name, REG_SZ, 
+                           (char *) conv_str, str_size);
+}
+
+static void map_dword_into_ctr(REGVAL_CTR *ctr, const char *val_name, 
+                              uint32 dword)
+{
+       regval_ctr_delvalue(ctr, val_name);
+       regval_ctr_addvalue(ctr, val_name, REG_DWORD,
+                           (char *) &dword, sizeof(dword));
+}
+
+static void map_bool_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+                             BOOL bool)
+{
+       uint8 bin_bool = (bool ? 1 : 0);
+       regval_ctr_delvalue(ctr, val_name);
+       regval_ctr_addvalue(ctr, val_name, REG_BINARY, 
+                           (char *) &bin_bool, sizeof(bin_bool));
+}
+
+static void map_single_multi_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+                                        const char *multi_sz)
+{
+       smb_ucs2_t *conv_strs = NULL;
+       size_t str_size;
+
+       /* a multi-sz has to have a null string terminator, i.e., the last
+          string must be followed by two nulls */
+       str_size = (strlen(multi_sz) + 2) * sizeof(smb_ucs2_t);
+       conv_strs = calloc(str_size, 1);
+
+       push_ucs2(NULL, conv_strs, multi_sz, str_size, 
+                 STR_TERMINATE | STR_NOALIGN);
+
+       regval_ctr_delvalue(ctr, val_name);
+       regval_ctr_addvalue(ctr, val_name, REG_MULTI_SZ, 
+                           (char *) conv_strs, str_size);      
+       safe_free(conv_strs);
+       
+}
+
+/****************************************************************************
+ * Map the NT_PRINTER_INFO_LEVEL_2 data into DsSpooler keys for publishing.
+ *
+ * @param info2 NT_PRINTER_INFO_LEVEL_2 describing printer - gets modified
+ * @return BOOL indicating success or failure
+ ***************************************************************************/
+
+static BOOL map_nt_printer_info2_to_dsspooler(NT_PRINTER_INFO_LEVEL_2 *info2)
+{
+       REGVAL_CTR *ctr = NULL;
+       fstring longname;
+       char *allocated_string = NULL;
+        const char *ascii_str;
+       int i;
+
+       if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+               i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+       ctr = &info2->data.keys[i].values;
+
+       map_sz_into_ctr(ctr, SPOOL_REG_PRINTERNAME, info2->sharename);
+       map_sz_into_ctr(ctr, SPOOL_REG_SHORTSERVERNAME, lp_netbios_name());
+
+       get_myfullname(longname);
+       map_sz_into_ctr(ctr, SPOOL_REG_SERVERNAME, longname);
+
+       asprintf(&allocated_string, "\\\\%s\\%s", longname, info2->sharename);
+       map_sz_into_ctr(ctr, SPOOL_REG_UNCNAME, allocated_string);
+       SAFE_FREE(allocated_string);
+
+       map_dword_into_ctr(ctr, SPOOL_REG_VERSIONNUMBER, 4);
+       map_sz_into_ctr(ctr, SPOOL_REG_DRIVERNAME, info2->drivername);
+       map_sz_into_ctr(ctr, SPOOL_REG_LOCATION, info2->location);
+       map_sz_into_ctr(ctr, SPOOL_REG_DESCRIPTION, info2->comment);
+       map_single_multi_sz_into_ctr(ctr, SPOOL_REG_PORTNAME, info2->portname);
+       map_sz_into_ctr(ctr, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);
+       map_dword_into_ctr(ctr, SPOOL_REG_PRINTSTARTTIME, info2->starttime);
+       map_dword_into_ctr(ctr, SPOOL_REG_PRINTENDTIME, info2->untiltime);
+       map_dword_into_ctr(ctr, SPOOL_REG_PRIORITY, info2->priority);
+
+       map_bool_into_ctr(ctr, SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+                         (info2->attributes & 
+                          PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS));
+
+       switch (info2->attributes & 0x3) {
+       case 0:
+               ascii_str = SPOOL_REGVAL_PRINTWHILESPOOLING;
+               break;
+       case 1:
+               ascii_str = SPOOL_REGVAL_PRINTAFTERSPOOLED;
+               break;
+       case 2:
+               ascii_str = SPOOL_REGVAL_PRINTDIRECT;
+               break;
+       default:
+               ascii_str = "unknown";
+       }
+       map_sz_into_ctr(ctr, SPOOL_REG_PRINTSPOOLING, ascii_str);
+
+       return True;
+}
+
+#ifdef HAVE_ADS
+static void store_printer_guid(NT_PRINTER_INFO_LEVEL_2 *info2, GUID guid)
+{
+       int i;
+       REGVAL_CTR *ctr=NULL;
+
+       /* find the DsSpooler key */
+       if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+               i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+       ctr = &info2->data.keys[i].values;
+
+       regval_ctr_delvalue(ctr, "objectGUID");
+       regval_ctr_addvalue(ctr, "objectGUID", REG_BINARY, 
+                           (char *) &guid, sizeof(GUID));      
+}
+
+static WERROR publish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+       ADS_STATUS ads_rc;
+       TALLOC_CTX *ctx = talloc_init("publish_it");
+       ADS_MODLIST mods = ads_init_mods(ctx);
+       char *prt_dn = NULL, *srv_dn, **srv_cn;
+       void *res = NULL;
+       ADS_STRUCT *ads;
+       const char *attrs[] = {"objectGUID", NULL};
+       GUID guid;
+       WERROR win_rc = WERR_OK;
+
+       ZERO_STRUCT(guid);
+       /* set the DsSpooler info and attributes */
+       if (!(map_nt_printer_info2_to_dsspooler(printer->info_2)))
+                       return WERR_NOMEM;
+       printer->info_2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
+       win_rc = mod_a_printer(*printer, 2);
+       if (!W_ERROR_IS_OK(win_rc)) {
+               DEBUG(3, ("err %d saving data\n",
+                                 W_ERROR_V(win_rc)));
+               return win_rc;
+       }
+
+       /* Build the ads mods */
+       get_local_printer_publishing_data(ctx, &mods, 
+                                         &printer->info_2->data);
+       ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME, 
+                   printer->info_2->sharename);
+
+       /* connect to the ADS server */
+       ads = ads_init(NULL, NULL, lp_ads_server());
+       if (!ads) {
+               DEBUG(3, ("ads_init() failed\n"));
+               return WERR_SERVER_UNAVAILABLE;
+       }
+       ads_rc = ads_connect(ads);
+       if (!ADS_ERR_OK(ads_rc)) {
+               DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+               ads_destroy(&ads);
+               return WERR_ACCESS_DENIED;
+       }
+
+       /* figure out where to publish */
+       ads_find_machine_acct(ads, &res, lp_netbios_name());
+       srv_dn = ldap_get_dn(ads->ld, res);
+       ads_msgfree(ads, res);
+       srv_cn = ldap_explode_dn(srv_dn, 1);
+       asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], 
+                printer->info_2->sharename, srv_dn);
+       ads_memfree(ads, srv_dn);
+
+       /* publish it */
+       ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
+       if (LDAP_ALREADY_EXISTS == ads_rc.err.rc)
+               ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx,&mods);
+       
+       /* retreive the guid and store it locally */
+       if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
+               ads_memfree(ads, prt_dn);
+               ads_pull_guid(ads, res, &guid);
+               ads_msgfree(ads, res);
+               store_printer_guid(printer->info_2, guid);
+               win_rc = mod_a_printer(*printer, 2);
+       } 
+
+       safe_free(prt_dn);
+       ads_destroy(&ads);
+
+       return WERR_OK;
+}
+
+WERROR unpublish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+       ADS_STATUS ads_rc;
+       ADS_STRUCT *ads;
+       void *res;
+       char *prt_dn = NULL;
+       WERROR win_rc;
+
+       printer->info_2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
+       win_rc = mod_a_printer(*printer, 2);
+       if (!W_ERROR_IS_OK(win_rc)) {
+               DEBUG(3, ("err %d saving data\n",
+                                 W_ERROR_V(win_rc)));
+               return win_rc;
+       }
+       
+       ads = ads_init(NULL, NULL, lp_ads_server());
+       if (!ads) {
+               DEBUG(3, ("ads_init() failed\n"));
+               return WERR_SERVER_UNAVAILABLE;
+       }
+       ads_rc = ads_connect(ads);
+       if (!ADS_ERR_OK(ads_rc)) {
+               DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+               ads_destroy(&ads);
+               return WERR_ACCESS_DENIED;
+       }
+       
+       /* remove the printer from the directory */
+       ads_rc = ads_find_printer_on_server(ads, &res, 
+                           printer->info_2->sharename, lp_netbios_name());
+       if (ADS_ERR_OK(ads_rc) && ads_count_replies(ads, res)) {
+               prt_dn = ads_get_dn(ads, res);
+               ads_msgfree(ads, res);
+               ads_rc = ads_del_dn(ads, prt_dn);
+               ads_memfree(ads, prt_dn);
+       }
+
+       ads_destroy(&ads);
+       return WERR_OK;
+}
+
+/****************************************************************************
+ * Publish a printer in the directory
+ *
+ * @param snum describing printer service
+ * @return WERROR indicating status of publishing
+ ***************************************************************************/
+
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       WERROR win_rc;
+
+       win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+       if (!W_ERROR_IS_OK(win_rc))
+               return win_rc;
+
+       switch(action) {
+       case SPOOL_DS_PUBLISH:
+       case SPOOL_DS_UPDATE:
+               win_rc = publish_it(printer);
+               break;
+       case SPOOL_DS_UNPUBLISH:
+               win_rc = unpublish_it(printer);
+               break;
+       default:
+               win_rc = WERR_NOT_SUPPORTED;
+       }
+       
+
+       free_a_printer(&printer, 2);
+       return win_rc;
+}
+
+BOOL is_printer_published(Printer_entry *print_hnd, int snum, GUID *guid)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       REGVAL_CTR *ctr;
+       REGISTRY_VALUE *guid_val;
+       WERROR win_rc;
+       int i;
+
+
+       win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+       if (!W_ERROR_IS_OK(win_rc))
+               return False;
+
+       if (!(printer->info_2->attributes & PRINTER_ATTRIBUTE_PUBLISHED))
+               return False;
+
+       if ((i = lookup_printerkey(&printer->info_2->data, 
+                                  SPOOL_DSSPOOLER_KEY)) < 0)
+               return False;
+
+       if (!(ctr = &printer->info_2->data.keys[i].values)) {
+               return False;
+       }
+
+       if (!(guid_val = regval_ctr_getvalue(ctr, "objectGUID"))) {
+               return False;
+       }
+
+       if (regval_size(guid_val) == sizeof(GUID))
+               memcpy(guid, regval_data_p(guid_val), sizeof(GUID));
+
+       return True;
+}
+       
+#else
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+       return WERR_OK;
+}
+BOOL is_printer_published(Printer_entry *print_hnd, int snum, GUID *guid)
+{
+       return False;
+}
+#endif
+/****************************************************************************
+ ***************************************************************************/
+WERROR delete_all_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key )
+{
+       NT_PRINTER_DATA *data;
+       int             i;
+       int             removed_keys = 0;
+       int             empty_slot;
+       
+       data = &p2->data;
+       empty_slot = data->num_keys;
+
+       if ( !key )
+               return WERR_INVALID_PARAM;
+       
+       /* remove all keys */
+
+       if ( !strlen(key) ) {
+               for ( i=0; i<data->num_keys; i++ ) {
+                       DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+                               data->keys[i].name));
+               
+                       SAFE_FREE( data->keys[i].name );
+                       regval_ctr_destroy( &data->keys[i].values );
+               }
+       
+               DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n",
+                       p2->printername ));
+       
+               SAFE_FREE( data->keys );
+               ZERO_STRUCTP( data );
+
+               return WERR_OK;
+       }
+
+       /* remove a specific key (and all subkeys) */
+       
+       for ( i=0; i<data->num_keys; i++ ) {
+               if ( StrnCaseCmp( data->keys[i].name, key, strlen(key)) == 0 ) {
+                       DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+                               data->keys[i].name));
+               
+                       SAFE_FREE( data->keys[i].name );
+                       regval_ctr_destroy( &data->keys[i].values );
+               
+                       /* mark the slot as empty */
+
+                       ZERO_STRUCTP( &data->keys[i] );
+               }
+       }
+
+       /* find the first empty slot */
+
+       for ( i=0; i<data->num_keys; i++ ) {
+               if ( !data->keys[i].name ) {
+                       empty_slot = i;
+                       removed_keys++;
+                       break;
+               }
+       }
+
+       if ( i == data->num_keys )
+               /* nothing was removed */
+               return WERR_INVALID_PARAM;
+
+       /* move everything down */
+       
+       for ( i=empty_slot+1; i<data->num_keys; i++ ) {
+               if ( data->keys[i].name ) {
+                       memcpy( &data->keys[empty_slot], &data->keys[i], sizeof(NT_PRINTER_KEY) ); 
+                       ZERO_STRUCTP( &data->keys[i] );
+                       empty_slot++;
+                       removed_keys++;
+               }
+       }
+
+       /* update count */
+               
+       data->num_keys -= removed_keys;
+
+       /* sanity check to see if anything is left */
+
+       if ( !data->num_keys ) {
+               DEBUG(8,("delete_all_printer_data: No keys left for printer [%s]\n", p2->printername ));
+
+               SAFE_FREE( data->keys );
+               ZERO_STRUCTP( data );
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+WERROR delete_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+       WERROR          result = WERR_OK;
+       int             key_index;
+       
+       /* we must have names on non-zero length */
+       
+       if ( !key || !*key|| !value || !*value )
+               return WERR_INVALID_NAME;
+               
+       /* find the printer key first */
+
+       key_index = lookup_printerkey( &p2->data, key );
+       if ( key_index == -1 )
+               return WERR_OK;
+               
+       regval_ctr_delvalue( &p2->data.keys[key_index].values, value );
+       
+       DEBUG(8,("delete_printer_data: Removed key => [%s], value => [%s]\n",
+               key, value ));
+       
+       return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+WERROR add_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value, 
+                           uint32 type, uint8 *data, int real_len )
+{
+       WERROR          result = WERR_OK;
+       int             key_index;
+
+       /* we must have names on non-zero length */
+       
+       if ( !key || !*key|| !value || !*value )
+               return WERR_INVALID_NAME;
+               
+       /* find the printer key first */
+       
+       key_index = lookup_printerkey( &p2->data, key );
+       if ( key_index == -1 )
+               key_index = add_new_printer_key( &p2->data, key );
+               
+       if ( key_index == -1 )
+               return WERR_NOMEM;
+       
+       regval_ctr_addvalue( &p2->data.keys[key_index].values, value,
+               type, data, real_len );
+       
+       DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], type=> [%d], size => [%d]\n",
+               key, value, type, real_len  ));
+       
+       return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+REGISTRY_VALUE* get_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+       int             key_index;
+
+       if ( (key_index = lookup_printerkey( &p2->data, key )) == -1 )
+               return NULL;
+
+       DEBUG(8,("get_printer_data: Attempting to lookup key => [%s], value => [%s]\n",
+               key, value ));
+
+       return regval_ctr_getvalue( &p2->data.keys[key_index].values, value );
+}
+
+/****************************************************************************
+ Unpack a list of registry values frem the TDB
+ ***************************************************************************/
+static int unpack_values(NT_PRINTER_DATA *printer_data, char *buf, int buflen)
+{
+       int             len = 0;
+       uint32          type;
+       pstring         string, valuename, keyname;
+       char            *str;
+       int             size;
+       uint8           *data_p;
+       REGISTRY_VALUE  *regval_p;
+       int             key_index;
+       
+       /* add the "PrinterDriverData" key first for performance reasons */
+       
+       add_new_printer_key( printer_data, SPOOL_PRINTERDATA_KEY );
+
+       /* loop and unpack the rest of the registry values */
+       
+       while ( True ) {
+       
+               /* check to see if there are any more registry values */
+               
+               len += tdb_unpack(buf+len, buflen-len, "p", &regval_p);         
+               if ( !regval_p ) 
+                       break;
+
+               /* unpack the next regval */
+               
+               len += tdb_unpack(buf+len, buflen-len, "fdB",
+                                 string,
+                                 &type,
+                                 &size,
+                                 &data_p);
+       
+               /*
+                * break of the keyname from the value name.  
+                * Should only be one '\' in the string returned.
+                */     
+                
+               str = strrchr( string, '\\');
+               
+               /* Put in "PrinterDriverData" is no key specified */
+               
+               if ( !str ) {
+                       pstrcpy( keyname, SPOOL_PRINTERDATA_KEY );
+                       pstrcpy( valuename, string );
+               }
+               else {
+                       *str = '\0';
+                       pstrcpy( keyname, string );
+                       pstrcpy( valuename, str+1 );
+               }
+                       
+               /* see if we need a new key */
+               
+               if ( (key_index=lookup_printerkey( printer_data, keyname )) == -1 )
+                       key_index = add_new_printer_key( printer_data, keyname );
+                       
+               if ( key_index == -1 ) {
+                       DEBUG(0,("unpack_values: Failed to allocate a new key [%s]!\n",
+                               keyname));
+                       break;
+               }
+               
+               /* add the new value */
+               
+               regval_ctr_addvalue( &printer_data->keys[key_index].values, valuename, type, data_p, size );
+
+               SAFE_FREE(data_p); /* 'B' option to tdbpack does a malloc() */
+
+               DEBUG(8,("specific: [%s:%s], len: %d\n", keyname, valuename, size));
+       }
+
+       return len;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+static void map_to_os2_driver(fstring drivername)
+{
+       static BOOL initialised=False;
+       static fstring last_from,last_to;
+       char *mapfile = lp_os2_driver_map();
+       char **lines = NULL;
+       int numlines = 0;
+       int i;
+
+       if (!strlen(drivername))
+               return;
+
+       if (!*mapfile)
+               return;
+
+       if (!initialised) {
+               *last_from = *last_to = 0;
+               initialised = True;
+       }
+
+       if (strequal(drivername,last_from)) {
+               DEBUG(3,("Mapped Windows driver %s to OS/2 driver %s\n",drivername,last_to));
+               fstrcpy(drivername,last_to);
+               return;
+       }
+
+       lines = file_lines_load(mapfile, &numlines);
+       if (numlines == 0) {
+               DEBUG(0,("No entries in OS/2 driver map %s\n",mapfile));
+               return;
+       }
+
+       DEBUG(4,("Scanning OS/2 driver map %s\n",mapfile));
+
+       for( i = 0; i < numlines; i++) {
+               char *nt_name = lines[i];
+               char *os2_name = strchr(nt_name,'=');
+
+               if (!os2_name)
+                       continue;
+
+               *os2_name++ = 0;
+
+               while (isspace(*nt_name))
+                       nt_name++;
+
+               if (!*nt_name || strchr("#;",*nt_name))
+                       continue;
+
+               {
+                       int l = strlen(nt_name);
+                       while (l && isspace(nt_name[l-1])) {
+                               nt_name[l-1] = 0;
+                               l--;
+                       }
+               }
+
+               while (isspace(*os2_name))
+                       os2_name++;
+
+               {
+                       int l = strlen(os2_name);
+                       while (l && isspace(os2_name[l-1])) {
+                               os2_name[l-1] = 0;
+                               l--;
+                       }
+               }
+
+               if (strequal(nt_name,drivername)) {
+                       DEBUG(3,("Mapped windows driver %s to os2 driver%s\n",drivername,os2_name));
+                       fstrcpy(last_from,drivername);
+                       fstrcpy(last_to,os2_name);
+                       fstrcpy(drivername,os2_name);
+                       file_lines_free(lines);
+                       return;
+               }
+       }
+
+       file_lines_free(lines);
+}
+
+/****************************************************************************
+ Get a default printer info 2 struct.
+****************************************************************************/
+static WERROR get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+       int snum;
+       NT_PRINTER_INFO_LEVEL_2 info;
+
+       ZERO_STRUCT(info);
+
+       snum = lp_servicenumber(sharename);
+
+       slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+       slprintf(info.printername, sizeof(info.printername)-1, "\\\\%s\\%s", 
+                get_called_name(), sharename);
+       fstrcpy(info.sharename, sharename);
+       fstrcpy(info.portname, SAMBA_PRINTER_PORT_NAME);
+
+       /* by setting the driver name to an empty string, a local NT admin
+          can now run the **local** APW to install a local printer driver
+          for a Samba shared printer in 2.2.  Without this, drivers **must** be 
+          installed on the Samba server for NT clients --jerry */
+#if 0  /* JERRY --do not uncomment-- */
+       if (!*info.drivername)
+               fstrcpy(info.drivername, "NO DRIVER AVAILABLE FOR THIS PRINTER");
+#endif
+
+
+       DEBUG(10,("get_a_printer_2_default: driver name set to [%s]\n", info.drivername));
+
+       pstrcpy(info.comment, "");
+       fstrcpy(info.printprocessor, "winprint");
+       fstrcpy(info.datatype, "RAW");
+
+       info.attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+       info.starttime = 0; /* Minutes since 12:00am GMT */
+       info.untiltime = 0; /* Minutes since 12:00am GMT */
+       info.priority = 1;
+       info.default_priority = 1;
+       info.setuptime = (uint32)time(NULL);
+
+       /*
+        * I changed this as I think it is better to have a generic
+        * DEVMODE than to crash Win2k explorer.exe   --jerry
+        * See the HP Deskjet 990c Win2k drivers for an example.
+        *
+        * However the default devmode appears to cause problems
+        * with the HP CLJ 8500 PCL driver.  Hence the addition of
+        * the "default devmode" parameter   --jerry 22/01/2002
+        */
+
+       if (lp_default_devmode(snum)) {
+               if ((info.devmode = construct_nt_devicemode(info.printername)) == NULL)
+                       goto fail;
+       }
+       else {
+               info.devmode = NULL;
+       }
+
+       /* This will get the current RPC talloc context, but we should be
+          passing this as a parameter... fixme... JRA ! */
+
+       if (!nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf))
+               goto fail;
+
+       *info_ptr = (NT_PRINTER_INFO_LEVEL_2 *)memdup(&info, sizeof(info));
+       if (! *info_ptr) {
+               DEBUG(0,("get_a_printer_2_default: malloc fail.\n"));
+               goto fail;
+       }
+
+       return WERR_OK;
+
+  fail:
+       if (info.devmode)
+               free_nt_devicemode(&info.devmode);
+       return WERR_ACCESS_DENIED;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+       pstring key;
+       NT_PRINTER_INFO_LEVEL_2 info;
+       int             len = 0;
+       TDB_DATA kbuf, dbuf;
+       fstring printername;
+               
+       ZERO_STRUCT(info);
+
+       slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+
+       dbuf = tdb_fetch(tdb_printers, kbuf);
+       if (!dbuf.dptr)
+               return get_a_printer_2_default(info_ptr, sharename);
+
+       len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff",
+                       &info.attributes,
+                       &info.priority,
+                       &info.default_priority,
+                       &info.starttime,
+                       &info.untiltime,
+                       &info.status,
+                       &info.cjobs,
+                       &info.averageppm,
+                       &info.changeid,
+                       &info.c_setprinter,
+                       &info.setuptime,
+                       info.servername,
+                       info.printername,
+                       info.sharename,
+                       info.portname,
+                       info.drivername,
+                       info.comment,
+                       info.location,
+                       info.sepfile,
+                       info.printprocessor,
+                       info.datatype,
+                       info.parameters);
+
+       /* Samba has to have shared raw drivers. */
+       info.attributes |= PRINTER_ATTRIBUTE_SAMBA;
+
+       /* Restore the stripped strings. */
+       slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+       slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", get_called_name(),
+                       info.printername);
+       fstrcpy(info.printername, printername);
+
+       len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+       /*
+        * Some client drivers freak out if there is a NULL devmode
+        * (probably the driver is not checking before accessing 
+        * the devmode pointer)   --jerry
+        *
+        * See comments in get_a_printer_2_default()
+        */
+
+       if (lp_default_devmode(lp_servicenumber(sharename)) && !info.devmode) {
+               DEBUG(8,("get_a_printer_2: Constructing a default device mode for [%s]\n",
+                       printername));
+               info.devmode = construct_nt_devicemode(printername);
+       }
+
+       len += unpack_values( &info.data, dbuf.dptr+len, dbuf.dsize-len );
+
+       /* This will get the current RPC talloc context, but we should be
+          passing this as a parameter... fixme... JRA ! */
+
+       nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf);
+
+       /* Fix for OS/2 drivers. */
+
+       if (get_remote_arch() == RA_OS2)
+               map_to_os2_driver(info.drivername);
+
+       SAFE_FREE(dbuf.dptr);
+       *info_ptr=memdup(&info, sizeof(info));
+
+       DEBUG(9,("Unpacked printer [%s] name [%s] running driver [%s]\n",
+                sharename, info.printername, info.drivername));
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+       uint32 result;
+       NT_PRINTER_INFO_LEVEL_2 *info2;
+       
+       DEBUG(106,("Dumping printer at level [%d]\n", level));
+       
+       switch (level) {
+               case 2:
+               {
+                       if (printer.info_2 == NULL)
+                               result=5;
+                       else
+                       {
+                               info2=printer.info_2;
+                       
+                               DEBUGADD(106,("attributes:[%d]\n", info2->attributes));
+                               DEBUGADD(106,("priority:[%d]\n", info2->priority));
+                               DEBUGADD(106,("default_priority:[%d]\n", info2->default_priority));
+                               DEBUGADD(106,("starttime:[%d]\n", info2->starttime));
+                               DEBUGADD(106,("untiltime:[%d]\n", info2->untiltime));
+                               DEBUGADD(106,("status:[%d]\n", info2->status));
+                               DEBUGADD(106,("cjobs:[%d]\n", info2->cjobs));
+                               DEBUGADD(106,("averageppm:[%d]\n", info2->averageppm));
+                               DEBUGADD(106,("changeid:[%d]\n", info2->changeid));
+                               DEBUGADD(106,("c_setprinter:[%d]\n", info2->c_setprinter));
+                               DEBUGADD(106,("setuptime:[%d]\n", info2->setuptime));
+
+                               DEBUGADD(106,("servername:[%s]\n", info2->servername));
+                               DEBUGADD(106,("printername:[%s]\n", info2->printername));
+                               DEBUGADD(106,("sharename:[%s]\n", info2->sharename));
+                               DEBUGADD(106,("portname:[%s]\n", info2->portname));
+                               DEBUGADD(106,("drivername:[%s]\n", info2->drivername));
+                               DEBUGADD(106,("comment:[%s]\n", info2->comment));
+                               DEBUGADD(106,("location:[%s]\n", info2->location));
+                               DEBUGADD(106,("sepfile:[%s]\n", info2->sepfile));
+                               DEBUGADD(106,("printprocessor:[%s]\n", info2->printprocessor));
+                               DEBUGADD(106,("datatype:[%s]\n", info2->datatype));
+                               DEBUGADD(106,("parameters:[%s]\n", info2->parameters));
+                               result=0;
+                       }
+                       break;
+               }
+               default:
+                       DEBUGADD(106,("dump_a_printer: Level %u not implemented\n", (unsigned int)level ));
+                       result=1;
+                       break;
+       }
+       
+       return result;
+}
+
+/****************************************************************************
+ Update the changeid time.
+ This is SO NASTY as some drivers need this to change, others need it
+ static. This value will change every second, and I must hope that this
+ is enough..... DON'T CHANGE THIS CODE WITHOUT A TEST MATRIX THE SIZE OF
+ UTAH ! JRA.
+****************************************************************************/
+
+static uint32 rev_changeid(void)
+{
+       struct timeval tv;
+
+       get_process_uptime(&tv);
+
+#if 1  /* JERRY */
+       /* Return changeid as msec since spooler restart */
+       return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#else
+       /*
+        * This setting seems to work well but is too untested
+        * to replace the above calculation.  Left in for experiementation
+        * of the reader            --jerry (Tue Mar 12 09:15:05 CST 2002)
+        */
+       return tv.tv_sec * 10 + tv.tv_usec / 100000;
+#endif
+}
+
+/*
+ * The function below are the high level ones.
+ * only those ones must be called from the spoolss code.
+ * JFM.
+ */
+
+/****************************************************************************
+ Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA.
+****************************************************************************/
+
+WERROR mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+       WERROR result;
+       
+       dump_a_printer(printer, level); 
+       
+       /* 
+        * invalidate cache for all open handles to this printer.
+        * cache for a given handle will be updated on the next 
+        * get_a_printer() 
+        */
+        
+       invalidate_printer_hnd_cache( printer.info_2->sharename );
+       
+       switch (level) {
+               case 2:
+               {
+                       /*
+                        * Update the changestamp.  Emperical tests show that the
+                        * ChangeID is always updated,but c_setprinter is  
+                        *  global spooler variable (not per printer).
+                        */
+
+                       /* ChangeID **must** be increasing over the lifetime
+                          of client's spoolss service in order for the
+                          client's cache to show updates */
+
+                       printer.info_2->changeid = rev_changeid();
+
+                       /*
+                        * Because one day someone will ask:
+                        * NT->NT       An admin connection to a remote
+                        *              printer show changes imeediately in
+                        *              the properities dialog
+                        *      
+                        *              A non-admin connection will only show the
+                        *              changes after viewing the properites page
+                        *              2 times.  Seems to be related to a
+                        *              race condition in the client between the spooler
+                        *              updating the local cache and the Explorer.exe GUI
+                        *              actually displaying the properties.
+                        *
+                        *              This is fixed in Win2k.  admin/non-admin
+                        *              connections both display changes immediately.
+                        *
+                        * 14/12/01     --jerry
+                        */
+
+                       result=update_a_printer_2(printer.info_2);
+                       
+                       break;
+               }
+               default:
+                       result=WERR_UNKNOWN_LEVEL;
+                       break;
+       }
+       
+       return result;
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+****************************************************************************/
+
+static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr )
+{
+       int                     len = 0;
+       pstring                 key;
+       TDB_DATA                kbuf, dbuf;
+       NT_PRINTER_INFO_LEVEL_2 info;
+
+
+       ZERO_STRUCT(info);
+
+       /*
+        * Delete any printer data 'values' already set. When called for driver
+        * replace, there will generally be some, but during an add printer, there
+        * should not be any (if there are delete them).
+        */
+        
+       delete_all_printer_data( info_ptr, "" );
+       
+       slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info_ptr->drivername);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+
+       dbuf = tdb_fetch(tdb_drivers, kbuf);
+       if (!dbuf.dptr) {
+               /*
+                * When changing to a driver that has no init info in the tdb, remove
+                * the previous drivers init info and leave the new on blank.
+                */
+               free_nt_devicemode(&info_ptr->devmode);
+               return False;
+       }
+       
+       /*
+        * Get the saved DEVMODE..
+        */
+        
+       len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+       /*
+        * The saved DEVMODE contains the devicename from the printer used during
+        * the initialization save. Change it to reflect the new printer.
+        */
+        
+       if ( info.devmode ) {
+               ZERO_STRUCT(info.devmode->devicename);
+               fstrcpy(info.devmode->devicename, info_ptr->printername);
+       }
+
+       /*
+        * NT/2k does not change out the entire DeviceMode of a printer
+        * when changing the driver.  Only the driverextra, private, & 
+        * driverversion fields.   --jerry  (Thu Mar 14 08:58:43 CST 2002)
+        *
+        * Later examination revealed that Windows NT/2k does reset the
+        * the printer's device mode, bit **only** when you change a 
+        * property of the device mode such as the page orientation.
+        * --jerry
+        */
+
+
+       /* Bind the saved DEVMODE to the new the printer */
+        
+       free_nt_devicemode(&info_ptr->devmode);
+       info_ptr->devmode = info.devmode;
+
+       DEBUG(10,("set_driver_init_2: Set printer [%s] init %s DEVMODE for driver [%s]\n",
+               info_ptr->printername, info_ptr->devmode?"VALID":"NULL", info_ptr->drivername));
+
+       /* Add the printer data 'values' to the new printer */
+        
+       len += unpack_values( &info_ptr->data, dbuf.dptr+len, dbuf.dsize-len );
+       
+
+       SAFE_FREE(dbuf.dptr);
+
+       return True;    
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+ When a printer is created using AddPrinter, the drivername bound to the
+ printer is used to lookup previously saved driver initialization info, which
+ is bound to the new printer.
+****************************************************************************/
+
+BOOL set_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+       BOOL result = False;
+       
+       switch (level) {
+               case 2:
+                       result = set_driver_init_2(printer->info_2);
+                       break;
+                       
+               default:
+                       DEBUG(0,("set_driver_init: Programmer's error!  Unknown driver_init level [%d]\n",
+                               level));
+                       break;
+       }
+       
+       return result;
+}
+
+/****************************************************************************
+ Delete driver init data stored for a specified driver
+****************************************************************************/
+
+BOOL del_driver_init(char *drivername)
+{
+       pstring key;
+       TDB_DATA kbuf;
+
+       if (!drivername || !*drivername) {
+               DEBUG(3,("del_driver_init: No drivername specified!\n"));
+               return False;
+       }
+
+       slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, drivername);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+
+       DEBUG(6,("del_driver_init: Removing driver init data for [%s]\n", drivername));
+
+       return (tdb_delete(tdb_drivers, kbuf) == 0);
+}
+
+/****************************************************************************
+ Pack up the DEVMODE and values for a printer into a 'driver init' entry 
+ in the tdb. Note: this is different from the driver entry and the printer
+ entry. There should be a single driver init entry for each driver regardless
+ of whether it was installed from NT or 2K. Technically, they should be
+ different, but they work out to the same struct.
+****************************************************************************/
+
+static uint32 update_driver_init_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+       pstring key;
+       char *buf;
+       int buflen, len, ret;
+       TDB_DATA kbuf, dbuf;
+
+       buf = NULL;
+       buflen = 0;
+
+ again:        
+       len = 0;
+       len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+       len += pack_values( &info->data, buf+len, buflen-len );
+
+       if (buflen != len) {
+               char *tb;
+
+               tb = (char *)Realloc(buf, len);
+               if (!tb) {
+                       DEBUG(0, ("update_driver_init_2: failed to enlarge buffer!\n"));
+                       ret = -1;
+                       goto done;
+               }
+               else
+                       buf = tb;
+               buflen = len;
+               goto again;
+       }
+
+       slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info->drivername);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
+
+       ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+       if (ret == -1)
+               DEBUG(8, ("update_driver_init_2: error updating printer init to tdb on disk\n"));
+
+       SAFE_FREE(buf);
+
+       DEBUG(10,("update_driver_init_2: Saved printer [%s] init DEVMODE & values for driver [%s]\n",
+                info->sharename, info->drivername));
+
+       return ret;
+}
+
+/****************************************************************************
+ Update (i.e. save) the driver init info (DEVMODE and values) for a printer
+****************************************************************************/
+
+uint32 update_driver_init(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+       uint32 result;
+       
+       dump_a_printer(printer, level); 
+       
+       switch (level) {
+               case 2:
+                       result = update_driver_init_2(printer.info_2);
+                       break;
+               default:
+                       result = 1;
+                       break;
+       }
+       
+       return result;
+}
+
+/****************************************************************************
+ Convert the printer data value, a REG_BINARY array, into an initialization 
+ DEVMODE. Note: the array must be parsed as if it was a DEVMODE in an rpc...
+ got to keep the endians happy :).
+****************************************************************************/
+
+static BOOL convert_driver_init( TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode, uint8 *data, uint32 data_len )
+{
+       BOOL       result = False;
+       prs_struct ps;
+       DEVICEMODE devmode;
+
+       ZERO_STRUCT(devmode);
+
+       prs_init(&ps, 0, ctx, UNMARSHALL);
+       ps.data_p      = (char *)data;
+       ps.buffer_size = data_len;
+
+       if (spoolss_io_devmode("phantom DEVMODE", &ps, 0, &devmode))
+               result = convert_devicemode("", &devmode, &nt_devmode);
+       else
+               DEBUG(10,("convert_driver_init: error parsing DEVMODE\n"));
+
+       return result;
+}
+
+/****************************************************************************
+ Set the DRIVER_INIT info in the tdb. Requires Win32 client code that:
+
+ 1. Use the driver's config DLL to this UNC printername and:
+    a. Call DrvPrintEvent with PRINTER_EVENT_INITIALIZE
+    b. Call DrvConvertDevMode with CDM_DRIVER_DEFAULT to get default DEVMODE
+ 2. Call SetPrinterData with the 'magic' key and the DEVMODE as data.
+
+ The last step triggers saving the "driver initialization" information for
+ this printer into the tdb. Later, new printers that use this driver will
+ have this initialization information bound to them. This simulates the
+ driver initialization, as if it had run on the Samba server (as it would
+ have done on NT).
+
+ The Win32 client side code requirement sucks! But until we can run arbitrary
+ Win32 printer driver code on any Unix that Samba runs on, we are stuck with it.
+ It would have been easier to use SetPrinter because all the UNMARSHALLING of
+ the DEVMODE is done there, but 2K/XP clients do not set the DEVMODE... think
+ about it and you will realize why.  JRR 010720
+****************************************************************************/
+
+static WERROR save_driver_init_2(NT_PRINTER_INFO_LEVEL *printer, uint8 *data, uint32 data_len )
+{
+       WERROR        status       = WERR_OK;
+       TALLOC_CTX    *ctx         = NULL;
+       NT_DEVICEMODE *nt_devmode  = NULL;
+       NT_DEVICEMODE *tmp_devmode = printer->info_2->devmode;
+       
+       /*
+        * When the DEVMODE is already set on the printer, don't try to unpack it.
+        */
+       DEBUG(8,("save_driver_init_2: Enter...\n"));
+       
+       if ( !printer->info_2->devmode && data_len ) {
+               /*
+                * Set devmode on printer info, so entire printer initialization can be
+                * saved to tdb.
+                */
+
+               if ((ctx = talloc_init("save_driver_init_2")) == NULL)
+                       return WERR_NOMEM;
+
+               if ((nt_devmode = (NT_DEVICEMODE*)malloc(sizeof(NT_DEVICEMODE))) == NULL) {
+                       status = WERR_NOMEM;
+                       goto done;
+               }
+       
+               ZERO_STRUCTP(nt_devmode);
+
+               /*
+                * The DEVMODE is held in the 'data' component of the param in raw binary.
+                * Convert it to to a devmode structure
+                */
+               if ( !convert_driver_init( ctx, nt_devmode, data, data_len )) {
+                       DEBUG(10,("save_driver_init_2: error converting DEVMODE\n"));
+                       status = WERR_INVALID_PARAM;
+                       goto done;
+               }
+
+               printer->info_2->devmode = nt_devmode;
+       }
+
+       /*
+        * Pack up and add (or update) the DEVMODE and any current printer data to
+        * a 'driver init' element in the tdb
+        * 
+        */
+
+       if ( update_driver_init(*printer, 2) != 0 ) {
+               DEBUG(10,("save_driver_init_2: error updating DEVMODE\n"));
+               status = WERR_NOMEM;
+               goto done;
+       }
+       
+       /*
+        * If driver initialization info was successfully saved, set the current 
+        * printer to match it. This allows initialization of the current printer 
+        * as well as the driver.
+        */
+       status = mod_a_printer(*printer, 2);
+       if (!W_ERROR_IS_OK(status)) {
+               DEBUG(10,("save_driver_init_2: error setting DEVMODE on printer [%s]\n",
+                                 printer->info_2->printername));
+       }
+       
+  done:
+       talloc_destroy(ctx);
+       free_nt_devicemode( &nt_devmode );
+       
+       printer->info_2->devmode = tmp_devmode;
+
+       return status;
+}
+
+/****************************************************************************
+ Update the driver init info (DEVMODE and specifics) for a printer
+****************************************************************************/
+
+WERROR save_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level, uint8 *data, uint32 data_len)
+{
+       WERROR status = WERR_OK;
+       
+       switch (level) {
+               case 2:
+                       status = save_driver_init_2( printer, data, data_len );
+                       break;
+               default:
+                       status = WERR_UNKNOWN_LEVEL;
+                       break;
+       }
+       
+       return status;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_DATA
+****************************************************************************/
+
+static NTSTATUS copy_printer_data( NT_PRINTER_DATA *dst, NT_PRINTER_DATA *src )
+{
+       int i, j, num_vals, new_key_index;
+       REGVAL_CTR *src_key, *dst_key;
+       
+       if ( !dst || !src )
+               return NT_STATUS_NO_MEMORY;
+       
+       for ( i=0; i<src->num_keys; i++ ) {
+                          
+               /* create a new instance of the printerkey in the destination 
+                  printer_data object */
+                  
+               new_key_index = add_new_printer_key( dst, src->keys[i].name );
+               dst_key = &dst->keys[new_key_index].values;
+
+               src_key = &src->keys[i].values;
+               num_vals = regval_ctr_numvals( src_key );
+               
+               /* dup the printer entire printer key */
+               
+               for ( j=0; j<num_vals; j++ ) {
+                       regval_ctr_copyvalue( dst_key, regval_ctr_specific_value(src_key, j) );
+               }
+       }
+               
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_INFO_LEVEL_2 structure using malloc()'d memeory
+ Caller must free.
+****************************************************************************/
+
+static NT_PRINTER_INFO_LEVEL_2* dup_printer_2( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL_2 *printer )
+{
+       NT_PRINTER_INFO_LEVEL_2 *copy;
+       
+       if ( !printer )
+               return NULL;
+       
+       if ( !(copy = (NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2))) )
+               return NULL;
+               
+       memcpy( copy, printer, sizeof(NT_PRINTER_INFO_LEVEL_2) );
+       
+       /* malloc()'d members copied here */
+       
+       copy->devmode = dup_nt_devicemode( printer->devmode );  
+
+       ZERO_STRUCT( copy->data );
+       copy_printer_data( &copy->data, &printer->data );
+       
+       /* this is talloc()'d; very ugly that we have a structure that 
+          is half malloc()'d and half talloc()'d but that is the way 
+          that the PRINTER_INFO stuff is written right now.  --jerry  */
+          
+       copy->secdesc_buf = dup_sec_desc_buf( ctx, printer->secdesc_buf );
+               
+       return copy;
+}
+
+/****************************************************************************
+ Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory.
+****************************************************************************/
+
+#define ENABLE_PRINT_HND_CACHE 1
+
+WERROR get_a_printer( Printer_entry *print_hnd, NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level, 
+                       const char *sharename)
+{
+       WERROR result;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       
+       *pp_printer = NULL;
+
+       DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level));
+
+       switch (level) {
+               case 2:
+                       if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+                               DEBUG(0,("get_a_printer: malloc fail.\n"));
+                               return WERR_NOMEM;
+                       }
+                       ZERO_STRUCTP(printer);
+                       
+                       /* 
+                        * check for cache first.  A Printer handle cannot changed
+                        * to another printer object so we only check that the printer 
+                        * is actually for a printer and that the printer_info pointer 
+                        * is valid
+                        */
+#ifdef ENABLE_PRINT_HND_CACHE  /* JERRY */
+                       if ( print_hnd 
+                               && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER) 
+                               && print_hnd->printer_info )
+                       {
+                               if ( !(printer->info_2 = dup_printer_2(print_hnd->ctx, print_hnd->printer_info->info_2)) ) {
+                                       DEBUG(0,("get_a_printer: unable to copy cached printer info!\n"));
+                                       
+                                       SAFE_FREE(printer);
+                                       return WERR_NOMEM;
+                               }
+                               
+                               DEBUG(10,("get_a_printer: using cached copy of printer_info_2\n"));
+                               
+                               *pp_printer = printer;                          
+                               result = WERR_OK;
+                               
+                               break;
+                       }
+#endif 
+
+                       /* no cache; look it up on disk */
+                       
+                       result=get_a_printer_2(&printer->info_2, sharename);
+                       if (W_ERROR_IS_OK(result)) {
+                               dump_a_printer(*printer, level);
+
+#if ENABLE_PRINT_HND_CACHE     /* JERRY */                                                             
+                               /* save a copy in cache */
+                               if ( print_hnd && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER)) {
+                                       if ( !print_hnd->printer_info )
+                                               print_hnd->printer_info = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL));
+                                       
+                                       if ( print_hnd->printer_info ) {
+                                               print_hnd->printer_info->info_2 = dup_printer_2(print_hnd->ctx, printer->info_2);
+                                               
+                                               /* don't fail the lookup just because the cache update failed */
+                                               if ( !print_hnd->printer_info->info_2 )
+                                                       DEBUG(0,("get_a_printer: unable to copy new printer info!\n"));
+                                       }
+                                       
+                               }
+#endif
+                               *pp_printer = printer;
+                       }
+                       else 
+                               SAFE_FREE(printer);
+       
+
+                       break;
+               default:
+                       result=WERR_UNKNOWN_LEVEL;
+                       break;
+       }
+       
+       DEBUG(10,("get_a_printer: [%s] level %u returning %s\n", sharename, (unsigned int)level, dos_errstr(result)));
+
+       return result;
+}
+
+/****************************************************************************
+ Deletes a NT_PRINTER_INFO_LEVEL struct.
+****************************************************************************/
+
+uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level)
+{
+       uint32 result;
+       NT_PRINTER_INFO_LEVEL *printer = *pp_printer;
+
+       DEBUG(104,("freeing a printer at level [%d]\n", level));
+
+       if (printer == NULL)
+               return 0;
+       
+       switch (level) {
+               case 2:
+                       if (printer->info_2 != NULL) {
+                               free_nt_printer_info_level_2(&printer->info_2);
+                               result=0;
+                       } else
+                               result=4;
+                       break;
+
+               default:
+                       result=1;
+                       break;
+       }
+
+       SAFE_FREE(*pp_printer);
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+       uint32 result;
+       DEBUG(104,("adding a printer at level [%d]\n", level));
+       dump_a_printer_driver(driver, level);
+       
+       switch (level) {
+               case 3:
+                       result=add_a_printer_driver_3(driver.info_3);
+                       break;
+
+               case 6:
+                       result=add_a_printer_driver_6(driver.info_6);
+                       break;
+
+               default:
+                       result=1;
+                       break;
+       }
+       
+       return result;
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
+                            fstring drivername, const char *architecture, uint32 version)
+{
+       WERROR result;
+       
+       switch (level) {
+               case 3:
+                       /* Sometime we just want any version of the driver */
+                       
+                       if ( version == DRIVER_ANY_VERSION ) {
+                               /* look for Win2k first and then for NT4 */
+                               result = get_a_printer_driver_3(&driver->info_3, drivername, 
+                                               architecture, 3);
+                                               
+                               if ( !W_ERROR_IS_OK(result) ) {
+                                       result = get_a_printer_driver_3( &driver->info_3, 
+                                                       drivername, architecture, 2 );
+                               }
+                       } else {
+                               result = get_a_printer_driver_3(&driver->info_3, drivername, 
+                                       architecture, version);                         
+                       }
+                       break;
+                       
+               default:
+                       result=W_ERROR(1);
+                       break;
+       }
+       
+       if (W_ERROR_IS_OK(result))
+               dump_a_printer_driver(*driver, level);
+               
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+       uint32 result;
+       
+       switch (level) {
+               case 3:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+                       if (driver.info_3 != NULL)
+                       {
+                               info3=driver.info_3;
+                               SAFE_FREE(info3->dependentfiles);
+                               ZERO_STRUCTP(info3);
+                               SAFE_FREE(info3);
+                               result=0;
+                       } else {
+                               result=4;
+                       }
+                       break;
+               }
+               case 6:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_6 *info6;
+                       if (driver.info_6 != NULL) {
+                               info6=driver.info_6;
+                               SAFE_FREE(info6->dependentfiles);
+                               SAFE_FREE(info6->previousnames);
+                               ZERO_STRUCTP(info6);
+                               SAFE_FREE(info6);
+                               result=0;
+                       } else {
+                               result=4;
+                       }
+                       break;
+               }
+               default:
+                       result=1;
+                       break;
+       }
+       return result;
+}
+
+
+/****************************************************************************
+  Determine whether or not a particular driver is currently assigned
+  to a printer
+****************************************************************************/
+
+BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3 )
+{
+       int snum;
+       int n_services = lp_numservices();
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+       if ( !info_3 ) 
+               return False;
+
+       DEBUG(5,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n"));
+       
+       /* loop through the printers.tdb and check for the drivername */
+       
+       for (snum=0; snum<n_services; snum++) {
+               if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+                       continue;
+               
+               if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))) )
+                       continue;
+               
+               if ( !StrCaseCmp(info_3->name, printer->info_2->drivername) ) {
+                       free_a_printer( &printer, 2 );
+                       return True;
+               }
+               
+               free_a_printer( &printer, 2 );
+       }
+       
+       DEBUG(5,("printer_driver_in_use: Completed search through ntprinters.tdb...\n"));
+       
+       /* report that the driver is not in use by default */
+       
+       return False;
+}
+
+
+/**********************************************************************
+ Check to see if a ogiven file is in use by *info
+ *********************************************************************/
+static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+       int i = 0;
+       
+       if ( !info )
+               return False;
+               
+       if ( strequal(file, info->driverpath) )
+               return True;
+
+       if ( strequal(file, info->datafile) )
+               return True;
+
+       if ( strequal(file, info->configfile) )
+               return True;
+
+       if ( strequal(file, info->helpfile) )
+               return True;
+       
+       /* see of there are any dependent files to examine */
+       
+       if ( !info->dependentfiles )
+               return False;
+       
+       while ( *info->dependentfiles[i] ) {
+               if ( strequal(file, info->dependentfiles[i]) )
+                       return True;
+               i++;
+       }
+       
+       return False;
+
+}
+
+/**********************************************************************
+ Utility function to remove the dependent file pointed to by the 
+ input parameter from the list 
+ *********************************************************************/
+
+static void trim_dependent_file( fstring files[], int idx )
+{
+       
+       /* bump everything down a slot */
+
+       while( *files[idx+1] ) {
+               fstrcpy( files[idx], files[idx+1] );
+               idx++;
+       }
+       
+       *files[idx] = '\0';
+
+       return; 
+}
+
+/**********************************************************************
+ Check if any of the files used by src are also used by drv 
+ *********************************************************************/
+
+static BOOL trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src, 
+                                      NT_PRINTER_DRIVER_INFO_LEVEL_3 *drv )
+{
+       BOOL    in_use = False;
+       int     i = 0;
+       
+       if ( !src || !drv )
+               return False;
+               
+       /* check each file.  Remove it from the src structure if it overlaps */
+       
+       if ( drv_file_in_use(src->driverpath, drv) ) {
+               in_use = True;
+               DEBUG(10,("Removing driverfile [%s] from list\n", src->driverpath));
+               fstrcpy( src->driverpath, "" );
+       }
+               
+       if ( drv_file_in_use(src->datafile, drv) ) {
+               in_use = True;
+               DEBUG(10,("Removing datafile [%s] from list\n", src->datafile));
+               fstrcpy( src->datafile, "" );
+       }
+               
+       if ( drv_file_in_use(src->configfile, drv) ) {
+               in_use = True;
+               DEBUG(10,("Removing configfile [%s] from list\n", src->configfile));
+               fstrcpy( src->configfile, "" );
+       }
+               
+       if ( drv_file_in_use(src->helpfile, drv) ) {
+               in_use = True;
+               DEBUG(10,("Removing helpfile [%s] from list\n", src->helpfile));
+               fstrcpy( src->helpfile, "" );
+       }
+       
+       /* are there any dependentfiles to examine? */
+       
+       if ( !src->dependentfiles )
+               return in_use;
+               
+       while ( *src->dependentfiles[i] ) {
+               if ( drv_file_in_use(src->dependentfiles[i], drv) ) {
+                       in_use = True;
+                       DEBUG(10,("Removing [%s] from dependent file list\n", src->dependentfiles[i]));
+                       trim_dependent_file( src->dependentfiles, i );
+               } else
+                       i++;
+       }               
+               
+       return in_use;
+}
+
+/****************************************************************************
+  Determine whether or not a particular driver files are currently being 
+  used by any other driver.  
+  
+  Return value is True if any files were in use by other drivers
+  and False otherwise.
+  
+  Upon return, *info has been modified to only contain the driver files
+  which are not in use
+****************************************************************************/
+
+BOOL printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+       int                             i;
+       int                             ndrivers;
+       uint32                          version;
+       fstring                         *list = NULL;
+       NT_PRINTER_DRIVER_INFO_LEVEL    driver;
+       
+       if ( !info )
+               return False;
+       
+       version = info->cversion;
+       
+       /* loop over all driver versions */
+       
+       DEBUG(5,("printer_driver_files_in_use: Beginning search through ntdrivers.tdb...\n"));
+       
+       /* get the list of drivers */
+               
+       list = NULL;
+       ndrivers = get_ntdrivers(&list, info->environment, version);
+               
+       DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", 
+               ndrivers, info->environment, version));
+
+       /* check each driver for overlap in files */
+               
+       for (i=0; i<ndrivers; i++) {
+               DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+                       
+               ZERO_STRUCT(driver);
+                       
+               if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, list[i], info->environment, version)) ) {
+                       SAFE_FREE(list);
+                       return True;
+               }
+                       
+               /* check if d2 uses any files from d1 */
+               /* only if this is a different driver than the one being deleted */
+                       
+               if ( !strequal(info->name, driver.info_3->name) ) {
+                       if ( trim_overlap_drv_files(info, driver.info_3) ) {
+                               free_a_printer_driver(driver, 3);
+                               SAFE_FREE( list );
+                               return True;
+                       }
+               }
+       
+               free_a_printer_driver(driver, 3);
+       }       
+       
+       SAFE_FREE(list);
+       
+       DEBUG(5,("printer_driver_files_in_use: Completed search through ntdrivers.tdb...\n"));
+       
+       driver.info_3 = info;
+       
+       if ( DEBUGLEVEL >= 20 )
+               dump_a_printer_driver( driver, 3 );
+       
+       return False;
+}
+
+/****************************************************************************
+  Actually delete the driver files.  Make sure that 
+  printer_driver_files_in_use() return False before calling 
+  this.
+****************************************************************************/
+
+static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user )
+{
+       int i = 0;
+       char *s;
+       struct tcon_context *conn;
+       DATA_BLOB null_pw;
+       NTSTATUS nt_status;
+       
+       if ( !info_3 )
+               return False;
+               
+       DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", info_3->name, info_3->cversion));
+       
+       /*
+        * Connect to the print$ share under the same account as the 
+        * user connected to the rpc pipe. Note we must be root to 
+        * do this.
+        */
+        
+       become_root();
+       null_pw = data_blob( NULL, 0 );
+        conn = make_connection_with_chdir( "print$", null_pw, "A:", user->vuid, &nt_status );
+       unbecome_root();
+       
+       if ( !conn ) {
+               DEBUG(0,("delete_driver_files: Unable to connect\n"));
+               return False;
+       }
+
+        /* Save who we are - we are temporarily becoming the connection user. */
+
+       if ( !become_user(conn, conn->vuid) ) {
+               DEBUG(0,("delete_driver_files: Can't become user!\n"));
+               return False;
+       }
+
+       /* now delete the files; must strip the '\print$' string from 
+          fron of path                                                */
+       
+       if ( *info_3->driverpath ) {
+               if ( (s = strchr( &info_3->driverpath[1], '\\' )) != NULL ) {
+                       DEBUG(10,("deleting driverfile [%s]\n", s));
+                       unlink_internals(conn, 0, s);
+               }
+       }
+               
+       if ( *info_3->configfile ) {
+               if ( (s = strchr( &info_3->configfile[1], '\\' )) != NULL ) {
+                       DEBUG(10,("deleting configfile [%s]\n", s));
+                       unlink_internals(conn, 0, s);
+               }
+       }
+       
+       if ( *info_3->datafile ) {
+               if ( (s = strchr( &info_3->datafile[1], '\\' )) != NULL ) {
+                       DEBUG(10,("deleting datafile [%s]\n", s));
+                       unlink_internals(conn, 0, s);
+               }
+       }
+       
+       if ( *info_3->helpfile ) {
+               if ( (s = strchr( &info_3->helpfile[1], '\\' )) != NULL ) {
+                       DEBUG(10,("deleting helpfile [%s]\n", s));
+                       unlink_internals(conn, 0, s);
+               }
+       }
+       
+       /* check if we are done removing files */
+       
+       if ( info_3->dependentfiles ) {
+               while ( *info_3->dependentfiles[i] ) {
+                       char *file;
+
+                       /* bypass the "\print$" portion of the path */
+                       
+                       if ( (file = strchr( info_3->dependentfiles[i]+1, '\\' )) != NULL ) {
+                               DEBUG(10,("deleting dependent file [%s]\n", file));
+                               unlink_internals(conn, 0, file );
+                       }
+                       
+                       i++;
+               }
+       }
+
+       unbecome_user();
+       
+       return True;
+}
+
+/****************************************************************************
+ Remove a printer driver from the TDB.  This assumes that the the driver was
+ previously looked up.
+ ***************************************************************************/
+
+WERROR delete_printer_driver( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user,
+                              uint32 version, BOOL delete_files )
+{
+       pstring         key;
+       fstring         arch;
+       TDB_DATA        kbuf, dbuf;
+       NT_PRINTER_DRIVER_INFO_LEVEL    ctr;
+
+       /* delete the tdb data first */
+
+       get_short_archi(arch, info_3->environment);
+       slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX,
+               arch, version, info_3->name);
+
+       DEBUG(5,("delete_printer_driver: key = [%s] delete_files = %s\n",
+               key, delete_files ? "TRUE" : "FALSE" ));
+
+       ctr.info_3 = info_3;
+       dump_a_printer_driver( ctr, 3 );
+
+       kbuf.dptr=key;
+       kbuf.dsize=strlen(key)+1;
+
+       /* check if the driver actually exists for this environment */
+       
+       dbuf = tdb_fetch( tdb_drivers, kbuf );
+       if ( !dbuf.dptr ) {
+               DEBUG(8,("delete_printer_driver: Driver unknown [%s]\n", key));
+               return WERR_UNKNOWN_PRINTER_DRIVER;
+       }
+               
+       SAFE_FREE( dbuf.dptr );
+       
+       /* ok... the driver exists so the delete should return success */
+               
+       if (tdb_delete(tdb_drivers, kbuf) == -1) {
+               DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key));
+               return WERR_ACCESS_DENIED;
+       }
+
+       /*
+        * now delete any associated files if delete_files == True
+        * even if this part failes, we return succes because the
+        * driver doesn not exist any more
+        */
+
+       if ( delete_files )
+               delete_driver_files( info_3, user );
+                       
+               
+       DEBUG(5,("delete_printer_driver: driver delete successful [%s]\n", key));
+
+       return WERR_OK;
+       }
+       
+/****************************************************************************
+ Store a security desc for a printer.
+****************************************************************************/
+
+WERROR nt_printing_setsec(const char *printername, SEC_DESC_BUF *secdesc_ctr)
+{
+       SEC_DESC_BUF *new_secdesc_ctr = NULL;
+       SEC_DESC_BUF *old_secdesc_ctr = NULL;
+       prs_struct ps;
+       TALLOC_CTX *mem_ctx = NULL;
+       fstring key;
+       WERROR status;
+
+       mem_ctx = talloc_init("nt_printing_setsec");
+       if (mem_ctx == NULL)
+               return WERR_NOMEM;
+
+        /* The old owner and group sids of the security descriptor are not
+          present when new ACEs are added or removed by changing printer
+          permissions through NT.  If they are NULL in the new security
+          descriptor then copy them over from the old one. */
+
+       if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) {
+               DOM_SID *owner_sid, *group_sid;
+               SEC_ACL *dacl, *sacl;
+               SEC_DESC *psd = NULL;
+               size_t size;
+
+               nt_printing_getsec(mem_ctx, printername, &old_secdesc_ctr);
+
+               /* Pick out correct owner and group sids */
+
+               owner_sid = secdesc_ctr->sec->owner_sid ?
+                       secdesc_ctr->sec->owner_sid :
+                       old_secdesc_ctr->sec->owner_sid;
+
+               group_sid = secdesc_ctr->sec->grp_sid ?
+                       secdesc_ctr->sec->grp_sid :
+                       old_secdesc_ctr->sec->grp_sid;
+
+               dacl = secdesc_ctr->sec->dacl ?
+                       secdesc_ctr->sec->dacl :
+                       old_secdesc_ctr->sec->dacl;
+
+               sacl = secdesc_ctr->sec->sacl ?
+                       secdesc_ctr->sec->sacl :
+                       old_secdesc_ctr->sec->sacl;
+
+               /* Make a deep copy of the security descriptor */
+
+               psd = make_sec_desc(mem_ctx, secdesc_ctr->sec->revision,
+                                   owner_sid, group_sid,
+                                   sacl,
+                                   dacl,
+                                   &size);
+
+               new_secdesc_ctr = make_sec_desc_buf(mem_ctx, size, psd);
+       }
+
+       if (!new_secdesc_ctr) {
+               new_secdesc_ctr = secdesc_ctr;
+       }
+
+       /* Store the security descriptor in a tdb */
+
+       prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) +
+                sizeof(SEC_DESC_BUF), mem_ctx, MARSHALL);
+
+       if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
+                            &ps, 1)) {
+               status = WERR_BADFUNC;
+               goto out;
+       }
+
+       slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+       if (tdb_prs_store(tdb_printers, key, &ps)==0) {
+               status = WERR_OK;
+       } else {
+               DEBUG(1,("Failed to store secdesc for %s\n", printername));
+               status = WERR_BADFUNC;
+       }
+
+       /* Free malloc'ed memory */
+
+ out:
+
+       prs_mem_free(&ps);
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+       return status;
+}
+
+/****************************************************************************
+ Construct a default security descriptor buffer for a printer.
+****************************************************************************/
+
+static SEC_DESC_BUF *construct_default_printer_sdb(TALLOC_CTX *ctx)
+{
+       SEC_ACE ace[3];
+       SEC_ACCESS sa;
+       SEC_ACL *psa = NULL;
+       SEC_DESC_BUF *sdb = NULL;
+       SEC_DESC *psd = NULL;
+       DOM_SID owner_sid;
+       size_t sd_size;
+
+       /* Create an ACE where Everyone is allowed to print */
+
+       init_sec_access(&sa, PRINTER_ACE_PRINT);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+       /* Make the security descriptor owned by the Administrators group
+          on the PDC of the domain. */
+
+       if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+               sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+       } else {
+
+               /* Backup plan - make printer owned by admins.
+                  This should emulate a lanman printer as security
+                  settings can't be changed. */
+
+               sid_copy(&owner_sid, get_global_sam_sid());
+               sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+       }
+
+       init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+       init_sec_ace(&ace[1], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_OBJECT_INHERIT |
+                    SEC_ACE_FLAG_INHERIT_ONLY);
+
+       init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+       init_sec_ace(&ace[2], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+       /* The ACL revision number in rpc_secdesc.h differs from the one
+          created by NT when setting ACE entries in printer
+          descriptors.  NT4 complains about the property being edited by a
+          NT5 machine. */
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) != NULL) {
+               psd = make_sec_desc(ctx, SEC_DESC_REVISION,
+                                   &owner_sid, NULL,
+                                   NULL, psa, &sd_size);
+       }
+
+       if (!psd) {
+               DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n"));
+               return NULL;
+       }
+
+       sdb = make_sec_desc_buf(ctx, sd_size, psd);
+
+       DEBUG(4,("construct_default_printer_sdb: size = %u.\n",
+                (unsigned int)sd_size));
+
+       return sdb;
+}
+
+/****************************************************************************
+ Get a security desc for a printer.
+****************************************************************************/
+
+BOOL nt_printing_getsec(TALLOC_CTX *ctx, const char *printername, SEC_DESC_BUF **secdesc_ctr)
+{
+       prs_struct ps;
+       fstring key;
+       char *temp;
+
+       if (strlen(printername) > 2 && (temp = strchr(printername + 2, '\\'))) {
+               printername = temp + 1;
+       }
+
+       /* Fetch security descriptor from tdb */
+
+       slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+       if (tdb_prs_fetch(tdb_printers, key, &ps, ctx)!=0 ||
+           !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+
+               DEBUG(4,("using default secdesc for %s\n", printername));
+
+               if (!(*secdesc_ctr = construct_default_printer_sdb(ctx))) {
+                       return False;
+               }
+
+               /* Save default security descriptor for later */
+
+               prs_init(&ps, (uint32)sec_desc_size((*secdesc_ctr)->sec) +
+                               sizeof(SEC_DESC_BUF), ctx, MARSHALL);
+
+               if (sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1))
+                       tdb_prs_store(tdb_printers, key, &ps);
+
+               prs_mem_free(&ps);
+
+               return True;
+       }
+
+       /* If security descriptor is owned by S-1-1-0 and winbindd is up,
+          this security descriptor has been created when winbindd was
+          down.  Take ownership of security descriptor. */
+
+       if (sid_equal((*secdesc_ctr)->sec->owner_sid, &global_sid_World)) {
+               DOM_SID owner_sid;
+
+               /* Change sd owner to workgroup administrator */
+
+               if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+                       SEC_DESC_BUF *new_secdesc_ctr = NULL;
+                       SEC_DESC *psd = NULL;
+                       size_t size;
+
+                       /* Create new sd */
+
+                       sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+
+                       psd = make_sec_desc(ctx, (*secdesc_ctr)->sec->revision,
+                                           &owner_sid,
+                                           (*secdesc_ctr)->sec->grp_sid,
+                                           (*secdesc_ctr)->sec->sacl,
+                                           (*secdesc_ctr)->sec->dacl,
+                                           &size);
+
+                       new_secdesc_ctr = make_sec_desc_buf(ctx, size, psd);
+
+                       /* Swap with other one */
+
+                       *secdesc_ctr = new_secdesc_ctr;
+
+                       /* Set it */
+
+                       nt_printing_setsec(printername, *secdesc_ctr);
+               }
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               SEC_ACL *the_acl = (*secdesc_ctr)->sec->dacl;
+               int i;
+
+               DEBUG(10, ("secdesc_ctr for %s has %d aces:\n", 
+                          printername, the_acl->num_aces));
+
+               for (i = 0; i < the_acl->num_aces; i++) {
+                       fstring sid_str;
+
+                       sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+                       DEBUG(10, ("%s %d %d 0x%08x\n", sid_str,
+                                  the_acl->ace[i].type, the_acl->ace[i].flags, 
+                                  the_acl->ace[i].info.mask)); 
+               }
+       }
+
+       prs_mem_free(&ps);
+       return True;
+}
+
+/* error code:
+       0: everything OK
+       1: level not implemented
+       2: file doesn't exist
+       3: can't allocate memory
+       4: can't free memory
+       5: non existant struct
+*/
+
+/*
+       A printer and a printer driver are 2 different things.
+       NT manages them separatelly, Samba does the same.
+       Why ? Simply because it's easier and it makes sense !
+       
+       Now explanation: You have 3 printers behind your samba server,
+       2 of them are the same make and model (laser A and B). But laser B
+       has an 3000 sheet feeder and laser A doesn't such an option.
+       Your third printer is an old dot-matrix model for the accounting :-).
+       
+       If the /usr/local/samba/lib directory (default dir), you will have
+       5 files to describe all of this.
+       
+       3 files for the printers (1 by printer):
+               NTprinter_laser A
+               NTprinter_laser B
+               NTprinter_accounting
+       2 files for the drivers (1 for the laser and 1 for the dot matrix)
+               NTdriver_printer model X
+               NTdriver_printer model Y
+
+jfm: I should use this comment for the text file to explain
+       same thing for the forms BTW.
+       Je devrais mettre mes commentaires en francais, ca serait mieux :-)
+
+*/
+
+/* Convert generic access rights to printer object specific access rights.
+   It turns out that NT4 security descriptors use generic access rights and
+   NT5 the object specific ones. */
+
+void map_printer_permissions(SEC_DESC *sd)
+{
+       int i;
+
+       for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+               se_map_generic(&sd->dacl->ace[i].info.mask,
+                              &printer_generic_mapping);
+       }
+}
+
+/****************************************************************************
+ Check a user has permissions to perform the given operation.  We use the
+ permission constants defined in include/rpc_spoolss.h to check the various
+ actions we perform when checking printer access.
+
+   PRINTER_ACCESS_ADMINISTER:
+       print_queue_pause, print_queue_resume, update_printer_sec,
+       update_printer, spoolss_addprinterex_level_2,
+       _spoolss_setprinterdata
+
+   PRINTER_ACCESS_USE:
+       print_job_start
+
+   JOB_ACCESS_ADMINISTER:
+       print_job_delete, print_job_pause, print_job_resume,
+       print_queue_purge
+
+ ****************************************************************************/
+BOOL print_access_check(struct current_user *user, int snum, int access_type)
+{
+       SEC_DESC_BUF *secdesc = NULL;
+       uint32 access_granted;
+       NTSTATUS status;
+       BOOL result;
+       const char *pname;
+       TALLOC_CTX *mem_ctx = NULL;
+       extern struct current_user current_user;
+       
+       /* If user is NULL then use the current_user structure */
+
+       if (!user)
+               user = &current_user;
+
+       /* Always allow root or printer admins to do anything */
+
+       if (user->uid == 0 ||
+           user_in_list(uidtoname(user->uid), lp_printer_admin(snum), user->groups, user->ngroups)) {
+               return True;
+       }
+
+       /* Get printer name */
+
+       pname = PRINTERNAME(snum);
+
+       if (!pname || !*pname) {
+               errno = EACCES;
+               return False;
+       }
+
+       /* Get printer security descriptor */
+
+       if(!(mem_ctx = talloc_init("print_access_check"))) {
+               errno = ENOMEM;
+               return False;
+       }
+
+       nt_printing_getsec(mem_ctx, pname, &secdesc);
+
+       if (access_type == JOB_ACCESS_ADMINISTER) {
+               SEC_DESC_BUF *parent_secdesc = secdesc;
+
+               /* Create a child security descriptor to check permissions
+                  against.  This is because print jobs are child objects
+                  objects of a printer. */
+
+               secdesc = se_create_child_secdesc(mem_ctx, parent_secdesc->sec, False);
+
+               /* Now this is the bit that really confuses me.  The access
+                  type needs to be changed from JOB_ACCESS_ADMINISTER to
+                  PRINTER_ACCESS_ADMINISTER for this to work.  Something
+                  to do with the child (job) object becoming like a
+                  printer??  -tpot */
+
+               access_type = PRINTER_ACCESS_ADMINISTER;
+       }
+       
+       /* Check access */
+       
+       map_printer_permissions(secdesc->sec);
+
+       result = se_access_check(secdesc->sec, user->nt_user_token, access_type,
+                                &access_granted, &status);
+
+       DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
+
+       talloc_destroy(mem_ctx);
+       
+       if (!result)
+               errno = EACCES;
+
+       return result;
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+BOOL print_time_access_check(int snum)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       BOOL ok = False;
+       time_t now = time(NULL);
+       struct tm *t;
+       uint32 mins;
+
+       if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))))
+               return False;
+
+       if (printer->info_2->starttime == 0 && printer->info_2->untiltime == 0)
+               ok = True;
+
+       t = gmtime(&now);
+       mins = (uint32)t->tm_hour*60 + (uint32)t->tm_min;
+
+       if (mins >= printer->info_2->starttime && mins <= printer->info_2->untiltime)
+               ok = True;
+
+       free_a_printer(&printer, 2);
+
+       if (!ok)
+               errno = EACCES;
+
+       return ok;
+}
+
diff --git a/source4/printing/pcap.c b/source4/printing/pcap.c
new file mode 100644 (file)
index 0000000..c399c3c
--- /dev/null
@@ -0,0 +1,413 @@
+/* 
+   Unix SMB/CIFS implementation.
+   printcap parsing
+   Copyright (C) Karl Auer 1993-1998
+
+   Re-working by Martin Kiff, 1994
+   
+   Re-written again by Andrew Tridgell
+
+   Modified for SVID support by Norm Jacobs, 1997
+
+   Modified for CUPS support by Michael Sweet, 1999
+   
+   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.
+*/
+
+/*
+ *  Parse printcap file.
+ *
+ *  This module does exactly one thing - it looks into the printcap file
+ *  and tells callers if a specified string appears as a printer name.
+ *
+ *  The way this module looks at the printcap file is very simplistic.
+ *  Only the local printcap file is inspected (no searching of NIS
+ *  databases etc).
+ *
+ *  There are assumed to be one or more printer names per record, held
+ *  as a set of sub-fields separated by vertical bar symbols ('|') in the
+ *  first field of the record. The field separator is assumed to be a colon
+ *  ':' and the record separator a newline.
+ * 
+ *  Lines ending with a backspace '\' are assumed to flag that the following
+ *  line is a continuation line so that a set of lines can be read as one
+ *  printcap entry.
+ *
+ *  A line stating with a hash '#' is assumed to be a comment and is ignored
+ *  Comments are discarded before the record is strung together from the
+ *  set of continuation lines.
+ *
+ *  Opening a pipe for "lpc status" and reading that would probably 
+ *  be pretty effective. Code to do this already exists in the freely
+ *  distributable PCNFS server code.
+ *
+ *  Modified to call SVID/XPG4 support if printcap name is set to "lpstat"
+ *  in smb.conf under Solaris.
+ *
+ *  Modified to call CUPS support if printcap name is set to "cups"
+ *  in smb.conf.
+ */
+
+#include "includes.h"
+
+#ifdef AIX
+/*  ******************************************
+     Extend for AIX system and qconfig file
+       from 'boulard@univ-rennes1.fr
+    ****************************************** */
+static int strlocate(char *xpLine,char *xpS)
+{
+       int iS,iL,iRet;
+       char *p;
+       iS = strlen(xpS);
+       iL = strlen(xpLine);
+
+       iRet = 0;
+       p = xpLine;
+       while (iL >= iS)
+       {
+               if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
+               p++;
+               iL--;
+       }
+       /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
+       
+       return(iRet);
+}
+       
+       
+/* ******************************************************************* */
+/* *    Scan qconfig and search all virtual printer (device printer) * */
+/* ******************************************************************* */
+static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *))
+{
+       int iEtat;
+       XFILE *pfile;
+       char *line,*p;
+       pstring name,comment;
+       line  = NULL;
+       *name = 0;
+       *comment = 0;
+
+       if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+       {
+             DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+             return;
+       }
+
+       iEtat = 0;
+       /* scan qconfig file for searching <printername>:       */
+       for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+       {
+               if (*line == '*' || *line == 0)
+               continue;
+               switch (iEtat)
+               {
+                       case 0: /* locate an entry */
+                        if (*line == '\t' || *line == ' ') continue;
+                        if ((p=strchr_m(line,':')))
+                        {
+                               *p = '\0';
+                               p = strtok(line,":");
+                               if (strcmp(p,"bsh")!=0)
+                                 {
+                                   pstrcpy(name,p);
+                                   iEtat = 1;
+                                   continue;
+                                 }
+                        }
+                        break;
+                       case 1: /* scanning device stanza */
+                        if (*line == '*' || *line == 0) continue;
+                        if (*line != '\t' && *line != ' ')
+                        {
+                          /* name is found without stanza device  */
+                          /* probably a good printer ???               */
+                          fn(name,comment);
+                          iEtat = 0;
+                          continue;
+                         }
+                       
+                         if (strlocate(line,"backend"))
+                         {
+                               /* it's a device, not a virtual printer*/
+                               iEtat = 0;
+                         }
+                         else if (strlocate(line,"device"))
+                         {
+                               /* it's a good virtual printer */
+                               fn(name,comment);
+                               iEtat = 0;
+                               continue;
+                         }
+                         break;
+               }
+       }
+       x_fclose(pfile);
+}
+
+/* Scan qconfig file and locate de printername */
+
+static BOOL ScanQconfig(char *psz,char *pszPrintername)
+{
+       int iLg,iEtat;
+       XFILE *pfile;
+       char *pName;
+       char *line;
+
+       pName = NULL;
+       line  = NULL;
+       if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
+        pName = malloc(iLg+10);
+       if (pName == NULL)
+       {
+               DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
+               return(False);
+       }
+       if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+       {
+             DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+             SAFE_FREE(pName);
+             return(False);
+       }
+       slprintf(pName, iLg + 9, "%s:",pszPrintername);
+       iLg = strlen(pName);
+       /*DEBUG(3,( " Looking for entry %s\n",pName));*/
+       iEtat = 0;
+       /* scan qconfig file for searching <printername>:       */
+       for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+       {
+               if (*line == '*' || *line == 0)
+               continue;
+               switch (iEtat)
+               {
+                       case 0: /* scanning entry */
+                        if (strncmp(line,pName,iLg) == 0)
+                        {
+                               iEtat = 1;
+                               continue;
+                        }
+                        break;
+                       case 1: /* scanning device stanza */
+                        if (*line == '*' || *line == 0) continue;
+                        if (*line != '\t' && *line != ' ')
+                        {
+                          /* name is found without stanza device  */
+                          /* probably a good printer ???               */
+                          free (line);
+                          SAFE_FREE(pName);
+                          fclose(pfile);
+                          return(True);
+                         }
+                       
+                         if (strlocate(line,"backend"))
+                         {
+                               /* it's a device, not a virtual printer*/
+                               iEtat = 0;
+                         }
+                         else if (strlocate(line,"device"))
+                         {
+                               /* it's a good virtual printer */
+                               free (line);
+                               SAFE_FREE(pName);
+                               fclose(pfile);
+                               return(True);
+                         }
+                         break;
+               }
+       }
+       free (pName);
+       x_fclose(pfile);
+       return(False);
+}
+#endif /* AIX */
+
+
+/***************************************************************************
+Scan printcap file pszPrintcapname for a printer called pszPrintername. 
+Return True if found, else False. Returns False on error, too, after logging 
+the error at level 0. For generality, the printcap name may be passed - if
+passed as NULL, the configuration will be queried for the name. 
+***************************************************************************/
+BOOL pcap_printername_ok(const char *pszPrintername, const char *pszPrintcapname)
+{
+  char *line=NULL;
+  const char *psz;
+  char *p,*q;
+  XFILE *pfile;
+
+  if (pszPrintername == NULL || pszPrintername[0] == '\0')
+    {
+      DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
+      return(False);
+    }
+
+  /* only go looking if no printcap name supplied */
+  if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
+    if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+      {
+       DEBUG(0,( "No printcap file name configured!\n"));
+       return(False);
+      }
+
+#ifdef HAVE_CUPS
+    if (strequal(psz, "cups"))
+       return (cups_printername_ok(pszPrintername));
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+    if (strequal(psz, "lpstat"))
+       return (sysv_printername_ok(pszPrintername));
+#endif
+
+#ifdef AIX
+  if (strlocate(psz,"/qconfig"))
+     return(ScanQconfig(psz,pszPrintername));
+#endif
+
+  if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+    {
+      DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+      return(False);
+    }
+
+  for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+    {
+      if (*line == '#' || *line == 0)
+       continue;
+
+      /* now we have a real printer line - cut it off at the first : */      
+      p = strchr_m(line,':');
+      if (p) *p = 0;
+      
+      /* now just check if the name is in the list */
+      /* NOTE: I avoid strtok as the fn calling this one may be using it */
+      for (p=line; p; p=q)
+       {
+         if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+         if (strequal(p,pszPrintername))
+           {
+             SAFE_FREE(line);
+             x_fclose(pfile);
+             return(True);           
+           }
+         p = q;
+       }
+    }
+
+  x_fclose(pfile);
+  return(False);
+}
+
+
+/***************************************************************************
+run a function on each printer name in the printcap file. The function is 
+passed the primary name and the comment (if possible). Note the fn() takes
+strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
+to return DOS codepage. FIXME !! JRA.
+***************************************************************************/
+void pcap_printer_fn(void (*fn)(char *, char *))
+{
+  pstring name,comment;
+  char *line;
+  char *psz;
+  char *p,*q;
+  XFILE *pfile;
+
+  /* only go looking if no printcap name supplied */
+  if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+    {
+      DEBUG(0,( "No printcap file name configured!\n"));
+      return;
+    }
+
+#ifdef HAVE_CUPS
+    if (strequal(psz, "cups")) {
+      cups_printer_fn(fn);
+      return;
+    }
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+    if (strequal(psz, "lpstat")) {
+      sysv_printer_fn(fn);
+      return;
+    }
+#endif
+
+#ifdef AIX
+  if (strlocate(psz,"/qconfig"))
+  {
+       ScanQconfig_fn(psz,fn);
+     return;
+  }
+#endif
+
+  if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+    {
+      DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+      return;
+    }
+
+  for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+    {
+      if (*line == '#' || *line == 0)
+       continue;
+
+      /* now we have a real printer line - cut it off at the first : */      
+      p = strchr_m(line,':');
+      if (p) *p = 0;
+      
+      /* now find the most likely printer name and comment 
+       this is pure guesswork, but it's better than nothing */
+      *name = 0;
+      *comment = 0;
+      for (p=line; p; p=q)
+       {
+         BOOL has_punctuation;
+         if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+         has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')'));
+
+         if (strlen(p)>strlen(comment) && has_punctuation)
+           {
+             StrnCpy(comment,p,sizeof(comment)-1);
+             continue;
+           }
+
+         if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
+           {
+             if (!*comment) pstrcpy(comment,name);
+             pstrcpy(name,p);
+             continue;
+           }
+
+         if (!strchr_m(comment,' ') && 
+             strlen(p) > strlen(comment))
+           {
+             StrnCpy(comment,p,sizeof(comment)-1);
+             continue;
+           }
+       }
+
+      comment[60] = 0;
+      name[MAXPRINTERLEN] = 0;
+
+      if (*name) 
+       fn(name,comment);
+    }
+  x_fclose(pfile);
+}
diff --git a/source4/printing/print_cups.c b/source4/printing/print_cups.c
new file mode 100644 (file)
index 0000000..7cf21c9
--- /dev/null
@@ -0,0 +1,1293 @@
+/*
+ * Support code for the Common UNIX Printing System ("CUPS")
+ *
+ * Copyright 1999-2003 by Michael R Sweet.
+ *
+ * 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 "printing.h"
+
+#ifdef HAVE_CUPS
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * CUPS printing interface definitions...
+ */
+
+static int cups_job_delete(int snum, struct printjob *pjob);
+static int cups_job_pause(int snum, struct printjob *pjob);
+static int cups_job_resume(int snum, struct printjob *pjob);
+static int cups_job_submit(int snum, struct printjob *pjob);
+static int cups_queue_get(int snum, print_queue_struct **q,
+                          print_status_struct *status);
+static int cups_queue_pause(int snum);
+static int cups_queue_resume(int snum);
+
+
+struct printif cups_printif =
+               {
+                 cups_queue_get,
+                 cups_queue_pause,
+                 cups_queue_resume,
+                 cups_job_delete,
+                 cups_job_pause,
+                 cups_job_resume,
+                 cups_job_submit,
+               };
+
+/*
+ * 'cups_passwd_cb()' - The CUPS password callback...
+ */
+
+static const char *                            /* O - Password or NULL */
+cups_passwd_cb(const char *prompt)     /* I - Prompt */
+{
+ /*
+  * Always return NULL to indicate that no password is available...
+  */
+
+  return (NULL);
+}
+
+
+/*
+ * 'cups_printer_fn()' - Call a function for every printer known to the
+ *                       system.
+ */
+
+void cups_printer_fn(void (*fn)(char *, char *))
+{
+       /* I - Function to call */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       ipp_attribute_t *attr;          /* Current attribute */
+       cups_lang_t     *language;      /* Default language */
+       char            *name,          /* printer-name attribute */
+                       *make_model,    /* printer-make-and-model attribute */
+                       *info;          /* printer-info attribute */
+       static const char *requested[] =/* Requested attributes */
+                       {
+                         "printer-name",
+                         "printer-make-and-model",
+                         "printer-info"
+                       };       
+
+
+       DEBUG(5,("cups_printer_fn(%p)\n", fn));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return;
+       }
+
+       /*
+       * Build a CUPS_GET_PRINTERS request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    requested-attributes
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = CUPS_GET_PRINTERS;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                     "requested-attributes",
+                     (sizeof(requested) / sizeof(requested[0])),
+                     NULL, requested);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       if ((response = cupsDoRequest(http, request, "/")) == NULL)
+       {
+               DEBUG(0,("Unable to get printer list - %s\n",
+                        ippErrorString(cupsLastError())));
+               httpClose(http);
+               return;
+       }
+
+       for (attr = response->attrs; attr != NULL;)
+       {
+              /*
+               * Skip leading attributes until we hit a printer...
+               */
+
+               while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+                       attr = attr->next;
+
+               if (attr == NULL)
+                       break;
+
+              /*
+               * Pull the needed attributes from this printer...
+               */
+
+               name       = NULL;
+               make_model = NULL;
+               info       = NULL;
+
+               while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+               {
+                       if (strcmp(attr->name, "printer-name") == 0 &&
+                           attr->value_tag == IPP_TAG_NAME)
+                               name = attr->values[0].string.text;
+
+                       if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+                           attr->value_tag == IPP_TAG_TEXT)
+                               make_model = attr->values[0].string.text;
+
+                       if (strcmp(attr->name, "printer-info") == 0 &&
+                           attr->value_tag == IPP_TAG_TEXT)
+                               info = attr->values[0].string.text;
+
+                       attr = attr->next;
+               }
+
+              /*
+               * See if we have everything needed...
+               */
+
+               if (name == NULL)
+                       break;
+
+               if (info == NULL || !info[0])
+                       (*fn)(name, make_model);
+               else
+                       (*fn)(name, info);
+               
+
+       }
+
+       ippDelete(response);
+
+
+       /*
+       * Build a CUPS_GET_CLASSES request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    requested-attributes
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = CUPS_GET_CLASSES;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                     "requested-attributes",
+                     (sizeof(requested) / sizeof(requested[0])),
+                     NULL, requested);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       if ((response = cupsDoRequest(http, request, "/")) == NULL)
+       {
+               DEBUG(0,("Unable to get printer list - %s\n",
+                        ippErrorString(cupsLastError())));
+               httpClose(http);
+               return;
+       }
+
+       for (attr = response->attrs; attr != NULL;)
+       {
+              /*
+               * Skip leading attributes until we hit a printer...
+               */
+
+               while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+                       attr = attr->next;
+
+               if (attr == NULL)
+                       break;
+
+              /*
+               * Pull the needed attributes from this printer...
+               */
+
+               name       = NULL;
+               make_model = NULL;
+               info       = NULL;
+
+               while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+               {
+                       if (strcmp(attr->name, "printer-name") == 0 &&
+                           attr->value_tag == IPP_TAG_NAME)
+                               name = attr->values[0].string.text;
+
+                       if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+                           attr->value_tag == IPP_TAG_TEXT)
+                               make_model = attr->values[0].string.text;
+
+                       if (strcmp(attr->name, "printer-info") == 0 &&
+                           attr->value_tag == IPP_TAG_TEXT)
+                               info = attr->values[0].string.text;
+
+                       attr = attr->next;
+               }
+
+              /*
+               * See if we have everything needed...
+               */
+
+               if (name == NULL)
+                       break;
+
+               if (info == NULL || !info[0])
+                       (*fn)(name, make_model);
+               else
+                       (*fn)(name, info);
+               
+
+       }
+
+       ippDelete(response);
+
+       /*
+        * Close the connection to the server...
+       */
+
+       httpClose(http);
+}
+
+
+/*
+ * 'cups_printername_ok()' - Provide the equivalent of pcap_printername_ok()
+ *                           for CUPS.
+ * O - 1 if printer name OK
+ * I - Name of printer 
+ */
+int cups_printername_ok(const char *name)
+{
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_printername_ok(\"%s\")\n", name));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(3,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (0);
+       }
+
+       /*
+       * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    requested-attributes
+       *    printer-uri
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                     "requested-attributes", NULL, "printer-uri");
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", name);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                     "printer-uri", NULL, uri);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       if ((response = cupsDoRequest(http, request, "/")) == NULL)
+       {
+               DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+                        ippErrorString(cupsLastError())));
+               httpClose(http);
+               return (0);
+       }
+
+       httpClose(http);
+
+       if (response->request.status.status_code >= IPP_OK_CONFLICT)
+       {
+               DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+                        ippErrorString(response->request.status.status_code)));
+               ippDelete(response);
+               return (0);
+       }
+       else
+       {
+               ippDelete(response);
+               return (1);
+       }
+}
+
+
+/*
+ * 'cups_job_delete()' - Delete a job.
+ */
+
+static int
+cups_job_delete(int snum, struct printjob *pjob)
+{
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+       * Build an IPP_CANCEL_JOB request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    job-uri
+       *    requesting-user-name
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_CANCEL_JOB;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, pjob->user);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+        ret = 1;
+
+       if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+       {
+         if (response->request.status.status_code >= IPP_OK_CONFLICT)
+               DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+                        ippErrorString(cupsLastError())));
+          else
+               ret = 0;
+
+         ippDelete(response);
+       }
+       else
+         DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+                  ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       return (ret);
+}
+
+
+/*
+ * 'cups_job_pause()' - Pause a job.
+ */
+
+static int
+cups_job_pause(int snum, struct printjob *pjob)
+{
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+       * Build an IPP_HOLD_JOB request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    job-uri
+       *    requesting-user-name
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_HOLD_JOB;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, pjob->user);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+        ret = 1;
+
+       if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+       {
+         if (response->request.status.status_code >= IPP_OK_CONFLICT)
+               DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+                        ippErrorString(cupsLastError())));
+          else
+               ret = 0;
+
+         ippDelete(response);
+       }
+       else
+         DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+                  ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       return (ret);
+}
+
+
+/*
+ * 'cups_job_resume()' - Resume a paused job.
+ */
+
+static int
+cups_job_resume(int snum, struct printjob *pjob)
+{
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+       * Build an IPP_RELEASE_JOB request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    job-uri
+       *    requesting-user-name
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_RELEASE_JOB;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, pjob->user);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+        ret = 1;
+
+       if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+       {
+         if (response->request.status.status_code >= IPP_OK_CONFLICT)
+               DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+                        ippErrorString(cupsLastError())));
+          else
+               ret = 0;
+
+         ippDelete(response);
+       }
+       else
+         DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+                  ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       return (ret);
+}
+
+
+/*
+ * 'cups_job_submit()' - Submit a job for printing.
+ */
+
+static int
+cups_job_submit(int snum, struct printjob *pjob)
+{
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+       * Build an IPP_PRINT_JOB request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    printer-uri
+       *    requesting-user-name
+       *    [document-data]
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_PRINT_JOB;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+                PRINTERNAME(snum));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                    "printer-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, pjob->user);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                    "job-originating-host-name", NULL,
+                    get_remote_machine_name());
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+                    pjob->jobname);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
+
+        ret = 1;
+       if ((response = cupsDoFileRequest(http, request, uri,
+                                         pjob->filename)) != NULL)
+       {
+               if (response->request.status.status_code >= IPP_OK_CONFLICT)
+                       DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum),
+                                ippErrorString(cupsLastError())));
+               else
+                       ret = 0;
+
+               ippDelete(response);
+       }
+       else
+               DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
+                        ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       if ( ret == 0 )
+               unlink(pjob->filename);
+       /* else print_job_end will do it for us */
+
+       return (ret);
+}
+
+
+/*
+ * 'cups_queue_get()' - Get all the jobs in the print queue.
+ */
+
+static int
+cups_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       ipp_attribute_t *attr;          /* Current attribute */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+       int             qcount,         /* Number of active queue entries */
+                       qalloc;         /* Number of queue entries allocated */
+       print_queue_struct *queue,      /* Queue entries */
+                       *temp;          /* Temporary pointer for queue */
+       const char      *user_name,     /* job-originating-user-name attribute */
+                       *job_name;      /* job-name attribute */
+       int             job_id;         /* job-id attribute */
+       int             job_k_octets;   /* job-k-octets attribute */
+       time_t          job_time;       /* time-at-creation attribute */
+       ipp_jstate_t    job_status;     /* job-status attribute */
+       int             job_priority;   /* job-priority attribute */
+       static const char *jattrs[] =   /* Requested job attributes */
+                       {
+                         "job-id",
+                         "job-k-octets",
+                         "job-name",
+                         "job-originating-user-name",
+                         "job-priority",
+                         "job-state",
+                         "time-at-creation",
+                       };
+       static const char *pattrs[] =   /* Requested printer attributes */
+                       {
+                         "printer-state",
+                         "printer-state-message"
+                       };
+
+
+       DEBUG(5,("cups_queue_get(%d, %p, %p)\n", snum, q, status));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (0);
+       }
+
+       /*
+        * Generate the printer URI...
+       */
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+                PRINTERNAME(snum));
+
+       /*
+       * Build an IPP_GET_JOBS request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    requested-attributes
+       *    printer-uri
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_GET_JOBS;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                     "requested-attributes",
+                     (sizeof(jattrs) / sizeof(jattrs[0])),
+                     NULL, jattrs);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                     "printer-uri", NULL, uri);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       if ((response = cupsDoRequest(http, request, "/")) == NULL)
+       {
+               DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+                        ippErrorString(cupsLastError())));
+               httpClose(http);
+               return (0);
+       }
+
+       if (response->request.status.status_code >= IPP_OK_CONFLICT)
+       {
+               DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+                        ippErrorString(response->request.status.status_code)));
+               ippDelete(response);
+               httpClose(http);
+
+               return (0);
+       }
+
+       /*
+        * Process the jobs...
+       */
+
+        qcount = 0;
+       qalloc = 0;
+       queue  = NULL;
+
+        for (attr = response->attrs; attr != NULL; attr = attr->next)
+       {
+              /*
+               * Skip leading attributes until we hit a job...
+               */
+
+               while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+                       attr = attr->next;
+
+               if (attr == NULL)
+                       break;
+
+              /*
+               * Allocate memory as needed...
+               */
+               if (qcount >= qalloc)
+               {
+                       qalloc += 16;
+
+                       temp = Realloc(queue, sizeof(print_queue_struct) * qalloc);
+
+                       if (temp == NULL)
+                       {
+                               DEBUG(0,("cups_queue_get: Not enough memory!"));
+                               ippDelete(response);
+                               httpClose(http);
+
+                               SAFE_FREE(queue);
+                               return (0);
+                       }
+
+                       queue = temp;
+               }
+
+               temp = queue + qcount;
+               memset(temp, 0, sizeof(print_queue_struct));
+
+              /*
+               * Pull the needed attributes from this job...
+               */
+
+               job_id       = 0;
+               job_priority = 50;
+               job_status   = IPP_JOB_PENDING;
+               job_time     = 0;
+               job_k_octets = 0;
+               user_name    = NULL;
+               job_name     = NULL;
+
+               while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+               {
+                       if (attr->name == NULL)
+                       {
+                               attr = attr->next;
+                               break;
+                       }
+
+                       if (strcmp(attr->name, "job-id") == 0 &&
+                           attr->value_tag == IPP_TAG_INTEGER)
+                               job_id = attr->values[0].integer;
+
+                       if (strcmp(attr->name, "job-k-octets") == 0 &&
+                           attr->value_tag == IPP_TAG_INTEGER)
+                               job_k_octets = attr->values[0].integer;
+
+                       if (strcmp(attr->name, "job-priority") == 0 &&
+                           attr->value_tag == IPP_TAG_INTEGER)
+                               job_priority = attr->values[0].integer;
+
+                       if (strcmp(attr->name, "job-state") == 0 &&
+                           attr->value_tag == IPP_TAG_ENUM)
+                               job_status = (ipp_jstate_t)(attr->values[0].integer);
+
+                       if (strcmp(attr->name, "time-at-creation") == 0 &&
+                           attr->value_tag == IPP_TAG_INTEGER)
+                               job_time = attr->values[0].integer;
+
+                       if (strcmp(attr->name, "job-name") == 0 &&
+                           attr->value_tag == IPP_TAG_NAME)
+                               job_name = attr->values[0].string.text;
+
+                       if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+                           attr->value_tag == IPP_TAG_NAME)
+                               user_name = attr->values[0].string.text;
+
+                       attr = attr->next;
+               }
+
+              /*
+               * See if we have everything needed...
+               */
+
+               if (user_name == NULL || job_name == NULL || job_id == 0)
+               {
+                 if (attr == NULL)
+                   break;
+                 else
+                   continue;
+               }
+
+               temp->job      = job_id;
+               temp->size     = job_k_octets * 1024;
+               temp->status   = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
+                                job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
+                                 job_status == IPP_JOB_HELD ? LPQ_PAUSED :
+                                LPQ_PRINTING;
+               temp->priority = job_priority;
+               temp->time     = job_time;
+               strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
+               strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
+
+               qcount ++;
+
+               if (attr == NULL)
+                 break;
+       }
+
+       ippDelete(response);
+
+       /*
+       * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+       * following attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    requested-attributes
+       *    printer-uri
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                     "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                     "attributes-natural-language", NULL, language->language);
+
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                     "requested-attributes",
+                     (sizeof(pattrs) / sizeof(pattrs[0])),
+                     NULL, pattrs);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                     "printer-uri", NULL, uri);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+       if ((response = cupsDoRequest(http, request, "/")) == NULL)
+       {
+               DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+                        ippErrorString(cupsLastError())));
+               httpClose(http);
+               *q = queue;
+               return (qcount);
+       }
+
+       if (response->request.status.status_code >= IPP_OK_CONFLICT)
+       {
+               DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+                        ippErrorString(response->request.status.status_code)));
+               ippDelete(response);
+               httpClose(http);
+               *q = queue;
+               return (qcount);
+       }
+
+       /*
+        * Get the current printer status and convert it to the SAMBA values.
+       */
+
+        if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+       {
+               if (attr->values[0].integer == IPP_PRINTER_STOPPED)
+                       status->status = LPSTAT_STOPPED;
+               else
+                       status->status = LPSTAT_OK;
+       }
+
+        if ((attr = ippFindAttribute(response, "printer-state-message",
+                                    IPP_TAG_TEXT)) != NULL)
+               fstrcpy(status->message, attr->values[0].string.text);
+
+        ippDelete(response);
+
+       /*
+        * Return the job queue...
+       */
+
+       httpClose(http);
+
+       *q = queue;
+       return (qcount);
+}
+
+
+/*
+ * 'cups_queue_pause()' - Pause a print queue.
+ */
+
+static int
+cups_queue_pause(int snum)
+{
+       extern userdom_struct current_user_info;
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_queue_pause(%d)\n", snum));
+
+       /*
+        * Make sure we don't ask for passwords...
+        */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+        * Try to connect to the server...
+        */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+        * Build an IPP_PAUSE_PRINTER request, which requires the following
+        * attributes:
+        *
+        *    attributes-charset
+        *    attributes-natural-language
+        *    printer-uri
+        *    requesting-user-name
+        */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_PAUSE_PRINTER;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+                PRINTERNAME(snum));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, current_user_info.unix_name);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+        ret = 1;
+
+       if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+       {
+         if (response->request.status.status_code >= IPP_OK_CONFLICT)
+               DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+                        ippErrorString(cupsLastError())));
+          else
+               ret = 0;
+
+         ippDelete(response);
+       }
+       else
+         DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+                  ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       return (ret);
+}
+
+
+/*
+ * 'cups_queue_resume()' - Restart a print queue.
+ */
+
+static int
+cups_queue_resume(int snum)
+{
+       extern userdom_struct current_user_info;
+       int             ret;            /* Return value */
+       http_t          *http;          /* HTTP connection to server */
+       ipp_t           *request,       /* IPP Request */
+                       *response;      /* IPP Response */
+       cups_lang_t     *language;      /* Default language */
+       char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+       DEBUG(5,("cups_queue_resume(%d)\n", snum));
+
+       /*
+        * Make sure we don't ask for passwords...
+       */
+
+        cupsSetPasswordCB(cups_passwd_cb);
+
+       /*
+       * Try to connect to the server...
+       */
+
+       if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+       {
+               DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
+                        cupsServer(), strerror(errno)));
+               return (1);
+       }
+
+       /*
+       * Build an IPP_RESUME_PRINTER request, which requires the following
+       * attributes:
+       *
+       *    attributes-charset
+       *    attributes-natural-language
+       *    printer-uri
+       *    requesting-user-name
+       */
+
+       request = ippNew();
+
+       request->request.op.operation_id = IPP_RESUME_PRINTER;
+       request->request.op.request_id   = 1;
+
+       language = cupsLangDefault();
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+                    "attributes-charset", NULL, cupsLangEncoding(language));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+                    "attributes-natural-language", NULL, language->language);
+
+       slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+                PRINTERNAME(snum));
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+                    NULL, current_user_info.unix_name);
+
+       /*
+       * Do the request and get back a response...
+       */
+
+        ret = 1;
+
+       if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+       {
+         if (response->request.status.status_code >= IPP_OK_CONFLICT)
+               DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+                        ippErrorString(cupsLastError())));
+          else
+               ret = 0;
+
+         ippDelete(response);
+       }
+       else
+         DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+                  ippErrorString(cupsLastError())));
+
+       httpClose(http);
+
+       return (ret);
+}
+
+
+#else
+ /* this keeps fussy compilers happy */
+ void print_cups_dummy(void) {}
+#endif /* HAVE_CUPS */
diff --git a/source4/printing/print_generic.c b/source4/printing/print_generic.c
new file mode 100644 (file)
index 0000000..4d77b82
--- /dev/null
@@ -0,0 +1,245 @@
+/* 
+   Unix SMB/CIFS implementation.
+   printing command routines
+   Copyright (C) Andrew Tridgell 1992-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 "printing.h"
+
+
+/*
+ * Generic printing interface definitions...
+ */
+
+static int generic_job_delete(int snum, struct printjob *pjob);
+static int generic_job_pause(int snum, struct printjob *pjob);
+static int generic_job_resume(int snum, struct printjob *pjob);
+static int generic_job_submit(int snum, struct printjob *pjob);
+static int generic_queue_get(int snum, print_queue_struct **q,
+                             print_status_struct *status);
+static int generic_queue_pause(int snum);
+static int generic_queue_resume(int snum);
+
+
+struct printif generic_printif =
+               {
+                 generic_queue_get,
+                 generic_queue_pause,
+                 generic_queue_resume,
+                 generic_job_delete,
+                 generic_job_pause,
+                 generic_job_resume,
+                 generic_job_submit,
+               };
+
+/****************************************************************************
+run a given print command 
+a null terminated list of value/substitute pairs is provided
+for local substitution strings
+****************************************************************************/
+static int print_run_command(int snum,char *command, int *outfd, ...)
+{
+
+       pstring syscmd;
+       char *arg;
+       int ret;
+       va_list ap;
+       va_start(ap, outfd);
+
+       if (!command || !*command) return -1;
+
+       if (!VALID_SNUM(snum)) {
+               DEBUG(0,("Invalid snum %d for command %s\n", snum, command));
+               return -1;
+       }
+
+       pstrcpy(syscmd, command);
+
+       while ((arg = va_arg(ap, char *))) {
+               char *value = va_arg(ap,char *);
+               pstring_sub(syscmd, arg, value);
+       }
+       va_end(ap);
+  
+       pstring_sub(syscmd, "%p", PRINTERNAME(snum));
+       standard_sub_snum(snum,syscmd,sizeof(syscmd));
+
+       ret = smbrun(syscmd,outfd);
+
+       DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
+       return ret;
+}
+
+
+/****************************************************************************
+delete a print job
+****************************************************************************/
+static int generic_job_delete(int snum, struct printjob *pjob)
+{
+       fstring jobstr;
+
+       /* need to delete the spooled entry */
+       slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+       return print_run_command(
+                  snum, 
+                  lp_lprmcommand(snum), NULL,
+                  "%j", jobstr,
+                  "%T", http_timestring(pjob->starttime),
+                  NULL);
+}
+
+/****************************************************************************
+pause a job
+****************************************************************************/
+static int generic_job_pause(int snum, struct printjob *pjob)
+{
+       fstring jobstr;
+       
+       /* need to pause the spooled entry */
+       slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+       return print_run_command(snum, 
+                                lp_lppausecommand(snum), NULL,
+                                "%j", jobstr,
+                                NULL);
+}
+
+/****************************************************************************
+resume a job
+****************************************************************************/
+static int generic_job_resume(int snum, struct printjob *pjob)
+{
+       fstring jobstr;
+       
+       /* need to pause the spooled entry */
+       slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+       return print_run_command(snum, 
+                                lp_lpresumecommand(snum), NULL,
+                                "%j", jobstr,
+                                NULL);
+}
+
+/****************************************************************************
+ Submit a file for printing - called from print_job_end()
+****************************************************************************/
+
+static int generic_job_submit(int snum, struct printjob *pjob)
+{
+       int ret;
+       pstring current_directory;
+       pstring print_directory;
+       char *wd, *p;
+       pstring jobname;
+       fstring job_page_count, job_size;
+
+       /* we print from the directory path to give the best chance of
+           parsing the lpq output */
+       wd = sys_getwd(current_directory);
+       if (!wd)
+               return 0;
+
+       pstrcpy(print_directory, pjob->filename);
+       p = strrchr_m(print_directory,'/');
+       if (!p)
+               return 0;
+       *p++ = 0;
+
+       if (chdir(print_directory) != 0)
+               return 0;
+
+       pstrcpy(jobname, pjob->jobname);
+       pstring_sub(jobname, "'", "_");
+       slprintf(job_page_count, sizeof(job_page_count)-1, "%d", pjob->page_count);
+       slprintf(job_size, sizeof(job_size)-1, "%d", pjob->size);
+
+       /* send it to the system spooler */
+       ret = print_run_command(snum, 
+                       lp_printcommand(snum), NULL,
+                       "%s", p,
+                       "%J", jobname,
+                       "%f", p,
+                       "%z", job_size,
+                       "%c", job_page_count,
+                       NULL);
+
+       chdir(wd);
+
+        return ret;
+}
+
+
+/****************************************************************************
+get the current list of queued jobs
+****************************************************************************/
+static int generic_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+       char **qlines;
+       int fd;
+       int numlines, i, qcount;
+       print_queue_struct *queue = NULL;
+       fstring printer_name;
+              
+       fstrcpy(printer_name, lp_servicename(snum));
+       
+       print_run_command(snum, lp_lpqcommand(snum), &fd, NULL);
+
+       if (fd == -1) {
+               DEBUG(5,("generic_queue_get: Can't read print queue status for printer %s\n",
+                       printer_name ));
+               return 0;
+       }
+       
+       numlines = 0;
+       qlines = fd_lines_load(fd, &numlines);
+       close(fd);
+
+       /* turn the lpq output into a series of job structures */
+       qcount = 0;
+       ZERO_STRUCTP(status);
+       if (numlines)
+               queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1));
+
+       if (queue) {
+               for (i=0; i<numlines; i++) {
+                       /* parse the line */
+                       if (parse_lpq_entry(snum,qlines[i],
+                                           &queue[qcount],status,qcount==0)) {
+                               qcount++;
+                       }
+               }               
+       }
+       file_lines_free(qlines);
+
+        *q = queue;
+       return qcount;
+}
+
+/****************************************************************************
+ pause a queue
+****************************************************************************/
+static int generic_queue_pause(int snum)
+{
+       return print_run_command(snum, lp_queuepausecommand(snum), NULL, NULL);
+}
+
+/****************************************************************************
+ resume a queue
+****************************************************************************/
+static int generic_queue_resume(int snum)
+{
+       return print_run_command(snum, lp_queueresumecommand(snum), NULL, NULL);
+}
diff --git a/source4/printing/print_svid.c b/source4/printing/print_svid.c
new file mode 100644 (file)
index 0000000..07b157b
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 1997-1998 by Norm Jacobs, Colorado Springs, Colorado, USA
+ * Copyright (C) 1997-1998 by Sun Microsystem, Inc.
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+/*
+ * This module implements support for gathering and comparing available
+ * printer information on a SVID or XPG4 compliant system.  It does this
+ * through the use of the SVID/XPG4 command "lpstat(1)".
+ *
+ * The expectations is that execution of the command "lpstat -v" will
+ * generate responses in the form of:
+ *
+ *     device for serial: /dev/term/b
+ *     system for fax: server
+ *     system for color: server (as printer chroma)
+ */
+
+
+#include "includes.h"
+
+#ifdef SYSV
+
+typedef struct printer {
+       char *name;
+       struct printer *next;
+} printer_t;
+static printer_t *printers = NULL;
+
+static void populate_printers(void)
+{
+       char **lines;
+       int i;
+
+       lines = file_lines_pload("/usr/bin/lpstat -v", NULL);
+       if (!lines) return;
+
+       for (i=0;lines[i];i++) {
+               printer_t *ptmp;
+               char *name, *tmp;
+               char *buf = lines[i];
+
+               /* eat "system/device for " */
+               if (((tmp = strchr_m(buf, ' ')) == NULL) ||
+                   ((tmp = strchr_m(++tmp, ' ')) == NULL))
+                       continue;
+
+               /*
+                * In case we're only at the "for ".
+                */
+
+               if(!strncmp("for ",++tmp,4)) {
+                       tmp=strchr_m(tmp, ' ');
+                       tmp++;
+               }
+
+               /* Eat whitespace. */
+
+               while(*tmp == ' ')
+                       ++tmp;
+
+               /*
+                * On HPUX there is an extra line that can be ignored.
+                * d.thibadeau 2001/08/09
+                */
+               if(!strncmp("remote to",tmp,9))
+                       continue;
+
+               name = tmp;
+
+               /* truncate the ": ..." */
+               if ((tmp = strchr_m(name, ':')) != NULL)
+                       *tmp = '\0';
+               
+               /* add it to the cache */
+               if ((ptmp = malloc(sizeof (*ptmp))) != NULL) {
+                       ZERO_STRUCTP(ptmp);
+                       if((ptmp->name = strdup(name)) == NULL)
+                               DEBUG(0,("populate_printers: malloc fail in strdup !\n"));
+                       ptmp->next = printers;
+                       printers = ptmp;
+               } else {
+                       DEBUG(0,("populate_printers: malloc fail for ptmp\n"));
+               }
+       }
+
+       file_lines_free(lines);
+}
+
+
+/*
+ * provide the equivalent of pcap_printer_fn() for SVID/XPG4 conforming
+ * systems.  It was unclear why pcap_printer_fn() was tossing names longer
+ * than 8 characters.  I suspect that its a protocol limit, but amazingly
+ * names longer than 8 characters appear to work with my test
+ * clients (Win95/NT).
+ */
+void sysv_printer_fn(void (*fn)(char *, char *))
+{
+       printer_t *tmp;
+
+       if (printers == NULL)
+               populate_printers();
+       for (tmp = printers; tmp != NULL; tmp = tmp->next)
+               (fn)(tmp->name, "");
+}
+
+
+/*
+ * provide the equivalent of pcap_printername_ok() for SVID/XPG4 conforming
+ * systems.
+ */
+int sysv_printername_ok(const char *name)
+{
+       printer_t *tmp;
+
+       if (printers == NULL)
+               populate_printers();
+       for (tmp = printers; tmp != NULL; tmp = tmp->next)
+               if (strcmp(tmp->name, name) == 0)
+                       return (True);
+       return (False);
+}
+
+#else
+/* this keeps fussy compilers happy */
+ void print_svid_dummy(void);
+ void print_svid_dummy(void) {}
+#endif
diff --git a/source4/printing/printfsp.c b/source4/printing/printfsp.c
new file mode 100644 (file)
index 0000000..5d230f8
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+   Unix SMB/CIFS implementation.
+   printing backend routines for smbd - using files_struct rather
+   than only snum
+   Copyright (C) Andrew Tridgell 1992-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"
+
+/***************************************************************************
+open a print file and setup a fsp for it. This is a wrapper around
+print_job_start().
+***************************************************************************/
+
+files_struct *print_fsp_open(struct tcon_context *conn, char *fname)
+{
+       int jobid;
+       SMB_STRUCT_STAT sbuf;
+       extern struct current_user current_user;
+       files_struct *fsp = file_new(conn);
+       fstring name;
+
+       if(!fsp)
+               return NULL;
+
+       fstrcpy( name, "Remote Downlevel Document");
+       if (fname) {
+               char *p = strrchr(fname, '/');
+               fstrcat(name, " ");
+               if (!p)
+                       p = fname;
+               fstrcat(name, p);
+       }
+
+       jobid = print_job_start(&current_user, SNUM(conn), name, NULL);
+       if (jobid == -1) {
+               file_free(fsp);
+               return NULL;
+       }
+
+       /* Convert to RAP id. */
+       fsp->rap_print_jobid = pjobid_to_rap(SNUM(conn), jobid);
+       if (fsp->rap_print_jobid == 0) {
+               /* We need to delete the entry in the tdb. */
+               pjob_delete(SNUM(conn), jobid);
+               file_free(fsp);
+               return NULL;
+       }
+
+       /* setup a full fsp */
+       fsp->fd = print_job_fd(SNUM(conn),jobid);
+       GetTimeOfDay(&fsp->open_time);
+       fsp->vuid = current_user.vuid;
+       fsp->size = 0;
+       fsp->pos = -1;
+       fsp->can_lock = True;
+       fsp->can_read = False;
+       fsp->can_write = True;
+       fsp->share_mode = 0;
+       fsp->print_file = True;
+       fsp->modified = False;
+       fsp->oplock_type = NO_OPLOCK;
+       fsp->sent_oplock_break = NO_BREAK_SENT;
+       fsp->is_directory = False;
+       fsp->directory_delete_on_close = False;
+       string_set(&fsp->fsp_name,print_job_fname(SNUM(conn),jobid));
+       fsp->wbmpx_ptr = NULL;      
+       fsp->wcp = NULL; 
+       conn->vfs_ops.fstat(fsp,fsp->fd, &sbuf);
+       fsp->mode = sbuf.st_mode;
+       fsp->inode = sbuf.st_ino;
+       fsp->dev = sbuf.st_dev;
+
+       conn->num_files_open++;
+
+       return fsp;
+}
+
+/****************************************************************************
+print a file - called on closing the file
+****************************************************************************/
+void print_fsp_end(files_struct *fsp, BOOL normal_close)
+{
+       uint32 jobid;
+       int snum;
+
+       if (fsp->share_mode == FILE_DELETE_ON_CLOSE) {
+               /*
+                * Truncate the job. print_job_end will take
+                * care of deleting it for us. JRA.
+                */
+               sys_ftruncate(fsp->fd, 0);
+       }
+
+       if (fsp->fsp_name) {
+               string_free(&fsp->fsp_name);
+       }
+
+       if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
+               DEBUG(3,("print_fsp_end: Unable to convert RAP jobid %u to print jobid.\n",
+                       (unsigned int)fsp->rap_print_jobid ));
+               return;
+       }
+
+       print_job_end(SNUM(fsp->conn),jobid, normal_close);
+}
diff --git a/source4/printing/printing.c b/source4/printing/printing.c
new file mode 100644 (file)
index 0000000..26ea52e
--- /dev/null
@@ -0,0 +1,2128 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   printing backend routines
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Jeremy Allison 2002
+   
+   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 "printing.h"
+
+/* Current printer interface */
+static struct printif *current_printif = &generic_printif;
+
+/* 
+   the printing backend revolves around a tdb database that stores the
+   SMB view of the print queue 
+   
+   The key for this database is a jobid - a internally generated number that
+   uniquely identifies a print job
+
+   reading the print queue involves two steps:
+     - possibly running lpq and updating the internal database from that
+     - reading entries from the database
+
+   jobids are assigned when a job starts spooling. 
+*/
+
+/***************************************************************************
+ Nightmare. LANMAN jobid's are 16 bit numbers..... We must map them to 32
+ bit RPC jobids.... JRA.
+***************************************************************************/
+
+static TDB_CONTEXT *rap_tdb;
+static uint16 next_rap_jobid;
+
+uint16 pjobid_to_rap(int snum, uint32 jobid)
+{
+       uint16 rap_jobid;
+       TDB_DATA data, key;
+       char jinfo[8];
+
+       DEBUG(10,("pjobid_to_rap: called.\n"));
+
+       if (!rap_tdb) {
+               /* Create the in-memory tdb. */
+               rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644);
+               if (!rap_tdb)
+                       return 0;
+       }
+
+       SIVAL(&jinfo,0,(int32)snum);
+       SIVAL(&jinfo,4,jobid);
+
+       key.dptr = (char *)&jinfo;
+       key.dsize = sizeof(jinfo);
+       data = tdb_fetch(rap_tdb, key);
+       if (data.dptr && data.dsize == sizeof(uint16)) {
+               memcpy(&rap_jobid, data.dptr, sizeof(uint16));
+               SAFE_FREE(data.dptr);
+               DEBUG(10,("pjobid_to_rap: jobid %u maps to RAP jobid %u\n",
+                               (unsigned int)jobid,
+                               (unsigned int)rap_jobid));
+               return rap_jobid;
+       }
+       SAFE_FREE(data.dptr);
+       /* Not found - create and store mapping. */
+       rap_jobid = ++next_rap_jobid;
+       if (rap_jobid == 0)
+               rap_jobid = ++next_rap_jobid;
+       data.dptr = (char *)&rap_jobid;
+       data.dsize = sizeof(rap_jobid);
+       tdb_store(rap_tdb, key, data, TDB_REPLACE);
+       tdb_store(rap_tdb, data, key, TDB_REPLACE);
+
+       DEBUG(10,("pjobid_to_rap: created jobid %u maps to RAP jobid %u\n",
+                               (unsigned int)jobid,
+                               (unsigned int)rap_jobid));
+       return rap_jobid;
+}
+
+BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid)
+{
+       TDB_DATA data, key;
+
+       DEBUG(10,("rap_to_pjobid called.\n"));
+
+       if (!rap_tdb)
+               return False;
+
+       key.dptr = (char *)&rap_jobid;
+       key.dsize = sizeof(rap_jobid);
+       data = tdb_fetch(rap_tdb, key);
+       if (data.dptr && data.dsize == 8) {
+               *psnum = IVAL(data.dptr,0);
+               *pjobid = IVAL(data.dptr,4);
+               DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n",
+                               (unsigned int)*pjobid,
+                               (unsigned int)rap_jobid));
+               SAFE_FREE(data.dptr);
+               return True;
+       }
+
+       DEBUG(10,("rap_to_pjobid: Failed to lookup RAP jobid %u\n",
+                               (unsigned int)rap_jobid));
+       SAFE_FREE(data.dptr);
+       return False;
+}
+
+static void rap_jobid_delete(int snum, uint32 jobid)
+{
+       TDB_DATA key, data;
+       uint16 rap_jobid;
+       char jinfo[8];
+
+       DEBUG(10,("rap_jobid_delete: called.\n"));
+
+       if (!rap_tdb)
+               return;
+
+       SIVAL(&jinfo,0,(int32)snum);
+       SIVAL(&jinfo,4,jobid);
+
+       key.dptr = (char *)&jinfo;
+       key.dsize = sizeof(jinfo);
+       data = tdb_fetch(rap_tdb, key);
+       if (!data.dptr || (data.dsize != sizeof(uint16))) {
+               DEBUG(10,("rap_jobid_delete: cannot find jobid %u\n",
+                                       (unsigned int)jobid ));
+               SAFE_FREE(data.dptr);
+               return;
+       }
+
+       DEBUG(10,("rap_jobid_delete: deleting jobid %u\n",
+                               (unsigned int)jobid ));
+
+       memcpy(&rap_jobid, data.dptr, sizeof(uint16));
+       SAFE_FREE(data.dptr);
+       data.dptr = (char *)&rap_jobid;
+       data.dsize = sizeof(rap_jobid);
+       tdb_delete(rap_tdb, key);
+       tdb_delete(rap_tdb, data);
+}
+
+static pid_t local_pid;
+
+static int get_queue_status(int, print_status_struct *);
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup before the fork().
+****************************************************************************/
+
+BOOL print_backend_init(void)
+{
+       const char *sversion = "INFO/version";
+       pstring printing_path;
+       int services = lp_numservices();
+       int snum;
+
+       if (local_pid == sys_getpid())
+               return True;
+
+       unlink(lock_path("printing.tdb"));
+       pstrcpy(printing_path,lock_path("printing"));
+       mkdir(printing_path,0755);
+
+       local_pid = sys_getpid();
+
+       /* handle a Samba upgrade */
+
+       for (snum = 0; snum < services; snum++) {
+               struct tdb_print_db *pdb;
+               if (!lp_print_ok(snum))
+                       continue;
+
+               pdb = get_print_db_byname(lp_const_servicename(snum));
+               if (!pdb)
+                       continue;
+               if (tdb_lock_bystring(pdb->tdb, sversion, 0) == -1) {
+                       DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) ));
+                       release_print_db(pdb);
+                       return False;
+               }
+               if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) {
+                       tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL);
+                       tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION);
+               }
+               tdb_unlock_bystring(pdb->tdb, sversion);
+               release_print_db(pdb);
+       }
+
+       close_all_print_db(); /* Don't leave any open. */
+
+       /* select the appropriate printing interface... */
+#ifdef HAVE_CUPS
+       if (strcmp(lp_printcapname(), "cups") == 0)
+               current_printif = &cups_printif;
+#endif /* HAVE_CUPS */
+
+       /* do NT print initialization... */
+       return nt_printing_init();
+}
+
+/****************************************************************************
+ Shut down printing backend. Called once at shutdown to close the tdb.
+****************************************************************************/
+
+void printing_end(void)
+{
+       close_all_print_db(); /* Don't leave any open. */
+}
+
+/****************************************************************************
+ Useful function to generate a tdb key.
+****************************************************************************/
+
+static TDB_DATA print_key(uint32 jobid)
+{
+       static uint32 j;
+       TDB_DATA ret;
+
+       j = jobid;
+       ret.dptr = (void *)&j;
+       ret.dsize = sizeof(j);
+       return ret;
+}
+
+/***********************************************************************
+ unpack a pjob from a tdb buffer 
+***********************************************************************/
+int unpack_pjob( char* buf, int buflen, struct printjob *pjob )
+{
+       int     len = 0;
+       int     used;
+       
+       if ( !buf || !pjob )
+               return -1;
+               
+       len += tdb_unpack(buf+len, buflen-len, "dddddddddffff",
+                               &pjob->pid,
+                               &pjob->sysjob,
+                               &pjob->fd,
+                               &pjob->starttime,
+                               &pjob->status,
+                               &pjob->size,
+                               &pjob->page_count,
+                               &pjob->spooled,
+                               &pjob->smbjob,
+                               pjob->filename,
+                               pjob->jobname,
+                               pjob->user,
+                               pjob->queuename);
+                               
+       if ( len == -1 )
+               return -1;
+               
+       if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 )
+               return -1;
+       
+       len += used;
+       
+       return len;
+
+}
+
+/****************************************************************************
+ Useful function to find a print job in the database.
+****************************************************************************/
+
+static struct printjob *print_job_find(int snum, uint32 jobid)
+{
+       static struct printjob  pjob;
+       TDB_DATA                ret;
+       struct tdb_print_db     *pdb = get_print_db_byname(lp_const_servicename(snum));
+       
+
+       if (!pdb)
+               return NULL;
+
+       ret = tdb_fetch(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+
+       if (!ret.dptr)
+               return NULL;
+       
+       if ( pjob.nt_devmode )
+               free_nt_devicemode( &pjob.nt_devmode );
+               
+       ZERO_STRUCT( pjob );
+       
+       if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) {
+               SAFE_FREE(ret.dptr);
+               return NULL;
+       }
+       
+       SAFE_FREE(ret.dptr);    
+       return &pjob;
+}
+
+/* Convert a unix jobid to a smb jobid */
+
+static uint32 sysjob_to_jobid_value;
+
+static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
+                              TDB_DATA data, void *state)
+{
+       struct printjob *pjob;
+       int *sysjob = (int *)state;
+
+       if (!data.dptr || data.dsize == 0)
+               return 0;
+
+       pjob = (struct printjob *)data.dptr;
+       if (key.dsize != sizeof(uint32))
+               return 0;
+
+       if (*sysjob == pjob->sysjob) {
+               uint32 *jobid = (uint32 *)key.dptr;
+
+               sysjob_to_jobid_value = *jobid;
+               return 1;
+       }
+
+       return 0;
+}
+
+/****************************************************************************
+ This is a *horribly expensive call as we have to iterate through all the
+ current printer tdb's. Don't do this often ! JRA.
+****************************************************************************/
+
+uint32 sysjob_to_jobid(int unix_jobid)
+{
+       int services = lp_numservices();
+       int snum;
+
+       sysjob_to_jobid_value = (uint32)-1;
+
+       for (snum = 0; snum < services; snum++) {
+               struct tdb_print_db *pdb;
+               if (!lp_print_ok(snum))
+                       continue;
+               pdb = get_print_db_byname(lp_const_servicename(snum));
+               if (pdb)
+                       tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid);
+               release_print_db(pdb);
+               if (sysjob_to_jobid_value != (uint32)-1)
+                       return sysjob_to_jobid_value;
+       }
+       return (uint32)-1;
+}
+
+/****************************************************************************
+ Send notifications based on what has changed after a pjob_store.
+****************************************************************************/
+
+static struct {
+       uint32 lpq_status;
+       uint32 spoolss_status;
+} lpq_to_spoolss_status_map[] = {
+       { LPQ_QUEUED, JOB_STATUS_QUEUED },
+       { LPQ_PAUSED, JOB_STATUS_PAUSED },
+       { LPQ_SPOOLING, JOB_STATUS_SPOOLING },
+       { LPQ_PRINTING, JOB_STATUS_PRINTING },
+       { LPQ_DELETING, JOB_STATUS_DELETING },
+       { LPQ_OFFLINE, JOB_STATUS_OFFLINE },
+       { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT },
+       { LPQ_PRINTED, JOB_STATUS_PRINTED },
+       { LPQ_DELETED, JOB_STATUS_DELETED },
+       { LPQ_BLOCKED, JOB_STATUS_BLOCKED },
+       { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
+       { -1, 0 }
+};
+
+/* Convert a lpq status value stored in printing.tdb into the
+   appropriate win32 API constant. */
+
+static uint32 map_to_spoolss_status(uint32 lpq_status)
+{
+       int i = 0;
+
+       while (lpq_to_spoolss_status_map[i].lpq_status != -1) {
+               if (lpq_to_spoolss_status_map[i].lpq_status == lpq_status)
+                       return lpq_to_spoolss_status_map[i].spoolss_status;
+               i++;
+       }
+
+       return 0;
+}
+
+static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data,
+                             struct printjob *new_data)
+{
+       BOOL new_job = False;
+
+       if (!old_data)
+               new_job = True;
+
+       /* Notify the job name first */
+
+       if (new_job || !strequal(old_data->jobname, new_data->jobname))
+               notify_job_name(snum, jobid, new_data->jobname);
+
+       /* Job attributes that can't be changed.  We only send
+          notification for these on a new job. */
+
+       if (new_job) {
+               notify_job_submitted(snum, jobid, new_data->starttime);
+               notify_job_username(snum, jobid, new_data->user);
+       }
+
+       /* Job attributes of a new job or attributes that can be
+          modified. */
+
+       if (new_job || old_data->status != new_data->status)
+               notify_job_status(snum, jobid, map_to_spoolss_status(new_data->status));
+
+       if (new_job || old_data->size != new_data->size)
+               notify_job_total_bytes(snum, jobid, new_data->size);
+
+       if (new_job || old_data->page_count != new_data->page_count)
+               notify_job_total_pages(snum, jobid, new_data->page_count);
+}
+
+/****************************************************************************
+ Store a job structure back to the database.
+****************************************************************************/
+
+static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob)
+{
+       TDB_DATA                old_data, new_data;
+       BOOL                    ret = False;
+       struct tdb_print_db     *pdb = get_print_db_byname(lp_const_servicename(snum));
+       char                    *buf = NULL;
+       int                     len, newlen, buflen;
+       
+
+       if (!pdb)
+               return False;
+
+       /* Get old data */
+
+       old_data = tdb_fetch(pdb->tdb, print_key(jobid));
+
+       /* Doh!  Now we have to pack/unpack data since the NT_DEVICEMODE was added */
+
+       newlen = 0;
+       
+       do {
+               len = 0;
+               buflen = newlen;
+               len += tdb_pack(buf+len, buflen-len, "dddddddddffff",
+                               pjob->pid,
+                               pjob->sysjob,
+                               pjob->fd,
+                               pjob->starttime,
+                               pjob->status,
+                               pjob->size,
+                               pjob->page_count,
+                               pjob->spooled,
+                               pjob->smbjob,
+                               pjob->filename,
+                               pjob->jobname,
+                               pjob->user,
+                               pjob->queuename);
+
+               len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len);
+       
+               if (buflen != len) {
+                       char *tb;
+
+                       tb = (char *)Realloc(buf, len);
+                       if (!tb) {
+                               DEBUG(0,("pjob_store: failed to enlarge buffer!\n"));
+                               goto done;
+                       }
+                       else 
+                               buf = tb;
+                       newlen = len;
+               }
+       } while ( buflen != len );
+               
+       
+       /* Store new data */
+
+       new_data.dptr = buf;
+       new_data.dsize = len;
+       ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0);
+
+       release_print_db(pdb);
+
+       /* Send notify updates for what has changed */
+
+       if ( ret && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob)) )
+               pjob_store_notify( snum, jobid, (struct printjob *)old_data.dptr, pjob );
+
+done:
+       SAFE_FREE( old_data.dptr );
+       SAFE_FREE( buf );
+
+       return ret;
+}
+
+/****************************************************************************
+ Remove a job structure from the database.
+****************************************************************************/
+
+void pjob_delete(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       uint32 job_status = 0;
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+       if (!pdb)
+               return;
+
+       if (!pjob) {
+               DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n",
+                                       (unsigned int)jobid));
+               release_print_db(pdb);
+               return;
+       }
+
+       /* Send a notification that a job has been deleted */
+
+       job_status = map_to_spoolss_status(pjob->status);
+
+       /* We must cycle through JOB_STATUS_DELETING and
+           JOB_STATUS_DELETED for the port monitor to delete the job
+           properly. */
+       
+       job_status |= JOB_STATUS_DELETING;
+       notify_job_status(snum, jobid, job_status);
+       
+       job_status |= JOB_STATUS_DELETED;
+       notify_job_status(snum, jobid, job_status);
+
+       /* Remove from printing.tdb */
+
+       tdb_delete(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+       rap_jobid_delete(snum, jobid);
+}
+
+/****************************************************************************
+ Parse a file name from the system spooler to generate a jobid.
+****************************************************************************/
+
+static uint32 print_parse_jobid(char *fname)
+{
+       int jobid;
+
+       if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0)
+               return (uint32)-1;
+       fname += strlen(PRINT_SPOOL_PREFIX);
+
+       jobid = atoi(fname);
+       if (jobid <= 0)
+               return (uint32)-1;
+
+       return (uint32)jobid;
+}
+
+/****************************************************************************
+ List a unix job in the print database.
+****************************************************************************/
+
+static void print_unix_job(int snum, print_queue_struct *q, uint32 jobid)
+{
+       struct printjob pj, *old_pj;
+
+       if (jobid == (uint32)-1)
+               jobid = q->job + UNIX_JOB_START;
+
+       /* Preserve the timestamp on an existing unix print job */
+
+       old_pj = print_job_find(snum, jobid);
+
+       ZERO_STRUCT(pj);
+
+       pj.pid = (pid_t)-1;
+       pj.sysjob = q->job;
+       pj.fd = -1;
+       pj.starttime = old_pj ? old_pj->starttime : q->time;
+       pj.status = q->status;
+       pj.size = q->size;
+       pj.spooled = True;
+       pj.smbjob = (old_pj != NULL ? True : False);
+       fstrcpy(pj.filename, old_pj ? old_pj->filename : "");
+       if (jobid < UNIX_JOB_START)
+               fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document");
+       else
+               fstrcpy(pj.jobname, old_pj ? old_pj->jobname : q->fs_file);
+       fstrcpy(pj.user, old_pj ? old_pj->user : q->fs_user);
+       fstrcpy(pj.queuename, old_pj ? old_pj->queuename : lp_const_servicename(snum));
+
+       pjob_store(snum, jobid, &pj);
+}
+
+
+struct traverse_struct {
+       print_queue_struct *queue;
+       int qcount, snum, maxcount, total_jobs;
+       time_t lpq_time;
+};
+
+/****************************************************************************
+ Utility fn to delete any jobs that are no longer active.
+****************************************************************************/
+
+static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+       struct traverse_struct *ts = (struct traverse_struct *)state;
+       struct printjob pjob;
+       uint32 jobid;
+       int i;
+
+       if (  key.dsize != sizeof(jobid) )
+               return 0;
+               
+       memcpy(&jobid, key.dptr, sizeof(jobid));
+       if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+               return 0;
+       free_nt_devicemode( &pjob.nt_devmode );
+
+
+       if (ts->snum != lp_servicenumber(pjob.queuename)) {
+               /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */
+               return 0;
+       }
+
+       if (!pjob.smbjob) {
+               /* remove a unix job if it isn't in the system queue any more */
+
+               for (i=0;i<ts->qcount;i++) {
+                       uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
+                       if (jobid == u_jobid)
+                               break;
+               }
+               if (i == ts->qcount)
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
+               return 0;
+       }
+
+       /* maybe it hasn't been spooled yet */
+       if (!pjob.spooled) {
+               /* if a job is not spooled and the process doesn't
+                   exist then kill it. This cleans up after smbd
+                   deaths */
+               if (!process_exists(pjob.pid))
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
+               return 0;
+       }
+
+       for (i=0;i<ts->qcount;i++) {
+               uint32 curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
+               if (jobid == curr_jobid)
+                       break;
+       }
+       
+       /* The job isn't in the system queue - we have to assume it has
+          completed, so delete the database entry. */
+
+       if (i == ts->qcount) {
+
+               /* A race can occur between the time a job is spooled and
+                  when it appears in the lpq output.  This happens when
+                  the job is added to printing.tdb when another smbd
+                  running print_queue_update() has completed a lpq and
+                  is currently traversing the printing tdb and deleting jobs.
+                  Don't delete the job if it was submitted after the lpq_time. */
+
+               if (pjob.starttime < ts->lpq_time)
+                       pjob_delete(ts->snum, jobid);
+               else
+                       ts->total_jobs++;
+       }
+       else
+               ts->total_jobs++;
+
+       return 0;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static void print_cache_flush(int snum)
+{
+       fstring key;
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+       if (!pdb)
+               return;
+       slprintf(key, sizeof(key)-1, "CACHE/%s", printername);
+       tdb_store_int32(pdb->tdb, key, -1);
+       release_print_db(pdb);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(fstring printer_name)
+{
+       fstring keystr;
+       TDB_DATA data, key;
+       pid_t updating_pid;
+       struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+       if (!pdb)
+               return (pid_t)-1;
+       slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+
+       data = tdb_fetch(pdb->tdb, key);
+       release_print_db(pdb);
+       if (!data.dptr || data.dsize != sizeof(pid_t)) {
+               SAFE_FREE(data.dptr);
+               return (pid_t)-1;
+       }
+
+       memcpy(&updating_pid, data.dptr, sizeof(pid_t));
+       SAFE_FREE(data.dptr);
+
+       if (process_exists(updating_pid))
+               return updating_pid;
+
+       return (pid_t)-1;
+}
+
+/****************************************************************************
+ Set the fact that we're doing the update, or have finished doing the update
+ in the tdb.
+****************************************************************************/
+
+static void set_updating_pid(const fstring printer_name, BOOL delete)
+{
+       fstring keystr;
+       TDB_DATA key;
+       TDB_DATA data;
+       pid_t updating_pid = sys_getpid();
+       struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+       if (!pdb)
+               return;
+
+       slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+
+       if (delete) {
+               tdb_delete(pdb->tdb, key);
+               release_print_db(pdb);
+               return;
+       }
+       
+       data.dptr = (void *)&updating_pid;
+       data.dsize = sizeof(pid_t);
+
+       tdb_store(pdb->tdb, key, data, TDB_REPLACE);    
+       release_print_db(pdb);
+}
+
+/****************************************************************************
+ Update the internal database from the system print queue for a queue.
+****************************************************************************/
+
+static void print_queue_update(int snum)
+{
+       int i, qcount;
+       print_queue_struct *queue = NULL;
+       print_status_struct status;
+       print_status_struct old_status;
+       struct printjob *pjob;
+       struct traverse_struct tstruct;
+       fstring keystr, printer_name, cachestr;
+       TDB_DATA data, key;
+       struct tdb_print_db *pdb;
+
+       fstrcpy(printer_name, lp_const_servicename(snum));
+       pdb = get_print_db_byname(printer_name);
+       if (!pdb)
+               return;
+
+       /*
+        * Check to see if someone else is doing this update.
+        * This is essentially a mutex on the update.
+        */
+
+       if (get_updating_pid(printer_name) != -1) {
+               release_print_db(pdb);
+               return;
+       }
+
+       /* Lock the queue for the database update */
+
+       slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name);
+       /* Only wait 10 seconds for this. */
+       if (tdb_lock_bystring(pdb->tdb, keystr, 10) == -1) {
+               DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name));
+               release_print_db(pdb);
+               return;
+       }
+
+       /*
+        * Ensure that no one else got in here.
+        * If the updating pid is still -1 then we are
+        * the winner.
+        */
+
+       if (get_updating_pid(printer_name) != -1) {
+               /*
+                * Someone else is doing the update, exit.
+                */
+               tdb_unlock_bystring(pdb->tdb, keystr);
+               release_print_db(pdb);
+               return;
+       }
+
+       /*
+        * We're going to do the update ourselves.
+        */
+
+       /* Tell others we're doing the update. */
+       set_updating_pid(printer_name, False);
+
+       /*
+        * Allow others to enter and notice we're doing
+        * the update.
+        */
+
+       tdb_unlock_bystring(pdb->tdb, keystr);
+
+       /*
+        * Update the cache time FIRST ! Stops others even
+        * attempting to get the lock and doing this
+        * if the lpq takes a long time.
+        */
+
+       slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name);
+       tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL));
+
+        /* get the current queue using the appropriate interface */
+       ZERO_STRUCT(status);
+
+       qcount = (*(current_printif->queue_get))(snum, &queue, &status);
+
+       DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ?
+               "s" : "", printer_name));
+
+       /*
+         any job in the internal database that is marked as spooled
+         and doesn't exist in the system queue is considered finished
+         and removed from the database
+
+         any job in the system database but not in the internal database 
+         is added as a unix job
+
+         fill in any system job numbers as we go
+       */
+       for (i=0; i<qcount; i++) {
+               uint32 jobid = print_parse_jobid(queue[i].fs_file);
+
+               if (jobid == (uint32)-1) {
+                       /* assume its a unix print job */
+                       print_unix_job(snum, &queue[i], jobid);
+                       continue;
+               }
+
+               /* we have an active SMB print job - update its status */
+               pjob = print_job_find(snum, jobid);
+               if (!pjob) {
+                       /* err, somethings wrong. Probably smbd was restarted
+                          with jobs in the queue. All we can do is treat them
+                          like unix jobs. Pity. */
+                       print_unix_job(snum, &queue[i], jobid);
+                       continue;
+               }
+
+               pjob->sysjob = queue[i].job;
+               pjob->status = queue[i].status;
+
+               pjob_store(snum, jobid, pjob);
+       }
+
+       /* now delete any queued entries that don't appear in the
+           system queue */
+       tstruct.queue = queue;
+       tstruct.qcount = qcount;
+       tstruct.snum = snum;
+       tstruct.total_jobs = 0;
+       tstruct.lpq_time = time(NULL);
+
+       tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct);
+
+       SAFE_FREE(tstruct.queue);
+
+       DEBUG(10,("print_queue_update: printer %s INFO/total_jobs = %d\n",
+                               printer_name, tstruct.total_jobs ));
+
+       tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs);
+
+       get_queue_status(snum, &old_status);
+       if (old_status.qcount != qcount)
+               DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n",
+                                       old_status.qcount, qcount, printer_name ));
+
+       /* store the new queue status structure */
+       slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+
+       status.qcount = qcount;
+       data.dptr = (void *)&status;
+       data.dsize = sizeof(status);
+       tdb_store(pdb->tdb, key, data, TDB_REPLACE);    
+
+       /*
+        * Update the cache time again. We want to do this call
+        * as little as possible...
+        */
+
+       slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name);
+       tdb_store_int32(pdb->tdb, keystr, (int32)time(NULL));
+
+       /* Delete our pid from the db. */
+       set_updating_pid(printer_name, True);
+       release_print_db(pdb);
+}
+
+/****************************************************************************
+ Create/Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's. 
+****************************************************************************/
+
+BOOL print_notify_register_pid(int snum)
+{
+       TDB_DATA data;
+       struct tdb_print_db *pdb = NULL;
+       TDB_CONTEXT *tdb = NULL;
+       const char *printername;
+       uint32 mypid = (uint32)sys_getpid();
+       BOOL ret = False;
+       size_t i;
+
+       /* if (snum == -1), then the change notify request was
+          on a print server handle and we need to register on
+          all print queus */
+          
+       if (snum == -1) 
+       {
+               int num_services = lp_numservices();
+               int idx;
+               
+               for ( idx=0; idx<num_services; idx++ ) {
+                       if (lp_snum_ok(idx) && lp_print_ok(idx) )
+                               print_notify_register_pid(idx);
+               }
+               
+               return True;
+       }
+       else /* register for a specific printer */
+       {
+               printername = lp_const_servicename(snum);
+               pdb = get_print_db_byname(printername);
+               if (!pdb)
+                       return False;
+               tdb = pdb->tdb;
+       }
+
+       if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+               DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n",
+                                       printername));
+               if (pdb)
+                       release_print_db(pdb);
+               return False;
+       }
+
+       data = get_printer_notify_pid_list( tdb, printername, True );
+
+       /* Add ourselves and increase the refcount. */
+
+       for (i = 0; i < data.dsize; i += 8) {
+               if (IVAL(data.dptr,i) == mypid) {
+                       uint32 new_refcount = IVAL(data.dptr, i+4) + 1;
+                       SIVAL(data.dptr, i+4, new_refcount);
+                       break;
+               }
+       }
+
+       if (i == data.dsize) {
+               /* We weren't in the list. Realloc. */
+               data.dptr = Realloc(data.dptr, data.dsize + 8);
+               if (!data.dptr) {
+                       DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n",
+                                               printername));
+                       goto done;
+               }
+               data.dsize += 8;
+               SIVAL(data.dptr,data.dsize - 8,mypid);
+               SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */
+       }
+
+       /* Store back the record. */
+       if (tdb_store_by_string(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+               DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+               goto done;
+       }
+
+       ret = True;
+
+ done:
+
+       tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+       if (pdb)
+               release_print_db(pdb);
+       SAFE_FREE(data.dptr);
+       return ret;
+}
+
+/****************************************************************************
+ Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's. 
+****************************************************************************/
+
+BOOL print_notify_deregister_pid(int snum)
+{
+       TDB_DATA data;
+       struct tdb_print_db *pdb = NULL;
+       TDB_CONTEXT *tdb = NULL;
+       const char *printername;
+       uint32 mypid = (uint32)sys_getpid();
+       size_t i;
+       BOOL ret = False;
+
+       /* if ( snum == -1 ), we are deregister a print server handle
+          which means to deregister on all print queues */
+          
+       if (snum == -1) 
+       {
+               int num_services = lp_numservices();
+               int idx;
+               
+               for ( idx=0; idx<num_services; idx++ ) {
+                       if ( lp_snum_ok(idx) && lp_print_ok(idx) )
+                               print_notify_deregister_pid(idx);
+               }
+               
+               return True;
+       }
+       else /* deregister a specific printer */
+       {
+               printername = lp_const_servicename(snum);
+               pdb = get_print_db_byname(printername);
+               if (!pdb)
+                       return False;
+               tdb = pdb->tdb;
+       }
+
+       if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+               DEBUG(0,("print_notify_register_pid: Failed to lock \
+printer %s database\n", printername));
+               if (pdb)
+                       release_print_db(pdb);
+               return False;
+       }
+
+       data = get_printer_notify_pid_list( tdb, printername, True );
+
+       /* Reduce refcount. Remove ourselves if zero. */
+
+       for (i = 0; i < data.dsize; ) {
+               if (IVAL(data.dptr,i) == mypid) {
+                       uint32 refcount = IVAL(data.dptr, i+4);
+
+                       refcount--;
+
+                       if (refcount == 0) {
+                               if (data.dsize - i > 8)
+                                       memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+                               data.dsize -= 8;
+                               continue;
+                       }
+                       SIVAL(data.dptr, i+4, refcount);
+               }
+
+               i += 8;
+       }
+
+       if (data.dsize == 0)
+               SAFE_FREE(data.dptr);
+
+       /* Store back the record. */
+       if (tdb_store_by_string(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+               DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+               goto done;
+       }
+
+       ret = True;
+
+  done:
+
+       tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+       if (pdb)
+               release_print_db(pdb);
+       SAFE_FREE(data.dptr);
+       return ret;
+}
+
+/****************************************************************************
+ Check if a jobid is valid. It is valid if it exists in the database.
+****************************************************************************/
+
+BOOL print_job_exists(int snum, uint32 jobid)
+{
+       struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+       BOOL ret;
+
+       if (!pdb)
+               return False;
+       ret = tdb_exists(pdb->tdb, print_key(jobid));
+       release_print_db(pdb);
+       return ret;
+}
+
+/****************************************************************************
+ Give the fd used for a jobid.
+****************************************************************************/
+
+int print_job_fd(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob)
+               return -1;
+       /* don't allow another process to get this info - it is meaningless */
+       if (pjob->pid != local_pid)
+               return -1;
+       return pjob->fd;
+}
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+char *print_job_fname(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob || pjob->spooled || pjob->pid != local_pid)
+               return NULL;
+       return pjob->filename;
+}
+
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+NT_DEVICEMODE *print_job_devmode(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       
+       if ( !pjob )
+               return NULL;
+               
+       return pjob->nt_devmode;
+}
+
+/****************************************************************************
+ Set the place in the queue for a job.
+****************************************************************************/
+
+BOOL print_job_set_place(int snum, uint32 jobid, int place)
+{
+       DEBUG(2,("print_job_set_place not implemented yet\n"));
+       return False;
+}
+
+/****************************************************************************
+ Set the name of a job. Only possible for owner.
+****************************************************************************/
+
+BOOL print_job_set_name(int snum, uint32 jobid, char *name)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob || pjob->pid != local_pid)
+               return False;
+
+       fstrcpy(pjob->jobname, name);
+       return pjob_store(snum, jobid, pjob);
+}
+
+/****************************************************************************
+ Delete a print job - don't update queue.
+****************************************************************************/
+
+static BOOL print_job_delete1(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int result = 0;
+
+       if (!pjob)
+               return False;
+
+       /*
+        * If already deleting just return.
+        */
+
+       if (pjob->status == LPQ_DELETING)
+               return True;
+
+       /* Hrm - we need to be able to cope with deleting a job before it
+          has reached the spooler. */
+
+       if (pjob->sysjob == -1) {
+               DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid));
+       }
+
+       /* Set the tdb entry to be deleting. */
+
+       pjob->status = LPQ_DELETING;
+       pjob_store(snum, jobid, pjob);
+
+       if (pjob->spooled && pjob->sysjob != -1)
+               result = (*(current_printif->job_delete))(snum, pjob);
+
+       /* Delete the tdb entry if the delete suceeded or the job hasn't
+          been spooled. */
+
+       if (result == 0)
+               pjob_delete(snum, jobid);
+
+       return (result == 0);
+}
+
+/****************************************************************************
+ Return true if the current user owns the print job.
+****************************************************************************/
+
+static BOOL is_owner(struct current_user *user, int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       user_struct *vuser;
+
+       if (!pjob || !user)
+               return False;
+
+       if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+               return strequal(pjob->user, vuser->user.smb_name);
+       } else {
+               return strequal(pjob->user, uidtoname(user->uid));
+       }
+}
+
+/****************************************************************************
+ Delete a print job.
+****************************************************************************/
+
+BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+       BOOL    owner, deleted;
+       char    *fname;
+
+       *errcode = WERR_OK;
+               
+       owner = is_owner(user, snum, jobid);
+       
+       /* Check access against security descriptor or whether the user
+          owns their job. */
+
+       if (!owner && 
+           !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+               DEBUG(3, ("delete denied by security descriptor\n"));
+               *errcode = WERR_ACCESS_DENIED;
+
+               /* BEGIN_ADMIN_LOG */
+               sys_adminlog( LOG_ERR, 
+                             "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+                             uidtoname(user->uid), PRINTERNAME(snum) );
+               /* END_ADMIN_LOG */
+
+               return False;
+       }
+
+       /* 
+        * get the spooled filename of the print job
+        * if this works, then the file has not been spooled
+        * to the underlying print system.  Just delete the 
+        * spool file & return.
+        */
+        
+       if ( (fname = print_job_fname( snum, jobid )) != NULL )
+       {
+               /* remove the spool file */
+               DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname ));
+               if ( unlink( fname ) == -1 ) {
+                       *errcode = map_werror_from_unix(errno);
+                       return False;
+               }
+               
+               return True;
+       }
+       
+       if (!print_job_delete1(snum, jobid)) {
+               *errcode = WERR_ACCESS_DENIED;
+               return False;
+       }
+
+       /* force update the database and say the delete failed if the
+           job still exists */
+
+       print_queue_update(snum);
+       
+       deleted = !print_job_exists(snum, jobid);
+       if ( !deleted )
+               *errcode = WERR_ACCESS_DENIED;
+
+       return deleted;
+}
+
+/****************************************************************************
+ Pause a job.
+****************************************************************************/
+
+BOOL print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret = -1;
+       
+       if (!pjob || !user) 
+               return False;
+
+       if (!pjob->spooled || pjob->sysjob == -1) 
+               return False;
+
+       if (!is_owner(user, snum, jobid) &&
+           !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+               DEBUG(3, ("pause denied by security descriptor\n"));
+
+               /* BEGIN_ADMIN_LOG */
+               sys_adminlog( LOG_ERR, 
+                       "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+                               uidtoname(user->uid), PRINTERNAME(snum) );
+               /* END_ADMIN_LOG */
+
+               *errcode = WERR_ACCESS_DENIED;
+               return False;
+       }
+
+       /* need to pause the spooled entry */
+       ret = (*(current_printif->job_pause))(snum, pjob);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
+
+       /* force update the database */
+       print_cache_flush(snum);
+
+       /* Send a printer notify message */
+
+       notify_job_status(snum, jobid, JOB_STATUS_PAUSED);
+
+       /* how do we tell if this succeeded? */
+
+       return True;
+}
+
+/****************************************************************************
+ Resume a job.
+****************************************************************************/
+
+BOOL print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret;
+       
+       if (!pjob || !user)
+               return False;
+
+       if (!pjob->spooled || pjob->sysjob == -1)
+               return False;
+
+       if (!is_owner(user, snum, jobid) &&
+           !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+               DEBUG(3, ("resume denied by security descriptor\n"));
+               *errcode = WERR_ACCESS_DENIED;
+
+               /* BEGIN_ADMIN_LOG */
+               sys_adminlog( LOG_ERR, 
+                        "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+                       uidtoname(user->uid), PRINTERNAME(snum) );
+               /* END_ADMIN_LOG */
+               return False;
+       }
+
+       ret = (*(current_printif->job_resume))(snum, pjob);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
+
+       /* force update the database */
+       print_cache_flush(snum);
+
+       /* Send a printer notify message */
+
+       notify_job_status(snum, jobid, JOB_STATUS_QUEUED);
+
+       return True;
+}
+
+/****************************************************************************
+ Write to a print file.
+****************************************************************************/
+
+int print_job_write(int snum, uint32 jobid, const char *buf, int size)
+{
+       int return_code;
+       struct printjob *pjob = print_job_find(snum, jobid);
+
+       if (!pjob)
+               return -1;
+       /* don't allow another process to get this info - it is meaningless */
+       if (pjob->pid != local_pid)
+               return -1;
+
+       return_code = write(pjob->fd, buf, size);
+       if (return_code>0) {
+               pjob->size += size;
+               pjob_store(snum, jobid, pjob);
+       }
+       return return_code;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static BOOL print_cache_expired(int snum)
+{
+       fstring key;
+       time_t last_qscan_time, time_now = time(NULL);
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+       if (!pdb)
+               return False;
+
+       slprintf(key, sizeof(key), "CACHE/%s", printername);
+       last_qscan_time = (time_t)tdb_fetch_int32(pdb->tdb, key);
+
+       /*
+        * Invalidate the queue for 3 reasons.
+        * (1). last queue scan time == -1.
+        * (2). Current time - last queue scan time > allowed cache time.
+        * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default).
+        * This last test picks up machines for which the clock has been moved
+        * forward, an lpq scan done and then the clock moved back. Otherwise
+        * that last lpq scan would stay around for a loooong loooong time... :-). JRA.
+        */
+
+       if (last_qscan_time == ((time_t)-1) || (time_now - last_qscan_time) >= lp_lpqcachetime() ||
+                       last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) {
+               DEBUG(3, ("print cache expired for queue %s \
+(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername,
+                       (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() ));
+               release_print_db(pdb);
+               return True;
+       }
+       release_print_db(pdb);
+       return False;
+}
+
+/****************************************************************************
+ Get the queue status - do not update if db is out of date.
+****************************************************************************/
+
+static int get_queue_status(int snum, print_status_struct *status)
+{
+       fstring keystr;
+       TDB_DATA data, key;
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+       int len;
+
+       if (!pdb)
+               return 0;
+
+       if (status) {
+               ZERO_STRUCTP(status);
+               slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+               key.dptr = keystr;
+               key.dsize = strlen(keystr);
+               data = tdb_fetch(pdb->tdb, key);
+               if (data.dptr) {
+                       if (data.dsize == sizeof(print_status_struct))
+                               memcpy(status, data.dptr, sizeof(print_status_struct));
+                       SAFE_FREE(data.dptr);
+               }
+       }
+       len = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs");
+       release_print_db(pdb);
+       return (len == -1 ? 0 : len);
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+       print_status_struct status;
+       int len;
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+       /* also fetch the queue status */
+       memset(&status, 0, sizeof(status));
+       len = get_queue_status(snum, &status);
+
+       if (pstatus)
+               *pstatus = status;
+
+       return len;
+}
+
+/***************************************************************************
+ Allocate a jobid. Hold the lock for as short a time as possible.
+***************************************************************************/
+
+static BOOL allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *printername, uint32 *pjobid)
+{
+       int i;
+       uint32 jobid;
+
+       *pjobid = (uint32)-1;
+
+       for (i = 0; i < 3; i++) {
+               /* Lock the database - only wait 20 seconds. */
+               if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob", 20) == -1) {
+                       DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", printername ));
+                       return False;
+               }
+
+               if (!tdb_fetch_uint32(pdb->tdb, "INFO/nextjob", &jobid)) {
+                       if (tdb_error(pdb->tdb) != TDB_ERR_NOEXIST) {
+                               DEBUG(0, ("allocate_print_jobid: failed to fetch INFO/nextjob for print queue %s\n",
+                                               printername ));
+                               return False;
+                       }
+                       jobid = 0;
+               }
+
+               jobid = NEXT_JOBID(jobid);
+
+               if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) {
+                       DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n"));
+                       tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+                       return False;
+               }
+
+               /* We've finished with the INFO/nextjob lock. */
+               tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+                               
+               if (!print_job_exists(snum, jobid))
+                       break;
+       }
+
+       if (i > 2) {
+               DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n",
+                               printername ));
+               /* Probably full... */
+               errno = ENOSPC;
+               return False;
+       }
+
+       /* Store a dummy placeholder. */
+       {
+               TDB_DATA dum;
+               dum.dptr = NULL;
+               dum.dsize = 0;
+               if (tdb_store(pdb->tdb, print_key(jobid), dum, TDB_INSERT) == -1) {
+                       DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n",
+                               jobid ));
+                       return False;
+               }
+       }
+
+       *pjobid = jobid;
+       return True;
+}
+
+/***************************************************************************
+ Start spooling a job - return the jobid.
+***************************************************************************/
+
+uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode )
+{
+       uint32 jobid;
+       char *path;
+       struct printjob pjob;
+       user_struct *vuser;
+       const char *printername = lp_const_servicename(snum);
+       struct tdb_print_db *pdb = get_print_db_byname(printername);
+       int njobs;
+
+       errno = 0;
+
+       if (!pdb)
+               return (uint32)-1;
+
+       if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
+               DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
+               release_print_db(pdb);
+               return (uint32)-1;
+       }
+
+       if (!print_time_access_check(snum)) {
+               DEBUG(3, ("print_job_start: job start denied by time check\n"));
+               release_print_db(pdb);
+               return (uint32)-1;
+       }
+
+       path = lp_pathname(snum);
+
+       /* see if we have sufficient disk space */
+       if (lp_minprintspace(snum)) {
+               SMB_BIG_UINT dspace, dsize;
+               if (sys_fsusage(path, &dspace, &dsize) == 0 &&
+                   dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) {
+                       DEBUG(3, ("print_job_start: disk space check failed.\n"));
+                       release_print_db(pdb);
+                       errno = ENOSPC;
+                       return (uint32)-1;
+               }
+       }
+
+       /* for autoloaded printers, check that the printcap entry still exists */
+       if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) {
+               DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) ));
+               release_print_db(pdb);
+               errno = ENOENT;
+               return (uint32)-1;
+       }
+
+       /* Insure the maximum queue size is not violated */
+       if ((njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) {
+               DEBUG(3, ("print_job_start: Queue %s number of jobs (%d) larger than max printjobs per queue (%d).\n",
+                       printername, njobs, lp_maxprintjobs(snum) ));
+               release_print_db(pdb);
+               errno = ENOSPC;
+               return (uint32)-1;
+       }
+
+       DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n",
+                       printername, njobs, lp_maxprintjobs(snum) ));
+
+       if (!allocate_print_jobid(pdb, snum, printername, &jobid))
+               goto fail;
+
+       /* create the database entry */
+       
+       ZERO_STRUCT(pjob);
+       
+       pjob.pid = local_pid;
+       pjob.sysjob = -1;
+       pjob.fd = -1;
+       pjob.starttime = time(NULL);
+       pjob.status = LPQ_SPOOLING;
+       pjob.size = 0;
+       pjob.spooled = False;
+       pjob.smbjob = True;
+       pjob.nt_devmode = nt_devmode;
+       
+       fstrcpy(pjob.jobname, jobname);
+
+       if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+               fstrcpy(pjob.user, vuser->user.smb_name);
+       } else {
+               fstrcpy(pjob.user, uidtoname(user->uid));
+       }
+
+       fstrcpy(pjob.queuename, lp_const_servicename(snum));
+
+       /* we have a job entry - now create the spool file */
+       slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX", 
+                path, PRINT_SPOOL_PREFIX, (unsigned int)jobid);
+       pjob.fd = smb_mkstemp(pjob.filename);
+
+       if (pjob.fd == -1) {
+               if (errno == EACCES) {
+                       /* Common setup error, force a report. */
+                       DEBUG(0, ("print_job_start: insufficient permissions \
+to open spool file %s.\n", pjob.filename));
+               } else {
+                       /* Normal case, report at level 3 and above. */
+                       DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename));
+                       DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno)));
+               }
+               goto fail;
+       }
+
+       pjob_store(snum, jobid, &pjob);
+
+       /* Ensure we keep a rough count of the number of total jobs... */
+       tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1);
+
+       release_print_db(pdb);
+
+       return jobid;
+
+ fail:
+       if (jobid != -1)
+               pjob_delete(snum, jobid);
+
+       release_print_db(pdb);
+
+       DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
+       return (uint32)-1;
+}
+
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int snum, uint32 jobid)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       if (!pjob)
+               return;
+       /* don't allow another process to get this info - it is meaningless */
+       if (pjob->pid != local_pid)
+               return;
+
+       pjob->page_count++;
+       pjob_store(snum, jobid, pjob);
+}
+
+/****************************************************************************
+ Print a file - called on closing the file. This spools the job.
+ If normal close is false then we're tearing down the jobs - treat as an
+ error.
+****************************************************************************/
+
+BOOL print_job_end(int snum, uint32 jobid, BOOL normal_close)
+{
+       struct printjob *pjob = print_job_find(snum, jobid);
+       int ret;
+       SMB_STRUCT_STAT sbuf;
+
+       if (!pjob)
+               return False;
+
+       if (pjob->spooled || pjob->pid != local_pid)
+               return False;
+
+       if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) {
+               pjob->size = sbuf.st_size;
+               close(pjob->fd);
+               pjob->fd = -1;
+       } else {
+
+               /* 
+                * Not a normal close or we couldn't stat the job file,
+                * so something has gone wrong. Cleanup.
+                */
+               close(pjob->fd);
+               pjob->fd = -1;
+               DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid ));
+               goto fail;
+       }
+
+       /* Technically, this is not quite right. If the printer has a separator
+        * page turned on, the NT spooler prints the separator page even if the
+        * print job is 0 bytes. 010215 JRR */
+       if (pjob->size == 0 || pjob->status == LPQ_DELETING) {
+               /* don't bother spooling empty files or something being deleted. */
+               DEBUG(5,("print_job_end: canceling spool of %s (%s)\n",
+                       pjob->filename, pjob->size ? "deleted" : "zero length" ));
+               unlink(pjob->filename);
+               pjob_delete(snum, jobid);
+               return True;
+       }
+
+       ret = (*(current_printif->job_submit))(snum, pjob);
+
+       if (ret)
+               goto fail;
+
+       /* The print job has been sucessfully handed over to the back-end */
+       
+       pjob->spooled = True;
+       pjob->status = LPQ_QUEUED;
+       pjob_store(snum, jobid, pjob);
+       
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+       
+       return True;
+
+fail:
+
+       /* The print job was not succesfully started. Cleanup */
+       /* Still need to add proper error return propagation! 010122:JRR */
+       unlink(pjob->filename);
+       pjob_delete(snum, jobid);
+       return False;
+}
+
+/****************************************************************************
+ Utility fn to enumerate the print queue.
+****************************************************************************/
+
+static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+       struct traverse_struct *ts = (struct traverse_struct *)state;
+       struct printjob pjob;
+       int i;
+       uint32 jobid;
+
+       /* sanity checks */
+       
+       if ( key.dsize != sizeof(jobid) )
+               return 0;
+               
+       memcpy(&jobid, key.dptr, sizeof(jobid));
+       
+       if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+               return 0;
+       free_nt_devicemode( &pjob.nt_devmode );
+
+       /* maybe it isn't for this queue */
+       if (ts->snum != lp_servicenumber(pjob.queuename))
+               return 0;
+
+       if (ts->qcount >= ts->maxcount)
+               return 0;
+
+       i = ts->qcount;
+
+       ts->queue[i].job = jobid;
+       ts->queue[i].size = pjob.size;
+       ts->queue[i].page_count = pjob.page_count;
+       ts->queue[i].status = pjob.status;
+       ts->queue[i].priority = 1;
+       ts->queue[i].time = pjob.starttime;
+       fstrcpy(ts->queue[i].fs_user, pjob.user);
+       fstrcpy(ts->queue[i].fs_file, pjob.jobname);
+
+       ts->qcount++;
+
+       return 0;
+}
+
+struct traverse_count_struct {
+       int snum, count;
+};
+
+/****************************************************************************
+ Utility fn to count the number of entries in the print queue.
+****************************************************************************/
+
+static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+       struct traverse_count_struct *ts = (struct traverse_count_struct *)state;
+       struct printjob pjob;
+       uint32 jobid;
+
+       /* sanity checks */
+       
+       if (  key.dsize != sizeof(jobid) )
+               return 0;
+               
+       memcpy(&jobid, key.dptr, sizeof(jobid));
+       
+       if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+               return 0;
+               
+       free_nt_devicemode( &pjob.nt_devmode );
+
+       /* maybe it isn't for this queue - this cannot happen with the tdb/printer code. JRA */
+       if (ts->snum != lp_servicenumber(pjob.queuename))
+               return 0;
+
+       ts->count++;
+
+       return 0;
+}
+
+/****************************************************************************
+ Sort print jobs by submittal time.
+****************************************************************************/
+
+static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2)
+{
+       /* Silly cases */
+
+       if (!j1 && !j2)
+               return 0;
+       if (!j1)
+               return -1;
+       if (!j2)
+               return 1;
+
+       /* Sort on job start time */
+
+       if (j1->time == j2->time)
+               return 0;
+       return (j1->time > j2->time) ? 1 : -1;
+}
+
+/****************************************************************************
+ Get a printer queue listing.
+ set queue = NULL and status = NULL if you just want to update the cache
+****************************************************************************/
+
+int print_queue_status(int snum, 
+                      print_queue_struct **queue,
+                      print_status_struct *status)
+{
+       struct traverse_struct tstruct;
+       struct traverse_count_struct tsc;
+       fstring keystr;
+       TDB_DATA data, key;
+       const char *printername;
+       struct tdb_print_db *pdb;
+
+       /* make sure the database is up to date */
+
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+
+       /* return if we are done */
+
+       if ( !queue || !status )
+               return 0;
+
+       *queue = NULL;
+       printername = lp_const_servicename(snum);
+       pdb = get_print_db_byname(printername);
+
+       if (!pdb)
+               return 0;
+
+       /*
+        * Fetch the queue status.  We must do this first, as there may
+        * be no jobs in the queue.
+        */
+       ZERO_STRUCTP(status);
+       slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr);
+       data = tdb_fetch(pdb->tdb, key);
+       if (data.dptr) {
+               if (data.dsize == sizeof(*status)) {
+                       memcpy(status, data.dptr, sizeof(*status));
+               }
+               SAFE_FREE(data.dptr);
+       }
+
+       /*
+        * Now, fetch the print queue information.  We first count the number
+        * of entries, and then only retrieve the queue if necessary.
+        */
+       tsc.count = 0;
+       tsc.snum = snum;
+       
+       tdb_traverse(pdb->tdb, traverse_count_fn_queue, (void *)&tsc);
+
+       if (tsc.count == 0) {
+               release_print_db(pdb);
+               return 0;
+       }
+
+       /* Allocate the queue size. */
+       if ((tstruct.queue = (print_queue_struct *)
+            malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) {
+               release_print_db(pdb);
+               return 0;
+       }
+
+       /*
+        * Fill in the queue.
+        * We need maxcount as the queue size may have changed between
+        * the two calls to tdb_traverse.
+        */
+       tstruct.qcount = 0;
+       tstruct.maxcount = tsc.count;
+       tstruct.snum = snum;
+
+       tdb_traverse(pdb->tdb, traverse_fn_queue, (void *)&tstruct);
+       release_print_db(pdb);
+
+       /* Sort the queue by submission time otherwise they are displayed
+          in hash order. */
+
+       qsort(tstruct.queue, tstruct.qcount, sizeof(print_queue_struct),
+             QSORT_CAST(printjob_comp));
+
+       *queue = tstruct.queue;
+       return tstruct.qcount;
+}
+
+/****************************************************************************
+ Pause a queue.
+****************************************************************************/
+
+BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode)
+{
+       int ret;
+       
+       if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+               *errcode = WERR_ACCESS_DENIED;
+               return False;
+       }
+
+       ret = (*(current_printif->queue_pause))(snum);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
+
+       /* force update the database */
+       print_cache_flush(snum);
+
+       /* Send a printer notify message */
+
+       notify_printer_status(snum, PRINTER_STATUS_PAUSED);
+
+       return True;
+}
+
+/****************************************************************************
+ Resume a queue.
+****************************************************************************/
+
+BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode)
+{
+       int ret;
+
+       if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+               *errcode = WERR_ACCESS_DENIED;
+               return False;
+       }
+
+       ret = (*(current_printif->queue_resume))(snum);
+
+       if (ret != 0) {
+               *errcode = WERR_INVALID_PARAM;
+               return False;
+       }
+
+       /* make sure the database is up to date */
+       if (print_cache_expired(snum))
+               print_queue_update(snum);
+
+       /* Send a printer notify message */
+
+       notify_printer_status(snum, PRINTER_STATUS_OK);
+
+       return True;
+}
+
+/****************************************************************************
+ Purge a queue - implemented by deleting all jobs that we can delete.
+****************************************************************************/
+
+BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode)
+{
+       print_queue_struct *queue;
+       print_status_struct status;
+       int njobs, i;
+       BOOL can_job_admin;
+
+       /* Force and update so the count is accurate (i.e. not a cached count) */
+       print_queue_update(snum);
+       
+       can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER);
+       njobs = print_queue_status(snum, &queue, &status);
+
+       for (i=0;i<njobs;i++) {
+               BOOL owner = is_owner(user, snum, queue[i].job);
+
+               if (owner || can_job_admin) {
+                       print_job_delete1(snum, queue[i].job);
+               }
+       }
+
+       SAFE_FREE(queue);
+
+       return True;
+}
diff --git a/source4/printing/printing_db.c b/source4/printing/printing_db.c
new file mode 100644 (file)
index 0000000..0aa8dfa
--- /dev/null
@@ -0,0 +1,204 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+   printing backend routines
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Jeremy Allison 2002
+   
+   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 "printing.h"
+
+static struct tdb_print_db *print_db_head;
+
+/****************************************************************************
+  Function to find or create the printer specific job tdb given a printername.
+  Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
+****************************************************************************/
+
+struct tdb_print_db *get_print_db_byname(const char *printername)
+{
+       struct tdb_print_db *p = NULL, *last_entry = NULL;
+       int num_open = 0;
+       pstring printdb_path;
+       BOOL done_become_root = False;
+
+       for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
+               /* Ensure the list terminates... JRA. */
+               SMB_ASSERT(p->next != print_db_head);
+
+               if (p->tdb && strequal(p->printer_name, printername)) {
+                       DLIST_PROMOTE(print_db_head, p);
+                       p->ref_count++;
+                       return p;
+               }
+               num_open++;
+               last_entry = p;
+       }
+
+       /* Not found. */
+       if (num_open >= MAX_PRINT_DBS_OPEN) {
+               /* Try and recycle the last entry. */
+               DLIST_PROMOTE(print_db_head, last_entry);
+
+               for (p = print_db_head; p; p = p->next) {
+                       if (p->ref_count)
+                               continue;
+                       if (p->tdb) {
+                               if (tdb_close(print_db_head->tdb)) {
+                                       DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
+                                                               print_db_head->printer_name ));
+                                       return NULL;
+                               }
+                       }
+                       p->tdb = NULL;
+                       p->ref_count = 0;
+                       memset(p->printer_name, '\0', sizeof(p->printer_name));
+                       break;
+               }
+               if (p) {
+                       DLIST_PROMOTE(print_db_head, p);
+                       p = print_db_head;
+               }
+       }
+       
+       if (!p) {
+               /* Create one. */
+               p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db));
+               if (!p) {
+                       DEBUG(0,("get_print_db: malloc fail !\n"));
+                       return NULL;
+               }
+               ZERO_STRUCTP(p);
+               DLIST_ADD(print_db_head, p);
+       }
+
+       pstrcpy(printdb_path, lock_path("printing/"));
+       pstrcat(printdb_path, printername);
+       pstrcat(printdb_path, ".tdb");
+
+       if (geteuid() != 0) {
+               become_root();
+               done_become_root = True;
+       }
+
+       p->tdb = tdb_open_log(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+       if (done_become_root)
+               unbecome_root();
+
+       if (!p->tdb) {
+               DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
+                                       printdb_path ));
+               DLIST_REMOVE(print_db_head, p);
+               SAFE_FREE(p);
+               return NULL;
+       }
+       fstrcpy(p->printer_name, printername);
+       p->ref_count++;
+       return p;
+}
+
+/***************************************************************************
+ Remove a reference count.
+****************************************************************************/
+
+void release_print_db( struct tdb_print_db *pdb)
+{
+       pdb->ref_count--;
+       SMB_ASSERT(pdb->ref_count >= 0);
+}
+
+/***************************************************************************
+ Close all open print db entries.
+****************************************************************************/
+
+void close_all_print_db(void)
+{
+       struct tdb_print_db *p = NULL, *next_p = NULL;
+
+       for (p = print_db_head; p; p = next_p) {
+               next_p = p->next;
+
+               if (p->tdb)
+                       tdb_close(p->tdb);
+               DLIST_REMOVE(print_db_head, p);
+               ZERO_STRUCTP(p);
+               SAFE_FREE(p);
+       }
+}
+
+
+/****************************************************************************
+ Fetch and clean the pid_t record list for all pids interested in notify
+ messages. data needs freeing on exit.
+****************************************************************************/
+
+TDB_DATA get_printer_notify_pid_list(TDB_CONTEXT *tdb, const char *printer_name, BOOL cleanlist)
+{
+       TDB_DATA data;
+       size_t i;
+
+       ZERO_STRUCT(data);
+
+       data = tdb_fetch_by_string( tdb, NOTIFY_PID_LIST_KEY );
+
+       if (!data.dptr) {
+               ZERO_STRUCT(data);
+               return data;
+       }
+
+       if (data.dsize % 8) {
+               DEBUG(0,("get_printer_notify_pid_list: Size of record for printer %s not a multiple of 8 !\n", printer_name ));
+               tdb_delete_by_string(tdb, NOTIFY_PID_LIST_KEY );
+               SAFE_FREE(data.dptr);
+               ZERO_STRUCT(data);
+               return data;
+       }
+
+       if (!cleanlist)
+               return data;
+
+       /*
+        * Weed out all dead entries.
+        */
+
+       for( i = 0; i < data.dsize; i += 8) {
+               pid_t pid = (pid_t)IVAL(data.dptr, i);
+
+               if (pid == sys_getpid())
+                       continue;
+
+               /* Entry is dead if process doesn't exist or refcount is zero. */
+
+               while ((i < data.dsize) && ((IVAL(data.dptr, i + 4) == 0) || !process_exists(pid))) {
+
+                       /* Refcount == zero is a logic error and should never happen. */
+                       if (IVAL(data.dptr, i + 4) == 0) {
+                               DEBUG(0,("get_printer_notify_pid_list: Refcount == 0 for pid = %u printer %s !\n",
+                                                       (unsigned int)pid, printer_name ));
+                       }
+
+                       if (data.dsize - i > 8)
+                               memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+                       data.dsize -= 8;
+               }
+       }
+
+       return data;
+}
+
+
diff --git a/source4/python/.cvsignore b/source4/python/.cvsignore
new file mode 100644 (file)
index 0000000..7e99e36
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
\ No newline at end of file
diff --git a/source4/python/README b/source4/python/README
new file mode 100644 (file)
index 0000000..04f7942
--- /dev/null
@@ -0,0 +1,28 @@
+This directory contains Python bindings to allow you to access various
+aspects of Samba.  At the moment their status is "experimental" and
+they are not built by default.
+
+In order to be able to compile samba-python you need to have python
+and the python-dev packages installed.
+
+Python libraries are always built for a particular version of Python
+(2.2, 2.1, etc), and libraries built for one version will not be seen
+by another.  By default Samba's libraries are built for whatever is
+installed as "python" on your $PATH, but you can override this using
+the --with-python option.  For example
+
+  $ ./configure --with-python=python2.2
+
+To build:
+
+$ autoconf
+$ ./configure 
+$ make python_ext
+
+Now, you can install the modules:
+
+$ cp build/lib.*/*.so /usr/lib/python2.1/lib-dynload/
+
+(the directory /usr/lib/python2.1 may vary, depending on your installation)
+
+Samba-python should work now!
diff --git a/source4/python/examples/spoolss/changeid.py b/source4/python/examples/spoolss/changeid.py
new file mode 100755 (executable)
index 0000000..85fe0ef
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+#
+# Display the changeid for a list of printers given on the command line
+#
+# Sample usage:
+#
+#     changeid.py '\\win2kdc1\magpie'
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) == 1:
+    print "Usage: changeid.py <printername>"
+    sys.exit(1)
+
+for printer in sys.argv[1:]:
+
+    # Open printer handle
+
+    try:
+        hnd = spoolss.openprinter(printer)
+    except:
+        print "error opening printer %s" % printer
+        sys.exit(1)
+
+    # Fetch and display changeid
+
+    info = hnd.getprinter(level = 0)
+    print info["change_id"]
+
+    # Clean up
+
+    spoolss.closeprinter(hnd)
diff --git a/source4/python/examples/spoolss/enumprinters.py b/source4/python/examples/spoolss/enumprinters.py
new file mode 100755 (executable)
index 0000000..478c46b
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+# Display information on all printers on a print server.  Defaults to
+# printer info level 1.
+#
+# Example: enumprinters.py win2kdc1
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+    print "Usage: enumprinters.py <servername> [infolevel]"
+    sys.exit(1)
+
+printserver = sys.argv[1]
+
+level = 1
+if len(sys.argv) == 3:
+    level = int(sys.argv[2])
+        
+# Get list of printers
+
+try:
+    printer_list = spoolss.enumprinters("\\\\%s" % printserver)
+except:
+    print "error enumerating printers on %s" % printserver
+    sys.exit(1)
+
+# Display basic info
+
+for printer in printer_list:
+    h = spoolss.openprinter("\\\\%s\\%s" % (printserver, printer))
+    info = h.getprinter(level = level)
+    print "Printer info %d for %s: %s" % (level, printer, info)
+    print
diff --git a/source4/python/examples/spoolss/psec.py b/source4/python/examples/spoolss/psec.py
new file mode 100755 (executable)
index 0000000..498a0ef
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Get or set the security descriptor on a printer
+#
+
+import sys, re, string
+from samba import spoolss
+
+if len(sys.argv) != 3:
+    print "Usage: psec.py getsec|setsec printername"
+    sys.exit(1)
+
+op = sys.argv[1]
+printername = sys.argv[2]
+
+# Display security descriptor
+
+if op == "getsec":
+
+    try:
+        hnd = spoolss.openprinter(printername)
+    except:
+        print "error opening printer %s" % printername
+        sys.exit(1)
+
+    secdesc = hnd.getprinter(level = 3)["security_descriptor"]
+
+    print secdesc["owner_sid"]
+    print secdesc["group_sid"]
+
+    for acl in secdesc["dacl"]["ace_list"]:
+        print "%d %d 0x%08x %s" % (acl["type"], acl["flags"],
+                                   acl["mask"], acl["trustee"])
+
+    spoolss.closeprinter(hnd)
+
+    sys.exit(0)
+
+# Set security descriptor
+
+if op == "setsec":
+
+    # Open printer
+
+    try:
+        hnd = spoolss.openprinter(printername,
+                                  creds = {"domain": "NPSD-TEST2",
+                                           "username": "Administrator",
+                                           "password": "penguin"})
+    except:
+        print "error opening printer %s" % printername
+        sys.exit(1)
+
+    # Read lines from standard input and build security descriptor
+
+    lines = sys.stdin.readlines()
+
+    secdesc = {}
+
+    secdesc["owner_sid"] = lines[0]
+    secdesc["group_sid"] = lines[1]
+
+    secdesc["revision"] = 1
+    secdesc["dacl"] = {}
+    secdesc["dacl"]["revision"] = 2
+    secdesc["dacl"]["ace_list"] = []
+
+    for acl in lines[2:]:
+        match = re.match("(\d+) (\d+) (0[xX][\dA-Fa-f]+) (\S+)", acl)
+        secdesc["dacl"]["ace_list"].append(
+            {"type": int(match.group(1)), "flags": int(match.group(2)),
+             "mask": string.atoi(match.group(3), 0), "trustee": match.group(4)})
+
+    # Build info3 structure
+
+    info3 = {}
+
+    info3["flags"] = 0x8004             # self-relative, dacl present
+    info3["level"] = 3
+    info3["security_descriptor"] = secdesc
+
+    hnd.setprinter(info3)
+
+    spoolss.closeprinter(hnd)
+    sys.exit(0)
+
+print "invalid operation %s" % op
+sys.exit(1)
diff --git a/source4/python/examples/tdbpack/.cvsignore b/source4/python/examples/tdbpack/.cvsignore
new file mode 100644 (file)
index 0000000..52e4e61
--- /dev/null
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
diff --git a/source4/python/examples/tdbpack/oldtdbutil.py b/source4/python/examples/tdbpack/oldtdbutil.py
new file mode 100644 (file)
index 0000000..ac435b8
--- /dev/null
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+#############################################################
+# tdbutil
+# 
+# Purpose:
+#   Contains functions that are used to pack and unpack data
+# from Samba's tdb databases.  Samba sometimes represents complex
+# data structures as a single value in a database.  These functions
+# allow other python scripts to package data types into a single python
+# string and unpackage them.
+#
+#
+# XXXXX: This code is no longer used; it's just here for testing
+# compatibility with the new (much faster) C implementation.
+#
+############################################################## 
+import string
+
+def pack(format,list):
+   retstring = ''
+   listind = 0
+   
+   # Cycle through format entries
+   for type in format:
+      # Null Terminated String
+      if (type == 'f' or type == 'P'):
+         retstring = retstring + list[listind] + "\000"
+      # 4 Byte Number
+      if (type == 'd'):
+         retstring = retstring + PackNum(list[listind],4)
+      # 2 Byte Number
+      if (type == 'w'):
+         retstring = retstring + PackNum(list[listind],2)
+      # Pointer Value
+      if (type == 'p'):
+         if (list[listind]):
+            retstring = retstring + PackNum(1,4)
+         else:
+            retstring = retstring + PackNum(0,4)
+      # Buffer and Length
+      if (type == 'B'):
+         # length
+         length = list[listind]
+         retstring = retstring + PackNum(length,4)
+         length = int(length)
+         listind = listind + 1
+         # buffer
+         retstring = retstring + list[listind][:length]
+         
+      listind = listind + 1
+      
+   return retstring
+
+def unpack(format,buffer):
+   retlist = []
+   bufind = 0
+   
+   lasttype = ""
+   for type in format:
+      # Pointer Value
+      if (type == 'p'):
+         newvalue = UnpackNum(buffer[bufind:bufind+4])
+         bufind = bufind + 4
+         if (newvalue):
+            newvalue = 1L
+         else:
+            newvalue = 0L
+         retlist.append(newvalue)
+      # Previous character till end of data
+      elif (type == '$'):
+         if (lasttype == 'f'):
+            while (bufind < len(buffer)):
+               newstring = ''
+               while (buffer[bufind] != '\000'):
+                  newstring = newstring + buffer[bufind]
+                  bufind = bufind + 1
+               bufind = bufind + 1
+               retlist.append(newstring)
+      # Null Terminated String
+      elif (type == 'f' or type == 'P'):
+         newstring = ''
+         while (buffer[bufind] != '\000'):
+            newstring = newstring + buffer[bufind]
+            bufind = bufind + 1
+         bufind = bufind + 1
+         retlist.append(newstring)
+      # 4 Byte Number
+      elif (type == 'd'):
+         newvalue = UnpackNum(buffer[bufind:bufind+4])
+         bufind = bufind + 4
+         retlist.append(newvalue)
+      # 2 Byte Number
+      elif (type == 'w'):
+         newvalue = UnpackNum(buffer[bufind:bufind+2])
+         bufind = bufind + 2
+         retlist.append(newvalue)
+      # Length and Buffer
+      elif (type == 'B'):
+         # Length
+         length = UnpackNum(buffer[bufind:bufind+4])
+         bufind = bufind + 4 
+         retlist.append(length)
+         length = int(length)
+         # Buffer
+         retlist.append(buffer[bufind:bufind+length])
+         bufind = bufind + length
+         
+      lasttype = type
+
+   return ((retlist,buffer[bufind:]))
+
+def PackNum(myint,size):
+    retstring = ''
+    size = size * 2
+    hint = hex(myint)[2:]
+
+    # Check for long notation
+    if (hint[-1:] == 'L'):
+       hint = hint[:-1]
+    
+    addon = size - len(hint)
+    for i in range(0,addon):
+       hint = '0' + hint
+    
+    while (size > 0):
+       val = string.atoi(hint[size-2:size],16)
+       retstring = retstring + chr(val)
+       size = size - 2
+    
+    return retstring
+   
+def UnpackNum(buffer):
+   size = len(buffer)
+   mystring = ''
+
+   for i in range(size-1,-1,-1):
+      val = hex(ord(buffer[i]))[2:]
+      if (len(val) == 1):
+         val = '0' + val
+      mystring = mystring + val
+   if (len(mystring) > 4):
+      return string.atol(mystring,16)
+   else:
+      return string.atoi(mystring,16)
diff --git a/source4/python/examples/tdbpack/tdbtimetrial.py b/source4/python/examples/tdbpack/tdbtimetrial.py
new file mode 100755 (executable)
index 0000000..be64048
--- /dev/null
@@ -0,0 +1,12 @@
+#! /usr/bin/python2.2
+
+def run_trial():
+    # import tdbutil
+    from samba.tdbpack import pack
+    
+    for i in xrange(500000):
+        pack("ddffd", (10, 2, "mbp", "martin", 0))
+        #s = "\n\0\0\0" + "\x02\0\0\0" + "mbp\0" + "martin\0" + "\0\0\0\0"
+
+if __name__ == '__main__':
+    run_trial()
diff --git a/source4/python/examples/tdbpack/test_tdbpack.py b/source4/python/examples/tdbpack/test_tdbpack.py
new file mode 100755 (executable)
index 0000000..837600f
--- /dev/null
@@ -0,0 +1,253 @@
+#! /usr/bin/env python2.2
+
+__doc__ = """test case for samba.tdbpack functions
+
+tdbpack provides a means of pickling values into binary formats
+compatible with that used by the samba tdbpack()/tdbunpack()
+functions.
+
+Numbers are always stored in little-endian format; strings are stored
+in either DOS or Unix codepage as appropriate.
+
+The format for any particular element is encoded as a short ASCII
+string, with one character per field."""
+
+# Copyright (C) 2002 Hewlett-Packard.
+
+__author__ = 'Martin Pool <mbp@sourcefrog.net>'
+
+import unittest
+import oldtdbutil
+import samba.tdbpack
+
+both_unpackers = (samba.tdbpack.unpack, oldtdbutil.unpack)
+both_packers = (samba.tdbpack.pack, oldtdbutil.pack)
+
+
+
+# #             ('B', [10, 'hello'], '\x0a\0\0\0hello'),
+#              ('BB', [11, 'hello\0world', 3, 'now'],
+#               '\x0b\0\0\0hello\0world\x03\0\0\0now'),
+#              ('pd', [1, 10], '\x01\0\0\0\x0a\0\0\0'),
+#              ('BBB', [5, 'hello', 0, '', 5, 'world'],
+#               '\x05\0\0\0hello\0\0\0\0\x05\0\0\0world'),
+
+             # strings are sequences in Python, there's no getting away
+             # from it
+#             ('ffff', 'evil', 'e\0v\0i\0l\0'),
+#              ('BBBB', 'evil',                   
+#               '\x01\0\0\0e'
+#               '\x01\0\0\0v'
+#               '\x01\0\0\0i'
+#               '\x01\0\0\0l'),
+
+#              ('', [], ''),
+
+#              # exercise some long strings
+#              ('PP', ['hello' * 255, 'world' * 255],
+#               'hello' * 255 + '\0' + 'world' * 255 + '\0'),
+#              ('PP', ['hello' * 40000, 'world' * 50000],
+#               'hello' * 40000 + '\0' + 'world' * 50000 + '\0'),
+#              ('B', [(5*51), 'hello' * 51], '\xff\0\0\0' + 'hello' * 51),
+#              ('BB', [(5 * 40000), 'hello' * 40000,
+#                      (5 * 50000), 'world' * 50000],
+#               '\x40\x0d\x03\0' + 'hello' * 40000 + '\x90\xd0\x03\x00' + 'world' * 50000),
+
+    
+class PackTests(unittest.TestCase):
+    symm_cases = [
+             ('w', [42], '\x2a\0'),
+             ('www', [42, 2, 69], '\x2a\0\x02\0\x45\0'),
+             ('wd', [42, 256], '\x2a\0\0\x01\0\0'),
+             ('w', [0], '\0\0'),
+             ('w', [255], '\xff\0'),
+             ('w', [256], '\0\x01'),
+             ('w', [0xdead], '\xad\xde'),
+             ('w', [0xffff], '\xff\xff'),
+             ('p', [0], '\0\0\0\0'),
+             ('p', [1], '\x01\0\0\0'),
+             ('d', [0x01020304], '\x04\x03\x02\x01'),
+             ('d', [0x7fffffff], '\xff\xff\xff\x7f'),
+             ('d', [0x80000000L], '\x00\x00\x00\x80'),
+             ('d', [0x80000069L], '\x69\x00\x00\x80'),
+             ('d', [0xffffffffL], '\xff\xff\xff\xff'),
+             ('d', [0xffffff00L], '\x00\xff\xff\xff'),
+             ('ddd', [1, 10, 50], '\x01\0\0\0\x0a\0\0\0\x32\0\0\0'),
+             ('ff', ['hello', 'world'], 'hello\0world\0'),
+             ('fP', ['hello', 'world'], 'hello\0world\0'),
+             ('PP', ['hello', 'world'], 'hello\0world\0'),
+             ('B', [0, ''], '\0\0\0\0'),
+# old implementation is wierd when string is not the right length             
+#             ('B', [2, 'hello'], '\x0a\0\0\0hello'),
+             ('B', [5, 'hello'], '\x05\0\0\0hello'),
+             ]
+
+    def test_symmetric(self):
+        """Cookbook of symmetric pack/unpack tests
+        """
+        for packer in [samba.tdbpack.pack]: # both_packers:
+            for unpacker in both_unpackers:
+                for format, values, expected in self.symm_cases:
+                    out_packed = packer(format, values)
+                    self.assertEquals(out_packed, expected)
+                    out, rest = unpacker(format, expected)
+                    self.assertEquals(rest, '')
+                    self.assertEquals(list(values), list(out))
+
+    def test_large(self):
+        """Test large pack/unpack strings"""
+        large_cases = [('w' * 1000, xrange(1000)), ]
+        for packer in both_packers:
+            for unpacker in both_unpackers:
+                for format, values in large_cases:
+                    packed = packer(format, values)
+                    out, rest = unpacker(format, packed)
+                    self.assertEquals(rest, '')
+                    self.assertEquals(list(values), list(out))
+
+                    
+    def test_pack(self):
+        """Cookbook of expected pack values
+
+        These can't be used for the symmetric test because the unpacked value is
+        not "canonical".
+        """
+        cases = [('w', (42,), '\x2a\0'),
+                 ]
+
+        for packer in both_packers:
+            for format, values, expected in cases:
+                self.assertEquals(packer(format, values), expected)
+
+    def test_unpack_extra(self):
+        # Test leftover data
+        for unpacker in both_unpackers:
+            for format, values, packed in self.symm_cases:
+                out, rest = unpacker(format, packed + 'hello sailor!')
+                self.assertEquals(rest, 'hello sailor!')
+                self.assertEquals(list(values), list(out))
+
+
+    def test_pack_extra(self):
+        """Leftover values when packing"""
+        cases = [
+            ('d', [10, 20], [10]),
+            ('d', [10, 'hello'], [10]),
+            ('ff', ['hello', 'world', 'sailor'], ['hello', 'world']),
+            ]
+        for unpacker in both_unpackers:
+            for packer in both_packers:
+                for format, values, chopped in cases:
+                    bin = packer(format, values)
+                    out, rest = unpacker(format, bin)
+                    self.assertEquals(list(out), list(chopped))
+                    self.assertEquals(rest, '')
+
+
+    def test_unpack(self):
+        """Cookbook of tricky unpack tests"""
+        cases = [
+                 # Apparently I couldn't think of any tests that weren't
+                 # symmetric :-/
+                 ]
+        for unpacker in both_unpackers:
+            for format, values, expected in cases:
+                out, rest = unpacker(format, expected)
+                self.assertEquals(rest, '')
+                self.assertEquals(list(values), list(out))
+
+
+    def test_pack_failures(self):
+        """Expected errors for incorrect packing"""
+        cases = [('w', []),
+#                 ('w', ()),
+#                 ('w', {}),
+                 ('ww', [2]),
+                 ('w', 2),
+#                  ('w', None),
+                 ('wwwwwwwwwwww', []),
+#                 ('w', [0x60A15EC5L]),
+#                 ('w', [None]),
+                 ('d', []),
+                 ('p', []),
+                 ('f', [2]),
+                 ('P', [None]),
+                 ('P', ()),
+                 ('f', [hex]),
+                 ('fw', ['hello']),
+#                  ('f', [u'hello']),
+                 ('B', [2]),
+                 (None, [2, 3, 4]),
+                 (ord('f'), [20]),
+                 # old code doesn't distinguish string from seq-of-char
+#                 (['w', 'w'], [2, 2]),
+                 # old code just ignores invalid characters
+#                 ('Q', [2]),
+#                 ('fQ', ['2', 3]),
+#                 ('fQ', ['2']),
+                 (2, [2]),
+                 # old code doesn't typecheck format
+#                 ({}, {})
+                 ]
+        for packer in both_packers:
+            for format, values in cases:
+                try:
+                    packer(format, values)
+                except StandardError:
+                    pass
+                else:
+                    raise AssertionError("didn't get exception: format %s, values %s, packer %s"
+                                         % (`format`, `values`, `packer`))
+
+
+    def test_unpack_failures(self):
+        """Expected errors for incorrect unpacking"""
+        cases = [
+# This ought to be illegal, but the old code doesn't prohibit it
+#                ('$', '', ValueError),
+#                ('Q', '', ValueError),
+#                ('Q$', '', ValueError),
+                 ('f', '', IndexError),
+                 ('d', '', IndexError),
+# This is an illegal packing, but the old code doesn't trap                 
+#                 ('d', '2', IndexError),
+#                 ('d', '22', IndexError),
+#                 ('d', '222', IndexError),
+#                 ('p', '\x01\0', IndexError),
+#                ('w', '2', IndexError),
+#                ('B', '\xff\0\0\0hello', IndexError),
+#                  ('B', '\xff\0', IndexError),
+                 ('w', '', IndexError),
+                 ('f', 'hello', IndexError),
+                 ('f', '', IndexError),
+#                ('B', '\x01\0\0\0', IndexError),
+#                 ('B', '\x05\0\0\0hell', IndexError),
+                 ('B', '\xff\xff\xff\xff', ValueError),
+#                 ('B', 'foobar', IndexError),
+#                 ('BB', '\x01\0\0\0a\x01', IndexError),
+                 ]
+
+        for unpacker in both_unpackers:
+            for format, values, throwable_class in cases:
+                try:
+                    unpacker(format, values)
+                except StandardError:
+                    pass
+                else:
+                    raise AssertionError("didn't get exception: format %s, values %s, unpacker %s"
+                                         % (`format`, `values`, `unpacker`))
+
+    def test_unpack_repeated(self):
+        cases = [(('df$',
+                  '\x00\x00\x00\x00HP C LaserJet 4500-PS\x00Windows 4.0\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.HLP\x00\x00RAW\x00\\print$\\WIN40\\0\\readme.wri\x00\\print$\\WIN40\\0\\pscript.drv\x00\\print$\\WIN40\\0\\pscript.hlp\x00'),
+                 ([0L, 'HP C LaserJet 4500-PS', 'Windows 4.0', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.HLP', '', 'RAW', '\\print$\\WIN40\\0\\readme.wri', '\\print$\\WIN40\\0\\pscript.drv', '\\print$\\WIN40\\0\\pscript.hlp'], ''))]
+        for unpacker in both_unpackers:
+            for input, expected in cases:
+                result = apply(unpacker, input)
+                if result != expected:
+                    raise AssertionError("%s:\n     input: %s\n    output: %s\n  expected: %s" % (`unpacker`, `input`, `result`, `expected`))
+        
+
+if __name__ == '__main__':
+    unittest.main()
+    
diff --git a/source4/python/gprinterdata b/source4/python/gprinterdata
new file mode 100755 (executable)
index 0000000..cd06207
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser, hex_string
+import gtk
+from samba import spoolss
+import string
+import printerdata
+
+# Initialise printerdata dictionary
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+    print "Usage: gprinterdata [--ex] <printer>"
+    print "where <printer> is a UNC printer name."
+    sys.exit(1)
+
+try:
+    host = string.replace(sys.argv[len(sys.argv) - 1], "/", "\\")
+    if sys.argv[1] == "--ex":
+        t = printerdata.printerdata_ex(host)
+    else:
+        t = printerdata.printerdata(host)
+except:
+    print "gprinterdata: error opening %s" % sys.argv[len(sys.argv) - 1]
+    sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+db.register_get_value_text_fn("", hex_string)
+db.build_ui('gprinterdata')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source4/python/gtdbtool b/source4/python/gtdbtool
new file mode 100755 (executable)
index 0000000..129f4fe
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser
+import gtk
+from samba import tdb
+import string
+
+# Open handle on tdb
+
+if len(sys.argv) != 2:
+    print "Usage: gdbtool <tdbfile>"
+    sys.exit(1)
+
+try:
+    t = tdb.open(sys.argv[1])
+except tdb.error, t:
+    print "gtdbtool: error opening %s: %s" % (sys.argv[1], t)
+    sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+
+def display_key_x00(key):
+    """Remove \x00 from all keys as they mucks up GTK."""
+    return string.replace(key, "\x00", "")
+
+db.register_get_key_text_fn(display_key_x00)
+
+db.build_ui('gtdbtool')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source4/python/gtkdictbrowser.py b/source4/python/gtkdictbrowser.py
new file mode 100755 (executable)
index 0000000..dd8bed8
--- /dev/null
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+#
+# Browse a Python dictionary in a two pane graphical interface written
+# in GTK.
+#
+# The GtkDictBrowser class is supposed to be generic enough to allow
+# applications to override enough methods and produce a
+# domain-specific browser provided the information is presented as a
+# Python dictionary.
+#
+# Possible applications:
+#
+#   - Windows registry browser
+#   - SPOOLSS printerdata browser
+#   - tdb file browser
+#
+
+from gtk import *
+import string, re
+
+class GtkDictBrowser:
+
+    def __init__(self, dict):
+        self.dict = dict
+        
+        # This variable stores a list of (regexp, function) used to
+        # convert the raw value data to a displayable string.
+
+        self.get_value_text_fns = []
+        self.get_key_text = lambda x: x
+
+        # We can filter the list of keys displayed using a regex
+
+        self.filter_regex = ""
+
+    # Create and configure user interface widgets.  A string argument is
+    # used to set the window title.
+
+    def build_ui(self, title):
+        win = GtkWindow()
+        win.set_title(title)
+
+        win.connect("destroy", mainquit)
+
+        hpaned = GtkHPaned()
+        win.add(hpaned)
+        hpaned.set_border_width(5)
+        hpaned.show()
+
+        vbox = GtkVBox()
+        hpaned.add1(vbox)
+        vbox.show()
+
+        scrolled_win = GtkScrolledWindow()
+        scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        vbox.pack_start(scrolled_win)
+        scrolled_win.show()
+
+        hbox = GtkHBox()
+        vbox.pack_end(hbox, expand = 0, padding = 5)
+        hbox.show()
+
+        label = GtkLabel("Filter:")
+        hbox.pack_start(label, expand = 0, padding = 5)
+        label.show()
+
+        self.entry = GtkEntry()
+        hbox.pack_end(self.entry, padding = 5)
+        self.entry.show()
+
+        self.entry.connect("activate", self.filter_activated)
+        
+        self.list = GtkList()
+        self.list.set_selection_mode(SELECTION_MULTIPLE)
+        self.list.set_selection_mode(SELECTION_BROWSE)
+        scrolled_win.add_with_viewport(self.list)
+        self.list.show()
+
+        self.list.connect("select_child", self.key_selected)
+
+        scrolled_win = GtkScrolledWindow()
+        scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+        hpaned.add2(scrolled_win)
+        scrolled_win.set_usize(500,400)
+        scrolled_win.show()
+        
+        self.text = GtkText()
+        self.text.set_editable(FALSE)
+        scrolled_win.add_with_viewport(self.text)
+        self.text.show()
+
+        self.text.connect("event", self.event_handler)
+
+        self.menu = GtkMenu()
+        self.menu.show()
+
+        self.font = load_font("fixed")
+
+        self.update_keylist()
+
+        win.show()
+
+    # Add a key to the left hand side of the user interface
+
+    def add_key(self, key):
+        display_key = self.get_key_text(key)
+        list_item = GtkListItem(display_key)
+        list_item.set_data("raw_key", key) # Store raw key in item data
+        self.list.add(list_item)
+        list_item.show()
+
+    # Event handler registered by build_ui()
+
+    def event_handler(self, event, menu):
+        return FALSE
+
+    # Set the text to appear in the right hand side of the user interface 
+
+    def set_value_text(self, item):
+
+        # Clear old old value in text window
+
+        self.text.delete_text(0, self.text.get_length())
+        
+        if type(item) == str:
+
+            # The text widget has trouble inserting text containing NULL
+            # characters.
+            
+            item = string.replace(item, "\x00", ".")
+            
+            self.text.insert(self.font, None, None, item)
+
+        else:
+
+            # A non-text item
+            
+            self.text.insert(self.font, None, None, repr(item))
+            
+    # This function is called when a key is selected in the left hand side
+    # of the user interface.
+
+    def key_selected(self, list, list_item):
+        key = list_item.children()[0].get()
+
+        # Look for a match in the value display function list
+
+        text = self.dict[list_item.get_data("raw_key")]
+
+        for entry in self.get_value_text_fns:
+            if re.match(entry[0], key):
+                text = entry[1](text)
+                break
+
+        self.set_value_text(text)
+
+    # Refresh the key list by removing all items and re-inserting them.
+    # Items are only inserted if they pass through the filter regexp.
+
+    def update_keylist(self):
+        self.list.remove_items(self.list.children())
+        self.set_value_text("")
+        for k in self.dict.keys():
+            if re.match(self.filter_regex, k):
+                self.add_key(k)
+
+    # Invoked when the user hits return in the filter text entry widget.
+
+    def filter_activated(self, entry):
+        self.filter_regex = entry.get_text()
+        self.update_keylist()
+
+    # Register a key display function
+
+    def register_get_key_text_fn(self, fn):
+        self.get_key_text = fn
+
+    # Register a value display function
+
+    def register_get_value_text_fn(self, regexp, fn):
+        self.get_value_text_fns.append((regexp, fn))
+
+#
+# A utility function to convert a string to the standard hex + ascii format.
+# To display all values in hex do:
+#   register_get_value_text_fn("", gtkdictbrowser.hex_string)
+#
+
+def hex_string(data):
+    """Return a hex dump of a string as a string.
+
+    The output produced is in the standard 16 characters per line hex +
+    ascii format:
+
+    00000000: 40 00 00 00 00 00 00 00  40 00 00 00 01 00 04 80  @....... @.......
+    00000010: 01 01 00 00 00 00 00 01  00 00 00 00              ........ ....
+    """
+    
+    pos = 0                             # Position in data
+    line = 0                            # Line of data
+    
+    hex = ""                            # Hex display
+    ascii = ""                          # ASCII display
+
+    result = ""
+    
+    while pos < len(data):
+        
+       # Start with header
+        
+       if pos % 16 == 0:
+            hex = "%08x: " % (line * 16)
+            ascii = ""
+            
+        # Add character
+            
+       hex = hex + "%02x " % (ord(data[pos]))
+        
+        if ord(data[pos]) < 32 or ord(data[pos]) > 176:
+            ascii = ascii + '.'
+        else:
+            ascii = ascii + data[pos]
+                
+        pos = pos + 1
+            
+        # Add separator if half way
+            
+       if pos % 16 == 8:
+            hex = hex + " "
+            ascii = ascii + " "
+
+        # End of line
+
+       if pos % 16 == 0:
+            result = result + "%s %s\n" % (hex, ascii)
+            line = line + 1
+            
+    # Leftover bits
+
+    if pos % 16 != 0:
+
+        # Pad hex string
+
+        for i in range(0, (16 - (pos % 16))):
+            hex = hex + "   "
+
+        # Half way separator
+
+        if (pos % 16) < 8:
+            hex = hex + " "
+
+        result = result + "%s %s\n" % (hex, ascii)
+
+    return result
+
+# For testing purposes, create a fixed dictionary to browse with
+
+if __name__ == "__main__":
+
+    dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}}
+
+    db = GtkDictBrowser(dict)
+
+    db.build_ui("GtkDictBrowser")
+
+    # Override Python's handling of ctrl-c so we can break out of the
+    # gui from the command line.
+
+    import signal
+    signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+    mainloop()
diff --git a/source4/python/mkpatch b/source4/python/mkpatch
new file mode 100755 (executable)
index 0000000..ab5be1b
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Make samba-head.patch.  Must be run from samba source directory.
+#
+
+cvs -z3 diff -u Makefile.in configure.in > python/samba-head.patch
diff --git a/source4/python/py_common.c b/source4/python/py_common.c
new file mode 100644 (file)
index 0000000..ea092d9
--- /dev/null
@@ -0,0 +1,259 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_common.h"
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_werror_tuple(WERROR werror)
+{
+       return Py_BuildValue("[is]", W_ERROR_V(werror), 
+                            dos_errstr(werror));
+}
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus)
+{
+       return Py_BuildValue("[is]", NT_STATUS_V(ntstatus), 
+                            nt_errstr(ntstatus));
+}
+
+/* Initialise samba client routines */
+
+static BOOL initialised;
+
+void py_samba_init(void)
+{
+       if (initialised)
+               return;
+
+       /* Load configuration file */
+
+       if (!lp_load(dyn_CONFIGFILE, True, False, False))
+               fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+       /* Misc other stuff */
+
+       load_interfaces();
+       init_names();
+
+       initialised = True;
+}
+
+/* Debuglevel routines */
+
+PyObject *get_debuglevel(PyObject *self, PyObject *args)
+{
+       PyObject *debuglevel;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+
+       debuglevel = PyInt_FromLong(DEBUGLEVEL);
+
+       return debuglevel;
+}
+
+PyObject *set_debuglevel(PyObject *self, PyObject *args)
+{
+       int debuglevel;
+
+       if (!PyArg_ParseTuple(args, "i", &debuglevel))
+               return NULL;
+
+       DEBUGLEVEL = debuglevel;
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Initialise logging */
+
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw)
+{
+       BOOL interactive = False;
+       char *logfilename = NULL;
+       static char *kwlist[] = {"interactive", "logfilename", NULL};
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "|is", kwlist, &interactive, &logfilename))
+               return NULL;
+       
+       if (interactive && logfilename) {
+               PyErr_SetString(PyExc_RuntimeError,
+                               "can't be interactive and set log file name");
+               return NULL;
+       }
+
+       if (interactive)
+               setup_logging("spoolss", True);
+
+       if (logfilename) {
+               lp_set_logfile(logfilename);
+               setup_logging(logfilename, False);
+               reopen_logs();
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Parse credentials from a python dictionary.  The dictionary can
+   only have the keys "username", "domain" and "password".  Return
+   True for valid credentials in which case the username, domain and
+   password are set to pointers to their values from the dicationary.
+   If returns False, the errstr is set to point at some mallocated
+   memory describing the error. */
+
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain, 
+                   char **password, char **errstr)
+{
+       /* Initialise anonymous credentials */
+
+       *username = "";
+       *domain = "";
+       *password = "";
+
+       if (creds && PyDict_Size(creds) > 0) {
+               PyObject *username_obj, *password_obj, *domain_obj;
+               PyObject *key, *value;
+               int i;
+
+               /* Check for presence of required fields */
+
+               username_obj = PyDict_GetItemString(creds, "username");
+               domain_obj = PyDict_GetItemString(creds, "domain");
+               password_obj = PyDict_GetItemString(creds, "password");
+
+               if (!username_obj) {
+                       *errstr = strdup("no username field in credential");
+                       return False;
+               }
+
+               if (!domain_obj) {
+                       *errstr = strdup("no domain field in credential");
+                       return False;
+               }
+
+               if (!password_obj) {
+                       *errstr = strdup("no password field in credential");
+                       return False;
+               }
+
+               /* Check type of required fields */
+
+               if (!PyString_Check(username_obj)) {
+                       *errstr = strdup("username field is not string type");
+                       return False;
+               }
+
+               if (!PyString_Check(domain_obj)) {
+                       *errstr = strdup("domain field is not string type");
+                       return False;
+               }
+
+               if (!PyString_Check(password_obj)) {
+                       *errstr = strdup("password field is not string type");
+                       return False;
+               }
+
+               /* Look for any extra fields */
+
+               i = 0;
+
+               while (PyDict_Next(creds, &i, &key, &value)) {
+                       if (strcmp(PyString_AsString(key), "domain") != 0 &&
+                           strcmp(PyString_AsString(key), "username") != 0 &&
+                           strcmp(PyString_AsString(key), "password") != 0) {
+                               asprintf(errstr,
+                                        "creds contain extra field '%s'",
+                                        PyString_AsString(key));
+                               return False;
+                       }
+               }
+
+               /* Assign values */
+
+               *username = PyString_AsString(username_obj);
+               *domain = PyString_AsString(domain_obj);
+               *password = PyString_AsString(password_obj);
+       }
+
+       *errstr = NULL;
+
+       return True;
+}
+
+/* Return a cli_state to a RPC pipe on the given server.  Use the
+   credentials passed if not NULL.  If an error occurs errstr is set to a
+   string describing the error and NULL is returned.  If set, errstr must
+   be freed by calling free(). */
+
+struct cli_state *open_pipe_creds(char *server, PyObject *creds, 
+                                 int pipe_idx, char **errstr)
+{
+       char *username, *password, *domain;
+       struct cli_state *cli;
+       NTSTATUS result;
+       
+       /* Extract credentials from the python dictionary */
+
+       if (!py_parse_creds(creds, &username, &domain, &password, errstr))
+               return NULL;
+
+       /* Now try to connect */
+
+       result = cli_full_connection(
+               &cli, NULL, server, NULL, 0, "IPC$", "IPC",
+               username, domain, password, 0, NULL);
+       
+       if (!NT_STATUS_IS_OK(result)) {
+               *errstr = strdup("error connecting to IPC$ pipe");
+               return NULL;
+       }
+
+       if (!cli_nt_session_open(cli, pipe_idx)) {
+               cli_shutdown(cli);
+               asprintf(errstr, "error opening pipe index %d", pipe_idx);
+               return NULL;
+       }
+
+       *errstr = NULL;
+
+       return cli;
+}
+
+/* Return true if a dictionary contains a "level" key with an integer
+   value.  Set the value if so. */
+
+BOOL get_level_value(PyObject *dict, uint32 *level)
+{
+       PyObject *obj;
+
+       if (!(obj = PyDict_GetItemString(dict, "level")) ||
+           !PyInt_Check(obj))
+               return False;
+
+       if (level)
+               *level = PyInt_AsLong(obj);
+
+       return True;
+}
diff --git a/source4/python/py_common.h b/source4/python/py_common.h
new file mode 100644 (file)
index 0000000..2bbd148
--- /dev/null
@@ -0,0 +1,67 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002-2003
+   
+   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.
+*/
+
+#ifndef _PY_COMMON_H
+#define _PY_COMMON_H
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+   annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Return a cli_state struct opened on the specified pipe.  If credentials
+   are passed use them. */
+
+typedef struct cli_state *(cli_pipe_fn)(
+       struct cli_state *cli, char *system_name,
+       struct ntuser_creds *creds);
+
+/* The following definitions come from python/py_common.c  */
+
+PyObject *py_werror_tuple(WERROR werror);
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus);
+void py_samba_init(void);
+PyObject *get_debuglevel(PyObject *self, PyObject *args);
+PyObject *set_debuglevel(PyObject *self, PyObject *args);
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw);
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain, 
+                   char **password, char **errstr);
+struct cli_state *open_pipe_creds(char *server, PyObject *creds, 
+                                 int pipe_idx, char **errstr);
+BOOL get_level_value(PyObject *dict, uint32 *level);
+
+/* The following definitions come from python/py_ntsec.c  */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid);
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj);
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace);
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict);
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl);
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx);
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd);
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx);
+
+#endif /*  _PY_COMMON_H  */
diff --git a/source4/python/py_conv.c b/source4/python/py_conv.c
new file mode 100644 (file)
index 0000000..d0a2d78
--- /dev/null
@@ -0,0 +1,223 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "py_conv.h"
+
+/* Helper for rpcstr_pull() function */
+
+static void fstr_pull(fstring str, UNISTR *uni)
+{
+       rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+static void fstr_pull2(fstring str, UNISTR2 *uni)
+{
+       rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/* Convert a structure to a Python dict */
+
+PyObject *from_struct(void *s, struct pyconv *conv)
+{
+       PyObject *obj, *item;
+       int i;
+
+       obj = PyDict_New();
+
+       for (i = 0; conv[i].name; i++) {
+               switch (conv[i].type) {
+               case PY_UNISTR: {
+                       UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+                       fstring str = "";
+
+                       if (u->buffer)
+                               fstr_pull(str, u);
+
+                       item = PyString_FromString(str);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               case PY_UNISTR2: {
+                       UNISTR2 *u = (UNISTR2 *)((char *)s + conv[i].offset);
+                       fstring str = "";
+
+                       if (u->buffer)
+                               fstr_pull2(str, u);
+
+                       item = PyString_FromString(str);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               case PY_UINT32: {
+                       uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+                       item = PyInt_FromLong(*u);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+                       
+                       break;
+               }
+               case PY_UINT16: {
+                       uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+                       item = PyInt_FromLong(*u);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               case PY_STRING: {
+                       char *str = (char *)s + conv[i].offset;
+
+                       item = PyString_FromString(str);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               case PY_UID: {
+                       uid_t *uid = (uid_t *)((char *)s + conv[i].offset);
+
+                       item = PyInt_FromLong(*uid);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               case PY_GID: {
+                       gid_t *gid = (gid_t *)((char *)s + conv[i].offset);
+
+                       item = PyInt_FromLong(*gid);
+                       PyDict_SetItemString(obj, conv[i].name, item);
+
+                       break;
+               }
+               default:
+                       
+                       break;
+               }
+       }
+
+       return obj;
+}
+
+/* Convert a Python dict to a structure */
+
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv)
+{
+       PyObject *visited, *key, *value;
+       BOOL result = False;
+       int i;
+
+       visited = PyDict_New();
+
+       for (i = 0; conv[i].name; i++) {
+               PyObject *obj;
+               
+               obj = PyDict_GetItemString(dict, conv[i].name);
+
+               if (!obj)
+                       goto done;
+               
+               switch (conv[i].type) {
+               case PY_UNISTR: {
+                       UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+                       char *str = "";
+
+                       if (!PyString_Check(obj))
+                               goto done;
+
+                       str = PyString_AsString(obj);
+                       init_unistr(u, str);
+                       
+                       break;
+               }
+               case PY_UINT32: {
+                       uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+                       if (!PyInt_Check(obj))
+                               goto done;
+
+                       *u = PyInt_AsLong(obj);
+
+                       break;
+               }
+               case PY_UINT16: {
+                       uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+                       if (!PyInt_Check(obj)) 
+                               goto done;
+
+                       *u = PyInt_AsLong(obj);
+                       break;
+               }
+               default:
+                       break;
+               }
+
+               /* Mark as visited */
+
+               PyDict_SetItemString(visited, conv[i].name, 
+                                    PyInt_FromLong(1));
+       }
+
+       /* Iterate over each item in the input dictionary and see if it was
+          visited.  If it wasn't then the user has added some extra crap
+          to the dictionary. */
+
+       i = 0;
+
+       while (PyDict_Next(dict, &i, &key, &value)) {
+               if (!PyDict_GetItem(visited, key))
+                       goto done;
+       }
+
+       result = True;
+
+done:
+       /* We must decrement the reference count here or the visited
+          dictionary will not be freed. */
+              
+       Py_DECREF(visited);
+
+       return result;
+}
+
+/* Convert a NULL terminated list of NULL terminated unicode strings
+   to a list of (char *) strings */
+
+PyObject *from_unistr_list(uint16 *dependentfiles)
+{
+       PyObject *list;
+       int offset = 0;
+
+       list = PyList_New(0);
+
+       while (*(dependentfiles + offset) != 0) {
+               fstring name;
+               int len;
+
+               len = rpcstr_pull(name, dependentfiles + offset,
+                                 sizeof(fstring), -1, STR_TERMINATE);
+
+               offset += len / 2;
+               PyList_Append(list, PyString_FromString(name));
+       }
+
+       return list;
+}
diff --git a/source4/python/py_conv.h b/source4/python/py_conv.h
new file mode 100644 (file)
index 0000000..798661c
--- /dev/null
@@ -0,0 +1,44 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_CONV_H
+#define _PY_CONV_H
+
+#include "python/py_common.h"
+
+enum pyconv_types { PY_UNISTR, PY_UNISTR2, PY_UINT32, PY_UINT16, PY_STRING, 
+                   PY_UID, PY_GID };
+
+struct pyconv {
+       char *name;             /* Name of member */
+       enum pyconv_types type; /* Type */
+       size_t offset;          /* Offset into structure */
+};
+
+PyObject *from_struct(void *s, struct pyconv *conv);
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv);
+PyObject *from_unistr_list(uint16 *dependentfiles);
+
+/* Another version of offsetof (-: */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif /* _PY_CONV_H */
diff --git a/source4/python/py_lsa.c b/source4/python/py_lsa.c
new file mode 100644 (file)
index 0000000..22db296
--- /dev/null
@@ -0,0 +1,468 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_lsa.h"
+
+PyObject *new_lsa_policy_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *pol)
+{
+       lsa_policy_hnd_object *o;
+
+       o = PyObject_New(lsa_policy_hnd_object, &lsa_policy_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+/* 
+ * Exceptions raised by this module 
+ */
+
+PyObject *lsa_error;           /* This indicates a non-RPC related error
+                                  such as name lookup failure */
+
+PyObject *lsa_ntstatus;                /* This exception is raised when a RPC call
+                                  returns a status code other than
+                                  NT_STATUS_OK */
+
+/*
+ * Open/close lsa handles
+ */
+
+static PyObject *lsa_open_policy(PyObject *self, PyObject *args, 
+                               PyObject *kw) 
+{
+       static char *kwlist[] = { "servername", "creds", "access", NULL };
+       char *server, *errstr;
+       PyObject *creds = NULL, *result = NULL;
+       uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+       struct cli_state *cli = NULL;
+       NTSTATUS ntstatus;
+       TALLOC_CTX *mem_ctx = NULL;
+       POLICY_HND hnd;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|Oi", kwlist, &server, &creds, &desired_access))
+               return NULL;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (!(cli = open_pipe_creds(server, creds, PI_LSARPC, &errstr))) {
+               PyErr_SetString(lsa_error, errstr);
+               free(errstr);
+               return NULL;
+       }
+
+       if (!(mem_ctx = talloc_init("lsa_open_policy"))) {
+               PyErr_SetString(lsa_error, "unable to init talloc context\n");
+               goto done;
+       }
+
+       ntstatus = cli_lsa_open_policy(cli, mem_ctx, True,
+                                      SEC_RIGHTS_MAXIMUM_ALLOWED, &hnd);
+
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+               goto done;
+       }
+
+       result = new_lsa_policy_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+       if (!result) {
+               if (cli)
+                       cli_shutdown(cli);
+
+               if (mem_ctx)
+                       talloc_destroy(mem_ctx);
+       }
+
+       return result;
+}
+
+static PyObject *lsa_close(PyObject *self, PyObject *args, PyObject *kw) 
+{
+       PyObject *po;
+       lsa_policy_hnd_object *hnd;
+       NTSTATUS result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTuple(args, "O!", &lsa_policy_hnd_type, &po))
+               return NULL;
+
+       hnd = (lsa_policy_hnd_object *)po;
+
+       /* Call rpc function */
+
+       result = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+       /* Cleanup samba stuff */
+
+       cli_shutdown(hnd->cli);
+       talloc_destroy(hnd->mem_ctx);
+
+       /* Return value */
+
+       Py_INCREF(Py_None);
+       return Py_None; 
+}
+
+static PyObject *lsa_lookup_names(PyObject *self, PyObject *args)
+{
+       PyObject *py_names, *result;
+       NTSTATUS ntstatus;
+       lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+       int num_names, i;
+       const char **names;
+       DOM_SID *sids;
+       uint32 *name_types;
+
+       if (!PyArg_ParseTuple(args, "O", &py_names))
+               return NULL;
+
+       if (!PyList_Check(py_names) && !PyString_Check(py_names)) {
+               PyErr_SetString(PyExc_TypeError, "must be list or string");
+               return NULL;
+       }
+
+       if (PyList_Check(py_names)) {
+
+               /* Convert list to char ** array */
+
+               num_names = PyList_Size(py_names);
+               names = (const char **)talloc(
+                       hnd->mem_ctx, num_names * sizeof(char *));
+               
+               for (i = 0; i < num_names; i++) {
+                       PyObject *obj = PyList_GetItem(py_names, i);
+                       
+                       names[i] = talloc_strdup(hnd->mem_ctx, PyString_AsString(obj));
+               }
+
+       } else {
+
+               /* Just a single element */
+
+               num_names = 1;
+               names = (const char **)talloc(hnd->mem_ctx, sizeof(char *));
+
+               names[0] = PyString_AsString(py_names);
+       }
+
+       ntstatus = cli_lsa_lookup_names(hnd->cli, hnd->mem_ctx, &hnd->pol,
+                                       num_names, names, &sids, &name_types);
+
+       if (!NT_STATUS_IS_OK(ntstatus) && NT_STATUS_V(ntstatus) != 0x107) {
+               PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+               return NULL;
+       }
+
+       result = PyList_New(num_names);
+
+       for (i = 0; i < num_names; i++) {
+               PyObject *sid_obj, *obj;
+
+               py_from_SID(&sid_obj, &sids[i]);
+
+               obj = Py_BuildValue("(Oi)", sid_obj, name_types[i]);
+
+               PyList_SetItem(result, i, obj);
+       }
+       
+       return result;
+}
+
+static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args, 
+                                PyObject *kw) 
+{
+       PyObject *py_sids, *result;
+       NTSTATUS ntstatus;
+       int num_sids, i;
+       char **domains, **names;
+       uint32 *types;
+       lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+       DOM_SID *sids;
+
+       if (!PyArg_ParseTuple(args, "O", &py_sids))
+               return NULL;
+
+       if (!PyList_Check(py_sids) && !PyString_Check(py_sids)) {
+               PyErr_SetString(PyExc_TypeError, "must be list or string");
+               return NULL;
+       }
+
+       if (PyList_Check(py_sids)) {
+
+               /* Convert dictionary to char ** array */
+               
+               num_sids = PyList_Size(py_sids);
+               sids = (DOM_SID *)talloc(hnd->mem_ctx, num_sids * sizeof(DOM_SID));
+               
+               memset(sids, 0, num_sids * sizeof(DOM_SID));
+               
+               for (i = 0; i < num_sids; i++) {
+                       PyObject *obj = PyList_GetItem(py_sids, i);
+                       
+                       if (!string_to_sid(&sids[i], PyString_AsString(obj))) {
+                               PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+                               return NULL;
+                       }
+               }
+
+       } else {
+
+               /* Just a single element */
+
+               num_sids = 1;
+               sids = (DOM_SID *)talloc(hnd->mem_ctx, sizeof(DOM_SID));
+
+               if (!string_to_sid(&sids[0], PyString_AsString(py_sids))) {
+                       PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+                       return NULL;
+               }
+       }
+
+       ntstatus = cli_lsa_lookup_sids(hnd->cli, hnd->mem_ctx, &hnd->pol,
+                                      num_sids, sids, &domains, &names, 
+                                      &types);
+
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+               return NULL;
+       }
+
+       result = PyList_New(num_sids);
+
+       for (i = 0; i < num_sids; i++) {
+               PyObject *obj;
+
+               obj = Py_BuildValue("{sssssi}", "username", names[i],
+                                   "domain", domains[i], "name_type", 
+                                   types[i]);
+
+               PyList_SetItem(result, i, obj);
+       }
+       
+       return result;
+}
+
+static PyObject *lsa_enum_trust_dom(PyObject *self, PyObject *args)
+{
+       lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+       NTSTATUS ntstatus;
+       uint32 enum_ctx = 0, num_domains, i;
+       char **domain_names;
+       DOM_SID *domain_sids;
+       PyObject *result;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+       
+       ntstatus = cli_lsa_enum_trust_dom(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, &enum_ctx,
+               &num_domains, &domain_names, &domain_sids);
+
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+               return NULL;
+       }
+
+       result = PyList_New(num_domains);
+
+       for (i = 0; i < num_domains; i++) {
+               fstring sid_str;
+
+               sid_to_string(sid_str, &domain_sids[i]);
+               PyList_SetItem(
+                       result, i, 
+                       Py_BuildValue("(ss)", domain_names[i], sid_str));
+       }
+
+       return result;
+}
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef lsa_hnd_methods[] = {
+
+       /* SIDs<->names */
+
+       { "lookup_sids", (PyCFunction)lsa_lookup_sids, 
+         METH_VARARGS | METH_KEYWORDS,
+         "Convert sids to names." },
+
+       { "lookup_names", (PyCFunction)lsa_lookup_names, 
+         METH_VARARGS | METH_KEYWORDS,
+         "Convert names to sids." },
+
+       /* Trusted domains */
+
+       { "enum_trusted_domains", (PyCFunction)lsa_enum_trust_dom, 
+         METH_VARARGS, 
+         "Enumerate trusted domains." },
+
+       { NULL }
+};
+
+static void py_lsa_policy_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyObject *py_lsa_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(lsa_hnd_methods, self, attrname);
+}
+
+PyTypeObject lsa_policy_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "LSA Policy Handle",
+       sizeof(lsa_policy_hnd_object),
+       0,
+       py_lsa_policy_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_lsa_policy_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+static PyMethodDef lsa_methods[] = {
+
+       /* Open/close lsa handles */
+       
+       { "open_policy", (PyCFunction)lsa_open_policy, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Open a policy handle" },
+       
+       { "close", (PyCFunction)lsa_close, 
+         METH_VARARGS, 
+         "Close a policy handle" },
+
+       /* Other stuff - this should really go into a samba config module
+          but for the moment let's leave it here. */
+
+       { "setup_logging", (PyCFunction)py_setup_logging, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Set up debug logging.
+
+Initialises Samba's debug logging system.  One argument is expected which
+is a boolean specifying whether debugging is interactive and sent to stdout
+or logged to a file.
+
+Example:
+
+>>> spoolss.setup_logging(interactive = 1)" },
+
+       { "get_debuglevel", (PyCFunction)get_debuglevel, 
+         METH_VARARGS, 
+         "Set the current debug level.
+
+Example:
+
+>>> spoolss.get_debuglevel()
+0" },
+
+       { "set_debuglevel", (PyCFunction)set_debuglevel, 
+         METH_VARARGS, 
+         "Get the current debug level.
+
+Example:
+
+>>> spoolss.set_debuglevel(10)" },
+
+       { NULL }
+};
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+       { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/*
+ * Module initialisation 
+ */
+
+void initlsa(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("lsa", lsa_methods);
+       dict = PyModule_GetDict(module);
+
+       lsa_error = PyErr_NewException("lsa.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", lsa_error);
+
+       lsa_ntstatus = PyErr_NewException("lsa.ntstatus", NULL, NULL);
+       PyDict_SetItemString(dict, "ntstatus", lsa_ntstatus);
+
+       /* Initialise policy handle object */
+
+       lsa_policy_hnd_type.ob_type = &PyType_Type;
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+
+       setup_logging("lsa", True);
+       DEBUGLEVEL = 10;
+}
diff --git a/source4/python/py_lsa.h b/source4/python/py_lsa.h
new file mode 100644 (file)
index 0000000..99f3de5
--- /dev/null
@@ -0,0 +1,41 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_LSA_H
+#define _PY_LSA_H
+
+#include "python/py_common.h"
+
+/* LSA policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND pol;
+} lsa_policy_hnd_object;
+     
+/* Exceptions raised by this module */
+
+extern PyTypeObject lsa_policy_hnd_type;
+
+extern PyObject *lsa_error;
+
+#endif /* _PY_LSA_H */
diff --git a/source4/python/py_ntsec.c b/source4/python/py_ntsec.c
new file mode 100644 (file)
index 0000000..47524d8
--- /dev/null
@@ -0,0 +1,281 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_common.h"
+
+/* Convert a SID to a Python dict */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid)
+{
+       fstring sidstr;
+
+       if (!sid) {
+               Py_INCREF(Py_None);
+               *obj = Py_None;
+               return True;
+       }
+
+       if (!sid_to_string(sidstr, sid))
+               return False;
+
+       *obj = PyString_FromString(sidstr);
+
+       return True;
+}
+
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj)
+{
+       if (!PyString_Check(obj))
+               return False;
+
+       return string_to_sid(sid, PyString_AsString(obj));
+}
+
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace)
+{
+       PyObject *obj;
+
+       if (!ace) {
+               Py_INCREF(Py_None);
+               *dict = Py_None;
+               return True;
+       }
+
+       *dict = PyDict_New();
+
+       PyDict_SetItemString(*dict, "type", PyInt_FromLong(ace->type));
+       PyDict_SetItemString(*dict, "flags", PyInt_FromLong(ace->flags));
+       PyDict_SetItemString(*dict, "mask", PyInt_FromLong(ace->info.mask));
+
+       if (py_from_SID(&obj, &ace->trustee))
+               PyDict_SetItemString(*dict, "trustee", obj);
+
+       return True;
+}
+
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict)
+{
+       PyObject *obj;
+       uint8 ace_type, ace_flags;
+       DOM_SID trustee;
+       SEC_ACCESS sec_access;
+
+       if (!PyDict_Check(dict))
+               return False;
+
+       if (!(obj = PyDict_GetItemString(dict, "type")) ||
+           !PyInt_Check(obj))
+               return False;
+
+       ace_type = PyInt_AsLong(obj);
+
+       if (!(obj = PyDict_GetItemString(dict, "flags")) ||
+           !PyInt_Check(obj))
+               return False;
+
+       ace_flags = PyInt_AsLong(obj);
+
+       if (!(obj = PyDict_GetItemString(dict, "trustee")) ||
+           !PyString_Check(obj))
+               return False;
+
+       if (!py_to_SID(&trustee, obj))
+               return False;
+
+       if (!(obj = PyDict_GetItemString(dict, "mask")) ||
+           !PyInt_Check(obj))
+               return False;
+
+       sec_access.mask = PyInt_AsLong(obj);
+
+       init_sec_ace(ace, &trustee, ace_type, sec_access, ace_flags);
+
+       /* Fill in size field */
+
+       ace->size = SEC_ACE_HEADER_SIZE + sid_size(&trustee);
+
+       return True;
+}
+
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl)
+{
+       PyObject *ace_list;
+       int i;
+
+       if (!acl) {
+               Py_INCREF(Py_None);
+               *dict = Py_None;
+               return True;
+       }
+
+       *dict = PyDict_New();
+
+       PyDict_SetItemString(*dict, "revision", PyInt_FromLong(acl->revision));
+
+       ace_list = PyList_New(acl->num_aces);
+
+       for (i = 0; i < acl->num_aces; i++) {
+               PyObject *obj;
+
+               if (py_from_ACE(&obj, &acl->ace[i]))
+                       PyList_SetItem(ace_list, i, obj);
+       }
+
+       PyDict_SetItemString(*dict, "ace_list", ace_list);
+
+       return True;
+}
+
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+       PyObject *obj;
+       uint32 i;
+
+       if (!(obj = PyDict_GetItemString(dict, "revision")) ||
+           !PyInt_Check(obj))
+               return False;
+
+       acl->revision = PyInt_AsLong(obj);
+
+       if (!(obj = PyDict_GetItemString(dict, "ace_list")) ||
+           !PyList_Check(obj)) 
+               return False;
+       
+       acl->num_aces = PyList_Size(obj);
+
+       acl->ace = talloc(mem_ctx, acl->num_aces * sizeof(SEC_ACE));
+       acl->size = SEC_ACL_HEADER_SIZE;
+
+       for (i = 0; i < acl->num_aces; i++) {
+               PyObject *py_ace = PyList_GetItem(obj, i);
+
+               if (!py_to_ACE(&acl->ace[i], py_ace))
+                       return False;
+
+               acl->size += acl->ace[i].size;
+       }
+
+       return True;
+}
+
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd)
+{
+       PyObject *obj;
+
+       *dict = PyDict_New();
+
+       PyDict_SetItemString(*dict, "revision", PyInt_FromLong(sd->revision));
+
+       if (py_from_SID(&obj, sd->owner_sid))
+               PyDict_SetItemString(*dict, "owner_sid", obj);
+
+       if (py_from_SID(&obj, sd->grp_sid))
+               PyDict_SetItemString(*dict, "group_sid", obj);
+
+       if (py_from_ACL(&obj, sd->dacl))
+               PyDict_SetItemString(*dict, "dacl", obj);
+
+       if (py_from_ACL(&obj, sd->sacl))
+               PyDict_SetItemString(*dict, "sacl", obj);
+
+       return True;
+}
+
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+       PyObject *obj;
+       uint16 revision;
+       DOM_SID owner_sid, group_sid;
+       SEC_ACL sacl, dacl;
+       BOOL got_dacl = False, got_sacl = False;
+       BOOL got_owner_sid = False, got_group_sid = False;
+
+       ZERO_STRUCT(dacl); ZERO_STRUCT(sacl);
+       ZERO_STRUCT(owner_sid); ZERO_STRUCT(group_sid);
+
+       if (!(obj = PyDict_GetItemString(dict, "revision")))
+               return False;
+
+       revision = PyInt_AsLong(obj);
+
+       if ((obj = PyDict_GetItemString(dict, "owner_sid"))) {
+
+               if (obj != Py_None) {
+
+                       if (!py_to_SID(&owner_sid, obj))
+                               return False;
+
+                       got_owner_sid = True;
+               }
+       }
+
+       if ((obj = PyDict_GetItemString(dict, "group_sid"))) {
+
+               if (obj != Py_None) {
+
+                       if (!py_to_SID(&group_sid, obj))
+                               return False;
+                       
+                       got_group_sid = True;
+               }
+       }
+
+       if ((obj = PyDict_GetItemString(dict, "dacl"))) {
+
+               if (obj != Py_None) {
+
+                       if (!py_to_ACL(&dacl, obj, mem_ctx))
+                               return False;
+                       
+                       got_dacl = True;
+               }
+       }
+
+       if ((obj = PyDict_GetItemString(dict, "sacl"))) {
+
+               if (obj != Py_None) {
+
+                       if (!py_to_ACL(&sacl, obj, mem_ctx))
+                               return False;
+
+                       got_sacl = True;
+               }
+       }
+
+#if 0                          /* For new secdesc code */
+       *sd = make_sec_desc(mem_ctx, revision, 
+                           got_owner_sid ? &owner_sid : NULL, 
+                           got_group_sid ? &group_sid : NULL,
+                           got_sacl ? &sacl : NULL, 
+                           got_dacl ? &dacl : NULL);
+#else
+       {
+               size_t sd_size;
+
+               *sd = make_sec_desc(mem_ctx, revision,
+                           got_owner_sid ? &owner_sid : NULL, 
+                           got_group_sid ? &group_sid : NULL,
+                           got_sacl ? &sacl : NULL, 
+                           got_dacl ? &dacl : NULL, &sd_size);
+       }
+#endif
+
+       return True;
+}
diff --git a/source4/python/py_samba.c b/source4/python/py_samba.c
new file mode 100644 (file)
index 0000000..c0ade12
--- /dev/null
@@ -0,0 +1,56 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "Python.h"
+#include "python/py_common.h"
+
+/*
+ * Module initialisation 
+ */
+
+static PyObject *lsa_open_policy(PyObject *self, PyObject *args, 
+                               PyObject *kw) 
+{
+       return NULL;
+}
+
+static PyMethodDef samba_methods[] = {
+       { NULL }
+};
+
+static PyMethodDef cheepy_methods[] = {
+       { "open_policy", (PyCFunction)lsa_open_policy, METH_VARARGS|METH_KEYWORDS,
+         "Foo"},
+       { NULL }
+};
+
+void initsamba(void)
+{
+       PyObject *module, *new_module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("samba", samba_methods);
+       dict = PyModule_GetDict(module);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+}
diff --git a/source4/python/py_samr.c b/source4/python/py_samr.c
new file mode 100644 (file)
index 0000000..182671d
--- /dev/null
@@ -0,0 +1,497 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_samr.h"
+
+/* 
+ * Exceptions raised by this module 
+ */
+
+PyObject *samr_error;          /* This indicates a non-RPC related error
+                                  such as name lookup failure */
+
+PyObject *samr_ntstatus;       /* This exception is raised when a RPC call
+                                  returns a status code other than
+                                  NT_STATUS_OK */
+
+/* SAMR connect handle object */
+
+static void py_samr_connect_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+PyObject *new_samr_domain_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                    POLICY_HND *pol)
+{
+       samr_domain_hnd_object *o;
+
+       o = PyObject_New(samr_domain_hnd_object, &samr_domain_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->domain_pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+static PyObject *samr_open_domain(PyObject *self, PyObject *args, PyObject *kw)
+{
+       samr_connect_hnd_object *connect_hnd = (samr_connect_hnd_object *)self;
+       static char *kwlist[] = { "sid", "access", NULL };
+       uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+       char *sid_str;
+       DOM_SID sid;
+       TALLOC_CTX *mem_ctx = NULL;
+       POLICY_HND domain_pol;
+       NTSTATUS ntstatus;
+       PyObject *result = NULL;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|i", kwlist, &sid_str, &desired_access))
+               return NULL;
+
+       if (!string_to_sid(&sid, sid_str)) {
+               PyErr_SetString(PyExc_TypeError, "string is not a sid");
+               return NULL;
+       }
+
+       if (!(mem_ctx = talloc_init("samr_open_domain"))) {
+               PyErr_SetString(samr_error, "unable to init talloc context");
+               return NULL;
+       }
+
+       ntstatus = cli_samr_open_domain(
+               connect_hnd->cli, mem_ctx, &connect_hnd->connect_pol,
+               desired_access, &sid, &domain_pol);
+                                       
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+               goto done;
+       }
+
+       result = new_samr_domain_hnd_object(
+               connect_hnd->cli, mem_ctx, &domain_pol);
+
+done:
+       if (!result) {
+               if (mem_ctx)
+                       talloc_destroy(mem_ctx);
+       }
+
+       return result;
+}
+
+static PyMethodDef samr_connect_methods[] = {
+       { "open_domain", (PyCFunction)samr_open_domain,
+         METH_VARARGS | METH_KEYWORDS,
+         "Open a handle on a domain" },
+
+       { NULL }
+};
+
+static PyObject *py_samr_connect_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(samr_connect_methods, self, attrname);
+}
+
+PyTypeObject samr_connect_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SAMR Connect Handle",
+       sizeof(samr_connect_hnd_object),
+       0,
+       py_samr_connect_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_samr_connect_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+PyObject *new_samr_connect_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                     POLICY_HND *pol)
+{
+       samr_connect_hnd_object *o;
+
+       o = PyObject_New(samr_connect_hnd_object, &samr_connect_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->connect_pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+/* SAMR domain handle object */
+
+static void py_samr_domain_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyObject *samr_enum_dom_groups(PyObject *self, PyObject *args, 
+                                     PyObject *kw)
+{
+       samr_domain_hnd_object *domain_hnd = (samr_domain_hnd_object *)self;
+       static char *kwlist[] = { NULL };
+       TALLOC_CTX *mem_ctx;
+/*     uint32 desired_access = MAXIMUM_ALLOWED_ACCESS; */
+       uint32 start_idx, size, num_dom_groups;
+       struct acct_info *dom_groups;
+       NTSTATUS result;
+       PyObject *py_result = NULL;
+       
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "", kwlist))
+               return NULL;
+
+       if (!(mem_ctx = talloc_init("samr_enum_dom_groups"))) {
+               PyErr_SetString(samr_error, "unable to init talloc context");
+               return NULL;
+       }
+
+       start_idx = 0;
+       size = 0xffff;
+
+       do {
+               result = cli_samr_enum_dom_groups(
+                       domain_hnd->cli, mem_ctx, &domain_hnd->domain_pol,
+                       &start_idx, size, &dom_groups, &num_dom_groups);
+
+               if (NT_STATUS_IS_OK(result) ||
+                   NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+                       py_from_acct_info(&py_result, dom_groups,
+                                         num_dom_groups);
+               }
+
+       } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+       return py_result;
+}      
+
+static PyMethodDef samr_domain_methods[] = {
+       { "enum_domain_groups", (PyCFunction)samr_enum_dom_groups,
+         METH_VARARGS | METH_KEYWORDS, "Enumerate domain groups" },
+       { NULL }
+};
+
+static PyObject *py_samr_domain_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(samr_domain_methods, self, attrname);
+}
+
+PyTypeObject samr_domain_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SAMR Domain Handle",
+       sizeof(samr_domain_hnd_object),
+       0,
+       py_samr_domain_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_samr_domain_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+/* SAMR user handle object */
+
+static void py_samr_user_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyMethodDef samr_user_methods[] = {
+       { NULL }
+};
+
+static PyObject *py_samr_user_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(samr_user_methods, self, attrname);
+}
+
+PyTypeObject samr_user_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SAMR User Handle",
+       sizeof(samr_user_hnd_object),
+       0,
+       py_samr_user_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_samr_user_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+PyObject *new_samr_user_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                     POLICY_HND *pol)
+{
+       samr_user_hnd_object *o;
+
+       o = PyObject_New(samr_user_hnd_object, &samr_user_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->user_pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+/* SAMR group handle object */
+
+static void py_samr_group_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyMethodDef samr_group_methods[] = {
+       { NULL }
+};
+
+static PyObject *py_samr_group_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(samr_group_methods, self, attrname);
+}
+
+PyTypeObject samr_group_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SAMR Group Handle",
+       sizeof(samr_group_hnd_object),
+       0,
+       py_samr_group_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_samr_group_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+PyObject *new_samr_group_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                     POLICY_HND *pol)
+{
+       samr_group_hnd_object *o;
+
+       o = PyObject_New(samr_group_hnd_object, &samr_group_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->group_pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+/* Alias handle object */
+
+static void py_samr_alias_hnd_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyMethodDef samr_alias_methods[] = {
+       { NULL }
+};
+
+static PyObject *py_samr_alias_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(samr_alias_methods, self, attrname);
+}
+
+PyTypeObject samr_alias_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SAMR Alias Handle",
+       sizeof(samr_alias_hnd_object),
+       0,
+       py_samr_alias_hnd_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_samr_alias_hnd_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+PyObject *new_samr_alias_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                     POLICY_HND *pol)
+{
+       samr_alias_hnd_object *o;
+
+       o = PyObject_New(samr_alias_hnd_object, &samr_alias_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->alias_pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
+
+static PyObject *samr_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = { "server", "creds", "access", NULL };
+       uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+       char *server, *errstr;
+       struct cli_state *cli = NULL;
+       POLICY_HND hnd;
+       TALLOC_CTX *mem_ctx = NULL;
+       PyObject *result = NULL, *creds = NULL;
+       NTSTATUS ntstatus;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|Oi", kwlist, &server, &creds,
+                   &desired_access)) 
+               return NULL;
+
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SAMR, &errstr))) {
+               PyErr_SetString(samr_error, errstr);
+               free(errstr);
+               return NULL;
+       }
+
+       if (!(mem_ctx = talloc_init("samr_connect"))) {
+               PyErr_SetString(samr_ntstatus,
+                               "unable to init talloc context\n");
+               goto done;
+       }
+
+       ntstatus = cli_samr_connect(cli, mem_ctx, desired_access, &hnd);
+
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               cli_shutdown(cli);
+               PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+               goto done;
+       }
+
+       result = new_samr_connect_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+       if (!result) {
+               if (cli)
+                       cli_shutdown(cli);
+
+               if (mem_ctx)
+                       talloc_destroy(mem_ctx);
+       }
+
+       return result;
+}
+
+/*
+ * Module initialisation 
+ */
+
+static PyMethodDef samr_methods[] = {
+
+       /* Open/close samr connect handles */
+       
+       { "connect", (PyCFunction)samr_connect, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Open a connect handle" },
+       
+       { NULL }
+};
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+       { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+void initsamr(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("samr", samr_methods);
+       dict = PyModule_GetDict(module);
+
+       samr_error = PyErr_NewException("samr.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", samr_error);
+
+       samr_ntstatus = PyErr_NewException("samr.ntstatus", NULL, NULL);
+       PyDict_SetItemString(dict, "ntstatus", samr_ntstatus);
+
+       /* Initialise policy handle object */
+
+       samr_connect_hnd_type.ob_type = &PyType_Type;
+       samr_domain_hnd_type.ob_type = &PyType_Type;
+       samr_user_hnd_type.ob_type = &PyType_Type;
+       samr_group_hnd_type.ob_type = &PyType_Type;
+       samr_alias_hnd_type.ob_type = &PyType_Type;
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+
+       setup_logging("samr", True);
+       DEBUGLEVEL = 10;
+}
diff --git a/source4/python/py_samr.h b/source4/python/py_samr.h
new file mode 100644 (file)
index 0000000..3292eb9
--- /dev/null
@@ -0,0 +1,81 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_SAMR_H
+#define _PY_SAMR_H
+
+#include "python/py_common.h"
+
+/* SAMR connect policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND connect_pol;
+} samr_connect_hnd_object;
+     
+/* SAMR domain policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND domain_pol;
+} samr_domain_hnd_object;
+
+/* SAMR user policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND user_pol;
+} samr_user_hnd_object;
+
+/* SAMR group policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND group_pol;
+} samr_group_hnd_object;
+     
+/* SAMR alias policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND alias_pol;
+} samr_alias_hnd_object;
+     
+extern PyTypeObject samr_connect_hnd_type, samr_domain_hnd_type,
+       samr_user_hnd_type, samr_group_hnd_type, samr_alias_hnd_type; 
+
+/* Exceptions raised by this module */
+
+extern PyObject *samr_error;
+
+/* The following definitions are from py_samr_conv.c */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts);
+#endif /* _PY_SAMR_H */
diff --git a/source4/python/py_samr_conv.c b/source4/python/py_samr_conv.c
new file mode 100644 (file)
index 0000000..fdf7164
--- /dev/null
@@ -0,0 +1,58 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_samr.h"
+#include "python/py_conv.h"
+
+/*
+ * Convert between acct_info and Python 
+ */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts)
+{
+       int i;
+
+       *array = PyList_New(num_accts);
+
+       for (i = 0; i < num_accts; i++) {
+               PyObject *obj;  
+
+               obj = PyDict_New();
+               
+               PyDict_SetItemString(
+                       obj, "name", PyString_FromString(info[i].acct_name));
+
+               PyDict_SetItemString(
+                       obj, "description", 
+                       PyString_FromString(info[i].acct_desc));
+
+               PyDict_SetItemString(obj, "rid", PyInt_FromLong(info[i].rid));
+
+               PyList_SetItem(*array, i, obj);
+       }
+
+       return True;
+}
+
+BOOL py_to_acct_info(PRINTER_INFO_3 *info, PyObject *dict,
+                    TALLOC_CTX *mem_ctx)
+{
+       return False;
+}
diff --git a/source4/python/py_smb.c b/source4/python/py_smb.c
new file mode 100644 (file)
index 0000000..3f05b5d
--- /dev/null
@@ -0,0 +1,399 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_smb.h"
+
+/* Create a new cli_state python object */
+
+PyObject *new_cli_state_object(struct cli_state *cli)
+{
+       cli_state_object *o;
+
+       o = PyObject_New(cli_state_object, &cli_state_type);
+
+       o->cli = cli;
+
+       return (PyObject*)o;
+}
+
+static PyObject *py_smb_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = { "server", NULL };
+       struct cli_state *cli;
+       char *server;
+       struct in_addr ip;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &server))
+               return NULL;
+
+       if (!(cli = cli_initialise(NULL)))
+               return NULL;
+
+       ZERO_STRUCT(ip);
+
+       if (!cli_connect(cli, server, &ip))
+               return NULL;
+
+       return new_cli_state_object(cli);
+}
+
+static PyObject *py_smb_session_request(PyObject *self, PyObject *args,
+                                       PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "called", "calling", NULL };
+       char *calling_name = NULL, *called_name;
+       struct nmb_name calling, called;
+       BOOL result;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s|s", kwlist, &called_name, 
+                                        &calling_name))
+               return NULL;
+
+       if (!calling_name)
+               calling_name = lp_netbios_name();
+
+       make_nmb_name(&calling, calling_name, 0x00);
+       make_nmb_name(&called, called_name, 0x20);
+
+       result = cli_session_request(cli->cli, &calling, &called);
+
+       return Py_BuildValue("i", result);
+}
+                                     
+static PyObject *py_smb_negprot(PyObject *self, PyObject *args, PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { NULL };
+       BOOL result;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+               return NULL;
+
+       result = cli_negprot(cli->cli);
+
+       return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_session_setup(PyObject *self, PyObject *args, 
+                                     PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "creds", NULL };
+       PyObject *creds;
+       char *username, *domain, *password, *errstr;
+       BOOL result;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "|O", kwlist, &creds))
+               return NULL;
+
+       if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) {
+               free(errstr);
+               return NULL;
+       }
+
+       result = cli_session_setup(
+               cli->cli, username, password, strlen(password) + 1,
+               password, strlen(password) + 1, domain);
+
+       if (cli_is_error(cli->cli)) {
+               PyErr_SetString(PyExc_RuntimeError, "session setup failed");
+               return NULL;
+       }
+
+       return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_tconx(PyObject *self, PyObject *args, PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "service", NULL };
+       char *service;
+       BOOL result;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &service))
+               return NULL;
+
+       result = cli_send_tconX(
+               cli->cli, service, strequal(service, "IPC$") ? "IPC" : 
+               "?????", "", 1);
+
+       if (cli_is_error(cli->cli)) {
+               PyErr_SetString(PyExc_RuntimeError, "tconx failed");
+               return NULL;
+       }
+
+       return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_nt_create_andx(PyObject *self, PyObject *args,
+                                      PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "filename", "desired_access", 
+                                 "file_attributes", "share_access",
+                                 "create_disposition", NULL };
+       char *filename;
+       uint32 desired_access, file_attributes = 0, 
+               share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
+               create_disposition = FILE_EXISTS_OPEN, create_options = 0;
+       int result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "si|iii", kwlist, &filename, &desired_access,
+                   &file_attributes, &share_access, &create_disposition,
+                   &create_options))
+               return NULL;
+
+       result = cli_nt_create_full(
+               cli->cli, filename, desired_access, file_attributes,
+               share_access, create_disposition, create_options);
+
+       if (cli_is_error(cli->cli)) {
+               PyErr_SetString(PyExc_RuntimeError, "nt_create_andx failed");
+               return NULL;
+       }
+
+       /* Return FID */
+
+       return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_close(PyObject *self, PyObject *args,
+                             PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "fnum", NULL };
+       BOOL result;
+       int fnum;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "i", kwlist, &fnum))
+               return NULL;
+
+       result = cli_close(cli->cli, fnum);
+
+       return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_unlink(PyObject *self, PyObject *args,
+                              PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "filename", NULL };
+       char *filename;
+       BOOL result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s", kwlist, &filename))
+               return NULL;
+
+       result = cli_unlink(cli->cli, filename);
+
+       return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_query_secdesc(PyObject *self, PyObject *args,
+                                     PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "fnum", NULL };
+       PyObject *result;
+       SEC_DESC *secdesc = NULL;
+       int fnum;
+       TALLOC_CTX *mem_ctx;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "i", kwlist, &fnum))
+               return NULL;
+
+       mem_ctx = talloc_init("py_smb_query_secdesc");
+
+       secdesc = cli_query_secdesc(cli->cli, fnum, mem_ctx);
+
+       if (cli_is_error(cli->cli)) {
+               PyErr_SetString(PyExc_RuntimeError, "query_secdesc failed");
+               return NULL;
+       }
+
+       if (!secdesc) {
+               Py_INCREF(Py_None);
+               result = Py_None;
+               goto done;
+       }
+
+       if (!py_from_SECDESC(&result, secdesc)) {
+               PyErr_SetString(
+                       PyExc_TypeError,
+                       "Invalid security descriptor returned");
+               result = NULL;
+               goto done;
+       }
+
+ done:
+       talloc_destroy(mem_ctx);
+
+       return result;
+       
+}
+
+static PyObject *py_smb_set_secdesc(PyObject *self, PyObject *args,
+                                   PyObject *kw)
+{
+       cli_state_object *cli = (cli_state_object *)self;
+       static char *kwlist[] = { "fnum", "security_descriptor", NULL };
+       PyObject *py_secdesc;
+       SEC_DESC *secdesc;
+       TALLOC_CTX *mem_ctx = talloc_init("py_smb_set_secdesc");
+       int fnum;
+       BOOL result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "iO", kwlist, &fnum, &py_secdesc))
+               return NULL;
+
+       if (!py_to_SECDESC(&secdesc, py_secdesc, mem_ctx)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "Invalid security descriptor");
+               return NULL;
+       }
+
+       result = cli_set_secdesc(cli->cli, fnum, secdesc);
+
+       if (cli_is_error(cli->cli)) {
+               PyErr_SetString(PyExc_RuntimeError, "set_secdesc failed");
+               return NULL;
+       }
+
+       return PyInt_FromLong(result);
+}
+
+static PyMethodDef smb_hnd_methods[] = {
+
+       /* Session and connection handling */
+
+       { "session_request", (PyCFunction)py_smb_session_request, 
+         METH_VARARGS | METH_KEYWORDS, "Request a session" },
+
+       { "negprot", (PyCFunction)py_smb_negprot, 
+         METH_VARARGS | METH_KEYWORDS, "Protocol negotiation" },
+
+       { "session_setup", (PyCFunction)py_smb_session_setup,
+         METH_VARARGS | METH_KEYWORDS, "Session setup" },
+
+       { "tconx", (PyCFunction)py_smb_tconx,
+         METH_VARARGS | METH_KEYWORDS, "Tree connect" },
+
+       /* File operations */
+
+       { "nt_create_andx", (PyCFunction)py_smb_nt_create_andx,
+         METH_VARARGS | METH_KEYWORDS, "NT Create&X" },
+
+       { "close", (PyCFunction)py_smb_close,
+         METH_VARARGS | METH_KEYWORDS, "Close" },
+
+       { "unlink", (PyCFunction)py_smb_unlink,
+         METH_VARARGS | METH_KEYWORDS, "Unlink" },
+
+       /* Security descriptors */
+
+       { "query_secdesc", (PyCFunction)py_smb_query_secdesc,
+         METH_VARARGS | METH_KEYWORDS, "Query security descriptor" },
+
+       { "set_secdesc", (PyCFunction)py_smb_set_secdesc,
+         METH_VARARGS | METH_KEYWORDS, "Set security descriptor" },
+
+       { NULL }
+};
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef smb_methods[] = {
+
+       { "connect", (PyCFunction)py_smb_connect, METH_VARARGS | METH_KEYWORDS,
+         "Connect to a host" },
+
+       { NULL }
+};
+
+static void py_cli_state_dealloc(PyObject* self)
+{
+       PyObject_Del(self);
+}
+
+static PyObject *py_cli_state_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(smb_hnd_methods, self, attrname);
+}
+
+PyTypeObject cli_state_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "SMB client connection",
+       sizeof(cli_state_object),
+       0,
+       py_cli_state_dealloc, /*tp_dealloc*/
+       0,          /*tp_print*/
+       py_cli_state_getattr,          /*tp_getattr*/
+       0,          /*tp_setattr*/
+       0,          /*tp_compare*/
+       0,          /*tp_repr*/
+       0,          /*tp_as_number*/
+       0,          /*tp_as_sequence*/
+       0,          /*tp_as_mapping*/
+       0,          /*tp_hash */
+};
+
+/*
+ * Module initialisation 
+ */
+
+void initsmb(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("smb", smb_methods);
+       dict = PyModule_GetDict(module);
+
+       /* Initialise policy handle object */
+
+       cli_state_type.ob_type = &PyType_Type;
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+
+       setup_logging("smb", True);
+       DEBUGLEVEL = 10;
+}
diff --git a/source4/python/py_smb.h b/source4/python/py_smb.h
new file mode 100644 (file)
index 0000000..31bcf4a
--- /dev/null
@@ -0,0 +1,39 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_SMB_H
+#define _PY_SMB_H
+
+#include "python/py_common.h"
+
+/* cli_state handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+} cli_state_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject cli_state_type;
+
+extern PyObject *smb_ntstatus;
+
+#endif /* _PY_SMB_H */
diff --git a/source4/python/py_spoolss.c b/source4/python/py_spoolss.c
new file mode 100644 (file)
index 0000000..7b0a102
--- /dev/null
@@ -0,0 +1,484 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Exceptions this module can raise */
+
+PyObject *spoolss_error, *spoolss_werror;
+
+/* 
+ * Method dispatch table
+ */
+
+static PyMethodDef spoolss_methods[] = {
+
+       /* Open/close printer handles */
+       
+       { "openprinter", (PyCFunction)spoolss_openprinter, METH_VARARGS | METH_KEYWORDS, 
+         "Open a printer by name in UNC format.
+
+Optionally a dictionary of (domain, username, password) may be given in
+which case they are used when opening the RPC pipe.  An access mask may
+also be given which defaults to MAXIMUM_ALLOWED_ACCESS.
+
+Example:
+
+>>> hnd = spoolss.openprinter(\"\\\\\\\\NPSD-PDC2\\\\meanie\")"},
+       
+       { "closeprinter", spoolss_closeprinter, METH_VARARGS, 
+         "Close a printer handle opened with openprinter or addprinter.
+
+Example:
+
+>>> spoolss.closeprinter(hnd)"},
+
+       { "addprinterex", (PyCFunction)spoolss_addprinterex, METH_VARARGS, 
+         "addprinterex()"},
+
+       /* Server enumeratation functions */
+
+       { "enumprinters", (PyCFunction)spoolss_enumprinters, 
+         METH_VARARGS | METH_KEYWORDS,
+         "Enumerate printers on a print server.
+
+Return a list of printers on a print server.  The credentials, info level
+and flags may be specified as keyword arguments.
+
+Example:
+
+>>> print spoolss.enumprinters(\"\\\\\\\\npsd-pdc2\")
+[{'comment': 'i am a comment', 'printer_name': 'meanie', 'flags': 8388608, 
+  'description': 'meanie,Generic / Text Only,i am a location'}, 
+ {'comment': '', 'printer_name': 'fileprint', 'flags': 8388608, 
+  'description': 'fileprint,Generic / Text Only,'}]"},
+
+       { "enumports", (PyCFunction)spoolss_enumports, 
+         METH_VARARGS | METH_KEYWORDS,
+         "Enumerate ports on a print server.
+
+Return a list of ports on a print server.
+
+Example:
+
+>>> print spoolss.enumports(\"\\\\\\\\npsd-pdc2\")
+[{'name': 'LPT1:'}, {'name': 'LPT2:'}, {'name': 'COM1:'}, {'name': 'COM2:'}, 
+ {'name': 'FILE:'}, {'name': '\\\\nautilus1\\zpekt3r'}]"},
+
+       { "enumprinterdrivers", (PyCFunction)spoolss_enumprinterdrivers, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Enumerate printer drivers on a print server.
+
+Return a list of printer drivers."},
+       /* Miscellaneous other commands */
+
+       { "getprinterdriverdir", (PyCFunction)spoolss_getprinterdriverdir, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Return printer driver directory.
+
+Return the printer driver directory for a given architecture.  The 
+architecture defaults to \"Windows NT x86\"."},
+
+       /* Other stuff - this should really go into a samba config module
+          but for the moment let's leave it here. */
+
+       { "setup_logging", (PyCFunction)py_setup_logging, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Set up debug logging.
+
+Initialises Samba's debug logging system.  One argument is expected which
+is a boolean specifying whether debugging is interactive and sent to stdout
+or logged to a file.
+
+Example:
+
+>>> spoolss.setup_logging(interactive = 1)" },
+
+       { "get_debuglevel", (PyCFunction)get_debuglevel, 
+         METH_VARARGS, 
+         "Set the current debug level.
+
+Example:
+
+>>> spoolss.get_debuglevel()
+0" },
+
+       { "set_debuglevel", (PyCFunction)set_debuglevel, 
+         METH_VARARGS, 
+         "Get the current debug level.
+
+Example:
+
+>>> spoolss.set_debuglevel(10)" },
+
+       /* Printer driver routines */
+       
+       { "addprinterdriver", (PyCFunction)spoolss_addprinterdriver, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Add a printer driver." },
+
+       { "addprinterdriverex", (PyCFunction)spoolss_addprinterdriverex, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Add a printer driver." },
+
+       { "deleteprinterdriver", (PyCFunction)spoolss_deleteprinterdriver, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Delete a printer driver." },
+
+       { "deleteprinterdriverex", (PyCFunction)spoolss_deleteprinterdriverex, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Delete a printer driver." },
+
+       { NULL }
+};
+
+/* Methods attached to a spoolss handle object */
+
+static PyMethodDef spoolss_hnd_methods[] = {
+
+       /* Printer info */
+
+       { "getprinter", (PyCFunction)spoolss_hnd_getprinter, 
+           METH_VARARGS | METH_KEYWORDS,
+         "Get printer information.
+
+Return a dictionary of print information.  The info level defaults to 1.
+
+Example:
+
+>>> hnd.getprinter()
+{'comment': 'i am a comment', 'printer_name': '\\\\NPSD-PDC2\\meanie', 
+ 'description': '\\\\NPSD-PDC2\\meanie,Generic / Text Only,i am a location',
+ 'flags': 8388608}"},
+
+       { "setprinter", (PyCFunction)spoolss_hnd_setprinter, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Set printer information."},
+
+       /* Printer drivers */
+
+       { "getprinterdriver", (PyCFunction)spoolss_hnd_getprinterdriver, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Return printer driver information.
+
+Return a dictionary of printer driver information for the printer driver
+bound to this printer."},
+
+       /* Forms */
+
+       { "enumforms", (PyCFunction)spoolss_hnd_enumforms, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Enumerate supported forms.
+
+Return a list of forms supported by this printer or print server."},
+
+       { "setform", (PyCFunction)spoolss_hnd_setform, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Set form data.
+
+Set the form given by the dictionary argument."},
+
+       { "addform", (PyCFunction)spoolss_hnd_addform, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Add a new form." },
+
+       { "getform", (PyCFunction)spoolss_hnd_getform, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Get form properties." },
+
+       { "deleteform", (PyCFunction)spoolss_hnd_deleteform, 
+          METH_VARARGS | METH_KEYWORDS,
+         "Delete a form." },
+
+        /* Job related methods */
+
+        { "enumjobs", (PyCFunction)spoolss_hnd_enumjobs, 
+          METH_VARARGS | METH_KEYWORDS,
+          "Enumerate jobs." },
+
+        { "setjob", (PyCFunction)spoolss_hnd_setjob, 
+          METH_VARARGS | METH_KEYWORDS,
+          "Set job information." },
+
+        { "getjob", (PyCFunction)spoolss_hnd_getjob, 
+          METH_VARARGS | METH_KEYWORDS,
+          "Get job information." },
+
+        { "startpageprinter", (PyCFunction)spoolss_hnd_startpageprinter, 
+           METH_VARARGS | METH_KEYWORDS,
+          "Notify spooler that a page is about to be printed." },
+
+        { "endpageprinter", (PyCFunction)spoolss_hnd_endpageprinter, 
+           METH_VARARGS | METH_KEYWORDS,
+          "Notify spooler that a page is about to be printed." },
+
+        { "startdocprinter", (PyCFunction)spoolss_hnd_startdocprinter, 
+           METH_VARARGS | METH_KEYWORDS,
+          "Notify spooler that a document is about to be printed." },
+
+        { "enddocprinter", (PyCFunction)spoolss_hnd_enddocprinter, 
+           METH_VARARGS | METH_KEYWORDS,
+          "Notify spooler that a document is about to be printed." },
+
+        { "writeprinter", (PyCFunction)spoolss_hnd_writeprinter,
+          METH_VARARGS | METH_KEYWORDS,
+          "Write job data to a printer." },
+
+        { "addjob", (PyCFunction)spoolss_hnd_addjob,
+          METH_VARARGS | METH_KEYWORDS,
+          "Add a job to the list of print jobs." },
+
+        /* Printer data */
+
+        { "getprinterdata", (PyCFunction)spoolss_hnd_getprinterdata,
+           METH_VARARGS | METH_KEYWORDS,
+          "Get printer data." },
+
+        { "setprinterdata", (PyCFunction)spoolss_hnd_setprinterdata,
+           METH_VARARGS | METH_KEYWORDS,
+          "Set printer data." },
+
+        { "enumprinterdata", (PyCFunction)spoolss_hnd_enumprinterdata,
+           METH_VARARGS | METH_KEYWORDS,
+          "Enumerate printer data." },
+
+        { "deleteprinterdata", (PyCFunction)spoolss_hnd_deleteprinterdata,
+           METH_VARARGS | METH_KEYWORDS,
+          "Delete printer data." },
+
+        { "getprinterdataex", (PyCFunction)spoolss_hnd_getprinterdataex,
+           METH_VARARGS | METH_KEYWORDS,
+          "Get printer data." },
+
+        { "setprinterdataex", (PyCFunction)spoolss_hnd_setprinterdataex,
+           METH_VARARGS | METH_KEYWORDS,
+          "Set printer data." },
+
+        { "enumprinterdataex", (PyCFunction)spoolss_hnd_enumprinterdataex,
+           METH_VARARGS | METH_KEYWORDS,
+          "Enumerate printer data." },
+
+        { "deleteprinterdataex", (PyCFunction)spoolss_hnd_deleteprinterdataex,
+           METH_VARARGS | METH_KEYWORDS,
+          "Delete printer data." },
+
+        { "enumprinterkey", (PyCFunction)spoolss_hnd_enumprinterkey,
+           METH_VARARGS | METH_KEYWORDS,
+          "Enumerate printer key." },
+
+#if 0
+        /* Not implemented */
+
+        { "deleteprinterkey", (PyCFunction)spoolss_hnd_deleteprinterkey,
+           METH_VARARGS | METH_KEYWORDS,
+          "Delete printer key." },
+#endif
+
+       { NULL }
+
+};
+
+static void py_policy_hnd_dealloc(PyObject* self)
+{
+        spoolss_policy_hnd_object *hnd;
+
+        /* Close down policy handle and free talloc context */
+
+        hnd = (spoolss_policy_hnd_object*)self;
+
+        cli_shutdown(hnd->cli);
+        talloc_destroy(hnd->mem_ctx);
+
+       PyObject_Del(self);
+}
+
+static PyObject *py_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(spoolss_hnd_methods, self, attrname);
+}
+
+static char spoolss_type_doc[] = 
+"Python wrapper for Windows NT SPOOLSS rpc pipe.";
+
+PyTypeObject spoolss_policy_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "spoolss.hnd",
+       sizeof(spoolss_policy_hnd_object),
+       0,
+       py_policy_hnd_dealloc,  /* tp_dealloc*/
+       0,                      /* tp_print*/
+       py_policy_hnd_getattr,  /* tp_getattr*/
+       0,                      /* tp_setattr*/
+       0,                      /* tp_compare*/
+       0,                      /* tp_repr*/
+       0,                      /* tp_as_number*/
+       0,                      /* tp_as_sequence*/
+       0,                      /* tp_as_mapping*/
+       0,                      /* tp_hash */
+       0,                      /* tp_call */
+       0,                      /* tp_str */
+       0,                      /* tp_getattro */
+       0,                      /* tp_setattro */
+       0,                      /* tp_as_buffer*/
+       Py_TPFLAGS_DEFAULT,     /* tp_flags */
+       spoolss_type_doc,       /* tp_doc */
+};
+
+/* Initialise constants */
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+       
+       /* Access permissions */
+
+       { "MAXIMUM_ALLOWED_ACCESS", MAXIMUM_ALLOWED_ACCESS },
+       { "SERVER_ALL_ACCESS", SERVER_ALL_ACCESS },
+       { "SERVER_READ", SERVER_READ },
+       { "SERVER_WRITE", SERVER_WRITE },
+       { "SERVER_EXECUTE", SERVER_EXECUTE },
+       { "SERVER_ACCESS_ADMINISTER", SERVER_ACCESS_ADMINISTER },
+       { "SERVER_ACCESS_ENUMERATE", SERVER_ACCESS_ENUMERATE },
+       { "PRINTER_ALL_ACCESS", PRINTER_ALL_ACCESS },
+       { "PRINTER_READ", PRINTER_READ },
+       { "PRINTER_WRITE", PRINTER_WRITE },
+       { "PRINTER_EXECUTE", PRINTER_EXECUTE },
+       { "PRINTER_ACCESS_ADMINISTER", PRINTER_ACCESS_ADMINISTER },
+       { "PRINTER_ACCESS_USE", PRINTER_ACCESS_USE },
+       { "JOB_ACCESS_ADMINISTER", JOB_ACCESS_ADMINISTER },
+       { "JOB_ALL_ACCESS", JOB_ALL_ACCESS },
+       { "JOB_READ", JOB_READ },
+       { "JOB_WRITE", JOB_WRITE },
+       { "JOB_EXECUTE", JOB_EXECUTE },
+       { "STANDARD_RIGHTS_ALL_ACCESS", STANDARD_RIGHTS_ALL_ACCESS },
+       { "STANDARD_RIGHTS_EXECUTE_ACCESS", STANDARD_RIGHTS_EXECUTE_ACCESS },
+       { "STANDARD_RIGHTS_READ_ACCESS", STANDARD_RIGHTS_READ_ACCESS },
+       { "STANDARD_RIGHTS_REQUIRED_ACCESS", STANDARD_RIGHTS_REQUIRED_ACCESS },
+       { "STANDARD_RIGHTS_WRITE_ACCESS", STANDARD_RIGHTS_WRITE_ACCESS },
+
+       /* Printer enumeration flags */
+
+       { "PRINTER_ENUM_DEFAULT", PRINTER_ENUM_DEFAULT },
+       { "PRINTER_ENUM_LOCAL", PRINTER_ENUM_LOCAL },
+       { "PRINTER_ENUM_CONNECTIONS", PRINTER_ENUM_CONNECTIONS },
+       { "PRINTER_ENUM_FAVORITE", PRINTER_ENUM_FAVORITE },
+       { "PRINTER_ENUM_NAME", PRINTER_ENUM_NAME },
+       { "PRINTER_ENUM_REMOTE", PRINTER_ENUM_REMOTE },
+       { "PRINTER_ENUM_SHARED", PRINTER_ENUM_SHARED },
+       { "PRINTER_ENUM_NETWORK", PRINTER_ENUM_NETWORK },
+
+       /* Form types */
+
+       { "FORM_USER", FORM_USER },
+       { "FORM_BUILTIN", FORM_BUILTIN },
+       { "FORM_PRINTER", FORM_PRINTER },
+
+       /* WERRORs */
+
+       { "WERR_OK", 0 },
+       { "WERR_BADFILE", 2 },
+       { "WERR_ACCESS_DENIED", 5 },
+       { "WERR_BADFID", 6 },
+       { "WERR_BADFUNC", 1 },
+       { "WERR_INSUFFICIENT_BUFFER", 122 },
+       { "WERR_NO_SUCH_SHARE", 67 },
+       { "WERR_ALREADY_EXISTS", 80 },
+       { "WERR_INVALID_PARAM", 87 },
+       { "WERR_NOT_SUPPORTED", 50 },
+       { "WERR_BAD_PASSWORD", 86 },
+       { "WERR_NOMEM", 8 },
+       { "WERR_INVALID_NAME", 123 },
+       { "WERR_UNKNOWN_LEVEL", 124 },
+       { "WERR_OBJECT_PATH_INVALID", 161 },
+       { "WERR_NO_MORE_ITEMS", 259 },
+       { "WERR_MORE_DATA", 234 },
+       { "WERR_UNKNOWN_PRINTER_DRIVER", 1797 },
+       { "WERR_INVALID_PRINTER_NAME", 1801 },
+       { "WERR_PRINTER_ALREADY_EXISTS", 1802 },
+       { "WERR_INVALID_DATATYPE", 1804 },
+       { "WERR_INVALID_ENVIRONMENT", 1805 },
+       { "WERR_INVALID_FORM_NAME", 1902 },
+       { "WERR_INVALID_FORM_SIZE", 1903 },
+       { "WERR_BUF_TOO_SMALL", 2123 },
+       { "WERR_JOB_NOT_FOUND", 2151 },
+       { "WERR_DEST_NOT_FOUND", 2152 },
+       { "WERR_NOT_LOCAL_DOMAIN", 2320 },
+       { "WERR_PRINTER_DRIVER_IN_USE", 3001 },
+       { "WERR_STATUS_MORE_ENTRIES  ", 0x0105 },
+
+       /* Job control constants */
+
+       { "JOB_CONTROL_PAUSE", JOB_CONTROL_PAUSE },
+       { "JOB_CONTROL_RESUME", JOB_CONTROL_RESUME },
+       { "JOB_CONTROL_CANCEL", JOB_CONTROL_CANCEL },
+       { "JOB_CONTROL_RESTART", JOB_CONTROL_RESTART },
+       { "JOB_CONTROL_DELETE", JOB_CONTROL_DELETE },
+
+       { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/* Module initialisation */
+
+void initspoolss(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("spoolss", spoolss_methods);
+       dict = PyModule_GetDict(module);
+
+       /* Exceptions we can raise */
+
+       spoolss_error = PyErr_NewException("spoolss.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", spoolss_error);
+
+       spoolss_werror = PyErr_NewException("spoolss.werror", NULL, NULL);
+       PyDict_SetItemString(dict, "werror", spoolss_werror);
+
+       /* Initialise policy handle object */
+
+       spoolss_policy_hnd_type.ob_type = &PyType_Type;
+
+       PyDict_SetItemString(dict, "spoolss.hnd", 
+                            (PyObject *)&spoolss_policy_hnd_type);
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+}
diff --git a/source4/python/py_spoolss.h b/source4/python/py_spoolss.h
new file mode 100644 (file)
index 0000000..34b4819
--- /dev/null
@@ -0,0 +1,160 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_SPOOLSS_H
+#define _PY_SPOOLSS_H
+
+#include "python/py_common.h"
+
+/* Spoolss policy handle object */
+
+typedef struct {
+       PyObject_HEAD
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       POLICY_HND pol;
+} spoolss_policy_hnd_object;
+     
+/* Exceptions raised by this module */
+
+extern PyTypeObject spoolss_policy_hnd_type;
+
+extern PyObject *spoolss_error, *spoolss_werror;
+
+/* The following definitions come from python/py_spoolss_common.c */
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, POLICY_HND *pol);
+
+/* The following definitions come from python/py_spoolss_drivers.c  */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+                                    PyObject *kw);
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+                                  PyObject *kw);
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args, 
+                                     PyObject *kw);
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+                                  PyObject *kw);
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+                                            PyObject *kw);
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+                                     PyObject *kw);
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+                                       PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_drivers_conv.c  */
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info);
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info);
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info);
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info);
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict);
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info);
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_forms.c  */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_forms_conv.c  */
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form);
+BOOL py_to_FORM(FORM *form, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_jobs.c  */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_jobs_conv.c  */
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info);
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict);
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info);
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict);
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info);
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_ports.c  */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_ports_conv.c  */
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info);
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict);
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info);
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_printerdata.c  */
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+                                    PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+                                      PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers.c  */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args);
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers_conv.c  */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode);
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict);
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info);
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info);
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info);
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+                         TALLOC_CTX *mem_ctx);
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info);
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+                         TALLOC_CTX *mem_ctx);
+
+#endif /* _PY_SPOOLSS_H */
diff --git a/source4/python/py_spoolss_common.c b/source4/python/py_spoolss_common.c
new file mode 100644 (file)
index 0000000..f34d2ac
--- /dev/null
@@ -0,0 +1,35 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2003
+   
+   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 "python/py_spoolss.h"
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, POLICY_HND *pol)
+{
+       spoolss_policy_hnd_object *o;
+
+       o = PyObject_New(spoolss_policy_hnd_object, &spoolss_policy_hnd_type);
+
+       o->cli = cli;
+       o->mem_ctx = mem_ctx;
+       memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+       return (PyObject*)o;
+}
diff --git a/source4/python/py_spoolss_drivers.c b/source4/python/py_spoolss_drivers.c
new file mode 100644 (file)
index 0000000..a072ac0
--- /dev/null
@@ -0,0 +1,421 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Enumerate printer drivers */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+                                    PyObject *kw)
+{
+       WERROR werror;
+       PyObject *result = NULL, *creds = NULL;
+       PRINTER_DRIVER_CTR ctr;
+       int level = 1, i;
+       uint32 needed, num_drivers;
+       char *arch = "Windows NT x86", *server, *errstr;
+       static char *kwlist[] = {"server", "level", "creds", "arch", NULL};
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
+       
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|iOs", kwlist, &server, &level, &creds,
+                   &arch)) 
+               return NULL;
+       
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       /* Call rpc function */
+       
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_enumprinterdrivers"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               goto done;
+       }       
+
+       werror = cli_spoolss_enumprinterdrivers(
+               cli, mem_ctx, 0, &needed, level, arch,
+               &num_drivers, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enumprinterdrivers(
+                       cli, mem_ctx, needed, NULL, level, arch, 
+                       &num_drivers, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       /* Return value */
+
+       switch (level) {
+       case 1:
+               result = PyDict_New();
+               
+               for (i = 0; i < num_drivers; i++) {
+                       PyObject *value;
+                       fstring name;
+                       
+                       rpcstr_pull(name, ctr.info1[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_DRIVER_INFO_1(&value, &ctr.info1[i]);
+
+                       PyDict_SetItemString(result, name, value);
+               }
+               
+               break;
+       case 2: 
+               result = PyDict_New();
+
+               for(i = 0; i < num_drivers; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, ctr.info2[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_DRIVER_INFO_2(&value, &ctr.info2[i]);
+
+                       PyDict_SetItemString(result, name, value);
+               }
+
+               break;
+       case 3: 
+               result = PyDict_New();
+
+               for(i = 0; i < num_drivers; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, ctr.info3[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_DRIVER_INFO_3(&value, &ctr.info3[i]);
+
+                       PyDict_SetItemString(result, name, value);
+               }
+
+               break;
+       case 6: 
+               result = PyDict_New();
+
+               for(i = 0; i < num_drivers; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, ctr.info6[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_DRIVER_INFO_6(&value, &ctr.info6[i]);
+
+                       PyList_SetItem(result, i, value);
+               }
+
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unknown info level");
+               goto done;
+       }
+       
+ done:
+       if (cli)
+               cli_shutdown(cli);
+
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+/* Fetch printer driver */
+
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+                                  PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *result = Py_None;
+       PRINTER_DRIVER_CTR ctr;
+       int level = 1;
+       uint32 needed;
+       char *arch = "Windows NT x86";
+       static char *kwlist[] = {"level", "arch", NULL};
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "|is", kwlist, &level, &arch))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_getprinterdriver(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+               arch, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getprinterdriver(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       level, arch, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       /* Return value */
+       
+       switch (level) {
+       case 1:
+               py_from_DRIVER_INFO_1(&result, ctr.info1);
+               break;
+       case 2: 
+               py_from_DRIVER_INFO_2(&result, ctr.info2);
+               break;
+       case 3: 
+               py_from_DRIVER_INFO_3(&result, ctr.info3);
+               break;
+       case 6:
+               py_from_DRIVER_INFO_6(&result, ctr.info6);
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+       
+       Py_INCREF(result);
+       return result;
+}
+
+/* Fetch printer driver directory */
+
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args, 
+                                     PyObject *kw)
+{
+       WERROR werror;
+       PyObject *result = NULL, *creds = NULL;
+       DRIVER_DIRECTORY_CTR ctr;
+       uint32 needed, level = 1;
+       char *arch = "Windows NT x86", *server, *errstr;
+       static char *kwlist[] = {"server", "level", "arch", "creds", NULL};
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|isO", kwlist, &server, &level,
+                   &arch, &creds))
+               return NULL;
+
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       /* Call rpc function */
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+       
+       if (!(mem_ctx = talloc_init("spoolss_getprinterdriverdir"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               goto done;
+       }       
+
+       werror = cli_spoolss_getprinterdriverdir(
+               cli, mem_ctx, 0, &needed, level, arch, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getprinterdriverdir(
+                       cli, mem_ctx, needed, NULL, level, arch, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       /* Return value */
+       
+       switch (level) {
+       case 1:
+               py_from_DRIVER_DIRECTORY_1(&result, ctr.info1);
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unknown info level");
+               goto done;      
+       }
+       
+ done:
+       if (cli)
+               cli_shutdown(cli);
+       
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+                                  PyObject *kw)
+{
+       static char *kwlist[] = { "server", "info", "creds", NULL };
+       char *server, *errstr;
+       uint32 level;
+       PyObject *info, *result = NULL, *creds = NULL;
+       WERROR werror;
+       TALLOC_CTX *mem_ctx = NULL;
+       struct cli_state *cli = NULL;
+       PRINTER_DRIVER_CTR ctr;
+       union {
+               DRIVER_INFO_3 driver_3;
+       } dinfo;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "sO!|O", kwlist, &server, &PyDict_Type,
+                   &info, &creds))
+               return NULL;
+       
+       if (server[0] == '\\' || server[1] == '\\')
+               server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_addprinterdriver"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!get_level_value(info, &level)) {
+               PyErr_SetString(spoolss_error, "invalid info level");
+               goto done;
+       }
+
+       if (level != 3) {
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               goto done;
+       }
+
+       ZERO_STRUCT(ctr);
+       ZERO_STRUCT(dinfo);
+
+       switch(level) {
+       case 3:
+               ctr.info3 = &dinfo.driver_3;
+
+               if (!py_to_DRIVER_INFO_3(&dinfo.driver_3, info)) {
+                       PyErr_SetString(spoolss_error,
+                                       "error converting to driver info 3");
+                       goto done;
+               }
+
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               goto done;
+       }
+
+       werror = cli_spoolss_addprinterdriver(cli, mem_ctx, level, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       Py_INCREF(Py_None);
+       result = Py_None;
+
+done:
+       if (cli)
+               cli_shutdown(cli);
+
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+       
+       return result;
+       
+}
+
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+                                            PyObject *kw)
+{
+       /* Not supported by Samba server */
+       
+       PyErr_SetString(spoolss_error, "Not implemented");
+       return NULL;
+}
+       
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+                                     PyObject *kw)
+{
+       PyErr_SetString(spoolss_error, "Not implemented");
+       return NULL;
+}
+
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+                                       PyObject *kw)
+{
+       PyErr_SetString(spoolss_error, "Not implemented");
+       return NULL;
+}
diff --git a/source4/python/py_spoolss_drivers_conv.c b/source4/python/py_spoolss_drivers_conv.c
new file mode 100644 (file)
index 0000000..9bc8408
--- /dev/null
@@ -0,0 +1,179 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+/* Structure/hash conversions */
+
+struct pyconv py_DRIVER_INFO_1[] = {
+       { "name", PY_UNISTR, offsetof(DRIVER_INFO_1, name) },
+       { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_2[] = {
+       { "version", PY_UINT32, offsetof(DRIVER_INFO_2, version) },
+       { "name", PY_UNISTR, offsetof(DRIVER_INFO_2, name) },
+       { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_2, architecture) },
+       { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_2, driverpath) },
+       { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_2, datafile) },
+       { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_2, configfile) },
+       { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_3[] = {
+       { "version", PY_UINT32, offsetof(DRIVER_INFO_3, version) },
+       { "name", PY_UNISTR, offsetof(DRIVER_INFO_3, name) },
+       { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_3, architecture) },
+       { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_3, driverpath) },
+       { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_3, datafile) },
+       { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_3, configfile) },
+       { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_3, helpfile) },
+       { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_3, monitorname) },
+       { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_3, defaultdatatype) },
+       { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_6[] = {
+       { "version", PY_UINT32, offsetof(DRIVER_INFO_6, version) },
+       { "name", PY_UNISTR, offsetof(DRIVER_INFO_6, name) },
+       { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_6, architecture) },
+       { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_6, driverpath) },
+       { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_6, datafile) },
+       { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_6, configfile) },
+       { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_6, helpfile) },
+       { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_6, monitorname) },
+       { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_6, defaultdatatype) },
+       /* driver_date */
+       { "padding", PY_UINT32, offsetof(DRIVER_INFO_6, padding) },
+       { "driver_version_low", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_low) },
+       { "driver_version_high", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_high) },
+       { "mfg_name", PY_UNISTR, offsetof(DRIVER_INFO_6, mfgname) },
+       { "oem_url", PY_UNISTR, offsetof(DRIVER_INFO_6, oem_url) },
+       { "hardware_id", PY_UNISTR, offsetof(DRIVER_INFO_6, hardware_id) },
+       { "provider", PY_UNISTR, offsetof(DRIVER_INFO_6, provider) },
+       
+       { NULL }
+};
+
+struct pyconv py_DRIVER_DIRECTORY_1[] = {
+       { "name", PY_UNISTR, offsetof(DRIVER_DIRECTORY_1, name) },
+       { NULL }
+};
+
+static uint16 *to_dependentfiles(PyObject *dict)
+{
+       return (uint16 *)"abcd\0";
+}
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info)
+{
+       *dict = from_struct(info, py_DRIVER_INFO_1);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+       return True;
+}
+
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info)
+{
+       *dict = from_struct(info, py_DRIVER_INFO_2);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+       return True;
+}
+
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info)
+{
+       *dict = from_struct(info, py_DRIVER_INFO_3);
+
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+       PyDict_SetItemString(
+               *dict, "dependent_files", 
+               from_unistr_list(info->dependentfiles));
+
+       return True;
+}
+
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict)
+{
+       PyObject *obj, *dict_copy = PyDict_Copy(dict);
+       BOOL result = False;
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "dependent_files")) ||
+           !PyList_Check(obj))
+               goto done;
+
+       info->dependentfiles = to_dependentfiles(obj);
+
+       PyDict_DelItemString(dict_copy, "dependent_files");
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+           !PyInt_Check(obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "level");
+
+       if (!to_struct(info, dict_copy, py_DRIVER_INFO_3))
+           goto done;
+
+       result = True;
+
+done:
+       Py_DECREF(dict_copy);
+       return result;
+}
+
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info)
+{
+       *dict = from_struct(info, py_DRIVER_INFO_6);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(6));
+       PyDict_SetItemString(
+               *dict, "dependent_files", 
+               from_unistr_list(info->dependentfiles));
+       return True;
+}
+
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info)
+{
+       *dict = from_struct(info, py_DRIVER_DIRECTORY_1);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+       return True;
+}
+
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict)
+{
+       return False;
+}
diff --git a/source4/python/py_spoolss_forms.c b/source4/python/py_spoolss_forms.c
new file mode 100644 (file)
index 0000000..ef9ed94
--- /dev/null
@@ -0,0 +1,266 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Add a form */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *info;
+       FORM form;
+       int level;
+       static char *kwlist[] = {"form", NULL};
+
+       /* Parse parameters */
+       
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &info))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       if (!py_to_FORM(&form, info)) {
+               PyErr_SetString(spoolss_error, "invalid form");
+               return NULL;
+       }
+
+       if (!get_level_value(info, &level)) {
+               PyErr_SetString(spoolss_error, "invalid info level");
+               return NULL;
+       }
+
+       if (level != 1) {
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+       
+       switch (level) {
+       case 1: {
+               PyObject *obj = PyDict_GetItemString(info, "name");
+               char *form_name = PyString_AsString(obj);
+
+               init_unistr2(&form.name, form_name, strlen(form_name) + 1);
+               break;
+       }
+       default:
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+               
+       werror = cli_spoolss_addform(hnd->cli, hnd->mem_ctx, &hnd->pol,
+                                    level, &form);
+
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Get form properties */
+
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *result;
+       char *form_name;
+       int level = 1;
+       static char *kwlist[] = {"form_name", "level", NULL};
+       uint32 needed;
+       FORM_1 form;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|i", kwlist, &form_name, &level))
+               return NULL;
+       
+       /* Call rpc function */
+
+       werror = cli_spoolss_getform(hnd->cli, hnd->mem_ctx, 0, &needed,
+                                    &hnd->pol, form_name, level, &form);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getform(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       form_name, 1, &form);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       result = Py_None;
+
+       switch(level) {
+       case 1:
+               py_from_FORM_1(&result, &form);
+               break;
+       }
+
+       Py_INCREF(result);
+       return result;
+}
+
+/* Set form properties */
+
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *info, *form_name;
+       int level;
+       static char *kwlist[] = { "form", NULL};
+       FORM form;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &info))
+               return NULL;
+
+       if (!get_level_value(info, &level)) {
+               PyErr_SetString(spoolss_error, "invalid info level");
+               return NULL;
+       }
+
+       if (level != 1) {
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+
+       /* Call rpc function */
+
+       if (!py_to_FORM(&form, info)) {
+               PyErr_SetString(spoolss_error, "invalid form");
+               return NULL;
+       }
+
+       form_name = PyDict_GetItemString(info, "name");
+
+       werror = cli_spoolss_setform(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, level, 
+               PyString_AsString(form_name), &form);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Delete a form */
+
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = {"form_name", NULL};
+       char *form_name;
+
+       /* Parse parameters */
+       
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s", kwlist, &form_name))
+               return NULL;
+       
+       /* Call rpc function */
+
+       werror = cli_spoolss_deleteform(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, form_name);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Enumerate forms */
+
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw)
+{
+       PyObject *result;
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       uint32 level = 1, num_forms, needed, i;
+       static char *kwlist[] = {"level", NULL};
+       FORM_1 *forms;
+
+       /* Parse parameters */
+       
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "|i", kwlist, &level))
+               return NULL;
+       
+       /* Call rpc function */
+
+       werror = cli_spoolss_enumforms(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+               &num_forms, &forms);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enumforms(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, level,
+                       &num_forms, &forms);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       switch(level) {
+       case 1:
+               result = PyDict_New();
+
+               for (i = 0; i < num_forms; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, forms[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_FORM_1(&value, &forms[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(1));
+
+                       PyDict_SetItemString(result, name, value);
+               }
+
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unknown info level");
+               return NULL;
+       }
+
+       return result;
+}
diff --git a/source4/python/py_spoolss_forms_conv.c b/source4/python/py_spoolss_forms_conv.c
new file mode 100644 (file)
index 0000000..095a318
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_FORM[] = {
+       { "flags", PY_UINT32, offsetof(FORM, flags) },
+       { "width", PY_UINT32, offsetof(FORM, size_x) },
+       { "length", PY_UINT32, offsetof(FORM, size_y) },
+       { "top", PY_UINT32, offsetof(FORM, top) },
+       { "left", PY_UINT32, offsetof(FORM, left) },
+       { "right", PY_UINT32, offsetof(FORM, right) },
+       { "bottom", PY_UINT32, offsetof(FORM, bottom) },
+       { NULL }
+};
+
+struct pyconv py_FORM_1[] = {
+       { "flags", PY_UINT32, offsetof(FORM_1, flag) },
+       { "width", PY_UINT32, offsetof(FORM_1, width) },
+       { "length", PY_UINT32, offsetof(FORM_1, length) },
+       { "top", PY_UINT32, offsetof(FORM_1, top) },
+       { "left", PY_UINT32, offsetof(FORM_1, left) },
+       { "right", PY_UINT32, offsetof(FORM_1, right) },
+       { "bottom", PY_UINT32, offsetof(FORM_1, bottom) },
+       { "name", PY_UNISTR, offsetof(FORM_1, name) },
+       { NULL }
+};
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form)
+{
+       *dict = from_struct(form, py_FORM_1);
+
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+       return True;
+}
+
+BOOL py_to_FORM(FORM *form, PyObject *dict)
+{
+       PyObject *obj, *dict_copy = PyDict_Copy(dict);
+       char *name;
+       BOOL result = False;
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "name")) || 
+           !PyString_Check(obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "name");
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+           !PyInt_Check(obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "level");
+
+       if (!to_struct(form, dict_copy, py_FORM))
+               goto done;
+
+       /* Careful!  We can't call PyString_AsString(obj) then delete
+          obj and still expect to have our pointer pointing somewhere
+          useful. */
+
+       obj = PyDict_GetItemString(dict, "name");
+       name = PyString_AsString(obj);
+
+       init_unistr2(&form->name, name, strlen(name) + 1);
+       
+       result = True;
+
+done:
+       Py_DECREF(dict_copy);
+       return result;
+}
diff --git a/source4/python/py_spoolss_jobs.c b/source4/python/py_spoolss_jobs.c
new file mode 100644 (file)
index 0000000..59754bd
--- /dev/null
@@ -0,0 +1,377 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Enumerate jobs */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *result;
+       int level = 1;
+       uint32 i, needed, num_jobs;
+       static char *kwlist[] = {"level", NULL};
+       JOB_INFO_CTR ctr;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_enumjobs(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, 0,
+               1000, &num_jobs, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enumjobs(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       level, 0, 1000, &num_jobs, &ctr);
+
+       /* Return value */
+       
+       result = Py_None;
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       result = PyList_New(num_jobs);
+
+       switch (level) {
+       case 1: 
+               for (i = 0; i < num_jobs; i++) {
+                       PyObject *value;
+
+                       py_from_JOB_INFO_1(&value, &ctr.job.job_info_1[i]);
+
+                       PyList_SetItem(result, i, value);
+               }
+
+               break;
+       case 2:
+               for(i = 0; i < num_jobs; i++) {
+                       PyObject *value;
+
+                       py_from_JOB_INFO_2(&value, &ctr.job.job_info_2[i]);
+
+                       PyList_SetItem(result, i, value);
+               }
+               
+               break;
+       }
+
+ done:
+       Py_INCREF(result);
+       return result;
+}
+
+/* Set job command */
+
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       uint32 level = 0, command, jobid;
+       static char *kwlist[] = {"jobid", "command", "level", NULL};
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "ii|i", kwlist, &jobid, &command, &level))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_setjob(hnd->cli, hnd->mem_ctx, &hnd->pol,
+                                   jobid, level, command);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+       
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Get job */
+
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *result;
+       uint32 level = 1, jobid, needed;
+       static char *kwlist[] = {"jobid", "level", NULL};
+       JOB_INFO_CTR ctr;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "i|i", kwlist, &jobid, &level))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_getjob(hnd->cli, hnd->mem_ctx, 0, &needed,
+                                   &hnd->pol, jobid, level, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getjob(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       jobid, level, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       switch(level) {
+       case 1:
+               py_from_JOB_INFO_1(&result, &ctr.job.job_info_1[0]);
+               break;
+       case 2:
+               py_from_JOB_INFO_2(&result, &ctr.job.job_info_2[0]);
+               break;
+       }
+
+       return result;
+}
+
+/* Start page printer.  This notifies the spooler that a page is about to be
+   printed on the specified printer. */
+
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = { NULL };
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_startpageprinter(
+               hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+       
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* End page printer.  This notifies the spooler that a page has finished
+   being printed on the specified printer. */
+
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = { NULL };
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_endpageprinter(
+               hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+       
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Start doc printer.  This notifies the spooler that a document is about to be
+   printed on the specified printer. */
+
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = { "document_info", NULL };
+       PyObject *info, *obj;
+       uint32 level, jobid;
+       char *document_name = NULL, *output_file = NULL, *data_type = NULL;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &info))
+               return NULL;
+       
+       /* Check document_info parameter */
+
+       if (!get_level_value(info, &level)) {
+               PyErr_SetString(spoolss_error, "invalid info level");
+               return NULL;
+       }
+
+       if (level != 1) {
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+
+       if ((obj = PyDict_GetItemString(info, "document_name"))) {
+
+               if (!PyString_Check(obj) && obj != Py_None) {
+                       PyErr_SetString(spoolss_error,
+                                       "document_name not a string");
+                       return NULL;
+               }
+               
+               if (PyString_Check(obj))
+                       document_name = PyString_AsString(obj);
+
+       } else {
+               PyErr_SetString(spoolss_error, "no document_name present");
+               return NULL;
+       }
+
+       if ((obj = PyDict_GetItemString(info, "output_file"))) {
+
+               if (!PyString_Check(obj) && obj != Py_None) {
+                       PyErr_SetString(spoolss_error,
+                                       "output_file not a string");
+                       return NULL;
+               }
+               
+               if (PyString_Check(obj))
+                       output_file = PyString_AsString(obj);
+
+       } else {
+               PyErr_SetString(spoolss_error, "no output_file present");
+               return NULL;
+       }
+
+       if ((obj = PyDict_GetItemString(info, "data_type"))) {
+               
+               if (!PyString_Check(obj) && obj != Py_None) {
+                       PyErr_SetString(spoolss_error,
+                                       "data_type not a string");
+                       return NULL;
+               }
+
+               if (PyString_Check(obj))
+                       data_type = PyString_AsString(obj);
+
+       } else {
+               PyErr_SetString(spoolss_error, "no data_type present");
+               return NULL;
+       }
+
+       /* Call rpc function */
+       
+       werror = cli_spoolss_startdocprinter(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, document_name,
+               output_file, data_type, &jobid);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+       
+       /* The return value is zero for an error (where does the status
+          code come from now??) and the return value is the jobid
+          allocated for the new job. */
+
+       return Py_BuildValue("i", jobid);
+}
+
+/* End doc printer.  This notifies the spooler that a document has finished
+   being printed on the specified printer. */
+
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = { NULL };
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_enddocprinter(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+       
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Write data to a printer */
+
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       static char *kwlist[] = { "data", NULL };
+       PyObject *data;
+       uint32 num_written;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyString_Type, &data))
+               return NULL;
+       
+       /* Call rpc function */
+       
+       werror = cli_spoolss_writeprinter(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, PyString_Size(data),
+               PyString_AsString(data), &num_written);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+       PyErr_SetString(spoolss_error, "Not implemented");
+       return NULL;
+}
diff --git a/source4/python/py_spoolss_jobs_conv.c b/source4/python/py_spoolss_jobs_conv.c
new file mode 100644 (file)
index 0000000..cb04ec6
--- /dev/null
@@ -0,0 +1,102 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_JOB_INFO_1[] = {
+       { "jobid", PY_UINT32, offsetof(JOB_INFO_1, jobid) },
+       { "printer_name", PY_UNISTR, offsetof(JOB_INFO_1, printername) },
+       { "server_name", PY_UNISTR, offsetof(JOB_INFO_1, machinename) },
+       { "user_name", PY_UNISTR, offsetof(JOB_INFO_1, username) },
+       { "document_name", PY_UNISTR, offsetof(JOB_INFO_1, document) },
+       { "data_type", PY_UNISTR, offsetof(JOB_INFO_1, datatype) },
+       { "text_status", PY_UNISTR, offsetof(JOB_INFO_1, text_status) },
+       { "status", PY_UINT32, offsetof(JOB_INFO_1, status) },
+       { "priority", PY_UINT32, offsetof(JOB_INFO_1, priority) },
+       { "position", PY_UINT32, offsetof(JOB_INFO_1, position) },
+       { "total_pages", PY_UINT32, offsetof(JOB_INFO_1, totalpages) },
+       { "pages_printed", PY_UINT32, offsetof(JOB_INFO_1, pagesprinted) },
+       { NULL }
+};
+
+struct pyconv py_JOB_INFO_2[] = {
+       { "jobid", PY_UINT32, offsetof(JOB_INFO_2, jobid) },
+       { "printer_name", PY_UNISTR, offsetof(JOB_INFO_2, printername) },
+       { "server_name", PY_UNISTR, offsetof(JOB_INFO_2, machinename) },
+       { "user_name", PY_UNISTR, offsetof(JOB_INFO_2, username) },
+       { "document_name", PY_UNISTR, offsetof(JOB_INFO_2, document) },
+       { "notify_name", PY_UNISTR, offsetof(JOB_INFO_2, notifyname) },
+       { "data_type", PY_UNISTR, offsetof(JOB_INFO_2, datatype) },
+       { "print_processor", PY_UNISTR, offsetof(JOB_INFO_2, printprocessor) },
+       { "parameters", PY_UNISTR, offsetof(JOB_INFO_2, parameters) },
+       { "driver_name", PY_UNISTR, offsetof(JOB_INFO_2, drivername) },
+       { "text_status", PY_UNISTR, offsetof(JOB_INFO_2, text_status) },
+       { "status", PY_UINT32, offsetof(JOB_INFO_2, status) },
+       { "priority", PY_UINT32, offsetof(JOB_INFO_2, priority) },
+       { "position", PY_UINT32, offsetof(JOB_INFO_2, position) },
+       { "start_time", PY_UINT32, offsetof(JOB_INFO_2, starttime) },
+       { "until_time", PY_UINT32, offsetof(JOB_INFO_2, untiltime) },
+       { "total_pages", PY_UINT32, offsetof(JOB_INFO_2, totalpages) },
+       { "size", PY_UINT32, offsetof(JOB_INFO_2, size) },
+       { "time_elapsed", PY_UINT32, offsetof(JOB_INFO_2, timeelapsed) },
+       { "pages_printed", PY_UINT32, offsetof(JOB_INFO_2, pagesprinted) },
+       { NULL }
+};
+
+struct pyconv py_DOC_INFO_1[] = {
+       { "document_name", PY_UNISTR, offsetof(DOC_INFO_1, docname) },
+       { "output_file", PY_UNISTR, offsetof(DOC_INFO_1, outputfile) },
+       { "data_type", PY_UNISTR, offsetof(DOC_INFO_1, datatype) },
+       { NULL }
+};
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info)
+{
+       *dict = from_struct(info, py_JOB_INFO_1);
+       return True;
+}
+
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info)
+{
+       *dict = from_struct(info, py_JOB_INFO_2);
+       return True;
+}
+
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info)
+{
+       *dict = from_struct(info, py_DOC_INFO_1);
+       return True;
+}
+
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict)
+{
+       return to_struct(info, dict, py_DOC_INFO_1);
+}
diff --git a/source4/python/py_spoolss_ports.c b/source4/python/py_spoolss_ports.c
new file mode 100644 (file)
index 0000000..ddc8868
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Enumerate ports */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw)
+{
+       WERROR werror;
+       PyObject *result = NULL, *creds = NULL;
+       uint32 level = 1;
+       uint32 i, needed, num_ports;
+       static char *kwlist[] = {"server", "level", "creds", NULL};
+       TALLOC_CTX *mem_ctx = NULL;
+       struct cli_state *cli = NULL;
+       char *server, *errstr;
+       PORT_INFO_CTR ctr;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|iO", kwlist, &server, &level, &creds))
+               return NULL;
+       
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_enumports"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               goto done;
+       }
+
+       /* Call rpc function */
+       
+       werror = cli_spoolss_enum_ports(
+               cli, mem_ctx, 0, &needed, level, &num_ports, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enum_ports(
+                       cli, mem_ctx, needed, NULL, level,
+                       &num_ports, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       /* Return value */
+       
+       switch (level) {
+       case 1: 
+               result = PyDict_New();
+
+               for (i = 0; i < num_ports; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, ctr.port.info_1[i].port_name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_PORT_INFO_1(&value, &ctr.port.info_1[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(1));
+
+                       PyDict_SetItemString(result, name, value);
+               }
+
+               break;
+       case 2:
+               result = PyDict_New();
+
+               for(i = 0; i < num_ports; i++) {
+                       PyObject *value;
+                       fstring name;
+
+                       rpcstr_pull(name, ctr.port.info_2[i].port_name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_PORT_INFO_2(&value, &ctr.port.info_2[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(2));
+
+                       PyDict_SetItemString(result, name, value);
+               }
+               
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unknown info level");
+               goto done;
+       }
+
+ done:
+       if (cli)
+               cli_shutdown(cli);
+       
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
diff --git a/source4/python/py_spoolss_ports_conv.c b/source4/python/py_spoolss_ports_conv.c
new file mode 100644 (file)
index 0000000..3f6d94b
--- /dev/null
@@ -0,0 +1,58 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PORT_INFO_1[] = {
+       { "name", PY_UNISTR, offsetof(PORT_INFO_1, port_name) },
+       { NULL }
+};
+
+struct pyconv py_PORT_INFO_2[] = {
+       { "name", PY_UNISTR, offsetof(PORT_INFO_2, port_name) },
+       { "monitor_name", PY_UNISTR, offsetof(PORT_INFO_2, monitor_name) },
+       { "description", PY_UNISTR, offsetof(PORT_INFO_2, description) },
+       { "reserved", PY_UINT32, offsetof(PORT_INFO_2, reserved) },
+       { "type", PY_UINT32, offsetof(PORT_INFO_2, port_type) },
+       { NULL }
+};     
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info)
+{
+       *dict = from_struct(info, py_PORT_INFO_1);
+       return True;
+}
+
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict)
+{
+       return False;
+}
+
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info)
+{
+       *dict = from_struct(info, py_PORT_INFO_2);
+       return True;
+}
+
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict)
+{
+       return False;
+}
diff --git a/source4/python/py_spoolss_printerdata.c b/source4/python/py_spoolss_printerdata.c
new file mode 100644 (file)
index 0000000..f165475
--- /dev/null
@@ -0,0 +1,450 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+static BOOL py_from_printerdata(PyObject **dict, char *key, char *value,
+                               uint16 data_type, uint8 *data, 
+                               uint32 data_size) 
+{
+       *dict = PyDict_New();
+
+       PyDict_SetItemString(*dict, "key", Py_BuildValue("s", key ? key : ""));
+       PyDict_SetItemString(*dict, "value", Py_BuildValue("s", value));
+       PyDict_SetItemString(*dict, "type", Py_BuildValue("i", data_type));
+
+       PyDict_SetItemString(*dict, "data", 
+                            Py_BuildValue("s#", data, data_size));
+
+       return True;
+}
+
+static BOOL py_to_printerdata(char **key, char **value, uint16 *data_type, 
+                             uint8 **data, uint32 *data_size, 
+                             PyObject *dict)
+{
+       PyObject *obj;
+
+       if ((obj = PyDict_GetItemString(dict, "key"))) {
+
+               if (!PyString_Check(obj)) {
+                       PyErr_SetString(spoolss_error,
+                                       "key not a string");
+                       return False;
+               }
+
+               if (key) {
+                       *key = PyString_AsString(obj);
+
+                       if (!key[0])
+                               *key = NULL;
+               }
+       } else
+               *key = NULL;
+
+       if ((obj = PyDict_GetItemString(dict, "value"))) {
+
+               if (!PyString_Check(obj)) {
+                       PyErr_SetString(spoolss_error,
+                                       "value not a string");
+                       return False;
+               }
+
+               *value = PyString_AsString(obj);
+       } else {
+               PyErr_SetString(spoolss_error, "no value present");
+               return False;
+       }
+
+       if ((obj = PyDict_GetItemString(dict, "type"))) {
+
+               if (!PyInt_Check(obj)) {
+                       PyErr_SetString(spoolss_error,
+                                       "type not an integer");
+                       return False;
+               }
+
+               *data_type = PyInt_AsLong(obj);
+       } else {
+               PyErr_SetString(spoolss_error, "no type present");
+               return False;
+       }
+       
+       if ((obj = PyDict_GetItemString(dict, "data"))) {
+
+               if (!PyString_Check(obj)) {
+                       PyErr_SetString(spoolss_error,
+                                       "data not a string");
+                       return False;
+               }
+
+               *data = PyString_AsString(obj);
+               *data_size = PyString_Size(obj);
+       } else {
+               PyErr_SetString(spoolss_error, "no data present");
+               return False;
+       }
+
+       return True;
+}
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "value", NULL };
+       char *valuename;
+       WERROR werror;
+       uint32 needed;
+       PyObject *result;
+       REGISTRY_VALUE value;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &valuename))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_getprinterdata(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, valuename,
+               &value);
+
+       if (W_ERROR_V(werror) == ERRmoredata) 
+               werror = cli_spoolss_getprinterdata(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, 
+                       valuename, &value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       py_from_printerdata(
+               &result, NULL, valuename, value.type, value.data_p, 
+               value.size);
+
+       return result;
+}
+
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "data", NULL };
+       PyObject *py_data;
+       char *valuename;
+       WERROR werror;
+       REGISTRY_VALUE value;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+               return NULL;
+       
+       if (!py_to_printerdata(
+                   NULL, &valuename, &value.type, &value.data_p, 
+                   &value.size, py_data))
+               return NULL;
+
+       fstrcpy(value.valuename, valuename);
+       
+       /* Call rpc function */
+
+       werror = cli_spoolss_setprinterdata(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, &value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { NULL };
+       uint32 data_needed, value_needed, ndx = 0;
+       WERROR werror;
+       PyObject *result;
+       REGISTRY_VALUE value;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+               return NULL;
+
+       /* Get max buffer sizes for value and data */
+
+       werror = cli_spoolss_enumprinterdata(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, ndx, 0, 0,
+               &value_needed, &data_needed, NULL);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       /* Iterate over all printerdata */
+
+       result = PyDict_New();
+
+       while (W_ERROR_IS_OK(werror)) {
+               PyObject *obj;
+
+               werror = cli_spoolss_enumprinterdata(
+                       hnd->cli, hnd->mem_ctx, &hnd->pol, ndx,
+                       value_needed, data_needed, NULL, NULL, &value);
+
+               if (py_from_printerdata(
+                           &obj, NULL, value.valuename, value.type, 
+                           value.data_p, value.size))
+                       PyDict_SetItemString(result, value.valuename, obj);
+
+               ndx++;
+       }
+
+       return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "value", NULL };
+       char *value;
+       WERROR werror;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &value))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_deleteprinterdata(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "key", "value", NULL };
+       char *key, *valuename;
+       WERROR werror;
+       uint32 needed;
+       PyObject *result;
+       REGISTRY_VALUE value;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &valuename))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_getprinterdataex(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key,
+               valuename, &value);
+
+       if (W_ERROR_V(werror) == ERRmoredata) 
+               werror = cli_spoolss_getprinterdataex(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key,
+                       valuename, &value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       py_from_printerdata(
+               &result, key, valuename, value.type, value.data_p, value.size);
+
+       return result;
+}
+
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "data", NULL };
+       PyObject *py_data;
+       char *keyname, *valuename;
+       WERROR werror;
+       REGISTRY_VALUE value;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+               return NULL;
+       
+       if (!py_to_printerdata(
+                   &keyname, &valuename, &value.type, &value.data_p, &value.size, py_data))
+               return NULL;
+
+       fstrcpy(value.valuename,  valuename);
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_setprinterdataex(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, keyname, &value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "key", NULL };
+       uint32 needed, i;
+       char *key;
+       WERROR werror;
+       PyObject *result;
+       REGVAL_CTR ctr;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &key))
+               return NULL;
+
+       /* Get max buffer sizes for value and data */
+
+       werror = cli_spoolss_enumprinterdataex(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key, &ctr);
+
+       if (W_ERROR_V(werror) == ERRmoredata) 
+               werror = cli_spoolss_enumprinterdataex(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key, 
+                       &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       /* Iterate over all printerdata */
+
+       result = PyDict_New();
+
+       for (i = 0; i < regval_ctr_numvals(&ctr); i++) {
+               REGISTRY_VALUE *value;
+               PyObject *item;
+
+               item = PyDict_New();
+               value = regval_ctr_specific_value(&ctr, i);
+
+               if (py_from_printerdata(
+                           &item, key, value->valuename, value->type, 
+                           value->data_p, value->size))
+                       PyDict_SetItemString(result, value->valuename, item);
+       }
+       
+       return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "key", "value", NULL };
+       char *key, *value;
+       WERROR werror;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &value))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_deleteprinterdataex(
+               hnd->cli, hnd->mem_ctx, &hnd->pol, key, value);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+                                    PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "key", NULL };
+       char *keyname;
+       WERROR werror;
+       uint32 needed, keylist_len;
+       uint16 *keylist;
+       PyObject *result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+               return NULL;
+
+       /* Call rpc function */
+
+       werror = cli_spoolss_enumprinterkey(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol,
+               keyname, &keylist, &keylist_len);
+
+       if (W_ERROR_V(werror) == ERRmoredata) 
+               werror = cli_spoolss_enumprinterkey(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       keyname, &keylist, &keylist_len);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       result = from_unistr_list(keylist);
+
+       return result;
+}
+
+#if 0
+
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+                                      PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       static char *kwlist[] = { "key", NULL };
+       char *keyname;
+       WERROR werror;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+               return NULL;
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+#endif
diff --git a/source4/python/py_spoolss_printers.c b/source4/python/py_spoolss_printers.c
new file mode 100644 (file)
index 0000000..d011681
--- /dev/null
@@ -0,0 +1,475 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+
+/* Open a printer */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       char *unc_name, *server, *errstr;
+       TALLOC_CTX *mem_ctx = NULL;
+       POLICY_HND hnd;
+       WERROR werror;
+       PyObject *result = NULL, *creds = NULL;
+       static char *kwlist[] = { "printername", "creds", "access", NULL };
+       uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+       struct cli_state *cli;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|Oi", kwlist, &unc_name, &creds,
+                   &desired_access))
+               return NULL;
+
+       if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server = strdup(unc_name + 2);
+
+       if (strchr(server, '\\')) {
+               char *c = strchr(server, '\\');
+               *c = 0;
+       }
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_openprinter"))) {
+               PyErr_SetString(spoolss_error, 
+                               "unable to init talloc context\n");
+               goto done;
+       }
+
+       werror = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, unc_name, "", desired_access, server, 
+               "", &hnd);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       result = new_spoolss_policy_hnd_object(cli, mem_ctx, &hnd);
+
+ done:
+       if (!result) {
+               if (cli)
+                       cli_shutdown(cli);
+
+               if (mem_ctx)
+                       talloc_destroy(mem_ctx);
+       }
+
+       SAFE_FREE(server);
+
+       return result;
+}
+
+/* Close a printer */
+
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args)
+{
+       PyObject *po;
+       spoolss_policy_hnd_object *hnd;
+       WERROR result;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTuple(args, "O!", &spoolss_policy_hnd_type, &po))
+               return NULL;
+
+       hnd = (spoolss_policy_hnd_object *)po;
+
+       /* Call rpc function */
+
+       result = cli_spoolss_close_printer(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+       /* Return value */
+
+       Py_INCREF(Py_None);
+       return Py_None; 
+}
+
+/* Fetch printer information */
+
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *result = NULL;
+       PRINTER_INFO_CTR ctr;
+       int level = 1;
+       uint32 needed;
+       static char *kwlist[] = {"level", NULL};
+       
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+               return NULL;
+       
+       ZERO_STRUCT(ctr);
+
+       /* Call rpc function */
+       
+       werror = cli_spoolss_getprinter(
+               hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getprinter(
+                       hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+                       level, &ctr);
+
+       /* Return value */
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       result = Py_None;
+
+       switch (level) {
+               
+       case 0:
+               py_from_PRINTER_INFO_0(&result, ctr.printers_0);
+               break;
+
+       case 1:
+               py_from_PRINTER_INFO_1(&result, ctr.printers_1);
+               break;
+
+       case 2:
+               py_from_PRINTER_INFO_2(&result, ctr.printers_2);
+               break;
+
+       case 3:
+               py_from_PRINTER_INFO_3(&result, ctr.printers_3);
+               break;
+       }
+
+       Py_INCREF(result);
+       return result;
+}
+
+/* Set printer information */
+
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+       spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+       WERROR werror;
+       PyObject *info;
+       PRINTER_INFO_CTR ctr;
+       uint32 level;
+       static char *kwlist[] = {"dict", NULL};
+       union {
+               PRINTER_INFO_1 printers_1;
+               PRINTER_INFO_2 printers_2;
+               PRINTER_INFO_3 printers_3;
+       } pinfo;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O!", kwlist, &PyDict_Type, &info))
+               return NULL;
+       
+       if (!get_level_value(info, &level)) {
+               PyErr_SetString(spoolss_error, "invalid info level");
+               return NULL;
+       }
+
+       if (level < 1 && level > 3) {
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+
+       /* Fill in printer info */
+
+       ZERO_STRUCT(ctr);
+
+       switch (level) {
+       case 1:
+               ctr.printers_1 = &pinfo.printers_1;
+
+               if (!py_to_PRINTER_INFO_1(ctr.printers_1, info)){
+                       PyErr_SetString(spoolss_error, 
+                                       "error converting printer to info 1");
+                       return NULL;
+               }
+
+               break;
+       case 2:
+               ctr.printers_2 = &pinfo.printers_2;
+
+               if (!py_to_PRINTER_INFO_2(ctr.printers_2, info,
+                                         hnd->mem_ctx)){
+                       PyErr_SetString(spoolss_error, 
+                                       "error converting printer to info 2");
+                       return NULL;
+               }
+
+               break;
+       case 3:
+               ctr.printers_3 = &pinfo.printers_3;
+
+               if (!py_to_PRINTER_INFO_3(ctr.printers_3, info,
+                                         hnd->mem_ctx)) {
+                       PyErr_SetString(spoolss_error,
+                                       "error converting to printer info 3");
+                       return NULL;
+               }
+
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unsupported info level");
+               return NULL;
+       }
+
+       /* Call rpc function */
+       
+       werror = cli_spoolss_setprinter(hnd->cli, hnd->mem_ctx, &hnd->pol,
+                                       level, &ctr, 0);
+
+       /* Return value */
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               return NULL;
+       }
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Enumerate printers */
+
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw)
+{
+       WERROR werror;
+       PyObject *result = NULL, *creds = NULL;
+       PRINTER_INFO_CTR ctr;
+       int level = 1, flags = PRINTER_ENUM_LOCAL, i;
+       uint32 needed, num_printers;
+       static char *kwlist[] = {"server", "name", "level", "flags", 
+                                "creds", NULL};
+       TALLOC_CTX *mem_ctx = NULL;
+       struct cli_state *cli = NULL;
+       char *server, *errstr, *name = NULL;
+
+       /* Parse parameters */
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|siiO", kwlist, &server, &name, &level, 
+                   &flags, &creds))
+               return NULL;
+       
+       if (server[0] != '\\' || server[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server += 2;
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_enumprinters"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               goto done;
+       }
+
+       /* This RPC is weird.  By setting the server name to different
+          values we can get different behaviour.  If however the server
+          name is not specified, we default it to being the full server
+          name as this is probably what the caller intended.  To pass a
+          NULL name, pass a value of "" */
+
+       if (!name)
+               name = server;
+       else {
+               if (!name[0])
+                       name = NULL;
+       }
+
+       /* Call rpc function */
+       
+       werror = cli_spoolss_enum_printers(
+               cli, mem_ctx, 0, &needed, name, flags, level,
+               &num_printers, &ctr);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enum_printers(
+                       cli, mem_ctx, needed, NULL, name, flags, 
+                       level, &num_printers, &ctr);
+
+       if (!W_ERROR_IS_OK(werror)) {
+               PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+               goto done;
+       }
+
+       /* Return value */
+       
+       switch (level) {
+       case 0: 
+               result = PyDict_New();
+
+               for (i = 0; i < num_printers; i++) {
+                       PyObject *value;
+                       fstring s;
+
+                       rpcstr_pull(s, ctr.printers_0[i].printername.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_PRINTER_INFO_0(&value, &ctr.printers_0[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(0));
+
+                       PyDict_SetItemString(result, s, value);
+               }
+
+               break;
+       case 1:
+               result = PyDict_New();
+
+               for(i = 0; i < num_printers; i++) {
+                       PyObject *value;
+                       fstring s;
+
+                       rpcstr_pull(s, ctr.printers_1[i].name.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_PRINTER_INFO_1(&value, &ctr.printers_1[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(1));
+
+                       PyDict_SetItemString(result, s, value);
+               }
+               
+               break;
+       case 2:
+               result = PyDict_New();
+
+               for(i = 0; i < num_printers; i++) {
+                       PyObject *value;
+                       fstring s;
+
+                       rpcstr_pull(s, ctr.printers_2[i].printername.buffer,
+                                   sizeof(fstring), -1, STR_TERMINATE);
+
+                       py_from_PRINTER_INFO_2(&value, &ctr.printers_2[i]);
+
+                       PyDict_SetItemString(
+                               value, "level", PyInt_FromLong(2));
+
+                       PyDict_SetItemString(result, s, value);
+               }
+               
+               break;
+       default:
+               PyErr_SetString(spoolss_error, "unknown info level");
+               goto done;
+       }
+
+done:
+       if (cli)
+               cli_shutdown(cli);
+
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+/* Add a printer */
+
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = { "server", "printername", "info", "creds", 
+                                 NULL};
+       char *printername, *server, *errstr;
+       PyObject *info, *result = NULL, *creds = NULL;
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
+       PRINTER_INFO_CTR ctr;
+       PRINTER_INFO_2 info2;
+       WERROR werror;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "ssO!|O!", kwlist, &server, &printername,
+                   &PyDict_Type, &info, &PyDict_Type, &creds))
+               return NULL;
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+               PyErr_SetString(spoolss_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("spoolss_addprinterex"))) {
+               PyErr_SetString(
+                       spoolss_error, "unable to init talloc context\n");
+               goto done;
+       }
+
+       if (!py_to_PRINTER_INFO_2(&info2, info, mem_ctx)) {
+               PyErr_SetString(spoolss_error,
+                               "error converting to printer info 2");
+               goto done;
+       }
+
+       ctr.printers_2 = &info2;
+
+       werror = cli_spoolss_addprinterex(cli, mem_ctx, 2, &ctr);
+
+       Py_INCREF(Py_None);
+       result = Py_None;
+
+done:
+       if (cli)
+               cli_shutdown(cli);
+
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
diff --git a/source4/python/py_spoolss_printers_conv.c b/source4/python/py_spoolss_printers_conv.c
new file mode 100644 (file)
index 0000000..f7b2f51
--- /dev/null
@@ -0,0 +1,354 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PRINTER_INFO_0[] = {
+       { "name", PY_UNISTR, offsetof(PRINTER_INFO_0, printername) },
+       { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_0, servername) },
+
+       { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_0, cjobs) },
+       { "total_jobs", PY_UINT32, offsetof(PRINTER_INFO_0, total_jobs) },
+       { "total_bytes", PY_UINT32, offsetof(PRINTER_INFO_0, total_bytes) },
+
+       { "year", PY_UINT16, offsetof(PRINTER_INFO_0, year) },
+       { "month", PY_UINT16, offsetof(PRINTER_INFO_0, month) },
+       { "day_of_week", PY_UINT16, offsetof(PRINTER_INFO_0, dayofweek) },
+       { "day", PY_UINT16, offsetof(PRINTER_INFO_0, day) },
+       { "hour", PY_UINT16, offsetof(PRINTER_INFO_0, hour) },
+       { "minute", PY_UINT16, offsetof(PRINTER_INFO_0, minute) },
+       { "second", PY_UINT16, offsetof(PRINTER_INFO_0, second) },
+       { "milliseconds", PY_UINT16, offsetof(PRINTER_INFO_0, milliseconds) },
+
+       { "global_counter", PY_UINT32, offsetof(PRINTER_INFO_0, global_counter) },
+       { "total_pages", PY_UINT32, offsetof(PRINTER_INFO_0, total_pages) },
+
+       { "major_version", PY_UINT16, offsetof(PRINTER_INFO_0, major_version) },
+       { "build_version", PY_UINT16, offsetof(PRINTER_INFO_0, build_version) },
+
+       { "unknown7", PY_UINT32, offsetof(PRINTER_INFO_0, unknown7) },
+       { "unknown8", PY_UINT32, offsetof(PRINTER_INFO_0, unknown8) },
+       { "unknown9", PY_UINT32, offsetof(PRINTER_INFO_0, unknown9) },
+       { "session_counter", PY_UINT32, offsetof(PRINTER_INFO_0, session_counter)},
+       { "unknown11", PY_UINT32, offsetof(PRINTER_INFO_0, unknown11) },
+       { "printer_errors", PY_UINT32, offsetof(PRINTER_INFO_0, printer_errors) },
+       { "unknown13", PY_UINT32, offsetof(PRINTER_INFO_0, unknown13) },
+       { "unknown14", PY_UINT32, offsetof(PRINTER_INFO_0, unknown14) },
+       { "unknown15", PY_UINT32, offsetof(PRINTER_INFO_0, unknown15) },
+       { "unknown16", PY_UINT32, offsetof(PRINTER_INFO_0, unknown16) },
+       { "change_id", PY_UINT32, offsetof(PRINTER_INFO_0, change_id) },
+       { "unknown18", PY_UINT32, offsetof(PRINTER_INFO_0, unknown18) },
+       { "status", PY_UINT32, offsetof(PRINTER_INFO_0, status) },
+       { "unknown20", PY_UINT32, offsetof(PRINTER_INFO_0, unknown20) },
+       { "c_setprinter", PY_UINT32, offsetof(PRINTER_INFO_0, c_setprinter) },
+       { "unknown22", PY_UINT32, offsetof(PRINTER_INFO_0, unknown22) },
+       { "unknown23", PY_UINT32, offsetof(PRINTER_INFO_0, unknown23) },
+       { "unknown24", PY_UINT32, offsetof(PRINTER_INFO_0, unknown24) },
+       { "unknown25", PY_UINT32, offsetof(PRINTER_INFO_0, unknown25) },
+       { "unknown26", PY_UINT32, offsetof(PRINTER_INFO_0, unknown26) },
+       { "unknown27", PY_UINT32, offsetof(PRINTER_INFO_0, unknown27) },
+       { "unknown28", PY_UINT32, offsetof(PRINTER_INFO_0, unknown28) },
+       { "unknown29", PY_UINT32, offsetof(PRINTER_INFO_0, unknown29) },
+
+       { NULL }
+};     
+
+struct pyconv py_PRINTER_INFO_1[] = {
+       { "name", PY_UNISTR, offsetof(PRINTER_INFO_1, name) },
+       { "description", PY_UNISTR, offsetof(PRINTER_INFO_1, description) },
+       { "comment", PY_UNISTR, offsetof(PRINTER_INFO_1, comment) },
+       { "flags", PY_UINT32, offsetof(PRINTER_INFO_1, flags) },
+       { NULL }
+};     
+
+struct pyconv py_PRINTER_INFO_2[] = {
+       { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_2, servername) },
+       { "name", PY_UNISTR, offsetof(PRINTER_INFO_2, printername) },
+       { "share_name", PY_UNISTR, offsetof(PRINTER_INFO_2, sharename) },
+       { "port_name", PY_UNISTR, offsetof(PRINTER_INFO_2, portname) },
+       { "driver_name", PY_UNISTR, offsetof(PRINTER_INFO_2, drivername) },
+       { "comment", PY_UNISTR, offsetof(PRINTER_INFO_2, comment) },
+       { "location", PY_UNISTR, offsetof(PRINTER_INFO_2, location) },
+       { "datatype", PY_UNISTR, offsetof(PRINTER_INFO_2, datatype) },
+       { "sepfile", PY_UNISTR, offsetof(PRINTER_INFO_2, sepfile) },
+       { "print_processor", PY_UNISTR, offsetof(PRINTER_INFO_2, printprocessor) },
+       { "parameters", PY_UNISTR, offsetof(PRINTER_INFO_2, parameters) },
+       { "attributes", PY_UINT32, offsetof(PRINTER_INFO_2, attributes) },
+       { "default_priority", PY_UINT32, offsetof(PRINTER_INFO_2, defaultpriority) },
+       { "priority", PY_UINT32, offsetof(PRINTER_INFO_2, priority) },
+       { "start_time", PY_UINT32, offsetof(PRINTER_INFO_2, starttime) },
+       { "until_time", PY_UINT32, offsetof(PRINTER_INFO_2, untiltime) },
+       { "status", PY_UINT32, offsetof(PRINTER_INFO_2, status) },
+       { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_2, cjobs) },
+       { "average_ppm", PY_UINT32, offsetof(PRINTER_INFO_2, averageppm) },
+       { NULL }
+};     
+
+struct pyconv py_PRINTER_INFO_3[] = {
+       { "flags", PY_UINT32, offsetof(PRINTER_INFO_3, flags) },
+       { NULL }
+};     
+
+struct pyconv py_DEVICEMODE[] = {
+       { "device_name", PY_UNISTR, offsetof(DEVICEMODE, devicename) },
+       { "spec_version", PY_UINT16, offsetof(DEVICEMODE, specversion) },
+       { "driver_version", PY_UINT16, offsetof(DEVICEMODE, driverversion) },
+       { "size", PY_UINT16, offsetof(DEVICEMODE, size) },
+       { "fields", PY_UINT16, offsetof(DEVICEMODE, fields) },
+       { "orientation", PY_UINT16, offsetof(DEVICEMODE, orientation) },
+       { "paper_size", PY_UINT16, offsetof(DEVICEMODE, papersize) },
+       { "paper_width", PY_UINT16, offsetof(DEVICEMODE, paperwidth) },
+       { "paper_length", PY_UINT16, offsetof(DEVICEMODE, paperlength) },
+       { "scale", PY_UINT16, offsetof(DEVICEMODE, scale) },
+       { "copies", PY_UINT16, offsetof(DEVICEMODE, copies) },
+       { "default_source", PY_UINT16, offsetof(DEVICEMODE, defaultsource) },
+       { "print_quality", PY_UINT16, offsetof(DEVICEMODE, printquality) },
+       { "color", PY_UINT16, offsetof(DEVICEMODE, color) },
+       { "duplex", PY_UINT16, offsetof(DEVICEMODE, duplex) },
+       { "y_resolution", PY_UINT16, offsetof(DEVICEMODE, yresolution) },
+       { "tt_option", PY_UINT16, offsetof(DEVICEMODE, ttoption) },
+       { "collate", PY_UINT16, offsetof(DEVICEMODE, collate) },
+       { "form_name", PY_UNISTR, offsetof(DEVICEMODE, formname) },
+       { "log_pixels", PY_UINT16, offsetof(DEVICEMODE, logpixels) },
+       { "bits_per_pel", PY_UINT32, offsetof(DEVICEMODE, bitsperpel) },
+       { "pels_width", PY_UINT32, offsetof(DEVICEMODE, pelswidth) },
+       { "pels_height", PY_UINT32, offsetof(DEVICEMODE, pelsheight) },
+       { "display_flags", PY_UINT32, offsetof(DEVICEMODE, displayflags) },
+       { "display_frequency", PY_UINT32, offsetof(DEVICEMODE, displayfrequency) },
+       { "icm_method", PY_UINT32, offsetof(DEVICEMODE, icmmethod) },
+       { "icm_intent", PY_UINT32, offsetof(DEVICEMODE, icmintent) },
+       { "media_type", PY_UINT32, offsetof(DEVICEMODE, mediatype) },
+       { "dither_type", PY_UINT32, offsetof(DEVICEMODE, dithertype) },
+       { "reserved1", PY_UINT32, offsetof(DEVICEMODE, reserved1) },
+       { "reserved2", PY_UINT32, offsetof(DEVICEMODE, reserved2) },
+       { "panning_width", PY_UINT32, offsetof(DEVICEMODE, panningwidth) },
+       { "panning_height", PY_UINT32, offsetof(DEVICEMODE, panningheight) },
+       { NULL }
+};
+
+/*
+ * Convert between DEVICEMODE and Python
+ */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode)
+{
+       *dict = from_struct(devmode, py_DEVICEMODE);
+
+       PyDict_SetItemString(*dict, "private",
+                            PyString_FromStringAndSize(
+                                    devmode->private, devmode->driverextra));
+
+       return True;
+}
+
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict)
+{
+       PyObject *obj, *dict_copy = PyDict_Copy(dict);
+       BOOL result = False;
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "private")))
+               goto done;
+
+       if (!PyString_Check(obj))
+               goto done;
+
+       devmode->private = PyString_AsString(obj);
+       devmode->driverextra = PyString_Size(obj);
+
+       PyDict_DelItemString(dict_copy, "private");
+
+       if (!to_struct(devmode, dict_copy, py_DEVICEMODE))
+               goto done;
+
+       result = True;
+
+done:
+       Py_DECREF(dict_copy);
+       return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_0 and Python 
+ */
+
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info)
+{
+       *dict = from_struct(info, py_PRINTER_INFO_0);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(0));
+       return True;
+}
+
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict)
+{
+       return False;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python 
+ */
+
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info)
+{
+       *dict = from_struct(info, py_PRINTER_INFO_1);
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+       return True;
+}
+
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict)
+{
+       PyObject *obj, *dict_copy = PyDict_Copy(dict);
+       BOOL result = False;
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+           !PyInt_Check(obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "level");
+
+       if (!to_struct(info, dict_copy, py_PRINTER_INFO_1))
+               goto done;
+
+       result = True;
+
+done:
+       Py_DECREF(dict_copy);
+       return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_2 and Python 
+ */
+
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info)
+{
+       PyObject *obj;
+
+       *dict = from_struct(info, py_PRINTER_INFO_2);
+
+       /* The security descriptor could be NULL */
+
+       if (info->secdesc) {
+               if (py_from_SECDESC(&obj, info->secdesc))
+                       PyDict_SetItemString(*dict, "security_descriptor", obj);
+       }
+
+       /* Bong!  The devmode could be NULL */
+
+       if (info->devmode)
+               py_from_DEVICEMODE(&obj, info->devmode);
+       else
+               obj = PyDict_New();
+
+       PyDict_SetItemString(*dict, "device_mode", obj);
+
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+       return True;
+}
+
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+                         TALLOC_CTX *mem_ctx)
+{
+       PyObject *obj, *dict_copy = PyDict_Copy(dict);
+       BOOL result = False;
+
+       /* Convert security descriptor - may be NULL */
+
+       info->secdesc = NULL;
+
+       if ((obj = PyDict_GetItemString(dict_copy, "security_descriptor"))) {
+
+               if (!PyDict_Check(obj))
+                       goto done;
+
+               if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+                       goto done;
+
+               PyDict_DelItemString(dict_copy, "security_descriptor");
+       }
+
+       /* Convert device mode */
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "device_mode"))
+           || !PyDict_Check(obj))
+               goto done;
+
+       info->devmode = talloc(mem_ctx, sizeof(DEVICEMODE));
+
+       if (!py_to_DEVICEMODE(info->devmode, obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "device_mode");
+
+       /* Check info level */
+
+       if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+           !PyInt_Check(obj))
+               goto done;
+
+       PyDict_DelItemString(dict_copy, "level");
+
+       /* Convert remaining elements of dictionary */
+
+       if (!to_struct(info, dict_copy, py_PRINTER_INFO_2))
+               goto done;
+
+       result = True;
+
+done:
+       Py_DECREF(dict_copy);
+       return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python 
+ */
+
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info)
+{
+       PyObject *obj;  
+
+       *dict = from_struct(info, py_PRINTER_INFO_3);
+
+       if (py_from_SECDESC(&obj, info->secdesc))
+               PyDict_SetItemString(*dict, "security_descriptor", obj);
+
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+       return True;
+}
+
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+                         TALLOC_CTX *mem_ctx)
+{
+       PyObject *obj;
+
+       if (!to_struct(info, dict, py_PRINTER_INFO_3))
+               return False;
+
+       if (!(obj = PyDict_GetItemString(dict, "security_descriptor")))
+               return False;
+
+       if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+               return False;
+
+       return True;
+}
diff --git a/source4/python/py_srvsvc.c b/source4/python/py_srvsvc.c
new file mode 100644 (file)
index 0000000..8ec2430
--- /dev/null
@@ -0,0 +1,215 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2003
+   
+   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 "python/py_srvsvc.h"
+
+/* Exceptions this module can raise */
+
+PyObject *srvsvc_error, *srvsvc_werror;
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+       { "SV_TYPE_WORKSTATION", SV_TYPE_WORKSTATION },
+       { "SV_TYPE_SERVER", SV_TYPE_SERVER },
+       { "SV_TYPE_SQLSERVER", SV_TYPE_SQLSERVER },
+       { "SV_TYPE_DOMAIN_CTRL", SV_TYPE_DOMAIN_CTRL },
+       { "SV_TYPE_DOMAIN_BAKCTRL", SV_TYPE_DOMAIN_BAKCTRL },
+       { "SV_TYPE_TIME_SOURCE", SV_TYPE_TIME_SOURCE },
+       { "SV_TYPE_AFP", SV_TYPE_AFP },
+       { "SV_TYPE_NOVELL", SV_TYPE_NOVELL },
+       { "SV_TYPE_DOMAIN_MEMBER", SV_TYPE_DOMAIN_MEMBER },
+       { "SV_TYPE_PRINTQ_SERVER", SV_TYPE_PRINTQ_SERVER },
+       { "SV_TYPE_DIALIN_SERVER", SV_TYPE_DIALIN_SERVER },
+       { "SV_TYPE_SERVER_UNIX", SV_TYPE_SERVER_UNIX },
+       { "SV_TYPE_NT", SV_TYPE_NT },
+       { "SV_TYPE_WFW", SV_TYPE_WFW },
+       { "SV_TYPE_SERVER_MFPN", SV_TYPE_SERVER_MFPN },
+       { "SV_TYPE_SERVER_NT", SV_TYPE_SERVER_NT },
+       { "SV_TYPE_POTENTIAL_BROWSER", SV_TYPE_POTENTIAL_BROWSER },
+       { "SV_TYPE_BACKUP_BROWSER", SV_TYPE_BACKUP_BROWSER },
+       { "SV_TYPE_MASTER_BROWSER", SV_TYPE_MASTER_BROWSER },
+       { "SV_TYPE_DOMAIN_MASTER", SV_TYPE_DOMAIN_MASTER },
+       { "SV_TYPE_SERVER_OSF", SV_TYPE_SERVER_OSF },
+       { "SV_TYPE_SERVER_VMS", SV_TYPE_SERVER_VMS },
+       { "SV_TYPE_WIN95_PLUS", SV_TYPE_WIN95_PLUS },
+       { "SV_TYPE_DFS_SERVER", SV_TYPE_DFS_SERVER },
+       { "SV_TYPE_ALTERNATE_XPORT", SV_TYPE_ALTERNATE_XPORT },
+       { "SV_TYPE_LOCAL_LIST_ONLY", SV_TYPE_LOCAL_LIST_ONLY },
+       { "SV_TYPE_DOMAIN_ENUM", SV_TYPE_DOMAIN_ENUM },
+       { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/* NetServerGetInfo */
+
+PyObject *srvsvc_netservergetinfo(PyObject *self, PyObject *args,
+                                 PyObject *kw)
+{
+       static char *kwlist[] = { "server", "level", "creds", NULL };
+       char *unc_name, *server, *errstr;
+       PyObject *creds = NULL, *result = NULL;
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx = NULL;
+       uint32 level;
+       SRV_INFO_CTR ctr;
+       WERROR status;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "si|O", kwlist, &unc_name, &level, &creds))
+               return NULL;
+
+       if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+               PyErr_SetString(PyExc_ValueError, "UNC name required");
+               return NULL;
+       }
+
+       server = strdup(unc_name + 2);
+
+       if (strchr(server, '\\')) {
+               char *c = strchr(server, '\\');
+               *c = 0;
+       }
+
+       if (creds && creds != Py_None && !PyDict_Check(creds)) {
+               PyErr_SetString(PyExc_TypeError, 
+                               "credentials must be dictionary or None");
+               return NULL;
+       }
+
+       if (!(cli = open_pipe_creds(server, creds, PI_SRVSVC, &errstr))) {
+               PyErr_SetString(srvsvc_error, errstr);
+               free(errstr);
+               goto done;
+       }
+
+       if (!(mem_ctx = talloc_init("srvsvc_netservergetinfo"))) {
+               PyErr_SetString(srvsvc_error, 
+                               "unable to init talloc context\n");
+               goto done;
+       }
+
+       ZERO_STRUCT(ctr);
+
+       status = cli_srvsvc_net_srv_get_info(cli, mem_ctx, level, &ctr);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetObject(srvsvc_error, py_werror_tuple(status));
+               goto done;
+       }
+
+       if (level != ctr.switch_value) {
+               PyErr_SetString(srvsvc_error, "container level value wrong");
+               goto done;
+       }
+
+       switch(level) {
+       case 101:
+               py_from_SRV_INFO_101(&result, &ctr.srv.sv101);
+               break;
+       }
+
+       Py_INCREF(result);
+
+done:
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+/*
+ * Module initialisation 
+ */
+
+static PyMethodDef srvsvc_methods[] = {
+       { "netservergetinfo", (PyCFunction)srvsvc_netservergetinfo,
+         METH_VARARGS | METH_KEYWORDS,
+         "Retrieve information about a particular server." },
+
+       { "setup_logging", (PyCFunction)py_setup_logging, 
+         METH_VARARGS | METH_KEYWORDS, 
+         "Set up debug logging.
+
+Initialises Samba's debug logging system.  One argument is expected which
+is a boolean specifying whether debugging is interactive and sent to stdout
+or logged to a file.
+
+Example:
+
+>>> srvsvc.setup_logging(interactive = 1)" },
+
+       { "get_debuglevel", (PyCFunction)get_debuglevel, 
+         METH_VARARGS, 
+         "Set the current debug level.
+
+Example:
+
+>>> srvsvc.get_debuglevel()
+0" },
+
+       { "set_debuglevel", (PyCFunction)set_debuglevel, 
+         METH_VARARGS, 
+         "Get the current debug level.
+
+Example:
+
+>>> srvsvc.set_debuglevel(10)" },
+
+       { NULL }
+};
+
+void initsrvsvc(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("srvsvc", srvsvc_methods);
+       dict = PyModule_GetDict(module);
+
+       /* Exceptions we can raise */
+
+       srvsvc_error = PyErr_NewException("srvsvc.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", srvsvc_error);
+
+       srvsvc_werror = PyErr_NewException("srvsvc.werror", NULL, NULL);
+       PyDict_SetItemString(dict, "werror", srvsvc_werror);
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+}
diff --git a/source4/python/py_srvsvc.h b/source4/python/py_srvsvc.h
new file mode 100644 (file)
index 0000000..b440c32
--- /dev/null
@@ -0,0 +1,26 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2003
+   
+   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.
+*/
+
+#ifndef _PY_SRVSVC_H
+#define _PY_SRVSVC_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_SRVSVC_H */
diff --git a/source4/python/py_srvsvc_conv.c b/source4/python/py_srvsvc_conv.c
new file mode 100644 (file)
index 0000000..de43f07
--- /dev/null
@@ -0,0 +1,43 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2003
+   
+   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 "python/py_srvsvc.h"
+#include "python/py_conv.h"
+
+static struct pyconv py_SRV_INFO_101[] = {
+       { "platform_id", PY_UINT32, offsetof(SRV_INFO_101, platform_id) },
+       { "major_version", PY_UINT32, offsetof(SRV_INFO_101, ver_major) },
+       { "minor_version", PY_UINT32, offsetof(SRV_INFO_101, ver_minor) },
+       { "server_type", PY_UINT32, offsetof(SRV_INFO_101, srv_type) },
+       { "name", PY_UNISTR2, offsetof(SRV_INFO_101, uni_name) },
+       { "comment", PY_UNISTR2, offsetof(SRV_INFO_101, uni_comment) },
+       { NULL }
+};     
+
+BOOL py_from_SRV_INFO_101(PyObject **dict, SRV_INFO_101 *info)
+{
+       PyObject *obj;  
+
+       *dict = from_struct(info, py_SRV_INFO_101);
+
+       PyDict_SetItemString(*dict, "level", PyInt_FromLong(101));
+
+       return True;
+}
diff --git a/source4/python/py_tdb.c b/source4/python/py_tdb.c
new file mode 100644 (file)
index 0000000..4969c10
--- /dev/null
@@ -0,0 +1,614 @@
+/* 
+   Python wrappers for TDB module
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+/* 
+   NOTE: Since tdb is licenced under the GPL any program that uses these bindings
+   must be distributed under the GPL license terms since this is what
+   the GPL requires.
+
+   http://www.gnu.org/licenses/gpl-faq.html#IfInterpreterIsGPL 
+*/
+
+#include "includes.h"
+#include "Python.h"
+
+/* Tdb exception */
+
+PyObject *py_tdb_error;
+
+/* tdb handle object */
+
+typedef struct {
+       PyObject_HEAD
+       TDB_CONTEXT *tdb;
+} tdb_hnd_object;
+
+PyTypeObject tdb_hnd_type;
+     
+PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
+{
+       tdb_hnd_object *obj;
+
+       obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
+       obj->tdb = tdb;
+
+       return (PyObject *)obj;
+}
+
+PyObject *py_tdb_close(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj;
+
+       if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
+               return NULL;
+
+       if (tdb_close(obj->tdb) == -1) {
+               obj->tdb = NULL;
+               PyErr_SetString(py_tdb_error, strerror(errno));
+               return NULL;
+       }
+
+       obj->tdb = NULL;
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = { "name", "hash_size", "tdb_flags",
+                                 "open_flags", "mode", NULL };
+       char *name;
+       int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;      
+       TDB_CONTEXT *tdb;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
+                   &open_flags, &open_mode))
+               return NULL;
+
+       /* Default open_flags to read/write */
+
+       if (open_flags == -1) {
+               if (access(name, W_OK) == -1)
+                       open_flags = O_RDONLY;
+               else
+                       open_flags = O_RDWR;
+       }
+
+       if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
+               PyErr_SetString(py_tdb_error, strerror(errno));
+               return NULL;
+       }
+
+       return new_tdb_hnd_object(tdb);
+}
+
+/*
+ * Allow a tdb to act as a python mapping (dictionary)
+ */
+
+static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                             void *state)
+{
+       /* Do nothing - tdb_traverse will return the number of records
+           traversed. */
+
+       return 0;
+}
+
+static int tdb_hnd_length(tdb_hnd_object *obj)
+{
+       int result;
+
+       result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
+
+       return result;
+}
+
+static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
+{
+       TDB_DATA drec, krec;
+       PyObject *result;
+
+       if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
+               return NULL;
+
+       drec = tdb_fetch(obj->tdb, krec);
+
+       if (!drec.dptr) {
+               PyErr_SetString(PyExc_KeyError,
+                               PyString_AsString(key));
+               return NULL;
+       }
+
+       result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
+       free(drec.dptr);
+
+       return result;
+}
+       
+static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
+{
+       TDB_DATA krec, drec;
+
+        if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
+               PyErr_SetString(PyExc_TypeError,
+                               "tdb mappings have string indices only");
+               return -1;
+       }
+
+        if (!obj->tdb) {
+               PyErr_SetString(
+                       py_tdb_error, "tdb object has been closed"); 
+               return -1; 
+        }
+
+       if (!value) {
+
+               /* Delete value */
+
+               if (tdb_delete(obj->tdb, krec) == -1) {
+                       PyErr_SetString(PyExc_KeyError,
+                                       PyString_AsString(value));
+                       return -1;
+               }
+
+       } else {
+
+               /* Set value */
+
+               if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
+                       PyErr_SetString(PyExc_TypeError,
+                                   "tdb mappings have string elements only");
+                       return -1;
+               }
+
+               errno = 0;
+
+               if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
+                       if (errno != 0)
+                               PyErr_SetFromErrno(py_tdb_error);
+                       else
+                               PyErr_SetString(
+                                       py_tdb_error, 
+                                       (char *)tdb_errorstr(obj->tdb));
+
+                       return -1;
+               }
+       }
+
+       return 0;
+} 
+
+static PyMappingMethods tdb_mapping = {
+       (inquiry) tdb_hnd_length,
+       (binaryfunc) tdb_hnd_subscript,
+       (objobjargproc) tdb_ass_subscript
+};
+
+/*
+ * Utility methods
+ */
+
+/* Return non-zero if a given key exists in the tdb */
+
+PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+
+       if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+               return NULL;
+
+        if (!obj->tdb) {
+               PyErr_SetString(
+                       py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       return PyInt_FromLong(tdb_exists(obj->tdb, key));
+}
+
+/* Return a list of keys in the tdb */
+
+static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                            void *state)
+{
+       PyObject *key_list = (PyObject *)state;
+
+       PyList_Append(key_list, 
+                     PyString_FromStringAndSize(key.dptr, key.dsize));
+
+       return 0;
+}
+
+PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       PyObject *key_list = PyList_New(0);
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
+               PyErr_SetString(py_tdb_error, "error traversing tdb");
+               Py_DECREF(key_list);
+               return NULL;
+       }
+
+       return key_list;        
+}
+
+PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       key = tdb_firstkey(obj->tdb);
+
+       return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key, oldkey;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
+               return NULL;
+
+       key = tdb_nextkey(obj->tdb, oldkey);
+
+       return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+/*
+ * Locking routines
+ */
+
+PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       int result;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       result = tdb_lockall(obj->tdb);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       tdb_unlockall(obj->tdb);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Return an array of keys from a python object which must be a string or a
+   list of strings. */
+
+static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
+{
+       /* Are we a list or a string? */
+
+       if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
+               PyErr_SetString(PyExc_TypeError, "arg must be list of string");
+               return False;
+       }
+
+       if (PyList_Check(py_keys)) {
+               int i;
+
+               /* Turn python list into array of keys */
+               
+               *num_keys = PyList_Size(py_keys);
+               *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA) * (*num_keys));
+               
+               for (i = 0; i < *num_keys; i++) {
+                       PyObject *key = PyList_GetItem(py_keys, i);
+                       
+                       if (!PyString_Check(key)) {
+                               PyErr_SetString(
+                                       PyExc_TypeError,
+                                       "list elements must be strings");
+                               return False;
+                       }
+
+                       PyArg_Parse(key, "s#", &(*keys)[i].dptr, 
+                                   &(*keys)[i].dsize);
+               }
+
+       } else {
+
+               /* Turn python string into a single key */
+
+               *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA));
+               *num_keys = 1;
+               PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
+       }
+
+       return True;
+}
+
+PyObject *py_tdb_hnd_lock(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       PyObject *py_keys;
+       TDB_DATA *keys;
+       int num_keys, result;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_ParseTuple(args, "O", &py_keys))
+               return NULL;
+
+       if (!make_lock_list(py_keys, &keys, &num_keys))
+               return NULL;
+
+       result = tdb_lockkeys(obj->tdb, num_keys, keys);
+
+       free(keys);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+       
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+       
+       tdb_unlockkeys(obj->tdb);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/*
+ * tdb traversal
+ */
+
+struct traverse_info {
+       PyObject *callback;
+       PyObject *state;
+};
+
+static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                                void *state)
+{
+       struct traverse_info *info = state;
+       PyObject *arglist, *py_result;
+       int result;
+
+       arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
+                               value.dsize, info->state);
+
+       py_result = PyEval_CallObject(info->callback, arglist);
+
+       Py_DECREF(arglist);
+       
+       if (!PyInt_Check(py_result)) {
+               result = 1;     /* Hmm - non-integer object returned by callback */
+               goto done;
+       }
+
+       result = PyInt_AsLong(py_result);
+
+done:
+       Py_DECREF(py_result);
+       return result;
+}
+
+PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       static char *kwlist[] = { "traverse_fn", "state", NULL };
+       PyObject *state = Py_None, *callback;
+       struct traverse_info info;
+       int result;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O|O", kwlist, &callback, &state))
+               return NULL;
+
+       if (!PyCallable_Check(callback)) {
+               PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+               return NULL;
+        }
+
+       Py_INCREF(callback);
+       Py_INCREF(state);
+
+       info.callback = callback;
+       info.state = state;
+
+       result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
+
+       Py_DECREF(callback);
+       Py_DECREF(state);
+
+       return PyInt_FromLong(result);
+}
+
+/* 
+ * Method dispatch table for this module
+ */
+
+static PyMethodDef tdb_methods[] = {
+       { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
+       { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
+       { NULL }
+};
+
+/* 
+ * Methods on a tdb object
+ */
+
+static PyMethodDef tdb_hnd_methods[] = {
+       { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
+       { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
+       { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
+       { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
+       { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
+       { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
+       { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
+       { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
+       { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
+       { NULL }
+};
+
+/* Deallocate a tdb handle object */
+
+static void tdb_hnd_dealloc(PyObject* self)
+{
+        tdb_hnd_object *hnd = (tdb_hnd_object *)self;
+
+       if (hnd->tdb) {
+               tdb_close(hnd->tdb);
+               hnd->tdb = NULL;
+       }
+}
+
+/* Return tdb handle attributes */
+
+static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(tdb_hnd_methods, self, attrname);
+}
+
+static char tdb_hnd_type_doc[] = 
+"Python wrapper for tdb.";
+
+PyTypeObject tdb_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "tdb",
+       sizeof(tdb_hnd_object),
+       0,
+       tdb_hnd_dealloc,        /* tp_dealloc*/
+       0,                      /* tp_print*/
+       tdb_hnd_getattr,        /* tp_getattr*/
+       0,                      /* tp_setattr*/
+       0,                      /* tp_compare*/
+       0,                      /* tp_repr*/
+       0,                      /* tp_as_number*/
+       0,                      /* tp_as_sequence*/
+       &tdb_mapping,           /* tp_as_mapping*/
+       0,                      /* tp_hash */
+       0,                      /* tp_call */
+       0,                      /* tp_str */
+       0,                      /* tp_getattro */
+       0,                      /* tp_setattro */
+       0,                      /* tp_as_buffer*/
+       Py_TPFLAGS_DEFAULT,     /* tp_flags */
+       tdb_hnd_type_doc,       /* tp_doc */
+};
+
+/* Constants */
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+
+        /* Flags for tdb_open() */
+
+       { "TDB_DEFAULT", TDB_DEFAULT },
+       { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
+       { "TDB_INTERNAL", TDB_INTERNAL },
+       { "TDB_NOLOCK", TDB_NOLOCK },
+       { "TDB_NOMMAP", TDB_NOMMAP },
+       { "TDB_CONVERT", TDB_CONVERT },
+       { "TDB_BIGENDIAN", TDB_BIGENDIAN },
+       
+       { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/* Module initialisation */
+
+void inittdb(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("tdb", tdb_methods);
+       dict = PyModule_GetDict(module);
+
+       py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", py_tdb_error);
+
+       /* Initialise policy handle object */
+
+       tdb_hnd_type.ob_type = &PyType_Type;
+
+       PyDict_SetItemString(dict, "tdb.hnd", 
+                            (PyObject *)&tdb_hnd_type);
+
+       /* Initialise constants */
+
+       const_init(dict);
+}
diff --git a/source4/python/py_tdb.h b/source4/python/py_tdb.h
new file mode 100644 (file)
index 0000000..69f251c
--- /dev/null
@@ -0,0 +1,26 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_TDB_H
+#define _PY_TDB_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_TDB_H */
diff --git a/source4/python/py_tdbpack.c b/source4/python/py_tdbpack.c
new file mode 100644 (file)
index 0000000..f0718b7
--- /dev/null
@@ -0,0 +1,725 @@
+/* -*- c-file-style: "python"; indent-tabs-mode: nil; -*-
+        
+   Python wrapper for Samba tdb pack/unpack functions
+   Copyright (C) Martin Pool 2002, 2003
+
+
+   NOTE PYTHON STYLE GUIDE
+   http://www.python.org/peps/pep-0007.html
+   
+   
+   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 "Python.h"
+
+/* This symbol is used in both config.h and Python.h which causes an
+   annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+/* This module is supposed to be standalone, however for portability
+   it would be good to use the FUNCTION_MACRO preprocessor define. */
+
+#include "include/config.h"
+
+#ifdef HAVE_FUNCTION_MACRO
+#define FUNCTION_MACRO  (__FUNCTION__)
+#else
+#define FUNCTION_MACRO  (__FILE__)
+#endif
+
+static PyObject * pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list);
+static PyObject * pytdbpack_str(char ch,
+                               PyObject *val_iter, PyObject *packed_list,
+                               const char *encoding);
+static PyObject * pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list);
+
+static PyObject *pytdbunpack_item(char, char **pbuf, int *plen, PyObject *);
+
+static PyObject *pytdbpack_data(const char *format_str,
+                                    PyObject *val_seq,
+                                    PyObject *val_list);
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding);
+
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf);
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+                                   const char *expected,
+                                   PyObject *val_obj);
+
+static const char * pytdbpack_docstring =
+"Convert between Python values and Samba binary encodings.
+
+This module is conceptually similar to the standard 'struct' module, but it
+uses both a different binary format and a different description string.
+
+Samba's encoding is based on that used inside DCE-RPC and SMB: a
+little-endian, unpadded, non-self-describing binary format.  It is intended
+that these functions be as similar as possible to the routines in Samba's
+tdb/tdbutil module, with appropriate adjustments for Python datatypes.
+
+Python strings are used to specify the format of data to be packed or
+unpacked.
+
+String encodings are implied by the database format: they may be either DOS
+codepage (currently hardcoded to 850), or Unix codepage (currently hardcoded
+to be the same as the default Python encoding).
+
+tdbpack format strings:
+
+    'f': NUL-terminated string in codepage iso8859-1
+   
+    'P': same as 'f'
+
+    'F': NUL-terminated string in iso-8859-1
+
+    'd':  4 byte little-endian unsigned number
+
+    'w':  2 byte little-endian unsigned number
+
+    'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is
+          really just an \"exists\" or \"does not exist\" flag.  The boolean
+          value of the Python object is used.
+    
+    'B': 4-byte LE length, followed by that many bytes of binary data.
+         Corresponds to a Python integer giving the length, followed by a byte
+         string of the appropriate length.
+
+    '$': Special flag indicating that the preceding format code should be
+         repeated while data remains.  This is only supported for unpacking.
+
+    Every code corresponds to a single Python object, except 'B' which
+    corresponds to two values (length and contents), and '$', which produces
+    however many make sense.
+";
+
+
+static char const pytdbpack_doc[] = 
+"pack(format, values) -> buffer
+Pack Python objects into Samba binary format according to format string.
+
+arguments:
+    format -- string of tdbpack format characters
+    values -- sequence of value objects corresponding 1:1 to format characters
+
+returns:
+    buffer -- string containing packed data
+
+raises:
+    IndexError -- if there are too few values for the format
+    ValueError -- if any of the format characters is illegal
+    TypeError  -- if the format is not a string, or values is not a sequence,
+        or any of the values is of the wrong type for the corresponding
+        format character
+
+notes:
+    For historical reasons, it is not an error to pass more values than are consumed
+    by the format.
+";
+
+
+static char const pytdbunpack_doc[] =
+"unpack(format, buffer) -> (values, rest)
+Unpack Samba binary data according to format string.
+
+arguments:
+    format -- string of tdbpack characters
+    buffer -- string of packed binary data
+
+returns:
+    2-tuple of:
+        values -- sequence of values corresponding 1:1 to format characters
+        rest -- string containing data that was not decoded, or '' if the
+            whole string was consumed
+
+raises:
+    IndexError -- if there is insufficient data in the buffer for the
+        format (or if the data is corrupt and contains a variable-length
+        field extending past the end)
+    ValueError -- if any of the format characters is illegal
+
+notes:
+    Because unconsumed data is returned, you can feed it back in to the
+    unpacker to extract further fields.  Alternatively, if you wish to modify
+    some fields near the start of the data, you may be able to save time by
+    only unpacking and repacking the necessary part.
+";
+
+
+const char *pytdb_dos_encoding = "cp850";
+
+/* NULL, meaning that the Samba default encoding *must* be the same as the
+   Python default encoding. */
+const char *pytdb_unix_encoding = NULL;
+
+
+/*
+  * Pack objects to bytes.
+  *
+  * All objects are first individually encoded onto a list, and then the list
+  * of strings is concatenated.  This is faster than concatenating strings,
+  * and reasonably simple to code.
+  */
+static PyObject *
+pytdbpack(PyObject *self,
+              PyObject *args)
+{
+       char *format_str;
+       PyObject *val_seq, *val_iter = NULL,
+               *packed_list = NULL, *packed_str = NULL,
+               *empty_str = NULL;
+
+       /* TODO: Test passing wrong types or too many arguments */
+       if (!PyArg_ParseTuple(args, "sO", &format_str, &val_seq))
+               return NULL;
+
+       if (!(val_iter = PyObject_GetIter(val_seq)))
+               goto out;
+
+       /* Create list to hold strings until we're done, then join them all. */
+       if (!(packed_list = PyList_New(0)))
+               goto out;
+
+       if (!pytdbpack_data(format_str, val_iter, packed_list))
+               goto out;
+
+       /* this function is not officially documented but it works */
+       if (!(empty_str = PyString_InternFromString("")))
+               goto out;
+       
+       packed_str = _PyString_Join(empty_str, packed_list);
+
+  out:
+       Py_XDECREF(empty_str);
+       Py_XDECREF(val_iter);
+       Py_XDECREF(packed_list);
+
+       return packed_str;
+}
+
+
+/*
+  Pack data according to FORMAT_STR from the elements of VAL_SEQ into
+  PACKED_BUF.
+
+  The string has already been checked out, so we know that VAL_SEQ is large
+  enough to hold the packed data, and that there are enough value items.
+  (However, their types may not have been thoroughly checked yet.)
+
+  In addition, val_seq is a Python Fast sequence.
+
+  Returns NULL for error (with exception set), or None.
+*/
+PyObject *
+pytdbpack_data(const char *format_str,
+                   PyObject *val_iter,
+                   PyObject *packed_list)
+{
+       int format_i, val_i = 0;
+
+       for (format_i = 0, val_i = 0; format_str[format_i]; format_i++) {
+               char ch = format_str[format_i];
+
+               switch (ch) {
+                       /* dispatch to the appropriate packer for this type,
+                          which should pull things off the iterator, and
+                          append them to the packed_list */
+               case 'w':
+               case 'd':
+               case 'p':
+                       if (!(packed_list = pytdbpack_number(ch, val_iter, packed_list)))
+                               return NULL;
+                       break;
+
+               case 'f':
+               case 'P':
+                       if (!(packed_list = pytdbpack_str(ch, val_iter, packed_list, pytdb_unix_encoding)))
+                               return NULL;
+                       break;
+
+               case 'B':
+                       if (!(packed_list = pytdbpack_buffer(val_iter, packed_list)))
+                               return NULL;
+                       break;
+
+               default:
+                       PyErr_Format(PyExc_ValueError,
+                                    "%s: format character '%c' is not supported",
+                                    FUNCTION_MACRO, ch);
+                       return NULL;
+               }
+       }
+
+       return packed_list;
+}
+
+
+static PyObject *
+pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list)
+{
+       unsigned long val_long;
+       PyObject *val_obj = NULL, *long_obj = NULL, *result_obj = NULL;
+       PyObject *new_list = NULL;
+       unsigned char pack_buf[4];
+
+       if (!(val_obj = PyIter_Next(val_iter)))
+               goto out;
+
+       if (!(long_obj = PyNumber_Long(val_obj))) {
+               pytdbpack_bad_type(ch, "Number", val_obj);
+               goto out;
+       }
+
+       val_long = PyLong_AsUnsignedLong(long_obj);
+       pack_le_uint32(val_long, pack_buf);
+
+       /* pack as 32-bit; if just packing a 'w' 16-bit word then only take
+          the first two bytes. */
+       
+       if (!(result_obj = PyString_FromStringAndSize(pack_buf, ch == 'w' ? 2 : 4)))
+               goto out;
+
+       if (PyList_Append(packed_list, result_obj) != -1)
+               new_list = packed_list;
+
+  out:
+       Py_XDECREF(val_obj);
+       Py_XDECREF(long_obj);
+       Py_XDECREF(result_obj);
+
+       return new_list;
+}
+
+
+/*
+ * Take one string from the iterator val_iter, convert it to 8-bit, and return
+ * it.
+ *
+ * If the input is neither a string nor Unicode, an exception is raised.
+ *
+ * If the input is Unicode, then it is converted to the appropriate encoding.
+ *
+ * If the input is a String, and encoding is not null, then it is converted to
+ * Unicode using the default decoding method, and then converted to the
+ * encoding.  If the encoding is NULL, then the string is written out as-is --
+ * this is used when the default Python encoding is the same as the Samba
+ * encoding.
+ *
+ * I hope this approach avoids being too fragile w.r.t. being passed either
+ * Unicode or String objects.
+ */
+static PyObject *
+pytdbpack_str(char ch,
+             PyObject *val_iter, PyObject *packed_list, const char *encoding)
+{
+       PyObject *val_obj = NULL;
+       PyObject *unicode_obj = NULL;
+       PyObject *coded_str = NULL;
+       PyObject *nul_str = NULL;
+       PyObject *new_list = NULL;
+
+       if (!(val_obj = PyIter_Next(val_iter)))
+               goto out;
+
+       if (PyUnicode_Check(val_obj)) {
+               if (!(coded_str = PyUnicode_AsEncodedString(val_obj, encoding, NULL)))
+                       goto out;
+       }
+       else if (PyString_Check(val_obj) && !encoding) {
+               /* For efficiency, we assume that the Python interpreter has
+                  the same default string encoding as Samba's native string
+                  encoding.  On the PSA, both are always 8859-1. */
+               coded_str = val_obj;
+               Py_INCREF(coded_str);
+       }
+       else if (PyString_Check(val_obj)) {
+               /* String, but needs to be converted */
+               if (!(unicode_obj = PyString_AsDecodedObject(val_obj, NULL, NULL)))
+                       goto out;
+               if (!(coded_str = PyUnicode_AsEncodedString(unicode_obj, encoding, NULL)))
+                       goto out;
+       }
+       else {
+               pytdbpack_bad_type(ch, "String or Unicode", val_obj);
+               goto out;
+       }
+
+       if (!nul_str)
+               /* this is constant and often-used; hold it forever */
+               if (!(nul_str = PyString_FromStringAndSize("", 1)))
+                       goto out;
+
+       if ((PyList_Append(packed_list, coded_str) != -1)
+           && (PyList_Append(packed_list, nul_str) != -1))
+               new_list = packed_list;
+
+  out:
+       Py_XDECREF(val_obj);
+       Py_XDECREF(unicode_obj);
+       Py_XDECREF(coded_str);
+
+       return new_list;
+}
+
+
+/*
+ * Pack (LENGTH, BUFFER) pair onto the list.
+ *
+ * The buffer must already be a String, not Unicode, because it contains 8-bit
+ * untranslated data.  In some cases it will actually be UTF_16_LE data.
+ */
+static PyObject *
+pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list)
+{
+       PyObject *val_obj;
+       PyObject *new_list = NULL;
+       
+       /* pull off integer and stick onto list */
+       if (!(packed_list = pytdbpack_number('d', val_iter, packed_list)))
+               return NULL;
+
+       /* this assumes that the string is the right length; the old code did
+          the same. */
+       if (!(val_obj = PyIter_Next(val_iter)))
+               return NULL;
+
+       if (!PyString_Check(val_obj)) {
+               pytdbpack_bad_type('B', "String", val_obj);
+               goto out;
+       }
+       
+       if (PyList_Append(packed_list, val_obj) != -1)
+               new_list = packed_list;
+
+  out:
+       Py_XDECREF(val_obj);
+       return new_list;
+}
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+                                   const char *expected,
+                                   PyObject *val_obj)
+{
+       PyObject *r = PyObject_Repr(val_obj);
+       if (!r)
+               return NULL;
+       PyErr_Format(PyExc_TypeError,
+                    "tdbpack: format '%c' requires %s, not %s",
+                    ch, expected, PyString_AS_STRING(r));
+       Py_DECREF(r);
+       return val_obj;
+}
+
+
+/*
+  XXX: glib and Samba have quicker macro for doing the endianness conversions,
+  but I don't know of one in plain libc, and it's probably not a big deal.  I
+  realize this is kind of dumb because we'll almost always be on x86, but
+  being safe is important.
+*/
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf)
+{
+       pbuf[0] =         val_long & 0xff;
+       pbuf[1] = (val_long >> 8)  & 0xff;
+       pbuf[2] = (val_long >> 16) & 0xff;
+       pbuf[3] = (val_long >> 24) & 0xff;
+}
+
+
+static void pack_bytes(long len, const char *from,
+                      unsigned char **pbuf)
+{
+       memcpy(*pbuf, from, len);
+       (*pbuf) += len;
+}
+
+
+
+static PyObject *
+pytdbunpack(PyObject *self,
+                PyObject *args)
+{
+       char *format_str, *packed_str, *ppacked;
+       PyObject *val_list = NULL, *ret_tuple = NULL;
+       PyObject *rest_string = NULL;
+       int format_len, packed_len;
+       char last_format = '#'; /* invalid */
+       int i;
+       
+       /* get arguments */
+       if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len))
+               return NULL;
+
+       format_len = strlen(format_str);
+       
+       /* Allocate list to hold results.  Initially empty, and we append
+          results as we go along. */
+       val_list = PyList_New(0);
+       if (!val_list)
+               goto failed;
+       ret_tuple = PyTuple_New(2);
+       if (!ret_tuple)
+               goto failed;
+       
+       /* For every object, unpack.  */
+       for (ppacked = packed_str, i = 0; i < format_len && format_str[i] != '$'; i++) {
+               last_format = format_str[i];
+               /* packed_len is reduced in place */
+               if (!pytdbunpack_item(format_str[i], &ppacked, &packed_len, val_list))
+                       goto failed;
+       }
+
+       /* If the last character was '$', keep going until out of space */
+       if (format_str[i] == '$') {
+               if (i == 0) {
+                       PyErr_Format(PyExc_ValueError,
+                                    "%s: '$' may not be first character in format",
+                                    FUNCTION_MACRO);
+                       return NULL;
+               } 
+               while (packed_len > 0)
+                       if (!pytdbunpack_item(last_format, &ppacked, &packed_len, val_list))
+                               goto failed;
+       }
+       
+       /* save leftovers for next time */
+       rest_string = PyString_FromStringAndSize(ppacked, packed_len);
+       if (!rest_string)
+               goto failed;
+
+       /* return (values, rest) tuple; give up references to them */
+       PyTuple_SET_ITEM(ret_tuple, 0, val_list);
+       val_list = NULL;
+       PyTuple_SET_ITEM(ret_tuple, 1, rest_string);
+       val_list = NULL;
+       return ret_tuple;
+
+  failed:
+       /* handle failure: deallocate anything.  XDECREF forms handle NULL
+          pointers for objects that haven't been allocated yet. */
+       Py_XDECREF(val_list);
+       Py_XDECREF(ret_tuple);
+       Py_XDECREF(rest_string);
+       return NULL;
+}
+
+
+static void
+pytdbunpack_err_too_short(void)
+{
+       PyErr_Format(PyExc_IndexError,
+                    "%s: data too short for unpack format", FUNCTION_MACRO);
+}
+
+
+static PyObject *
+pytdbunpack_uint32(char **pbuf, int *plen)
+{
+       unsigned long v;
+       unsigned char *b;
+       
+       if (*plen < 4) {
+               pytdbunpack_err_too_short();
+               return NULL;
+       }
+
+       b = *pbuf;
+       v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+       
+       (*pbuf) += 4;
+       (*plen) -= 4;
+
+       return PyLong_FromUnsignedLong(v);
+}
+
+
+static PyObject *pytdbunpack_int16(char **pbuf, int *plen)
+{
+       long v;
+       unsigned char *b;
+       
+       if (*plen < 2) {
+               pytdbunpack_err_too_short();
+               return NULL;
+       }
+
+       b = *pbuf;
+       v = b[0] | b[1]<<8;
+       
+       (*pbuf) += 2;
+       (*plen) -= 2;
+
+       return PyInt_FromLong(v);
+}
+
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding)
+{
+       int len;
+       char *nul_ptr, *start;
+
+       start = *pbuf;
+       
+       nul_ptr = memchr(start, '\0', *plen);
+       if (!nul_ptr) {
+               pytdbunpack_err_too_short();
+               return NULL;
+       }
+
+       len = nul_ptr - start;
+
+       *pbuf += len + 1;       /* skip \0 */
+       *plen -= len + 1;
+
+       return PyString_Decode(start, len, encoding, NULL);
+}
+
+
+static PyObject *
+pytdbunpack_buffer(char **pbuf, int *plen, PyObject *val_list)
+{
+       /* first get 32-bit len */
+       long slen;
+       unsigned char *b;
+       unsigned char *start;
+       PyObject *str_obj = NULL, *len_obj = NULL;
+       
+       if (*plen < 4) {
+               pytdbunpack_err_too_short();
+               return NULL;
+       }
+       
+       b = *pbuf;
+       slen = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+
+       if (slen < 0) { /* surely you jest */
+               PyErr_Format(PyExc_ValueError,
+                            "%s: buffer seems to have negative length", FUNCTION_MACRO);
+               return NULL;
+       }
+
+       (*pbuf) += 4;
+       (*plen) -= 4;
+       start = *pbuf;
+
+       if (*plen < slen) {
+               PyErr_Format(PyExc_IndexError,
+                            "%s: not enough data to unpack buffer: "
+                            "need %d bytes, have %d", FUNCTION_MACRO,
+                            (int) slen, *plen);
+               return NULL;
+       }
+
+       (*pbuf) += slen;
+       (*plen) -= slen;
+
+       if (!(len_obj = PyInt_FromLong(slen)))
+               goto failed;
+
+       if (PyList_Append(val_list, len_obj) == -1)
+               goto failed;
+       
+       if (!(str_obj = PyString_FromStringAndSize(start, slen)))
+               goto failed;
+       
+       if (PyList_Append(val_list, str_obj) == -1)
+               goto failed;
+       
+       Py_DECREF(len_obj);
+       Py_DECREF(str_obj);
+       
+       return val_list;
+
+  failed:
+       Py_XDECREF(len_obj);    /* handles NULL */
+       Py_XDECREF(str_obj);
+       return NULL;
+}
+
+
+/* Unpack a single field from packed data, according to format character CH.
+   Remaining data is at *PBUF, of *PLEN.
+
+   *PBUF is advanced, and *PLEN reduced to reflect the amount of data that has
+   been consumed.
+
+   Returns a reference to None, or NULL for failure.
+*/
+static PyObject *pytdbunpack_item(char ch,
+                                 char **pbuf,
+                                 int *plen,
+                                 PyObject *val_list)
+{
+       PyObject *unpacked;
+       
+       if (ch == 'w') {        /* 16-bit int */
+               unpacked = pytdbunpack_int16(pbuf, plen);
+       }
+       else if (ch == 'd' || ch == 'p') { /* 32-bit int */
+               /* pointers can just come through as integers */
+               unpacked = pytdbunpack_uint32(pbuf, plen);
+       }
+       else if (ch == 'f' || ch == 'P') { /* nul-term string  */
+               unpacked = pytdbunpack_string(pbuf, plen, pytdb_unix_encoding);
+       }
+       else if (ch == 'B') { /* length, buffer */
+               return pytdbunpack_buffer(pbuf, plen, val_list);
+       }
+       else {
+               PyErr_Format(PyExc_ValueError,
+                            "%s: format character '%c' is not supported", 
+                             FUNCTION_MACRO, ch);
+               
+               return NULL;
+       }
+
+       /* otherwise OK */
+       if (!unpacked)
+               return NULL;
+
+       if (PyList_Append(val_list, unpacked) == -1)
+               val_list = NULL;
+
+       /* PyList_Append takes a new reference to the inserted object.
+          Therefore, we no longer need the original reference. */
+       Py_DECREF(unpacked);
+       
+       return val_list;
+}
+
+
+
+
+
+
+static PyMethodDef pytdbpack_methods[] = {
+       { "pack", pytdbpack, METH_VARARGS, (char *) pytdbpack_doc },
+       { "unpack", pytdbunpack, METH_VARARGS, (char *) pytdbunpack_doc },
+};
+
+DL_EXPORT(void)
+inittdbpack(void)
+{
+       Py_InitModule3("tdbpack", pytdbpack_methods,
+                      (char *) pytdbpack_docstring);
+}
diff --git a/source4/python/py_winbind.c b/source4/python/py_winbind.c
new file mode 100644 (file)
index 0000000..e9fc4b7
--- /dev/null
@@ -0,0 +1,724 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Python wrapper for winbind client functions.
+
+   Copyright (C) Tim Potter      2002
+   
+   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 "py_winbind.h"
+
+/* 
+ * Exceptions raised by this module 
+ */
+
+PyObject *winbind_error;       /* A winbind call returned WINBINDD_ERROR */
+
+/* Prototypes from common.h */
+
+NSS_STATUS winbindd_request(int req_type, 
+                           struct winbindd_request *request,
+                           struct winbindd_response *response);
+
+/*
+ * Name <-> SID conversion
+ */
+
+/* Convert a name to a sid */
+
+static PyObject *py_name_to_sid(PyObject *self, PyObject *args)
+
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       PyObject *result;
+       char *name, *p;
+       const char *sep;
+
+       if (!PyArg_ParseTuple(args, "s", &name))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       sep = lp_winbind_separator();
+
+       if ((p = strchr(name, sep[0]))) {
+               *p = 0;
+               fstrcpy(request.data.name.dom_name, name);
+               fstrcpy(request.data.name.name, p + 1);
+       } else {
+               fstrcpy(request.data.name.dom_name, lp_workgroup());
+               fstrcpy(request.data.name.name, name);
+       }
+
+       if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response)  
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;
+       }
+
+       result = PyString_FromString(response.data.sid.sid);
+
+       return result;
+}
+
+/* Convert a sid to a name */
+
+static PyObject *py_sid_to_name(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       PyObject *result;
+       char *sid, *name;
+
+       if (!PyArg_ParseTuple(args, "s", &sid))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response)  
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;
+       }
+
+       asprintf(&name, "%s%s%s", response.data.name.dom_name,
+                lp_winbind_separator(), response.data.name.name);
+
+       result = PyString_FromString(name);
+
+       free(name);
+
+       return result;
+}
+
+/*
+ * Enumerate users/groups
+ */
+
+/* Enumerate domain users */
+
+static PyObject *py_enum_domain_users(PyObject *self, PyObject *args)
+{
+       struct winbindd_response response;
+       PyObject *result;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       result = PyList_New(0);
+
+       if (response.extra_data) {
+               const char *extra_data = response.extra_data;
+               fstring name;
+
+               while (next_token(&extra_data, name, ",", sizeof(fstring)))
+                       PyList_Append(result, PyString_FromString(name));
+       }
+
+       return result;
+}
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_domain_groups(PyObject *self, PyObject *args)
+{
+       struct winbindd_response response;
+       PyObject *result = NULL;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       result = PyList_New(0);
+
+       if (response.extra_data) {
+               const char *extra_data = response.extra_data;
+               fstring name;
+
+               while (next_token(&extra_data, name, ",", sizeof(fstring)))
+                       PyList_Append(result, PyString_FromString(name));
+       }
+
+       return result;
+}
+
+/*
+ * Miscellaneous domain related
+ */
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_trust_dom(PyObject *self, PyObject *args)
+{
+       struct winbindd_response response;
+       PyObject *result = NULL;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       result = PyList_New(0);
+
+       if (response.extra_data) {
+               const char *extra_data = response.extra_data;
+               fstring name;
+
+               while (next_token(&extra_data, name, ",", sizeof(fstring)))
+                       PyList_Append(result, PyString_FromString(name));
+       }
+
+       return result;
+}
+
+/* Check machine account password */
+
+static PyObject *py_check_secret(PyObject *self, PyObject *args)
+{
+       struct winbindd_response response;
+
+       if (!PyArg_ParseTuple(args, ""))
+               return NULL;
+
+       ZERO_STRUCT(response);
+
+       if (winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       return PyInt_FromLong(response.data.num_entries);
+}
+
+/*
+ * Return a dictionary consisting of all the winbind related smb.conf
+ * parameters.  This is stored in the module object.
+ */
+
+static PyObject *py_config_dict(void)
+{
+       PyObject *result;
+       uid_t ulow, uhi;
+       gid_t glow, ghi;
+       
+       if (!(result = PyDict_New()))
+               return NULL;
+
+       /* Various string parameters */
+
+       PyDict_SetItemString(result, "workgroup", 
+                            PyString_FromString(lp_workgroup()));
+
+       PyDict_SetItemString(result, "separator", 
+                            PyString_FromString(lp_winbind_separator()));
+
+       PyDict_SetItemString(result, "template_homedir", 
+                            PyString_FromString(lp_template_homedir()));
+
+       PyDict_SetItemString(result, "template_shell", 
+                            PyString_FromString(lp_template_shell()));
+
+       /* Winbind uid/gid range */
+
+       if (lp_winbind_uid(&ulow, &uhi)) {
+               PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow));
+               PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi));
+       }
+
+       if (lp_winbind_gid(&glow, &ghi)) {
+               PyDict_SetItemString(result, "gid_low", PyInt_FromLong(glow));
+               PyDict_SetItemString(result, "gid_high", PyInt_FromLong(ghi));
+       }
+
+       return result;
+}
+
+/*
+ * ID mapping
+ */
+
+/* Convert a uid to a SID */
+
+static PyObject *py_uid_to_sid(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int id;
+
+       if (!PyArg_ParseTuple(args, "i", &id))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.uid = id;
+
+       if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a gid to a SID */
+
+static PyObject *py_gid_to_sid(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int id;
+
+       if (!PyArg_ParseTuple(args, "i", &id))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.gid = id;
+
+       if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a sid to a uid */
+
+static PyObject *py_sid_to_uid(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       char *sid;
+
+       if (!PyArg_ParseTuple(args, "s", &sid))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+
+       return PyInt_FromLong(response.data.uid);
+}
+
+/* Convert a sid to a gid */
+
+static PyObject *py_sid_to_gid(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       char *sid;
+
+       if (!PyArg_ParseTuple(args, "s", &sid))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.sid, sid);
+
+       if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+       
+       return PyInt_FromLong(response.data.gid);
+}
+
+/*
+ * PAM authentication functions
+ */
+
+/* Plaintext authentication */
+
+static PyObject *py_auth_plaintext(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       char *username, *password;
+
+       if (!PyArg_ParseTuple(args, "ss", &username, &password))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.auth.user, username);
+       fstrcpy(request.data.auth.pass, password);
+
+       if (winbindd_request(WINBINDD_PAM_AUTH, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+       
+       return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+/* Challenge/response authentication */
+
+static PyObject *py_auth_crap(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = 
+               {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
+       struct winbindd_request request;
+       struct winbindd_response response;
+       char *username, *password;
+       int use_lm_hash = 1, use_nt_hash = 1;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "ss|ii", kwlist, &username, &password, 
+                   &use_lm_hash, &use_nt_hash))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.auth_crap.user, username);
+
+       generate_random_buffer(request.data.auth_crap.chal, 8, False);
+        
+       if (use_lm_hash) {
+               SMBencrypt((uchar *)password, request.data.auth_crap.chal, 
+                          (uchar *)request.data.auth_crap.lm_resp);
+               request.data.auth_crap.lm_resp_len = 24;
+       }
+
+       if (use_nt_hash) {
+               SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
+                            (uchar *)request.data.auth_crap.nt_resp);
+               request.data.auth_crap.nt_resp_len = 24;
+       }
+
+       if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+       
+       return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+/* Get user info from name */
+
+static PyObject *py_getpwnam(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       char *username;
+       PyObject *result;
+
+       if (!PyArg_ParseTuple(args, "s", &username))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.username, username);
+
+       if (winbindd_request(WINBINDD_GETPWNAM, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+       
+       if (!py_from_winbind_passwd(&result, &response)) {
+               result = Py_None;
+               Py_INCREF(result);
+       }
+
+       return result;
+}
+
+/* Get user info from uid */
+
+static PyObject *py_getpwuid(PyObject *self, PyObject *args)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       uid_t uid;
+       PyObject *result;
+
+       if (!PyArg_ParseTuple(args, "i", &uid))
+               return NULL;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.uid = uid;
+
+       if (winbindd_request(WINBINDD_GETPWUID, &request, &response) 
+           != NSS_STATUS_SUCCESS) {
+               PyErr_SetString(winbind_error, "lookup failed");
+               return NULL;            
+       }
+       
+       if (!py_from_winbind_passwd(&result, &response)) {
+               result = Py_None;
+               Py_INCREF(result);
+       }
+
+       return result;
+}
+
+/*
+ * Method dispatch table
+ */
+
+static PyMethodDef winbind_methods[] = {
+
+       { "getpwnam", (PyCFunction)py_getpwnam, METH_VARARGS, "getpwnam(3)" },
+       { "getpwuid", (PyCFunction)py_getpwuid, METH_VARARGS, "getpwuid(3)" },
+
+       /* Name <-> SID conversion */
+
+       { "name_to_sid", (PyCFunction)py_name_to_sid, METH_VARARGS,
+         "name_to_sid(s) -> string
+
+Return the SID for a name.
+
+Example:
+
+>>> winbind.name_to_sid('FOO/Administrator')
+'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+       { "sid_to_name", (PyCFunction)py_sid_to_name, METH_VARARGS,
+         "sid_to_name(s) -> string
+
+Return the name for a SID.
+
+Example:
+
+>>> import winbind
+>>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')
+'FOO/Administrator' " },
+
+       /* Enumerate users/groups */
+
+       { "enum_domain_users", (PyCFunction)py_enum_domain_users, METH_VARARGS,
+         "enum_domain_users() -> list of strings
+
+Return a list of domain users.
+
+Example:
+
+>>> winbind.enum_domain_users()
+['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', 
+'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', 
+'FOO/whoops-ptang'] " },
+
+       { "enum_domain_groups", (PyCFunction)py_enum_domain_groups, 
+         METH_VARARGS,
+         "enum_domain_groups() -> list of strings
+
+Return a list of domain groups.
+
+Example:
+
+>>> winbind.enum_domain_groups()
+['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', 
+'FOO/Domain Users'] " },
+
+       /* ID mapping */
+
+       { "uid_to_sid", (PyCFunction)py_uid_to_sid, METH_VARARGS,
+         "uid_to_sid(int) -> string
+
+Return the SID for a UNIX uid.
+
+Example:
+
+>>> winbind.uid_to_sid(10000)   
+'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+       { "gid_to_sid", (PyCFunction)py_gid_to_sid, METH_VARARGS,
+         "gid_to_sid(int) -> string
+
+Return the UNIX gid for a SID.
+
+Example:
+
+>>> winbind.gid_to_sid(10001)
+'S-1-5-21-406022937-1377575209-526660263-512' " },
+
+       { "sid_to_uid", (PyCFunction)py_sid_to_uid, METH_VARARGS,
+         "sid_to_uid(string) -> int
+
+Return the UNIX uid for a SID.
+
+Example:
+
+>>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')
+10000 " },
+
+       { "sid_to_gid", (PyCFunction)py_sid_to_gid, METH_VARARGS,
+         "sid_to_gid(string) -> int
+
+Return the UNIX gid corresponding to a SID.
+
+Example:
+
+>>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')
+10001 " },
+
+       /* Miscellaneous */
+
+       { "check_secret", (PyCFunction)py_check_secret, METH_VARARGS,
+         "check_secret() -> int
+
+Check the machine trust account password.  The NT status is returned
+with zero indicating success. " },
+
+       { "enum_trust_dom", (PyCFunction)py_enum_trust_dom, METH_VARARGS,
+         "enum_trust_dom() -> list of strings
+
+Return a list of trusted domains.  The domain the server is a member 
+of is not included.
+
+Example:
+
+>>> winbind.enum_trust_dom()
+['NPSD-TEST2', 'SP2NDOM'] " },
+
+       /* PAM authorisation functions */
+
+       { "auth_plaintext", (PyCFunction)py_auth_plaintext, METH_VARARGS,
+         "auth_plaintext(s, s) -> int
+
+Authenticate a username and password using plaintext authentication.
+The NT status code is returned with zero indicating success." },
+
+       { "auth_crap", (PyCFunction)py_auth_crap, METH_VARARGS,
+         "auth_crap(s, s) -> int
+
+Authenticate a username and password using the challenge/response
+protocol.  The NT status code is returned with zero indicating
+success." },
+
+       { NULL }
+};
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+       char *docstring;
+} module_const_vals[] = {
+
+       /* Well known RIDs */
+       
+       { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN, 
+         "Well-known RID for Administrator user" },
+
+       { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
+         "Well-known RID for Guest user" },
+
+       { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
+         "Well-known RID for Domain Admins group" },
+
+       { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
+         "Well-known RID for Domain Users group" },
+
+       { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
+         "Well-known RID for Domain Guests group" }, 
+       
+       { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/*
+ * Module initialisation 
+ */
+
+static char winbind_module__doc__[] =
+"A python extension to winbind client functions.";
+
+void initwinbind(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+        module = Py_InitModule3("winbind", winbind_methods,
+                               winbind_module__doc__);
+
+       dict = PyModule_GetDict(module);
+
+       winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", winbind_error);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Insert configuration dictionary */
+
+       PyDict_SetItemString(dict, "config", py_config_dict());
+}
diff --git a/source4/python/py_winbind.h b/source4/python/py_winbind.h
new file mode 100644 (file)
index 0000000..10927ea
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_WINBIND_H
+#define _PY_WINBIND_H
+
+#include "python/py_common.h"
+
+/* The following definitions are from py_winbind_conv.c */
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response);
+
+#endif /* _PY_WINBIND_H */
diff --git a/source4/python/py_winbind_conv.c b/source4/python/py_winbind_conv.c
new file mode 100644 (file)
index 0000000..6e2eab5
--- /dev/null
@@ -0,0 +1,41 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_common.h"
+#include "python/py_conv.h"
+
+/* Convert a struct passwd to a dictionary */
+
+static struct pyconv py_passwd[] = {
+       { "pw_name", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_name) },
+       { "pw_passwd", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_passwd) },
+       { "pw_uid", PY_UID, offsetof(struct winbindd_response, data.pw.pw_uid) },
+       { "pw_guid", PY_GID, offsetof(struct winbindd_response, data.pw.pw_gid) },
+       { "pw_gecos", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_gecos) },
+       { "pw_dir", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_dir) },
+       { "pw_shell", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_shell) },
+       { NULL} 
+};
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response)
+{
+       *dict = from_struct(response, py_passwd);
+       return True;
+}
diff --git a/source4/python/py_winreg.c b/source4/python/py_winreg.c
new file mode 100644 (file)
index 0000000..ce27f5c
--- /dev/null
@@ -0,0 +1,82 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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 "python/py_winreg.h"
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+       
+       /* Registry value types */
+
+       { "REG_NONE", REG_NONE },
+       { "REG_SZ", REG_SZ },
+       { "REG_EXPAND_SZ", REG_EXPAND_SZ },
+       { "REG_BINARY", REG_BINARY },
+       { "REG_DWORD", REG_DWORD },
+       { "REG_DWORD_LE", REG_DWORD_LE },
+       { "REG_DWORD_BE", REG_DWORD_BE },
+       { "REG_LINK", REG_LINK },
+       { "REG_MULTI_SZ", REG_MULTI_SZ },
+       { "REG_RESOURCE_LIST", REG_RESOURCE_LIST },
+       { "REG_FULL_RESOURCE_DESCRIPTOR", REG_FULL_RESOURCE_DESCRIPTOR },
+       { "REG_RESOURCE_REQUIREMENTS_LIST", REG_RESOURCE_REQUIREMENTS_LIST },
+
+       { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/*
+ * Module initialisation 
+ */
+
+static PyMethodDef winreg_methods[] = {
+       { NULL }
+};
+
+void initwinreg(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("winreg", winreg_methods);
+       dict = PyModule_GetDict(module);
+
+       /* Initialise constants */
+
+       const_init(dict);
+
+       /* Do samba initialisation */
+
+       py_samba_init();
+}
diff --git a/source4/python/py_winreg.h b/source4/python/py_winreg.h
new file mode 100644 (file)
index 0000000..e19674d
--- /dev/null
@@ -0,0 +1,29 @@
+/* 
+   Python wrappers for DCERPC/SMB client routines.
+
+   Copyright (C) Tim Potter, 2002
+   
+   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.
+*/
+
+#ifndef _PY_WINREG_H
+#define _PY_WINREG_H
+
+#include "includes.h"
+#include "Python.h"
+
+#include "python/py_common.h"
+
+#endif /* _PY_WINREG_H */
diff --git a/source4/python/samba/.cvsignore b/source4/python/samba/.cvsignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/source4/python/samba/__init__.py b/source4/python/samba/__init__.py
new file mode 100644 (file)
index 0000000..c818ca3
--- /dev/null
@@ -0,0 +1,7 @@
+"""samba
+
+Various Python modules for interfacing to Samba.
+
+Try using help() to examine their documentation.
+"""
+
diff --git a/source4/python/samba/printerdata.py b/source4/python/samba/printerdata.py
new file mode 100644 (file)
index 0000000..33251f6
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+
+#
+# A python module that maps printerdata to a dictionary.  We define
+# two classes.  The printerdata class maps to Get/Set/Enum/DeletePrinterData
+# and the printerdata_ex class maps to Get/Set/Enum/DeletePrinterDataEx
+#
+
+#
+# TODO:
+#
+#   - Implement __delitem__
+#
+
+from samba import spoolss
+
+class printerdata:
+    def __init__(self, host, creds = {}):
+        self.hnd = spoolss.openprinter(host, creds = creds)
+
+    def keys(self):
+        return self.hnd.enumprinterdata().keys()
+
+    def __getitem__(self, key):
+        return self.hnd.getprinterdata(key)['data']
+
+    def __setitem__(self, key, value):
+        # Store as REG_BINARY for now
+        self.hnd.setprinterdata({"key": "", "value": key, "type": 3,
+                                 "data": value})
+        
+class printerdata_ex:
+    def __init__(self, host):
+        self.host = host
+        self.top_level_keys = ["PrinterDriverData", "DsSpooler", "DsDriver",
+                               "DsUser"]
+
+    def keys(self):
+        return self.top_level_keys
+
+    def has_key(self, key):
+        for k in self.top_level_keys:
+            if k == key:
+                return 1
+        return 0
+
+    class printerdata_ex_subkey:
+        def __init__(self, host, key):
+            self.hnd = spoolss.openprinter(host)
+            self.key = key
+
+        def keys(self):
+            return self.hnd.enumprinterdataex(self.key).keys()
+
+        def __getitem__(self, key):
+            return self.hnd.getprinterdataex(self.key, key)['data']
+
+    def __getitem__(self, key):
+        return self.printerdata_ex_subkey(self.host, key)
diff --git a/source4/python/setup.py b/source4/python/setup.py
new file mode 100755 (executable)
index 0000000..48487fe
--- /dev/null
@@ -0,0 +1,198 @@
+# -*- mode: python -*-
+#
+# Unix SMB/CIFS implementation.
+# Module packaging setup for Samba python extensions
+#
+# Copyright (C) Tim Potter, 2002-2003
+# Copyright (C) Martin Pool, 2002
+#
+# 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.
+#
+
+from distutils.core import setup
+from distutils.extension import Extension
+
+import sys, string, os
+
+# The Makefile passes in environment variable $PYTHON_OBJ as being the
+# list of Samba objects.  This kind of goes against the distutils.cmd
+# method of adding setup commands and will also confuse people who are
+# familiar with the python Distutils module.
+
+samba_objs = os.environ.get("PYTHON_OBJS", "")
+
+samba_cflags = os.environ.get("PYTHON_CFLAGS", "")
+
+samba_srcdir = os.environ.get("SRCDIR", "")
+
+# These variables are filled in by configure
+
+samba_libs = os.environ.get("LIBS", "")
+
+# Convert libs and objs from space separated strings to lists of strings
+# for distutils to digest.  Split "-l" prefix off library list.
+
+obj_list = string.split(samba_objs)
+
+lib_list = []
+
+for lib in string.split(samba_libs):
+    lib_list.append(string.replace(lib, "-l", ""))
+
+flags_list = string.split(samba_cflags)
+
+# Invoke distutils.setup
+
+setup(
+
+    # Overview information
+    
+    name = "Samba Python Extensions",
+    version = "0.1",
+    author = "Tim Potter",
+    author_email = "tpot@samba.org",
+    license = "GPL",
+
+    # Get the "samba" directory of Python source.  At the moment this
+    # just contains the __init__ file that makes it work as a
+    # subpackage.  This is needed even though everything else is an
+    # extension module.
+    package_dir = {"samba": os.path.join(samba_srcdir, "python", "samba")},
+    packages = ["samba"],
+    
+    # Module list
+    ext_package = "samba", 
+    ext_modules = [
+
+    # SPOOLSS pipe module
+
+    Extension(name = "spoolss",
+              sources = [samba_srcdir + "python/py_spoolss.c",
+                         samba_srcdir + "python/py_common.c",
+                         samba_srcdir + "python/py_conv.c",
+                         samba_srcdir + "python/py_ntsec.c",
+                         samba_srcdir + "python/py_spoolss_common.c",
+                         samba_srcdir + "python/py_spoolss_forms.c",
+                         samba_srcdir + "python/py_spoolss_forms_conv.c",
+                         samba_srcdir + "python/py_spoolss_drivers.c",
+                         samba_srcdir + "python/py_spoolss_drivers_conv.c",
+                         samba_srcdir + "python/py_spoolss_printers.c",
+                         samba_srcdir + "python/py_spoolss_printers_conv.c",
+                         samba_srcdir + "python/py_spoolss_printerdata.c",
+                         samba_srcdir + "python/py_spoolss_ports.c",
+                         samba_srcdir + "python/py_spoolss_ports_conv.c",
+                         samba_srcdir + "python/py_spoolss_jobs.c",
+                         samba_srcdir + "python/py_spoolss_jobs_conv.c",
+                         ],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # LSA pipe module
+
+    Extension(name = "lsa",
+              sources = [samba_srcdir + "python/py_lsa.c",
+                         samba_srcdir + "python/py_common.c",
+                         samba_srcdir + "python/py_ntsec.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # SAMR pipe module
+
+    Extension(name = "samr",
+              sources = [samba_srcdir + "python/py_samr.c",
+                         samba_srcdir + "python/py_samr_conv.c",
+                         samba_srcdir + "python/py_common.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # winbind client module
+
+    Extension(name = "winbind",
+              sources = [samba_srcdir + "python/py_winbind.c",
+                         samba_srcdir + "python/py_winbind_conv.c",
+                         samba_srcdir + "python/py_conv.c",
+                         samba_srcdir + "python/py_common.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # WINREG pipe module
+
+    Extension(name = "winreg",
+              sources = [samba_srcdir + "python/py_winreg.c",
+                         samba_srcdir + "python/py_common.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # SRVSVC pipe module
+
+    Extension(name = "srvsvc",
+              sources = [samba_srcdir + "python/py_srvsvc.c",
+                         samba_srcdir + "python/py_conv.c",
+                         samba_srcdir + "python/py_srvsvc_conv.c",
+                         samba_srcdir + "python/py_common.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # tdb module
+
+    Extension(name = "tdb",
+              sources = [samba_srcdir + "python/py_tdb.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # libsmb module
+
+    Extension(name = "smb",
+              sources = [samba_srcdir + "python/py_smb.c",
+                         samba_srcdir + "python/py_common.c",
+                         samba_srcdir + "python/py_ntsec.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # Moving to merge all individual extensions in to one big
+    # extension.  This is to avoid the fact that each extension is 3MB
+    # in size due to the lack of proper depedency management in Samba.
+
+    Extension(name = "samba",
+              sources = [samba_srcdir + "python/py_samba.c",
+                         samba_srcdir + "python/py_common.c"],
+              libraries = lib_list,
+              library_dirs = ["/usr/kerberos/lib"],
+              extra_compile_args = flags_list,
+              extra_objects = obj_list),
+
+    # tdbpack/unpack extensions.  Does not actually link to any Samba
+    # code, although it implements a compatible data format.
+    Extension(name = "tdbpack",
+              sources = [os.path.join(samba_srcdir, "python", "py_tdbpack.c")],
+              extra_compile_args = ["-I."])
+    ],
+)
diff --git a/source4/registry/reg_cachehook.c b/source4/registry/reg_cachehook.c
new file mode 100644 (file)
index 0000000..547eed3
--- /dev/null
@@ -0,0 +1,112 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+/* Implementation of registry hook cache tree */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static SORTED_TREE *cache_tree;
+extern REGISTRY_OPS regdb_ops;         /* these are the default */
+static REGISTRY_HOOK default_hook = { KEY_TREE_ROOT, &regdb_ops };
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+BOOL reghook_cache_init( void )
+{
+       cache_tree = sorted_tree_init( &default_hook, NULL, NULL );
+
+       return ( cache_tree == NULL );
+}
+
+/**********************************************************************
+ Add a new REGISTRY_HOOK to the cache.  Note that the keyname
+ is not in the exact format that a SORTED_TREE expects.
+ *********************************************************************/
+
+BOOL reghook_cache_add( REGISTRY_HOOK *hook )
+{
+       pstring key;
+       
+       if ( !hook )
+               return False;
+               
+       pstrcpy( key, "\\");
+       pstrcat( key, hook->keyname );  
+       
+       pstring_sub( key, "\\", "/" );
+
+       DEBUG(10,("reghook_cache_add: Adding key [%s]\n", key));
+               
+       return sorted_tree_add( cache_tree, key, hook );
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+REGISTRY_HOOK* reghook_cache_find( char *keyname )
+{
+       char *key;
+       int len;
+       REGISTRY_HOOK *hook;
+       
+       if ( !keyname )
+               return NULL;
+       
+       /* prepend the string with a '\' character */
+       
+       len = strlen( keyname );
+       if ( !(key = malloc( len + 2 )) ) {
+               DEBUG(0,("reghook_cache_find: malloc failed for string [%s] !?!?!\n",
+                       keyname));
+               return NULL;
+       }
+
+       *key = '\\';
+       strncpy( key+1, keyname, len+1);
+       
+       /* swap to a form understood by the SORTED_TREE */
+
+       string_sub( key, "\\", "/", 0 );
+               
+       DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key));
+       
+       hook = sorted_tree_find( cache_tree, key ) ;
+       
+       SAFE_FREE( key );
+       
+       return hook;
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+void reghook_dump_cache( int debuglevel )
+{
+       DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n"));
+       
+       sorted_tree_print_keys( cache_tree, debuglevel );
+}
diff --git a/source4/registry/reg_db.c b/source4/registry/reg_db.c
new file mode 100644 (file)
index 0000000..b0917c8
--- /dev/null
@@ -0,0 +1,311 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+/* Implementation of internal registry database functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static TDB_CONTEXT *tdb_reg;
+
+
+/***********************************************************************
+ Open the registry data in the tdb
+ ***********************************************************************/
+static BOOL init_registry_data( void )
+{
+       pstring         keyname;
+       REGSUBKEY_CTR   subkeys;
+
+       ZERO_STRUCTP( &subkeys );
+
+       /* HKEY_LOCAL_MACHINE */
+       
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       regsubkey_ctr_addkey( &subkeys, "SYSTEM" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+               
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM" );
+       regsubkey_ctr_addkey( &subkeys, "CurrentControlSet" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+               
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet" );
+       regsubkey_ctr_addkey( &subkeys, "Control" );
+       regsubkey_ctr_addkey( &subkeys, "Services" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control" );
+       regsubkey_ctr_addkey( &subkeys, "Print" );
+       regsubkey_ctr_addkey( &subkeys, "ProductOptions" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control/ProductOptions" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services" );
+       regsubkey_ctr_addkey( &subkeys, "Netlogon" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+               
+       regsubkey_ctr_init( &subkeys );
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon" );
+       regsubkey_ctr_addkey( &subkeys, "Parameters" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       regsubkey_ctr_destroy( &subkeys );
+               
+       pstrcpy( keyname, KEY_HKLM );
+       pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon/Parameters" );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ))
+               return False;
+       
+       /* HKEY_USER */
+               
+       pstrcpy( keyname, KEY_HKU );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+               return False;
+               
+       /* HKEY_CLASSES_ROOT*/
+               
+       pstrcpy( keyname, KEY_HKCR );
+       if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+               return False;
+               
+       return True;
+}
+
+/***********************************************************************
+ Open the registry database
+ ***********************************************************************/
+BOOL init_registry_db( void )
+{
+       static pid_t local_pid;
+
+       if (tdb_reg && local_pid == sys_getpid())
+               return True;
+
+       /* 
+        * try to open first without creating so we can determine
+        * if we need to init the data in the registry
+        */
+       
+       tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
+       if ( !tdb_reg ) 
+       {
+               tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+               if ( !tdb_reg ) {
+                       DEBUG(0,("init_registry: Failed to open registry %s (%s)\n",
+                               lock_path("registry.tdb"), strerror(errno) ));
+                       return False;
+               }
+               
+               DEBUG(10,("init_registry: Successfully created registry tdb\n"));
+               
+               /* create the registry here */
+               if ( !init_registry_data() ) {
+                       DEBUG(0,("init_registry: Failed to initiailize data in registry!\n"));
+                       return False;
+               }
+       }
+
+       local_pid = sys_getpid();
+               
+       return True;
+}
+
+
+
+/***********************************************************************
+ Add subkey strings to the registry tdb under a defined key
+ fmt is the same format as tdb_pack except this function only supports
+ fstrings
+
+ The full path to the registry key is used as database after the 
+ \'s are converted to /'s.  Key string is also normalized to UPPER
+ case.
+ ***********************************************************************/
+BOOL regdb_store_reg_keys( char *keyname, REGSUBKEY_CTR *ctr )
+{
+       TDB_DATA kbuf, dbuf;
+       char *buffer, *tmpbuf;
+       int i = 0;
+       uint32 len, buflen;
+       BOOL ret = True;
+       uint32 num_subkeys = regsubkey_ctr_numkeys( ctr );
+       
+       if ( !keyname )
+               return False;
+       
+       strupper_m( keyname  );
+       
+       /* allocate some initial memory */
+               
+       buffer = malloc(sizeof(pstring));
+       buflen = sizeof(pstring);
+       len = 0;
+       
+       /* store the number of subkeys */
+       
+       len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys );
+       
+       /* pack all the strings */
+       
+       for (i=0; i<num_subkeys; i++) {
+               len += tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+               if ( len > buflen ) {
+                       /* allocate some extra space */
+                       if ((tmpbuf = Realloc( buffer, len*2 )) == NULL) {
+                               DEBUG(0,("regdb_store_reg_keys: Failed to realloc memory of size [%d]\n", len*2));
+                               ret = False;
+                               goto done;
+                       }
+                       buffer = tmpbuf;
+                       buflen = len*2;
+                                       
+                       len = tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+               }               
+       }
+       
+       /* finally write out the data */
+       
+       kbuf.dptr = keyname;
+       kbuf.dsize = strlen(keyname)+1;
+       dbuf.dptr = buffer;
+       dbuf.dsize = len;
+       if ( tdb_store( tdb_reg, kbuf, dbuf, TDB_REPLACE ) == -1) {
+               ret = False;
+               goto done;
+       }
+
+done:          
+       SAFE_FREE( buffer );
+       
+       return ret;
+}
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys.  Memory should be 
+ released by the caller.  The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_keys( char* key, REGSUBKEY_CTR *ctr )
+{
+       pstring path;
+       uint32 num_items;
+       TDB_DATA dbuf;
+       char *buf;
+       uint32 buflen, len;
+       int i;
+       fstring subkeyname;
+
+       DEBUG(10,("regdb_fetch_reg_keys: Enter key => [%s]\n", key ? key : "NULL"));
+       
+       pstrcpy( path, key );
+       
+       /* convert to key format */
+       pstring_sub( path, "\\", "/" ); 
+       strupper_m( path );
+       
+       dbuf = tdb_fetch_by_string( tdb_reg, path );
+       
+       buf = dbuf.dptr;
+       buflen = dbuf.dsize;
+       
+       if ( !buf ) {
+               DEBUG(5,("regdb_fetch_reg_keys: tdb lookup failed to locate key [%s]\n", key));
+               return -1;
+       }
+       
+       len = tdb_unpack( buf, buflen, "d", &num_items);
+       
+       for (i=0; i<num_items; i++) {
+               len += tdb_unpack( buf+len, buflen-len, "f", subkeyname );
+               regsubkey_ctr_addkey( ctr, subkeyname );
+       }
+
+       SAFE_FREE( dbuf.dptr );
+       
+       DEBUG(10,("regdb_fetch_reg_keys: Exit [%d] items\n", num_items));
+       
+       return num_items;
+}
+
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys.  Memory should be 
+ released by the caller.  The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_values( char* key, REGVAL_CTR *val )
+{
+       return 0;
+}
+
+/***********************************************************************
+ Stub function since we do not currently support storing registry 
+ values in the registry.tdb
+ ***********************************************************************/
+
+BOOL regdb_store_reg_values( char *key, REGVAL_CTR *val )
+{
+       return False;
+}
+
+
+/* 
+ * Table of function pointers for default access
+ */
+REGISTRY_OPS regdb_ops = {
+       regdb_fetch_reg_keys,
+       regdb_fetch_reg_values,
+       regdb_store_reg_keys,
+       regdb_store_reg_values
+};
+
+
diff --git a/source4/registry/reg_frontend.c b/source4/registry/reg_frontend.c
new file mode 100644 (file)
index 0000000..a9dfb52
--- /dev/null
@@ -0,0 +1,260 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern REGISTRY_OPS printing_ops;
+extern REGISTRY_OPS regdb_ops;         /* these are the default */
+
+/* array of REGISTRY_HOOK's which are read into a tree for easy access */
+
+
+REGISTRY_HOOK reg_hooks[] = {
+  { KEY_PRINTING,   &printing_ops },
+  { NULL, NULL }
+};
+
+
+/***********************************************************************
+ Open the registry database and initialize the REGISTRY_HOOK cache
+ ***********************************************************************/
+BOOL init_registry( void )
+{
+       int i;
+       
+       if ( !init_registry_db() ) {
+               DEBUG(0,("init_registry: failed to initialize the registry tdb!\n"));
+               return False;
+       }
+               
+       /* build the cache tree of registry hooks */
+       
+       reghook_cache_init();
+       
+       for ( i=0; reg_hooks[i].keyname; i++ ) {
+               if ( !reghook_cache_add(&reg_hooks[i]) )
+                       return False;
+       }
+
+       if ( DEBUGLEVEL >= 20 )
+               reghook_dump_cache(20);
+
+       return True;
+}
+
+
+
+
+/***********************************************************************
+ High level wrapper function for storing registry subkeys
+ ***********************************************************************/
+BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
+{
+       if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys_fn )
+               return key->hook->ops->store_subkeys_fn( key->name, subkeys );
+       else
+               return False;
+
+}
+
+/***********************************************************************
+ High level wrapper function for storing registry values
+ ***********************************************************************/
+BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+       if ( key->hook && key->hook->ops && key->hook->ops->store_values_fn )
+               return key->hook->ops->store_values_fn( key->name, val );
+       else
+               return False;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry subkeys
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
+{
+       int result = -1;
+       
+       if ( key->hook && key->hook->ops && key->hook->ops->subkey_fn )
+               result = key->hook->ops->subkey_fn( key->name, subkey_ctr );
+
+       return result;
+}
+
+/***********************************************************************
+ retreive a specific subkey specified by index.  Caller is 
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_keys_specific( REGISTRY_KEY *key, char** subkey, uint32 key_index )
+{
+       static REGSUBKEY_CTR ctr;
+       static pstring save_path;
+       static BOOL ctr_init = False;
+       char *s;
+       
+       *subkey = NULL;
+       
+       /* simple caching for performance; very basic heuristic */
+       
+       if ( !ctr_init ) {
+               DEBUG(8,("fetch_reg_keys_specific: Initializing cache of subkeys for [%s]\n", key->name));
+               ZERO_STRUCTP( &ctr );   
+               regsubkey_ctr_init( &ctr );
+               
+               pstrcpy( save_path, key->name );
+               
+               if ( fetch_reg_keys( key, &ctr) == -1 )
+                       return False;
+                       
+               ctr_init = True;
+       }
+       /* clear the cache when key_index == 0 or the path has changed */
+       else if ( !key_index || StrCaseCmp( save_path, key->name) ) {
+
+               DEBUG(8,("fetch_reg_keys_specific: Updating cache of subkeys for [%s]\n", key->name));
+               
+               regsubkey_ctr_destroy( &ctr );  
+               regsubkey_ctr_init( &ctr );
+               
+               pstrcpy( save_path, key->name );
+               
+               if ( fetch_reg_keys( key, &ctr) == -1 )
+                       return False;
+       }
+       
+       if ( !(s = regsubkey_ctr_specific_key( &ctr, key_index )) )
+               return False;
+
+       *subkey = strdup( s );
+
+       return True;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry values
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+       int result = -1;
+       
+       if ( key->hook && key->hook->ops && key->hook->ops->value_fn )
+               result = key->hook->ops->value_fn( key->name, val );
+
+       return result;
+}
+
+
+/***********************************************************************
+ retreive a specific subkey specified by index.  Caller is 
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32 val_index )
+{
+       static REGVAL_CTR       ctr;
+       static pstring          save_path;
+       static BOOL             ctr_init = False;
+       REGISTRY_VALUE          *v;
+       
+       *val = NULL;
+       
+       /* simple caching for performance; very basic heuristic */
+       
+       if ( !ctr_init ) {
+               DEBUG(8,("fetch_reg_values_specific: Initializing cache of values for [%s]\n", key->name));
+
+               ZERO_STRUCTP( &ctr );   
+               regval_ctr_init( &ctr );
+               
+               pstrcpy( save_path, key->name );
+               
+               if ( fetch_reg_values( key, &ctr) == -1 )
+                       return False;
+                       
+               ctr_init = True;
+       }
+       /* clear the cache when val_index == 0 or the path has changed */
+       else if ( !val_index || StrCaseCmp(save_path, key->name) ) {
+
+               DEBUG(8,("fetch_reg_values_specific: Updating cache of values for [%s]\n", key->name));         
+               
+               regval_ctr_destroy( &ctr );     
+               regval_ctr_init( &ctr );
+               
+               pstrcpy( save_path, key->name );
+               
+               if ( fetch_reg_values( key, &ctr) == -1 )
+                       return False;
+       }
+       
+       if ( !(v = regval_ctr_specific_value( &ctr, val_index )) )
+               return False;
+
+       *val = dup_registry_value( v );
+
+       return True;
+}
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the apprapriate offsets withing the
+ path.
+ WARNING!!  Does modify the original string!
+ ***********************************************************************/
+
+BOOL reg_split_path( char *path, char **base, char **new_path )
+{
+       char *p;
+       
+       *new_path = *base = NULL;
+       
+       if ( !path)
+               return False;
+       
+       *base = path;
+       
+       p = strchr( path, '\\' );
+       
+       if ( p ) {
+               *p = '\0';
+               *new_path = p+1;
+       }
+       
+       return True;
+}
+
+
+
diff --git a/source4/registry/reg_objects.c b/source4/registry/reg_objects.c
new file mode 100644 (file)
index 0000000..9cfeb7f
--- /dev/null
@@ -0,0 +1,374 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_init( REGSUBKEY_CTR *ctr )
+{
+       if ( !ctr->ctx )
+               ctr->ctx = talloc_init("regsubkey_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ Add a new key to the array
+ **********************************************************************/
+
+int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, const char *keyname )
+{
+       uint32 len;
+       char **pp;
+       
+       if ( keyname )
+       {
+               len = strlen( keyname );
+
+               /* allocate a space for the char* in the array */
+               
+               if (  ctr->subkeys == 0 )
+                       ctr->subkeys = talloc( ctr->ctx, sizeof(char*) );
+               else {
+                       pp = talloc_realloc( ctr->ctx, ctr->subkeys, sizeof(char*)*(ctr->num_subkeys+1) );
+                       if ( pp )
+                               ctr->subkeys = pp;
+               }
+
+               /* allocate the string and save it in the array */
+               
+               ctr->subkeys[ctr->num_subkeys] = talloc( ctr->ctx, len+1 );
+               strncpy( ctr->subkeys[ctr->num_subkeys], keyname, len+1 );
+               ctr->num_subkeys++;
+       }
+       
+       return ctr->num_subkeys;
+}
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr )
+{
+       return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ Retreive a specific key string
+ **********************************************************************/
+
+char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index )
+{
+       if ( ! (key_index < ctr->num_subkeys) )
+               return NULL;
+               
+       return ctr->subkeys[key_index];
+}
+
+/***********************************************************************
+ free memory held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_destroy( REGSUBKEY_CTR *ctr )
+{
+       if ( ctr ) {
+               talloc_destroy( ctr->ctx );     
+               ZERO_STRUCTP( ctr );
+       }
+}
+
+
+/*
+ * Utility functions for REGVAL_CTR
+ */
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regval_ctr_init( REGVAL_CTR *ctr )
+{
+       if ( !ctr->ctx )
+               ctr->ctx = talloc_init("regval_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regval_ctr_numvals( REGVAL_CTR *ctr )
+{
+       return ctr->num_values;
+}
+
+/***********************************************************************
+ allocate memory for and duplicate a REGISTRY_VALUE.
+ This is malloc'd memory so the caller should free it when done
+ **********************************************************************/
+
+REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val )
+{
+       REGISTRY_VALUE  *copy = NULL;
+       
+       if ( !val )
+               return NULL;
+       
+       if ( !(copy = malloc( sizeof(REGISTRY_VALUE) )) ) {
+               DEBUG(0,("dup_registry_value: malloc() failed!\n"));
+               return NULL;
+       }
+       
+       /* copy all the non-pointer initial data */
+       
+       memcpy( copy, val, sizeof(REGISTRY_VALUE) );
+       if ( val->data_p ) 
+       {
+               if ( !(copy->data_p = memdup( val->data_p, val->size )) ) {
+                       DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
+                               val->size));
+                       SAFE_FREE( copy );
+               }
+       }
+       
+       return copy;    
+}
+
+/**********************************************************************
+ free the memory allocated to a REGISTRY_VALUE 
+ *********************************************************************/
+void free_registry_value( REGISTRY_VALUE *val )
+{
+       if ( !val )
+               return;
+               
+       SAFE_FREE( val->data_p );
+       SAFE_FREE( val );
+       
+       return;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint8* regval_data_p( REGISTRY_VALUE *val )
+{
+       return val->data_p;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+int regval_size( REGISTRY_VALUE *val )
+{
+       return val->size;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char* regval_name( REGISTRY_VALUE *val )
+{
+       return val->valuename;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32 regval_type( REGISTRY_VALUE *val )
+{
+       return val->type;
+}
+
+/***********************************************************************
+ Retreive a pointer to a specific value.  Caller shoud dup the structure
+ since this memory may go away with a regval_ctr_destroy()
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx )
+{
+       if ( !(idx < ctr->num_values) )
+               return NULL;
+               
+       return ctr->values[idx];
+}
+
+/***********************************************************************
+ Retrive the TALLOC_CTX associated with a REGISTRY_VALUE 
+ **********************************************************************/
+
+TALLOC_CTX* regval_ctr_getctx( REGVAL_CTR *val )
+{
+       if ( !val )
+               return NULL;
+
+       return val->ctx;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue( REGVAL_CTR *ctr, const char *name, uint16 type, 
+                         const char *data_p, size_t size )
+{
+       REGISTRY_VALUE **ppreg;
+       
+       if ( name )
+       {
+               /* allocate a slot in the array of pointers */
+               
+               if (  ctr->num_values == 0 )
+                       ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+               else {
+                       ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+                       if ( ppreg )
+                               ctr->values = ppreg;
+               }
+
+               /* allocate a new value and store the pointer in the arrya */
+               
+               ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+               /* init the value */
+       
+               fstrcpy( ctr->values[ctr->num_values]->valuename, name );
+               ctr->values[ctr->num_values]->type = type;
+               ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, data_p, size );
+               ctr->values[ctr->num_values]->size = size;
+               ctr->num_values++;
+       }
+
+       return ctr->num_values;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_copyvalue( REGVAL_CTR *ctr, REGISTRY_VALUE *val )
+{
+       REGISTRY_VALUE **ppreg;
+       
+       if ( val )
+       {
+               /* allocate a slot in the array of pointers */
+               
+               if (  ctr->num_values == 0 )
+                       ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+               else {
+                       ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+                       if ( ppreg )
+                               ctr->values = ppreg;
+               }
+
+               /* allocate a new value and store the pointer in the arrya */
+               
+               ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+               /* init the value */
+       
+               fstrcpy( ctr->values[ctr->num_values]->valuename, val->valuename );
+               ctr->values[ctr->num_values]->type = val->type;
+               ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, val->data_p, val->size );
+               ctr->values[ctr->num_values]->size = val->size;
+               ctr->num_values++;
+       }
+
+       return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+int regval_ctr_delvalue( REGVAL_CTR *ctr, const char *name )
+{
+       int     i;
+       
+       /* search for the value */
+       if (!(ctr->num_values))
+               return 0;
+       
+       for ( i=0; i<ctr->num_values; i++ ) {
+               if ( strcmp( ctr->values[i]->valuename, name ) == 0)
+                       break;
+       }
+       
+       /* just return if we don't find it */
+       
+       if ( i == ctr->num_values )
+               return ctr->num_values;
+       
+       /* just shift everything down one */
+       
+       for ( /* use previous i */; i<(ctr->num_values-1); i++ )
+               memcpy( ctr->values[i], ctr->values[i+1], sizeof(REGISTRY_VALUE) );
+               
+       /* paranoia */
+       
+       ZERO_STRUCTP( ctr->values[i] );
+       
+       ctr->num_values--;
+       
+       return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, const char *name )
+{
+       int     i;
+       
+       /* search for the value */
+       
+       for ( i=0; i<ctr->num_values; i++ ) {
+               if ( strequal( ctr->values[i]->valuename, name ) )
+                       return ctr->values[i];
+       }
+       
+       return NULL;
+}
+
+/***********************************************************************
+ free memory held by a REGVAL_CTR structure
+ **********************************************************************/
+
+void regval_ctr_destroy( REGVAL_CTR *ctr )
+{
+       if ( ctr ) {
+               talloc_destroy( ctr->ctx );
+               ZERO_STRUCTP( ctr );
+       }
+}
+
+
diff --git a/source4/registry/reg_printing.c b/source4/registry/reg_printing.c
new file mode 100644 (file)
index 0000000..619ffc7
--- /dev/null
@@ -0,0 +1,829 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                     2002.
+ *
+ *  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.
+ */
+
+/* Implementation of registry virtual views for printing information */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_TOP_LEVEL_KEYS     3
+
+/* some symbolic indexes into the top_level_keys */
+
+#define KEY_INDEX_ENVIR                0
+#define KEY_INDEX_FORMS                1
+#define KEY_INDEX_PRINTER      2
+
+static char *top_level_keys[MAX_TOP_LEVEL_KEYS] = { 
+       "Environments", 
+       "Forms",
+       "Printers" 
+};
+
+
+/**********************************************************************
+ It is safe to assume that every registry path passed into on of 
+ the exported functions here begins with KEY_PRINTING else
+ these functions would have never been called.  This is a small utility
+ function to strip the beginning of the path and make a copy that the 
+ caller can modify.  Note that the caller is responsible for releasing
+ the memory allocated here.
+ **********************************************************************/
+
+static char* trim_reg_path( char *path )
+{
+       char *p;
+       uint16 key_len = strlen(KEY_PRINTING);
+       
+       /* 
+        * sanity check...this really should never be True.
+        * It is only here to prevent us from accessing outside
+        * the path buffer in the extreme case.
+        */
+       
+       if ( strlen(path) < key_len ) {
+               DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
+               DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
+               return NULL;
+       }
+       
+       
+       p = path + strlen( KEY_PRINTING );
+       
+       if ( *p == '\\' )
+               p++;
+       
+       if ( *p )
+               return strdup(p);
+       else
+               return NULL;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Environments
+ *********************************************************************/
+static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
+{
+       const char *environments[] = {
+               "Windows 4.0",
+               "Windows NT x86",
+               "Windows NT R4000",
+               "Windows NT Alpha_AXP",
+               "Windows NT PowerPC",
+               NULL };
+       fstring *drivers = NULL;
+       int i, env_index, num_drivers;
+       BOOL valid_env = False;
+       char *base, *new_path;
+       char *keystr;
+       char *key2 = NULL;
+       int num_subkeys = -1;
+
+       DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
+       
+       /* listed architectures of installed drivers */
+       
+       if ( !key ) 
+       {
+               /* Windows 9x drivers */
+               
+               if ( get_ntdrivers( &drivers, environments[0], 0 ) )
+                       regsubkey_ctr_addkey( subkeys,  environments[0] );
+               SAFE_FREE( drivers );
+                                       
+               /* Windows NT/2k intel drivers */
+               
+               if ( get_ntdrivers( &drivers, environments[1], 2 ) 
+                       || get_ntdrivers( &drivers, environments[1], 3 ) )
+               {
+                       regsubkey_ctr_addkey( subkeys,  environments[1] );
+               }
+               SAFE_FREE( drivers );
+               
+               /* Windows NT 4.0; non-intel drivers */
+               for ( i=2; environments[i]; i++ ) {
+                       if ( get_ntdrivers( &drivers, environments[i], 2 ) )
+                               regsubkey_ctr_addkey( subkeys,  environments[i] );
+               
+               }
+               SAFE_FREE( drivers );
+
+               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
+               goto done;
+       }
+       
+       /* we are dealing with a subkey of "Environments */
+       
+       key2 = strdup( key );
+       keystr = key2;
+       reg_split_path( keystr, &base, &new_path );
+       
+       /* sanity check */
+       
+       for ( env_index=0; environments[env_index]; env_index++ ) {
+               if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
+                       valid_env = True;
+                       break;
+               }
+       }
+               
+       if ( !valid_env )
+               return -1;
+
+       /* enumerate driver versions; environment is environments[env_index] */
+       
+       if ( !new_path ) {
+               switch ( env_index ) {
+                       case 0: /* Win9x */
+                               if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
+                                       regsubkey_ctr_addkey( subkeys, "0" );
+                                       SAFE_FREE( drivers );
+                               }
+                               break;
+                       case 1: /* Windows NT/2k - intel */
+                               if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
+                                       regsubkey_ctr_addkey( subkeys, "2" );
+                                       SAFE_FREE( drivers );
+                               }
+                               if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
+                                       regsubkey_ctr_addkey( subkeys, "3" );
+                                       SAFE_FREE( drivers );
+                               }
+                               break;
+                       default: /* Windows NT - nonintel */
+                               if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
+                                       regsubkey_ctr_addkey( subkeys, "2" );
+                                       SAFE_FREE( drivers );
+                               }
+                       
+               }
+               
+               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
+               goto done;
+       }
+       
+       /* we finally get to enumerate the drivers */
+       
+       keystr = new_path;
+       reg_split_path( keystr, &base, &new_path );
+       
+       if ( !new_path ) {
+               num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
+               for ( i=0; i<num_drivers; i++ )
+                       regsubkey_ctr_addkey( subkeys, drivers[i] );
+                       
+               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
+               goto done;
+       }
+       
+done:
+       SAFE_FREE( key2 );
+               
+       return num_subkeys;
+}
+
+/***********************************************************************
+ simple function to prune a pathname down to the basename of a file 
+ **********************************************************************/
+static char* dos_basename ( char *path )
+{
+       char *p;
+       
+       p = strrchr( path, '\\' );
+       if ( p )
+               p++;
+       else
+               p = path;
+               
+       return p;
+}
+
+/**********************************************************************
+ handle enumeration of values below 
+ KEY_PRINTING\Environments\<arch>\<version>\<drivername>
+ *********************************************************************/
+static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
+{
+       char            *keystr;
+       char            *key2 = NULL;
+       char            *base, *new_path;
+       fstring         env;
+       fstring         driver;
+       int             version;
+       NT_PRINTER_DRIVER_INFO_LEVEL    driver_ctr;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3  *info3;
+       WERROR          w_result;
+       char            *buffer = NULL;
+       char            *buffer2 = NULL;
+       int             buffer_size = 0;
+       int             i, length;
+       char            *filename;
+       UNISTR2         data;;
+       
+       DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
+       
+       if ( !key )
+               return 0;
+               
+       /* 
+        * The only key below KEY_PRINTING\Environments that 
+        * posseses values is each specific printer driver 
+        * First get the arch, version, & driver name
+        */
+       
+       /* env */
+       
+       key2 = strdup( key );
+       keystr = key2;
+       reg_split_path( keystr, &base, &new_path );
+       if ( !base || !new_path )
+               return 0;
+       fstrcpy( env, base );
+       
+       /* version */
+       
+       keystr = new_path;
+       reg_split_path( keystr, &base, &new_path );
+       if ( !base || !new_path )
+               return 0;
+       version = atoi( base );
+
+       /* printer driver name */
+       
+       keystr = new_path;
+       reg_split_path( keystr, &base, &new_path );
+       /* new_path should be NULL here since this must be the last key */
+       if ( !base || new_path )
+               return 0;
+       fstrcpy( driver, base );
+
+       w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
+
+       if ( !W_ERROR_IS_OK(w_result) )
+               return -1;
+               
+       /* build the values out of the driver information */
+       info3 = driver_ctr.info_3;
+       
+       filename = dos_basename( info3->driverpath );
+       init_unistr2( &data, filename, strlen(filename)+1 ); 
+       regval_ctr_addvalue( val, "Driver",             REG_SZ,       (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+       
+       filename = dos_basename( info3->configfile );
+       init_unistr2( &data, filename, strlen(filename)+1 );
+       regval_ctr_addvalue( val, "Configuration File", REG_SZ,       (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+       
+       filename = dos_basename( info3->datafile );
+       init_unistr2( &data, filename, strlen(filename)+1 );
+       regval_ctr_addvalue( val, "Data File",          REG_SZ,       (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+       
+       filename = dos_basename( info3->helpfile );
+       init_unistr2( &data, filename, strlen(filename)+1 );
+       regval_ctr_addvalue( val, "Help File",          REG_SZ,       (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+       
+       init_unistr2( &data, info3->defaultdatatype, strlen(info3->defaultdatatype)+1 );
+       regval_ctr_addvalue( val, "Data Type",          REG_SZ,       (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+       
+       regval_ctr_addvalue( val, "Version",            REG_DWORD,    (char*)&info3->cversion, sizeof(info3->cversion) );
+       
+       if ( info3->dependentfiles )
+       {
+               /* place the list of dependent files in a single 
+                  character buffer, separating each file name by
+                  a NULL */
+                  
+               for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ )
+               {
+                       /* strip the path to only the file's base name */
+               
+                       filename = dos_basename( info3->dependentfiles[i] );
+                       
+                       length = strlen(filename);
+               
+                       buffer2 = Realloc( buffer, buffer_size + (length + 1)*sizeof(uint16) );
+                       if ( !buffer2 )
+                               break;
+                       buffer = buffer2;
+                       
+                       init_unistr2( &data, filename, length+1 );
+                       memcpy( buffer+buffer_size, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               
+                       buffer_size += (length + 1)*sizeof(uint16);
+               }
+               
+               /* terminated by double NULL.  Add the final one here */
+               
+               buffer2 = Realloc( buffer, buffer_size + 2 );
+               if ( !buffer2 ) {
+                       SAFE_FREE( buffer );
+                       buffer_size = 0;
+               }
+               else {
+                       buffer = buffer2;
+                       buffer[buffer_size++] = '\0';
+                       buffer[buffer_size++] = '\0';
+               }
+       }
+       
+       regval_ctr_addvalue( val, "Dependent Files",    REG_MULTI_SZ, buffer, buffer_size );
+       
+       free_a_printer_driver( driver_ctr, 3 );
+       
+       SAFE_FREE( key2 );
+       SAFE_FREE( buffer );
+               
+       DEBUG(8,("print_subpath_values_environments: Exit\n"));
+       
+       return regval_ctr_numvals( val );
+}
+
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Forms
+ Really just a stub function, but left here in case it needs to
+ be expanded later on
+ *********************************************************************/
+static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
+{
+       DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
+       
+       /* there are no subkeys */
+       
+       if ( key )
+               return -1;
+       
+       return 0;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Forms
+ *********************************************************************/
+static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
+{
+       int             num_values = 0;
+       uint32          data[8];
+       int             form_index = 1;
+       
+       DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
+       
+       /* handle ..\Forms\ */
+       
+       if ( !key )
+       {
+               nt_forms_struct *forms_list = NULL;
+               nt_forms_struct *form = NULL;
+               int i;
+               
+               if ( (num_values = get_ntforms( &forms_list )) == 0 ) 
+                       return 0;
+               
+               DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
+                       num_values));
+
+               /* handle user defined forms */
+                               
+               for ( i=0; i<num_values; i++ )
+               {
+                       form = &forms_list[i];
+                       
+                       data[0] = form->width;
+                       data[1] = form->length;
+                       data[2] = form->left;
+                       data[3] = form->top;
+                       data[4] = form->right;
+                       data[5] = form->bottom;
+                       data[6] = form_index++;
+                       data[7] = form->flag;
+                       
+                       regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+               
+               }
+               
+               SAFE_FREE( forms_list );
+               forms_list = NULL;
+               
+               /* handle built-on forms */
+               
+               if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 ) 
+                       return 0;
+               
+               DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
+                       num_values));
+                       
+               for ( i=0; i<num_values; i++ )
+               {
+                       form = &forms_list[i];
+                       
+                       data[0] = form->width;
+                       data[1] = form->length;
+                       data[2] = form->left;
+                       data[3] = form->top;
+                       data[4] = form->right;
+                       data[5] = form->bottom;
+                       data[6] = form_index++;
+                       data[7] = form->flag;
+                                       
+                       regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+               }
+               
+               SAFE_FREE( forms_list );
+       }
+       
+       return num_values;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Printers
+ *********************************************************************/
+static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
+{
+       int n_services = lp_numservices();      
+       int snum;
+       fstring sname;
+       int i;
+       int num_subkeys = 0;
+       char *keystr, *key2 = NULL;
+       char *base, *new_path;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       fstring *subkey_names = NULL;
+       
+       DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
+       
+       if ( !key )
+       {
+               /* enumerate all printers */
+               
+               for (snum=0; snum<n_services; snum++) {
+                       if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+                               continue;
+                               
+                       fstrcpy( sname, lp_servicename(snum) );
+                               
+                       regsubkey_ctr_addkey( subkeys, sname );
+               }
+               
+               num_subkeys = regsubkey_ctr_numkeys( subkeys );
+               goto done;
+       }
+
+       /* get information for a specific printer */
+       
+       key2 = strdup( key );
+       keystr = key2;
+       reg_split_path( keystr, &base, &new_path );
+
+               if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, base) ) )
+               goto done;
+
+       num_subkeys = get_printer_subkeys( &printer->info_2->data, new_path?new_path:"", &subkey_names );
+       
+       for ( i=0; i<num_subkeys; i++ )
+               regsubkey_ctr_addkey( subkeys, subkey_names[i] );
+       
+       free_a_printer( &printer, 2 );
+                       
+       /* no other subkeys below here */
+
+done:  
+       SAFE_FREE( key2 );
+       SAFE_FREE( subkey_names );
+       
+       return num_subkeys;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Printers
+ *********************************************************************/
+static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
+{
+       int             num_values = 0;
+       char            *keystr, *key2 = NULL;
+       char            *base, *new_path;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       NT_PRINTER_INFO_LEVEL_2 *info2;
+       DEVICEMODE      *devmode;
+       prs_struct      prs;
+       uint32          offset;
+       int             snum;
+       fstring         printername; 
+       NT_PRINTER_DATA *p_data;
+       int             i, key_index;
+       UNISTR2         data;
+       
+       /* 
+        * Theres are tw cases to deal with here
+        * (1) enumeration of printer_info_2 values
+        * (2) enumeration of the PrinterDriverData subney
+        */
+        
+       if ( !key ) {
+               /* top level key has no values */
+               goto done;
+       }
+       
+       key2 = strdup( key );
+       keystr = key2;
+       reg_split_path( keystr, &base, &new_path );
+       
+       fstrcpy( printername, base );
+       
+       if ( !new_path ) 
+       {
+               /* we are dealing with the printer itself */
+
+               if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+                       goto done;
+
+               info2 = printer->info_2;
+               
+
+               regval_ctr_addvalue( val, "Attributes",       REG_DWORD, (char*)&info2->attributes,       sizeof(info2->attributes) );
+               regval_ctr_addvalue( val, "Priority",         REG_DWORD, (char*)&info2->priority,         sizeof(info2->attributes) );
+               regval_ctr_addvalue( val, "ChangeID",         REG_DWORD, (char*)&info2->changeid,         sizeof(info2->changeid) );
+               regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
+               regval_ctr_addvalue( val, "Status",           REG_DWORD, (char*)&info2->status,           sizeof(info2->status) );
+               regval_ctr_addvalue( val, "StartTime",        REG_DWORD, (char*)&info2->starttime,        sizeof(info2->starttime) );
+               regval_ctr_addvalue( val, "UntilTime",        REG_DWORD, (char*)&info2->untiltime,        sizeof(info2->untiltime) );
+               regval_ctr_addvalue( val, "cjobs",            REG_DWORD, (char*)&info2->cjobs,            sizeof(info2->cjobs) );
+               regval_ctr_addvalue( val, "AveragePPM",       REG_DWORD, (char*)&info2->averageppm,       sizeof(info2->averageppm) );
+
+               init_unistr2( &data, info2->printername, strlen(info2->printername)+1 );
+               regval_ctr_addvalue( val, "Name",             REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->location, strlen(info2->location)+1 );
+               regval_ctr_addvalue( val, "Location",         REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->comment, strlen(info2->comment)+1 );
+               regval_ctr_addvalue( val, "Comment",          REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->parameters, strlen(info2->parameters)+1 );
+               regval_ctr_addvalue( val, "Parameters",       REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->portname, strlen(info2->portname)+1 );
+               regval_ctr_addvalue( val, "Port",             REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->servername, strlen(info2->servername)+1 );
+               regval_ctr_addvalue( val, "Server",           REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->sharename, strlen(info2->sharename)+1 );
+               regval_ctr_addvalue( val, "Share",            REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->drivername, strlen(info2->drivername)+1 );
+               regval_ctr_addvalue( val, "Driver",           REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, info2->sepfile, strlen(info2->sepfile)+1 );
+               regval_ctr_addvalue( val, "Separator File",   REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               init_unistr2( &data, "winprint", strlen("winprint")+1 );
+               regval_ctr_addvalue( val, "Print Processor",  REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+               
+               
+               /* use a prs_struct for converting the devmode and security 
+                  descriptor to REG_BIARY */
+               
+               prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
+
+               /* stream the device mode */
+               
+               snum = lp_servicenumber(info2->sharename);
+               if ( (devmode = construct_dev_mode( snum )) != NULL )
+               {                       
+                       if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
+                       
+                               offset = prs_offset( &prs );
+                               
+                               regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
+                       }
+                       
+                       
+               }
+               
+               prs_mem_clear( &prs );
+               prs_set_offset( &prs, 0 );
+               
+               if ( info2->secdesc_buf && info2->secdesc_buf->len ) 
+               {
+                       if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
+                       
+                               offset = prs_offset( &prs );
+                       
+                               regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
+                       }
+               }
+
+               prs_mem_free( &prs );
+               
+               num_values = regval_ctr_numvals( val ); 
+               
+               goto done;
+               
+       }
+               
+       /* now enumerate the key */
+       
+       if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+               goto done;
+       
+       /* iterate over all printer data and fill the regval container */
+       
+       p_data = &printer->info_2->data;
+       if ( (key_index = lookup_printerkey( p_data, new_path )) == -1  ) {
+               DEBUG(10,("print_subpath_values_printer: Unknown keyname [%s]\n", new_path));
+               goto done;
+       }
+       
+       num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+       
+       for ( i=0; i<num_values; i++ )
+               regval_ctr_copyvalue( val, regval_ctr_specific_value(&p_data->keys[key_index].values, i) );
+                       
+
+done:
+       if ( printer )
+               free_a_printer( &printer, 2 );
+               
+       SAFE_FREE( key2 ); 
+       
+       return num_values;
+}
+
+/**********************************************************************
+ Routine to handle enumeration of subkeys and values 
+ below KEY_PRINTING (depending on whether or not subkeys/val are 
+ valid pointers. 
+ *********************************************************************/
+static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
+{
+       int result = 0;
+       char *p, *base;
+       int i;
+       
+       DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
+       
+       /* 
+        * break off the first part of the path 
+        * topmost base **must** be one of the strings 
+        * in top_level_keys[]
+        */
+       
+       reg_split_path( key, &base, &p);
+               
+       for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
+               if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
+                       break;
+       }
+       
+       DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));  
+               
+       if ( !(i < MAX_TOP_LEVEL_KEYS) )
+               return -1;
+                                               
+       /* Call routine to handle each top level key */
+       switch ( i )
+       {
+               case KEY_INDEX_ENVIR:
+                       if ( subkeys )
+                               print_subpath_environments( p, subkeys );
+                       if ( val )
+                               print_subpath_values_environments( p, val );
+                       break;
+               
+               case KEY_INDEX_FORMS:
+                       if ( subkeys )
+                               print_subpath_forms( p, subkeys );
+                       if ( val )
+                               print_subpath_values_forms( p, val );
+                       break;
+                       
+               case KEY_INDEX_PRINTER:
+                       if ( subkeys )
+                               print_subpath_printers( p, subkeys );
+                       if ( val )
+                               print_subpath_values_printers( p, val );
+                       break;
+       
+               /* default case for top level key that has no handler */
+               
+               default:
+                       break;
+       }
+       
+       
+       
+       return result;
+
+}
+/**********************************************************************
+ Enumerate registry subkey names given a registry path.  
+ Caller is responsible for freeing memory to **subkeys
+ *********************************************************************/
+int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
+{
+       char            *path;
+       BOOL            top_level = False;
+       int             num_subkeys = 0;
+       
+       DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
+       
+       path = trim_reg_path( key );
+       
+       /* check to see if we are dealing with the top level key */
+       
+       if ( !path )
+               top_level = True;
+               
+       if ( top_level ) {
+               for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
+                       regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
+       }
+       else
+               num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
+       
+       SAFE_FREE( path );
+       
+       return num_subkeys;
+}
+
+/**********************************************************************
+ Enumerate registry values given a registry path.  
+ Caller is responsible for freeing memory 
+ *********************************************************************/
+
+int printing_value_info( char *key, REGVAL_CTR *val )
+{
+       char            *path;
+       BOOL            top_level = False;
+       int             num_values = 0;
+       
+       DEBUG(10,("printing_value_info: key=>[%s]\n", key));
+       
+       path = trim_reg_path( key );
+       
+       /* check to see if we are dealing with the top level key */
+       
+       if ( !path )
+               top_level = True;
+       
+       /* fill in values from the getprinterdata_printer_server() */
+       if ( top_level )
+               num_values = 0;
+       else
+               num_values = handle_printing_subpath( path, NULL, val );
+               
+       
+       return num_values;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
+{
+       return False;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_value( char *key, REGVAL_CTR *val )
+{
+       return False;
+}
+
+/* 
+ * Table of function pointers for accessing printing data
+ */
+REGISTRY_OPS printing_ops = {
+       printing_subkey_info,
+       printing_value_info,
+       printing_store_subkey,
+       printing_store_value
+};
+
+
diff --git a/source4/rpc_client/cli_dfs.c b/source4/rpc_client/cli_dfs.c
new file mode 100644 (file)
index 0000000..2136b69
--- /dev/null
@@ -0,0 +1,247 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+   Copyright (C) Tim Potter                        2000-2001,
+   
+   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"
+
+/* Query DFS support */
+
+NTSTATUS cli_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                       BOOL *dfs_exists)
+{
+       prs_struct qbuf, rbuf;
+       DFS_Q_DFS_EXIST q;
+       DFS_R_DFS_EXIST r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_dfs_q_dfs_exist(&q);
+
+       if (!dfs_io_q_dfs_exist("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, DFS_EXIST, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!dfs_io_r_dfs_exist("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return result */
+
+       *dfs_exists = (r.status != 0);
+
+       result = NT_STATUS_OK;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+NTSTATUS cli_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                     const char *entrypath, const char *servername, 
+                    const char *sharename, const char *comment, uint32 flags)
+{
+       prs_struct qbuf, rbuf;
+       DFS_Q_DFS_ADD q;
+       DFS_R_DFS_ADD r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_dfs_q_dfs_add(&q, entrypath, servername, sharename, comment,
+                          flags);
+
+       if (!dfs_io_q_dfs_add("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, DFS_ADD, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!dfs_io_r_dfs_add("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return result */
+
+        result = werror_to_ntstatus(r.status);
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+NTSTATUS cli_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                        const char *entrypath, const char *servername, 
+                       const char *sharename)
+{
+       prs_struct qbuf, rbuf;
+       DFS_Q_DFS_REMOVE q;
+       DFS_R_DFS_REMOVE r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_dfs_q_dfs_remove(&q, entrypath, servername, sharename);
+
+       if (!dfs_io_q_dfs_remove("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, DFS_REMOVE, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!dfs_io_r_dfs_remove("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return result */
+
+       result = werror_to_ntstatus(r.status);
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+NTSTATUS cli_dfs_get_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          const char *entrypath, const char *servername, 
+                         const char *sharename, uint32 info_level, 
+                         DFS_INFO_CTR *ctr)
+
+{
+       prs_struct qbuf, rbuf;
+       DFS_Q_DFS_GET_INFO q;
+       DFS_R_DFS_GET_INFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_dfs_q_dfs_get_info(&q, entrypath, servername, sharename,
+                               info_level);
+
+       if (!dfs_io_q_dfs_get_info("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, DFS_GET_INFO, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!dfs_io_r_dfs_get_info("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return result */
+
+       result = werror_to_ntstatus(r.status);
+       *ctr = r.ctr;
+       
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Enumerate dfs shares */
+
+NTSTATUS cli_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                      uint32 info_level, DFS_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       DFS_Q_DFS_ENUM q;
+       DFS_R_DFS_ENUM r;
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_dfs_q_dfs_enum(&q, info_level, ctr);
+
+       if (!dfs_io_q_dfs_enum("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, DFS_ENUM, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+       
+       r.ctr = ctr;
+
+       if (!dfs_io_r_dfs_enum("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return result */
+
+       result = werror_to_ntstatus(r.status);
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
diff --git a/source4/rpc_client/cli_ds.c b/source4/rpc_client/cli_ds.c
new file mode 100644 (file)
index 0000000..f0edeca
--- /dev/null
@@ -0,0 +1,73 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+   Copyright (C) Gerald Carter                        2002,
+   
+   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"
+
+/* implementations of client side DsXXX() functions */
+
+NTSTATUS cli_ds_getprimarydominfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                 uint16 level, DS_DOMINFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       DS_Q_GETPRIMDOMINFO q;
+       DS_R_GETPRIMDOMINFO r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+       
+       q.level = level;
+       
+       if (!ds_io_q_getprimdominfo("", &q, &qbuf, 0) 
+           || !rpc_api_pipe_req(cli, DS_GETPRIMDOMINFO, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!ds_io_r_getprimdominfo("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       
+       /* Return basic info - if we are requesting at info != 1 then
+          there could be trouble. */ 
+
+       result = r.status;
+
+       if (ctr) {
+               ctr->basic = talloc(mem_ctx, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+               if (!ctr->basic)
+                       goto done;
+               memcpy(ctr->basic, r.info.basic, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+       }
+       
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
diff --git a/source4/rpc_client/cli_lsarpc.c b/source4/rpc_client/cli_lsarpc.c
new file mode 100644 (file)
index 0000000..bbd40b2
--- /dev/null
@@ -0,0 +1,1441 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+   Copyright (C) Tim Potter                        2000-2001,
+   Copyright (C) Andrew Tridgell              1992-1997,2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+   Copyright (C) Paul Ashton                       1997,2000,
+   Copyright (C) Elrond                                 2000,
+   Copyright (C) Rafal Szczesniak                       2002
+   
+   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"
+
+/** @defgroup lsa LSA - Local Security Architecture
+ *  @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**
+ * @file cli_lsarpc.c
+ *
+ * RPC client routines for the LSA RPC pipe.  LSA means "local
+ * security authority", which is half of a password database.
+ **/
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_OPEN_POL q;
+       LSA_R_OPEN_POL r;
+       LSA_SEC_QOS qos;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       if (sec_qos) {
+               init_lsa_sec_qos(&qos, 2, 1, 0);
+               init_q_open_pol(&q, '\\', 0, des_access, &qos);
+       } else {
+               init_q_open_pol(&q, '\\', 0, des_access, NULL);
+       }
+
+       /* Marshall data and send request */
+
+       if (!lsa_io_q_open_pol("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_OPENPOLICY, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_open_pol("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Open a LSA policy handle
+  *
+  * @param cli Handle on an initialised SMB connection 
+  */
+
+NTSTATUS cli_lsa_open_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                              BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_OPEN_POL2 q;
+       LSA_R_OPEN_POL2 r;
+       LSA_SEC_QOS qos;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       if (sec_qos) {
+               init_lsa_sec_qos(&qos, 2, 1, 0);
+               init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access, 
+                                 &qos);
+       } else {
+               init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access, 
+                                 NULL);
+       }
+
+       /* Marshall data and send request */
+
+       if (!lsa_io_q_open_pol2("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_OPENPOLICY2, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_open_pol2("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Close a LSA policy handle */
+
+NTSTATUS cli_lsa_close(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                       POLICY_HND *pol)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_CLOSE q;
+       LSA_R_CLOSE r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_lsa_q_close(&q, pol);
+
+       if (!lsa_io_q_close("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_CLOSE, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_close("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Lookup a list of sids */
+
+NTSTATUS cli_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *pol, int num_sids, DOM_SID *sids, 
+                             char ***domains, char ***names, uint32 **types)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_LOOKUP_SIDS q;
+       LSA_R_LOOKUP_SIDS r;
+       DOM_R_REF ref;
+       LSA_TRANS_NAME_ENUM t_names;
+       NTSTATUS result;
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_lookup_sids(mem_ctx, &q, pol, num_sids, sids, 1);
+
+       if (!lsa_io_q_lookup_sids("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_LOOKUPSIDS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       ZERO_STRUCT(ref);
+       ZERO_STRUCT(t_names);
+
+       r.dom_ref = &ref;
+       r.names = &t_names;
+
+       if (!lsa_io_r_lookup_sids("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+         
+               /* An actual error occured */
+
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (r.mapped_count == 0) {
+               result = NT_STATUS_NONE_MAPPED;
+               goto done;
+       }
+
+       if (!((*domains) = (char **)talloc(mem_ctx, sizeof(char *) *
+                                          num_sids))) {
+               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!((*names) = (char **)talloc(mem_ctx, sizeof(char *) *
+                                        num_sids))) {
+               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!((*types) = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+                                         num_sids))) {
+               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+               
+       for (i = 0; i < num_sids; i++) {
+               fstring name, dom_name;
+               uint32 dom_idx = t_names.name[i].domain_idx;
+
+               /* Translate optimised name through domain index array */
+
+               if (dom_idx != 0xffffffff) {
+
+                       rpcstr_pull_unistr2_fstring(
+                                dom_name, &ref.ref_dom[dom_idx].uni_dom_name);
+                       rpcstr_pull_unistr2_fstring(
+                                name, &t_names.uni_name[i]);
+
+                       (*names)[i] = talloc_strdup(mem_ctx, name);
+                       (*domains)[i] = talloc_strdup(mem_ctx, dom_name);
+                       (*types)[i] = t_names.name[i].sid_name_use;
+                       
+                       if (((*names)[i] == NULL) || ((*domains)[i] == NULL)) {
+                               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+                               result = NT_STATUS_UNSUCCESSFUL;
+                               goto done;
+                       }
+
+               } else {
+                       (*names)[i] = NULL;
+                       (*types)[i] = SID_NAME_UNKNOWN;
+               }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Lookup a list of names */
+
+NTSTATUS cli_lsa_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                              POLICY_HND *pol, int num_names, 
+                             const char **names, DOM_SID **sids, 
+                             uint32 **types)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_LOOKUP_NAMES q;
+       LSA_R_LOOKUP_NAMES r;
+       DOM_R_REF ref;
+       NTSTATUS result;
+       int i;
+       
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_lookup_names(mem_ctx, &q, pol, num_names, names);
+
+       if (!lsa_io_q_lookup_names("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_LOOKUPNAMES, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       
+       /* Unmarshall response */
+
+       ZERO_STRUCT(ref);
+       r.dom_ref = &ref;
+
+       if (!lsa_io_r_lookup_names("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+           NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+
+               /* An actual error occured */
+
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (r.mapped_count == 0) {
+               result = NT_STATUS_NONE_MAPPED;
+               goto done;
+       }
+
+       if (!((*sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+                                        num_names)))) {
+               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!((*types = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+                                        num_names)))) {
+               DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       for (i = 0; i < num_names; i++) {
+               DOM_RID2 *t_rids = r.dom_rid;
+               uint32 dom_idx = t_rids[i].rid_idx;
+               uint32 dom_rid = t_rids[i].rid;
+               DOM_SID *sid = &(*sids)[i];
+
+               /* Translate optimised sid through domain index array */
+
+               if (dom_idx != 0xffffffff) {
+
+                       sid_copy(sid, &ref.ref_dom[dom_idx].ref_dom.sid);
+
+                       if (dom_rid != 0xffffffff) {
+                               sid_append_rid(sid, dom_rid);
+                       }
+
+                       (*types)[i] = t_rids[i].type;
+               } else {
+                       ZERO_STRUCTP(sid);
+                       (*types)[i] = SID_NAME_UNKNOWN;
+               }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Query info policy
+ *
+ *  @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *pol, uint16 info_class, 
+                                   fstring domain_name, DOM_SID *domain_sid)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_QUERY_INFO q;
+       LSA_R_QUERY_INFO r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_query(&q, pol, info_class);
+
+       if (!lsa_io_q_query("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_QUERYINFOPOLICY, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_query("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       ZERO_STRUCTP(domain_sid);
+       domain_name[0] = '\0';
+
+       switch (info_class) {
+
+       case 3:
+               if (r.dom.id3.buffer_dom_name != 0) {
+                       unistr2_to_ascii(domain_name,
+                                        &r.dom.id3.
+                                        uni_domain_name,
+                                        sizeof (fstring) - 1);
+               }
+
+               if (r.dom.id3.buffer_dom_sid != 0) {
+                       *domain_sid = r.dom.id3.dom_sid.sid;
+               }
+
+               break;
+
+       case 5:
+               
+               if (r.dom.id5.buffer_dom_name != 0) {
+                       unistr2_to_ascii(domain_name, &r.dom.id5.
+                                        uni_domain_name,
+                                        sizeof (fstring) - 1);
+               }
+                       
+               if (r.dom.id5.buffer_dom_sid != 0) {
+                       *domain_sid = r.dom.id5.dom_sid.sid;
+               }
+
+               break;
+               
+       default:
+               DEBUG(3, ("unknown info class %d\n", info_class));
+               break;                
+       }
+       
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Query info policy2
+ *
+ *  @param domain_name - returned remote server's domain name
+ *  @param dns_name - returned remote server's dns domain name
+ *  @param forest_name - returned remote server's forest name
+ *  @param domain_guid - returned remote server's domain guid
+ *  @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *pol, uint16 info_class, 
+                                   fstring domain_name, fstring dns_name,
+                                   fstring forest_name, GUID *domain_guid,
+                                   DOM_SID *domain_sid)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_QUERY_INFO2 q;
+       LSA_R_QUERY_INFO2 r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       if (info_class != 12)
+               goto done;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_query2(&q, pol, info_class);
+
+       if (!lsa_io_q_query_info2("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_QUERYINFO2, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_query_info2("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       ZERO_STRUCTP(domain_sid);
+       ZERO_STRUCTP(domain_guid);
+       domain_name[0] = '\0';
+
+       if (r.info.dns_dom_info.hdr_nb_dom_name.buffer) {
+               unistr2_to_ascii(domain_name,
+                                &r.info.dns_dom_info.uni_nb_dom_name,
+                                sizeof(fstring) - 1);
+       }
+       if (r.info.dns_dom_info.hdr_dns_dom_name.buffer) {
+               unistr2_to_ascii(dns_name,
+                                &r.info.dns_dom_info.uni_dns_dom_name,
+                                sizeof(fstring) - 1);
+       }
+       if (r.info.dns_dom_info.hdr_forest_name.buffer) {
+               unistr2_to_ascii(forest_name,
+                                &r.info.dns_dom_info.uni_forest_name,
+                                sizeof(fstring) - 1);
+       }
+       
+       memcpy(domain_guid, &r.info.dns_dom_info.dom_guid, sizeof(GUID));
+
+       if (r.info.dns_dom_info.ptr_dom_sid != 0) {
+               *domain_sid = r.info.dns_dom_info.dom_sid.sid;
+       }
+       
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/**
+ * Enumerate list of trusted domains
+ *
+ * @param cli client state (cli_state) structure of the connection
+ * @param mem_ctx memory context
+ * @param pol opened lsa policy handle
+ * @param enum_ctx enumeration context ie. index of first returned domain entry
+ * @param pref_num_domains preferred max number of entries returned in one response
+ * @param num_domains total number of trusted domains returned by response
+ * @param domain_names returned trusted domain names
+ * @param domain_sids returned trusted domain sids
+ *
+ * @return nt status code of response
+ **/
+
+NTSTATUS cli_lsa_enum_trust_dom(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                POLICY_HND *pol, uint32 *enum_ctx, 
+                                uint32 *num_domains,
+                                char ***domain_names, DOM_SID **domain_sids)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUM_TRUST_DOM q;
+       LSA_R_ENUM_TRUST_DOM r;
+       NTSTATUS result;
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       /* 64k is enough for about 2000 trusted domains */
+        init_q_enum_trust_dom(&q, pol, *enum_ctx, 0x10000);
+
+       if (!lsa_io_q_enum_trust_dom("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUMTRUSTDOM, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_enum_trust_dom("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+           !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+
+               /* An actual error ocured */
+
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (r.num_domains) {
+
+               /* Allocate memory for trusted domain names and sids */
+
+               *domain_names = (char **)talloc(mem_ctx, sizeof(char *) *
+                                               r.num_domains);
+
+               if (!*domain_names) {
+                       DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               *domain_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+                                                r.num_domains);
+               if (!domain_sids) {
+                       DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               /* Copy across names and sids */
+
+               for (i = 0; i < r.num_domains; i++) {
+                       fstring tmp;
+
+                       unistr2_to_ascii(tmp, &r.uni_domain_name[i], 
+                                        sizeof(tmp) - 1);
+                       (*domain_names)[i] = talloc_strdup(mem_ctx, tmp);
+                       sid_copy(&(*domain_sids)[i], &r.domain_sid[i].sid);
+               }
+       }
+
+       *num_domains = r.num_domains;
+       *enum_ctx = r.enum_context;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+
+/** Enumerate privileges*/
+
+NTSTATUS cli_lsa_enum_privilege(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                POLICY_HND *pol, uint32 *enum_context, uint32 pref_max_length,
+                               uint32 *count, char ***privs_name, uint32 **privs_high, uint32 **privs_low)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUM_PRIVS q;
+       LSA_R_ENUM_PRIVS r;
+       NTSTATUS result;
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_enum_privs(&q, pol, *enum_context, pref_max_length);
+
+       if (!lsa_io_q_enum_privs("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUM_PRIVS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_enum_privs("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       *enum_context = r.enum_context;
+       *count = r.count;
+
+       if (!((*privs_name = (char **)talloc(mem_ctx, sizeof(char *) * r.count)))) {
+               DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!((*privs_high = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+               DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!((*privs_low = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+               DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       for (i = 0; i < r.count; i++) {
+               fstring name;
+
+               rpcstr_pull_unistr2_fstring( name, &r.privs[i].name);
+
+               (*privs_name)[i] = talloc_strdup(mem_ctx, name);
+
+               (*privs_high)[i] = r.privs[i].luid_high;
+               (*privs_low)[i] = r.privs[i].luid_low;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Get privilege name */
+
+NTSTATUS cli_lsa_get_dispname(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *pol, const char *name, 
+                             uint16 lang_id, uint16 lang_id_sys,
+                             fstring description, uint16 *lang_id_desc)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_PRIV_GET_DISPNAME q;
+       LSA_R_PRIV_GET_DISPNAME r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_lsa_priv_get_dispname(&q, pol, name, lang_id, lang_id_sys);
+
+       if (!lsa_io_q_priv_get_dispname("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_PRIV_GET_DISPNAME, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_priv_get_dispname("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+       
+       rpcstr_pull_unistr2_fstring(description , &r.desc);
+       *lang_id_desc = r.lang_id;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Enumerate list of SIDs  */
+
+NTSTATUS cli_lsa_enum_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                POLICY_HND *pol, uint32 *enum_ctx, uint32 pref_max_length, 
+                                uint32 *num_sids, DOM_SID **sids)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUM_ACCOUNTS q;
+       LSA_R_ENUM_ACCOUNTS r;
+       NTSTATUS result;
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+        init_lsa_q_enum_accounts(&q, pol, *enum_ctx, pref_max_length);
+
+       if (!lsa_io_q_enum_accounts("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUM_ACCOUNTS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_enum_accounts("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (r.sids.num_entries==0)
+               goto done;
+
+       /* Return output parameters */
+
+       *sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.sids.num_entries);
+       if (!*sids) {
+               DEBUG(0, ("(cli_lsa_enum_sids): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Copy across names and sids */
+
+       for (i = 0; i < r.sids.num_entries; i++) {
+               sid_copy(&(*sids)[i], &r.sids.sid[i].sid);
+       }
+
+       *num_sids= r.sids.num_entries;
+       *enum_ctx = r.enum_context;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Open a LSA user handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_account(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *dom_pol, DOM_SID *sid, uint32 des_access, 
+                            POLICY_HND *user_pol)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_OPENACCOUNT q;
+       LSA_R_OPENACCOUNT r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_lsa_q_open_account(&q, dom_pol, sid, des_access);
+
+       /* Marshall data and send request */
+
+       if (!lsa_io_q_open_account("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_OPENACCOUNT, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_open_account("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *user_pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Enumerate user privileges
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_enum_privsaccount(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *pol, uint32 *count, LUID_ATTR **set)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUMPRIVSACCOUNT q;
+       LSA_R_ENUMPRIVSACCOUNT r;
+       NTSTATUS result;
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_lsa_q_enum_privsaccount(&q, pol);
+
+       /* Marshall data and send request */
+
+       if (!lsa_io_q_enum_privsaccount("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUMPRIVSACCOUNT, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_enum_privsaccount("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (r.count == 0)
+               goto done;
+
+       if (!((*set = (LUID_ATTR *)talloc(mem_ctx, sizeof(LUID_ATTR) * r.count)))) {
+               DEBUG(0, ("(cli_lsa_enum_privsaccount): out of memory\n"));
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       for (i=0; i<r.count; i++) {
+               (*set)[i].luid.low = r.set.set[i].luid.low;
+               (*set)[i].luid.high = r.set.set[i].luid.high;
+               (*set)[i].attr = r.set.set[i].attr;
+       }
+
+       *count=r.count;
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Get a privilege value given its name */
+
+NTSTATUS cli_lsa_lookupprivvalue(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                POLICY_HND *pol, const char *name, LUID *luid)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_LOOKUPPRIVVALUE q;
+       LSA_R_LOOKUPPRIVVALUE r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_lsa_q_lookupprivvalue(&q, pol, name);
+
+       if (!lsa_io_q_lookupprivvalue("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_LOOKUPPRIVVALUE, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_lookupprivvalue("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       (*luid).low=r.luid.low;
+       (*luid).high=r.luid.high;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Query LSA security object */
+
+NTSTATUS cli_lsa_query_secobj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *pol, uint32 sec_info, 
+                             SEC_DESC_BUF **psdb)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_QUERY_SEC_OBJ q;
+       LSA_R_QUERY_SEC_OBJ r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_q_query_sec_obj(&q, pol, sec_info);
+
+       if (!lsa_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_QUERYSECOBJ, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (psdb)
+               *psdb = r.buf;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+
+/* Enumerate account rights This is similar to enum_privileges but
+   takes a SID directly, avoiding the open_account call.
+*/
+
+NTSTATUS cli_lsa_enum_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                    POLICY_HND *pol, DOM_SID sid,
+                                    uint32 *count, char ***privs_name)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUM_ACCT_RIGHTS q;
+       LSA_R_ENUM_ACCT_RIGHTS r;
+       NTSTATUS result;
+       unsigned int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+       init_q_enum_acct_rights(&q, pol, 2, &sid);
+
+       if (!lsa_io_q_enum_acct_rights("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUMACCTRIGHTS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!lsa_io_r_enum_acct_rights("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       *count = r.count;
+       if (! *count) {
+               goto done;
+       }
+
+       *privs_name = (char **)talloc(mem_ctx, (*count) * sizeof(char **));
+       for (i=0;i<*count;i++) {
+               (*privs_name)[i] = unistr2_tdup(mem_ctx, &r.rights.strings[i].string);
+       }
+
+done:
+
+       return result;
+}
+
+
+
+/* add account rights to an account. */
+
+NTSTATUS cli_lsa_add_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *pol, DOM_SID sid,
+                                   uint32 count, const char **privs_name)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ADD_ACCT_RIGHTS q;
+       LSA_R_ADD_ACCT_RIGHTS r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+
+       /* Initialise parse structures */
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+       init_q_add_acct_rights(&q, pol, &sid, count, privs_name);
+
+       if (!lsa_io_q_add_acct_rights("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ADDACCTRIGHTS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_add_acct_rights("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+done:
+
+       return result;
+}
+
+
+/* remove account rights for an account. */
+
+NTSTATUS cli_lsa_remove_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                      POLICY_HND *pol, DOM_SID sid, BOOL removeall,
+                                      uint32 count, const char **privs_name)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_REMOVE_ACCT_RIGHTS q;
+       LSA_R_REMOVE_ACCT_RIGHTS r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+
+       /* Initialise parse structures */
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+       init_q_remove_acct_rights(&q, pol, &sid, removeall?1:0, count, privs_name);
+
+       if (!lsa_io_q_remove_acct_rights("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_REMOVEACCTRIGHTS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_remove_acct_rights("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+done:
+
+       return result;
+}
+
+
+/* list account SIDs that have the specified right */
+
+NTSTATUS cli_lsa_enum_account_with_right(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                        POLICY_HND *pol, const char *right,
+                                        uint32 *count, DOM_SID **sids)
+{
+       prs_struct qbuf, rbuf;
+       LSA_Q_ENUM_ACCT_WITH_RIGHT q;
+       LSA_R_ENUM_ACCT_WITH_RIGHT r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+
+       /* Initialise parse structures */
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+       init_q_enum_acct_with_right(&q, pol, right);
+
+       if (!lsa_io_q_enum_acct_with_right("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, LSA_ENUMACCTWITHRIGHT, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!lsa_io_r_enum_acct_with_right("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       *count = r.count;
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (*count) {
+               int i;
+               (*sids) = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * (*count));
+               for (i=0; i<*count; i++) {
+                       sid_copy(&(*sids)[i], &r.sids.sids[i].sid.sid);
+               }
+       }
+done:
+
+       return result;
+}
+
+
+#if 0
+
+/** An example of how to use the routines in this file.  Fetch a DOMAIN
+    sid. Does complete cli setup / teardown anonymously. */
+
+BOOL fetch_domain_sid( char *domain, char *remote_machine, DOM_SID *psid)
+{
+       struct cli_state cli;
+       NTSTATUS result;
+       POLICY_HND lsa_pol;
+       BOOL ret = False;
+       ZERO_STRUCT(cli);
+       if(cli_initialise(&cli) == False) {
+               DEBUG(0,("fetch_domain_sid: unable to initialize client connection.\n"));
+               return False;
+       }
+       if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) {
+               DEBUG(0,("fetch_domain_sid: Can't resolve address for %s\n", remote_machine));
+               goto done;
+       }
+       if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) {
+               DEBUG(0,("fetch_domain_sid: unable to connect to SMB server on \
+machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+               goto done;
+       }
+
+       if (!attempt_netbios_session_request(&cli, lp_netbios_name(), remote_machine, &cli.dest_ip)) {
+               DEBUG(0,("fetch_domain_sid: machine %s rejected the NetBIOS session request.\n", 
+                       remote_machine));
+               goto done;
+       }
+       cli.protocol = PROTOCOL_NT1;
+       if (!cli_negprot(&cli)) {
+               DEBUG(0,("fetch_domain_sid: machine %s rejected the negotiate protocol. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+               goto done;
+       }
+       if (cli.protocol != PROTOCOL_NT1) {
+               DEBUG(0,("fetch_domain_sid: machine %s didn't negotiate NT protocol.\n",
+                       remote_machine));
+               goto done;
+       }
+       /*
+        * Do an anonymous session setup.
+        */
+       if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
+               DEBUG(0,("fetch_domain_sid: machine %s rejected the session setup. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+               goto done;
+       }
+       if (!(cli.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+               DEBUG(0,("fetch_domain_sid: machine %s isn't in user level security mode\n",
+                       remote_machine));
+               goto done;
+       }
+
+       if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+               DEBUG(0,("fetch_domain_sid: machine %s rejected the tconX on the IPC$ share. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+               goto done;
+       }
+
+       /* Fetch domain sid */
+       if (!cli_nt_session_open(&cli, PI_LSARPC)) {
+               DEBUG(0, ("fetch_domain_sid: Error connecting to SAM pipe\n"));
+               goto done;
+       }
+       result = cli_lsa_open_policy(&cli, cli.mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, &lsa_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(0, ("fetch_domain_sid: Error opening lsa policy handle. %s\n",
+                       nt_errstr(result) ));
+               goto done;
+       }
+       result = cli_lsa_query_info_policy(&cli, cli.mem_ctx, &lsa_pol, 5, domain, psid);
+       if (!NT_STATUS_IS_OK(result)) {
+               DEBUG(0, ("fetch_domain_sid: Error querying lsa policy handle. %s\n",
+                       nt_errstr(result) ));
+               goto done;
+       }
+       ret = True;
+
+  done:
+
+       cli_shutdown(&cli);
+       return ret;
+}
+
+#endif
+
+/** @} **/
diff --git a/source4/rpc_client/cli_netlogon.c b/source4/rpc_client/cli_netlogon.c
new file mode 100644 (file)
index 0000000..18ffe07
--- /dev/null
@@ -0,0 +1,759 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT Domain Authentication SMB / MSRPC client
+   Copyright (C) Andrew Tridgell 1992-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Tim Potter 2001
+   Copyright (C) Paul Ashton                       1997.
+   Copyright (C) Jeremy Allison                    1998.
+   Copyright (C) Andrew Bartlett                   2001.
+   
+   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"
+
+/* LSA Request Challenge. Sends our challenge to server, then gets
+   server response. These are used to generate the credentials. */
+
+NTSTATUS cli_net_req_chal(struct cli_state *cli, DOM_CHAL *clnt_chal, 
+                         DOM_CHAL *srv_chal)
+{
+        prs_struct qbuf, rbuf;
+        NET_Q_REQ_CHAL q;
+        NET_R_REQ_CHAL r;
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+        prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+        prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+        
+        /* create and send a MSRPC command with api NET_REQCHAL */
+
+        DEBUG(4,("cli_net_req_chal: LSA Request Challenge from %s to %s: %s\n",
+                 lp_netbios_name(), cli->desthost, credstr(clnt_chal->data)));
+        
+        /* store the parameters */
+        init_q_req_chal(&q, cli->srv_name_slash, lp_netbios_name(), clnt_chal);
+        
+        /* Marshall data and send request */
+
+        if (!net_io_q_req_chal("", &q,  &qbuf, 0) ||
+            !rpc_api_pipe_req(cli, NET_REQCHAL, &qbuf, &rbuf)) {
+                goto done;
+        }
+
+        /* Unmarhall response */
+
+        if (!net_io_r_req_chal("", &r, &rbuf, 0)) {
+                goto done;
+        }
+
+        result = r.status;
+
+        /* Return result */
+
+        if (NT_STATUS_IS_OK(result)) {
+                memcpy(srv_chal, r.srv_chal.data, sizeof(srv_chal->data));
+        }
+        
+ done:
+        prs_mem_free(&qbuf);
+        prs_mem_free(&rbuf);
+        
+        return result;
+}
+
+/****************************************************************************
+LSA Authenticate 2
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key 
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth2(struct cli_state *cli, 
+                      uint16 sec_chan, 
+                      uint32 neg_flags, DOM_CHAL *srv_chal)
+{
+        prs_struct qbuf, rbuf;
+        NET_Q_AUTH_2 q;
+        NET_R_AUTH_2 r;
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+        prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+        prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+        /* create and send a MSRPC command with api NET_AUTH2 */
+
+        DEBUG(4,("cli_net_auth2: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+                 cli->srv_name_slash, cli->mach_acct, sec_chan, lp_netbios_name(),
+                 credstr(cli->clnt_cred.challenge.data), neg_flags));
+
+        /* store the parameters */
+        init_q_auth_2(&q, cli->srv_name_slash, cli->mach_acct, 
+                      sec_chan, lp_netbios_name(), &cli->clnt_cred.challenge, 
+                      neg_flags);
+
+        /* turn parameters into data stream */
+
+        if (!net_io_q_auth_2("", &q,  &qbuf, 0) ||
+            !rpc_api_pipe_req(cli, NET_AUTH2, &qbuf, &rbuf)) {
+                goto done;
+        }
+        
+        /* Unmarshall response */
+        
+        if (!net_io_r_auth_2("", &r, &rbuf, 0)) {
+                goto done;
+        }
+
+        result = r.status;
+
+        if (NT_STATUS_IS_OK(result)) {
+                UTIME zerotime;
+                
+                /*
+                 * Check the returned value using the initial
+                 * server received challenge.
+                 */
+
+                zerotime.time = 0;
+                if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal, 
+                                 zerotime) == 0) {
+
+                        /*
+                         * Server replied with bad credential. Fail.
+                         */
+                        DEBUG(0,("cli_net_auth2: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+                        result = NT_STATUS_ACCESS_DENIED;
+                        goto done;
+                }
+        }
+
+ done:
+        prs_mem_free(&qbuf);
+        prs_mem_free(&rbuf);
+        
+        return result;
+}
+
+/****************************************************************************
+LSA Authenticate 3
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key 
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth3(struct cli_state *cli, 
+                      uint16 sec_chan, 
+                      uint32 *neg_flags, DOM_CHAL *srv_chal)
+{
+        prs_struct qbuf, rbuf;
+        NET_Q_AUTH_3 q;
+        NET_R_AUTH_3 r;
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+        prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+        prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+        /* create and send a MSRPC command with api NET_AUTH2 */
+
+        DEBUG(4,("cli_net_auth3: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+                 cli->srv_name_slash, cli->mach_acct, sec_chan, lp_netbios_name(),
+                 credstr(cli->clnt_cred.challenge.data), *neg_flags));
+
+        /* store the parameters */
+        init_q_auth_3(&q, cli->srv_name_slash, cli->mach_acct, 
+                      sec_chan, lp_netbios_name(), &cli->clnt_cred.challenge, 
+                      *neg_flags);
+
+        /* turn parameters into data stream */
+
+        if (!net_io_q_auth_3("", &q,  &qbuf, 0) ||
+            !rpc_api_pipe_req(cli, NET_AUTH3, &qbuf, &rbuf)) {
+                goto done;
+        }
+        
+        /* Unmarshall response */
+        
+        if (!net_io_r_auth_3("", &r, &rbuf, 0)) {
+                goto done;
+        }
+
+        result = r.status;
+       *neg_flags = r.srv_flgs.neg_flags;
+
+        if (NT_STATUS_IS_OK(result)) {
+                UTIME zerotime;
+                
+                /*
+                 * Check the returned value using the initial
+                 * server received challenge.
+                 */
+
+                zerotime.time = 0;
+                if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal, 
+                                 zerotime) == 0) {
+
+                        /*
+                         * Server replied with bad credential. Fail.
+                         */
+                        DEBUG(0,("cli_net_auth3: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+                        result = NT_STATUS_ACCESS_DENIED;
+                        goto done;
+                }
+        }
+
+ done:
+        prs_mem_free(&qbuf);
+        prs_mem_free(&rbuf);
+        
+        return result;
+}
+
+/* Return the secure channel type depending on the server role. */
+
+uint16 get_sec_chan(void)
+{
+       uint16 sec_chan = SEC_CHAN_WKSTA;
+
+       switch (lp_server_role()) {
+       case ROLE_DOMAIN_PDC:
+               sec_chan = SEC_CHAN_DOMAIN;
+               break;
+       case ROLE_DOMAIN_BDC:
+               sec_chan = SEC_CHAN_BDC;
+               break;
+       }
+
+       return sec_chan;
+}
+
+/* Initialize domain session credentials */
+
+NTSTATUS cli_nt_setup_creds(struct cli_state *cli, 
+                           uint16 sec_chan,
+                           const unsigned char mach_pwd[16], uint32 *neg_flags, int level)
+{
+        DOM_CHAL clnt_chal;
+        DOM_CHAL srv_chal;
+        UTIME zerotime;
+        NTSTATUS result;
+
+        /******************* Request Challenge ********************/
+
+        generate_random_buffer(clnt_chal.data, 8, False);
+       
+        /* send a client challenge; receive a server challenge */
+        result = cli_net_req_chal(cli, &clnt_chal, &srv_chal);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                DEBUG(0,("cli_nt_setup_creds: request challenge failed\n"));
+                return result;
+        }
+        
+        /**************** Long-term Session key **************/
+
+        /* calculate the session key */
+        cred_session_key(&clnt_chal, &srv_chal, mach_pwd, 
+                         cli->sess_key);
+        memset((char *)cli->sess_key+8, '\0', 8);
+
+        /******************* Authenticate 2/3 ********************/
+
+        /* calculate auth-2/3 credentials */
+        zerotime.time = 0;
+        cred_create(cli->sess_key, &clnt_chal, zerotime, &cli->clnt_cred.challenge);
+
+        /*  
+         * Send client auth-2/3 challenge.
+         * Receive an auth-2/3 challenge response and check it.
+         */
+        switch (level) {
+               case 2:
+                       result = cli_net_auth2(cli, sec_chan, *neg_flags, &srv_chal);
+                       break;
+               case 3:
+                       result = cli_net_auth3(cli, sec_chan, neg_flags, &srv_chal);
+                       break;
+               default:
+                       DEBUG(1,("cli_nt_setup_creds: unsupported auth level: %d\n", level));
+                       break;
+       }
+
+       if (!NT_STATUS_IS_OK(result))
+                DEBUG(1,("cli_nt_setup_creds: auth%d challenge failed %s\n", level, nt_errstr(result)));
+
+        return result;
+}
+
+/* Logon Control 2 */
+
+NTSTATUS cli_netlogon_logon_ctrl2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                  uint32 query_level)
+{
+       prs_struct qbuf, rbuf;
+       NET_Q_LOGON_CTRL2 q;
+       NET_R_LOGON_CTRL2 r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_net_q_logon_ctrl2(&q, cli->srv_name_slash, query_level);
+
+       /* Marshall data and send request */
+
+       if (!net_io_q_logon_ctrl2("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, NET_LOGON_CTRL2, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!net_io_r_logon_ctrl2("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/****************************************************************************
+Generate the next creds to use.  Yuck - this is a cut&paste from another
+file.  They should be combined at some stage.  )-:
+****************************************************************************/
+
+static void gen_next_creds( struct cli_state *cli, DOM_CRED *new_clnt_cred)
+{
+       /*
+        * Create the new client credentials.
+        */
+       
+       cli->clnt_cred.timestamp.time = time(NULL);
+       
+       memcpy(new_clnt_cred, &cli->clnt_cred, sizeof(*new_clnt_cred));
+
+       /* Calculate the new credentials. */
+       cred_create(cli->sess_key, &(cli->clnt_cred.challenge),
+                   new_clnt_cred->timestamp, &(new_clnt_cred->challenge));
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_sync(struct cli_state *cli, TALLOC_CTX *mem_ctx, DOM_CRED *ret_creds,
+                               uint32 database_id, uint32 next_rid, uint32 *num_deltas,
+                               SAM_DELTA_HDR **hdr_deltas, 
+                               SAM_DELTA_CTR **deltas)
+{
+       prs_struct qbuf, rbuf;
+       NET_Q_SAM_SYNC q;
+       NET_R_SAM_SYNC r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        DOM_CRED clnt_creds;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        gen_next_creds(cli, &clnt_creds);
+
+       init_net_q_sam_sync(&q, cli->srv_name_slash, cli->clnt_name_slash + 2,
+                            &clnt_creds, ret_creds, database_id, next_rid);
+
+       /* Marshall data and send request */
+
+       if (!net_io_q_sam_sync("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, NET_SAM_SYNC, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!net_io_r_sam_sync("", cli->sess_key, &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+        /* Return results */
+
+       result = r.status;
+        *num_deltas = r.num_deltas2;
+        *hdr_deltas = r.hdr_deltas;
+        *deltas = r.deltas;
+
+       memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_deltas(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 uint32 database_id, UINT64_S seqnum,
+                                 uint32 *num_deltas, 
+                                 SAM_DELTA_HDR **hdr_deltas, 
+                                 SAM_DELTA_CTR **deltas)
+{
+       prs_struct qbuf, rbuf;
+       NET_Q_SAM_DELTAS q;
+       NET_R_SAM_DELTAS r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        DOM_CRED clnt_creds;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        gen_next_creds(cli, &clnt_creds);
+
+       init_net_q_sam_deltas(&q, cli->srv_name_slash, 
+                              cli->clnt_name_slash + 2, &clnt_creds, 
+                              database_id, seqnum);
+
+       /* Marshall data and send request */
+
+       if (!net_io_q_sam_deltas("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, NET_SAM_DELTAS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!net_io_r_sam_deltas("", cli->sess_key, &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+        /* Return results */
+
+       result = r.status;
+        *num_deltas = r.num_deltas2;
+        *hdr_deltas = r.hdr_deltas;
+        *deltas = r.deltas;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Logon domain user */
+
+NTSTATUS cli_netlogon_sam_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                const char *username, const char *password,
+                                int logon_type)
+{
+       prs_struct qbuf, rbuf;
+       NET_Q_SAM_LOGON q;
+       NET_R_SAM_LOGON r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        DOM_CRED clnt_creds, dummy_rtn_creds;
+        NET_ID_INFO_CTR ctr;
+        NET_USER_INFO_3 user;
+        int validation_level = 3;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+        /* Initialise input parameters */
+
+        gen_next_creds(cli, &clnt_creds);
+
+        q.validation_level = validation_level;
+
+       memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds));
+       dummy_rtn_creds.timestamp.time = time(NULL);
+
+        ctr.switch_value = logon_type;
+
+        switch (logon_type) {
+        case INTERACTIVE_LOGON_TYPE: {
+                unsigned char lm_owf_user_pwd[16], nt_owf_user_pwd[16];
+
+                nt_lm_owf_gen(password, nt_owf_user_pwd, lm_owf_user_pwd);
+
+                init_id_info1(&ctr.auth.id1, lp_workgroup(), 
+                              0, /* param_ctrl */
+                              0xdead, 0xbeef, /* LUID? */
+                              username, cli->clnt_name_slash,
+                              cli->sess_key, lm_owf_user_pwd,
+                              nt_owf_user_pwd);
+
+                break;
+        }
+        case NET_LOGON_TYPE: {
+                uint8 chal[8];
+                unsigned char local_lm_response[24];
+                unsigned char local_nt_response[24];
+
+                generate_random_buffer(chal, 8, False);
+
+                SMBencrypt(password, chal, local_lm_response);
+                SMBNTencrypt(password, chal, local_nt_response);
+
+                init_id_info2(&ctr.auth.id2, lp_workgroup(), 
+                              0, /* param_ctrl */
+                              0xdead, 0xbeef, /* LUID? */
+                              username, cli->clnt_name_slash, chal,
+                              local_lm_response, 24, local_nt_response, 24);
+                break;
+        }
+        default:
+                DEBUG(0, ("switch value %d not supported\n", 
+                          ctr.switch_value));
+                goto done;
+        }
+
+        init_sam_info(&q.sam_id, cli->srv_name_slash, lp_netbios_name(),
+                      &clnt_creds, &dummy_rtn_creds, logon_type,
+                      &ctr);
+
+        /* Marshall data and send request */
+
+       if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+        r.user = &user;
+
+       if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+        /* Return results */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+        return result;
+}
+
+
+/** 
+ * Logon domain user with an 'network' SAM logon 
+ *
+ * @param info3 Pointer to a NET_USER_INFO_3 already allocated by the caller.
+ **/
+
+NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                       const char *username, const char *domain, const char *workstation, 
+                                       const uint8 chal[8],
+                                       DATA_BLOB lm_response, DATA_BLOB nt_response,
+                                       NET_USER_INFO_3 *info3)
+
+{
+       prs_struct qbuf, rbuf;
+       NET_Q_SAM_LOGON q;
+       NET_R_SAM_LOGON r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        DOM_CRED clnt_creds, dummy_rtn_creds;
+       NET_ID_INFO_CTR ctr;
+       int validation_level = 3;
+       char *workstation_name_slash;
+       
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       workstation_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", workstation);
+       
+       if (!workstation_name_slash) {
+               DEBUG(0, ("talloc_asprintf failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       gen_next_creds(cli, &clnt_creds);
+
+       q.validation_level = validation_level;
+
+       memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds));
+       dummy_rtn_creds.timestamp.time = time(NULL);
+
+        ctr.switch_value = NET_LOGON_TYPE;
+
+       init_id_info2(&ctr.auth.id2, domain,
+                     0, /* param_ctrl */
+                     0xdead, 0xbeef, /* LUID? */
+                     username, workstation_name_slash, (const uchar*)chal,
+                     lm_response.data, lm_response.length, nt_response.data, nt_response.length);
+        init_sam_info(&q.sam_id, cli->srv_name_slash, lp_netbios_name(),
+                      &clnt_creds, &dummy_rtn_creds, NET_LOGON_TYPE,
+                      &ctr);
+
+        /* Marshall data and send request */
+
+       if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+        r.user = info3;
+
+       if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+        /* Return results */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+        return result;
+}
+
+/***************************************************************************
+LSA Server Password Set.
+****************************************************************************/
+
+NTSTATUS cli_net_srv_pwset(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                          const char *machine_name, uint8 hashed_mach_pwd[16])
+{
+       prs_struct rbuf;
+       prs_struct qbuf; 
+       DOM_CRED new_clnt_cred;
+       NET_Q_SRV_PWSET q_s;
+       uint16 sec_chan_type = 2;
+       NTSTATUS nt_status;
+       char *mach_acct;
+
+       gen_next_creds( cli, &new_clnt_cred);
+       
+       prs_init(&qbuf , 1024, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0,    mem_ctx, UNMARSHALL);
+       
+       /* create and send a MSRPC command with api NET_SRV_PWSET */
+       
+       mach_acct = talloc_asprintf(mem_ctx, "%s$", machine_name);
+       
+       if (!mach_acct) {
+               DEBUG(0,("talloc_asprintf failed!\n"));
+               nt_status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n",
+                cli->srv_name_slash, mach_acct, sec_chan_type, machine_name,
+                credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time));
+       
+        /* store the parameters */
+       init_q_srv_pwset(&q_s, cli->srv_name_slash, cli->sess_key,
+                        mach_acct, sec_chan_type, machine_name, 
+                        &new_clnt_cred, (char *)hashed_mach_pwd);
+       
+       /* turn parameters into data stream */
+       if(!net_io_q_srv_pwset("", &q_s,  &qbuf, 0)) {
+               DEBUG(0,("cli_net_srv_pwset: Error : failed to marshall NET_Q_SRV_PWSET struct.\n"));
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       
+       /* send the data on \PIPE\ */
+       if (rpc_api_pipe_req(cli, NET_SRVPWSET, &qbuf, &rbuf))
+       {
+               NET_R_SRV_PWSET r_s;
+               
+               if (!net_io_r_srv_pwset("", &r_s, &rbuf, 0)) {
+                       nt_status =  NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+               }
+               
+               nt_status = r_s.status;
+
+               if (!NT_STATUS_IS_OK(r_s.status))
+               {
+                       /* report error code */
+                       DEBUG(0,("cli_net_srv_pwset: %s\n", nt_errstr(nt_status)));
+                       goto done;
+               }
+
+               /* Update the credentials. */
+               if (!clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_cred)))
+               {
+                       /*
+                        * Server replied with bad credential. Fail.
+                        */
+                       DEBUG(0,("cli_net_srv_pwset: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+       
+       return nt_status;
+}
+
diff --git a/source4/rpc_client/cli_pipe.c b/source4/rpc_client/cli_pipe.c
new file mode 100644 (file)
index 0000000..0fa2b4a
--- /dev/null
@@ -0,0 +1,1357 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1998,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ *  Copyright (C) Paul Ashton                       1998.
+ *  Copyright (C) Jeremy Allison                    1999.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+extern struct pipe_id_info pipe_names[];
+
+/********************************************************************
+ Rpc pipe call id.
+ ********************************************************************/
+
+static uint32 get_rpc_call_id(void)
+{
+       static uint32 call_id = 0;
+       return ++call_id;
+}
+
+/*******************************************************************
+ Use SMBreadX to get rest of one fragment's worth of rpc data.
+ ********************************************************************/
+
+static BOOL rpc_read(struct cli_state *cli, prs_struct *rdata, uint32 data_to_read, uint32 *rdata_offset)
+{
+       size_t size = (size_t)cli->max_recv_frag;
+       int stream_offset = 0;
+       int num_read;
+       char *pdata;
+       int extra_data_size = ((int)*rdata_offset) + ((int)data_to_read) - (int)prs_data_size(rdata);
+
+       DEBUG(5,("rpc_read: data_to_read: %u rdata offset: %u extra_data_size: %d\n",
+               (int)data_to_read, (unsigned int)*rdata_offset, extra_data_size));
+
+       /*
+        * Grow the buffer if needed to accommodate the data to be read.
+        */
+
+       if (extra_data_size > 0) {
+               if(!prs_force_grow(rdata, (uint32)extra_data_size)) {
+                       DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", extra_data_size ));
+                       return False;
+               }
+               DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", extra_data_size, prs_data_size(rdata) ));
+       }
+
+       pdata = prs_data_p(rdata) + *rdata_offset;
+
+       do /* read data using SMBreadX */
+       {
+               uint32 ecode;
+               uint8 eclass;
+
+               if (size > (size_t)data_to_read)
+                       size = (size_t)data_to_read;
+
+               num_read = (int)cli_read(cli, cli->nt_pipe_fnum, pdata, (off_t)stream_offset, size);
+
+               DEBUG(5,("rpc_read: num_read = %d, read offset: %d, to read: %d\n",
+                         num_read, stream_offset, data_to_read));
+
+               if (cli_is_dos_error(cli)) {
+                        cli_dos_error(cli, &eclass, &ecode);
+                        if (eclass != ERRDOS && ecode != ERRmoredata) {
+                                DEBUG(0,("rpc_read: Error %d/%u in cli_read\n",
+                                         eclass, (unsigned int)ecode));
+                                return False;
+                        }
+               }
+
+               data_to_read -= num_read;
+               stream_offset += num_read;
+               pdata += num_read;
+
+       } while (num_read > 0 && data_to_read > 0);
+       /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */
+
+       /*
+        * Update the current offset into rdata by the amount read.
+        */
+       *rdata_offset += stream_offset;
+
+       return True;
+}
+
+/****************************************************************************
+ Checks the header. This will set the endian bit in the rdata prs_struct. JRA.
+ ****************************************************************************/
+
+static BOOL rpc_check_hdr(prs_struct *rdata, RPC_HDR *rhdr, 
+                          BOOL *first, BOOL *last, uint32 *len)
+{
+       DEBUG(5,("rpc_check_hdr: rdata->data_size = %u\n", (uint32)prs_data_size(rdata) ));
+
+       /* Next call sets endian bit. */
+
+       if(!smb_io_rpc_hdr("rpc_hdr   ", rhdr, rdata, 0)) {
+               DEBUG(0,("rpc_check_hdr: Failed to unmarshall RPC_HDR.\n"));
+               return False;
+       }
+
+       if (prs_offset(rdata) != RPC_HEADER_LEN) {
+               DEBUG(0,("rpc_check_hdr: offset was %x, should be %x.\n", prs_offset(rdata), RPC_HEADER_LEN));
+               return False;
+       }
+
+       (*first) = ((rhdr->flags & RPC_FLG_FIRST) != 0);
+       (*last) = ((rhdr->flags & RPC_FLG_LAST ) != 0);
+       (*len) = (uint32)rhdr->frag_len - prs_data_size(rdata);
+
+       return (rhdr->pkt_type != RPC_FAULT);
+}
+
+static void NTLMSSPcalc_ap( struct cli_state *cli, unsigned char *data, uint32 len)
+{
+       unsigned char *hash = cli->ntlmssp_hash;
+       unsigned char index_i = hash[256];
+       unsigned char index_j = hash[257];
+       int ind;
+
+       for( ind = 0; ind < len; ind++) {
+               unsigned char tc;
+               unsigned char t;
+
+               index_i++;
+               index_j += hash[index_i];
+
+               tc = hash[index_i];
+               hash[index_i] = hash[index_j];
+               hash[index_j] = tc;
+
+               t = hash[index_i] + hash[index_j];
+               data[ind] = data[ind] ^ hash[t];
+       }
+
+       hash[256] = index_i;
+       hash[257] = index_j;
+}
+
+/****************************************************************************
+ Verify data on an rpc pipe.
+ The VERIFY & SEAL code is only executed on packets that look like this :
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR      | AUTH DATA   |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Never on bind requests/responses.
+ ****************************************************************************/
+
+static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata, int len, int auth_len)
+{
+       /*
+        * The following is that length of the data we must sign or seal.
+        * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+        * preceeding the auth_data.
+        */
+
+       int data_len = len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len;
+
+       /*
+        * The start of the data to sign/seal is just after the RPC headers.
+        */
+       char *reply_data = prs_data_p(rdata) + RPC_HEADER_LEN + RPC_HDR_REQ_LEN;
+
+       BOOL auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0);
+       BOOL auth_seal = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0);
+
+       DEBUG(5,("rpc_auth_pipe: len: %d auth_len: %d verify %s seal %s\n",
+                 len, auth_len, BOOLSTR(auth_verify), BOOLSTR(auth_seal)));
+
+       /*
+        * Unseal any sealed data in the PDU, not including the
+        * 8 byte auth_header or the auth_data.
+        */
+
+       if (auth_seal) {
+               DEBUG(10,("rpc_auth_pipe: unseal\n"));
+               dump_data(100, reply_data, data_len);
+               NTLMSSPcalc_ap(cli, (uchar*)reply_data, data_len);
+               dump_data(100, reply_data, data_len);
+       }
+
+       if (auth_verify || auth_seal) {
+               RPC_HDR_AUTH rhdr_auth; 
+               prs_struct auth_req;
+               char data[RPC_HDR_AUTH_LEN];
+               /*
+                * We set dp to be the end of the packet, minus the auth_len
+                * and the length of the header that preceeds the auth_data.
+                */
+               char *dp = prs_data_p(rdata) + len - auth_len - RPC_HDR_AUTH_LEN;
+
+               if(dp - prs_data_p(rdata) > prs_data_size(rdata)) {
+                       DEBUG(0,("rpc_auth_pipe: auth data > data size !\n"));
+                       return False;
+               }
+
+               memcpy(data, dp, sizeof(data));
+               
+               prs_init(&auth_req , 0, cli->mem_ctx, UNMARSHALL);
+
+               /* The endianness must be preserved... JRA. */
+
+               prs_set_endian_data(&auth_req, rdata->bigendian_data);
+
+               prs_give_memory(&auth_req, data, RPC_HDR_AUTH_LEN, False);
+
+               /*
+                * Unmarshall the 8 byte auth_header that comes before the
+                * auth data.
+                */
+
+               if(!smb_io_rpc_hdr_auth("hdr_auth", &rhdr_auth, &auth_req, 0)) {
+                       DEBUG(0,("rpc_auth_pipe: unmarshalling RPC_HDR_AUTH failed.\n"));
+                       return False;
+               }
+
+               if (!rpc_hdr_auth_chk(&rhdr_auth)) {
+                       DEBUG(0,("rpc_auth_pipe: rpc_hdr_auth_chk failed.\n"));
+                       return False;
+               }
+       }
+
+       /*
+        * Now unseal and check the auth verifier in the auth_data at
+        * then end of the packet. The 4 bytes skipped in the unseal
+        * seem to be a buffer pointer preceeding the sealed data.
+        */
+
+       if (auth_verify) {
+               RPC_AUTH_NTLMSSP_CHK chk;
+               uint32 crc32;
+               prs_struct auth_verf;
+               char data[RPC_AUTH_NTLMSSP_CHK_LEN];
+               char *dp = prs_data_p(rdata) + len - auth_len;
+
+               if(dp - prs_data_p(rdata) > prs_data_size(rdata)) {
+                       DEBUG(0,("rpc_auth_pipe: auth data > data size !\n"));
+                       return False;
+               }
+
+               DEBUG(10,("rpc_auth_pipe: verify\n"));
+               dump_data(100, dp, auth_len);
+               NTLMSSPcalc_ap(cli, (uchar*)(dp+4), auth_len - 4);
+
+               memcpy(data, dp, RPC_AUTH_NTLMSSP_CHK_LEN);
+               dump_data(100, data, auth_len);
+
+               prs_init(&auth_verf, 0, cli->mem_ctx, UNMARSHALL);
+
+               /* The endinness must be preserved. JRA. */
+               prs_set_endian_data( &auth_verf, rdata->bigendian_data);
+
+               prs_give_memory(&auth_verf, data, RPC_AUTH_NTLMSSP_CHK_LEN, False);
+
+               if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, &auth_verf, 0)) {
+                       DEBUG(0,("rpc_auth_pipe: unmarshalling RPC_AUTH_NTLMSSP_CHK failed.\n"));
+                       return False;
+               }
+
+               crc32 = crc32_calc_buffer(reply_data, data_len);
+
+               if (!rpc_auth_ntlmssp_chk(&chk, crc32 , cli->ntlmssp_seq_num)) {
+                       DEBUG(0,("rpc_auth_pipe: rpc_auth_ntlmssp_chk failed.\n"));
+                       return False;
+               }
+               cli->ntlmssp_seq_num++;
+       }
+       return True;
+}
+
+
+/****************************************************************************
+ Send data on an rpc pipe via trans, which *must* be the last fragment.
+ receive response data from an rpc pipe, which may be large...
+
+ Read the first fragment: unfortunately have to use SMBtrans for the first
+ bit, then SMBreadX for subsequent bits.
+
+ If first fragment received also wasn't the last fragment, continue
+ getting fragments until we _do_ receive the last fragment.
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR      | AUTH DATA   |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Where the presence of the AUTH_HDR and AUTH are dependent on the
+ signing & sealing being neogitated.
+
+ ****************************************************************************/
+
+static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata)
+{
+       uint32 len;
+       char *rparam = NULL;
+       uint32 rparam_len = 0;
+       uint16 setup[2];
+       BOOL first = True;
+       BOOL last  = True;
+       RPC_HDR rhdr;
+       char *pdata = data ? prs_data_p(data) : NULL;
+       uint32 data_len = data ? prs_offset(data) : 0;
+       char *prdata = NULL;
+       uint32 rdata_len = 0;
+       uint32 current_offset = 0;
+       uint32 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024;
+
+       /* Create setup parameters - must be in native byte order. */
+
+       setup[0] = TRANSACT_DCERPCCMD; 
+       setup[1] = cli->nt_pipe_fnum; /* Pipe file handle. */
+
+       DEBUG(5,("rpc_api_pipe: fnum:%x\n", (int)cli->nt_pipe_fnum));
+
+       /* Send the RPC request and receive a response.  For short RPC
+          calls (about 1024 bytes or so) the RPC request and response
+          appears in a SMBtrans request and response.  Larger RPC
+          responses are received further on. */
+
+       if (!cli_api_pipe(cli, "\\PIPE\\",
+                 setup, 2, 0,                     /* Setup, length, max */
+                 NULL, 0, 0,                      /* Params, length, max */
+                 pdata, data_len, max_data,       /* data, length, max */
+                 &rparam, &rparam_len,            /* return params, len */
+                 &prdata, &rdata_len))            /* return data, len */
+       {
+               DEBUG(0, ("cli_pipe: return critical error. Error was %s\n", cli_errstr(cli)));
+               return False;
+       }
+
+       /* Throw away returned params - we know we won't use them. */
+
+       SAFE_FREE(rparam);
+
+       if (prdata == NULL) {
+               DEBUG(0,("rpc_api_pipe: pipe %x failed to return data.\n",
+                       (int)cli->nt_pipe_fnum));
+               return False;
+       }
+
+       /*
+        * Give this memory as dynamically allocated to the return parse
+        * struct.  
+        */
+
+       prs_give_memory(rdata, prdata, rdata_len, True);
+       current_offset = rdata_len;
+
+       /* This next call sets the endian bit correctly in rdata. */
+
+       if (!rpc_check_hdr(rdata, &rhdr, &first, &last, &len)) {
+               prs_mem_free(rdata);
+               return False;
+       }
+
+       if (rhdr.pkt_type == RPC_BINDACK) {
+               if (!last && !first) {
+                       DEBUG(5,("rpc_api_pipe: bug in server (AS/U?), setting fragment first/last ON.\n"));
+                       first = True;
+                       last = True;
+               }
+       }
+
+       if (rhdr.pkt_type == RPC_RESPONSE) {
+               RPC_HDR_RESP rhdr_resp;
+               if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, rdata, 0)) {
+                       DEBUG(5,("rpc_api_pipe: failed to unmarshal RPC_HDR_RESP.\n"));
+                       prs_mem_free(rdata);
+                       return False;
+               }
+       }
+
+       DEBUG(5,("rpc_api_pipe: len left: %u smbtrans read: %u\n",
+                 (unsigned int)len, (unsigned int)rdata_len ));
+
+       /* check if data to be sent back was too large for one SMBtrans */
+       /* err status is only informational: the _real_ check is on the
+           length */
+
+       if (len > 0) { 
+               /* || err == (0x80000000 | STATUS_BUFFER_OVERFLOW)) */
+
+               /* Read the remaining part of the first response fragment */
+
+               if (!rpc_read(cli, rdata, len, &current_offset)) {
+                       prs_mem_free(rdata);
+                       return False;
+               }
+       }
+
+       /*
+        * Now we have a complete PDU, check the auth struct if any was sent.
+        */
+
+       if (rhdr.auth_len != 0) {
+               if(!rpc_auth_pipe(cli, rdata, rhdr.frag_len, rhdr.auth_len))
+                       return False;
+               /*
+                * Drop the auth footers from the current offset.
+                * We need this if there are more fragments.
+                * The auth footers consist of the auth_data and the
+                * preceeding 8 byte auth_header.
+                */
+               current_offset -= (rhdr.auth_len + RPC_HDR_AUTH_LEN);
+       }
+       
+       /* 
+        * Only one rpc fragment, and it has been read.
+        */
+
+       if (first && last) {
+               DEBUG(6,("rpc_api_pipe: fragment first and last both set\n"));
+               return True;
+       }
+
+       /*
+        * Read more fragments using SMBreadX until we get one with the
+        * last bit set.
+        */
+
+       while (!last) {
+               RPC_HDR_RESP rhdr_resp;
+               int num_read;
+               char hdr_data[RPC_HEADER_LEN+RPC_HDR_RESP_LEN];
+               prs_struct hps;
+               uint8 eclass;
+               uint32 ecode;
+
+               /*
+                * First read the header of the next PDU.
+                */
+
+               prs_init(&hps, 0, cli->mem_ctx, UNMARSHALL);
+               prs_give_memory(&hps, hdr_data, sizeof(hdr_data), False);
+
+               num_read = cli_read(cli, cli->nt_pipe_fnum, hdr_data, 0, RPC_HEADER_LEN+RPC_HDR_RESP_LEN);
+               if (cli_is_dos_error(cli)) {
+                        cli_dos_error(cli, &eclass, &ecode);
+                        if (eclass != ERRDOS && ecode != ERRmoredata) {
+                                DEBUG(0,("rpc_api_pipe: cli_read error : %d/%d\n", eclass, ecode));
+                                return False;
+                        }
+               }
+
+               DEBUG(5,("rpc_api_pipe: read header (size:%d)\n", num_read));
+
+               if (num_read != RPC_HEADER_LEN+RPC_HDR_RESP_LEN) {
+                       DEBUG(0,("rpc_api_pipe: Error : requested %d bytes, got %d.\n",
+                               RPC_HEADER_LEN+RPC_HDR_RESP_LEN, num_read ));
+                       return False;
+               }
+
+               /* This call sets the endianness in hps. */
+
+               if (!rpc_check_hdr(&hps, &rhdr, &first, &last, &len))
+                       return False;
+
+               /* Ensure the endianness in rdata is set correctly - must be same as hps. */
+
+               if (hps.bigendian_data != rdata->bigendian_data) {
+                       DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to %s\n",
+                               rdata->bigendian_data ? "big" : "little",
+                               hps.bigendian_data ? "big" : "little" ));
+                       return False;
+               }
+
+               if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, &hps, 0)) {
+                       DEBUG(0,("rpc_api_pipe: Error in unmarshalling RPC_HDR_RESP.\n"));
+                       return False;
+               }
+
+               if (first) {
+                       DEBUG(0,("rpc_api_pipe: secondary PDU rpc header has 'first' set !\n"));
+                       return False;
+               }
+
+               /*
+                * Now read the rest of the PDU.
+                */
+
+               if (!rpc_read(cli, rdata, len, &current_offset))
+                       return False;
+
+               /*
+                * Verify any authentication footer.
+                */
+
+               if (rhdr.auth_len != 0 ) {
+                       if(!rpc_auth_pipe(cli, rdata, rhdr.frag_len, rhdr.auth_len))
+                               return False;
+                       /*
+                        * Drop the auth footers from the current offset.
+                        * The auth footers consist of the auth_data and the
+                        * preceeding 8 byte auth_header.
+                        * We need this if there are more fragments.
+                        */
+                       current_offset -= (rhdr.auth_len + RPC_HDR_AUTH_LEN);
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ creates a DCE/RPC bind request
+
+ - initialises the parse structure.
+ - dynamically allocates the header data structure
+ - caller is expected to free the header data structure once used.
+
+ ********************************************************************/
+
+static BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, uint32 rpc_call_id,
+                                RPC_IFACE *abstract, RPC_IFACE *transfer,
+                                const char *my_name, const char *domain, uint32 neg_flags)
+{
+       RPC_HDR hdr;
+       RPC_HDR_RB hdr_rb;
+       char buffer[4096];
+       prs_struct auth_info;
+       int auth_len = 0;
+
+       prs_init(&auth_info, 0, prs_get_mem_context(rpc_out), MARSHALL);
+
+       if (do_auth) {
+               RPC_HDR_AUTH hdr_auth;
+               RPC_AUTH_VERIFIER auth_verifier;
+               RPC_AUTH_NTLMSSP_NEG ntlmssp_neg;
+
+               /*
+                * Create the auth structs we will marshall.
+                */
+
+               init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, 0x00, 1);
+               init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_NEGOTIATE);
+               init_rpc_auth_ntlmssp_neg(&ntlmssp_neg, neg_flags, my_name, domain);
+
+               /*
+                * Use the 4k buffer to store the auth info.
+                */
+
+               prs_give_memory( &auth_info, buffer, sizeof(buffer), False);
+
+               /*
+                * Now marshall the data into the temporary parse_struct.
+                */
+
+               if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &auth_info, 0)) {
+                       DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_AUTH.\n"));
+                       return False;
+               }
+
+               if(!smb_io_rpc_auth_verifier("auth_verifier", &auth_verifier, &auth_info, 0)) {
+                       DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_AUTH_VERIFIER.\n"));
+                       return False;
+               }
+
+               if(!smb_io_rpc_auth_ntlmssp_neg("ntlmssp_neg", &ntlmssp_neg, &auth_info, 0)) {
+                       DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_AUTH_NTLMSSP_NEG.\n"));
+                       return False;
+               }
+
+               /* Auth len in the rpc header doesn't include auth_header. */
+               auth_len = prs_offset(&auth_info) - RPC_HDR_AUTH_LEN;
+       }
+
+       /* create the request RPC_HDR */
+       init_rpc_hdr(&hdr, RPC_BIND, 0x3, rpc_call_id, 
+               RPC_HEADER_LEN + RPC_HDR_RB_LEN + prs_offset(&auth_info),
+               auth_len);
+
+       if(!smb_io_rpc_hdr("hdr"   , &hdr, rpc_out, 0)) {
+               DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR.\n"));
+               return False;
+       }
+
+       /* create the bind request RPC_HDR_RB */
+       init_rpc_hdr_rb(&hdr_rb, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN, 0x0,
+                       0x1, 0x0, 0x1, abstract, transfer);
+
+       /* Marshall the bind request data */
+       if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_out, 0)) {
+               DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_RB.\n"));
+               return False;
+       }
+
+       /*
+        * Grow the outgoing buffer to store any auth info.
+        */
+
+       if(hdr.auth_len != 0) {
+               if(!prs_append_prs_data( rpc_out, &auth_info)) {
+                       DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+                       return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind authentication response.
+ This is the packet that is sent back to the server once we
+ have received a BIND-ACK, to finish the third leg of
+ the authentication handshake.
+ ********************************************************************/
+
+static BOOL create_rpc_bind_resp(struct pwd_info *pwd,
+                               const char *domain, const char *user_name, const char *my_name,
+                               uint32 ntlmssp_cli_flgs,
+                               uint32 rpc_call_id,
+                               prs_struct *rpc_out)
+{
+       unsigned char lm_owf[24];
+       unsigned char nt_owf[24];
+       RPC_HDR hdr;
+       RPC_HDR_AUTHA hdr_autha;
+       RPC_AUTH_VERIFIER auth_verifier;
+       RPC_AUTH_NTLMSSP_RESP ntlmssp_resp;
+       char buffer[4096];
+       prs_struct auth_info;
+
+       /*
+        * Marshall the variable length data into a temporary parse
+        * struct, pointing into a 4k local buffer.
+        */
+       prs_init(&auth_info, 0, prs_get_mem_context(rpc_out), MARSHALL);
+
+       /*
+        * Use the 4k buffer to store the auth info.
+        */
+
+       prs_give_memory( &auth_info, buffer, sizeof(buffer), False);
+
+       /*
+        * Create the variable length auth_data.
+        */
+
+       init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH);
+
+       pwd_get_lm_nt_owf(pwd, lm_owf, nt_owf);
+                       
+       init_rpc_auth_ntlmssp_resp(&ntlmssp_resp,
+                                lm_owf, nt_owf,
+                                domain, user_name, my_name,
+                                ntlmssp_cli_flgs);
+
+       /*
+        * Marshall the variable length auth_data into a temp parse_struct.
+        */
+
+       if(!smb_io_rpc_auth_verifier("auth_verifier", &auth_verifier, &auth_info, 0)) {
+               DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_AUTH_VERIFIER.\n"));
+               return False;
+       }
+
+       if(!smb_io_rpc_auth_ntlmssp_resp("ntlmssp_resp", &ntlmssp_resp, &auth_info, 0)) {
+               DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_AUTH_NTLMSSP_RESP.\n"));
+               return False;
+       }
+
+       /* Create the request RPC_HDR */
+       init_rpc_hdr(&hdr, RPC_BINDRESP, 0x0, rpc_call_id,
+                       RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN + prs_offset(&auth_info),
+                       prs_offset(&auth_info) );
+
+       /* Marshall it. */
+       if(!smb_io_rpc_hdr("hdr", &hdr, rpc_out, 0)) {
+               DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR.\n"));
+               return False;
+       }
+
+       /* Create the request RPC_HDR_AUTHA */
+       init_rpc_hdr_autha(&hdr_autha, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN,
+                       NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, 0x00);
+
+       if(!smb_io_rpc_hdr_autha("hdr_autha", &hdr_autha, rpc_out, 0)) {
+               DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR_AUTHA.\n"));
+               return False;
+       }
+
+       /*
+        * Append the auth data to the outgoing buffer.
+        */
+
+       if(!prs_append_prs_data(rpc_out, &auth_info)) {
+               DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+ Creates a DCE/RPC request.
+ ********************************************************************/
+
+static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len, int auth_len, uint8 flags, uint32 oldid, uint32 data_left)
+{
+       uint32 alloc_hint;
+       RPC_HDR     hdr;
+       RPC_HDR_REQ hdr_req;
+       uint32 callid = oldid ? oldid : get_rpc_call_id();
+
+       DEBUG(5,("create_rpc_request: opnum: 0x%x data_len: 0x%x\n", op_num, data_len));
+
+       /* create the rpc header RPC_HDR */
+       init_rpc_hdr(&hdr, RPC_REQUEST, flags,
+                    callid, data_len, auth_len);
+
+       /*
+        * The alloc hint should be the amount of data, not including 
+        * RPC headers & footers.
+        */
+
+       if (auth_len != 0)
+               alloc_hint = data_left - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len;
+       else
+               alloc_hint = data_left - RPC_HEADER_LEN;
+
+       DEBUG(10,("create_rpc_request: data_len: %x auth_len: %x alloc_hint: %x\n",
+                  data_len, auth_len, alloc_hint));
+
+       /* Create the rpc request RPC_HDR_REQ */
+       init_rpc_hdr_req(&hdr_req, alloc_hint, op_num);
+
+       /* stream-time... */
+       if(!smb_io_rpc_hdr("hdr    ", &hdr, rpc_out, 0))
+               return 0;
+
+       if(!smb_io_rpc_hdr_req("hdr_req", &hdr_req, rpc_out, 0))
+               return 0;
+
+       if (prs_offset(rpc_out) != RPC_HEADER_LEN + RPC_HDR_REQ_LEN)
+               return 0;
+
+       return callid;
+}
+
+/*******************************************************************
+ Puts an auth header into an rpc request.
+ ********************************************************************/
+
+static BOOL create_auth_hdr(prs_struct *outgoing_packet, BOOL auth_verify)
+{
+       RPC_HDR_AUTH hdr_auth;
+
+       init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE,
+                         NTLMSSP_AUTH_LEVEL, 0x08, 
+                         (auth_verify ? 1 : 0));
+       if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, 
+                               outgoing_packet, 0)) {
+               DEBUG(0,("create_auth_hdr:Failed to marshal RPC_HDR_AUTH.\n"));
+               return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Puts auth data into an rpc request.
+ ********************************************************************/
+
+static BOOL create_auth_data(struct cli_state *cli, uint32 crc32, 
+                            prs_struct *outgoing_packet)
+{
+       char *pdata_out = prs_data_p(outgoing_packet);
+       RPC_AUTH_NTLMSSP_CHK chk;
+       uint32 current_offset = prs_offset(outgoing_packet);
+
+       init_rpc_auth_ntlmssp_chk(&chk, NTLMSSP_SIGN_VERSION, 
+                                 crc32, cli->ntlmssp_seq_num++);
+       if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, 
+                                       outgoing_packet, 0)) {
+               DEBUG(0,("create_auth_data: Failed to marshal RPC_AUTH_NTLMSSP_CHK.\n"));
+               return False;
+       }
+       NTLMSSPcalc_ap(cli, (unsigned char*)
+                      &pdata_out[current_offset+4], 
+                      RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+       return True;
+}
+
+/**
+ * Send a request on an RPC pipe and get a response.
+ *
+ * @param data NDR contents of the request to be sent.
+ * @param rdata Unparsed NDR response data.
+**/
+
+BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
+                      prs_struct *data, prs_struct *rdata)
+{
+       uint32 auth_len, max_data, data_left, data_sent;
+       BOOL ret = False;
+       BOOL auth_verify, auth_seal;
+       fstring dump_name;
+
+       auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0);
+       auth_seal   = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0);
+
+       auth_len = (auth_verify ? RPC_AUTH_NTLMSSP_CHK_LEN : 0);
+
+       /*
+        * calc how much actual data we can send in a PDU fragment
+        */
+       max_data = cli->max_xmit_frag - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+               (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len;
+
+       for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) {
+               prs_struct outgoing_packet;
+               uint32 data_len, send_size;
+               uint8 flags = 0;
+               uint32 crc32 = 0;
+               uint32 callid = 0;
+
+               /*
+                * how much will we send this time
+                */
+               send_size = MIN(data_left, max_data);
+               data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + send_size +
+                       (auth_verify ? RPC_HDR_AUTH_LEN : 0) + auth_len;
+
+               /*
+                * Malloc parse struct to hold it (and enough for alignments).
+                */
+               if(!prs_init(&outgoing_packet, data_len + 8, 
+                            cli->mem_ctx, MARSHALL)) {
+                       DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len ));
+                       return False;
+               }
+
+               if (data_left == prs_offset(data))
+                       flags |= RPC_FLG_FIRST;
+
+               if (data_left < max_data)
+                       flags |= RPC_FLG_LAST;
+               /*
+                * Write out the RPC header and the request header.
+                */
+               if(!(callid = create_rpc_request(&outgoing_packet, op_num, 
+                                                data_len, auth_len, flags, 
+                                                callid, data_left))) {
+                       DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n"));
+                       prs_mem_free(&outgoing_packet);
+                       return False;
+               }
+
+               /*
+                * Seal the outgoing data if requested.
+                */
+               if (auth_seal) {
+                       crc32 = crc32_calc_buffer(prs_data_p(data) + data_sent,
+                                                 send_size);
+                       NTLMSSPcalc_ap(cli, (unsigned char*)prs_data_p(data) +
+                                      data_sent, send_size);
+               }
+
+               /*
+                * Now copy the data into the outgoing packet.
+                */
+               if(!prs_append_some_prs_data(&outgoing_packet, data, 
+                                            data_sent, send_size)) {
+                       DEBUG(0,("rpc_api_pipe_req: Failed to append data to outgoing packet.\n"));
+                       prs_mem_free(&outgoing_packet);
+                       return False;
+               }
+
+               /*
+                * Add a trailing auth_verifier if needed.
+                */
+               if (auth_seal || auth_verify) {
+                       if(!create_auth_hdr(&outgoing_packet, auth_verify)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+               }
+
+               /*
+                * Finally the auth data itself.
+                */
+               if (auth_verify) {
+                       if (!create_auth_data(cli, crc32, &outgoing_packet)) {
+                               prs_mem_free(&outgoing_packet);
+                               return False;
+                       }
+               }
+
+               DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len, 
+                          prs_offset(&outgoing_packet)));
+               
+               if (flags & RPC_FLG_LAST)
+                       ret = rpc_api_pipe(cli, &outgoing_packet, rdata);
+               else {
+                       cli_write(cli, cli->nt_pipe_fnum, 0x0008,
+                                  prs_data_p(&outgoing_packet),
+                                  data_sent, data_len);
+               }
+               prs_mem_free(&outgoing_packet);
+               data_sent += send_size;
+               data_left -= send_size;
+       }
+       /* Also capture received data */
+       slprintf(dump_name, sizeof(dump_name) - 1, "reply_%s",
+                cli_pipe_get_name(cli));
+       prs_dump(dump_name, op_num, rdata);
+
+       return ret;
+}
+
+/****************************************************************************
+ Set the handle state.
+****************************************************************************/
+
+static BOOL rpc_pipe_set_hnd_state(struct cli_state *cli, const char *pipe_name, uint16 device_state)
+{
+       BOOL state_set = False;
+       char param[2];
+       uint16 setup[2]; /* only need 2 uint16 setup parameters */
+       char *rparam = NULL;
+       char *rdata = NULL;
+       uint32 rparam_len, rdata_len;
+
+       if (pipe_name == NULL)
+               return False;
+
+       DEBUG(5,("Set Handle state Pipe[%x]: %s - device state:%x\n",
+       cli->nt_pipe_fnum, pipe_name, device_state));
+
+       /* create parameters: device state */
+       SSVAL(param, 0, device_state);
+
+       /* create setup parameters. */
+       setup[0] = 0x0001; 
+       setup[1] = cli->nt_pipe_fnum; /* pipe file handle.  got this from an SMBOpenX. */
+
+       /* send the data on \PIPE\ */
+       if (cli_api_pipe(cli, "\\PIPE\\",
+                   setup, 2, 0,                /* setup, length, max */
+                   param, 2, 0,                /* param, length, max */
+                   NULL, 0, 1024,              /* data, length, max */
+                   &rparam, &rparam_len,        /* return param, length */
+                   &rdata, &rdata_len))         /* return data, length */
+       {
+               DEBUG(5, ("Set Handle state: return OK\n"));
+               state_set = True;
+       }
+
+       SAFE_FREE(rparam);
+       SAFE_FREE(rdata);
+
+       return state_set;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+int get_pipe_index( const char *pipe_name )
+{
+       int pipe_idx = 0;
+
+       while (pipe_names[pipe_idx].client_pipe != NULL) {
+               if (strequal(pipe_name, pipe_names[pipe_idx].client_pipe )) 
+                       return pipe_idx;
+               pipe_idx++;
+       };
+
+       return -1;
+}
+
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+const char* get_pipe_name_from_index( const int pipe_index )
+{
+
+       if ( (pipe_index < 0) || (pipe_index >= PI_MAX_PIPES) )
+               return NULL;
+
+       return pipe_names[pipe_index].client_pipe;              
+}
+
+/****************************************************************************
+ Check to see if this pipe index points to one of 
+ the pipes only supported by Win2k
+ ****************************************************************************/
+
+BOOL is_win2k_pipe( const int pipe_idx )
+{
+       switch ( pipe_idx )
+       {
+               case PI_LSARPC_DS:
+                       return True;
+       }
+       
+       return False;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL valid_pipe_name(const int pipe_idx, RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+       if ( pipe_idx >= PI_MAX_PIPES ) {
+               DEBUG(0,("valid_pipe_name: Programmer error!  Invalid pipe index [%d]\n",
+                       pipe_idx));
+               return False;
+       }
+
+       DEBUG(5,("Bind Abstract Syntax: "));    
+       dump_data(5, (char*)&(pipe_names[pipe_idx].abstr_syntax), 
+                 sizeof(pipe_names[pipe_idx].abstr_syntax));
+       DEBUG(5,("Bind Transfer Syntax: "));
+       dump_data(5, (char*)&(pipe_names[pipe_idx].trans_syntax),
+                 sizeof(pipe_names[pipe_idx].trans_syntax));
+
+       /* copy the required syntaxes out so we can do the right bind */
+       
+       *transfer = pipe_names[pipe_idx].trans_syntax;
+       *abstract = pipe_names[pipe_idx].abstr_syntax;
+
+       return True;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const int pipe_idx, RPC_IFACE *transfer)
+{
+       int i = 0;
+
+       if ( hdr_ba->addr.len <= 0)
+               return False;
+               
+       if ( !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].server_pipe )) 
+       {
+               DEBUG(4,("bind_rpc_pipe: pipe_name %s != expected pipe %s.  oh well!\n",
+                        pipe_names[i].server_pipe ,hdr_ba->addr.str));
+               return False;
+       }
+       
+       DEBUG(5,("bind_rpc_pipe: server pipe_name found: %s\n", pipe_names[i].server_pipe ));
+
+       if (pipe_names[pipe_idx].server_pipe == NULL) {
+               DEBUG(2,("bind_rpc_pipe: pipe name %s unsupported\n", hdr_ba->addr.str));
+               return False;
+       }
+
+       /* check the transfer syntax */
+       if ((hdr_ba->transfer.version != transfer->version) ||
+            (memcmp(&hdr_ba->transfer.uuid, &transfer->uuid, sizeof(transfer->uuid)) !=0)) {
+               DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
+               return False;
+       }
+
+       /* lkclXXXX only accept one result: check the result(s) */
+       if (hdr_ba->res.num_results != 0x1 || hdr_ba->res.result != 0) {
+               DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n",
+                         hdr_ba->res.num_results, hdr_ba->res.reason));
+       }
+
+       DEBUG(5,("bind_rpc_pipe: accepted!\n"));
+       return True;
+}
+
+/****************************************************************************
+ Create and send the third packet in an RPC auth.
+****************************************************************************/
+
+static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32 rpc_call_id)
+{
+       RPC_HDR_AUTH rhdr_auth;
+       RPC_AUTH_VERIFIER rhdr_verf;
+       RPC_AUTH_NTLMSSP_CHAL rhdr_chal;
+       char buffer[MAX_PDU_FRAG_LEN];
+       prs_struct rpc_out;
+       ssize_t ret;
+
+       unsigned char p24[24];
+       unsigned char lm_owf[24];
+       unsigned char lm_hash[16];
+
+       if(!smb_io_rpc_hdr_auth("", &rhdr_auth, rdata, 0)) {
+               DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_HDR_AUTH.\n"));
+               return False;
+       }
+       if(!smb_io_rpc_auth_verifier("", &rhdr_verf, rdata, 0)) {
+               DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_AUTH_VERIFIER.\n"));
+               return False;
+       }
+       if(!smb_io_rpc_auth_ntlmssp_chal("", &rhdr_chal, rdata, 0)) {
+               DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_AUTH_NTLMSSP_CHAL.\n"));
+               return False;
+       }
+
+       cli->ntlmssp_cli_flgs = rhdr_chal.neg_flags;
+
+       pwd_make_lm_nt_owf(&cli->pwd, rhdr_chal.challenge);
+
+       prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL);
+
+       prs_give_memory( &rpc_out, buffer, sizeof(buffer), False);
+
+       create_rpc_bind_resp(&cli->pwd, cli->domain,
+                            cli->user_name, lp_netbios_name(), 
+                            cli->ntlmssp_cli_flgs, rpc_call_id,
+                            &rpc_out);
+                                           
+       pwd_get_lm_nt_owf(&cli->pwd, lm_owf, NULL);
+       pwd_get_lm_nt_16(&cli->pwd, lm_hash, NULL);
+
+       NTLMSSPOWFencrypt(lm_hash, lm_owf, p24);
+
+       {
+               unsigned char j = 0;
+               int ind;
+               unsigned char k2[8];
+
+               memcpy(k2, p24, 5);
+               k2[5] = 0xe5;
+               k2[6] = 0x38;
+               k2[7] = 0xb0;
+
+               for (ind = 0; ind < 256; ind++)
+                       cli->ntlmssp_hash[ind] = (unsigned char)ind;
+
+               for( ind = 0; ind < 256; ind++) {
+                       unsigned char tc;
+
+                       j += (cli->ntlmssp_hash[ind] + k2[ind%8]);
+
+                       tc = cli->ntlmssp_hash[ind];
+                       cli->ntlmssp_hash[ind] = cli->ntlmssp_hash[j];
+                       cli->ntlmssp_hash[j] = tc;
+               }
+
+               cli->ntlmssp_hash[256] = 0;
+               cli->ntlmssp_hash[257] = 0;
+       }
+
+       memset((char *)lm_hash, '\0', sizeof(lm_hash));
+
+       if ((ret = cli_write(cli, cli->nt_pipe_fnum, 0x8, prs_data_p(&rpc_out), 
+                       0, (size_t)prs_offset(&rpc_out))) != (ssize_t)prs_offset(&rpc_out)) {
+               DEBUG(0,("rpc_send_auth_reply: cli_write failed. Return was %d\n", (int)ret));
+               return False;
+       }
+
+       cli->ntlmssp_srv_flgs = rhdr_chal.neg_flags;
+       return True;
+}
+
+/****************************************************************************
+ Do an rpc bind.
+****************************************************************************/
+
+BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const char *my_name)
+{
+       RPC_IFACE abstract;
+       RPC_IFACE transfer;
+       prs_struct rpc_out;
+       prs_struct rdata;
+       BOOL do_auth = (cli->ntlmssp_cli_flgs != 0);
+       uint32 rpc_call_id;
+       char buffer[MAX_PDU_FRAG_LEN];
+
+       if ( (pipe_idx < 0) || (pipe_idx >= PI_MAX_PIPES) )
+               return False;
+
+       DEBUG(5,("Bind RPC Pipe[%x]: %s\n", cli->nt_pipe_fnum, pipe_names[pipe_idx].client_pipe));
+
+       if (!valid_pipe_name(pipe_idx, &abstract, &transfer))
+               return False;
+
+       prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL);
+
+       /*
+        * Use the MAX_PDU_FRAG_LEN buffer to store the bind request.
+        */
+
+       prs_give_memory( &rpc_out, buffer, sizeof(buffer), False);
+
+       rpc_call_id = get_rpc_call_id();
+
+       /* Marshall the outgoing data. */
+       create_rpc_bind_req(&rpc_out, do_auth, rpc_call_id,
+                           &abstract, &transfer,
+                           lp_netbios_name(), cli->domain, cli->ntlmssp_cli_flgs);
+
+       /* Initialize the incoming data struct. */
+       prs_init(&rdata, 0, cli->mem_ctx, UNMARSHALL);
+
+       /* send data on \PIPE\.  receive a response */
+       if (rpc_api_pipe(cli, &rpc_out, &rdata)) {
+               RPC_HDR_BA   hdr_ba;
+
+               DEBUG(5, ("rpc_pipe_bind: rpc_api_pipe returned OK.\n"));
+
+               if(!smb_io_rpc_hdr_ba("", &hdr_ba, &rdata, 0)) {
+                       DEBUG(0,("rpc_pipe_bind: Failed to unmarshall RPC_HDR_BA.\n"));
+                       prs_mem_free(&rdata);
+                       return False;
+               }
+
+               if(!check_bind_response(&hdr_ba, pipe_idx, &transfer)) {
+                       DEBUG(2,("rpc_pipe_bind: check_bind_response failed.\n"));
+                       prs_mem_free(&rdata);
+                       return False;
+               }
+
+               cli->max_xmit_frag = hdr_ba.bba.max_tsize;
+               cli->max_recv_frag = hdr_ba.bba.max_rsize;
+
+               /*
+                * If we're doing NTLMSSP auth we need to send a reply to
+                * the bind-ack to complete the 3-way challenge response
+                * handshake.
+                */
+
+               if (do_auth && !rpc_send_auth_reply(cli, &rdata, rpc_call_id)) {
+                       DEBUG(0,("rpc_pipe_bind: rpc_send_auth_reply failed.\n"));
+                       prs_mem_free(&rdata);
+                       return False;
+               }
+       }
+
+       prs_mem_free(&rdata);
+       return True;
+}
+
+/****************************************************************************
+ Open a session.
+ ****************************************************************************/
+
+BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
+{
+       int fnum;
+
+       /* At the moment we can't have more than one pipe open over
+           a cli connection. )-: */
+
+       SMB_ASSERT(cli->nt_pipe_fnum == 0);
+       
+       /* The pipe index must fall within our array */
+
+       SMB_ASSERT((pipe_idx >= 0) && (pipe_idx < PI_MAX_PIPES));
+
+       if (cli->capabilities & CAP_NT_SMBS) {
+               if ((fnum = cli_nt_create(cli, &pipe_names[pipe_idx].client_pipe[5], DESIRED_ACCESS_PIPE)) == -1) {
+                       DEBUG(0,("cli_nt_session_open: cli_nt_create failed on pipe %s to machine %s.  Error was %s\n",
+                                &pipe_names[pipe_idx].client_pipe[5], cli->desthost, cli_errstr(cli)));
+                       return False;
+               }
+
+               cli->nt_pipe_fnum = (uint16)fnum;
+       } else {
+               if ((fnum = cli_open(cli, pipe_names[pipe_idx].client_pipe, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+                       DEBUG(0,("cli_nt_session_open: cli_open failed on pipe %s to machine %s.  Error was %s\n",
+                                pipe_names[pipe_idx].client_pipe, cli->desthost, cli_errstr(cli)));
+                       return False;
+               }
+
+               cli->nt_pipe_fnum = (uint16)fnum;
+
+               /**************** Set Named Pipe State ***************/
+               if (!rpc_pipe_set_hnd_state(cli, pipe_names[pipe_idx].client_pipe, 0x4300)) {
+                       DEBUG(0,("cli_nt_session_open: pipe hnd state failed.  Error was %s\n",
+                                 cli_errstr(cli)));
+                       cli_close(cli, cli->nt_pipe_fnum);
+                       return False;
+               }
+       }
+
+       /******************* bind request on pipe *****************/
+
+       if (!rpc_pipe_bind(cli, pipe_idx, lp_netbios_name())) {
+               DEBUG(2,("cli_nt_session_open: rpc bind to %s failed\n",
+                        get_pipe_name_from_index(pipe_idx)));
+               cli_close(cli, cli->nt_pipe_fnum);
+               return False;
+       }
+
+       /* 
+        * Setup the remote server name prefixed by \ and the machine account name.
+        */
+
+       fstrcpy(cli->srv_name_slash, "\\\\");
+       fstrcat(cli->srv_name_slash, cli->desthost);
+       strupper(cli->srv_name_slash);
+
+       fstrcpy(cli->clnt_name_slash, "\\\\");
+       fstrcat(cli->clnt_name_slash, lp_netbios_name());
+       strupper(cli->clnt_name_slash);
+
+       fstrcpy(cli->mach_acct, lp_netbios_name());
+       fstrcat(cli->mach_acct, "$");
+       strupper(cli->mach_acct);
+
+       /* Remember which pipe we're talking to */
+       fstrcpy(cli->pipe_name, pipe_names[pipe_idx].client_pipe);
+
+       return True;
+}
+
+
+const char *cli_pipe_get_name(struct cli_state *cli)
+{
+       return cli->pipe_name;
+}
+
+
+/****************************************************************************
+close the session
+****************************************************************************/
+
+void cli_nt_session_close(struct cli_state *cli)
+{
+       cli_close(cli, cli->nt_pipe_fnum);
+       cli->nt_pipe_fnum = 0;
+}
diff --git a/source4/rpc_client/cli_reg.c b/source4/rpc_client/cli_reg.c
new file mode 100644 (file)
index 0000000..5cfbf68
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC Pipe client
+   Copyright (C) Andrew Tridgell              1992-1998,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+   Copyright (C) Paul Ashton                  1997-1998.
+   Copyright (C) Jeremy Allison                    1999.
+   Copyright (C) Simo Sorce                        2001
+   
+   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"
+
+/* Shutdown a server */
+
+NTSTATUS cli_reg_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx,
+                          const char *msg, uint32 timeout, BOOL do_reboot,
+                         BOOL force)
+{
+       prs_struct qbuf;
+       prs_struct rbuf; 
+       REG_Q_SHUTDOWN q_s;
+       REG_R_SHUTDOWN r_s;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       if (msg == NULL) return NT_STATUS_INVALID_PARAMETER;
+
+       ZERO_STRUCT (q_s);
+       ZERO_STRUCT (r_s);
+
+       prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_reg_q_shutdown(&q_s, msg, timeout, do_reboot, force);
+
+       if (!reg_io_q_shutdown("", &q_s, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, REG_SHUTDOWN, &qbuf, &rbuf))
+               goto done;
+       
+       /* Unmarshall response */
+       
+       if(reg_io_r_shutdown("", &r_s, &rbuf, 0))
+               result = r_s.status;
+
+done:
+       prs_mem_free(&rbuf);
+       prs_mem_free(&qbuf);
+
+       return result;
+}
+
+
+/* Abort a server shutdown */
+
+NTSTATUS cli_reg_abort_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx)
+{
+       prs_struct rbuf;
+       prs_struct qbuf; 
+       REG_Q_ABORT_SHUTDOWN q_s;
+       REG_R_ABORT_SHUTDOWN r_s;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT (q_s);
+       ZERO_STRUCT (r_s);
+
+       prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+       
+       /* Marshall data and send request */
+
+       init_reg_q_abort_shutdown(&q_s);
+
+       if (!reg_io_q_abort_shutdown("", &q_s, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, REG_ABORT_SHUTDOWN, &qbuf, &rbuf))
+               goto done;
+       
+               /* Unmarshall response */
+       
+       if (reg_io_r_abort_shutdown("", &r_s, &rbuf, 0))
+               result = r_s.status;
+
+done:
+       prs_mem_free(&rbuf);
+       prs_mem_free(&qbuf );
+
+       return result;
+}
diff --git a/source4/rpc_client/cli_samr.c b/source4/rpc_client/cli_samr.c
new file mode 100644 (file)
index 0000000..edfdb38
--- /dev/null
@@ -0,0 +1,1445 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+   Copyright (C) Tim Potter                        2000-2001,
+   Copyright (C) Andrew Tridgell              1992-1997,2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+   Copyright (C) Paul Ashton                       1997,2000,
+   Copyright (C) Elrond                                 2000,
+   Copyright (C) Rafal Szczesniak                       2002.
+   
+   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"
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                          uint32 access_mask, POLICY_HND *connect_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_CONNECT q;
+       SAMR_R_CONNECT r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_connect(&q, cli->desthost, access_mask);
+
+       if (!samr_io_q_connect("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_CONNECT, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_connect("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *connect_pol = r.connect_pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect4(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                          uint32 access_mask, POLICY_HND *connect_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_CONNECT4 q;
+       SAMR_R_CONNECT4 r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_connect4(&q, cli->desthost, access_mask);
+
+       if (!samr_io_q_connect4("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_CONNECT4, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_connect4("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *connect_pol = r.connect_pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Close SAMR handle */
+
+NTSTATUS cli_samr_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                        POLICY_HND *connect_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_CLOSE_HND q;
+       SAMR_R_CLOSE_HND r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_close_hnd(&q, connect_pol);
+
+       if (!samr_io_q_close_hnd("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_CLOSE_HND, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_close_hnd("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *connect_pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Open handle on a domain */
+
+NTSTATUS cli_samr_open_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                              POLICY_HND *connect_pol, uint32 access_mask, 
+                              const DOM_SID *domain_sid, POLICY_HND *domain_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_OPEN_DOMAIN q;
+       SAMR_R_OPEN_DOMAIN r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_open_domain(&q, connect_pol, access_mask, domain_sid);
+
+       if (!samr_io_q_open_domain("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_OPEN_DOMAIN, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_open_domain("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *domain_pol = r.domain_pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Open handle on a user */
+
+NTSTATUS cli_samr_open_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                            POLICY_HND *domain_pol, uint32 access_mask, 
+                            uint32 user_rid, POLICY_HND *user_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_OPEN_USER q;
+       SAMR_R_OPEN_USER r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_open_user(&q, domain_pol, access_mask, user_rid);
+
+       if (!samr_io_q_open_user("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_OPEN_USER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_open_user("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *user_pol = r.user_pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Open handle on a group */
+
+NTSTATUS cli_samr_open_group(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                             POLICY_HND *domain_pol, uint32 access_mask, 
+                             uint32 group_rid, POLICY_HND *group_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_OPEN_GROUP q;
+       SAMR_R_OPEN_GROUP r;
+       NTSTATUS result =  NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_open_group(&q, domain_pol, access_mask, group_rid);
+
+       if (!samr_io_q_open_group("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_OPEN_GROUP, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_open_group("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *group_pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query user info */
+
+NTSTATUS cli_samr_query_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *user_pol, uint16 switch_value, 
+                                 SAM_USERINFO_CTR **ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_USERINFO q;
+       SAMR_R_QUERY_USERINFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_userinfo(&q, user_pol, switch_value);
+
+       if (!samr_io_q_query_userinfo("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_USERINFO, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_userinfo("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+       *ctr = r.ctr;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query group info */
+
+NTSTATUS cli_samr_query_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                  POLICY_HND *group_pol, uint32 info_level, 
+                                  GROUP_INFO_CTR **ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_GROUPINFO q;
+       SAMR_R_QUERY_GROUPINFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_groupinfo(&q, group_pol, info_level);
+
+       if (!samr_io_q_query_groupinfo("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPINFO, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_groupinfo("", &r, &rbuf, 0))
+               goto done;
+
+       *ctr = r.ctr;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_usergroups(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                   POLICY_HND *user_pol, uint32 *num_groups, 
+                                   DOM_GID **gid)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_USERGROUPS q;
+       SAMR_R_QUERY_USERGROUPS r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_usergroups(&q, user_pol);
+
+       if (!samr_io_q_query_usergroups("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_USERGROUPS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_usergroups("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *num_groups = r.num_entries;
+               *gid = r.gid;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query user aliases */
+
+NTSTATUS cli_samr_query_useraliases(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                   POLICY_HND *user_pol, uint32 num_sids, DOM_SID2 *sid,
+                                  uint32 *num_aliases, uint32 **als_rids)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_USERALIASES q;
+       SAMR_R_QUERY_USERALIASES r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       unsigned int ptr=1;
+       
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_useraliases(&q, user_pol, num_sids, &ptr, sid);
+
+       if (!samr_io_q_query_useraliases("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_USERALIASES, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_useraliases("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *num_aliases = r.num_entries;
+               *als_rids = r.rid;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *group_pol, uint32 *num_mem, 
+                                 uint32 **rid, uint32 **attr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_GROUPMEM q;
+       SAMR_R_QUERY_GROUPMEM r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_groupmem(&q, group_pol);
+
+       if (!samr_io_q_query_groupmem("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPMEM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_groupmem("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *num_mem = r.num_entries;
+               *rid = r.rid;
+               *attr = r.attr;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/**
+ * Enumerate domain users
+ *
+ * @param cli client state structure
+ * @param mem_ctx talloc context
+ * @param pol opened domain policy handle
+ * @param start_idx starting index of enumeration, returns context for
+                    next enumeration
+ * @param acb_mask account control bit mask (to enumerate some particular
+ *                 kind of accounts)
+ * @param size max acceptable size of response
+ * @param dom_users returned array of domain user names
+ * @param rids returned array of domain user RIDs
+ * @param num_dom_users numer returned entries
+ * 
+ * @return NTSTATUS returned in rpc response
+ **/
+NTSTATUS cli_samr_enum_dom_users(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *pol, uint32 *start_idx, uint16 acb_mask,
+                                 uint32 size, char ***dom_users, uint32 **rids,
+                                 uint32 *num_dom_users)
+{
+       prs_struct qbuf;
+       prs_struct rbuf;
+       SAMR_Q_ENUM_DOM_USERS q;
+       SAMR_R_ENUM_DOM_USERS r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       int i;
+       
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+       
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+       
+       /* Fill query structure with parameters */
+
+       init_samr_q_enum_dom_users(&q, pol, *start_idx, acb_mask, 0, size);
+       
+       if (!samr_io_q_enum_dom_users("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_USERS, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* unpack received stream */
+
+       if(!samr_io_r_enum_dom_users("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+               goto done;
+       
+       *start_idx = r.next_idx;
+       *num_dom_users = r.num_entries2;
+
+       if (r.num_entries2) {
+               /* allocate memory needed to return received data */    
+               *rids = (uint32*)talloc(mem_ctx, sizeof(uint32) * r.num_entries2);
+               if (!*rids) {
+                       DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+               
+               *dom_users = (char**)talloc(mem_ctx, sizeof(char*) * r.num_entries2);
+               if (!*dom_users) {
+                       DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+               
+               /* fill output buffers with rpc response */
+               for (i = 0; i < r.num_entries2; i++) {
+                       fstring conv_buf;
+                       
+                       (*rids)[i] = r.sam[i].rid;
+                       unistr2_to_ascii(conv_buf, &(r.uni_acct_name[i]), sizeof(conv_buf) - 1);
+                       (*dom_users)[i] = talloc_strdup(mem_ctx, conv_buf);
+               }
+       }
+       
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+       
+       return result;
+};
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_dom_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                  POLICY_HND *pol, uint32 *start_idx, 
+                                  uint32 size, struct acct_info **dom_groups,
+                                  uint32 *num_dom_groups)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_ENUM_DOM_GROUPS q;
+       SAMR_R_ENUM_DOM_GROUPS r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 name_idx, i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_enum_dom_groups(&q, pol, *start_idx, size);
+
+       if (!samr_io_q_enum_dom_groups("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_GROUPS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_enum_dom_groups("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+               goto done;
+
+       *num_dom_groups = r.num_entries2;
+
+       if (!((*dom_groups) = (struct acct_info *)
+             talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       memset(*dom_groups, 0, sizeof(struct acct_info) * *num_dom_groups);
+
+       name_idx = 0;
+
+       for (i = 0; i < *num_dom_groups; i++) {
+
+               (*dom_groups)[i].rid = r.sam[i].rid;
+
+               if (r.sam[i].hdr_name.buffer) {
+                       unistr2_to_ascii((*dom_groups)[i].acct_name,
+                                        &r.uni_grp_name[name_idx],
+                                        sizeof(fstring) - 1);
+                       name_idx++;
+               }
+
+               *start_idx = r.next_idx;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_als_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                  POLICY_HND *pol, uint32 *start_idx, 
+                                  uint32 size, struct acct_info **dom_groups,
+                                  uint32 *num_dom_groups)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_ENUM_DOM_ALIASES q;
+       SAMR_R_ENUM_DOM_ALIASES r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 name_idx, i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_enum_dom_aliases(&q, pol, *start_idx, size);
+
+       if (!samr_io_q_enum_dom_aliases("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_ALIASES, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_enum_dom_aliases("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+               goto done;
+       }
+
+       *num_dom_groups = r.num_entries2;
+
+       if (!((*dom_groups) = (struct acct_info *)
+             talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       memset(*dom_groups, 0, sizeof(struct acct_info) * *num_dom_groups);
+
+       name_idx = 0;
+
+       for (i = 0; i < *num_dom_groups; i++) {
+
+               (*dom_groups)[i].rid = r.sam[i].rid;
+
+               if (r.sam[i].hdr_name.buffer) {
+                       unistr2_to_ascii((*dom_groups)[i].acct_name,
+                                        &r.uni_grp_name[name_idx],
+                                        sizeof(fstring) - 1);
+                       name_idx++;
+               }
+
+               *start_idx = r.next_idx;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query alias members */
+
+NTSTATUS cli_samr_query_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *alias_pol, uint32 *num_mem, 
+                                 DOM_SID **sids)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_ALIASMEM q;
+       SAMR_R_QUERY_ALIASMEM r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_aliasmem(&q, alias_pol);
+
+       if (!samr_io_q_query_aliasmem("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASMEM, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_aliasmem("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       *num_mem = r.num_sids;
+
+       if (!(*sids = talloc(mem_ctx, sizeof(DOM_SID) * *num_mem))) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       for (i = 0; i < *num_mem; i++) {
+               (*sids)[i] = r.sid[i].sid;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Open handle on an alias */
+
+NTSTATUS cli_samr_open_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                             POLICY_HND *domain_pol, uint32 access_mask, 
+                             uint32 alias_rid, POLICY_HND *alias_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_OPEN_ALIAS q;
+       SAMR_R_OPEN_ALIAS r;
+       NTSTATUS result;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_open_alias(&q, domain_pol, access_mask, alias_rid);
+
+       if (!samr_io_q_open_alias("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_OPEN_ALIAS, &qbuf, &rbuf)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_open_alias("", &r, &rbuf, 0)) {
+               result = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (NT_STATUS_IS_OK(result = r.status)) {
+               *alias_pol = r.pol;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query domain info */
+
+NTSTATUS cli_samr_query_dom_info(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                 POLICY_HND *domain_pol, uint16 switch_value,
+                                 SAM_UNK_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_DOMAIN_INFO q;
+       SAMR_R_QUERY_DOMAIN_INFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_dom_info(&q, domain_pol, switch_value);
+
+       if (!samr_io_q_query_dom_info("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_DOMAIN_INFO, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       r.ctr = ctr;
+
+       if (!samr_io_r_query_dom_info("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* This function returns the bizzare set of (max_entries, max_size) required
+   for the QueryDisplayInfo RPC to actually work against a domain controller
+   with large (10k and higher) numbers of users.  These values were 
+   obtained by inspection using ethereal and NT4 running User Manager. */
+
+void get_query_dispinfo_params(int loop_count, uint32 *max_entries,
+                              uint32 *max_size)
+{
+       switch(loop_count) {
+       case 0:
+               *max_entries = 512;
+               *max_size = 16383;
+               break;
+       case 1:
+               *max_entries = 1024;
+               *max_size = 32766;
+               break;
+       case 2:
+               *max_entries = 2048;
+               *max_size = 65532;
+               break;
+       case 3:
+               *max_entries = 4096;
+               *max_size = 131064;
+               break;
+       default:              /* loop_count >= 4 */
+               *max_entries = 4096;
+               *max_size = 131071;
+               break;
+       }
+}                   
+
+/* Query display info */
+
+NTSTATUS cli_samr_query_dispinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                 POLICY_HND *domain_pol, uint32 *start_idx,
+                                 uint16 switch_value, uint32 *num_entries,
+                                 uint32 max_entries, uint32 max_size,
+                                SAM_DISPINFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_DISPINFO q;
+       SAMR_R_QUERY_DISPINFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_dispinfo(&q, domain_pol, switch_value,
+                                  *start_idx, max_entries, max_size);
+
+       if (!samr_io_q_query_dispinfo("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_DISPINFO, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       r.ctr = ctr;
+
+       if (!samr_io_r_query_dispinfo("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+        result = r.status;
+
+       if (!NT_STATUS_IS_OK(result) &&
+           NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+               goto done;
+       }
+
+       *num_entries = r.num_entries;
+       *start_idx += r.num_entries;  /* No next_idx in this structure! */
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Lookup rids.  Note that NT4 seems to crash if more than ~1000 rids are
+   looked up in one packet. */
+
+NTSTATUS cli_samr_lookup_rids(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                              POLICY_HND *domain_pol, uint32 flags,
+                              uint32 num_rids, uint32 *rids, 
+                              uint32 *num_names, char ***names,
+                              uint32 **name_types)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_LOOKUP_RIDS q;
+       SAMR_R_LOOKUP_RIDS r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 i;
+
+        if (num_rids > 1000) {
+                DEBUG(2, ("cli_samr_lookup_rids: warning: NT4 can crash if "
+                          "more than ~1000 rids are looked up at once.\n"));
+        }
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_lookup_rids(mem_ctx, &q, domain_pol, flags,
+                               num_rids, rids);
+
+       if (!samr_io_q_lookup_rids("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_LOOKUP_RIDS, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_lookup_rids("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (r.num_names1 == 0) {
+               *num_names = 0;
+               *names = NULL;
+               goto done;
+       }
+
+       *num_names = r.num_names1;
+       *names = talloc(mem_ctx, sizeof(char *) * r.num_names1);
+       *name_types = talloc(mem_ctx, sizeof(uint32) * r.num_names1);
+
+       for (i = 0; i < r.num_names1; i++) {
+               fstring tmp;
+
+               unistr2_to_ascii(tmp, &r.uni_name[i], sizeof(tmp) - 1);
+               (*names)[i] = talloc_strdup(mem_ctx, tmp);
+               (*name_types)[i] = r.type[i];
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Lookup names */
+
+NTSTATUS cli_samr_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                               POLICY_HND *domain_pol, uint32 flags,
+                               uint32 num_names, const char **names,
+                               uint32 *num_rids, uint32 **rids,
+                               uint32 **rid_types)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_LOOKUP_NAMES q;
+       SAMR_R_LOOKUP_NAMES r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_lookup_names(mem_ctx, &q, domain_pol, flags,
+                                num_names, names);
+
+       if (!samr_io_q_lookup_names("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_LOOKUP_NAMES, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_lookup_names("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (r.num_rids1 == 0) {
+               *num_rids = 0;
+               goto done;
+       }
+
+       *num_rids = r.num_rids1;
+       *rids = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+       *rid_types = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+
+       for (i = 0; i < r.num_rids1; i++) {
+               (*rids)[i] = r.rids[i];
+               (*rid_types)[i] = r.types[i];
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Create a domain user */
+
+NTSTATUS cli_samr_create_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                  POLICY_HND *domain_pol, const char *acct_name,
+                                  uint32 acb_info, uint32 unknown, 
+                                  POLICY_HND *user_pol, uint32 *rid)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_CREATE_USER q;
+       SAMR_R_CREATE_USER r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_create_user(&q, domain_pol, acct_name, acb_info, unknown);
+
+       if (!samr_io_q_create_user("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_CREATE_USER, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_create_user("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+       if (user_pol)
+               *user_pol = r.user_pol;
+
+       if (rid)
+               *rid = r.user_rid;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Set userinfo */
+
+NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                               POLICY_HND *user_pol, uint16 switch_value,
+                               uchar sess_key[16], SAM_USERINFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_SET_USERINFO q;
+       SAMR_R_SET_USERINFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       q.ctr = ctr;
+
+       init_samr_q_set_userinfo(&q, user_pol, sess_key, switch_value, 
+                                ctr->info.id);
+
+       if (!samr_io_q_set_userinfo("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_SET_USERINFO, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_set_userinfo("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Set userinfo2 */
+
+NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                POLICY_HND *user_pol, uint16 switch_value,
+                                uchar sess_key[16], SAM_USERINFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_SET_USERINFO2 q;
+       SAMR_R_SET_USERINFO2 r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_set_userinfo2(&q, user_pol, sess_key, switch_value, ctr);
+
+       if (!samr_io_q_set_userinfo2("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_SET_USERINFO2, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_set_userinfo2("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       if (!NT_STATUS_IS_OK(result = r.status)) {
+               goto done;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Delete domain user */
+
+NTSTATUS cli_samr_delete_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                  POLICY_HND *user_pol)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_DELETE_DOM_USER q;
+       SAMR_R_DELETE_DOM_USER r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_delete_dom_user(&q, user_pol);
+
+       if (!samr_io_q_delete_dom_user("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_DELETE_DOM_USER, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_delete_dom_user("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Query user security object */
+
+NTSTATUS cli_samr_query_sec_obj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *user_pol, uint16 switch_value, 
+                                 TALLOC_CTX *ctx, SEC_DESC_BUF **sec_desc_buf)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_QUERY_SEC_OBJ q;
+       SAMR_R_QUERY_SEC_OBJ r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_query_sec_obj(&q, user_pol, switch_value);
+
+       if (!samr_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_QUERY_SEC_OBJECT, &qbuf, &rbuf)) {
+               goto done;
+       }
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+               goto done;
+       }
+
+       /* Return output parameters */
+
+       result = r.status;
+       *sec_desc_buf=dup_sec_desc_buf(ctx, r.buf);
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Get domain password info */
+
+NTSTATUS cli_samr_get_dom_pwinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                uint16 *unk_0, uint16 *unk_1, uint16 *unk_2)
+{
+       prs_struct qbuf, rbuf;
+       SAMR_Q_GET_DOM_PWINFO q;
+       SAMR_R_GET_DOM_PWINFO r;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Marshall data and send request */
+
+       init_samr_q_get_dom_pwinfo(&q, cli->desthost);
+
+       if (!samr_io_q_get_dom_pwinfo("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SAMR_GET_DOM_PWINFO, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!samr_io_r_get_dom_pwinfo("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (NT_STATUS_IS_OK(result)) {
+               if (unk_0)
+                       *unk_0 = r.unk_0;
+               if (unk_1)
+                       *unk_1 = r.unk_1;
+               if (unk_2)
+                       *unk_2 = r.unk_2;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
diff --git a/source4/rpc_client/cli_spoolss.c b/source4/rpc_client/cli_spoolss.c
new file mode 100644 (file)
index 0000000..bb6ce1b
--- /dev/null
@@ -0,0 +1,2466 @@
+/*
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Gerald Carter                2001-2002,
+   Copyright (C) Tim Potter                   2000-2002,
+   Copyright (C) Andrew Tridgell              1994-2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+   Copyright (C) Jean-Francois Micouleau      1999-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"
+
+/** @defgroup spoolss SPOOLSS - NT printing routines
+ *  @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**********************************************************************
+ Initialize a new spoolss buff for use by a client rpc
+**********************************************************************/
+static void init_buffer(NEW_BUFFER *buffer, uint32 size, TALLOC_CTX *ctx)
+{
+       buffer->ptr = (size != 0);
+       buffer->size = size;
+       buffer->string_at_end = size;
+       prs_init(&buffer->prs, size, ctx, MARSHALL);
+       buffer->struct_start = prs_offset(&buffer->prs);
+}
+
+/*********************************************************************
+ Decode various spoolss rpc's and info levels
+ ********************************************************************/
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_0(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+                               uint32 returned, PRINTER_INFO_0 **info)
+{
+        uint32 i;
+        PRINTER_INFO_0  *inf;
+
+        inf=(PRINTER_INFO_0 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_0));
+       memset(inf, 0, returned*sizeof(PRINTER_INFO_0));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_printer_info_0("", buffer, &inf[i], 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+                               uint32 returned, PRINTER_INFO_1 **info)
+{
+        uint32 i;
+        PRINTER_INFO_1  *inf;
+
+        inf=(PRINTER_INFO_1 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_1));
+       memset(inf, 0, returned*sizeof(PRINTER_INFO_1));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_printer_info_1("", buffer, &inf[i], 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                               uint32 returned, PRINTER_INFO_2 **info)
+{
+        uint32 i;
+        PRINTER_INFO_2  *inf;
+
+        inf=(PRINTER_INFO_2 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_2));
+       memset(inf, 0, returned*sizeof(PRINTER_INFO_2));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+               /* a little initialization as we go */
+               inf[i].secdesc = NULL;
+                smb_io_printer_info_2("", buffer, &inf[i], 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                               uint32 returned, PRINTER_INFO_3 **info)
+{
+        uint32 i;
+        PRINTER_INFO_3  *inf;
+
+        inf=(PRINTER_INFO_3 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_3));
+       memset(inf, 0, returned*sizeof(PRINTER_INFO_3));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+               inf[i].secdesc = NULL;
+                smb_io_printer_info_3("", buffer, &inf[i], 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                       uint32 returned, PORT_INFO_1 **info)
+{
+        uint32 i;
+        PORT_INFO_1 *inf;
+
+        inf=(PORT_INFO_1*)talloc(mem_ctx, returned*sizeof(PORT_INFO_1));
+       memset(inf, 0, returned*sizeof(PORT_INFO_1));
+
+        prs_set_offset(&buffer->prs, 0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_port_info_1("", buffer, &(inf[i]), 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                       uint32 returned, PORT_INFO_2 **info)
+{
+        uint32 i;
+        PORT_INFO_2 *inf;
+
+        inf=(PORT_INFO_2*)talloc(mem_ctx, returned*sizeof(PORT_INFO_2));
+       memset(inf, 0, returned*sizeof(PORT_INFO_2));
+
+        prs_set_offset(&buffer->prs, 0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_port_info_2("", buffer, &(inf[i]), 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                       uint32 returned, DRIVER_INFO_1 **info)
+{
+        uint32 i;
+        DRIVER_INFO_1 *inf;
+
+        inf=(DRIVER_INFO_1 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_1));
+       memset(inf, 0, returned*sizeof(DRIVER_INFO_1));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_printer_driver_info_1("", buffer, &(inf[i]), 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                       uint32 returned, DRIVER_INFO_2 **info)
+{
+        uint32 i;
+        DRIVER_INFO_2 *inf;
+
+        inf=(DRIVER_INFO_2 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_2));
+       memset(inf, 0, returned*sizeof(DRIVER_INFO_2));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_printer_driver_info_2("", buffer, &(inf[i]), 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                       uint32 returned, DRIVER_INFO_3 **info)
+{
+        uint32 i;
+        DRIVER_INFO_3 *inf;
+
+        inf=(DRIVER_INFO_3 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_3));
+       memset(inf, 0, returned*sizeof(DRIVER_INFO_3));
+
+       prs_set_offset(&buffer->prs,0);
+
+        for (i=0; i<returned; i++) {
+                smb_io_printer_driver_info_3("", buffer, &(inf[i]), 0);
+        }
+
+        *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printerdriverdir_1 (TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+                       uint32 returned, DRIVER_DIRECTORY_1 **info
+)
+{
+       DRIVER_DIRECTORY_1 *inf;
+        inf=(DRIVER_DIRECTORY_1 *)talloc(mem_ctx, sizeof(DRIVER_DIRECTORY_1));
+       memset(inf, 0, sizeof(DRIVER_DIRECTORY_1));
+
+        prs_set_offset(&buffer->prs, 0);
+
+        smb_io_driverdir_1("", buffer, inf, 0);
+       *info=inf;
+}
+
+/** Return a handle to the specified printer or print server.
+ *
+ * @param cli              Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param printername      The name of the printer or print server to be
+ * opened in UNC format.
+ *
+ * @param datatype         Specifies the default data type for the printer.
+ *
+ * @param access_required  The access rights requested on the printer or
+ * print server.
+ *
+ * @param station          The UNC name of the requesting workstation.
+ *
+ * @param username         The name of the user requesting the open.
+ *
+ * @param pol              Returned policy handle.
+ */
+
+/*********************************************************************************
+ Win32 API - OpenPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_open_printer_ex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               const char *printername, const char *datatype, uint32 access_required,
+                               const char *station, const char *username, POLICY_HND *pol)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_OPEN_PRINTER_EX q;
+       SPOOL_R_OPEN_PRINTER_EX r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_open_printer_ex(&q, printername, datatype,
+                                       access_required, station, username);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_open_printer_ex("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_OPENPRINTEREX, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_open_printer_ex("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (W_ERROR_IS_OK(result))
+               *pol = r.handle;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Close a printer handle
+ *
+ * @param cli              Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param pol              Policy handle of printer or print server to close.
+ */
+/*********************************************************************************
+ Win32 API - ClosePrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                POLICY_HND *pol)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_CLOSEPRINTER q;
+       SPOOL_R_CLOSEPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_closeprinter(&q, pol);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_closeprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_CLOSEPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_closeprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (W_ERROR_IS_OK(result))
+               *pol = r.handle;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Enumerate printers on a print server.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param offered          Buffer size offered in the request.
+ * @param needed           Number of bytes needed to complete the request.
+ *                         may be NULL.
+ *
+ * @param flags            Selected from PRINTER_ENUM_* flags.
+ * @param level            Request information level.
+ *
+ * @param num_printers     Pointer to number of printers returned.  May be
+ *                         NULL.
+ * @param ctr              Return structure for printer information.  May
+ *                         be NULL.
+ */
+/*********************************************************************************
+ Win32 API - EnumPrinters()
+ ********************************************************************************/
+
+WERROR cli_spoolss_enum_printers(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                uint32 offered, uint32 *needed,
+                                char *name, uint32 flags, uint32 level,
+                                uint32 *num_printers, PRINTER_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPRINTERS q;
+        SPOOL_R_ENUMPRINTERS r;
+       NEW_BUFFER buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_enumprinters(&q, flags, name, level, &buffer, 
+                                   offered);
+
+       /* Marshall data and send request */
+       
+       if (!spoolss_io_q_enumprinters("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (spoolss_io_r_enumprinters("", &r, &rbuf, 0)) {
+               if (needed)
+                       *needed = r.needed;
+       }
+       
+       result = r.status;
+
+       /* Return output parameters */
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;
+
+       if (num_printers)
+               *num_printers = r.returned;
+
+       if (!ctr)
+               goto done;
+
+       switch (level) {
+       case 0:
+               decode_printer_info_0(mem_ctx, r.buffer, r.returned, 
+                                     &ctr->printers_0);
+               break;
+       case 1:
+               decode_printer_info_1(mem_ctx, r.buffer, r.returned, 
+                                     &ctr->printers_1);
+               break;
+       case 2:
+               decode_printer_info_2(mem_ctx, r.buffer, r.returned, 
+                                     &ctr->printers_2);
+               break;
+       case 3:
+               decode_printer_info_3(mem_ctx, r.buffer, r.returned, 
+                                     &ctr->printers_3);
+               break;
+       }                       
+       
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - EnumPorts()
+ ********************************************************************************/
+/** Enumerate printer ports on a print server.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param offered          Buffer size offered in the request.
+ * @param needed           Number of bytes needed to complete the request.
+ *                         May be NULL.
+ *
+ * @param level            Requested information level.
+ *
+ * @param num_ports        Pointer to number of ports returned.  May be NULL.
+ * @param ctr              Pointer to structure holding port information.
+ *                         May be NULL.
+ */
+
+WERROR cli_spoolss_enum_ports(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             uint32 offered, uint32 *needed,
+                             uint32 level, uint32 *num_ports, PORT_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPORTS q;
+        SPOOL_R_ENUMPORTS r;
+       NEW_BUFFER buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+
+       /* Initialise input parameters */
+       
+       init_buffer(&buffer, offered, mem_ctx);
+       
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+       
+       make_spoolss_q_enumports(&q, server, level, &buffer, offered);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumports("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMPORTS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (spoolss_io_r_enumports("", &r, &rbuf, 0)) {
+               if (needed)
+                       *needed = r.needed;
+       }
+               
+       result = r.status;
+
+       /* Return output parameters */
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       if (num_ports)
+               *num_ports = r.returned;
+
+       if (!ctr)
+               goto done;
+       
+       switch (level) {
+       case 1:
+               decode_port_info_1(mem_ctx, r.buffer, r.returned, 
+                                  &ctr->port.info_1);
+               break;
+       case 2:
+               decode_port_info_2(mem_ctx, r.buffer, r.returned, 
+                                  &ctr->port.info_2);
+               break;
+       }                       
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+       
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             uint32 offered, uint32 *needed,
+                             POLICY_HND *pol, uint32 level, 
+                             PRINTER_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETPRINTER q;
+       SPOOL_R_GETPRINTER r;
+       NEW_BUFFER buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+       
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_getprinter(mem_ctx, &q, pol, level, &buffer, offered);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_getprinter("", &r, &rbuf, 0))
+               goto done;
+
+       if (needed)
+               *needed = r.needed;
+       
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (W_ERROR_IS_OK(result)) {
+               switch (level) {
+               case 0:
+                       decode_printer_info_0(mem_ctx, r.buffer, 1, &ctr->printers_0);
+                       break;
+               case 1:
+                       decode_printer_info_1(mem_ctx, r.buffer, 1, &ctr->printers_1);
+                       break;
+               case 2:
+                       decode_printer_info_2(mem_ctx, r.buffer, 1, &ctr->printers_2);
+                       break;
+               case 3:
+                       decode_printer_info_3(mem_ctx, r.buffer, 1, &ctr->printers_3);
+                       break;
+               }                       
+       }
+       
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - SetPrinter()
+ ********************************************************************************/
+/** Set printer info 
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param pol              Policy handle on printer to set info.
+ * @param level            Information level to set.
+ * @param ctr              Pointer to structure holding printer information.
+ * @param command          Specifies the action performed.  See
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_13ua.asp 
+ * for details.
+ *
+ */
+
+WERROR cli_spoolss_setprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *pol, uint32 level, 
+                             PRINTER_INFO_CTR *ctr, uint32 command)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_SETPRINTER q;
+       SPOOL_R_SETPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+               
+       if (!make_spoolss_q_setprinter(mem_ctx, &q, pol, level, ctr, command))
+               goto done;
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_setprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_setprinter("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriver()
+ ********************************************************************************/
+/** Get installed printer drivers for a given printer
+ *
+ * @param cli              Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param offered          Buffer size offered in the request.
+ * @param needed           Number of bytes needed to complete the request.
+ *                         may be NULL.
+ *
+ * @param pol              Pointer to an open policy handle for the printer
+ *                         opened with cli_spoolss_open_printer_ex().
+ * @param level            Requested information level.
+ * @param env              The print environment or archictecture.  This is
+ *                         "Windows NT x86" for NT4.
+ * @param ctr              Returned printer driver information.
+ */
+
+WERROR cli_spoolss_getprinterdriver(struct cli_state *cli, 
+                                   TALLOC_CTX *mem_ctx, 
+                                   uint32 offered, uint32 *needed,
+                                   POLICY_HND *pol, uint32 level, 
+                                   const char *env, PRINTER_DRIVER_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETPRINTERDRIVER2 q;
+        SPOOL_R_GETPRINTERDRIVER2 r;
+       NEW_BUFFER buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       fstrcpy (server, cli->desthost);
+       strupper (server);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_getprinterdriver2(&q, pol, env, level, 2, 2,
+                                        &buffer, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprinterdriver2 ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVER2, &qbuf, &rbuf)) 
+               goto done;
+
+       /* Unmarshall response */
+
+       if (spoolss_io_r_getprinterdriver2 ("", &r, &rbuf, 0)) {
+               if (needed)
+                       *needed = r.needed;
+       }
+
+       result = r.status;
+
+       /* Return output parameters */
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       if (!ctr)
+               goto done;
+
+       switch (level) {
+       case 1:
+               decode_printer_driver_1(mem_ctx, r.buffer, 1, &ctr->info1);
+               break;
+       case 2:
+               decode_printer_driver_2(mem_ctx, r.buffer, 1, &ctr->info2);
+               break;
+       case 3:
+               decode_printer_driver_3(mem_ctx, r.buffer, 1, &ctr->info3);
+               break;
+       default:
+               DEBUG(10, ("cli_spoolss_getprinterdriver: unknown info level %d", level));
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+               
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - EnumPrinterDrivers()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_enumprinterdrivers (struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx,
+                                      uint32 offered, uint32 *needed,
+                                      uint32 level, const char *env,
+                                      uint32 *num_drivers,
+                                      PRINTER_DRIVER_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPRINTERDRIVERS q;
+        SPOOL_R_ENUMPRINTERDRIVERS r;
+       NEW_BUFFER buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Write the request */
+
+       make_spoolss_q_enumprinterdrivers(&q, server, env, level, &buffer, 
+                                         offered);
+       
+       /* Marshall data and send request */
+       
+       if (!spoolss_io_q_enumprinterdrivers ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_ENUMPRINTERDRIVERS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumprinterdrivers ("", &r, &rbuf, 0))
+               goto done;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (num_drivers)
+               *num_drivers = r.returned;
+
+       result = r.status;
+
+       /* Return output parameters */
+
+       if (W_ERROR_IS_OK(result) && (r.returned != 0)) {
+               *num_drivers = r.returned;
+
+               switch (level) {
+               case 1:
+                       decode_printer_driver_1(mem_ctx, r.buffer, r.returned, &ctr->info1);
+                       break;
+               case 2:
+                       decode_printer_driver_2(mem_ctx, r.buffer, r.returned, &ctr->info2);
+                       break;
+               case 3:
+                       decode_printer_driver_3(mem_ctx, r.buffer, r.returned, &ctr->info3);
+                       break;
+               default:
+                       DEBUG(10, ("cli_spoolss_enumprinterdrivers: unknown info level %d\n",
+                                  level));
+                       return WERR_UNKNOWN_LEVEL;
+               }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+               
+       return result;
+}
+
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriverDirectory()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_getprinterdriverdir (struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx,
+                                       uint32 offered, uint32 *needed,
+                                       uint32 level, char *env,
+                                       DRIVER_DIRECTORY_CTR *ctr)
+{
+       prs_struct                      qbuf, rbuf;
+       SPOOL_Q_GETPRINTERDRIVERDIR     q;
+        SPOOL_R_GETPRINTERDRIVERDIR    r;
+       NEW_BUFFER                      buffer;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring                         server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Write the request */
+
+       make_spoolss_q_getprinterdriverdir(&q, server, env, level, &buffer, 
+                                          offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprinterdriverdir ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVERDIRECTORY,
+                              &qbuf, &rbuf)) 
+               goto done;
+
+       /* Unmarshall response */
+
+       if (spoolss_io_r_getprinterdriverdir ("", &r, &rbuf, 0)) {
+               if (needed)
+                       *needed = r.needed;
+       }
+               
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (W_ERROR_IS_OK(result)) {
+               switch (level) {
+               case 1:
+                       decode_printerdriverdir_1(mem_ctx, r.buffer, 1, 
+                                                 &ctr->info1);
+                       break;
+               }                       
+       }
+               
+       done:
+               prs_mem_free(&qbuf);
+               prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer driver
+ */
+WERROR cli_spoolss_addprinterdriver (struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx, uint32 level,
+                                    PRINTER_DRIVER_CTR *ctr)
+{
+       prs_struct                      qbuf, rbuf;
+       SPOOL_Q_ADDPRINTERDRIVER        q;
+        SPOOL_R_ADDPRINTERDRIVER       r;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring                         server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+       
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Write the request */
+
+       make_spoolss_q_addprinterdriver (mem_ctx, &q, server, level, ctr);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_addprinterdriver ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTERDRIVER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_addprinterdriver ("", &r, &rbuf, 0))
+               goto done;
+               
+       /* Return output parameters */
+
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+       
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinter()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer
+ */
+WERROR cli_spoolss_addprinterex (struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                uint32 level, PRINTER_INFO_CTR*ctr)
+{
+       prs_struct                      qbuf, rbuf;
+       SPOOL_Q_ADDPRINTEREX            q;
+        SPOOL_R_ADDPRINTEREX           r;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring                         server,
+                                       client,
+                                       user;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+        slprintf (client, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (client);
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+       fstrcpy  (user, cli->user_name);
+
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Write the request */
+
+       make_spoolss_q_addprinterex (mem_ctx, &q, server, client, user,
+                                    level, ctr);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_addprinterex ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTEREX, &qbuf, &rbuf)) 
+               goto done;
+               
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_addprinterex ("", &r, &rbuf, 0))
+               goto done;
+               
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - DeltePrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Delete a Printer Driver from the server (does not remove 
+ * the driver files
+ */
+WERROR cli_spoolss_deleteprinterdriver (struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, const char *arch,
+                                       const char *driver)
+{
+       prs_struct                      qbuf, rbuf;
+       SPOOL_Q_DELETEPRINTERDRIVER     q;
+        SPOOL_R_DELETEPRINTERDRIVER    r;
+       WERROR result = W_ERROR(ERRgeneral);
+       fstring                         server;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+
+       /* Initialise input parameters */
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+        slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+        strupper (server);
+
+       /* Write the request */
+
+       make_spoolss_q_deleteprinterdriver(mem_ctx, &q, server, arch, driver);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_deleteprinterdriver ("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli,SPOOLSS_DELETEPRINTERDRIVER , &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_deleteprinterdriver ("", &r, &rbuf, 0))
+               goto done;
+               
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterProcessorDirectory()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprintprocessordirectory(struct cli_state *cli,
+                                             TALLOC_CTX *mem_ctx,
+                                             uint32 offered, uint32 *needed,
+                                             char *name, char *environment,
+                                             fstring procdir)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETPRINTPROCESSORDIRECTORY q;
+       SPOOL_R_GETPRINTPROCESSORDIRECTORY r;
+       int level = 1;
+       WERROR result = W_ERROR(ERRgeneral);
+       NEW_BUFFER buffer;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       make_spoolss_q_getprintprocessordirectory(
+               &q, name, environment, level, &buffer, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprintprocessordirectory("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTPROCESSORDIRECTORY,
+                             &qbuf, &rbuf))
+               goto done;
+               
+       /* Unmarshall response */
+               
+       if (!spoolss_io_r_getprintprocessordirectory("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+               
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (W_ERROR_IS_OK(result))
+               fstrcpy(procdir, "Not implemented!");
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Add a form to a printer.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param handle           Policy handle opened with cli_spoolss_open_printer_ex
+ *                         or cli_spoolss_addprinterex.
+ * @param level            Form info level to add - should always be 1.
+ * @param form             A pointer to the form to be added.
+ *
+ */
+
+WERROR cli_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          POLICY_HND *handle, uint32 level, FORM *form)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ADDFORM q;
+       SPOOL_R_ADDFORM r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_addform(&q, handle, level, form);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_addform("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ADDFORM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_addform("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Set a form on a printer.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param handle           Policy handle opened with cli_spoolss_open_printer_ex 
+ *                         or cli_spoolss_addprinterex.
+ * @param level            Form info level to set - should always be 1.
+ * @param form             A pointer to the form to be set.
+ *
+ */
+
+WERROR cli_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          POLICY_HND *handle, uint32 level, 
+                          const char *form_name, FORM *form)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_SETFORM q;
+       SPOOL_R_SETFORM r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_setform(&q, handle, level, form_name, form);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_setform("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_SETFORM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_setform("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Get a form on a printer.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param handle           Policy handle opened with cli_spoolss_open_printer_ex 
+ *                         or cli_spoolss_addprinterex.
+ * @param formname         Name of the form to get
+ * @param level            Form info level to get - should always be 1.
+ *
+ */
+
+WERROR cli_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                          uint32 offered, uint32 *needed,
+                          POLICY_HND *handle, const char *formname, 
+                          uint32 level, FORM_1 *form)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETFORM q;
+       SPOOL_R_GETFORM r;
+       WERROR result = W_ERROR(ERRgeneral);
+       NEW_BUFFER buffer;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_getform(&q, handle, formname, level, &buffer, offered);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getform("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETFORM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_getform("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (W_ERROR_IS_OK(result)) {
+               switch(level) {
+               case 1:
+                       smb_io_form_1("", r.buffer, form, 0);
+                       break;
+               default:
+                       DEBUG(10, ("cli_spoolss_getform: unknown info level %d", level));
+                       return WERR_UNKNOWN_LEVEL;
+               }
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/** Delete a form on a printer.
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param handle           Policy handle opened with cli_spoolss_open_printer_ex 
+ *                         or cli_spoolss_addprinterex.
+ * @param form             The name of the form to delete.
+ *
+ */
+
+WERROR cli_spoolss_deleteform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             POLICY_HND *handle, const char *form_name)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_DELETEFORM q;
+       SPOOL_R_DELETEFORM r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_deleteform(&q, handle, form_name);
+       
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_deleteform("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_DELETEFORM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_deleteform("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+static void decode_forms_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                          uint32 num_forms, FORM_1 **forms)
+{
+       int i;
+
+       *forms = (FORM_1 *)talloc(mem_ctx, num_forms * sizeof(FORM_1));
+       prs_set_offset(&buffer->prs,0);
+
+       for (i = 0; i < num_forms; i++)
+               smb_io_form_1("", buffer, &((*forms)[i]), 0);
+}
+
+/** Enumerate forms
+ *
+ * @param cli              Pointer to client state structure which is open
+ *                         on the SPOOLSS pipe.
+ * @param mem_ctx          Pointer to an initialised talloc context.
+ *
+ * @param offered          Buffer size offered in the request.
+ * @param needed           Number of bytes needed to complete the request.
+ *                         may be NULL.
+ *                         or cli_spoolss_addprinterex.
+ * @param level            Form info level to get - should always be 1.
+ * @param handle           Open policy handle
+ *
+ */
+
+WERROR cli_spoolss_enumforms(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                            uint32 offered, uint32 *needed,
+                            POLICY_HND *handle, int level, uint32 *num_forms,
+                            FORM_1 **forms)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMFORMS q;
+       SPOOL_R_ENUMFORMS r;
+       WERROR result = W_ERROR(ERRgeneral);
+       NEW_BUFFER buffer;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enumforms(&q, handle, level, &buffer, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumforms("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMFORMS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumforms("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (num_forms)
+               *num_forms = r.numofforms;
+
+       decode_forms_1(mem_ctx, r.buffer, *num_forms, forms);
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+static void decode_jobs_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                         uint32 num_jobs, JOB_INFO_1 **jobs)
+{
+       uint32 i;
+
+       *jobs = (JOB_INFO_1 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_1));
+       prs_set_offset(&buffer->prs,0);
+
+       for (i = 0; i < num_jobs; i++) 
+               smb_io_job_info_1("", buffer, &((*jobs)[i]), 0);
+}
+
+static void decode_jobs_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, 
+                         uint32 num_jobs, JOB_INFO_2 **jobs)
+{
+       uint32 i;
+
+       *jobs = (JOB_INFO_2 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_2));
+       prs_set_offset(&buffer->prs,0);
+
+       for (i = 0; i < num_jobs; i++) 
+               smb_io_job_info_2("", buffer, &((*jobs)[i]), 0);
+}
+
+/* Enumerate jobs */
+
+WERROR cli_spoolss_enumjobs(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                           uint32 offered, uint32 *needed,
+                           POLICY_HND *hnd, uint32 level, uint32 firstjob, 
+                           uint32 num_jobs, uint32 *returned, JOB_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMJOBS q;
+       SPOOL_R_ENUMJOBS r;
+       WERROR result = W_ERROR(ERRgeneral);
+       NEW_BUFFER buffer;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enumjobs(&q, hnd, firstjob, num_jobs, level, &buffer, 
+                               offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumjobs("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMJOBS, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumjobs("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;
+
+       *returned = r.returned;
+
+       switch(level) {
+       case 1:
+               decode_jobs_1(mem_ctx, r.buffer, r.returned,
+                             &ctr->job.job_info_1);
+               break;
+       case 2:
+               decode_jobs_2(mem_ctx, r.buffer, r.returned,
+                             &ctr->job.job_info_2);
+               break;
+       default:
+               DEBUG(3, ("unsupported info level %d", level));
+               break;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Set job */
+
+WERROR cli_spoolss_setjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                         POLICY_HND *hnd, uint32 jobid, uint32 level, 
+                         uint32 command)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_SETJOB q;
+       SPOOL_R_SETJOB r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_setjob(&q, hnd, jobid, level, command);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_setjob("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_SETJOB, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_setjob("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Get job */
+
+WERROR cli_spoolss_getjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                         uint32 offered, uint32 *needed,
+                         POLICY_HND *hnd, uint32 jobid, uint32 level,
+                         JOB_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETJOB q;
+       SPOOL_R_GETJOB r;
+       WERROR result = W_ERROR(ERRgeneral);
+       NEW_BUFFER buffer;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       init_buffer(&buffer, offered, mem_ctx);
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_getjob(&q, hnd, jobid, level, &buffer, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getjob("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETJOB, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_getjob("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;
+
+       switch(level) {
+       case 1:
+               decode_jobs_1(mem_ctx, r.buffer, 1, &ctr->job.job_info_1);
+               break;
+       case 2:
+               decode_jobs_2(mem_ctx, r.buffer, 1, &ctr->job.job_info_2);
+               break;
+       default:
+               DEBUG(3, ("unsupported info level %d", level));
+               break;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Startpageprinter.  Sent to notify the spooler when a page is about to be
+   sent to a printer. */ 
+
+WERROR cli_spoolss_startpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *hnd)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_STARTPAGEPRINTER q;
+       SPOOL_R_STARTPAGEPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_startpageprinter(&q, hnd);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_startpageprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_STARTPAGEPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_startpageprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Endpageprinter.  Sent to notify the spooler when a page has finished
+   being sent to a printer. */
+
+WERROR cli_spoolss_endpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *hnd)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENDPAGEPRINTER q;
+       SPOOL_R_ENDPAGEPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_endpageprinter(&q, hnd);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_endpageprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENDPAGEPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_endpageprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Startdocprinter.  Sent to notify the spooler that a document is about
+   to be spooled for printing. */
+
+WERROR cli_spoolss_startdocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                  POLICY_HND *hnd, char *docname, 
+                                  char *outputfile, char *datatype, 
+                                  uint32 *jobid)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_STARTDOCPRINTER q;
+       SPOOL_R_STARTDOCPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+       uint32 level = 1;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_startdocprinter(&q, hnd, level, docname, outputfile, 
+                                      datatype);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_startdocprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_STARTDOCPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_startdocprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+       
+       if (W_ERROR_IS_OK(result))
+               *jobid = r.jobid;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Enddocprinter.  Sent to notify the spooler that a document has finished
+   being spooled. */
+
+WERROR cli_spoolss_enddocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *hnd)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENDDOCPRINTER q;
+       SPOOL_R_ENDDOCPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enddocprinter(&q, hnd);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enddocprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENDDOCPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enddocprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Get printer data */
+
+WERROR cli_spoolss_getprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 uint32 offered, uint32 *needed,
+                                 POLICY_HND *hnd, const char *valuename, 
+                                 REGISTRY_VALUE *value)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETPRINTERDATA q;
+       SPOOL_R_GETPRINTERDATA r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_getprinterdata(&q, hnd, valuename, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprinterdata("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATA, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_getprinterdata("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+       /* Return output parameters */
+
+       value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+       value->type = r.type;
+       value->size = r.size;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_spoolss_getprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   uint32 offered, uint32 *needed,
+                                   POLICY_HND *hnd, const char *keyname, 
+                                   const char *valuename, 
+                                   REGISTRY_VALUE *value)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_GETPRINTERDATAEX q;
+       SPOOL_R_GETPRINTERDATAEX r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_getprinterdataex(&q, hnd, keyname, valuename, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_getprinterdataex("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATAEX, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_getprinterdataex("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+       /* Return output parameters */
+
+       value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+       value->type = r.type;
+       value->size = r.needed;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Set printer data */
+
+WERROR cli_spoolss_setprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 POLICY_HND *hnd, REGISTRY_VALUE *value)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_SETPRINTERDATA q;
+       SPOOL_R_SETPRINTERDATA r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_setprinterdata(
+               &q, hnd, value->valuename, value->type, value->data_p, value->size);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_setprinterdata("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATA, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_setprinterdata("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_spoolss_setprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *hnd, char *keyname, 
+                                   REGISTRY_VALUE *value)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_SETPRINTERDATAEX q;
+       SPOOL_R_SETPRINTERDATAEX r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_setprinterdataex(
+               &q, hnd, keyname, value->valuename, value->type, value->data_p, 
+               value->size);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_setprinterdataex("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATAEX, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_setprinterdataex("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Enum printer data */
+
+WERROR cli_spoolss_enumprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                  POLICY_HND *hnd, uint32 ndx,
+                                  uint32 value_offered, uint32 data_offered,
+                                  uint32 *value_needed, uint32 *data_needed,
+                                  REGISTRY_VALUE *value)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPRINTERDATA q;
+       SPOOL_R_ENUMPRINTERDATA r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enumprinterdata(&q, hnd, ndx, value_offered, data_offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumprinterdata("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATA, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumprinterdata("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;
+
+       /* Return data */
+       
+       if (value_needed)
+               *value_needed = r.realvaluesize;
+
+       if (data_needed)
+               *data_needed = r.realdatasize;
+
+       if (value) {
+               rpcstr_pull(value->valuename, r.value, sizeof(value->valuename), -1,
+                           STR_TERMINATE);
+               value->data_p = talloc_memdup(mem_ctx, r.data, r.realdatasize);
+               value->type = r.type;
+               value->size = r.realdatasize;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_spoolss_enumprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                    uint32 offered, uint32 *needed,
+                                    POLICY_HND *hnd, const char *keyname, 
+                                    REGVAL_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPRINTERDATAEX q;
+       SPOOL_R_ENUMPRINTERDATAEX r;
+       WERROR result = W_ERROR(ERRgeneral);
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enumprinterdataex(&q, hnd, keyname, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumprinterdataex("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATAEX, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumprinterdataex("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+       
+       if (needed)
+               *needed = r.needed;
+       
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;
+
+       /* Return data */
+
+       ZERO_STRUCTP(ctr);
+       regval_ctr_init(ctr);
+
+       for (i = 0; i < r.returned; i++) {
+               PRINTER_ENUM_VALUES *v = &r.ctr.values[i];
+               fstring name;
+
+               rpcstr_pull(name, v->valuename.buffer, sizeof(name), -1, 
+                           STR_TERMINATE);
+               regval_ctr_addvalue(ctr, name, v->type, v->data, v->data_len);
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Write data to printer */
+
+WERROR cli_spoolss_writeprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               POLICY_HND *hnd, uint32 data_size, char *data,
+                               uint32 *num_written)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_WRITEPRINTER q;
+       SPOOL_R_WRITEPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_writeprinter(&q, hnd, data_size, data);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_writeprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_WRITEPRINTER, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_writeprinter("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+       if (num_written)
+               *num_written = r.buffer_written;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Delete printer data */
+
+WERROR cli_spoolss_deleteprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                    POLICY_HND *hnd, char *valuename)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_DELETEPRINTERDATA q;
+       SPOOL_R_DELETEPRINTERDATA r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_deleteprinterdata(&q, hnd, valuename);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_deleteprinterdata("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATA, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_deleteprinterdata("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_spoolss_deleteprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                      POLICY_HND *hnd, char *keyname, 
+                                      char *valuename)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_DELETEPRINTERDATAEX q;
+       SPOOL_R_DELETEPRINTERDATAEX r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_deleteprinterdataex(&q, hnd, keyname, valuename);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_deleteprinterdataex("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATAEX, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_deleteprinterdataex("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_spoolss_enumprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 uint32 offered, uint32 *needed,
+                                 POLICY_HND *hnd, const char *keyname,
+                                 uint16 **keylist, uint32 *len)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ENUMPRINTERKEY q;
+       SPOOL_R_ENUMPRINTERKEY r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_enumprinterkey(&q, hnd, keyname, offered);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_enumprinterkey("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERKEY, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_enumprinterkey("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (needed)
+               *needed = r.needed;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+       /* Copy results */
+       
+       if (keylist) {
+               *keylist = (uint16 *)malloc(r.keys.buf_len * 2);
+               memcpy(*keylist, r.keys.buffer, r.keys.buf_len * 2);
+               if (len)
+                       *len = r.keys.buf_len * 2;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+WERROR cli_spoolss_deleteprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   POLICY_HND *hnd, char *keyname)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_DELETEPRINTERKEY q;
+       SPOOL_R_DELETEPRINTERKEY r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+        make_spoolss_q_deleteprinterkey(&q, hnd, keyname);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_deleteprinterkey("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERKEY, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!spoolss_io_r_deleteprinterkey("", &r, &rbuf, 0))
+               goto done;
+       
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(r.status))
+               goto done;      
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;          
+}
+
+/** @} **/
diff --git a/source4/rpc_client/cli_spoolss_notify.c b/source4/rpc_client/cli_spoolss_notify.c
new file mode 100644 (file)
index 0000000..f4eda33
--- /dev/null
@@ -0,0 +1,272 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Gerald Carter                2001-2002,
+   Copyright (C) Tim Potter                   2000-2002,
+   Copyright (C) Andrew Tridgell              1994-2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+   Copyright (C) Jean-Francois Micouleau      1999-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"
+
+/*
+ * SPOOLSS Client RPC's used by servers as the notification
+ * back channel.
+ */
+
+/* Send a ReplyOpenPrinter request.  This rpc is made by the printer
+   server to the printer client in response to a rffpcnex request.
+   The rrfpcnex request names a printer and a handle (the printerlocal
+   value) and this rpc establishes a back-channel over which printer
+   notifications are performed. */
+
+WERROR cli_spoolss_reply_open_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                     const char *printer, uint32 printerlocal, uint32 type, 
+                                     POLICY_HND *handle)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_REPLYOPENPRINTER q;
+       SPOOL_R_REPLYOPENPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+       
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_replyopenprinter(&q, printer, printerlocal, type);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_replyopenprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_REPLYOPENPRINTER, &qbuf, &rbuf)) 
+               goto done;
+       
+       /* Unmarshall response */
+       
+       if (!spoolss_io_r_replyopenprinter("", &r, &rbuf, 0))
+               goto done;
+               
+       /* Return result */
+
+       memcpy(handle, &r.handle, sizeof(r.handle));
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/* Close a back-channel notification connection */
+
+WERROR cli_spoolss_reply_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                      POLICY_HND *handle)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_REPLYCLOSEPRINTER q;
+       SPOOL_R_REPLYCLOSEPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_reply_closeprinter(&q, handle);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_replycloseprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_REPLYCLOSEPRINTER, &qbuf, &rbuf)) 
+               goto done;
+       
+       /* Unmarshall response */
+       
+       if (!spoolss_io_r_replycloseprinter("", &r, &rbuf, 0))
+               goto done;
+               
+       /* Return result */
+
+       result = r.status;
+       
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/*********************************************************************
+ This SPOOLSS_ROUTERREPLYPRINTER function is used to send a change 
+ notification event when the registration **did not** use 
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor.
+ Also see cli_spolss_reply_rrpcn()
+ *********************************************************************/
+WERROR cli_spoolss_routerreplyprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                     POLICY_HND *pol, uint32 condition, uint32 change_id)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_ROUTERREPLYPRINTER q;
+        SPOOL_R_ROUTERREPLYPRINTER r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       /* Initialise input parameters */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       make_spoolss_q_routerreplyprinter(&q, pol, condition, change_id);
+
+       /* Marshall data and send request */
+
+       if (!spoolss_io_q_routerreplyprinter("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req (cli, SPOOLSS_ROUTERREPLYPRINTER, &qbuf, &rbuf)) 
+               goto done;
+       
+       /* Unmarshall response */
+       
+       if (!spoolss_io_r_routerreplyprinter("", &r, &rbuf, 0))
+               goto done;
+
+       /* Return output parameters */
+
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+/*********************************************************************
+ This SPOOLSS_REPLY_RRPCN function is used to send a change 
+ notification event when the registration **did** use 
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor
+ Also see cli_spoolss_routereplyprinter()
+ *********************************************************************/
+
+WERROR cli_spoolss_rrpcn(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                        POLICY_HND *pol, uint32 notify_data_len,
+                        SPOOL_NOTIFY_INFO_DATA *notify_data,
+                        uint32 change_low, uint32 change_high)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_REPLY_RRPCN q;
+       SPOOL_R_REPLY_RRPCN r;
+       WERROR result = W_ERROR(ERRgeneral);
+       SPOOL_NOTIFY_INFO       notify_info;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       ZERO_STRUCT(notify_info);
+
+       /* Initialise input parameters */
+
+       notify_info.version = 0x2;
+       notify_info.flags   = 0x00020000;       /* ?? */
+       notify_info.count   = notify_data_len;
+       notify_info.data    = notify_data;
+
+       /* create and send a MSRPC command with api  */
+       /* store the parameters */
+
+       make_spoolss_q_reply_rrpcn(&q, pol, change_low, change_high, 
+                                  &notify_info);
+
+       /* Marshall data and send request */
+
+       if(!spoolss_io_q_reply_rrpcn("", &q,  &qbuf, 0) ||
+          !rpc_api_pipe_req(cli, SPOOLSS_RRPCN, &qbuf, &rbuf)) 
+               goto done;
+
+       /* Unmarshall response */
+       
+       if(!spoolss_io_r_reply_rrpcn("", &r, &rbuf, 0))
+               goto done;
+
+       if (r.unknown0 == 0x00080000)
+               DEBUG(8,("cli_spoolss_reply_rrpcn: I think the spooler resonded that the notification was ignored.\n"));
+       else if ( r.unknown0 != 0x0 )
+               DEBUG(8,("cli_spoolss_reply_rrpcn: unknown0 is non-zero [0x%x]\n", r.unknown0));
+       
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+/*********************************************************************
+ *********************************************************************/
+WERROR cli_spoolss_rffpcnex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                           POLICY_HND *pol, uint32 flags, uint32 options,
+                           const char *localmachine, uint32 printerlocal,
+                           SPOOL_NOTIFY_OPTION *option)
+{
+       prs_struct qbuf, rbuf;
+       SPOOL_Q_RFFPCNEX q;
+       SPOOL_R_RFFPCNEX r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       make_spoolss_q_rffpcnex(
+               &q, pol, flags, options, localmachine, printerlocal,
+               option);
+
+       /* Marshall data and send request */
+
+       if(!spoolss_io_q_rffpcnex("", &q,  &qbuf, 0) ||
+          !rpc_api_pipe_req(cli, SPOOLSS_RFFPCNEX, &qbuf, &rbuf)) 
+               goto done;
+
+       /* Unmarshall response */
+       
+       if(!spoolss_io_r_rffpcnex("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
diff --git a/source4/rpc_client/cli_srvsvc.c b/source4/rpc_client/cli_srvsvc.c
new file mode 100644 (file)
index 0000000..6cd18f2
--- /dev/null
@@ -0,0 +1,442 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT Domain Authentication SMB / MSRPC client
+   Copyright (C) Andrew Tridgell 1994-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Tim Potter 2001
+   Copyright (C) Jim McDonough 2002
+   
+   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"
+
+WERROR cli_srvsvc_net_srv_get_info(struct cli_state *cli, 
+                                  TALLOC_CTX *mem_ctx,
+                                  uint32 switch_value, SRV_INFO_CTR *ctr)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_SRV_GET_INFO q;
+       SRV_R_NET_SRV_GET_INFO r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_srv_get_info(&q, cli->srv_name_slash, switch_value);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_srv_get_info("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_SRV_GET_INFO, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       r.ctr = ctr;
+
+       if (!srv_io_r_net_srv_get_info("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_srvsvc_net_share_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                uint32 info_level, SRV_SHARE_INFO_CTR *ctr,
+                                int preferred_len, ENUM_HND *hnd)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_SHARE_ENUM q;
+       SRV_R_NET_SHARE_ENUM r;
+       WERROR result = W_ERROR(ERRgeneral);
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_share_enum(
+               &q, cli->srv_name_slash, info_level, preferred_len, hnd);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_share_enum("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_SHARE_ENUM_ALL, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_share_enum("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Oh yuck yuck yuck - we have to copy all the info out of the
+          SRV_SHARE_INFO_CTR in the SRV_R_NET_SHARE_ENUM as when we do a
+          prs_mem_free() it will all be invalidated.  The various share
+          info structures suck badly too.  This really is gross. */
+
+       ZERO_STRUCTP(ctr);
+
+       if (!r.ctr.num_entries)
+               goto done;
+
+       ctr->info_level = info_level;
+       ctr->num_entries = r.ctr.num_entries;
+
+       switch(info_level) {
+       case 1:
+               ctr->share.info1 = (SRV_SHARE_INFO_1 *)talloc(
+                       mem_ctx, sizeof(SRV_SHARE_INFO_1) * ctr->num_entries);
+               
+               memset(ctr->share.info1, 0, sizeof(SRV_SHARE_INFO_1));
+
+               for (i = 0; i < ctr->num_entries; i++) {
+                       SRV_SHARE_INFO_1 *info1 = &ctr->share.info1[i];
+                       char *s;
+                       
+                       /* Copy pointer crap */
+
+                       memcpy(&info1->info_1, &r.ctr.share.info1[i].info_1, 
+                              sizeof(SH_INFO_1));
+
+                       /* Duplicate strings */
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_netname);
+                       if (s)
+                               init_unistr2(&info1->info_1_str.uni_netname, s, strlen(s) + 1);
+               
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_remark);
+                       if (s)
+                               init_unistr2(&info1->info_1_str.uni_remark, s, strlen(s) + 1);
+
+               }               
+
+               break;
+       case 2:
+               ctr->share.info2 = (SRV_SHARE_INFO_2 *)talloc(
+                       mem_ctx, sizeof(SRV_SHARE_INFO_2) * ctr->num_entries);
+               
+               memset(ctr->share.info2, 0, sizeof(SRV_SHARE_INFO_2));
+
+               for (i = 0; i < ctr->num_entries; i++) {
+                       SRV_SHARE_INFO_2 *info2 = &ctr->share.info2[i];
+                       char *s;
+                       
+                       /* Copy pointer crap */
+
+                       memcpy(&info2->info_2, &r.ctr.share.info2[i].info_2, 
+                              sizeof(SH_INFO_2));
+
+                       /* Duplicate strings */
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_netname);
+                       if (s)
+                               init_unistr2(&info2->info_2_str.uni_netname, s, strlen(s) + 1);
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_remark);
+                       if (s)
+                               init_unistr2(&info2->info_2_str.uni_remark, s, strlen(s) + 1);
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_path);
+                       if (s)
+                               init_unistr2(&info2->info_2_str.uni_path, s, strlen(s) + 1);
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_passwd);
+                       if (s)
+                               init_unistr2(&info2->info_2_str.uni_passwd, s, strlen(s) + 1);
+               }
+               break;
+       }
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_srvsvc_net_share_del(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               const char *sharename)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_SHARE_DEL q;
+       SRV_R_NET_SHARE_DEL r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_share_del(&q, cli->srv_name_slash, sharename);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_share_del("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_SHARE_DEL, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_share_del("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_srvsvc_net_share_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               const char *netname, uint32 type, 
+                               const char *remark, uint32 perms, 
+                               uint32 max_uses, uint32 num_uses, 
+                               const char *path, const char *passwd)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_SHARE_ADD q;
+       SRV_R_NET_SHARE_ADD r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       init_srv_q_net_share_add(&q,cli->srv_name_slash, netname, type, remark,
+                                perms, max_uses, num_uses, path, passwd);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_share_add("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_SHARE_ADD, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_share_add("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+WERROR cli_srvsvc_net_remote_tod(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                char *server, TIME_OF_DAY_INFO *tod)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_REMOTE_TOD q;
+       SRV_R_NET_REMOTE_TOD r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_remote_tod(&q, cli->srv_name_slash);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_remote_tod("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_REMOTE_TOD, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       r.tod = tod;
+
+       if (!srv_io_r_net_remote_tod("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;  
+}
+
+WERROR cli_srvsvc_net_file_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               uint32 file_level, const char *user_name,
+                               SRV_FILE_INFO_CTR *ctr, int preferred_len,
+                               ENUM_HND *hnd)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_FILE_ENUM q;
+       SRV_R_NET_FILE_ENUM r;
+       WERROR result = W_ERROR(ERRgeneral);
+       int i;
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_file_enum(&q, cli->srv_name_slash, NULL, user_name, 
+                                file_level, ctr, preferred_len, hnd);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_file_enum("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_FILE_ENUM, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_file_enum("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* copy the data over to the ctr */
+
+       ZERO_STRUCTP(ctr);
+
+       ctr->switch_value = file_level;
+
+       ctr->num_entries = ctr->num_entries2 = r.ctr.num_entries;
+       
+       switch(file_level) {
+       case 3:
+               ctr->file.info3 = (SRV_FILE_INFO_3 *)talloc(
+                       mem_ctx, sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+               memset(ctr->file.info3, 0, 
+                      sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+               for (i = 0; i < r.ctr.num_entries; i++) {
+                       SRV_FILE_INFO_3 *info3 = &ctr->file.info3[i];
+                       char *s;
+                       
+                       /* Copy pointer crap */
+
+                       memcpy(&info3->info_3, &r.ctr.file.info3[i].info_3, 
+                              sizeof(FILE_INFO_3));
+
+                       /* Duplicate strings */
+
+                       s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_path_name);
+                       if (s)
+                               init_unistr2(&info3->info_3_str.uni_path_name, s, strlen(s) + 1);
+               
+                       s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_user_name);
+                       if (s)
+                               init_unistr2(&info3->info_3_str.uni_user_name, s, strlen(s) + 1);
+
+               }               
+
+               break;
+       }
+
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+
+       return result;
+}
+
+WERROR cli_srvsvc_net_file_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                uint32 file_id)
+{
+       prs_struct qbuf, rbuf;
+       SRV_Q_NET_FILE_CLOSE q;
+       SRV_R_NET_FILE_CLOSE r;
+       WERROR result = W_ERROR(ERRgeneral);
+
+       ZERO_STRUCT(q);
+       ZERO_STRUCT(r);
+
+       /* Initialise parse structures */
+
+       prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       /* Initialise input parameters */
+
+       init_srv_q_net_file_close(&q, cli->srv_name_slash, file_id);
+
+       /* Marshall data and send request */
+
+       if (!srv_io_q_net_file_close("", &q, &qbuf, 0) ||
+           !rpc_api_pipe_req(cli, SRV_NET_FILE_CLOSE, &qbuf, &rbuf))
+               goto done;
+
+       /* Unmarshall response */
+
+       if (!srv_io_r_net_file_close("", &r, &rbuf, 0))
+               goto done;
+
+       result = r.status;
+ done:
+       prs_mem_free(&qbuf);
+       prs_mem_free(&rbuf);
+       return result;
+}
diff --git a/source4/rpc_client/cli_wkssvc.c b/source4/rpc_client/cli_wkssvc.c
new file mode 100644 (file)
index 0000000..97b948b
--- /dev/null
@@ -0,0 +1,93 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT Domain Authentication SMB / MSRPC client
+   Copyright (C) Andrew Tridgell 1994-2000
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+   Copyright (C) Tim Potter 2001
+   Copytight (C) Rafal Szczesniak 2002
+   
+   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"
+
+/**
+ * WksQueryInfo rpc call (like query for server's capabilities)
+ *
+ * @param initialised client structure with \PIPE\wkssvc opened
+ * @param mem_ctx memory context assigned to this rpc binding
+ * @param wks100 WksQueryInfo structure
+ *
+ * @return NTSTATUS of rpc call
+ */
+NTSTATUS cli_wks_query_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                           WKS_INFO_100 *wks100)
+{
+       prs_struct buf;
+       prs_struct rbuf;
+       WKS_Q_QUERY_INFO q_o;
+       WKS_R_QUERY_INFO r_o;
+
+       if (cli == NULL || wks100 == NULL)
+               return NT_STATUS_UNSUCCESSFUL;
+
+       /* init rpc parse structures */
+       prs_init(&buf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+       prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+       DEBUG(4, ("WksQueryInfo\n"));
+       
+       /* init query structure with rpc call arguments */
+       init_wks_q_query_info(&q_o, cli->desthost, 100);
+       
+       /* marshall data */
+       if (!wks_io_q_query_info("", &q_o, &buf, 0)) {
+               prs_mem_free(&buf);
+               prs_mem_free(&rbuf);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* actual rpc call over \PIPE\wkssvc */
+       if (!rpc_api_pipe_req(cli, WKS_QUERY_INFO, &buf, &rbuf)) {
+               prs_mem_free(&buf);
+               prs_mem_free(&rbuf);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       prs_mem_free(&buf);
+
+       r_o.wks100 = wks100;
+
+       /* get call results from response buffer */
+       if (!wks_io_r_query_info("", &r_o, &rbuf, 0)) {
+               prs_mem_free(&rbuf);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* check returnet status code */
+       if (NT_STATUS_IS_ERR(r_o.status)) {
+               /* report the error */
+               DEBUG(0,("WKS_R_QUERY_INFO: %s\n", nt_errstr(r_o.status)));
+               prs_mem_free(&rbuf);
+               return r_o.status;
+       }
+       
+       /* do clean up */
+       prs_mem_free(&rbuf);
+       
+       return NT_STATUS_OK;
+}
+
diff --git a/source4/rpc_parse/.cvsignore b/source4/rpc_parse/.cvsignore
new file mode 100644 (file)
index 0000000..5f2a5c4
--- /dev/null
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source4/rpc_parse/parse_dfs.c b/source4/rpc_parse/parse_dfs.c
new file mode 100644 (file)
index 0000000..6f13500
--- /dev/null
@@ -0,0 +1,546 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  MSDfs RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ *  Copyright (C) Shirish Kalele               2000.
+ *  Copyright (C) Jeremy Allison                               2001.
+ *  
+ *  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 "nterr.h"
+#include "rpc_parse.h"   
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/******************************************************************* 
+Make a DFS_Q_DFS_QUERY structure
+*******************************************************************/
+
+void init_dfs_q_dfs_exist(DFS_Q_DFS_EXIST *q_d)
+{
+       q_d->dummy = 0;
+}
+
+/*************************************************************
+ Read/write a DFS_Q_DFS_EXIST structure - dummy...
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_exist(const char *desc, DFS_Q_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+       if(q_d == NULL)
+               return False;
+  
+       prs_debug(ps, depth, desc, "dfs_io_q_dfs_exist");
+
+       return True;
+}
+  
+/*************************************************************
+ Read/write a DFS_R_DFS_EXIST structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_exist(const char *desc, DFS_R_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+       if(q_d == NULL)
+               return False;
+  
+       prs_debug(ps, depth, desc, "dfs_io_r_dfs_exist");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("exist flag", ps, 0, &q_d->status))
+               return False;
+
+       return True;
+}
+  
+/******************************************************************* 
+Make a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_remove(DFS_Q_DFS_REMOVE *q_d, const char *entrypath, 
+                          const char *servername, const char *sharename)
+{
+       DEBUG(5,("init_dfs_q_dfs_remove\n"));
+       init_unistr2(&q_d->DfsEntryPath, entrypath,  strlen(entrypath)+1);
+       init_unistr2(&q_d->ServerName,   servername, strlen(servername)+1);
+       init_unistr2(&q_d->ShareName,    sharename,  strlen(sharename)+1);
+       q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+       return True;
+}
+
+/******************************************************************* 
+Read/write a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_q_dfs_remove(const char *desc, DFS_Q_DFS_REMOVE *q_d, prs_struct *ps, int depth)
+{
+       if(q_d == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_q_dfs_remove");
+       depth++;
+  
+       if(!prs_align(ps))
+               return False;
+  
+       if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_ServerName", ps, depth, &q_d->ptr_ServerName))
+               return False;
+       if(q_d->ptr_ServerName)
+               if (!smb_io_unistr2("ServerName",&q_d->ServerName, q_d->ptr_ServerName, ps, depth))
+                       return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+               return False;
+       if(q_d->ptr_ShareName)
+               if (!smb_io_unistr2("ShareName",&q_d->ShareName,  q_d->ptr_ShareName, ps, depth))
+                       return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/******************************************************************* 
+Read/write a DFS_R_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_r_dfs_remove(const char *desc, DFS_R_DFS_REMOVE *r_d, prs_struct *ps, int depth)
+{
+       if(r_d == NULL) 
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_r_dfs_remove");
+       depth++;
+
+       if(!prs_werror("status", ps, depth, &r_d->status))
+               return False;
+
+       return True;
+}
+
+/******************************************************************* 
+Make a DFS_Q_DFS_ADD structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_add(DFS_Q_DFS_ADD *q_d, const char *entrypath, 
+                       const char *servername, const char *sharename, 
+                       const char *comment, uint32 flags)
+{
+       DEBUG(5,("init_dfs_q_dfs_add\n"));
+       q_d->ptr_DfsEntryPath = q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+       init_unistr2(&q_d->DfsEntryPath, entrypath,  strlen(entrypath)+1);
+       init_unistr2(&q_d->ServerName,   servername, strlen(servername)+1);
+       init_unistr2(&q_d->ShareName,    sharename,  strlen(sharename)+1);
+       if(comment != NULL) {
+               init_unistr2(&q_d->Comment,      comment,    strlen(comment)+1);
+               q_d->ptr_Comment = 1;
+       } else {
+               q_d->ptr_Comment = 0;
+       }
+
+       q_d->Flags = flags;
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_DFS_ADD structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_add(const char *desc, DFS_Q_DFS_ADD *q_d, prs_struct *ps, int depth)
+{
+       if(q_d == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_q_dfs_add");
+       depth++;
+  
+       if(!prs_align(ps))
+               return False;
+  
+       if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("ServerName",&q_d->ServerName, 1, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+               return False;
+       if(!smb_io_unistr2("ShareName",&q_d->ShareName,  1, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_Comment", ps, depth, &q_d->ptr_Comment))
+               return False;
+       if(!smb_io_unistr2("",&q_d->Comment, q_d->ptr_Comment , ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("Flags", ps, depth, &q_d->Flags))
+               return True;
+
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ADD structure 
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_add(const char *desc, DFS_R_DFS_ADD *r_d, prs_struct *ps, int depth)
+{
+       if(r_d == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_r_dfs_add");
+       depth++;
+
+       if(!prs_werror("status", ps, depth, &r_d->status))
+               return False;
+
+       return True;
+}
+
+BOOL init_dfs_q_dfs_get_info(DFS_Q_DFS_GET_INFO *q_d, const char *entrypath,
+                            const char *servername, const char *sharename, 
+                            uint32 info_level)
+{
+       DEBUG(5,("init_dfs_q2_get_info\n"));
+       init_unistr2(&q_d->uni_path, entrypath,  strlen(entrypath)+1);
+       init_unistr2(&q_d->uni_server,   servername, strlen(servername)+1);
+       init_unistr2(&q_d->uni_share,    sharename,  strlen(sharename)+1);
+       q_d->level = info_level;
+       q_d->ptr_server = q_d->ptr_share = 1;
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_get_info(const char *desc, DFS_Q_DFS_GET_INFO* q_i, prs_struct* ps, int depth)
+{
+       if(q_i == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_q_dfs_get_info");
+       depth++;
+
+       if(!smb_io_unistr2("",&q_i->uni_path, 1, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_server", ps, depth, &q_i->ptr_server))
+               return False;
+
+       if(q_i->ptr_server)
+               if (!smb_io_unistr2("",&q_i->uni_server, q_i->ptr_server, ps, depth))
+                       return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_share", ps, depth, &q_i->ptr_share))
+               return False;
+       if(q_i->ptr_share)
+               if(!smb_io_unistr2("", &q_i->uni_share, q_i->ptr_share, ps, depth))
+                       return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("level", ps, depth, &q_i->level))
+               return False;
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_get_info(const char *desc, DFS_R_DFS_GET_INFO* r_i, prs_struct* ps, int depth)
+{
+       if(r_i == NULL)
+               return False;
+  
+       if(!prs_uint32("level", ps, depth, &r_i->level))
+               return False;
+       if(!prs_uint32("ptr_ctr", ps, depth, &r_i->ptr_ctr))
+               return False;
+
+       if(!dfs_io_dfs_info_ctr("", &r_i->ctr, 1, r_i->level, ps, depth))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_i->status))
+               return False;
+       return True;
+}
+                          
+/************************************************************
+ Make a DFS_Q_DFS_ENUM structure
+ ************************************************************/
+BOOL init_dfs_q_dfs_enum(DFS_Q_DFS_ENUM *q_d, uint32 level, DFS_INFO_CTR *ctr)
+{
+       q_d->level = level;
+       q_d->maxpreflen = -1;
+       q_d->ptr_buffer = 1;
+       q_d->level2 = level;
+  
+       q_d->ptr_num_entries = 1;
+       q_d->num_entries = 0;
+       q_d->num_entries2 = 0;
+       q_d->reshnd.ptr_hnd = 1;
+       q_d->reshnd.handle = 0;
+       return True;
+}
+  
+/************************************************************
+ Read or write the DFS_Q_DFS_ENUM structure 
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_enum(const char *desc, DFS_Q_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+       if(q_d == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_q_dfs_enum");
+       depth++;
+  
+       if(!prs_align(ps))
+               return False;
+  
+       if(!prs_uint32("level", ps, depth, &q_d->level))
+               return False;
+       if(!prs_uint32("maxpreflen", ps, depth, &q_d->maxpreflen))
+               return False;
+       if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+               return False;
+       if(!prs_uint32("level2", ps, depth, &q_d->level2))
+               return False;
+       if(!prs_uint32("level3", ps, depth, &q_d->level2))
+               return False;
+  
+       if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+               return False;
+       if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+               return False;
+       if(!prs_uint32("num_entries2", ps, depth, &q_d->num_entries2))
+               return False;
+       if(!smb_io_enum_hnd("resume_hnd",&q_d->reshnd, ps, depth))
+               return False;
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_INFO_CTR structure
+ ************************************************************/
+
+BOOL dfs_io_dfs_info_ctr(const char *desc, DFS_INFO_CTR* ctr, uint32 num_entries, uint32 level, prs_struct* ps, int depth)
+{
+       int i=0;
+
+       switch(level) {
+       case 1:
+               depth++;
+               /* should depend on whether marshalling or unmarshalling! */
+               if(UNMARSHALLING(ps)) {
+                       ctr->dfs.info1 = (DFS_INFO_1 *)prs_alloc_mem(ps, sizeof(DFS_INFO_1)*num_entries);
+                       if (!ctr->dfs.info1)
+                               return False;
+               }
+
+               for(i=0;i<num_entries;i++) {
+                       if(!prs_uint32("ptr_entrypath",ps, depth, &ctr->dfs.info1[i].ptr_entrypath))
+                               return False;
+               }
+               for(i=0;i<num_entries;i++) {
+                       if(!smb_io_unistr2("", &ctr->dfs.info1[i].entrypath, ctr->dfs.info1[i].ptr_entrypath, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+               }
+               depth--;
+               break;
+       case 2:
+               depth++;
+               if(UNMARSHALLING(ps)) {
+                       ctr->dfs.info2 = (DFS_INFO_2 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_2));
+                       if (!ctr->dfs.info2)
+                               return False;
+               }
+
+               for(i=0;i<num_entries;i++) {
+                       if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info2[i].ptr_entrypath))
+                               return False;
+                       if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info2[i].ptr_comment))
+                               return False;
+                       if(!prs_uint32("state", ps, depth, &ctr->dfs.info2[i].state))
+                               return False;
+                       if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info2[i].num_storages))
+                               return False;
+               }
+               for(i=0;i<num_entries;i++) {
+                       if(!smb_io_unistr2("", &ctr->dfs.info2[i].entrypath, ctr->dfs.info2[i].ptr_entrypath, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+                       if(!smb_io_unistr2("",&ctr->dfs.info2[i].comment, ctr->dfs.info2[i].ptr_comment, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+               }
+               depth--;
+               break;
+       case 3:
+               depth++;
+               if(UNMARSHALLING(ps)) {
+                       ctr->dfs.info3 = (DFS_INFO_3 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_3));
+                       if (!ctr->dfs.info3)
+                               return False;
+               }
+
+               for(i=0;i<num_entries;i++) {
+                       if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info3[i].ptr_entrypath))
+                               return False;
+                       if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info3[i].ptr_comment))
+                               return False;
+                       if(!prs_uint32("state", ps, depth, &ctr->dfs.info3[i].state))
+                               return False;
+                       if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info3[i].num_storages))
+                               return False;
+                       if(!prs_uint32("ptr_storages", ps, depth, &ctr->dfs.info3[i].ptr_storages))
+                               return False;
+               }
+               for(i=0;i<num_entries;i++) {
+                       if(!smb_io_unistr2("", &ctr->dfs.info3[i].entrypath, ctr->dfs.info3[i].ptr_entrypath, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+                       if(!smb_io_unistr2("", &ctr->dfs.info3[i].comment, ctr->dfs.info3[i].ptr_comment, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+                       if(!prs_uint32("num_storage_infos", ps, depth, &ctr->dfs.info3[i].num_storage_infos))
+                               return False;
+
+                       if(!dfs_io_dfs_storage_info("storage_info", &ctr->dfs.info3[i], ps, depth))
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ENUM structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_enum(const char *desc, DFS_R_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+       DFS_INFO_CTR *ctr;
+       if(q_d == NULL)
+               return False;
+       ctr = q_d->ctr;
+       if(ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "dfs_io_r_dfs_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+               return False;
+       if(!prs_uint32("level", ps, depth, &q_d->level))
+               return False;
+       if(!prs_uint32("level2", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+               return False;
+       if(q_d->ptr_num_entries)
+               if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+                       return False;
+       if(!prs_uint32("ptr_num_entries2", ps, depth, &q_d->ptr_num_entries2))
+               return False;
+       if(q_d->ptr_num_entries2)
+               if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries))
+                       return False;
+
+       if(!dfs_io_dfs_info_ctr("", ctr, q_d->num_entries, q_d->level, ps, depth))
+               return False;
+
+       if(!smb_io_enum_hnd("resume_hnd", &q_d->reshnd, ps, depth))
+               return False;
+       if(!prs_werror("status", ps, depth, &q_d->status))
+               return False;
+       return True;
+}
+
+BOOL dfs_io_dfs_storage_info(const char *desc, DFS_INFO_3* info3, prs_struct *ps, int depth)
+{
+       int i=0;
+       if(info3 == NULL)
+               return False;
+  
+       prs_debug(ps, depth, desc, "smb_io_dfs_storage_info");
+       depth++;
+
+       if(UNMARSHALLING(ps)) {
+               info3->storages = (DFS_STORAGE_INFO *)prs_alloc_mem(ps, info3->num_storage_infos*sizeof(DFS_STORAGE_INFO));
+               if (!info3->storages)
+                       return False;
+       }
+
+       for(i=0;i<info3->num_storage_infos;i++) {
+               if(!prs_uint32("storage_state", ps, depth, &info3->storages[i].state))
+                       return False;
+               if(!prs_uint32("ptr_servername", ps, depth, &info3->storages[i].ptr_servername))
+                       return False;
+               if(!prs_uint32("ptr_sharename", ps, depth, &info3->storages[i].ptr_sharename))
+                       return False;
+       }
+
+       for(i=0;i<info3->num_storage_infos;i++) {
+               if(!smb_io_unistr2("servername", &info3->storages[i].servername, info3->storages[i].ptr_servername, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+               if(!smb_io_unistr2("sharename", &info3->storages[i].sharename, info3->storages[i].ptr_sharename, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_ds.c b/source4/rpc_parse/parse_ds.c
new file mode 100644 (file)
index 0000000..ab07631
--- /dev/null
@@ -0,0 +1,122 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Gerald Carter                                2002
+ *  
+ *  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"
+
+static BOOL ds_io_dominfobasic( const char *desc, prs_struct *ps, int depth, DSROLE_PRIMARY_DOMAIN_INFO_BASIC **basic)
+{
+       DSROLE_PRIMARY_DOMAIN_INFO_BASIC *p = *basic;
+       
+       if ( UNMARSHALLING(ps) )
+               p = *basic = (DSROLE_PRIMARY_DOMAIN_INFO_BASIC *)prs_alloc_mem(ps, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+               
+       if ( !p )
+               return False;
+               
+       if ( !prs_uint16("machine_role", ps, depth, &p->machine_role) )
+               return False;
+       if ( !prs_uint16("unknown", ps, depth, &p->unknown) )
+               return False;
+
+       if ( !prs_uint32("flags", ps, depth, &p->flags) )
+               return False;
+
+       if ( !prs_uint32("netbios_ptr", ps, depth, &p->netbios_ptr) )
+               return False;
+       if ( !prs_uint32("dnsname_ptr", ps, depth, &p->dnsname_ptr) )
+               return False;
+       if ( !prs_uint32("forestname_ptr", ps, depth, &p->forestname_ptr) )
+               return False;
+               
+       if ( !prs_uint8s(False, "domain_guid", ps, depth, p->domain_guid.info, GUID_SIZE) )
+               return False;
+               
+       if ( !smb_io_unistr2( "netbios_domain", &p->netbios_domain, p->netbios_ptr, ps, depth) )
+               return False;
+       if ( !prs_align(ps) )
+               return False;
+       
+       if ( !smb_io_unistr2( "dns_domain", &p->dns_domain, p->dnsname_ptr, ps, depth) )
+               return False;
+       if ( !prs_align(ps) )
+               return False;
+       
+       if ( !smb_io_unistr2( "forest_domain", &p->forest_domain, p->forestname_ptr, ps, depth) )
+               return False;
+       if ( !prs_align(ps) )
+               return False;
+       
+               
+       return True;
+               
+}
+
+BOOL ds_io_q_getprimdominfo( const char *desc, DS_Q_GETPRIMDOMINFO *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "ds_io_q_getprimdominfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if ( !prs_uint16( "level", ps, depth, &q_u->level ) )
+               return False;
+               
+       return True;
+}
+
+BOOL ds_io_r_getprimdominfo( const char *desc, DS_R_GETPRIMDOMINFO *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "ds_io_r_getprimdominfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if ( !prs_uint32( "ptr", ps, depth, &r_u->ptr ) )
+               return False;
+               
+       if ( r_u->ptr )
+       {
+               if ( !prs_uint16( "level", ps, depth, &r_u->level ) )
+                       return False;
+       
+               if ( !prs_uint16( "unknown0", ps, depth, &r_u->unknown0 ) )
+                       return False;
+               
+               switch ( r_u->level )
+               {
+                       case DsRolePrimaryDomainInfoBasic:
+                               if ( !ds_io_dominfobasic( "dominfobasic", ps, depth, &r_u->info.basic ) )
+                                       return False;
+                               break;
+                       default:
+                               return False;
+               }
+       }
+
+       if ( !prs_align(ps) )
+               return False;
+       
+       if ( !prs_ntstatus("status", ps, depth, &r_u->status ) )
+               return False;           
+               
+       return True;
+}
diff --git a/source4/rpc_parse/parse_lsa.c b/source4/rpc_parse/parse_lsa.c
new file mode 100644 (file)
index 0000000..53a0fc9
--- /dev/null
@@ -0,0 +1,2525 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Andrew Bartlett                   2002,
+ *  Copyright (C) Jim McDonough                     2002.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn, prs_struct *ps, int depth);
+
+/*******************************************************************
+ Inits a LSA_TRANS_NAME structure.
+********************************************************************/
+
+void init_lsa_trans_name(LSA_TRANS_NAME *trn, UNISTR2 *uni_name,
+                        uint16 sid_name_use, const char *name, uint32 idx)
+{
+       int len_name = strlen(name);
+
+       if(len_name == 0)
+               len_name = 1;
+
+       trn->sid_name_use = sid_name_use;
+       init_uni_hdr(&trn->hdr_name, len_name);
+       init_unistr2(uni_name, name, len_name);
+       trn->domain_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_TRANS_NAME structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_name(const char *desc, LSA_TRANS_NAME *trn, prs_struct *ps, 
+                             int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_trans_name");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint16("sid_name_use", ps, depth, &trn->sid_name_use))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_unihdr ("hdr_name", &trn->hdr_name, ps, depth))
+               return False;
+       if(!prs_uint32("domain_idx  ", ps, depth, &trn->domain_idx))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_R_REF structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_r_ref(const char *desc, DOM_R_REF *r_r, prs_struct *ps, 
+                            int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "lsa_io_dom_r_ref");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("num_ref_doms_1", ps, depth, &r_r->num_ref_doms_1)) /* num referenced domains? */
+               return False;
+       if(!prs_uint32("ptr_ref_dom   ", ps, depth, &r_r->ptr_ref_dom)) /* undocumented buffer pointer. */
+               return False;
+       if(!prs_uint32("max_entries   ", ps, depth, &r_r->max_entries)) /* 32 - max number of entries */
+               return False;
+
+       SMB_ASSERT_ARRAY(r_r->hdr_ref_dom, r_r->num_ref_doms_1);
+
+       if (r_r->ptr_ref_dom != 0) {
+
+               if(!prs_uint32("num_ref_doms_2", ps, depth, &r_r->num_ref_doms_2)) /* 4 - num referenced domains? */
+                       return False;
+
+               SMB_ASSERT_ARRAY(r_r->ref_dom, r_r->num_ref_doms_2);
+
+               for (i = 0; i < r_r->num_ref_doms_1; i++) {
+                       fstring t;
+
+                       slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+                       if(!smb_io_unihdr(t, &r_r->hdr_ref_dom[i].hdr_dom_name, ps, depth))
+                               return False;
+
+                       slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+                       if(!prs_uint32(t, ps, depth, &r_r->hdr_ref_dom[i].ptr_dom_sid))
+                               return False;
+               }
+
+               for (i = 0; i < r_r->num_ref_doms_2; i++) {
+                       fstring t;
+
+                       if (r_r->hdr_ref_dom[i].hdr_dom_name.buffer != 0) {
+                               slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+                               if(!smb_io_unistr2(t, &r_r->ref_dom[i].uni_dom_name, True, ps, depth)) /* domain name unicode string */
+                                       return False;
+                               if(!prs_align(ps))
+                                       return False;
+                       }
+
+                       if (r_r->hdr_ref_dom[i].ptr_dom_sid != 0) {
+                               slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+                               if(!smb_io_dom_sid2(t, &r_r->ref_dom[i].ref_dom, ps, depth)) /* referenced domain SIDs */
+                                       return False;
+                       }
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_SEC_QOS structure.
+********************************************************************/
+
+void init_lsa_sec_qos(LSA_SEC_QOS *qos, uint16 imp_lev, uint8 ctxt, uint8 eff)
+{
+       DEBUG(5, ("init_lsa_sec_qos\n"));
+
+       qos->len = 0x0c; /* length of quality of service block, in bytes */
+       qos->sec_imp_level = imp_lev;
+       qos->sec_ctxt_mode = ctxt;
+       qos->effective_only = eff;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_SEC_QOS structure.
+********************************************************************/
+
+static BOOL lsa_io_sec_qos(const char *desc,  LSA_SEC_QOS *qos, prs_struct *ps, 
+                          int depth)
+{
+       uint32 start;
+
+       prs_debug(ps, depth, desc, "lsa_io_obj_qos");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       start = prs_offset(ps);
+
+       /* these pointers had _better_ be zero, because we don't know
+          what they point to!
+        */
+       if(!prs_uint32("len           ", ps, depth, &qos->len)) /* 0x18 - length (in bytes) inc. the length field. */
+               return False;
+       if(!prs_uint16("sec_imp_level ", ps, depth, &qos->sec_imp_level ))
+               return False;
+       if(!prs_uint8 ("sec_ctxt_mode ", ps, depth, &qos->sec_ctxt_mode ))
+               return False;
+       if(!prs_uint8 ("effective_only", ps, depth, &qos->effective_only))
+               return False;
+
+       if (qos->len != prs_offset(ps) - start) {
+               DEBUG(3,("lsa_io_sec_qos: length %x does not match size %x\n",
+                        qos->len, prs_offset(ps) - start));
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static void init_lsa_obj_attr(LSA_OBJ_ATTR *attr, uint32 attributes, LSA_SEC_QOS *qos)
+{
+       DEBUG(5, ("init_lsa_obj_attr\n"));
+
+       attr->len = 0x18; /* length of object attribute block, in bytes */
+       attr->ptr_root_dir = 0;
+       attr->ptr_obj_name = 0;
+       attr->attributes = attributes;
+       attr->ptr_sec_desc = 0;
+       
+       if (qos != NULL) {
+               attr->ptr_sec_qos = 1;
+               attr->sec_qos = qos;
+       } else {
+               attr->ptr_sec_qos = 0;
+               attr->sec_qos = NULL;
+       }
+}
+
+/*******************************************************************
+ Reads or writes an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_obj_attr(const char *desc, LSA_OBJ_ATTR *attr, prs_struct *ps, 
+                           int depth)
+{
+       uint32 start;
+
+       prs_debug(ps, depth, desc, "lsa_io_obj_attr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       start = prs_offset(ps);
+
+       /* these pointers had _better_ be zero, because we don't know
+          what they point to!
+        */
+       if(!prs_uint32("len         ", ps, depth, &attr->len)) /* 0x18 - length (in bytes) inc. the length field. */
+               return False;
+       if(!prs_uint32("ptr_root_dir", ps, depth, &attr->ptr_root_dir)) /* 0 - root directory (pointer) */
+               return False;
+       if(!prs_uint32("ptr_obj_name", ps, depth, &attr->ptr_obj_name)) /* 0 - object name (pointer) */
+               return False;
+       if(!prs_uint32("attributes  ", ps, depth, &attr->attributes)) /* 0 - attributes (undocumented) */
+               return False;
+       if(!prs_uint32("ptr_sec_desc", ps, depth, &attr->ptr_sec_desc)) /* 0 - security descriptior (pointer) */
+               return False;
+       if(!prs_uint32("ptr_sec_qos ", ps, depth, &attr->ptr_sec_qos )) /* security quality of service (pointer) */
+               return False;
+
+       /* code commented out as it's not necessary true (tested with hyena). JFM, 11/22/2001 */
+#if 0
+       if (attr->len != prs_offset(ps) - start) {
+               DEBUG(3,("lsa_io_obj_attr: length %x does not match size %x\n",
+                        attr->len, prs_offset(ps) - start));
+               return False;
+       }
+#endif
+
+       if (attr->ptr_sec_qos != 0) {
+               if (UNMARSHALLING(ps))
+                       if (!(attr->sec_qos = (LSA_SEC_QOS *)prs_alloc_mem(ps,sizeof(LSA_SEC_QOS))))
+                               return False;
+
+               if(!lsa_io_sec_qos("sec_qos", attr->sec_qos, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+void init_q_open_pol(LSA_Q_OPEN_POL *r_q, uint16 system_name,
+                    uint32 attributes, uint32 desired_access,
+                    LSA_SEC_QOS *qos)
+{
+       DEBUG(5, ("init_open_pol: attr:%d da:%d\n", attributes, 
+                 desired_access));
+
+       r_q->ptr = 1; /* undocumented pointer */
+
+       r_q->des_access = desired_access;
+
+       r_q->system_name = system_name;
+       init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol(const char *desc, LSA_Q_OPEN_POL *r_q, prs_struct *ps, 
+                      int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_open_pol");
+       depth++;
+
+       if(!prs_uint32("ptr       ", ps, depth, &r_q->ptr))
+               return False;
+       if(!prs_uint16("system_name", ps, depth, &r_q->system_name))
+               return False;
+       if(!prs_align( ps ))
+               return False;
+
+       if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+               return False;
+
+       if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol(const char *desc, LSA_R_OPEN_POL *r_p, prs_struct *ps, 
+                      int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_open_pol");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_p->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+void init_q_open_pol2(LSA_Q_OPEN_POL2 *r_q, const char *server_name,
+                       uint32 attributes, uint32 desired_access,
+                       LSA_SEC_QOS *qos)
+{
+       DEBUG(5, ("init_q_open_pol2: attr:%d da:%d\n", attributes, 
+                 desired_access));
+
+       r_q->ptr = 1; /* undocumented pointer */
+
+       r_q->des_access = desired_access;
+
+       init_unistr2(&r_q->uni_server_name, server_name, 
+                    strlen(server_name) + 1);
+
+       init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol2(const char *desc, LSA_Q_OPEN_POL2 *r_q, prs_struct *ps, 
+                       int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_open_pol2");
+       depth++;
+
+       if(!prs_uint32("ptr       ", ps, depth, &r_q->ptr))
+               return False;
+
+       if(!smb_io_unistr2 ("", &r_q->uni_server_name, r_q->ptr, ps, depth))
+               return False;
+       if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+               return False;
+
+       if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol2(const char *desc, LSA_R_OPEN_POL2 *r_p, prs_struct *ps, 
+                       int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_open_pol2");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_p->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+void init_q_query_sec_obj(LSA_Q_QUERY_SEC_OBJ *q_q, const POLICY_HND *hnd, 
+                         uint32 sec_info)
+{
+       DEBUG(5, ("init_q_query_sec_obj\n"));
+
+       q_q->pol = *hnd;
+       q_q->sec_info = sec_info;
+
+       return;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_sec_obj(const char *desc, LSA_Q_QUERY_SEC_OBJ *q_q, 
+                           prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_query_sec_obj");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if (!prs_uint32("sec_info", ps, depth, &q_q->sec_info))
+               return False;
+
+       return True;
+} 
+
+/*******************************************************************
+ Reads or writes a LSA_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_sec_obj(const char *desc, LSA_R_QUERY_SEC_OBJ *r_u, 
+                           prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_query_sec_obj");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if (!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+                       return False;
+       }
+
+       if (!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+void init_q_query(LSA_Q_QUERY_INFO *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+       DEBUG(5, ("init_q_query\n"));
+
+       memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+       q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query(const char *desc, LSA_Q_QUERY_INFO *q_q, prs_struct *ps, 
+                   int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_query");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("info_class", ps, depth, &q_q->info_class))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+BOOL init_q_enum_trust_dom(LSA_Q_ENUM_TRUST_DOM * q_e, POLICY_HND *pol,
+                          uint32 enum_context, uint32 preferred_len)
+{
+       DEBUG(5, ("init_q_enum_trust_dom\n"));
+
+       q_e->pol = *pol;
+       q_e->enum_context = enum_context;
+       q_e->preferred_len = preferred_len;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_trust_dom(const char *desc, LSA_Q_ENUM_TRUST_DOM *q_e, 
+                            prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_trust_dom");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("enum_context ", ps, depth, &q_e->enum_context))
+               return False;
+       if(!prs_uint32("preferred_len", ps, depth, &q_e->preferred_len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_r_enum_trust_dom(TALLOC_CTX *ctx, LSA_R_ENUM_TRUST_DOM *r_e, uint32 enum_context,
+                          uint32 req_num_domains, uint32 num_domains, TRUSTDOM **td)
+{
+       int i;
+
+        DEBUG(5, ("init_r_enum_trust_dom\n"));
+       
+        r_e->enum_context = enum_context;
+       r_e->num_domains = num_domains;
+       r_e->ptr_enum_domains = 0;
+       r_e->num_domains2 = num_domains;
+       
+       if (num_domains != 0) {
+       
+               /* 
+                * allocating empty arrays of unicode headers, strings
+                * and sids of enumerated trusted domains
+                */
+               if (!(r_e->hdr_domain_name = (UNIHDR2 *)talloc(ctx,sizeof(UNIHDR2) * num_domains))) {
+                       r_e->status = NT_STATUS_NO_MEMORY;
+                       return;
+               }
+               
+               if (!(r_e->uni_domain_name = (UNISTR2 *)talloc(ctx,sizeof(UNISTR2) * num_domains))) {
+                       r_e->status = NT_STATUS_NO_MEMORY;
+                       return;
+               }
+
+               if (!(r_e->domain_sid = (DOM_SID2 *)talloc(ctx,sizeof(DOM_SID2) * num_domains))) {
+                       r_e->status = NT_STATUS_NO_MEMORY;
+                       return;
+               }
+                               
+               for (i = 0; i < num_domains; i++) {
+                       
+                       /* don't know what actually is this for */
+                       r_e->ptr_enum_domains = 1;
+                       
+                       init_uni_hdr2(&r_e->hdr_domain_name[i], strlen_w((td[i])->name));
+                       init_dom_sid2(&r_e->domain_sid[i], &(td[i])->sid);
+                       
+                       init_unistr2_w(ctx, &r_e->uni_domain_name[i], (td[i])->name);
+                       
+               };
+       }
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_trust_dom(const char *desc, LSA_R_ENUM_TRUST_DOM *r_e, 
+                            prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_trust_dom");
+       depth++;
+
+       if(!prs_uint32("enum_context    ", ps, depth, &r_e->enum_context))
+               return False;
+       if(!prs_uint32("num_domains     ", ps, depth, &r_e->num_domains))
+               return False;
+       if(!prs_uint32("ptr_enum_domains", ps, depth, &r_e->ptr_enum_domains))
+               return False;
+
+       if (r_e->ptr_enum_domains) {
+               int i, num_domains;
+
+               if(!prs_uint32("num_domains2", ps, depth, &r_e->num_domains2))
+                       return False;
+
+               num_domains = r_e->num_domains2;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(r_e->hdr_domain_name = (UNIHDR2 *)prs_alloc_mem(ps,sizeof(UNIHDR2) * num_domains)))
+                               return False;
+
+                       if (!(r_e->uni_domain_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2) * num_domains)))
+                               return False;
+
+                       if (!(r_e->domain_sid = (DOM_SID2 *)prs_alloc_mem(ps,sizeof(DOM_SID2) * num_domains)))
+                               return False;
+               }
+
+               for (i = 0; i < num_domains; i++) {
+                       if(!smb_io_unihdr2 ("", &r_e->hdr_domain_name[i], ps, 
+                                           depth))
+                               return False;
+               }
+               
+               for (i = 0; i < num_domains; i++) {
+                       if(!smb_io_unistr2 ("", &r_e->uni_domain_name[i],
+                                           r_e->hdr_domain_name[i].buffer,
+                                           ps, depth))
+                               return False;
+                       if(!smb_io_dom_sid2("", &r_e->domain_sid[i], ps, 
+                                           depth))
+                               return False;
+               }
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_e->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query(const char *desc, DOM_QUERY *d_q, prs_struct *ps, int depth)
+{
+       if (d_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_dom_query");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint16("uni_dom_max_len", ps, depth, &d_q->uni_dom_max_len)) /* domain name string length * 2 */
+               return False;
+       if(!prs_uint16("uni_dom_str_len", ps, depth, &d_q->uni_dom_str_len)) /* domain name string length * 2 */
+               return False;
+
+       if(!prs_uint32("buffer_dom_name", ps, depth, &d_q->buffer_dom_name)) /* undocumented domain name string buffer pointer */
+               return False;
+       if(!prs_uint32("buffer_dom_sid ", ps, depth, &d_q->buffer_dom_sid)) /* undocumented domain SID string buffer pointer */
+               return False;
+
+       if(!smb_io_unistr2("unistr2", &d_q->uni_domain_name, d_q->buffer_dom_name, ps, depth)) /* domain name (unicode string) */
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (d_q->buffer_dom_sid != 0) {
+               if(!smb_io_dom_sid2("", &d_q->dom_sid, ps, depth)) /* domain SID */
+                       return False;
+       } else {
+               memset((char *)&d_q->dom_sid, '\0', sizeof(d_q->dom_sid));
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_2(const char *desc, DOM_QUERY_2 *d_q, prs_struct *ps, int depth)
+{
+       uint32 ptr = 1;
+
+       if (d_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_dom_query_2");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("auditing_enabled", ps, depth, &d_q->auditing_enabled))
+               return False;
+       if (!prs_uint32("ptr   ", ps, depth, &ptr))
+               return False;
+       if (!prs_uint32("count1", ps, depth, &d_q->count1))
+               return False;
+       if (!prs_uint32("count2", ps, depth, &d_q->count2))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               d_q->auditsettings = (uint32 *)talloc_zero(ps->mem_ctx, d_q->count2 * sizeof(uint32));
+       }
+
+       if (d_q->auditsettings == NULL) {
+               DEBUG(1, ("lsa_io_dom_query_2: NULL auditsettings!\n"));
+               return False;
+       }
+
+       if (!prs_uint32s(False, "auditsettings", ps, depth, d_q->auditsettings, d_q->count2))
+               return False;
+
+    return True;
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_3(const char *desc, DOM_QUERY_3 *d_q, prs_struct *ps, int depth)
+{
+       return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_5(const char *desc, DOM_QUERY_5 *d_q, prs_struct *ps, int depth)
+{
+       return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_6(const char *desc, DOM_QUERY_6 *d_q, prs_struct *ps, int depth)
+{
+       if (d_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_dom_query_6");
+       depth++;
+
+       if (!prs_uint16("server_role", ps, depth, &d_q->server_role))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query(const char *desc, LSA_R_QUERY_INFO *r_q, prs_struct *ps,
+                   int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_query");
+       depth++;
+
+       if(!prs_uint32("undoc_buffer", ps, depth, &r_q->undoc_buffer))
+               return False;
+
+       if (r_q->undoc_buffer != 0) {
+               if(!prs_uint16("info_class", ps, depth, &r_q->info_class))
+                       return False;
+
+               if(!prs_align(ps))
+                       return False;
+
+               switch (r_q->info_class) {
+               case 2:
+                       if(!lsa_io_dom_query_2("", &r_q->dom.id2, ps, depth))
+                               return False;
+                       break;
+               case 3:
+                       if(!lsa_io_dom_query_3("", &r_q->dom.id3, ps, depth))
+                               return False;
+                       break;
+               case 5:
+                       if(!lsa_io_dom_query_5("", &r_q->dom.id5, ps, depth))
+                               return False;
+                       break;
+               case 6:
+                       if(!lsa_io_dom_query_6("", &r_q->dom.id6, ps, depth))
+                               return False;
+                       break;
+               default:
+                       /* PANIC! */
+                       break;
+               }
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a LSA_SID_ENUM structure.
+********************************************************************/
+
+static void init_lsa_sid_enum(TALLOC_CTX *mem_ctx, LSA_SID_ENUM *sen, 
+                      int num_entries, DOM_SID *sids)
+{
+       int i;
+
+       DEBUG(5, ("init_lsa_sid_enum\n"));
+
+       sen->num_entries  = num_entries;
+       sen->ptr_sid_enum = (num_entries != 0);
+       sen->num_entries2 = num_entries;
+
+       /* Allocate memory for sids and sid pointers */
+
+       if (num_entries == 0) return;
+
+       if ((sen->ptr_sid = (uint32 *)talloc_zero(mem_ctx, num_entries * 
+                                            sizeof(uint32))) == NULL) {
+               DEBUG(3, ("init_lsa_sid_enum(): out of memory for ptr_sid\n"));
+               return;
+       }
+
+       if ((sen->sid = (DOM_SID2 *)talloc_zero(mem_ctx, num_entries * 
+                                          sizeof(DOM_SID2))) == NULL) {
+               DEBUG(3, ("init_lsa_sid_enum(): out of memory for sids\n"));
+               return;
+       }
+
+       /* Copy across SIDs and SID pointers */
+
+       for (i = 0; i < num_entries; i++) {
+               sen->ptr_sid[i] = 1;
+               init_dom_sid2(&sen->sid[i], &sids[i]);
+       }
+}
+
+/*******************************************************************
+ Reads or writes a LSA_SID_ENUM structure.
+********************************************************************/
+
+static BOOL lsa_io_sid_enum(const char *desc, LSA_SID_ENUM *sen, prs_struct *ps, 
+                           int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "lsa_io_sid_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("num_entries ", ps, depth, &sen->num_entries))
+               return False;
+       if(!prs_uint32("ptr_sid_enum", ps, depth, &sen->ptr_sid_enum))
+               return False;
+
+       /*
+          if the ptr is NULL, leave here. checked from a real w2k trace.
+          JFM, 11/23/2001
+        */
+       
+       if (sen->ptr_sid_enum==0)
+               return True;
+
+       if(!prs_uint32("num_entries2", ps, depth, &sen->num_entries2))
+               return False;
+
+       /* Mallocate memory if we're unpacking from the wire */
+
+       if (UNMARSHALLING(ps)) {
+               if ((sen->ptr_sid = (uint32 *)prs_alloc_mem( ps,
+                       sen->num_entries * sizeof(uint32))) == NULL) {
+                       DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+                                 "ptr_sid\n"));
+                       return False;
+               }
+
+               if ((sen->sid = (DOM_SID2 *)prs_alloc_mem( ps,
+                       sen->num_entries * sizeof(DOM_SID2))) == NULL) {
+                       DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+                                 "sids\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < sen->num_entries; i++) {        
+               fstring temp;
+
+               slprintf(temp, sizeof(temp) - 1, "ptr_sid[%d]", i);
+               if(!prs_uint32(temp, ps, depth, &sen->ptr_sid[i])) {
+                       return False;
+               }
+       }
+
+       for (i = 0; i < sen->num_entries; i++) {
+               fstring temp;
+
+               slprintf(temp, sizeof(temp) - 1, "sid[%d]", i);
+               if(!smb_io_dom_sid2(temp, &sen->sid[i], ps, depth)) {
+                       return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_q_lookup_sids(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_SIDS *q_l, 
+                       POLICY_HND *hnd, int num_sids, DOM_SID *sids,
+                       uint16 level)
+{
+       DEBUG(5, ("init_r_enum_trust_dom\n"));
+
+       ZERO_STRUCTP(q_l);
+
+       memcpy(&q_l->pol, hnd, sizeof(q_l->pol));
+       init_lsa_sid_enum(mem_ctx, &q_l->sids, num_sids, sids);
+       
+       q_l->level.value = level;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_Q_LOOKUP_SIDS structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_sids(const char *desc, LSA_Q_LOOKUP_SIDS *q_s, prs_struct *ps,
+                         int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_lookup_sids");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("pol_hnd", &q_s->pol, ps, depth)) /* policy handle */
+               return False;
+       if(!lsa_io_sid_enum("sids   ", &q_s->sids, ps, depth)) /* sids to be looked up */
+               return False;
+       if(!lsa_io_trans_names("names  ", &q_s->names, ps, depth)) /* translated names */
+               return False;
+       if(!smb_io_lookup_level("switch ", &q_s->level, ps, depth)) /* lookup level */
+               return False;
+
+       if(!prs_uint32("mapped_count", ps, depth, &q_s->mapped_count))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn,
+                prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "lsa_io_trans_names");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+   
+       if(!prs_uint32("num_entries    ", ps, depth, &trn->num_entries))
+               return False;
+       if(!prs_uint32("ptr_trans_names", ps, depth, &trn->ptr_trans_names))
+               return False;
+
+       if (trn->ptr_trans_names != 0) {
+               if(!prs_uint32("num_entries2   ", ps, depth, 
+                              &trn->num_entries2))
+                       return False;
+
+               if (UNMARSHALLING(ps)) {
+                       if ((trn->name = (LSA_TRANS_NAME *)
+                            prs_alloc_mem(ps, trn->num_entries * 
+                                   sizeof(LSA_TRANS_NAME))) == NULL) {
+                               return False;
+                       }
+
+                       if ((trn->uni_name = (UNISTR2 *)
+                            prs_alloc_mem(ps, trn->num_entries *
+                                   sizeof(UNISTR2))) == NULL) {
+                               return False;
+                       }
+               }
+
+               for (i = 0; i < trn->num_entries2; i++) {
+                       fstring t;
+                       slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+                       if(!lsa_io_trans_name(t, &trn->name[i], ps, depth)) /* translated name */
+                               return False;
+               }
+
+               for (i = 0; i < trn->num_entries2; i++) {
+                       fstring t;
+                       slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+                       if(!smb_io_unistr2(t, &trn->uni_name[i], trn->name[i].hdr_name.buffer, ps, depth))
+                               return False;
+                       if(!prs_align(ps))
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_sids(const char *desc, LSA_R_LOOKUP_SIDS *r_s, 
+                         prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_lookup_sids");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_dom_ref", ps, depth, &r_s->ptr_dom_ref))
+               return False;
+
+       if (r_s->ptr_dom_ref != 0)
+               if(!lsa_io_dom_r_ref ("dom_ref", r_s->dom_ref, ps, depth)) /* domain reference info */
+                       return False;
+
+       if(!lsa_io_trans_names("names  ", r_s->names, ps, depth)) /* translated names */
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("mapped_count", ps, depth, &r_s->mapped_count))
+               return False;
+
+       if(!prs_ntstatus("status      ", ps, depth, &r_s->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_q_lookup_names(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_NAMES *q_l, 
+                        POLICY_HND *hnd, int num_names, const char **names)
+{
+       int i;
+
+       DEBUG(5, ("init_q_lookup_names\n"));
+
+       ZERO_STRUCTP(q_l);
+
+       q_l->pol = *hnd;
+       q_l->num_entries = num_names;
+       q_l->num_entries2 = num_names;
+       q_l->lookup_level = 1;
+
+       if ((q_l->uni_name = (UNISTR2 *)talloc_zero(
+               mem_ctx, num_names * sizeof(UNISTR2))) == NULL) {
+               DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+               return;
+       }
+
+       if ((q_l->hdr_name = (UNIHDR *)talloc_zero(
+               mem_ctx, num_names * sizeof(UNIHDR))) == NULL) {
+               DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+               return;
+       }
+
+       for (i = 0; i < num_names; i++) {
+               int len;
+               len = strlen(names[i]);
+
+               init_uni_hdr(&q_l->hdr_name[i], len);
+               init_unistr2(&q_l->uni_name[i], names[i], len);
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_names(const char *desc, LSA_Q_LOOKUP_NAMES *q_r, 
+                          prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "lsa_io_q_lookup_names");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("", &q_r->pol, ps, depth)) /* policy handle */
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_entries    ", ps, depth, &q_r->num_entries))
+               return False;
+       if(!prs_uint32("num_entries2   ", ps, depth, &q_r->num_entries2))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (q_r->num_entries) {
+                       if ((q_r->hdr_name = (UNIHDR *)prs_alloc_mem(ps,
+                                       q_r->num_entries * sizeof(UNIHDR))) == NULL)
+                               return False;
+                       if ((q_r->uni_name = (UNISTR2 *)prs_alloc_mem(ps,
+                                       q_r->num_entries * sizeof(UNISTR2))) == NULL)
+                               return False;
+               }
+       }
+
+       for (i = 0; i < q_r->num_entries; i++) {
+               if(!prs_align(ps))
+                       return False;
+               if(!smb_io_unihdr("hdr_name", &q_r->hdr_name[i], ps, depth)) /* pointer names */
+                       return False;
+       }
+
+       for (i = 0; i < q_r->num_entries; i++) {
+               if(!prs_align(ps))
+                       return False;
+               if(!smb_io_unistr2("dom_name", &q_r->uni_name[i], q_r->hdr_name[i].buffer, ps, depth)) /* names to be looked up */
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_trans_entries ", ps, depth, &q_r->num_trans_entries))
+               return False;
+       if(!prs_uint32("ptr_trans_sids ", ps, depth, &q_r->ptr_trans_sids))
+               return False;
+       if(!prs_uint32("lookup_level   ", ps, depth, &q_r->lookup_level))
+               return False;
+       if(!prs_uint32("mapped_count   ", ps, depth, &q_r->mapped_count))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_names(const char *desc, LSA_R_LOOKUP_NAMES *r_r, 
+                          prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "lsa_io_r_lookup_names");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_dom_ref", ps, depth, &r_r->ptr_dom_ref))
+               return False;
+
+       if (r_r->ptr_dom_ref != 0)
+               if(!lsa_io_dom_r_ref("", r_r->dom_ref, ps, depth))
+                       return False;
+
+       if(!prs_uint32("num_entries", ps, depth, &r_r->num_entries))
+               return False;
+       if(!prs_uint32("ptr_entries", ps, depth, &r_r->ptr_entries))
+               return False;
+
+       if (r_r->ptr_entries != 0) {
+               if(!prs_uint32("num_entries2", ps, depth, &r_r->num_entries2))
+                       return False;
+
+               if (r_r->num_entries2 != r_r->num_entries) {
+                       /* RPC fault */
+                       return False;
+               }
+
+               if (UNMARSHALLING(ps)) {
+                       if ((r_r->dom_rid = (DOM_RID2 *)prs_alloc_mem(ps, r_r->num_entries2 * sizeof(DOM_RID2)))
+                           == NULL) {
+                               DEBUG(3, ("lsa_io_r_lookup_names(): out of memory\n"));
+                               return False;
+                       }
+               }
+
+               for (i = 0; i < r_r->num_entries2; i++)
+                       if(!smb_io_dom_rid2("", &r_r->dom_rid[i], ps, depth)) /* domain RIDs being looked up */
+                               return False;
+       }
+
+       if(!prs_uint32("mapped_count", ps, depth, &r_r->mapped_count))
+               return False;
+
+       if(!prs_ntstatus("status      ", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_CLOSE structure.
+********************************************************************/
+
+void init_lsa_q_close(LSA_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_lsa_q_close\n"));
+
+       memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_q_close(const char *desc, LSA_Q_CLOSE *q_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_close");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &q_c->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_r_close(const char *desc,  LSA_R_CLOSE *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_close");
+       depth++;
+
+       if(!smb_io_pol_hnd("", &r_c->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_secret(const char *desc, LSA_Q_OPEN_SECRET *q_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_open_secret");
+       depth++;
+
+       /* Don't bother to read or write at present... */
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_secret(const char *desc, LSA_R_OPEN_SECRET *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_open_secret");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+   
+       if(!prs_uint32("dummy1", ps, depth, &r_c->dummy1))
+               return False;
+       if(!prs_uint32("dummy2", ps, depth, &r_c->dummy2))
+               return False;
+       if(!prs_uint32("dummy3", ps, depth, &r_c->dummy3))
+               return False;
+       if(!prs_uint32("dummy4", ps, depth, &r_c->dummy4))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_q_enum_privs(LSA_Q_ENUM_PRIVS *q_q, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+       DEBUG(5, ("init_q_enum_privs\n"));
+
+       memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+       q_q->enum_context = enum_context;
+       q_q->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_privs(const char *desc, LSA_Q_ENUM_PRIVS *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_privs");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("enum_context   ", ps, depth, &q_q->enum_context))
+               return False;
+       if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL lsa_io_priv_entries(const char *desc, LSA_PRIV_ENTRY *entries, uint32 count, prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (entries == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_priv_entries");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       for (i = 0; i < count; i++) {
+               if (!smb_io_unihdr("", &entries[i].hdr_name, ps, depth))
+                       return False;
+               if(!prs_uint32("luid_low ", ps, depth, &entries[i].luid_low))
+                       return False;
+               if(!prs_uint32("luid_high", ps, depth, &entries[i].luid_high))
+                       return False;
+       }
+
+       for (i = 0; i < count; i++)
+               if (!smb_io_unistr2("", &entries[i].name, entries[i].hdr_name.buffer, ps, depth))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_privs(LSA_R_ENUM_PRIVS *r_u, uint32 enum_context,
+                         uint32 count, LSA_PRIV_ENTRY *entries)
+{
+       DEBUG(5, ("init_lsa_r_enum_privs\n"));
+
+       r_u->enum_context=enum_context;
+       r_u->count=count;
+       
+       if (entries!=NULL) {
+               r_u->ptr=1;
+               r_u->count1=count;
+               r_u->privs=entries;
+       } else {
+               r_u->ptr=0;
+               r_u->count1=0;
+               r_u->privs=NULL;
+       }               
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_privs(const char *desc, LSA_R_ENUM_PRIVS *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_privs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+               return False;
+       if(!prs_uint32("count", ps, depth, &r_q->count))
+               return False;
+       if(!prs_uint32("ptr", ps, depth, &r_q->ptr))
+               return False;
+
+       if (r_q->ptr) {
+               if(!prs_uint32("count1", ps, depth, &r_q->count1))
+                       return False;
+
+               if (UNMARSHALLING(ps))
+                       if (!(r_q->privs = (LSA_PRIV_ENTRY *)prs_alloc_mem(ps, sizeof(LSA_PRIV_ENTRY) * r_q->count1)))
+                               return False;
+
+               if (!lsa_io_priv_entries("", r_q->privs, r_q->count1, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+void init_lsa_priv_get_dispname(LSA_Q_PRIV_GET_DISPNAME *trn, POLICY_HND *hnd, const char *name, uint16 lang_id, uint16 lang_id_sys)
+{
+       int len_name = strlen(name);
+
+       if(len_name == 0)
+               len_name = 1;
+
+       memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+       init_uni_hdr(&trn->hdr_name, len_name);
+       init_unistr2(&trn->name, name, len_name);
+       trn->lang_id = lang_id;
+       trn->lang_id_sys = lang_id_sys;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_priv_get_dispname(const char *desc, LSA_Q_PRIV_GET_DISPNAME *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_q_priv_get_dispname");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if (!smb_io_unihdr("hdr_name", &q_q->hdr_name, ps, depth))
+               return False;
+
+       if (!smb_io_unistr2("name", &q_q->name, q_q->hdr_name.buffer, ps, depth))
+               return False;
+
+       if(!prs_uint16("lang_id    ", ps, depth, &q_q->lang_id))
+               return False;
+       if(!prs_uint16("lang_id_sys", ps, depth, &q_q->lang_id_sys))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_priv_get_dispname(const char *desc, LSA_R_PRIV_GET_DISPNAME *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_r_priv_get_dispname");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr_info", ps, depth, &r_q->ptr_info))
+               return False;
+
+       if (r_q->ptr_info){
+               if (!smb_io_unihdr("hdr_name", &r_q->hdr_desc, ps, depth))
+                       return False;
+
+               if (!smb_io_unistr2("desc", &r_q->desc, r_q->hdr_desc.buffer, ps, depth))
+                       return False;
+       }
+/*
+       if(!prs_align(ps))
+               return False;
+*/
+       if(!prs_uint16("lang_id", ps, depth, &r_q->lang_id))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*
+  initialise a LSA_Q_ENUM_ACCOUNTS structure
+*/
+void init_lsa_q_enum_accounts(LSA_Q_ENUM_ACCOUNTS *trn, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+       memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+       trn->enum_context = enum_context;
+       trn->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_accounts(const char *desc, LSA_Q_ENUM_ACCOUNTS *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_accounts");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("enum_context   ", ps, depth, &q_q->enum_context))
+               return False;
+       if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_accounts(LSA_R_ENUM_ACCOUNTS *r_u, uint32 enum_context)
+{
+       DEBUG(5, ("init_lsa_r_enum_accounts\n"));
+
+       r_u->enum_context=enum_context;
+       if (r_u->enum_context!=0) {
+               r_u->sids.num_entries=enum_context;
+               r_u->sids.ptr_sid_enum=1;
+               r_u->sids.num_entries2=enum_context;
+       } else {
+               r_u->sids.num_entries=0;
+               r_u->sids.ptr_sid_enum=0;
+               r_u->sids.num_entries2=0;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_accounts(const char *desc, LSA_R_ENUM_ACCOUNTS *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_accounts");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+               return False;
+
+       if (!lsa_io_sid_enum("sids", &r_q->sids, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_q_unk_get_connuser(const char *desc, LSA_Q_UNK_GET_CONNUSER *q_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_unk_get_connuser");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+   
+       if(!prs_uint32("ptr_srvname", ps, depth, &q_c->ptr_srvname))
+               return False;
+
+       if(!smb_io_unistr2("uni2_srvname", &q_c->uni2_srvname, q_c->ptr_srvname, ps, depth)) /* server name to be looked up */
+               return False;
+
+       if (!prs_align(ps))
+         return False;
+
+       if(!prs_uint32("unk1", ps, depth, &q_c->unk1))
+               return False;
+       if(!prs_uint32("unk2", ps, depth, &q_c->unk2))
+               return False;
+       if(!prs_uint32("unk3", ps, depth, &q_c->unk3))
+               return False;
+
+       /* Don't bother to read or write at present... */
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_r_unk_get_connuser(const char *desc, LSA_R_UNK_GET_CONNUSER *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_unk_get_connuser");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+   
+       if(!prs_uint32("ptr_user_name", ps, depth, &r_c->ptr_user_name))
+               return False;
+       if(!smb_io_unihdr("hdr_user_name", &r_c->hdr_user_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni2_user_name", &r_c->uni2_user_name, r_c->ptr_user_name, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+         return False;
+       
+       if(!prs_uint32("unk1", ps, depth, &r_c->unk1))
+               return False;
+
+       if(!prs_uint32("ptr_dom_name", ps, depth, &r_c->ptr_dom_name))
+               return False;
+       if(!smb_io_unihdr("hdr_dom_name", &r_c->hdr_dom_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni2_dom_name", &r_c->uni2_dom_name, r_c->ptr_dom_name, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+         return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+void init_lsa_q_open_account(LSA_Q_OPENACCOUNT *trn, POLICY_HND *hnd, DOM_SID *sid, uint32 desired_access)
+{
+       memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+       init_dom_sid2(&trn->sid, sid);
+       trn->access = desired_access;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_account(const char *desc, LSA_Q_OPENACCOUNT *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_open_account");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &r_c->sid, ps, depth)) /* domain SID */
+               return False;
+
+       if(!prs_uint32("access", ps, depth, &r_c->access))
+               return False;
+  
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_account(const char *desc, LSA_R_OPENACCOUNT  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_open_account");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+void init_lsa_q_enum_privsaccount(LSA_Q_ENUMPRIVSACCOUNT *trn, POLICY_HND *hnd)
+{
+       memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_privsaccount(const char *desc, LSA_Q_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_privsaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID structure.
+********************************************************************/
+
+static BOOL lsa_io_luid(const char *desc, LUID *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_luid");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("low", ps, depth, &r_c->low))
+               return False;
+
+       if(!prs_uint32("high", ps, depth, &r_c->high))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_luid_attr(const char *desc, LUID_ATTR *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_luid_attr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if (!lsa_io_luid(desc, &r_c->luid, ps, depth))
+               return False;
+
+       if(!prs_uint32("attr", ps, depth, &r_c->attr))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an PRIVILEGE_SET structure.
+********************************************************************/
+
+static BOOL lsa_io_privilege_set(const char *desc, PRIVILEGE_SET *r_c, prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       prs_debug(ps, depth, desc, "lsa_io_privilege_set");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("count", ps, depth, &r_c->count))
+               return False;
+       if(!prs_uint32("control", ps, depth, &r_c->control))
+               return False;
+
+       for (i=0; i<r_c->count; i++) {
+               if (!lsa_io_luid_attr(desc, &r_c->set[i], ps, depth))
+                       return False;
+       }
+       
+       return True;
+}
+
+void init_lsa_r_enum_privsaccount(LSA_R_ENUMPRIVSACCOUNT *r_u, LUID_ATTR *set, uint32 count, uint32 control)
+{
+       r_u->ptr=1;
+       r_u->count=count;
+       r_u->set.set=set;
+       r_u->set.count=count;
+       r_u->set.control=control;
+       DEBUG(10,("init_lsa_r_enum_privsaccount: %d %d privileges\n", r_u->count, r_u->set.count));
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_privsaccount(const char *desc, LSA_R_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_privsaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+               return False;
+
+       if (r_c->ptr!=0) {
+               if(!prs_uint32("count", ps, depth, &r_c->count))
+                       return False;
+
+               /* malloc memory if unmarshalling here */
+
+               if (UNMARSHALLING(ps) && r_c->count!=0) {
+                       if (!(r_c->set.set = (LUID_ATTR *)prs_alloc_mem(ps,sizeof(LUID_ATTR) * r_c->count)))
+                               return False;
+
+               }
+               
+               if(!lsa_io_privilege_set(desc, &r_c->set, ps, depth))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+
+/*******************************************************************
+ Reads or writes an  LSA_Q_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_q_getsystemaccount(const char *desc, LSA_Q_GETSYSTEMACCOUNT  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_getsystemaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an  LSA_R_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_r_getsystemaccount(const char *desc, LSA_R_GETSYSTEMACCOUNT  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_getsystemaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("access", ps, depth, &r_c->access))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_setsystemaccount(const char *desc, LSA_Q_SETSYSTEMACCOUNT  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_setsystemaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("access", ps, depth, &r_c->access))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_setsystemaccount(const char *desc, LSA_R_SETSYSTEMACCOUNT  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_setsystemaccount");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+void init_lsa_q_lookupprivvalue(LSA_Q_LOOKUPPRIVVALUE *trn, POLICY_HND *hnd, const char *name)
+{
+       int len_name = strlen(name);
+       memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+       if(len_name == 0)
+               len_name = 1;
+
+       init_uni_hdr(&trn->hdr_right, len_name);
+       init_unistr2(&trn->uni2_right, name, len_name);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_LOOKUPPRIVVALUE  structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookupprivvalue(const char *desc, LSA_Q_LOOKUPPRIVVALUE  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_lookupprivvalue");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+       if(!smb_io_unihdr ("hdr_name", &r_c->hdr_right, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni2_right", &r_c->uni2_right, r_c->hdr_right.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an  LSA_R_LOOKUPPRIVVALUE structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookupprivvalue(const char *desc, LSA_R_LOOKUPPRIVVALUE  *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_lookupprivvalue");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if(!lsa_io_luid("luid", &r_c->luid, ps, depth))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_addprivs(const char *desc, LSA_Q_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_addprivs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+       
+       if(!prs_uint32("count", ps, depth, &r_c->count))
+               return False;
+
+       if (UNMARSHALLING(ps) && r_c->count!=0) {
+               if (!(r_c->set.set = (LUID_ATTR *)prs_alloc_mem(ps,sizeof(LUID_ATTR) * r_c->count)))
+                       return False;
+       }
+       
+       if(!lsa_io_privilege_set(desc, &r_c->set, ps, depth))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_addprivs(const char *desc, LSA_R_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_addprivs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_removeprivs(const char *desc, LSA_Q_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_removeprivs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+               return False;
+       
+       if(!prs_uint32("allrights", ps, depth, &r_c->allrights))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+               return False;
+
+       /* 
+        * JFM: I'm not sure at all if the count is inside the ptr
+        * never seen one with ptr=0
+        */
+
+       if (r_c->ptr!=0) {
+               if(!prs_uint32("count", ps, depth, &r_c->count))
+                       return False;
+
+               if (UNMARSHALLING(ps) && r_c->count!=0) {
+                       if (!(r_c->set.set = (LUID_ATTR *)prs_alloc_mem(ps,sizeof(LUID_ATTR) * r_c->count)))
+                               return False;
+               }
+
+               if(!lsa_io_privilege_set(desc, &r_c->set, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_removeprivs(const char *desc, LSA_R_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_removeprivs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+BOOL policy_handle_is_valid(const POLICY_HND *hnd)
+{
+       POLICY_HND zero_pol;
+
+       ZERO_STRUCT(zero_pol);
+       return ((memcmp(&zero_pol, hnd, sizeof(POLICY_HND)) == 0) ? False : True );
+}
+
+/*******************************************************************
+ Reads or writes an LSA_DNS_DOM_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_dns_dom_info(const char *desc, LSA_DNS_DOM_INFO *info,
+                        prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_dns_dom_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unihdr("nb_name", &info->hdr_nb_dom_name, ps, depth))
+               return False;
+       if(!smb_io_unihdr("dns_name", &info->hdr_dns_dom_name, ps, depth))
+               return False;
+       if(!smb_io_unihdr("forest", &info->hdr_forest_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if (!prs_uint8s(False, "dom_guid", ps, depth, info->dom_guid.info, GUID_SIZE))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("dom_sid", ps, depth, &info->ptr_dom_sid))
+               return False;
+
+       if(!smb_io_unistr2("nb_name", &info->uni_nb_dom_name,
+                          info->hdr_nb_dom_name.buffer, ps, depth))
+               return False;
+       if(!smb_io_unistr2("dns_name", &info->uni_dns_dom_name, 
+                          info->hdr_dns_dom_name.buffer, ps, depth))
+               return False;
+       if(!smb_io_unistr2("forest", &info->uni_forest_name, 
+                          info->hdr_forest_name.buffer, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
+               return False;
+
+       return True;
+       
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO2 structure.
+********************************************************************/
+
+void init_q_query2(LSA_Q_QUERY_INFO2 *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+       DEBUG(5, ("init_q_query2\n"));
+
+       memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+       q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_info2(const char *desc, LSA_Q_QUERY_INFO2 *q_c,
+                         prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_query_info2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("pol", &q_c->pol, ps, depth))
+               return False;
+       
+       if(!prs_uint16("info_class", ps, depth, &q_c->info_class))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_info2(const char *desc, LSA_R_QUERY_INFO2 *r_c,
+                         prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_query_info2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+               return False;
+       if(!prs_uint16("info_class", ps, depth, &r_c->info_class))
+               return False;
+       switch(r_c->info_class) {
+       case 0x000c:
+               if (!lsa_io_dns_dom_info("info12", &r_c->info.dns_dom_info,
+                                        ps, depth))
+                       return False;
+               break;
+       default:
+               DEBUG(0,("lsa_io_r_query_info2: unknown info class %d\n",
+                        r_c->info_class));
+               return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_enum_acct_rights(LSA_Q_ENUM_ACCT_RIGHTS *q_q, 
+                            POLICY_HND *hnd, 
+                            uint32 count, 
+                            DOM_SID *sid)
+{
+       DEBUG(5, ("init_q_enum_acct_rights\n"));
+
+       q_q->pol = *hnd;
+       init_dom_sid2(&q_q->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_enum_acct_rights(const char *desc, LSA_Q_ENUM_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+       
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_acct_rights");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_enum_acct_rights(const char *desc, LSA_R_ENUM_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_acct_rights");
+       depth++;
+
+       if(!prs_uint32("count   ", ps, depth, &r_c->count))
+               return False;
+
+       if(!smb_io_unistr2_array("rights", &r_c->rights, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+void init_r_enum_acct_rights(LSA_R_ENUM_ACCT_RIGHTS *q_r, 
+                            uint32 count, 
+                            const char **rights)
+{
+       DEBUG(5, ("init_r_enum_acct_rights\n"));
+
+       q_r->count = count;
+       init_unistr2_array(&q_r->rights, count, rights);
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_add_acct_rights(LSA_Q_ADD_ACCT_RIGHTS *q_q, 
+                           POLICY_HND *hnd, 
+                           DOM_SID *sid,
+                           uint32 count, 
+                           const char **rights)
+{
+       DEBUG(5, ("init_q_add_acct_rights\n"));
+
+       q_q->pol = *hnd;
+       init_dom_sid2(&q_q->sid, sid);
+       init_unistr2_array(&q_q->rights, count, rights);
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_add_acct_rights(const char *desc, LSA_Q_ADD_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_add_acct_rights");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+               return False;
+
+       if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+               return False;
+
+       if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_add_acct_rights(const char *desc, LSA_R_ADD_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_add_acct_rights");
+       depth++;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+void init_r_add_acct_rights(LSA_R_ADD_ACCT_RIGHTS *q_r)
+{
+       DEBUG(5, ("init_r_add_acct_rights\n"));
+       /* oh what a silly function! */
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_remove_acct_rights(LSA_Q_REMOVE_ACCT_RIGHTS *q_q, 
+                              POLICY_HND *hnd, 
+                              DOM_SID *sid,
+                              uint32 removeall,
+                              uint32 count, 
+                              const char **rights)
+{
+       DEBUG(5, ("init_q_remove_acct_rights\n"));
+
+       q_q->pol = *hnd;
+       init_dom_sid2(&q_q->sid, sid);
+       q_q->removeall = removeall;
+       init_unistr2_array(&q_q->rights, count, rights);
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_remove_acct_rights(const char *desc, LSA_Q_REMOVE_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_remove_acct_rights");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+               return False;
+
+       if(!prs_uint32("removeall", ps, depth, &q_q->removeall))
+               return False;
+
+       if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+               return False;
+
+       if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_remove_acct_rights(const char *desc, LSA_R_REMOVE_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_remove_acct_rights");
+       depth++;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+void init_r_remove_acct_rights(LSA_R_REMOVE_ACCT_RIGHTS *q_r)
+{
+       DEBUG(5, ("init_r_remove_acct_rights\n"));
+}
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_ACCT_WITH_RIGHT structure.
+********************************************************************/
+void init_q_enum_acct_with_right(LSA_Q_ENUM_ACCT_WITH_RIGHT *q_q, 
+                                POLICY_HND *hnd, 
+                                const char *right)
+{
+       DEBUG(5, ("init_q_enum_acct_with_right\n"));
+
+       q_q->pol = *hnd;
+       init_unistr2(&q_q->right, right, strlen(right));
+       init_str_hdr(&q_q->right_hdr, 
+                    q_q->right.uni_max_len*2, 
+                    q_q->right.uni_max_len*2, right?1:0);
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_ENUM_ACCT_WITH_RIGHT structure.
+********************************************************************/
+BOOL lsa_io_q_enum_acct_with_right(const char *desc, LSA_Q_ENUM_ACCT_WITH_RIGHT *q_q, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_q_enum_acct_with_right");
+       depth++;
+
+       if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+
+       if (!prs_uint32("ref_id  ", ps, depth, &q_q->right_hdr.buffer))
+               return False;
+
+       if (UNMARSHALLING(ps) && q_q->right_hdr.buffer == 0) {
+               return True;
+       }
+
+       if (!smb_io_strhdr("", &q_q->right_hdr, ps, depth))
+               return False;
+
+       if (!smb_io_unistr2("", &q_q->right, q_q->right_hdr.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_WITH_RIGHT structure.
+********************************************************************/
+BOOL lsa_io_r_enum_acct_with_right(const char *desc, LSA_R_ENUM_ACCT_WITH_RIGHT *r_c, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "lsa_io_r_enum_acct_with_right");
+       depth++;
+
+       if (!prs_uint32("count  ", ps, depth, &r_c->count))
+               return False;
+
+       if (!smb_io_sid_array("sids  ", &r_c->sids, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_ACCT_WITH_RIGHT structure.
+********************************************************************/
+void init_r_enum_acct_with_right(LSA_R_ENUM_ACCT_WITH_RIGHT *r_c, 
+                                uint32 count,
+                                DOM_SID *sids)
+{
+       DEBUG(5, ("init_r_enum_acct_with_right\n"));
+
+       r_c->count = count;
+       init_sid_array(&r_c->sids, count, sids);
+}
diff --git a/source4/rpc_parse/parse_misc.c b/source4/rpc_parse/parse_misc.c
new file mode 100644 (file)
index 0000000..ad50c4c
--- /dev/null
@@ -0,0 +1,1784 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/****************************************************************************
+ A temporary TALLOC context for things like unistrs, that is valid for
+ the life of a complete RPC call.
+****************************************************************************/
+
+static TALLOC_CTX *current_rpc_talloc = NULL;
+
+TALLOC_CTX *get_current_rpc_talloc(void)
+{
+    return current_rpc_talloc;
+}
+
+void set_current_rpc_talloc( TALLOC_CTX *ctx)
+{
+       current_rpc_talloc = ctx;
+}
+
+static TALLOC_CTX *main_loop_talloc = NULL;
+
+/*******************************************************************
+free up temporary memory - called from the main loop
+********************************************************************/
+
+void main_loop_talloc_freeREWRITE(void)
+{
+    if (!main_loop_talloc)
+        return;
+    talloc_destroy(main_loop_talloc);
+    main_loop_talloc = NULL;
+}
+
+/*******************************************************************
+ Get a talloc context that is freed in the main loop...
+********************************************************************/
+
+TALLOC_CTX *main_loop_talloc_get(void)
+{
+    if (!main_loop_talloc) {
+        main_loop_talloc = talloc_init("main loop talloc (mainly parse_misc)");
+        if (!main_loop_talloc)
+            smb_panic("main_loop_talloc: malloc fail\n");
+    }
+
+    return main_loop_talloc;
+}
+
+/*******************************************************************
+ Try and get a talloc context. Get the rpc one if possible, else
+ get the main loop one. The main loop one is more dangerous as it
+ goes away between packets, the rpc one will stay around for as long
+ as a current RPC lasts.
+********************************************************************/ 
+
+TALLOC_CTX *get_talloc_ctx(void)
+{
+       TALLOC_CTX *tc = get_current_rpc_talloc();
+
+       if (tc)
+               return tc;
+       return main_loop_talloc_get();
+}
+
+/*******************************************************************
+ Reads or writes a UTIME type.
+********************************************************************/
+
+static BOOL smb_io_utime(const char *desc, UTIME *t, prs_struct *ps, int depth)
+{
+       if (t == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_utime");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32 ("time", ps, depth, &t->time))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+
+BOOL smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+       if (nttime == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_time");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+               return False;
+       if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a LOOKUP_LEVEL structure.
+********************************************************************/
+
+BOOL smb_io_lookup_level(const char *desc, LOOKUP_LEVEL *level, prs_struct *ps, int depth)
+{
+       if (level == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_lookup_level");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint16("value", ps, depth, &level->value))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Gets an enumeration handle from an ENUM_HND structure.
+********************************************************************/
+
+uint32 get_enum_hnd(ENUM_HND *enh)
+{
+       return (enh && enh->ptr_hnd != 0) ? enh->handle : 0;
+}
+
+/*******************************************************************
+ Inits an ENUM_HND structure.
+********************************************************************/
+
+void init_enum_hnd(ENUM_HND *enh, uint32 hnd)
+{
+       DEBUG(5,("smb_io_enum_hnd\n"));
+
+       enh->ptr_hnd = (hnd != 0) ? 1 : 0;
+       enh->handle = hnd;
+}
+
+/*******************************************************************
+ Reads or writes an ENUM_HND structure.
+********************************************************************/
+
+BOOL smb_io_enum_hnd(const char *desc, ENUM_HND *hnd, prs_struct *ps, int depth)
+{
+       if (hnd == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_enum_hnd");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_hnd", ps, depth, &hnd->ptr_hnd)) /* pointer */
+               return False;
+
+       if (hnd->ptr_hnd != 0) {
+               if(!prs_uint32("handle ", ps, depth, &hnd->handle )) /* enum handle */
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+       int i;
+
+       if (sid == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_dom_sid");
+       depth++;
+
+       if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+               return False;
+
+       if(!prs_uint8 ("num_auths  ", ps, depth, &sid->num_auths))
+               return False;
+
+       for (i = 0; i < 6; i++)
+       {
+               fstring tmp;
+               slprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+               if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+                       return False;
+       }
+
+       /* oops! XXXX should really issue a warning here... */
+       if (sid->num_auths > MAXSUBAUTHS)
+               sid->num_auths = MAXSUBAUTHS;
+
+       if(!prs_uint32s(False, "sub_auths ", ps, depth, sid->sub_auths, sid->num_auths))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_SID structure.
+
+ BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 
+ identauth >= 2^32 can be detected because it will be specified in hex
+********************************************************************/
+
+void init_dom_sid(DOM_SID *sid, const char *str_sid)
+{
+       pstring domsid;
+       int identauth;
+       char *p;
+
+       if (str_sid == NULL) {
+               DEBUG(4,("netlogon domain SID: none\n"));
+               sid->sid_rev_num = 0;
+               sid->num_auths = 0;
+               return;
+       }
+               
+       pstrcpy(domsid, str_sid);
+
+       DEBUG(4,("init_dom_sid %d SID:  %s\n", __LINE__, domsid));
+
+       /* assume, but should check, that domsid starts "S-" */
+       p = strtok(domsid+2,"-");
+       sid->sid_rev_num = atoi(p);
+
+       /* identauth in decimal should be <  2^32 */
+       /* identauth in hex     should be >= 2^32 */
+       identauth = atoi(strtok(0,"-"));
+
+       DEBUG(4,("netlogon rev %d\n", sid->sid_rev_num));
+       DEBUG(4,("netlogon %s ia %d\n", p, identauth));
+
+       sid->id_auth[0] = 0;
+       sid->id_auth[1] = 0;
+       sid->id_auth[2] = (identauth & 0xff000000) >> 24;
+       sid->id_auth[3] = (identauth & 0x00ff0000) >> 16;
+       sid->id_auth[4] = (identauth & 0x0000ff00) >> 8;
+       sid->id_auth[5] = (identauth & 0x000000ff);
+
+       sid->num_auths = 0;
+
+       while ((p = strtok(0, "-")) != NULL && sid->num_auths < MAXSUBAUTHS)
+               sid->sub_auths[sid->num_auths++] = atoi(p);
+
+       DEBUG(4,("init_dom_sid: %d SID:  %s\n", __LINE__, domsid));
+}
+
+/*******************************************************************
+ Inits a DOM_SID2 structure.
+********************************************************************/
+
+void init_dom_sid2(DOM_SID2 *sid2, const DOM_SID *sid)
+{
+       sid2->sid = *sid;
+       sid2->num_auths = sid2->sid.num_auths;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid2(const char *desc, DOM_SID2 *sid, prs_struct *ps, int depth)
+{
+       if (sid == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_dom_sid2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("num_auths", ps, depth, &sid->num_auths))
+               return False;
+
+       if(!smb_io_dom_sid("sid", &sid->sid, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+creates a STRHDR structure.
+********************************************************************/
+
+void init_str_hdr(STRHDR *hdr, int max_len, int len, uint32 buffer)
+{
+       hdr->str_max_len = max_len;
+       hdr->str_str_len = len;
+       hdr->buffer      = buffer;
+}
+
+/*******************************************************************
+ Reads or writes a STRHDR structure.
+********************************************************************/
+
+BOOL smb_io_strhdr(const char *desc,  STRHDR *hdr, prs_struct *ps, int depth)
+{
+       if (hdr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_strhdr");
+       depth++;
+
+       prs_align(ps);
+       
+       if(!prs_uint16("str_str_len", ps, depth, &hdr->str_str_len))
+               return False;
+       if(!prs_uint16("str_max_len", ps, depth, &hdr->str_max_len))
+               return False;
+       if(!prs_uint32("buffer     ", ps, depth, &hdr->buffer))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a UNIHDR structure.
+********************************************************************/
+
+void init_uni_hdr(UNIHDR *hdr, int len)
+{
+       hdr->uni_str_len = 2 * len;
+       hdr->uni_max_len = 2 * len;
+       hdr->buffer      = len != 0 ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR structure.
+********************************************************************/
+
+BOOL smb_io_unihdr(const char *desc, UNIHDR *hdr, prs_struct *ps, int depth)
+{
+       if (hdr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_unihdr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint16("uni_str_len", ps, depth, &hdr->uni_str_len))
+               return False;
+       if(!prs_uint16("uni_max_len", ps, depth, &hdr->uni_max_len))
+               return False;
+       if(!prs_uint32("buffer     ", ps, depth, &hdr->buffer))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a BUFHDR structure.
+********************************************************************/
+
+void init_buf_hdr(BUFHDR *hdr, int max_len, int len)
+{
+       hdr->buf_max_len = max_len;
+       hdr->buf_len     = len;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_pre(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth, uint32 *offset)
+{
+       (*offset) = prs_offset(ps);
+       if (ps->io) {
+
+               /* reading. */
+
+               if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+                       return False;
+
+       } else {
+
+               /* writing. */
+
+               if(!prs_set_offset(ps, prs_offset(ps) + (sizeof(uint32) * 2)))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ smb_io_hdrbuf wrapper. Call this and it retrospectively stores the size.
+ Does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_post(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth, 
+                               uint32 ptr_hdrbuf, uint32 max_len, uint32 len)
+{
+       if (!ps->io) {
+               /* writing: go back and do a retrospective job.  i hate this */
+
+               uint32 old_offset = prs_offset(ps);
+
+               init_buf_hdr(hdr, max_len, len);
+               if(!prs_set_offset(ps, ptr_hdrbuf))
+                       return False;
+               if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+                       return False;
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a BUFHDR structure.
+********************************************************************/
+
+BOOL smb_io_hdrbuf(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth)
+{
+       if (hdr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_hdrbuf");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("buf_max_len", ps, depth, &hdr->buf_max_len))
+               return False;
+       if(!prs_uint32("buf_len    ", ps, depth, &hdr->buf_len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+creates a UNIHDR2 structure.
+********************************************************************/
+
+void init_uni_hdr2(UNIHDR2 *hdr, int len)
+{
+       init_uni_hdr(&hdr->unihdr, len);
+       hdr->buffer = (len > 0) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR2 structure.
+********************************************************************/
+
+BOOL smb_io_unihdr2(const char *desc, UNIHDR2 *hdr2, prs_struct *ps, int depth)
+{
+       if (hdr2 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_unihdr2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr", &hdr2->unihdr, ps, depth))
+               return False;
+       if(!prs_uint32("buffer", ps, depth, &hdr2->buffer))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR structure.
+********************************************************************/
+
+void init_unistr(UNISTR *str, const char *buf)
+{
+       size_t len;
+
+       if (buf == NULL) {
+               str->buffer = NULL;
+               return;
+       }
+               
+
+       len = strlen(buf) + 1;
+
+       if (len < MAX_UNISTRLEN)
+               len = MAX_UNISTRLEN;
+       len *= sizeof(uint16);
+
+       str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+       if (str->buffer == NULL)
+               smb_panic("init_unistr: malloc fail\n");
+
+       rpcstr_push(str->buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+reads or writes a UNISTR structure.
+XXXX NOTE: UNISTR structures NEED to be null-terminated.
+********************************************************************/
+
+BOOL smb_io_unistr(const char *desc, UNISTR *uni, prs_struct *ps, int depth)
+{
+       if (uni == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_unistr");
+       depth++;
+
+       if(!prs_unistr("unistr", ps, depth, uni))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Allocate the BUFFER3 memory.
+********************************************************************/
+
+static void create_buffer3(BUFFER3 *str, size_t len)
+{
+       if (len < MAX_BUFFERLEN)
+               len = MAX_BUFFERLEN;
+
+    str->buffer = talloc_zero(get_talloc_ctx(), len);
+       if (str->buffer == NULL)
+               smb_panic("create_buffer3: talloc fail\n");
+
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a uint32
+********************************************************************/
+
+void init_buffer3_uint32(BUFFER3 *str, uint32 val)
+{
+       ZERO_STRUCTP(str);
+
+       /* set up string lengths. */
+       str->buf_max_len = sizeof(uint32);
+       str->buf_len     = sizeof(uint32);
+
+       create_buffer3(str, sizeof(uint32));
+       SIVAL(str->buffer, 0, val);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_str(BUFFER3 *str, const char *buf, int len)
+{
+       ZERO_STRUCTP(str);
+
+       /* set up string lengths. */
+       str->buf_max_len = len * 2;
+       str->buf_len = len * 2;
+
+       create_buffer3(str, str->buf_max_len);
+
+       rpcstr_push(str->buffer, buf, str->buf_max_len, STR_TERMINATE);
+       
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a hex string.
+********************************************************************/
+
+void init_buffer3_hex(BUFFER3 *str, const char *buf)
+{
+       ZERO_STRUCTP(str);
+       create_buffer3(str, strlen(buf));
+       str->buf_max_len = str->buf_len = strhex_to_str((char *)str->buffer, sizeof(str->buffer), buf);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_bytes(BUFFER3 *str, uint8 *buf, int len)
+{
+       ZERO_STRUCTP(str);
+
+       /* max buffer size (allocated size) */
+       str->buf_max_len = len;
+       if (buf != NULL) {
+               create_buffer3(str, len);
+               memcpy(str->buffer, buf, len);
+       }
+       str->buf_len = buf != NULL ? len : 0;
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER3 structure.
+   the uni_max_len member tells you how large the buffer is.
+   the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer3(const char *desc, BUFFER3 *buf3, prs_struct *ps, int depth)
+{
+       if (buf3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_buffer3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("uni_max_len", ps, depth, &buf3->buf_max_len))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               buf3->buffer = (unsigned char *)prs_alloc_mem(ps, buf3->buf_max_len);
+               if (buf3->buffer == NULL)
+                       return False;
+       }
+
+       if(!prs_uint8s(True, "buffer     ", ps, depth, buf3->buffer, buf3->buf_max_len))
+               return False;
+
+       if(!prs_uint32("buf_len    ", ps, depth, &buf3->buf_len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER5 structure.
+the buf_len member tells you how large the buffer is.
+********************************************************************/
+BOOL smb_io_buffer5(const char *desc, BUFFER5 *buf5, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_buffer5");
+       depth++;
+
+       if (buf5 == NULL) return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("buf_len", ps, depth, &buf5->buf_len))
+               return False;
+
+       if(buf5->buf_len) {
+               if(!prs_buffer5(True, "buffer" , ps, depth, buf5))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a BUFFER2 structure.
+********************************************************************/
+
+void init_buffer2(BUFFER2 *str, const uint8 *buf, int len)
+{
+       ZERO_STRUCTP(str);
+
+       /* max buffer size (allocated size) */
+       str->buf_max_len = len;
+       str->undoc       = 0;
+       str->buf_len = buf != NULL ? len : 0;
+
+       if (buf != NULL) {
+               if (len < MAX_BUFFERLEN)
+                       len = MAX_BUFFERLEN;
+               str->buffer = talloc_zero(get_talloc_ctx(), len);
+               if (str->buffer == NULL)
+                       smb_panic("init_buffer2: talloc fail\n");
+               memcpy(str->buffer, buf, MIN(str->buf_len, len));
+       }
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER2 structure.
+   the uni_max_len member tells you how large the buffer is.
+   the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer2(const char *desc, BUFFER2 *buf2, uint32 buffer, prs_struct *ps, int depth)
+{
+       if (buf2 == NULL)
+               return False;
+
+       if (buffer) {
+
+               prs_debug(ps, depth, desc, "smb_io_buffer2");
+               depth++;
+
+               if(!prs_align(ps))
+                       return False;
+               
+               if(!prs_uint32("uni_max_len", ps, depth, &buf2->buf_max_len))
+                       return False;
+               if(!prs_uint32("undoc      ", ps, depth, &buf2->undoc))
+                       return False;
+               if(!prs_uint32("buf_len    ", ps, depth, &buf2->buf_len))
+                       return False;
+
+               /* buffer advanced by indicated length of string
+                  NOT by searching for null-termination */
+
+               if(!prs_buffer2(True, "buffer     ", ps, depth, buf2))
+                       return False;
+
+       } else {
+
+               prs_debug(ps, depth, desc, "smb_io_buffer2 - NULL");
+               depth++;
+               memset((char *)buf2, '\0', sizeof(*buf2));
+
+       }
+       return True;
+}
+
+/*******************************************************************
+creates a UNISTR2 structure: sets up the buffer, too
+********************************************************************/
+
+void init_buf_unistr2(UNISTR2 *str, uint32 *ptr, const char *buf)
+{
+       if (buf != NULL) {
+
+               *ptr = 1;
+               init_unistr2(str, buf, strlen(buf)+1);
+
+       } else {
+
+               *ptr = 0;
+               init_unistr2(str, "", 0);
+
+       }
+}
+
+/*******************************************************************
+ Copies a UNISTR2 structure.
+********************************************************************/
+
+void copy_unistr2(UNISTR2 *str, const UNISTR2 *from)
+{
+
+       /* set up string lengths. add one if string is not null-terminated */
+       str->uni_max_len = from->uni_max_len;
+       str->undoc       = from->undoc;
+       str->uni_str_len = from->uni_str_len;
+
+       if (from->buffer == NULL)
+               return;
+               
+       /* the string buffer is allocated to the maximum size
+          (the the length of the source string) to prevent
+          reallocation of memory. */
+       if (str->buffer == NULL) {
+               size_t len = from->uni_max_len * sizeof(uint16);
+
+               if (len < MAX_UNISTRLEN)
+                       len = MAX_UNISTRLEN;
+               len *= sizeof(uint16);
+
+               str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+               if ((str->buffer == NULL) && (len > 0 ))
+               {
+                       smb_panic("copy_unistr2: talloc fail\n");
+                       return;
+               }
+       }
+
+       /* copy the string */
+       memcpy(str->buffer, from->buffer, from->uni_max_len*sizeof(uint16));
+}
+
+/*******************************************************************
+ Creates a STRING2 structure.
+********************************************************************/
+
+void init_string2(STRING2 *str, const char *buf, int max_len, int str_len)
+{
+       int alloc_len = 0;
+
+       /* set up string lengths. */
+       str->str_max_len = max_len;
+       str->undoc       = 0;
+       str->str_str_len = str_len;
+
+       /* store the string */
+       if(str_len != 0) {
+               if (str_len < MAX_STRINGLEN)
+                       alloc_len = MAX_STRINGLEN;
+               str->buffer = talloc_zero(get_talloc_ctx(), alloc_len);
+               if (str->buffer == NULL)
+                       smb_panic("init_string2: malloc fail\n");
+               memcpy(str->buffer, buf, str_len);
+  }
+}
+
+/*******************************************************************
+ Reads or writes a STRING2 structure.
+ XXXX NOTE: STRING2 structures need NOT be null-terminated.
+   the str_str_len member tells you how long the string is;
+   the str_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_string2(const char *desc, STRING2 *str2, uint32 buffer, prs_struct *ps, int depth)
+{
+       if (str2 == NULL)
+               return False;
+
+       if (buffer) {
+
+               prs_debug(ps, depth, desc, "smb_io_string2");
+               depth++;
+
+               if(!prs_align(ps))
+                       return False;
+               
+               if(!prs_uint32("str_max_len", ps, depth, &str2->str_max_len))
+                       return False;
+               if(!prs_uint32("undoc      ", ps, depth, &str2->undoc))
+                       return False;
+               if(!prs_uint32("str_str_len", ps, depth, &str2->str_str_len))
+                       return False;
+
+               /* buffer advanced by indicated length of string
+                  NOT by searching for null-termination */
+               if(!prs_string2(True, "buffer     ", ps, depth, str2))
+                       return False;
+
+       } else {
+
+               prs_debug(ps, depth, desc, "smb_io_string2 - NULL");
+               depth++;
+               memset((char *)str2, '\0', sizeof(*str2));
+
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure.
+********************************************************************/
+
+void init_unistr2(UNISTR2 *str, const char *buf, size_t len)
+{
+       ZERO_STRUCTP(str);
+
+       /* set up string lengths. */
+       str->uni_max_len = (uint32)len;
+       str->undoc       = 0;
+       str->uni_str_len = (uint32)len;
+
+       if (len < MAX_UNISTRLEN)
+               len = MAX_UNISTRLEN;
+       len *= sizeof(uint16);
+
+       str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+       if ((str->buffer == NULL) && (len > 0))
+       {
+               smb_panic("init_unistr2: malloc fail\n");
+               return;
+       }
+
+       /*
+        * don't move this test above ! The UNISTR2 must be initialized !!!
+        * jfm, 7/7/2001.
+        */
+       if (buf==NULL)
+               return;
+
+       rpcstr_push((char *)str->buffer, buf, len, STR_TERMINATE);
+}
+
+/** 
+ *  Inits a UNISTR2 structure.
+ *  @param  ctx talloc context to allocate string on
+ *  @param  str pointer to string to create
+ *  @param  buf UCS2 null-terminated buffer to init from
+*/
+
+void init_unistr2_w(TALLOC_CTX *ctx, UNISTR2 *str, const smb_ucs2_t *buf)
+{
+       uint32 len = strlen_w(buf);
+       uint32 max_len = len;
+       uint32 alloc_len;
+
+       ZERO_STRUCTP(str);
+
+       /* set up string lengths. */
+       str->uni_max_len = len;
+       str->undoc       = 0;
+       str->uni_str_len = len;
+
+       if (max_len < MAX_UNISTRLEN)
+               max_len = MAX_UNISTRLEN;
+
+       alloc_len = (max_len + 1) * sizeof(uint16);
+
+       str->buffer = (uint16 *)talloc_zero(ctx, alloc_len);
+       if ((str->buffer == NULL) && (alloc_len > 0))
+       {
+               smb_panic("init_unistr2_w: malloc fail\n");
+               return;
+       }
+       
+       /*
+        * don't move this test above ! The UNISTR2 must be initialized !!!
+        * jfm, 7/7/2001.
+        */
+       if (buf==NULL)
+               return;
+       
+       /* Yes, this is a strncpy( foo, bar, strlen(bar)) - but as
+           long as the buffer above is talloc()ed correctly then this
+           is the correct thing to do */
+       strncpy_w(str->buffer, buf, len + 1);
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure from a UNISTR
+********************************************************************/
+void init_unistr2_from_unistr (UNISTR2 *to, const UNISTR *from)
+{
+
+       uint32 i;
+
+       /* the destination UNISTR2 should never be NULL.
+          if it is it is a programming error */
+
+       /* if the source UNISTR is NULL, then zero out
+          the destination string and return */
+       ZERO_STRUCTP (to);
+       if ((from == NULL) || (from->buffer == NULL))
+               return;
+
+       /* get the length; UNISTR must be NULL terminated */
+       i = 0;
+       while ((from->buffer)[i]!='\0')
+               i++;
+       i++;    /* one more to catch the terminating NULL */
+               /* is this necessary -- jerry?  I need to think */
+
+       /* set up string lengths; uni_max_len is set to i+1
+           because we need to account for the final NULL termination */
+       to->uni_max_len = i;
+       to->undoc       = 0;
+       to->uni_str_len = i;
+
+       /* allocate the space and copy the string buffer */
+       to->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), sizeof(uint16)*(to->uni_str_len));
+       if (to->buffer == NULL)
+               smb_panic("init_unistr2_from_unistr: malloc fail\n");
+       memcpy(to->buffer, from->buffer, to->uni_max_len*sizeof(uint16));
+               
+       return;
+}
+
+
+/*******************************************************************
+ Reads or writes a UNISTR2 structure.
+ XXXX NOTE: UNISTR2 structures need NOT be null-terminated.
+   the uni_str_len member tells you how long the string is;
+   the uni_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_unistr2(const char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth)
+{
+       if (uni2 == NULL)
+               return False;
+
+       if (buffer) {
+
+               prs_debug(ps, depth, desc, "smb_io_unistr2");
+               depth++;
+
+               if(!prs_align(ps))
+                       return False;
+               
+               if(!prs_uint32("uni_max_len", ps, depth, &uni2->uni_max_len))
+                       return False;
+               if(!prs_uint32("undoc      ", ps, depth, &uni2->undoc))
+                       return False;
+               if(!prs_uint32("uni_str_len", ps, depth, &uni2->uni_str_len))
+                       return False;
+
+               /* buffer advanced by indicated length of string
+                  NOT by searching for null-termination */
+               if(!prs_unistr2(True, "buffer     ", ps, depth, uni2))
+                       return False;
+
+       } else {
+
+               prs_debug(ps, depth, desc, "smb_io_unistr2 - NULL");
+               depth++;
+               memset((char *)uni2, '\0', sizeof(*uni2));
+
+       }
+
+       return True;
+}
+
+
+/*
+  initialise a UNISTR_ARRAY from a char**
+*/
+BOOL init_unistr2_array(UNISTR2_ARRAY *array, 
+                      uint32 count, const char **strings)
+{
+       int i;
+
+       array->count = count;
+       array->ref_id = count?1:0;
+       if (array->count == 0) {
+               return True;
+       }
+
+       array->strings = (UNISTR2_ARRAY_EL *)talloc_zero(get_talloc_ctx(), count * sizeof(UNISTR2_ARRAY_EL));
+       if (!array->strings) {
+               return False;
+       }
+
+       for (i=0;i<count;i++) {
+               init_unistr2(&array->strings[i].string, strings[i], strlen(strings[i]));
+               array->strings[i].size = array->strings[i].string.uni_max_len*2;
+               array->strings[i].length = array->strings[i].size;
+               array->strings[i].ref_id = 1;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR2_ARRAY structure.
+********************************************************************/
+BOOL smb_io_unistr2_array(const char *desc, UNISTR2_ARRAY *array, prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "smb_io_unistr2_array");
+       depth++;
+
+       if(!prs_uint32("ref_id", ps, depth, &array->ref_id))
+               return False;
+
+       if (! array->ref_id) {
+               return True;
+       }
+
+       if(!prs_uint32("count", ps, depth, &array->count))
+               return False;
+
+       if (array->count == 0) {
+               return True;
+       }
+
+       if (UNMARSHALLING(ps)) {
+               array->strings = talloc_zero(get_talloc_ctx(), array->count * sizeof(array->strings[0]));
+       }
+       if (! array->strings) {
+               return False;
+       }
+
+       for (i=0;i<array->count;i++) {
+               if(!prs_uint16("length", ps, depth, &array->strings[i].length))
+                       return False;
+               if(!prs_uint16("size", ps, depth, &array->strings[i].size))
+                       return False;
+               if(!prs_uint32("ref_id", ps, depth, &array->strings[i].ref_id))
+                       return False;
+       }
+
+       for (i=0;i<array->count;i++) {
+               if (! smb_io_unistr2("string", &array->strings[i].string, array->strings[i].ref_id, ps, depth)) 
+                       return False;
+       }
+       
+       return True;
+}
+
+
+/*
+  initialise a SID_ARRAY from a list of sids
+*/
+BOOL init_sid_array(SID_ARRAY *array,
+                   uint32 count, DOM_SID *sids)
+{
+       int i;
+
+       array->count = count;
+       array->ref_id = count?1:0;
+       if (array->count == 0) {
+               return True;
+       }
+
+       array->sids = (SID_ARRAY_EL *)talloc_zero(get_talloc_ctx(), count * sizeof(SID_ARRAY_EL));
+       if (!array->sids) {
+               return False;
+       }
+
+       for (i=0;i<count;i++) {
+               array->sids[i].ref_id = 1;
+               init_dom_sid2(&array->sids[i].sid, &sids[i]);
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a SID_ARRAY structure.
+********************************************************************/
+BOOL smb_io_sid_array(const char *desc, SID_ARRAY *array, prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "smb_io_sid_array");
+       depth++;
+
+       if(!prs_uint32("ref_id", ps, depth, &array->ref_id))
+               return False;
+
+       if (! array->ref_id) {
+               return True;
+       }
+
+       if(!prs_uint32("count", ps, depth, &array->count))
+               return False;
+
+       if (array->count == 0) {
+               return True;
+       }
+
+       if (UNMARSHALLING(ps)) {
+               array->sids = talloc_zero(get_talloc_ctx(), array->count * sizeof(array->sids[0]));
+       }
+       if (! array->sids) {
+               return False;
+       }
+
+       for (i=0;i<array->count;i++) {
+               if(!prs_uint32("ref_id", ps, depth, &array->sids[i].ref_id))
+                       return False;
+       }
+
+       for (i=0;i<array->count;i++) {
+               if (!smb_io_dom_sid2("sid", &array->sids[i].sid, ps, depth)) 
+                       return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_RID2 structure.
+********************************************************************/
+
+void init_dom_rid2(DOM_RID2 *rid2, uint32 rid, uint8 type, uint32 idx)
+{
+       rid2->type    = type;
+       rid2->rid     = rid;
+       rid2->rid_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_RID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid2(const char *desc, DOM_RID2 *rid2, prs_struct *ps, int depth)
+{
+       if (rid2 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_dom_rid2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+   
+       if(!prs_uint8("type   ", ps, depth, &rid2->type))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("rid    ", ps, depth, &rid2->rid))
+               return False;
+       if(!prs_uint32("rid_idx", ps, depth, &rid2->rid_idx))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+creates a DOM_RID3 structure.
+********************************************************************/
+
+void init_dom_rid3(DOM_RID3 *rid3, uint32 rid, uint8 type)
+{
+    rid3->rid      = rid;
+    rid3->type1    = type;
+    rid3->ptr_type = 0x1; /* non-zero, basically. */
+    rid3->type2    = 0x1;
+    rid3->unk      = type;
+}
+
+/*******************************************************************
+reads or writes a DOM_RID3 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid3(const char *desc, DOM_RID3 *rid3, prs_struct *ps, int depth)
+{
+       if (rid3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_dom_rid3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("rid     ", ps, depth, &rid3->rid))
+               return False;
+       if(!prs_uint32("type1   ", ps, depth, &rid3->type1))
+               return False;
+       if(!prs_uint32("ptr_type", ps, depth, &rid3->ptr_type))
+               return False;
+       if(!prs_uint32("type2   ", ps, depth, &rid3->type2))
+               return False;
+       if(!prs_uint32("unk     ", ps, depth, &rid3->unk))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_RID4 structure.
+********************************************************************/
+
+void init_dom_rid4(DOM_RID4 *rid4, uint16 unknown, uint16 attr, uint32 rid)
+{
+    rid4->unknown = unknown;
+    rid4->attr    = attr;
+    rid4->rid     = rid;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static void init_clnt_srv(DOM_CLNT_SRV *logp, const char *logon_srv, const char *comp_name)
+{
+       DEBUG(5,("init_clnt_srv: %d\n", __LINE__));
+
+       if (logon_srv != NULL) {
+               logp->undoc_buffer = 1;
+               init_unistr2(&logp->uni_logon_srv, logon_srv, strlen(logon_srv)+1);
+       } else {
+               logp->undoc_buffer = 0;
+       }
+
+       if (comp_name != NULL) {
+               logp->undoc_buffer2 = 1;
+               init_unistr2(&logp->uni_comp_name, comp_name, strlen(comp_name)+1);
+       } else {
+               logp->undoc_buffer2 = 0;
+       }
+}
+
+/*******************************************************************
+ Inits or writes a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static BOOL smb_io_clnt_srv(const char *desc, DOM_CLNT_SRV *logp, prs_struct *ps, int depth)
+{
+       if (logp == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_clnt_srv");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("undoc_buffer ", ps, depth, &logp->undoc_buffer))
+               return False;
+
+       if (logp->undoc_buffer != 0) {
+               if(!smb_io_unistr2("unistr2", &logp->uni_logon_srv, logp->undoc_buffer, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("undoc_buffer2", ps, depth, &logp->undoc_buffer2))
+               return False;
+
+       if (logp->undoc_buffer2 != 0) {
+               if(!smb_io_unistr2("unistr2", &logp->uni_comp_name, logp->undoc_buffer2, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOG_INFO structure.
+********************************************************************/
+
+void init_log_info(DOM_LOG_INFO *logp, const char *logon_srv, const char *acct_name,
+               uint16 sec_chan, const char *comp_name)
+{
+       DEBUG(5,("make_log_info %d\n", __LINE__));
+
+       logp->undoc_buffer = 1;
+
+       init_unistr2(&logp->uni_logon_srv, logon_srv, strlen(logon_srv)+1);
+       init_unistr2(&logp->uni_acct_name, acct_name, strlen(acct_name)+1);
+
+       logp->sec_chan = sec_chan;
+
+       init_unistr2(&logp->uni_comp_name, comp_name, strlen(comp_name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOG_INFO structure.
+********************************************************************/
+
+BOOL smb_io_log_info(const char *desc, DOM_LOG_INFO *logp, prs_struct *ps, int depth)
+{
+       if (logp == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_log_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("undoc_buffer", ps, depth, &logp->undoc_buffer))
+               return False;
+
+       if(!smb_io_unistr2("unistr2", &logp->uni_logon_srv, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("unistr2", &logp->uni_acct_name, True, ps, depth))
+               return False;
+
+       if(!prs_uint16("sec_chan", ps, depth, &logp->sec_chan))
+               return False;
+
+       if(!smb_io_unistr2("unistr2", &logp->uni_comp_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_chal(const char *desc, DOM_CHAL *chal, prs_struct *ps, int depth)
+{
+       if (chal == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_chal");
+       depth++;
+       
+       if(!prs_uint8s (False, "data", ps, depth, chal->data, 8))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CRED structure.
+********************************************************************/
+
+BOOL smb_io_cred(const char *desc,  DOM_CRED *cred, prs_struct *ps, int depth)
+{
+       if (cred == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_cred");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_chal ("", &cred->challenge, ps, depth))
+               return False;
+
+       if(!smb_io_utime("", &cred->timestamp, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+void init_clnt_info2(DOM_CLNT_INFO2 *clnt,
+                               const char *logon_srv, const char *comp_name,
+                               const DOM_CRED *clnt_cred)
+{
+       DEBUG(5,("make_clnt_info: %d\n", __LINE__));
+
+       init_clnt_srv(&clnt->login, logon_srv, comp_name);
+
+       if (clnt_cred != NULL) {
+               clnt->ptr_cred = 1;
+               memcpy(&clnt->cred, clnt_cred, sizeof(clnt->cred));
+       } else {
+               clnt->ptr_cred = 0;
+       }
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info2(const char *desc, DOM_CLNT_INFO2 *clnt, prs_struct *ps, int depth)
+{
+       if (clnt == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_clnt_info2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_clnt_srv("", &clnt->login, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_cred", ps, depth, &clnt->ptr_cred))
+               return False;
+       if(!smb_io_cred("", &clnt->cred, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO structure.
+********************************************************************/
+
+void init_clnt_info(DOM_CLNT_INFO *clnt,
+               const char *logon_srv, const char *acct_name,
+               uint16 sec_chan, const char *comp_name,
+               const DOM_CRED *cred)
+{
+       DEBUG(5,("make_clnt_info\n"));
+
+       init_log_info(&clnt->login, logon_srv, acct_name, sec_chan, comp_name);
+       memcpy(&clnt->cred, cred, sizeof(clnt->cred));
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info(const char *desc,  DOM_CLNT_INFO *clnt, prs_struct *ps, int depth)
+{
+       if (clnt == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_clnt_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_log_info("", &clnt->login, ps, depth))
+               return False;
+       if(!smb_io_cred("", &clnt->cred, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOGON_ID structure.
+********************************************************************/
+
+void init_logon_id(DOM_LOGON_ID *logp, uint32 log_id_low, uint32 log_id_high)
+{
+       DEBUG(5,("make_logon_id: %d\n", __LINE__));
+
+       logp->low  = log_id_low;
+       logp->high = log_id_high;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOGON_ID structure.
+********************************************************************/
+
+BOOL smb_io_logon_id(const char *desc, DOM_LOGON_ID *logp, prs_struct *ps, int depth)
+{
+       if (logp == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_logon_id");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("low ", ps, depth, &logp->low ))
+               return False;
+       if(!prs_uint32("high", ps, depth, &logp->high))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an OWF_INFO structure.
+********************************************************************/
+
+void init_owf_info(OWF_INFO *hash, const uint8 data[16])
+{
+       DEBUG(5,("init_owf_info: %d\n", __LINE__));
+       
+       if (data != NULL)
+               memcpy(hash->data, data, sizeof(hash->data));
+       else
+               memset((char *)hash->data, '\0', sizeof(hash->data));
+}
+
+/*******************************************************************
+ Reads or writes an OWF_INFO structure.
+********************************************************************/
+
+BOOL smb_io_owf_info(const char *desc, OWF_INFO *hash, prs_struct *ps, int depth)
+{
+       if (hash == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_owf_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint8s (False, "data", ps, depth, hash->data, 16))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_GID structure.
+********************************************************************/
+
+BOOL smb_io_gid(const char *desc,  DOM_GID *gid, prs_struct *ps, int depth)
+{
+       if (gid == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_gid");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("g_rid", ps, depth, &gid->g_rid))
+               return False;
+       if(!prs_uint32("attr ", ps, depth, &gid->attr))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an POLICY_HND structure.
+********************************************************************/
+
+BOOL smb_io_pol_hnd(const char *desc, POLICY_HND *pol, prs_struct *ps, int depth)
+{
+       if (pol == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_pol_hnd");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(UNMARSHALLING(ps))
+               ZERO_STRUCTP(pol);
+       
+       if (!prs_uint32("data1", ps, depth, &pol->data1))
+               return False;
+       if (!prs_uint32("data2", ps, depth, &pol->data2))
+               return False;
+       if (!prs_uint16("data3", ps, depth, &pol->data3))
+               return False;
+       if (!prs_uint16("data4", ps, depth, &pol->data4))
+               return False;
+       if(!prs_uint8s (False, "data5", ps, depth, pol->data5, sizeof(pol->data5)))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Create a UNISTR3.
+********************************************************************/
+
+void init_unistr3(UNISTR3 *str, const char *buf)
+{
+       size_t len;
+
+       if (buf == NULL) {
+               str->uni_str_len=0;
+               str->str.buffer = NULL;
+               return;
+       }
+
+       len = strlen(buf) + 1;
+
+       str->uni_str_len=len;
+
+       if (len < MAX_UNISTRLEN)
+               len = MAX_UNISTRLEN;
+
+       len *= sizeof(uint16);
+
+       str->str.buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+       if (str->str.buffer == NULL)
+               smb_panic("init_unistr3: malloc fail\n");
+
+       rpcstr_push((char *)str->str.buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR3 structure.
+********************************************************************/
+
+BOOL smb_io_unistr3(const char *desc, UNISTR3 *name, prs_struct *ps, int depth)
+{
+       if (name == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_unistr3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("uni_str_len", ps, depth, &name->uni_str_len))
+               return False;
+
+       /* don't know if len is specified by uni_str_len member... */
+       /* assume unicode string is unicode-null-terminated, instead */
+
+       if(!prs_unistr3(True, "unistr", name, ps, depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Stream a uint64_struct
+ ********************************************************************/
+BOOL prs_uint64(const char *name, prs_struct *ps, int depth, UINT64_S *data64)
+{
+       return prs_uint32(name, ps, depth+1, &data64->low) &&
+               prs_uint32(name, ps, depth+1, &data64->high);
+}
+
+/*******************************************************************
+reads or writes a BUFHDR2 structure.
+********************************************************************/
+BOOL smb_io_bufhdr2(const char *desc, BUFHDR2 *hdr, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_bufhdr2");
+       depth++;
+
+       prs_align(ps);
+       prs_uint32("info_level", ps, depth, &(hdr->info_level));
+       prs_uint32("length    ", ps, depth, &(hdr->length    ));
+       prs_uint32("buffer    ", ps, depth, &(hdr->buffer    ));
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER4 structure.
+********************************************************************/
+BOOL smb_io_buffer4(const char *desc, BUFFER4 *buf4, uint32 buffer, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_buffer4");
+       depth++;
+
+       prs_align(ps);
+       prs_uint32("buf_len", ps, depth, &(buf4->buf_len));
+
+       if (buf4->buf_len > MAX_BUFFERLEN)
+       {
+               buf4->buf_len = MAX_BUFFERLEN;
+       }
+
+       prs_uint8s(True, "buffer", ps, depth, buf4->buffer, buf4->buf_len);
+
+       return True;
+}
+
+/*******************************************************************
+creates a UNIHDR structure.
+********************************************************************/
+
+BOOL make_uni_hdr(UNIHDR *hdr, int len)
+{
+       if (hdr == NULL)
+       {
+               return False;
+       }
+       hdr->uni_str_len = 2 * len;
+       hdr->uni_max_len = 2 * len;
+       hdr->buffer      = len != 0 ? 1 : 0;
+
+       return True;
+}
+
+/*******************************************************************
+creates a BUFHDR2 structure.
+********************************************************************/
+BOOL make_bufhdr2(BUFHDR2 *hdr, uint32 info_level, uint32 length, uint32 buffer)
+{
+       hdr->info_level = info_level;
+       hdr->length     = length;
+       hdr->buffer     = buffer;
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_net.c b/source4/rpc_parse/parse_net.c
new file mode 100644 (file)
index 0000000..53f660f
--- /dev/null
@@ -0,0 +1,2971 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  Copyright (C) Jean François Micouleau           2002.
+ *
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL net_io_neg_flags(const char *desc, NEG_FLAGS *neg, prs_struct *ps, int depth)
+{
+       if (neg == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_neg_flags");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("neg_flags", ps, depth, &neg->neg_flags))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static void init_netinfo_3(NETLOGON_INFO_3 *info, uint32 flags, uint32 logon_attempts)
+{
+       info->flags          = flags;
+       info->logon_attempts = logon_attempts;
+       info->reserved_1     = 0x0;
+       info->reserved_2     = 0x0;
+       info->reserved_3     = 0x0;
+       info->reserved_4     = 0x0;
+       info->reserved_5     = 0x0;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_3(const char *desc,  NETLOGON_INFO_3 *info, prs_struct *ps, int depth)
+{
+       if (info == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_netinfo_3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("flags         ", ps, depth, &info->flags))
+               return False;
+       if(!prs_uint32("logon_attempts", ps, depth, &info->logon_attempts))
+               return False;
+       if(!prs_uint32("reserved_1    ", ps, depth, &info->reserved_1))
+               return False;
+       if(!prs_uint32("reserved_2    ", ps, depth, &info->reserved_2))
+               return False;
+       if(!prs_uint32("reserved_3    ", ps, depth, &info->reserved_3))
+               return False;
+       if(!prs_uint32("reserved_4    ", ps, depth, &info->reserved_4))
+               return False;
+       if(!prs_uint32("reserved_5    ", ps, depth, &info->reserved_5))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static void init_netinfo_1(NETLOGON_INFO_1 *info, uint32 flags, uint32 pdc_status)
+{
+       info->flags      = flags;
+       info->pdc_status = pdc_status;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_1(const char *desc, NETLOGON_INFO_1 *info, prs_struct *ps, int depth)
+{
+       if (info == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_netinfo_1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("flags     ", ps, depth, &info->flags))
+               return False;
+       if(!prs_uint32("pdc_status", ps, depth, &info->pdc_status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static void init_netinfo_2(NETLOGON_INFO_2 *info, uint32 flags, uint32 pdc_status,
+                               uint32 tc_status, const char *trusted_dc_name)
+{
+       int len_dc_name = strlen(trusted_dc_name);
+       info->flags      = flags;
+       info->pdc_status = pdc_status;
+       info->ptr_trusted_dc_name = 1;
+       info->tc_status  = tc_status;
+
+       if (trusted_dc_name != NULL)
+               init_unistr2(&info->uni_trusted_dc_name, trusted_dc_name, len_dc_name+1);
+       else
+               init_unistr2(&info->uni_trusted_dc_name, "", 1);
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_2(const char *desc, NETLOGON_INFO_2 *info, prs_struct *ps, int depth)
+{
+       if (info == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_netinfo_2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("flags              ", ps, depth, &info->flags))
+               return False;
+       if(!prs_uint32("pdc_status         ", ps, depth, &info->pdc_status))
+               return False;
+       if(!prs_uint32("ptr_trusted_dc_name", ps, depth, &info->ptr_trusted_dc_name))
+               return False;
+       if(!prs_uint32("tc_status          ", ps, depth, &info->tc_status))
+               return False;
+
+       if (info->ptr_trusted_dc_name != 0) {
+               if(!smb_io_unistr2("unistr2", &info->uni_trusted_dc_name, info->ptr_trusted_dc_name, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl2(const char *desc, NET_Q_LOGON_CTRL2 *q_l, prs_struct *ps, int depth)
+{
+       if (q_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_logon_ctrl2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr          ", ps, depth, &q_l->ptr))
+               return False;
+
+       if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+               return False;
+       if(!prs_uint32("query_level  ", ps, depth, &q_l->query_level))
+               return False;
+       if(!prs_uint32("switch_value ", ps, depth, &q_l->switch_value))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl2(NET_Q_LOGON_CTRL2 *q_l, const char *srv_name,
+                           uint32 query_level)
+{
+       DEBUG(5,("init_q_logon_ctrl2\n"));
+
+       q_l->function_code = 0x01;
+       q_l->query_level = query_level;
+       q_l->switch_value  = 0x01;
+
+       init_unistr2(&q_l->uni_server_name, srv_name, strlen(srv_name) + 1);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl2(NET_R_LOGON_CTRL2 *r_l, uint32 query_level,
+                           uint32 flags, uint32 pdc_status, 
+                           uint32 logon_attempts, uint32 tc_status, 
+                           const char *trusted_domain_name)
+{
+       DEBUG(5,("init_r_logon_ctrl2\n"));
+
+       r_l->switch_value  = query_level; /* should only be 0x1 */
+
+       switch (query_level) {
+       case 1:
+               r_l->ptr = 1; /* undocumented pointer */
+               init_netinfo_1(&r_l->logon.info1, flags, pdc_status);   
+               r_l->status = NT_STATUS_OK;
+               break;
+       case 2:
+               r_l->ptr = 1; /* undocumented pointer */
+               init_netinfo_2(&r_l->logon.info2, flags, pdc_status,
+                              tc_status, trusted_domain_name); 
+               r_l->status = NT_STATUS_OK;
+               break;
+       case 3:
+               r_l->ptr = 1; /* undocumented pointer */
+               init_netinfo_3(&r_l->logon.info3, flags, logon_attempts);       
+               r_l->status = NT_STATUS_OK;
+               break;
+       default:
+               DEBUG(2,("init_r_logon_ctrl2: unsupported switch value %d\n",
+                       r_l->switch_value));
+               r_l->ptr = 0; /* undocumented pointer */
+
+               /* take a guess at an error code... */
+               r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+               break;
+       }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl2(const char *desc, NET_R_LOGON_CTRL2 *r_l, prs_struct *ps, int depth)
+{
+       if (r_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_logon_ctrl2");
+       depth++;
+
+       if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+               return False;
+       if(!prs_uint32("ptr          ", ps, depth, &r_l->ptr))
+               return False;
+
+       if (r_l->ptr != 0) {
+               switch (r_l->switch_value) {
+               case 1:
+                       if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+                               return False;
+                       break;
+               case 2:
+                       if(!net_io_netinfo_2("", &r_l->logon.info2, ps, depth))
+                               return False;
+                       break;
+               case 3:
+                       if(!net_io_netinfo_3("", &r_l->logon.info3, ps, depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(2,("net_io_r_logon_ctrl2: unsupported switch value %d\n",
+                               r_l->switch_value));
+                       break;
+               }
+       }
+
+       if(!prs_ntstatus("status       ", ps, depth, &r_l->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl(const char *desc, NET_Q_LOGON_CTRL *q_l, prs_struct *ps, 
+                        int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_q_logon_ctrl");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr          ", ps, depth, &q_l->ptr))
+               return False;
+
+       if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+               return False;
+       if(!prs_uint32("query_level  ", ps, depth, &q_l->query_level))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl(NET_Q_LOGON_CTRL *q_l, const char *srv_name,
+                          uint32 query_level)
+{
+       DEBUG(5,("init_q_logon_ctrl\n"));
+
+       q_l->function_code = 0x01; /* ??? */
+       q_l->query_level = query_level;
+
+       init_unistr2(&q_l->uni_server_name, srv_name, strlen(srv_name) + 1);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl(NET_R_LOGON_CTRL *r_l, uint32 query_level,
+                          uint32 flags, uint32 pdc_status)
+{
+       DEBUG(5,("init_r_logon_ctrl\n"));
+
+       r_l->switch_value  = query_level; /* should only be 0x1 */
+
+       switch (query_level) {
+       case 1:
+               r_l->ptr = 1; /* undocumented pointer */
+               init_netinfo_1(&r_l->logon.info1, flags, pdc_status);   
+               r_l->status = NT_STATUS_OK;
+               break;
+       default:
+               DEBUG(2,("init_r_logon_ctrl: unsupported switch value %d\n",
+                       r_l->switch_value));
+               r_l->ptr = 0; /* undocumented pointer */
+
+               /* take a guess at an error code... */
+               r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+               break;
+       }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl(const char *desc, NET_R_LOGON_CTRL *r_l, prs_struct *ps, 
+                        int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_r_logon_ctrl");
+       depth++;
+
+       if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+               return False;
+       if(!prs_uint32("ptr          ", ps, depth, &r_l->ptr))
+               return False;
+
+       if (r_l->ptr != 0) {
+               switch (r_l->switch_value) {
+               case 1:
+                       if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(2,("net_io_r_logon_ctrl: unsupported switch value %d\n",
+                               r_l->switch_value));
+                       break;
+               }
+       }
+
+       if(!prs_ntstatus("status       ", ps, depth, &r_l->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+void init_r_trust_dom(NET_R_TRUST_DOM_LIST *r_t,
+                       uint32 num_doms, const char *dom_name)
+{
+       int i = 0;
+
+       DEBUG(5,("init_r_trust_dom\n"));
+
+       for (i = 0; i < MAX_TRUST_DOMS; i++) {
+               r_t->uni_trust_dom_name[i].uni_str_len = 0;
+               r_t->uni_trust_dom_name[i].uni_max_len = 0;
+       }
+       if (num_doms > MAX_TRUST_DOMS)
+               num_doms = MAX_TRUST_DOMS;
+
+       for (i = 0; i < num_doms; i++) {
+               fstring domain_name;
+               fstrcpy(domain_name, dom_name);
+               strupper(domain_name);
+               init_unistr2(&r_t->uni_trust_dom_name[i], domain_name, strlen(domain_name)+1);
+               /* the use of UNISTR2 here is non-standard. */
+               r_t->uni_trust_dom_name[i].undoc = 0x1;
+       }
+       
+       r_t->status = NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_r_trust_dom(const char *desc, NET_R_TRUST_DOM_LIST *r_t, prs_struct *ps, int depth)
+{
+       uint32 value;
+
+       if (r_t == NULL)
+                return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_trust_dom");
+       depth++;
+
+       /* temporary code to give a valid response */
+       value=2;
+       if(!prs_uint32("status", ps, depth, &value))
+                return False;
+
+       value=1;
+       if(!prs_uint32("status", ps, depth, &value))
+                return False;
+       value=2;
+       if(!prs_uint32("status", ps, depth, &value))
+                return False;
+
+       value=0;
+       if(!prs_uint32("status", ps, depth, &value))
+                return False;
+
+       value=0;
+       if(!prs_uint32("status", ps, depth, &value))
+                return False;
+
+/* old non working code */
+#if 0
+       int i;
+
+       for (i = 0; i < MAX_TRUST_DOMS; i++) {
+               if (r_t->uni_trust_dom_name[i].uni_str_len == 0)
+                       break;
+               if(!smb_io_unistr2("", &r_t->uni_trust_dom_name[i], True, ps, depth))
+                        return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_t->status))
+                return False;
+#endif
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an NET_Q_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_q_trust_dom(const char *desc, NET_Q_TRUST_DOM_LIST *q_l, prs_struct *ps, int depth)
+{
+       if (q_l == NULL)
+                return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_trust_dom");
+       depth++;
+
+       if(!prs_uint32("ptr          ", ps, depth, &q_l->ptr))
+                return False;
+       if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+void init_q_req_chal(NET_Q_REQ_CHAL *q_c,
+                    const char *logon_srv, const char *logon_clnt,
+                    DOM_CHAL *clnt_chal)
+{
+       DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+
+       q_c->undoc_buffer = 1; /* don't know what this buffer is */
+
+       init_unistr2(&q_c->uni_logon_srv, logon_srv , strlen(logon_srv )+1);
+       init_unistr2(&q_c->uni_logon_clnt, logon_clnt, strlen(logon_clnt)+1);
+
+       memcpy(q_c->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+
+       DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+BOOL net_io_q_req_chal(const char *desc,  NET_Q_REQ_CHAL *q_c, prs_struct *ps, int depth)
+{
+       if (q_c == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_req_chal");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!prs_uint32("undoc_buffer", ps, depth, &q_c->undoc_buffer))
+               return False;
+
+       if(!smb_io_unistr2("", &q_c->uni_logon_srv, True, ps, depth)) /* logon server unicode string */
+               return False;
+       if(!smb_io_unistr2("", &q_c->uni_logon_clnt, True, ps, depth)) /* logon client unicode string */
+               return False;
+
+       if(!smb_io_chal("", &q_c->clnt_chal, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_req_chal(const char *desc, NET_R_REQ_CHAL *r_c, prs_struct *ps, int depth)
+{
+       if (r_c == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_req_chal");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_chal("", &r_c->srv_chal, ps, depth)) /* server challenge */
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_c->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth(const char *desc, NET_Q_AUTH *q_a, prs_struct *ps, int depth)
+{
+       if (q_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_auth");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+               return False;
+       if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth(const char *desc, NET_R_AUTH *r_a, prs_struct *ps, int depth)
+{
+       if (r_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_auth");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_a->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_2 struct.
+********************************************************************/
+
+void init_q_auth_2(NET_Q_AUTH_2 *q_a,
+               const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+               DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+       DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+
+       init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+       memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+       q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+       DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_2(const char *desc, NET_Q_AUTH_2 *q_a, prs_struct *ps, int depth)
+{
+       if (q_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_auth_2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+               return False;
+       if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+               return False;
+       if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_2(const char *desc, NET_R_AUTH_2 *r_a, prs_struct *ps, int depth)
+{
+       if (r_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_auth_2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+               return False;
+       if(!net_io_neg_flags("", &r_a->srv_flgs, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_a->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_3 struct.
+********************************************************************/
+
+void init_q_auth_3(NET_Q_AUTH_3 *q_a,
+               const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+               DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+       DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+
+       init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+       memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+       q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+       DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_3(const char *desc, NET_Q_AUTH_3 *q_a, prs_struct *ps, int depth)
+{
+       if (q_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_auth_3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+               return False;
+       if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+               return False;
+       if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_3(const char *desc, NET_R_AUTH_3 *r_a, prs_struct *ps, int depth)
+{
+       if (r_a == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_auth_3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_chal("srv_chal", &r_a->srv_chal, ps, depth)) /* server challenge */
+               return False;
+       if(!net_io_neg_flags("srv_flgs", &r_a->srv_flgs, ps, depth))
+               return False;
+       if (!prs_uint32("unknown", ps, depth, &r_a->unknown))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_a->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits a NET_Q_SRV_PWSET.
+********************************************************************/
+
+void init_q_srv_pwset(NET_Q_SRV_PWSET *q_s,
+               const char *logon_srv, const char *sess_key, const char *acct_name, 
+                uint16 sec_chan, const char *comp_name,
+               DOM_CRED *cred, uchar hashed_mach_pwd[16])
+{
+       unsigned char nt_cypher[16];
+       
+       DEBUG(5,("init_q_srv_pwset\n"));
+       
+       /* Process the new password. */
+       cred_hash3( nt_cypher, hashed_mach_pwd, sess_key, 1);
+
+       init_clnt_info(&q_s->clnt_id, logon_srv, acct_name, sec_chan, comp_name, cred);
+
+       memcpy(q_s->pwd, nt_cypher, sizeof(q_s->pwd)); 
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_srv_pwset(const char *desc, NET_Q_SRV_PWSET *q_s, prs_struct *ps, int depth)
+{
+       if (q_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_srv_pwset");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_clnt_info("", &q_s->clnt_id, ps, depth)) /* client identification/authentication info */
+               return False;
+       if(!prs_uint8s (False, "pwd", ps, depth, q_s->pwd, 16)) /* new password - undocumented */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_srv_pwset(const char *desc, NET_R_SRV_PWSET *r_s, prs_struct *ps, int depth)
+{
+       if (r_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_srv_pwset");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+    
+       if(!smb_io_cred("", &r_s->srv_cred, ps, depth)) /* server challenge */
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_s->status))
+               return False;
+
+       return True;
+}
+
+/*************************************************************************
+ Init DOM_SID2 array from a string containing multiple sids
+ *************************************************************************/
+
+static int init_dom_sid2s(TALLOC_CTX *ctx, const char *sids_str, DOM_SID2 **ppsids)
+{
+       const char *ptr;
+       pstring s2;
+       int count = 0;
+
+       DEBUG(4,("init_dom_sid2s: %s\n", sids_str ? sids_str:""));
+
+       *ppsids = NULL;
+
+       if(sids_str) {
+               int number;
+               DOM_SID2 *sids;
+
+               /* Count the number of valid SIDs. */
+               for (count = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+                       DOM_SID tmpsid;
+                       if (string_to_sid(&tmpsid, s2))
+                               count++;
+               }
+
+               /* Now allocate space for them. */
+               *ppsids = (DOM_SID2 *)talloc_zero(ctx, count * sizeof(DOM_SID2));
+               if (*ppsids == NULL)
+                       return 0;
+
+               sids = *ppsids;
+
+               for (number = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+                       DOM_SID tmpsid;
+                       if (string_to_sid(&tmpsid, s2)) {
+                               /* count only valid sids */
+                               init_dom_sid2(&sids[number], &tmpsid);
+                               number++;
+                       }
+               }
+       }
+
+       return count;
+}
+
+/*******************************************************************
+ Inits a NET_ID_INFO_1 structure.
+********************************************************************/
+
+void init_id_info1(NET_ID_INFO_1 *id, const char *domain_name,
+                               uint32 param_ctrl, uint32 log_id_low, uint32 log_id_high,
+                               const char *user_name, const char *wksta_name,
+                               const char *sess_key,
+                               unsigned char lm_cypher[16], unsigned char nt_cypher[16])
+{
+       int len_domain_name = strlen(domain_name);
+       int len_user_name   = strlen(user_name  );
+       int len_wksta_name  = strlen(wksta_name );
+
+       unsigned char lm_owf[16];
+       unsigned char nt_owf[16];
+
+       DEBUG(5,("init_id_info1: %d\n", __LINE__));
+
+       id->ptr_id_info1 = 1;
+
+       init_uni_hdr(&id->hdr_domain_name, len_domain_name);
+
+       id->param_ctrl = param_ctrl;
+       init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+       init_uni_hdr(&id->hdr_user_name, len_user_name);
+       init_uni_hdr(&id->hdr_wksta_name, len_wksta_name);
+
+       if (lm_cypher && nt_cypher) {
+               unsigned char key[16];
+#ifdef DEBUG_PASSWORD
+               DEBUG(100,("lm cypher:"));
+               dump_data(100, (char *)lm_cypher, 16);
+
+               DEBUG(100,("nt cypher:"));
+               dump_data(100, (char *)nt_cypher, 16);
+#endif
+
+               memset(key, 0, 16);
+               memcpy(key, sess_key, 8);
+
+               memcpy(lm_owf, lm_cypher, 16);
+               SamOEMhash(lm_owf, key, 16);
+               memcpy(nt_owf, nt_cypher, 16);
+               SamOEMhash(nt_owf, key, 16);
+
+#ifdef DEBUG_PASSWORD
+               DEBUG(100,("encrypt of lm owf password:"));
+               dump_data(100, (char *)lm_owf, 16);
+
+               DEBUG(100,("encrypt of nt owf password:"));
+               dump_data(100, (char *)nt_owf, 16);
+#endif
+               /* set up pointers to cypher blocks */
+               lm_cypher = lm_owf;
+               nt_cypher = nt_owf;
+       }
+
+       init_owf_info(&id->lm_owf, lm_cypher);
+       init_owf_info(&id->nt_owf, nt_cypher);
+
+       init_unistr2(&id->uni_domain_name, domain_name, len_domain_name);
+       init_unistr2(&id->uni_user_name, user_name, len_user_name);
+       init_unistr2(&id->uni_wksta_name, wksta_name, len_wksta_name);
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_id_info1(const char *desc,  NET_ID_INFO_1 *id, prs_struct *ps, int depth)
+{
+       if (id == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_id_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_id_info1", ps, depth, &id->ptr_id_info1))
+               return False;
+
+       if (id->ptr_id_info1 != 0) {
+               if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+                       return False;
+
+               if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+                       return False;
+               if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+                       return False;
+
+               if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+                       return False;
+               if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+                       return False;
+
+               if(!smb_io_owf_info("", &id->lm_owf, ps, depth))
+                       return False;
+               if(!smb_io_owf_info("", &id->nt_owf, ps, depth))
+                       return False;
+
+               if(!smb_io_unistr2("unistr2", &id->uni_domain_name,
+                               id->hdr_domain_name.buffer, ps, depth))
+                       return False;
+               if(!smb_io_unistr2("unistr2", &id->uni_user_name,
+                               id->hdr_user_name.buffer, ps, depth))
+                       return False;
+               if(!smb_io_unistr2("unistr2", &id->uni_wksta_name,
+                               id->hdr_wksta_name.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+Inits a NET_ID_INFO_2 structure.
+
+This is a network logon packet. The log_id parameters
+are what an NT server would generate for LUID once the
+user is logged on. I don't think we care about them.
+
+Note that this has no access to the NT and LM hashed passwords,
+so it forwards the challenge, and the NT and LM responses (24
+bytes each) over the secure channel to the Domain controller
+for it to say yea or nay. This is the preferred method of 
+checking for a logon as it doesn't export the password
+hashes to anyone who has compromised the secure channel. JRA.
+********************************************************************/
+
+void init_id_info2(NET_ID_INFO_2 * id, const char *domain_name,
+                  uint32 param_ctrl,
+                  uint32 log_id_low, uint32 log_id_high,
+                  const char *user_name, const char *wksta_name,
+                  const uchar lm_challenge[8],
+                  const uchar * lm_chal_resp, int lm_chal_resp_len,
+                  const uchar * nt_chal_resp, int nt_chal_resp_len)
+{
+       int len_domain_name = strlen(domain_name);
+       int len_user_name   = strlen(user_name  );
+       int len_wksta_name  = strlen(wksta_name );
+       unsigned char lm_owf[24];
+       unsigned char nt_owf[128];
+
+       DEBUG(5,("init_id_info2: %d\n", __LINE__));
+
+       id->ptr_id_info2 = 1;
+
+       init_uni_hdr(&id->hdr_domain_name, len_domain_name);
+
+       id->param_ctrl = param_ctrl;
+       init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+       init_uni_hdr(&id->hdr_user_name, len_user_name);
+       init_uni_hdr(&id->hdr_wksta_name, len_wksta_name);
+
+       if (nt_chal_resp) {
+               /* oops.  can only send what-ever-it-is direct */
+               memcpy(nt_owf, nt_chal_resp, MIN(sizeof(nt_owf), nt_chal_resp_len));
+               nt_chal_resp = nt_owf;
+       }
+       if (lm_chal_resp) {
+               /* oops.  can only send what-ever-it-is direct */
+               memcpy(lm_owf, lm_chal_resp, MIN(sizeof(lm_owf), lm_chal_resp_len));
+               lm_chal_resp = lm_owf;
+       }
+
+       memcpy(id->lm_chal, lm_challenge, sizeof(id->lm_chal));
+       init_str_hdr(&id->hdr_nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len, (nt_chal_resp != NULL) ? 1 : 0);
+       init_str_hdr(&id->hdr_lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len, (lm_chal_resp != NULL) ? 1 : 0);
+
+       init_unistr2(&id->uni_domain_name, domain_name, len_domain_name);
+       init_unistr2(&id->uni_user_name, user_name, len_user_name);
+       init_unistr2(&id->uni_wksta_name, wksta_name, len_wksta_name);
+
+       init_string2(&id->nt_chal_resp, (const char *)nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len);
+       init_string2(&id->lm_chal_resp, (const char *)lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len);
+
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_id_info2(const char *desc,  NET_ID_INFO_2 *id, prs_struct *ps, int depth)
+{
+       if (id == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_id_info2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_id_info2", ps, depth, &id->ptr_id_info2))
+               return False;
+
+       if (id->ptr_id_info2 != 0) {
+               if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+                       return False;
+
+               if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+                       return False;
+               if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+                       return False;
+
+               if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+                       return False;
+               if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+                       return False;
+
+               if(!prs_uint8s (False, "lm_chal", ps, depth, id->lm_chal, 8)) /* lm 8 byte challenge */
+                       return False;
+
+               if(!smb_io_strhdr("hdr_nt_chal_resp", &id->hdr_nt_chal_resp, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_lm_chal_resp", &id->hdr_lm_chal_resp, ps, depth))
+                       return False;
+
+               if(!smb_io_unistr2("uni_domain_name", &id->uni_domain_name,
+                               id->hdr_domain_name.buffer, ps, depth))
+                       return False;
+               if(!smb_io_unistr2("uni_user_name  ", &id->uni_user_name,
+                               id->hdr_user_name.buffer, ps, depth))
+                       return False;
+               if(!smb_io_unistr2("uni_wksta_name ", &id->uni_wksta_name,
+                               id->hdr_wksta_name.buffer, ps, depth))
+                       return False;
+               if(!smb_io_string2("nt_chal_resp", &id->nt_chal_resp,
+                               id->hdr_nt_chal_resp.buffer, ps, depth))
+                       return False;
+               if(!smb_io_string2("lm_chal_resp", &id->lm_chal_resp,
+                               id->hdr_lm_chal_resp.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits a DOM_SAM_INFO structure.
+********************************************************************/
+
+void init_sam_info(DOM_SAM_INFO *sam,
+                               const char *logon_srv, const char *comp_name,
+                               DOM_CRED *clnt_cred,
+                               DOM_CRED *rtn_cred, uint16 logon_level,
+                               NET_ID_INFO_CTR *ctr)
+{
+       DEBUG(5,("init_sam_info: %d\n", __LINE__));
+
+       init_clnt_info2(&sam->client, logon_srv, comp_name, clnt_cred);
+
+       if (rtn_cred != NULL) {
+               sam->ptr_rtn_cred = 1;
+               memcpy(&sam->rtn_cred, rtn_cred, sizeof(sam->rtn_cred));
+       } else {
+               sam->ptr_rtn_cred = 0;
+       }
+
+       sam->logon_level  = logon_level;
+       sam->ctr          = ctr;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+********************************************************************/
+
+static BOOL net_io_id_info_ctr(const char *desc, NET_ID_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+       NET_ID_INFO_CTR *ctr = *pp_ctr;
+
+       prs_debug(ps, depth, desc, "smb_io_sam_info");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               ctr = *pp_ctr = (NET_ID_INFO_CTR *)prs_alloc_mem(ps, sizeof(NET_ID_INFO_CTR));
+               if (ctr == NULL)
+                       return False;
+       }
+       
+       if (ctr == NULL)
+               return False;
+
+       /* don't 4-byte align here! */
+
+       if(!prs_uint16("switch_value ", ps, depth, &ctr->switch_value))
+               return False;
+
+       switch (ctr->switch_value) {
+       case 1:
+               if(!net_io_id_info1("", &ctr->auth.id1, ps, depth))
+                       return False;
+               break;
+       case 2:
+               if(!net_io_id_info2("", &ctr->auth.id2, ps, depth))
+                       return False;
+               break;
+       default:
+               /* PANIC! */
+               DEBUG(4,("smb_io_sam_info: unknown switch_value!\n"));
+               break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+ ********************************************************************/
+
+static BOOL smb_io_sam_info(const char *desc, DOM_SAM_INFO *sam, prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_sam_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_clnt_info2("", &sam->client, ps, depth))
+               return False;
+
+       if(!prs_uint32("ptr_rtn_cred ", ps, depth, &sam->ptr_rtn_cred))
+               return False;
+       if(!smb_io_cred("", &sam->rtn_cred, ps, depth))
+               return False;
+
+       if(!prs_uint16("logon_level  ", ps, depth, &sam->logon_level))
+               return False;
+
+       if (sam->logon_level != 0) {
+               if(!net_io_id_info_ctr("logon_info", &sam->ctr, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ Inits a NET_USER_INFO_3 structure.
+
+ This is a network logon reply packet, and contains much information about
+ the user.  This information is passed as a (very long) paramater list
+ to avoid having to link in the PASSDB code to every program that deals 
+ with this file.
+ *************************************************************************/
+
+void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr, 
+                        uint32                user_rid,
+                        uint32                group_rid,
+
+                        const char*            user_name,
+                        const char*            full_name,
+                        const char*            home_dir,
+                        const char*            dir_drive,
+                        const char*            logon_script,
+                        const char*            profile_path,
+
+                        time_t unix_logon_time,
+                        time_t unix_logoff_time,
+                        time_t unix_kickoff_time,
+                        time_t unix_pass_last_set_time,
+                        time_t unix_pass_can_change_time,
+                        time_t unix_pass_must_change_time,
+                        
+                        uint16 logon_count, uint16 bad_pw_count,
+                        uint32 num_groups, const DOM_GID *gids,
+                        uint32 user_flgs, uchar sess_key[16],
+                        const char *logon_srv, const char *logon_dom,
+                        const DOM_SID *dom_sid, const char *other_sids)
+{
+       /* only cope with one "other" sid, right now. */
+       /* need to count the number of space-delimited sids */
+       int i;
+       int num_other_sids = 0;
+       
+       NTTIME          logon_time, logoff_time, kickoff_time,
+                       pass_last_set_time, pass_can_change_time,
+                       pass_must_change_time;
+
+       int             len_user_name, len_full_name, len_home_dir,
+                       len_dir_drive, len_logon_script, len_profile_path;
+                       
+       int len_logon_srv    = strlen(logon_srv);
+       int len_logon_dom    = strlen(logon_dom);
+
+       len_user_name    = strlen(user_name   );
+       len_full_name    = strlen(full_name   );
+       len_home_dir     = strlen(home_dir    );
+       len_dir_drive    = strlen(dir_drive   );
+       len_logon_script = strlen(logon_script);
+       len_profile_path = strlen(profile_path);
+
+
+       ZERO_STRUCTP(usr);
+
+       usr->ptr_user_info = 1; /* yes, we're bothering to put USER_INFO data here */
+
+
+       /* Create NTTIME structs */
+       unix_to_nt_time (&logon_time,            unix_logon_time);
+       unix_to_nt_time (&logoff_time,           unix_logoff_time);
+       unix_to_nt_time (&kickoff_time,          unix_kickoff_time);
+       unix_to_nt_time (&pass_last_set_time,    unix_pass_last_set_time);
+       unix_to_nt_time (&pass_can_change_time,  unix_pass_can_change_time);
+       unix_to_nt_time (&pass_must_change_time, unix_pass_must_change_time);
+
+       usr->logon_time            = logon_time;
+       usr->logoff_time           = logoff_time;
+       usr->kickoff_time          = kickoff_time;
+       usr->pass_last_set_time    = pass_last_set_time;
+       usr->pass_can_change_time  = pass_can_change_time;
+       usr->pass_must_change_time = pass_must_change_time;
+
+       init_uni_hdr(&usr->hdr_user_name, len_user_name);
+       init_uni_hdr(&usr->hdr_full_name, len_full_name);
+       init_uni_hdr(&usr->hdr_logon_script, len_logon_script);
+       init_uni_hdr(&usr->hdr_profile_path, len_profile_path);
+       init_uni_hdr(&usr->hdr_home_dir, len_home_dir);
+       init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive);
+
+       usr->logon_count = logon_count;
+       usr->bad_pw_count = bad_pw_count;
+
+       usr->user_rid = user_rid;
+       usr->group_rid = group_rid;
+       usr->num_groups = num_groups;
+
+       usr->buffer_groups = 1; /* indicates fill in groups, below, even if there are none */
+       usr->user_flgs = user_flgs;
+
+       if (sess_key != NULL)
+               memcpy(usr->user_sess_key, sess_key, sizeof(usr->user_sess_key));
+       else
+               memset((char *)usr->user_sess_key, '\0', sizeof(usr->user_sess_key));
+
+       init_uni_hdr(&usr->hdr_logon_srv, len_logon_srv);
+       init_uni_hdr(&usr->hdr_logon_dom, len_logon_dom);
+
+       usr->buffer_dom_id = dom_sid ? 1 : 0; /* yes, we're bothering to put a domain SID in */
+
+       memset((char *)usr->padding, '\0', sizeof(usr->padding));
+
+       num_other_sids = init_dom_sid2s(ctx, other_sids, &usr->other_sids);
+
+       usr->num_other_sids = num_other_sids;
+       usr->buffer_other_sids = (num_other_sids != 0) ? 1 : 0; 
+       
+       init_unistr2(&usr->uni_user_name, user_name, len_user_name);
+       init_unistr2(&usr->uni_full_name, full_name, len_full_name);
+       init_unistr2(&usr->uni_logon_script, logon_script, len_logon_script);
+       init_unistr2(&usr->uni_profile_path, profile_path, len_profile_path);
+       init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir);
+       init_unistr2(&usr->uni_dir_drive, dir_drive, len_dir_drive);
+
+       usr->num_groups2 = num_groups;
+
+       usr->gids = (DOM_GID *)talloc_zero(ctx,sizeof(DOM_GID) * (num_groups));
+       if (usr->gids == NULL && num_groups>0)
+               return;
+
+       for (i = 0; i < num_groups; i++) 
+               usr->gids[i] = gids[i]; 
+               
+       init_unistr2(&usr->uni_logon_srv, logon_srv, len_logon_srv);
+       init_unistr2(&usr->uni_logon_dom, logon_dom, len_logon_dom);
+
+       init_dom_sid2(&usr->dom_sid, dom_sid);
+       /* "other" sids are set up above */
+}
+
+/*******************************************************************
+ This code has been modified to cope with a NET_USER_INFO_2 - which is
+ exactly the same as a NET_USER_INFO_3, minus the other sids parameters.
+ We use validation level to determine if we're marshalling a info 2 or
+ INFO_3 - be we always return an INFO_3. Based on code donated by Marc
+ Jacobsen at HP. JRA.
+********************************************************************/
+
+BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps, 
+                      int depth, uint16 validation_level)
+{
+       int i;
+
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_user_info3");
+       depth++;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(usr);
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_user_info ", ps, depth, &usr->ptr_user_info))
+               return False;
+
+       if (usr->ptr_user_info == 0)
+               return True;
+
+       if(!smb_io_time("logon time", &usr->logon_time, ps, depth)) /* logon time */
+               return False;
+       if(!smb_io_time("logoff time", &usr->logoff_time, ps, depth)) /* logoff time */
+               return False;
+       if(!smb_io_time("kickoff time", &usr->kickoff_time, ps, depth)) /* kickoff time */
+               return False;
+       if(!smb_io_time("last set time", &usr->pass_last_set_time, ps, depth)) /* password last set time */
+               return False;
+       if(!smb_io_time("can change time", &usr->pass_can_change_time , ps, depth)) /* password can change time */
+               return False;
+       if(!smb_io_time("must change time", &usr->pass_must_change_time, ps, depth)) /* password must change time */
+               return False;
+
+       if(!smb_io_unihdr("hdr_user_name", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_full_name", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_home_dir", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_dir_drive", &usr->hdr_dir_drive, ps, depth)) /* home directory drive unicode string header */
+               return False;
+
+       if(!prs_uint16("logon_count   ", ps, depth, &usr->logon_count))  /* logon count */
+               return False;
+       if(!prs_uint16("bad_pw_count  ", ps, depth, &usr->bad_pw_count)) /* bad password count */
+               return False;
+
+       if(!prs_uint32("user_rid      ", ps, depth, &usr->user_rid))       /* User RID */
+               return False;
+       if(!prs_uint32("group_rid     ", ps, depth, &usr->group_rid))      /* Group RID */
+               return False;
+       if(!prs_uint32("num_groups    ", ps, depth, &usr->num_groups))    /* num groups */
+               return False;
+       if(!prs_uint32("buffer_groups ", ps, depth, &usr->buffer_groups)) /* undocumented buffer pointer to groups. */
+               return False;
+       if(!prs_uint32("user_flgs     ", ps, depth, &usr->user_flgs))     /* user flags */
+               return False;
+
+       if(!prs_uint8s(False, "user_sess_key", ps, depth, usr->user_sess_key, 16)) /* user session key */
+               return False;
+
+       if(!smb_io_unihdr("hdr_logon_srv", &usr->hdr_logon_srv, ps, depth)) /* logon server unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_logon_dom", &usr->hdr_logon_dom, ps, depth)) /* logon domain unicode string header */
+               return False;
+
+       if(!prs_uint32("buffer_dom_id ", ps, depth, &usr->buffer_dom_id)) /* undocumented logon domain id pointer */
+               return False;
+       if(!prs_uint8s (False, "padding       ", ps, depth, usr->padding, 40)) /* unused padding bytes? */
+               return False;
+
+       if (validation_level == 3) {
+               if(!prs_uint32("num_other_sids", ps, depth, &usr->num_other_sids)) /* 0 - num_sids */
+                       return False;
+               if(!prs_uint32("buffer_other_sids", ps, depth, &usr->buffer_other_sids)) /* NULL - undocumented pointer to SIDs. */
+                       return False;
+       } else {
+               if (UNMARSHALLING(ps)) {
+                       usr->num_other_sids = 0;
+                       usr->buffer_other_sids = 0;
+               }
+       }
+               
+       if(!smb_io_unistr2("uni_user_name", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_full_name", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_home_dir", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_dir_drive", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_groups2   ", ps, depth, &usr->num_groups2))        /* num groups */
+               return False;
+
+       if (UNMARSHALLING(ps) && usr->num_groups2 > 0) {
+               usr->gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_groups2);
+               if (usr->gids == NULL)
+                       return False;
+       }
+
+       for (i = 0; i < usr->num_groups2; i++) {
+               if(!smb_io_gid("", &usr->gids[i], ps, depth)) /* group info */
+                       return False;
+       }
+
+       if(!smb_io_unistr2("uni_logon_srv", &usr->uni_logon_srv, usr->hdr_logon_srv.buffer, ps, depth)) /* logon server unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_logon_dom", &usr->uni_logon_dom, usr->hdr_logon_srv.buffer, ps, depth)) /* logon domain unicode string */
+               return False;
+
+       if(!smb_io_dom_sid2("", &usr->dom_sid, ps, depth))           /* domain SID */
+               return False;
+
+       if (usr->num_other_sids) {
+
+               if (UNMARSHALLING(ps)) {
+                       usr->other_sids = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(DOM_SID2)*usr->num_other_sids);
+                       if (usr->other_sids == NULL)
+                               return False;
+               }
+       
+               if(!prs_uint32("num_other_groups", ps, depth, &usr->num_other_groups))
+                       return False;
+
+               if (UNMARSHALLING(ps) && usr->num_other_groups > 0) {
+                       usr->other_gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_other_groups);
+                       if (usr->other_gids == NULL)
+                               return False;
+               }
+       
+               for (i = 0; i < usr->num_other_groups; i++) {
+                       if(!smb_io_gid("", &usr->other_gids[i], ps, depth)) /* other GIDs */
+                               return False;
+               }
+               for (i = 0; i < usr->num_other_sids; i++) {
+                       if(!smb_io_dom_sid2("", &usr->other_sids[i], ps, depth)) /* other domain SIDs */
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logon(const char *desc, NET_Q_SAM_LOGON *q_l, prs_struct *ps, int depth)
+{
+       if (q_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_sam_logon");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_sam_info("", &q_l->sam_id, ps, depth))
+               return False;
+
+       if(!prs_uint16("validation_level", ps, depth, &q_l->validation_level))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logon(const char *desc, NET_R_SAM_LOGON *r_l, prs_struct *ps, int depth)
+{
+       if (r_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_sam_logon");
+       depth++;
+
+       if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+               return False;
+       if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials.  server time stamp appears to be ignored. */
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &r_l->switch_value))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+#if 1 /* W2k always needs this - even for bad passwd. JRA */
+       if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+               return False;
+#else
+       if (r_l->switch_value != 0) {
+               if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+                       return False;
+       }
+#endif
+
+       if(!prs_uint32("auth_resp   ", ps, depth, &r_l->auth_resp)) /* 1 - Authoritative response; 0 - Non-Auth? */
+               return False;
+
+       if(!prs_ntstatus("status      ", ps, depth, &r_l->status))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logoff(const char *desc,  NET_Q_SAM_LOGOFF *q_l, prs_struct *ps, int depth)
+{
+       if (q_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_q_sam_logoff");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_sam_info("", &q_l->sam_id, ps, depth))           /* domain SID */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logoff(const char *desc, NET_R_SAM_LOGOFF *r_l, prs_struct *ps, int depth)
+{
+       if (r_l == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "net_io_r_sam_logoff");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+               return False;
+       if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials.  server time stamp appears to be ignored. */
+               return False;
+
+       if(!prs_ntstatus("status      ", ps, depth, &r_l->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_SYNC structure.
+********************************************************************/
+BOOL init_net_q_sam_sync(NET_Q_SAM_SYNC * q_s, const char *srv_name,
+                         const char *cli_name, DOM_CRED *cli_creds, 
+                         DOM_CRED *ret_creds, uint32 database_id, 
+                        uint32 next_rid)
+{
+       DEBUG(5, ("init_q_sam_sync\n"));
+
+       init_unistr2(&q_s->uni_srv_name, srv_name, strlen(srv_name) + 1);
+       init_unistr2(&q_s->uni_cli_name, cli_name, strlen(cli_name) + 1);
+
+        if (cli_creds)
+                memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+
+       if (cli_creds)
+                memcpy(&q_s->ret_creds, ret_creds, sizeof(q_s->ret_creds));
+       else
+               memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+       q_s->database_id = database_id;
+       q_s->restart_state = 0;
+       q_s->sync_context = next_rid;
+       q_s->max_size = 0xffff;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_sync(const char *desc, NET_Q_SAM_SYNC * q_s, prs_struct *ps,
+                      int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_q_sam_sync");
+       depth++;
+
+       if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+                return False;
+       if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+                return False;
+
+       if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+                return False;
+       if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+                return False;
+
+       if (!prs_uint32("database_id  ", ps, depth, &q_s->database_id))
+                return False;
+       if (!prs_uint32("restart_state", ps, depth, &q_s->restart_state))
+                return False;
+       if (!prs_uint32("sync_context ", ps, depth, &q_s->sync_context))
+                return False;
+
+       if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_hdr(const char *desc, SAM_DELTA_HDR * delta,
+                                prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_delta_hdr");
+       depth++;
+
+       if (!prs_uint16("type", ps, depth, &delta->type))
+                return False;
+       if (!prs_uint16("type2", ps, depth, &delta->type2))
+                return False;
+       if (!prs_uint32("target_rid", ps, depth, &delta->target_rid))
+                return False;
+
+       if (!prs_uint32("type3", ps, depth, &delta->type3))
+                return False;
+
+        /* Not sure why we need this but it seems to be necessary to get
+           sam deltas working. */
+
+        if (delta->type != 0x16) {
+                if (!prs_uint32("ptr_delta", ps, depth, &delta->ptr_delta))
+                        return False;
+        }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_mod_count(const char *desc, SAM_DELTA_MOD_COUNT *info,
+                                   prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_delta_stamp");
+       depth++;
+
+        if (!prs_uint32("seqnum", ps, depth, &info->seqnum))
+                return False;
+        if (!prs_uint32("dom_mod_count_ptr", ps, depth, 
+                        &info->dom_mod_count_ptr))
+                return False;
+
+        if (info->dom_mod_count_ptr) {
+                if (!prs_uint64("dom_mod_count", ps, depth,
+                                &info->dom_mod_count))
+                        return False;
+        }
+
+        return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_domain_info(const char *desc, SAM_DOMAIN_INFO * info,
+                                  prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_domain_info");
+       depth++;
+
+       if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_oem_info", &info->hdr_oem_info, ps, depth))
+                return False;
+
+        if (!prs_uint64("force_logoff", ps, depth, &info->force_logoff))
+                return False;
+       if (!prs_uint16("min_pwd_len", ps, depth, &info->min_pwd_len))
+                return False;
+       if (!prs_uint16("pwd_history_len", ps, depth, &info->pwd_history_len))
+                return False;
+       if (!prs_uint64("max_pwd_age", ps, depth, &info->max_pwd_age))
+                return False;
+       if (!prs_uint64("min_pwd_age", ps, depth, &info->min_pwd_age))
+                return False;
+       if (!prs_uint64("dom_mod_count", ps, depth, &info->dom_mod_count))
+                return False;
+       if (!smb_io_time("creation_time", &info->creation_time, ps, depth))
+                return False;
+
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_unknown", &info->hdr_unknown, ps, depth))
+                return False;
+
+       if (prs_offset(ps) + 40 > prs_data_size(ps))
+                return False;
+        prs_set_offset(ps, prs_offset(ps) + 40);
+
+       if (!smb_io_unistr2("uni_dom_name", &info->uni_dom_name,
+                            info->hdr_dom_name.buffer, ps, depth))
+                return False;
+       if (!smb_io_unistr2("buf_oem_info", &info->buf_oem_info,
+                            info->hdr_oem_info.buffer, ps, depth))
+                return False;
+
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+                return False;
+       if (!smb_io_unistr2("buf_unknown", &info->buf_unknown,
+                            info->hdr_unknown.buffer, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_info(const char *desc, SAM_GROUP_INFO * info,
+                                 prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_group_info");
+       depth++;
+
+       if (!smb_io_unihdr("hdr_grp_name", &info->hdr_grp_name, ps, depth))
+                return False;
+       if (!smb_io_gid("gid", &info->gid, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_grp_desc", &info->hdr_grp_desc, ps, depth))
+                return False;
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+
+       if (prs_offset(ps) + 48 > prs_data_size(ps))
+                return False;
+        prs_set_offset(ps, prs_offset(ps) + 48);
+
+       if (!smb_io_unistr2("uni_grp_name", &info->uni_grp_name,
+                            info->hdr_grp_name.buffer, ps, depth))
+                return False;
+       if (!smb_io_unistr2("uni_grp_desc", &info->uni_grp_desc,
+                            info->hdr_grp_desc.buffer, ps, depth))
+                return False;
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_passwd_info(const char *desc, SAM_PWD * pwd,
+                                  prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_passwd_info");
+       depth++;
+
+       if (!prs_uint32("unk_0 ", ps, depth, &pwd->unk_0))
+                return False;
+
+       if (!smb_io_unihdr("hdr_lm_pwd", &pwd->hdr_lm_pwd, ps, depth))
+                return False;
+       if (!prs_uint8s(False, "buf_lm_pwd", ps, depth, pwd->buf_lm_pwd, 16))
+                return False;
+
+       if (!smb_io_unihdr("hdr_nt_pwd", &pwd->hdr_nt_pwd, ps, depth))
+                return False;
+       if (!prs_uint8s(False, "buf_nt_pwd", ps, depth, pwd->buf_nt_pwd, 16))
+                return False;
+
+       if (!smb_io_unihdr("", &pwd->hdr_empty_lm, ps, depth))
+                return False;
+       if (!smb_io_unihdr("", &pwd->hdr_empty_nt, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a SAM_ACCOUNT_INFO structure.
+********************************************************************/
+BOOL make_sam_account_info(SAM_ACCOUNT_INFO * info,
+                          const UNISTR2 *user_name,
+                          const UNISTR2 *full_name,
+                          uint32 user_rid, uint32 group_rid,
+                          const UNISTR2 *home_dir,
+                          const UNISTR2 *dir_drive,
+                          const UNISTR2 *log_scr,
+                          const UNISTR2 *desc,
+                          uint32 acb_info,
+                          const UNISTR2 *prof_path,
+                          const UNISTR2 *wkstas,
+                          const UNISTR2 *unk_str, const UNISTR2 *mung_dial)
+{
+       int len_user_name = user_name != NULL ? user_name->uni_str_len : 0;
+       int len_full_name = full_name != NULL ? full_name->uni_str_len : 0;
+       int len_home_dir = home_dir != NULL ? home_dir->uni_str_len : 0;
+       int len_dir_drive = dir_drive != NULL ? dir_drive->uni_str_len : 0;
+       int len_logon_script = log_scr != NULL ? log_scr->uni_str_len : 0;
+       int len_profile_path = prof_path != NULL ? prof_path->uni_str_len : 0;
+       int len_description = desc != NULL ? desc->uni_str_len : 0;
+       int len_workstations = wkstas != NULL ? wkstas->uni_str_len : 0;
+       int len_unknown_str = unk_str != NULL ? unk_str->uni_str_len : 0;
+       int len_munged_dial = mung_dial != NULL ? mung_dial->uni_str_len : 0;
+
+       DEBUG(5, ("make_sam_account_info\n"));
+
+       make_uni_hdr(&info->hdr_acct_name, len_user_name);
+       make_uni_hdr(&info->hdr_full_name, len_full_name);
+       make_uni_hdr(&info->hdr_home_dir, len_home_dir);
+       make_uni_hdr(&info->hdr_dir_drive, len_dir_drive);
+       make_uni_hdr(&info->hdr_logon_script, len_logon_script);
+       make_uni_hdr(&info->hdr_profile, len_profile_path);
+       make_uni_hdr(&info->hdr_acct_desc, len_description);
+       make_uni_hdr(&info->hdr_workstations, len_workstations);
+       make_uni_hdr(&info->hdr_comment, len_unknown_str);
+       make_uni_hdr(&info->hdr_parameters, len_munged_dial);
+
+       /* not present */
+       make_bufhdr2(&info->hdr_sec_desc, 0, 0, 0);
+
+       info->user_rid = user_rid;
+       info->group_rid = group_rid;
+
+       init_nt_time(&info->logon_time);
+       init_nt_time(&info->logoff_time);
+       init_nt_time(&info->pwd_last_set_time);
+       init_nt_time(&info->acct_expiry_time);
+
+       info->logon_divs = 0xA8;
+       info->ptr_logon_hrs = 0;        /* Don't care right now */
+
+       info->bad_pwd_count = 0;
+       info->logon_count = 0;
+       info->acb_info = acb_info;
+       info->nt_pwd_present = 0;
+       info->lm_pwd_present = 0;
+       info->pwd_expired = 0;
+       info->country = 0;
+       info->codepage = 0;
+
+       info->unknown1 = 0x4EC;
+       info->unknown2 = 0;
+
+       copy_unistr2(&info->uni_acct_name, user_name);
+       copy_unistr2(&info->uni_full_name, full_name);
+       copy_unistr2(&info->uni_home_dir, home_dir);
+       copy_unistr2(&info->uni_dir_drive, dir_drive);
+       copy_unistr2(&info->uni_logon_script, log_scr);
+       copy_unistr2(&info->uni_profile, prof_path);
+       copy_unistr2(&info->uni_acct_desc, desc);
+       copy_unistr2(&info->uni_workstations, wkstas);
+       copy_unistr2(&info->uni_comment, unk_str);
+       copy_unistr2(&info->uni_parameters, mung_dial);
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_account_info(const char *desc, uint8 sess_key[16],
+                                   SAM_ACCOUNT_INFO * info, prs_struct *ps,
+                                   int depth)
+{
+       BUFHDR2 hdr_priv_data;
+       uint32 i;
+
+       prs_debug(ps, depth, desc, "net_io_sam_account_info");
+       depth++;
+
+       if (!smb_io_unihdr("hdr_acct_name", &info->hdr_acct_name, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
+                return False;
+
+       if (!prs_uint32("user_rid ", ps, depth, &info->user_rid))
+                return False;
+       if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
+                return False;
+
+       if (!smb_io_unihdr("hdr_home_dir ", &info->hdr_home_dir, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script, ps,
+                           depth))
+                return False;
+
+       if (!smb_io_unihdr("hdr_acct_desc", &info->hdr_acct_desc, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_workstations", &info->hdr_workstations, ps,
+                           depth))
+                return False;
+
+       if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
+                return False;
+       if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
+                return False;
+
+       if (!prs_uint32("logon_divs   ", ps, depth, &info->logon_divs))
+                return False;
+       if (!prs_uint32("ptr_logon_hrs", ps, depth, &info->ptr_logon_hrs))
+                return False;
+
+       if (!prs_uint16("bad_pwd_count", ps, depth, &info->bad_pwd_count))
+                return False;
+       if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
+                return False;
+       if (!smb_io_time("pwd_last_set_time", &info->pwd_last_set_time, ps,
+                         depth))
+                return False;
+       if (!smb_io_time("acct_expiry_time", &info->acct_expiry_time, ps, 
+                         depth))
+                return False;
+
+       if (!prs_uint32("acb_info", ps, depth, &info->acb_info))
+                return False;
+       if (!prs_uint8s(False, "nt_pwd", ps, depth, info->nt_pwd, 16))
+                return False;
+       if (!prs_uint8s(False, "lm_pwd", ps, depth, info->lm_pwd, 16))
+                return False;
+       if (!prs_uint8("lm_pwd_present", ps, depth, &info->lm_pwd_present))
+                return False;
+       if (!prs_uint8("nt_pwd_present", ps, depth, &info->nt_pwd_present))
+                return False;
+       if (!prs_uint8("pwd_expired", ps, depth, &info->pwd_expired))
+                return False;
+
+       if (!smb_io_unihdr("hdr_comment", &info->hdr_comment, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_parameters", &info->hdr_parameters, ps, 
+                           depth))
+                return False;
+       if (!prs_uint16("country", ps, depth, &info->country))
+                return False;
+       if (!prs_uint16("codepage", ps, depth, &info->codepage))
+                return False;
+
+       if (!smb_io_bufhdr2("hdr_priv_data", &hdr_priv_data, ps, depth))
+                return False;
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_profile", &info->hdr_profile, ps, depth))
+                return False;
+
+       for (i = 0; i < 3; i++)
+       {
+               if (!smb_io_unihdr("hdr_reserved", &info->hdr_reserved[i], 
+                                   ps, depth))
+                        return False;                                          
+       }
+
+       for (i = 0; i < 4; i++)
+       {
+               if (!prs_uint32("dw_reserved", ps, depth, 
+                                &info->dw_reserved[i]))
+                        return False;
+       }
+
+       if (!smb_io_unistr2("uni_acct_name", &info->uni_acct_name,
+                            info->hdr_acct_name.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_full_name", &info->uni_full_name,
+                            info->hdr_full_name.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_home_dir ", &info->uni_home_dir,
+                            info->hdr_home_dir.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
+                            info->hdr_dir_drive.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_logon_script", &info->uni_logon_script,
+                            info->hdr_logon_script.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_acct_desc", &info->uni_acct_desc,
+                            info->hdr_acct_desc.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_workstations", &info->uni_workstations,
+                            info->hdr_workstations.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+
+       if (!prs_uint32("unknown1", ps, depth, &info->unknown1))
+                return False;
+       if (!prs_uint32("unknown2", ps, depth, &info->unknown2))
+                return False;
+
+       if (!smb_io_buffer4("buf_logon_hrs", &info->buf_logon_hrs,
+                            info->ptr_logon_hrs, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_comment", &info->uni_comment,
+                            info->hdr_comment.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_parameters", &info->uni_parameters,
+                            info->hdr_parameters.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (hdr_priv_data.buffer != 0)
+       {
+               int old_offset = 0;
+               uint32 len = 0x44;
+               if (!prs_uint32("pwd_len", ps, depth, &len))
+                        return False;
+               old_offset = prs_offset(ps);
+               if (len == 0x44)
+               {
+                       if (ps->io)
+                       {
+                               /* reading */
+                                if (!prs_hash1(ps, prs_offset(ps), sess_key))
+                                        return False;
+                       }
+                       if (!net_io_sam_passwd_info("pass", &info->pass, 
+                                                    ps, depth))
+                                return False;
+
+                       if (!ps->io)
+                       {
+                               /* writing */
+                                if (!prs_hash1(ps, old_offset, sess_key))
+                                        return False;
+                       }
+               }
+                if (old_offset + len > prs_data_size(ps))
+                        return False;
+               prs_set_offset(ps, old_offset + len);
+       }
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+                return False;
+       prs_align(ps);
+       if (!smb_io_unistr2("uni_profile", &info->uni_profile,
+                            info->hdr_profile.buffer, ps, depth))
+                return False;
+
+       prs_align(ps);
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_mem_info(const char *desc, SAM_GROUP_MEM_INFO * info,
+                                     prs_struct *ps, int depth)
+{
+       uint32 i;
+       fstring tmp;
+
+       prs_debug(ps, depth, desc, "net_io_sam_group_mem_info");
+       depth++;
+
+       prs_align(ps);
+       if (!prs_uint32("ptr_rids   ", ps, depth, &info->ptr_rids))
+                return False;
+       if (!prs_uint32("ptr_attribs", ps, depth, &info->ptr_attribs))
+                return False;
+       if (!prs_uint32("num_members", ps, depth, &info->num_members))
+                return False;
+
+        if (prs_offset(ps) + 16 > prs_data_size(ps))
+                return False;
+       prs_set_offset(ps, prs_offset(ps) + 16);
+
+       if (info->ptr_rids != 0)
+       {
+               if (!prs_uint32("num_members2", ps, depth, 
+                                &info->num_members2))
+                        return False;
+
+               if (info->num_members2 != info->num_members)
+               {
+                       /* RPC fault */
+                       return False;
+               }
+
+                info->rids = talloc(ps->mem_ctx, sizeof(uint32) *
+                                    info->num_members2);
+
+                if (info->rids == NULL) {
+                        DEBUG(0, ("out of memory allocating %d rids\n",
+                                  info->num_members2));
+                        return False;
+                }
+
+               for (i = 0; i < info->num_members2; i++)
+               {
+                       slprintf(tmp, sizeof(tmp) - 1, "rids[%02d]", i);
+                       if (!prs_uint32(tmp, ps, depth, &info->rids[i]))
+                                return False;
+               }
+       }
+
+       if (info->ptr_attribs != 0)
+       {
+               if (!prs_uint32("num_members3", ps, depth, 
+                                &info->num_members3))
+                        return False;
+               if (info->num_members3 != info->num_members)
+               {
+                       /* RPC fault */
+                       return False;
+               }
+
+                info->attribs = talloc(ps->mem_ctx, sizeof(uint32) *
+                                       info->num_members3);
+
+                if (info->attribs == NULL) {
+                        DEBUG(0, ("out of memory allocating %d attribs\n",
+                                  info->num_members3));
+                        return False;
+                }
+
+               for (i = 0; i < info->num_members3; i++)
+               {
+                       slprintf(tmp, sizeof(tmp) - 1, "attribs[%02d]", i);
+                       if (!prs_uint32(tmp, ps, depth, &info->attribs[i]))
+                                return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_info(const char *desc, SAM_ALIAS_INFO * info,
+                                 prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_alias_info");
+       depth++;
+
+       if (!smb_io_unihdr("hdr_als_name", &info->hdr_als_name, ps, depth))
+                return False;
+       if (!prs_uint32("als_rid", ps, depth, &info->als_rid))
+                return False;
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+       if (!smb_io_unihdr("hdr_als_desc", &info->hdr_als_desc, ps, depth))
+                return False;
+
+        if (prs_offset(ps) + 40 > prs_data_size(ps))
+                return False;
+       prs_set_offset(ps, prs_offset(ps) + 40);
+
+       if (!smb_io_unistr2("uni_als_name", &info->uni_als_name,
+                            info->hdr_als_name.buffer, ps, depth))
+                return False;
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+                return False;
+       if (!smb_io_unistr2("uni_als_desc", &info->uni_als_desc,
+                            info->hdr_als_name.buffer, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_mem_info(const char *desc, SAM_ALIAS_MEM_INFO * info,
+                                     prs_struct *ps, int depth)
+{
+       uint32 i;
+       fstring tmp;
+
+       prs_debug(ps, depth, desc, "net_io_sam_alias_mem_info");
+       depth++;
+
+       prs_align(ps);
+       if (!prs_uint32("num_members", ps, depth, &info->num_members))
+                return False;
+       if (!prs_uint32("ptr_members", ps, depth, &info->ptr_members))
+                return False;
+
+       if (info->ptr_members != 0)
+       {
+                if (prs_offset(ps) + 16 > prs_data_size(ps))
+                        return False;
+                prs_set_offset(ps, prs_offset(ps) + 16);
+
+               if (!prs_uint32("num_sids", ps, depth, &info->num_sids))
+                        return False;
+               if (info->num_sids != info->num_members)
+               {
+                       /* RPC fault */
+                       return False;
+               }
+
+                info->ptr_sids = talloc(ps->mem_ctx, sizeof(uint32) *
+                                        info->num_sids);
+                
+                if (info->ptr_sids == NULL) {
+                        DEBUG(0, ("out of memory allocating %d ptr_sids\n",
+                                  info->num_sids));
+                        return False;
+                }
+
+               for (i = 0; i < info->num_sids; i++)
+               {
+                       slprintf(tmp, sizeof(tmp) - 1, "ptr_sids[%02d]", i);
+                       if (!prs_uint32(tmp, ps, depth, &info->ptr_sids[i]))
+                                return False;
+               }
+
+                info->sids = talloc(ps->mem_ctx, sizeof(DOM_SID2) *
+                                    info->num_sids);
+
+                if (info->sids == NULL) {
+                        DEBUG(0, ("error allocating %d sids\n",
+                                  info->num_sids));
+                        return False;
+                }
+
+               for (i = 0; i < info->num_sids; i++)
+               {
+                       if (info->ptr_sids[i] != 0)
+                       {
+                               slprintf(tmp, sizeof(tmp) - 1, "sids[%02d]",
+                                        i);
+                               if (!smb_io_dom_sid2(tmp, &info->sids[i], 
+                                                     ps, depth))
+                                        return False;
+                       }
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_policy_info(const char *desc, SAM_DELTA_POLICY *info,
+                                     prs_struct *ps, int depth)
+{
+       int i;
+       prs_debug(ps, depth, desc, "net_io_sam_policy_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("max_log_size", ps, depth, &info->max_log_size))
+                return False;
+       if (!prs_uint64("audit_retention_period", ps, depth,
+                       &info->audit_retention_period))
+                return False;
+       if (!prs_uint32("auditing_mode", ps, depth, &info->auditing_mode))
+                return False;
+       if (!prs_uint32("num_events", ps, depth, &info->num_events))
+                return False;
+       if (!prs_uint32("ptr_events", ps, depth, &info->ptr_events))
+                return False;
+
+       if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+               return False;
+
+       if (!prs_uint32("sid_ptr", ps, depth, &info->sid_ptr))
+                return False;
+
+       if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+                return False;
+       if (!prs_uint32("non_paged_pool_limit", ps, depth,
+                       &info->non_paged_pool_limit))
+                return False;
+       if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+                return False;
+       if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+                return False;
+       if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+                return False;
+       if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+                return False;
+       if (!smb_io_time("modify_time", &info->modify_time, ps, depth))
+                return False;
+       if (!smb_io_time("create_time", &info->create_time, ps, depth))
+                return False;
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+
+       for (i=0; i<4; i++) {
+               UNIHDR dummy;
+               if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+                       return False;
+       }
+
+       for (i=0; i<4; i++) {
+               uint32 reserved;
+               if (!prs_uint32("reserved", ps, depth, &reserved))
+                       return False;
+       }
+
+       if (!prs_uint32("num_event_audit_options", ps, depth,
+                       &info->num_event_audit_options))
+                return False;
+
+       for (i=0; i<info->num_event_audit_options; i++)
+               if (!prs_uint32("event_audit_option", ps, depth,
+                               &info->event_audit_option))
+                       return False;
+
+       if (!smb_io_unistr2("domain_name", &info->domain_name, True, ps, depth))
+                return False;
+
+       if(!smb_io_dom_sid2("domain_sid", &info->domain_sid, ps, depth))
+               return False;
+
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_trustdoms_info(const char *desc, SAM_DELTA_TRUSTDOMS *info,
+                                     prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "net_io_sam_trustdoms_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+                return False;
+
+       if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_domain", &info->hdr_domain, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown0", ps, depth, &info->unknown0))
+                return False;
+       if(!prs_uint32("unknown1", ps, depth, &info->unknown1))
+                return False;
+       if(!prs_uint32("unknown2", ps, depth, &info->unknown2))
+                return False;
+
+       if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+                return False;
+       if(!prs_uint32("ptr", ps, depth, &info->ptr))
+                return False;
+
+       for (i=0; i<12; i++)
+               if(!prs_uint32("unknown3", ps, depth, &info->unknown3))
+                       return False;
+
+       if (!smb_io_unistr2("domain", &info->domain, True, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_secret_info(const char *desc, SAM_DELTA_SECRET *info,
+                                  prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "net_io_sam_secret_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+                return False;
+
+       if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+               return False;
+
+       if (!smb_io_unistr2("secret", &info->secret, True, ps, depth))
+                return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("count1", ps, depth, &info->count1))
+                return False;
+       if(!prs_uint32("count2", ps, depth, &info->count2))
+                return False;
+       if(!prs_uint32("ptr", ps, depth, &info->ptr))
+                return False;
+
+
+       if(!smb_io_time("time1", &info->time1, ps, depth)) /* logon time */
+               return False;
+       if(!prs_uint32("count3", ps, depth, &info->count3))
+                return False;
+       if(!prs_uint32("count4", ps, depth, &info->count4))
+                return False;
+       if(!prs_uint32("ptr2", ps, depth, &info->ptr2))
+                return False;
+       if(!smb_io_time("time2", &info->time2, ps, depth)) /* logon time */
+               return False;
+       if(!prs_uint32("unknow1", ps, depth, &info->unknow1))
+                return False;
+
+
+       if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+                return False;
+       if(!prs_uint32("ptr3", ps, depth, &info->ptr3))
+                return False;
+       for(i=0; i<12; i++)
+               if(!prs_uint32("unknow2", ps, depth, &info->unknow2))
+                       return False;
+
+       if(!prs_uint32("chal_len", ps, depth, &info->chal_len))
+                return False;
+       if(!prs_uint32("reserved1", ps, depth, &info->reserved1))
+                return False;
+       if(!prs_uint32("chal_len2", ps, depth, &info->chal_len2))
+                return False;
+
+       if(!prs_uint8s (False, "chal", ps, depth, info->chal, info->chal_len2))
+               return False;
+
+       if(!prs_uint32("key_len", ps, depth, &info->key_len))
+                return False;
+       if(!prs_uint32("reserved2", ps, depth, &info->reserved2))
+                return False;
+       if(!prs_uint32("key_len2", ps, depth, &info->key_len2))
+                return False;
+
+       if(!prs_uint8s (False, "key", ps, depth, info->key, info->key_len2))
+               return False;
+
+
+       if(!prs_uint32("buf_size3", ps, depth, &info->buf_size3))
+                return False;
+
+       if(!sec_io_desc("sec_desc2", &info->sec_desc2, ps, depth))
+               return False;
+
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_privs_info(const char *desc, SAM_DELTA_PRIVS *info,
+                                     prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "net_io_sam_privs_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+               return False;
+
+       if(!prs_uint32("priv_count", ps, depth, &info->priv_count))
+                return False;
+       if(!prs_uint32("priv_control", ps, depth, &info->priv_control))
+                return False;
+
+       if(!prs_uint32("priv_attr_ptr", ps, depth, &info->priv_attr_ptr))
+                return False;
+       if(!prs_uint32("priv_name_ptr", ps, depth, &info->priv_name_ptr))
+                return False;
+
+       if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+                return False;
+       if (!prs_uint32("non_paged_pool_limit", ps, depth,
+                       &info->non_paged_pool_limit))
+                return False;
+       if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+                return False;
+       if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+                return False;
+       if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+                return False;
+       if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+                return False;
+       if (!prs_uint32("system_flags", ps, depth, &info->system_flags))
+                return False;
+       if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+                return False;
+
+       for (i=0; i<4; i++) {
+               UNIHDR dummy;
+               if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+                       return False;
+       }
+
+       for (i=0; i<4; i++) {
+               uint32 reserved;
+               if (!prs_uint32("reserved", ps, depth, &reserved))
+                       return False;
+       }
+
+       if(!prs_uint32("attribute_count", ps, depth, &info->attribute_count))
+                return False;
+
+       info->attributes = talloc(ps->mem_ctx, sizeof(uint32) * info->attribute_count);
+
+       for (i=0; i<info->attribute_count; i++)
+               if(!prs_uint32("attributes", ps, depth, &info->attributes[i]))
+                       return False;
+
+       if(!prs_uint32("privlist_count", ps, depth, &info->privlist_count))
+                return False;
+
+       info->hdr_privslist = talloc(ps->mem_ctx, sizeof(UNIHDR) * info->privlist_count);
+       info->uni_privslist = talloc(ps->mem_ctx, sizeof(UNISTR2) * info->privlist_count);
+
+       for (i=0; i<info->privlist_count; i++)
+               if(!smb_io_unihdr("hdr_privslist", &info->hdr_privslist[i], ps, depth))
+                       return False;
+
+       for (i=0; i<info->privlist_count; i++)
+               if (!smb_io_unistr2("uni_privslist", &info->uni_privslist[i], True, ps, depth))
+                       return False;
+
+       if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+                            info->hdr_sec_desc.buffer, ps, depth))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_ctr(const char *desc, uint8 sess_key[16],
+                                SAM_DELTA_CTR * delta, uint16 type,
+                                prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_sam_delta_ctr");
+       depth++;
+
+       switch (type) {
+                /* Seen in sam deltas */
+                case SAM_DELTA_MODIFIED_COUNT:
+                        if (!net_io_sam_delta_mod_count("", &delta->mod_count, ps, depth))
+                                return False;
+                        break;
+
+               case SAM_DELTA_DOMAIN_INFO:
+                       if (!net_io_sam_domain_info("", &delta->domain_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_GROUP_INFO:
+                       if (!net_io_sam_group_info("", &delta->group_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_ACCOUNT_INFO:
+                       if (!net_io_sam_account_info("", sess_key, &delta->account_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_GROUP_MEM:
+                       if (!net_io_sam_group_mem_info("", &delta->grp_mem_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_ALIAS_INFO:
+                        if (!net_io_sam_alias_info("", &delta->alias_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_POLICY_INFO:
+                        if (!net_io_sam_policy_info("", &delta->policy_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_ALIAS_MEM:
+                       if (!net_io_sam_alias_mem_info("", &delta->als_mem_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_PRIVS_INFO:
+                       if (!net_io_sam_privs_info("", &delta->privs_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_TRUST_DOMS:
+                       if (!net_io_sam_trustdoms_info("", &delta->trustdoms_info, ps, depth))
+                                return False;
+                       break;
+
+               case SAM_DELTA_SECRET_INFO:
+                       if (!net_io_sam_secret_info("", &delta->secret_info, ps, depth))
+                                return False;
+                       break;
+
+                       /* These guys are not implemented yet */
+
+               case SAM_DELTA_RENAME_GROUP:
+               case SAM_DELTA_RENAME_USER:
+               case SAM_DELTA_RENAME_ALIAS:
+               case SAM_DELTA_DELETE_GROUP:
+               case SAM_DELTA_DELETE_USER:
+               default:
+                       DEBUG(0, ("Replication error: Unknown delta type 0x%x\n", type));
+                       break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_sync(const char *desc, uint8 sess_key[16],
+                      NET_R_SAM_SYNC * r_s, prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       prs_debug(ps, depth, desc, "net_io_r_sam_sync");
+       depth++;
+
+       if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+                return False;
+       if (!prs_uint32("sync_context", ps, depth, &r_s->sync_context))
+                return False;
+
+       if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+                return False;
+       if (r_s->ptr_deltas != 0)
+       {
+               if (!prs_uint32("num_deltas ", ps, depth, &r_s->num_deltas))
+                        return False;
+               if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->ptr_deltas2))
+                        return False;
+               if (r_s->ptr_deltas2 != 0)
+               {
+                       if (!prs_uint32("num_deltas2", ps, depth,
+                                        &r_s->num_deltas2))
+                                return False;
+
+                       if (r_s->num_deltas2 != r_s->num_deltas)
+                       {
+                               /* RPC fault */
+                               return False;
+                       }
+
+                        if (r_s->num_deltas2 > 0) {
+                                r_s->hdr_deltas = (SAM_DELTA_HDR *)
+                                        talloc(ps->mem_ctx, r_s->num_deltas2 *
+                                               sizeof(SAM_DELTA_HDR));
+                          
+                                if (r_s->hdr_deltas == NULL) {
+                                        DEBUG(0, ("error tallocating memory "
+                                                  "for %d delta headers\n", 
+                                                  r_s->num_deltas2));
+                                        return False;
+                                }
+                        }
+
+                       for (i = 0; i < r_s->num_deltas2; i++)
+                       {
+                               if (!net_io_sam_delta_hdr("", 
+                                                          &r_s->hdr_deltas[i],
+                                                          ps, depth))
+                                        return False;
+                       }
+
+                        if (r_s->num_deltas2 > 0) {
+                                r_s->deltas = (SAM_DELTA_CTR *)
+                                        talloc(ps->mem_ctx, r_s->num_deltas2 *
+                                               sizeof(SAM_DELTA_CTR));
+
+                                if (r_s->deltas == NULL) {
+                                        DEBUG(0, ("error tallocating memory "
+                                                  "for %d deltas\n", 
+                                                  r_s->num_deltas2));
+                                        return False;
+                                }
+                        }
+
+                       for (i = 0; i < r_s->num_deltas2; i++)
+                       {
+                               if (!net_io_sam_delta_ctr(
+                                        "", sess_key, &r_s->deltas[i],
+                                        r_s->hdr_deltas[i].type3,
+                                        ps, depth)) {
+                                        DEBUG(0, ("hmm, failed on i=%d\n", i));
+                                        return False;
+                                }
+                       }
+               }
+       }
+
+       prs_align(ps);
+       if (!prs_ntstatus("status", ps, depth, &(r_s->status)))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_DELTAS structure.
+********************************************************************/
+BOOL init_net_q_sam_deltas(NET_Q_SAM_DELTAS *q_s, const char *srv_name, 
+                           const char *cli_name, DOM_CRED *cli_creds, 
+                           uint32 database_id, UINT64_S dom_mod_count)
+{
+       DEBUG(5, ("init_net_q_sam_deltas\n"));
+
+       init_unistr2(&q_s->uni_srv_name, srv_name, strlen(srv_name) + 1);
+       init_unistr2(&q_s->uni_cli_name, cli_name, strlen(cli_name) + 1);
+
+       memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+       memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+       q_s->database_id = database_id;
+        q_s->dom_mod_count.low = dom_mod_count.low;
+        q_s->dom_mod_count.high = dom_mod_count.high;
+       q_s->max_size = 0xffff;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_deltas(const char *desc, NET_Q_SAM_DELTAS *q_s, prs_struct *ps,
+                         int depth)
+{
+       prs_debug(ps, depth, desc, "net_io_q_sam_deltas");
+       depth++;
+
+       if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+                return False;
+       if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+                return False;
+
+       if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+                return False;
+       if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+                return False;
+
+       if (!prs_uint32("database_id  ", ps, depth, &q_s->database_id))
+                return False;
+        if (!prs_uint64("dom_mod_count", ps, depth, &q_s->dom_mod_count))
+                return False;
+       if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+                return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_deltas(const char *desc, uint8 sess_key[16],
+                         NET_R_SAM_DELTAS *r_s, prs_struct *ps, int depth)
+{
+        int i;
+
+       prs_debug(ps, depth, desc, "net_io_r_sam_deltas");
+       depth++;
+
+       if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+                return False;
+        if (!prs_uint64("dom_mod_count", ps, depth, &r_s->dom_mod_count))
+                return False;
+
+       if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+                return False;
+       if (!prs_uint32("num_deltas", ps, depth, &r_s->num_deltas))
+                return False;
+       if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->num_deltas2))
+                return False;
+
+       if (r_s->num_deltas2 != 0)
+       {
+               if (!prs_uint32("num_deltas2 ", ps, depth, &r_s->num_deltas2))
+                        return False;
+
+               if (r_s->ptr_deltas != 0)
+               {
+                        if (r_s->num_deltas > 0) {
+                                r_s->hdr_deltas = (SAM_DELTA_HDR *)
+                                        talloc(ps->mem_ctx, r_s->num_deltas *
+                                               sizeof(SAM_DELTA_HDR));
+                                if (r_s->hdr_deltas == NULL) {
+                                        DEBUG(0, ("error tallocating memory "
+                                                  "for %d delta headers\n", 
+                                                  r_s->num_deltas));
+                                        return False;
+                                }
+                        }
+
+                       for (i = 0; i < r_s->num_deltas; i++)
+                       {
+                               net_io_sam_delta_hdr("", &r_s->hdr_deltas[i],
+                                                      ps, depth);
+                       }
+                        
+                        if (r_s->num_deltas > 0) {
+                                r_s->deltas = (SAM_DELTA_CTR *)
+                                        talloc(ps->mem_ctx, r_s->num_deltas *
+                                               sizeof(SAM_DELTA_CTR));
+
+                                if (r_s->deltas == NULL) {
+                                        DEBUG(0, ("error tallocating memory "
+                                                  "for %d deltas\n", 
+                                                  r_s->num_deltas));
+                                        return False;
+                                }
+                        }
+
+                       for (i = 0; i < r_s->num_deltas; i++)
+                       {
+                               if (!net_io_sam_delta_ctr(
+                                        "", sess_key,
+                                        &r_s->deltas[i],
+                                        r_s->hdr_deltas[i].type2,
+                                        ps, depth))
+                                        
+                                        return False;
+                       }
+               }
+       }
+
+       prs_align(ps);
+       if (!prs_ntstatus("status", ps, depth, &r_s->status))
+                return False;
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_prs.c b/source4/rpc_parse/parse_prs.c
new file mode 100644 (file)
index 0000000..46879de
--- /dev/null
@@ -0,0 +1,1329 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba memory buffer functions
+   Copyright (C) Andrew Tridgell              1992-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Jeremy Allison 1999.
+   
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/**
+ * Dump a prs to a file: from the current location through to the end.
+ **/
+void prs_dump(char *name, int v, prs_struct *ps)
+{
+       prs_dump_region(name, v, ps, ps->data_offset, ps->buffer_size);
+}
+
+
+/**
+ * Dump from the start of the prs to the current location.
+ **/
+void prs_dump_before(char *name, int v, prs_struct *ps)
+{
+       prs_dump_region(name, v, ps, 0, ps->data_offset);
+}
+
+
+/**
+ * Dump everything from the start of the prs up to the current location.
+ **/
+void prs_dump_region(char *name, int v, prs_struct *ps,
+                    int from_off, int to_off)
+{
+       int fd, i;
+       pstring fname;
+       if (DEBUGLEVEL < 50) return;
+       for (i=1;i<100;i++) {
+               if (v != -1) {
+                       slprintf(fname,sizeof(fname)-1, "/tmp/%s_%d.%d.prs", name, v, i);
+               } else {
+                       slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.prs", name, i);
+               }
+               fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+               if (fd != -1 || errno != EEXIST) break;
+       }
+       if (fd != -1) {
+               write(fd, ps->data_p + from_off, to_off - from_off);
+               close(fd);
+               DEBUG(0,("created %s\n", fname));
+       }
+}
+
+
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void prs_debug(prs_struct *ps, int depth, const char *desc, const char *fn_name)
+{
+       DEBUG(5+depth, ("%s%06x %s %s\n", DEBUGTAB(depth), ps->data_offset, fn_name, desc));
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size.  If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return False if allocation fails, otherwise True.
+ **/
+BOOL prs_init(prs_struct *ps, uint32 size, TALLOC_CTX *ctx, BOOL io)
+{
+       ZERO_STRUCTP(ps);
+       ps->io = io;
+       ps->bigendian_data = RPC_LITTLE_ENDIAN;
+       ps->align = RPC_PARSE_ALIGN;
+       ps->is_dynamic = False;
+       ps->data_offset = 0;
+       ps->buffer_size = 0;
+       ps->data_p = NULL;
+       ps->mem_ctx = ctx;
+
+       if (size != 0) {
+               ps->buffer_size = size;
+               if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+                       DEBUG(0,("prs_init: malloc fail for %u bytes.\n", (unsigned int)size));
+                       return False;
+               }
+               memset(ps->data_p, '\0', (size_t)size);
+               ps->is_dynamic = True; /* We own this memory. */
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Delete the memory in a parse structure - if we own it.
+ ********************************************************************/
+
+void prs_mem_free(prs_struct *ps)
+{
+       if(ps->is_dynamic)
+               SAFE_FREE(ps->data_p);
+       ps->is_dynamic = False;
+       ps->buffer_size = 0;
+       ps->data_offset = 0;
+}
+
+/*******************************************************************
+ Clear the memory in a parse structure.
+ ********************************************************************/
+
+void prs_mem_clear(prs_struct *ps)
+{
+       if (ps->buffer_size)
+               memset(ps->data_p, '\0', (size_t)ps->buffer_size);
+}
+
+/*******************************************************************
+ Allocate memory when unmarshalling... Always zero clears.
+ ********************************************************************/
+
+char *prs_alloc_mem(prs_struct *ps, size_t size)
+{
+       char *ret = NULL;
+
+       if (size) {
+               ret = talloc(ps->mem_ctx, size);
+               if (ret)
+                       memset(ret, '\0', size);
+       }
+       return ret;
+}
+
+/*******************************************************************
+ Return the current talloc context we're using.
+ ********************************************************************/
+
+TALLOC_CTX *prs_get_mem_context(prs_struct *ps)
+{
+       return ps->mem_ctx;
+}
+
+/*******************************************************************
+ Hand some already allocated memory to a prs_struct.
+ ********************************************************************/
+
+void prs_give_memory(prs_struct *ps, char *buf, uint32 size, BOOL is_dynamic)
+{
+       ps->is_dynamic = is_dynamic;
+       ps->data_p = buf;
+       ps->buffer_size = size;
+}
+
+/*******************************************************************
+ Take some memory back from a prs_struct.
+ ********************************************************************/
+
+char *prs_take_memory(prs_struct *ps, uint32 *psize)
+{
+       char *ret = ps->data_p;
+       if(psize)
+               *psize = ps->buffer_size;
+       ps->is_dynamic = False;
+       prs_mem_free(ps);
+       return ret;
+}
+
+/*******************************************************************
+ Set a prs_struct to exactly a given size. Will grow or tuncate if neccessary.
+ ********************************************************************/
+
+BOOL prs_set_buffer_size(prs_struct *ps, uint32 newsize)
+{
+       if (newsize > ps->buffer_size)
+               return prs_force_grow(ps, newsize - ps->buffer_size);
+
+       if (newsize < ps->buffer_size) {
+               char *new_data_p = Realloc(ps->data_p, newsize);
+               /* if newsize is zero, Realloc acts like free() & returns NULL*/
+               if (new_data_p == NULL && newsize != 0) {
+                       DEBUG(0,("prs_set_buffer_size: Realloc failure for size %u.\n",
+                               (unsigned int)newsize));
+                       DEBUG(0,("prs_set_buffer_size: Reason %s\n",strerror(errno)));
+                       return False;
+               }
+               ps->data_p = new_data_p;
+               ps->buffer_size = newsize;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL prs_grow(prs_struct *ps, uint32 extra_space)
+{
+       uint32 new_size;
+       char *new_data;
+
+       ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+       if(ps->data_offset + extra_space <= ps->buffer_size)
+               return True;
+
+       /*
+        * We cannot grow the buffer if we're not reading
+        * into the prs_struct, or if we don't own the memory.
+        */
+
+       if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+               DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+                               (unsigned int)extra_space));
+               return False;
+       }
+       
+       /*
+        * Decide how much extra space we really need.
+        */
+
+       extra_space -= (ps->buffer_size - ps->data_offset);
+       if(ps->buffer_size == 0) {
+               /*
+                * Ensure we have at least a PDU's length, or extra_space, whichever
+                * is greater.
+                */
+
+               new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+               if((new_data = malloc(new_size)) == NULL) {
+                       DEBUG(0,("prs_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+                       return False;
+               }
+               memset(new_data, '\0', (size_t)new_size );
+       } else {
+               /*
+                * If the current buffer size is bigger than the space needed, just 
+                * double it, else add extra_space.
+                */
+               new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);               
+
+               if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+                       DEBUG(0,("prs_grow: Realloc failure for size %u.\n",
+                               (unsigned int)new_size));
+                       return False;
+               }
+
+               memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+       }
+       ps->buffer_size = new_size;
+       ps->data_p = new_data;
+
+       return True;
+}
+
+/*******************************************************************
+ Attempt to force a data buffer to grow by len bytes.
+ This is only used when appending more data onto a prs_struct
+ when reading an rpc reply, before unmarshalling it.
+ ********************************************************************/
+
+BOOL prs_force_grow(prs_struct *ps, uint32 extra_space)
+{
+       uint32 new_size = ps->buffer_size + extra_space;
+       char *new_data;
+
+       if(!UNMARSHALLING(ps) || !ps->is_dynamic) {
+               DEBUG(0,("prs_force_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+                               (unsigned int)extra_space));
+               return False;
+       }
+
+       if((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+               DEBUG(0,("prs_force_grow: Realloc failure for size %u.\n",
+                       (unsigned int)new_size));
+               return False;
+       }
+
+       memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+
+       ps->buffer_size = new_size;
+       ps->data_p = new_data;
+
+       return True;
+}
+
+/*******************************************************************
+ Get the data pointer (external interface).
+********************************************************************/
+
+char *prs_data_p(prs_struct *ps)
+{
+       return ps->data_p;
+}
+
+/*******************************************************************
+ Get the current data size (external interface).
+ ********************************************************************/
+
+uint32 prs_data_size(prs_struct *ps)
+{
+       return ps->buffer_size;
+}
+
+/*******************************************************************
+ Fetch the current offset (external interface).
+ ********************************************************************/
+
+uint32 prs_offset(prs_struct *ps)
+{
+       return ps->data_offset;
+}
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+
+BOOL prs_set_offset(prs_struct *ps, uint32 offset)
+{
+       if(offset <= ps->data_offset) {
+               ps->data_offset = offset;
+               return True;
+       }
+
+       if(!prs_grow(ps, offset - ps->data_offset))
+               return False;
+
+       ps->data_offset = offset;
+       return True;
+}
+
+/*******************************************************************
+ Append the data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_prs_data(prs_struct *dst, prs_struct *src)
+{
+       if (prs_offset(src) == 0)
+               return True;
+
+       if(!prs_grow(dst, prs_offset(src)))
+               return False;
+
+       memcpy(&dst->data_p[dst->data_offset], src->data_p, (size_t)prs_offset(src));
+       dst->data_offset += prs_offset(src);
+
+       return True;
+}
+
+/*******************************************************************
+ Append some data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_some_prs_data(prs_struct *dst, prs_struct *src, int32 start, uint32 len)
+{      
+       if (len == 0)
+               return True;
+
+       if(!prs_grow(dst, len))
+               return False;
+       
+       memcpy(&dst->data_p[dst->data_offset], src->data_p + start, (size_t)len);
+       dst->data_offset += len;
+
+       return True;
+}
+
+/*******************************************************************
+ Append the data from a buffer into a parse_struct.
+ ********************************************************************/
+
+BOOL prs_copy_data_in(prs_struct *dst, char *src, uint32 len)
+{
+       if (len == 0)
+               return True;
+
+       if(!prs_grow(dst, len))
+               return False;
+
+       memcpy(&dst->data_p[dst->data_offset], src, (size_t)len);
+       dst->data_offset += len;
+
+       return True;
+}
+
+/*******************************************************************
+ Copy some data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_data_out(char *dst, prs_struct *src, uint32 len)
+{
+       if (len == 0)
+               return True;
+
+       if(!prs_mem_get(src, len))
+               return False;
+
+       memcpy(dst, &src->data_p[src->data_offset], (size_t)len);
+       src->data_offset += len;
+
+       return True;
+}
+
+/*******************************************************************
+ Copy all the data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_all_data_out(char *dst, prs_struct *src)
+{
+       uint32 len = prs_offset(src);
+
+       if (!len)
+               return True;
+
+       prs_set_offset(src, 0);
+       return prs_copy_data_out(dst, src, len);
+}
+
+/*******************************************************************
+ Set the data as X-endian (external interface).
+ ********************************************************************/
+
+void prs_set_endian_data(prs_struct *ps, BOOL endian)
+{
+       ps->bigendian_data = endian;
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL prs_align(prs_struct *ps)
+{
+       uint32 mod = ps->data_offset & (ps->align-1);
+
+       if (ps->align != 0 && mod != 0) {
+               uint32 extra_space = (ps->align - mod);
+               if(!prs_grow(ps, extra_space))
+                       return False;
+               memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+               ps->data_offset += extra_space;
+       }
+
+       return True;
+}
+
+/******************************************************************
+ Align on a 2 byte boundary
+ *****************************************************************/
+BOOL prs_align_uint16(prs_struct *ps)
+{
+       BOOL ret;
+       uint8 old_align = ps->align;
+
+       ps->align = 2;
+       ret = prs_align(ps);
+       ps->align = old_align;
+       
+       return ret;
+}
+
+/******************************************************************
+ Align on a 8 byte boundary
+ *****************************************************************/
+BOOL prs_align_uint64(prs_struct *ps)
+{
+       BOOL ret;
+       uint8 old_align = ps->align;
+
+       ps->align = 8;
+       ret = prs_align(ps);
+       ps->align = old_align;
+       
+       return ret;
+}
+
+/*******************************************************************
+ Align only if required (for the unistr2 string mainly)
+ ********************************************************************/
+
+BOOL prs_align_needed(prs_struct *ps, uint32 needed)
+{
+       if (needed==0)
+               return True;
+       else
+               return prs_align(ps);
+}
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+       if(UNMARSHALLING(ps)) {
+               /*
+                * If reading, ensure that we can read the requested size item.
+                */
+               if (ps->data_offset + extra_size > ps->buffer_size) {
+                       DEBUG(0,("prs_mem_get: reading data of size %u would overrun buffer.\n",
+                                       (unsigned int)extra_size ));
+                       return NULL;
+               }
+       } else {
+               /*
+                * Writing - grow the buffer if needed.
+                */
+               if(!prs_grow(ps, extra_size))
+                       return NULL;
+       }
+       return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Change the struct type.
+ ********************************************************************/
+
+void prs_switch_type(prs_struct *ps, BOOL io)
+{
+       if ((ps->io ^ io) == True)
+               ps->io=io;
+}
+
+/*******************************************************************
+ Force a prs_struct to be dynamic even when it's size is 0.
+ ********************************************************************/
+
+void prs_force_dynamic(prs_struct *ps)
+{
+       ps->is_dynamic=True;
+}
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+
+BOOL prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+       char *q = prs_mem_get(ps, 1);
+       if (q == NULL)
+               return False;
+
+    if (UNMARSHALLING(ps))
+               *data8 = CVAL(q,0);
+       else
+               SCVAL(q,0,*data8);
+
+    DEBUG(5,("%s%04x %s: %02x\n", DEBUGTAB(depth), ps->data_offset, name, *data8));
+
+       ps->data_offset += 1;
+
+       return True;
+}
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+
+BOOL prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+       char *q = prs_mem_get(ps, sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+    if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data)
+                       *data16 = RSVAL(q,0);
+               else
+                       *data16 = SVAL(q,0);
+    } else {
+               if (ps->bigendian_data)
+                       RSSVAL(q,0,*data16);
+               else
+                       SSVAL(q,0,*data16);
+       }
+
+       DEBUG(5,("%s%04x %s: %04x\n", DEBUGTAB(depth), ps->data_offset, name, *data16));
+
+       ps->data_offset += sizeof(uint16);
+
+       return True;
+}
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+
+BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+       char *q = prs_mem_get(ps, sizeof(uint32));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data)
+                       *data32 = RIVAL(q,0);
+               else
+                       *data32 = IVAL(q,0);
+       } else {
+               if (ps->bigendian_data)
+                       RSIVAL(q,0,*data32);
+               else
+                       SIVAL(q,0,*data32);
+       }
+
+       DEBUG(5,("%s%04x %s: %08x\n", DEBUGTAB(depth), ps->data_offset, name, *data32));
+
+       ps->data_offset += sizeof(uint32);
+
+       return True;
+}
+
+/*******************************************************************
+ Stream a NTSTATUS
+ ********************************************************************/
+
+BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status)
+{
+       char *q = prs_mem_get(ps, sizeof(uint32));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data)
+                       *status = NT_STATUS(RIVAL(q,0));
+               else
+                       *status = NT_STATUS(IVAL(q,0));
+       } else {
+               if (ps->bigendian_data)
+                       RSIVAL(q,0,NT_STATUS_V(*status));
+               else
+                       SIVAL(q,0,NT_STATUS_V(*status));
+       }
+
+       DEBUG(5,("%s%04x %s: %s\n", DEBUGTAB(depth), ps->data_offset, name, 
+                nt_errstr(*status)));
+
+       ps->data_offset += sizeof(uint32);
+
+       return True;
+}
+
+/*******************************************************************
+ Stream a WERROR
+ ********************************************************************/
+
+BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status)
+{
+       char *q = prs_mem_get(ps, sizeof(uint32));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data)
+                       *status = W_ERROR(RIVAL(q,0));
+               else
+                       *status = W_ERROR(IVAL(q,0));
+       } else {
+               if (ps->bigendian_data)
+                       RSIVAL(q,0,W_ERROR_V(*status));
+               else
+                       SIVAL(q,0,W_ERROR_V(*status));
+       }
+
+       ps->data_offset += sizeof(uint32);
+
+       return True;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+
+BOOL prs_uint8s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint8 *data8s, int len)
+{
+       int i;
+       char *q = prs_mem_get(ps, len);
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               for (i = 0; i < len; i++)
+                       data8s[i] = CVAL(q,i);
+       } else {
+               for (i = 0; i < len; i++)
+                       SCVAL(q, i, data8s[i]);
+       }
+
+    DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset ,name));
+    if (charmode)
+               print_asc(5, (unsigned char*)data8s, len);
+       else {
+       for (i = 0; i < len; i++)
+                       DEBUG(5,("%02x ", data8s[i]));
+       }
+    DEBUG(5,("\n"));
+
+       ps->data_offset += len;
+
+       return True;
+}
+
+/******************************************************************
+ Stream an array of uint16s. Length is number of uint16s.
+ ********************************************************************/
+
+BOOL prs_uint16s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+       int i;
+       char *q = prs_mem_get(ps, len * sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               data16s[i] = RSVAL(q, 2*i);
+               } else {
+                       for (i = 0; i < len; i++)
+                               data16s[i] = SVAL(q, 2*i);
+               }
+       } else {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               RSSVAL(q, 2*i, data16s[i]);
+               } else {
+                       for (i = 0; i < len; i++)
+                               SSVAL(q, 2*i, data16s[i]);
+               }
+       }
+
+       DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+       if (charmode)
+               print_asc(5, (unsigned char*)data16s, 2*len);
+       else {
+               for (i = 0; i < len; i++)
+                       DEBUG(5,("%04x ", data16s[i]));
+       }
+    DEBUG(5,("\n"));
+
+       ps->data_offset += (len * sizeof(uint16));
+
+       return True;
+}
+
+/******************************************************************
+ Start using a function for streaming unicode chars. If unmarshalling,
+ output must be little-endian, if marshalling, input must be little-endian.
+ ********************************************************************/
+
+static void dbg_rw_punival(BOOL charmode, const char *name, int depth, prs_struct *ps,
+                                                       char *in_buf, char *out_buf, int len)
+{
+       int i;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               SSVAL(out_buf,2*i,RSVAL(in_buf, 2*i));
+               } else {
+                       for (i = 0; i < len; i++)
+                               SSVAL(out_buf, 2*i, SVAL(in_buf, 2*i));
+               }
+       } else {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               RSSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+               } else {
+                       for (i = 0; i < len; i++)
+                               SSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+               }
+       }
+
+       DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+       if (charmode)
+               print_asc(5, (unsigned char*)out_buf, 2*len);
+       else {
+               for (i = 0; i < len; i++)
+                       DEBUG(5,("%04x ", out_buf[i]));
+       }
+    DEBUG(5,("\n"));
+}
+
+/******************************************************************
+ Stream a unistr. Always little endian.
+ ********************************************************************/
+
+BOOL prs_uint16uni(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+       char *q = prs_mem_get(ps, len * sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+       dbg_rw_punival(charmode, name, depth, ps, q, (char *)data16s, len);
+       ps->data_offset += (len * sizeof(uint16));
+
+       return True;
+}
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+
+BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len)
+{
+       int i;
+       char *q = prs_mem_get(ps, len * sizeof(uint32));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               data32s[i] = RIVAL(q, 4*i);
+               } else {
+                       for (i = 0; i < len; i++)
+                               data32s[i] = IVAL(q, 4*i);
+               }
+       } else {
+               if (ps->bigendian_data) {
+                       for (i = 0; i < len; i++)
+                               RSIVAL(q, 4*i, data32s[i]);
+               } else {
+                       for (i = 0; i < len; i++)
+                               SIVAL(q, 4*i, data32s[i]);
+               }
+       }
+
+       DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+       if (charmode)
+               print_asc(5, (unsigned char*)data32s, 4*len);
+       else {
+               for (i = 0; i < len; i++)
+                       DEBUG(5,("%08x ", data32s[i]));
+       }
+    DEBUG(5,("\n"));
+
+       ps->data_offset += (len * sizeof(uint32));
+
+       return True;
+}
+
+/******************************************************************
+ Stream an array of unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer5(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER5 *str)
+{
+       char *p;
+       char *q = prs_mem_get(ps, str->buf_len * sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len * sizeof(uint16));
+               if (str->buffer == NULL)
+                       return False;
+       }
+
+       /* If the string is empty, we don't have anything to stream */
+       if (str->buf_len==0)
+               return True;
+
+       p = (char *)str->buffer;
+
+       dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len);
+       
+       ps->data_offset += (str->buf_len * sizeof(uint16));
+
+       return True;
+}
+
+/******************************************************************
+ Stream a "not" unicode string, length/buffer specified separately,
+ in byte chars. String is in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer2(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER2 *str)
+{
+       char *p;
+       char *q = prs_mem_get(ps, str->buf_len);
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               if ( str->buf_len ) {
+                       str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len);
+                       if ( str->buffer == NULL )
+                               return False;
+               }
+       }
+
+       p = (char *)str->buffer;
+
+       dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len/2);
+       ps->data_offset += str->buf_len;
+
+       return True;
+}
+
+/******************************************************************
+ Stream a string, length/buffer specified separately,
+ in uint8 chars.
+ ********************************************************************/
+
+BOOL prs_string2(BOOL charmode, const char *name, prs_struct *ps, int depth, STRING2 *str)
+{
+       int i;
+       char *q = prs_mem_get(ps, str->str_max_len);
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               str->buffer = (unsigned char *)prs_alloc_mem(ps,str->str_max_len);
+               if (str->buffer == NULL)
+                       return False;
+       }
+
+       if (UNMARSHALLING(ps)) {
+               for (i = 0; i < str->str_str_len; i++)
+                       str->buffer[i] = CVAL(q,i);
+       } else {
+               for (i = 0; i < str->str_str_len; i++)
+                       SCVAL(q, i, str->buffer[i]);
+       }
+
+    DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+    if (charmode)
+               print_asc(5, (unsigned char*)str->buffer, str->str_str_len);
+       else {
+       for (i = 0; i < str->str_str_len; i++)
+                       DEBUG(5,("%02x ", str->buffer[i]));
+       }
+    DEBUG(5,("\n"));
+
+       ps->data_offset += str->str_str_len;
+
+       return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr2(BOOL charmode, const char *name, prs_struct *ps, int depth, UNISTR2 *str)
+{
+       char *p;
+       char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+       /* If the string is empty, we don't have anything to stream */
+       if (str->uni_str_len==0)
+               return True;
+
+       if (UNMARSHALLING(ps)) {
+               str->buffer = (uint16 *)prs_alloc_mem(ps,str->uni_max_len * sizeof(uint16));
+               if (str->buffer == NULL)
+                       return False;
+       }
+
+       p = (char *)str->buffer;
+
+       dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+       
+       ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+       return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr3(BOOL charmode, const char *name, UNISTR3 *str, prs_struct *ps, int depth)
+{
+       char *p;
+       char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+       if (q == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               str->str.buffer = (uint16 *)prs_alloc_mem(ps,str->uni_str_len * sizeof(uint16));
+               if (str->str.buffer == NULL)
+                       return False;
+       }
+
+       p = (char *)str->str.buffer;
+
+       dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+       ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+       return True;
+}
+
+/*******************************************************************
+ Stream a unicode  null-terminated string. As the string is already
+ in little-endian format then do it as a stream of bytes.
+ ********************************************************************/
+
+BOOL prs_unistr(const char *name, prs_struct *ps, int depth, UNISTR *str)
+{
+       int len = 0;
+       unsigned char *p = (unsigned char *)str->buffer;
+       uint8 *start;
+       char *q;
+       uint32 max_len;
+       uint16* ptr;
+
+       if (MARSHALLING(ps)) {
+
+               for(len = 0; str->buffer[len] != 0; len++)
+                       ;
+
+               q = prs_mem_get(ps, (len+1)*2);
+               if (q == NULL)
+                       return False;
+
+               start = (uint8*)q;
+
+               for(len = 0; str->buffer[len] != 0; len++) 
+               {
+                       if(ps->bigendian_data) 
+                       {
+                               /* swap bytes - p is little endian, q is big endian. */
+                               q[0] = (char)p[1];
+                               q[1] = (char)p[0];
+                               p += 2;
+                               q += 2;
+                       } 
+                       else 
+                       {
+                               q[0] = (char)p[0];
+                               q[1] = (char)p[1];
+                               p += 2;
+                               q += 2;
+                       }
+               }
+
+               /*
+                * even if the string is 'empty' (only an \0 char)
+                * at this point the leading \0 hasn't been parsed.
+                * so parse it now
+                */
+
+               q[0] = 0;
+               q[1] = 0;
+               q += 2;
+
+               len++;
+
+               DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+               print_asc(5, (unsigned char*)start, 2*len);     
+               DEBUG(5, ("\n"));
+       }
+       else { /* unmarshalling */
+       
+               uint32 alloc_len = 0;
+               q = ps->data_p + prs_offset(ps);
+
+               /*
+                * Work out how much space we need and talloc it.
+                */
+               max_len = (ps->buffer_size - ps->data_offset)/sizeof(uint16);
+
+               /* the test of the value of *ptr helps to catch the circumstance
+                  where we have an emtpty (non-existent) string in the buffer */
+               for ( ptr = (uint16 *)q; *ptr && (alloc_len <= max_len); alloc_len++)
+                       /* do nothing */ 
+                       ;
+
+               /* should we allocate anything at all? */
+               str->buffer = (uint16 *)prs_alloc_mem(ps,alloc_len * sizeof(uint16));
+               if ((str->buffer == NULL) && (alloc_len > 0))
+                       return False;
+
+               p = (unsigned char *)str->buffer;
+
+               len = 0;
+               /* the (len < alloc_len) test is to prevent us from overwriting
+                  memory that is not ours...if we get that far, we have a non-null
+                  terminated string in the buffer and have messed up somewhere */
+               while ((len < alloc_len) && (*(uint16 *)q != 0))
+               {
+                       if(ps->bigendian_data) 
+                       {
+                               /* swap bytes - q is big endian, p is little endian. */
+                               p[0] = (unsigned char)q[1];
+                               p[1] = (unsigned char)q[0];
+                               p += 2;
+                               q += 2;
+                       } else {
+
+                               p[0] = (unsigned char)q[0];
+                               p[1] = (unsigned char)q[1];
+                               p += 2;
+                               q += 2;
+                       }
+
+                       len++;
+               } 
+               if (len < alloc_len)
+               {
+                       /* NULL terminate the UNISTR */
+                       str->buffer[len++] = '\0';
+               }
+
+               DEBUG(5,("%s%04x %s: ", DEBUGTAB(depth), ps->data_offset, name));
+               print_asc(5, (unsigned char*)str->buffer, 2*len);       
+               DEBUG(5, ("\n"));
+       }
+
+       /* set the offset in the prs_struct; 'len' points to the
+          terminiating NULL in the UNISTR so we need to go one more
+          uint16 */
+       ps->data_offset += (len)*2;
+       
+       return True;
+}
+
+
+/*******************************************************************
+ Stream a null-terminated string.  len is strlen, and therefore does
+ not include the null-termination character.
+ ********************************************************************/
+
+BOOL prs_string(const char *name, prs_struct *ps, int depth, char *str, int len, int max_buf_size)
+{
+       char *q;
+       int i;
+
+       len = MIN(len, (max_buf_size-1));
+
+       q = prs_mem_get(ps, len+1);
+       if (q == NULL)
+               return False;
+
+       for(i = 0; i < len; i++) {
+               if (UNMARSHALLING(ps))
+                       str[i] = q[i];
+               else
+                       q[i] = str[i];
+       }
+
+       /* The terminating null. */
+       str[i] = '\0';
+
+       if (MARSHALLING(ps)) {
+               q[i] = '\0';
+       }
+
+       ps->data_offset += len+1;
+
+       dump_data(5+depth, q, len);
+
+       return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint16_pre(const char *name, prs_struct *ps, int depth, uint16 *data16, uint32 *offset)
+{
+       *offset = ps->data_offset;
+       if (UNMARSHALLING(ps)) {
+               /* reading. */
+               return prs_uint16(name, ps, depth, data16);
+       } else {
+               char *q = prs_mem_get(ps, sizeof(uint16));
+               if(q ==NULL)
+                       return False;
+               ps->data_offset += sizeof(uint16);
+       }
+       return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper.  call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint16_post(const char *name, prs_struct *ps, int depth, uint16 *data16,
+                               uint32 ptr_uint16, uint32 start_offset)
+{
+       if (MARSHALLING(ps)) {
+               /* 
+                * Writing - temporarily move the offset pointer.
+                */
+               uint16 data_size = ps->data_offset - start_offset;
+               uint32 old_offset = ps->data_offset;
+
+               ps->data_offset = ptr_uint16;
+               if(!prs_uint16(name, ps, depth, &data_size)) {
+                       ps->data_offset = old_offset;
+                       return False;
+               }
+               ps->data_offset = old_offset;
+       } else {
+               ps->data_offset = start_offset + (uint32)(*data16);
+       }
+       return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper. Call this and it sets up a pointer to where the
+ uint32 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint32_pre(const char *name, prs_struct *ps, int depth, uint32 *data32, uint32 *offset)
+{
+       *offset = ps->data_offset;
+       if (UNMARSHALLING(ps) && (data32 != NULL)) {
+               /* reading. */
+               return prs_uint32(name, ps, depth, data32);
+       } else {
+               ps->data_offset += sizeof(uint32);
+       }
+       return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper.  call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint32_post(const char *name, prs_struct *ps, int depth, uint32 *data32,
+                               uint32 ptr_uint32, uint32 data_size)
+{
+       if (MARSHALLING(ps)) {
+               /* 
+                * Writing - temporarily move the offset pointer.
+                */
+               uint32 old_offset = ps->data_offset;
+               ps->data_offset = ptr_uint32;
+               if(!prs_uint32(name, ps, depth, &data_size)) {
+                       ps->data_offset = old_offset;
+                       return False;
+               }
+               ps->data_offset = old_offset;
+       }
+       return True;
+}
+
+/* useful function to store a structure in rpc wire format */
+int tdb_prs_store(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps)
+{
+    TDB_DATA kbuf, dbuf;
+    kbuf.dptr = keystr;
+    kbuf.dsize = strlen(keystr)+1;
+    dbuf.dptr = ps->data_p;
+    dbuf.dsize = prs_offset(ps);
+    return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+}
+
+/* useful function to fetch a structure into rpc wire format */
+int tdb_prs_fetch(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps, TALLOC_CTX *mem_ctx)
+{
+    TDB_DATA kbuf, dbuf;
+    kbuf.dptr = keystr;
+    kbuf.dsize = strlen(keystr)+1;
+
+    dbuf = tdb_fetch(tdb, kbuf);
+    if (!dbuf.dptr)
+           return -1;
+
+    ZERO_STRUCTP(ps);
+    prs_init(ps, 0, mem_ctx, UNMARSHALL);
+    prs_give_memory(ps, dbuf.dptr, dbuf.dsize, True);
+
+    return 0;
+} 
+
+/*******************************************************************
+ hash a stream.
+ ********************************************************************/
+BOOL prs_hash1(prs_struct *ps, uint32 offset, uint8 sess_key[16])
+{
+       char *q;
+
+       q = ps->data_p;
+        q = &q[offset];
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100, ("prs_hash1\n"));
+       dump_data(100, sess_key, 16);
+       dump_data(100, q, 68);
+#endif
+       SamOEMhash((uchar *) q, sess_key, 68);
+
+#ifdef DEBUG_PASSWORD
+       dump_data(100, q, 68);
+#endif
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_reg.c b/source4/rpc_parse/parse_reg.c
new file mode 100644 (file)
index 0000000..b4d20bf
--- /dev/null
@@ -0,0 +1,1872 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  Copyright (C) Marc Jacobsen                     1999.
+ *  Copyright (C) Simo Sorce                        2000.
+ *  Copyright (C) Gerald Carter                     2002.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Fill in a BUFFER2 for the data given a REGISTRY_VALUE
+ *******************************************************************/
+
+static uint32 reg_init_buffer2( BUFFER2 *buf2, REGISTRY_VALUE *val )
+{
+       uint32          real_size = 0;
+       
+       if ( !buf2 || !val )
+               return 0;
+               
+       real_size = regval_size(val);
+       init_buffer2( buf2, (char*)regval_data_p(val), real_size );
+
+       return real_size;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hkcr(REG_Q_OPEN_HKCR *q_o,
+                               uint16 unknown_0, uint32 level)
+{
+       q_o->ptr = 1;
+       q_o->unknown_0 = unknown_0;
+       q_o->unknown_1 = 0x0; /* random - changes */
+       q_o->level = level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hkcr(const char *desc,  REG_Q_OPEN_HKCR *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_open_hkcr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr      ", ps, depth, &r_q->ptr))
+               return False;
+
+       if (r_q->ptr != 0) {
+               if(!prs_uint16("unknown_0", ps, depth, &r_q->unknown_0))
+                       return False;
+               if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+                       return False;
+               if(!prs_uint32("level    ", ps, depth, &r_q->level))
+                       return False;
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hkcr(const char *desc,  REG_R_OPEN_HKCR *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_open_hkcr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hklm(REG_Q_OPEN_HKLM * q_o,
+                         uint16 unknown_0, uint32 access_mask)
+{
+       q_o->ptr = 1;
+       q_o->unknown_0 = unknown_0;
+       q_o->unknown_1 = 0x0;   /* random - changes */
+       q_o->access_mask = access_mask;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_open_hklm(const char *desc, REG_Q_OPEN_HKLM * r_q, prs_struct *ps,
+                       int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_open_hklm");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr      ", ps, depth, &(r_q->ptr)))
+               return False;
+       if (r_q->ptr != 0)
+       {
+               if (!prs_uint16("unknown_0", ps, depth, &(r_q->unknown_0)))
+                       return False;
+               if (!prs_uint16("unknown_1", ps, depth, &(r_q->unknown_1)))
+                       return False;
+               if (!prs_uint32("access_mask", ps, depth, &(r_q->access_mask)))
+                       return False;
+       }
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_open_hklm(const char *desc, REG_R_OPEN_HKLM * r_r, prs_struct *ps,
+                       int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_open_hklm");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+               return False;
+
+       if (!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_flush_key(REG_Q_FLUSH_KEY *q_u, POLICY_HND *pol)
+{
+       memcpy(&q_u->pol, pol, sizeof(q_u->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_flush_key(const char *desc,  REG_Q_FLUSH_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_flush_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_flush_key(const char *desc,  REG_R_FLUSH_KEY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_flush_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes SEC_DESC_BUF and SEC_DATA structures.
+********************************************************************/
+
+static BOOL reg_io_hdrbuf_sec(uint32 ptr, uint32 *ptr3, BUFHDR *hdr_sec, SEC_DESC_BUF *data, prs_struct *ps, int depth)
+{
+       if (ptr != 0) {
+               uint32 hdr_offset;
+               uint32 old_offset;
+               if(!smb_io_hdrbuf_pre("hdr_sec", hdr_sec, ps, depth, &hdr_offset))
+                       return False;
+
+               old_offset = prs_offset(ps);
+
+               if (ptr3 != NULL) {
+                       if(!prs_uint32("ptr3", ps, depth, ptr3))
+                               return False;
+               }
+
+               if (ptr3 == NULL || *ptr3 != 0) {
+                       if(!sec_io_desc_buf("data   ", &data, ps, depth)) /* JRA - this line is probably wrong... */
+                               return False;
+               }
+
+               if(!smb_io_hdrbuf_post("hdr_sec", hdr_sec, ps, depth, hdr_offset,
+                                  data->max_len, data->len))
+                               return False;
+               if(!prs_set_offset(ps, old_offset + data->len + sizeof(uint32) * ((ptr3 != NULL) ? 5 : 3)))
+                       return False;
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_create_key(REG_Q_CREATE_KEY *q_c, POLICY_HND *hnd,
+                               char *name, char *class, SEC_ACCESS *sam_access,
+                               SEC_DESC_BUF *sec_buf)
+{
+       int len_name  = name  != NULL ? strlen(name ) + 1: 0;
+       int len_class = class != NULL ? strlen(class) + 1: 0;
+
+       ZERO_STRUCTP(q_c);
+
+       memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+       init_uni_hdr(&q_c->hdr_name, len_name);
+       init_unistr2(&q_c->uni_name, name, len_name);
+
+       init_uni_hdr(&q_c->hdr_class, len_class);
+       init_unistr2(&q_c->uni_class, class, len_class);
+
+       q_c->reserved = 0x00000000;
+       memcpy(&q_c->sam_access, sam_access, sizeof(q_c->sam_access));
+
+       q_c->ptr1 = 1;
+       q_c->sec_info = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
+
+       q_c->data = sec_buf;
+       q_c->ptr2 = 1;
+       init_buf_hdr(&q_c->hdr_sec, sec_buf->len, sec_buf->len);
+       q_c->ptr3 = 1;
+       q_c->unknown_2 = 0x00000000;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_key(const char *desc,  REG_Q_CREATE_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_create_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("reserved", ps, depth, &r_q->reserved))
+               return False;
+       if(!sec_io_access("sam_access", &r_q->sam_access, ps, depth))
+               return False;
+
+       if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+               return False;
+
+       if (r_q->ptr1 != 0) {
+               if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+               return False;
+       if(!reg_io_hdrbuf_sec(r_q->ptr2, &r_q->ptr3, &r_q->hdr_sec, r_q->data, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_key(const char *desc,  REG_R_CREATE_KEY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_create_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_r->key_pol, ps, depth))
+               return False;
+       if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_val(REG_Q_DELETE_VALUE *q_c, POLICY_HND *hnd,
+                               char *name)
+{
+       int len_name  = name  != NULL ? strlen(name ) + 1: 0;
+       ZERO_STRUCTP(q_c);
+
+       memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+       init_uni_hdr(&q_c->hdr_name, len_name);
+       init_unistr2(&q_c->uni_name, name, len_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_val(const char *desc,  REG_Q_DELETE_VALUE *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_delete_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_val(const char *desc,  REG_R_DELETE_VALUE *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_delete_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_key(REG_Q_DELETE_KEY *q_c, POLICY_HND *hnd,
+                               char *name)
+{
+       int len_name  = name  != NULL ? strlen(name ) + 1: 0;
+       ZERO_STRUCTP(q_c);
+
+       memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+       init_uni_hdr(&q_c->hdr_name, len_name);
+       init_unistr2(&q_c->uni_name, name, len_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_key(const char *desc,  REG_Q_DELETE_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_delete_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_key(const char *desc,  REG_R_DELETE_KEY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_delete_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_query_key(REG_Q_QUERY_KEY *q_o, POLICY_HND *hnd,
+                               uint32 max_class_len)
+{
+       ZERO_STRUCTP(q_o);
+
+       memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+       init_uni_hdr(&q_o->hdr_class, max_class_len);
+       q_o->uni_class.uni_max_len = max_class_len;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_query_key(const char *desc,  REG_Q_QUERY_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_query_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+       if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_query_key(const char *desc,  REG_R_QUERY_KEY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_query_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_unihdr ("", &r_r->hdr_class, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_r->uni_class, r_r->hdr_class.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_subkeys   ", ps, depth, &r_r->num_subkeys))
+               return False;
+       if(!prs_uint32("max_subkeylen ", ps, depth, &r_r->max_subkeylen))
+               return False;
+       if(!prs_uint32("reserved      ", ps, depth, &r_r->reserved))
+               return False;
+       if(!prs_uint32("num_values    ", ps, depth, &r_r->num_values))
+               return False;
+       if(!prs_uint32("max_valnamelen", ps, depth, &r_r->max_valnamelen))
+               return False;
+       if(!prs_uint32("max_valbufsize", ps, depth, &r_r->max_valbufsize))
+               return False;
+       if(!prs_uint32("sec_desc      ", ps, depth, &r_r->sec_desc))
+               return False;
+       if(!smb_io_time("mod_time     ", &r_r->mod_time, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_unknown_1a(REG_Q_UNKNOWN_1A *q_o, POLICY_HND *hnd)
+{
+       memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_unknown_1a(const char *desc,  REG_Q_UNKNOWN_1A *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_unknown_1a");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_unknown_1a(const char *desc,  REG_R_UNKNOWN_1A *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_unknown_1a");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+               return False;
+       if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_save_key(const char *desc,  REG_Q_SAVE_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_save_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr ("hdr_file", &r_q->hdr_file, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_file", &r_q->uni_file, r_q->hdr_file.buffer, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown", ps, depth, &r_q->unknown))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_save_key(const char *desc,  REG_R_SAVE_KEY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_save_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hku(REG_Q_OPEN_HKU *q_o,
+                               uint16 unknown_0, uint32 access_mask)
+{
+       q_o->ptr = 1;
+       q_o->unknown_0 = unknown_0;
+       q_o->unknown_1 = 0x0; /* random - changes */
+       q_o->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hku(const char *desc,  REG_Q_OPEN_HKU *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_open_hku");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr      ", ps, depth, &r_q->ptr))
+               return False;
+       if (r_q->ptr != 0) {
+               if(!prs_uint16("unknown_0   ", ps, depth, &r_q->unknown_0))
+                       return False;
+               if(!prs_uint16("unknown_1   ", ps, depth, &r_q->unknown_1))
+                       return False;
+               if(!prs_uint32("access_mask ", ps, depth, &r_q->access_mask))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hku(const char *desc,  REG_R_OPEN_HKU *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_open_hku");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an REG_Q_CLOSE structure.
+********************************************************************/
+
+void init_reg_q_close(REG_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+       DEBUG(5,("init_reg_q_close\n"));
+
+       memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_close(const char *desc,  REG_Q_CLOSE *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_close");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("", &q_u->pol, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_close(const char *desc,  REG_R_CLOSE *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_close");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("", &r_u->pol, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_set_key_sec(REG_Q_SET_KEY_SEC *q_i, POLICY_HND *pol, SEC_DESC_BUF *sec_desc_buf)
+{
+       memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+       q_i->sec_info = DACL_SECURITY_INFORMATION;
+
+       q_i->ptr = 1;
+       init_buf_hdr(&q_i->hdr_sec, sec_desc_buf->len, sec_desc_buf->len);
+       q_i->data = sec_desc_buf;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_set_key_sec(const char *desc,  REG_Q_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_set_key_sec");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+               return False;
+       if(!prs_uint32("ptr    ", ps, depth, &r_q->ptr))
+               return False;
+
+       if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_set_key_sec(const char *desc, REG_R_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_set_key_sec");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_get_key_sec(REG_Q_GET_KEY_SEC *q_i, POLICY_HND *pol, 
+                               uint32 sec_buf_size, SEC_DESC_BUF *psdb)
+{
+       memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+       q_i->sec_info = OWNER_SECURITY_INFORMATION |
+                       GROUP_SECURITY_INFORMATION |
+                       DACL_SECURITY_INFORMATION;
+
+       q_i->ptr = psdb != NULL ? 1 : 0;
+       q_i->data = psdb;
+
+       init_buf_hdr(&q_i->hdr_sec, sec_buf_size, 0);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_get_key_sec(const char *desc,  REG_Q_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_get_key_sec");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+               return False;
+       if(!prs_uint32("ptr     ", ps, depth, &r_q->ptr))
+               return False;
+
+       if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+               return False;
+
+       return True;
+}
+
+#if 0
+/*******************************************************************
+makes a structure.
+********************************************************************/
+ void init_reg_r_get_key_sec(REG_R_GET_KEY_SEC *r_i, POLICY_HND *pol, 
+                               uint32 buf_len, uint8 *buf,
+                               NTSTATUS status)
+{
+       r_i->ptr = 1;
+       init_buf_hdr(&r_i->hdr_sec, buf_len, buf_len);
+       init_sec_desc_buf(r_i->data, buf_len, 1);
+
+       r_i->status = status; /* 0x0000 0000 or 0x0000 007a */
+}
+#endif 
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_get_key_sec(const char *desc,  REG_R_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_get_key_sec");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr      ", ps, depth, &r_q->ptr))
+               return False;
+
+       if (r_q->ptr != 0) {
+               if(!smb_io_hdrbuf("", &r_q->hdr_sec, ps, depth))
+                       return False;
+               if(!sec_io_desc_buf("", &r_q->data, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+BOOL init_reg_q_info(REG_Q_INFO *q_i, POLICY_HND *pol, char* val_name)
+{
+        int len_type = val_name != NULL ? strlen(val_name) + 1 : 0;
+
+        if (q_i == NULL)
+                return False;
+
+        q_i->pol = *pol;
+
+        init_uni_hdr(&(q_i->hdr_type), len_type);
+        init_unistr2(&(q_i->uni_type), val_name, len_type);
+
+        q_i->ptr_reserved = 1;
+        q_i->ptr_buf = 1;
+
+        q_i->ptr_bufsize = 1;
+        q_i->bufsize = 0;
+        q_i->buf_unk = 0;
+
+        q_i->unk1 = 0;
+        q_i->ptr_buflen = 1;
+        q_i->buflen = 0;
+
+        q_i->ptr_buflen2 = 1;
+        q_i->buflen2 = 0;
+
+        return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_info(const char *desc,  REG_Q_INFO *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+       if(!smb_io_unihdr ("", &r_q->hdr_type, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_type, r_q->hdr_type.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_reserved", ps, depth, &(r_q->ptr_reserved)))
+               return False;
+
+       if(!prs_uint32("ptr_buf", ps, depth, &(r_q->ptr_buf)))
+               return False;
+
+       if(r_q->ptr_buf) {
+               if(!prs_uint32("ptr_bufsize", ps, depth, &(r_q->ptr_bufsize)))
+                       return False;
+               if(!prs_uint32("bufsize", ps, depth, &(r_q->bufsize)))
+                       return False;
+               if(!prs_uint32("buf_unk", ps, depth, &(r_q->buf_unk)))
+                       return False;
+       }
+
+       if(!prs_uint32("unk1", ps, depth, &(r_q->unk1)))
+               return False;
+
+       if(!prs_uint32("ptr_buflen", ps, depth, &(r_q->ptr_buflen)))
+               return False;
+
+       if (r_q->ptr_buflen) {
+               if(!prs_uint32("buflen", ps, depth, &(r_q->buflen)))
+                       return False;
+               if(!prs_uint32("ptr_buflen2", ps, depth, &(r_q->ptr_buflen2)))
+                       return False;
+               if(!prs_uint32("buflen2", ps, depth, &(r_q->buflen2)))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+ New version to replace older init_reg_r_info()
+********************************************************************/
+
+BOOL new_init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+                    REGISTRY_VALUE *val, NTSTATUS status)
+{
+       uint32          buf_len = 0;
+       BUFFER2         buf2;
+               
+       if(r_r == NULL)
+               return False;
+       
+       if ( !val )
+               return False;
+  
+       r_r->ptr_type = 1;
+       r_r->type = val->type;
+
+       /* if include_keyval is not set, don't send the key value, just
+          the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+       if ( include_keyval ) {
+               r_r->ptr_uni_val = 1;
+               buf_len = reg_init_buffer2( &r_r->uni_val, val );
+       
+       }
+       else {
+               /* dummy buffer used so we can get the size */
+               r_r->ptr_uni_val = 0;
+               buf_len = reg_init_buffer2( &buf2, val );
+       }
+
+       r_r->ptr_max_len = 1;
+       r_r->buf_max_len = buf_len;
+
+       r_r->ptr_len = 1;
+       r_r->buf_len = buf_len;
+
+       r_r->status = status;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+BOOL init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+                    BUFFER2* buf, uint32 type, NTSTATUS status)
+{
+       if(r_r == NULL)
+               return False;
+  
+       r_r->ptr_type = 1;
+       r_r->type = type;
+
+       /* if include_keyval is not set, don't send the key value, just
+          the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+       r_r->ptr_uni_val = include_keyval ? 1:0;
+       r_r->uni_val = *buf;
+
+       r_r->ptr_max_len = 1;
+       r_r->buf_max_len = r_r->uni_val.buf_max_len;
+
+       r_r->ptr_len = 1;
+       r_r->buf_len = r_r->uni_val.buf_len;
+
+       r_r->status = status;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_info(const char *desc, REG_R_INFO *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_type", ps, depth, &(r_r->ptr_type)))
+               return False;
+
+       if (r_r->ptr_type != 0) {
+               if(!prs_uint32("type", ps, depth, &r_r->type))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr_uni_val", ps, depth, &(r_r->ptr_uni_val)))
+               return False;
+
+       if(r_r->ptr_uni_val != 0) {
+               if(!smb_io_buffer2("uni_val", &r_r->uni_val, r_r->ptr_uni_val, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_max_len", ps, depth, &(r_r->ptr_max_len)))
+               return False;
+
+       if (r_r->ptr_max_len != 0) {
+               if(!prs_uint32("buf_max_len", ps, depth, &(r_r->buf_max_len)))
+               return False;
+       }
+
+       if(!prs_uint32("ptr_len", ps, depth, &(r_r->ptr_len)))
+               return False;
+       if (r_r->ptr_len != 0) {
+               if(!prs_uint32("buf_len", ps, depth, &(r_r->buf_len)))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_val(REG_Q_ENUM_VALUE *q_i, POLICY_HND *pol,
+                               uint32 val_idx, uint32 max_val_len,
+                               uint32 max_buf_len)
+{
+       ZERO_STRUCTP(q_i);
+
+       memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+       q_i->val_index = val_idx;
+       init_uni_hdr(&q_i->hdr_name, max_val_len);
+       q_i->uni_name.uni_max_len = max_val_len;
+       
+       q_i->ptr_type = 1;
+       q_i->type = 0x0;
+
+       q_i->ptr_value = 1;
+       q_i->buf_value.buf_max_len = max_buf_len;
+
+       q_i->ptr1 = 1;
+       q_i->len_value1 = max_buf_len;
+
+       q_i->ptr2 = 1;
+       q_i->len_value2 = 0;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_r_enum_val(REG_R_ENUM_VALUE *r_u, REGISTRY_VALUE *val )
+{
+       uint32 real_size;
+       
+       DEBUG(8,("init_reg_r_enum_val: Enter\n"));
+       
+       ZERO_STRUCTP(r_u);
+
+       /* value name */
+
+       DEBUG(10,("init_reg_r_enum_val: Valuename => [%s]\n", val->valuename));
+       
+       init_uni_hdr( &r_u->hdr_name, strlen(val->valuename)+1 );
+       init_unistr2( &r_u->uni_name, val->valuename, strlen(val->valuename)+1 );
+               
+       /* type */
+       
+       r_u->ptr_type = 1;
+       r_u->type = val->type;
+
+       /* REG_SZ & REG_MULTI_SZ must be converted to UNICODE */
+       
+       r_u->ptr_value = 1;
+       real_size = reg_init_buffer2( &r_u->buf_value, val );
+       
+       /* lengths */
+
+       r_u->ptr1 = 1;
+       r_u->len_value1 = real_size;
+       
+       r_u->ptr2 = 1;
+       r_u->len_value2 = real_size;
+               
+       DEBUG(8,("init_reg_r_enum_val: Exit\n"));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_val(const char *desc,  REG_Q_ENUM_VALUE *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_enum_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+       
+       if(!prs_uint32("val_index", ps, depth, &q_q->val_index))
+               return False;
+               
+       if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_type", ps, depth, &q_q->ptr_type))
+               return False;
+
+       if (q_q->ptr_type != 0) {
+               if(!prs_uint32("type", ps, depth, &q_q->type))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr_value", ps, depth, &q_q->ptr_value))
+               return False;
+       if(!smb_io_buffer2("buf_value", &q_q->buf_value, q_q->ptr_value, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+               return False;
+       if (q_q->ptr1 != 0) {
+               if(!prs_uint32("len_value1", ps, depth, &q_q->len_value1))
+                       return False;
+       }
+       if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+               return False;
+       if (q_q->ptr2 != 0) {
+               if(!prs_uint32("len_value2", ps, depth, &q_q->len_value2))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_val(const char *desc,  REG_R_ENUM_VALUE *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_enum_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_unihdr ("hdr_name", &r_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_name", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_type", ps, depth, &r_q->ptr_type))
+               return False;
+
+       if (r_q->ptr_type != 0) {
+               if(!prs_uint32("type", ps, depth, &r_q->type))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr_value", ps, depth, &r_q->ptr_value))
+               return False;
+       if(!smb_io_buffer2("buf_value", &r_q->buf_value, r_q->ptr_value, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+               return False;
+       if (r_q->ptr1 != 0) {
+               if(!prs_uint32("len_value1", ps, depth, &r_q->len_value1))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+               return False;
+       if (r_q->ptr2 != 0) {
+               if(!prs_uint32("len_value2", ps, depth, &r_q->len_value2))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_create_val(REG_Q_CREATE_VALUE *q_i, POLICY_HND *pol,
+                               char *val_name, uint32 type,
+                               BUFFER3 *val)
+{
+       int val_len = strlen(val_name) + 1;
+
+       ZERO_STRUCTP(q_i);
+
+       memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+       init_uni_hdr(&q_i->hdr_name, val_len);
+       init_unistr2(&q_i->uni_name, val_name, val_len);
+       
+       q_i->type      = type;
+       q_i->buf_value = val;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_val(const char *desc,  REG_Q_CREATE_VALUE *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_create_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+       
+       if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("type", ps, depth, &q_q->type))
+               return False;
+       if(!smb_io_buffer3("buf_value", q_q->buf_value, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_val(const char *desc,  REG_R_CREATE_VALUE *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_create_val");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_key(REG_Q_ENUM_KEY *q_i, POLICY_HND *pol, uint32 key_idx)
+{
+       memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+       q_i->key_index = key_idx;
+       q_i->key_name_len = 0;
+       q_i->unknown_1 = 0x0414;
+
+       q_i->ptr1 = 1;
+       q_i->unknown_2 = 0x0000020A;
+       memset(q_i->pad1, 0, sizeof(q_i->pad1));
+
+       q_i->ptr2 = 1;
+       memset(q_i->pad2, 0, sizeof(q_i->pad2));
+
+       q_i->ptr3 = 1;
+       unix_to_nt_time(&q_i->time, 0);            /* current time? */
+}
+
+/*******************************************************************
+makes a reply structure.
+********************************************************************/
+
+void init_reg_r_enum_key(REG_R_ENUM_KEY *r_u, char *subkey, uint32 unknown_1,
+                       uint32 unknown_2)
+{
+       if ( !r_u )
+               return;
+               
+       r_u->unknown_1 = unknown_1;
+       r_u->unknown_2 = unknown_2;
+       r_u->unknown_3 = 0x0;
+       
+       r_u->key_name_len = (strlen(subkey)+1) * 2;
+       if (r_u->key_name_len)
+               r_u->ptr1 = 0x1;
+       init_unistr3( &r_u->key_name, subkey );
+       
+       r_u->ptr2 = 0x1;
+       r_u->ptr3 = 0x1;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_key(const char *desc,  REG_Q_ENUM_KEY *q_q, prs_struct *ps, int depth)
+{
+       if (q_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_enum_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+               return False;
+       
+       if(!prs_uint32("key_index", ps, depth, &q_q->key_index))
+               return False;
+       if(!prs_uint16("key_name_len", ps, depth, &q_q->key_name_len))
+               return False;
+       if(!prs_uint16("unknown_1", ps, depth, &q_q->unknown_1))
+               return False;
+
+       if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+               return False;
+
+       if (q_q->ptr1 != 0) {
+               if(!prs_uint32("unknown_2", ps, depth, &q_q->unknown_2))
+                       return False;
+               if(!prs_uint8s(False, "pad1", ps, depth, q_q->pad1, sizeof(q_q->pad1)))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+               return False;
+
+       if (q_q->ptr2 != 0) {
+               if(!prs_uint8s(False, "pad2", ps, depth, q_q->pad2, sizeof(q_q->pad2)))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr3", ps, depth, &q_q->ptr3))
+               return False;
+
+       if (q_q->ptr3 != 0) {
+               if(!smb_io_time("", &q_q->time, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_key(const char *desc,  REG_R_ENUM_KEY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_enum_key");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint16("key_name_len", ps, depth, &r_q->key_name_len))
+               return False;
+       if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+               return False;
+
+       if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+               return False;
+
+       if (r_q->ptr1 != 0) {
+               if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+                       return False;
+               if(!prs_uint32("unknown_3", ps, depth, &r_q->unknown_3))
+                       return False;
+               if(!smb_io_unistr3("key_name", &r_q->key_name, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+               return False;
+
+       if (r_q->ptr2 != 0) {
+               if(!prs_uint8s(False, "pad2", ps, depth, r_q->pad2, sizeof(r_q->pad2)))
+                       return False;
+       }
+
+       if(!prs_uint32("ptr3", ps, depth, &r_q->ptr3))
+               return False;
+
+       if (r_q->ptr3 != 0) {
+               if(!smb_io_time("", &r_q->time, ps, depth))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_q->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_open_entry(REG_Q_OPEN_ENTRY *r_q, POLICY_HND *pol,
+                               char *key_name, uint32 access_desired)
+{
+       int len_name = strlen(key_name)+1;
+
+       memcpy(&r_q->pol, pol, sizeof(r_q->pol));
+
+       init_uni_hdr(&r_q->hdr_name, len_name);
+       init_unistr2(&r_q->uni_name, key_name, len_name);
+
+       r_q->unknown_0 = 0x00000000;
+       r_q->access_desired = access_desired;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_entry(const char *desc,  REG_Q_OPEN_ENTRY *r_q, prs_struct *ps, int depth)
+{
+       if (r_q == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_entry");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+               return False;
+       if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("unknown_0        ", ps, depth, &r_q->unknown_0))
+               return False;
+       if(!prs_uint32("access_desired  ", ps, depth, &r_q->access_desired))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_r_open_entry(REG_R_OPEN_ENTRY *r_r,
+                          POLICY_HND *pol, NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               memcpy(&r_r->pol, pol, sizeof(r_r->pol));
+       } else {
+               ZERO_STRUCT(r_r->pol);
+       }
+       r_r->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_entry(const char *desc,  REG_R_OPEN_ENTRY *r_r, prs_struct *ps, int depth)
+{
+       if (r_r == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_open_entry");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_r->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_reg_q_shutdown(REG_Q_SHUTDOWN * q_s, const char *msg,
+                       uint32 timeout, BOOL do_reboot, BOOL force)
+{
+       int msg_len;
+       msg_len = strlen(msg);
+
+       q_s->ptr_0 = 1;
+       q_s->ptr_1 = 1;
+       q_s->ptr_2 = 1;
+
+       init_uni_hdr(&(q_s->hdr_msg), msg_len);
+       init_unistr2(&(q_s->uni_msg), msg, msg_len);
+
+       q_s->timeout = timeout;
+
+       q_s->reboot = do_reboot ? 1 : 0;
+       q_s->force = force ? 1 : 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_shutdown(const char *desc, REG_Q_SHUTDOWN * q_s, prs_struct *ps,
+                      int depth)
+{
+       if (q_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_shutdown");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr_0", ps, depth, &(q_s->ptr_0)))
+               return False;
+       if (!prs_uint32("ptr_1", ps, depth, &(q_s->ptr_1)))
+               return False;
+       if (!prs_uint32("ptr_2", ps, depth, &(q_s->ptr_2)))
+               return False;
+
+       if (!smb_io_unihdr("hdr_msg", &(q_s->hdr_msg), ps, depth))
+               return False;
+       if (!smb_io_unistr2("uni_msg", &(q_s->uni_msg), q_s->hdr_msg.buffer, ps, depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("timeout", ps, depth, &(q_s->timeout)))
+               return False;
+       if (!prs_uint8("force  ", ps, depth, &(q_s->force)))
+               return False;
+       if (!prs_uint8("reboot ", ps, depth, &(q_s->reboot)))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_shutdown(const char *desc, REG_R_SHUTDOWN * r_s, prs_struct *ps,
+                      int depth)
+{
+       if (r_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_shutdown");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_s->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_reg_q_abort_shutdown(REG_Q_ABORT_SHUTDOWN * q_s)
+{
+
+       q_s->ptr_server = 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_abort_shutdown(const char *desc, REG_Q_ABORT_SHUTDOWN * q_s,
+                            prs_struct *ps, int depth)
+{
+       if (q_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_q_abort_shutdown");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+               return False;
+       if (q_s->ptr_server != 0)
+               if (!prs_uint16("server", ps, depth, &(q_s->server)))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_abort_shutdown(const char *desc, REG_R_ABORT_SHUTDOWN * r_s,
+                            prs_struct *ps, int depth)
+{
+       if (r_s == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "reg_io_r_abort_shutdown");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_ntstatus("status", ps, depth, &r_s->status))
+               return False;
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_rpc.c b/source4/rpc_parse/parse_rpc.c
new file mode 100644 (file)
index 0000000..fafbbb1
--- /dev/null
@@ -0,0 +1,1106 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  Copyright (C) Jeremy Allison                    1999.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+interface/version dce/rpc pipe identification
+********************************************************************/
+
+#define TRANS_SYNT_V2                       \
+{                                           \
+       {                                   \
+               0x8a885d04, 0x1ceb, 0x11c9, \
+               { 0x9f, 0xe8, 0x08, 0x00,   \
+               0x2b, 0x10, 0x48, 0x60 }    \
+       }, 0x02                             \
+}
+
+#define SYNT_NETLOGON_V2                    \
+{                                           \
+       {                                   \
+               0x8a885d04, 0x1ceb, 0x11c9, \
+               { 0x9f, 0xe8, 0x08, 0x00,   \
+               0x2b, 0x10, 0x48, 0x60 }    \
+       }, 0x02                             \
+}
+
+#define SYNT_WKSSVC_V1                      \
+{                                           \
+       {                                   \
+               0x6bffd098, 0xa112, 0x3610, \
+               { 0x98, 0x33, 0x46, 0xc3,   \
+               0xf8, 0x7e, 0x34, 0x5a }    \
+       }, 0x01                             \
+}
+
+#define SYNT_SRVSVC_V3                      \
+{                                           \
+       {                                   \
+               0x4b324fc8, 0x1670, 0x01d3, \
+               { 0x12, 0x78, 0x5a, 0x47,   \
+               0xbf, 0x6e, 0xe1, 0x88 }    \
+       }, 0x03                             \
+}
+
+#define SYNT_LSARPC_V0                      \
+{                                           \
+       {                                   \
+               0x12345778, 0x1234, 0xabcd, \
+               { 0xef, 0x00, 0x01, 0x23,   \
+               0x45, 0x67, 0x89, 0xab }    \
+       }, 0x00                             \
+}
+
+#define SYNT_LSARPC_V0_DS                \
+{                                           \
+       {                                   \
+               0x3919286a, 0xb10c, 0x11d0, \
+               { 0x9b, 0xa8, 0x00, 0xc0,   \
+               0x4f, 0xd9, 0x2e, 0xf5 }    \
+       }, 0x00                             \
+}
+
+#define SYNT_SAMR_V1                        \
+{                                           \
+       {                                   \
+               0x12345778, 0x1234, 0xabcd, \
+               { 0xef, 0x00, 0x01, 0x23,   \
+               0x45, 0x67, 0x89, 0xac }    \
+       }, 0x01                             \
+}
+
+#define SYNT_NETLOGON_V1                    \
+{                                           \
+       {                                   \
+               0x12345678, 0x1234, 0xabcd, \
+               { 0xef, 0x00, 0x01, 0x23,   \
+               0x45, 0x67, 0xcf, 0xfb }    \
+       }, 0x01                             \
+}
+
+#define SYNT_WINREG_V1                      \
+{                                           \
+       {                                   \
+               0x338cd001, 0x2244, 0x31f1, \
+               { 0xaa, 0xaa, 0x90, 0x00,   \
+               0x38, 0x00, 0x10, 0x03 }    \
+       }, 0x01                             \
+}
+
+#define SYNT_SPOOLSS_V1                     \
+{                                           \
+       {                                   \
+               0x12345678, 0x1234, 0xabcd, \
+               { 0xef, 0x00, 0x01, 0x23,   \
+               0x45, 0x67, 0x89, 0xab }    \
+       }, 0x01                             \
+}
+
+#define SYNT_NONE_V0                        \
+{                                           \
+       {                                   \
+               0x0, 0x0, 0x0,              \
+               { 0x00, 0x00, 0x00, 0x00,   \
+               0x00, 0x00, 0x00, 0x00 }    \
+       }, 0x00                             \
+}
+
+#define SYNT_NETDFS_V3                      \
+{                                           \
+        {                                   \
+                0x4fc742e0, 0x4a10, 0x11cf, \
+                { 0x82, 0x73, 0x00, 0xaa,   \
+                  0x00, 0x4a, 0xe6, 0x73 }  \
+        }, 0x03                             \
+}
+
+/*
+ * IMPORTANT!!  If you update this structure, make sure to
+ * update the index #defines in smb.h.
+ */
+
+const struct pipe_id_info pipe_names [] =
+{
+       /* client pipe , abstract syntax       , server pipe   , transfer syntax */
+       { PIPE_LSARPC  , SYNT_LSARPC_V0        , PIPE_LSASS    , TRANS_SYNT_V2 },
+       { PIPE_LSARPC  , SYNT_LSARPC_V0_DS     , PIPE_LSASS    , TRANS_SYNT_V2 },
+       { PIPE_SAMR    , SYNT_SAMR_V1          , PIPE_LSASS    , TRANS_SYNT_V2 },
+       { PIPE_NETLOGON, SYNT_NETLOGON_V1      , PIPE_LSASS    , TRANS_SYNT_V2 },
+       { PIPE_SRVSVC  , SYNT_SRVSVC_V3        , PIPE_NTSVCS   , TRANS_SYNT_V2 },
+       { PIPE_WKSSVC  , SYNT_WKSSVC_V1        , PIPE_NTSVCS   , TRANS_SYNT_V2 },
+       { PIPE_WINREG  , SYNT_WINREG_V1        , PIPE_WINREG   , TRANS_SYNT_V2 },
+       { PIPE_SPOOLSS , SYNT_SPOOLSS_V1       , PIPE_SPOOLSS  , TRANS_SYNT_V2 },
+       { PIPE_NETDFS  , SYNT_NETDFS_V3        , PIPE_NETDFS   , TRANS_SYNT_V2 },
+       { NULL         , SYNT_NONE_V0          , NULL          , SYNT_NONE_V0  }
+};
+
+/*******************************************************************
+ Inits an RPC_HDR structure.
+********************************************************************/
+
+void init_rpc_hdr(RPC_HDR *hdr, enum RPC_PKT_TYPE pkt_type, uint8 flags,
+                               uint32 call_id, int data_len, int auth_len)
+{
+       hdr->major        = 5;               /* RPC version 5 */
+       hdr->minor        = 0;               /* minor version 0 */
+       hdr->pkt_type     = pkt_type;        /* RPC packet type */
+       hdr->flags        = flags;           /* dce/rpc flags */
+       hdr->pack_type[0] = 0x10;            /* little-endian data representation */
+       hdr->pack_type[1] = 0;               /* packed data representation */
+       hdr->pack_type[2] = 0;               /* packed data representation */
+       hdr->pack_type[3] = 0;               /* packed data representation */
+       hdr->frag_len     = data_len;        /* fragment length, fill in later */
+       hdr->auth_len     = auth_len;        /* authentication length */
+       hdr->call_id      = call_id;         /* call identifier - match incoming RPC */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr(const char *desc,  RPC_HDR *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr");
+       depth++;
+
+       if(!prs_uint8 ("major     ", ps, depth, &rpc->major))
+               return False;
+
+       if(!prs_uint8 ("minor     ", ps, depth, &rpc->minor))
+               return False;
+       if(!prs_uint8 ("pkt_type  ", ps, depth, &rpc->pkt_type))
+               return False;
+       if(!prs_uint8 ("flags     ", ps, depth, &rpc->flags))
+               return False;
+
+       /* We always marshall in little endian format. */
+       if (MARSHALLING(ps))
+               rpc->pack_type[0] = 0x10;
+
+       if(!prs_uint8("pack_type0", ps, depth, &rpc->pack_type[0]))
+               return False;
+       if(!prs_uint8("pack_type1", ps, depth, &rpc->pack_type[1]))
+               return False;
+       if(!prs_uint8("pack_type2", ps, depth, &rpc->pack_type[2]))
+               return False;
+       if(!prs_uint8("pack_type3", ps, depth, &rpc->pack_type[3]))
+               return False;
+
+       /*
+        * If reading and pack_type[0] == 0 then the data is in big-endian
+        * format. Set the flag in the prs_struct to specify reverse-endainness.
+        */
+
+       if (UNMARSHALLING(ps) && rpc->pack_type[0] == 0) {
+               DEBUG(10,("smb_io_rpc_hdr: PDU data format is big-endian. Setting flag.\n"));
+               prs_set_endian_data(ps, RPC_BIG_ENDIAN);
+       }
+
+       if(!prs_uint16("frag_len  ", ps, depth, &rpc->frag_len))
+               return False;
+       if(!prs_uint16("auth_len  ", ps, depth, &rpc->auth_len))
+               return False;
+       if(!prs_uint32("call_id   ", ps, depth, &rpc->call_id))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_IFACE structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_iface(const char *desc, RPC_IFACE *ifc, prs_struct *ps, int depth)
+{
+       if (ifc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_iface");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32 ("data   ", ps, depth, &ifc->uuid.time_low))
+               return False;
+       if(!prs_uint16 ("data   ", ps, depth, &ifc->uuid.time_mid))
+               return False;
+       if(!prs_uint16 ("data   ", ps, depth, &ifc->uuid.time_hi_and_version))
+               return False;
+
+       if(!prs_uint8s (False, "data   ", ps, depth, ifc->uuid.remaining, sizeof(ifc->uuid.remaining)))
+               return False;
+       if(!prs_uint32 (       "version", ps, depth, &ifc->version))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_ADDR_STR structure.
+********************************************************************/
+
+static void init_rpc_addr_str(RPC_ADDR_STR *str, const char *name)
+{
+       str->len = strlen(name) + 1;
+       fstrcpy(str->str, name);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_ADDR_STR structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_addr_str(const char *desc,  RPC_ADDR_STR *str, prs_struct *ps, int depth)
+{
+       if (str == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_addr_str");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint16 (      "len", ps, depth, &str->len))
+               return False;
+       if(!prs_uint8s (True, "str", ps, depth, (uchar*)str->str, MIN(str->len, sizeof(str->str)) ))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_BBA structure.
+********************************************************************/
+
+static void init_rpc_hdr_bba(RPC_HDR_BBA *bba, uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid)
+{
+       bba->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+       bba->max_rsize = max_rsize; /* max receive fragment size (0x1630) */   
+       bba->assoc_gid = assoc_gid; /* associated group id (0x0) */ 
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BBA structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_hdr_bba(const char *desc,  RPC_HDR_BBA *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_bba");
+       depth++;
+
+       if(!prs_uint16("max_tsize", ps, depth, &rpc->max_tsize))
+               return False;
+       if(!prs_uint16("max_rsize", ps, depth, &rpc->max_rsize))
+               return False;
+       if(!prs_uint32("assoc_gid", ps, depth, &rpc->assoc_gid))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_RB structure.
+********************************************************************/
+
+void init_rpc_hdr_rb(RPC_HDR_RB *rpc, 
+                               uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+                               uint32 num_elements, uint16 context_id, uint8 num_syntaxes,
+                               RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+       init_rpc_hdr_bba(&rpc->bba, max_tsize, max_rsize, assoc_gid);
+
+       rpc->num_elements = num_elements ; /* the number of elements (0x1) */
+       rpc->context_id   = context_id   ; /* presentation context identifier (0x0) */
+       rpc->num_syntaxes = num_syntaxes ; /* the number of syntaxes (has always been 1?)(0x1) */
+
+       /* num and vers. of interface client is using */
+       rpc->abstract = *abstract;
+
+       /* num and vers. of interface to use for replies */
+       rpc->transfer = *transfer;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RB structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_rb(const char *desc, RPC_HDR_RB *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_rb");
+       depth++;
+
+       if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+               return False;
+
+       if(!prs_uint32("num_elements", ps, depth, &rpc->num_elements))
+               return False;
+       if(!prs_uint16("context_id  ", ps, depth, &rpc->context_id ))
+               return False;
+       if(!prs_uint8 ("num_syntaxes", ps, depth, &rpc->num_syntaxes))
+               return False;
+
+       if(!smb_io_rpc_iface("", &rpc->abstract, ps, depth))
+               return False;
+       if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static void init_rpc_results(RPC_RESULTS *res, 
+                               uint8 num_results, uint16 result, uint16 reason)
+{
+       res->num_results = num_results; /* the number of results (0x01) */
+       res->result      = result     ;  /* result (0x00 = accept) */
+       res->reason      = reason     ;  /* reason (0x00 = no reason specified) */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static BOOL smb_io_rpc_results(const char *desc, RPC_RESULTS *res, prs_struct *ps, int depth)
+{
+       if (res == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_results");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint8 ("num_results", ps, depth, &res->num_results))    
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint16("result     ", ps, depth, &res->result))
+               return False;
+       if(!prs_uint16("reason     ", ps, depth, &res->reason))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_BA structure.
+
+ lkclXXXX only one reason at the moment!
+
+********************************************************************/
+
+void init_rpc_hdr_ba(RPC_HDR_BA *rpc, 
+                               uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+                               const char *pipe_addr,
+                               uint8 num_results, uint16 result, uint16 reason,
+                               RPC_IFACE *transfer)
+{
+       init_rpc_hdr_bba (&rpc->bba, max_tsize, max_rsize, assoc_gid);
+       init_rpc_addr_str(&rpc->addr, pipe_addr);
+       init_rpc_results (&rpc->res, num_results, result, reason);
+
+       /* the transfer syntax from the request */
+       memcpy(&rpc->transfer, transfer, sizeof(rpc->transfer));
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_ba(const char *desc, RPC_HDR_BA *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_ba");
+       depth++;
+
+       if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+               return False;
+       if(!smb_io_rpc_addr_str("", &rpc->addr, ps, depth))
+               return False;
+       if(!smb_io_rpc_results("", &rpc->res, ps, depth))
+               return False;
+       if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_REQ structure.
+********************************************************************/
+
+void init_rpc_hdr_req(RPC_HDR_REQ *hdr, uint32 alloc_hint, uint16 opnum)
+{
+       hdr->alloc_hint   = alloc_hint; /* allocation hint */
+       hdr->context_id   = 0;         /* presentation context identifier */
+       hdr->opnum        = opnum;     /* opnum */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_REQ structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_req(const char *desc, RPC_HDR_REQ *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_req");
+       depth++;
+
+       if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+               return False;
+       if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+               return False;
+       if(!prs_uint16("opnum     ", ps, depth, &rpc->opnum))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RESP structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_resp(const char *desc, RPC_HDR_RESP *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_resp");
+       depth++;
+
+       if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+               return False;
+       if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+               return False;
+       if(!prs_uint8 ("cancel_ct ", ps, depth, &rpc->cancel_count))
+               return False;
+       if(!prs_uint8 ("reserved  ", ps, depth, &rpc->reserved))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_FAULT structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_fault(const char *desc, RPC_HDR_FAULT *rpc, prs_struct *ps, int depth)
+{
+       if (rpc == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_fault");
+       depth++;
+
+       if(!prs_ntstatus("status  ", ps, depth, &rpc->status))
+               return False;
+       if(!prs_uint32("reserved", ps, depth, &rpc->reserved))
+               return False;
+
+    return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+void init_rpc_hdr_autha(RPC_HDR_AUTHA *rai,
+                               uint16 max_tsize, uint16 max_rsize,
+                               uint8 auth_type, uint8 auth_level,
+                               uint8 stub_type_len)
+{
+       rai->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+       rai->max_rsize = max_rsize; /* max receive fragment size (0x1630) */   
+
+       rai->auth_type     = auth_type; /* nt lm ssp 0x0a */
+       rai->auth_level    = auth_level; /* 0x06 */
+       rai->stub_type_len = stub_type_len; /* 0x00 */
+       rai->padding       = 0; /* padding 0x00 */
+
+       rai->unknown       = 0x0014a0c0; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_autha(const char *desc, RPC_HDR_AUTHA *rai, prs_struct *ps, int depth)
+{
+       if (rai == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_autha");
+       depth++;
+
+       if(!prs_uint16("max_tsize    ", ps, depth, &rai->max_tsize))
+               return False;
+       if(!prs_uint16("max_rsize    ", ps, depth, &rai->max_rsize))
+               return False;
+
+       if(!prs_uint8 ("auth_type    ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+               return False;
+       if(!prs_uint8 ("auth_level   ", ps, depth, &rai->auth_level)) /* 0x06 */
+               return False;
+       if(!prs_uint8 ("stub_type_len", ps, depth, &rai->stub_type_len))
+               return False;
+       if(!prs_uint8 ("padding      ", ps, depth, &rai->padding))
+               return False;
+
+       if(!prs_uint32("unknown      ", ps, depth, &rai->unknown)) /* 0x0014a0c0 */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Checks an RPC_HDR_AUTH structure.
+********************************************************************/
+
+BOOL rpc_hdr_auth_chk(RPC_HDR_AUTH *rai)
+{
+       return (rai->auth_type == NTLMSSP_AUTH_TYPE && rai->auth_level == NTLMSSP_AUTH_LEVEL);
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_AUTH structure.
+********************************************************************/
+
+void init_rpc_hdr_auth(RPC_HDR_AUTH *rai,
+                               uint8 auth_type, uint8 auth_level,
+                               uint8 stub_type_len,
+                               uint32 ptr)
+{
+       rai->auth_type     = auth_type; /* nt lm ssp 0x0a */
+       rai->auth_level    = auth_level; /* 0x06 */
+       rai->stub_type_len = stub_type_len; /* 0x00 */
+       rai->padding       = 0; /* padding 0x00 */
+
+       rai->unknown       = ptr; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTH structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_auth(const char *desc, RPC_HDR_AUTH *rai, prs_struct *ps, int depth)
+{
+       if (rai == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_hdr_auth");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint8 ("auth_type    ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+               return False;
+       if(!prs_uint8 ("auth_level   ", ps, depth, &rai->auth_level)) /* 0x06 */
+               return False;
+       if(!prs_uint8 ("stub_type_len", ps, depth, &rai->stub_type_len))
+               return False;
+       if(!prs_uint8 ("padding      ", ps, depth, &rai->padding))
+               return False;
+
+       if(!prs_uint32("unknown      ", ps, depth, &rai->unknown)) /* 0x0014a0c0 */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL rpc_auth_verifier_chk(RPC_AUTH_VERIFIER *rav,
+                               const char *signature, uint32 msg_type)
+{
+       return (strequal(rav->signature, signature) && rav->msg_type == msg_type);
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+void init_rpc_auth_verifier(RPC_AUTH_VERIFIER *rav,
+                               const char *signature, uint32 msg_type)
+{
+       fstrcpy(rav->signature, signature); /* "NTLMSSP" */
+       rav->msg_type = msg_type; /* NTLMSSP_MESSAGE_TYPE */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+       if (rav == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+       depth++;
+
+       /* "NTLMSSP" */
+       if(!prs_string("signature", ps, depth, rav->signature, strlen("NTLMSSP"),
+                       sizeof(rav->signature)))
+               return False;
+       if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type)) /* NTLMSSP_MESSAGE_TYPE */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_NEG structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_neg(RPC_AUTH_NTLMSSP_NEG *neg,
+                               uint32 neg_flgs,
+                               const char *myname, const char *domain)
+{
+       int len_myname = strlen(myname);
+       int len_domain = strlen(domain);
+
+       neg->neg_flgs = neg_flgs ; /* 0x00b2b3 */
+
+       init_str_hdr(&neg->hdr_domain, len_domain, len_domain, 0x20 + len_myname); 
+       init_str_hdr(&neg->hdr_myname, len_myname, len_myname, 0x20); 
+
+       fstrcpy(neg->myname, myname);
+       fstrcpy(neg->domain, domain);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_NEG structure.
+
+ *** lkclXXXX HACK ALERT! ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_neg(const char *desc, RPC_AUTH_NTLMSSP_NEG *neg, prs_struct *ps, int depth)
+{
+       uint32 start_offset = prs_offset(ps);
+       if (neg == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_neg");
+       depth++;
+
+       if(!prs_uint32("neg_flgs ", ps, depth, &neg->neg_flgs))
+               return False;
+
+       if (ps->io) {
+               uint32 old_offset;
+               uint32 old_neg_flags = neg->neg_flgs;
+
+               /* reading */
+
+               ZERO_STRUCTP(neg);
+
+               neg->neg_flgs = old_neg_flags;
+
+               if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+                       return False;
+
+               old_offset = prs_offset(ps);
+
+               if(!prs_set_offset(ps, neg->hdr_myname.buffer + start_offset - 12))
+                       return False;
+
+               if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname, 
+                               MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+                       return False;
+
+               old_offset += neg->hdr_myname.str_str_len;
+
+               if(!prs_set_offset(ps, neg->hdr_domain.buffer + start_offset - 12))
+                       return False;
+
+               if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain, 
+                       MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain  ))))
+                       return False;
+
+               old_offset += neg->hdr_domain  .str_str_len;
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       } else {
+               /* writing */
+               if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+                       return False;
+
+               if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname, 
+                                       MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+                       return False;
+               if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain, 
+                                       MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain  ))))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chal(RPC_AUTH_NTLMSSP_CHAL *chl,
+                               uint32 neg_flags,
+                               uint8 challenge[8])
+{
+       chl->unknown_1 = 0x0; 
+       chl->unknown_2 = 0x00000028;
+       chl->neg_flags = neg_flags; /* 0x0082b1 */
+
+       memcpy(chl->challenge, challenge, sizeof(chl->challenge)); 
+       memset((char *)chl->reserved , '\0', sizeof(chl->reserved)); 
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chal(const char *desc, RPC_AUTH_NTLMSSP_CHAL *chl, prs_struct *ps, int depth)
+{
+       if (chl == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chal");
+       depth++;
+
+       if(!prs_uint32("unknown_1", ps, depth, &chl->unknown_1)) /* 0x0000 0000 */
+               return False;
+       if(!prs_uint32("unknown_2", ps, depth, &chl->unknown_2)) /* 0x0000 b2b3 */
+               return False;
+       if(!prs_uint32("neg_flags", ps, depth, &chl->neg_flags)) /* 0x0000 82b1 */
+               return False;
+
+       if(!prs_uint8s (False, "challenge", ps, depth, chl->challenge, sizeof(chl->challenge)))
+               return False;
+       if(!prs_uint8s (False, "reserved ", ps, depth, chl->reserved , sizeof(chl->reserved )))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE!  HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier    ***
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_resp(RPC_AUTH_NTLMSSP_RESP *rsp,
+                               uchar lm_resp[24], uchar nt_resp[24],
+                               const char *domain, const char *user, const char *wks,
+                               uint32 neg_flags)
+{
+       uint32 offset;
+       int dom_len = strlen(domain);
+       int wks_len = strlen(wks);
+       int usr_len = strlen(user);
+       int lm_len  = (lm_resp != NULL) ? 24 : 0;
+       int nt_len  = (nt_resp != NULL) ? 24 : 0;
+
+       DEBUG(5,("make_rpc_auth_ntlmssp_resp\n"));
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("lm_resp\n"));
+       dump_data(100, (char *)lm_resp, 24);
+       DEBUG(100,("nt_resp\n"));
+       dump_data(100, (char *)nt_resp, 24);
+#endif
+
+       DEBUG(6,("dom: %s user: %s wks: %s neg_flgs: 0x%x\n",
+                 domain, user, wks, neg_flags));
+
+       offset = 0x40;
+
+       if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+               dom_len *= 2;
+               wks_len *= 2;
+               usr_len *= 2;
+       }
+
+       init_str_hdr(&rsp->hdr_domain, dom_len, dom_len, offset);
+       offset += dom_len;
+
+       init_str_hdr(&rsp->hdr_usr, usr_len, usr_len, offset);
+       offset += usr_len;
+
+       init_str_hdr(&rsp->hdr_wks, wks_len, wks_len, offset);
+       offset += wks_len;
+
+       init_str_hdr(&rsp->hdr_lm_resp, lm_len, lm_len, offset);
+       offset += lm_len;
+
+       init_str_hdr(&rsp->hdr_nt_resp, nt_len, nt_len, offset);
+       offset += nt_len;
+
+       init_str_hdr(&rsp->hdr_sess_key, 0, 0, offset);
+
+       rsp->neg_flags = neg_flags;
+
+       memcpy(rsp->lm_resp, lm_resp, 24);
+       memcpy(rsp->nt_resp, nt_resp, 24);
+
+       if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+               rpcstr_push(rsp->domain, domain, sizeof(rsp->domain), 0);
+               rpcstr_push(rsp->user, user, sizeof(rsp->user), 0);
+               rpcstr_push(rsp->wks, wks, sizeof(rsp->wks), 0);
+       } else {
+               fstrcpy(rsp->domain, domain);
+               fstrcpy(rsp->user, user);
+               fstrcpy(rsp->wks, wks);
+       }
+       
+       rsp->sess_key[0] = 0;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE!  HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier    ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_resp(const char *desc, RPC_AUTH_NTLMSSP_RESP *rsp, prs_struct *ps, int depth)
+{
+       if (rsp == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_resp");
+       depth++;
+
+       if (ps->io) {
+               uint32 old_offset;
+
+               /* reading */
+
+               ZERO_STRUCTP(rsp);
+
+               if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_domain  ", &rsp->hdr_domain, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_user    ", &rsp->hdr_usr, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_wks     ", &rsp->hdr_wks, ps, depth)) 
+                       return False;
+               if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+                       return False;
+
+               if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+                       return False;
+
+               old_offset = prs_offset(ps);
+
+               if(!prs_set_offset(ps, rsp->hdr_domain.buffer + 0xc))
+                       return False;
+
+               if(!prs_uint8s(True , "domain  ", ps, depth, (uint8*)rsp->domain,
+                               MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+                       return False;
+
+               old_offset += rsp->hdr_domain.str_str_len;
+
+               if(!prs_set_offset(ps, rsp->hdr_usr.buffer + 0xc))
+                       return False;
+
+               if(!prs_uint8s(True , "user    ", ps, depth, (uint8*)rsp->user,
+                               MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+                       return False;
+
+               old_offset += rsp->hdr_usr.str_str_len;
+
+               if(!prs_set_offset(ps, rsp->hdr_wks.buffer + 0xc))
+                       return False;
+
+               if(!prs_uint8s(True, "wks     ", ps, depth, (uint8*)rsp->wks,
+                               MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+                       return False;
+
+               old_offset += rsp->hdr_wks.str_str_len;
+
+               if(!prs_set_offset(ps, rsp->hdr_lm_resp.buffer + 0xc))
+                       return False;
+
+               if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+                               MIN(rsp->hdr_lm_resp.str_str_len, sizeof(rsp->lm_resp ))))
+                       return False;
+
+               old_offset += rsp->hdr_lm_resp.str_str_len;
+
+               if(!prs_set_offset(ps, rsp->hdr_nt_resp.buffer + 0xc))
+                       return False;
+
+               if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+                               MIN(rsp->hdr_nt_resp.str_str_len, sizeof(rsp->nt_resp ))))
+                       return False;
+
+               old_offset += rsp->hdr_nt_resp.str_str_len;
+
+               if (rsp->hdr_sess_key.str_str_len != 0) {
+
+                       if(!prs_set_offset(ps, rsp->hdr_sess_key.buffer + 0x10))
+                               return False;
+
+                       old_offset += rsp->hdr_sess_key.str_str_len;
+
+                       if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+                                       MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+                               return False;
+               }
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       } else {
+               /* writing */
+               if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_domain  ", &rsp->hdr_domain, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_user    ", &rsp->hdr_usr, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_wks     ", &rsp->hdr_wks, ps, depth))
+                       return False;
+               if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+                       return False;
+
+               if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+                       return False;
+
+               if(!prs_uint8s(True , "domain  ", ps, depth, (uint8*)rsp->domain,
+                               MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+                       return False;
+
+               if(!prs_uint8s(True , "user    ", ps, depth, (uint8*)rsp->user,
+                               MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+                       return False;
+
+               if(!prs_uint8s(True , "wks     ", ps, depth, (uint8*)rsp->wks,
+                               MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+                       return False;
+               if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+                               MIN(rsp->hdr_lm_resp .str_str_len, sizeof(rsp->lm_resp))))
+                       return False;
+               if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+                               MIN(rsp->hdr_nt_resp .str_str_len, sizeof(rsp->nt_resp ))))
+                       return False;
+               if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+                               MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk, uint32 crc32, uint32 seq_num)
+{
+       if (chk == NULL)
+               return False;
+
+       if (chk->crc32 != crc32 ||
+           chk->ver   != NTLMSSP_SIGN_VERSION ||
+           chk->seq_num != seq_num)
+       {
+               DEBUG(5,("verify failed - crc %x ver %x seq %d\n",
+                       crc32, NTLMSSP_SIGN_VERSION, seq_num));
+               DEBUG(5,("verify expect - crc %x ver %x seq %d\n",
+                       chk->crc32, chk->ver, chk->seq_num));
+               return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk,
+                               uint32 ver, uint32 crc32, uint32 seq_num)
+{
+       chk->ver      = ver;
+       chk->reserved = 0x0;
+       chk->crc32    = crc32;
+       chk->seq_num  = seq_num;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chk(const char *desc, RPC_AUTH_NTLMSSP_CHK *chk, prs_struct *ps, int depth)
+{
+       if (chk == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chk");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ver     ", ps, depth, &chk->ver))
+               return False;
+       if(!prs_uint32("reserved", ps, depth, &chk->reserved))
+               return False;
+       if(!prs_uint32("crc32   ", ps, depth, &chk->crc32))
+               return False;
+       if(!prs_uint32("seq_num ", ps, depth, &chk->seq_num))
+               return False;
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_samr.c b/source4/rpc_parse/parse_samr.c
new file mode 100644 (file)
index 0000000..d031d13
--- /dev/null
@@ -0,0 +1,7448 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ *  Copyright (C) Paul Ashton                  1997-2000,
+ *  Copyright (C) Elrond                            2000,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Jean François Micouleau      1998-2001,
+ *  Copyright (C) Anthony Liguori                   2002,
+ *  Copyright (C) Jim McDonough                     2002.
+ *  
+ *  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 "rpc_parse.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+inits a SAMR_Q_CLOSE_HND structure.
+********************************************************************/
+
+void init_samr_q_close_hnd(SAMR_Q_CLOSE_HND * q_c, POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_close_hnd\n"));
+       
+       q_c->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_close_hnd(const char *desc, SAMR_Q_CLOSE_HND * q_u,
+                        prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_close_hnd");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       return smb_io_pol_hnd("pol", &q_u->pol, ps, depth);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_close_hnd(const char *desc, SAMR_R_CLOSE_HND * r_u,
+                        prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_close_hnd");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_q_lookup_domain(SAMR_Q_LOOKUP_DOMAIN * q_u,
+                              POLICY_HND *pol, char *dom_name)
+{
+       int len_name = strlen(dom_name);
+
+       DEBUG(5, ("init_samr_q_lookup_domain\n"));
+
+       q_u->connect_pol = *pol;
+
+       init_uni_hdr(&q_u->hdr_domain, len_name);
+       init_unistr2(&q_u->uni_domain, dom_name, len_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL samr_io_q_lookup_domain(const char *desc, SAMR_Q_LOOKUP_DOMAIN * q_u,
+                            prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_lookup_domain");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("connect_pol", &q_u->connect_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_domain", &q_u->hdr_domain, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("uni_domain", &q_u->uni_domain, q_u->hdr_domain.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_r_lookup_domain(SAMR_R_LOOKUP_DOMAIN * r_u,
+                              DOM_SID *dom_sid, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_lookup_domain\n"));
+
+       r_u->status = status;
+       r_u->ptr_sid = 0;
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->ptr_sid = 1;
+               init_dom_sid2(&r_u->dom_sid, dom_sid);
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_domain(const char *desc, SAMR_R_LOOKUP_DOMAIN * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_lookup_domain");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr_sid))
+               return False;
+
+       if (r_u->ptr_sid != 0) {
+               if(!smb_io_dom_sid2("sid", &r_u->dom_sid, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_unknown_2d(SAMR_Q_UNKNOWN_2D * q_u, POLICY_HND *dom_pol, DOM_SID *sid)
+{
+       DEBUG(5, ("samr_init_samr_q_unknown_2d\n"));
+
+       q_u->dom_pol = *dom_pol;
+       init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_unknown_2d(const char *desc, SAMR_Q_UNKNOWN_2D * q_u,
+                         prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_unknown_2d");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &q_u->sid, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_unknown_2d(const char *desc, SAMR_R_UNKNOWN_2D * r_u,
+                         prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_unknown_2d");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_domain(SAMR_Q_OPEN_DOMAIN * q_u,
+                            POLICY_HND *pol, uint32 flags,
+                            const DOM_SID *sid)
+{
+       DEBUG(5, ("samr_init_samr_q_open_domain\n"));
+
+       q_u->pol = *pol;
+       q_u->flags = flags;
+       init_dom_sid2(&q_u->dom_sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_domain(const char *desc, SAMR_Q_OPEN_DOMAIN * q_u,
+                          prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_open_domain");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("flags", ps, depth, &q_u->flags))
+               return False;
+
+       if(!smb_io_dom_sid2("sid", &q_u->dom_sid, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_domain(const char *desc, SAMR_R_OPEN_DOMAIN * r_u,
+                          prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_open_domain");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &r_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_get_usrdom_pwinfo(SAMR_Q_GET_USRDOM_PWINFO * q_u,
+                                  POLICY_HND *user_pol)
+{
+       DEBUG(5, ("samr_init_samr_q_get_usrdom_pwinfo\n"));
+
+       q_u->user_pol = *user_pol;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_usrdom_pwinfo(const char *desc, SAMR_Q_GET_USRDOM_PWINFO * q_u,
+                                prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_get_usrdom_pwinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       return smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth);
+}
+
+/*******************************************************************
+ Init.
+********************************************************************/
+
+void init_samr_r_get_usrdom_pwinfo(SAMR_R_GET_USRDOM_PWINFO *r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_get_usrdom_pwinfo\n"));
+       
+       r_u->unknown_0 = 0x0000;
+
+       /*
+        * used to be   
+        * r_u->unknown_1 = 0x0015;
+        * but for trusts.
+        */
+       r_u->unknown_1 = 0x01D1;
+       r_u->unknown_1 = 0x0015;
+
+       r_u->unknown_2 = 0x00000000;
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_usrdom_pwinfo(const char *desc, SAMR_R_GET_USRDOM_PWINFO * r_u,
+                                prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_get_usrdom_pwinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint16("unknown_0", ps, depth, &r_u->unknown_0))
+               return False;
+       if(!prs_uint16("unknown_1", ps, depth, &r_u->unknown_1))
+               return False;
+       if(!prs_uint32("unknown_2", ps, depth, &r_u->unknown_2))
+               return False;
+       if(!prs_ntstatus("status   ", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_sec_obj(const char *desc, SAMR_Q_SET_SEC_OBJ * q_u,
+                            prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_sec_obj");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+               return False;
+               
+       if(!sec_io_desc_buf("sec_desc", &q_u->buf, ps, depth))
+               return False;
+       
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_sec_obj(SAMR_Q_QUERY_SEC_OBJ * q_u,
+                              POLICY_HND *user_pol, uint32 sec_info)
+{
+       DEBUG(5, ("samr_init_samr_q_query_sec_obj\n"));
+
+       q_u->user_pol = *user_pol;
+       q_u->sec_info = sec_info;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_sec_obj(const char *desc, SAMR_Q_QUERY_SEC_OBJ * q_u,
+                            prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_sec_obj");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_dom_info(SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+                               POLICY_HND *domain_pol, uint16 switch_value)
+{
+       DEBUG(5, ("samr_init_samr_q_query_dom_info\n"));
+
+       q_u->domain_pol = *domain_pol;
+       q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dom_info(const char *desc, SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_dom_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info3(SAM_UNK_INFO_3 *u_3, NTTIME nt_logout)
+{
+       u_3->logout.low = nt_logout.low;
+       u_3->logout.high = nt_logout.high;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info3(const char *desc, SAM_UNK_INFO_3 * u_3,
+                            prs_struct *ps, int depth)
+{
+       if (u_3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info3");
+       depth++;
+
+       if(!smb_io_time("logout", &u_3->logout, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info6(SAM_UNK_INFO_6 * u_6)
+{
+       u_6->unknown_0 = 0x00000000;
+       u_6->ptr_0 = 1;
+       memset(u_6->padding, 0, sizeof(u_6->padding));  /* 12 bytes zeros */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info6(const char *desc, SAM_UNK_INFO_6 * u_6,
+                            prs_struct *ps, int depth)
+{
+       if (u_6 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info6");
+       depth++;
+
+       if(!prs_uint32("unknown_0", ps, depth, &u_6->unknown_0)) /* 0x0000 0000 */
+               return False;
+       if(!prs_uint32("ptr_0", ps, depth, &u_6->ptr_0)) /* pointer to unknown structure */
+               return False;
+       if(!prs_uint8s(False, "padding", ps, depth, u_6->padding, sizeof(u_6->padding)))        /* 12 bytes zeros */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info7(SAM_UNK_INFO_7 * u_7)
+{
+       u_7->unknown_0 = 0x0003;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info7(const char *desc, SAM_UNK_INFO_7 * u_7,
+                            prs_struct *ps, int depth)
+{
+       if (u_7 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info7");
+       depth++;
+
+       if(!prs_uint16("unknown_0", ps, depth, &u_7->unknown_0)) /* 0x0003 */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info12(SAM_UNK_INFO_12 * u_12, NTTIME nt_lock_duration, NTTIME nt_reset_time, uint16 lockout)
+{
+       u_12->duration.low = nt_lock_duration.low;
+       u_12->duration.high = nt_lock_duration.high;
+       u_12->reset_count.low = nt_reset_time.low;
+       u_12->reset_count.high = nt_reset_time.high;
+
+       u_12->bad_attempt_lockout = lockout;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info12(const char *desc, SAM_UNK_INFO_12 * u_12,
+                             prs_struct *ps, int depth)
+{
+       if (u_12 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info12");
+       depth++;
+
+       if(!smb_io_time("duration", &u_12->duration, ps, depth))
+               return False;
+       if(!smb_io_time("reset_count", &u_12->reset_count, ps, depth))
+               return False;
+       if(!prs_uint16("bad_attempt_lockout", ps, depth, &u_12->bad_attempt_lockout))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+void init_unk_info5(SAM_UNK_INFO_5 * u_5,const char *server)
+{
+       int len_server = strlen(server);
+
+       init_uni_hdr(&u_5->hdr_server, len_server);
+
+       init_unistr2(&u_5->uni_server, server, len_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info5(const char *desc, SAM_UNK_INFO_5 * u_5,
+                            prs_struct *ps, int depth)
+{
+       if (u_5 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info5");
+       depth++;
+
+       if(!smb_io_unihdr("hdr_server", &u_5->hdr_server, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("uni_server", &u_5->uni_server, u_5->hdr_server.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+void init_unk_info2(SAM_UNK_INFO_2 * u_2,
+                       const char *domain, const char *server,
+                       uint32 seq_num, uint32 num_users, uint32 num_groups, uint32 num_alias)
+{
+       int len_domain = strlen(domain);
+       int len_server = strlen(server);
+
+       u_2->unknown_0 = 0x00000000;
+       u_2->unknown_1 = 0x80000000;
+       u_2->unknown_2 = 0x00000000;
+
+       u_2->ptr_0 = 1;
+       init_uni_hdr(&u_2->hdr_domain, len_domain);
+       init_uni_hdr(&u_2->hdr_server, len_server);
+
+       u_2->seq_num = seq_num;
+       u_2->unknown_3 = 0x00000000;
+
+       u_2->unknown_4 = 0x00000001;
+       u_2->unknown_5 = 0x00000003;
+       u_2->unknown_6 = 0x00000001;
+       u_2->num_domain_usrs = num_users;
+       u_2->num_domain_grps = num_groups;
+       u_2->num_local_grps = num_alias;
+
+       memset(u_2->padding, 0, sizeof(u_2->padding));  /* 12 bytes zeros */
+
+       init_unistr2(&u_2->uni_domain, domain, len_domain);
+       init_unistr2(&u_2->uni_server, server, len_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info2(const char *desc, SAM_UNK_INFO_2 * u_2,
+                            prs_struct *ps, int depth)
+{
+       if (u_2 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info2");
+       depth++;
+
+       if(!prs_uint32("unknown_0", ps, depth, &u_2->unknown_0)) /* 0x0000 0000 */
+               return False;
+       if(!prs_uint32("unknown_1", ps, depth, &u_2->unknown_1)) /* 0x8000 0000 */
+               return False;
+       if(!prs_uint32("unknown_2", ps, depth, &u_2->unknown_2))        /* 0x0000 0000 */
+               return False;
+
+       if(!prs_uint32("ptr_0", ps, depth, &u_2->ptr_0))
+               return False;
+       if(!smb_io_unihdr("hdr_domain", &u_2->hdr_domain, ps, depth))
+               return False;
+       if(!smb_io_unihdr("hdr_server", &u_2->hdr_server, ps, depth))
+               return False;
+
+       /* put all the data in here, at the moment, including what the above
+          pointer is referring to
+        */
+
+       if(!prs_uint32("seq_num ", ps, depth, &u_2->seq_num))   /* 0x0000 0099 or 0x1000 0000 */
+               return False;
+       if(!prs_uint32("unknown_3 ", ps, depth, &u_2->unknown_3))       /* 0x0000 0000 */
+               return False;
+
+       if(!prs_uint32("unknown_4 ", ps, depth, &u_2->unknown_4)) /* 0x0000 0001 */
+               return False;
+       if(!prs_uint32("unknown_5 ", ps, depth, &u_2->unknown_5)) /* 0x0000 0003 */
+               return False;
+       if(!prs_uint32("unknown_6 ", ps, depth, &u_2->unknown_6)) /* 0x0000 0001 */
+               return False;
+       if(!prs_uint32("num_domain_usrs ", ps, depth, &u_2->num_domain_usrs))
+               return False;
+       if(!prs_uint32("num_domain_grps", ps, depth, &u_2->num_domain_grps))
+               return False;
+       if(!prs_uint32("num_local_grps", ps, depth, &u_2->num_local_grps))
+               return False;
+
+       if (u_2->ptr_0) {
+               /* this was originally marked as 'padding'. It isn't
+                  padding, it is some sort of optional 12 byte
+                  structure. When it is present it contains zeros
+                  !? */
+               if(!prs_uint8s(False, "unknown", ps, depth, u_2->padding,sizeof(u_2->padding)))
+                       return False;
+       }
+
+       if(!smb_io_unistr2("uni_domain", &u_2->uni_domain, u_2->hdr_domain.buffer, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_server", &u_2->uni_server, u_2->hdr_server.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info1(SAM_UNK_INFO_1 *u_1, uint16 min_pass_len, uint16 pass_hist, 
+                   uint32 flag, NTTIME nt_expire, NTTIME nt_min_age)
+{
+       u_1->min_length_password = min_pass_len;
+       u_1->password_history = pass_hist;
+       u_1->flag = flag;
+       
+       /* password never expire */
+       u_1->expire.high = nt_expire.high;
+       u_1->expire.low = nt_expire.low;
+       
+       /* can change the password now */
+       u_1->min_passwordage.high = nt_min_age.high;
+       u_1->min_passwordage.low = nt_min_age.low;
+       
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info1(const char *desc, SAM_UNK_INFO_1 * u_1,
+                            prs_struct *ps, int depth)
+{
+       if (u_1 == NULL)
+         return False;
+
+       prs_debug(ps, depth, desc, "sam_io_unk_info1");
+       depth++;
+
+       if(!prs_uint16("min_length_password", ps, depth, &u_1->min_length_password))
+               return False;
+       if(!prs_uint16("password_history", ps, depth, &u_1->password_history))
+               return False;
+       if(!prs_uint32("flag", ps, depth, &u_1->flag))
+               return False;
+       if(!smb_io_time("expire", &u_1->expire, ps, depth))
+               return False;
+       if(!smb_io_time("min_passwordage", &u_1->min_passwordage, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_query_dom_info(SAMR_R_QUERY_DOMAIN_INFO * r_u,
+                               uint16 switch_value, SAM_UNK_CTR * ctr,
+                               NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_dom_info\n"));
+
+       r_u->ptr_0 = 0;
+       r_u->switch_value = 0;
+       r_u->status = status;   /* return status */
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->switch_value = switch_value;
+               r_u->ptr_0 = 1;
+               r_u->ctr = ctr;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dom_info(const char *desc, SAMR_R_QUERY_DOMAIN_INFO * r_u,
+                             prs_struct *ps, int depth)
+{
+        if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_dom_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+               return False;
+
+       if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+               if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+
+               switch (r_u->switch_value) {
+               case 0x0c:
+                       if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+                               return False;
+                       break;
+               case 0x07:
+                       if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+                               return False;
+                       break;
+               case 0x06:
+                       if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+                               return False;
+                       break;
+               case 0x05:
+                       if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+                               return False;
+                       break;
+               case 0x03:
+                       if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+                               return False;
+                       break;
+               case 0x02:
+                       if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+                               return False;
+                       break;
+               case 0x01:
+                       if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(0, ("samr_io_r_query_dom_info: unknown switch level 0x%x\n",
+                               r_u->switch_value));
+                       r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+                       return False;
+               }
+       }
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_SET_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_set_sec_obj(const char *desc, SAMR_R_SET_SEC_OBJ * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+  
+       prs_debug(ps, depth, desc, "samr_io_r_set_sec_obj");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_query_sec_obj(const char *desc, SAMR_R_QUERY_SEC_OBJ * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+  
+       prs_debug(ps, depth, desc, "samr_io_r_query_sec_obj");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+       if (r_u->ptr != 0) {
+               if(!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+                       return False;
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str1(const char *desc, SAM_STR1 * sam, uint32 acct_buf,
+                           uint32 name_buf, uint32 desc_buf,
+                           prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_str1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("name", &sam->uni_acct_name, acct_buf, ps, depth))
+               return False;
+
+       if (!smb_io_unistr2("desc", &sam->uni_acct_desc, desc_buf, ps, depth))
+               return False;
+
+       if (!smb_io_unistr2("full", &sam->uni_full_name, name_buf, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY1 structure.
+********************************************************************/
+
+static void init_sam_entry1(SAM_ENTRY1 * sam, uint32 user_idx,
+                           uint32 len_sam_name, uint32 len_sam_full,
+                           uint32 len_sam_desc, uint32 rid_user,
+                           uint16 acb_info)
+{
+       DEBUG(5, ("init_sam_entry1\n"));
+
+       ZERO_STRUCTP(sam);
+
+       sam->user_idx = user_idx;
+       sam->rid_user = rid_user;
+       sam->acb_info = acb_info;
+
+       init_uni_hdr(&sam->hdr_acct_name, len_sam_name);
+       init_uni_hdr(&sam->hdr_user_name, len_sam_full);
+       init_uni_hdr(&sam->hdr_user_desc, len_sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry1(const char *desc, SAM_ENTRY1 * sam,
+                             prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+               return False;
+
+       if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+               return False;
+       if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!smb_io_unihdr("hdr_acct_name", &sam->hdr_acct_name, ps, depth))
+               return False;
+       if (!smb_io_unihdr("hdr_user_desc", &sam->hdr_user_desc, ps, depth))
+               return False;
+       if (!smb_io_unihdr("hdr_user_name", &sam->hdr_user_name, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str2(const char *desc, SAM_STR2 * sam, uint32 acct_buf,
+                           uint32 desc_buf, prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_str2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("uni_srv_name", &sam->uni_srv_name, acct_buf, ps, depth)) /* account name unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_srv_desc", &sam->uni_srv_desc, desc_buf, ps, depth))    /* account desc unicode string */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY2 structure.
+********************************************************************/
+static void init_sam_entry2(SAM_ENTRY2 * sam, uint32 user_idx,
+                           uint32 len_sam_name, uint32 len_sam_desc,
+                           uint32 rid_user, uint16 acb_info)
+{
+       DEBUG(5, ("init_sam_entry2\n"));
+
+       sam->user_idx = user_idx;
+       sam->rid_user = rid_user;
+       sam->acb_info = acb_info;
+
+       init_uni_hdr(&sam->hdr_srv_name, len_sam_name);
+       init_uni_hdr(&sam->hdr_srv_desc, len_sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry2(const char *desc, SAM_ENTRY2 * sam,
+                             prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+               return False;
+
+       if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+               return False;
+       if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("unihdr", &sam->hdr_srv_name, ps, depth))     /* account name unicode string header */
+               return False;
+       if(!smb_io_unihdr("unihdr", &sam->hdr_srv_desc, ps, depth))     /* account name unicode string header */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str3(const char *desc, SAM_STR3 * sam, uint32 acct_buf,
+                           uint32 desc_buf, prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_str3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("uni_grp_name", &sam->uni_grp_name, acct_buf, ps, depth))    /* account name unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_grp_desc", &sam->uni_grp_desc, desc_buf, ps, depth))    /* account desc unicode string */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY3 structure.
+********************************************************************/
+
+static void init_sam_entry3(SAM_ENTRY3 * sam, uint32 grp_idx,
+                           uint32 len_grp_name, uint32 len_grp_desc,
+                           uint32 rid_grp)
+{
+       DEBUG(5, ("init_sam_entry3\n"));
+
+       sam->grp_idx = grp_idx;
+       sam->rid_grp = rid_grp;
+       sam->attr = 0x07;       /* group rid attributes - gets ignored by nt 4.0 */
+
+       init_uni_hdr(&sam->hdr_grp_name, len_grp_name);
+       init_uni_hdr(&sam->hdr_grp_desc, len_grp_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry3(const char *desc, SAM_ENTRY3 * sam,
+                             prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+               return False;
+
+       if(!prs_uint32("rid_grp", ps, depth, &sam->rid_grp))
+               return False;
+       if(!prs_uint32("attr   ", ps, depth, &sam->attr))
+               return False;
+
+       if(!smb_io_unihdr("unihdr", &sam->hdr_grp_name, ps, depth))     /* account name unicode string header */
+               return False;
+       if(!smb_io_unihdr("unihdr", &sam->hdr_grp_desc, ps, depth))     /* account name unicode string header */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY4 structure.
+********************************************************************/
+
+static void init_sam_entry4(SAM_ENTRY4 * sam, uint32 user_idx,
+                           uint32 len_acct_name)
+{
+       DEBUG(5, ("init_sam_entry4\n"));
+
+       sam->user_idx = user_idx;
+       init_str_hdr(&sam->hdr_acct_name, len_acct_name+1, len_acct_name, len_acct_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY4 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry4(const char *desc, SAM_ENTRY4 * sam,
+                             prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry4");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("user_idx", ps, depth, &sam->user_idx))
+               return False;
+       if(!smb_io_strhdr("strhdr", &sam->hdr_acct_name, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY5 structure.
+********************************************************************/
+
+static void init_sam_entry5(SAM_ENTRY5 * sam, uint32 grp_idx,
+                           uint32 len_grp_name)
+{
+       DEBUG(5, ("init_sam_entry5\n"));
+
+       sam->grp_idx = grp_idx;
+       init_str_hdr(&sam->hdr_grp_name, len_grp_name, len_grp_name,
+                    len_grp_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY5 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry5(const char *desc, SAM_ENTRY5 * sam,
+                             prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry5");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+               return False;
+       if(!smb_io_strhdr("strhdr", &sam->hdr_grp_name, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY structure.
+********************************************************************/
+
+void init_sam_entry(SAM_ENTRY * sam, uint32 len_sam_name, uint32 rid)
+{
+       DEBUG(10, ("init_sam_entry: %d %d\n", len_sam_name, rid));
+
+       sam->rid = rid;
+       init_uni_hdr(&sam->hdr_name, len_sam_name);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry(const char *desc, SAM_ENTRY * sam,
+                            prs_struct *ps, int depth)
+{
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_entry");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("rid", ps, depth, &sam->rid))
+               return False;
+       if(!smb_io_unihdr("unihdr", &sam->hdr_name, ps, depth)) /* account name unicode string header */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_users(SAMR_Q_ENUM_DOM_USERS * q_e, POLICY_HND *pol,
+                               uint32 start_idx,
+                               uint16 acb_mask, uint16 unk_1, uint32 size)
+{
+       DEBUG(5, ("init_samr_q_enum_dom_users\n"));
+
+       q_e->pol = *pol;
+
+       q_e->start_idx = start_idx;     /* zero indicates lots */
+       q_e->acb_mask = acb_mask;
+       q_e->unknown_1 = unk_1;
+       q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_users(const char *desc, SAMR_Q_ENUM_DOM_USERS * q_e,
+                             prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_enum_dom_users");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+               return False;
+       if(!prs_uint16("acb_mask ", ps, depth, &q_e->acb_mask))
+               return False;
+       if(!prs_uint16("unknown_1", ps, depth, &q_e->unknown_1))
+               return False;
+
+       if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_users(SAMR_R_ENUM_DOM_USERS * r_u,
+                               uint32 next_idx, uint32 num_sam_entries)
+{
+       DEBUG(5, ("init_samr_r_enum_dom_users\n"));
+
+       r_u->next_idx = next_idx;
+
+       if (num_sam_entries != 0) {
+               r_u->ptr_entries1 = 1;
+               r_u->ptr_entries2 = 1;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->num_entries3 = num_sam_entries;
+
+               r_u->num_entries4 = num_sam_entries;
+       } else {
+               r_u->ptr_entries1 = 0;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->ptr_entries2 = 1;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_users(const char *desc, SAMR_R_ENUM_DOM_USERS * r_u,
+                             prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_enum_dom_users");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("next_idx    ", ps, depth, &r_u->next_idx))
+               return False;
+       if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+               return False;
+
+       if (r_u->ptr_entries1 != 0) {
+               if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+                       return False;
+               if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+                       return False;
+               if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+                       return False;
+
+               if (UNMARSHALLING(ps) && (r_u->num_entries2 != 0)) {
+                       r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+                       r_u->uni_acct_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+               }
+
+               if ((r_u->sam == NULL || r_u->uni_acct_name == NULL) && r_u->num_entries2 != 0) {
+                       DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_USERS\n"));
+                       r_u->num_entries4 = 0;
+                       r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!smb_io_unistr2("", &r_u->uni_acct_name[i],r_u->sam[i].hdr_name.buffer, ps,depth))
+                               return False;
+               }
+
+       }
+
+       if(!prs_align(ps))
+               return False;
+               
+       if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_dispinfo(SAMR_Q_QUERY_DISPINFO * q_e, POLICY_HND *pol,
+                               uint16 switch_level, uint32 start_idx,
+                               uint32 max_entries, uint32 max_size)
+{
+       DEBUG(5, ("init_samr_q_query_dispinfo\n"));
+
+       q_e->domain_pol = *pol;
+
+       q_e->switch_level = switch_level;
+
+       q_e->start_idx = start_idx;
+       q_e->max_entries = max_entries;
+       q_e->max_size = max_size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dispinfo(const char *desc, SAMR_Q_QUERY_DISPINFO * q_e,
+                             prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_dispinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_e->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("start_idx   ", ps, depth, &q_e->start_idx))
+               return False;
+       if(!prs_uint32("max_entries ", ps, depth, &q_e->max_entries))
+               return False;
+       if(!prs_uint32("max_size    ", ps, depth, &q_e->max_size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_1 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_1(TALLOC_CTX *ctx, SAM_DISPINFO_1 *sam, uint32 num_entries,
+                            uint32 start_idx, DISP_USER_INFO *disp_user_info,
+                            DOM_SID *domain_sid)
+{
+       uint32 len_sam_name, len_sam_full, len_sam_desc;
+       uint32 i;
+
+       SAM_ACCOUNT *pwd = NULL;
+       ZERO_STRUCTP(sam);
+
+       DEBUG(10, ("init_sam_dispinfo_1: num_entries: %d\n", num_entries));
+
+       if (num_entries==0)
+               return NT_STATUS_OK;
+
+       sam->sam=(SAM_ENTRY1 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY1));
+       if (!sam->sam)
+               return NT_STATUS_NO_MEMORY;
+
+       sam->str=(SAM_STR1 *)talloc(ctx, num_entries*sizeof(SAM_STR1));
+       if (!sam->str)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(sam->sam);
+       ZERO_STRUCTP(sam->str);
+
+       for (i = 0; i < num_entries ; i++) {
+               const char *username;
+               const char *fullname;
+               const char *acct_desc;
+               uint32 user_rid;
+               const DOM_SID *user_sid;
+               fstring user_sid_string, domain_sid_string;                     
+
+               DEBUG(11, ("init_sam_dispinfo_1: entry: %d\n",i));
+               
+               pwd=disp_user_info[i+start_idx].sam;
+               
+               username = pdb_get_username(pwd);
+               fullname = pdb_get_fullname(pwd);
+               acct_desc = pdb_get_acct_desc(pwd);
+               
+               if (!username) 
+                       username = "";
+
+               if (!fullname) 
+                       fullname = "";
+               
+               if (!acct_desc) 
+                       acct_desc = "";
+
+               user_sid = pdb_get_user_sid(pwd);
+
+               if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+                       DEBUG(0, ("init_sam_dispinfo_1: User %s has SID %s, which conflicts with "
+                                 "the domain sid %s.  Failing operation.\n", 
+                                 username, 
+                                 sid_to_string(user_sid_string, user_sid),
+                                 sid_to_string(domain_sid_string, domain_sid)));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+                       
+               len_sam_name = strlen(username);
+               len_sam_full = strlen(fullname);
+               len_sam_desc = strlen(acct_desc);
+
+               init_sam_entry1(&sam->sam[i], start_idx + i + 1,
+                               len_sam_name, len_sam_full, len_sam_desc,
+                               user_rid, pdb_get_acct_ctrl(pwd));
+               
+               ZERO_STRUCTP(&sam->str[i].uni_acct_name);
+               ZERO_STRUCTP(&sam->str[i].uni_full_name);
+               ZERO_STRUCTP(&sam->str[i].uni_acct_desc);
+
+               init_unistr2(&sam->str[i].uni_acct_name, pdb_get_username(pwd),  len_sam_name);
+               init_unistr2(&sam->str[i].uni_full_name, pdb_get_fullname(pwd),  len_sam_full);
+               init_unistr2(&sam->str[i].uni_acct_desc, pdb_get_acct_desc(pwd), len_sam_desc);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_1(const char *desc, SAM_DISPINFO_1 * sam,
+                                 uint32 num_entries,
+                                 prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (UNMARSHALLING(ps) && num_entries > 0) {
+
+               if ((sam->sam = (SAM_ENTRY1 *)
+                    prs_alloc_mem(ps, sizeof(SAM_ENTRY1) *
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_ENTRY1\n"));
+                       return False;
+               }
+
+               if ((sam->str = (SAM_STR1 *)
+                    prs_alloc_mem(ps, sizeof(SAM_STR1) * 
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_STR1\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_entry1("", &sam->sam[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_str1("", &sam->str[i],
+                             sam->sam[i].hdr_acct_name.buffer,
+                             sam->sam[i].hdr_user_name.buffer,
+                             sam->sam[i].hdr_user_desc.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_2 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_2(TALLOC_CTX *ctx, SAM_DISPINFO_2 *sam, uint32 num_entries,
+                            uint32 start_idx, DISP_USER_INFO *disp_user_info, 
+                            DOM_SID *domain_sid )
+{
+       uint32 len_sam_name, len_sam_desc;
+       uint32 i;
+
+       SAM_ACCOUNT *pwd = NULL;
+       ZERO_STRUCTP(sam);
+
+       DEBUG(10, ("init_sam_dispinfo_2: num_entries: %d\n", num_entries));
+
+       if (num_entries==0)
+               return NT_STATUS_OK;
+
+       if (!(sam->sam=(SAM_ENTRY2 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY2))))
+               return NT_STATUS_NO_MEMORY;
+
+       if (!(sam->str=(SAM_STR2 *)talloc(ctx, num_entries*sizeof(SAM_STR2))))
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(sam->sam);
+       ZERO_STRUCTP(sam->str);
+
+       for (i = 0; i < num_entries; i++) {
+               uint32 user_rid;
+               const DOM_SID *user_sid;
+               const char *username;
+               const char *acct_desc;
+               fstring user_sid_string, domain_sid_string;                     
+
+               DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+               pwd=disp_user_info[i+start_idx].sam;
+
+               username = pdb_get_username(pwd);
+               acct_desc = pdb_get_acct_desc(pwd);
+               user_sid = pdb_get_user_sid(pwd);
+
+               if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+                       DEBUG(0, ("init_sam_dispinfo_2: User %s has SID %s, which conflicts with "
+                                 "the domain sid %s.  Failing operation.\n", 
+                                 username, 
+                                 sid_to_string(user_sid_string, user_sid),
+                                 sid_to_string(domain_sid_string, domain_sid)));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+                       
+               len_sam_name = strlen(username);
+               len_sam_desc = strlen(acct_desc);
+         
+               init_sam_entry2(&sam->sam[i], start_idx + i + 1,
+                         len_sam_name, len_sam_desc,
+                         user_rid, pdb_get_acct_ctrl(pwd));
+         
+               ZERO_STRUCTP(&sam->str[i].uni_srv_name);
+               ZERO_STRUCTP(&sam->str[i].uni_srv_desc);
+
+               init_unistr2(&sam->str[i].uni_srv_name, username,  len_sam_name);
+               init_unistr2(&sam->str[i].uni_srv_desc, pdb_get_acct_desc(pwd), len_sam_desc);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_2(const char *desc, SAM_DISPINFO_2 * sam,
+                                 uint32 num_entries,
+                                 prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (UNMARSHALLING(ps) && num_entries > 0) {
+
+               if ((sam->sam = (SAM_ENTRY2 *)
+                    prs_alloc_mem(ps, sizeof(SAM_ENTRY2) *
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_ENTRY2\n"));
+                       return False;
+               }
+
+               if ((sam->str = (SAM_STR2 *)
+                    prs_alloc_mem(ps, sizeof(SAM_STR2) * 
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_STR2\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_entry2("", &sam->sam[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_str2("", &sam->str[i],
+                             sam->sam[i].hdr_srv_name.buffer,
+                             sam->sam[i].hdr_srv_desc.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_3 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_3(TALLOC_CTX *ctx, SAM_DISPINFO_3 *sam, uint32 num_entries,
+                        uint32 start_idx, DISP_GROUP_INFO *disp_group_info)
+{
+       uint32 len_sam_name, len_sam_desc;
+       uint32 i;
+
+       ZERO_STRUCTP(sam);
+
+       DEBUG(5, ("init_sam_dispinfo_3: num_entries: %d\n", num_entries));
+
+       if (num_entries==0)
+               return NT_STATUS_OK;
+
+       if (!(sam->sam=(SAM_ENTRY3 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY3))))
+               return NT_STATUS_NO_MEMORY;
+
+       if (!(sam->str=(SAM_STR3 *)talloc(ctx, num_entries*sizeof(SAM_STR3))))
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(sam->sam);
+       ZERO_STRUCTP(sam->str);
+
+       for (i = 0; i < num_entries; i++) {
+               DOMAIN_GRP *grp = disp_group_info[i+start_idx].grp;
+
+               DEBUG(11, ("init_sam_dispinfo_3: entry: %d\n",i));
+
+               len_sam_name = strlen(grp->name);
+               len_sam_desc = strlen(grp->comment);
+
+               init_sam_entry3(&sam->sam[i], start_idx + i + 1, len_sam_name, len_sam_desc, grp->rid);
+         
+               init_unistr2(&sam->str[i].uni_grp_name, grp->name, len_sam_name);
+               init_unistr2(&sam->str[i].uni_grp_desc, grp->comment, len_sam_desc);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_3(const char *desc, SAM_DISPINFO_3 * sam,
+                                 uint32 num_entries,
+                                 prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (UNMARSHALLING(ps) && num_entries > 0) {
+
+               if ((sam->sam = (SAM_ENTRY3 *)
+                    prs_alloc_mem(ps, sizeof(SAM_ENTRY3) *
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_ENTRY3\n"));
+                       return False;
+               }
+
+               if ((sam->str = (SAM_STR3 *)
+                    prs_alloc_mem(ps, sizeof(SAM_STR3) * 
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_STR3\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_entry3("", &sam->sam[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_str3("", &sam->str[i],
+                             sam->sam[i].hdr_grp_name.buffer,
+                             sam->sam[i].hdr_grp_desc.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_4 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_4(TALLOC_CTX *ctx, SAM_DISPINFO_4 *sam, uint32 num_entries,
+                        uint32 start_idx, DISP_USER_INFO *disp_user_info)
+{
+       uint32 len_sam_name;
+       uint32 i;
+
+       SAM_ACCOUNT *pwd = NULL;
+       ZERO_STRUCTP(sam);
+
+       DEBUG(5, ("init_sam_dispinfo_4: num_entries: %d\n", num_entries));
+
+       if (num_entries==0)
+               return NT_STATUS_OK;
+
+       if (!(sam->sam=(SAM_ENTRY4 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY4))))
+               return NT_STATUS_NO_MEMORY;
+
+       if (!(sam->str=(SAM_STR4 *)talloc(ctx, num_entries*sizeof(SAM_STR4))))
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(sam->sam);
+       ZERO_STRUCTP(sam->str);
+
+       for (i = 0; i < num_entries; i++) {
+               DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+               pwd=disp_user_info[i+start_idx].sam;
+
+               len_sam_name = strlen(pdb_get_username(pwd));
+         
+               init_sam_entry4(&sam->sam[i], start_idx + i + 1, len_sam_name);
+
+               init_string2(&sam->str[i].acct_name, pdb_get_username(pwd), len_sam_name+1, len_sam_name);
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_4(const char *desc, SAM_DISPINFO_4 * sam,
+                                 uint32 num_entries,
+                                 prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_4");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (UNMARSHALLING(ps) && num_entries > 0) {
+
+               if ((sam->sam = (SAM_ENTRY4 *)
+                    prs_alloc_mem(ps, sizeof(SAM_ENTRY4) *
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_ENTRY4\n"));
+                       return False;
+               }
+
+               if ((sam->str = (SAM_STR4 *)
+                    prs_alloc_mem(ps, sizeof(SAM_STR4) * 
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_STR4\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_entry4("", &sam->sam[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!smb_io_string2("acct_name", &sam->str[i].acct_name,
+                            sam->sam[i].hdr_acct_name.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_5 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_5(TALLOC_CTX *ctx, SAM_DISPINFO_5 *sam, uint32 num_entries,
+                        uint32 start_idx, DISP_GROUP_INFO *disp_group_info)
+{
+       uint32 len_sam_name;
+       uint32 i;
+
+       ZERO_STRUCTP(sam);
+
+       DEBUG(5, ("init_sam_dispinfo_5: num_entries: %d\n", num_entries));
+
+       if (num_entries==0)
+               return NT_STATUS_OK;
+
+       if (!(sam->sam=(SAM_ENTRY5 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY5))))
+               return NT_STATUS_NO_MEMORY;
+
+       if (!(sam->str=(SAM_STR5 *)talloc(ctx, num_entries*sizeof(SAM_STR5))))
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(sam->sam);
+       ZERO_STRUCTP(sam->str);
+
+       for (i = 0; i < num_entries; i++) {
+               DOMAIN_GRP *grp = disp_group_info[i+start_idx].grp;
+
+               DEBUG(11, ("init_sam_dispinfo_5: entry: %d\n",i));
+
+               len_sam_name = strlen(grp->name);
+         
+               init_sam_entry5(&sam->sam[i], start_idx + i + 1, len_sam_name);
+               init_string2(&sam->str[i].grp_name, grp->name, len_sam_name+1, len_sam_name);
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_5(const char *desc, SAM_DISPINFO_5 * sam,
+                                 uint32 num_entries,
+                                 prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (sam == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_5");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (UNMARSHALLING(ps) && num_entries > 0) {
+
+               if ((sam->sam = (SAM_ENTRY5 *)
+                    prs_alloc_mem(ps, sizeof(SAM_ENTRY5) *
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_ENTRY5\n"));
+                       return False;
+               }
+
+               if ((sam->str = (SAM_STR5 *)
+                    prs_alloc_mem(ps, sizeof(SAM_STR5) * 
+                                  num_entries)) == NULL) {
+                       DEBUG(0, ("out of memory allocating SAM_STR5\n"));
+                       return False;
+               }
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!sam_io_sam_entry5("", &sam->sam[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               if(!smb_io_string2("grp_name", &sam->str[i].grp_name,
+                            sam->sam[i].hdr_grp_name.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_dispinfo(SAMR_R_QUERY_DISPINFO * r_u,
+                               uint32 num_entries, uint32 total_size, uint32 data_size,
+                               uint16 switch_level, SAM_DISPINFO_CTR * ctr,
+                               NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_dispinfo: level %d\n", switch_level));
+
+       r_u->total_size = total_size;
+
+       r_u->data_size = data_size;
+
+       r_u->switch_level = switch_level;
+       r_u->num_entries = num_entries;
+
+       if (num_entries==0)
+               r_u->ptr_entries = 0;
+       else
+               r_u->ptr_entries = 1;
+
+       r_u->num_entries2 = num_entries;
+       r_u->ctr = ctr;
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dispinfo(const char *desc, SAMR_R_QUERY_DISPINFO * r_u,
+                             prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_dispinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("total_size  ", ps, depth, &r_u->total_size))
+               return False;
+       if(!prs_uint32("data_size   ", ps, depth, &r_u->data_size))
+               return False;
+       if(!prs_uint16("switch_level", ps, depth, &r_u->switch_level))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+               return False;
+       if(!prs_uint32("ptr_entries ", ps, depth, &r_u->ptr_entries))
+               return False;
+
+       if (r_u->ptr_entries==0) {
+               if(!prs_align(ps))
+                       return False;
+               if(!prs_ntstatus("status", ps, depth, &r_u->status))
+                       return False;
+
+               return True;
+       }
+
+       if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+               return False;
+
+       switch (r_u->switch_level) {
+       case 0x1:
+               if(!sam_io_sam_dispinfo_1("users", r_u->ctr->sam.info1,
+                               r_u->num_entries, ps, depth))
+                       return False;
+               break;
+       case 0x2:
+               if(!sam_io_sam_dispinfo_2("servers", r_u->ctr->sam.info2,
+                               r_u->num_entries, ps, depth))
+                       return False;
+               break;
+       case 0x3:
+               if(!sam_io_sam_dispinfo_3("groups", r_u->ctr->sam.info3,
+                                   r_u->num_entries, ps, depth))
+                       return False;
+               break;
+       case 0x4:
+               if(!sam_io_sam_dispinfo_4("user list",
+                                   r_u->ctr->sam.info4,
+                                   r_u->num_entries, ps, depth))
+                       return False;
+               break;
+       case 0x5:
+               if(!sam_io_sam_dispinfo_5("group list",
+                                   r_u->ctr->sam.info5,
+                                   r_u->num_entries, ps, depth))
+                       return False;
+               break;
+       default:
+               DEBUG(0,("samr_io_r_query_dispinfo: unknown switch value\n"));
+               break;
+       }
+       
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_GROUP structure.
+********************************************************************/
+
+void init_samr_q_open_group(SAMR_Q_OPEN_GROUP * q_c,
+                           POLICY_HND *hnd,
+                           uint32 access_mask, uint32 rid)
+{
+       DEBUG(5, ("init_samr_q_open_group\n"));
+
+       q_c->domain_pol = *hnd;
+       q_c->access_mask = access_mask;
+       q_c->rid_group = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_group(const char *desc, SAMR_Q_OPEN_GROUP * q_u,
+                         prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_open_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+       if(!prs_uint32("rid_group", ps, depth, &q_u->rid_group))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_group(const char *desc, SAMR_R_OPEN_GROUP * r_u,
+                         prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_open_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO1 structure.
+********************************************************************/
+
+void init_samr_group_info1(GROUP_INFO1 * gr1,
+                          char *acct_name, char *acct_desc,
+                          uint32 num_members)
+{
+       int desc_len = acct_desc != NULL ? strlen(acct_desc) : 0;
+       int acct_len = acct_name != NULL ? strlen(acct_name) : 0;
+
+       DEBUG(5, ("init_samr_group_info1\n"));
+
+       init_uni_hdr(&gr1->hdr_acct_name, acct_len);
+
+       gr1->unknown_1 = 0x3;
+       gr1->num_members = num_members;
+
+       init_uni_hdr(&gr1->hdr_acct_desc, desc_len);
+
+       init_unistr2(&gr1->uni_acct_name, acct_name, acct_len);
+       init_unistr2(&gr1->uni_acct_desc, acct_desc, desc_len);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info1(const char *desc, GROUP_INFO1 * gr1,
+                        prs_struct *ps, int depth)
+{
+       if (gr1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_group_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_name", &gr1->hdr_acct_name, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown_1", ps, depth, &gr1->unknown_1))
+               return False;
+       if(!prs_uint32("num_members", ps, depth, &gr1->num_members))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_desc", &gr1->hdr_acct_desc, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_name", &gr1->uni_acct_name,
+                          gr1->hdr_acct_name.buffer, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_desc", &gr1->uni_acct_desc,
+                          gr1->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO3 structure.
+********************************************************************/
+
+void init_samr_group_info3(GROUP_INFO3 *gr3)
+{
+       DEBUG(5, ("init_samr_group_info3\n"));
+
+       gr3->unknown_1 = 0x3;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info3(const char *desc, GROUP_INFO3 *gr3, prs_struct *ps, int depth)
+{
+       if (gr3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_group_info3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("unknown_1", ps, depth, &gr3->unknown_1))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO4 structure.
+********************************************************************/
+
+void init_samr_group_info4(GROUP_INFO4 * gr4, char *acct_desc)
+{
+       int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0;
+
+       DEBUG(5, ("init_samr_group_info4\n"));
+
+       init_uni_hdr(&gr4->hdr_acct_desc, acct_len);
+       init_unistr2(&gr4->uni_acct_desc, acct_desc, acct_len);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info4(const char *desc, GROUP_INFO4 * gr4,
+                        prs_struct *ps, int depth)
+{
+       if (gr4 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_group_info4");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_desc", &gr4->hdr_acct_desc, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_acct_desc", &gr4->uni_acct_desc,
+                          gr4->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_group_info_ctr(const char *desc, GROUP_INFO_CTR **ctr,
+                               prs_struct *ps, int depth)
+{
+       if (UNMARSHALLING(ps))
+               *ctr = (GROUP_INFO_CTR *)prs_alloc_mem(ps,sizeof(GROUP_INFO_CTR));
+
+       if (*ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_group_info_ctr");
+       depth++;
+
+       if(!prs_uint16("switch_value1", ps, depth, &(*ctr)->switch_value1))
+               return False;
+
+       switch ((*ctr)->switch_value1) {
+       case 1:
+               if(!samr_io_group_info1("group_info1", &(*ctr)->group.info1, ps, depth))
+                       return False;
+               break;
+       case 3:
+               if(!samr_io_group_info3("group_info3", &(*ctr)->group.info3, ps, depth))
+                       return False;
+               break;
+       case 4:
+               if(!samr_io_group_info4("group_info4", &(*ctr)->group.info4, ps, depth))
+                       return False;
+               break;
+       default:
+               DEBUG(0,("samr_group_info_ctr: unsupported switch level\n"));
+               break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_create_dom_group(SAMR_Q_CREATE_DOM_GROUP * q_e,
+                                 POLICY_HND *pol, char *acct_desc,
+                                 uint32 access_mask)
+{
+       int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0;
+
+       DEBUG(5, ("init_samr_q_create_dom_group\n"));
+
+       q_e->pol = *pol;
+
+       init_uni_hdr(&q_e->hdr_acct_desc, acct_len);
+       init_unistr2(&q_e->uni_acct_desc, acct_desc, acct_len);
+
+       q_e->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_group(const char *desc, SAMR_Q_CREATE_DOM_GROUP * q_e,
+                               prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_create_dom_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_desc", &q_e->hdr_acct_desc, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_acct_desc", &q_e->uni_acct_desc,
+                      q_e->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("access", ps, depth, &q_e->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_group(const char *desc, SAMR_R_CREATE_DOM_GROUP * r_u,
+                               prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_create_dom_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("rid   ", ps, depth, &r_u->rid))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_group(SAMR_Q_DELETE_DOM_GROUP * q_c,
+                                 POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_delete_dom_group\n"));
+
+       q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_group(const char *desc, SAMR_Q_DELETE_DOM_GROUP * q_u,
+                               prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_delete_dom_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_group(const char *desc, SAMR_R_DELETE_DOM_GROUP * r_u,
+                               prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_delete_dom_group");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_del_groupmem(SAMR_Q_DEL_GROUPMEM * q_e,
+                             POLICY_HND *pol, uint32 rid)
+{
+       DEBUG(5, ("init_samr_q_del_groupmem\n"));
+
+       q_e->pol = *pol;
+       q_e->rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_groupmem(const char *desc, SAMR_Q_DEL_GROUPMEM * q_e,
+                           prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_del_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("rid", ps, depth, &q_e->rid))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_del_groupmem(SAMR_R_DEL_GROUPMEM * r_u, POLICY_HND *pol,
+                             NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_del_groupmem\n"));
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_groupmem(const char *desc, SAMR_R_DEL_GROUPMEM * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_del_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_add_groupmem(SAMR_Q_ADD_GROUPMEM * q_e,
+                             POLICY_HND *pol, uint32 rid)
+{
+       DEBUG(5, ("init_samr_q_add_groupmem\n"));
+
+       q_e->pol = *pol;
+       q_e->rid = rid;
+       q_e->unknown = 0x0005;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_groupmem(const char *desc, SAMR_Q_ADD_GROUPMEM * q_e,
+                           prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_add_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("rid    ", ps, depth, &q_e->rid))
+               return False;
+       if(!prs_uint32("unknown", ps, depth, &q_e->unknown))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_add_groupmem(SAMR_R_ADD_GROUPMEM * r_u, POLICY_HND *pol,
+                             NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_add_groupmem\n"));
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_groupmem(const char *desc, SAMR_R_ADD_GROUPMEM * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_add_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_set_groupinfo(SAMR_Q_SET_GROUPINFO * q_e,
+                              POLICY_HND *pol, GROUP_INFO_CTR * ctr)
+{
+       DEBUG(5, ("init_samr_q_set_groupinfo\n"));
+
+       q_e->pol = *pol;
+       q_e->ctr = ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_groupinfo(const char *desc, SAMR_Q_SET_GROUPINFO * q_e,
+                            prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_groupinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+       
+       if(!samr_group_info_ctr("ctr", &q_e->ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_set_groupinfo(SAMR_R_SET_GROUPINFO * r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_set_groupinfo\n"));
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_groupinfo(const char *desc, SAMR_R_SET_GROUPINFO * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_set_groupinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_groupinfo(SAMR_Q_QUERY_GROUPINFO * q_e,
+                                POLICY_HND *pol, uint16 switch_level)
+{
+       DEBUG(5, ("init_samr_q_query_groupinfo\n"));
+
+       q_e->pol = *pol;
+
+       q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupinfo(const char *desc, SAMR_Q_QUERY_GROUPINFO * q_e,
+                              prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_groupinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_groupinfo(SAMR_R_QUERY_GROUPINFO * r_u,
+                                GROUP_INFO_CTR * ctr, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_groupinfo\n"));
+
+       r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+       r_u->ctr = ctr;
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupinfo(const char *desc, SAMR_R_QUERY_GROUPINFO * r_u,
+                              prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_groupinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if(!samr_group_info_ctr("ctr", &r_u->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_query_groupmem(SAMR_Q_QUERY_GROUPMEM * q_c, POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_query_groupmem\n"));
+
+       q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupmem(const char *desc, SAMR_Q_QUERY_GROUPMEM * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_query_groupmem(SAMR_R_QUERY_GROUPMEM * r_u,
+                               uint32 num_entries, uint32 *rid,
+                               uint32 *attr, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_groupmem\n"));
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->ptr = 1;
+               r_u->num_entries = num_entries;
+
+               r_u->ptr_attrs = attr != NULL ? 1 : 0;
+               r_u->ptr_rids = rid != NULL ? 1 : 0;
+
+               r_u->num_rids = num_entries;
+               r_u->rid = rid;
+
+               r_u->num_attrs = num_entries;
+               r_u->attr = attr;
+       } else {
+               r_u->ptr = 0;
+               r_u->num_entries = 0;
+       }
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupmem(const char *desc, SAMR_R_QUERY_GROUPMEM * r_u,
+                             prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (r_u == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(r_u);
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_groupmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+       if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+                       return False;
+               if(!prs_uint32("ptr_attrs", ps, depth, &r_u->ptr_attrs))
+                       return False;
+
+               if (r_u->ptr_rids != 0) {
+                       if(!prs_uint32("num_rids", ps, depth, &r_u->num_rids))
+                               return False;
+                       if (UNMARSHALLING(ps) && r_u->num_rids != 0) {
+                               r_u->rid = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->rid[0])*r_u->num_rids);
+                               if (r_u->rid == NULL)
+                                       return False;
+                       }
+
+                       for (i = 0; i < r_u->num_rids; i++) {
+                               if(!prs_uint32("", ps, depth, &r_u->rid[i]))
+                                       return False;
+                       }
+               }
+
+               if (r_u->ptr_attrs != 0) {
+                       if(!prs_uint32("num_attrs", ps, depth, &r_u->num_attrs))
+                               return False;
+
+                       if (UNMARSHALLING(ps) && r_u->num_attrs != 0) {
+                               r_u->attr = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->attr[0])*r_u->num_attrs);
+                               if (r_u->attr == NULL)
+                                       return False;
+                       }
+
+                       for (i = 0; i < r_u->num_attrs; i++) {
+                               if(!prs_uint32("", ps, depth, &r_u->attr[i]))
+                                       return False;
+                       }
+               }
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_q_query_usergroups(SAMR_Q_QUERY_USERGROUPS * q_u,
+                                 POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_query_usergroups\n"));
+
+       q_u->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_usergroups(const char *desc, SAMR_Q_QUERY_USERGROUPS * q_u,
+                               prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_usergroups");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_r_query_usergroups(SAMR_R_QUERY_USERGROUPS * r_u,
+                                 uint32 num_gids, DOM_GID * gid,
+                                 NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_usergroups\n"));
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->ptr_0 = 1;
+               r_u->num_entries = num_gids;
+               r_u->ptr_1 = (num_gids != 0) ? 1 : 0;
+               r_u->num_entries2 = num_gids;
+
+               r_u->gid = gid;
+       } else {
+               r_u->ptr_0 = 0;
+               r_u->num_entries = 0;
+               r_u->ptr_1 = 0;
+               r_u->gid = NULL;
+       }
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_gids(const char *desc, uint32 *num_gids, DOM_GID ** gid,
+                 prs_struct *ps, int depth)
+{
+       uint32 i;
+       if (gid == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_gids");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_gids", ps, depth, num_gids))
+               return False;
+
+       if ((*num_gids) != 0) {
+               if (UNMARSHALLING(ps)) {
+                       (*gid) = (DOM_GID *)prs_alloc_mem(ps,sizeof(DOM_GID)*(*num_gids));
+               }
+
+               if ((*gid) == NULL) {
+                       return False;
+               }
+
+               for (i = 0; i < (*num_gids); i++) {
+                       if(!smb_io_gid("gids", &(*gid)[i], ps, depth))
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_usergroups(const char *desc, SAMR_R_QUERY_USERGROUPS * r_u,
+                               prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_usergroups");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_0       ", ps, depth, &r_u->ptr_0))
+               return False;
+
+       if (r_u->ptr_0 != 0) {
+               if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+                       return False;
+               if(!prs_uint32("ptr_1       ", ps, depth, &r_u->ptr_1))
+                       return False;
+
+               if (r_u->num_entries != 0 && r_u->ptr_1 != 0) {
+                       if(!samr_io_gids("gids", &r_u->num_entries2, &r_u->gid, ps, depth))
+                               return False;
+               }
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+         return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_q_enum_domains(SAMR_Q_ENUM_DOMAINS * q_e,
+                             POLICY_HND *pol,
+                             uint32 start_idx, uint32 size)
+{
+       DEBUG(5, ("init_samr_q_enum_domains\n"));
+
+       q_e->pol = *pol;
+
+       q_e->start_idx = start_idx;
+       q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_domains(const char *desc, SAMR_Q_ENUM_DOMAINS * q_e,
+                           prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_enum_domains");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+               return False;
+       if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_r_enum_domains(SAMR_R_ENUM_DOMAINS * r_u,
+                             uint32 next_idx, uint32 num_sam_entries)
+{
+       DEBUG(5, ("init_samr_r_enum_domains\n"));
+
+       r_u->next_idx = next_idx;
+
+       if (num_sam_entries != 0) {
+               r_u->ptr_entries1 = 1;
+               r_u->ptr_entries2 = 1;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->num_entries3 = num_sam_entries;
+
+               r_u->num_entries4 = num_sam_entries;
+       } else {
+               r_u->ptr_entries1 = 0;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->ptr_entries2 = 1;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_domains(const char *desc, SAMR_R_ENUM_DOMAINS * r_u,
+                           prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_enum_domains");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("next_idx    ", ps, depth, &r_u->next_idx))
+               return False;
+       if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+               return False;
+
+       if (r_u->ptr_entries1 != 0) {
+               if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+                       return False;
+               if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+                       return False;
+               if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+                       return False;
+
+               if (UNMARSHALLING(ps)) {
+                       r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+                       r_u->uni_dom_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+               }
+
+               if ((r_u->sam == NULL || r_u->uni_dom_name == NULL) && r_u->num_entries2 != 0) {
+                       DEBUG(0, ("NULL pointers in SAMR_R_ENUM_DOMAINS\n"));
+                       r_u->num_entries4 = 0;
+                       r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       fstring tmp;
+                       slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+                       if(!sam_io_sam_entry(tmp, &r_u->sam[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       fstring tmp;
+                       slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+                       if(!smb_io_unistr2(tmp, &r_u->uni_dom_name[i],
+                                      r_u->sam[i].hdr_name.buffer, ps,
+                                      depth))
+                               return False;
+               }
+
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_groups(SAMR_Q_ENUM_DOM_GROUPS * q_e,
+                                POLICY_HND *pol,
+                                uint32 start_idx, uint32 size)
+{
+       DEBUG(5, ("init_samr_q_enum_dom_groups\n"));
+
+       q_e->pol = *pol;
+
+       q_e->start_idx = start_idx;
+       q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_groups(const char *desc, SAMR_Q_ENUM_DOM_GROUPS * q_e,
+                              prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_enum_dom_groups");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+               return False;
+
+       if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+               return False;
+       if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_groups(SAMR_R_ENUM_DOM_GROUPS * r_u,
+                                uint32 next_idx, uint32 num_sam_entries)
+{
+       DEBUG(5, ("init_samr_r_enum_dom_groups\n"));
+
+       r_u->next_idx = next_idx;
+
+       if (num_sam_entries != 0) {
+               r_u->ptr_entries1 = 1;
+               r_u->ptr_entries2 = 1;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->num_entries3 = num_sam_entries;
+
+               r_u->num_entries4 = num_sam_entries;
+       } else {
+               r_u->ptr_entries1 = 0;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->ptr_entries2 = 1;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_groups(const char *desc, SAMR_R_ENUM_DOM_GROUPS * r_u,
+                              prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_enum_dom_groups");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("next_idx    ", ps, depth, &r_u->next_idx))
+               return False;
+       if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+               return False;
+
+       if (r_u->ptr_entries1 != 0) {
+               if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+                       return False;
+               if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+                       return False;
+               if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+                       return False;
+
+               if (UNMARSHALLING(ps)) {
+                       r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+                       r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+               }
+
+               if ((r_u->sam == NULL || r_u->uni_grp_name == NULL) && r_u->num_entries2 != 0) {
+                       DEBUG(0,
+                             ("NULL pointers in SAMR_R_ENUM_DOM_GROUPS\n"));
+                       r_u->num_entries4 = 0;
+                       r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+                                      r_u->sam[i].hdr_name.buffer, ps, depth))
+                               return False;
+               }
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_aliases(SAMR_Q_ENUM_DOM_ALIASES * q_e,
+                                 POLICY_HND *pol, uint32 start_idx,
+                                 uint32 size)
+{
+       DEBUG(5, ("init_samr_q_enum_dom_aliases\n"));
+
+       q_e->pol = *pol;
+
+       q_e->start_idx = start_idx;
+       q_e->max_size = size;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_aliases(const char *desc, SAMR_Q_ENUM_DOM_ALIASES * q_e,
+                               prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_enum_dom_aliases");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+               return False;
+       if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_aliases(SAMR_R_ENUM_DOM_ALIASES *r_u, uint32 next_idx, uint32 num_sam_entries)
+{
+       DEBUG(5, ("init_samr_r_enum_dom_aliases\n"));
+
+       r_u->next_idx = next_idx;
+
+       if (num_sam_entries != 0) {
+               r_u->ptr_entries1 = 1;
+               r_u->ptr_entries2 = 1;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->num_entries3 = num_sam_entries;
+
+               r_u->num_entries4 = num_sam_entries;
+       } else {
+               r_u->ptr_entries1 = 0;
+               r_u->num_entries2 = num_sam_entries;
+               r_u->ptr_entries2 = 1;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_aliases(const char *desc, SAMR_R_ENUM_DOM_ALIASES * r_u,
+                               prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_enum_dom_aliases");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("next_idx    ", ps, depth, &r_u->next_idx))
+               return False;
+       if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+               return False;
+
+       if (r_u->ptr_entries1 != 0) {
+               if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+                       return False;
+               if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+                       return False;
+               if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+                       return False;
+
+               if (UNMARSHALLING(ps) && (r_u->num_entries2 > 0)) {
+                       r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+                       r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+               }
+
+               if (r_u->num_entries2 != 0 && 
+                   (r_u->sam == NULL || r_u->uni_grp_name == NULL)) {
+                       DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_ALIASES\n"));
+                       r_u->num_entries4 = 0;
+                       r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < r_u->num_entries2; i++) {
+                       if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+                                      r_u->sam[i].hdr_name.buffer, ps,
+                                      depth))
+                               return False;
+               }
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO1 structure.
+********************************************************************/
+
+void init_samr_alias_info1(ALIAS_INFO1 * al1, char *acct_name, uint32 num_member, char *acct_desc)
+{
+       int acct_len_name = acct_name != NULL ? strlen(acct_name) : 0;
+       int acct_len_desc = acct_desc != NULL ? strlen(acct_desc) : 0;
+
+       DEBUG(5, ("init_samr_alias_info1\n"));
+
+       init_uni_hdr(&al1->hdr_acct_name, acct_len_name);
+       init_unistr2(&al1->uni_acct_name, acct_name, acct_len_name);
+
+       al1->num_member=num_member;
+
+       init_uni_hdr(&al1->hdr_acct_desc, acct_len_desc);
+       init_unistr2(&al1->uni_acct_desc, acct_desc, acct_len_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info1(const char *desc, ALIAS_INFO1 * al1,
+                        prs_struct *ps, int depth)
+{
+       if (al1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_alias_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_name", &al1->hdr_acct_name, ps, depth))
+               return False;
+       if(!prs_uint32("num_member", ps, depth, &al1->num_member))
+               return False;
+       if(!smb_io_unihdr("hdr_acct_desc", &al1->hdr_acct_desc, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_name", &al1->uni_acct_name,
+                      al1->hdr_acct_name.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_desc", &al1->uni_acct_desc,
+                      al1->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO3 structure.
+********************************************************************/
+
+void init_samr_alias_info3(ALIAS_INFO3 * al3, char *acct_desc)
+{
+       int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0;
+
+       DEBUG(5, ("init_samr_alias_info3\n"));
+
+       init_uni_hdr(&al3->hdr_acct_desc, acct_len);
+       init_unistr2(&al3->uni_acct_desc, acct_desc, acct_len);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info3(const char *desc, ALIAS_INFO3 * al3,
+                        prs_struct *ps, int depth)
+{
+       if (al3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_alias_info3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_desc", &al3->hdr_acct_desc, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_acct_desc", &al3->uni_acct_desc,
+                      al3->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_alias_info_ctr(const char *desc, ALIAS_INFO_CTR * ctr,
+                        prs_struct *ps, int depth)
+{
+       if (ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_alias_info_ctr");
+       depth++;
+
+       if(!prs_uint16("switch_value1", ps, depth, &ctr->switch_value1))
+               return False;
+       if(!prs_uint16("switch_value2", ps, depth, &ctr->switch_value2))
+               return False;
+
+       switch (ctr->switch_value1) {
+       case 1: 
+               if(!samr_io_alias_info1("alias_info1", &ctr->alias.info1, ps, depth))
+                       return False;
+               break;
+       case 3: 
+               if(!samr_io_alias_info3("alias_info3", &ctr->alias.info3, ps, depth))
+                       return False;
+               break;
+       default:
+               DEBUG(0,("samr_alias_info_ctr: unsupported switch level\n"));
+               break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_query_aliasinfo(SAMR_Q_QUERY_ALIASINFO * q_e,
+                                POLICY_HND *pol, uint16 switch_level)
+{
+       DEBUG(5, ("init_samr_q_query_aliasinfo\n"));
+
+       q_e->pol = *pol;
+       q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasinfo(const char *desc, SAMR_Q_QUERY_ALIASINFO * q_e,
+                              prs_struct *ps, int depth)
+{
+       if (q_e == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_aliasinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_r_query_aliasinfo(SAMR_R_QUERY_ALIASINFO * r_u,
+                                ALIAS_INFO_CTR * ctr, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_aliasinfo\n"));
+
+       r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+       r_u->ctr = *ctr;
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasinfo(const char *desc, SAMR_R_QUERY_ALIASINFO * r_u,
+                              prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_aliasinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if(!samr_alias_info_ctr("ctr", &r_u->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_set_aliasinfo(SAMR_Q_SET_ALIASINFO * q_u,
+                              POLICY_HND *hnd, ALIAS_INFO_CTR * ctr)
+{
+       DEBUG(5, ("init_samr_q_set_aliasinfo\n"));
+
+       q_u->alias_pol = *hnd;
+       q_u->ctr = *ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_aliasinfo(const char *desc, SAMR_Q_SET_ALIASINFO * q_u,
+                            prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_aliasinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+       if(!samr_alias_info_ctr("ctr", &q_u->ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_aliasinfo(const char *desc, SAMR_R_SET_ALIASINFO * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_set_aliasinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_q_query_useraliases(SAMR_Q_QUERY_USERALIASES * q_u,
+                                  POLICY_HND *hnd,
+                                  uint32 num_sids,
+                                  uint32 *ptr_sid, DOM_SID2 * sid)
+{
+       DEBUG(5, ("init_samr_q_query_useraliases\n"));
+
+       q_u->pol = *hnd;
+
+       q_u->num_sids1 = num_sids;
+       q_u->ptr = 1;
+       q_u->num_sids2 = num_sids;
+
+       q_u->ptr_sid = ptr_sid;
+       q_u->sid = sid;
+}
+
+/*******************************************************************
+reads or writes a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+BOOL samr_io_q_query_useraliases(const char *desc, SAMR_Q_QUERY_USERALIASES * q_u,
+                                prs_struct *ps, int depth)
+{
+       fstring tmp;
+       uint32 i;
+
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_useraliases");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("num_sids1", ps, depth, &q_u->num_sids1))
+               return False;
+       if(!prs_uint32("ptr      ", ps, depth, &q_u->ptr))
+               return False;
+
+       if (q_u->ptr==0)
+               return True;
+
+       if(!prs_uint32("num_sids2", ps, depth, &q_u->num_sids2))
+               return False;
+
+       if (UNMARSHALLING(ps) && (q_u->num_sids2 != 0)) {
+               q_u->ptr_sid = (uint32 *)prs_alloc_mem(ps,sizeof(q_u->ptr_sid[0])*q_u->num_sids2);
+               if (q_u->ptr_sid == NULL)
+                       return False;
+
+               q_u->sid = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(q_u->sid[0]) * q_u->num_sids2);
+               if (q_u->sid == NULL)
+                       return False;
+       }
+
+       for (i = 0; i < q_u->num_sids2; i++) {
+               slprintf(tmp, sizeof(tmp) - 1, "ptr[%02d]", i);
+               if(!prs_uint32(tmp, ps, depth, &q_u->ptr_sid[i]))
+                       return False;
+       }
+
+       for (i = 0; i < q_u->num_sids2; i++) {
+               if (q_u->ptr_sid[i] != 0) {
+                       slprintf(tmp, sizeof(tmp) - 1, "sid[%02d]", i);
+                       if(!smb_io_dom_sid2(tmp, &q_u->sid[i], ps, depth))
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_r_query_useraliases(SAMR_R_QUERY_USERALIASES * r_u,
+                                  uint32 num_rids, uint32 *rid,
+                                  NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_useraliases\n"));
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->num_entries = num_rids;
+               r_u->ptr = 1;
+               r_u->num_entries2 = num_rids;
+
+               r_u->rid = rid;
+       } else {
+               r_u->num_entries = 0;
+               r_u->ptr = 0;
+               r_u->num_entries2 = 0;
+       }
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_rids(const char *desc, uint32 *num_rids, uint32 **rid,
+                 prs_struct *ps, int depth)
+{
+       fstring tmp;
+       uint32 i;
+       if (rid == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_rids");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_rids", ps, depth, num_rids))
+               return False;
+
+       if ((*num_rids) != 0) {
+               if (UNMARSHALLING(ps)) {
+                       /* reading */
+                       (*rid) = (uint32 *)prs_alloc_mem(ps,sizeof(uint32)*(*num_rids));
+               }
+               if ((*rid) == NULL)
+                       return False;
+
+               for (i = 0; i < (*num_rids); i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "rid[%02d]", i);
+                       if(!prs_uint32(tmp, ps, depth, &((*rid)[i])))
+                               return False;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_useraliases(const char *desc, SAMR_R_QUERY_USERALIASES * r_u,
+                                prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_useraliases");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries", ps, depth, &r_u->num_entries))
+               return False;
+       if(!prs_uint32("ptr        ", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if(!samr_io_rids("rids", &r_u->num_entries2, &r_u->rid, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_open_alias(SAMR_Q_OPEN_ALIAS * q_u, POLICY_HND *pol,
+                           uint32 access_mask, uint32 rid)
+{
+       DEBUG(5, ("init_samr_q_open_alias\n"));
+
+       q_u->dom_pol = *pol;
+       q_u->access_mask = access_mask;
+       q_u->rid_alias = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_alias(const char *desc, SAMR_Q_OPEN_ALIAS * q_u,
+                         prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_open_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+       if(!prs_uint32("rid_alias", ps, depth, &q_u->rid_alias))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_alias(const char *desc, SAMR_R_OPEN_ALIAS * r_u,
+                         prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_open_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_q_lookup_rids(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_RIDS * q_u,
+                            POLICY_HND *pol, uint32 flags,
+                            uint32 num_rids, uint32 *rid)
+{
+       DEBUG(5, ("init_samr_q_lookup_rids\n"));
+
+       q_u->pol = *pol;
+
+       q_u->num_rids1 = num_rids;
+       q_u->flags = flags;
+       q_u->ptr = 0;
+       q_u->num_rids2 = num_rids;
+       q_u->rid = (uint32 *)talloc_zero(ctx, num_rids * sizeof(q_u->rid[0]));
+       if (q_u->rid == NULL) {
+               q_u->num_rids1 = 0;
+               q_u->num_rids2 = 0;
+       } else {
+               memcpy(q_u->rid, rid, num_rids * sizeof(q_u->rid[0]));
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_rids(const char *desc, SAMR_Q_LOOKUP_RIDS * q_u,
+                          prs_struct *ps, int depth)
+{
+       uint32 i;
+       fstring tmp;
+
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_lookup_rids");
+       depth++;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(q_u);
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("num_rids1", ps, depth, &q_u->num_rids1))
+               return False;
+       if(!prs_uint32("flags    ", ps, depth, &q_u->flags))
+               return False;
+       if(!prs_uint32("ptr      ", ps, depth, &q_u->ptr))
+               return False;
+       if(!prs_uint32("num_rids2", ps, depth, &q_u->num_rids2))
+               return False;
+
+       if (UNMARSHALLING(ps) && (q_u->num_rids2 != 0)) {
+               q_u->rid = (uint32 *)prs_alloc_mem(ps, sizeof(q_u->rid[0])*q_u->num_rids2);
+               if (q_u->rid == NULL)
+                       return False;
+       }
+
+       for (i = 0; i < q_u->num_rids2; i++) {
+               slprintf(tmp, sizeof(tmp) - 1, "rid[%02d]  ", i);
+               if(!prs_uint32(tmp, ps, depth, &q_u->rid[i]))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_r_lookup_rids(SAMR_R_LOOKUP_RIDS * r_u,
+                            uint32 num_names, UNIHDR * hdr_name,
+                            UNISTR2 *uni_name, uint32 *type)
+{
+       DEBUG(5, ("init_samr_r_lookup_rids\n"));
+
+       r_u->hdr_name = NULL;
+       r_u->uni_name = NULL;
+       r_u->type = NULL;
+
+       if (num_names != 0) {
+               r_u->num_names1 = num_names;
+               r_u->ptr_names = 1;
+               r_u->num_names2 = num_names;
+
+               r_u->num_types1 = num_names;
+               r_u->ptr_types = 1;
+               r_u->num_types2 = num_names;
+
+               r_u->hdr_name = hdr_name;
+               r_u->uni_name = uni_name;
+               r_u->type = type;
+       } else {
+               r_u->num_names1 = num_names;
+               r_u->ptr_names = 0;
+               r_u->num_names2 = num_names;
+
+               r_u->num_types1 = num_names;
+               r_u->ptr_types = 0;
+               r_u->num_types2 = num_names;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_rids(const char *desc, SAMR_R_LOOKUP_RIDS * r_u,
+                          prs_struct *ps, int depth)
+{
+       uint32 i;
+       fstring tmp;
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_lookup_rids");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_names1", ps, depth, &r_u->num_names1))
+               return False;
+       if(!prs_uint32("ptr_names ", ps, depth, &r_u->ptr_names))
+               return False;
+
+       if (r_u->ptr_names != 0) {
+
+               if(!prs_uint32("num_names2", ps, depth, &r_u->num_names2))
+                       return False;
+
+
+               if (UNMARSHALLING(ps) && (r_u->num_names2 != 0)) {
+                       r_u->hdr_name = (UNIHDR *) prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->hdr_name[0]));
+                       if (r_u->hdr_name == NULL)
+                               return False;
+
+                       r_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->uni_name[0]));
+                       if (r_u->uni_name == NULL)
+                               return False;
+               }
+               
+               for (i = 0; i < r_u->num_names2; i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "hdr[%02d]  ", i);
+                       if(!smb_io_unihdr("", &r_u->hdr_name[i], ps, depth))
+                               return False;
+               }
+               for (i = 0; i < r_u->num_names2; i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "str[%02d]  ", i);
+                       if(!smb_io_unistr2("", &r_u->uni_name[i], r_u->hdr_name[i].buffer, ps, depth))
+                               return False;
+               }
+
+       }
+       
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+               return False;
+       if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+               return False;
+
+       if (r_u->ptr_types != 0) {
+
+               if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+                       return False;
+
+               if (UNMARSHALLING(ps) && (r_u->num_types2 != 0)) {
+                       r_u->type = (uint32 *)prs_alloc_mem(ps, r_u->num_types2 * sizeof(r_u->type[0]));
+                       if (r_u->type == NULL)
+                               return False;
+               }
+
+               for (i = 0; i < r_u->num_types2; i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "type[%02d]  ", i);
+                       if(!prs_uint32(tmp, ps, depth, &r_u->type[i]))
+                               return False;
+               }
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_alias(SAMR_Q_DELETE_DOM_ALIAS * q_u, POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_delete_alias\n"));
+
+       q_u->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+                           prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_delete_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_delete_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_create_dom_alias(SAMR_Q_CREATE_DOM_ALIAS * q_u,
+                                 POLICY_HND *hnd, char *acct_desc)
+{
+       int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0;
+
+       DEBUG(5, ("init_samr_q_create_dom_alias\n"));
+
+       q_u->dom_pol = *hnd;
+
+       init_uni_hdr(&q_u->hdr_acct_desc, acct_len);
+       init_unistr2(&q_u->uni_acct_desc, acct_desc, acct_len);
+
+       q_u->access_mask = 0x001f000f;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_alias(const char *desc, SAMR_Q_CREATE_DOM_ALIAS * q_u,
+                               prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_create_dom_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("dom_pol", &q_u->dom_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_acct_desc", &q_u->hdr_acct_desc, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_acct_desc", &q_u->uni_acct_desc,
+                      q_u->hdr_acct_desc.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_alias(const char *desc, SAMR_R_CREATE_DOM_ALIAS * r_u,
+                               prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_create_dom_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &r_u->alias_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("rid", ps, depth, &r_u->rid))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_add_aliasmem(SAMR_Q_ADD_ALIASMEM * q_u, POLICY_HND *hnd,
+                             DOM_SID *sid)
+{
+       DEBUG(5, ("init_samr_q_add_aliasmem\n"));
+
+       q_u->alias_pol = *hnd;
+       init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_aliasmem(const char *desc, SAMR_Q_ADD_ALIASMEM * q_u,
+                           prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_add_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+       if(!smb_io_dom_sid2("sid      ", &q_u->sid, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_aliasmem(const char *desc, SAMR_R_ADD_ALIASMEM * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_add_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_del_aliasmem(SAMR_Q_DEL_ALIASMEM * q_u, POLICY_HND *hnd,
+                             DOM_SID *sid)
+{
+       DEBUG(5, ("init_samr_q_del_aliasmem\n"));
+
+       q_u->alias_pol = *hnd;
+       init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_aliasmem(const char *desc, SAMR_Q_DEL_ALIASMEM * q_u,
+                           prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_del_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+       if(!smb_io_dom_sid2("sid      ", &q_u->sid, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_aliasmem(const char *desc, SAMR_R_DEL_ALIASMEM * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_del_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_alias(SAMR_Q_DELETE_DOM_ALIAS * q_c,
+                                 POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_delete_dom_alias\n"));
+
+       q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+                               prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_delete_dom_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_r_delete_dom_alias(SAMR_R_DELETE_DOM_ALIAS * r_u,
+                                 NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_delete_dom_alias\n"));
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+                               prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_delete_dom_alias");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_query_aliasmem(SAMR_Q_QUERY_ALIASMEM * q_c,
+                               POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_query_aliasmem\n"));
+
+       q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasmem(const char *desc, SAMR_Q_QUERY_ALIASMEM * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_r_query_aliasmem(SAMR_R_QUERY_ALIASMEM * r_u,
+                               uint32 num_sids, DOM_SID2 * sid,
+                               NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_aliasmem\n"));
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->num_sids = num_sids;
+               r_u->ptr = (num_sids != 0) ? 1 : 0;
+               r_u->num_sids1 = num_sids;
+
+               r_u->sid = sid;
+       } else {
+               r_u->ptr = 0;
+               r_u->num_sids = 0;
+       }
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasmem(const char *desc, SAMR_R_QUERY_ALIASMEM * r_u,
+                             prs_struct *ps, int depth)
+{
+       uint32 i;
+       uint32 ptr_sid[MAX_LOOKUP_SIDS];
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_aliasmem");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_sids ", ps, depth, &r_u->num_sids))
+               return False;
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               SMB_ASSERT_ARRAY(ptr_sid, r_u->num_sids);
+
+               if (r_u->num_sids != 0) {
+                       if(!prs_uint32("num_sids1", ps, depth, &r_u->num_sids1))
+                               return False;
+
+                       for (i = 0; i < r_u->num_sids1; i++) {
+                               ptr_sid[i] = 1;
+                               if(!prs_uint32("", ps, depth, &ptr_sid[i]))
+                                 return False;
+                       }
+
+                       for (i = 0; i < r_u->num_sids1; i++) {
+                               if (ptr_sid[i] != 0) {
+                                       if(!smb_io_dom_sid2("", &r_u->sid[i], ps, depth))
+                                               return False;
+                               }
+                       }
+               }
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_q_lookup_names(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_NAMES * q_u,
+                             POLICY_HND *pol, uint32 flags,
+                             uint32 num_names, const char **name)
+{
+       uint32 i;
+
+       DEBUG(5, ("init_samr_q_lookup_names\n"));
+
+       q_u->pol = *pol;
+
+       q_u->num_names1 = num_names;
+       q_u->flags = flags;
+       q_u->ptr = 0;
+       q_u->num_names2 = num_names;
+
+       if (!(q_u->hdr_name = (UNIHDR *)talloc_zero(ctx, num_names * sizeof(UNIHDR))))
+               return NT_STATUS_NO_MEMORY;
+
+       if (!(q_u->uni_name = (UNISTR2 *)talloc_zero(ctx, num_names * sizeof(UNISTR2))))
+               return NT_STATUS_NO_MEMORY;
+
+       for (i = 0; i < num_names; i++) {
+               int len_name = name[i] != NULL ? strlen(name[i]) : 0;
+               init_uni_hdr(&q_u->hdr_name[i], len_name);      /* unicode header for user_name */
+               init_unistr2(&q_u->uni_name[i], name[i], len_name);     /* unicode string for machine account */
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_names(const char *desc, SAMR_Q_LOOKUP_NAMES * q_u,
+                           prs_struct *ps, int depth)
+{
+       uint32 i;
+
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_lookup_names");
+       depth++;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(q_u);
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("num_names1", ps, depth, &q_u->num_names1))
+               return False;
+       if(!prs_uint32("flags     ", ps, depth, &q_u->flags))
+               return False;
+       if(!prs_uint32("ptr       ", ps, depth, &q_u->ptr))
+               return False;
+       if(!prs_uint32("num_names2", ps, depth, &q_u->num_names2))
+               return False;
+
+       if (UNMARSHALLING(ps) && (q_u->num_names2 != 0)) {
+               q_u->hdr_name = (UNIHDR *)prs_alloc_mem(ps, sizeof(UNIHDR) *
+                                                       q_u->num_names2);
+               q_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, sizeof(UNISTR2) *
+                                                        q_u->num_names2);
+               if (!q_u->hdr_name || !q_u->uni_name)
+                       return False;
+       }
+
+       for (i = 0; i < q_u->num_names2; i++) {
+               if(!smb_io_unihdr("", &q_u->hdr_name[i], ps, depth))
+                       return False;
+       }
+
+       for (i = 0; i < q_u->num_names2; i++) {
+               if(!smb_io_unistr2("", &q_u->uni_name[i], q_u->hdr_name[i].buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_r_lookup_names(TALLOC_CTX *ctx, SAMR_R_LOOKUP_NAMES * r_u,
+                             uint32 num_rids,
+                             uint32 *rid, uint32 *type,
+                             NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_lookup_names\n"));
+
+       if (NT_STATUS_IS_OK(status) && (num_rids != 0)) {
+               uint32 i;
+
+               r_u->num_types1 = num_rids;
+               r_u->ptr_types = 1;
+               r_u->num_types2 = num_rids;
+
+               r_u->num_rids1 = num_rids;
+               r_u->ptr_rids = 1;
+               r_u->num_rids2 = num_rids;
+
+               if (!(r_u->rids = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+                       return NT_STATUS_NO_MEMORY;
+               if (!(r_u->types = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+                       return NT_STATUS_NO_MEMORY;
+
+               if (!r_u->rids || !r_u->types)
+                       goto empty;
+
+               for (i = 0; i < num_rids; i++) {
+                       r_u->rids[i] = rid[i];
+                       r_u->types[i] = type[i];
+               }
+       } else {
+
+  empty:
+               r_u->num_types1 = 0;
+               r_u->ptr_types = 0;
+               r_u->num_types2 = 0;
+
+               r_u->num_rids1 = 0;
+               r_u->ptr_rids = 0;
+               r_u->num_rids2 = 0;
+
+               r_u->rids = NULL;
+               r_u->types = NULL;
+       }
+
+       r_u->status = status;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_names(const char *desc, SAMR_R_LOOKUP_NAMES * r_u,
+                           prs_struct *ps, int depth)
+{
+       uint32 i;
+       fstring tmp;
+
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_lookup_names");
+       depth++;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(r_u);
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_rids1", ps, depth, &r_u->num_rids1))
+               return False;
+       if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+               return False;
+
+       if (r_u->ptr_rids != 0) {
+               if(!prs_uint32("num_rids2", ps, depth, &r_u->num_rids2))
+                       return False;
+
+               if (r_u->num_rids2 != r_u->num_rids1) {
+                       /* RPC fault */
+                       return False;
+               }
+
+               if (UNMARSHALLING(ps))
+                       r_u->rids = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_rids2);
+
+               if (!r_u->rids) {
+                       DEBUG(0, ("NULL rids in samr_io_r_lookup_names\n"));
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_rids2; i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "rid[%02d]  ", i);
+                       if(!prs_uint32(tmp, ps, depth, &r_u->rids[i]))
+                               return False;
+               }
+       }
+
+       if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+               return False;
+       if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+               return False;
+
+       if (r_u->ptr_types != 0) {
+               if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+                       return False;
+
+               if (r_u->num_types2 != r_u->num_types1) {
+                       /* RPC fault */
+                       return False;
+               }
+
+               if (UNMARSHALLING(ps))
+                       r_u->types = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_types2);
+
+               if (!r_u->types) {
+                       DEBUG(0, ("NULL types in samr_io_r_lookup_names\n"));
+                       return False;
+               }
+
+               for (i = 0; i < r_u->num_types2; i++) {
+                       slprintf(tmp, sizeof(tmp) - 1, "type[%02d]  ", i);
+                       if(!prs_uint32(tmp, ps, depth, &r_u->types[i]))
+                               return False;
+               }
+       }
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_USER structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_user(SAMR_Q_DELETE_DOM_USER * q_c,
+                                POLICY_HND *hnd)
+{
+       DEBUG(5, ("init_samr_q_delete_dom_user\n"));
+
+       q_c->user_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_user(const char *desc, SAMR_Q_DELETE_DOM_USER * q_u,
+                              prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_delete_dom_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_user(const char *desc, SAMR_R_DELETE_DOM_USER * r_u,
+                              prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_delete_dom_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_user(SAMR_Q_OPEN_USER * q_u,
+                          POLICY_HND *pol,
+                          uint32 access_mask, uint32 rid)
+{
+       DEBUG(5, ("samr_init_samr_q_open_user\n"));
+
+       q_u->domain_pol = *pol;
+       q_u->access_mask = access_mask;
+       q_u->user_rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_user(const char *desc, SAMR_Q_OPEN_USER * q_u,
+                        prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_open_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+       if(!prs_uint32("user_rid ", ps, depth, &q_u->user_rid))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_user(const char *desc, SAMR_R_OPEN_USER * r_u,
+                        prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_open_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_create_user(SAMR_Q_CREATE_USER * q_u,
+                            POLICY_HND *pol,
+                            const char *name,
+                            uint32 acb_info, uint32 access_mask)
+{
+       int len_name;
+       len_name = strlen(name);
+
+       DEBUG(5, ("samr_init_samr_q_create_user\n"));
+
+       q_u->domain_pol = *pol;
+
+       init_uni_hdr(&q_u->hdr_name, len_name);
+       init_unistr2(&q_u->uni_name, name, len_name);
+
+       q_u->acb_info = acb_info;
+       q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_user(const char *desc, SAMR_Q_CREATE_USER * q_u,
+                          prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_create_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_name", &q_u->hdr_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_name", &q_u->uni_name, q_u->hdr_name.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("acb_info   ", ps, depth, &q_u->acb_info))
+               return False;
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_user(const char *desc, SAMR_R_CREATE_USER * r_u,
+                          prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_create_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+               return False;
+
+       if(!prs_uint32("access_granted", ps, depth, &r_u->access_granted))
+               return False;
+       if(!prs_uint32("user_rid ", ps, depth, &r_u->user_rid))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_query_userinfo(SAMR_Q_QUERY_USERINFO * q_u,
+                               POLICY_HND *hnd, uint16 switch_value)
+{
+       DEBUG(5, ("init_samr_q_query_userinfo\n"));
+
+       q_u->pol = *hnd;
+       q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_userinfo(const char *desc, SAMR_Q_QUERY_USERINFO * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_query_userinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value)) /* 0x0015 or 0x0011 */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a LOGON_HRS structure.
+********************************************************************/
+
+static BOOL sam_io_logon_hrs(const char *desc, LOGON_HRS * hrs,
+                            prs_struct *ps, int depth)
+{
+       if (hrs == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_logon_hrs");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("len  ", ps, depth, &hrs->len))
+               return False;
+
+       if (hrs->len > sizeof(hrs->hours)) {
+               DEBUG(3, ("sam_io_logon_hrs: truncating length from %d\n", hrs->len));
+               hrs->len = sizeof(hrs->hours);
+       }
+
+       if(!prs_uint8s(False, "hours", ps, depth, hrs->hours, hrs->len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_12 structure.
+********************************************************************/
+
+void init_sam_user_info12(SAM_USER_INFO_12 * usr,
+                         const uint8 lm_pwd[16], const uint8 nt_pwd[16])
+{
+       DEBUG(5, ("init_sam_user_info12\n"));
+
+       usr->lm_pwd_active =
+               memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd)) ? 1 : 0;
+       usr->nt_pwd_active =
+               memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd)) ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info12(const char *desc, SAM_USER_INFO_12 * u,
+                       prs_struct *ps, int depth)
+{
+       if (u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_user_info12");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint8s(False, "lm_pwd", ps, depth, u->lm_pwd, sizeof(u->lm_pwd)))
+               return False;
+       if(!prs_uint8s(False, "nt_pwd", ps, depth, u->nt_pwd, sizeof(u->nt_pwd)))
+               return False;
+
+       if(!prs_uint8("lm_pwd_active", ps, depth, &u->lm_pwd_active))
+               return False;
+       if(!prs_uint8("nt_pwd_active", ps, depth, &u->nt_pwd_active))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_10 structure.
+********************************************************************/
+
+void init_sam_user_info10(SAM_USER_INFO_10 * usr, uint32 acb_info)
+{
+       DEBUG(5, ("init_sam_user_info10\n"));
+
+       usr->acb_info = acb_info;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info10(const char *desc, SAM_USER_INFO_10 * usr,
+                       prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_user_info10");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("acb_info", ps, depth, &usr->acb_info))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_11 structure.
+********************************************************************/
+
+void init_sam_user_info11(SAM_USER_INFO_11 * usr,
+                         NTTIME * expiry,
+                         char *mach_acct,
+                         uint32 rid_user, uint32 rid_group, uint16 acct_ctrl)
+{
+       int len_mach_acct;
+
+       DEBUG(5, ("init_sam_user_info11\n"));
+
+       len_mach_acct = strlen(mach_acct);
+
+       memcpy(&(usr->expiry), expiry, sizeof(usr->expiry));    /* expiry time or something? */
+       ZERO_STRUCT(usr->padding_1);    /* 0 - padding 24 bytes */
+
+       init_uni_hdr(&usr->hdr_mach_acct, len_mach_acct);       /* unicode header for machine account */
+       usr->padding_2 = 0;     /* 0 - padding 4 bytes */
+
+       usr->ptr_1 = 1;         /* pointer */
+       ZERO_STRUCT(usr->padding_3);    /* 0 - padding 32 bytes */
+       usr->padding_4 = 0;     /* 0 - padding 4 bytes */
+
+       usr->ptr_2 = 1;         /* pointer */
+       usr->padding_5 = 0;     /* 0 - padding 4 bytes */
+
+       usr->ptr_3 = 1;         /* pointer */
+       ZERO_STRUCT(usr->padding_6);    /* 0 - padding 32 bytes */
+
+       usr->rid_user = rid_user;
+       usr->rid_group = rid_group;
+
+       usr->acct_ctrl = acct_ctrl;
+       usr->unknown_3 = 0x0000;
+
+       usr->unknown_4 = 0x003f;        /* 0x003f      - 16 bit unknown */
+       usr->unknown_5 = 0x003c;        /* 0x003c      - 16 bit unknown */
+
+       ZERO_STRUCT(usr->padding_7);    /* 0 - padding 16 bytes */
+       usr->padding_8 = 0;     /* 0 - padding 4 bytes */
+
+       init_unistr2(&usr->uni_mach_acct, mach_acct, len_mach_acct);    /* unicode string for machine account */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info11(const char *desc, SAM_USER_INFO_11 * usr,
+                       prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_unknown_11");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint8s(False, "padding_0", ps, depth, usr->padding_0, sizeof(usr->padding_0)))
+               return False;
+
+       if(!smb_io_time("time", &usr->expiry, ps, depth))
+               return False;
+
+       if(!prs_uint8s(False, "padding_1", ps, depth, usr->padding_1, sizeof(usr->padding_1)))
+               return False;
+
+       if(!smb_io_unihdr("unihdr", &usr->hdr_mach_acct, ps, depth))
+               return False;
+
+       if(!prs_uint32("padding_2", ps, depth, &usr->padding_2))
+               return False;
+
+       if(!prs_uint32("ptr_1    ", ps, depth, &usr->ptr_1))
+               return False;
+       if(!prs_uint8s(False, "padding_3", ps, depth, usr->padding_3, sizeof(usr->padding_3)))
+               return False;
+
+       if(!prs_uint32("padding_4", ps, depth, &usr->padding_4))
+               return False;
+
+       if(!prs_uint32("ptr_2    ", ps, depth, &usr->ptr_2))
+               return False;
+       if(!prs_uint32("padding_5", ps, depth, &usr->padding_5))
+               return False;
+
+       if(!prs_uint32("ptr_3    ", ps, depth, &usr->ptr_3))
+               return False;
+       if(!prs_uint8s(False, "padding_6", ps, depth, usr->padding_6,sizeof(usr->padding_6)))
+               return False;
+
+       if(!prs_uint32("rid_user ", ps, depth, &usr->rid_user))
+               return False;
+       if(!prs_uint32("rid_group", ps, depth, &usr->rid_group))
+               return False;
+       if(!prs_uint16("acct_ctrl", ps, depth, &usr->acct_ctrl))
+               return False;
+       if(!prs_uint16("unknown_3", ps, depth, &usr->unknown_3))
+               return False;
+       if(!prs_uint16("unknown_4", ps, depth, &usr->unknown_4))
+               return False;
+       if(!prs_uint16("unknown_5", ps, depth, &usr->unknown_5))
+               return False;
+
+       if(!prs_uint8s(False, "padding_7", ps, depth, usr->padding_7, sizeof(usr->padding_7)))
+               return False;
+
+       if(!prs_uint32("padding_8", ps, depth, &(usr->padding_8)))
+               return False;
+
+       if(!smb_io_unistr2("unistr2", &usr->uni_mach_acct, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint8s(False, "padding_9", ps, depth, usr->padding_9, sizeof(usr->padding_9)))
+               return False;
+
+       return True;
+}
+
+/*************************************************************************
+ init_sam_user_infoa
+
+ unknown_3 = 0x09f8 27fa
+ unknown_5 = 0x0001 0000
+ unknown_6 = 0x0000 04ec 
+
+ *************************************************************************/
+
+void init_sam_user_info24(SAM_USER_INFO_24 * usr, char newpass[516], uint16 pw_len)
+{
+       DEBUG(10, ("init_sam_user_info24:\n"));
+       memcpy(usr->pass, newpass, sizeof(usr->pass));
+       usr->pw_len = pw_len;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info24(const char *desc, SAM_USER_INFO_24 * usr,
+                              prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_user_info24");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint8s(False, "password", ps, depth, usr->pass, 
+                      sizeof(usr->pass)))
+               return False;
+       
+       if (MARSHALLING(ps) && (usr->pw_len != 0)) {
+               if (!prs_uint16("pw_len", ps, depth, &usr->pw_len))
+                       return False;
+       }
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_3 = 0x09f8 27fa
+ unknown_5 = 0x0001 0000
+ unknown_6 = 0x0000 04ec 
+
+ *************************************************************************/
+
+void init_sam_user_info23W(SAM_USER_INFO_23 * usr, NTTIME * logon_time,        /* all zeros */
+                       NTTIME * logoff_time,   /* all zeros */
+                       NTTIME * kickoff_time,  /* all zeros */
+                       NTTIME * pass_last_set_time,    /* all zeros */
+                       NTTIME * pass_can_change_time,  /* all zeros */
+                       NTTIME * pass_must_change_time, /* all zeros */
+                       UNISTR2 *user_name,
+                       UNISTR2 *full_name,
+                       UNISTR2 *home_dir,
+                       UNISTR2 *dir_drive,
+                       UNISTR2 *log_scr,
+                       UNISTR2 *prof_path,
+                       UNISTR2 *desc,
+                       UNISTR2 *wkstas,
+                       UNISTR2 *unk_str,
+                       UNISTR2 *mung_dial,
+                       uint32 user_rid,        /* 0x0000 0000 */
+                       uint32 group_rid,
+                       uint32 acb_info,
+                       uint32 unknown_3,
+                       uint16 logon_divs,
+                       LOGON_HRS * hrs,
+                       uint32 unknown_5,
+                       char newpass[516], uint32 unknown_6)
+{
+       int len_user_name = user_name != NULL ? user_name->uni_str_len : 0;
+       int len_full_name = full_name != NULL ? full_name->uni_str_len : 0;
+       int len_home_dir = home_dir != NULL ? home_dir->uni_str_len : 0;
+       int len_dir_drive = dir_drive != NULL ? dir_drive->uni_str_len : 0;
+       int len_logon_script = log_scr != NULL ? log_scr->uni_str_len : 0;
+       int len_profile_path = prof_path != NULL ? prof_path->uni_str_len : 0;
+       int len_description = desc != NULL ? desc->uni_str_len : 0;
+       int len_workstations = wkstas != NULL ? wkstas->uni_str_len : 0;
+       int len_unknown_str = unk_str != NULL ? unk_str->uni_str_len : 0;
+       int len_munged_dial = mung_dial != NULL ? mung_dial->uni_str_len : 0;
+
+       usr->logon_time = *logon_time;  /* all zeros */
+       usr->logoff_time = *logoff_time;        /* all zeros */
+       usr->kickoff_time = *kickoff_time;      /* all zeros */
+       usr->pass_last_set_time = *pass_last_set_time;  /* all zeros */
+       usr->pass_can_change_time = *pass_can_change_time;      /* all zeros */
+       usr->pass_must_change_time = *pass_must_change_time;    /* all zeros */
+
+       init_uni_hdr(&usr->hdr_user_name, len_user_name);       /* NULL */
+       init_uni_hdr(&usr->hdr_full_name, len_full_name);
+       init_uni_hdr(&usr->hdr_home_dir, len_home_dir);
+       init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive);
+       init_uni_hdr(&usr->hdr_logon_script, len_logon_script);
+       init_uni_hdr(&usr->hdr_profile_path, len_profile_path);
+       init_uni_hdr(&usr->hdr_acct_desc, len_description);
+       init_uni_hdr(&usr->hdr_workstations, len_workstations);
+       init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str);
+       init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial);
+
+       ZERO_STRUCT(usr->nt_pwd);
+       ZERO_STRUCT(usr->lm_pwd);
+
+       usr->user_rid = user_rid;       /* 0x0000 0000 */
+       usr->group_rid = group_rid;
+       usr->acb_info = acb_info;
+       usr->unknown_3 = unknown_3;     /* 09f8 27fa */
+
+       usr->logon_divs = logon_divs;   /* should be 168 (hours/week) */
+       usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+       if (nt_time_is_zero(pass_must_change_time)) {
+               usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+       } else {
+               usr->passmustchange=0;
+       }
+
+
+       ZERO_STRUCT(usr->padding1);
+       ZERO_STRUCT(usr->padding2);
+
+       usr->unknown_5 = unknown_5;     /* 0x0001 0000 */
+
+       memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+       copy_unistr2(&usr->uni_user_name, user_name);
+       copy_unistr2(&usr->uni_full_name, full_name);
+       copy_unistr2(&usr->uni_home_dir, home_dir);
+       copy_unistr2(&usr->uni_dir_drive, dir_drive);
+       copy_unistr2(&usr->uni_logon_script, log_scr);
+       copy_unistr2(&usr->uni_profile_path, prof_path);
+       copy_unistr2(&usr->uni_acct_desc, desc);
+       copy_unistr2(&usr->uni_workstations, wkstas);
+       copy_unistr2(&usr->uni_unknown_str, unk_str);
+       copy_unistr2(&usr->uni_munged_dial, mung_dial);
+
+       usr->unknown_6 = unknown_6;     /* 0x0000 04ec */
+       usr->padding4 = 0;
+
+       memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_3 = 0x09f8 27fa
+ unknown_5 = 0x0001 0000
+ unknown_6 = 0x0000 04ec 
+
+ *************************************************************************/
+
+void init_sam_user_info23A(SAM_USER_INFO_23 * usr, NTTIME * logon_time,        /* all zeros */
+                          NTTIME * logoff_time,        /* all zeros */
+                          NTTIME * kickoff_time,       /* all zeros */
+                          NTTIME * pass_last_set_time, /* all zeros */
+                          NTTIME * pass_can_change_time,       /* all zeros */
+                          NTTIME * pass_must_change_time,      /* all zeros */
+                          char *user_name,     /* NULL */
+                          char *full_name,
+                          char *home_dir, char *dir_drive, char *log_scr,
+                          char *prof_path, const char *desc, char *wkstas,
+                          char *unk_str, char *mung_dial, uint32 user_rid,     /* 0x0000 0000 */
+                          uint32 group_rid, uint32 acb_info,
+                          uint32 unknown_3, uint16 logon_divs,
+                          LOGON_HRS * hrs, uint32 unknown_5,
+                          char newpass[516], uint32 unknown_6)
+{
+       int len_user_name = user_name != NULL ? strlen(user_name) : 0;
+       int len_full_name = full_name != NULL ? strlen(full_name) : 0;
+       int len_home_dir = home_dir != NULL ? strlen(home_dir) : 0;
+       int len_dir_drive = dir_drive != NULL ? strlen(dir_drive) : 0;
+       int len_logon_script = log_scr != NULL ? strlen(log_scr) : 0;
+       int len_profile_path = prof_path != NULL ? strlen(prof_path) : 0;
+       int len_description = desc != NULL ? strlen(desc) : 0;
+       int len_workstations = wkstas != NULL ? strlen(wkstas) : 0;
+       int len_unknown_str = unk_str != NULL ? strlen(unk_str) : 0;
+       int len_munged_dial = mung_dial != NULL ? strlen(mung_dial) : 0;
+
+       usr->logon_time = *logon_time;  /* all zeros */
+       usr->logoff_time = *logoff_time;        /* all zeros */
+       usr->kickoff_time = *kickoff_time;      /* all zeros */
+       usr->pass_last_set_time = *pass_last_set_time;  /* all zeros */
+       usr->pass_can_change_time = *pass_can_change_time;      /* all zeros */
+       usr->pass_must_change_time = *pass_must_change_time;    /* all zeros */
+
+       init_uni_hdr(&usr->hdr_user_name, len_user_name);       /* NULL */
+       init_uni_hdr(&usr->hdr_full_name, len_full_name);
+       init_uni_hdr(&usr->hdr_home_dir, len_home_dir);
+       init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive);
+       init_uni_hdr(&usr->hdr_logon_script, len_logon_script);
+       init_uni_hdr(&usr->hdr_profile_path, len_profile_path);
+       init_uni_hdr(&usr->hdr_acct_desc, len_description);
+       init_uni_hdr(&usr->hdr_workstations, len_workstations);
+       init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str);
+       init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial);
+
+       ZERO_STRUCT(usr->nt_pwd);
+       ZERO_STRUCT(usr->lm_pwd);
+
+       usr->user_rid = user_rid;       /* 0x0000 0000 */
+       usr->group_rid = group_rid;
+       usr->acb_info = acb_info;
+       usr->unknown_3 = unknown_3;     /* 09f8 27fa */
+
+       usr->logon_divs = logon_divs;   /* should be 168 (hours/week) */
+       usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+       if (nt_time_is_zero(pass_must_change_time)) {
+               usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+       } else {
+               usr->passmustchange=0;
+       }
+
+       ZERO_STRUCT(usr->padding1);
+       ZERO_STRUCT(usr->padding2);
+
+       usr->unknown_5 = unknown_5;     /* 0x0001 0000 */
+
+       memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+       init_unistr2(&usr->uni_user_name, user_name, len_user_name);    /* NULL */
+       init_unistr2(&usr->uni_full_name, full_name, len_full_name);
+       init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir);
+       init_unistr2(&usr->uni_dir_drive, dir_drive, len_dir_drive);
+       init_unistr2(&usr->uni_logon_script, log_scr, len_logon_script);
+       init_unistr2(&usr->uni_profile_path, prof_path, len_profile_path);
+       init_unistr2(&usr->uni_acct_desc, desc, len_description);
+       init_unistr2(&usr->uni_workstations, wkstas, len_workstations);
+       init_unistr2(&usr->uni_unknown_str, unk_str, len_unknown_str);
+       init_unistr2(&usr->uni_munged_dial, mung_dial, len_munged_dial);
+
+       usr->unknown_6 = unknown_6;     /* 0x0000 04ec */
+       usr->padding4 = 0;
+
+       memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info23(const char *desc, SAM_USER_INFO_23 * usr,
+                              prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_user_info23");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_time("logon_time           ", &usr->logon_time, ps, depth))
+               return False;
+       if(!smb_io_time("logoff_time          ", &usr->logoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("kickoff_time         ", &usr->kickoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_last_set_time   ", &usr->pass_last_set_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_user_name   ", &usr->hdr_user_name, ps, depth))  /* username unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_full_name   ", &usr->hdr_full_name, ps, depth))  /* user's full name unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_home_dir    ", &usr->hdr_home_dir, ps, depth))   /* home directory unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_dir_drive   ", &usr->hdr_dir_drive, ps, depth))  /* home directory drive */
+               return False;
+       if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth))       /* logon script unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth))       /* profile path unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_acct_desc   ", &usr->hdr_acct_desc, ps, depth))  /* account desc */
+               return False;
+       if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth))       /* wkstas user can log on from */
+               return False;
+       if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth))        /* unknown string */
+               return False;
+       if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth))        /* wkstas user can log on from */
+               return False;
+
+       if(!prs_uint8s(False, "lm_pwd        ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+               return False;
+       if(!prs_uint8s(False, "nt_pwd        ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+               return False;
+
+       if(!prs_uint32("user_rid      ", ps, depth, &usr->user_rid))    /* User ID */
+               return False;
+       if(!prs_uint32("group_rid     ", ps, depth, &usr->group_rid))   /* Group ID */
+               return False;
+       if(!prs_uint32("acb_info      ", ps, depth, &usr->acb_info))
+               return False;
+
+       if(!prs_uint32("unknown_3     ", ps, depth, &usr->unknown_3))
+               return False;
+       if(!prs_uint16("logon_divs    ", ps, depth, &usr->logon_divs))  /* logon divisions per week */
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+               return False;
+
+       if(!prs_uint32("unknown_5     ", ps, depth, &usr->unknown_5))
+               return False;
+
+       if(!prs_uint8s(False, "padding1      ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+               return False;
+       if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+               return False;
+       if(!prs_uint8("padding2       ", ps, depth, &usr->padding2))
+               return False;
+
+
+       if(!prs_uint8s(False, "password      ", ps, depth, usr->pass, sizeof(usr->pass)))
+               return False;
+
+       /* here begins pointed-to data */
+
+       if(!smb_io_unistr2("uni_user_name   ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth))      /* username unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_full_name   ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth))      /* user's full name unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_home_dir    ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth))        /* home directory unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_dir_drive   ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth))      /* home directory drive unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth))        /* logon script unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth))        /* profile path unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_desc   ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth))      /* user desc unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth))        /* worksations user can log on from */
+               return False;
+
+       if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth))  /* unknown string */
+               return False;
+
+       if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+               return False;
+
+       /* ok, this is only guess-work (as usual) */
+       if (usr->ptr_logon_hrs) {
+               if(!prs_uint32("unknown_6     ", ps, depth, &usr->unknown_6))
+                       return False;
+               if(!prs_uint32("padding4      ", ps, depth, &usr->padding4))
+                       return False;
+               if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+                       return False;
+       } else if (UNMARSHALLING(ps)) {
+               usr->unknown_6 = 0;
+               usr->padding4 = 0;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ reads or writes a structure.
+ NB. This structure is *definately* incorrect. It's my best guess
+ currently for W2K SP2. The password field is encrypted in a different
+ way than normal... And there are definately other problems. JRA.
+********************************************************************/
+
+static BOOL sam_io_user_info25(const char *desc, SAM_USER_INFO_25 * usr, prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_user_info25");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_time("logon_time           ", &usr->logon_time, ps, depth))
+               return False;
+       if(!smb_io_time("logoff_time          ", &usr->logoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("kickoff_time         ", &usr->kickoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_last_set_time   ", &usr->pass_last_set_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_user_name   ", &usr->hdr_user_name, ps, depth))  /* username unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_full_name   ", &usr->hdr_full_name, ps, depth))  /* user's full name unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_home_dir    ", &usr->hdr_home_dir, ps, depth))   /* home directory unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_dir_drive   ", &usr->hdr_dir_drive, ps, depth))  /* home directory drive */
+               return False;
+       if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth))       /* logon script unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth))       /* profile path unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_acct_desc   ", &usr->hdr_acct_desc, ps, depth))  /* account desc */
+               return False;
+       if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth))       /* wkstas user can log on from */
+               return False;
+       if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth))        /* unknown string */
+               return False;
+       if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth))        /* wkstas user can log on from */
+               return False;
+
+       if(!prs_uint8s(False, "lm_pwd        ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+               return False;
+       if(!prs_uint8s(False, "nt_pwd        ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+               return False;
+
+       if(!prs_uint32("user_rid      ", ps, depth, &usr->user_rid))    /* User ID */
+               return False;
+       if(!prs_uint32("group_rid     ", ps, depth, &usr->group_rid))   /* Group ID */
+               return False;
+       if(!prs_uint32("acb_info      ", ps, depth, &usr->acb_info))
+               return False;
+
+       if(!prs_uint32s(False, "unknown_6      ", ps, depth, usr->unknown_6, 6))
+               return False;
+
+       if(!prs_uint8s(False, "password      ", ps, depth, usr->pass, sizeof(usr->pass)))
+               return False;
+
+       /* here begins pointed-to data */
+
+       if(!smb_io_unistr2("uni_user_name   ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth))      /* username unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_full_name   ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth))      /* user's full name unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_home_dir    ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth))        /* home directory unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_dir_drive   ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth))      /* home directory drive unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth))        /* logon script unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth))        /* profile path unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_acct_desc   ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth))      /* user desc unicode string */
+               return False;
+
+       if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth))        /* worksations user can log on from */
+               return False;
+
+       if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth))  /* unknown string */
+               return False;
+
+       if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+               return False;
+
+#if 0 /* JRA - unknown... */
+       /* ok, this is only guess-work (as usual) */
+       if (usr->ptr_logon_hrs) {
+               if(!prs_uint32("unknown_6     ", ps, depth, &usr->unknown_6))
+                       return False;
+               if(!prs_uint32("padding4      ", ps, depth, &usr->padding4))
+                       return False;
+               if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+                       return False;
+       } else if (UNMARSHALLING(ps)) {
+               usr->unknown_6 = 0;
+               usr->padding4 = 0;
+       }
+#endif
+
+       return True;
+}
+
+
+/*************************************************************************
+ init_sam_user_info21W
+
+ unknown_3 = 0x00ff ffff
+ unknown_5 = 0x0002 0000
+ unknown_6 = 0x0000 04ec 
+
+ *************************************************************************/
+
+void init_sam_user_info21W(SAM_USER_INFO_21 * usr,
+                          NTTIME * logon_time,
+                          NTTIME * logoff_time,
+                          NTTIME * kickoff_time,
+                          NTTIME * pass_last_set_time,
+                          NTTIME * pass_can_change_time,
+                          NTTIME * pass_must_change_time,
+                          UNISTR2 *user_name,
+                          UNISTR2 *full_name,
+                          UNISTR2 *home_dir,
+                          UNISTR2 *dir_drive,
+                          UNISTR2 *log_scr,
+                          UNISTR2 *prof_path,
+                          UNISTR2 *desc,
+                          UNISTR2 *wkstas,
+                          UNISTR2 *unk_str,
+                          UNISTR2 *mung_dial,
+                          uchar lm_pwd[16],
+                          uchar nt_pwd[16],
+                          uint32 user_rid,
+                          uint32 group_rid,
+                          uint32 acb_info,
+                          uint32 unknown_3,
+                          uint16 logon_divs,
+                          LOGON_HRS * hrs,
+                          uint32 unknown_5, uint32 unknown_6)
+{
+       int len_user_name = user_name != NULL ? user_name->uni_str_len : 0;
+       int len_full_name = full_name != NULL ? full_name->uni_str_len : 0;
+       int len_home_dir = home_dir != NULL ? home_dir->uni_str_len : 0;
+       int len_dir_drive = dir_drive != NULL ? dir_drive->uni_str_len : 0;
+       int len_logon_script = log_scr != NULL ? log_scr->uni_str_len : 0;
+       int len_profile_path = prof_path != NULL ? prof_path->uni_str_len : 0;
+       int len_description = desc != NULL ? desc->uni_str_len : 0;
+       int len_workstations = wkstas != NULL ? wkstas->uni_str_len : 0;
+       int len_unknown_str = unk_str != NULL ? unk_str->uni_str_len : 0;
+       int len_munged_dial = mung_dial != NULL ? mung_dial->uni_str_len : 0;
+
+       usr->logon_time = *logon_time;
+       usr->logoff_time = *logoff_time;
+       usr->kickoff_time = *kickoff_time;
+       usr->pass_last_set_time = *pass_last_set_time;
+       usr->pass_can_change_time = *pass_can_change_time;
+       usr->pass_must_change_time = *pass_must_change_time;
+
+       init_uni_hdr(&usr->hdr_user_name, len_user_name);
+       init_uni_hdr(&usr->hdr_full_name, len_full_name);
+       init_uni_hdr(&usr->hdr_home_dir, len_home_dir);
+       init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive);
+       init_uni_hdr(&usr->hdr_logon_script, len_logon_script);
+       init_uni_hdr(&usr->hdr_profile_path, len_profile_path);
+       init_uni_hdr(&usr->hdr_acct_desc, len_description);
+       init_uni_hdr(&usr->hdr_workstations, len_workstations);
+       init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str);
+       init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial);
+
+       memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd));
+       memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd));
+
+       usr->user_rid = user_rid;
+       usr->group_rid = group_rid;
+       usr->acb_info = acb_info;
+       usr->unknown_3 = unknown_3;     /* 0x00ff ffff */
+
+       usr->logon_divs = logon_divs;   /* should be 168 (hours/week) */
+       usr->ptr_logon_hrs = hrs ? 1 : 0;
+       usr->unknown_5 = unknown_5;     /* 0x0002 0000 */
+
+       if (nt_time_is_zero(pass_must_change_time)) {
+               usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+       } else {
+               usr->passmustchange=0;
+       }
+
+
+       ZERO_STRUCT(usr->padding1);
+       ZERO_STRUCT(usr->padding2);
+
+       copy_unistr2(&usr->uni_user_name, user_name);
+       copy_unistr2(&usr->uni_full_name, full_name);
+       copy_unistr2(&usr->uni_home_dir, home_dir);
+       copy_unistr2(&usr->uni_dir_drive, dir_drive);
+       copy_unistr2(&usr->uni_logon_script, log_scr);
+       copy_unistr2(&usr->uni_profile_path, prof_path);
+       copy_unistr2(&usr->uni_acct_desc, desc);
+       copy_unistr2(&usr->uni_workstations, wkstas);
+       copy_unistr2(&usr->uni_unknown_str, unk_str);
+       copy_unistr2(&usr->uni_munged_dial, mung_dial);
+
+       usr->unknown_6 = unknown_6;     /* 0x0000 04ec */
+       usr->padding4 = 0;
+
+       memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info21
+
+ unknown_3 = 0x00ff ffff
+ unknown_5 = 0x0002 0000
+ unknown_6 = 0x0000 04ec 
+
+ *************************************************************************/
+
+NTSTATUS init_sam_user_info21A(SAM_USER_INFO_21 *usr, SAM_ACCOUNT *pw, DOM_SID *domain_sid)
+{
+       NTTIME          logon_time, logoff_time, kickoff_time,
+                       pass_last_set_time, pass_can_change_time,
+                       pass_must_change_time;
+
+       int             len_user_name, len_full_name, len_home_dir,
+                       len_dir_drive, len_logon_script, len_profile_path,
+                       len_description, len_workstations, len_unknown_str,
+                       len_munged_dial;
+                       
+       const char*             user_name = pdb_get_username(pw);
+       const char*             full_name = pdb_get_fullname(pw);
+       const char*             home_dir  = pdb_get_homedir(pw);
+       const char*             dir_drive = pdb_get_dir_drive(pw);
+       const char*             logon_script = pdb_get_logon_script(pw);
+       const char*             profile_path = pdb_get_profile_path(pw);
+       const char*             description = pdb_get_acct_desc(pw);
+       const char*             workstations = pdb_get_workstations(pw);
+       const char*             munged_dial = pdb_get_munged_dial(pw);
+
+       uint32 user_rid;
+       const DOM_SID *user_sid;
+
+       uint32 group_rid;
+       const DOM_SID *group_sid;
+
+       len_user_name    = user_name    != NULL ? strlen(user_name   )+1 : 0;
+       len_full_name    = full_name    != NULL ? strlen(full_name   )+1 : 0;
+       len_home_dir     = home_dir     != NULL ? strlen(home_dir    )+1 : 0;
+       len_dir_drive    = dir_drive    != NULL ? strlen(dir_drive   )+1 : 0;
+       len_logon_script = logon_script != NULL ? strlen(logon_script)+1 : 0;
+       len_profile_path = profile_path != NULL ? strlen(profile_path)+1 : 0;
+       len_description  = description  != NULL ? strlen(description )+1 : 0;
+       len_workstations = workstations != NULL ? strlen(workstations)+1 : 0;
+       len_unknown_str  = 0;
+       len_munged_dial  = munged_dial  != NULL ? strlen(munged_dial )+1 : 0;
+
+
+       /* Create NTTIME structs */
+       unix_to_nt_time (&logon_time,           pdb_get_logon_time(pw));
+       unix_to_nt_time (&logoff_time,          pdb_get_logoff_time(pw));
+       unix_to_nt_time (&kickoff_time,         pdb_get_kickoff_time(pw));
+       unix_to_nt_time (&pass_last_set_time,   pdb_get_pass_last_set_time(pw));
+       unix_to_nt_time (&pass_can_change_time, pdb_get_pass_can_change_time(pw));
+       unix_to_nt_time (&pass_must_change_time,pdb_get_pass_must_change_time(pw));
+       
+       /* structure assignment */
+       usr->logon_time            = logon_time;
+       usr->logoff_time           = logoff_time;
+       usr->kickoff_time          = kickoff_time;
+       usr->pass_last_set_time    = pass_last_set_time;
+       usr->pass_can_change_time  = pass_can_change_time;
+       usr->pass_must_change_time = pass_must_change_time;
+
+       init_uni_hdr(&usr->hdr_user_name, len_user_name);
+       init_uni_hdr(&usr->hdr_full_name, len_full_name);
+       init_uni_hdr(&usr->hdr_home_dir, len_home_dir);
+       init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive);
+       init_uni_hdr(&usr->hdr_logon_script, len_logon_script);
+       init_uni_hdr(&usr->hdr_profile_path, len_profile_path);
+       init_uni_hdr(&usr->hdr_acct_desc, len_description);
+       init_uni_hdr(&usr->hdr_workstations, len_workstations);
+       init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str);
+       init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial);
+
+       ZERO_STRUCT(usr->nt_pwd);
+       ZERO_STRUCT(usr->lm_pwd);
+
+       user_sid = pdb_get_user_sid(pw);
+       
+       if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+               fstring user_sid_string;
+               fstring domain_sid_string;
+               DEBUG(0, ("init_sam_user_info_21A: User %s has SID %s, \nwhich conflicts with "
+                         "the domain sid %s.  Failing operation.\n", 
+                         user_name, 
+                         sid_to_string(user_sid_string, user_sid),
+                         sid_to_string(domain_sid_string, domain_sid)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       group_sid = pdb_get_group_sid(pw);
+       
+       if (!sid_peek_check_rid(domain_sid, group_sid, &group_rid)) {
+               fstring group_sid_string;
+               fstring domain_sid_string;
+               DEBUG(0, ("init_sam_user_info_21A: User %s has Primary Group SID %s, \n"
+                         "which conflicts with the domain sid %s.  Failing operation.\n", 
+                         user_name, 
+                         sid_to_string(group_sid_string, group_sid),
+                         sid_to_string(domain_sid_string, domain_sid)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       usr->user_rid  = user_rid;
+       usr->group_rid = group_rid;
+       usr->acb_info  = pdb_get_acct_ctrl(pw);
+
+       /*
+         Look at a user on a real NT4 PDC with usrmgr, press
+         'ok'. Then you will see that unknown_3 is set to
+         0x08f827fa. Look at the user immediately after that again,
+         and you will see that 0x00fffff is returned. This solves
+         the problem that you get access denied after having looked
+         at the user.
+         -- Volker
+       */
+       usr->unknown_3 = 0x00ffffff;
+
+       usr->logon_divs = pdb_get_logon_divs(pw); 
+       usr->ptr_logon_hrs = pdb_get_hours(pw) ? 1 : 0;
+       usr->unknown_5 = pdb_get_unknown_5(pw); /* 0x0002 0000 */
+
+       if (pdb_get_pass_must_change_time(pw) == 0) {
+               usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+       } else {
+               usr->passmustchange=0;
+       }
+
+
+       ZERO_STRUCT(usr->padding1);
+       ZERO_STRUCT(usr->padding2);
+
+       init_unistr2(&usr->uni_user_name, user_name, len_user_name);
+       init_unistr2(&usr->uni_full_name, full_name, len_full_name);
+       init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir);
+       init_unistr2(&usr->uni_dir_drive, dir_drive, len_dir_drive);
+       init_unistr2(&usr->uni_logon_script, logon_script, len_logon_script);
+       init_unistr2(&usr->uni_profile_path, profile_path, len_profile_path);
+       init_unistr2(&usr->uni_acct_desc, description, len_description);
+       init_unistr2(&usr->uni_workstations, workstations, len_workstations);
+       init_unistr2(&usr->uni_unknown_str, NULL, len_unknown_str);
+       init_unistr2(&usr->uni_munged_dial, munged_dial, len_munged_dial);
+
+       usr->unknown_6 = pdb_get_unknown_6(pw);
+       usr->padding4 = 0;
+
+       if (pdb_get_hours(pw)) {
+               usr->logon_hrs.len = pdb_get_hours_len(pw);
+               memcpy(&usr->logon_hrs.hours, pdb_get_hours(pw), MAX_HOURS_LEN);
+       } else
+               memset(&usr->logon_hrs, 0xff, sizeof(usr->logon_hrs));
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info21(const char *desc, SAM_USER_INFO_21 * usr,
+                       prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_user_info21");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_time("logon_time           ", &usr->logon_time, ps, depth))
+               return False;
+       if(!smb_io_time("logoff_time          ", &usr->logoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_last_set_time   ", &usr->pass_last_set_time, ps,depth))
+               return False;
+       if(!smb_io_time("kickoff_time         ", &usr->kickoff_time, ps, depth))
+               return False;
+       if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps,depth))
+               return False;
+       if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time,  ps, depth))
+               return False;
+
+       if(!smb_io_unihdr("hdr_user_name   ", &usr->hdr_user_name, ps, depth))  /* username unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_full_name   ", &usr->hdr_full_name, ps, depth))  /* user's full name unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_home_dir    ", &usr->hdr_home_dir, ps, depth))   /* home directory unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_dir_drive   ", &usr->hdr_dir_drive, ps, depth))  /* home directory drive */
+               return False;
+       if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth))       /* logon script unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth))       /* profile path unicode string header */
+               return False;
+       if(!smb_io_unihdr("hdr_acct_desc   ", &usr->hdr_acct_desc, ps, depth))  /* account desc */
+               return False;
+       if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth))       /* wkstas user can log on from */
+               return False;
+       if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth))        /* unknown string */
+               return False;
+       if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth))        /* wkstas user can log on from */
+               return False;
+
+       if(!prs_uint8s(False, "lm_pwd        ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+               return False;
+       if(!prs_uint8s(False, "nt_pwd        ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+               return False;
+
+       if(!prs_uint32("user_rid      ", ps, depth, &usr->user_rid))    /* User ID */
+               return False;
+       if(!prs_uint32("group_rid     ", ps, depth, &usr->group_rid))   /* Group ID */
+               return False;
+       if(!prs_uint32("acb_info      ", ps, depth, &usr->acb_info))
+               return False;
+
+       if(!prs_uint32("unknown_3     ", ps, depth, &usr->unknown_3))
+               return False;
+       if(!prs_uint16("logon_divs    ", ps, depth, &usr->logon_divs))  /* logon divisions per week */
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+               return False;
+
+       if(!prs_uint32("unknown_5     ", ps, depth, &usr->unknown_5))
+               return False;
+
+       if(!prs_uint8s(False, "padding1      ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+               return False;
+       if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+               return False;
+       if(!prs_uint8("padding2       ", ps, depth, &usr->padding2))
+               return False;
+
+       /* here begins pointed-to data */
+
+       if(!smb_io_unistr2("uni_user_name   ", &usr->uni_user_name,usr->hdr_user_name.buffer, ps, depth))       /* username unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_full_name   ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth))      /* user's full name unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_home_dir    ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth))        /* home directory unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_dir_drive   ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth))      /* home directory drive unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth))        /* logon script unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth))        /* profile path unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_acct_desc   ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth))      /* user desc unicode string */
+               return False;
+       if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth))        /* worksations user can log on from */
+               return False;
+       if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth))  /* unknown string */
+               return False;
+       if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth))   /* worksations user can log on from */
+               return False;
+
+       /* ok, this is only guess-work (as usual) */
+       if (usr->ptr_logon_hrs) {
+               if(!prs_align(ps))
+                       return False;
+               if(!prs_uint32("unknown_6     ", ps, depth, &usr->unknown_6))
+                       return False;
+               if(!prs_uint32("padding4      ", ps, depth, &usr->padding4))
+                       return False;
+               if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+                       return False;
+       } else if (UNMARSHALLING(ps)) {
+               usr->unknown_6 = 0;
+               usr->padding4 = 0;
+       }
+
+       return True;
+}
+
+void init_sam_user_info20A(SAM_USER_INFO_20 *usr, SAM_ACCOUNT *pw)
+{
+       int             len_munged_dial;
+       const char*             munged_dial = pdb_get_munged_dial(pw);
+
+       len_munged_dial  = munged_dial  != NULL ? strlen(munged_dial )+1 : 0;
+       init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial);
+       init_unistr2(&usr->uni_munged_dial, munged_dial, len_munged_dial);
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info20(const char *desc, SAM_USER_INFO_20 *usr,
+                       prs_struct *ps, int depth)
+{
+       if (usr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sam_io_user_info20");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth))        /* wkstas user can log on from */
+               return False;
+
+       if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth))   /* worksations user can log on from */
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+NTSTATUS make_samr_userinfo_ctr_usr21(TALLOC_CTX *ctx, SAM_USERINFO_CTR * ctr,
+                                   uint16 switch_value,
+                                   SAM_USER_INFO_21 * usr)
+{
+       DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+       ctr->switch_value = switch_value;
+       ctr->info.id = NULL;
+
+       switch (switch_value) {
+       case 0x10:
+               ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_10));
+               if (ctr->info.id10 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+
+               init_sam_user_info10(ctr->info.id10, usr->acb_info);
+               break;
+#if 0
+/* whoops - got this wrong.  i think.  or don't understand what's happening. */
+       case 0x11:
+               {
+                       NTTIME expire;
+                       info = (void *)&id11;
+
+                       expire.low = 0xffffffff;
+                       expire.high = 0x7fffffff;
+
+                       ctr->info.id = (SAM_USER_INFO_11 *) talloc_zero(ctx,sizeof(*ctr->info.id11));
+                       init_sam_user_info11(ctr->info.id11, &expire,
+                                            "BROOKFIELDS$",    /* name */
+                                            0x03ef,    /* user rid */
+                                            0x201,     /* group rid */
+                                            0x0080);   /* acb info */
+
+                       break;
+               }
+#endif
+       case 0x12:
+               ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_12));
+               if (ctr->info.id12 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+
+               init_sam_user_info12(ctr->info.id12, usr->lm_pwd, usr->nt_pwd);
+               break;
+       case 21:
+               {
+                       SAM_USER_INFO_21 *cusr;
+                       cusr = (SAM_USER_INFO_21 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_21));
+                       ctr->info.id21 = cusr;
+                       if (ctr->info.id21 == NULL)
+                               return NT_STATUS_NO_MEMORY;
+                       memcpy(cusr, usr, sizeof(*usr));
+                       memset(cusr->lm_pwd, 0, sizeof(cusr->lm_pwd));
+                       memset(cusr->nt_pwd, 0, sizeof(cusr->nt_pwd));
+                       break;
+               }
+       default:
+               DEBUG(4,("make_samr_userinfo_ctr: unsupported info\n"));
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, uchar * sess_key,
+                           uint16 switch_value, void *info)
+{
+       DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+       ctr->switch_value = switch_value;
+       ctr->info.id = info;
+
+       switch (switch_value) {
+       case 0x18:
+               SamOEMhash(ctr->info.id24->pass, sess_key, 516);
+               dump_data(100, (char *)sess_key, 16);
+               dump_data(100, (char *)ctr->info.id24->pass, 516);
+               break;
+       case 0x17:
+               SamOEMhash(ctr->info.id23->pass, sess_key, 516);
+               dump_data(100, (char *)sess_key, 16);
+               dump_data(100, (char *)ctr->info.id23->pass, 516);
+               break;
+       default:
+               DEBUG(4,("init_samr_userinfo_ctr: unsupported switch level\n"));
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_io_userinfo_ctr(const char *desc, SAM_USERINFO_CTR **ppctr,
+                                prs_struct *ps, int depth)
+{
+       BOOL ret;
+       SAM_USERINFO_CTR *ctr;
+
+       prs_debug(ps, depth, desc, "samr_io_userinfo_ctr");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               ctr = (SAM_USERINFO_CTR *)prs_alloc_mem(ps,sizeof(SAM_USERINFO_CTR));
+               if (ctr == NULL)
+                       return False;
+               *ppctr = ctr;
+       } else {
+               ctr = *ppctr;
+       }
+
+       /* lkclXXXX DO NOT ALIGN BEFORE READING SWITCH VALUE! */
+
+       if(!prs_uint16("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       ret = False;
+
+       switch (ctr->switch_value) {
+       case 0x10:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id10 = (SAM_USER_INFO_10 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_10));
+               if (ctr->info.id10 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info10("", ctr->info.id10, ps, depth);
+               break;
+       case 0x11:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id11 = (SAM_USER_INFO_11 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_11));
+
+               if (ctr->info.id11 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info11("", ctr->info.id11, ps, depth);
+               break;
+       case 0x12:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id12 = (SAM_USER_INFO_12 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_12));
+
+               if (ctr->info.id12 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info12("", ctr->info.id12, ps, depth);
+               break;
+       case 20:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id20 = (SAM_USER_INFO_20 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_20));
+
+               if (ctr->info.id20 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info20("", ctr->info.id20, ps, depth);
+               break;
+       case 21:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id21 = (SAM_USER_INFO_21 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_21));
+
+               if (ctr->info.id21 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info21("", ctr->info.id21, ps, depth);
+               break;
+       case 23:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id23 = (SAM_USER_INFO_23 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_23));
+
+               if (ctr->info.id23 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info23("", ctr->info.id23, ps, depth);
+               break;
+       case 24:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id24 = (SAM_USER_INFO_24 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_24));
+
+               if (ctr->info.id24 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info24("", ctr->info.id24, ps,  depth);
+               break;
+       case 25:
+               if (UNMARSHALLING(ps))
+                       ctr->info.id25 = (SAM_USER_INFO_25 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_25));
+
+               if (ctr->info.id25 == NULL) {
+                       DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+                       return False;
+               }
+               ret = sam_io_user_info25("", ctr->info.id25, ps, depth);
+               break;
+       default:
+               DEBUG(2, ("samr_io_userinfo_ctr: unknown switch level 0x%x\n", ctr->switch_value));
+               ret = False;
+               break;
+       }
+
+       return ret;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_query_userinfo(SAMR_R_QUERY_USERINFO * r_u,
+                               SAM_USERINFO_CTR * ctr, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_query_userinfo\n"));
+
+       r_u->ptr = 0;
+       r_u->ctr = NULL;
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->ptr = 1;
+               r_u->ctr = ctr;
+       }
+
+       r_u->status = status;   /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_userinfo(const char *desc, SAMR_R_QUERY_USERINFO * r_u,
+                             prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_query_userinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+               return False;
+
+       if (r_u->ptr != 0) {
+               if(!samr_io_userinfo_ctr("ctr", &r_u->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo(SAMR_Q_SET_USERINFO * q_u,
+                             POLICY_HND *hnd,  unsigned char sess_key[16],
+                             uint16 switch_value, void *info)
+{
+       DEBUG(5, ("init_samr_q_set_userinfo\n"));
+
+       q_u->pol = *hnd;
+       q_u->switch_value = switch_value;
+       init_samr_userinfo_ctr(q_u->ctr, sess_key, switch_value, info);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo(const char *desc, SAMR_Q_SET_USERINFO * q_u,
+                           prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_userinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       smb_io_pol_hnd("pol", &(q_u->pol), ps, depth);
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+       if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo(SAMR_R_SET_USERINFO * r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_set_userinfo\n"));
+
+       r_u->status = status;   /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo(const char *desc, SAMR_R_SET_USERINFO * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_set_userinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo2(SAMR_Q_SET_USERINFO2 * q_u,
+                              POLICY_HND *hnd, unsigned char sess_key[16],
+                              uint16 switch_value, SAM_USERINFO_CTR * ctr)
+{
+       DEBUG(5, ("init_samr_q_set_userinfo2\n"));
+
+       q_u->pol = *hnd;
+       q_u->switch_value = switch_value;
+       q_u->ctr = ctr;
+
+       if (q_u->ctr != NULL)
+               q_u->ctr->switch_value = switch_value;
+
+       switch (switch_value) {
+       case 0x12:
+               SamOEMhash(ctr->info.id12->lm_pwd, sess_key, 16);
+               SamOEMhash(ctr->info.id12->nt_pwd, sess_key, 16);
+               dump_data(100, (char *)sess_key, 16);
+               dump_data(100, (char *)ctr->info.id12->lm_pwd, 16);
+               dump_data(100, (char *)ctr->info.id12->nt_pwd, 16);
+               break;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo2(const char *desc, SAMR_Q_SET_USERINFO2 * q_u,
+                            prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_userinfo2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+       if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo2(SAMR_R_SET_USERINFO2 * r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_set_userinfo2\n"));
+
+       r_u->status = status;   /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo2(const char *desc, SAMR_R_SET_USERINFO2 * r_u,
+                            prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_set_userinfo2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT structure.
+********************************************************************/
+
+void init_samr_q_connect(SAMR_Q_CONNECT * q_u,
+                        char *srv_name, uint32 access_mask)
+{
+       int len_srv_name = strlen(srv_name);
+
+       DEBUG(5, ("init_samr_q_connect\n"));
+
+       /* make PDC server name \\server */
+       q_u->ptr_srv_name = len_srv_name > 0 ? 1 : 0;
+       init_unistr2(&q_u->uni_srv_name, srv_name, len_srv_name + 1);
+
+       /* example values: 0x0000 0002 */
+       q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect(const char *desc, SAMR_Q_CONNECT * q_u,
+                      prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_connect");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect(const char *desc, SAMR_R_CONNECT * r_u,
+                      prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_connect");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT4 structure.
+********************************************************************/
+
+void init_samr_q_connect4(SAMR_Q_CONNECT4 * q_u,
+                         char *srv_name, uint32 access_mask)
+{
+       int len_srv_name = strlen(srv_name);
+
+       DEBUG(5, ("init_samr_q_connect\n"));
+
+       /* make PDC server name \\server */
+       q_u->ptr_srv_name = len_srv_name > 0 ? 1 : 0;
+       init_unistr2(&q_u->uni_srv_name, srv_name, len_srv_name + 1);
+
+       /* Only value we've seen, possibly an address type ? */
+       q_u->unk_0 = 2;
+
+       /* example values: 0x0000 0002 */
+       q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect4(const char *desc, SAMR_Q_CONNECT4 * q_u,
+                       prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_connect4");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("unk_0", ps, depth, &q_u->unk_0))
+               return False;
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect4(const char *desc, SAMR_R_CONNECT4 * r_u,
+                       prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_connect4");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT_ANON structure.
+********************************************************************/
+
+void init_samr_q_connect_anon(SAMR_Q_CONNECT_ANON * q_u)
+{
+       DEBUG(5, ("init_samr_q_connect_anon\n"));
+
+       q_u->ptr = 1;
+       q_u->unknown_0 = 0x5c;  /* server name (?!!) */
+       q_u->unknown_1 = 0x01;
+       q_u->access_mask = 0x20;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect_anon(const char *desc, SAMR_Q_CONNECT_ANON * q_u,
+                           prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_connect_anon");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr      ", ps, depth, &q_u->ptr))
+               return False;
+       if(!prs_uint16("unknown_0", ps, depth, &q_u->unknown_0))
+               return False;
+       if(!prs_uint16("unknown_1", ps, depth, &q_u->unknown_1))
+               return False;
+       if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect_anon(const char *desc, SAMR_R_CONNECT_ANON * r_u,
+                           prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_connect_anon");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_get_dom_pwinfo(SAMR_Q_GET_DOM_PWINFO * q_u,
+                               char *srv_name)
+{
+       int len_srv_name = strlen(srv_name);
+
+       DEBUG(5, ("init_samr_q_get_dom_pwinfo\n"));
+
+       q_u->ptr = 1;
+       init_uni_hdr(&q_u->hdr_srv_name, len_srv_name);
+       init_unistr2(&q_u->uni_srv_name, srv_name, len_srv_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_dom_pwinfo(const char *desc, SAMR_Q_GET_DOM_PWINFO * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_get_dom_pwinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &q_u->ptr))
+               return False;
+       if (q_u->ptr != 0) {
+               if(!smb_io_unihdr("", &q_u->hdr_srv_name, ps, depth))
+                       return False;
+               if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->hdr_srv_name.buffer, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_dom_pwinfo(const char *desc, SAMR_R_GET_DOM_PWINFO * r_u,
+                             prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_get_dom_pwinfo");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       /*
+        * We need 16 bytes here according to tests.  Don't know
+        * what they are, but the length is important for the singing
+       */
+
+       if(!prs_uint32("unk_0", ps, depth, &r_u->unk_0))
+               return False;
+       if(!prs_uint32("unk_1", ps, depth, &r_u->unk_1))
+               return False;
+       if(!prs_uint32("unk_2", ps, depth, &r_u->unk_2))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+make a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+void init_enc_passwd(SAMR_ENC_PASSWD * pwd, char pass[512])
+{
+       ZERO_STRUCTP(pwd);
+
+       if (pass == NULL) {
+               pwd->ptr = 0;
+       } else {
+               pwd->ptr = 1;
+               memcpy(pwd->pass, pass, sizeof(pwd->pass));
+       }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+BOOL samr_io_enc_passwd(const char *desc, SAMR_ENC_PASSWD * pwd,
+                       prs_struct *ps, int depth)
+{
+       if (pwd == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_enc_passwd");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr", ps, depth, &pwd->ptr))
+               return False;
+
+       if (pwd->ptr != 0) {
+               if(!prs_uint8s(False, "pwd", ps, depth, pwd->pass, sizeof(pwd->pass)))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_ENC_HASH structure.
+********************************************************************/
+
+void init_enc_hash(SAMR_ENC_HASH * hsh, uchar hash[16])
+{
+       ZERO_STRUCTP(hsh);
+
+       if (hash == NULL) {
+               hsh->ptr = 0;
+       } else {
+               hsh->ptr = 1;
+               memcpy(hsh->hash, hash, sizeof(hsh->hash));
+       }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_HASH structure.
+********************************************************************/
+
+BOOL samr_io_enc_hash(const char *desc, SAMR_ENC_HASH * hsh,
+                     prs_struct *ps, int depth)
+{
+       if (hsh == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_enc_hash");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr ", ps, depth, &hsh->ptr))
+               return False;
+       if (hsh->ptr != 0) {
+               if(!prs_uint8s(False, "hash", ps, depth, hsh->hash,sizeof(hsh->hash)))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_chgpasswd_user(SAMR_Q_CHGPASSWD_USER * q_u,
+                               char *dest_host, char *user_name,
+                               char nt_newpass[516],
+                               uchar nt_oldhash[16],
+                               char lm_newpass[516],
+                               uchar lm_oldhash[16])
+{
+       int len_dest_host = strlen(dest_host);
+       int len_user_name = strlen(user_name);
+
+       DEBUG(5, ("init_samr_q_chgpasswd_user\n"));
+
+       q_u->ptr_0 = 1;
+       init_uni_hdr(&q_u->hdr_dest_host, len_dest_host);
+       init_unistr2(&q_u->uni_dest_host, dest_host, len_dest_host);
+       init_uni_hdr(&q_u->hdr_user_name, len_user_name);
+       init_unistr2(&q_u->uni_user_name, user_name, len_user_name);
+
+       init_enc_passwd(&q_u->nt_newpass, nt_newpass);
+       init_enc_hash(&q_u->nt_oldhash, nt_oldhash);
+
+       q_u->unknown = 0x01;
+
+       init_enc_passwd(&q_u->lm_newpass, lm_newpass);
+       init_enc_hash(&q_u->lm_oldhash, lm_oldhash);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_chgpasswd_user(const char *desc, SAMR_Q_CHGPASSWD_USER * q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_chgpasswd_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_0", ps, depth, &q_u->ptr_0))
+               return False;
+
+       if(!smb_io_unihdr("", &q_u->hdr_dest_host, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &q_u->uni_dest_host, q_u->hdr_dest_host.buffer, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unihdr("", &q_u->hdr_user_name, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &q_u->uni_user_name, q_u->hdr_user_name.buffer,ps, depth))
+               return False;
+
+       if(!samr_io_enc_passwd("nt_newpass", &q_u->nt_newpass, ps, depth))
+               return False;
+       if(!samr_io_enc_hash("nt_oldhash", &q_u->nt_oldhash, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown", ps, depth, &q_u->unknown))
+               return False;
+
+       if(!samr_io_enc_passwd("lm_newpass", &q_u->lm_newpass, ps, depth))
+               return False;
+       if(!samr_io_enc_hash("lm_oldhash", &q_u->lm_oldhash, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_CHGPASSWD_USER structure.
+********************************************************************/
+
+void init_samr_r_chgpasswd_user(SAMR_R_CHGPASSWD_USER * r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_r_chgpasswd_user\n"));
+
+       r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_chgpasswd_user(const char *desc, SAMR_R_CHGPASSWD_USER * r_u,
+                             prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_chgpasswd_user");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_unknown_2e(SAMR_Q_UNKNOWN_2E *q_u,
+                               POLICY_HND *domain_pol, uint16 switch_value)
+{
+       DEBUG(5, ("init_samr_q_unknown_2e\n"));
+
+       q_u->domain_pol = *domain_pol;
+       q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_unknown_2e(const char *desc, SAMR_Q_UNKNOWN_2E *q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_unknown_2e");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_samr_unknown_2e(SAMR_R_UNKNOWN_2E * r_u,
+                               uint16 switch_value, SAM_UNK_CTR * ctr,
+                               NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_samr_unknown_2e\n"));
+
+       r_u->ptr_0 = 0;
+       r_u->switch_value = 0;
+       r_u->status = status;   /* return status */
+
+       if (NT_STATUS_IS_OK(status)) {
+               r_u->switch_value = switch_value;
+               r_u->ptr_0 = 1;
+               r_u->ctr = ctr;
+       }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_samr_unknown_2e(const char *desc, SAMR_R_UNKNOWN_2E * r_u,
+                             prs_struct *ps, int depth)
+{
+        if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+               return False;
+
+       if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+               if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+
+               switch (r_u->switch_value) {
+               case 0x0c:
+                       if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+                               return False;
+                       break;
+               case 0x07:
+                       if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+                               return False;
+                       break;
+               case 0x06:
+                       if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+                               return False;
+                       break;
+               case 0x05:
+                       if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+                               return False;
+                       break;
+               case 0x03:
+                       if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+                               return False;
+                       break;
+               case 0x02:
+                       if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+                               return False;
+                       break;
+               case 0x01:
+                       if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+                               r_u->switch_value));
+                       r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+                       return False;
+               }
+       }
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+       
+       return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_set_domain_info(SAMR_Q_SET_DOMAIN_INFO *q_u,
+                               POLICY_HND *domain_pol, uint16 switch_value, SAM_UNK_CTR *ctr)
+{
+       DEBUG(5, ("init_samr_q_set_domain_info\n"));
+
+       q_u->domain_pol = *domain_pol;
+       q_u->switch_value0 = switch_value;
+
+       q_u->switch_value = switch_value;
+       q_u->ctr = ctr;
+       
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_domain_info(const char *desc, SAMR_Q_SET_DOMAIN_INFO *q_u,
+                             prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_q_set_domain_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+               return False;
+
+       if(!prs_uint16("switch_value0", ps, depth, &q_u->switch_value0))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if ((q_u->ctr = (SAM_UNK_CTR *)prs_alloc_mem(ps, sizeof(SAM_UNK_CTR))) == NULL)
+               return False;
+       
+       switch (q_u->switch_value) {
+
+       case 0x0c:
+               if(!sam_io_unk_info12("unk_inf12", &q_u->ctr->info.inf12, ps, depth))
+                       return False;
+               break;
+       case 0x07:
+               if(!sam_io_unk_info7("unk_inf7",&q_u->ctr->info.inf7, ps,depth))
+                       return False;
+               break;
+       case 0x06:
+               if(!sam_io_unk_info6("unk_inf6",&q_u->ctr->info.inf6, ps,depth))
+                       return False;
+               break;
+       case 0x05:
+               if(!sam_io_unk_info5("unk_inf5",&q_u->ctr->info.inf5, ps,depth))
+                       return False;
+               break;
+       case 0x03:
+               if(!sam_io_unk_info3("unk_inf3",&q_u->ctr->info.inf3, ps,depth))
+                       return False;
+               break;
+       case 0x02:
+               if(!sam_io_unk_info2("unk_inf2",&q_u->ctr->info.inf2, ps,depth))
+                       return False;
+               break;
+       case 0x01:
+               if(!sam_io_unk_info1("unk_inf1",&q_u->ctr->info.inf1, ps,depth))
+                       return False;
+               break;
+       default:
+               DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+                       q_u->switch_value));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_set_domain_info(SAMR_R_SET_DOMAIN_INFO * r_u, NTSTATUS status)
+{
+       DEBUG(5, ("init_samr_r_set_domain_info\n"));
+
+       r_u->status = status;   /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_domain_info(const char *desc, SAMR_R_SET_DOMAIN_INFO * r_u,
+                             prs_struct *ps, int depth)
+{
+        if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_ntstatus("status", ps, depth, &r_u->status))
+               return False;
+       
+       return True;
+}
diff --git a/source4/rpc_parse/parse_sec.c b/source4/rpc_parse/parse_sec.c
new file mode 100644 (file)
index 0000000..dbd72e5
--- /dev/null
@@ -0,0 +1,1028 @@
+/* 
+ *  Unix SMB/Netbios implementation.
+ *  Version 1.9.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1998,
+ *  Copyright (C) Jeremy R. Allison            1995-1998
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ *  Copyright (C) Paul Ashton                  1997-1998.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Sets up a SEC_ACCESS structure.
+********************************************************************/
+
+void init_sec_access(SEC_ACCESS *t, uint32 mask)
+{
+       t->mask = mask;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+
+BOOL sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+       if (t == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sec_io_access");
+       depth++;
+       
+       if(!prs_uint32("mask", ps, depth, &(t->mask)))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+BOOL sec_ace_object(uint8 type)
+{
+       if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+            type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+               return True;
+       }
+       return False;
+}
+
+/*******************************************************************
+ copy a SEC_ACE structure.
+********************************************************************/
+void sec_ace_copy(SEC_ACE *ace_dest, SEC_ACE *ace_src)
+{
+       ace_dest->type  = ace_src->type;
+       ace_dest->flags = ace_src->flags;
+       ace_dest->size  = ace_src->size;
+       ace_dest->info.mask = ace_src->info.mask;
+       ace_dest->obj_flags = ace_src->obj_flags;
+       memcpy(&ace_dest->obj_guid, &ace_src->obj_guid, GUID_SIZE);
+       memcpy(&ace_dest->inh_guid, &ace_src->inh_guid, GUID_SIZE);     
+       sid_copy(&ace_dest->trustee, &ace_src->trustee);
+}
+
+/*******************************************************************
+ Sets up a SEC_ACE structure.
+********************************************************************/
+
+void init_sec_ace(SEC_ACE *t, DOM_SID *sid, uint8 type, SEC_ACCESS mask, uint8 flag)
+{
+       t->type = type;
+       t->flags = flag;
+       t->size = sid_size(sid) + 8;
+       t->info = mask;
+
+       ZERO_STRUCTP(&t->trustee);
+       sid_copy(&t->trustee, sid);
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+
+BOOL sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+       uint32 old_offset;
+       uint32 offset_ace_size;
+
+       if (psa == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "sec_io_ace");
+       depth++;
+       
+       old_offset = prs_offset(ps);
+
+       if(!prs_uint8("type ", ps, depth, &psa->type))
+               return False;
+
+       if(!prs_uint8("flags", ps, depth, &psa->flags))
+               return False;
+
+       if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+               return False;
+
+       if(!sec_io_access("info ", &psa->info, ps, depth))
+               return False;
+
+       /* check whether object access is present */
+       if (!sec_ace_object(psa->type)) {
+               if (!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+                       return False;
+       } else {
+               if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+                       return False;
+
+               if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+                       if (!prs_uint8s(False, "obj_guid", ps, depth, psa->obj_guid.info, GUID_SIZE))
+                               return False;
+
+               if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+                       if (!prs_uint8s(False, "inh_guid", ps, depth, psa->inh_guid.info, GUID_SIZE))
+                               return False;
+
+               if(!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_ace_size, old_offset))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ adds new SID with its permissions to ACE list
+********************************************************************/
+
+NTSTATUS sec_ace_add_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, unsigned *num, DOM_SID *sid, uint32 mask)
+{
+       int i = 0;
+       
+       if (!ctx || !new || !old || !sid || !num)  return NT_STATUS_INVALID_PARAMETER;
+
+       *num += 1;
+       
+       if((new[0] = (SEC_ACE *) talloc_zero(ctx, (*num) * sizeof(SEC_ACE))) == 0)
+               return NT_STATUS_NO_MEMORY;
+
+       for (i = 0; i < *num - 1; i ++)
+               sec_ace_copy(&(*new)[i], &old[i]);
+
+       (*new)[i].type  = 0;
+       (*new)[i].flags = 0;
+       (*new)[i].size  = SEC_ACE_HEADER_SIZE + sid_size(sid);
+       (*new)[i].info.mask = mask;
+       sid_copy(&(*new)[i].trustee, sid);
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+  modify SID's permissions at ACL 
+********************************************************************/
+
+NTSTATUS sec_ace_mod_sid(SEC_ACE *ace, size_t num, DOM_SID *sid, uint32 mask)
+{
+       int i = 0;
+
+       if (!ace || !sid)  return NT_STATUS_INVALID_PARAMETER;
+
+       for (i = 0; i < num; i ++) {
+               if (sid_compare(&ace[i].trustee, sid) == 0) {
+                       ace[i].info.mask = mask;
+                       return NT_STATUS_OK;
+               }
+       }
+       return NT_STATUS_NOT_FOUND;
+}
+
+/*******************************************************************
+ delete SID from ACL
+********************************************************************/
+
+NTSTATUS sec_ace_del_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, size_t *num, DOM_SID *sid)
+{
+       int i     = 0;
+       int n_del = 0;
+
+       if (!ctx || !new || !old || !sid || !num)  return NT_STATUS_INVALID_PARAMETER;
+
+       if((new[0] = (SEC_ACE *) talloc_zero(ctx, *num * sizeof(SEC_ACE))) == 0)
+               return NT_STATUS_NO_MEMORY;
+
+       for (i = 0; i < *num; i ++) {
+               if (sid_compare(&old[i].trustee, sid) != 0)
+                       sec_ace_copy(&(*new)[i], &old[i]);
+               else
+                       n_del ++;
+       }
+       if (n_del == 0)
+               return NT_STATUS_NOT_FOUND;
+       else {
+               *num -= n_del;
+               return NT_STATUS_OK;
+       }
+}
+
+/*******************************************************************
+ Create a SEC_ACL structure.  
+********************************************************************/
+
+SEC_ACL *make_sec_acl(TALLOC_CTX *ctx, uint16 revision, int num_aces, SEC_ACE *ace_list)
+{
+       SEC_ACL *dst;
+       int i;
+
+       if((dst = (SEC_ACL *)talloc_zero(ctx,sizeof(SEC_ACL))) == NULL)
+               return NULL;
+
+       dst->revision = revision;
+       dst->num_aces = num_aces;
+       dst->size = SEC_ACL_HEADER_SIZE;
+
+       /* Now we need to return a non-NULL address for the ace list even
+          if the number of aces required is zero.  This is because there
+          is a distinct difference between a NULL ace and an ace with zero
+          entries in it.  This is achieved by checking that num_aces is a
+          positive number. */
+
+       if ((num_aces) && 
+            ((dst->ace = (SEC_ACE *)talloc(ctx, sizeof(SEC_ACE) * num_aces)) 
+             == NULL)) {
+               return NULL;
+       }
+        
+       for (i = 0; i < num_aces; i++) {
+               dst->ace[i] = ace_list[i]; /* Structure copy. */
+               dst->size += ace_list[i].size;
+       }
+
+       return dst;
+}
+
+/*******************************************************************
+ Duplicate a SEC_ACL structure.  
+********************************************************************/
+
+SEC_ACL *dup_sec_acl(TALLOC_CTX *ctx, SEC_ACL *src)
+{
+       if(src == NULL)
+               return NULL;
+
+       return make_sec_acl(ctx, src->revision, src->num_aces, src->ace);
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.  
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+
+BOOL sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+       int i;
+       uint32 old_offset;
+       uint32 offset_acl_size;
+       SEC_ACL *psa;
+
+       /*
+        * Note that the size is always a multiple of 4 bytes due to the
+        * nature of the data structure.  Therefore the prs_align() calls
+        * have been removed as they through us off when doing two-layer
+        * marshalling such as in the printing code (NEW_BUFFER).  --jerry
+        */
+
+       if (ppsa == NULL)
+               return False;
+
+       psa = *ppsa;
+
+       if(UNMARSHALLING(ps) && psa == NULL) {
+               /*
+                * This is a read and we must allocate the stuct to read into.
+                */
+               if((psa = (SEC_ACL *)prs_alloc_mem(ps, sizeof(SEC_ACL))) == NULL)
+                       return False;
+               *ppsa = psa;
+       }
+
+       prs_debug(ps, depth, desc, "sec_io_acl");
+       depth++;
+       
+       old_offset = prs_offset(ps);
+
+       if(!prs_uint16("revision", ps, depth, &psa->revision))
+               return False;
+
+       if(!prs_uint16_pre("size     ", ps, depth, &psa->size, &offset_acl_size))
+               return False;
+
+       if(!prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               /*
+                * Even if the num_aces is zero, allocate memory as there's a difference
+                * between a non-present DACL (allow all access) and a DACL with no ACE's
+                * (allow no access).
+                */
+               if((psa->ace = (SEC_ACE *)prs_alloc_mem(ps,sizeof(psa->ace[0]) * (psa->num_aces+1))) == NULL)
+                       return False;
+       }
+
+       for (i = 0; i < psa->num_aces; i++) {
+               fstring tmp;
+               slprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+               if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint16_post("size     ", ps, depth, &psa->size, offset_acl_size, old_offset))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Works out the linearization size of a SEC_DESC.
+********************************************************************/
+
+size_t sec_desc_size(SEC_DESC *psd)
+{
+       size_t offset;
+
+       if (!psd) return 0;
+
+       offset = SEC_DESC_HEADER_SIZE;
+
+       /* don't align */
+
+       if (psd->owner_sid != NULL)
+               offset += sid_size(psd->owner_sid);
+
+       if (psd->grp_sid != NULL)
+               offset += sid_size(psd->grp_sid);
+
+       if (psd->sacl != NULL)
+               offset += psd->sacl->size;
+
+       if (psd->dacl != NULL)
+               offset += psd->dacl->size;
+
+       return offset;
+}
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+
+BOOL sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
+{
+       /* Trivial case */
+
+       if (!s1 && !s2) return True;
+
+       /* Check top level stuff */
+
+       if (s1->type != s2->type || s1->flags != s2->flags ||
+           s1->info.mask != s2->info.mask) {
+               return False;
+       }
+
+       /* Check SID */
+
+       if (!sid_equal(&s1->trustee, &s2->trustee)) {
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+BOOL sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
+{
+       int i, j;
+
+       /* Trivial cases */
+
+       if (!s1 && !s2) return True;
+       if (!s1 || !s2) return False;
+
+       /* Check top level stuff */
+
+       if (s1->revision != s2->revision) {
+               DEBUG(10, ("sec_acl_equal(): revision differs (%d != %d)\n",
+                          s1->revision, s2->revision));
+               return False;
+       }
+
+       if (s1->num_aces != s2->num_aces) {
+               DEBUG(10, ("sec_acl_equal(): num_aces differs (%d != %d)\n",
+                          s1->revision, s2->revision));
+               return False;
+       }
+
+       /* The ACEs could be in any order so check each ACE in s1 against 
+          each ACE in s2. */
+
+       for (i = 0; i < s1->num_aces; i++) {
+               BOOL found = False;
+
+               for (j = 0; j < s2->num_aces; j++) {
+                       if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
+                               found = True;
+                               break;
+                       }
+               }
+
+               if (!found) return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+
+BOOL sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
+{
+       /* Trivial case */
+
+       if (!s1 && !s2) {
+               goto done;
+       }
+
+       /* Check top level stuff */
+
+       if (s1->revision != s2->revision) {
+               DEBUG(10, ("sec_desc_equal(): revision differs (%d != %d)\n",
+                          s1->revision, s2->revision));
+               return False;
+       }
+
+       if (s1->type!= s2->type) {
+               DEBUG(10, ("sec_desc_equal(): type differs (%d != %d)\n",
+                          s1->type, s2->type));
+               return False;
+       }
+
+       /* Check owner and group */
+
+       if (!sid_equal(s1->owner_sid, s2->owner_sid)) {
+               fstring str1, str2;
+
+               sid_to_string(str1, s1->owner_sid);
+               sid_to_string(str2, s2->owner_sid);
+
+               DEBUG(10, ("sec_desc_equal(): owner differs (%s != %s)\n",
+                          str1, str2));
+               return False;
+       }
+
+       if (!sid_equal(s1->grp_sid, s2->grp_sid)) {
+               fstring str1, str2;
+
+               sid_to_string(str1, s1->grp_sid);
+               sid_to_string(str2, s2->grp_sid);
+
+               DEBUG(10, ("sec_desc_equal(): group differs (%s != %s)\n",
+                          str1, str2));
+               return False;
+       }
+
+       /* Check ACLs present in one but not the other */
+
+       if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+           (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) {
+               DEBUG(10, ("sec_desc_equal(): dacl or sacl not present\n"));
+               return False;
+       }
+
+       /* Sigh - we have to do it the hard way by iterating over all
+          the ACEs in the ACLs */
+
+       if (!sec_acl_equal(s1->dacl, s2->dacl) ||
+           !sec_acl_equal(s1->sacl, s2->sacl)) {
+               DEBUG(10, ("sec_desc_equal(): dacl/sacl list not equal\n"));
+               return False;
+       }
+
+ done:
+       DEBUG(10, ("sec_desc_equal(): secdescs are identical\n"));
+       return True;
+}
+
+/*******************************************************************
+ Merge part of security descriptor old_sec in to the empty sections of 
+ security descriptor new_sec.
+********************************************************************/
+
+SEC_DESC_BUF *sec_desc_merge(TALLOC_CTX *ctx, SEC_DESC_BUF *new_sdb, SEC_DESC_BUF *old_sdb)
+{
+       DOM_SID *owner_sid, *group_sid;
+       SEC_DESC_BUF *return_sdb;
+       SEC_ACL *dacl, *sacl;
+       SEC_DESC *psd = NULL;
+       uint16 secdesc_type;
+       size_t secdesc_size;
+
+       /* Copy over owner and group sids.  There seems to be no flag for
+          this so just check the pointer values. */
+
+       owner_sid = new_sdb->sec->owner_sid ? new_sdb->sec->owner_sid :
+               old_sdb->sec->owner_sid;
+
+       group_sid = new_sdb->sec->grp_sid ? new_sdb->sec->grp_sid :
+               old_sdb->sec->grp_sid;
+       
+       secdesc_type = new_sdb->sec->type;
+
+       /* Ignore changes to the system ACL.  This has the effect of making
+          changes through the security tab audit button not sticking. 
+          Perhaps in future Samba could implement these settings somehow. */
+
+       sacl = NULL;
+       secdesc_type &= ~SEC_DESC_SACL_PRESENT;
+
+       /* Copy across discretionary ACL */
+
+       if (secdesc_type & SEC_DESC_DACL_PRESENT) {
+               dacl = new_sdb->sec->dacl;
+       } else {
+               dacl = old_sdb->sec->dacl;
+       }
+
+       /* Create new security descriptor from bits */
+
+       psd = make_sec_desc(ctx, new_sdb->sec->revision, 
+                           owner_sid, group_sid, sacl, dacl, &secdesc_size);
+
+       return_sdb = make_sec_desc_buf(ctx, secdesc_size, psd);
+
+       return(return_sdb);
+}
+
+/*******************************************************************
+ Tallocs a duplicate SID. 
+********************************************************************/ 
+
+static DOM_SID *sid_dup_talloc(TALLOC_CTX *ctx, DOM_SID *src)
+{
+  DOM_SID *dst;
+
+  if(!src)
+    return NULL;
+
+  if((dst = talloc_zero(ctx, sizeof(DOM_SID))) != NULL) {
+    sid_copy( dst, src);
+  }
+
+  return dst;
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure
+********************************************************************/
+
+SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, 
+                       DOM_SID *owner_sid, DOM_SID *grp_sid,
+                       SEC_ACL *sacl, SEC_ACL *dacl, size_t *sd_size)
+{
+       SEC_DESC *dst;
+       uint32 offset     = 0;
+       uint32 offset_sid = SEC_DESC_HEADER_SIZE;
+       uint32 offset_acl = 0;
+
+       *sd_size = 0;
+
+       if(( dst = (SEC_DESC *)talloc_zero(ctx, sizeof(SEC_DESC))) == NULL)
+               return NULL;
+
+       dst->revision = revision;
+       dst->type     = SEC_DESC_SELF_RELATIVE;
+
+       if (sacl) dst->type |= SEC_DESC_SACL_PRESENT;
+       if (dacl) dst->type |= SEC_DESC_DACL_PRESENT;
+
+       dst->off_owner_sid = 0;
+       dst->off_grp_sid   = 0;
+       dst->off_sacl      = 0;
+       dst->off_dacl      = 0;
+
+       if(owner_sid && ((dst->owner_sid = sid_dup_talloc(ctx,owner_sid)) == NULL))
+               goto error_exit;
+
+       if(grp_sid && ((dst->grp_sid = sid_dup_talloc(ctx,grp_sid)) == NULL))
+               goto error_exit;
+
+       if(sacl && ((dst->sacl = dup_sec_acl(ctx, sacl)) == NULL))
+               goto error_exit;
+
+       if(dacl && ((dst->dacl = dup_sec_acl(ctx, dacl)) == NULL))
+               goto error_exit;
+
+       offset = 0;
+
+       /*
+        * Work out the linearization sizes.
+        */
+       if (dst->owner_sid != NULL) {
+
+               if (offset == 0)
+                       offset = SEC_DESC_HEADER_SIZE;
+
+               offset += sid_size(dst->owner_sid);
+       }
+
+       if (dst->grp_sid != NULL) {
+
+               if (offset == 0)
+                       offset = SEC_DESC_HEADER_SIZE;
+
+               offset += sid_size(dst->grp_sid);
+       }
+
+       if (dst->sacl != NULL) {
+
+               offset_acl = SEC_DESC_HEADER_SIZE;
+
+               dst->off_sacl  = offset_acl;
+               offset_acl    += dst->sacl->size;
+               offset        += dst->sacl->size;
+               offset_sid    += dst->sacl->size;
+       }
+
+       if (dst->dacl != NULL) {
+
+               if (offset_acl == 0)
+                       offset_acl = SEC_DESC_HEADER_SIZE;
+
+               dst->off_dacl  = offset_acl;
+               offset_acl    += dst->dacl->size;
+               offset        += dst->dacl->size;
+               offset_sid    += dst->dacl->size;
+       }
+
+       *sd_size = (size_t)((offset == 0) ? SEC_DESC_HEADER_SIZE : offset);
+
+       if (dst->owner_sid != NULL)
+               dst->off_owner_sid = offset_sid;
+               
+       /* sid_size() returns 0 if the sid is NULL so this is ok */
+               
+       if (dst->grp_sid != NULL)
+               dst->off_grp_sid = offset_sid + sid_size(dst->owner_sid);
+
+       return dst;
+
+error_exit:
+
+       *sd_size = 0;
+       return NULL;
+}
+
+/*******************************************************************
+ Duplicate a SEC_DESC structure.  
+********************************************************************/
+
+SEC_DESC *dup_sec_desc( TALLOC_CTX *ctx, SEC_DESC *src)
+{
+       size_t dummy;
+
+       if(src == NULL)
+               return NULL;
+
+       return make_sec_desc( ctx, src->revision, 
+                               src->owner_sid, src->grp_sid, src->sacl,
+                               src->dacl, &dummy);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure with typical defaults.
+********************************************************************/
+
+SEC_DESC *make_standard_sec_desc(TALLOC_CTX *ctx, DOM_SID *owner_sid, DOM_SID *grp_sid,
+                                SEC_ACL *dacl, size_t *sd_size)
+{
+       return make_sec_desc(ctx, SEC_DESC_REVISION,
+                            owner_sid, grp_sid, NULL, dacl, sd_size);
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+
+BOOL sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+       uint32 old_offset;
+       uint32 max_offset = 0; /* after we're done, move offset to end */
+       uint32 tmp_offset = 0;
+       
+       SEC_DESC *psd;
+
+       if (ppsd == NULL)
+               return False;
+
+       psd = *ppsd;
+
+       if (psd == NULL) {
+               if(UNMARSHALLING(ps)) {
+                       if((psd = (SEC_DESC *)prs_alloc_mem(ps,sizeof(SEC_DESC))) == NULL)
+                               return False;
+                       *ppsd = psd;
+               } else {
+                       /* Marshalling - just ignore. */
+                       return True;
+               }
+       }
+
+       prs_debug(ps, depth, desc, "sec_io_desc");
+       depth++;
+
+#if 0  
+       /*
+        * if alignment is needed, should be done by the the 
+        * caller.  Not here.  This caused me problems when marshalling
+        * printer info into a buffer.   --jerry
+        */
+       if(!prs_align(ps))
+               return False;
+#endif
+       
+       /* start of security descriptor stored for back-calc offset purposes */
+       old_offset = prs_offset(ps);
+
+       if(!prs_uint16("revision ", ps, depth, &psd->revision))
+               return False;
+
+       if(!prs_uint16("type     ", ps, depth, &psd->type))
+               return False;
+
+       if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid))
+               return False;
+
+       if(!prs_uint32("off_grp_sid  ", ps, depth, &psd->off_grp_sid))
+               return False;
+
+       if(!prs_uint32("off_sacl     ", ps, depth, &psd->off_sacl))
+               return False;
+
+       if(!prs_uint32("off_dacl     ", ps, depth, &psd->off_dacl))
+               return False;
+
+       max_offset = MAX(max_offset, prs_offset(ps));
+
+       if (psd->off_owner_sid != 0) {
+
+               tmp_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+                       return False;
+
+               if (UNMARSHALLING(ps)) {
+                       /* reading */
+                       if((psd->owner_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->owner_sid))) == NULL)
+                               return False;
+               }
+
+               if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+                       return False;
+
+               max_offset = MAX(max_offset, prs_offset(ps));
+
+               if (!prs_set_offset(ps,tmp_offset))
+                       return False;
+       }
+
+       if (psd->off_grp_sid != 0) {
+
+               tmp_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+                       return False;
+
+               if (UNMARSHALLING(ps)) {
+                       /* reading */
+                       if((psd->grp_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->grp_sid))) == NULL)
+                               return False;
+               }
+
+               if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+                       return False;
+                       
+               max_offset = MAX(max_offset, prs_offset(ps));
+
+               if (!prs_set_offset(ps,tmp_offset))
+                       return False;
+       }
+
+       if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) {
+               tmp_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, old_offset + psd->off_sacl))
+                       return False;
+               if(!sec_io_acl("sacl", &psd->sacl, ps, depth))
+                       return False;
+               max_offset = MAX(max_offset, prs_offset(ps));
+               if (!prs_set_offset(ps,tmp_offset))
+                       return False;
+       }
+
+
+       if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) {
+               tmp_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, old_offset + psd->off_dacl))
+                       return False;
+               if(!sec_io_acl("dacl", &psd->dacl, ps, depth))
+                       return False;
+               max_offset = MAX(max_offset, prs_offset(ps));
+               if (!prs_set_offset(ps,tmp_offset))
+                       return False;
+       }
+
+       if(!prs_set_offset(ps, max_offset))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Creates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *make_sec_desc_buf(TALLOC_CTX *ctx, size_t len, SEC_DESC *sec_desc)
+{
+       SEC_DESC_BUF *dst;
+
+       if((dst = (SEC_DESC_BUF *)talloc_zero(ctx, sizeof(SEC_DESC_BUF))) == NULL)
+               return NULL;
+
+       /* max buffer size (allocated size) */
+       dst->max_len = (uint32)len;
+       dst->len = (uint32)len;
+       
+       if(sec_desc && ((dst->sec = dup_sec_desc(ctx, sec_desc)) == NULL)) {
+               return NULL;
+       }
+
+       dst->ptr = 0x1;
+
+       return dst;
+}
+
+/*******************************************************************
+ Duplicates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *dup_sec_desc_buf(TALLOC_CTX *ctx, SEC_DESC_BUF *src)
+{
+       if(src == NULL)
+               return NULL;
+
+       return make_sec_desc_buf( ctx, src->len, src->sec);
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC_BUF structure.
+********************************************************************/
+
+BOOL sec_io_desc_buf(const char *desc, SEC_DESC_BUF **ppsdb, prs_struct *ps, int depth)
+{
+       uint32 off_len;
+       uint32 off_max_len;
+       uint32 old_offset;
+       uint32 size;
+       SEC_DESC_BUF *psdb;
+
+       if (ppsdb == NULL)
+               return False;
+
+       psdb = *ppsdb;
+
+       if (UNMARSHALLING(ps) && psdb == NULL) {
+               if((psdb = (SEC_DESC_BUF *)prs_alloc_mem(ps,sizeof(SEC_DESC_BUF))) == NULL)
+                       return False;
+               *ppsdb = psdb;
+       }
+
+       prs_debug(ps, depth, desc, "sec_io_desc_buf");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32_pre("max_len", ps, depth, &psdb->max_len, &off_max_len))
+               return False;
+
+       if(!prs_uint32    ("ptr  ", ps, depth, &psdb->ptr))
+               return False;
+
+       if(!prs_uint32_pre("len    ", ps, depth, &psdb->len, &off_len))
+               return False;
+
+       old_offset = prs_offset(ps);
+
+       /* reading, length is non-zero; writing, descriptor is non-NULL */
+       if ((UNMARSHALLING(ps) && psdb->len != 0) || (MARSHALLING(ps) && psdb->sec != NULL)) {
+               if(!sec_io_desc("sec   ", &psdb->sec, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+       
+       size = prs_offset(ps) - old_offset;
+       if(!prs_uint32_post("max_len", ps, depth, &psdb->max_len, off_max_len, size == 0 ? psdb->max_len : size))
+               return False;
+
+       if(!prs_uint32_post("len    ", ps, depth, &psdb->len, off_len, size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ adds new SID with its permissions to SEC_DESC
+********************************************************************/
+
+NTSTATUS sec_desc_add_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, uint32 mask, size_t *sd_size)
+{
+       SEC_DESC *sd   = 0;
+       SEC_ACL  *dacl = 0;
+       SEC_ACE  *ace  = 0;
+       NTSTATUS  status;
+
+       *sd_size = 0;
+
+       if (!ctx || !psd || !sid || !sd_size)  return NT_STATUS_INVALID_PARAMETER;
+
+       status = sec_ace_add_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid, mask);
+       
+       if (!NT_STATUS_IS_OK(status))
+               return status;
+
+       if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+               return NT_STATUS_UNSUCCESSFUL;
+       
+       if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->owner_sid, 
+               psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       *psd = sd;
+        sd  = 0;
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ modify SID's permissions at SEC_DESC
+********************************************************************/
+
+NTSTATUS sec_desc_mod_sid(SEC_DESC *sd, DOM_SID *sid, uint32 mask)
+{
+       NTSTATUS status;
+
+       if (!sd || !sid) return NT_STATUS_INVALID_PARAMETER;
+
+       status = sec_ace_mod_sid(sd->dacl->ace, sd->dacl->num_aces, sid, mask);
+
+       if (!NT_STATUS_IS_OK(status))
+               return status;
+       
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ delete SID from SEC_DESC
+********************************************************************/
+
+NTSTATUS sec_desc_del_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, size_t *sd_size)
+{
+       SEC_DESC *sd   = 0;
+       SEC_ACL  *dacl = 0;
+       SEC_ACE  *ace  = 0;
+       NTSTATUS  status;
+
+       *sd_size = 0;
+       
+       if (!ctx || !psd[0] || !sid || !sd_size) return NT_STATUS_INVALID_PARAMETER;
+
+       status = sec_ace_del_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid);
+
+       if (!NT_STATUS_IS_OK(status))
+               return status;
+
+       if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+               return NT_STATUS_UNSUCCESSFUL;
+       
+       if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->owner_sid, 
+               psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       *psd = sd;
+        sd  = 0;
+       return NT_STATUS_OK;
+}
diff --git a/source4/rpc_parse/parse_spoolss.c b/source4/rpc_parse/parse_spoolss.c
new file mode 100644 (file)
index 0000000..4773790
--- /dev/null
@@ -0,0 +1,7751 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2000,
+ *  Copyright (C) Gerald Carter                2000-2002,
+ *  Copyright (C) Tim Potter                  2001-2002.
+ *
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+return the length of a UNISTR string.
+********************************************************************/  
+
+static uint32 str_len_uni(UNISTR *source)
+{
+       uint32 i=0;
+
+       if (!source->buffer)
+               return 0;
+
+       while (source->buffer[i])
+               i++;
+
+       return i;
+}
+
+/*******************************************************************
+This should be moved in a more generic lib.
+********************************************************************/  
+
+BOOL spoolss_io_system_time(const char *desc, prs_struct *ps, int depth, SYSTEMTIME *systime)
+{
+       if(!prs_uint16("year", ps, depth, &systime->year))
+               return False;
+       if(!prs_uint16("month", ps, depth, &systime->month))
+               return False;
+       if(!prs_uint16("dayofweek", ps, depth, &systime->dayofweek))
+               return False;
+       if(!prs_uint16("day", ps, depth, &systime->day))
+               return False;
+       if(!prs_uint16("hour", ps, depth, &systime->hour))
+               return False;
+       if(!prs_uint16("minute", ps, depth, &systime->minute))
+               return False;
+       if(!prs_uint16("second", ps, depth, &systime->second))
+               return False;
+       if(!prs_uint16("milliseconds", ps, depth, &systime->milliseconds))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL make_systemtime(SYSTEMTIME *systime, struct tm *unixtime)
+{
+       systime->year=unixtime->tm_year+1900;
+       systime->month=unixtime->tm_mon+1;
+       systime->dayofweek=unixtime->tm_wday;
+       systime->day=unixtime->tm_mday;
+       systime->hour=unixtime->tm_hour;
+       systime->minute=unixtime->tm_min;
+       systime->second=unixtime->tm_sec;
+       systime->milliseconds=0;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/  
+
+static BOOL smb_io_doc_info_1(const char *desc, DOC_INFO_1 *info_1, prs_struct *ps, int depth)
+{
+       if (info_1 == NULL) return False;
+
+       prs_debug(ps, depth, desc, "smb_io_doc_info_1");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("p_docname",    ps, depth, &info_1->p_docname))
+               return False;
+       if(!prs_uint32("p_outputfile", ps, depth, &info_1->p_outputfile))
+               return False;
+       if(!prs_uint32("p_datatype",   ps, depth, &info_1->p_datatype))
+               return False;
+
+       if(!smb_io_unistr2("", &info_1->docname,    info_1->p_docname,    ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &info_1->outputfile, info_1->p_outputfile, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &info_1->datatype,   info_1->p_datatype,   ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/  
+
+static BOOL smb_io_doc_info(const char *desc, DOC_INFO *info, prs_struct *ps, int depth)
+{
+       uint32 useless_ptr=0;
+       
+       if (info == NULL) return False;
+
+       prs_debug(ps, depth, desc, "smb_io_doc_info");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+        
+       if(!prs_uint32("switch_value", ps, depth, &info->switch_value))
+               return False;
+       
+       if(!prs_uint32("doc_info_X ptr", ps, depth, &useless_ptr))
+               return False;
+
+       switch (info->switch_value)
+       {
+               case 1: 
+                       if(!smb_io_doc_info_1("",&info->doc_info_1, ps, depth))
+                               return False;
+                       break;
+               case 2:
+                       /*
+                         this is just a placeholder
+                         
+                         MSDN July 1998 says doc_info_2 is only on
+                         Windows 95, and as Win95 doesn't do RPC to print
+                         this case is nearly impossible
+                         
+                         Maybe one day with Windows for dishwasher 2037 ...
+                         
+                       */
+                       /* smb_io_doc_info_2("",&info->doc_info_2, ps, depth); */
+                       break;
+               default:
+                       DEBUG(0,("Something is obviously wrong somewhere !\n"));
+                       break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO_CONTAINER structure.
+********************************************************************/  
+
+static BOOL smb_io_doc_info_container(const char *desc, DOC_INFO_CONTAINER *cont, prs_struct *ps, int depth)
+{
+       if (cont == NULL) return False;
+
+       prs_debug(ps, depth, desc, "smb_io_doc_info_container");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+        
+       if(!prs_uint32("level", ps, depth, &cont->level))
+               return False;
+       
+       if(!smb_io_doc_info("",&cont->docinfo, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE structure.
+********************************************************************/  
+
+/* NOTIFY_OPTION_TYPE and NOTIFY_OPTION_TYPE_DATA are really one
+   structure.  The _TYPE structure is really the deferred referrants (i.e
+   the notify fields array) of the _TYPE structure. -tpot */
+
+static BOOL smb_io_notify_option_type(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_notify_option_type");
+       depth++;
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint16("type", ps, depth, &type->type))
+               return False;
+       if(!prs_uint16("reserved0", ps, depth, &type->reserved0))
+               return False;
+       if(!prs_uint32("reserved1", ps, depth, &type->reserved1))
+               return False;
+       if(!prs_uint32("reserved2", ps, depth, &type->reserved2))
+               return False;
+       if(!prs_uint32("count", ps, depth, &type->count))
+               return False;
+       if(!prs_uint32("fields_ptr", ps, depth, &type->fields_ptr))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE DATA.
+********************************************************************/  
+
+static BOOL smb_io_notify_option_type_data(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "smb_io_notify_option_type_data");
+       depth++;
+       /* if there are no fields just return */
+       if (type->fields_ptr==0)
+               return True;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("count2", ps, depth, &type->count2))
+               return False;
+       
+       if (type->count2 != type->count)
+               DEBUG(4,("What a mess, count was %x now is %x !\n", type->count, type->count2));
+
+       /* parse the option type data */
+       for(i=0;i<type->count2;i++)
+               if(!prs_uint16("fields",ps,depth,&type->fields[i]))
+                       return False;
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/  
+
+static BOOL smb_io_notify_option_type_ctr(const char *desc, SPOOL_NOTIFY_OPTION_TYPE_CTR *ctr , prs_struct *ps, int depth)
+{              
+       int i;
+       
+       prs_debug(ps, depth, desc, "smb_io_notify_option_type_ctr");
+       depth++;
+       if(!prs_uint32("count", ps, depth, &ctr->count))
+               return False;
+
+       /* reading */
+       if (UNMARSHALLING(ps))
+               if((ctr->type=(SPOOL_NOTIFY_OPTION_TYPE *)prs_alloc_mem(ps,ctr->count*sizeof(SPOOL_NOTIFY_OPTION_TYPE))) == NULL)
+                       return False;
+               
+       /* the option type struct */
+       for(i=0;i<ctr->count;i++)
+               if(!smb_io_notify_option_type("", &ctr->type[i] , ps, depth))
+                       return False;
+
+       /* the type associated with the option type struct */
+       for(i=0;i<ctr->count;i++)
+               if(!smb_io_notify_option_type_data("", &ctr->type[i] , ps, depth))
+                       return False;
+       
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/  
+
+static BOOL smb_io_notify_option(const char *desc, SPOOL_NOTIFY_OPTION *option, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_notify_option");
+       depth++;
+       
+       if(!prs_uint32("version", ps, depth, &option->version))
+               return False;
+       if(!prs_uint32("flags", ps, depth, &option->flags))
+               return False;
+       if(!prs_uint32("count", ps, depth, &option->count))
+               return False;
+       if(!prs_uint32("option_type_ptr", ps, depth, &option->option_type_ptr))
+               return False;
+       
+       /* marshalling or unmarshalling, that would work */     
+       if (option->option_type_ptr!=0) {
+               if(!smb_io_notify_option_type_ctr("", &option->ctr ,ps, depth))
+                       return False;
+       }
+       else {
+               option->ctr.type=NULL;
+               option->ctr.count=0;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/  
+
+static BOOL smb_io_notify_info_data(const char *desc,SPOOL_NOTIFY_INFO_DATA *data, prs_struct *ps, int depth)
+{
+       uint32 useless_ptr=0x0FF0ADDE;
+
+       prs_debug(ps, depth, desc, "smb_io_notify_info_data");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint16("type",           ps, depth, &data->type))
+               return False;
+       if(!prs_uint16("field",          ps, depth, &data->field))
+               return False;
+
+       if(!prs_uint32("how many words", ps, depth, &data->size))
+               return False;
+       if(!prs_uint32("id",             ps, depth, &data->id))
+               return False;
+       if(!prs_uint32("how many words", ps, depth, &data->size))
+               return False;
+
+       switch (data->enc_type) {
+
+               /* One and two value data has two uint32 values */
+
+       case NOTIFY_ONE_VALUE:
+       case NOTIFY_TWO_VALUE:
+
+               if(!prs_uint32("value[0]", ps, depth, &data->notify_data.value[0]))
+                       return False;
+               if(!prs_uint32("value[1]", ps, depth, &data->notify_data.value[1]))
+                       return False;
+               break;
+
+               /* Pointers and strings have a string length and a
+                  pointer.  For a string the length is expressed as
+                  the number of uint16 characters plus a trailing
+                  \0\0. */
+
+       case NOTIFY_POINTER:
+
+               if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length ))
+                       return False;
+               if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+                       return False;
+
+               break;
+
+       case NOTIFY_STRING:
+
+               if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+                       return False;
+
+               if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+                       return False;
+
+               break;
+
+       case NOTIFY_SECDESC:
+               if( !prs_uint32( "sd size", ps, depth, &data->notify_data.sd.size ) )
+                       return False;
+               if( !prs_uint32( "pointer", ps, depth, &useless_ptr ) )
+                       return False;
+               
+               break;
+
+       default:
+               DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data\n",
+                         data->enc_type));
+               break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/  
+
+BOOL smb_io_notify_info_data_strings(const char *desc,SPOOL_NOTIFY_INFO_DATA *data,
+                                     prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "smb_io_notify_info_data_strings");
+       depth++;
+       
+       if(!prs_align(ps))
+               return False;
+
+       switch(data->enc_type) {
+
+               /* No data for values */
+
+       case NOTIFY_ONE_VALUE:
+       case NOTIFY_TWO_VALUE:
+
+               break;
+
+               /* Strings start with a length in uint16s */
+
+       case NOTIFY_STRING:
+
+               if (UNMARSHALLING(ps)) {
+                       data->notify_data.data.string = 
+                               (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+                       if (!data->notify_data.data.string) 
+                               return False;
+               }
+
+               if (MARSHALLING(ps))
+                       data->notify_data.data.length /= 2;
+
+               if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+                       return False;
+
+               if (!prs_uint16uni(True, "string", ps, depth, data->notify_data.data.string,
+                                  data->notify_data.data.length))
+                       return False;
+
+               if (MARSHALLING(ps))
+                       data->notify_data.data.length *= 2;
+
+               break;
+
+       case NOTIFY_POINTER:
+
+               if (UNMARSHALLING(ps)) {
+                       data->notify_data.data.string = 
+                               (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+                       if (!data->notify_data.data.string) 
+                               return False;
+               }
+
+               if(!prs_uint8s(True,"buffer",ps,depth,(uint8*)data->notify_data.data.string,data->notify_data.data.length))
+                       return False;
+
+               break;
+
+       case NOTIFY_SECDESC:    
+               if( !prs_uint32("secdesc size ", ps, depth, &data->notify_data.sd.size ) )
+                       return False;
+               if ( !sec_io_desc( "sec_desc", &data->notify_data.sd.desc, ps, depth ) )
+                       return False;
+               break;
+
+       default:
+               DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data_strings\n",
+                         data->enc_type));
+               break;
+       }
+
+#if 0
+       if (isvalue==False) {
+
+               /* length of string in unicode include \0 */
+               x=data->notify_data.data.length+1;
+
+               if (data->field != 16)
+               if(!prs_uint32("string length", ps, depth, &x ))
+                       return False;
+
+               if (MARSHALLING(ps)) {
+                       /* These are already in little endian format. Don't byte swap. */
+                       if (x == 1) {
+
+                               /* No memory allocated for this string
+                                  therefore following the data.string
+                                  pointer is a bad idea.  Use a pointer to
+                                  the uint32 length union member to
+                                  provide a source for a unicode NULL */
+
+                               if(!prs_uint8s(True,"string",ps,depth, (uint8 *)&data->notify_data.data.length,x*2)) 
+                                       return False;
+                       } else {
+
+                               if (data->field == 16)
+                                       x /= 2;
+
+                               if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+                                       return False;
+                       }
+               } else {
+
+                       /* Tallocate memory for string */
+
+                       data->notify_data.data.string = (uint16 *)prs_alloc_mem(ps, x * 2);
+                       if (!data->notify_data.data.string) 
+                               return False;
+
+                       if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+                               return False;
+               }
+       }
+
+#endif
+
+#if 0  /* JERRY */
+       /* Win2k does not seem to put this parse align here */
+       if(!prs_align(ps))
+               return False;
+#endif
+
+       return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO structure.
+********************************************************************/  
+
+static BOOL smb_io_notify_info(const char *desc, SPOOL_NOTIFY_INFO *info, prs_struct *ps, int depth)
+{
+       int i;
+
+       prs_debug(ps, depth, desc, "smb_io_notify_info");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("count", ps, depth, &info->count))
+               return False;
+       if(!prs_uint32("version", ps, depth, &info->version))
+               return False;
+       if(!prs_uint32("flags", ps, depth, &info->flags))
+               return False;
+       if(!prs_uint32("count", ps, depth, &info->count))
+               return False;
+
+       for (i=0;i<info->count;i++) {
+               if(!smb_io_notify_info_data(desc, &info->data[i], ps, depth))
+                       return False;
+       }
+
+       /* now do the strings at the end of the stream */       
+       for (i=0;i<info->count;i++) {
+               if(!smb_io_notify_info_data_strings(desc, &info->data[i], ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+static BOOL spool_io_user_level_1(const char *desc, SPOOL_USER_1 *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "");
+       depth++;
+
+       /* reading */
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(q_u);
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("size", ps, depth, &q_u->size))
+               return False;
+       if (!prs_uint32("client_name_ptr", ps, depth, &q_u->client_name_ptr))
+               return False;
+       if (!prs_uint32("user_name_ptr", ps, depth, &q_u->user_name_ptr))
+               return False;
+       if (!prs_uint32("build", ps, depth, &q_u->build))
+               return False;
+       if (!prs_uint32("major", ps, depth, &q_u->major))
+               return False;
+       if (!prs_uint32("minor", ps, depth, &q_u->minor))
+               return False;
+       if (!prs_uint32("processor", ps, depth, &q_u->processor))
+               return False;
+
+       if (!smb_io_unistr2("", &q_u->client_name, q_u->client_name_ptr, ps, depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("", &q_u->user_name,   q_u->user_name_ptr,   ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+static BOOL spool_io_user_level(const char *desc, SPOOL_USER_CTR *q_u, prs_struct *ps, int depth)
+{
+       if (q_u==NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spool_io_user_level");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       /* From looking at many captures in ethereal, it looks like
+          the level and ptr fields should be transposed.  -tpot */
+
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+       if (!prs_uint32("ptr", ps, depth, &q_u->ptr))
+               return False;
+       
+       switch (q_u->level) {   
+       case 1:
+               if (!spool_io_user_level_1("", &q_u->user1, ps, depth))
+                       return False;
+               break;
+       default:
+               return False;   
+       }       
+
+       return True;
+}
+
+/*******************************************************************
+ * read or write a DEVICEMODE struct.
+ * on reading allocate memory for the private member
+ ********************************************************************/
+
+#define DM_NUM_OPTIONAL_FIELDS                 8
+
+BOOL spoolss_io_devmode(const char *desc, prs_struct *ps, int depth, DEVICEMODE *devmode)
+{
+       uint32 available_space;         /* size of the device mode left to parse */
+                                       /* only important on unmarshalling       */
+       int i = 0;
+                                       
+       struct optional_fields {
+               fstring         name;
+               uint32*         field;
+       } opt_fields[DM_NUM_OPTIONAL_FIELDS] = {
+               { "icmmethod",          NULL },
+               { "icmintent",          NULL },
+               { "mediatype",          NULL },
+               { "dithertype",         NULL },
+               { "reserved1",          NULL },
+               { "reserved2",          NULL },
+               { "panningwidth",       NULL },
+               { "panningheight",      NULL }
+       };
+
+       /* assign at run time to keep non-gcc compilers happy */
+
+       opt_fields[0].field = &devmode->icmmethod;
+       opt_fields[1].field = &devmode->icmintent;
+       opt_fields[2].field = &devmode->mediatype;
+       opt_fields[3].field = &devmode->dithertype;
+       opt_fields[4].field = &devmode->reserved1;
+       opt_fields[5].field = &devmode->reserved2;
+       opt_fields[6].field = &devmode->panningwidth;
+       opt_fields[7].field = &devmode->panningheight;
+               
+       
+       prs_debug(ps, depth, desc, "spoolss_io_devmode");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               devmode->devicename.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+               if (devmode->devicename.buffer == NULL)
+                       return False;
+       }
+
+       if (!prs_uint16uni(True,"devicename", ps, depth, devmode->devicename.buffer, 32))
+               return False;
+       
+       if (!prs_uint16("specversion",      ps, depth, &devmode->specversion))
+               return False;
+               
+       /* Sanity Check - look for unknown specversions, but don't fail if we see one.
+          Let the size determine that */
+          
+       switch (devmode->specversion) {
+               /* list of observed spec version's */
+               case 0x0320:
+               case 0x0400:
+               case 0x0401:
+               case 0x040d:
+                       break;
+                       
+               default:
+                       DEBUG(0,("spoolss_io_devmode: Unknown specversion in devicemode [0x%x]\n",
+                               devmode->specversion));
+                       DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+                       break;
+       }
+                       
+       
+       if (!prs_uint16("driverversion",    ps, depth, &devmode->driverversion))
+               return False;
+       if (!prs_uint16("size",             ps, depth, &devmode->size))
+               return False;
+       if (!prs_uint16("driverextra",      ps, depth, &devmode->driverextra))
+               return False;
+       if (!prs_uint32("fields",           ps, depth, &devmode->fields))
+               return False;
+       if (!prs_uint16("orientation",      ps, depth, &devmode->orientation))
+               return False;
+       if (!prs_uint16("papersize",        ps, depth, &devmode->papersize))
+               return False;
+       if (!prs_uint16("paperlength",      ps, depth, &devmode->paperlength))
+               return False;
+       if (!prs_uint16("paperwidth",       ps, depth, &devmode->paperwidth))
+               return False;
+       if (!prs_uint16("scale",            ps, depth, &devmode->scale))
+               return False;
+       if (!prs_uint16("copies",           ps, depth, &devmode->copies))
+               return False;
+       if (!prs_uint16("defaultsource",    ps, depth, &devmode->defaultsource))
+               return False;
+       if (!prs_uint16("printquality",     ps, depth, &devmode->printquality))
+               return False;
+       if (!prs_uint16("color",            ps, depth, &devmode->color))
+               return False;
+       if (!prs_uint16("duplex",           ps, depth, &devmode->duplex))
+               return False;
+       if (!prs_uint16("yresolution",      ps, depth, &devmode->yresolution))
+               return False;
+       if (!prs_uint16("ttoption",         ps, depth, &devmode->ttoption))
+               return False;
+       if (!prs_uint16("collate",          ps, depth, &devmode->collate))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               devmode->formname.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+               if (devmode->formname.buffer == NULL)
+                       return False;
+       }
+
+       if (!prs_uint16uni(True, "formname",  ps, depth, devmode->formname.buffer, 32))
+               return False;
+       if (!prs_uint16("logpixels",        ps, depth, &devmode->logpixels))
+               return False;
+       if (!prs_uint32("bitsperpel",       ps, depth, &devmode->bitsperpel))
+               return False;
+       if (!prs_uint32("pelswidth",        ps, depth, &devmode->pelswidth))
+               return False;
+       if (!prs_uint32("pelsheight",       ps, depth, &devmode->pelsheight))
+               return False;
+       if (!prs_uint32("displayflags",     ps, depth, &devmode->displayflags))
+               return False;
+       if (!prs_uint32("displayfrequency", ps, depth, &devmode->displayfrequency))
+               return False;
+       /* 
+        * every device mode I've ever seen on the wire at least has up 
+        * to the displayfrequency field.   --jerry (05-09-2002)
+        */
+        
+       /* add uint32's + uint16's + two UNICODE strings */
+        
+       available_space = devmode->size - (sizeof(uint32)*6 + sizeof(uint16)*18 + sizeof(uint16)*64);
+       
+       /* Sanity check - we only have uint32's left tp parse */
+       
+       if ( available_space && ((available_space % sizeof(uint32)) != 0) ) {
+               DEBUG(0,("spoolss_io_devmode: available_space [%d] no in multiple of 4 bytes (size = %d)!\n",
+                       available_space, devmode->size));
+               DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+               return False;
+       }
+
+       /* 
+        * Conditional parsing.  Assume that the DeviceMode has been 
+        * zero'd by the caller. 
+        */
+       
+       while ((available_space > 0)  && (i < DM_NUM_OPTIONAL_FIELDS))
+       {
+               DEBUG(10, ("spoolss_io_devmode: [%d] bytes left to parse in devmode\n", available_space));
+               if (!prs_uint32(opt_fields[i].name, ps, depth, opt_fields[i].field))
+                       return False;
+               available_space -= sizeof(uint32);
+               i++;
+       }        
+       
+       /* Sanity Check - we should no available space at this point unless 
+          MS changes the device mode structure */
+               
+       if (available_space) {
+               DEBUG(0,("spoolss_io_devmode: I've parsed all I know and there is still stuff left|\n"));
+               DEBUG(0,("spoolss_io_devmode: available_space = [%d], devmode_size = [%d]!\n",
+                       available_space, devmode->size));
+               DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+               return False;
+       }
+
+
+       if (devmode->driverextra!=0) {
+               if (UNMARSHALLING(ps)) {
+                       devmode->private=(uint8 *)prs_alloc_mem(ps, devmode->driverextra*sizeof(uint8));
+                       if(devmode->private == NULL)
+                               return False;
+                       DEBUG(7,("spoolss_io_devmode: allocated memory [%d] for private\n",devmode->driverextra)); 
+               }
+                       
+               DEBUG(7,("spoolss_io_devmode: parsing [%d] bytes of private\n",devmode->driverextra));
+               if (!prs_uint8s(False, "private",  ps, depth,
+                               devmode->private, devmode->driverextra))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Read or write a DEVICEMODE container
+********************************************************************/  
+
+static BOOL spoolss_io_devmode_cont(const char *desc, DEVMODE_CTR *dm_c, prs_struct *ps, int depth)
+{
+       if (dm_c==NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_devmode_cont");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if (!prs_uint32("size", ps, depth, &dm_c->size))
+               return False;
+
+       if (!prs_uint32("devmode_ptr", ps, depth, &dm_c->devmode_ptr))
+               return False;
+
+       if (dm_c->size==0 || dm_c->devmode_ptr==0) {
+               if (UNMARSHALLING(ps))
+                       /* if while reading there is no DEVMODE ... */
+                       dm_c->devmode=NULL;
+               return True;
+       }
+       
+       /* so we have a DEVICEMODE to follow */         
+       if (UNMARSHALLING(ps)) {
+               DEBUG(9,("Allocating memory for spoolss_io_devmode\n"));
+               dm_c->devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE));
+               if(dm_c->devmode == NULL)
+                       return False;
+       }
+       
+       /* this is bad code, shouldn't be there */
+       if (!prs_uint32("size", ps, depth, &dm_c->size))
+               return False;
+               
+       if (!spoolss_io_devmode(desc, ps, depth, dm_c->devmode))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+static BOOL spoolss_io_printer_default(const char *desc, PRINTER_DEFAULT *pd, prs_struct *ps, int depth)
+{
+       if (pd==NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_printer_default");
+       depth++;
+
+       if (!prs_uint32("datatype_ptr", ps, depth, &pd->datatype_ptr))
+               return False;
+
+       if (!smb_io_unistr2("datatype", &pd->datatype, pd->datatype_ptr, ps,depth))
+               return False;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!spoolss_io_devmode_cont("", &pd->devmode_cont, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("access_required", ps, depth, &pd->access_required))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_open_printer_ex(SPOOL_Q_OPEN_PRINTER_EX *q_u,
+               const fstring printername, 
+               const fstring datatype, 
+               uint32 access_required,
+               const fstring clientname,
+               const fstring user_name)
+{
+       DEBUG(5,("make_spoolss_q_open_printer_ex\n"));
+       q_u->printername_ptr = (printername!=NULL)?1:0;
+       init_unistr2(&q_u->printername, printername, strlen(printername)+1);
+
+       q_u->printer_default.datatype_ptr = 0;
+/*
+       q_u->printer_default.datatype_ptr = (datatype!=NULL)?1:0;
+       init_unistr2(&q_u->printer_default.datatype, datatype, strlen(datatype));
+*/
+       q_u->printer_default.devmode_cont.size=0;
+       q_u->printer_default.devmode_cont.devmode_ptr=0;
+       q_u->printer_default.devmode_cont.devmode=NULL;
+       q_u->printer_default.access_required=access_required;
+       q_u->user_switch=1;
+       q_u->user_ctr.level=1;
+       q_u->user_ctr.ptr=1;
+       q_u->user_ctr.user1.size=strlen(clientname)+strlen(user_name)+10;
+       q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+       q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+       q_u->user_ctr.user1.build=1381;
+       q_u->user_ctr.user1.major=2;
+       q_u->user_ctr.user1.minor=0;
+       q_u->user_ctr.user1.processor=0;
+       init_unistr2(&q_u->user_ctr.user1.client_name, clientname, strlen(clientname)+1);
+       init_unistr2(&q_u->user_ctr.user1.user_name, user_name, strlen(user_name)+1);
+       
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addprinterex(
+       TALLOC_CTX *mem_ctx,
+       SPOOL_Q_ADDPRINTEREX *q_u, 
+       const char *srv_name,
+       const char* clientname, 
+       const char* user_name,
+       uint32 level, 
+       PRINTER_INFO_CTR *ctr)
+{
+       DEBUG(5,("make_spoolss_q_addprinterex\n"));
+       
+       if (!ctr) return False;
+
+       ZERO_STRUCTP(q_u);
+
+       q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+       init_unistr2(&q_u->server_name, srv_name, strlen(srv_name));
+
+       q_u->level = level;
+       
+       q_u->info.level = level;
+       q_u->info.info_ptr = (ctr->printers_2!=NULL)?1:0;
+       switch (level) {
+               case 2:
+                       /* init q_u->info.info2 from *info */
+                       if (!make_spoolss_printer_info_2(mem_ctx, &q_u->info.info_2, ctr->printers_2)) {
+                               DEBUG(0,("make_spoolss_q_addprinterex: Unable to fill SPOOL_Q_ADDPRINTEREX struct!\n"));
+                               return False;
+                       }
+                       break;
+               default :
+                       break;
+       }
+
+       q_u->user_switch=1;
+
+       q_u->user_ctr.level=1;
+       q_u->user_ctr.ptr=1;
+       q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+       q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+       q_u->user_ctr.user1.build=1381;
+       q_u->user_ctr.user1.major=2;
+       q_u->user_ctr.user1.minor=0;
+       q_u->user_ctr.user1.processor=0;
+       init_unistr2(&q_u->user_ctr.user1.client_name, clientname, strlen(clientname)+1);
+       init_unistr2(&q_u->user_ctr.user1.user_name, user_name, strlen(user_name)+1);
+       q_u->user_ctr.user1.size=q_u->user_ctr.user1.user_name.uni_str_len +
+                                q_u->user_ctr.user1.client_name.uni_str_len + 2;
+       
+       return True;
+}
+       
+/*******************************************************************
+create a SPOOL_PRINTER_INFO_2 stuct from a PRINTER_INFO_2 struct
+*******************************************************************/
+
+BOOL make_spoolss_printer_info_2(TALLOC_CTX *mem_ctx, SPOOL_PRINTER_INFO_LEVEL_2 **spool_info2, 
+                               PRINTER_INFO_2 *info)
+{
+
+       SPOOL_PRINTER_INFO_LEVEL_2 *inf;
+
+       /* allocate the necessary memory */
+       if (!(inf=(SPOOL_PRINTER_INFO_LEVEL_2*)talloc(mem_ctx, sizeof(SPOOL_PRINTER_INFO_LEVEL_2)))) {
+               DEBUG(0,("make_spoolss_printer_info_2: Unable to allocate SPOOL_PRINTER_INFO_LEVEL_2 sruct!\n"));
+               return False;
+       }
+       
+       inf->servername_ptr     = (info->servername.buffer!=NULL)?1:0;
+       inf->printername_ptr    = (info->printername.buffer!=NULL)?1:0;
+       inf->sharename_ptr      = (info->sharename.buffer!=NULL)?1:0;
+       inf->portname_ptr       = (info->portname.buffer!=NULL)?1:0;
+       inf->drivername_ptr     = (info->drivername.buffer!=NULL)?1:0;
+       inf->comment_ptr        = (info->comment.buffer!=NULL)?1:0;
+       inf->location_ptr       = (info->location.buffer!=NULL)?1:0;
+       inf->devmode_ptr        = (info->devmode!=NULL)?1:0;
+       inf->sepfile_ptr        = (info->sepfile.buffer!=NULL)?1:0;
+       inf->printprocessor_ptr = (info->printprocessor.buffer!=NULL)?1:0;
+       inf->datatype_ptr       = (info->datatype.buffer!=NULL)?1:0;
+       inf->parameters_ptr     = (info->parameters.buffer!=NULL)?1:0;
+       inf->secdesc_ptr        = (info->secdesc!=NULL)?1:0;
+       inf->attributes         = info->attributes;
+       inf->priority           = info->priority;
+       inf->default_priority   = info->defaultpriority;
+       inf->starttime          = info->starttime;
+       inf->untiltime          = info->untiltime;
+       inf->cjobs              = info->cjobs;
+       inf->averageppm = info->averageppm;
+       init_unistr2_from_unistr(&inf->servername,      &info->servername);
+       init_unistr2_from_unistr(&inf->printername,     &info->printername);
+       init_unistr2_from_unistr(&inf->sharename,       &info->sharename);
+       init_unistr2_from_unistr(&inf->portname,        &info->portname);
+       init_unistr2_from_unistr(&inf->drivername,      &info->drivername);
+       init_unistr2_from_unistr(&inf->comment,         &info->comment);
+       init_unistr2_from_unistr(&inf->location,        &info->location);
+       init_unistr2_from_unistr(&inf->sepfile,         &info->sepfile);
+       init_unistr2_from_unistr(&inf->printprocessor,  &info->printprocessor);
+       init_unistr2_from_unistr(&inf->datatype,        &info->datatype);
+       init_unistr2_from_unistr(&inf->parameters,      &info->parameters);
+       init_unistr2_from_unistr(&inf->datatype,        &info->datatype);
+
+       *spool_info2 = inf;
+
+       return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer(const char *desc, SPOOL_Q_OPEN_PRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_open_printer");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+               return False;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+               return False;
+               
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer(const char *desc, SPOOL_R_OPEN_PRINTER *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_open_printer");
+       depth++;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+               return False;   
+
+       if (!prs_werror("status code", ps, depth, &(r_u->status)))
+               return False;
+               
+       return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer_ex(const char *desc, SPOOL_Q_OPEN_PRINTER_EX *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_open_printer_ex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+               return False;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+               return False;
+
+       if (!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+               return False;   
+       if (!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer_ex(const char *desc, SPOOL_R_OPEN_PRINTER_EX *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_open_printer_ex");
+       depth++;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+               return False;
+
+       if (!prs_werror("status code", ps, depth, &(r_u->status)))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_deleteprinterdriver(
+       TALLOC_CTX *mem_ctx,
+       SPOOL_Q_DELETEPRINTERDRIVER *q_u, 
+       const char *server,
+       const char* arch, 
+       const char* driver 
+)
+{
+       DEBUG(5,("make_spoolss_q_deleteprinterdriver\n"));
+       
+       q_u->server_ptr = (server!=NULL)?1:0;
+
+       /* these must be NULL terminated or else NT4 will
+          complain about invalid parameters --jerry */
+       init_unistr2(&q_u->server, server, strlen(server)+1);
+       init_unistr2(&q_u->arch, arch, strlen(arch)+1);
+       init_unistr2(&q_u->driver, driver, strlen(driver)+1);
+
+       
+       return True;
+}
+
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdata(SPOOL_Q_GETPRINTERDATA *q_u,
+                                  const POLICY_HND *handle,
+                                  const char *valuename, uint32 size)
+{
+        if (q_u == NULL) return False;
+
+        DEBUG(5,("make_spoolss_q_getprinterdata\n"));
+
+        q_u->handle = *handle;
+       init_unistr2(&q_u->valuename, valuename, strlen(valuename) + 1);
+        q_u->size = size;
+
+        return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdataex(SPOOL_Q_GETPRINTERDATAEX *q_u,
+                                    const POLICY_HND *handle,
+                                    const char *keyname, 
+                                    const char *valuename, uint32 size)
+{
+        if (q_u == NULL) return False;
+
+        DEBUG(5,("make_spoolss_q_getprinterdataex\n"));
+
+        q_u->handle = *handle;
+       init_unistr2(&q_u->valuename, valuename, strlen(valuename) + 1);
+       init_unistr2(&q_u->keyname, keyname, strlen(keyname) + 1);
+        q_u->size = size;
+
+        return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdata(const char *desc, SPOOL_Q_GETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdata");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("size", ps, depth, &q_u->size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdata(const char *desc, SPOOL_Q_DELETEPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdata");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdata(const char *desc, SPOOL_R_DELETEPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdata");
+       depth++;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdataex(const char *desc, SPOOL_Q_DELETEPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdataex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       
+       if (!smb_io_unistr2("keyname  ", &q_u->keyname, True, ps, depth))
+               return False;
+       if (!smb_io_unistr2("valuename", &q_u->valuename, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdataex(const char *desc, SPOOL_R_DELETEPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdataex");
+       depth++;
+       
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdata(const char *desc, SPOOL_R_GETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdata");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("type", ps, depth, &r_u->type))
+               return False;
+       if (!prs_uint32("size", ps, depth, &r_u->size))
+               return False;
+       
+       if (UNMARSHALLING(ps) && r_u->size) {
+               r_u->data = prs_alloc_mem(ps, r_u->size);
+               if(!r_u->data)
+                       return False;
+       }
+
+       if (!prs_uint8s( False, "data", ps, depth, r_u->data, r_u->size ))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+               
+       return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_closeprinter(SPOOL_Q_CLOSEPRINTER *q_u, POLICY_HND *hnd)
+{
+       if (q_u == NULL) return False;
+
+       DEBUG(5,("make_spoolss_q_closeprinter\n"));
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_abortprinter (srv_spoolss.c)
+ * called from spoolss_abortprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_abortprinter(const char *desc, SPOOL_Q_ABORTPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_abortprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_abortprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_abortprinter(const char *desc, SPOOL_R_ABORTPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_abortprinter");
+       depth++;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinter(const char *desc, SPOOL_Q_DELETEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinter(const char *desc, SPOOL_R_DELETEPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinter");
+       depth++;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+               return False;
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+       
+       return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriver(const char *desc, SPOOL_Q_DELETEPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriver");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+               return False;           
+       if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+               return False;
+
+
+       return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriver(const char *desc, SPOOL_R_DELETEPRINTERDRIVER *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriver");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriverex(const char *desc, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriverex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+               return False;           
+       if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("delete_flags ", ps, depth, &q_u->delete_flags))
+               return False;           
+       if(!prs_uint32("version      ", ps, depth, &q_u->version))
+               return False;           
+
+
+       return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriverex(const char *desc, SPOOL_R_DELETEPRINTERDRIVEREX *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriverex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_closeprinter(const char *desc, SPOOL_Q_CLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_closeprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_closeprinter(const char *desc, SPOOL_R_CLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_closeprinter");
+       depth++;
+       
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+               return False;
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startdocprinter(const char *desc, SPOOL_Q_STARTDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_startdocprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       
+       if(!smb_io_doc_info_container("",&q_u->doc_info_container, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startdocprinter(const char *desc, SPOOL_R_STARTDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_startdocprinter");
+       depth++;
+       if(!prs_uint32("jobid", ps, depth, &r_u->jobid))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enddocprinter(const char *desc, SPOOL_Q_ENDDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_enddocprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_enddocprinter(const char *desc, SPOOL_R_ENDDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enddocprinter");
+       depth++;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startpageprinter(const char *desc, SPOOL_Q_STARTPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_startpageprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startpageprinter(const char *desc, SPOOL_R_STARTPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_startpageprinter");
+       depth++;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_endpageprinter(const char *desc, SPOOL_Q_ENDPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_endpageprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_endpageprinter(const char *desc, SPOOL_R_ENDPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_endpageprinter");
+       depth++;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_writeprinter(const char *desc, SPOOL_Q_WRITEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL) return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_writeprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if(!prs_uint32("buffer_size", ps, depth, &q_u->buffer_size))
+               return False;
+       
+       if (q_u->buffer_size!=0)
+       {
+               if (UNMARSHALLING(ps))
+                       q_u->buffer=(uint8 *)prs_alloc_mem(ps,q_u->buffer_size*sizeof(uint8));
+               if(q_u->buffer == NULL)
+                       return False;   
+               if(!prs_uint8s(True, "buffer", ps, depth, q_u->buffer, q_u->buffer_size))
+                       return False;
+       }
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("buffer_size2", ps, depth, &q_u->buffer_size2))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_writeprinter(const char *desc, SPOOL_R_WRITEPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_writeprinter");
+       depth++;
+       if(!prs_uint32("buffer_written", ps, depth, &r_u->buffer_written))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rffpcnex(const char *desc, SPOOL_Q_RFFPCNEX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_rffpcnex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!prs_uint32("flags", ps, depth, &q_u->flags))
+               return False;
+       if(!prs_uint32("options", ps, depth, &q_u->options))
+               return False;
+       if(!prs_uint32("localmachine_ptr", ps, depth, &q_u->localmachine_ptr))
+               return False;
+       if(!smb_io_unistr2("localmachine", &q_u->localmachine, q_u->localmachine_ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if(!prs_uint32("printerlocal", ps, depth, &q_u->printerlocal))
+               return False;
+
+       if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+               return False;
+       
+       if (q_u->option_ptr!=0) {
+       
+               if (UNMARSHALLING(ps))
+                       if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+                               return False;
+       
+               if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+                       return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rffpcnex(const char *desc, SPOOL_R_RFFPCNEX *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_rffpcnex");
+       depth++;
+
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rfnpcnex(const char *desc, SPOOL_Q_RFNPCNEX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_rfnpcnex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       if(!prs_uint32("change", ps, depth, &q_u->change))
+               return False;
+       
+       if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+               return False;
+       
+       if (q_u->option_ptr!=0) {
+       
+               if (UNMARSHALLING(ps))
+                       if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+                               return False;
+       
+               if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rfnpcnex(const char *desc, SPOOL_R_RFNPCNEX *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_rfnpcnex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("info_ptr", ps, depth, &r_u->info_ptr))
+               return False;
+
+       if(!smb_io_notify_info("notify info", &r_u->info ,ps,depth))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * return the length of a uint16 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint16(uint16 *value)
+{
+       return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint32(uint32 *value)
+{
+       return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a NTTIME (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_nttime(NTTIME *value)
+{
+       return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a UNICODE string in number of char, includes:
+ * - the leading zero
+ * - the relative pointer size
+ ********************************************************************/
+
+static uint32 size_of_relative_string(UNISTR *string)
+{
+       uint32 size=0;
+       
+       size=str_len_uni(string);       /* the string length       */
+       size=size+1;                    /* add the trailing zero   */
+       size=size*2;                    /* convert in char         */
+       size=size+4;                    /* add the size of the ptr */   
+
+#if 0  /* JERRY */
+       /* 
+        * Do not include alignment as Win2k does not align relative
+        * strings within a buffer   --jerry 
+        */
+       /* Ensure size is 4 byte multiple (prs_align is being called...). */
+       /* size += ((4 - (size & 3)) & 3); */
+#endif 
+
+       return size;
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_device_mode(DEVICEMODE *devmode)
+{
+       if (devmode==NULL)
+               return (4);
+       else 
+               return (4+devmode->size+devmode->driverextra);
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_systemtime(SYSTEMTIME *systime)
+{
+       if (systime==NULL)
+               return (4);
+       else 
+               return (sizeof(SYSTEMTIME) +4);
+}
+
+/*******************************************************************
+ * write a UNICODE string and its relative pointer.
+ * used by all the RPC structs passing a buffer
+ *
+ * As I'm a nice guy, I'm forcing myself to explain this code.
+ * MS did a good job in the overall spoolss code except in some
+ * functions where they are passing the API buffer directly in the
+ * RPC request/reply. That's to maintain compatiility at the API level.
+ * They could have done it the good way the first time.
+ *
+ * So what happen is: the strings are written at the buffer's end, 
+ * in the reverse order of the original structure. Some pointers to
+ * the strings are also in the buffer. Those are relative to the
+ * buffer's start.
+ *
+ * If you don't understand or want to change that function,
+ * first get in touch with me: jfm@samba.org
+ *
+ ********************************************************************/
+
+static BOOL smb_io_relstr(const char *desc, NEW_BUFFER *buffer, int depth, UNISTR *string)
+{
+       prs_struct *ps=&buffer->prs;
+       
+       if (MARSHALLING(ps)) {
+               uint32 struct_offset = prs_offset(ps);
+               uint32 relative_offset;
+               
+               buffer->string_at_end -= (size_of_relative_string(string) - 4);
+               if(!prs_set_offset(ps, buffer->string_at_end))
+                       return False;
+#if 0  /* JERRY */
+               /*
+                * Win2k does not align strings in a buffer
+                * Tested against WinNT 4.0 SP 6a & 2k SP2  --jerry
+                */
+               if (!prs_align(ps))
+                       return False;
+#endif
+               buffer->string_at_end = prs_offset(ps);
+               
+               /* write the string */
+               if (!smb_io_unistr(desc, string, ps, depth))
+                       return False;
+
+               if(!prs_set_offset(ps, struct_offset))
+                       return False;
+               
+               relative_offset=buffer->string_at_end - buffer->struct_start;
+               /* write its offset */
+               if (!prs_uint32("offset", ps, depth, &relative_offset))
+                       return False;
+       }
+       else {
+               uint32 old_offset;
+               
+               /* read the offset */
+               if (!prs_uint32("offset", ps, depth, &(buffer->string_at_end)))
+                       return False;
+
+               if (buffer->string_at_end == 0)
+                       return True;
+
+               old_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, buffer->string_at_end+buffer->struct_start))
+                       return False;
+
+               /* read the string */
+               if (!smb_io_unistr(desc, string, ps, depth))
+                       return False;
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ * write a array of UNICODE strings and its relative pointer.
+ * used by 2 RPC structs
+ ********************************************************************/
+
+static BOOL smb_io_relarraystr(const char *desc, NEW_BUFFER *buffer, int depth, uint16 **string)
+{
+       UNISTR chaine;
+       
+       prs_struct *ps=&buffer->prs;
+       
+       if (MARSHALLING(ps)) {
+               uint32 struct_offset = prs_offset(ps);
+               uint32 relative_offset;
+               uint16 *p;
+               uint16 *q;
+               uint16 zero=0;
+               p=*string;
+               q=*string;
+
+               /* first write the last 0 */
+               buffer->string_at_end -= 2;
+               if(!prs_set_offset(ps, buffer->string_at_end))
+                       return False;
+
+               if(!prs_uint16("leading zero", ps, depth, &zero))
+                       return False;
+
+               while (p && (*p!=0)) {  
+                       while (*q!=0)
+                               q++;
+
+                       /* Yes this should be malloc not talloc. Don't change. */
+
+                       chaine.buffer = malloc((q-p+1)*sizeof(uint16));
+                       if (chaine.buffer == NULL)
+                               return False;
+
+                       memcpy(chaine.buffer, p, (q-p+1)*sizeof(uint16));
+
+                       buffer->string_at_end -= (q-p+1)*sizeof(uint16);
+
+                       if(!prs_set_offset(ps, buffer->string_at_end)) {
+                               SAFE_FREE(chaine.buffer);
+                               return False;
+                       }
+
+                       /* write the string */
+                       if (!smb_io_unistr(desc, &chaine, ps, depth)) {
+                               SAFE_FREE(chaine.buffer);
+                               return False;
+                       }
+                       q++;
+                       p=q;
+
+                       SAFE_FREE(chaine.buffer);
+               }
+               
+               if(!prs_set_offset(ps, struct_offset))
+                       return False;
+               
+               relative_offset=buffer->string_at_end - buffer->struct_start;
+               /* write its offset */
+               if (!prs_uint32("offset", ps, depth, &relative_offset))
+                       return False;
+
+       } else {
+
+               /* UNMARSHALLING */
+
+               uint32 old_offset;
+               uint16 *chaine2=NULL;
+               int l_chaine=0;
+               int l_chaine2=0;
+               size_t realloc_size = 0;
+
+               *string=NULL;
+                               
+               /* read the offset */
+               if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+                       return False;
+
+               old_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+                       return False;
+       
+               do {
+                       if (!smb_io_unistr(desc, &chaine, ps, depth))
+                               return False;
+                       
+                       l_chaine=str_len_uni(&chaine);
+                       
+                       /* we're going to add two more bytes here in case this
+                          is the last string in the array and we need to add 
+                          an extra NULL for termination */
+                       if (l_chaine > 0)
+                       {
+                               uint16 *tc2;
+                       
+                               realloc_size = (l_chaine2+l_chaine+2)*sizeof(uint16);
+
+                               /* Yes this should be realloc - it's freed below. JRA */
+
+                               if((tc2=(uint16 *)Realloc(chaine2, realloc_size)) == NULL) {
+                                       SAFE_FREE(chaine2);
+                                       return False;
+                               }
+                               else chaine2 = tc2;
+                               memcpy(chaine2+l_chaine2, chaine.buffer, (l_chaine+1)*sizeof(uint16));
+                               l_chaine2+=l_chaine+1;
+                       }
+               
+               } while(l_chaine!=0);
+               
+               /* the end should be bould NULL terminated so add 
+                  the second one here */
+               if (chaine2)
+               {
+                       chaine2[l_chaine2] = '\0';
+                       *string=(uint16 *)talloc_memdup(prs_get_mem_context(ps),chaine2,realloc_size);
+                       SAFE_FREE(chaine2);
+               }
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_relsecdesc(const char *desc, NEW_BUFFER *buffer, int depth, SEC_DESC **secdesc)
+{
+       prs_struct *ps= &buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_relsecdesc");
+       depth++;
+
+       if (MARSHALLING(ps)) {
+               uint32 struct_offset = prs_offset(ps);
+               uint32 relative_offset;
+
+               if (! *secdesc) {
+                       relative_offset = 0;
+                       if (!prs_uint32("offset", ps, depth, &relative_offset))
+                               return False;
+                       return True;
+               }
+               
+               if (*secdesc != NULL) {
+                       buffer->string_at_end -= sec_desc_size(*secdesc);
+
+                       if(!prs_set_offset(ps, buffer->string_at_end))
+                               return False;
+                       /* write the secdesc */
+                       if (!sec_io_desc(desc, secdesc, ps, depth))
+                               return False;
+
+                       if(!prs_set_offset(ps, struct_offset))
+                               return False;
+               }
+
+               relative_offset=buffer->string_at_end - buffer->struct_start;
+               /* write its offset */
+
+               if (!prs_uint32("offset", ps, depth, &relative_offset))
+                       return False;
+       } else {
+               uint32 old_offset;
+               
+               /* read the offset */
+               if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+                       return False;
+
+               old_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+                       return False;
+
+               /* read the sd */
+               if (!sec_io_desc(desc, secdesc, ps, depth))
+                       return False;
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_reldevmode(const char *desc, NEW_BUFFER *buffer, int depth, DEVICEMODE **devmode)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_reldevmode");
+       depth++;
+
+       if (MARSHALLING(ps)) {
+               uint32 struct_offset = prs_offset(ps);
+               uint32 relative_offset;
+               
+               if (*devmode == NULL) {
+                       relative_offset=0;
+                       if (!prs_uint32("offset", ps, depth, &relative_offset))
+                               return False;
+                       DEBUG(8, ("boing, the devmode was NULL\n"));
+                       
+                       return True;
+               }
+               
+               buffer->string_at_end -= ((*devmode)->size + (*devmode)->driverextra);
+               
+               if(!prs_set_offset(ps, buffer->string_at_end))
+                       return False;
+               
+               /* write the DEVMODE */
+               if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+                       return False;
+
+               if(!prs_set_offset(ps, struct_offset))
+                       return False;
+               
+               relative_offset=buffer->string_at_end - buffer->struct_start;
+               /* write its offset */
+               if (!prs_uint32("offset", ps, depth, &relative_offset))
+                       return False;
+       }
+       else {
+               uint32 old_offset;
+               
+               /* read the offset */
+               if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+                       return False;
+               if (buffer->string_at_end == 0) {
+                       *devmode = NULL;
+                       return True;
+               }
+
+               old_offset = prs_offset(ps);
+               if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+                       return False;
+
+               /* read the string */
+               if((*devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE))) == NULL)
+                       return False;
+               if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+                       return False;
+
+               if(!prs_set_offset(ps, old_offset))
+                       return False;
+       }
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_0 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_0(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_0 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_0");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+               return False;
+       
+       if(!prs_uint32("cjobs", ps, depth, &info->cjobs))
+               return False;
+       if(!prs_uint32("total_jobs", ps, depth, &info->total_jobs))
+               return False;
+       if(!prs_uint32("total_bytes", ps, depth, &info->total_bytes))
+               return False;
+
+       if(!prs_uint16("year", ps, depth, &info->year))
+               return False;
+       if(!prs_uint16("month", ps, depth, &info->month))
+               return False;
+       if(!prs_uint16("dayofweek", ps, depth, &info->dayofweek))
+               return False;
+       if(!prs_uint16("day", ps, depth, &info->day))
+               return False;
+       if(!prs_uint16("hour", ps, depth, &info->hour))
+               return False;
+       if(!prs_uint16("minute", ps, depth, &info->minute))
+               return False;
+       if(!prs_uint16("second", ps, depth, &info->second))
+               return False;
+       if(!prs_uint16("milliseconds", ps, depth, &info->milliseconds))
+               return False;
+
+       if(!prs_uint32("global_counter", ps, depth, &info->global_counter))
+               return False;
+       if(!prs_uint32("total_pages", ps, depth, &info->total_pages))
+               return False;
+
+       if(!prs_uint16("major_version", ps, depth, &info->major_version))
+               return False;
+       if(!prs_uint16("build_version", ps, depth, &info->build_version))
+               return False;
+       if(!prs_uint32("unknown7", ps, depth, &info->unknown7))
+               return False;
+       if(!prs_uint32("unknown8", ps, depth, &info->unknown8))
+               return False;
+       if(!prs_uint32("unknown9", ps, depth, &info->unknown9))
+               return False;
+       if(!prs_uint32("session_counter", ps, depth, &info->session_counter))
+               return False;
+       if(!prs_uint32("unknown11", ps, depth, &info->unknown11))
+               return False;
+       if(!prs_uint32("printer_errors", ps, depth, &info->printer_errors))
+               return False;
+       if(!prs_uint32("unknown13", ps, depth, &info->unknown13))
+               return False;
+       if(!prs_uint32("unknown14", ps, depth, &info->unknown14))
+               return False;
+       if(!prs_uint32("unknown15", ps, depth, &info->unknown15))
+               return False;
+       if(!prs_uint32("unknown16", ps, depth, &info->unknown16))
+               return False;
+       if(!prs_uint32("change_id", ps, depth, &info->change_id))
+               return False;
+       if(!prs_uint32("unknown18", ps, depth, &info->unknown18))
+               return False;
+       if(!prs_uint32("status"   , ps, depth, &info->status))
+               return False;
+       if(!prs_uint32("unknown20", ps, depth, &info->unknown20))
+               return False;
+       if(!prs_uint32("c_setprinter", ps, depth, &info->c_setprinter))
+               return False;
+       if(!prs_uint16("unknown22", ps, depth, &info->unknown22))
+               return False;
+       if(!prs_uint16("unknown23", ps, depth, &info->unknown23))
+               return False;
+       if(!prs_uint16("unknown24", ps, depth, &info->unknown24))
+               return False;
+       if(!prs_uint16("unknown25", ps, depth, &info->unknown25))
+               return False;
+       if(!prs_uint16("unknown26", ps, depth, &info->unknown26))
+               return False;
+       if(!prs_uint16("unknown27", ps, depth, &info->unknown27))
+               return False;
+       if(!prs_uint16("unknown28", ps, depth, &info->unknown28))
+               return False;
+       if(!prs_uint16("unknown29", ps, depth, &info->unknown29))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_1 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_1(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_1");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!prs_uint32("flags", ps, depth, &info->flags))
+               return False;
+       if (!smb_io_relstr("description", buffer, depth, &info->description))
+               return False;
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+       if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+               return False;   
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_2 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_2(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_2 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+       uint32 dm_offset, sd_offset, current_offset;
+       uint32 dummy_value = 0, has_secdesc = 0;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_2");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+               return False;
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("sharename", buffer, depth, &info->sharename))
+               return False;
+       if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+               return False;
+       if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+               return False;
+       if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+               return False;
+       if (!smb_io_relstr("location", buffer, depth, &info->location))
+               return False;
+
+       /* save current offset and wind forwared by a uint32 */
+       dm_offset = prs_offset(ps);
+       if (!prs_uint32("devmode", ps, depth, &dummy_value))
+               return False;
+       
+       if (!smb_io_relstr("sepfile", buffer, depth, &info->sepfile))
+               return False;
+       if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+               return False;
+       if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+               return False;
+       if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+               return False;
+
+       /* save current offset for the sec_desc */
+       sd_offset = prs_offset(ps);
+       if (!prs_uint32("sec_desc", ps, depth, &has_secdesc))
+               return False;
+
+       
+       /* save current location so we can pick back up here */
+       current_offset = prs_offset(ps);
+       
+       /* parse the devmode */
+       if (!prs_set_offset(ps, dm_offset))
+               return False;
+       if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+               return False;
+       
+       /* parse the sec_desc */
+       if (has_secdesc) {
+               if (!prs_set_offset(ps, sd_offset))
+                       return False;
+               if (!smb_io_relsecdesc("secdesc", buffer, depth, &info->secdesc))
+                       return False;
+       }
+
+       /* pick up where we left off */
+       if (!prs_set_offset(ps, current_offset))
+               return False;
+
+       if (!prs_uint32("attributes", ps, depth, &info->attributes))
+               return False;
+       if (!prs_uint32("priority", ps, depth, &info->priority))
+               return False;
+       if (!prs_uint32("defpriority", ps, depth, &info->defaultpriority))
+               return False;
+       if (!prs_uint32("starttime", ps, depth, &info->starttime))
+               return False;
+       if (!prs_uint32("untiltime", ps, depth, &info->untiltime))
+               return False;
+       if (!prs_uint32("status", ps, depth, &info->status))
+               return False;
+       if (!prs_uint32("jobs", ps, depth, &info->cjobs))
+               return False;
+       if (!prs_uint32("averageppm", ps, depth, &info->averageppm))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_3 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_3(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_3 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_3");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!prs_uint32("flags", ps, depth, &info->flags))
+               return False;
+       if (!sec_io_desc("sec_desc", &info->secdesc, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_4 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_4(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_4 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_4");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+               return False;
+       if (!prs_uint32("attributes", ps, depth, &info->attributes))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_5 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_5(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_5 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_5");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+               return False;
+       if (!prs_uint32("attributes", ps, depth, &info->attributes))
+               return False;
+       if (!prs_uint32("device_not_selected_timeout", ps, depth, &info->device_not_selected_timeout))
+               return False;
+       if (!prs_uint32("transmission_retry_timeout", ps, depth, &info->transmission_retry_timeout))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_7 structure.
+********************************************************************/  
+
+BOOL smb_io_printer_info_7(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_7 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_info_7");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("guid", buffer, depth, &info->guid))
+               return False;
+       if (!prs_uint32("action", ps, depth, &info->action))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/  
+
+BOOL smb_io_port_info_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_port_info_1");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/  
+
+BOOL smb_io_port_info_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_port_info_2");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+               return False;
+       if (!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+               return False;
+       if (!smb_io_relstr("description", buffer, depth, &info->description))
+               return False;
+       if (!prs_uint32("port_type", ps, depth, &info->port_type))
+               return False;
+       if (!prs_uint32("reserved", ps, depth, &info->reserved))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_1(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_1 *info, int depth) 
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_driver_info_1");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_2(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_2 *info, int depth) 
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_driver_info_2");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!prs_uint32("version", ps, depth, &info->version))
+               return False;
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+       if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+               return False;
+       if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+               return False;
+       if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+               return False;
+       if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_3 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_3(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_3 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_driver_info_3");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!prs_uint32("version", ps, depth, &info->version))
+               return False;
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+       if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+               return False;
+       if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+               return False;
+       if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+               return False;
+       if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+               return False;
+       if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+               return False;
+
+       if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+               return False;
+
+       if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+               return False;
+       if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_6 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_6(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_6 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printer_driver_info_6");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!prs_uint32("version", ps, depth, &info->version))
+               return False;
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+       if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+               return False;
+       if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+               return False;
+       if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+               return False;
+       if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+               return False;
+       if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+               return False;
+
+       if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+               return False;
+
+       if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+               return False;
+       if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+               return False;
+
+       if (!smb_io_relarraystr("previousdrivernames", buffer, depth, &info->previousdrivernames))
+               return False;
+
+       if (!prs_uint32("date.low", ps, depth, &info->driver_date.low))
+               return False;
+       if (!prs_uint32("date.high", ps, depth, &info->driver_date.high))
+               return False;
+
+       if (!prs_uint32("padding", ps, depth, &info->padding))
+               return False;
+
+       if (!prs_uint32("driver_version_low", ps, depth, &info->driver_version_low))
+               return False;
+
+       if (!prs_uint32("driver_version_high", ps, depth, &info->driver_version_high))
+               return False;
+
+       if (!smb_io_relstr("mfgname", buffer, depth, &info->mfgname))
+               return False;
+       if (!smb_io_relstr("oem_url", buffer, depth, &info->oem_url))
+               return False;
+       if (!smb_io_relstr("hardware_id", buffer, depth, &info->hardware_id))
+               return False;
+       if (!smb_io_relstr("provider", buffer, depth, &info->provider))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_1 structure.
+********************************************************************/  
+
+BOOL smb_io_job_info_1(const char *desc, NEW_BUFFER *buffer, JOB_INFO_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_job_info_1");
+       depth++;        
+       
+       buffer->struct_start=prs_offset(ps);
+
+       if (!prs_uint32("jobid", ps, depth, &info->jobid))
+               return False;
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+               return False;
+       if (!smb_io_relstr("username", buffer, depth, &info->username))
+               return False;
+       if (!smb_io_relstr("document", buffer, depth, &info->document))
+               return False;
+       if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+               return False;
+       if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+               return False;
+       if (!prs_uint32("status", ps, depth, &info->status))
+               return False;
+       if (!prs_uint32("priority", ps, depth, &info->priority))
+               return False;
+       if (!prs_uint32("position", ps, depth, &info->position))
+               return False;
+       if (!prs_uint32("totalpages", ps, depth, &info->totalpages))
+               return False;
+       if (!prs_uint32("pagesprinted", ps, depth, &info->pagesprinted))
+               return False;
+       if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_2 structure.
+********************************************************************/  
+
+BOOL smb_io_job_info_2(const char *desc, NEW_BUFFER *buffer, JOB_INFO_2 *info, int depth)
+{      
+       uint32 pipo=0;
+       prs_struct *ps=&buffer->prs;
+       
+       prs_debug(ps, depth, desc, "smb_io_job_info_2");
+       depth++;        
+
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!prs_uint32("jobid",ps, depth, &info->jobid))
+               return False;
+       if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+               return False;
+       if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+               return False;
+       if (!smb_io_relstr("username", buffer, depth, &info->username))
+               return False;
+       if (!smb_io_relstr("document", buffer, depth, &info->document))
+               return False;
+       if (!smb_io_relstr("notifyname", buffer, depth, &info->notifyname))
+               return False;
+       if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+               return False;
+
+       if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+               return False;
+       if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+               return False;
+       if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+               return False;
+       if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+               return False;
+       if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+               return False;
+
+/*     SEC_DESC sec_desc;*/
+       if (!prs_uint32("Hack! sec desc", ps, depth, &pipo))
+               return False;
+
+       if (!prs_uint32("status",ps, depth, &info->status))
+               return False;
+       if (!prs_uint32("priority",ps, depth, &info->priority))
+               return False;
+       if (!prs_uint32("position",ps, depth, &info->position)) 
+               return False;
+       if (!prs_uint32("starttime",ps, depth, &info->starttime))
+               return False;
+       if (!prs_uint32("untiltime",ps, depth, &info->untiltime))       
+               return False;
+       if (!prs_uint32("totalpages",ps, depth, &info->totalpages))
+               return False;
+       if (!prs_uint32("size",ps, depth, &info->size))
+               return False;
+       if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted) )
+               return False;
+       if (!prs_uint32("timeelapsed",ps, depth, &info->timeelapsed))
+               return False;
+       if (!prs_uint32("pagesprinted",ps, depth, &info->pagesprinted))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL smb_io_form_1(const char *desc, NEW_BUFFER *buffer, FORM_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+       
+       prs_debug(ps, depth, desc, "smb_io_form_1");
+       depth++;
+               
+       buffer->struct_start=prs_offset(ps);
+       
+       if (!prs_uint32("flag", ps, depth, &info->flag))
+               return False;
+               
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+
+       if (!prs_uint32("width", ps, depth, &info->width))
+               return False;
+       if (!prs_uint32("length", ps, depth, &info->length))
+               return False;
+       if (!prs_uint32("left", ps, depth, &info->left))
+               return False;
+       if (!prs_uint32("top", ps, depth, &info->top))
+               return False;
+       if (!prs_uint32("right", ps, depth, &info->right))
+               return False;
+       if (!prs_uint32("bottom", ps, depth, &info->bottom))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Read/write a BUFFER struct.
+********************************************************************/  
+
+static BOOL spoolss_io_buffer(const char *desc, prs_struct *ps, int depth, NEW_BUFFER **pp_buffer)
+{
+       NEW_BUFFER *buffer = *pp_buffer;
+
+       prs_debug(ps, depth, desc, "spoolss_io_buffer");
+       depth++;
+       
+       if (UNMARSHALLING(ps))
+               buffer = *pp_buffer = (NEW_BUFFER *)prs_alloc_mem(ps, sizeof(NEW_BUFFER));
+
+       if (buffer == NULL)
+               return False;
+
+       if (!prs_uint32("ptr", ps, depth, &buffer->ptr))
+               return False;
+       
+       /* reading */
+       if (UNMARSHALLING(ps)) {
+               buffer->size=0;
+               buffer->string_at_end=0;
+               
+               if (buffer->ptr==0) {
+                       /*
+                        * JRA. I'm not sure if the data in here is in big-endian format if
+                        * the client is big-endian. Leave as default (little endian) for now.
+                        */
+
+                       if (!prs_init(&buffer->prs, 0, prs_get_mem_context(ps), UNMARSHALL))
+                               return False;
+                       return True;
+               }
+               
+               if (!prs_uint32("size", ps, depth, &buffer->size))
+                       return False;
+                                       
+               /*
+                * JRA. I'm not sure if the data in here is in big-endian format if
+                * the client is big-endian. Leave as default (little endian) for now.
+                */
+
+               if (!prs_init(&buffer->prs, buffer->size, prs_get_mem_context(ps), UNMARSHALL))
+                       return False;
+
+               if (!prs_append_some_prs_data(&buffer->prs, ps, prs_offset(ps), buffer->size))
+                       return False;
+
+               if (!prs_set_offset(&buffer->prs, 0))
+                       return False;
+
+               if (!prs_set_offset(ps, buffer->size+prs_offset(ps)))
+                       return False;
+
+               buffer->string_at_end=buffer->size;
+               
+               return True;
+       }
+       else {
+               BOOL ret = False;
+
+               /* writing */
+               if (buffer->ptr==0) {
+                       /* We have finished with the data in buffer->prs - free it. */
+                       prs_mem_free(&buffer->prs);
+                       return True;
+               }
+       
+               if (!prs_uint32("size", ps, depth, &buffer->size))
+                       goto out;
+
+               if (!prs_append_some_prs_data(ps, &buffer->prs, 0, buffer->size))
+                       goto out;
+
+               ret = True;
+       out:
+
+               /* We have finished with the data in buffer->prs - free it. */
+               prs_mem_free(&buffer->prs);
+
+               return ret;
+       }
+}
+
+/*******************************************************************
+ move a BUFFER from the query to the reply.
+ As the data pointers in NEW_BUFFER are malloc'ed, not talloc'ed,
+ this is ok. This is an OPTIMIZATION and is not strictly neccessary.
+ Clears the memory to zero also.
+********************************************************************/  
+
+void spoolss_move_buffer(NEW_BUFFER *src, NEW_BUFFER **dest)
+{
+       prs_switch_type(&src->prs, MARSHALL);
+       if(!prs_set_offset(&src->prs, 0))
+               return;
+       prs_force_dynamic(&src->prs);
+       prs_mem_clear(&src->prs);
+       *dest=src;
+}
+
+/*******************************************************************
+ Get the size of a BUFFER struct.
+********************************************************************/  
+
+uint32 new_get_buffer_size(NEW_BUFFER *buffer)
+{
+       return (buffer->size);
+}
+
+/*******************************************************************
+ Parse a DRIVER_DIRECTORY_1 structure.
+********************************************************************/  
+
+BOOL smb_io_driverdir_1(const char *desc, NEW_BUFFER *buffer, DRIVER_DIRECTORY_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_driverdir_1");
+       depth++;
+
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_unistr(desc, &info->name, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/  
+
+BOOL smb_io_port_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_port_1");
+       depth++;
+
+       buffer->struct_start=prs_offset(ps);
+
+       if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/  
+
+BOOL smb_io_port_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_port_2");
+       depth++;
+
+       buffer->struct_start=prs_offset(ps);
+
+       if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+               return False;
+       if(!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+               return False;
+       if(!smb_io_relstr("description", buffer, depth, &info->description))
+               return False;
+       if(!prs_uint32("port_type", ps, depth, &info->port_type))
+               return False;
+       if(!prs_uint32("reserved", ps, depth, &info->reserved))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL smb_io_printprocessor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printprocessor_info_1");
+       depth++;        
+
+       buffer->struct_start=prs_offset(ps);
+       
+       if (smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL smb_io_printprocdatatype_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCDATATYPE_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printprocdatatype_info_1");
+       depth++;        
+
+       buffer->struct_start=prs_offset(ps);
+       
+       if (smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL smb_io_printmonitor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printmonitor_info_1");
+       depth++;        
+
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL smb_io_printmonitor_info_2(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_2 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printmonitor_info_2");
+       depth++;        
+
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_relstr("name", buffer, depth, &info->name))
+               return False;
+       if (!smb_io_relstr("environment", buffer, depth, &info->environment))
+               return False;
+       if (!smb_io_relstr("dll_name", buffer, depth, &info->dll_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printer_info_0(PRINTER_INFO_0 *info)
+{
+       int size=0;
+       
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->servername );
+
+       size+=size_of_uint32( &info->cjobs);
+       size+=size_of_uint32( &info->total_jobs);
+       size+=size_of_uint32( &info->total_bytes);
+
+       size+=size_of_uint16( &info->year);
+       size+=size_of_uint16( &info->month);
+       size+=size_of_uint16( &info->dayofweek);
+       size+=size_of_uint16( &info->day);
+       size+=size_of_uint16( &info->hour);
+       size+=size_of_uint16( &info->minute);
+       size+=size_of_uint16( &info->second);
+       size+=size_of_uint16( &info->milliseconds);
+
+       size+=size_of_uint32( &info->global_counter);
+       size+=size_of_uint32( &info->total_pages);
+
+       size+=size_of_uint16( &info->major_version);
+       size+=size_of_uint16( &info->build_version);
+
+       size+=size_of_uint32( &info->unknown7);
+       size+=size_of_uint32( &info->unknown8);
+       size+=size_of_uint32( &info->unknown9);
+       size+=size_of_uint32( &info->session_counter);
+       size+=size_of_uint32( &info->unknown11);
+       size+=size_of_uint32( &info->printer_errors);
+       size+=size_of_uint32( &info->unknown13);
+       size+=size_of_uint32( &info->unknown14);
+       size+=size_of_uint32( &info->unknown15);
+       size+=size_of_uint32( &info->unknown16);
+       size+=size_of_uint32( &info->change_id);
+       size+=size_of_uint32( &info->unknown18);
+       size+=size_of_uint32( &info->status);
+       size+=size_of_uint32( &info->unknown20);
+       size+=size_of_uint32( &info->c_setprinter);
+       
+       size+=size_of_uint16( &info->unknown22);
+       size+=size_of_uint16( &info->unknown23);
+       size+=size_of_uint16( &info->unknown24);
+       size+=size_of_uint16( &info->unknown25);
+       size+=size_of_uint16( &info->unknown26);
+       size+=size_of_uint16( &info->unknown27);
+       size+=size_of_uint16( &info->unknown28);
+       size+=size_of_uint16( &info->unknown29);
+       
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printer_info_1(PRINTER_INFO_1 *info)
+{
+       int size=0;
+               
+       size+=size_of_uint32( &info->flags );   
+       size+=size_of_relative_string( &info->description );
+       size+=size_of_relative_string( &info->name );
+       size+=size_of_relative_string( &info->comment );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_2(PRINTER_INFO_2 *info)
+{
+       uint32 size=0;
+               
+       size += 4;
+       
+       size += sec_desc_size( info->secdesc );
+
+       size+=size_of_device_mode( info->devmode );
+       
+       size+=size_of_relative_string( &info->servername );
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->sharename );
+       size+=size_of_relative_string( &info->portname );
+       size+=size_of_relative_string( &info->drivername );
+       size+=size_of_relative_string( &info->comment );
+       size+=size_of_relative_string( &info->location );
+       
+       size+=size_of_relative_string( &info->sepfile );
+       size+=size_of_relative_string( &info->printprocessor );
+       size+=size_of_relative_string( &info->datatype );
+       size+=size_of_relative_string( &info->parameters );
+
+       size+=size_of_uint32( &info->attributes );
+       size+=size_of_uint32( &info->priority );
+       size+=size_of_uint32( &info->defaultpriority );
+       size+=size_of_uint32( &info->starttime );
+       size+=size_of_uint32( &info->untiltime );
+       size+=size_of_uint32( &info->status );
+       size+=size_of_uint32( &info->cjobs );
+       size+=size_of_uint32( &info->averageppm );      
+               
+       /* 
+        * add any adjustments for alignment.  This is
+        * not optimal since we could be calling this
+        * function from a loop (e.g. enumprinters), but 
+        * it is easier to maintain the calculation here and
+        * not place the burden on the caller to remember.   --jerry
+        */
+       if ((size % 4) != 0)
+               size += 4 - (size % 4);
+       
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_4(PRINTER_INFO_4 *info)
+{
+       uint32 size=0;
+               
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->servername );
+
+       size+=size_of_uint32( &info->attributes );
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_5(PRINTER_INFO_5 *info)
+{
+       uint32 size=0;
+               
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->portname );
+
+       size+=size_of_uint32( &info->attributes );
+       size+=size_of_uint32( &info->device_not_selected_timeout );
+       size+=size_of_uint32( &info->transmission_retry_timeout );
+       return size;
+}
+
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_3(PRINTER_INFO_3 *info)
+{
+       /* The 4 is for the self relative pointer.. */
+       /* JRA !!!! TESTME - WHAT ABOUT prs_align.... !!! */
+       return 4 + (uint32)sec_desc_size( info->secdesc );
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_7(PRINTER_INFO_7 *info)
+{
+       uint32 size=0;
+               
+       size+=size_of_relative_string( &info->guid );
+       size+=size_of_uint32( &info->action );
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_1(DRIVER_INFO_1 *info)
+{
+       int size=0;
+       size+=size_of_relative_string( &info->name );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_2(DRIVER_INFO_2 *info)
+{
+       int size=0;
+       size+=size_of_uint32( &info->version ); 
+       size+=size_of_relative_string( &info->name );
+       size+=size_of_relative_string( &info->architecture );
+       size+=size_of_relative_string( &info->driverpath );
+       size+=size_of_relative_string( &info->datafile );
+       size+=size_of_relative_string( &info->configfile );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a string array.
+********************************************************************/
+
+uint32 spoolss_size_string_array(uint16 *string)
+{
+       uint32 i = 0;
+
+       if (string) {
+               for (i=0; (string[i]!=0x0000) || (string[i+1]!=0x0000); i++);
+       }
+       i=i+2; /* to count all chars including the leading zero */
+       i=2*i; /* because we need the value in bytes */
+       i=i+4; /* the offset pointer size */
+
+       return i;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+       int size=0;
+
+       size+=size_of_uint32( &info->version ); 
+       size+=size_of_relative_string( &info->name );
+       size+=size_of_relative_string( &info->architecture );
+       size+=size_of_relative_string( &info->driverpath );
+       size+=size_of_relative_string( &info->datafile );
+       size+=size_of_relative_string( &info->configfile );
+       size+=size_of_relative_string( &info->helpfile );
+       size+=size_of_relative_string( &info->monitorname );
+       size+=size_of_relative_string( &info->defaultdatatype );
+       
+       size+=spoolss_size_string_array(info->dependentfiles);
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+       uint32 size=0;
+
+       size+=size_of_uint32( &info->version ); 
+       size+=size_of_relative_string( &info->name );
+       size+=size_of_relative_string( &info->architecture );
+       size+=size_of_relative_string( &info->driverpath );
+       size+=size_of_relative_string( &info->datafile );
+       size+=size_of_relative_string( &info->configfile );
+       size+=size_of_relative_string( &info->helpfile );
+
+       size+=spoolss_size_string_array(info->dependentfiles);
+
+       size+=size_of_relative_string( &info->monitorname );
+       size+=size_of_relative_string( &info->defaultdatatype );
+       
+       size+=spoolss_size_string_array(info->previousdrivernames);
+
+       size+=size_of_nttime(&info->driver_date);
+       size+=size_of_uint32( &info->padding ); 
+       size+=size_of_uint32( &info->driver_version_low );      
+       size+=size_of_uint32( &info->driver_version_high );     
+       size+=size_of_relative_string( &info->mfgname );
+       size+=size_of_relative_string( &info->oem_url );
+       size+=size_of_relative_string( &info->hardware_id );
+       size+=size_of_relative_string( &info->provider );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_job_info_1(JOB_INFO_1 *info)
+{
+       int size=0;
+       size+=size_of_uint32( &info->jobid );
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->machinename );
+       size+=size_of_relative_string( &info->username );
+       size+=size_of_relative_string( &info->document );
+       size+=size_of_relative_string( &info->datatype );
+       size+=size_of_relative_string( &info->text_status );
+       size+=size_of_uint32( &info->status );
+       size+=size_of_uint32( &info->priority );
+       size+=size_of_uint32( &info->position );
+       size+=size_of_uint32( &info->totalpages );
+       size+=size_of_uint32( &info->pagesprinted );
+       size+=size_of_systemtime( &info->submitted );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_job_info_2(JOB_INFO_2 *info)
+{
+       int size=0;
+
+       size+=4; /* size of sec desc ptr */
+
+       size+=size_of_uint32( &info->jobid );
+       size+=size_of_relative_string( &info->printername );
+       size+=size_of_relative_string( &info->machinename );
+       size+=size_of_relative_string( &info->username );
+       size+=size_of_relative_string( &info->document );
+       size+=size_of_relative_string( &info->notifyname );
+       size+=size_of_relative_string( &info->datatype );
+       size+=size_of_relative_string( &info->printprocessor );
+       size+=size_of_relative_string( &info->parameters );
+       size+=size_of_relative_string( &info->drivername );
+       size+=size_of_device_mode( info->devmode );
+       size+=size_of_relative_string( &info->text_status );
+/*     SEC_DESC sec_desc;*/
+       size+=size_of_uint32( &info->status );
+       size+=size_of_uint32( &info->priority );
+       size+=size_of_uint32( &info->position );
+       size+=size_of_uint32( &info->starttime );
+       size+=size_of_uint32( &info->untiltime );
+       size+=size_of_uint32( &info->totalpages );
+       size+=size_of_uint32( &info->size );
+       size+=size_of_systemtime( &info->submitted );
+       size+=size_of_uint32( &info->timeelapsed );
+       size+=size_of_uint32( &info->pagesprinted );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_form_1(FORM_1 *info)
+{
+       int size=0;
+
+       size+=size_of_uint32( &info->flag );
+       size+=size_of_relative_string( &info->name );
+       size+=size_of_uint32( &info->width );
+       size+=size_of_uint32( &info->length );
+       size+=size_of_uint32( &info->left );
+       size+=size_of_uint32( &info->top );
+       size+=size_of_uint32( &info->right );
+       size+=size_of_uint32( &info->bottom );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_port_info_1(PORT_INFO_1 *info)
+{
+       int size=0;
+
+       size+=size_of_relative_string( &info->port_name );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_driverdir_info_1(DRIVER_DIRECTORY_1 *info)
+{
+       int size=0;
+
+       size=str_len_uni(&info->name);  /* the string length       */
+       size=size+1;                    /* add the leading zero    */
+       size=size*2;                    /* convert in char         */
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printprocessordirectory_info_1(PRINTPROCESSOR_DIRECTORY_1 *info)
+{
+       int size=0;
+
+       size=str_len_uni(&info->name);  /* the string length       */
+       size=size+1;                    /* add the leading zero    */
+       size=size*2;                    /* convert in char         */
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_port_info_2(PORT_INFO_2 *info)
+{
+       int size=0;
+
+       size+=size_of_relative_string( &info->port_name );
+       size+=size_of_relative_string( &info->monitor_name );
+       size+=size_of_relative_string( &info->description );
+
+       size+=size_of_uint32( &info->port_type );
+       size+=size_of_uint32( &info->reserved );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printprocessor_info_1(PRINTPROCESSOR_1 *info)
+{
+       int size=0;
+       size+=size_of_relative_string( &info->name );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printprocdatatype_info_1(PRINTPROCDATATYPE_1 *info)
+{
+       int size=0;
+       size+=size_of_relative_string( &info->name );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+uint32 spoolss_size_printer_enum_values(PRINTER_ENUM_VALUES *p)
+{
+       uint32  size = 0; 
+       
+       if (!p)
+               return 0;
+       
+       /* uint32(offset) + uint32(length) + length) */
+       size += (size_of_uint32(&p->value_len)*2) + p->value_len;
+       size += (size_of_uint32(&p->data_len)*2) + p->data_len + (p->data_len%2) ;
+       
+       size += size_of_uint32(&p->type);
+                      
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printmonitor_info_1(PRINTMONITOR_1 *info)
+{
+       int size=0;
+       size+=size_of_relative_string( &info->name );
+
+       return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/  
+
+uint32 spoolss_size_printmonitor_info_2(PRINTMONITOR_2 *info)
+{
+       int size=0;
+       size+=size_of_relative_string( &info->name);
+       size+=size_of_relative_string( &info->environment);
+       size+=size_of_relative_string( &info->dll_name);
+
+       return size;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriver2(SPOOL_Q_GETPRINTERDRIVER2 *q_u, 
+                              const POLICY_HND *hnd,
+                              const fstring architecture,
+                              uint32 level, uint32 clientmajor, uint32 clientminor,
+                              NEW_BUFFER *buffer, uint32 offered)
+{      
+       if (q_u == NULL)
+               return False;
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       init_buf_unistr2(&q_u->architecture, &q_u->architecture_ptr, architecture);
+
+       q_u->level=level;
+       q_u->clientmajorversion=clientmajor;
+       q_u->clientminorversion=clientminor;
+
+       q_u->buffer=buffer;
+       q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdriver2(const char *desc, SPOOL_Q_GETPRINTERDRIVER2 *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriver2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!prs_uint32("architecture_ptr", ps, depth, &q_u->architecture_ptr))
+               return False;
+       if(!smb_io_unistr2("architecture", &q_u->architecture, q_u->architecture_ptr, ps, depth))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+               
+       if(!prs_uint32("clientmajorversion", ps, depth, &q_u->clientmajorversion))
+               return False;
+       if(!prs_uint32("clientminorversion", ps, depth, &q_u->clientminorversion))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdriver2(const char *desc, SPOOL_R_GETPRINTERDRIVER2 *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriver2");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+       if (!prs_uint32("servermajorversion", ps, depth, &r_u->servermajorversion))
+               return False;
+       if (!prs_uint32("serverminorversion", ps, depth, &r_u->serverminorversion))
+               return False;           
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinters(
+       SPOOL_Q_ENUMPRINTERS *q_u, 
+       uint32 flags, 
+       char *servername, 
+       uint32 level, 
+       NEW_BUFFER *buffer, 
+       uint32 offered
+)
+{
+       q_u->flags=flags;
+       
+       q_u->servername_ptr = (servername != NULL) ? 1 : 0;
+       init_buf_unistr2(&q_u->servername, &q_u->servername_ptr, servername);
+
+       q_u->level=level;
+       q_u->buffer=buffer;
+       q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumports(SPOOL_Q_ENUMPORTS *q_u, 
+                               fstring servername, uint32 level, 
+                               NEW_BUFFER *buffer, uint32 offered)
+{
+       q_u->name_ptr = (servername != NULL) ? 1 : 0;
+       init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+
+       q_u->level=level;
+       q_u->buffer=buffer;
+       q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_enumprinters (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinters(const char *desc, SPOOL_Q_ENUMPRINTERS *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprinters");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("flags", ps, depth, &q_u->flags))
+               return False;
+       if (!prs_uint32("servername_ptr", ps, depth, &q_u->servername_ptr))
+               return False;
+
+       if (!smb_io_unistr2("", &q_u->servername, q_u->servername_ptr, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERS structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinters(const char *desc, SPOOL_R_ENUMPRINTERS *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprinters");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enum_printers (srv_spoolss.c)
+ *
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinter(const char *desc, SPOOL_R_GETPRINTER *r_u, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinter(const char *desc, SPOOL_Q_GETPRINTER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinter(
+       TALLOC_CTX *mem_ctx,
+       SPOOL_Q_GETPRINTER *q_u, 
+       const POLICY_HND *hnd, 
+       uint32 level, 
+       NEW_BUFFER *buffer, 
+       uint32 offered
+)
+{
+       if (q_u == NULL)
+       {
+               return False;
+       }
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       q_u->level=level;
+       q_u->buffer=buffer;
+       q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_setprinter(TALLOC_CTX *mem_ctx, SPOOL_Q_SETPRINTER *q_u, 
+                               const POLICY_HND *hnd, uint32 level, PRINTER_INFO_CTR *info, 
+                               uint32 command)
+{
+       SEC_DESC *secdesc;
+       DEVICEMODE *devmode;
+
+       if (q_u == NULL)
+               return False;
+       
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       q_u->level = level;
+       q_u->info.level = level;
+       q_u->info.info_ptr = (info != NULL) ? 1 : 0;
+       switch (level) {
+
+         /* There's no such thing as a setprinter level 1 */
+
+       case 2:
+               secdesc = info->printers_2->secdesc;
+               devmode = info->printers_2->devmode;
+               
+               make_spoolss_printer_info_2 (mem_ctx, &q_u->info.info_2, info->printers_2);
+#if 1  /* JERRY TEST */
+               q_u->secdesc_ctr = (SEC_DESC_BUF*)malloc(sizeof(SEC_DESC_BUF));
+               if (!q_u->secdesc_ctr)
+                       return False;
+               q_u->secdesc_ctr->ptr = (secdesc != NULL) ? 1: 0;
+               q_u->secdesc_ctr->max_len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+               q_u->secdesc_ctr->len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+               q_u->secdesc_ctr->sec = secdesc;
+
+               q_u->devmode_ctr.devmode_ptr = (devmode != NULL) ? 1 : 0;
+               q_u->devmode_ctr.size = (devmode != NULL) ? sizeof(DEVICEMODE) + (3*sizeof(uint32)) : 0;
+               q_u->devmode_ctr.devmode = devmode;
+#else
+               q_u->secdesc_ctr = NULL;
+       
+               q_u->devmode_ctr.devmode_ptr = 0;
+               q_u->devmode_ctr.size = 0;
+               q_u->devmode_ctr.devmode = NULL;
+#endif
+               break;
+       default: 
+               DEBUG(0,("make_spoolss_q_setprinter: Unknown info level [%d]\n", level));
+                       break;
+       }
+
+       
+       q_u->command = command;
+
+       return True;
+}
+
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_setprinter(const char *desc, SPOOL_R_SETPRINTER *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_setprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Marshall/unmarshall a SPOOL_Q_SETPRINTER struct.
+********************************************************************/  
+
+BOOL spoolss_io_q_setprinter(const char *desc, SPOOL_Q_SETPRINTER *q_u, prs_struct *ps, int depth)
+{
+       uint32 ptr_sec_desc = 0;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_setprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle ,ps, depth))
+               return False;
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+
+       if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+               return False;
+
+       if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+
+       switch (q_u->level)
+       {
+               case 2:
+               {
+                       ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+                       break;
+               }
+               case 3:
+               {
+                       ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+                       break;
+               }
+       }
+       if (ptr_sec_desc)
+       {
+               if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+                       return False;
+       } else {
+               uint32 dummy = 0;
+
+               /* Parse a NULL security descriptor.  This should really
+                  happen inside the sec_io_desc_buf() function. */
+
+               prs_debug(ps, depth, "", "sec_io_desc_buf");
+               if (!prs_uint32("size", ps, depth + 1, &dummy))
+                       return False;
+               if (!prs_uint32("ptr", ps, depth + 1, &dummy)) return
+                                                                      False;
+       }
+       
+       if(!prs_uint32("command", ps, depth, &q_u->command))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_fcpn(const char *desc, SPOOL_R_FCPN *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_fcpn");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_fcpn(const char *desc, SPOOL_Q_FCPN *q_u, prs_struct *ps, int depth)
+{
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_fcpn");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addjob(const char *desc, SPOOL_R_ADDJOB *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_addjob(const char *desc, SPOOL_Q_ADDJOB *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+       
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumjobs(const char *desc, SPOOL_R_ENUMJOBS *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumjobs");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL make_spoolss_q_enumjobs(SPOOL_Q_ENUMJOBS *q_u, const POLICY_HND *hnd,
+                               uint32 firstjob,
+                               uint32 numofjobs,
+                               uint32 level,
+                               NEW_BUFFER *buffer,
+                               uint32 offered)
+{
+       if (q_u == NULL)
+       {
+               return False;
+       }
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       q_u->firstjob = firstjob;
+       q_u->numofjobs = numofjobs;
+       q_u->level = level;
+       q_u->buffer= buffer;
+       q_u->offered = offered;
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumjobs(const char *desc, SPOOL_Q_ENUMJOBS *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumjobs");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle, ps, depth))
+               return False;
+               
+       if (!prs_uint32("firstjob", ps, depth, &q_u->firstjob))
+               return False;
+       if (!prs_uint32("numofjobs", ps, depth, &q_u->numofjobs))
+               return False;
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;   
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_schedulejob(const char *desc, SPOOL_R_SCHEDULEJOB *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_schedulejob");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_schedulejob(const char *desc, SPOOL_Q_SCHEDULEJOB *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_schedulejob");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_setjob(const char *desc, SPOOL_R_SETJOB *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_setjob");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_setjob(const char *desc, SPOOL_Q_SETJOB *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_setjob");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+               return False;
+       /* 
+        * level is usually 0. If (level!=0) then I'm in trouble !
+        * I will try to generate setjob command with level!=0, one day.
+        */
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+       if(!prs_uint32("command", ps, depth, &q_u->command))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERDRIVERS structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_enumprinterdrivers(const char *desc, SPOOL_R_ENUMPRINTERDRIVERS *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdrivers");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdrivers(SPOOL_Q_ENUMPRINTERDRIVERS *q_u,
+                                const char *name,
+                                const char *environment,
+                                uint32 level,
+                                NEW_BUFFER *buffer, uint32 offered)
+{
+        init_buf_unistr2(&q_u->name, &q_u->name_ptr, name);
+        init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, environment);
+
+        q_u->level=level;
+        q_u->buffer=buffer;
+        q_u->offered=offered;
+
+        return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTERDRIVERS structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_enumprinterdrivers(const char *desc, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, prs_struct *ps, int depth)
+{
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdrivers");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->name, q_u->name_ptr,ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("environment_ptr", ps, depth, &q_u->environment_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumforms(const char *desc, SPOOL_Q_ENUMFORMS *q_u, prs_struct *ps, int depth)
+{
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumforms");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;                   
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;           
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;   
+       
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumforms(const char *desc, SPOOL_R_ENUMFORMS *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumforms");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("numofforms", ps, depth, &r_u->numofforms))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_getform(const char *desc, SPOOL_Q_GETFORM *q_u, prs_struct *ps, int depth)
+{
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_getform");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;                   
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;           
+       if (!smb_io_unistr2("", &q_u->formname,True,ps,depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;   
+       
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_getform(const char *desc, SPOOL_R_GETFORM *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_getform");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPORTS structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_enumports(const char *desc, SPOOL_R_ENUMPORTS *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumports");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumports(const char *desc, SPOOL_Q_ENUMPORTS *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("", ps, depth, &q_u->name_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->name,True,ps,depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_1 structure.
+********************************************************************/  
+
+BOOL spool_io_printer_info_level_1(const char *desc, SPOOL_PRINTER_INFO_LEVEL_1 *il, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spool_io_printer_info_level_1");
+       depth++;
+               
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("flags", ps, depth, &il->flags))
+               return False;
+       if(!prs_uint32("description_ptr", ps, depth, &il->description_ptr))
+               return False;
+       if(!prs_uint32("name_ptr", ps, depth, &il->name_ptr))
+               return False;
+       if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+               return False;
+               
+       if(!smb_io_unistr2("description", &il->description, il->description_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_3 structure.
+********************************************************************/  
+
+BOOL spool_io_printer_info_level_3(const char *desc, SPOOL_PRINTER_INFO_LEVEL_3 *il, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spool_io_printer_info_level_3");
+       depth++;
+               
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_2 structure.
+********************************************************************/  
+
+BOOL spool_io_printer_info_level_2(const char *desc, SPOOL_PRINTER_INFO_LEVEL_2 *il, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spool_io_printer_info_level_2");
+       depth++;
+               
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("servername_ptr", ps, depth, &il->servername_ptr))
+               return False;
+       if(!prs_uint32("printername_ptr", ps, depth, &il->printername_ptr))
+               return False;
+       if(!prs_uint32("sharename_ptr", ps, depth, &il->sharename_ptr))
+               return False;
+       if(!prs_uint32("portname_ptr", ps, depth, &il->portname_ptr))
+               return False;
+
+       if(!prs_uint32("drivername_ptr", ps, depth, &il->drivername_ptr))
+               return False;
+       if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+               return False;
+       if(!prs_uint32("location_ptr", ps, depth, &il->location_ptr))
+               return False;
+       if(!prs_uint32("devmode_ptr", ps, depth, &il->devmode_ptr))
+               return False;
+       if(!prs_uint32("sepfile_ptr", ps, depth, &il->sepfile_ptr))
+               return False;
+       if(!prs_uint32("printprocessor_ptr", ps, depth, &il->printprocessor_ptr))
+               return False;
+       if(!prs_uint32("datatype_ptr", ps, depth, &il->datatype_ptr))
+               return False;
+       if(!prs_uint32("parameters_ptr", ps, depth, &il->parameters_ptr))
+               return False;
+       if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+               return False;
+
+       if(!prs_uint32("attributes", ps, depth, &il->attributes))
+               return False;
+       if(!prs_uint32("priority", ps, depth, &il->priority))
+               return False;
+       if(!prs_uint32("default_priority", ps, depth, &il->default_priority))
+               return False;
+       if(!prs_uint32("starttime", ps, depth, &il->starttime))
+               return False;
+       if(!prs_uint32("untiltime", ps, depth, &il->untiltime))
+               return False;
+       if(!prs_uint32("status", ps, depth, &il->status))
+               return False;
+       if(!prs_uint32("cjobs", ps, depth, &il->cjobs))
+               return False;
+       if(!prs_uint32("averageppm", ps, depth, &il->averageppm))
+               return False;
+
+       if(!smb_io_unistr2("servername", &il->servername, il->servername_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("printername", &il->printername, il->printername_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("sharename", &il->sharename, il->sharename_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("portname", &il->portname, il->portname_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("drivername", &il->drivername, il->drivername_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("location", &il->location, il->location_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("sepfile", &il->sepfile, il->sepfile_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("printprocessor", &il->printprocessor, il->printprocessor_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("datatype", &il->datatype, il->datatype_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("parameters", &il->parameters, il->parameters_ptr, ps, depth))
+               return False;
+
+       return True;
+}
+
+BOOL spool_io_printer_info_level_7(const char *desc, SPOOL_PRINTER_INFO_LEVEL_7 *il, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spool_io_printer_info_level_7");
+       depth++;
+               
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("guid_ptr", ps, depth, &il->guid_ptr))
+               return False;
+       if(!prs_uint32("action", ps, depth, &il->action))
+               return False;
+
+       if(!smb_io_unistr2("servername", &il->guid, il->guid_ptr, ps, depth))
+               return False;
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spool_io_printer_info_level(const char *desc, SPOOL_PRINTER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spool_io_printer_info_level");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("level", ps, depth, &il->level))
+               return False;
+       if(!prs_uint32("info_ptr", ps, depth, &il->info_ptr))
+               return False;
+       
+       /* if no struct inside just return */
+       if (il->info_ptr==0) {
+               if (UNMARSHALLING(ps)) {
+                       il->info_1=NULL;
+                       il->info_2=NULL;
+               }
+               return True;
+       }
+                       
+       switch (il->level) {
+               /*
+                * level 0 is used by setprinter when managing the queue
+                * (hold, stop, start a queue)
+                */
+               case 0:
+                       break;
+               /* DOCUMENT ME!!! What is level 1 used for? */
+               case 1:
+               {
+                       if (UNMARSHALLING(ps)) {
+                               if ((il->info_1=(SPOOL_PRINTER_INFO_LEVEL_1 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_1))) == NULL)
+                                       return False;
+                       }
+                       if (!spool_io_printer_info_level_1("", il->info_1, ps, depth))
+                               return False;
+                       break;          
+               }
+               /* 
+                * level 2 is used by addprinter
+                * and by setprinter when updating printer's info
+                */     
+               case 2:
+                       if (UNMARSHALLING(ps)) {
+                               if ((il->info_2=(SPOOL_PRINTER_INFO_LEVEL_2 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_2))) == NULL)
+                                       return False;
+                       }
+                       if (!spool_io_printer_info_level_2("", il->info_2, ps, depth))
+                               return False;
+                       break;          
+               /* DOCUMENT ME!!! What is level 3 used for? */
+               case 3:
+               {
+                       if (UNMARSHALLING(ps)) {
+                               if ((il->info_3=(SPOOL_PRINTER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_3))) == NULL)
+                                       return False;
+                       }
+                       if (!spool_io_printer_info_level_3("", il->info_3, ps, depth))
+                               return False;
+                       break;          
+               }
+               case 7:
+                       if (UNMARSHALLING(ps))
+                               if ((il->info_7=(SPOOL_PRINTER_INFO_LEVEL_7 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_7))) == NULL)
+                                       return False;
+                       if (!spool_io_printer_info_level_7("", il->info_7, ps, depth))
+                               return False;
+                       break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_addprinterex(const char *desc, SPOOL_Q_ADDPRINTEREX *q_u, prs_struct *ps, int depth)
+{
+       uint32 ptr_sec_desc = 0;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_addprinterex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("", ps, depth, &q_u->server_name_ptr))
+               return False;
+       if(!smb_io_unistr2("", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("info_level", ps, depth, &q_u->level))
+               return False;
+       
+       if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+               return False;
+       
+       if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       switch (q_u->level) {
+               case 2:
+                       ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+                       break;
+               case 3:
+                       ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+                       break;
+       }
+       if (ptr_sec_desc) {
+               if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+                       return False;
+       } else {
+               uint32 dummy;
+
+               /* Parse a NULL security descriptor.  This should really
+                       happen inside the sec_io_desc_buf() function. */
+
+               prs_debug(ps, depth, "", "sec_io_desc_buf");
+               if (!prs_uint32("size", ps, depth + 1, &dummy))
+                       return False;
+               if (!prs_uint32("ptr", ps, depth + 1, &dummy))
+                       return False;
+       }
+
+       if(!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+               return False;
+       if(!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addprinterex(const char *desc, SPOOL_R_ADDPRINTEREX *r_u, 
+                              prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_addprinterex");
+       depth++;
+       
+       if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spool_io_printer_driver_info_level_3(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **q_u, 
+                                          prs_struct *ps, int depth)
+{      
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *il;
+       
+       prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_3");
+       depth++;
+               
+       /* reading */
+       if (UNMARSHALLING(ps)) {
+               il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3));
+               if(il == NULL)
+                       return False;
+               *q_u=il;
+       }
+       else {
+               il=*q_u;
+       }
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("cversion", ps, depth, &il->cversion))
+               return False;
+       if(!prs_uint32("name", ps, depth, &il->name_ptr))
+               return False;
+       if(!prs_uint32("environment", ps, depth, &il->environment_ptr))
+               return False;
+       if(!prs_uint32("driverpath", ps, depth, &il->driverpath_ptr))
+               return False;
+       if(!prs_uint32("datafile", ps, depth, &il->datafile_ptr))
+               return False;
+       if(!prs_uint32("configfile", ps, depth, &il->configfile_ptr))
+               return False;
+       if(!prs_uint32("helpfile", ps, depth, &il->helpfile_ptr))
+               return False;
+       if(!prs_uint32("monitorname", ps, depth, &il->monitorname_ptr))
+               return False;
+       if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+               return False;
+       if(!prs_uint32("dependentfilessize", ps, depth, &il->dependentfilessize))
+               return False;
+       if(!prs_uint32("dependentfiles", ps, depth, &il->dependentfiles_ptr))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+               return False;
+       if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if (il->dependentfiles_ptr)
+               smb_io_buffer5("", &il->dependentfiles, ps, depth);
+
+       return True;
+}
+
+/*******************************************************************
+parse a SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure
+********************************************************************/  
+
+BOOL spool_io_printer_driver_info_level_6(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 **q_u, 
+                                          prs_struct *ps, int depth)
+{      
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *il;
+       
+       prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_6");
+       depth++;
+               
+       /* reading */
+       if (UNMARSHALLING(ps)) {
+               il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6));
+               if(il == NULL)
+                       return False;
+               *q_u=il;
+       }
+       else {
+               il=*q_u;
+       }
+       
+       if(!prs_align(ps))
+               return False;
+
+       /* 
+        * I know this seems weird, but I have no other explanation.
+        * This is observed behavior on both NT4 and 2K servers.
+        * --jerry
+        */
+        
+       if (!prs_align_uint64(ps))
+               return False;
+
+       /* parse the main elements the packet */
+
+       if(!prs_uint32("cversion       ", ps, depth, &il->version))
+               return False;
+       if(!prs_uint32("name           ", ps, depth, &il->name_ptr))
+               return False;
+       if(!prs_uint32("environment    ", ps, depth, &il->environment_ptr))
+               return False;
+       if(!prs_uint32("driverpath     ", ps, depth, &il->driverpath_ptr))
+               return False;
+       if(!prs_uint32("datafile       ", ps, depth, &il->datafile_ptr))
+               return False;
+       if(!prs_uint32("configfile     ", ps, depth, &il->configfile_ptr))
+               return False;
+       if(!prs_uint32("helpfile       ", ps, depth, &il->helpfile_ptr))
+               return False;
+       if(!prs_uint32("monitorname    ", ps, depth, &il->monitorname_ptr))
+               return False;
+       if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+               return False;
+       if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_len))
+               return False;
+       if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_ptr))
+               return False;
+       if(!prs_uint32("previousnames  ", ps, depth, &il->previousnames_len))
+               return False;
+       if(!prs_uint32("previousnames  ", ps, depth, &il->previousnames_ptr))
+               return False;
+       if(!smb_io_time("driverdate    ", &il->driverdate, ps, depth))
+               return False;
+       if(!prs_uint32("dummy4         ", ps, depth, &il->dummy4))
+               return False;
+       if(!prs_uint64("driverversion  ", ps, depth, &il->driverversion))
+               return False;
+       if(!prs_uint32("mfgname        ", ps, depth, &il->mfgname_ptr))
+               return False;
+       if(!prs_uint32("oemurl         ", ps, depth, &il->oemurl_ptr))
+               return False;
+       if(!prs_uint32("hardwareid     ", ps, depth, &il->hardwareid_ptr))
+               return False;
+       if(!prs_uint32("provider       ", ps, depth, &il->provider_ptr))
+               return False;
+
+       /* parse the structures in the packet */
+
+       if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if (il->dependentfiles_ptr) {
+               if(!smb_io_buffer5("dependentfiles", &il->dependentfiles, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+       if (il->previousnames_ptr) {
+               if(!smb_io_buffer5("previousnames", &il->previousnames, ps, depth))
+                       return False;
+               if(!prs_align(ps))
+                       return False;
+       }
+       if(!smb_io_unistr2("mfgname", &il->mfgname, il->mfgname_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("oemurl", &il->oemurl, il->oemurl_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("hardwareid", &il->hardwareid, il->hardwareid_ptr, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("provider", &il->provider, il->provider_ptr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ convert a buffer of UNICODE strings null terminated
+ the buffer is terminated by a NULL
+ convert to an dos codepage array (null terminated)
+ dynamically allocate memory
+********************************************************************/  
+static BOOL uniarray_2_dosarray(BUFFER5 *buf5, fstring **ar)
+{
+       fstring f, *tar;
+       int n = 0;
+       char *src;
+
+       if (buf5==NULL)
+               return False;
+
+       src = (char *)buf5->buffer;
+       *ar = NULL;
+
+       while (src < ((char *)buf5->buffer) + buf5->buf_len*2) {
+               rpcstr_pull(f, src, sizeof(f)-1, -1, STR_TERMINATE);
+               src = skip_unibuf(src, 2*buf5->buf_len - PTR_DIFF(src,buf5->buffer));
+               tar = (fstring *)Realloc(*ar, sizeof(fstring)*(n+2));
+               if (!tar)
+                       return False;
+               else
+                       *ar = tar;
+               fstrcpy((*ar)[n], f);
+               n++;
+       }
+       fstrcpy((*ar)[n], "");
+       return True;
+}
+
+
+
+
+/*******************************************************************
+ read a UNICODE array with null terminated strings 
+ and null terminated array 
+ and size of array at beginning
+********************************************************************/  
+
+BOOL smb_io_unibuffer(const char *desc, UNISTR2 *buffer, prs_struct *ps, int depth)
+{
+       if (buffer==NULL) return False;
+
+       buffer->undoc=0;
+       buffer->uni_str_len=buffer->uni_max_len;
+       
+       if(!prs_uint32("buffer_size", ps, depth, &buffer->uni_max_len))
+               return False;
+
+       if(!prs_unistr2(True, "buffer     ", ps, depth, buffer))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spool_io_printer_driver_info_level(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("level", ps, depth, &il->level))
+               return False;
+       if(!prs_uint32("ptr", ps, depth, &il->ptr))
+               return False;
+
+       if (il->ptr==0)
+               return True;
+               
+       switch (il->level) {
+               case 3:
+                       if(!spool_io_printer_driver_info_level_3("", &il->info_3, ps, depth))
+                               return False;
+                       break;          
+               case 6:
+                       if(!spool_io_printer_driver_info_level_6("", &il->info_6, ps, depth))
+                               return False;
+                       break;          
+       default:
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ init a SPOOL_Q_ADDPRINTERDRIVER struct
+ ******************************************************************/
+
+BOOL make_spoolss_q_addprinterdriver(TALLOC_CTX *mem_ctx,
+                               SPOOL_Q_ADDPRINTERDRIVER *q_u, const char* srv_name, 
+                               uint32 level, PRINTER_DRIVER_CTR *info)
+{
+       DEBUG(5,("make_spoolss_q_addprinterdriver\n"));
+       
+       q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+       init_unistr2(&q_u->server_name, srv_name, strlen(srv_name)+1);
+       
+       q_u->level = level;
+       
+       q_u->info.level = level;
+       q_u->info.ptr = (info!=NULL)?1:0;
+       switch (level)
+       {
+       /* info level 3 is supported by Windows 95/98, WinNT and Win2k */
+       case 3 :
+               make_spoolss_driver_info_3(mem_ctx, &q_u->info.info_3, info->info3);
+               break;
+               
+       default:
+               DEBUG(0,("make_spoolss_q_addprinterdriver: Unknown info level [%d]\n", level));
+               break;
+       }
+       
+       return True;
+}
+
+BOOL make_spoolss_driver_info_3(TALLOC_CTX *mem_ctx, 
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **spool_drv_info,
+                               DRIVER_INFO_3 *info3)
+{
+       uint32          len = 0;
+       uint16          *ptr = info3->dependentfiles;
+       BOOL            done = False;
+       BOOL            null_char = False;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *inf;
+
+       if (!(inf=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3*)talloc_zero(mem_ctx, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3))))
+               return False;
+       
+       inf->cversion   = info3->version;
+       inf->name_ptr   = (info3->name.buffer!=NULL)?1:0;
+       inf->environment_ptr    = (info3->architecture.buffer!=NULL)?1:0;
+       inf->driverpath_ptr     = (info3->driverpath.buffer!=NULL)?1:0;
+       inf->datafile_ptr       = (info3->datafile.buffer!=NULL)?1:0;
+       inf->configfile_ptr     = (info3->configfile.buffer!=NULL)?1:0;
+       inf->helpfile_ptr       = (info3->helpfile.buffer!=NULL)?1:0;
+       inf->monitorname_ptr    = (info3->monitorname.buffer!=NULL)?1:0;
+       inf->defaultdatatype_ptr        = (info3->defaultdatatype.buffer!=NULL)?1:0;
+
+       init_unistr2_from_unistr(&inf->name, &info3->name);
+       init_unistr2_from_unistr(&inf->environment, &info3->architecture);
+       init_unistr2_from_unistr(&inf->driverpath, &info3->driverpath);
+       init_unistr2_from_unistr(&inf->datafile, &info3->datafile);
+       init_unistr2_from_unistr(&inf->configfile, &info3->configfile);
+       init_unistr2_from_unistr(&inf->helpfile, &info3->helpfile);
+       init_unistr2_from_unistr(&inf->monitorname, &info3->monitorname);
+       init_unistr2_from_unistr(&inf->defaultdatatype, &info3->defaultdatatype);
+
+       while (!done)
+       {
+               switch (*ptr)
+               {
+                       case 0:
+                               /* the null_char BOOL is used to help locate
+                                  two '\0's back to back */
+                               if (null_char)
+                                       done = True;
+                               else
+                                       null_char = True;
+                               break;
+                                       
+                       default:
+                               null_char = False;
+                               ;;
+                               break;                          
+               }
+               len++;
+               ptr++;
+       }
+       inf->dependentfiles_ptr = (info3->dependentfiles != NULL) ? 1 : 0;
+       inf->dependentfilessize = len;
+       if(!make_spoolss_buffer5(mem_ctx, &inf->dependentfiles, len, info3->dependentfiles))
+       {
+               SAFE_FREE(inf);
+               return False;
+       }
+       
+       *spool_drv_info = inf;
+       
+       return True;
+}
+
+/*******************************************************************
+ make a BUFFER5 struct from a uint16*
+ ******************************************************************/
+BOOL make_spoolss_buffer5(TALLOC_CTX *mem_ctx, BUFFER5 *buf5, uint32 len, uint16 *src)
+{
+
+       buf5->buf_len = len;
+       if((buf5->buffer=(uint16*)talloc_memdup(mem_ctx, src, sizeof(uint16)*len)) == NULL)
+       {
+               DEBUG(0,("make_spoolss_buffer5: Unable to malloc memory for buffer!\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/  
+
+BOOL spoolss_io_q_addprinterdriver(const char *desc, SPOOL_Q_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriver");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+               return False;
+       if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+               return False;
+               
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("info_level", ps, depth, &q_u->level))
+               return False;
+
+       if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addprinterdriver(const char *desc, SPOOL_R_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriver");
+       depth++;
+
+       if(!prs_werror("status", ps, depth, &q_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/  
+
+BOOL spoolss_io_q_addprinterdriverex(const char *desc, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriverex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+               return False;
+       if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+               return False;
+               
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("info_level", ps, depth, &q_u->level))
+               return False;
+
+       if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("copy flags", ps, depth, &q_u->copy_flags))
+               return False;
+               
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addprinterdriverex(const char *desc, SPOOL_R_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriverex");
+       depth++;
+
+       if(!prs_werror("status", ps, depth, &q_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL uni_2_asc_printer_driver_3(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *uni,
+                                NT_PRINTER_DRIVER_INFO_LEVEL_3 **asc)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *d;
+       
+       DEBUG(7,("uni_2_asc_printer_driver_3: Converting from UNICODE to ASCII\n"));
+       
+       if (*asc==NULL)
+       {
+               *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_3 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_3));
+               if(*asc == NULL)
+                       return False;
+               ZERO_STRUCTP(*asc);
+       }       
+
+       d=*asc;
+
+       d->cversion=uni->cversion;
+
+       unistr2_to_ascii(d->name,            &uni->name,            sizeof(d->name)-1);
+       unistr2_to_ascii(d->environment,     &uni->environment,     sizeof(d->environment)-1);
+       unistr2_to_ascii(d->driverpath,      &uni->driverpath,      sizeof(d->driverpath)-1);
+       unistr2_to_ascii(d->datafile,        &uni->datafile,        sizeof(d->datafile)-1);
+       unistr2_to_ascii(d->configfile,      &uni->configfile,      sizeof(d->configfile)-1);
+       unistr2_to_ascii(d->helpfile,        &uni->helpfile,        sizeof(d->helpfile)-1);
+       unistr2_to_ascii(d->monitorname,     &uni->monitorname,     sizeof(d->monitorname)-1);
+       unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+       DEBUGADD(8,( "version:         %d\n", d->cversion));
+       DEBUGADD(8,( "name:            %s\n", d->name));
+       DEBUGADD(8,( "environment:     %s\n", d->environment));
+       DEBUGADD(8,( "driverpath:      %s\n", d->driverpath));
+       DEBUGADD(8,( "datafile:        %s\n", d->datafile));
+       DEBUGADD(8,( "configfile:      %s\n", d->configfile));
+       DEBUGADD(8,( "helpfile:        %s\n", d->helpfile));
+       DEBUGADD(8,( "monitorname:     %s\n", d->monitorname));
+       DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+       if (uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+               return True;
+       
+       SAFE_FREE(*asc);
+       return False;
+}
+
+/*******************************************************************
+********************************************************************/  
+BOOL uni_2_asc_printer_driver_6(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *uni,
+                                NT_PRINTER_DRIVER_INFO_LEVEL_6 **asc)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_6 *d;
+       
+       DEBUG(7,("uni_2_asc_printer_driver_6: Converting from UNICODE to ASCII\n"));
+       
+       if (*asc==NULL)
+       {
+               *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_6 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_6));
+               if(*asc == NULL)
+                       return False;
+               ZERO_STRUCTP(*asc);
+       }       
+
+       d=*asc;
+
+       d->version=uni->version;
+
+       unistr2_to_ascii(d->name,            &uni->name,            sizeof(d->name)-1);
+       unistr2_to_ascii(d->environment,     &uni->environment,     sizeof(d->environment)-1);
+       unistr2_to_ascii(d->driverpath,      &uni->driverpath,      sizeof(d->driverpath)-1);
+       unistr2_to_ascii(d->datafile,        &uni->datafile,        sizeof(d->datafile)-1);
+       unistr2_to_ascii(d->configfile,      &uni->configfile,      sizeof(d->configfile)-1);
+       unistr2_to_ascii(d->helpfile,        &uni->helpfile,        sizeof(d->helpfile)-1);
+       unistr2_to_ascii(d->monitorname,     &uni->monitorname,     sizeof(d->monitorname)-1);
+       unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+       DEBUGADD(8,( "version:         %d\n", d->version));
+       DEBUGADD(8,( "name:            %s\n", d->name));
+       DEBUGADD(8,( "environment:     %s\n", d->environment));
+       DEBUGADD(8,( "driverpath:      %s\n", d->driverpath));
+       DEBUGADD(8,( "datafile:        %s\n", d->datafile));
+       DEBUGADD(8,( "configfile:      %s\n", d->configfile));
+       DEBUGADD(8,( "helpfile:        %s\n", d->helpfile));
+       DEBUGADD(8,( "monitorname:     %s\n", d->monitorname));
+       DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+       if (!uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+               goto error;
+       if (!uniarray_2_dosarray(&uni->previousnames, &d->previousnames ))
+               goto error;
+       
+       return True;
+       
+error:
+       SAFE_FREE(*asc);
+       return False;
+}
+
+BOOL uni_2_asc_printer_info_2(const SPOOL_PRINTER_INFO_LEVEL_2 *uni,
+                              NT_PRINTER_INFO_LEVEL_2  **asc)
+{
+       NT_PRINTER_INFO_LEVEL_2 *d;
+       time_t time_unix;
+       
+       DEBUG(7,("Converting from UNICODE to ASCII\n"));
+       time_unix=time(NULL);
+       
+       if (*asc==NULL) {
+               DEBUGADD(8,("allocating memory\n"));
+
+               *asc=(NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2));
+               if(*asc == NULL)
+                       return False;
+               ZERO_STRUCTP(*asc);
+               
+               /* we allocate memory iff called from 
+                * addprinter(ex) so we can do one time stuff here.
+                */
+               (*asc)->setuptime=time_unix;
+
+       }       
+       DEBUGADD(8,("start converting\n"));
+
+       d=*asc;
+               
+       d->attributes=uni->attributes;
+       d->priority=uni->priority;
+       d->default_priority=uni->default_priority;
+       d->starttime=uni->starttime;
+       d->untiltime=uni->untiltime;
+       d->status=uni->status;
+       d->cjobs=uni->cjobs;
+       
+       unistr2_to_ascii(d->servername, &uni->servername, sizeof(d->servername)-1);
+       unistr2_to_ascii(d->printername, &uni->printername, sizeof(d->printername)-1);
+       unistr2_to_ascii(d->sharename, &uni->sharename, sizeof(d->sharename)-1);
+       unistr2_to_ascii(d->portname, &uni->portname, sizeof(d->portname)-1);
+       unistr2_to_ascii(d->drivername, &uni->drivername, sizeof(d->drivername)-1);
+       unistr2_to_ascii(d->comment, &uni->comment, sizeof(d->comment)-1);
+       unistr2_to_ascii(d->location, &uni->location, sizeof(d->location)-1);
+       unistr2_to_ascii(d->sepfile, &uni->sepfile, sizeof(d->sepfile)-1);
+       unistr2_to_ascii(d->printprocessor, &uni->printprocessor, sizeof(d->printprocessor)-1);
+       unistr2_to_ascii(d->datatype, &uni->datatype, sizeof(d->datatype)-1);
+       unistr2_to_ascii(d->parameters, &uni->parameters, sizeof(d->parameters)-1);
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriverdir(SPOOL_Q_GETPRINTERDRIVERDIR *q_u,
+                                fstring servername, fstring env_name, uint32 level,
+                                NEW_BUFFER *buffer, uint32 offered)
+{
+       init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+       init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, env_name);
+
+       q_u->level=level;
+       q_u->buffer=buffer;
+       q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETPRINTERDRIVERDIR structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_getprinterdriverdir(const char *desc, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriverdir");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+               return False;
+       if(!smb_io_unistr2("", &q_u->name, q_u->name_ptr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if(!prs_uint32("", ps, depth, &q_u->environment_ptr))
+               return False;
+       if(!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+               return False;
+               
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+               
+       if(!prs_align(ps))
+               return False;
+               
+       if(!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETPRINTERDRIVERDIR structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_getprinterdriverdir(const char *desc, SPOOL_R_GETPRINTERDRIVERDIR *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriverdir");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumprintprocessors(const char *desc, SPOOL_R_ENUMPRINTPROCESSORS *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocessors");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumprintprocessors(const char *desc, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocessors");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+               return False;
+       if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("", ps, depth, &q_u->environment_ptr))
+               return False;
+       if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+               return False;
+       
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_addprintprocessor(const char *desc, SPOOL_Q_ADDPRINTPROCESSOR *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_addprintprocessor");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+               return False;
+       if (!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("environment", &q_u->environment, True, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("path", &q_u->path, True, ps, depth))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addprintprocessor(const char *desc, SPOOL_R_ADDPRINTPROCESSOR *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_addprintproicessor");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumprintprocdatatypes(const char *desc, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocdatatypes");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumprintprocdatatypes(const char *desc, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocdatatypes");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+               return False;
+       if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("processor_ptr", ps, depth, &q_u->processor_ptr))
+               return False;
+       if (!smb_io_unistr2("processor", &q_u->processor, q_u->processor_ptr, ps, depth))
+               return False;
+       
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if(!spoolss_io_buffer("buffer", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTMONITORS structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_enumprintmonitors(const char *desc, SPOOL_Q_ENUMPRINTMONITORS *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprintmonitors");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+               return False;
+       if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+                               
+       if (!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+               
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumprintmonitors(const char *desc, SPOOL_R_ENUMPRINTMONITORS *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprintmonitors");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_uint32("returned", ps, depth, &r_u->returned))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_enumprinterdata(const char *desc, SPOOL_R_ENUMPRINTERDATA *r_u, prs_struct *ps, int depth)
+{      
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdata");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("valuesize", ps, depth, &r_u->valuesize))
+               return False;
+
+       if (UNMARSHALLING(ps) && r_u->valuesize) {
+               r_u->value = (uint16 *)prs_alloc_mem(ps, r_u->valuesize * 2);
+               if (!r_u->value) {
+                       DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata value\n"));
+                       return False;
+               }
+       }
+
+       if(!prs_uint16uni(False, "value", ps, depth, r_u->value, r_u->valuesize ))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("realvaluesize", ps, depth, &r_u->realvaluesize))
+               return False;
+
+       if(!prs_uint32("type", ps, depth, &r_u->type))
+               return False;
+
+       if(!prs_uint32("datasize", ps, depth, &r_u->datasize))
+               return False;
+
+       if (UNMARSHALLING(ps) && r_u->datasize) {
+               r_u->data = (uint8 *)prs_alloc_mem(ps, r_u->datasize);
+               if (!r_u->data) {
+                       DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata data\n"));
+                       return False;
+               }
+       }
+
+       if(!prs_uint8s(False, "data", ps, depth, r_u->data, r_u->datasize))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("realdatasize", ps, depth, &r_u->realdatasize))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_enumprinterdata(const char *desc, SPOOL_Q_ENUMPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdata");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if(!prs_uint32("index", ps, depth, &q_u->index))
+               return False;
+       if(!prs_uint32("valuesize", ps, depth, &q_u->valuesize))
+               return False;
+       if(!prs_uint32("datasize", ps, depth, &q_u->datasize))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL make_spoolss_q_enumprinterdata(SPOOL_Q_ENUMPRINTERDATA *q_u,
+               const POLICY_HND *hnd,
+               uint32 idx, uint32 valuelen, uint32 datalen)
+{
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       q_u->index=idx;
+       q_u->valuesize=valuelen;
+       q_u->datasize=datalen;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL make_spoolss_q_enumprinterdataex(SPOOL_Q_ENUMPRINTERDATAEX *q_u,
+                                     const POLICY_HND *hnd, const char *key,
+                                     uint32 size)
+{
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       init_unistr2(&q_u->key, key, strlen(key)+1);
+       q_u->size = size;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+BOOL make_spoolss_q_setprinterdata(SPOOL_Q_SETPRINTERDATA *q_u, const POLICY_HND *hnd,
+                                  char* value, uint32 data_type, char* data, uint32 data_size)
+{
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       q_u->type = data_type;
+       init_unistr2(&q_u->value, value, strlen(value)+1);
+
+       q_u->max_len = q_u->real_len = data_size;
+       q_u->data = data;
+       
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+BOOL make_spoolss_q_setprinterdataex(SPOOL_Q_SETPRINTERDATAEX *q_u, const POLICY_HND *hnd,
+                                    char *key, char* value, uint32 data_type, char* data, 
+                                    uint32 data_size)
+{
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       q_u->type = data_type;
+       init_unistr2(&q_u->value, value, strlen(value)+1);
+       init_unistr2(&q_u->key, key, strlen(key)+1);
+
+       q_u->max_len = q_u->real_len = data_size;
+       q_u->data = data;
+       
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_setprinterdata(const char *desc, SPOOL_Q_SETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdata");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("type", ps, depth, &q_u->type))
+               return False;
+
+       if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+               return False;
+
+       switch (q_u->type)
+       {
+               case REG_SZ:
+               case REG_BINARY:
+               case REG_DWORD:
+               case REG_MULTI_SZ:
+            if (q_u->max_len) {
+                if (UNMARSHALLING(ps))
+                               q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+                       if(q_u->data == NULL)
+                               return False;
+                       if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+                               return False;
+            }
+                       if(!prs_align(ps))
+                               return False;
+                       break;
+       }       
+       
+       if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_setprinterdata(const char *desc, SPOOL_R_SETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdata");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+BOOL spoolss_io_q_resetprinter(const char *desc, SPOOL_Q_RESETPRINTER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_resetprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+
+       if (!prs_uint32("datatype_ptr", ps, depth, &q_u->datatype_ptr))
+               return False;
+               
+       if (q_u->datatype_ptr) {
+               if (!smb_io_unistr2("datatype", &q_u->datatype, q_u->datatype_ptr?True:False, ps, depth))
+               return False;
+       }
+
+       if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+********************************************************************/  
+BOOL spoolss_io_r_resetprinter(const char *desc, SPOOL_R_RESETPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_resetprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+static BOOL spoolss_io_addform(const char *desc, FORM *f, uint32 ptr, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_addform");
+       depth++;
+       if(!prs_align(ps))
+               return False;
+
+       if (ptr!=0)
+       {
+               if(!prs_uint32("flags",    ps, depth, &f->flags))
+                       return False;
+               if(!prs_uint32("name_ptr", ps, depth, &f->name_ptr))
+                       return False;
+               if(!prs_uint32("size_x",   ps, depth, &f->size_x))
+                       return False;
+               if(!prs_uint32("size_y",   ps, depth, &f->size_y))
+                       return False;
+               if(!prs_uint32("left",     ps, depth, &f->left))
+                       return False;
+               if(!prs_uint32("top",      ps, depth, &f->top))
+                       return False;
+               if(!prs_uint32("right",    ps, depth, &f->right))
+                       return False;
+               if(!prs_uint32("bottom",   ps, depth, &f->bottom))
+                       return False;
+
+               if(!smb_io_unistr2("", &f->name, f->name_ptr, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_deleteform(const char *desc, SPOOL_Q_DELETEFORM *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!smb_io_unistr2("form name", &q_u->name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_deleteform(const char *desc, SPOOL_R_DELETEFORM *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",        ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_addform(const char *desc, SPOOL_Q_ADDFORM *q_u, prs_struct *ps, int depth)
+{
+       uint32 useless_ptr=1;
+       prs_debug(ps, depth, desc, "spoolss_io_q_addform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!prs_uint32("level",  ps, depth, &q_u->level))
+               return False;
+       if(!prs_uint32("level2", ps, depth, &q_u->level2))
+               return False;
+
+       if (q_u->level==1)
+       {
+               if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+                       return False;
+               if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_addform(const char *desc, SPOOL_R_ADDFORM *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_addform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",        ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_q_setform(const char *desc, SPOOL_Q_SETFORM *q_u, prs_struct *ps, int depth)
+{
+       uint32 useless_ptr=1;
+       prs_debug(ps, depth, desc, "spoolss_io_q_setform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &q_u->name, True, ps, depth))
+               return False;
+             
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("level",  ps, depth, &q_u->level))
+               return False;
+       if(!prs_uint32("level2", ps, depth, &q_u->level2))
+               return False;
+
+       if (q_u->level==1)
+       {
+               if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+                       return False;
+               if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+BOOL spoolss_io_r_setform(const char *desc, SPOOL_R_SETFORM *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_setform");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",        ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETJOB structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_getjob(const char *desc, SPOOL_R_GETJOB *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_getjob");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+
+       if (!prs_align(ps))
+               return False;
+               
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+               
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETJOB structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_getjob(const char *desc, SPOOL_Q_GETJOB *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+               return False;
+       if(!prs_uint32("level", ps, depth, &q_u->level))
+               return False;
+       
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+void free_devmode(DEVICEMODE *devmode)
+{
+       if (devmode!=NULL) {
+               SAFE_FREE(devmode->private);
+               SAFE_FREE(devmode);
+       }
+}
+
+void free_printer_info_1(PRINTER_INFO_1 *printer)
+{
+       SAFE_FREE(printer);
+}
+
+void free_printer_info_2(PRINTER_INFO_2 *printer)
+{
+       if (printer!=NULL) {
+               free_devmode(printer->devmode);
+               printer->devmode = NULL;
+               SAFE_FREE(printer);
+       }
+}
+
+void free_printer_info_3(PRINTER_INFO_3 *printer)
+{
+       SAFE_FREE(printer);
+}
+
+void free_printer_info_4(PRINTER_INFO_4 *printer)
+{
+       SAFE_FREE(printer);
+}
+
+void free_printer_info_5(PRINTER_INFO_5 *printer)
+{
+       SAFE_FREE(printer);
+}
+
+void free_printer_info_7(PRINTER_INFO_7 *printer)
+{
+       SAFE_FREE(printer);
+}
+
+void free_job_info_2(JOB_INFO_2 *job)
+{
+    if (job!=NULL)
+        free_devmode(job->devmode);
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_replyopenprinter(SPOOL_Q_REPLYOPENPRINTER *q_u, 
+                              const fstring string, uint32 printer, uint32 type)
+{      
+       if (q_u == NULL)
+               return False;
+
+       init_unistr2(&q_u->string, string, strlen(string)+1);
+
+       q_u->printer=printer;
+       q_u->type=type;
+
+       q_u->unknown0=0x0;
+       q_u->unknown1=0x0;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYOPENPRINTER structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_replyopenprinter(const char *desc, SPOOL_Q_REPLYOPENPRINTER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_replyopenprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &q_u->string, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("printer", ps, depth, &q_u->printer))
+               return False;
+       if(!prs_uint32("type", ps, depth, &q_u->type))
+               return False;
+       
+       if(!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+               return False;
+       if(!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYOPENPRINTER structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_replyopenprinter(const char *desc, SPOOL_R_REPLYOPENPRINTER *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_replyopenprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_routerreplyprinter(SPOOL_Q_ROUTERREPLYPRINTER *q_u, POLICY_HND *hnd, 
+                                       uint32 condition, uint32 change_id)
+{
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       q_u->condition = condition;
+       q_u->change_id = change_id;
+
+       /* magic values */
+       q_u->unknown1 = 0x1;
+       memset(q_u->unknown2, 0x0, 5);
+       q_u->unknown2[0] = 0x1;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_q_routerreplyprinter (const char *desc, SPOOL_Q_ROUTERREPLYPRINTER *q_u, prs_struct *ps, int depth)
+{
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_routerreplyprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       if (!prs_uint32("condition", ps, depth, &q_u->condition))
+               return False;
+
+       if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+               return False;
+
+       if (!prs_uint32("change_id", ps, depth, &q_u->change_id))
+               return False;
+
+       if (!prs_uint8s(False, "private",  ps, depth, q_u->unknown2, 5))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_r_routerreplyprinter (const char *desc, SPOOL_R_ROUTERREPLYPRINTER *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_routerreplyprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_closeprinter(SPOOL_Q_REPLYCLOSEPRINTER *q_u, POLICY_HND *hnd)
+{      
+       if (q_u == NULL)
+               return False;
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYCLOSEPRINTER structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_replycloseprinter(const char *desc, SPOOL_Q_REPLYCLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_replycloseprinter");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYCLOSEPRINTER structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_replycloseprinter(const char *desc, SPOOL_R_REPLYCLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_replycloseprinter");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+#if 0  /* JERRY - not currently used but could be :-) */
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO_DATA structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info_data(SPOOL_NOTIFY_INFO_DATA *dst, 
+                               SPOOL_NOTIFY_INFO_DATA *src, int n)
+{
+       int i;
+
+       memcpy(dst, src, sizeof(SPOOL_NOTIFY_INFO_DATA)*n);
+       
+       for (i=0; i<n; i++) {
+               int len;
+               uint16 *s = NULL;
+               
+               if (src->size != POINTER) 
+                       continue;
+               len = src->notify_data.data.length;
+               s = malloc(sizeof(uint16)*len);
+               if (s == NULL) {
+                       DEBUG(0,("copy_spool_notify_info_data: malloc() failed!\n"));
+                       return False;
+               }
+               
+               memcpy(s, src->notify_data.data.string, len*2);
+               dst->notify_data.data.string = s;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info(SPOOL_NOTIFY_INFO *dst, SPOOL_NOTIFY_INFO *src)
+{
+       if (!dst) {
+               DEBUG(0,("copy_spool_notify_info: NULL destination pointer!\n"));
+               return False;
+       }
+               
+       dst->version = src->version;
+       dst->flags   = src->flags;
+       dst->count   = src->count;
+       
+       if (dst->count) 
+       {
+               dst->data = malloc(dst->count * sizeof(SPOOL_NOTIFY_INFO_DATA));
+               
+               DEBUG(10,("copy_spool_notify_info: allocating space for [%d] PRINTER_NOTIFY_INFO_DATA entries\n",
+                       dst->count));
+
+               if (dst->data == NULL) {
+                       DEBUG(0,("copy_spool_notify_info: malloc() failed for [%d] entries!\n", 
+                               dst->count));
+                       return False;
+               }
+               
+               return (copy_spool_notify_info_data(dst->data, src->data, src->count));
+       }
+       
+       return True;
+}
+#endif /* JERRY */
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_rrpcn(SPOOL_Q_REPLY_RRPCN *q_u, POLICY_HND *hnd,
+                               uint32 change_low, uint32 change_high,
+                               SPOOL_NOTIFY_INFO *info)
+{      
+       if (q_u == NULL)
+               return False;
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+       q_u->change_low=change_low;
+       q_u->change_high=change_high;
+
+       q_u->unknown0=0x0;
+       q_u->unknown1=0x0;
+
+       q_u->info_ptr=0x0FF0ADDE;
+
+       q_u->info.version=2;
+       
+       if (info->count) {
+               DEBUG(10,("make_spoolss_q_reply_rrpcn: [%d] PRINTER_NOTIFY_INFO_DATA\n",
+                       info->count));
+               q_u->info.version = info->version;
+               q_u->info.flags   = info->flags;
+               q_u->info.count   = info->count;
+               /* pointer field - be careful! */
+               q_u->info.data    = info->data;
+       }
+       else  {
+       q_u->info.flags=PRINTER_NOTIFY_INFO_DISCARDED;
+       q_u->info.count=0;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLY_RRPCN structure.
+********************************************************************/  
+
+BOOL spoolss_io_q_reply_rrpcn(const char *desc, SPOOL_Q_REPLY_RRPCN *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_reply_rrpcn");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+
+       if (!prs_uint32("change_low", ps, depth, &q_u->change_low))
+               return False;
+
+       if (!prs_uint32("change_high", ps, depth, &q_u->change_high))
+               return False;
+
+       if (!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+               return False;
+
+       if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+               return False;
+
+       if (!prs_uint32("info_ptr", ps, depth, &q_u->info_ptr))
+               return False;
+
+       if(q_u->info_ptr!=0)
+               if(!smb_io_notify_info(desc, &q_u->info, ps, depth))
+                       return False;
+               
+       return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLY_RRPCN structure.
+********************************************************************/  
+
+BOOL spoolss_io_r_reply_rrpcn(const char *desc, SPOOL_R_REPLY_RRPCN *r_u, prs_struct *ps, int depth)
+{              
+       prs_debug(ps, depth, desc, "spoolss_io_r_reply_rrpcn");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("unknown0", ps, depth, &r_u->unknown0))
+               return False;
+
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+
+       return True;            
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdataex(const char *desc, SPOOL_Q_GETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdataex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("keyname", &q_u->keyname,True,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+               return False;
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("size", ps, depth, &q_u->size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdataex(const char *desc, SPOOL_R_GETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdataex");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+       if (!prs_uint32("type", ps, depth, &r_u->type))
+               return False;
+       if (!prs_uint32("size", ps, depth, &r_u->size))
+               return False;
+       
+       if (UNMARSHALLING(ps) && r_u->size) {
+               r_u->data = prs_alloc_mem(ps, r_u->size);
+               if(!r_u->data)
+                       return False;
+       }
+
+       if (!prs_uint8s(False,"data", ps, depth, r_u->data, r_u->size))
+               return False;
+               
+       if (!prs_align(ps))
+               return False;
+       
+       if (!prs_uint32("needed", ps, depth, &r_u->needed))
+               return False;
+       if (!prs_werror("status", ps, depth, &r_u->status))
+               return False;
+               
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_q_setprinterdataex(const char *desc, SPOOL_Q_SETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdataex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("type", ps, depth, &q_u->type))
+               return False;
+
+       if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+               return False;
+
+       switch (q_u->type)
+       {
+               case 0x1:
+               case 0x3:
+               case 0x4:
+               case 0x7:
+                       if (q_u->max_len) {
+                               if (UNMARSHALLING(ps))
+                                       q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+                               if(q_u->data == NULL)
+                                       return False;
+                               if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+                                       return False;
+                       }
+                       if(!prs_align(ps))
+                               return False;
+                       break;
+       }       
+       
+       if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_r_setprinterdataex(const char *desc, SPOOL_R_SETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdataex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+BOOL make_spoolss_q_enumprinterkey(SPOOL_Q_ENUMPRINTERKEY *q_u, 
+                                  POLICY_HND *hnd, const char *key, 
+                                  uint32 size)
+{
+       DEBUG(5,("make_spoolss_q_enumprinterkey\n"));
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       init_unistr2(&q_u->key, key, strlen(key)+1);
+       q_u->size = size;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_q_enumprinterkey(const char *desc, SPOOL_Q_ENUMPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterkey");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+               
+       if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("size", ps, depth, &q_u->size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_r_enumprinterkey(const char *desc, SPOOL_R_ENUMPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterkey");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!smb_io_buffer5("", &r_u->keys, ps, depth))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("needed",     ps, depth, &r_u->needed))
+               return False;
+
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+
+BOOL make_spoolss_q_deleteprinterkey(SPOOL_Q_DELETEPRINTERKEY *q_u, 
+                                    POLICY_HND *hnd, char *keyname)
+{
+       DEBUG(5,("make_spoolss_q_deleteprinterkey\n"));
+
+       memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+       init_unistr2(&q_u->keyname, keyname, strlen(keyname)+1);
+
+       return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_q_deleteprinterkey(const char *desc, SPOOL_Q_DELETEPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterkey");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+               
+       if(!smb_io_unistr2("", &q_u->keyname, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_r_deleteprinterkey(const char *desc, SPOOL_R_DELETEPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterkey");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+               
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_q_enumprinterdataex(const char *desc, SPOOL_Q_ENUMPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdataex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+               return False;
+               
+       if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("size", ps, depth, &q_u->size))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+********************************************************************/  
+
+static BOOL spoolss_io_printer_enum_values_ctr(const char *desc, prs_struct *ps, 
+                               PRINTER_ENUM_VALUES_CTR *ctr, int depth)
+{
+       int     i;
+       uint32  valuename_offset,
+               data_offset,
+               current_offset;
+       const uint32 basic_unit = 20; /* size of static portion of enum_values */
+       
+       prs_debug(ps, depth, desc, "spoolss_io_printer_enum_values_ctr");
+       depth++;        
+       
+       /* 
+        * offset data begins at 20 bytes per structure * size_of_array.
+        * Don't forget the uint32 at the beginning 
+        * */
+       
+       current_offset = basic_unit * ctr->size_of_array;
+       
+       /* first loop to write basic enum_value information */
+       
+       if (UNMARSHALLING(ps)) {
+               ctr->values = (PRINTER_ENUM_VALUES *)prs_alloc_mem(
+                       ps, ctr->size_of_array * sizeof(PRINTER_ENUM_VALUES));
+               if (!ctr->values)
+                       return False;
+       }
+
+       for (i=0; i<ctr->size_of_array; i++) {
+               valuename_offset = current_offset;
+               if (!prs_uint32("valuename_offset", ps, depth, &valuename_offset))
+                       return False;
+
+               if (!prs_uint32("value_len", ps, depth, &ctr->values[i].value_len))
+                       return False;
+       
+               if (!prs_uint32("type", ps, depth, &ctr->values[i].type))
+                       return False;
+       
+               data_offset = ctr->values[i].value_len + valuename_offset;
+               
+               if (!prs_uint32("data_offset", ps, depth, &data_offset))
+                       return False;
+
+               if (!prs_uint32("data_len", ps, depth, &ctr->values[i].data_len))
+                       return False;
+                       
+               current_offset  = data_offset + ctr->values[i].data_len - basic_unit;
+               /* account for 2 byte alignment */
+               current_offset += (current_offset % 2);
+       }
+
+       /* 
+        * loop #2 for writing the dynamically size objects; pay 
+        * attention to 2-byte alignment here....
+        */
+       
+       for (i=0; i<ctr->size_of_array; i++) {
+       
+               if (!prs_unistr("valuename", ps, depth, &ctr->values[i].valuename))
+                       return False;
+               
+               if (UNMARSHALLING(ps)) {
+                       ctr->values[i].data = (uint8 *)prs_alloc_mem(
+                               ps, ctr->values[i].data_len);
+                       if (!ctr->values[i].data)
+                               return False;
+               }
+
+               if (!prs_uint8s(False, "data", ps, depth, ctr->values[i].data, ctr->values[i].data_len))
+                       return False;
+                       
+               if ( !prs_align_uint16(ps) )
+                       return False;
+       }
+
+       return True;    
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_r_enumprinterdataex(const char *desc, SPOOL_R_ENUMPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+       uint32 data_offset, end_offset;
+       prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdataex");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("size", ps, depth, &r_u->ctr.size))
+               return False;
+
+       data_offset = prs_offset(ps);
+
+       if (!prs_set_offset(ps, data_offset + r_u->ctr.size))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("needed",     ps, depth, &r_u->needed))
+               return False;
+
+       if(!prs_uint32("returned",   ps, depth, &r_u->returned))
+               return False;
+
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       r_u->ctr.size_of_array = r_u->returned;
+
+       end_offset = prs_offset(ps);
+
+       if (!prs_set_offset(ps, data_offset))
+               return False;
+
+       if (r_u->ctr.size)
+               if (!spoolss_io_printer_enum_values_ctr("", ps, &r_u->ctr, depth ))
+                       return False;
+
+       if (!prs_set_offset(ps, end_offset))
+               return False;
+                                                                                                                                                       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+/* 
+   uint32 GetPrintProcessorDirectory(
+       [in] unistr2 *name,
+       [in] unistr2 *environment,
+       [in] uint32 level,
+       [in,out] NEW_BUFFER buffer,
+       [in] uint32 offered,
+       [out] uint32 needed,
+       [out] uint32 returned
+   );
+
+*/
+
+BOOL make_spoolss_q_getprintprocessordirectory(SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, const char *name, char *environment, int level, NEW_BUFFER *buffer, uint32 offered)
+{
+       DEBUG(5,("make_spoolss_q_getprintprocessordirectory\n"));
+
+       init_unistr2(&q_u->name, name, strlen(name)+1);
+       init_unistr2(&q_u->environment, environment, strlen(environment)+1);
+
+       q_u->level = level;
+
+       q_u->buffer = buffer;
+       q_u->offered = offered;
+
+       return True;
+}
+
+BOOL spoolss_io_q_getprintprocessordirectory(const char *desc, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, prs_struct *ps, int depth)
+{
+       uint32 ptr;
+
+       prs_debug(ps, depth, desc, "spoolss_io_q_getprintprocessordirectory");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;   
+
+       if (!prs_uint32("ptr", ps, depth, &ptr)) 
+               return False;
+
+       if (ptr) {
+               if(!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+                       return False;
+       }
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr", ps, depth, &ptr))
+               return False;
+
+       if (ptr) {
+               if(!smb_io_unistr2("environment", &q_u->environment, True, 
+                                  ps, depth))
+                       return False;
+       }
+
+       if (!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("level",   ps, depth, &q_u->level))
+               return False;
+
+       if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("offered", ps, depth, &q_u->offered))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/  
+
+BOOL spoolss_io_r_getprintprocessordirectory(const char *desc, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u, prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "spoolss_io_r_getprintprocessordirectory");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+               return False;
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("needed",     ps, depth, &r_u->needed))
+               return False;
+               
+       if(!prs_werror("status",     ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
+
+BOOL smb_io_printprocessordirectory_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_DIRECTORY_1 *info, int depth)
+{
+       prs_struct *ps=&buffer->prs;
+
+       prs_debug(ps, depth, desc, "smb_io_printprocessordirectory_1");
+       depth++;
+
+       buffer->struct_start=prs_offset(ps);
+
+       if (!smb_io_unistr(desc, &info->name, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addform(SPOOL_Q_ADDFORM *q_u, POLICY_HND *handle, 
+                           int level, FORM *form)
+{
+       memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       q_u->level = level;
+       q_u->level2 = level;
+       memcpy(&q_u->form, form, sizeof(FORM));
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setform(SPOOL_Q_SETFORM *q_u, POLICY_HND *handle, 
+                           int level, const char *form_name, FORM *form)
+{
+       memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       q_u->level = level;
+       q_u->level2 = level;
+       memcpy(&q_u->form, form, sizeof(FORM));
+       init_unistr2(&q_u->name, form_name, strlen(form_name) + 1);
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteform(SPOOL_Q_DELETEFORM *q_u, POLICY_HND *handle, 
+                              const char *form)
+{
+       memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       init_unistr2(&q_u->name, form, strlen(form) + 1);
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getform(SPOOL_Q_GETFORM *q_u, POLICY_HND *handle, 
+                            const char *formname, uint32 level, 
+                           NEW_BUFFER *buffer, uint32 offered)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+        q_u->level = level;
+        init_unistr2(&q_u->formname, formname, strlen(formname) + 1);
+        q_u->buffer=buffer;
+        q_u->offered=offered;
+
+        return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumforms(SPOOL_Q_ENUMFORMS *q_u, POLICY_HND *handle, 
+                             uint32 level, NEW_BUFFER *buffer,
+                             uint32 offered)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+        q_u->level = level;
+        q_u->buffer=buffer;
+        q_u->offered=offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setjob(SPOOL_Q_SETJOB *q_u, POLICY_HND *handle, 
+                          uint32 jobid, uint32 level, uint32 command)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       q_u->jobid = jobid;
+        q_u->level = level;
+
+       /* Hmm - the SPOOL_Q_SETJOB structure has a JOB_INFO ctr in it but
+          the server side code has it marked as unused. */
+
+        q_u->command = command;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getjob(SPOOL_Q_GETJOB *q_u, POLICY_HND *handle, 
+                          uint32 jobid, uint32 level, NEW_BUFFER *buffer,
+                          uint32 offered)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+        q_u->jobid = jobid;
+        q_u->level = level;
+        q_u->buffer = buffer;
+        q_u->offered = offered;
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startpageprinter(SPOOL_Q_STARTPAGEPRINTER *q_u, 
+                                    POLICY_HND *handle)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_endpageprinter(SPOOL_Q_ENDPAGEPRINTER *q_u, 
+                                  POLICY_HND *handle)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startdocprinter(SPOOL_Q_STARTDOCPRINTER *q_u, 
+                                   POLICY_HND *handle, uint32 level,
+                                   char *docname, char *outputfile,
+                                   char *datatype)
+{
+       DOC_INFO_CONTAINER *ctr = &q_u->doc_info_container;
+
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+       ctr->level = level;
+
+       switch (level) {
+       case 1:
+               ctr->docinfo.switch_value = level;
+
+               ctr->docinfo.doc_info_1.p_docname = docname ? 1 : 0;
+               ctr->docinfo.doc_info_1.p_outputfile = outputfile ? 1 : 0;
+               ctr->docinfo.doc_info_1.p_datatype = datatype ? 1 : 0;
+
+               if (docname)
+                       init_unistr2(&ctr->docinfo.doc_info_1.docname, docname,
+                                    strlen(docname) + 1);
+
+               if (outputfile)
+                       init_unistr2(&ctr->docinfo.doc_info_1.outputfile, outputfile,
+                                    strlen(outputfile) + 1);
+
+               if (datatype)
+                       init_unistr2(&ctr->docinfo.doc_info_1.datatype, datatype,
+                                    strlen(datatype) + 1);
+
+               break;
+       case 2:
+               /* DOC_INFO_2 is only used by Windows 9x and since it
+                  doesn't do printing over RPC we don't have to worry
+                  about it. */
+       default:
+               DEBUG(3, ("unsupported info level %d\n", level));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enddocprinter(SPOOL_Q_ENDDOCPRINTER *q_u, 
+                                 POLICY_HND *handle)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_writeprinter(SPOOL_Q_WRITEPRINTER *q_u, 
+                                POLICY_HND *handle, uint32 data_size,
+                                char *data)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       q_u->buffer_size = q_u->buffer_size2 = data_size;
+       q_u->buffer = data;
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdata(SPOOL_Q_DELETEPRINTERDATA *q_u, 
+                                POLICY_HND *handle, char *valuename)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       init_unistr2(&q_u->valuename, valuename, strlen(valuename) + 1);
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdataex(SPOOL_Q_DELETEPRINTERDATAEX *q_u, 
+                                       POLICY_HND *handle, char *key,
+                                       char *value)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+       init_unistr2(&q_u->valuename, value, strlen(value) + 1);
+       init_unistr2(&q_u->keyname, key, strlen(key) + 1);
+
+       return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_rffpcnex(SPOOL_Q_RFFPCNEX *q_u, POLICY_HND *handle,
+                            uint32 flags, uint32 options, const char *localmachine,
+                            uint32 printerlocal, SPOOL_NOTIFY_OPTION *option)
+{
+        memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+       q_u->flags = flags;
+       q_u->options = options;
+
+       q_u->localmachine_ptr = 1;
+
+       init_unistr2(&q_u->localmachine, localmachine, 
+                    strlen(localmachine) + 1);
+
+       q_u->printerlocal = printerlocal;
+
+       if (option)
+               q_u->option_ptr = 1;
+
+       q_u->option = option;
+
+       return True;
+}
diff --git a/source4/rpc_parse/parse_srv.c b/source4/rpc_parse/parse_srv.c
new file mode 100644 (file)
index 0000000..bfa1a13
--- /dev/null
@@ -0,0 +1,3590 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Jeremy Allison                   1999,
+ *  Copyright (C) Nigel Williams                   2001,
+ *  Copyright (C) Jim McDonough (jmcd@us.ibm.com)   2002.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Inits a SH_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_share_info0_str(SH_INFO_0_STR *sh0, const char *net_name)
+{
+       DEBUG(5,("init_srv_share_info0_str\n"));
+
+       if(net_name)
+               init_unistr2(&sh0->uni_netname, net_name, strlen(net_name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0_str(const char *desc, SH_INFO_0_STR *sh0, prs_struct *ps, int depth)
+{
+       if (sh0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info0_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(sh0->ptrs->ptr_netname)
+               if(!smb_io_unistr2("", &sh0->uni_netname, True, ps, depth))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_0 structure
+********************************************************************/
+
+void init_srv_share_info0(SH_INFO_0 *sh0, const char *net_name)
+{
+       DEBUG(5,("init_srv_share_info0: %s\n", net_name));
+
+       sh0->ptr_netname = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0(const char *desc, SH_INFO_0 *sh0, prs_struct *ps, int depth)
+{
+       if (sh0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info0");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_netname", ps, depth, &sh0->ptr_netname))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_share_info1_str(SH_INFO_1_STR *sh1, const char *net_name, const char *remark)
+{
+       DEBUG(5,("init_srv_share_info1_str\n"));
+
+       if(net_name)
+               init_unistr2(&sh1->uni_netname, net_name, strlen(net_name)+1);
+       if(remark)
+               init_unistr2(&sh1->uni_remark, remark, strlen(remark)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1_str(const char *desc, SH_INFO_1_STR *sh1, prs_struct *ps, int depth)
+{
+       if (sh1 == NULL)
+               return False;
+       
+       prs_debug(ps, depth, desc, "srv_io_share_info1_str");
+       depth++;
+       
+       if(!prs_align(ps))
+               return False;
+
+       if(sh1->ptrs->ptr_netname)
+               if(!smb_io_unistr2("", &sh1->uni_netname, True, ps, depth))
+                       return False;
+       
+       if(!prs_align(ps))
+               return False;
+       
+       if(sh1->ptrs->ptr_remark)
+               if(!smb_io_unistr2("", &sh1->uni_remark, True, ps, depth))
+                       return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1 structure
+********************************************************************/
+
+void init_srv_share_info1(SH_INFO_1 *sh1, const char *net_name, uint32 type, const char *remark)
+{
+       DEBUG(5,("init_srv_share_info1: %s %8x %s\n", net_name, type, remark));
+       
+       sh1->ptr_netname = (net_name != NULL) ? 1 : 0;
+       sh1->type        = type;
+       sh1->ptr_remark  = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1(const char *desc, SH_INFO_1 *sh1, prs_struct *ps, int depth)
+{
+       if (sh1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_netname", ps, depth, &sh1->ptr_netname))
+               return False;
+       if(!prs_uint32("type       ", ps, depth, &sh1->type))
+               return False;
+       if(!prs_uint32("ptr_remark ", ps, depth, &sh1->ptr_remark))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2_STR structure
+********************************************************************/
+
+void init_srv_share_info2_str(SH_INFO_2_STR *sh2,
+                               const char *net_name, const char *remark,
+                               const char *path, const char *passwd)
+{
+       DEBUG(5,("init_srv_share_info2_str\n"));
+
+       if (net_name)
+               init_unistr2(&sh2->uni_netname, net_name, strlen(net_name)+1);
+       if (remark)
+               init_unistr2(&sh2->uni_remark, remark, strlen(remark)+1);
+       if (path)
+               init_unistr2(&sh2->uni_path, path, strlen(path)+1);
+       if (passwd)
+               init_unistr2(&sh2->uni_passwd, passwd, strlen(passwd)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2_str(const char *desc, SH_INFO_2 *sh, SH_INFO_2_STR *sh2, prs_struct *ps, int depth)
+{
+       if (sh2 == NULL)
+               return False;
+
+       if (UNMARSHALLING(ps))
+               ZERO_STRUCTP(sh2);
+
+       prs_debug(ps, depth, desc, "srv_io_share_info2_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (sh->ptr_netname)
+               if(!smb_io_unistr2("", &sh2->uni_netname, True, ps, depth))
+                       return False;
+
+       if (sh->ptr_remark)
+               if(!smb_io_unistr2("", &sh2->uni_remark, True, ps, depth))
+                       return False;
+
+       if (sh->ptr_netname)
+               if(!smb_io_unistr2("", &sh2->uni_path, True, ps, depth))
+                       return False;
+
+       if (sh->ptr_passwd)
+               if(!smb_io_unistr2("", &sh2->uni_passwd, True, ps, depth))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+********************************************************************/
+
+void init_srv_share_info2(SH_INFO_2 *sh2,
+                               const char *net_name, uint32 type, const char *remark,
+                               uint32 perms, uint32 max_uses, uint32 num_uses,
+                               const char *path, const char *passwd)
+{
+       DEBUG(5,("init_srv_share_info2: %s %8x %s\n", net_name, type, remark));
+
+       sh2->ptr_netname = (net_name != NULL) ? 1 : 0;
+       sh2->type        = type;
+       sh2->ptr_remark  = (remark != NULL) ? 1 : 0;
+       sh2->perms       = perms;
+       sh2->max_uses    = max_uses;
+       sh2->num_uses    = num_uses;
+       sh2->ptr_path    = (path != NULL) ? 1 : 0;
+       sh2->ptr_passwd  = (passwd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2(const char *desc, SH_INFO_2 *sh2, prs_struct *ps, int depth)
+{
+       if (sh2 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info2");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_netname", ps, depth, &sh2->ptr_netname))
+               return False;
+       if(!prs_uint32("type       ", ps, depth, &sh2->type))
+               return False;
+       if(!prs_uint32("ptr_remark ", ps, depth, &sh2->ptr_remark))
+               return False;
+       if(!prs_uint32("perms      ", ps, depth, &sh2->perms))
+               return False;
+       if(!prs_uint32("max_uses   ", ps, depth, &sh2->max_uses))
+               return False;
+       if(!prs_uint32("num_uses   ", ps, depth, &sh2->num_uses))
+               return False;
+       if(!prs_uint32("ptr_path   ", ps, depth, &sh2->ptr_path))
+               return False;
+       if(!prs_uint32("ptr_passwd ", ps, depth, &sh2->ptr_passwd))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_501_STR structure
+********************************************************************/
+
+void init_srv_share_info501_str(SH_INFO_501_STR *sh501,
+                               const char *net_name, const char *remark)
+{
+       DEBUG(5,("init_srv_share_info501_str\n"));
+
+       if(net_name)
+               init_unistr2(&sh501->uni_netname, net_name, strlen(net_name)+1);
+       if(remark)
+               init_unistr2(&sh501->uni_remark, remark, strlen(remark)+1);
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+*******************************************************************/
+
+void init_srv_share_info501(SH_INFO_501 *sh501, const char *net_name, uint32 type, const char *remark, uint32 csc_policy)
+{
+       DEBUG(5,("init_srv_share_info501: %s %8x %s %08x\n", net_name, type,
+               remark, csc_policy));
+
+       ZERO_STRUCTP(sh501);
+
+       sh501->ptr_netname = (net_name != NULL) ? 1 : 0;
+       sh501->type = type;
+       sh501->ptr_remark = (remark != NULL) ? 1 : 0;
+       sh501->csc_policy = csc_policy;
+}
+
+/*******************************************************************
+ Reads of writes a structure.
+*******************************************************************/
+
+static BOOL srv_io_share_info501(const char *desc, SH_INFO_501 *sh501, prs_struct *ps, int depth)
+{
+       if (sh501 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info501");
+       depth++;
+
+       if (!prs_align(ps))
+               return False;
+
+       if (!prs_uint32("ptr_netname", ps, depth, &sh501->ptr_netname))
+               return False;
+       if (!prs_uint32("type     ", ps, depth, &sh501->type))
+               return False;
+       if (!prs_uint32("ptr_remark ", ps, depth, &sh501->ptr_remark))
+               return False;
+       if (!prs_uint32("csc_policy ", ps, depth, &sh501->csc_policy))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info501_str(const char *desc, SH_INFO_501_STR *sh501, prs_struct *ps, int depth)
+{
+       if (sh501 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info501_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("", &sh501->uni_netname, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("", &sh501->uni_remark, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502 structure
+********************************************************************/
+
+void init_srv_share_info502(SH_INFO_502 *sh502,
+                               const char *net_name, uint32 type, const char *remark,
+                               uint32 perms, uint32 max_uses, uint32 num_uses,
+                               const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+       DEBUG(5,("init_srv_share_info502: %s %8x %s\n", net_name, type, remark));
+
+       ZERO_STRUCTP(sh502);
+
+       sh502->ptr_netname = (net_name != NULL) ? 1 : 0;
+       sh502->type        = type;
+       sh502->ptr_remark  = (remark != NULL) ? 1 : 0;
+       sh502->perms       = perms;
+       sh502->max_uses    = max_uses;
+       sh502->num_uses    = num_uses;
+       sh502->ptr_path    = (path != NULL) ? 1 : 0;
+       sh502->ptr_passwd  = (passwd != NULL) ? 1 : 0;
+       sh502->reserved    = 0;  /* actual size within rpc */
+       sh502->sd_size     = (uint32)sd_size;
+       sh502->ptr_sd      = (psd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502(const char *desc, SH_INFO_502 *sh502, prs_struct *ps, int depth)
+{
+       if (sh502 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info502");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_netname", ps, depth, &sh502->ptr_netname))
+               return False;
+       if(!prs_uint32("type       ", ps, depth, &sh502->type))
+               return False;
+       if(!prs_uint32("ptr_remark ", ps, depth, &sh502->ptr_remark))
+               return False;
+       if(!prs_uint32("perms      ", ps, depth, &sh502->perms))
+               return False;
+       if(!prs_uint32("max_uses   ", ps, depth, &sh502->max_uses))
+               return False;
+       if(!prs_uint32("num_uses   ", ps, depth, &sh502->num_uses))
+               return False;
+       if(!prs_uint32("ptr_path   ", ps, depth, &sh502->ptr_path))
+               return False;
+       if(!prs_uint32("ptr_passwd ", ps, depth, &sh502->ptr_passwd))
+               return False;
+       if(!prs_uint32_pre("reserved   ", ps, depth, &sh502->reserved, &sh502->reserved_offset))
+               return False;
+       if(!prs_uint32("ptr_sd     ", ps, depth, &sh502->ptr_sd))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502_STR structure
+********************************************************************/
+
+void init_srv_share_info502_str(SH_INFO_502_STR *sh502str,
+                               const char *net_name, const char *remark,
+                               const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+       DEBUG(5,("init_srv_share_info502_str\n"));
+
+       if(net_name)
+               init_unistr2(&sh502str->uni_netname, net_name, strlen(net_name)+1);
+       if(remark)
+               init_unistr2(&sh502str->uni_remark, remark, strlen(remark)+1);
+       if(path)
+               init_unistr2(&sh502str->uni_path, path, strlen(path)+1);
+       if(passwd)
+               init_unistr2(&sh502str->uni_passwd, passwd, strlen(passwd)+1);
+               sh502str->sd = psd;
+       sh502str->reserved = 0;
+               sh502str->sd_size = sd_size;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502_str(const char *desc, SH_INFO_502_STR *sh502, prs_struct *ps, int depth)
+{
+       if (sh502 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info502_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(sh502->ptrs->ptr_netname) {
+               if(!smb_io_unistr2("", &sh502->uni_netname, True, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(sh502->ptrs->ptr_remark) {
+               if(!smb_io_unistr2("", &sh502->uni_remark, True, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(sh502->ptrs->ptr_path) {
+               if(!smb_io_unistr2("", &sh502->uni_path, True, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(sh502->ptrs->ptr_passwd) {
+               if(!smb_io_unistr2("", &sh502->uni_passwd, True, ps, depth))
+                       return False;
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(sh502->ptrs->ptr_sd) {
+               uint32 old_offset;
+               uint32 reserved_offset;
+
+               if(!prs_uint32_pre("reserved ", ps, depth, &sh502->reserved, &reserved_offset))
+                       return False;
+         
+               old_offset = prs_offset(ps);
+         
+               if (!sec_io_desc(desc, &sh502->sd, ps, depth))
+                       return False;
+
+               if(UNMARSHALLING(ps)) {
+
+                       sh502->ptrs->sd_size = sh502->sd_size = sec_desc_size(sh502->sd);
+
+                       prs_set_offset(ps, old_offset + sh502->reserved);
+               }
+
+               prs_align(ps);
+
+               if(MARSHALLING(ps)) {
+
+                       sh502->ptrs->reserved = sh502->reserved = prs_offset(ps) - old_offset;
+               }
+           
+               if(!prs_uint32_post("reserved ", ps, depth, 
+                                   &sh502->reserved, reserved_offset, sh502->reserved))
+                       return False;
+               if(!prs_uint32_post("reserved ", ps, depth, 
+                                   &sh502->ptrs->reserved, sh502->ptrs->reserved_offset, sh502->ptrs->reserved))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1004_STR structure
+********************************************************************/
+
+void init_srv_share_info1004_str(SH_INFO_1004_STR *sh1004, const char *remark)
+{
+       DEBUG(5,("init_srv_share_info1004_str\n"));
+
+       if(remark)
+               init_unistr2(&sh1004->uni_remark, remark, strlen(remark)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004_str(const char *desc, SH_INFO_1004_STR *sh1004, prs_struct *ps, int depth)
+{
+       if (sh1004 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1004_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(sh1004->ptrs->ptr_remark)
+               if(!smb_io_unistr2("", &sh1004->uni_remark, True, ps, depth))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1004 structure
+********************************************************************/
+
+void init_srv_share_info1004(SH_INFO_1004 *sh1004, const char *remark)
+{
+       DEBUG(5,("init_srv_share_info1004: %s\n", remark));
+
+       sh1004->ptr_remark = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004(const char *desc, SH_INFO_1004 *sh1004, prs_struct *ps, int depth)
+{
+       if (sh1004 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1004");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_remark", ps, depth, &sh1004->ptr_remark))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1005(const char* desc, SRV_SHARE_INFO_1005* sh1005, prs_struct* ps, int depth)
+{
+       if(sh1005 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1005");
+               depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("dfs_root_flag", ps, depth, &sh1005->dfs_root_flag))
+               return False;
+
+       return True;
+}   
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1006(const char* desc, SRV_SHARE_INFO_1006* sh1006, prs_struct* ps, int depth)
+{
+       if(sh1006 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1006");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("max uses     ", ps, depth, &sh1006->max_uses))
+               return False;
+
+       return True;
+}   
+
+/*******************************************************************
+ Inits a SH_INFO_1007_STR structure
+********************************************************************/
+
+void init_srv_share_info1007_str(SH_INFO_1007_STR *sh1007, const char *alternate_directory_name)
+{
+       DEBUG(5,("init_srv_share_info1007_str\n"));
+
+       if(alternate_directory_name)
+               init_unistr2(&sh1007->uni_AlternateDirectoryName, alternate_directory_name, strlen(alternate_directory_name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007_str(const char *desc, SH_INFO_1007_STR *sh1007, prs_struct *ps, int depth)
+{
+       if (sh1007 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1007_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       if(sh1007->ptrs->ptr_AlternateDirectoryName)
+               if(!smb_io_unistr2("", &sh1007->uni_AlternateDirectoryName, True, ps, depth))
+                       return False;
+
+       return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1007 structure
+********************************************************************/
+
+void init_srv_share_info1007(SH_INFO_1007 *sh1007, uint32 flags, const char *alternate_directory_name)
+{
+       DEBUG(5,("init_srv_share_info1007: %s\n", alternate_directory_name));
+
+       sh1007->flags                      = flags;
+       sh1007->ptr_AlternateDirectoryName = (alternate_directory_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007(const char *desc, SH_INFO_1007 *sh1007, prs_struct *ps, int depth)
+{
+       if (sh1007 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1007");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("flags      ", ps, depth, &sh1007->flags))
+               return False;
+       if(!prs_uint32("ptr_Alter..", ps, depth, &sh1007->ptr_AlternateDirectoryName))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1501(const char* desc, SRV_SHARE_INFO_1501* sh1501,
+                                 prs_struct* ps, int depth)
+{
+       if(sh1501 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_share_info1501");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if (!sec_io_desc_buf(desc, &sh1501->sdb, ps, depth))
+               return False;
+
+       return True;
+}   
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_ctr(const char *desc, SRV_SHARE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+       if (ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_share_ctr");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               memset(ctr, '\0', sizeof(SRV_SHARE_INFO_CTR));
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("info_level", ps, depth, &ctr->info_level))
+               return False;
+
+       if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_uint32("ptr_share_info", ps, depth, &ctr->ptr_share_info))
+               return False;
+
+       if (ctr->ptr_share_info == 0)
+               return True;
+
+       if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+               return False;
+       if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+               return False;
+
+       if (ctr->ptr_entries == 0) {
+               if (ctr->num_entries == 0)
+                       return True;
+               else
+                       return False;
+       }
+
+       if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries2))
+               return False;
+
+       if (ctr->num_entries2 != ctr->num_entries)
+               return False;
+
+       switch (ctr->switch_value) {
+
+       case 0:
+       {
+               SRV_SHARE_INFO_0 *info0 = ctr->share.info0;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info0 = (SRV_SHARE_INFO_0 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_0))))
+                               return False;
+                       ctr->share.info0 = info0;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info0("", &info0[i].info_0, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       info0[i].info_0_str.ptrs = &info0[i].info_0;
+                       if(!srv_io_share_info0_str("", &info0[i].info_0_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1:
+       {
+               SRV_SHARE_INFO_1 *info1 = ctr->share.info1;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1 = (SRV_SHARE_INFO_1 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_1))))
+                               return False;
+                       ctr->share.info1 = info1;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1("", &info1[i].info_1, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       info1[i].info_1_str.ptrs = &info1[i].info_1;
+                       if(!srv_io_share_info1_str("", &info1[i].info_1_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 2:
+       {
+               SRV_SHARE_INFO_2 *info2 = ctr->share.info2;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info2 = (SRV_SHARE_INFO_2 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_2))))
+                               return False;
+                       ctr->share.info2 = info2;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info2("", &info2[i].info_2, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info2_str("", &info2[i].info_2, &info2[i].info_2_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 501:
+       {
+               SRV_SHARE_INFO_501 *info501 = ctr->share.info501;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info501 = (SRV_SHARE_INFO_501 *) prs_alloc_mem(ps, num_entries *
+                                       sizeof (SRV_SHARE_INFO_501))))
+                               return False;
+                       ctr->share.info501 = info501;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if (!srv_io_share_info501("", &info501[i].info_501, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if (!srv_io_share_info501_str("", &info501[i].info_501_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 502:
+       {
+               SRV_SHARE_INFO_502 *info502 = ctr->share.info502;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info502 = (SRV_SHARE_INFO_502 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_502))))
+                               return False;
+                       ctr->share.info502 = info502;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info502("", &info502[i].info_502, ps, depth))
+                               return False;
+       }
+               
+               for (i = 0; i < num_entries; i++) {
+                       info502[i].info_502_str.ptrs = &info502[i].info_502;
+                       if(!srv_io_share_info502_str("", &info502[i].info_502_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1004:
+       {
+               SRV_SHARE_INFO_1004 *info1004 = ctr->share.info1004;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1004 = (SRV_SHARE_INFO_1004 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1004))))
+                               return False;
+                       ctr->share.info1004 = info1004;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1004("", &info1004[i].info_1004, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       info1004[i].info_1004_str.ptrs = &info1004[i].info_1004;
+                       if(!srv_io_share_info1004_str("", &info1004[i].info_1004_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1005:
+       {
+               SRV_SHARE_INFO_1005 *info1005 = ctr->share.info1005;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1005 = (SRV_SHARE_INFO_1005 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1005))))
+                               return False;
+                       ctr->share.info1005 = info1005;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1005("", &info1005[i], ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1006:
+       {
+               SRV_SHARE_INFO_1006 *info1006 = ctr->share.info1006;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1006 = (SRV_SHARE_INFO_1006 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1006))))
+                               return False;
+                       ctr->share.info1006 = info1006;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1006("", &info1006[i], ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1007:
+       {
+               SRV_SHARE_INFO_1007 *info1007 = ctr->share.info1007;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1007 = (SRV_SHARE_INFO_1007 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1007))))
+                               return False;
+                       ctr->share.info1007 = info1007;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1007("", &info1007[i].info_1007, ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       info1007[i].info_1007_str.ptrs = &info1007[i].info_1007;
+                       if(!srv_io_share_info1007_str("", &info1007[i].info_1007_str, ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       case 1501:
+       {
+               SRV_SHARE_INFO_1501 *info1501 = ctr->share.info1501;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info1501 = (SRV_SHARE_INFO_1501 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1501))))
+                               return False;
+                       ctr->share.info1501 = info1501;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_share_info1501("", &info1501[i], ps, depth))
+                               return False;
+               }
+
+               break;
+       }
+
+       default:
+               DEBUG(5,("%s no share info at switch_value %d\n",
+                        tab_depth(depth), ctr->switch_value));
+               break;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SHARE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_share_enum(SRV_Q_NET_SHARE_ENUM *q_n, 
+                               const char *srv_name, uint32 info_level,
+                               uint32 preferred_len, ENUM_HND *hnd)
+{
+
+       DEBUG(5,("init_q_net_share_enum\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+       q_n->ctr.info_level = q_n->ctr.switch_value = info_level;
+       q_n->ctr.ptr_share_info = 1;
+       q_n->ctr.num_entries  = 0;
+       q_n->ctr.ptr_entries  = 0;
+       q_n->ctr.num_entries2 = 0;
+       q_n->preferred_len = preferred_len;
+
+       memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_enum(const char *desc, SRV_Q_NET_SHARE_ENUM *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_share_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!srv_io_srv_share_ctr("share_ctr", &q_n->ctr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_enum(const char *desc, SRV_R_NET_SHARE_ENUM *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_share_enum");
+       depth++;
+
+       if(!srv_io_srv_share_ctr("share_ctr", &r_n->ctr, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_get_info(SRV_Q_NET_SHARE_GET_INFO *q_n, const char *srv_name, const char *share_name, uint32 info_level)
+{
+
+       uint32 ptr_share_name;
+
+       DEBUG(5,("init_srv_q_net_share_get_info\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name,   &q_n->ptr_srv_name, srv_name);
+       init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name,    share_name);
+
+       q_n->info_level = info_level;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_get_info(const char *desc, SRV_Q_NET_SHARE_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_share_get_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_info(const char *desc, prs_struct *ps, int depth, SRV_SHARE_INFO *r_n)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_share_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value ", ps, depth, &r_n->switch_value ))
+               return False;
+
+       if(!prs_uint32("ptr_share_ctr", ps, depth, &r_n->ptr_share_ctr))
+               return False;
+
+       if (r_n->ptr_share_ctr != 0) {
+               switch (r_n->switch_value) {
+               case 0:
+                       if(!srv_io_share_info0("", &r_n->share.info0.info_0, ps, depth))
+                               return False;
+
+                       /* allow access to pointers in the str part. */
+                       r_n->share.info0.info_0_str.ptrs = &r_n->share.info0.info_0;
+
+                       if(!srv_io_share_info0_str("", &r_n->share.info0.info_0_str, ps, depth))
+                               return False;
+
+                       break;
+               case 1:
+                       if(!srv_io_share_info1("", &r_n->share.info1.info_1, ps, depth))
+                               return False;
+
+                       /* allow access to pointers in the str part. */
+                       r_n->share.info1.info_1_str.ptrs = &r_n->share.info1.info_1;
+
+                       if(!srv_io_share_info1_str("", &r_n->share.info1.info_1_str, ps, depth))
+                               return False;
+
+                       break;
+               case 2:
+                       if(!srv_io_share_info2("", &r_n->share.info2.info_2, ps, depth))
+                               return False;
+
+                       if(!srv_io_share_info2_str("", &r_n->share.info2.info_2, &r_n->share.info2.info_2_str, ps, depth))
+                               return False;
+
+                       break;
+               case 501:
+                       if (!srv_io_share_info501("", &r_n->share.info501.info_501, ps, depth))
+                               return False;
+                       if (!srv_io_share_info501_str("", &r_n->share.info501.info_501_str, ps, depth))
+                               return False;
+                       break;
+
+               case 502:
+                       if(!srv_io_share_info502("", &r_n->share.info502.info_502, ps, depth))
+                               return False;
+
+                       /* allow access to pointers in the str part. */
+                       r_n->share.info502.info_502_str.ptrs = &r_n->share.info502.info_502;
+
+                       if(!srv_io_share_info502_str("", &r_n->share.info502.info_502_str, ps, depth))
+                               return False;
+                       break;
+               case 1004:
+                       if(!srv_io_share_info1004("", &r_n->share.info1004.info_1004, ps, depth))
+                               return False;
+
+                       /* allow access to pointers in the str part. */
+                       r_n->share.info1004.info_1004_str.ptrs = &r_n->share.info1004.info_1004;
+
+                       if(!srv_io_share_info1004_str("", &r_n->share.info1004.info_1004_str, ps, depth))
+                               return False;
+                       break;
+               case 1005:
+                       if(!srv_io_share_info1005("", &r_n->share.info1005, ps, depth))
+                               return False;           
+                       break;
+               case 1006:
+                       if(!srv_io_share_info1006("", &r_n->share.info1006, ps, depth))
+                               return False;           
+                       break;
+               case 1007:
+                       if(!srv_io_share_info1007("", &r_n->share.info1007.info_1007, ps, depth))
+                               return False;
+
+                       /* allow access to pointers in the str part. */
+                       r_n->share.info1007.info_1007_str.ptrs = &r_n->share.info1007.info_1007;
+
+                       if(!srv_io_share_info1007_str("", &r_n->share.info1007.info_1007_str, ps, depth))
+                               return False;
+                       break;
+               case 1501:
+                       if (!srv_io_share_info1501("", &r_n->share.info1501, ps, depth))
+                               return False;
+               default:
+                       DEBUG(5,("%s no share info at switch_value %d\n",
+                                tab_depth(depth), r_n->switch_value));
+                       break;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_get_info(const char *desc, SRV_R_NET_SHARE_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_share_get_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!srv_io_srv_share_info("info  ", ps, depth, &r_n->info))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ intialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_set_info(SRV_Q_NET_SHARE_SET_INFO *q_n, 
+                                  const char *srv_name, 
+                                  const char *share_name, 
+                                  uint32 info_level, 
+                                  const SRV_SHARE_INFO *info) 
+{
+
+       uint32 ptr_share_name;
+
+       DEBUG(5,("init_srv_q_net_share_set_info\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name,   &q_n->ptr_srv_name, srv_name);
+       init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name,    share_name);
+
+       q_n->info_level = info_level;
+  
+       q_n->info = *info;
+
+       q_n->ptr_parm_error = 1;
+       q_n->parm_error     = 0;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_set_info(const char *desc, SRV_Q_NET_SHARE_SET_INFO *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_share_set_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!srv_io_srv_share_info("info  ", ps, depth, &q_n->info))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("ptr_parm_error", ps, depth, &q_n->ptr_parm_error))
+               return False;
+       if(q_n->ptr_parm_error!=0) {
+               if(!prs_uint32("parm_error", ps, depth, &q_n->parm_error))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_set_info(const char *desc, SRV_R_NET_SHARE_SET_INFO *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_share_set_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_parm_error  ", ps, depth, &r_n->ptr_parm_error))
+               return False;
+
+       if(r_n->ptr_parm_error) {
+
+               if(!prs_uint32("parm_error  ", ps, depth, &r_n->parm_error))
+                       return False;
+       }
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}      
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_add(const char *desc, SRV_Q_NET_SHARE_ADD *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_share_add");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!srv_io_srv_share_info("info  ", ps, depth, &q_n->info))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_err_index", ps, depth, &q_n->ptr_err_index))
+               return False;
+       if (q_n->ptr_err_index)
+               if (!prs_uint32("err_index", ps, depth, &q_n->err_index))
+                       return False;
+
+       return True;
+}
+
+void init_srv_q_net_share_add(SRV_Q_NET_SHARE_ADD *q, const char *srvname,
+                             const char *netname, uint32 type, const char *remark, 
+                             uint32 perms, uint32 max_uses, uint32 num_uses,
+                             const char *path, const char *passwd)
+{
+       q->ptr_srv_name = 1;
+       init_unistr2(&q->uni_srv_name, srvname, strlen(srvname) +1);
+       q->info.switch_value = q->info_level = 2;
+
+       q->info.ptr_share_ctr = 1;
+       init_srv_share_info2(&q->info.share.info2.info_2, netname, type,
+                            remark, perms, max_uses, num_uses, path, passwd);
+       init_srv_share_info2_str(&q->info.share.info2.info_2_str, netname,
+                                remark, path, passwd);
+       q->ptr_err_index = 1;
+       q->err_index = 0;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_add(const char *desc, SRV_R_NET_SHARE_ADD *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_share_add");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_parm_error", ps, depth, &r_n->ptr_parm_error))
+               return False;
+
+       if(r_n->ptr_parm_error) {
+         
+               if(!prs_uint32("parm_error", ps, depth, &r_n->parm_error))
+                       return False;
+       }
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}      
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+void init_srv_q_net_share_del(SRV_Q_NET_SHARE_DEL *del, const char *srvname,
+                             const char *sharename)
+{
+       del->ptr_srv_name = 1;
+       init_unistr2(&del->uni_srv_name, srvname, strlen(srvname) +1 );
+       init_unistr2(&del->uni_share_name, sharename, strlen(sharename) + 1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_del(const char *desc, SRV_Q_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_share_del");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("reserved", ps, depth, &q_n->reserved))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_del(const char *desc, SRV_R_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_share_del");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &q_n->status))
+               return False;
+
+       return True;
+}      
+
+/*******************************************************************
+ Inits a SESS_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_sess_info0_str(SESS_INFO_0_STR *ss0, const char *name)
+{
+       DEBUG(5,("init_srv_sess_info0_str\n"));
+
+       init_unistr2(&ss0->uni_name, name, strlen(name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0_str(const char *desc,  SESS_INFO_0_STR *ss0, prs_struct *ps, int depth)
+{
+       if (ss0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_sess_info0_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &ss0->uni_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_0 structure
+********************************************************************/
+
+void init_srv_sess_info0(SESS_INFO_0 *ss0, const char *name)
+{
+       DEBUG(5,("init_srv_sess_info0: %s\n", name));
+
+       ss0->ptr_name = (name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0(const char *desc, SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+       if (ss0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_sess_info0");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_name", ps, depth, &ss0->ptr_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_0(const char *desc, SRV_SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+       if (ss0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_sess_info_0");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+               return False;
+       if(!prs_uint32("ptr_sess_info", ps, depth, &ss0->ptr_sess_info))
+               return False;
+
+       if (ss0->ptr_sess_info != 0) {
+               int i;
+               int num_entries = ss0->num_entries_read;
+
+               if (num_entries > MAX_SESS_ENTRIES) {
+                       num_entries = MAX_SESS_ENTRIES; /* report this! */
+               }
+
+               if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+                       return False;
+
+               SMB_ASSERT_ARRAY(ss0->info_0, num_entries);
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_sess_info0("", &ss0->info_0[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_sess_info0_str("", &ss0->info_0_str[i], ps, depth))
+                               return False;
+               }
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_sess_info1_str(SESS_INFO_1_STR *ss1, const char *name, const char *user)
+{
+       DEBUG(5,("init_srv_sess_info1_str\n"));
+
+       init_unistr2(&ss1->uni_name, name, strlen(name)+1);
+       init_unistr2(&ss1->uni_user, user, strlen(user)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1_str(const char *desc, SESS_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_sess_info1_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &ss1->uni_name, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &(ss1->uni_user), True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1 structure
+********************************************************************/
+
+void init_srv_sess_info1(SESS_INFO_1 *ss1, 
+                               const char *name, const char *user,
+                               uint32 num_opens, uint32 open_time, uint32 idle_time,
+                               uint32 user_flags)
+{
+       DEBUG(5,("init_srv_sess_info1: %s\n", name));
+
+       ss1->ptr_name = (name != NULL) ? 1 : 0;
+       ss1->ptr_user = (user != NULL) ? 1 : 0;
+
+       ss1->num_opens  = num_opens;
+       ss1->open_time  = open_time;
+       ss1->idle_time  = idle_time;
+       ss1->user_flags = user_flags;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1(const char *desc, SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_sess_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_name  ", ps, depth, &ss1->ptr_name))
+               return False;
+       if(!prs_uint32("ptr_user  ", ps, depth, &ss1->ptr_user))
+               return False;
+
+       if(!prs_uint32("num_opens ", ps, depth, &ss1->num_opens))
+               return False;
+       if(!prs_uint32("open_time ", ps, depth, &ss1->open_time))
+               return False;
+       if(!prs_uint32("idle_time ", ps, depth, &ss1->idle_time))
+               return False;
+       if(!prs_uint32("user_flags", ps, depth, &ss1->user_flags))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_1(const char *desc, SRV_SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_sess_info_1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+               return False;
+       if(!prs_uint32("ptr_sess_info", ps, depth, &ss1->ptr_sess_info))
+               return False;
+
+       if (ss1->ptr_sess_info != 0) {
+               int i;
+               int num_entries = ss1->num_entries_read;
+
+               if (num_entries > MAX_SESS_ENTRIES) {
+                       num_entries = MAX_SESS_ENTRIES; /* report this! */
+               }
+
+               if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+                       return False;
+
+               SMB_ASSERT_ARRAY(ss1->info_1, num_entries);
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_sess_info1("", &ss1->info_1[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_sess_info1_str("", &ss1->info_1_str[i], ps, depth))
+                               return False;
+               }
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_ctr(const char *desc, SRV_SESS_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+       SRV_SESS_INFO_CTR *ctr = *pp_ctr;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_sess_ctr");
+       depth++;
+
+       if(UNMARSHALLING(ps)) {
+               ctr = *pp_ctr = (SRV_SESS_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_SESS_INFO_CTR));
+               if (ctr == NULL)
+                       return False;
+       }
+
+       if (ctr == NULL)
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_uint32("ptr_sess_ctr", ps, depth, &ctr->ptr_sess_ctr))
+               return False;
+
+       if (ctr->ptr_sess_ctr != 0) {
+               switch (ctr->switch_value) {
+               case 0:
+                       if(!srv_io_srv_sess_info_0("", &ctr->sess.info0, ps, depth))
+                               return False;
+                       break;
+               case 1:
+                       if(!srv_io_srv_sess_info_1("", &ctr->sess.info1, ps, depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(5,("%s no session info at switch_value %d\n",
+                                tab_depth(depth), ctr->switch_value));
+                       break;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SESS_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_sess_enum(SRV_Q_NET_SESS_ENUM *q_n, 
+                             const char *srv_name, const char *qual_name,
+                             const char *user_name, uint32 sess_level, 
+                             SRV_SESS_INFO_CTR *ctr, uint32 preferred_len,
+                             ENUM_HND *hnd)
+{
+       q_n->ctr = ctr;
+
+       DEBUG(5,("init_q_net_sess_enum\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+       init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+       init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+       q_n->sess_level    = sess_level;
+       q_n->preferred_len = preferred_len;
+
+       memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_sess_enum(const char *desc, SRV_Q_NET_SESS_ENUM *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_sess_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("sess_level", ps, depth, &q_n->sess_level))
+               return False;
+       
+       if (q_n->sess_level != -1) {
+               if(!srv_io_srv_sess_ctr("sess_ctr", &q_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_sess_enum(const char *desc, SRV_R_NET_SESS_ENUM *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_sess_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("sess_level", ps, depth, &r_n->sess_level))
+               return False;
+
+       if (r_n->sess_level != -1) {
+               if(!srv_io_srv_sess_ctr("sess_ctr", &r_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+               return False;
+       if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_0 structure
+********************************************************************/
+
+void init_srv_conn_info0(CONN_INFO_0 *ss0, uint32 id)
+{
+       DEBUG(5,("init_srv_conn_info0\n"));
+
+       ss0->id = id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info0(const char *desc, CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+       if (ss0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_conn_info0");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("id", ps, depth, &ss0->id))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_0(const char *desc, SRV_CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+       if (ss0 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_conn_info_0");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+               return False;
+       if(!prs_uint32("ptr_conn_info", ps, depth, &ss0->ptr_conn_info))
+               return False;
+
+       if (ss0->ptr_conn_info != 0) {
+               int i;
+               int num_entries = ss0->num_entries_read;
+
+               if (num_entries > MAX_CONN_ENTRIES) {
+                       num_entries = MAX_CONN_ENTRIES; /* report this! */
+               }
+
+               if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+                       return False;
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_conn_info0("", &ss0->info_0[i], ps, depth))
+                               return False;
+               }
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_conn_info1_str(CONN_INFO_1_STR *ss1, const char *usr_name, const char *net_name)
+{
+       DEBUG(5,("init_srv_conn_info1_str\n"));
+
+       init_unistr2(&ss1->uni_usr_name, usr_name, strlen(usr_name)+1);
+       init_unistr2(&ss1->uni_net_name, net_name, strlen(net_name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1_str(const char *desc, CONN_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_conn_info1_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &ss1->uni_usr_name, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &ss1->uni_net_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1 structure
+********************************************************************/
+
+void init_srv_conn_info1(CONN_INFO_1 *ss1, 
+                               uint32 id, uint32 type,
+                               uint32 num_opens, uint32 num_users, uint32 open_time,
+                               const char *usr_name, const char *net_name)
+{
+       DEBUG(5,("init_srv_conn_info1: %s %s\n", usr_name, net_name));
+
+       ss1->id        = id       ;
+       ss1->type      = type     ;
+       ss1->num_opens = num_opens ;
+       ss1->num_users = num_users;
+       ss1->open_time = open_time;
+
+       ss1->ptr_usr_name = (usr_name != NULL) ? 1 : 0;
+       ss1->ptr_net_name = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1(const char *desc, CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_conn_info1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("id          ", ps, depth, &ss1->id))
+               return False;
+       if(!prs_uint32("type        ", ps, depth, &ss1->type))
+               return False;
+       if(!prs_uint32("num_opens   ", ps, depth, &ss1->num_opens))
+               return False;
+       if(!prs_uint32("num_users   ", ps, depth, &ss1->num_users))
+               return False;
+       if(!prs_uint32("open_time   ", ps, depth, &ss1->open_time))
+               return False;
+
+       if(!prs_uint32("ptr_usr_name", ps, depth, &ss1->ptr_usr_name))
+               return False;
+       if(!prs_uint32("ptr_net_name", ps, depth, &ss1->ptr_net_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_1(const char *desc, SRV_CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+       if (ss1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_conn_info_1");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+               return False;
+       if(!prs_uint32("ptr_conn_info", ps, depth, &ss1->ptr_conn_info))
+               return False;
+
+       if (ss1->ptr_conn_info != 0) {
+               int i;
+               int num_entries = ss1->num_entries_read;
+
+               if (num_entries > MAX_CONN_ENTRIES) {
+                       num_entries = MAX_CONN_ENTRIES; /* report this! */
+               }
+
+               if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+                       return False;
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_conn_info1("", &ss1->info_1[i], ps, depth))
+                               return False;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_conn_info1_str("", &ss1->info_1_str[i], ps, depth))
+                               return False;
+               }
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_ctr(const char *desc, SRV_CONN_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+       SRV_CONN_INFO_CTR *ctr = *pp_ctr;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_conn_ctr");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               ctr = *pp_ctr = (SRV_CONN_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_CONN_INFO_CTR));
+               if (ctr == NULL)
+                       return False;
+       }
+               
+       if (ctr == NULL)
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_uint32("ptr_conn_ctr", ps, depth, &ctr->ptr_conn_ctr))
+               return False;
+
+       if (ctr->ptr_conn_ctr != 0) {
+               switch (ctr->switch_value) {
+               case 0:
+                       if(!srv_io_srv_conn_info_0("", &ctr->conn.info0, ps, depth))
+                               return False;
+                       break;
+               case 1:
+                       if(!srv_io_srv_conn_info_1("", &ctr->conn.info1, ps, depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(5,("%s no connection info at switch_value %d\n",
+                                tab_depth(depth), ctr->switch_value));
+                       break;
+               }
+       }
+
+       return True;
+}
+
+/*******************************************************************
+  Reads or writes a structure.
+********************************************************************/
+
+void init_srv_q_net_conn_enum(SRV_Q_NET_CONN_ENUM *q_n, 
+                               const char *srv_name, const char *qual_name,
+                               uint32 conn_level, SRV_CONN_INFO_CTR *ctr,
+                               uint32 preferred_len,
+                               ENUM_HND *hnd)
+{
+       DEBUG(5,("init_q_net_conn_enum\n"));
+
+       q_n->ctr = ctr;
+
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name );
+       init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+
+       q_n->conn_level    = conn_level;
+       q_n->preferred_len = preferred_len;
+
+       memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_conn_enum(const char *desc, SRV_Q_NET_CONN_ENUM *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_conn_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, q_n->ptr_srv_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("conn_level", ps, depth, &q_n->conn_level))
+               return False;
+       
+       if (q_n->conn_level != -1) {
+               if(!srv_io_srv_conn_ctr("conn_ctr", &q_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_conn_enum(const char *desc,  SRV_R_NET_CONN_ENUM *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_conn_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("conn_level", ps, depth, &r_n->conn_level))
+               return False;
+
+       if (r_n->conn_level != -1) {
+               if(!srv_io_srv_conn_ctr("conn_ctr", &r_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+               return False;
+       if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3_STR structure
+********************************************************************/
+
+void init_srv_file_info3_str(FILE_INFO_3_STR *fi3, const char *user_name, const char *path_name)
+{
+       DEBUG(5,("init_srv_file_info3_str\n"));
+
+       init_unistr2(&fi3->uni_path_name, path_name, strlen(path_name)+1);
+       init_unistr2(&fi3->uni_user_name, user_name, strlen(user_name)+1);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3_str(const char *desc, FILE_INFO_3_STR *sh1, prs_struct *ps, int depth)
+{
+       if (sh1 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_file_info3_str");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &sh1->uni_path_name, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("", &sh1->uni_user_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3 structure
+********************************************************************/
+
+void init_srv_file_info3(FILE_INFO_3 *fl3,
+                        uint32 id, uint32 perms, uint32 num_locks,
+                        const char *path_name, const char *user_name)
+{
+       DEBUG(5,("init_srv_file_info3: %s %s\n", path_name, user_name));
+
+       fl3->id        = id;    
+       fl3->perms     = perms;
+       fl3->num_locks = num_locks;
+
+       fl3->ptr_path_name = (path_name != NULL) ? 1 : 0;
+       fl3->ptr_user_name = (user_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3(const char *desc, FILE_INFO_3 *fl3, prs_struct *ps, int depth)
+{
+       if (fl3 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_file_info3");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("id           ", ps, depth, &fl3->id))
+               return False;
+       if(!prs_uint32("perms        ", ps, depth, &fl3->perms))
+               return False;
+       if(!prs_uint32("num_locks    ", ps, depth, &fl3->num_locks))
+               return False;
+       if(!prs_uint32("ptr_path_name", ps, depth, &fl3->ptr_path_name))
+               return False;
+       if(!prs_uint32("ptr_user_name", ps, depth, &fl3->ptr_user_name))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_file_ctr(const char *desc, SRV_FILE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+       if (ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_srv_file_ctr");
+       depth++;
+
+       if (UNMARSHALLING(ps)) {
+               memset(ctr, '\0', sizeof(SRV_FILE_INFO_CTR));
+       }
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if (ctr->switch_value != 3) {
+               DEBUG(5,("%s File info %d level not supported\n",
+                        tab_depth(depth), ctr->switch_value));
+       }
+       if(!prs_uint32("ptr_file_info", ps, depth, &ctr->ptr_file_info))
+               return False;
+       if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+               return False;
+       if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+               return False;
+       if (ctr->ptr_entries == 0)
+               return True;
+       if(!prs_uint32("num_entries2", ps, depth, 
+                      &ctr->num_entries2))
+               return False;
+
+       switch (ctr->switch_value) {
+       case 3: {
+               SRV_FILE_INFO_3 *info3 = ctr->file.info3;
+               int num_entries = ctr->num_entries;
+               int i;
+
+               if (UNMARSHALLING(ps)) {
+                       if (!(info3 = (SRV_FILE_INFO_3 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_FILE_INFO_3))))
+                               return False;
+                       ctr->file.info3 = info3;
+               }
+
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_file_info3("", &ctr->file.info3[i].info_3, ps, depth))
+                               return False;
+               }
+               for (i = 0; i < num_entries; i++) {
+                       if(!srv_io_file_info3_str("", &ctr->file.info3[i].info_3_str, ps, depth))
+                               return False;
+               }
+               break;
+       }
+       default:
+               DEBUG(5,("%s no file info at switch_value %d\n",
+                        tab_depth(depth), ctr->switch_value));
+               break;
+       }
+                       
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_FILE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_file_enum(SRV_Q_NET_FILE_ENUM *q_n, 
+                             const char *srv_name, const char *qual_name, 
+                             const char *user_name,
+                             uint32 file_level, SRV_FILE_INFO_CTR *ctr,
+                             uint32 preferred_len,
+                             ENUM_HND *hnd)
+{
+       DEBUG(5,("init_q_net_file_enum\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+       init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+       init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+       q_n->file_level    = q_n->ctr.switch_value = file_level;
+       q_n->preferred_len = preferred_len;
+       q_n->ctr.ptr_file_info = 1;
+       q_n->ctr.num_entries = 0;
+       q_n->ctr.num_entries2 = 0;
+
+       memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_enum(const char *desc, SRV_Q_NET_FILE_ENUM *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_file_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+       if(!prs_uint32("file_level", ps, depth, &q_n->file_level))
+               return False;
+
+       if (q_n->file_level != -1) {
+               if(!srv_io_srv_file_ctr("file_ctr", &q_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_enum(const char *desc, SRV_R_NET_FILE_ENUM *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_file_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("file_level", ps, depth, &r_n->file_level))
+               return False;
+
+       if (r_n->file_level != 0) {
+               if(!srv_io_srv_file_ctr("file_ctr", &r_n->ctr, ps, depth))
+                       return False;
+       }
+
+       if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+               return False;
+       if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+               return False;
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Initialize a net file close request
+********************************************************************/
+void init_srv_q_net_file_close(SRV_Q_NET_FILE_CLOSE *q_n, const char *server,
+                              uint32 file_id)
+{
+       q_n->ptr_srv_name = 1;
+       init_unistr2(&q_n->uni_srv_name, server, strlen(server) + 1);
+       q_n->file_id = file_id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+BOOL srv_io_q_net_file_close(const char *desc, SRV_Q_NET_FILE_CLOSE *q_n,
+                            prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_file_close");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("file_id", ps, depth, &q_n->file_id))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_close(const char *desc, SRV_R_NET_FILE_CLOSE *q_n, 
+                            prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_file_close");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &q_n->status))
+               return False;
+
+       return True;
+}      
+
+/*******************************************************************
+ Inits a SRV_INFO_100 structure.
+ ********************************************************************/
+
+void init_srv_info_100(SRV_INFO_100 *sv100, uint32 platform_id, const char *name)
+{
+       DEBUG(5,("init_srv_info_100\n"));
+
+       sv100->platform_id  = platform_id;
+       init_buf_unistr2(&sv100->uni_name, &sv100->ptr_name, name);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_100(const char *desc, SRV_INFO_100 *sv100, prs_struct *ps, int depth)
+{
+       if (sv100 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_info_100");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("platform_id ", ps, depth, &sv100->platform_id))
+               return False;
+       if(!prs_uint32("ptr_name    ", ps, depth, &sv100->ptr_name))
+               return False;
+
+       if(!smb_io_unistr2("uni_name    ", &sv100->uni_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ Inits a SRV_INFO_101 structure.
+ ********************************************************************/
+
+void init_srv_info_101(SRV_INFO_101 *sv101, uint32 platform_id, const char *name,
+                               uint32 ver_major, uint32 ver_minor,
+                               uint32 srv_type, const char *comment)
+{
+       DEBUG(5,("init_srv_info_101\n"));
+
+       sv101->platform_id  = platform_id;
+       init_buf_unistr2(&sv101->uni_name, &sv101->ptr_name, name);
+       sv101->ver_major    = ver_major;
+       sv101->ver_minor    = ver_minor;
+       sv101->srv_type     = srv_type;
+       init_buf_unistr2(&sv101->uni_comment, &sv101->ptr_comment, comment);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_101(const char *desc, SRV_INFO_101 *sv101, prs_struct *ps, int depth)
+{
+       if (sv101 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_info_101");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("platform_id ", ps, depth, &sv101->platform_id))
+               return False;
+       if(!prs_uint32("ptr_name    ", ps, depth, &sv101->ptr_name))
+               return False;
+       if(!prs_uint32("ver_major   ", ps, depth, &sv101->ver_major))
+               return False;
+       if(!prs_uint32("ver_minor   ", ps, depth, &sv101->ver_minor))
+               return False;
+       if(!prs_uint32("srv_type    ", ps, depth, &sv101->srv_type))
+               return False;
+       if(!prs_uint32("ptr_comment ", ps, depth, &sv101->ptr_comment))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("uni_name    ", &sv101->uni_name, True, ps, depth))
+               return False;
+       if(!smb_io_unistr2("uni_comment ", &sv101->uni_comment, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_INFO_102 structure.
+ ********************************************************************/
+
+void init_srv_info_102(SRV_INFO_102 *sv102, uint32 platform_id, const char *name,
+                               const char *comment, uint32 ver_major, uint32 ver_minor,
+                               uint32 srv_type, uint32 users, uint32 disc, uint32 hidden,
+                               uint32 announce, uint32 ann_delta, uint32 licenses,
+                               const char *usr_path)
+{
+       DEBUG(5,("init_srv_info_102\n"));
+
+       sv102->platform_id  = platform_id;
+       init_buf_unistr2(&sv102->uni_name, &sv102->ptr_name, name);
+       sv102->ver_major    = ver_major;
+       sv102->ver_minor    = ver_minor;
+       sv102->srv_type     = srv_type;
+       init_buf_unistr2(&sv102->uni_comment, &sv102->ptr_comment, comment);
+
+       /* same as 101 up to here */
+
+       sv102->users        = users;
+       sv102->disc         = disc;
+       sv102->hidden       = hidden;
+       sv102->announce     = announce;
+       sv102->ann_delta    = ann_delta;
+       sv102->licenses     = licenses;
+       init_buf_unistr2(&sv102->uni_usr_path, &sv102->ptr_usr_path, usr_path);
+}
+
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_102(const char *desc, SRV_INFO_102 *sv102, prs_struct *ps, int depth)
+{
+       if (sv102 == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_info102");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("platform_id ", ps, depth, &sv102->platform_id))
+               return False;
+       if(!prs_uint32("ptr_name    ", ps, depth, &sv102->ptr_name))
+               return False;
+       if(!prs_uint32("ver_major   ", ps, depth, &sv102->ver_major))
+               return False;
+       if(!prs_uint32("ver_minor   ", ps, depth, &sv102->ver_minor))
+               return False;
+       if(!prs_uint32("srv_type    ", ps, depth, &sv102->srv_type))
+               return False;
+       if(!prs_uint32("ptr_comment ", ps, depth, &sv102->ptr_comment))
+               return False;
+
+       /* same as 101 up to here */
+
+       if(!prs_uint32("users       ", ps, depth, &sv102->users))
+               return False;
+       if(!prs_uint32("disc        ", ps, depth, &sv102->disc))
+               return False;
+       if(!prs_uint32("hidden      ", ps, depth, &sv102->hidden))
+               return False;
+       if(!prs_uint32("announce    ", ps, depth, &sv102->announce))
+               return False;
+       if(!prs_uint32("ann_delta   ", ps, depth, &sv102->ann_delta))
+               return False;
+       if(!prs_uint32("licenses    ", ps, depth, &sv102->licenses))
+               return False;
+       if(!prs_uint32("ptr_usr_path", ps, depth, &sv102->ptr_usr_path))
+               return False;
+
+       if(!smb_io_unistr2("uni_name    ", &sv102->uni_name, True, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("uni_comment ", &sv102->uni_comment, True, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+       if(!smb_io_unistr2("uni_usr_path", &sv102->uni_usr_path, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_ctr(const char *desc, SRV_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+       if (ctr == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_info_ctr");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+               return False;
+       if(!prs_uint32("ptr_srv_ctr ", ps, depth, &ctr->ptr_srv_ctr))
+               return False;
+
+       if (ctr->ptr_srv_ctr != 0 && ctr->switch_value != 0 && ctr != NULL) {
+               switch (ctr->switch_value) {
+               case 100:
+                       if(!srv_io_info_100("sv100", &ctr->srv.sv100, ps, depth))
+                               return False;
+                       break;
+               case 101:
+                       if(!srv_io_info_101("sv101", &ctr->srv.sv101, ps, depth))
+                               return False;
+                       break;
+               case 102:
+                       if(!srv_io_info_102("sv102", &ctr->srv.sv102, ps, depth))
+                               return False;
+                       break;
+               default:
+                       DEBUG(5,("%s no server info at switch_value %d\n",
+                                        tab_depth(depth), ctr->switch_value));
+                       break;
+               }
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_q_net_srv_get_info(SRV_Q_NET_SRV_GET_INFO *srv,
+                               const char *server_name, uint32 switch_value)
+{
+       DEBUG(5,("init_srv_q_net_srv_get_info\n"));
+
+       init_buf_unistr2(&srv->uni_srv_name, &srv->ptr_srv_name, server_name);
+
+       srv->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_get_info(const char *desc, SRV_Q_NET_SRV_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_srv_get_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name  ", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value  ", ps, depth, &q_n->switch_value))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_get_info(SRV_R_NET_SRV_GET_INFO *srv,
+                               uint32 switch_value, SRV_INFO_CTR *ctr, WERROR status)
+{
+       DEBUG(5,("init_srv_r_net_srv_get_info\n"));
+
+       srv->ctr = ctr;
+
+       if (W_ERROR_IS_OK(status)) {
+               srv->ctr->switch_value = switch_value;
+               srv->ctr->ptr_srv_ctr  = 1;
+       } else {
+               srv->ctr->switch_value = 0;
+               srv->ctr->ptr_srv_ctr  = 0;
+       }
+
+       srv->status = status;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_SET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_set_info(SRV_R_NET_SRV_SET_INFO *srv,
+                                uint32 switch_value, WERROR status)
+{
+       DEBUG(5,("init_srv_r_net_srv_set_info\n"));
+
+       srv->switch_value = switch_value;
+       srv->status = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_set_info(const char *desc, SRV_Q_NET_SRV_SET_INFO *q_n, 
+                              prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "srv_io_q_net_srv_set_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name  ", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch_value  ", ps, depth, &q_n->switch_value))
+               return False;
+
+       if (UNMARSHALLING(ps)) {
+               q_n->ctr = (SRV_INFO_CTR *)
+                       prs_alloc_mem(ps, sizeof(SRV_INFO_CTR));
+
+               if (!q_n->ctr)
+                       return False;
+       }
+
+       if(!srv_io_info_ctr("ctr", q_n->ctr, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_get_info(const char *desc, SRV_R_NET_SRV_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_srv_get_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!srv_io_info_ctr("ctr", r_n->ctr, ps, depth))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_set_info(const char *desc, SRV_R_NET_SRV_SET_INFO *r_n, 
+                              prs_struct *ps, int depth)
+{
+       prs_debug(ps, depth, desc, "srv_io_r_net_srv_set_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("switch value ", ps, depth, &r_n->switch_value))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_remote_tod(const char *desc, SRV_Q_NET_REMOTE_TOD *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_remote_tod");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name  ", ps, depth, &q_n->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+static BOOL srv_io_time_of_day_info(const char *desc, TIME_OF_DAY_INFO *tod, prs_struct *ps, int depth)
+{
+       if (tod == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_time_of_day_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("elapsedt   ", ps, depth, &tod->elapsedt))
+               return False;
+       if(!prs_uint32("msecs      ", ps, depth, &tod->msecs))
+               return False;
+       if(!prs_uint32("hours      ", ps, depth, &tod->hours))
+               return False;
+       if(!prs_uint32("mins       ", ps, depth, &tod->mins))
+               return False;
+       if(!prs_uint32("secs       ", ps, depth, &tod->secs))
+               return False;
+       if(!prs_uint32("hunds      ", ps, depth, &tod->hunds))
+               return False;
+       if(!prs_uint32("timezone   ", ps, depth, &tod->zone))
+               return False;
+       if(!prs_uint32("tintervals ", ps, depth, &tod->tintervals))
+               return False;
+       if(!prs_uint32("day        ", ps, depth, &tod->day))
+               return False;
+       if(!prs_uint32("month      ", ps, depth, &tod->month))
+               return False;
+       if(!prs_uint32("year       ", ps, depth, &tod->year))
+               return False;
+       if(!prs_uint32("weekday    ", ps, depth, &tod->weekday))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+void init_time_of_day_info(TIME_OF_DAY_INFO *tod, uint32 elapsedt, uint32 msecs,
+                           uint32 hours, uint32 mins, uint32 secs, uint32 hunds,
+                          uint32 zone, uint32 tintervals, uint32 day,
+                          uint32 month, uint32 year, uint32 weekday)
+{
+       DEBUG(5,("init_time_of_day_info\n"));
+
+       tod->elapsedt   = elapsedt;
+       tod->msecs      = msecs;
+       tod->hours      = hours;
+       tod->mins       = mins;
+       tod->secs       = secs;
+       tod->hunds      = hunds;
+       tod->zone       = zone;
+       tod->tintervals = tintervals;
+       tod->day        = day;
+       tod->month      = month;
+       tod->year       = year;
+       tod->weekday    = weekday;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_remote_tod(const char *desc, SRV_R_NET_REMOTE_TOD *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_remote_tod");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+       
+       if(!prs_uint32("ptr_srv_tod ", ps, depth, &r_n->ptr_srv_tod))
+               return False;
+
+       if(!srv_io_time_of_day_info("tod", r_n->tod, ps, depth))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_disk_enum(SRV_Q_NET_DISK_ENUM *q_n,
+                             const char *srv_name,
+                             uint32 preferred_len,
+                             ENUM_HND *enum_hnd
+       ) 
+{
+  
+
+       DEBUG(5,("init_srv_q_net_srv_disk_enum\n"));
+
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+       q_n->disk_enum_ctr.level = 0;
+       q_n->disk_enum_ctr.disk_info_ptr   = 0;
+  
+       q_n->preferred_len = preferred_len;
+       memcpy(&q_n->enum_hnd, enum_hnd, sizeof(*enum_hnd));
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_disk_enum(const char *desc, SRV_Q_NET_DISK_ENUM *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_disk_enum");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("level", ps, depth, &q_n->disk_enum_ctr.level))
+               return False;
+
+       if(!prs_uint32("entries_read", ps, depth, &q_n->disk_enum_ctr.entries_read))
+               return False;
+
+       if(!prs_uint32("buffer", ps, depth, &q_n->disk_enum_ctr.disk_info_ptr))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+               return False;
+       if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_disk_enum(const char *desc, SRV_R_NET_DISK_ENUM *r_n, prs_struct *ps, int depth)
+{
+
+       int i;
+       uint32 entries_read, entries_read2, entries_read3;
+
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_disk_enum");
+       depth++;
+
+       entries_read = entries_read2 = entries_read3 = r_n->disk_enum_ctr.entries_read;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("entries_read", ps, depth, &entries_read))
+               return False;
+       if(!prs_uint32("ptr_disk_info", ps, depth, &r_n->disk_enum_ctr.disk_info_ptr))
+               return False;
+
+       /*this may be max, unknown, actual?*/
+
+       if(!prs_uint32("max_elements", ps, depth, &entries_read2))
+               return False;
+       if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.unknown))
+               return False;
+       if(!prs_uint32("actual_elements", ps, depth, &entries_read3))
+               return False;
+
+       r_n->disk_enum_ctr.entries_read = entries_read3;
+
+       if(UNMARSHALLING(ps)) {
+
+               DISK_INFO *dinfo;
+
+               if(!(dinfo = (DISK_INFO *)prs_alloc_mem(ps, sizeof(*dinfo) * entries_read3)))
+               return False;
+               r_n->disk_enum_ctr.disk_info = dinfo;
+       }
+
+       for(i=0; i < r_n->disk_enum_ctr.entries_read; i++) {
+
+               if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.disk_info[i].unknown))
+                       return False;
+   
+               if(!smb_io_unistr3("disk_name", &r_n->disk_enum_ctr.disk_info[i].disk_name, ps, depth))
+                       return False;
+
+               if(!prs_align(ps))
+                       return False;
+       }
+
+       if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+               return False;
+
+       if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_name_validate(SRV_Q_NET_NAME_VALIDATE *q_n, const char *srv_name, const char *share_name, int type) 
+{
+       uint32 ptr_share_name;
+
+       DEBUG(5,("init_srv_q_net_name_validate\n"));
+  
+       init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+       init_buf_unistr2(&q_n->uni_name,     &ptr_share_name,    share_name);
+
+       q_n->type  = type;
+       q_n->flags = 0;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_name_validate(const char *desc, SRV_Q_NET_NAME_VALIDATE *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_name_validate");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("type", ps, depth, &q_n->type))
+               return False;
+
+       if(!prs_uint32("flags", ps, depth, &q_n->flags))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_name_validate(const char *desc, SRV_R_NET_NAME_VALIDATE *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_name_validate");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_query_secdesc(const char *desc, SRV_Q_NET_FILE_QUERY_SECDESC *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_file_query_secdesc");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+               return False;
+
+       if(!prs_uint32("unknown1", ps, depth, &q_n->unknown1))
+               return False;
+
+       if(!prs_uint32("unknown2", ps, depth, &q_n->unknown2))
+               return False;
+
+       if(!prs_uint32("unknown3", ps, depth, &q_n->unknown3))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_query_secdesc(const char *desc, SRV_R_NET_FILE_QUERY_SECDESC *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_file_query_secdesc");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_response", ps, depth, &r_n->ptr_response))
+               return False;
+
+       if(!prs_uint32("size_response", ps, depth, &r_n->size_response))
+               return False;
+
+       if(!prs_uint32("ptr_secdesc", ps, depth, &r_n->ptr_secdesc))
+               return False;
+
+       if(!prs_uint32("size_secdesc", ps, depth, &r_n->size_secdesc))
+               return False;
+
+       if(!sec_io_desc("sec_desc", &r_n->sec_desc, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_set_secdesc(const char *desc, SRV_Q_NET_FILE_SET_SECDESC *q_n, prs_struct *ps, int depth)
+{
+       if (q_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_q_net_file_set_secdesc");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+               return False;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("sec_info", ps, depth, &q_n->sec_info))
+               return False;
+
+       if(!prs_uint32("size_set", ps, depth, &q_n->size_set))
+               return False;
+
+       if(!prs_uint32("ptr_secdesc", ps, depth, &q_n->ptr_secdesc))
+               return False;
+
+       if(!prs_uint32("size_secdesc", ps, depth, &q_n->size_secdesc))
+               return False;
+
+       if(!sec_io_desc("sec_desc", &q_n->sec_desc, ps, depth))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_set_secdesc(const char *desc, SRV_R_NET_FILE_SET_SECDESC *r_n, prs_struct *ps, int depth)
+{
+       if (r_n == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "srv_io_r_net_file_set_secdesc");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_werror("status", ps, depth, &r_n->status))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a structure
+********************************************************************/
+
+void init_srv_q_net_remote_tod(SRV_Q_NET_REMOTE_TOD *q_u, const char *server)
+{
+       q_u->ptr_srv_name = 1;
+       init_unistr2(&q_u->uni_srv_name, server, strlen(server) + 1);
+}
+
diff --git a/source4/rpc_parse/parse_wks.c b/source4/rpc_parse/parse_wks.c
new file mode 100644 (file)
index 0000000..b6de058
--- /dev/null
@@ -0,0 +1,178 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Init
+ ********************************************************************/
+
+void init_wks_q_query_info(WKS_Q_QUERY_INFO *q_u,
+                               char *server, uint16 switch_value)  
+{
+       DEBUG(5,("init_wks_q_query_info\n"));
+
+       init_buf_unistr2(&q_u->uni_srv_name, &q_u->ptr_srv_name, server);
+       q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a WKS_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL wks_io_q_query_info(const char *desc, WKS_Q_QUERY_INFO *q_u, prs_struct *ps, int depth)
+{
+       if (q_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "wks_io_q_query_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+               return False;
+       if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ wks_info_100
+ ********************************************************************/
+
+void init_wks_info_100(WKS_INFO_100 *inf,
+                               uint32 platform_id, uint32 ver_major, uint32 ver_minor,
+                               char *my_name, char *domain_name)
+{
+       DEBUG(5,("Init WKS_INFO_100: %d\n", __LINE__));
+
+       inf->platform_id = platform_id; /* 0x0000 01f4 - unknown */
+       inf->ver_major   = ver_major;   /* os major version */
+       inf->ver_minor   = ver_minor;   /* os minor version */
+
+       init_buf_unistr2(&inf->uni_compname, &inf->ptr_compname, my_name    );
+       init_buf_unistr2(&inf->uni_lan_grp, &inf->ptr_lan_grp, domain_name);
+}
+
+/*******************************************************************
+ Reads or writes a WKS_INFO_100 structure.
+********************************************************************/
+
+static BOOL wks_io_wks_info_100(const char *desc, WKS_INFO_100 *inf, prs_struct *ps, int depth)
+{
+       if (inf == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "wks_io_wks_info_100");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("platform_id ", ps, depth, &inf->platform_id)) /* 0x0000 01f4 - unknown */
+               return False;
+       if(!prs_uint32("ptr_compname", ps, depth, &inf->ptr_compname)) /* pointer to computer name */
+               return False;
+       if(!prs_uint32("ptr_lan_grp ", ps, depth, &inf->ptr_lan_grp)) /* pointer to LAN group name */
+               return False;
+       if(!prs_uint32("ver_major   ", ps, depth, &inf->ver_major)) /* 4 - major os version */
+               return False;
+       if(!prs_uint32("ver_minor   ", ps, depth, &inf->ver_minor)) /* 0 - minor os version */
+               return False;
+
+       if(!smb_io_unistr2("", &inf->uni_compname, inf->ptr_compname, ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!smb_io_unistr2("", &inf->uni_lan_grp, inf->ptr_lan_grp , ps, depth))
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Inits WKS_R_QUERY_INFO.
+
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+void init_wks_r_query_info(WKS_R_QUERY_INFO *r_u,
+                          uint32 switch_value, WKS_INFO_100 *wks100,
+                          NTSTATUS status)  
+{
+       DEBUG(5,("init_wks_r_unknown_0: %d\n", __LINE__));
+
+       r_u->switch_value = switch_value;  /* same as in request */
+
+       r_u->ptr_1     = 1;          /* pointer 1 */
+       r_u->wks100    = wks100;
+
+       r_u->status    = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL wks_io_r_query_info(const char *desc, WKS_R_QUERY_INFO *r_u, prs_struct *ps, int depth)
+{
+       if (r_u == NULL)
+               return False;
+
+       prs_debug(ps, depth, desc, "wks_io_r_query_info");
+       depth++;
+
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value)) /* level 100 (0x64) */
+               return False;
+       if(!prs_align(ps))
+               return False;
+
+       if(!prs_uint32("ptr_1       ", ps, depth, &r_u->ptr_1)) /* pointer 1 */
+               return False;
+       if(!wks_io_wks_info_100("inf", r_u->wks100, ps, depth))
+               return False;
+
+       if(!prs_ntstatus("status      ", ps, depth, &r_u->status))
+               return False;
+
+       return True;
+}
diff --git a/source4/rpc_server/.cvsignore b/source4/rpc_server/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/source4/rpc_server/srv_dfs.c b/source4/rpc_server/srv_dfs.c
new file mode 100644 (file)
index 0000000..14c1cb4
--- /dev/null
@@ -0,0 +1,177 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines for Dfs
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Shirish Kalele                    2000,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *  
+ *  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.
+ */
+
+/* This is the interface to the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/**********************************************************************
+ api_dfs_exist
+ **********************************************************************/
+
+static BOOL api_dfs_exist(pipes_struct *p)
+{
+       DFS_Q_DFS_EXIST q_u;
+       DFS_R_DFS_EXIST r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       if(!dfs_io_q_dfs_exist("", &q_u, data, 0))
+               return False;
+       
+       r_u.status = _dfs_exist(p, &q_u, &r_u);
+       
+       if (!dfs_io_r_dfs_exist("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*****************************************************************
+ api_dfs_add
+ *****************************************************************/
+
+static BOOL api_dfs_add(pipes_struct *p)
+{
+       DFS_Q_DFS_ADD q_u;
+       DFS_R_DFS_ADD r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!dfs_io_q_dfs_add("", &q_u, data, 0))
+               return False;
+       
+       r_u.status = _dfs_add(p, &q_u, &r_u);
+       
+       if (!dfs_io_r_dfs_add("", &r_u, rdata, 0))
+               return False;
+       
+       return True;
+}
+
+/*****************************************************************
+ api_dfs_remove
+ *****************************************************************/
+
+static BOOL api_dfs_remove(pipes_struct *p)
+{
+       DFS_Q_DFS_REMOVE q_u;
+       DFS_R_DFS_REMOVE r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!dfs_io_q_dfs_remove("", &q_u, data, 0))
+               return False;
+       
+       r_u.status = _dfs_remove(p, &q_u, &r_u);
+       
+       if (!dfs_io_r_dfs_remove("", &r_u, rdata, 0))
+               return False;
+       
+       return True;
+}
+
+/*******************************************************************
+ api_dfs_get_info
+ *******************************************************************/
+
+static BOOL api_dfs_get_info(pipes_struct *p)
+{
+       DFS_Q_DFS_GET_INFO q_u;
+       DFS_R_DFS_GET_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!dfs_io_q_dfs_get_info("", &q_u, data, 0))
+               return False;
+       
+       r_u.status = _dfs_get_info(p, &q_u, &r_u);
+       
+       if(!dfs_io_r_dfs_get_info("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_dfs_enum
+ *******************************************************************/
+
+static BOOL api_dfs_enum(pipes_struct *p)
+{
+       DFS_Q_DFS_ENUM q_u;
+       DFS_R_DFS_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!dfs_io_q_dfs_enum("", &q_u, data, 0))
+               return False;
+       
+       r_u.status = _dfs_enum(p, &q_u, &r_u);
+       
+       if(!dfs_io_r_dfs_enum("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+\pipe\netdfs commands
+********************************************************************/
+
+#ifdef RPC_DFS_DYNAMIC
+int init_module(void)
+#else
+int rpc_dfs_init(void)
+#endif
+{
+  struct api_struct api_netdfs_cmds[] =
+    {
+      {"DFS_EXIST",        DFS_EXIST,               api_dfs_exist    },
+      {"DFS_ADD",          DFS_ADD,                 api_dfs_add      },
+      {"DFS_REMOVE",       DFS_REMOVE,              api_dfs_remove   },
+      {"DFS_GET_INFO",     DFS_GET_INFO,            api_dfs_get_info },
+      {"DFS_ENUM",         DFS_ENUM,                api_dfs_enum     }
+    };
+  return rpc_pipe_register_commands("netdfs", "netdfs", api_netdfs_cmds,
+                                   sizeof(api_netdfs_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_dfs_nt.c b/source4/rpc_server/srv_dfs_nt.c
new file mode 100644 (file)
index 0000000..bb9ed87
--- /dev/null
@@ -0,0 +1,371 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines for Dfs
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Shirish Kalele               2000.
+ *  Copyright (C) Jeremy Allison                               2001.
+ *  
+ *  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.
+ */
+
+/* This is the implementation of the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+/* This function does not return a WERROR or NTSTATUS code but rather 1 if
+   dfs exists, or 0 otherwise. */
+
+uint32 _dfs_exist(pipes_struct *p, DFS_Q_DFS_EXIST *q_u, DFS_R_DFS_EXIST *r_u)
+{
+       if(lp_host_msdfs()) 
+               return 1;
+       else
+               return 0;
+}
+
+WERROR _dfs_add(pipes_struct *p, DFS_Q_DFS_ADD* q_u, DFS_R_DFS_ADD *r_u)
+{
+  struct current_user user;
+  struct junction_map jn;
+  struct referral* old_referral_list = NULL;
+  BOOL exists = False;
+
+  pstring dfspath, servername, sharename;
+  pstring altpath;
+
+  get_current_user(&user,p);
+
+  if (user.uid != 0) {
+       DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
+       return WERR_ACCESS_DENIED;
+  }
+
+  unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+  unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+  unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+
+  DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n",
+          dfspath, servername, sharename));
+
+  pstrcpy(altpath, servername);
+  pstrcat(altpath, "\\");
+  pstrcat(altpath, sharename);
+
+  if(get_referred_path(dfspath, &jn, NULL, NULL))
+    {
+      exists = True;
+      jn.referral_count += 1;
+      old_referral_list = jn.referral_list;
+    }
+  else
+    jn.referral_count = 1;
+
+  jn.referral_list = (struct referral*) talloc(p->mem_ctx, jn.referral_count 
+                                              * sizeof(struct referral));
+
+  if(jn.referral_list == NULL)
+    {
+      DEBUG(0,("init_reply_dfs_add: talloc failed for referral list!\n"));
+      return WERR_DFS_INTERNAL_ERROR;
+    }
+
+  if(old_referral_list)
+    {
+      memcpy(jn.referral_list, old_referral_list, 
+            sizeof(struct referral)*jn.referral_count-1);
+      SAFE_FREE(old_referral_list);
+    }
+  
+  jn.referral_list[jn.referral_count-1].proximity = 0;
+  jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL;
+
+  pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath);
+  
+  if(!create_msdfs_link(&jn, exists))
+    return WERR_DFS_CANT_CREATE_JUNCT;
+
+  return WERR_OK;
+}
+
+WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u, 
+                   DFS_R_DFS_REMOVE *r_u)
+{
+  struct current_user user;
+  struct junction_map jn;
+  BOOL found = False;
+
+  pstring dfspath, servername, sharename;
+  pstring altpath;
+
+  get_current_user(&user,p);
+
+  if (user.uid != 0) {
+       DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
+       return WERR_ACCESS_DENIED;
+  }
+
+  unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+  if(q_u->ptr_ServerName)
+    unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+
+  if(q_u->ptr_ShareName)
+    unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+
+  if(q_u->ptr_ServerName && q_u->ptr_ShareName)
+    {
+      pstrcpy(altpath, servername);
+      pstrcat(altpath, "\\");
+      pstrcat(altpath, sharename);
+      strlower(altpath);
+    }
+
+  DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
+          dfspath, servername, sharename));
+
+  if(!get_referred_path(dfspath, &jn, NULL, NULL))
+         return WERR_DFS_NO_SUCH_VOL;
+
+  /* if no server-share pair given, remove the msdfs link completely */
+  if(!q_u->ptr_ServerName && !q_u->ptr_ShareName)
+    {
+      if(!remove_msdfs_link(&jn))
+       return WERR_DFS_NO_SUCH_VOL;
+    }
+  else
+    {
+      int i=0;
+      /* compare each referral in the list with the one to remove */
+      DEBUG(10,("altpath: .%s. refcnt: %d\n", altpath, jn.referral_count));
+      for(i=0;i<jn.referral_count;i++)
+       {
+         pstring refpath;
+         pstrcpy(refpath,jn.referral_list[i].alternate_path);
+         trim_string(refpath, "\\", "\\");
+         DEBUG(10,("_dfs_remove:  refpath: .%s.\n", refpath));
+         if(strequal(refpath, altpath))
+           {
+             *(jn.referral_list[i].alternate_path)='\0';
+             DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
+                       refpath));
+             found = True;
+           }
+       }
+      if(!found)
+       return WERR_DFS_NO_SUCH_SHARE;
+      
+      /* Only one referral, remove it */
+      if(jn.referral_count == 1)
+       {
+         if(!remove_msdfs_link(&jn))
+           return WERR_DFS_NO_SUCH_VOL;
+       }
+      else
+       {
+         if(!create_msdfs_link(&jn, True))
+           return WERR_DFS_CANT_CREATE_JUNCT;
+       }
+    }
+
+  return WERR_OK;
+}
+
+static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j)
+{
+  int i=0;
+  for(i=0;i<num_j;i++) 
+    {
+      pstring str;
+      dfs1[i].ptr_entrypath = 1;
+      slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", lp_netbios_name(), 
+              j[i].service_name, j[i].volume_name);
+      DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str));
+      init_unistr2(&dfs1[i].entrypath,str,strlen(str)+1);
+    }
+  return True;
+}
+
+static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j)
+{
+  int i=0;
+  for(i=0;i<num_j;i++)
+    {
+      pstring str;
+      dfs2[i].ptr_entrypath = 1;
+      slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", lp_netbios_name(),
+              j[i].service_name, j[i].volume_name);
+      init_unistr2(&dfs2[i].entrypath, str, strlen(str)+1);
+      dfs2[i].ptr_comment = 0;
+      dfs2[i].state = 1; /* set up state of dfs junction as OK */
+      dfs2[i].num_storages = j[i].referral_count;
+    }
+  return True;
+}
+
+static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j)
+{
+  int i=0,ii=0;
+  for(i=0;i<num_j;i++)
+    {
+      pstring str;
+      dfs3[i].ptr_entrypath = 1;
+      if (j[i].volume_name[0] == '\0')
+             slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s",
+                      lp_netbios_name(), j[i].service_name);
+      else
+             slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", lp_netbios_name(),
+                      j[i].service_name, j[i].volume_name);
+
+      init_unistr2(&dfs3[i].entrypath, str, strlen(str)+1);
+      dfs3[i].ptr_comment = 1;
+      init_unistr2(&dfs3[i].comment, "", 1); 
+      dfs3[i].state = 1;
+      dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count;
+      dfs3[i].ptr_storages = 1;
+     
+      /* also enumerate the storages */
+      dfs3[i].storages = (DFS_STORAGE_INFO*) talloc(ctx, j[i].referral_count * 
+                                                   sizeof(DFS_STORAGE_INFO));
+      if (!dfs3[i].storages)
+        return False;
+
+      memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO));
+
+      for(ii=0;ii<j[i].referral_count;ii++)
+       {
+         char* p; 
+         pstring path;
+         DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]);
+         struct referral* ref = &(j[i].referral_list[ii]);
+         
+         pstrcpy(path, ref->alternate_path);
+         trim_string(path,"\\","");
+         p = strrchr_m(path,'\\');
+         if(p==NULL)
+           {
+             DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
+             continue;
+           }
+         *p = '\0';
+         DEBUG(5,("storage %d: %s.%s\n",ii,path,p+1));
+         stor->state = 2; /* set all storages as ONLINE */
+         init_unistr2(&stor->servername, path, strlen(path)+1);
+         init_unistr2(&stor->sharename,  p+1, strlen(p+1)+1);
+         stor->ptr_servername = stor->ptr_sharename = 1;
+       }
+    }
+  return True;
+}
+
+static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level, 
+                                   DFS_INFO_CTR* ctr, struct junction_map* jn,
+                                   int num_jn)
+{
+  /* do the levels */
+  switch(level)
+    {
+    case 1:
+      {
+       DFS_INFO_1* dfs1;
+       dfs1 = (DFS_INFO_1*) talloc(ctx, num_jn * sizeof(DFS_INFO_1));
+       if (!dfs1)
+               return WERR_NOMEM;
+       init_reply_dfs_info_1(jn, dfs1, num_jn);
+       ctr->dfs.info1 = dfs1;
+       break;
+      }
+    case 2:
+      {
+       DFS_INFO_2* dfs2;
+       dfs2 = (DFS_INFO_2*) talloc(ctx, num_jn * sizeof(DFS_INFO_2));
+       if (!dfs2)
+               return WERR_NOMEM;
+       init_reply_dfs_info_2(jn, dfs2, num_jn);
+       ctr->dfs.info2 = dfs2;
+       break;
+      }
+    case 3:
+      {
+       DFS_INFO_3* dfs3;
+       dfs3 = (DFS_INFO_3*) talloc(ctx, num_jn * sizeof(DFS_INFO_3));
+       if (!dfs3)
+               return WERR_NOMEM;
+       init_reply_dfs_info_3(ctx, jn, dfs3, num_jn);
+       ctr->dfs.info3 = dfs3;
+       break;
+      }
+       default:
+               return WERR_INVALID_PARAM;
+    }
+  return WERR_OK;
+}
+      
+WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u)
+{
+  uint32 level = q_u->level;
+  struct junction_map jn[MAX_MSDFS_JUNCTIONS];
+  int num_jn = 0;
+
+  num_jn = enum_msdfs_links(jn);
+  
+  DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level));
+
+  r_u->ptr_buffer = level;
+  r_u->level = r_u->level2 = level;
+  r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1;
+  r_u->num_entries = r_u->num_entries2 = num_jn;
+  r_u->reshnd.ptr_hnd = 1;
+  r_u->reshnd.handle = num_jn;
+  
+  r_u->ctr = (DFS_INFO_CTR*)talloc(p->mem_ctx, sizeof(DFS_INFO_CTR));
+  if (!r_u->ctr)
+    return WERR_NOMEM;
+  ZERO_STRUCTP(r_u->ctr);
+  r_u->ctr->switch_value = level;
+  r_u->ctr->num_entries = num_jn;
+  r_u->ctr->ptr_dfs_ctr = 1;
+  
+  r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn);
+
+  return r_u->status;
+}
+      
+WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u, 
+                     DFS_R_DFS_GET_INFO *r_u)
+{
+  UNISTR2* uni_path = &q_u->uni_path;
+  uint32 level = q_u->level;
+  pstring path;
+  struct junction_map jn;
+
+  unistr2_to_ascii(path, uni_path, sizeof(path)-1);
+  if(!create_junction(path, &jn))
+     return WERR_DFS_NO_SUCH_SERVER;
+  
+  if(!get_referred_path(path, &jn, NULL, NULL))
+     return WERR_DFS_NO_SUCH_VOL;
+
+  r_u->level = level;
+  r_u->ptr_ctr = 1;
+  r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1);
+  
+  return r_u->status;
+}
diff --git a/source4/rpc_server/srv_lsa.c b/source4/rpc_server/srv_lsa.c
new file mode 100644 (file)
index 0000000..0e40393
--- /dev/null
@@ -0,0 +1,810 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Jim McDonough                     2002,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *
+ *  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.
+ */
+
+/* This is the interface to the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/***************************************************************************
+ api_lsa_open_policy2
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy2(pipes_struct *p)
+{
+       LSA_Q_OPEN_POL2 q_u;
+       LSA_R_OPEN_POL2 r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the server, object attributes and desired access flag...*/
+       if(!lsa_io_q_open_pol2("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_open_policy2: unable to unmarshall LSA_Q_OPEN_POL2.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_open_policy2(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_open_pol2("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_open_policy2: unable to marshall LSA_R_OPEN_POL2.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+api_lsa_open_policy
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy(pipes_struct *p)
+{
+       LSA_Q_OPEN_POL q_u;
+       LSA_R_OPEN_POL r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the server, object attributes and desired access flag...*/
+       if(!lsa_io_q_open_pol("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_open_policy: unable to unmarshall LSA_Q_OPEN_POL.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_open_policy(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_open_pol("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_open_policy: unable to marshall LSA_R_OPEN_POL.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_enum_trust_dom
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_trust_dom(pipes_struct *p)
+{
+       LSA_Q_ENUM_TRUST_DOM q_u;
+       LSA_R_ENUM_TRUST_DOM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the enum trust domain context etc. */
+       if(!lsa_io_q_enum_trust_dom("", &q_u, data, 0))
+               return False;
+
+       /* get required trusted domains information */
+       r_u.status = _lsa_enum_trust_dom(p, &q_u, &r_u);
+
+       /* prepare the response */
+       if(!lsa_io_r_enum_trust_dom("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_query_info
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info(pipes_struct *p)
+{
+       LSA_Q_QUERY_INFO q_u;
+       LSA_R_QUERY_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the info class and policy handle */
+       if(!lsa_io_q_query("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_query_info: failed to unmarshall LSA_Q_QUERY_INFO.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_query_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_query("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_query_info: failed to marshall LSA_R_QUERY_INFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_sids
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_sids(pipes_struct *p)
+{
+       LSA_Q_LOOKUP_SIDS q_u;
+       LSA_R_LOOKUP_SIDS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the info class and policy handle */
+       if(!lsa_io_q_lookup_sids("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_lookup_sids: failed to unmarshall LSA_Q_LOOKUP_SIDS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_lookup_sids(p, &q_u, &r_u);
+
+       if(!lsa_io_r_lookup_sids("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_lookup_sids: Failed to marshall LSA_R_LOOKUP_SIDS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_names
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_names(pipes_struct *p)
+{
+       LSA_Q_LOOKUP_NAMES q_u;
+       LSA_R_LOOKUP_NAMES r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the info class and policy handle */
+       if(!lsa_io_q_lookup_names("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_lookup_names: failed to unmarshall LSA_Q_LOOKUP_NAMES.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_lookup_names(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_lookup_names("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_lookup_names: Failed to marshall LSA_R_LOOKUP_NAMES.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_close.
+ ***************************************************************************/
+
+static BOOL api_lsa_close(pipes_struct *p)
+{
+       LSA_Q_CLOSE q_u;
+       LSA_R_CLOSE r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!lsa_io_q_close("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_close: lsa_io_q_close failed.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_close(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if (!lsa_io_r_close("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_close: lsa_io_r_close failed.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_open_secret(pipes_struct *p)
+{
+       LSA_Q_OPEN_SECRET q_u;
+       LSA_R_OPEN_SECRET r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_open_secret("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_open_secret: failed to unmarshall LSA_Q_OPEN_SECRET.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_open_secret(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_open_secret("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_open_secret: Failed to marshall LSA_R_OPEN_SECRET.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privs(pipes_struct *p)
+{
+       LSA_Q_ENUM_PRIVS q_u;
+       LSA_R_ENUM_PRIVS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_enum_privs("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_enum_privs: failed to unmarshall LSA_Q_ENUM_PRIVS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_enum_privs(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_enum_privs("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_enum_privs: Failed to marshall LSA_R_ENUM_PRIVS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_priv_get_dispname(pipes_struct *p)
+{
+       LSA_Q_PRIV_GET_DISPNAME q_u;
+       LSA_R_PRIV_GET_DISPNAME r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_priv_get_dispname("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_priv_get_dispname: failed to unmarshall LSA_Q_PRIV_GET_DISPNAME.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_priv_get_dispname(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_priv_get_dispname("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_priv_get_dispname: Failed to marshall LSA_R_PRIV_GET_DISPNAME.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_accounts(pipes_struct *p)
+{
+       LSA_Q_ENUM_ACCOUNTS q_u;
+       LSA_R_ENUM_ACCOUNTS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_enum_accounts("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_enum_accounts: failed to unmarshall LSA_Q_ENUM_ACCOUNTS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_enum_accounts(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_enum_accounts("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_enum_accounts: Failed to marshall LSA_R_ENUM_ACCOUNTS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_UNK_GET_CONNUSER
+ ***************************************************************************/
+
+static BOOL api_lsa_unk_get_connuser(pipes_struct *p)
+{
+       LSA_Q_UNK_GET_CONNUSER q_u;
+       LSA_R_UNK_GET_CONNUSER r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_unk_get_connuser("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_unk_get_connuser: failed to unmarshall LSA_Q_UNK_GET_CONNUSER.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_unk_get_connuser(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_unk_get_connuser("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_unk_get_connuser: Failed to marshall LSA_R_UNK_GET_CONNUSER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_open_user
+ ***************************************************************************/
+
+static BOOL api_lsa_open_account(pipes_struct *p)
+{
+       LSA_Q_OPENACCOUNT q_u;
+       LSA_R_OPENACCOUNT r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_open_account("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_open_account: failed to unmarshall LSA_Q_OPENACCOUNT.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_open_account(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_open_account("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_open_account: Failed to marshall LSA_R_OPENACCOUNT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_get_privs
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privsaccount(pipes_struct *p)
+{
+       LSA_Q_ENUMPRIVSACCOUNT q_u;
+       LSA_R_ENUMPRIVSACCOUNT r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_enum_privsaccount("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_enum_privsaccount: failed to unmarshall LSA_Q_ENUMPRIVSACCOUNT.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_enum_privsaccount(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_enum_privsaccount("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_enum_privsaccount: Failed to marshall LSA_R_ENUMPRIVSACCOUNT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_getsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_getsystemaccount(pipes_struct *p)
+{
+       LSA_Q_GETSYSTEMACCOUNT q_u;
+       LSA_R_GETSYSTEMACCOUNT r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_getsystemaccount("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_getsystemaccount: failed to unmarshall LSA_Q_GETSYSTEMACCOUNT.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_getsystemaccount(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_getsystemaccount("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_getsystemaccount: Failed to marshall LSA_R_GETSYSTEMACCOUNT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/***************************************************************************
+ api_lsa_setsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_setsystemaccount(pipes_struct *p)
+{
+       LSA_Q_SETSYSTEMACCOUNT q_u;
+       LSA_R_SETSYSTEMACCOUNT r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_setsystemaccount("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_setsystemaccount: failed to unmarshall LSA_Q_SETSYSTEMACCOUNT.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_setsystemaccount(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_setsystemaccount("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_setsystemaccount: Failed to marshall LSA_R_SETSYSTEMACCOUNT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_addprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_addprivs(pipes_struct *p)
+{
+       LSA_Q_ADDPRIVS q_u;
+       LSA_R_ADDPRIVS r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_addprivs("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_addprivs: failed to unmarshall LSA_Q_ADDPRIVS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_addprivs(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_addprivs("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_addprivs: Failed to marshall LSA_R_ADDPRIVS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_removeprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_removeprivs(pipes_struct *p)
+{
+       LSA_Q_REMOVEPRIVS q_u;
+       LSA_R_REMOVEPRIVS r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_removeprivs("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_removeprivs: failed to unmarshall LSA_Q_REMOVEPRIVS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_removeprivs(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_removeprivs("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_removeprivs: Failed to marshall LSA_R_REMOVEPRIVS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_query_secobj
+ ***************************************************************************/
+
+static BOOL api_lsa_query_secobj(pipes_struct *p)
+{
+       LSA_Q_QUERY_SEC_OBJ q_u;
+       LSA_R_QUERY_SEC_OBJ r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_query_sec_obj("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_query_secobj: failed to unmarshall LSA_Q_QUERY_SEC_OBJ.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_query_secobj(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_query_secobj: Failed to marshall LSA_R_QUERY_SEC_OBJ.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/***************************************************************************
+ api_lsa_query_dnsdomainfo
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info2(pipes_struct *p)
+{
+       LSA_Q_QUERY_INFO2 q_u;
+       LSA_R_QUERY_INFO2 r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_query_info2("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_query_info2: failed to unmarshall LSA_Q_QUERY_INFO2.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_query_info2(p, &q_u, &r_u);
+
+       if (!lsa_io_r_query_info2("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_query_info2: failed to marshall LSA_R_QUERY_INFO2.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+
+/***************************************************************************
+ api_lsa_enum_acctrights
+ ***************************************************************************/
+static BOOL api_lsa_enum_acct_rights(pipes_struct *p)
+{
+       LSA_Q_ENUM_ACCT_RIGHTS q_u;
+       LSA_R_ENUM_ACCT_RIGHTS r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_enum_acct_rights("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_enum_acct_rights: failed to unmarshall LSA_Q_ENUM_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_enum_acct_rights(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_enum_acct_rights("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_enum_acct_rights: Failed to marshall LSA_R_ENUM_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/***************************************************************************
+ api_lsa_enum_acct_with_right
+ ***************************************************************************/
+static BOOL api_lsa_enum_acct_with_right(pipes_struct *p)
+{
+       LSA_Q_ENUM_ACCT_WITH_RIGHT q_u;
+       LSA_R_ENUM_ACCT_WITH_RIGHT r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_enum_acct_with_right("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_enum_acct_with_right: failed to unmarshall LSA_Q_ENUM_ACCT_WITH_RIGHT.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_enum_acct_with_right(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_enum_acct_with_right("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_enum_acct_with_right: Failed to marshall LSA_R_ENUM_ACCT_WITH_RIGHT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/***************************************************************************
+ api_lsa_add_acctrights
+ ***************************************************************************/
+static BOOL api_lsa_add_acct_rights(pipes_struct *p)
+{
+       LSA_Q_ADD_ACCT_RIGHTS q_u;
+       LSA_R_ADD_ACCT_RIGHTS r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_add_acct_rights("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_add_acct_rights: failed to unmarshall LSA_Q_ADD_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_add_acct_rights(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_add_acct_rights("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_add_acct_rights: Failed to marshall LSA_R_ADD_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/***************************************************************************
+ api_lsa_remove_acctrights
+ ***************************************************************************/
+static BOOL api_lsa_remove_acct_rights(pipes_struct *p)
+{
+       LSA_Q_REMOVE_ACCT_RIGHTS q_u;
+       LSA_R_REMOVE_ACCT_RIGHTS r_u;
+       
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!lsa_io_q_remove_acct_rights("", &q_u, data, 0)) {
+               DEBUG(0,("api_lsa_remove_acct_rights: failed to unmarshall LSA_Q_REMOVE_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       r_u.status = _lsa_remove_acct_rights(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!lsa_io_r_remove_acct_rights("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_lsa_remove_acct_rights: Failed to marshall LSA_R_REMOVE_ACCT_RIGHTS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/***************************************************************************
+ \PIPE\ntlsa commands
+ ***************************************************************************/
+
+#ifdef RPC_LSA_DYNAMIC
+int init_module(void)
+#else
+int rpc_lsa_init(void)
+#endif
+{
+  static const struct api_struct api_lsa_cmds[] =
+    {
+      { "LSA_OPENPOLICY2"     , LSA_OPENPOLICY2     , api_lsa_open_policy2     },
+      { "LSA_OPENPOLICY"      , LSA_OPENPOLICY      , api_lsa_open_policy      },
+      { "LSA_QUERYINFOPOLICY" , LSA_QUERYINFOPOLICY , api_lsa_query_info       },
+      { "LSA_ENUMTRUSTDOM"    , LSA_ENUMTRUSTDOM    , api_lsa_enum_trust_dom   },
+      { "LSA_CLOSE"           , LSA_CLOSE           , api_lsa_close            },
+      { "LSA_OPENSECRET"      , LSA_OPENSECRET      , api_lsa_open_secret      },
+      { "LSA_LOOKUPSIDS"      , LSA_LOOKUPSIDS      , api_lsa_lookup_sids      },
+      { "LSA_LOOKUPNAMES"     , LSA_LOOKUPNAMES     , api_lsa_lookup_names     },
+      { "LSA_ENUM_PRIVS"      , LSA_ENUM_PRIVS      , api_lsa_enum_privs       },
+      { "LSA_PRIV_GET_DISPNAME",LSA_PRIV_GET_DISPNAME,api_lsa_priv_get_dispname},
+      { "LSA_ENUM_ACCOUNTS"   , LSA_ENUM_ACCOUNTS   , api_lsa_enum_accounts    },
+      { "LSA_UNK_GET_CONNUSER", LSA_UNK_GET_CONNUSER, api_lsa_unk_get_connuser },
+      { "LSA_OPENACCOUNT"     , LSA_OPENACCOUNT     , api_lsa_open_account     },
+      { "LSA_ENUMPRIVSACCOUNT", LSA_ENUMPRIVSACCOUNT, api_lsa_enum_privsaccount},
+      { "LSA_GETSYSTEMACCOUNT", LSA_GETSYSTEMACCOUNT, api_lsa_getsystemaccount },
+      { "LSA_SETSYSTEMACCOUNT", LSA_SETSYSTEMACCOUNT, api_lsa_setsystemaccount },
+      { "LSA_ADDPRIVS"        , LSA_ADDPRIVS        , api_lsa_addprivs         },
+      { "LSA_REMOVEPRIVS"     , LSA_REMOVEPRIVS     , api_lsa_removeprivs      },
+      { "LSA_QUERYSECOBJ"     , LSA_QUERYSECOBJ     , api_lsa_query_secobj     },
+      { "LSA_QUERYINFO2"      , LSA_QUERYINFO2      , api_lsa_query_info2      },
+      { "LSA_ENUMACCTRIGHTS"  , LSA_ENUMACCTRIGHTS  , api_lsa_enum_acct_rights },
+      { "LSA_ENUMACCTWITHRIGHT", LSA_ENUMACCTWITHRIGHT, api_lsa_enum_acct_with_right },
+      { "LSA_ADDACCTRIGHTS"   , LSA_ADDACCTRIGHTS   , api_lsa_add_acct_rights  },
+      { "LSA_REMOVEACCTRIGHTS", LSA_REMOVEACCTRIGHTS, api_lsa_remove_acct_rights},
+    };
+
+  return rpc_pipe_register_commands("lsarpc", "lsass", api_lsa_cmds, 
+                                   sizeof(api_lsa_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_lsa_hnd.c b/source4/rpc_server/srv_lsa_hnd.c
new file mode 100644 (file)
index 0000000..814fa60
--- /dev/null
@@ -0,0 +1,265 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Jeremy Allison                          2001.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* This is the max handles across all instances of a pipe name. */
+#ifndef MAX_OPEN_POLS
+#define MAX_OPEN_POLS 1024
+#endif
+
+/****************************************************************************
+ Hack as handles need to be persisant over lsa pipe closes so long as a samr
+ pipe is open. JRA.
+****************************************************************************/
+
+static BOOL is_samr_lsa_pipe(const char *pipe_name)
+{
+       return (strstr(pipe_name, "samr") || strstr(pipe_name, "lsa"));
+}
+
+/****************************************************************************
+ Initialise a policy handle list on a pipe. Handle list is shared between all
+ pipes of the same name.
+****************************************************************************/
+
+BOOL init_pipe_handle_list(pipes_struct *p, char *pipe_name)
+{
+       pipes_struct *plist = get_first_internal_pipe();
+       struct handle_list *hl = NULL;
+
+       for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist)) {
+               if (strequal( plist->name, pipe_name) ||
+                               (is_samr_lsa_pipe(plist->name) && is_samr_lsa_pipe(pipe_name))) {
+                       if (!plist->pipe_handles) {
+                               pstring msg;
+                               slprintf(msg, sizeof(msg)-1, "init_pipe_handles: NULL pipe_handle pointer in pipe %s",
+                                               pipe_name );
+                               smb_panic(msg);
+                       }
+                       hl = plist->pipe_handles;
+                       break;
+               }
+       }
+
+       if (!hl) {
+               /*
+                * No handle list for this pipe (first open of pipe).
+                * Create list.
+                */
+
+               if ((hl = (struct handle_list *)malloc(sizeof(struct handle_list))) == NULL)
+                       return False;
+               ZERO_STRUCTP(hl);
+
+               DEBUG(10,("init_pipe_handles: created handle list for pipe %s\n", pipe_name ));
+       }
+
+       /*
+        * One more pipe is using this list.
+        */
+
+       hl->pipe_ref_count++;
+
+       /*
+        * Point this pipe at this list.
+        */
+
+       p->pipe_handles = hl;
+
+       DEBUG(10,("init_pipe_handles: pipe_handles ref count = %u for pipe %s\n",
+                       p->pipe_handles->pipe_ref_count, pipe_name ));
+
+       return True;
+}
+
+/****************************************************************************
+  find first available policy slot.  creates a policy handle for you.
+****************************************************************************/
+
+BOOL create_policy_hnd(pipes_struct *p, POLICY_HND *hnd, void (*free_fn)(void *), void *data_ptr)
+{
+       static uint32 pol_hnd_low  = 0;
+       static uint32 pol_hnd_high = 0;
+
+       struct policy *pol;
+
+       if (p->pipe_handles->count > MAX_OPEN_POLS) {
+               DEBUG(0,("create_policy_hnd: ERROR: too many handles (%d) on this pipe.\n",
+                               (int)p->pipe_handles->count));
+               return False;
+       }
+
+       pol = (struct policy *)malloc(sizeof(*p));
+       if (!pol) {
+               DEBUG(0,("create_policy_hnd: ERROR: out of memory!\n"));
+               return False;
+       }
+
+       ZERO_STRUCTP(pol);
+
+       pol->data_ptr = data_ptr;
+       pol->free_fn = free_fn;
+
+       pol_hnd_low++;
+       if (pol_hnd_low == 0)
+               (pol_hnd_high)++;
+
+       SIVAL(&pol->pol_hnd.data1, 0 , 0);  /* first bit must be null */
+       SIVAL(&pol->pol_hnd.data2, 0 , pol_hnd_low ); /* second bit is incrementing */
+       SSVAL(&pol->pol_hnd.data3, 0 , pol_hnd_high); /* second bit is incrementing */
+       SSVAL(&pol->pol_hnd.data4, 0 , (pol_hnd_high>>16)); /* second bit is incrementing */
+       SIVAL(pol->pol_hnd.data5, 0, time(NULL)); /* something random */
+       SIVAL(pol->pol_hnd.data5, 4, sys_getpid()); /* something more random */
+
+       DLIST_ADD(p->pipe_handles->Policy, pol);
+       p->pipe_handles->count++;
+
+       *hnd = pol->pol_hnd;
+       
+       DEBUG(4,("Opened policy hnd[%d] ", (int)p->pipe_handles->count));
+       dump_data(4, (char *)hnd, sizeof(*hnd));
+
+       return True;
+}
+
+/****************************************************************************
+  find policy by handle - internal version.
+****************************************************************************/
+
+static struct policy *find_policy_by_hnd_internal(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+       struct policy *pol;
+       size_t i;
+
+       if (data_p)
+               *data_p = NULL;
+
+       for (i = 0, pol=p->pipe_handles->Policy;pol;pol=pol->next, i++) {
+               if (memcmp(&pol->pol_hnd, hnd, sizeof(*hnd)) == 0) {
+                       DEBUG(4,("Found policy hnd[%d] ", (int)i));
+                       dump_data(4, (char *)hnd, sizeof(*hnd));
+                       if (data_p)
+                               *data_p = pol->data_ptr;
+                       return pol;
+               }
+       }
+
+       DEBUG(4,("Policy not found: "));
+       dump_data(4, (char *)hnd, sizeof(*hnd));
+
+       p->bad_handle_fault_state = True;
+
+       return NULL;
+}
+
+/****************************************************************************
+  find policy by handle
+****************************************************************************/
+
+BOOL find_policy_by_hnd(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+       return find_policy_by_hnd_internal(p, hnd, data_p) == NULL ? False : True;
+}
+
+/****************************************************************************
+  Close a policy.
+****************************************************************************/
+
+BOOL close_policy_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+       struct policy *pol = find_policy_by_hnd_internal(p, hnd, NULL);
+
+       if (!pol) {
+               DEBUG(3,("Error closing policy\n"));
+               return False;
+       }
+
+       DEBUG(3,("Closed policy\n"));
+
+       if (pol->free_fn && pol->data_ptr)
+               (*pol->free_fn)(pol->data_ptr);
+
+       p->pipe_handles->count--;
+
+       DLIST_REMOVE(p->pipe_handles->Policy, pol);
+
+       ZERO_STRUCTP(pol);
+
+       SAFE_FREE(pol);
+
+       return True;
+}
+
+/****************************************************************************
+ Close a pipe - free the handle list if it was the last pipe reference.
+****************************************************************************/
+
+void close_policy_by_pipe(pipes_struct *p)
+{
+       p->pipe_handles->pipe_ref_count--;
+
+       if (p->pipe_handles->pipe_ref_count == 0) {
+               /*
+                * Last pipe open on this list - free the list.
+                */
+               while (p->pipe_handles->Policy)
+                       close_policy_hnd(p, &p->pipe_handles->Policy->pol_hnd);
+
+               p->pipe_handles->Policy = NULL;
+               p->pipe_handles->count = 0;
+
+               SAFE_FREE(p->pipe_handles);
+               DEBUG(10,("close_policy_by_pipe: deleted handle list for pipe %s\n", p->name ));
+       }
+}
+
+/*******************************************************************
+Shall we allow access to this rpc?  Currently this function
+implements the 'restrict anonymous' setting by denying access to
+anonymous users if the restrict anonymous level is > 0.  Further work
+will be checking a security descriptor to determine whether a user
+token has enough access to access the pipe.
+********************************************************************/
+
+BOOL pipe_access_check(pipes_struct *p)
+{
+       /* Don't let anonymous users access this RPC if restrict
+          anonymous > 0 */
+
+       if (lp_restrict_anonymous() > 0) {
+               user_struct *user = get_valid_user_struct(p->vuid);
+
+               if (!user) {
+                       DEBUG(3, ("invalid vuid %d\n", p->vuid));
+                       return False;
+               }
+
+               if (user->guest)
+                       return False;
+       }
+
+       return True;
+}
diff --git a/source4/rpc_server/srv_lsa_nt.c b/source4/rpc_server/srv_lsa_nt.c
new file mode 100644 (file)
index 0000000..3af3e75
--- /dev/null
@@ -0,0 +1,1399 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Rafal Szczesniak                  2002,
+ *  Copyright (C) Jim McDonough                     2002.
+ *
+ *  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.
+ */
+
+/* This is the implementation of the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern PRIVS privs[];
+
+struct lsa_info {
+       DOM_SID sid;
+       uint32 access;
+};
+
+struct generic_mapping lsa_generic_mapping = {
+       POLICY_READ,
+       POLICY_WRITE,
+       POLICY_EXECUTE,
+       POLICY_ALL_ACCESS
+};
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_lsa_info(void *ptr)
+{
+       struct lsa_info *lsa = (struct lsa_info *)ptr;
+
+       SAFE_FREE(lsa);
+}
+
+/***************************************************************************
+Init dom_query
+ ***************************************************************************/
+
+static void init_dom_query(DOM_QUERY *d_q, const char *dom_name, DOM_SID *dom_sid)
+{
+       int domlen = (dom_name != NULL) ? strlen(dom_name) : 0;
+
+       /*
+        * I'm not sure why this really odd combination of length
+        * values works, but it does appear to. I need to look at
+        * this *much* more closely - but at the moment leave alone
+        * until it's understood. This allows a W2k client to join
+        * a domain with both odd and even length names... JRA.
+        */
+
+       d_q->uni_dom_str_len = domlen ? ((domlen + 1) * 2) : 0;
+       d_q->uni_dom_max_len = domlen * 2;
+       d_q->buffer_dom_name = domlen != 0 ? 1 : 0; /* domain buffer pointer */
+       d_q->buffer_dom_sid = dom_sid != NULL ? 1 : 0;  /* domain sid pointer */
+
+       /* this string is supposed to be character short */
+       init_unistr2(&d_q->uni_domain_name, dom_name, domlen);
+       d_q->uni_domain_name.uni_max_len++;
+
+       if (dom_sid != NULL)
+               init_dom_sid2(&d_q->dom_sid, dom_sid);
+}
+
+/***************************************************************************
+ init_dom_ref - adds a domain if it's not already in, returns the index.
+***************************************************************************/
+
+static int init_dom_ref(DOM_R_REF *ref, char *dom_name, DOM_SID *dom_sid)
+{
+       int num = 0;
+       int len;
+
+       if (dom_name != NULL) {
+               for (num = 0; num < ref->num_ref_doms_1; num++) {
+                       fstring domname;
+                       rpcstr_pull(domname, &ref->ref_dom[num].uni_dom_name, sizeof(domname), -1, 0);
+                       if (strequal(domname, dom_name))
+                               return num;
+               }
+       } else {
+               num = ref->num_ref_doms_1;
+       }
+
+       if (num >= MAX_REF_DOMAINS) {
+               /* index not found, already at maximum domain limit */
+               return -1;
+       }
+
+       ref->num_ref_doms_1 = num+1;
+       ref->ptr_ref_dom  = 1;
+       ref->max_entries = MAX_REF_DOMAINS;
+       ref->num_ref_doms_2 = num+1;
+
+       len = (dom_name != NULL) ? strlen(dom_name) : 0;
+       if(dom_name != NULL && len == 0)
+               len = 1;
+
+       init_uni_hdr(&ref->hdr_ref_dom[num].hdr_dom_name, len);
+       ref->hdr_ref_dom[num].ptr_dom_sid = dom_sid != NULL ? 1 : 0;
+
+       init_unistr2(&ref->ref_dom[num].uni_dom_name, dom_name, len);
+       init_dom_sid2(&ref->ref_dom[num].ref_dom, dom_sid );
+
+       return num;
+}
+
+/***************************************************************************
+ init_lsa_rid2s
+ ***************************************************************************/
+
+static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2,
+                               int num_entries, UNISTR2 *name,
+                               uint32 *mapped_count, BOOL endian)
+{
+       int i;
+       int total = 0;
+       *mapped_count = 0;
+
+       SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+       become_root(); /* lookup_name can require root privs */
+
+       for (i = 0; i < num_entries; i++) {
+               BOOL status = False;
+               DOM_SID sid;
+               uint32 rid = 0xffffffff;
+               int dom_idx = -1;
+               pstring full_name;
+               fstring dom_name, user;
+               enum SID_NAME_USE name_type = SID_NAME_UNKNOWN;
+
+               /* Split name into domain and user component */
+
+               unistr2_to_ascii(full_name, &name[i], sizeof(full_name));
+               split_domain_name(full_name, dom_name, user);
+
+               /* Lookup name */
+
+               DEBUG(5, ("init_lsa_rid2s: looking up name %s\n", full_name));
+
+               status = lookup_name(dom_name, user, &sid, &name_type);
+
+               DEBUG(5, ("init_lsa_rid2s: %s\n", status ? "found" : 
+                         "not found"));
+
+               if (status && name_type != SID_NAME_UNKNOWN) {
+                       sid_split_rid(&sid, &rid);
+                       dom_idx = init_dom_ref(ref, dom_name, &sid);
+                       (*mapped_count)++;
+               } else {
+                       dom_idx = -1;
+                       rid = 0xffffffff;
+                       name_type = SID_NAME_UNKNOWN;
+               }
+
+               init_dom_rid2(&rid2[total], rid, name_type, dom_idx);
+               total++;
+       }
+
+       unbecome_root();
+}
+
+/***************************************************************************
+ init_reply_lookup_names
+ ***************************************************************************/
+
+static void init_reply_lookup_names(LSA_R_LOOKUP_NAMES *r_l,
+                DOM_R_REF *ref, uint32 num_entries,
+                DOM_RID2 *rid2, uint32 mapped_count)
+{
+       r_l->ptr_dom_ref  = 1;
+       r_l->dom_ref      = ref;
+
+       r_l->num_entries  = num_entries;
+       r_l->ptr_entries  = 1;
+       r_l->num_entries2 = num_entries;
+       r_l->dom_rid      = rid2;
+
+       r_l->mapped_count = mapped_count;
+
+       if (mapped_count == 0)
+               r_l->status = NT_STATUS_NONE_MAPPED;
+       else
+               r_l->status = NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Init lsa_trans_names.
+ ***************************************************************************/
+
+static void init_lsa_trans_names(TALLOC_CTX *ctx, DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *trn,
+                                int num_entries, DOM_SID2 *sid,
+                                uint32 *mapped_count)
+{
+       int i;
+       int total = 0;
+       *mapped_count = 0;
+
+       /* Allocate memory for list of names */
+
+       if (num_entries > 0) {
+               if (!(trn->name = (LSA_TRANS_NAME *)talloc(ctx, sizeof(LSA_TRANS_NAME) *
+                                                         num_entries))) {
+                       DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+                       return;
+               }
+
+               if (!(trn->uni_name = (UNISTR2 *)talloc(ctx, sizeof(UNISTR2) * 
+                                                       num_entries))) {
+                       DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+                       return;
+               }
+       }
+
+       become_root(); /* Need root to get to passdb to for local sids */
+
+       for (i = 0; i < num_entries; i++) {
+               BOOL status = False;
+               DOM_SID find_sid = sid[i].sid;
+               uint32 rid = 0xffffffff;
+               int dom_idx = -1;
+               fstring name, dom_name;
+               enum SID_NAME_USE sid_name_use = (enum SID_NAME_USE)0;
+
+               sid_to_string(name, &find_sid);
+               DEBUG(5, ("init_lsa_trans_names: looking up sid %s\n", name));
+
+               /* Lookup sid from winbindd */
+
+               memset(dom_name, '\0', sizeof(dom_name));
+               memset(name, '\0', sizeof(name));
+
+               status = lookup_sid(&find_sid, dom_name, name, &sid_name_use);
+
+               DEBUG(5, ("init_lsa_trans_names: %s\n", status ? "found" : 
+                         "not found"));
+
+               if (!status) {
+                       sid_name_use = SID_NAME_UNKNOWN;
+               } else {
+                       (*mapped_count)++;
+               }
+
+               /* Store domain sid in ref array */
+
+               if (find_sid.num_auths == 5) {
+                       sid_split_rid(&find_sid, &rid);
+               }
+
+               dom_idx = init_dom_ref(ref, dom_name, &find_sid);
+
+               DEBUG(10,("init_lsa_trans_names: added user '%s\\%s' to "
+                         "referenced list.\n", dom_name, name ));
+
+               init_lsa_trans_name(&trn->name[total], &trn->uni_name[total],
+                                       sid_name_use, name, dom_idx);
+               total++;
+       }
+
+       unbecome_root();
+
+       trn->num_entries = total;
+       trn->ptr_trans_names = 1;
+       trn->num_entries2 = total;
+}
+
+/***************************************************************************
+ Init_reply_lookup_sids.
+ ***************************************************************************/
+
+static void init_reply_lookup_sids(LSA_R_LOOKUP_SIDS *r_l,
+                DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *names,
+                uint32 mapped_count)
+{
+       r_l->ptr_dom_ref  = 1;
+       r_l->dom_ref      = ref;
+       r_l->names        = names;
+       r_l->mapped_count = mapped_count;
+
+       if (mapped_count == 0)
+               r_l->status = NT_STATUS_NONE_MAPPED;
+       else
+               r_l->status = NT_STATUS_OK;
+}
+
+static NTSTATUS lsa_get_generic_sd(TALLOC_CTX *mem_ctx, SEC_DESC **sd, size_t *sd_size)
+{
+       extern DOM_SID global_sid_World;
+       extern DOM_SID global_sid_Builtin;
+       DOM_SID local_adm_sid;
+       DOM_SID adm_sid;
+
+       SEC_ACE ace[3];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       init_sec_access(&mask, POLICY_EXECUTE);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       sid_copy(&adm_sid, get_global_sam_sid());
+       sid_append_rid(&adm_sid, DOMAIN_GROUP_RID_ADMINS);
+       init_sec_access(&mask, POLICY_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       sid_copy(&local_adm_sid, &global_sid_Builtin);
+       sid_append_rid(&local_adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+       init_sec_access(&mask, POLICY_ALL_ACCESS);
+       init_sec_ace(&ace[2], &local_adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if((*sd = make_sec_desc(mem_ctx, SEC_DESC_REVISION, &adm_sid, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Init_dns_dom_info.
+***************************************************************************/
+
+static void init_dns_dom_info(LSA_DNS_DOM_INFO *r_l, const char *nb_name,
+                             const char *dns_name, const char *forest_name,
+                             GUID *dom_guid, DOM_SID *dom_sid)
+{
+       if (nb_name && *nb_name) {
+               init_uni_hdr(&r_l->hdr_nb_dom_name, strlen(nb_name));
+               init_unistr2(&r_l->uni_nb_dom_name, nb_name, 
+                            strlen(nb_name));
+               r_l->hdr_nb_dom_name.uni_max_len += 2;
+               r_l->uni_nb_dom_name.uni_max_len += 1;
+       }
+       
+       if (dns_name && *dns_name) {
+               init_uni_hdr(&r_l->hdr_dns_dom_name, strlen(dns_name));
+               init_unistr2(&r_l->uni_dns_dom_name, dns_name,
+                            strlen(dns_name));
+               r_l->hdr_dns_dom_name.uni_max_len += 2;
+               r_l->uni_dns_dom_name.uni_max_len += 1;
+       }
+
+       if (forest_name && *forest_name) {
+               init_uni_hdr(&r_l->hdr_forest_name, strlen(forest_name));
+               init_unistr2(&r_l->uni_forest_name, forest_name,
+                            strlen(forest_name));
+               r_l->hdr_forest_name.uni_max_len += 2;
+               r_l->uni_forest_name.uni_max_len += 1;
+       }
+
+       /* how do we init the guid ? probably should write an init fn */
+       if (dom_guid) {
+               memcpy(&r_l->dom_guid, dom_guid, sizeof(GUID));
+       }
+       
+       if (dom_sid) {
+               r_l->ptr_dom_sid = 1;
+               init_dom_sid2(&r_l->dom_sid, dom_sid);
+       }
+}
+
+/***************************************************************************
+ _lsa_open_policy2.
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy2(pipes_struct *p, LSA_Q_OPEN_POL2 *q_u, LSA_R_OPEN_POL2 *r_u)
+{
+       struct lsa_info *info;
+       SEC_DESC *psd = NULL;
+       size_t sd_size;
+       uint32 des_access=q_u->des_access;
+       uint32 acc_granted;
+       NTSTATUS status;
+
+
+       /* map the generic bits to the lsa policy ones */
+       se_map_generic(&des_access, &lsa_generic_mapping);
+
+       /* get the generic lsa policy SD until we store it */
+       lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+       if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status))
+               return status;
+
+       /* associate the domain SID with the (unique) handle. */
+       if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(info);
+       sid_copy(&info->sid,get_global_sam_sid());
+       info->access = acc_granted;
+
+       /* set up the LSA QUERY INFO response */
+       if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_open_policy
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy(pipes_struct *p, LSA_Q_OPEN_POL *q_u, LSA_R_OPEN_POL *r_u)
+{
+       struct lsa_info *info;
+       SEC_DESC *psd = NULL;
+       size_t sd_size;
+       uint32 des_access=q_u->des_access;
+       uint32 acc_granted;
+       NTSTATUS status;
+
+
+       /* map the generic bits to the lsa policy ones */
+       se_map_generic(&des_access, &lsa_generic_mapping);
+
+       /* get the generic lsa policy SD until we store it */
+       lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+       if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status))
+               return status;
+
+       /* associate the domain SID with the (unique) handle. */
+       if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(info);
+       sid_copy(&info->sid,get_global_sam_sid());
+       info->access = acc_granted;
+
+       /* set up the LSA QUERY INFO response */
+       if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_enum_trust_dom - this needs fixing to do more than return NULL ! JRA.
+ ufff, done :)  mimir
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_trust_dom(pipes_struct *p, LSA_Q_ENUM_TRUST_DOM *q_u, LSA_R_ENUM_TRUST_DOM *r_u)
+{
+       struct lsa_info *info;
+       uint32 enum_context = q_u->enum_context;
+
+       /*
+        * preferred length is set to 5 as a "our" preferred length
+        * nt sets this parameter to 2
+        * update (20.08.2002): it's not preferred length, but preferred size!
+        * it needs further investigation how to optimally choose this value
+        */
+       uint32 max_num_domains = q_u->preferred_len < 5 ? q_u->preferred_len : 10;
+       TRUSTDOM **trust_doms;
+       uint32 num_domains;
+       NTSTATUS nt_status;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+       if (!(info->access & POLICY_VIEW_LOCAL_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+       nt_status = secrets_get_trusted_domains(p->mem_ctx, &enum_context, max_num_domains, &num_domains, &trust_doms);
+
+       if (!NT_STATUS_IS_OK(nt_status) &&
+           !NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES) &&
+           !NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
+               return nt_status;
+       } else {
+               r_u->status = nt_status;
+       }
+
+       /* set up the lsa_enum_trust_dom response */
+       init_r_enum_trust_dom(p->mem_ctx, r_u, enum_context, max_num_domains, num_domains, trust_doms);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_query_info. See the POLICY_INFOMATION_CLASS docs at msdn.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_info(pipes_struct *p, LSA_Q_QUERY_INFO *q_u, LSA_R_QUERY_INFO *r_u)
+{
+       struct lsa_info *handle;
+       LSA_INFO_UNION *info = &r_u->dom;
+       DOM_SID domain_sid;
+       const char *name;
+       DOM_SID *sid = NULL;
+
+       r_u->status = NT_STATUS_OK;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       switch (q_u->info_class) {
+       case 0x02:
+               {
+               unsigned int i;
+               /* check if the user have enough rights */
+               if (!(handle->access & POLICY_VIEW_AUDIT_INFORMATION))
+                       return NT_STATUS_ACCESS_DENIED;
+
+               /* fake info: We audit everything. ;) */
+               info->id2.auditing_enabled = 1;
+               info->id2.count1 = 7;
+               info->id2.count2 = 7;
+               if ((info->id2.auditsettings = (uint32 *)talloc(p->mem_ctx,7*sizeof(uint32))) == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               for (i = 0; i < 7; i++)
+                       info->id2.auditsettings[i] = 3;
+               break;
+               }
+       case 0x03:
+               /* check if the user have enough rights */
+               if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+                       return NT_STATUS_ACCESS_DENIED;
+
+               /* Request PolicyPrimaryDomainInformation. */
+               switch (lp_server_role()) {
+                       case ROLE_DOMAIN_PDC:
+                       case ROLE_DOMAIN_BDC:
+                               name = lp_workgroup();
+                               sid = get_global_sam_sid();
+                               break;
+                       case ROLE_DOMAIN_MEMBER:
+                               name = lp_workgroup();
+                               /* We need to return the Domain SID here. */
+                               if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid))
+                                       sid = &domain_sid;
+                               else
+                                       return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+                               break;
+                       case ROLE_STANDALONE:
+                               name = lp_workgroup();
+                               sid = NULL;
+                               break;
+                       default:
+                               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+               init_dom_query(&r_u->dom.id3, name, sid);
+               break;
+       case 0x05:
+               /* check if the user have enough rights */
+               if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+                       return NT_STATUS_ACCESS_DENIED;
+
+               /* Request PolicyAccountDomainInformation. */
+               switch (lp_server_role()) {
+                       case ROLE_DOMAIN_PDC:
+                       case ROLE_DOMAIN_BDC:
+                               name = lp_workgroup();
+                               sid = get_global_sam_sid();
+                               break;
+                       case ROLE_DOMAIN_MEMBER:
+                               name = lp_netbios_name();
+                               sid = get_global_sam_sid();
+                               break;
+                       case ROLE_STANDALONE:
+                               name = lp_netbios_name();
+                               sid = get_global_sam_sid();
+                               break;
+                       default:
+                               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+               init_dom_query(&r_u->dom.id5, name, sid);
+               break;
+       case 0x06:
+               /* check if the user have enough rights */
+               if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+                       return NT_STATUS_ACCESS_DENIED;
+
+               switch (lp_server_role()) {
+                       case ROLE_DOMAIN_BDC:
+                               /*
+                                * only a BDC is a backup controller
+                                * of the domain, it controls.
+                                */
+                               info->id6.server_role = 2;
+                               break;
+                       default:
+                               /*
+                                * any other role is a primary
+                                * of the domain, it controls.
+                                */
+                               info->id6.server_role = 3;
+                               break; 
+               }
+               break;
+       default:
+               DEBUG(0,("_lsa_query_info: unknown info level in Lsa Query: %d\n", q_u->info_class));
+               r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+               break;
+       }
+
+       if (NT_STATUS_IS_OK(r_u->status)) {
+               r_u->undoc_buffer = 0x22000000; /* bizarre */
+               r_u->info_class = q_u->info_class;
+       }
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_lookup_sids
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_sids(pipes_struct *p, LSA_Q_LOOKUP_SIDS *q_u, LSA_R_LOOKUP_SIDS *r_u)
+{
+       struct lsa_info *handle;
+       DOM_SID2 *sid = q_u->sids.sid;
+       int num_entries = q_u->sids.num_entries;
+       DOM_R_REF *ref = NULL;
+       LSA_TRANS_NAME_ENUM *names = NULL;
+       uint32 mapped_count = 0;
+
+       ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+       names = (LSA_TRANS_NAME_ENUM *)talloc_zero(p->mem_ctx, sizeof(LSA_TRANS_NAME_ENUM));
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+               r_u->status = NT_STATUS_INVALID_HANDLE;
+               goto done;
+       }
+
+       /* check if the user have enough rights */
+       if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+       if (!ref || !names)
+               return NT_STATUS_NO_MEMORY;
+
+done:
+
+       /* set up the LSA Lookup SIDs response */
+       init_lsa_trans_names(p->mem_ctx, ref, names, num_entries, sid, &mapped_count);
+       init_reply_lookup_sids(r_u, ref, names, mapped_count);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+lsa_reply_lookup_names
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_names(pipes_struct *p,LSA_Q_LOOKUP_NAMES *q_u, LSA_R_LOOKUP_NAMES *r_u)
+{
+       struct lsa_info *handle;
+       UNISTR2 *names = q_u->uni_name;
+       int num_entries = q_u->num_entries;
+       DOM_R_REF *ref;
+       DOM_RID2 *rids;
+       uint32 mapped_count = 0;
+
+       if (num_entries >  MAX_LOOKUP_SIDS) {
+               num_entries = MAX_LOOKUP_SIDS;
+               DEBUG(5,("_lsa_lookup_names: truncating name lookup list to %d\n", num_entries));
+       }
+               
+       ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+       rids = (DOM_RID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_RID2)*num_entries);
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+               r_u->status = NT_STATUS_INVALID_HANDLE;
+               goto done;
+       }
+
+       /* check if the user have enough rights */
+       if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
+       if (!ref || !rids)
+               return NT_STATUS_NO_MEMORY;
+
+done:
+
+       /* set up the LSA Lookup RIDs response */
+       init_lsa_rid2s(ref, rids, num_entries, names, &mapped_count, p->endian);
+       init_reply_lookup_names(r_u, ref, num_entries, rids, mapped_count);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_close. Also weird - needs to check if lsa handle is correct. JRA.
+ ***************************************************************************/
+
+NTSTATUS _lsa_close(pipes_struct *p, LSA_Q_CLOSE *q_u, LSA_R_CLOSE *r_u)
+{
+       if (!find_policy_by_hnd(p, &q_u->pol, NULL))
+               return NT_STATUS_INVALID_HANDLE;
+
+       close_policy_hnd(p, &q_u->pol);
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+  "No more secrets Marty...." :-).
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_secret(pipes_struct *p, LSA_Q_OPEN_SECRET *q_u, LSA_R_OPEN_SECRET *r_u)
+{
+       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/***************************************************************************
+_lsa_enum_privs.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privs(pipes_struct *p, LSA_Q_ENUM_PRIVS *q_u, LSA_R_ENUM_PRIVS *r_u)
+{
+       struct lsa_info *handle;
+       uint32 i;
+
+       uint32 enum_context=q_u->enum_context;
+       LSA_PRIV_ENTRY *entry;
+       LSA_PRIV_ENTRY *entries=NULL;
+
+       if (enum_context >= PRIV_ALL_INDEX)
+               return NT_STATUS_NO_MORE_ENTRIES;
+
+       entries = (LSA_PRIV_ENTRY *)talloc_zero(p->mem_ctx, sizeof(LSA_PRIV_ENTRY) * (PRIV_ALL_INDEX));
+       if (entries==NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+
+       /*
+        * I don't know if it's the right one. not documented.
+        */
+       if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+       entry = entries;
+       
+       DEBUG(10,("_lsa_enum_privs: enum_context:%d total entries:%d\n", enum_context, PRIV_ALL_INDEX));
+
+       for (i = 0; i < PRIV_ALL_INDEX; i++, entry++) {
+               if( i<enum_context) {
+                       init_uni_hdr(&entry->hdr_name, 0);
+                       init_unistr2(&entry->name, NULL, 0 );
+                       entry->luid_low = 0;
+                       entry->luid_high = 0;
+               } else {
+                       init_uni_hdr(&entry->hdr_name, strlen(privs[i+1].priv));
+                       init_unistr2(&entry->name, privs[i+1].priv, strlen(privs[i+1].priv) );
+                       entry->luid_low = privs[i+1].se_priv;
+                       entry->luid_high = 0;
+               }
+       }
+
+       enum_context = PRIV_ALL_INDEX;
+       init_lsa_r_enum_privs(r_u, enum_context, PRIV_ALL_INDEX, entries);
+
+       return NT_STATUS_OK;
+}
+
+/***************************************************************************
+_lsa_priv_get_dispname.
+ ***************************************************************************/
+
+NTSTATUS _lsa_priv_get_dispname(pipes_struct *p, LSA_Q_PRIV_GET_DISPNAME *q_u, LSA_R_PRIV_GET_DISPNAME *r_u)
+{
+       struct lsa_info *handle;
+       fstring name_asc;
+       int i=1;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+
+       /*
+        * I don't know if it's the right one. not documented.
+        */
+       if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+       unistr2_to_ascii(name_asc, &q_u->name, sizeof(name_asc));
+
+       DEBUG(10,("_lsa_priv_get_dispname: %s", name_asc));
+
+       while (privs[i].se_priv!=SE_PRIV_ALL && strcmp(name_asc, privs[i].priv))
+               i++;
+       
+       if (privs[i].se_priv!=SE_PRIV_ALL) {
+               DEBUG(10,(": %s\n", privs[i].description));
+               init_uni_hdr(&r_u->hdr_desc, strlen(privs[i].description));
+               init_unistr2(&r_u->desc, privs[i].description, strlen(privs[i].description) );
+
+               r_u->ptr_info=0xdeadbeef;
+               r_u->lang_id=q_u->lang_id;
+               return NT_STATUS_OK;
+       } else {
+               DEBUG(10,("_lsa_priv_get_dispname: doesn't exist\n"));
+               r_u->ptr_info=0;
+               return NT_STATUS_NO_SUCH_PRIVILEGE;
+       }
+}
+
+/***************************************************************************
+_lsa_enum_accounts.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_accounts(pipes_struct *p, LSA_Q_ENUM_ACCOUNTS *q_u, LSA_R_ENUM_ACCOUNTS *r_u)
+{
+       struct lsa_info *handle;
+       GROUP_MAP *map=NULL;
+       int num_entries=0;
+       LSA_SID_ENUM *sids=&r_u->sids;
+       int i=0,j=0;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+
+       /*
+        * I don't know if it's the right one. not documented.
+        */
+       if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* get the list of mapped groups (domain, local, builtin) */
+       if(!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_OK;
+
+       if (q_u->enum_context >= num_entries)
+               return NT_STATUS_NO_MORE_ENTRIES;
+
+       sids->ptr_sid = (uint32 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(uint32));
+       sids->sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(DOM_SID2));
+
+       if (sids->ptr_sid==NULL || sids->sid==NULL) {
+               SAFE_FREE(map);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=q_u->enum_context, j=0; i<num_entries; i++) {
+               init_dom_sid2( &(*sids).sid[j],  &map[i].sid);
+               (*sids).ptr_sid[j]=1;
+               j++;
+       }
+
+       SAFE_FREE(map);
+
+       init_lsa_r_enum_accounts(r_u, j);
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS _lsa_unk_get_connuser(pipes_struct *p, LSA_Q_UNK_GET_CONNUSER *q_u, LSA_R_UNK_GET_CONNUSER *r_u)
+{
+       fstring username, domname;
+       int ulen, dlen;
+       user_struct *vuser = get_valid_user_struct(p->vuid);
+  
+       if (vuser == NULL)
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+  
+       fstrcpy(username, vuser->user.smb_name);
+       fstrcpy(domname, vuser->user.domain);
+  
+       ulen = strlen(username) + 1;
+       dlen = strlen(domname) + 1;
+  
+       init_uni_hdr(&r_u->hdr_user_name, ulen);
+       r_u->ptr_user_name = 1;
+       init_unistr2(&r_u->uni2_user_name, username, ulen);
+
+       r_u->unk1 = 1;
+  
+       init_uni_hdr(&r_u->hdr_dom_name, dlen);
+       r_u->ptr_dom_name = 1;
+       init_unistr2(&r_u->uni2_dom_name, domname, dlen);
+
+       r_u->status = NT_STATUS_OK;
+  
+       return r_u->status;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_account(pipes_struct *p, LSA_Q_OPENACCOUNT *q_u, LSA_R_OPENACCOUNT *r_u)
+{
+       struct lsa_info *handle;
+       struct lsa_info *info;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+
+       /*
+        * I don't know if it's the right one. not documented.
+        * but guessed with rpcclient.
+        */
+       if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* associate the user/group SID with the (unique) handle. */
+       if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(info);
+       info->sid = q_u->sid.sid;
+       info->access = q_u->access;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, enumerate all the privilege this account has.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privsaccount(pipes_struct *p, LSA_Q_ENUMPRIVSACCOUNT *q_u, LSA_R_ENUMPRIVSACCOUNT *r_u)
+{
+       struct lsa_info *info=NULL;
+       GROUP_MAP map;
+       int i=0;
+
+       LUID_ATTR *set=NULL;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!pdb_getgrsid(&map, info->sid, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       DEBUG(10,("_lsa_enum_privsaccount: %d privileges\n", map.priv_set.count));
+       if (map.priv_set.count!=0) {
+       
+               set=(LUID_ATTR *)talloc(p->mem_ctx, map.priv_set.count*sizeof(LUID_ATTR));
+               if (set == NULL) {
+                       free_privilege(&map.priv_set);  
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               for (i=0; i<map.priv_set.count; i++) {
+                       set[i].luid.low=map.priv_set.set[i].luid.low;
+                       set[i].luid.high=map.priv_set.set[i].luid.high;
+                       set[i].attr=map.priv_set.set[i].attr;
+                       DEBUG(10,("_lsa_enum_privsaccount: priv %d: %d:%d:%d\n", i, 
+                                  set[i].luid.high, set[i].luid.low, set[i].attr));
+               }
+       }
+
+       init_lsa_r_enum_privsaccount(r_u, set, map.priv_set.count, 0);  
+       free_privilege(&map.priv_set);  
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_getsystemaccount(pipes_struct *p, LSA_Q_GETSYSTEMACCOUNT *q_u, LSA_R_GETSYSTEMACCOUNT *r_u)
+{
+       struct lsa_info *info=NULL;
+       GROUP_MAP map;
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!pdb_getgrsid(&map, info->sid, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       /*
+         0x01 -> Log on locally
+         0x02 -> Access this computer from network
+         0x04 -> Log on as a batch job
+         0x10 -> Log on as a service
+         
+         they can be ORed together
+       */
+
+       r_u->access=map.systemaccount;
+
+       return r_u->status;
+}
+
+/***************************************************************************
+  update the systemaccount information
+ ***************************************************************************/
+
+NTSTATUS _lsa_setsystemaccount(pipes_struct *p, LSA_Q_SETSYSTEMACCOUNT *q_u, LSA_R_SETSYSTEMACCOUNT *r_u)
+{
+       struct lsa_info *info=NULL;
+       GROUP_MAP map;
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!pdb_getgrsid(&map, info->sid, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       map.systemaccount=q_u->access;
+
+       if(!pdb_update_group_mapping_entry(&map))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       free_privilege(&map.priv_set);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, add some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_addprivs(pipes_struct *p, LSA_Q_ADDPRIVS *q_u, LSA_R_ADDPRIVS *r_u)
+{
+       struct lsa_info *info=NULL;
+       GROUP_MAP map;
+       int i=0;
+
+       LUID_ATTR *luid_attr=NULL;
+       PRIVILEGE_SET *set=NULL;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!pdb_getgrsid(&map, info->sid, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       set=&q_u->set;
+
+       for (i=0; i<set->count; i++) {
+               luid_attr=&set->set[i];
+               
+               /* check if the privilege is already there */
+               if (check_priv_in_privilege(&map.priv_set, *luid_attr)){
+                       free_privilege(&map.priv_set);
+                       return NT_STATUS_NO_SUCH_PRIVILEGE;
+               }
+               
+               add_privilege(&map.priv_set, *luid_attr);
+       }
+
+       if(!pdb_update_group_mapping_entry(&map))
+               return NT_STATUS_NO_SUCH_GROUP;
+       
+       free_privilege(&map.priv_set);  
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_removeprivs(pipes_struct *p, LSA_Q_REMOVEPRIVS *q_u, LSA_R_REMOVEPRIVS *r_u)
+{
+       struct lsa_info *info=NULL;
+       GROUP_MAP map;
+       int i=0;
+
+       LUID_ATTR *luid_attr=NULL;
+       PRIVILEGE_SET *set=NULL;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!pdb_getgrsid(&map, info->sid, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       if (q_u->allrights!=0) {
+               /* log it and return, until I see one myself don't do anything */
+               DEBUG(5,("_lsa_removeprivs: trying to remove all privileges ?\n"));
+               return NT_STATUS_OK;
+       }
+
+       if (q_u->ptr==0) {
+               /* log it and return, until I see one myself don't do anything */
+               DEBUG(5,("_lsa_removeprivs: no privileges to remove ?\n"));
+               return NT_STATUS_OK;
+       }
+
+       set=&q_u->set;
+
+       for (i=0; i<set->count; i++) {
+               luid_attr=&set->set[i];
+               
+               /* if we don't have the privilege, we're trying to remove, give up */
+               /* what else can we do ??? JFM. */
+               if (!check_priv_in_privilege(&map.priv_set, *luid_attr)){
+                       free_privilege(&map.priv_set);
+                       return NT_STATUS_NO_SUCH_PRIVILEGE;
+               }
+               
+               remove_privilege(&map.priv_set, *luid_attr);
+       }
+
+       if(!pdb_update_group_mapping_entry(&map))
+               return NT_STATUS_NO_SUCH_GROUP;
+       
+       free_privilege(&map.priv_set);  
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_secobj(pipes_struct *p, LSA_Q_QUERY_SEC_OBJ *q_u, LSA_R_QUERY_SEC_OBJ *r_u)
+{
+       struct lsa_info *handle=NULL;
+       SEC_DESC *psd = NULL;
+       size_t sd_size;
+       NTSTATUS status;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       /* check if the user have enough rights */
+       if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+               return NT_STATUS_ACCESS_DENIED;
+
+
+       switch (q_u->sec_info) {
+       case 1:
+               /* SD contains only the owner */
+
+               status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+               if(!NT_STATUS_IS_OK(status))
+                       return NT_STATUS_NO_MEMORY;
+
+
+               if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               break;
+       case 4:
+               /* SD contains only the ACL */
+
+               status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+               if(!NT_STATUS_IS_OK(status))
+                       return NT_STATUS_NO_MEMORY;
+
+               if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               break;
+       default:
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       r_u->ptr=1;
+
+       return r_u->status;
+}
+
+
+NTSTATUS _lsa_query_info2(pipes_struct *p, LSA_Q_QUERY_INFO2 *q_u, LSA_R_QUERY_INFO2 *r_u)
+{
+       struct lsa_info *handle;
+       const char *nb_name;
+       char *dns_name = NULL;
+       char *forest_name = NULL;
+       DOM_SID *sid = NULL;
+       GUID guid;
+
+       ZERO_STRUCT(guid);
+       r_u->status = NT_STATUS_OK;
+
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+               return NT_STATUS_INVALID_HANDLE;
+
+       switch (q_u->info_class) {
+       case 0x0c:
+               /* check if the user have enough rights */
+               if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+                       return NT_STATUS_ACCESS_DENIED;
+
+               /* Request PolicyPrimaryDomainInformation. */
+               switch (lp_server_role()) {
+                       case ROLE_DOMAIN_PDC:
+                       case ROLE_DOMAIN_BDC:
+                               nb_name = lp_workgroup();
+                               /* ugly temp hack for these next two */
+                               dns_name = lp_realm();
+                               forest_name = lp_realm();
+                               sid = get_global_sam_sid();
+                               secrets_fetch_domain_guid(lp_workgroup(), &guid);
+                               break;
+                       default:
+                               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+               }
+               init_dns_dom_info(&r_u->info.dns_dom_info, nb_name, dns_name, 
+                                 forest_name,&guid,sid);
+               break;
+       default:
+               DEBUG(0,("_lsa_query_info2: unknown info level in Lsa Query: %d\n", q_u->info_class));
+               r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+               break;
+       }
+
+       if (NT_STATUS_IS_OK(r_u->status)) {
+               r_u->ptr = 0x1;
+               r_u->info_class = q_u->info_class;
+       }
+
+       return r_u->status;
+}
+
+
+/***************************************************************************
+ For a given SID, enumerate all the privilege this account has.
+ ***************************************************************************/
+NTSTATUS _lsa_enum_acct_rights(pipes_struct *p, LSA_Q_ENUM_ACCT_RIGHTS *q_u, LSA_R_ENUM_ACCT_RIGHTS *r_u)
+{
+       struct lsa_info *info=NULL;
+       char **rights = NULL;
+       int num_rights = 0;
+       int i;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       r_u->status = privilege_enum_account_rights(&q_u->sid.sid, &num_rights, &rights);
+
+       init_r_enum_acct_rights(r_u, num_rights, (const char **)rights);
+
+       for (i=0;i<num_rights;i++) {
+               free(rights[i]);
+       }
+       safe_free(rights);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+return a list of SIDs for a particular privilege
+ ***************************************************************************/
+NTSTATUS _lsa_enum_acct_with_right(pipes_struct *p, 
+                                  LSA_Q_ENUM_ACCT_WITH_RIGHT *q_u, 
+                                  LSA_R_ENUM_ACCT_WITH_RIGHT *r_u)
+{
+       struct lsa_info *info=NULL;
+       char *right;
+       DOM_SID *sids = NULL;
+       uint32 count = 0;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       right = unistr2_tdup(p->mem_ctx, &q_u->right);
+
+       DEBUG(5,("lsa_enum_acct_with_right on right %s\n", right));
+
+       r_u->status = privilege_enum_account_with_right(right, &count, &sids);
+
+       init_r_enum_acct_with_right(r_u, count, sids);
+
+       safe_free(sids);
+
+       return r_u->status;
+}
+
+/***************************************************************************
+ add privileges to a acct by SID
+ ***************************************************************************/
+NTSTATUS _lsa_add_acct_rights(pipes_struct *p, LSA_Q_ADD_ACCT_RIGHTS *q_u, LSA_R_ADD_ACCT_RIGHTS *r_u)
+{
+       struct lsa_info *info=NULL;
+       int i;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       DEBUG(5,("_lsa_add_acct_rights to %s (%d rights)\n", 
+                sid_string_static(&q_u->sid.sid), q_u->rights.count));
+
+       for (i=0;i<q_u->rights.count;i++) {
+               DEBUG(5,("\t%s\n", unistr2_static(&q_u->rights.strings[i].string)));
+       }
+
+
+       for (i=0;i<q_u->rights.count;i++) {
+               r_u->status = privilege_add_account_right(unistr2_static(&q_u->rights.strings[i].string),
+                                                         &q_u->sid.sid);
+               if (!NT_STATUS_IS_OK(r_u->status)) {
+                       DEBUG(2,("Failed to add right '%s'\n", 
+                                unistr2_static(&q_u->rights.strings[i].string)));
+                       break;
+               }
+       }
+
+       init_r_add_acct_rights(r_u);
+
+       return r_u->status;
+}
+
+
+/***************************************************************************
+ remove privileges from a acct by SID
+ ***************************************************************************/
+NTSTATUS _lsa_remove_acct_rights(pipes_struct *p, LSA_Q_REMOVE_ACCT_RIGHTS *q_u, LSA_R_REMOVE_ACCT_RIGHTS *r_u)
+{
+       struct lsa_info *info=NULL;
+       int i;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+
+       DEBUG(5,("_lsa_remove_acct_rights from %s all=%d (%d rights)\n", 
+                sid_string_static(&q_u->sid.sid),
+                q_u->removeall,
+                q_u->rights.count));
+
+       for (i=0;i<q_u->rights.count;i++) {
+               DEBUG(5,("\t%s\n", unistr2_static(&q_u->rights.strings[i].string)));
+       }
+
+       for (i=0;i<q_u->rights.count;i++) {
+               r_u->status = privilege_remove_account_right(unistr2_static(&q_u->rights.strings[i].string),
+                                                            &q_u->sid.sid);
+               if (!NT_STATUS_IS_OK(r_u->status)) {
+                       DEBUG(2,("Failed to remove right '%s'\n", 
+                                unistr2_static(&q_u->rights.strings[i].string)));
+                       break;
+               }
+       }
+
+       init_r_remove_acct_rights(r_u);
+
+       return r_u->status;
+}
diff --git a/source4/rpc_server/srv_netlog.c b/source4/rpc_server/srv_netlog.c
new file mode 100644 (file)
index 0000000..c9e4fc1
--- /dev/null
@@ -0,0 +1,345 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Jeremy Allison               1998-2001,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *
+ *  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.
+ */
+
+/* This is the interface to the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ api_net_req_chal:
+ *************************************************************************/
+
+static BOOL api_net_req_chal(pipes_struct *p)
+{
+       NET_Q_REQ_CHAL q_u;
+       NET_R_REQ_CHAL r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the challenge... */
+       if(!net_io_q_req_chal("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_req_chal: Failed to unmarshall NET_Q_REQ_CHAL.\n"));
+               return False;
+       }
+
+       r_u.status = _net_req_chal(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_req_chal("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_req_chal: Failed to marshall NET_R_REQ_CHAL.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_auth:
+ *************************************************************************/
+
+static BOOL api_net_auth(pipes_struct *p)
+{
+       NET_Q_AUTH q_u;
+       NET_R_AUTH r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the challenge... */
+       if(!net_io_q_auth("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_auth: Failed to unmarshall NET_Q_AUTH.\n"));
+               return False;
+       }
+
+       r_u.status = _net_auth(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_auth("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_auth: Failed to marshall NET_R_AUTH.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_auth_2:
+ *************************************************************************/
+
+static BOOL api_net_auth_2(pipes_struct *p)
+{
+       NET_Q_AUTH_2 q_u;
+       NET_R_AUTH_2 r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the challenge... */
+       if(!net_io_q_auth_2("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_auth_2: Failed to unmarshall NET_Q_AUTH_2.\n"));
+               return False;
+       }
+
+       r_u.status = _net_auth_2(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_auth_2("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_auth_2: Failed to marshall NET_R_AUTH_2.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_srv_pwset:
+ *************************************************************************/
+
+static BOOL api_net_srv_pwset(pipes_struct *p)
+{
+       NET_Q_SRV_PWSET q_u;
+       NET_R_SRV_PWSET r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the challenge and encrypted password ... */
+       if(!net_io_q_srv_pwset("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_srv_pwset: Failed to unmarshall NET_Q_SRV_PWSET.\n"));
+               return False;
+       }
+
+       r_u.status = _net_srv_pwset(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_srv_pwset("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_srv_pwset: Failed to marshall NET_R_SRV_PWSET.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_sam_logoff:
+ *************************************************************************/
+
+static BOOL api_net_sam_logoff(pipes_struct *p)
+{
+       NET_Q_SAM_LOGOFF q_u;
+       NET_R_SAM_LOGOFF r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!net_io_q_sam_logoff("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_sam_logoff: Failed to unmarshall NET_Q_SAM_LOGOFF.\n"));
+               return False;
+       }
+
+       r_u.status = _net_sam_logoff(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_sam_logoff("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_sam_logoff: Failed to marshall NET_R_SAM_LOGOFF.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_sam_logon:
+ *************************************************************************/
+
+static BOOL api_net_sam_logon(pipes_struct *p)
+{
+       NET_Q_SAM_LOGON q_u;
+       NET_R_SAM_LOGON r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+    
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+    if(!net_io_q_sam_logon("", &q_u, data, 0)) {
+        DEBUG(0, ("api_net_sam_logon: Failed to unmarshall NET_Q_SAM_LOGON.\n"));
+        return False;
+    }
+   
+       r_u.status = _net_sam_logon(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_sam_logon("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_net_sam_logon: Failed to marshall NET_R_SAM_LOGON.\n"));
+               return False;
+       }
+
+    return True;
+}
+
+/*************************************************************************
+ api_net_trust_dom_list:
+ *************************************************************************/
+
+static BOOL api_net_trust_dom_list(pipes_struct *p)
+{
+       NET_Q_TRUST_DOM_LIST q_u;
+       NET_R_TRUST_DOM_LIST r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+       /* grab the lsa trusted domain list query... */
+       if(!net_io_q_trust_dom("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_trust_dom_list: Failed to unmarshall NET_Q_TRUST_DOM_LIST.\n"));
+               return False;
+       }
+
+       /* construct reply. */
+       r_u.status = _net_trust_dom_list(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!net_io_r_trust_dom("", &r_u, rdata, 0)) {
+               DEBUG(0,("net_reply_trust_dom_list: Failed to marshall NET_R_TRUST_DOM_LIST.\n"));
+               return False;
+       }
+
+       DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl2:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl2(pipes_struct *p)
+{
+       NET_Q_LOGON_CTRL2 q_u;
+       NET_R_LOGON_CTRL2 r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+       /* grab the lsa netlogon ctrl2 query... */
+       if(!net_io_q_logon_ctrl2("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_logon_ctrl2: Failed to unmarshall NET_Q_LOGON_CTRL2.\n"));
+               return False;
+       }
+
+       r_u.status = _net_logon_ctrl2(p, &q_u, &r_u);
+
+       if(!net_io_r_logon_ctrl2("", &r_u, rdata, 0)) {
+               DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+               return False;
+       }
+
+       DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+       return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl(pipes_struct *p)
+{
+       NET_Q_LOGON_CTRL q_u;
+       NET_R_LOGON_CTRL r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       DEBUG(6,("api_net_logon_ctrl: %d\n", __LINE__));
+
+       /* grab the lsa netlogon ctrl query... */
+       if(!net_io_q_logon_ctrl("", &q_u, data, 0)) {
+               DEBUG(0,("api_net_logon_ctrl: Failed to unmarshall NET_Q_LOGON_CTRL.\n"));
+               return False;
+       }
+
+       r_u.status = _net_logon_ctrl(p, &q_u, &r_u);
+
+       if(!net_io_r_logon_ctrl("", &r_u, rdata, 0)) {
+               DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+               return False;
+       }
+
+       DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+       return True;
+}
+
+/*******************************************************************
+ array of \PIPE\NETLOGON operations
+ ********************************************************************/
+
+#ifdef RPC_NETLOG_DYNAMIC
+int init_module(void)
+#else
+int rpc_net_init(void)
+#endif
+{
+  static struct api_struct api_net_cmds [] =
+    {
+      { "NET_REQCHAL"       , NET_REQCHAL       , api_net_req_chal       }, 
+      { "NET_AUTH"          , NET_AUTH          , api_net_auth           }, 
+      { "NET_AUTH2"         , NET_AUTH2         , api_net_auth_2         }, 
+      { "NET_SRVPWSET"      , NET_SRVPWSET      , api_net_srv_pwset      }, 
+      { "NET_SAMLOGON"      , NET_SAMLOGON      , api_net_sam_logon      }, 
+      { "NET_SAMLOGOFF"     , NET_SAMLOGOFF     , api_net_sam_logoff     }, 
+      { "NET_LOGON_CTRL2"   , NET_LOGON_CTRL2   , api_net_logon_ctrl2    }, 
+      { "NET_TRUST_DOM_LIST", NET_TRUST_DOM_LIST, api_net_trust_dom_list },
+      { "NET_LOGON_CTRL"    , NET_LOGON_CTRL    , api_net_logon_ctrl     }
+    };
+
+  return rpc_pipe_register_commands("NETLOGON", "lsass", api_net_cmds,
+                                   sizeof(api_net_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_netlog_nt.c b/source4/rpc_server/srv_netlog_nt.c
new file mode 100644 (file)
index 0000000..daf3e2a
--- /dev/null
@@ -0,0 +1,743 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  Copyright (C) Jeremy Allison               1998-2001.
+ *  Copyirht  (C) Andrew Bartlett                   2001.
+ *
+ *  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.
+ */
+
+/* This is the implementation of the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ init_net_r_req_chal:
+ *************************************************************************/
+
+static void init_net_r_req_chal(NET_R_REQ_CHAL *r_c,
+                                DOM_CHAL *srv_chal, NTSTATUS status)
+{
+       DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__));
+       memcpy(r_c->srv_chal.data, srv_chal->data, sizeof(srv_chal->data));
+       r_c->status = status;
+}
+
+/*************************************************************************
+ error messages cropping up when using nltest.exe...
+ *************************************************************************/
+
+#define ERROR_NO_SUCH_DOMAIN   0x54b
+#define ERROR_NO_LOGON_SERVERS 0x51f
+
+/*************************************************************************
+ net_reply_logon_ctrl:
+ *************************************************************************/
+
+/* Some flag values reverse engineered from NLTEST.EXE */
+
+#define LOGON_CTRL_IN_SYNC          0x00
+#define LOGON_CTRL_REPL_NEEDED      0x01
+#define LOGON_CTRL_REPL_IN_PROGRESS 0x02
+
+NTSTATUS _net_logon_ctrl(pipes_struct *p, NET_Q_LOGON_CTRL *q_u, 
+                      NET_R_LOGON_CTRL *r_u)
+{
+       uint32 flags = 0x0;
+       uint32 pdc_connection_status = 0x00; /* Maybe a win32 error code? */
+       
+       /* Setup the Logon Control response */
+
+       init_net_r_logon_ctrl(r_u, q_u->query_level, flags, 
+                             pdc_connection_status);
+
+       return r_u->status;
+}
+
+/****************************************************************************
+Send a message to smbd to do a sam synchronisation
+**************************************************************************/
+static void send_sync_message(void)
+{
+        TDB_CONTEXT *tdb;
+
+        tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+                           TDB_DEFAULT, O_RDONLY, 0);
+
+        if (!tdb) {
+                DEBUG(3, ("send_sync_message(): failed to open connections "
+                          "database\n"));
+                return;
+        }
+
+        DEBUG(3, ("sending sam synchronisation message\n"));
+        
+        message_send_all(tdb, MSG_SMB_SAM_SYNC, NULL, 0, False, NULL);
+
+        tdb_close(tdb);
+}
+
+/*************************************************************************
+ net_reply_logon_ctrl2:
+ *************************************************************************/
+
+NTSTATUS _net_logon_ctrl2(pipes_struct *p, NET_Q_LOGON_CTRL2 *q_u, NET_R_LOGON_CTRL2 *r_u)
+{
+        uint32 flags = 0x0;
+        uint32 pdc_connection_status = 0x0;
+        uint32 logon_attempts = 0x0;
+        uint32 tc_status = ERROR_NO_LOGON_SERVERS;
+        const char *trusted_domain = "test_domain";
+
+        DEBUG(0, ("*** net long ctrl2 %d, %d, %d\n",
+                  q_u->function_code, q_u->query_level, q_u->switch_value));
+
+       DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+
+       /* set up the Logon Control2 response */
+       init_net_r_logon_ctrl2(r_u, q_u->query_level,
+                              flags, pdc_connection_status, logon_attempts,
+                              tc_status, trusted_domain);
+
+        if (lp_server_role() == ROLE_DOMAIN_BDC)
+                send_sync_message();
+
+       DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*************************************************************************
+ net_reply_trust_dom_list:
+ *************************************************************************/
+
+NTSTATUS _net_trust_dom_list(pipes_struct *p, NET_Q_TRUST_DOM_LIST *q_u, NET_R_TRUST_DOM_LIST *r_u)
+{
+       const char *trusted_domain = "test_domain";
+       uint32 num_trust_domains = 1;
+
+       DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+       /* set up the Trusted Domain List response */
+       init_r_trust_dom(r_u, num_trust_domains, trusted_domain);
+
+       DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/***********************************************************************************
+ init_net_r_srv_pwset:
+ ***********************************************************************************/
+
+static void init_net_r_srv_pwset(NET_R_SRV_PWSET *r_s,
+                                DOM_CRED *srv_cred, NTSTATUS status)  
+{
+       DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+
+       memcpy(&r_s->srv_cred, srv_cred, sizeof(r_s->srv_cred));
+       r_s->status = status;
+
+       DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+}
+
+/******************************************************************
+ gets a machine password entry.  checks access rights of the host.
+ ******************************************************************/
+
+static BOOL get_md4pw(char *md4pw, char *mach_acct)
+{
+       SAM_ACCOUNT *sampass = NULL;
+       const uint8 *pass;
+       BOOL ret;
+       uint32 acct_ctrl;
+
+#if 0
+    /*
+     * Currently this code is redundent as we already have a filter
+     * by hostname list. What this code really needs to do is to 
+     * get a hosts allowed/hosts denied list from the SAM database
+     * on a per user basis, and make the access decision there.
+     * I will leave this code here for now as a reminder to implement
+     * this at a later date. JRA.
+     */
+
+       if (!allow_access(lp_domain_hostsdeny(), lp_domain_hostsallow(),
+                         client_name(), client_addr()))
+       {
+               DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
+               return False;
+       }
+#endif /* 0 */
+
+       if(!NT_STATUS_IS_OK(pdb_init_sam(&sampass)))
+               return False;
+
+       /* JRA. This is ok as it is only used for generating the challenge. */
+       become_root();
+       ret=pdb_getsampwnam(sampass, mach_acct);
+       unbecome_root();
+       if (ret==False) {
+               DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+               pdb_free_sam(&sampass);
+               return False;
+       }
+
+       acct_ctrl = pdb_get_acct_ctrl(sampass);
+       if (!(acct_ctrl & ACB_DISABLED) &&
+           ((acct_ctrl & ACB_DOMTRUST) ||
+            (acct_ctrl & ACB_WSTRUST) ||
+            (acct_ctrl & ACB_SVRTRUST)) &&
+           ((pass=pdb_get_nt_passwd(sampass)) != NULL)) {
+               memcpy(md4pw, pass, 16);
+               dump_data(5, md4pw, 16);
+               pdb_free_sam(&sampass);
+               return True;
+       }
+       
+       DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+       pdb_free_sam(&sampass);
+       return False;
+
+}
+
+/*************************************************************************
+ _net_req_chal
+ *************************************************************************/
+
+NTSTATUS _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+
+       rpcstr_pull(p->dc.remote_machine,q_u->uni_logon_clnt.buffer,sizeof(fstring),q_u->uni_logon_clnt.uni_str_len*2,0);
+
+       /* create a server challenge for the client */
+       /* Set these to random values. */
+       generate_random_buffer(p->dc.srv_chal.data, 8, False);
+       
+       memcpy(p->dc.srv_cred.challenge.data, p->dc.srv_chal.data, 8);
+
+       memcpy(p->dc.clnt_chal.data          , q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+       memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+       memset((char *)p->dc.sess_key, '\0', sizeof(p->dc.sess_key));
+
+       p->dc.challenge_sent = True;
+       /* set up the LSA REQUEST CHALLENGE response */
+       init_net_r_req_chal(r_u, &p->dc.srv_chal, status);
+       
+       return status;
+}
+
+/*************************************************************************
+ init_net_r_auth:
+ *************************************************************************/
+
+static void init_net_r_auth(NET_R_AUTH *r_a, DOM_CHAL *resp_cred, NTSTATUS status)
+{
+       memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+       r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth
+ *************************************************************************/
+
+NTSTATUS _net_auth(pipes_struct *p, NET_Q_AUTH *q_u, NET_R_AUTH *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       DOM_CHAL srv_cred;
+       UTIME srv_time;
+       fstring mach_acct;
+
+       srv_time.time = 0;
+
+       rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+       if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+
+               /* from client / server challenges and md4 password, generate sess key */
+               cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+                                p->dc.md4pw, p->dc.sess_key);
+               
+               /* check that the client credentials are valid */
+               if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+                       
+                       /* create server challenge for inclusion in the reply */
+                       cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+               
+                       /* copy the received client credentials for use next time */
+                       memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+                       memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+                       
+                       /* Save the machine account name. */
+                       fstrcpy(p->dc.mach_acct, mach_acct);
+               
+                       p->dc.authenticated = True;
+
+               } else {
+                       status = NT_STATUS_ACCESS_DENIED;
+               }
+       } else {
+               status = NT_STATUS_ACCESS_DENIED;
+       }
+       
+       /* set up the LSA AUTH response */
+       init_net_r_auth(r_u, &srv_cred, status);
+
+       return r_u->status;
+}
+
+/*************************************************************************
+ init_net_r_auth_2:
+ *************************************************************************/
+
+static void init_net_r_auth_2(NET_R_AUTH_2 *r_a,
+                              DOM_CHAL *resp_cred, NEG_FLAGS *flgs, NTSTATUS status)
+{
+       memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+       memcpy(&r_a->srv_flgs, flgs, sizeof(r_a->srv_flgs));
+       r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth_2
+ *************************************************************************/
+
+NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       DOM_CHAL srv_cred;
+       UTIME srv_time;
+       NEG_FLAGS srv_flgs;
+       fstring mach_acct;
+
+       srv_time.time = 0;
+
+       rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+       if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+               
+               /* from client / server challenges and md4 password, generate sess key */
+               cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+                                p->dc.md4pw, p->dc.sess_key);
+               
+               /* check that the client credentials are valid */
+               if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+                       
+                       /* create server challenge for inclusion in the reply */
+                       cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+                       
+                       /* copy the received client credentials for use next time */
+                       memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+                       memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+                       
+                       /* Save the machine account name. */
+                       fstrcpy(p->dc.mach_acct, mach_acct);
+                       
+                       p->dc.authenticated = True;
+
+               } else {
+                       status = NT_STATUS_ACCESS_DENIED;
+               }
+       } else {
+               status = NT_STATUS_ACCESS_DENIED;
+       }
+       
+       srv_flgs.neg_flags = 0x000001ff;
+
+       /* set up the LSA AUTH 2 response */
+       init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status);
+
+       return r_u->status;
+}
+
+/*************************************************************************
+ _net_srv_pwset
+ *************************************************************************/
+
+NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *r_u)
+{
+       NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+       DOM_CRED srv_cred;
+       pstring workstation;
+       SAM_ACCOUNT *sampass=NULL;
+       BOOL ret = False;
+       unsigned char pwd[16];
+       int i;
+       uint32 acct_ctrl;
+
+       /* checks and updates credentials.  creates reply credentials */
+       if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->clnt_id.cred, &srv_cred)))
+               return NT_STATUS_INVALID_HANDLE;
+
+       memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+       DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
+
+       rpcstr_pull(workstation,q_u->clnt_id.login.uni_comp_name.buffer,
+                   sizeof(workstation),q_u->clnt_id.login.uni_comp_name.uni_str_len*2,0);
+
+       DEBUG(3,("Server Password Set by Wksta:[%s] on account [%s]\n", workstation, p->dc.mach_acct));
+       
+       pdb_init_sam(&sampass);
+
+       become_root();
+       ret=pdb_getsampwnam(sampass, p->dc.mach_acct);
+       unbecome_root();
+
+       /* Ensure the account exists and is a machine account. */
+       
+       acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+       if (!(ret 
+             && (acct_ctrl & ACB_WSTRUST ||
+                     acct_ctrl & ACB_SVRTRUST ||
+                     acct_ctrl & ACB_DOMTRUST))) {
+               pdb_free_sam(&sampass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+               pdb_free_sam(&sampass);
+               return NT_STATUS_ACCOUNT_DISABLED;
+       }
+
+       DEBUG(100,("Server password set : new given value was :\n"));
+       for(i = 0; i < 16; i++)
+               DEBUG(100,("%02X ", q_u->pwd[i]));
+       DEBUG(100,("\n"));
+
+       cred_hash3( pwd, q_u->pwd, p->dc.sess_key, 0);
+
+       /* lies!  nt and lm passwords are _not_ the same: don't care */
+       if (!pdb_set_lanman_passwd (sampass, pwd, PDB_CHANGED)) {
+               pdb_free_sam(&sampass);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_nt_passwd     (sampass, pwd, PDB_CHANGED)) {
+               pdb_free_sam(&sampass);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pdb_set_pass_changed_now     (sampass)) {
+               pdb_free_sam(&sampass);
+               /* Not quite sure what this one qualifies as, but this will do */
+               return NT_STATUS_UNSUCCESSFUL; 
+       }
+       become_root();
+       ret = pdb_update_sam_account (sampass);
+       unbecome_root();
+       if (ret)
+               status = NT_STATUS_OK;
+
+       /* set up the LSA Server Password Set response */
+       init_net_r_srv_pwset(r_u, &srv_cred, status);
+
+       pdb_free_sam(&sampass);
+       return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logoff:
+ *************************************************************************/
+
+NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u)
+{
+       DOM_CRED srv_cred;
+
+       if (!get_valid_user_struct(p->vuid))
+               return NT_STATUS_NO_SUCH_USER;
+
+       /* checks and updates credentials.  creates reply credentials */
+       if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, 
+                                                    &q_u->sam_id.client.cred, &srv_cred)))
+               return NT_STATUS_INVALID_HANDLE;
+
+       memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+       /* XXXX maybe we want to say 'no', reject the client's credentials */
+       r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+       memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+       r_u->status = NT_STATUS_OK;
+
+       return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logon
+ *************************************************************************/
+
+NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       NET_USER_INFO_3 *usr_info = NULL;
+       NET_ID_INFO_CTR *ctr = q_u->sam_id.ctr;
+       DOM_CRED srv_cred;
+       UNISTR2 *uni_samlogon_user = NULL;
+       UNISTR2 *uni_samlogon_domain = NULL;
+       UNISTR2 *uni_samlogon_workstation = NULL;
+       fstring nt_username, nt_domain, nt_workstation;
+       auth_usersupplied_info *user_info = NULL;
+       auth_serversupplied_info *server_info = NULL;
+       extern userdom_struct current_user_info;
+       SAM_ACCOUNT *sampw;
+               
+       usr_info = (NET_USER_INFO_3 *)talloc(p->mem_ctx, sizeof(NET_USER_INFO_3));
+       if (!usr_info)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(usr_info);
+
+       /* store the user information, if there is any. */
+       r_u->user = usr_info;
+       r_u->switch_value = 0; /* indicates no info */
+       r_u->auth_resp = 1; /* authoritative response */
+       r_u->switch_value = 3; /* indicates type of validation user info */
+       if (!get_valid_user_struct(p->vuid))
+               return NT_STATUS_NO_SUCH_USER;
+    
+       /* checks and updates credentials.  creates reply credentials */
+       if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred)))
+               return NT_STATUS_INVALID_HANDLE;
+
+       memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+    
+       r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+       memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+       /* find the username */
+    
+       switch (q_u->sam_id.logon_level) {
+       case INTERACTIVE_LOGON_TYPE:
+               uni_samlogon_user = &ctr->auth.id1.uni_user_name;
+               uni_samlogon_domain = &ctr->auth.id1.uni_domain_name;
+
+                uni_samlogon_workstation = &ctr->auth.id1.uni_wksta_name;
+            
+               DEBUG(3,("SAM Logon (Interactive). Domain:[%s].  ", lp_workgroup()));
+               break;
+       case NET_LOGON_TYPE:
+               uni_samlogon_user = &ctr->auth.id2.uni_user_name;
+               uni_samlogon_domain = &ctr->auth.id2.uni_domain_name;
+               uni_samlogon_workstation = &ctr->auth.id2.uni_wksta_name;
+            
+               DEBUG(3,("SAM Logon (Network). Domain:[%s].  ", lp_workgroup()));
+               break;
+       default:
+               DEBUG(2,("SAM Logon: unsupported switch value\n"));
+               return NT_STATUS_INVALID_INFO_CLASS;
+       } /* end switch */
+
+       /* check username exists */
+
+       rpcstr_pull(nt_username,uni_samlogon_user->buffer,sizeof(nt_username),uni_samlogon_user->uni_str_len*2,0);
+       rpcstr_pull(nt_domain,uni_samlogon_domain->buffer,sizeof(nt_domain),uni_samlogon_domain->uni_str_len*2,0);
+       rpcstr_pull(nt_workstation,uni_samlogon_workstation->buffer,sizeof(nt_workstation),uni_samlogon_workstation->uni_str_len*2,0);
+
+       DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username, 
+                 nt_workstation, nt_domain));
+       
+       fstrcpy(current_user_info.smb_name, nt_username);
+       sub_set_smb_name(nt_username);
+     
+       /*
+        * Convert to a UNIX username.
+        */
+
+       DEBUG(5,("Attempting validation level %d for unmapped username %s.\n", q_u->sam_id.ctr->switch_value, nt_username));
+
+       switch (ctr->switch_value) {
+       case NET_LOGON_TYPE:
+       {
+               struct auth_context *auth_context = NULL;
+               if (!NT_STATUS_IS_OK(status = make_auth_context_fixed(&auth_context, ctr->auth.id2.lm_chal))) {
+                       return status;
+               }
+
+               /* Standard challenge/response authenticaion */
+               if (!make_user_info_netlogon_network(&user_info, 
+                                                    nt_username, nt_domain, 
+                                                    nt_workstation, 
+                                                    ctr->auth.id2.lm_chal_resp.buffer,
+                                                    ctr->auth.id2.lm_chal_resp.str_str_len,
+                                                    ctr->auth.id2.nt_chal_resp.buffer,
+                                                    ctr->auth.id2.nt_chal_resp.str_str_len)) {
+                       status = NT_STATUS_NO_MEMORY;
+               } else {
+                       status = auth_context->check_ntlm_password(auth_context, user_info, &server_info);
+               }
+               (auth_context->free)(&auth_context);
+                       
+               break;
+       }
+       case INTERACTIVE_LOGON_TYPE:
+               /* 'Interactive' autheticaion, supplies the password in its
+                  MD4 form, encrypted with the session key.  We will
+                  convert this to chellange/responce for the auth
+                  subsystem to chew on */
+       {
+               struct auth_context *auth_context = NULL;
+               const uint8 *chal;
+               if (!NT_STATUS_IS_OK(status = make_auth_context_subsystem(&auth_context))) {
+                       return status;
+               }
+               
+               chal = auth_context->get_ntlm_challenge(auth_context);
+
+               if (!make_user_info_netlogon_interactive(&user_info, 
+                                                        nt_username, nt_domain, 
+                                                        nt_workstation, chal,
+                                                        ctr->auth.id1.lm_owf.data, 
+                                                        ctr->auth.id1.nt_owf.data, 
+                                                        p->dc.sess_key)) {
+                       status = NT_STATUS_NO_MEMORY;
+               } else {
+                       status = auth_context->check_ntlm_password(auth_context, user_info, &server_info);
+               }
+
+               (auth_context->free)(&auth_context);
+
+               break;
+       }
+       default:
+               DEBUG(2,("SAM Logon: unsupported switch value\n"));
+               return NT_STATUS_INVALID_INFO_CLASS;
+       } /* end switch */
+       
+       free_user_info(&user_info);
+       
+       DEBUG(5, ("_net_sam_logon: check_password returned status %s\n", 
+                 nt_errstr(status)));
+
+       /* Check account and password */
+    
+       if (!NT_STATUS_IS_OK(status)) {
+               free_server_info(&server_info);
+               return status;
+       }
+
+       if (server_info->guest) {
+               /* We don't like guest domain logons... */
+               DEBUG(5,("_net_sam_logon: Attempted domain logon as GUEST denied.\n"));
+               free_server_info(&server_info);
+               return NT_STATUS_LOGON_FAILURE;
+       }
+
+       /* This is the point at which, if the login was successful, that
+           the SAM Local Security Authority should record that the user is
+           logged in to the domain.  */
+    
+       {
+               DOM_GID *gids = NULL;
+               const DOM_SID *user_sid = NULL;
+               const DOM_SID *group_sid = NULL;
+               DOM_SID domain_sid;
+               uint32 user_rid, group_rid; 
+
+               int num_gids = 0;
+               pstring my_name;
+               fstring user_sid_string;
+               fstring group_sid_string;
+               uchar user_sess_key[16];
+               uchar netlogon_sess_key[16];
+
+               sampw = server_info->sam_account;
+
+               /* set up pointer indicating user/password failed to be found */
+               usr_info->ptr_user_info = 0;
+
+               user_sid = pdb_get_user_sid(sampw);
+               group_sid = pdb_get_group_sid(sampw);
+
+               sid_copy(&domain_sid, user_sid);
+               sid_split_rid(&domain_sid, &user_rid);
+
+               if (!sid_peek_check_rid(&domain_sid, group_sid, &group_rid)) {
+                       DEBUG(1, ("_net_sam_logon: user %s\\%s has user sid %s\n but group sid %s.\nThe conflicting domain portions are not supported for NETLOGON calls\n",        
+                                 pdb_get_domain(sampw), pdb_get_username(sampw),
+                                 sid_to_string(user_sid_string, user_sid),
+                                 sid_to_string(group_sid_string, group_sid)));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               
+               pstrcpy(my_name, lp_netbios_name());
+
+               if (!NT_STATUS_IS_OK(status 
+                                    = nt_token_to_group_list(p->mem_ctx, 
+                                                             &domain_sid, 
+                                                             server_info->ptok, 
+                                                             &num_gids, 
+                                                             &gids))) {
+                       return status;
+               }
+
+               ZERO_STRUCT(netlogon_sess_key);
+               memcpy(netlogon_sess_key, p->dc.sess_key, 8);
+               memcpy(user_sess_key, server_info->session_key, sizeof(user_sess_key));
+               SamOEMhash(user_sess_key, netlogon_sess_key, 16);
+               ZERO_STRUCT(netlogon_sess_key);
+
+               init_net_user_info3(p->mem_ctx, usr_info, 
+                                   user_rid,
+                                   group_rid,
+                                   
+                                   pdb_get_username(sampw),
+                                   pdb_get_fullname(sampw),
+                                   pdb_get_homedir(sampw),
+                                   pdb_get_dir_drive(sampw),
+                                   pdb_get_logon_script(sampw),
+                                   pdb_get_profile_path(sampw),
+                                   pdb_get_logon_time(sampw),
+                                   get_time_t_max(),
+                                   get_time_t_max(),
+                                   pdb_get_pass_last_set_time(sampw),
+                                   pdb_get_pass_can_change_time(sampw),
+                                   pdb_get_pass_must_change_time(sampw),
+                                   
+                                   0, /* logon_count */
+                                   0, /* bad_pw_count */
+                                   num_gids,    /* uint32 num_groups */
+                                   gids    , /* DOM_GID *gids */
+                                   0x20    , /* uint32 user_flgs (?) */
+                                   user_sess_key,
+                                   my_name     , /* char *logon_srv */
+                                   pdb_get_domain(sampw),
+                                   &domain_sid,     /* DOM_SID *dom_sid */  
+                                   /* Should be users domain sid, not servers - for trusted domains */
+                                 
+                                   NULL); /* char *other_sids */
+               ZERO_STRUCT(user_sess_key);
+       }
+       free_server_info(&server_info);
+       return status;
+}
+
+
diff --git a/source4/rpc_server/srv_pipe.c b/source4/rpc_server/srv_pipe.c
new file mode 100644 (file)
index 0000000..f6deac6
--- /dev/null
@@ -0,0 +1,1386 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1998
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ *  Copyright (C) Paul Ashton                  1997-1998,
+ *  Copyright (C) Jeremy Allison                    1999,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *  
+ *  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.
+ */
+
+/*  this module apparently provides an implementation of DCE/RPC over a
+ *  named pipe (IPC$ connection using SMBtrans).  details of DCE/RPC
+ *  documentation are available (in on-line form) from the X-Open group.
+ *
+ *  this module should provide a level of abstraction between SMB
+ *  and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ *  data copies, and network traffic.
+ *
+ *  in this version, which takes a "let's learn what's going on and
+ *  get something running" approach, there is additional network
+ *  traffic generated, but the code should be easier to understand...
+ *
+ *  ... if you read the docs.  or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static void NTLMSSPcalc_p( pipes_struct *p, unsigned char *data, int len)
+{
+    unsigned char *hash = p->ntlmssp_hash;
+    unsigned char index_i = hash[256];
+    unsigned char index_j = hash[257];
+    int ind;
+
+    for( ind = 0; ind < len; ind++) {
+        unsigned char tc;
+        unsigned char t;
+
+        index_i++;
+        index_j += hash[index_i];
+
+        tc = hash[index_i];
+        hash[index_i] = hash[index_j];
+        hash[index_j] = tc;
+
+        t = hash[index_i] + hash[index_j];
+        data[ind] = data[ind] ^ hash[t];
+    }
+
+    hash[256] = index_i;
+    hash[257] = index_j;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata. 
+ We cheat here as this function doesn't handle the special auth
+ footers of the authenticated bind response reply.
+ ********************************************************************/
+
+BOOL create_next_pdu(pipes_struct *p)
+{
+       RPC_HDR_RESP hdr_resp;
+       BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+       BOOL auth_seal   = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+       uint32 data_len;
+       uint32 data_space_available;
+       uint32 data_len_left;
+       prs_struct outgoing_pdu;
+       uint32 data_pos;
+
+       /*
+        * If we're in the fault state, keep returning fault PDU's until
+        * the pipe gets closed. JRA.
+        */
+
+       if(p->fault_state) {
+               setup_fault_pdu(p, NT_STATUS(0x1c010002));
+               return True;
+       }
+
+       memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+       /* Change the incoming request header to a response. */
+       p->hdr.pkt_type = RPC_RESPONSE;
+
+       /* Set up rpc header flags. */
+       if (p->out_data.data_sent_length == 0)
+               p->hdr.flags = RPC_FLG_FIRST;
+       else
+               p->hdr.flags = 0;
+
+       /*
+        * Work out how much we can fit in a single PDU.
+        */
+
+       data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+       if(p->ntlmssp_auth_validated)
+               data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN);
+
+       /*
+        * The amount we send is the minimum of the available
+        * space and the amount left to send.
+        */
+
+       data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+       /*
+        * Ensure there really is data left to send.
+        */
+
+       if(!data_len_left) {
+               DEBUG(0,("create_next_pdu: no data left to send !\n"));
+               return False;
+       }
+
+       data_len = MIN(data_len_left, data_space_available);
+
+       /*
+        * Set up the alloc hint. This should be the data left to
+        * send.
+        */
+
+       hdr_resp.alloc_hint = data_len_left;
+
+       /*
+        * Set up the header lengths.
+        */
+
+       if (p->ntlmssp_auth_validated) {
+               p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+                                       RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN;
+               p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+       } else {
+               p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len;
+               p->hdr.auth_len = 0;
+       }
+
+       /*
+        * Work out if this PDU will be the last.
+        */
+
+       if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata))
+               p->hdr.flags |= RPC_FLG_LAST;
+
+       /*
+        * Init the parse struct to point at the outgoing
+        * data.
+        */
+
+       prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+       prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+       /* Store the header in the data stream. */
+       if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+               DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR.\n"));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+               DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_RESP.\n"));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       /* Store the current offset. */
+       data_pos = prs_offset(&outgoing_pdu);
+
+       /* Copy the data into the PDU. */
+
+       if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+               DEBUG(0,("create_next_pdu: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       if (p->hdr.auth_len > 0) {
+               uint32 crc32 = 0;
+               char *data;
+
+               DEBUG(5,("create_next_pdu: sign: %s seal: %s data %d auth %d\n",
+                        BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, p->hdr.auth_len));
+
+               /*
+                * Set data to point to where we copied the data into.
+                */
+
+               data = prs_data_p(&outgoing_pdu) + data_pos;
+
+               if (auth_seal) {
+                       crc32 = crc32_calc_buffer(data, data_len);
+                       NTLMSSPcalc_p(p, (uchar*)data, data_len);
+               }
+
+               if (auth_seal || auth_verify) {
+                       RPC_HDR_AUTH auth_info;
+
+                       init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, 
+                                       (auth_verify ? RPC_HDR_AUTH_LEN : 0), (auth_verify ? 1 : 0));
+                       if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+                               DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+                               prs_mem_free(&outgoing_pdu);
+                               return False;
+                       }
+               }
+
+               if (auth_verify) {
+                       RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+                       char *auth_data = prs_data_p(&outgoing_pdu);
+
+                       p->ntlmssp_seq_num++;
+                       init_rpc_auth_ntlmssp_chk(&ntlmssp_chk, NTLMSSP_SIGN_VERSION,
+                                       crc32, p->ntlmssp_seq_num++);
+                       auth_data = prs_data_p(&outgoing_pdu) + prs_offset(&outgoing_pdu) + 4;
+                       if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, &outgoing_pdu, 0)) {
+                               DEBUG(0,("create_next_pdu: failed to marshall RPC_AUTH_NTLMSSP_CHK.\n"));
+                               prs_mem_free(&outgoing_pdu);
+                               return False;
+                       }
+                       NTLMSSPcalc_p(p, (uchar*)auth_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+               }
+       }
+
+       /*
+        * Setup the counts for this PDU.
+        */
+
+       p->out_data.data_sent_length += data_len;
+       p->out_data.current_pdu_len = p->hdr.frag_len;
+       p->out_data.current_pdu_sent = 0;
+
+       prs_mem_free(&outgoing_pdu);
+       return True;
+}
+
+/*******************************************************************
+ Process an NTLMSSP authentication response.
+ If this function succeeds, the user has been authenticated
+ and their domain, name and calling workstation stored in
+ the pipe struct.
+ The initial challenge is stored in p->challenge.
+ *******************************************************************/
+
+static BOOL api_pipe_ntlmssp_verify(pipes_struct *p, RPC_AUTH_NTLMSSP_RESP *ntlmssp_resp)
+{
+       uchar lm_owf[24];
+       uchar nt_owf[128];
+       int nt_pw_len;
+       int lm_pw_len;
+       fstring user_name;
+       fstring domain;
+       fstring wks;
+
+       NTSTATUS nt_status;
+
+       struct auth_context *auth_context = NULL;
+       auth_usersupplied_info *user_info = NULL;
+       auth_serversupplied_info *server_info = NULL;
+
+       DEBUG(5,("api_pipe_ntlmssp_verify: checking user details\n"));
+
+       memset(p->user_name, '\0', sizeof(p->user_name));
+       memset(p->pipe_user_name, '\0', sizeof(p->pipe_user_name));
+       memset(p->domain, '\0', sizeof(p->domain));
+       memset(p->wks, '\0', sizeof(p->wks));
+
+       /* Set up for non-authenticated user. */
+       delete_nt_token(&p->pipe_user.nt_user_token);
+       p->pipe_user.ngroups = 0;
+       SAFE_FREE( p->pipe_user.groups);
+
+       /* 
+        * Setup an empty password for a guest user.
+        */
+
+       /*
+        * We always negotiate UNICODE.
+        */
+
+       if (p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+               rpcstr_pull(user_name, ntlmssp_resp->user, sizeof(fstring), ntlmssp_resp->hdr_usr.str_str_len*2, 0 );
+               rpcstr_pull(domain, ntlmssp_resp->domain, sizeof(fstring), ntlmssp_resp->hdr_domain.str_str_len*2, 0);
+               rpcstr_pull(wks, ntlmssp_resp->wks, sizeof(fstring), ntlmssp_resp->hdr_wks.str_str_len*2, 0);
+       } else {
+               pull_ascii_fstring(user_name, ntlmssp_resp->user);
+               pull_ascii_fstring(domain, ntlmssp_resp->domain);
+               pull_ascii_fstring(wks, ntlmssp_resp->wks);
+       }
+
+       DEBUG(5,("user: %s domain: %s wks: %s\n", user_name, domain, wks));
+
+       nt_pw_len = MIN(sizeof(nt_owf), ntlmssp_resp->hdr_nt_resp.str_str_len);
+       lm_pw_len = MIN(sizeof(lm_owf), ntlmssp_resp->hdr_lm_resp.str_str_len);
+
+       memcpy(lm_owf, ntlmssp_resp->lm_resp, sizeof(lm_owf));
+       memcpy(nt_owf, ntlmssp_resp->nt_resp, nt_pw_len);
+
+#ifdef DEBUG_PASSWORD
+       DEBUG(100,("lm, nt owfs, chal\n"));
+       dump_data(100, (char *)lm_owf, sizeof(lm_owf));
+       dump_data(100, (char *)nt_owf, nt_pw_len);
+       dump_data(100, (char *)p->challenge, 8);
+#endif
+
+       /*
+        * Allow guest access. Patch from Shirish Kalele <kalele@veritas.com>.
+        */
+
+       if (*user_name) {
+
+               /* 
+                * Do the length checking only if user is not NULL.
+                */
+
+               if (ntlmssp_resp->hdr_lm_resp.str_str_len == 0)
+                       return False;
+               if (ntlmssp_resp->hdr_nt_resp.str_str_len == 0)
+                       return False;
+               if (ntlmssp_resp->hdr_usr.str_str_len == 0)
+                       return False;
+               if (ntlmssp_resp->hdr_domain.str_str_len == 0)
+                       return False;
+               if (ntlmssp_resp->hdr_wks.str_str_len == 0)
+                       return False;
+
+       }
+       
+       make_auth_context_fixed(&auth_context, (uchar*)p->challenge);
+
+       if (!make_user_info_netlogon_network(&user_info, 
+                                            user_name, domain, wks,
+                                            lm_owf, lm_pw_len, 
+                                            nt_owf, nt_pw_len)) {
+               DEBUG(0,("make_user_info_netlogon_network failed!  Failing authenticaion.\n"));
+               return False;
+       }
+       
+       nt_status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); 
+       
+       (auth_context->free)(&auth_context);
+       free_user_info(&user_info);
+       
+       p->ntlmssp_auth_validated = NT_STATUS_IS_OK(nt_status);
+       
+       if (!p->ntlmssp_auth_validated) {
+               DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \
+failed authentication on named pipe %s.\n", domain, user_name, wks, p->name ));
+               free_server_info(&server_info);
+               return False;
+       }
+
+       /*
+        * Set up the sign/seal data.
+        */
+
+       {
+               uchar p24[24];
+               NTLMSSPOWFencrypt(server_info->first_8_lm_hash, lm_owf, p24);
+               {
+                       unsigned char j = 0;
+                       int ind;
+
+                       unsigned char k2[8];
+
+                       memcpy(k2, p24, 5);
+                       k2[5] = 0xe5;
+                       k2[6] = 0x38;
+                       k2[7] = 0xb0;
+
+                       for (ind = 0; ind < 256; ind++)
+                               p->ntlmssp_hash[ind] = (unsigned char)ind;
+
+                       for( ind = 0; ind < 256; ind++) {
+                               unsigned char tc;
+
+                               j += (p->ntlmssp_hash[ind] + k2[ind%8]);
+
+                               tc = p->ntlmssp_hash[ind];
+                               p->ntlmssp_hash[ind] = p->ntlmssp_hash[j];
+                               p->ntlmssp_hash[j] = tc;
+                       }
+
+                       p->ntlmssp_hash[256] = 0;
+                       p->ntlmssp_hash[257] = 0;
+               }
+/*             NTLMSSPhash(p->ntlmssp_hash, p24); */
+               p->ntlmssp_seq_num = 0;
+
+       }
+
+       fstrcpy(p->user_name, user_name);
+       fstrcpy(p->pipe_user_name, pdb_get_username(server_info->sam_account));
+       fstrcpy(p->domain, domain);
+       fstrcpy(p->wks, wks);
+
+       /*
+        * Store the UNIX credential data (uid/gid pair) in the pipe structure.
+        */
+
+       if (!IS_SAM_UNIX_USER(server_info->sam_account)) {
+               DEBUG(0,("Attempted authenticated pipe with invalid user.  No uid/gid in SAM_ACCOUNT\n"));
+               free_server_info(&server_info);
+               return False;
+       }
+       
+       memcpy(p->session_key, server_info->session_key, sizeof(p->session_key));
+
+       p->pipe_user.uid = pdb_get_uid(server_info->sam_account);
+       p->pipe_user.gid = pdb_get_gid(server_info->sam_account);
+       
+       p->pipe_user.ngroups = server_info->n_groups;
+       if (p->pipe_user.ngroups) {
+               if (!(p->pipe_user.groups = memdup(server_info->groups, sizeof(gid_t) * p->pipe_user.ngroups))) {
+                       DEBUG(0,("failed to memdup group list to p->pipe_user.groups\n"));
+                       free_server_info(&server_info);
+                       return False;
+               }
+       }
+
+       if (server_info->ptok)
+               p->pipe_user.nt_user_token = dup_nt_token(server_info->ptok);
+       else {
+               DEBUG(1,("Error: Authmodule failed to provide nt_user_token\n"));
+               p->pipe_user.nt_user_token = NULL;
+               free_server_info(&server_info);
+               return False;
+       }
+
+       p->ntlmssp_auth_validated = True;
+
+       free_server_info(&server_info);
+       return True;
+}
+
+/*******************************************************************
+ The switch table for the pipe names and the functions to handle them.
+ *******************************************************************/
+
+struct api_cmd
+{
+  const char *name;
+  int (*init)(void);
+};
+
+static struct api_cmd api_fd_commands[] =
+{
+#ifndef RPC_LSA_DYNAMIC
+    { "lsarpc",   rpc_lsa_init },
+#endif
+#ifndef RPC_SAMR_DYNAMIC
+    { "samr",     rpc_samr_init },
+#endif
+#ifndef RPC_SVC_DYNAMIC
+    { "srvsvc",   rpc_srv_init },
+#endif
+#ifndef RPC_WKS_DYNAMIC
+    { "wkssvc",   rpc_wks_init },
+#endif
+#ifndef RPC_NETLOG_DYNAMIC
+    { "NETLOGON", rpc_net_init },
+#endif
+#ifndef RPC_REG_DYNAMIC
+    { "winreg",   rpc_reg_init },
+#endif
+#ifndef RPC_SPOOLSS_DYNAMIC
+    { "spoolss",  rpc_spoolss_init },
+#endif
+#ifndef RPC_DFS_DYNAMIC
+    { "netdfs",   rpc_dfs_init },
+#endif
+    { NULL, NULL }
+};
+
+struct rpc_table
+{
+  struct
+  {
+    const char *clnt;
+    const char *srv;
+  } pipe;
+  struct api_struct *cmds;
+  int n_cmds;
+};
+
+static struct rpc_table *rpc_lookup;
+static int rpc_lookup_size;
+
+/*******************************************************************
+ This is the client reply to our challenge for an authenticated 
+ bind request. The challenge we sent is in p->challenge.
+*******************************************************************/
+
+BOOL api_pipe_bind_auth_resp(pipes_struct *p, prs_struct *rpc_in_p)
+{
+       RPC_HDR_AUTHA autha_info;
+       RPC_AUTH_VERIFIER auth_verifier;
+       RPC_AUTH_NTLMSSP_RESP ntlmssp_resp;
+
+       DEBUG(5,("api_pipe_bind_auth_resp: decode request. %d\n", __LINE__));
+
+       if (p->hdr.auth_len == 0) {
+               DEBUG(0,("api_pipe_bind_auth_resp: No auth field sent !\n"));
+               return False;
+       }
+
+       /*
+        * Decode the authentication verifier response.
+        */
+
+       if(!smb_io_rpc_hdr_autha("", &autha_info, rpc_in_p, 0)) {
+               DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_HDR_AUTHA failed.\n"));
+               return False;
+       }
+
+       if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != NTLMSSP_AUTH_LEVEL) {
+               DEBUG(0,("api_pipe_bind_auth_resp: incorrect auth type (%d) or level (%d).\n",
+                       (int)autha_info.auth_type, (int)autha_info.auth_level ));
+               return False;
+       }
+
+       if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+               DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_AUTH_VERIFIER failed.\n"));
+               return False;
+       }
+
+       /*
+        * Ensure this is a NTLMSSP_AUTH packet type.
+        */
+
+       if (!rpc_auth_verifier_chk(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH)) {
+               DEBUG(0,("api_pipe_bind_auth_resp: rpc_auth_verifier_chk failed.\n"));
+               return False;
+       }
+
+       if(!smb_io_rpc_auth_ntlmssp_resp("", &ntlmssp_resp, rpc_in_p, 0)) {
+               DEBUG(0,("api_pipe_bind_auth_resp: Failed to unmarshall RPC_AUTH_NTLMSSP_RESP.\n"));
+               return False;
+       }
+
+       /*
+        * The following call actually checks the challenge/response data.
+        * for correctness against the given DOMAIN\user name.
+        */
+       
+       if (!api_pipe_ntlmssp_verify(p, &ntlmssp_resp))
+               return False;
+
+       p->pipe_bound = True
+;
+       return True;
+}
+
+/*******************************************************************
+ Marshall a bind_nak pdu.
+*******************************************************************/
+
+static BOOL setup_bind_nak(pipes_struct *p)
+{
+       prs_struct outgoing_rpc;
+       RPC_HDR nak_hdr;
+       uint16 zero = 0;
+
+       /* Free any memory in the current return data buffer. */
+       prs_mem_free(&p->out_data.rdata);
+
+       /*
+        * Marshall directly into the outgoing PDU space. We
+        * must do this as we need to set to the bind response
+        * header and are never sending more than one PDU here.
+        */
+
+       prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+       prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+
+       /*
+        * Initialize a bind_nak header.
+        */
+
+       init_rpc_hdr(&nak_hdr, RPC_BINDNACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+            p->hdr.call_id, RPC_HEADER_LEN + sizeof(uint16), 0);
+
+       /*
+        * Marshall the header into the outgoing PDU.
+        */
+
+       if(!smb_io_rpc_hdr("", &nak_hdr, &outgoing_rpc, 0)) {
+               DEBUG(0,("setup_bind_nak: marshalling of RPC_HDR failed.\n"));
+               prs_mem_free(&outgoing_rpc);
+               return False;
+       }
+
+       /*
+        * Now add the reject reason.
+        */
+
+       if(!prs_uint16("reject code", &outgoing_rpc, 0, &zero)) {
+               prs_mem_free(&outgoing_rpc);
+        return False;
+       }
+
+       p->out_data.data_sent_length = 0;
+       p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+       p->out_data.current_pdu_sent = 0;
+
+       p->pipe_bound = False;
+
+       return True;
+}
+
+/*******************************************************************
+ Marshall a fault pdu.
+*******************************************************************/
+
+BOOL setup_fault_pdu(pipes_struct *p, NTSTATUS status)
+{
+       prs_struct outgoing_pdu;
+       RPC_HDR fault_hdr;
+       RPC_HDR_RESP hdr_resp;
+       RPC_HDR_FAULT fault_resp;
+
+       /* Free any memory in the current return data buffer. */
+       prs_mem_free(&p->out_data.rdata);
+
+       /*
+        * Marshall directly into the outgoing PDU space. We
+        * must do this as we need to set to the bind response
+        * header and are never sending more than one PDU here.
+        */
+
+       prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+       prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+       /*
+        * Initialize a fault header.
+        */
+
+       init_rpc_hdr(&fault_hdr, RPC_FAULT, RPC_FLG_FIRST | RPC_FLG_LAST | RPC_FLG_NOCALL,
+            p->hdr.call_id, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_FAULT_LEN, 0);
+
+       /*
+        * Initialize the HDR_RESP and FAULT parts of the PDU.
+        */
+
+       memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+       fault_resp.status = status;
+       fault_resp.reserved = 0;
+
+       /*
+        * Marshall the header into the outgoing PDU.
+        */
+
+       if(!smb_io_rpc_hdr("", &fault_hdr, &outgoing_pdu, 0)) {
+               DEBUG(0,("setup_fault_pdu: marshalling of RPC_HDR failed.\n"));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+               DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_RESP.\n"));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       if(!smb_io_rpc_hdr_fault("fault", &fault_resp, &outgoing_pdu, 0)) {
+               DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_FAULT.\n"));
+               prs_mem_free(&outgoing_pdu);
+               return False;
+       }
+
+       p->out_data.data_sent_length = 0;
+       p->out_data.current_pdu_len = prs_offset(&outgoing_pdu);
+       p->out_data.current_pdu_sent = 0;
+
+       prs_mem_free(&outgoing_pdu);
+       return True;
+}
+
+/*******************************************************************
+ Ensure a bind request has the correct abstract & transfer interface.
+ Used to reject unknown binds from Win2k.
+*******************************************************************/
+
+BOOL check_bind_req(char* pipe_name, RPC_IFACE* abstract,
+                                       RPC_IFACE* transfer)
+{
+       extern struct pipe_id_info pipe_names[];
+       int i=0;
+       fstring pname;
+       fstrcpy(pname,"\\PIPE\\");
+       fstrcat(pname,pipe_name);
+
+       DEBUG(3,("check_bind_req for %s\n", pname));
+
+#ifndef SUPPORT_NEW_LSARPC_UUID
+
+       /* check for the first pipe matching the name */
+       
+       for ( i=0; pipe_names[i].client_pipe; i++ ) {
+               if ( strequal(pipe_names[i].client_pipe, pname) )
+                       break;
+       }
+#else
+       /* we have to check all now since win2k introduced a new UUID on the lsaprpc pipe */
+               
+       for ( i=0; pipe_names[i].client_pipe; i++ ) 
+       {
+               if ( strequal(pipe_names[i].client_pipe, pname)
+                       && (abstract->version == pipe_names[i].abstr_syntax.version) 
+                       && (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, sizeof(RPC_UUID)) == 0)
+                       && (transfer->version == pipe_names[i].trans_syntax.version)
+                       && (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(RPC_UUID)) == 0) )
+               {
+                       break;
+               }
+       }
+#endif
+
+       if(pipe_names[i].client_pipe == NULL)
+               return False;
+
+#ifndef SUPPORT_NEW_LSARPC_UUID
+       /* check the abstract interface */
+       if ( (abstract->version != pipe_names[i].abstr_syntax.version) 
+               || (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, sizeof(RPC_UUID)) != 0) )
+       {
+               return False;
+       }
+
+       /* check the transfer interface */
+       if ( (transfer->version != pipe_names[i].trans_syntax.version) 
+               || (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(RPC_UUID)) != 0) )
+       {
+               return False;
+       }
+#endif
+       return True;
+}
+
+/*******************************************************************
+ Register commands to an RPC pipe
+*******************************************************************/
+int rpc_pipe_register_commands(const char *clnt, const char *srv, const struct api_struct *cmds, int size)
+{
+        struct rpc_table *rpc_entry;
+
+
+        /* We use a temporary variable because this call can fail and 
+           rpc_lookup will still be valid afterwards.  It could then succeed if
+           called again later */
+        rpc_entry = realloc(rpc_lookup, 
+                            ++rpc_lookup_size*sizeof(struct rpc_table));
+        if (NULL == rpc_entry) {
+                rpc_lookup_size--;
+                DEBUG(0, ("rpc_pipe_register_commands: memory allocation failed\n"));
+                return 0;
+        } else {
+                rpc_lookup = rpc_entry;
+        }
+        
+        rpc_entry = rpc_lookup + (rpc_lookup_size - 1);
+        ZERO_STRUCTP(rpc_entry);
+        rpc_entry->pipe.clnt = strdup(clnt);
+        rpc_entry->pipe.srv = strdup(srv);
+        rpc_entry->cmds = realloc(rpc_entry->cmds, 
+                                  (rpc_entry->n_cmds + size) *
+                                  sizeof(struct api_struct));
+        memcpy(rpc_entry->cmds + rpc_entry->n_cmds, cmds,
+               size * sizeof(struct api_struct));
+        rpc_entry->n_cmds += size;
+        
+        return size;
+}
+
+/*******************************************************************
+ Register commands to an RPC pipe
+*******************************************************************/
+int rpc_load_module(const char *module)
+{
+        pstring full_path;
+               int status;
+        
+        pstrcpy(full_path, lib_path("rpc"));
+        pstrcat(full_path, "/librpc_");
+        pstrcat(full_path, module);
+        pstrcat(full_path, ".");
+        pstrcat(full_path, shlib_ext());
+               
+               if (!(status = smb_load_module(full_path)))  {
+                DEBUG(0, ("Could not load requested pipe %s as %s\n", 
+                    module, full_path));
+        }
+        
+               return status;
+}
+
+/*******************************************************************
+ Respond to a pipe bind request.
+*******************************************************************/
+
+BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
+{
+       RPC_HDR_BA hdr_ba;
+       RPC_HDR_RB hdr_rb;
+       RPC_HDR_AUTH auth_info;
+       uint16 assoc_gid;
+       fstring ack_pipe_name;
+       prs_struct out_hdr_ba;
+       prs_struct out_auth;
+       prs_struct outgoing_rpc;
+       int i = 0;
+       int auth_len = 0;
+       enum RPC_PKT_TYPE reply_pkt_type;
+
+       p->ntlmssp_auth_requested = False;
+
+       DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__));
+
+       /*
+        * Try and find the correct pipe name to ensure
+        * that this is a pipe name we support.
+        */
+
+
+       for (i = 0; i < rpc_lookup_size; i++) {
+               if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+                  DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+                            rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+                  fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+                  break;
+                }
+       }
+
+       if (i == rpc_lookup_size) {
+                for (i = 0; api_fd_commands[i].name; i++) {
+                       if (strequal(api_fd_commands[i].name, p->name)) {
+                               api_fd_commands[i].init();
+                               break;
+                       }
+                }
+
+                if (!api_fd_commands[i].name && !rpc_load_module(p->name)) {
+                       DEBUG(3,("api_pipe_bind_req: Unknown pipe name %s in bind request.\n",
+                                p->name ));
+                       if(!setup_bind_nak(p))
+                               return False;
+                       return True;
+                }
+
+                for (i = 0; i < rpc_lookup_size; i++) {
+                       if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+                               DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+                                         rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+                               fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+                               break;
+                       }
+                }
+       }
+
+       /* decode the bind request */
+       if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_in_p, 0))  {
+               DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_RB struct.\n"));
+               return False;
+       }
+
+       /*
+        * Check if this is an authenticated request.
+        */
+
+       if (p->hdr.auth_len != 0) {
+               RPC_AUTH_VERIFIER auth_verifier;
+               RPC_AUTH_NTLMSSP_NEG ntlmssp_neg;
+
+               /* 
+                * Decode the authentication verifier.
+                */
+
+               if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+                       return False;
+               }
+
+               /*
+                * We only support NTLMSSP_AUTH_TYPE requests.
+                */
+
+               if(auth_info.auth_type != NTLMSSP_AUTH_TYPE) {
+                       DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n",
+                               auth_info.auth_type ));
+                       return False;
+               }
+
+               if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+                       return False;
+               }
+
+               if(!strequal(auth_verifier.signature, "NTLMSSP")) {
+                       DEBUG(0,("api_pipe_bind_req: auth_verifier.signature != NTLMSSP\n"));
+                       return False;
+               }
+
+               if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) {
+                       DEBUG(0,("api_pipe_bind_req: auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n",
+                               auth_verifier.msg_type));
+                       return False;
+               }
+
+               if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n"));
+                       return False;
+               }
+
+               p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS;
+               p->ntlmssp_auth_requested = True;
+       }
+
+       switch(p->hdr.pkt_type) {
+               case RPC_BIND:
+                       /* name has to be \PIPE\xxxxx */
+                       fstrcpy(ack_pipe_name, "\\PIPE\\");
+                       fstrcat(ack_pipe_name, p->pipe_srv_name);
+                       reply_pkt_type = RPC_BINDACK;
+                       break;
+               case RPC_ALTCONT:
+                       /* secondary address CAN be NULL
+                        * as the specs say it's ignored.
+                        * It MUST NULL to have the spoolss working.
+                        */
+                       fstrcpy(ack_pipe_name,"");
+                       reply_pkt_type = RPC_ALTCONTRESP;
+                       break;
+               default:
+                       return False;
+       }
+
+       DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__));
+
+       /* 
+        * Marshall directly into the outgoing PDU space. We
+        * must do this as we need to set to the bind response
+        * header and are never sending more than one PDU here.
+        */
+
+       prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+       prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+       /*
+        * Setup the memory to marshall the ba header, and the
+        * auth footers.
+        */
+
+       if(!prs_init(&out_hdr_ba, 1024, p->mem_ctx, MARSHALL)) {
+               DEBUG(0,("api_pipe_bind_req: malloc out_hdr_ba failed.\n"));
+               prs_mem_free(&outgoing_rpc);
+               return False;
+       }
+
+       if(!prs_init(&out_auth, 1024, p->mem_ctx, MARSHALL)) {
+               DEBUG(0,("pi_pipe_bind_req: malloc out_auth failed.\n"));
+               prs_mem_free(&outgoing_rpc);
+               prs_mem_free(&out_hdr_ba);
+               return False;
+       }
+
+       if (p->ntlmssp_auth_requested)
+               assoc_gid = 0x7a77;
+       else
+               assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0;
+
+       /*
+        * Create the bind response struct.
+        */
+
+       /* If the requested abstract synt uuid doesn't match our client pipe,
+               reject the bind_ack & set the transfer interface synt to all 0's,
+               ver 0 (observed when NT5 attempts to bind to abstract interfaces
+               unknown to NT4)
+               Needed when adding entries to a DACL from NT5 - SK */
+
+       if(check_bind_req(p->name, &hdr_rb.abstract, &hdr_rb.transfer)) {
+               init_rpc_hdr_ba(&hdr_ba,
+                       MAX_PDU_FRAG_LEN,
+                       MAX_PDU_FRAG_LEN,
+                       assoc_gid,
+                       ack_pipe_name,
+                       0x1, 0x0, 0x0,
+                       &hdr_rb.transfer);
+       } else {
+               RPC_IFACE null_interface;
+               ZERO_STRUCT(null_interface);
+               /* Rejection reason: abstract syntax not supported */
+               init_rpc_hdr_ba(&hdr_ba, MAX_PDU_FRAG_LEN,
+                                       MAX_PDU_FRAG_LEN, assoc_gid,
+                                       ack_pipe_name, 0x1, 0x2, 0x1,
+                                       &null_interface);
+       }
+
+       /*
+        * and marshall it.
+        */
+
+       if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) {
+               DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n"));
+               goto err_exit;
+       }
+
+       /*
+        * Now the authentication.
+        */
+
+       if (p->ntlmssp_auth_requested) {
+               RPC_AUTH_VERIFIER auth_verifier;
+               RPC_AUTH_NTLMSSP_CHAL ntlmssp_chal;
+
+               generate_random_buffer(p->challenge, 8, False);
+
+               /*** Authentication info ***/
+
+               init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, RPC_HDR_AUTH_LEN, 1);
+               if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+                       goto err_exit;
+               }
+
+               /*** NTLMSSP verifier ***/
+
+               init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_CHALLENGE);
+               if(!smb_io_rpc_auth_verifier("", &auth_verifier, &out_auth, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+                       goto err_exit;
+               }
+
+               /* NTLMSSP challenge ***/
+
+               init_rpc_auth_ntlmssp_chal(&ntlmssp_chal, p->ntlmssp_chal_flags, p->challenge);
+               if(!smb_io_rpc_auth_ntlmssp_chal("", &ntlmssp_chal, &out_auth, 0)) {
+                       DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_NTLMSSP_CHAL failed.\n"));
+                       goto err_exit;
+               }
+
+               /* Auth len in the rpc header doesn't include auth_header. */
+               auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+       }
+
+       /*
+        * Create the header, now we know the length.
+        */
+
+       init_rpc_hdr(&p->hdr, reply_pkt_type, RPC_FLG_FIRST | RPC_FLG_LAST,
+                       p->hdr.call_id,
+                       RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth),
+                       auth_len);
+
+       /*
+        * Marshall the header into the outgoing PDU.
+        */
+
+       if(!smb_io_rpc_hdr("", &p->hdr, &outgoing_rpc, 0)) {
+               DEBUG(0,("pi_pipe_bind_req: marshalling of RPC_HDR failed.\n"));
+               goto err_exit;
+       }
+
+       /*
+        * Now add the RPC_HDR_BA and any auth needed.
+        */
+
+       if(!prs_append_prs_data( &outgoing_rpc, &out_hdr_ba)) {
+               DEBUG(0,("api_pipe_bind_req: append of RPC_HDR_BA failed.\n"));
+               goto err_exit;
+       }
+
+       if(p->ntlmssp_auth_requested && !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+               DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n"));
+               goto err_exit;
+       }
+
+       if(!p->ntlmssp_auth_requested)
+               p->pipe_bound = True;
+
+       /*
+        * Setup the lengths for the initial reply.
+        */
+
+       p->out_data.data_sent_length = 0;
+       p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+       p->out_data.current_pdu_sent = 0;
+
+       prs_mem_free(&out_hdr_ba);
+       prs_mem_free(&out_auth);
+
+       return True;
+
+  err_exit:
+
+       prs_mem_free(&outgoing_rpc);
+       prs_mem_free(&out_hdr_ba);
+       prs_mem_free(&out_auth);
+       return False;
+}
+
+/****************************************************************************
+ Deal with sign & seal processing on an RPC request.
+****************************************************************************/
+
+BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in)
+{
+       /*
+        * We always negotiate the following two bits....
+        */
+       BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+       BOOL auth_seal   = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+       int data_len;
+       int auth_len;
+       uint32 old_offset;
+       uint32 crc32 = 0;
+
+       auth_len = p->hdr.auth_len;
+
+       if ((auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) && auth_verify) {
+               DEBUG(0,("api_pipe_auth_process: Incorrect auth_len %d.\n", auth_len ));
+               return False;
+       }
+
+       /*
+        * The following is that length of the data we must verify or unseal.
+        * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+        * preceeding the auth_data.
+        */
+
+       data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - 
+                       (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len;
+       
+       DEBUG(5,("api_pipe_auth_process: sign: %s seal: %s data %d auth %d\n",
+                BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, auth_len));
+
+       if (auth_seal) {
+               /*
+                * The data in rpc_in doesn't contain the RPC_HEADER as this
+                * has already been consumed.
+                */
+               char *data = prs_data_p(rpc_in) + RPC_HDR_REQ_LEN;
+               NTLMSSPcalc_p(p, (uchar*)data, data_len);
+               crc32 = crc32_calc_buffer(data, data_len);
+       }
+
+       old_offset = prs_offset(rpc_in);
+
+       if (auth_seal || auth_verify) {
+               RPC_HDR_AUTH auth_info;
+
+               if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+                       DEBUG(0,("api_pipe_auth_process: cannot move offset to %u.\n",
+                               (unsigned int)old_offset + data_len ));
+                       return False;
+               }
+
+               if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+                       DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_HDR_AUTH.\n"));
+                       return False;
+               }
+       }
+
+       if (auth_verify) {
+               RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+               char *req_data = prs_data_p(rpc_in) + prs_offset(rpc_in) + 4;
+
+               DEBUG(5,("api_pipe_auth_process: auth %d\n", prs_offset(rpc_in) + 4));
+
+               /*
+                * Ensure we have RPC_AUTH_NTLMSSP_CHK_LEN - 4 more bytes in the
+                * incoming buffer.
+                */
+               if(prs_mem_get(rpc_in, RPC_AUTH_NTLMSSP_CHK_LEN - 4) == NULL) {
+                       DEBUG(0,("api_pipe_auth_process: missing %d bytes in buffer.\n",
+                               RPC_AUTH_NTLMSSP_CHK_LEN - 4 ));
+                       return False;
+               }
+
+               NTLMSSPcalc_p(p, (uchar*)req_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+               if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, rpc_in, 0)) {
+                       DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_AUTH_NTLMSSP_CHK.\n"));
+                       return False;
+               }
+
+               if (!rpc_auth_ntlmssp_chk(&ntlmssp_chk, crc32, p->ntlmssp_seq_num)) {
+                       DEBUG(0,("api_pipe_auth_process: NTLMSSP check failed.\n"));
+                       return False;
+               }
+       }
+
+       /*
+        * Return the current pointer to the data offset.
+        */
+
+       if(!prs_set_offset(rpc_in, old_offset)) {
+               DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",
+                       (unsigned int)old_offset ));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Return a user struct for a pipe user.
+****************************************************************************/
+
+struct current_user *get_current_user(struct current_user *user, pipes_struct *p)
+{
+       if (p->ntlmssp_auth_validated) {
+               memcpy(user, &p->pipe_user, sizeof(struct current_user));
+       } else {
+               extern struct current_user current_user;
+               memcpy(user, &current_user, sizeof(struct current_user));
+       }
+
+       return user;
+}
+
+/****************************************************************************
+ Find the correct RPC function to call for this request.
+ If the pipe is authenticated then become the correct UNIX user
+ before doing the call.
+****************************************************************************/
+
+BOOL api_pipe_request(pipes_struct *p)
+{
+       int i = 0;
+       BOOL ret = False;
+
+       if (p->ntlmssp_auth_validated) {
+
+               if(!become_authenticated_pipe_user(p)) {
+                       prs_mem_free(&p->out_data.rdata);
+                       return False;
+               }
+       }
+
+       DEBUG(5, ("Requested \\PIPE\\%s\n", p->name));
+
+       for (i = 0; i < rpc_lookup_size; i++) {
+               if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+                        DEBUG(3,("Doing \\PIPE\\%s\n", 
+                                 rpc_lookup[i].pipe.clnt));
+                        set_current_rpc_talloc(p->mem_ctx);
+                        ret = api_rpcTNP(p, rpc_lookup[i].pipe.clnt,
+                                         rpc_lookup[i].cmds,
+                                         rpc_lookup[i].n_cmds);
+                        set_current_rpc_talloc(NULL);
+                        break;
+                }
+       }
+
+
+       if (i == rpc_lookup_size) {
+               for (i = 0; api_fd_commands[i].name; i++) {
+                        if (strequal(api_fd_commands[i].name, p->name)) {
+                                api_fd_commands[i].init();
+                                break;
+                        }
+                }
+
+                if (!api_fd_commands[i].name) {
+                       rpc_load_module(p->name);
+                }
+
+                for (i = 0; i < rpc_lookup_size; i++) {
+                        if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+                                DEBUG(3,("Doing \\PIPE\\%s\n",
+                                         rpc_lookup[i].pipe.clnt));
+                                set_current_rpc_talloc(p->mem_ctx);
+                                ret = api_rpcTNP(p, rpc_lookup[i].pipe.clnt,
+                                                 rpc_lookup[i].cmds,
+                                                 rpc_lookup[i].n_cmds);
+                                set_current_rpc_talloc(NULL);
+                                break;
+                        }
+                }
+       }
+
+       if(p->ntlmssp_auth_validated)
+               unbecome_authenticated_pipe_user();
+
+       return ret;
+}
+
+/*******************************************************************
+ Calls the underlying RPC function for a named pipe.
+ ********************************************************************/
+
+BOOL api_rpcTNP(pipes_struct *p, const char *rpc_name, 
+               const struct api_struct *api_rpc_cmds, int n_cmds)
+{
+       int fn_num;
+       fstring name;
+       uint32 offset1, offset2;
+       /* interpret the command */
+       DEBUG(4,("api_rpcTNP: %s op 0x%x - ", rpc_name, p->hdr_req.opnum));
+
+       slprintf(name, sizeof(name)-1, "in_%s", rpc_name);
+       prs_dump(name, p->hdr_req.opnum, &p->in_data.data);
+
+       for (fn_num = 0; fn_num < n_cmds; fn_num++) {
+               if (api_rpc_cmds[fn_num].opnum == p->hdr_req.opnum && api_rpc_cmds[fn_num].fn != NULL) {
+                       DEBUG(3,("api_rpcTNP: rpc command: %s\n", api_rpc_cmds[fn_num].name));
+                       break;
+               }
+       }
+
+       if (fn_num == n_cmds) {
+               /*
+                * For an unknown RPC just return a fault PDU but
+                * return True to allow RPC's on the pipe to continue
+                * and not put the pipe into fault state. JRA.
+                */
+               DEBUG(4, ("unknown\n"));
+               setup_fault_pdu(p, NT_STATUS(0x1c010002));
+               return True;
+       }
+
+       offset1 = prs_offset(&p->out_data.rdata);
+
+        DEBUG(6, ("api_rpc_cmds[%d].fn == %p\n", 
+                fn_num, api_rpc_cmds[fn_num].fn));
+       /* do the actual command */
+       if(!api_rpc_cmds[fn_num].fn(p)) {
+               DEBUG(0,("api_rpcTNP: %s: %s failed.\n", rpc_name, api_rpc_cmds[fn_num].name));
+               prs_mem_free(&p->out_data.rdata);
+               return False;
+       }
+
+       if (p->bad_handle_fault_state) {
+               DEBUG(4,("api_rpcTNP: bad handle fault return.\n"));
+               p->bad_handle_fault_state = False;
+               setup_fault_pdu(p, NT_STATUS(0x1C00001A));
+               return True;
+       }
+
+       slprintf(name, sizeof(name)-1, "out_%s", rpc_name);
+       offset2 = prs_offset(&p->out_data.rdata);
+       prs_set_offset(&p->out_data.rdata, offset1);
+       prs_dump(name, p->hdr_req.opnum, &p->out_data.rdata);
+       prs_set_offset(&p->out_data.rdata, offset2);
+
+       DEBUG(5,("api_rpcTNP: called %s successfully\n", rpc_name));
+
+       /* Check for buffer underflow in rpc parsing */
+
+       if ((DEBUGLEVEL >= 10) && 
+           (prs_offset(&p->in_data.data) != prs_data_size(&p->in_data.data))) {
+               size_t data_len = prs_data_size(&p->in_data.data) - prs_offset(&p->in_data.data);
+               char *data;
+
+               data = malloc(data_len);
+
+               DEBUG(10, ("api_rpcTNP: rpc input buffer underflow (parse error?)\n"));
+               if (data) {
+                       prs_uint8s(False, "", &p->in_data.data, 0, (unsigned char *)data, (uint32)data_len);
+                       SAFE_FREE(data);
+               }
+
+       }
+
+       return True;
+}
diff --git a/source4/rpc_server/srv_pipe_hnd.c b/source4/rpc_server/srv_pipe_hnd.c
new file mode 100644 (file)
index 0000000..602a7ed
--- /dev/null
@@ -0,0 +1,1156 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1998,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ *  Copyright (C) Jeremy Allison                                   1999.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define        PIPE            "\\PIPE\\"
+#define        PIPELEN         strlen(PIPE)
+
+static smb_np_struct *chain_p;
+static int pipes_open;
+
+/*
+ * Sometimes I can't decide if I hate Windows printer driver
+ * writers more than I hate the Windows spooler service driver
+ * writers. This gets around a combination of bugs in the spooler
+ * and the HP 8500 PCL driver that causes a spooler spin. JRA.
+ *
+ * bumped up from 20 -> 64 after viewing traffic from WordPerfect
+ * 2002 running on NT 4.- SP6
+ * bumped up from 64 -> 256 after viewing traffic from con2prt
+ * for lots of printers on a WinNT 4.x SP6 box.
+ */
+#ifndef MAX_OPEN_SPOOLSS_PIPES
+#define MAX_OPEN_SPOOLSS_PIPES 256
+#endif
+static int current_spoolss_pipes_open;
+
+static smb_np_struct *Pipes;
+static pipes_struct *InternalPipes;
+static struct bitmap *bmap;
+
+/* TODO
+ * the following prototypes are declared here to avoid
+ * code being moved about too much for a patch to be
+ * disrupted / less obvious.
+ *
+ * these functions, and associated functions that they
+ * call, should be moved behind a .so module-loading
+ * system _anyway_.  so that's the next step...
+ */
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+               BOOL *is_data_outstanding);
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n);
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn);
+static void *make_internal_rpc_pipe_p(char *pipe_name, 
+                             struct tcon_context *conn, uint16 vuid);
+
+/****************************************************************************
+ Pipe iterator functions.
+****************************************************************************/
+
+smb_np_struct *get_first_pipe(void)
+{
+       return Pipes;
+}
+
+smb_np_struct *get_next_pipe(smb_np_struct *p)
+{
+       return p->next;
+}
+
+/****************************************************************************
+ Internal Pipe iterator functions.
+****************************************************************************/
+
+pipes_struct *get_first_internal_pipe(void)
+{
+       return InternalPipes;
+}
+
+pipes_struct *get_next_internal_pipe(pipes_struct *p)
+{
+       return p->next;
+}
+
+/* this must be larger than the sum of the open files and directories */
+static int pipe_handle_offset;
+
+/****************************************************************************
+ Set the pipe_handle_offset. Called from smbd/files.c
+****************************************************************************/
+
+void set_pipe_handle_offset(int max_open_files)
+{
+  if(max_open_files < 0x7000)
+    pipe_handle_offset = 0x7000;
+  else
+    pipe_handle_offset = max_open_files + 10; /* For safety. :-) */
+}
+
+/****************************************************************************
+ Reset pipe chain handle number.
+****************************************************************************/
+
+void reset_chain_p(void)
+{
+       chain_p = NULL;
+}
+
+/****************************************************************************
+ Initialise pipe handle states.
+****************************************************************************/
+
+void init_rpc_pipe_hnd(void)
+{
+       bmap = bitmap_allocate(MAX_OPEN_PIPES);
+       if (!bmap)
+               exit_server("out of memory in init_rpc_pipe_hnd");
+}
+
+/****************************************************************************
+ Initialise an outgoing packet.
+****************************************************************************/
+
+static BOOL pipe_init_outgoing_data(pipes_struct *p)
+{
+       output_data *o_data = &p->out_data;
+
+       /* Reset the offset counters. */
+       o_data->data_sent_length = 0;
+       o_data->current_pdu_len = 0;
+       o_data->current_pdu_sent = 0;
+
+       memset(o_data->current_pdu, '\0', sizeof(o_data->current_pdu));
+
+       /* Free any memory in the current return data buffer. */
+       prs_mem_free(&o_data->rdata);
+
+       /*
+        * Initialize the outgoing RPC data buffer.
+        * we will use this as the raw data area for replying to rpc requests.
+        */     
+       if(!prs_init(&o_data->rdata, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+               DEBUG(0,("pipe_init_outgoing_data: malloc fail.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Find first available pipe slot.
+****************************************************************************/
+
+smb_np_struct *open_rpc_pipe_p(char *pipe_name, 
+                             struct tcon_context *conn, uint16 vuid)
+{
+       int i;
+       smb_np_struct *p, *p_it;
+       static int next_pipe;
+       BOOL is_spoolss_pipe = False;
+
+       DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n",
+                pipe_name, pipes_open));
+
+       if (strstr(pipe_name, "spoolss"))
+               is_spoolss_pipe = True;
+       if (is_spoolss_pipe && current_spoolss_pipes_open >= MAX_OPEN_SPOOLSS_PIPES) {
+               DEBUG(10,("open_rpc_pipe_p: spooler bug workaround. Denying open on pipe %s\n",
+                       pipe_name ));
+               return NULL;
+       }
+
+       /* not repeating pipe numbers makes it easier to track things in 
+          log files and prevents client bugs where pipe numbers are reused
+          over connection restarts */
+       if (next_pipe == 0)
+               next_pipe = (sys_getpid() ^ time(NULL)) % MAX_OPEN_PIPES;
+
+       i = bitmap_find(bmap, next_pipe);
+
+       if (i == -1) {
+               DEBUG(0,("ERROR! Out of pipe structures\n"));
+               return NULL;
+       }
+
+       next_pipe = (i+1) % MAX_OPEN_PIPES;
+
+       for (p = Pipes; p; p = p->next)
+               DEBUG(5,("open_rpc_pipe_p: name %s pnum=%x\n", p->name, p->pnum));  
+
+       p = (smb_np_struct *)malloc(sizeof(*p));
+
+       if (!p) {
+               DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+               return NULL;
+       }
+
+       ZERO_STRUCTP(p);
+
+       /* add a dso mechanism instead of this, here */
+
+       p->namedpipe_create = make_internal_rpc_pipe_p;
+       p->namedpipe_read = read_from_internal_pipe;
+       p->namedpipe_write = write_to_internal_pipe;
+       p->namedpipe_close = close_internal_rpc_pipe_hnd;
+
+       p->np_state = p->namedpipe_create(pipe_name, conn, vuid);
+
+       if (p->np_state == NULL) {
+               DEBUG(0,("open_rpc_pipe_p: make_internal_rpc_pipe_p failed.\n"));
+               SAFE_FREE(p);
+               return NULL;
+       }
+
+       DLIST_ADD(Pipes, p);
+
+       /*
+        * Initialize the incoming RPC data buffer with one PDU worth of memory.
+        * We cheat here and say we're marshalling, as we intend to add incoming
+        * data directly into the prs_struct and we want it to auto grow. We will
+        * change the type to UNMARSALLING before processing the stream.
+        */
+
+       bitmap_set(bmap, i);
+       i += pipe_handle_offset;
+
+       pipes_open++;
+
+       p->pnum = i;
+
+       p->open = True;
+       p->device_state = 0;
+       p->priority = 0;
+       p->conn = conn;
+       p->vuid  = vuid;
+
+       p->max_trans_reply = 0;
+       
+       fstrcpy(p->name, pipe_name);
+       
+       DEBUG(4,("Opened pipe %s with handle %x (pipes_open=%d)\n",
+                pipe_name, i, pipes_open));
+       
+       chain_p = p;
+       
+       /* Iterate over p_it as a temp variable, to display all open pipes */ 
+       for (p_it = Pipes; p_it; p_it = p_it->next)
+               DEBUG(5,("open pipes: name %s pnum=%x\n", p_it->name, p_it->pnum));  
+
+       return chain_p;
+}
+
+/****************************************************************************
+ Make an internal namedpipes structure
+****************************************************************************/
+
+static void *make_internal_rpc_pipe_p(char *pipe_name, 
+                             struct tcon_context *conn, uint16 vuid)
+{
+       pipes_struct *p;
+       user_struct *vuser = get_valid_user_struct(vuid);
+
+       DEBUG(4,("Create pipe requested %s\n", pipe_name));
+
+       if (!vuser && vuid != UID_FIELD_INVALID) {
+               DEBUG(0,("ERROR! vuid %d did not map to a valid vuser struct!\n", vuid));
+               return NULL;
+       }
+
+       p = (pipes_struct *)malloc(sizeof(*p));
+
+       if (!p)
+       {
+               DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+               return NULL;
+       }
+
+       ZERO_STRUCTP(p);
+
+       if ((p->mem_ctx = talloc_init("pipe %s %p", pipe_name, p)) == NULL) {
+               DEBUG(0,("open_rpc_pipe_p: talloc_init failed.\n"));
+               SAFE_FREE(p);
+               return NULL;
+       }
+
+       if (!init_pipe_handle_list(p, pipe_name)) {
+               DEBUG(0,("open_rpc_pipe_p: init_pipe_handles failed.\n"));
+               talloc_destroy(p->mem_ctx);
+               SAFE_FREE(p);
+               return NULL;
+       }
+
+       /*
+        * Initialize the incoming RPC data buffer with one PDU worth of memory.
+        * We cheat here and say we're marshalling, as we intend to add incoming
+        * data directly into the prs_struct and we want it to auto grow. We will
+        * change the type to UNMARSALLING before processing the stream.
+        */
+
+       if(!prs_init(&p->in_data.data, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+               DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n"));
+               return NULL;
+       }
+
+       DLIST_ADD(InternalPipes, p);
+
+       p->conn = conn;
+
+       /* Ensure the connection isn't idled whilst this pipe is open. */
+       p->conn->num_files_open++;
+
+       p->vuid  = vuid;
+
+       p->ntlmssp_chal_flags = 0;
+       p->ntlmssp_auth_validated = False;
+       p->ntlmssp_auth_requested = False;
+
+       p->pipe_bound = False;
+       p->fault_state = False;
+       p->endian = RPC_LITTLE_ENDIAN;
+
+       ZERO_STRUCT(p->pipe_user);
+
+       p->pipe_user.uid = (uid_t)-1;
+       p->pipe_user.gid = (gid_t)-1;
+       
+       /* Store the session key and NT_TOKEN */
+       if (vuser) {
+               memcpy(p->session_key, vuser->session_key, sizeof(p->session_key));
+               p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token);
+       }
+
+       /*
+        * Initialize the incoming RPC struct.
+        */
+
+       p->in_data.pdu_needed_len = 0;
+       p->in_data.pdu_received_len = 0;
+
+       /*
+        * Initialize the outgoing RPC struct.
+        */
+
+       p->out_data.current_pdu_len = 0;
+       p->out_data.current_pdu_sent = 0;
+       p->out_data.data_sent_length = 0;
+
+       /*
+        * Initialize the outgoing RPC data buffer with no memory.
+        */     
+       prs_init(&p->out_data.rdata, 0, p->mem_ctx, MARSHALL);
+       
+       fstrcpy(p->name, pipe_name);
+       
+       DEBUG(4,("Created internal pipe %s (pipes_open=%d)\n",
+                pipe_name, pipes_open));
+
+       return (void*)p;
+}
+
+/****************************************************************************
+ Sets the fault state on incoming packets.
+****************************************************************************/
+
+static void set_incoming_fault(pipes_struct *p)
+{
+       prs_mem_free(&p->in_data.data);
+       p->in_data.pdu_needed_len = 0;
+       p->in_data.pdu_received_len = 0;
+       p->fault_state = True;
+       DEBUG(10,("set_incoming_fault: Setting fault state on pipe %s : vuid = 0x%x\n",
+               p->name, p->vuid ));
+}
+
+/****************************************************************************
+ Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer.
+****************************************************************************/
+
+static ssize_t fill_rpc_header(pipes_struct *p, char *data, size_t data_to_copy)
+{
+       size_t len_needed_to_complete_hdr = MIN(data_to_copy, RPC_HEADER_LEN - p->in_data.pdu_received_len);
+
+       DEBUG(10,("fill_rpc_header: data_to_copy = %u, len_needed_to_complete_hdr = %u, receive_len = %u\n",
+                       (unsigned int)data_to_copy, (unsigned int)len_needed_to_complete_hdr,
+                       (unsigned int)p->in_data.pdu_received_len ));
+
+       memcpy((char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, len_needed_to_complete_hdr);
+       p->in_data.pdu_received_len += len_needed_to_complete_hdr;
+
+       return (ssize_t)len_needed_to_complete_hdr;
+}
+
+/****************************************************************************
+ Unmarshalls a new PDU header. Assumes the raw header data is in current_in_pdu.
+****************************************************************************/
+
+static ssize_t unmarshall_rpc_header(pipes_struct *p)
+{
+       /*
+        * Unmarshall the header to determine the needed length.
+        */
+
+       prs_struct rpc_in;
+
+       if(p->in_data.pdu_received_len != RPC_HEADER_LEN) {
+               DEBUG(0,("unmarshall_rpc_header: assert on rpc header length failed.\n"));
+               set_incoming_fault(p);
+               return -1;
+       }
+
+       prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+       prs_set_endian_data( &rpc_in, p->endian);
+
+       prs_give_memory( &rpc_in, (char *)&p->in_data.current_in_pdu[0],
+                                       p->in_data.pdu_received_len, False);
+
+       /*
+        * Unmarshall the header as this will tell us how much
+        * data we need to read to get the complete pdu.
+        * This also sets the endian flag in rpc_in.
+        */
+
+       if(!smb_io_rpc_hdr("", &p->hdr, &rpc_in, 0)) {
+               DEBUG(0,("unmarshall_rpc_header: failed to unmarshall RPC_HDR.\n"));
+               set_incoming_fault(p);
+               prs_mem_free(&rpc_in);
+               return -1;
+       }
+
+       /*
+        * Validate the RPC header.
+        */
+
+       if(p->hdr.major != 5 && p->hdr.minor != 0) {
+               DEBUG(0,("unmarshall_rpc_header: invalid major/minor numbers in RPC_HDR.\n"));
+               set_incoming_fault(p);
+               prs_mem_free(&rpc_in);
+               return -1;
+       }
+
+       /*
+        * If there's not data in the incoming buffer this should be the start of a new RPC.
+        */
+
+       if(prs_offset(&p->in_data.data) == 0) {
+
+               /*
+                * AS/U doesn't set FIRST flag in a BIND packet it seems.
+                */
+
+               if ((p->hdr.pkt_type == RPC_REQUEST) && !(p->hdr.flags & RPC_FLG_FIRST)) {
+                       /*
+                        * Ensure that the FIRST flag is set. If not then we have
+                        * a stream missmatch.
+                        */
+
+                       DEBUG(0,("unmarshall_rpc_header: FIRST flag not set in first PDU !\n"));
+                       set_incoming_fault(p);
+                       prs_mem_free(&rpc_in);
+                       return -1;
+               }
+
+               /*
+                * If this is the first PDU then set the endianness
+                * flag in the pipe. We will need this when parsing all
+                * data in this RPC.
+                */
+
+               p->endian = rpc_in.bigendian_data;
+
+               DEBUG(5,("unmarshall_rpc_header: using %sendian RPC\n",
+                               p->endian == RPC_LITTLE_ENDIAN ? "little-" : "big-" ));
+
+       } else {
+
+               /*
+                * If this is *NOT* the first PDU then check the endianness
+                * flag in the pipe is the same as that in the PDU.
+                */
+
+               if (p->endian != rpc_in.bigendian_data) {
+                       DEBUG(0,("unmarshall_rpc_header: FIRST endianness flag (%d) different in next PDU !\n", (int)p->endian));
+                       set_incoming_fault(p);
+                       prs_mem_free(&rpc_in);
+                       return -1;
+               }
+       }
+
+       /*
+        * Ensure that the pdu length is sane.
+        */
+
+       if((p->hdr.frag_len < RPC_HEADER_LEN) || (p->hdr.frag_len > MAX_PDU_FRAG_LEN)) {
+               DEBUG(0,("unmarshall_rpc_header: assert on frag length failed.\n"));
+               set_incoming_fault(p);
+               prs_mem_free(&rpc_in);
+               return -1;
+       }
+
+       DEBUG(10,("unmarshall_rpc_header: type = %u, flags = %u\n", (unsigned int)p->hdr.pkt_type,
+                       (unsigned int)p->hdr.flags ));
+
+       /*
+        * Adjust for the header we just ate.
+        */
+       p->in_data.pdu_received_len = 0;
+       p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN;
+
+       /*
+        * Null the data we just ate.
+        */
+
+       memset((char *)&p->in_data.current_in_pdu[0], '\0', RPC_HEADER_LEN);
+
+       prs_mem_free(&rpc_in);
+
+       return 0; /* No extra data processed. */
+}
+
+/****************************************************************************
+ Call this to free any talloc'ed memory. Do this before and after processing
+ a complete PDU.
+****************************************************************************/
+
+void free_pipe_context(pipes_struct *p)
+{
+       if (p->mem_ctx) {
+               DEBUG(3,("free_pipe_context: destroying talloc pool of size %u\n", talloc_pool_size(p->mem_ctx) ));
+               talloc_destroy_pool(p->mem_ctx);
+       } else {
+               p->mem_ctx = talloc_init("pipe %s %p", p->name, p);
+               if (p->mem_ctx == NULL)
+                       p->fault_state = True;
+       }
+}
+
+/****************************************************************************
+ Processes a request pdu. This will do auth processing if needed, and
+ appends the data into the complete stream if the LAST flag is not set.
+****************************************************************************/
+
+static BOOL process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p)
+{
+       BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+       size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+                               (auth_verify ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len;
+
+       if(!p->pipe_bound) {
+               DEBUG(0,("process_request_pdu: rpc request with no bind.\n"));
+               set_incoming_fault(p);
+               return False;
+       }
+
+       /*
+        * Check if we need to do authentication processing.
+        * This is only done on requests, not binds.
+        */
+
+       /*
+        * Read the RPC request header.
+        */
+
+       if(!smb_io_rpc_hdr_req("req", &p->hdr_req, rpc_in_p, 0)) {
+               DEBUG(0,("process_request_pdu: failed to unmarshall RPC_HDR_REQ.\n"));
+               set_incoming_fault(p);
+               return False;
+       }
+
+       if(p->ntlmssp_auth_validated && !api_pipe_auth_process(p, rpc_in_p)) {
+               DEBUG(0,("process_request_pdu: failed to do auth processing.\n"));
+               set_incoming_fault(p);
+               return False;
+       }
+
+       if (p->ntlmssp_auth_requested && !p->ntlmssp_auth_validated) {
+
+               /*
+                * Authentication _was_ requested and it already failed.
+                */
+
+               DEBUG(0,("process_request_pdu: RPC request received on pipe %s where \
+authentication failed. Denying the request.\n", p->name));
+               set_incoming_fault(p);
+        return False;
+    }
+
+       /*
+        * Check the data length doesn't go over the 15Mb limit.
+        * increased after observing a bug in the Windows NT 4.0 SP6a
+        * spoolsv.exe when the response to a GETPRINTERDRIVER2 RPC
+        * will not fit in the initial buffer of size 0x1068   --jerry 22/01/2002
+        */
+       
+       if(prs_offset(&p->in_data.data) + data_len > 15*1024*1024) {
+               DEBUG(0,("process_request_pdu: rpc data buffer too large (%u) + (%u)\n",
+                               (unsigned int)prs_data_size(&p->in_data.data), (unsigned int)data_len ));
+               set_incoming_fault(p);
+               return False;
+       }
+
+       /*
+        * Append the data portion into the buffer and return.
+        */
+
+       if(!prs_append_some_prs_data(&p->in_data.data, rpc_in_p, prs_offset(rpc_in_p), data_len)) {
+               DEBUG(0,("process_request_pdu: Unable to append data size %u to parse buffer of size %u.\n",
+                               (unsigned int)data_len, (unsigned int)prs_data_size(&p->in_data.data) ));
+               set_incoming_fault(p);
+               return False;
+       }
+
+       if(p->hdr.flags & RPC_FLG_LAST) {
+               BOOL ret = False;
+               /*
+                * Ok - we finally have a complete RPC stream.
+                * Call the rpc command to process it.
+                */
+
+               /*
+                * Ensure the internal prs buffer size is *exactly* the same
+                * size as the current offset.
+                */
+
+               if(!prs_set_buffer_size(&p->in_data.data, prs_offset(&p->in_data.data)))
+               {
+                       DEBUG(0,("process_request_pdu: Call to prs_set_buffer_size failed!\n"));
+                       set_incoming_fault(p);
+                       return False;
+               }
+
+               /*
+                * Set the parse offset to the start of the data and set the
+                * prs_struct to UNMARSHALL.
+                */
+
+               prs_set_offset(&p->in_data.data, 0);
+               prs_switch_type(&p->in_data.data, UNMARSHALL);
+
+               /*
+                * Process the complete data stream here.
+                */
+
+               free_pipe_context(p);
+
+               if(pipe_init_outgoing_data(p))
+                       ret = api_pipe_request(p);
+
+               free_pipe_context(p);
+
+               /*
+                * We have consumed the whole data stream. Set back to
+                * marshalling and set the offset back to the start of
+                * the buffer to re-use it (we could also do a prs_mem_free()
+                * and then re_init on the next start of PDU. Not sure which
+                * is best here.... JRA.
+                */
+
+               prs_switch_type(&p->in_data.data, MARSHALL);
+               prs_set_offset(&p->in_data.data, 0);
+               return ret;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Processes a finished PDU stored in current_in_pdu. The RPC_HEADER has
+ already been parsed and stored in p->hdr.
+****************************************************************************/
+
+static ssize_t process_complete_pdu(pipes_struct *p)
+{
+       prs_struct rpc_in;
+       size_t data_len = p->in_data.pdu_received_len;
+       char *data_p = (char *)&p->in_data.current_in_pdu[0];
+       BOOL reply = False;
+
+       if(p->fault_state) {
+               DEBUG(10,("process_complete_pdu: pipe %s in fault state.\n",
+                       p->name ));
+               set_incoming_fault(p);
+               setup_fault_pdu(p, NT_STATUS(0x1c010002));
+               return (ssize_t)data_len;
+       }
+
+       prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+
+       /*
+        * Ensure we're using the corrent endianness for both the 
+        * RPC header flags and the raw data we will be reading from.
+        */
+
+       prs_set_endian_data( &rpc_in, p->endian);
+       prs_set_endian_data( &p->in_data.data, p->endian);
+
+       prs_give_memory( &rpc_in, data_p, (uint32)data_len, False);
+
+       DEBUG(10,("process_complete_pdu: processing packet type %u\n",
+                       (unsigned int)p->hdr.pkt_type ));
+
+       switch (p->hdr.pkt_type) {
+               case RPC_BIND:
+               case RPC_ALTCONT:
+                       /*
+                        * We assume that a pipe bind is only in one pdu.
+                        */
+                       if(pipe_init_outgoing_data(p))
+                               reply = api_pipe_bind_req(p, &rpc_in);
+                       break;
+               case RPC_BINDRESP:
+                       /*
+                        * We assume that a pipe bind_resp is only in one pdu.
+                        */
+                       if(pipe_init_outgoing_data(p))
+                               reply = api_pipe_bind_auth_resp(p, &rpc_in);
+                       break;
+               case RPC_REQUEST:
+                       reply = process_request_pdu(p, &rpc_in);
+                       break;
+               default:
+                       DEBUG(0,("process_complete_pdu: Unknown rpc type = %u received.\n", (unsigned int)p->hdr.pkt_type ));
+                       break;
+       }
+
+       /* Reset to little endian. Probably don't need this but it won't hurt. */
+       prs_set_endian_data( &p->in_data.data, RPC_LITTLE_ENDIAN);
+
+       if (!reply) {
+               DEBUG(3,("process_complete_pdu: DCE/RPC fault sent on pipe %s\n", p->pipe_srv_name));
+               set_incoming_fault(p);
+               setup_fault_pdu(p, NT_STATUS(0x1c010002));
+               prs_mem_free(&rpc_in);
+       } else {
+               /*
+                * Reset the lengths. We're ready for a new pdu.
+                */
+               p->in_data.pdu_needed_len = 0;
+               p->in_data.pdu_received_len = 0;
+       }
+
+       prs_mem_free(&rpc_in);
+       return (ssize_t)data_len;
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe. Processes the data in pdu sized units.
+****************************************************************************/
+
+static ssize_t process_incoming_data(pipes_struct *p, char *data, size_t n)
+{
+       size_t data_to_copy = MIN(n, MAX_PDU_FRAG_LEN - p->in_data.pdu_received_len);
+
+       DEBUG(10,("process_incoming_data: Start: pdu_received_len = %u, pdu_needed_len = %u, incoming data = %u\n",
+               (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len,
+               (unsigned int)n ));
+
+       if(data_to_copy == 0) {
+               /*
+                * This is an error - data is being received and there is no
+                * space in the PDU. Free the received data and go into the fault state.
+                */
+               DEBUG(0,("process_incoming_data: No space in incoming pdu buffer. Current size = %u \
+incoming data size = %u\n", (unsigned int)p->in_data.pdu_received_len, (unsigned int)n ));
+               set_incoming_fault(p);
+               return -1;
+       }
+
+       /*
+        * If we have no data already, wait until we get at least a RPC_HEADER_LEN
+        * number of bytes before we can do anything.
+        */
+
+       if((p->in_data.pdu_needed_len == 0) && (p->in_data.pdu_received_len < RPC_HEADER_LEN)) {
+               /*
+                * Always return here. If we have more data then the RPC_HEADER
+                * will be processed the next time around the loop.
+                */
+               return fill_rpc_header(p, data, data_to_copy);
+       }
+
+       /*
+        * At this point we know we have at least an RPC_HEADER_LEN amount of data
+        * stored in current_in_pdu.
+        */
+
+       /*
+        * If pdu_needed_len is zero this is a new pdu. 
+        * Unmarshall the header so we know how much more
+        * data we need, then loop again.
+        */
+
+       if(p->in_data.pdu_needed_len == 0)
+               return unmarshall_rpc_header(p);
+
+       /*
+        * Ok - at this point we have a valid RPC_HEADER in p->hdr.
+        * Keep reading until we have a full pdu.
+        */
+
+       data_to_copy = MIN(data_to_copy, p->in_data.pdu_needed_len);
+
+       /*
+        * Copy as much of the data as we need into the current_in_pdu buffer.
+        */
+
+       memcpy( (char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, data_to_copy);
+       p->in_data.pdu_received_len += data_to_copy;
+
+       /*
+        * Do we have a complete PDU ?
+        */
+
+       if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len)
+               return process_complete_pdu(p);
+
+       DEBUG(10,("process_incoming_data: not a complete PDU yet. pdu_received_len = %u, pdu_needed_len = %u\n",
+               (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len ));
+
+       return (ssize_t)data_to_copy;
+
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe.
+****************************************************************************/
+
+ssize_t write_to_pipe(smb_np_struct *p, char *data, size_t n)
+{
+       DEBUG(6,("write_to_pipe: %x", p->pnum));
+
+       DEBUG(6,(" name: %s open: %s len: %d\n",
+                p->name, BOOLSTR(p->open), (int)n));
+
+       dump_data(50, data, n);
+
+       return p->namedpipe_write(p->np_state, data, n);
+}
+
+/****************************************************************************
+ Accepts incoming data on an internal rpc pipe.
+****************************************************************************/
+
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n)
+{
+       pipes_struct *p = (pipes_struct*)np_conn;
+       size_t data_left = n;
+
+       while(data_left) {
+               ssize_t data_used;
+
+               DEBUG(10,("write_to_pipe: data_left = %u\n", (unsigned int)data_left ));
+
+               data_used = process_incoming_data(p, data, data_left);
+
+               DEBUG(10,("write_to_pipe: data_used = %d\n", (int)data_used ));
+
+               if(data_used < 0)
+                       return -1;
+
+               data_left -= data_used;
+               data += data_used;
+       }       
+
+       return n;
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+ssize_t read_from_pipe(smb_np_struct *p, char *data, size_t n,
+               BOOL *is_data_outstanding)
+{
+       if (!p || !p->open) {
+               DEBUG(0,("read_from_pipe: pipe not open\n"));
+               return -1;              
+       }
+
+       DEBUG(6,("read_from_pipe: %x", p->pnum));
+
+       return p->namedpipe_read(p->np_state, data, n, is_data_outstanding);
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+               BOOL *is_data_outstanding)
+{
+       pipes_struct *p = (pipes_struct*)np_conn;
+       uint32 pdu_remaining = 0;
+       ssize_t data_returned = 0;
+
+       if (!p) {
+               DEBUG(0,("read_from_pipe: pipe not open\n"));
+               return -1;              
+       }
+
+       DEBUG(6,(" name: %s len: %u\n", p->name, (unsigned int)n));
+
+       /*
+        * We cannot return more than one PDU length per
+        * read request.
+        */
+
+       /*
+        * This condition should result in the connection being closed.  
+        * Netapp filers seem to set it to 0xffff which results in domain
+        * authentications failing.  Just ignore it so things work.
+        */
+
+       if(n > MAX_PDU_FRAG_LEN) {
+                DEBUG(5,("read_from_pipe: too large read (%u) requested on \
+pipe %s. We can only service %d sized reads.\n", (unsigned int)n, p->name, MAX_PDU_FRAG_LEN ));
+       }
+
+       /*
+        * Determine if there is still data to send in the
+        * pipe PDU buffer. Always send this first. Never
+        * send more than is left in the current PDU. The
+        * client should send a new read request for a new
+        * PDU.
+        */
+
+       if((pdu_remaining = p->out_data.current_pdu_len - p->out_data.current_pdu_sent) > 0) {
+               data_returned = (ssize_t)MIN(n, pdu_remaining);
+
+               DEBUG(10,("read_from_pipe: %s: current_pdu_len = %u, current_pdu_sent = %u \
+returning %d bytes.\n", p->name, (unsigned int)p->out_data.current_pdu_len, 
+                       (unsigned int)p->out_data.current_pdu_sent, (int)data_returned));
+
+               memcpy( data, &p->out_data.current_pdu[p->out_data.current_pdu_sent], (size_t)data_returned);
+               p->out_data.current_pdu_sent += (uint32)data_returned;
+               goto out;
+       }
+
+       /*
+        * At this point p->current_pdu_len == p->current_pdu_sent (which
+        * may of course be zero if this is the first return fragment.
+        */
+
+       DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length \
+= %u, prs_offset(&p->out_data.rdata) = %u.\n",
+               p->name, (int)p->fault_state, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) ));
+
+       if(p->out_data.data_sent_length >= prs_offset(&p->out_data.rdata)) {
+               /*
+                * We have sent all possible data, return 0.
+                */
+               data_returned = 0;
+               goto out;
+       }
+
+       /*
+        * We need to create a new PDU from the data left in p->rdata.
+        * Create the header/data/footers. This also sets up the fields
+        * p->current_pdu_len, p->current_pdu_sent, p->data_sent_length
+        * and stores the outgoing PDU in p->current_pdu.
+        */
+
+       if(!create_next_pdu(p)) {
+               DEBUG(0,("read_from_pipe: %s: create_next_pdu failed.\n", p->name));
+               return -1;
+       }
+
+       data_returned = MIN(n, p->out_data.current_pdu_len);
+
+       memcpy( data, p->out_data.current_pdu, (size_t)data_returned);
+       p->out_data.current_pdu_sent += (uint32)data_returned;
+
+  out:
+
+       (*is_data_outstanding) = p->out_data.current_pdu_len > n;
+       return data_returned;
+}
+
+/****************************************************************************
+ Wait device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL wait_rpc_pipe_hnd_state(smb_np_struct *p, uint16 priority)
+{
+       if (p == NULL)
+               return False;
+
+       if (p->open) {
+               DEBUG(3,("wait_rpc_pipe_hnd_state: Setting pipe wait state priority=%x on pipe (name=%s)\n",
+                        priority, p->name));
+
+               p->priority = priority;
+               
+               return True;
+       } 
+
+       DEBUG(3,("wait_rpc_pipe_hnd_state: Error setting pipe wait state priority=%x (name=%s)\n",
+                priority, p->name));
+       return False;
+}
+
+
+/****************************************************************************
+ Set device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL set_rpc_pipe_hnd_state(smb_np_struct *p, uint16 device_state)
+{
+       if (p == NULL)
+               return False;
+
+       if (p->open) {
+               DEBUG(3,("set_rpc_pipe_hnd_state: Setting pipe device state=%x on pipe (name=%s)\n",
+                        device_state, p->name));
+
+               p->device_state = device_state;
+               
+               return True;
+       } 
+
+       DEBUG(3,("set_rpc_pipe_hnd_state: Error setting pipe device state=%x (name=%s)\n",
+                device_state, p->name));
+       return False;
+}
+
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+BOOL close_rpc_pipe_hnd(smb_np_struct *p)
+{
+       if (!p) {
+               DEBUG(0,("Invalid pipe in close_rpc_pipe_hnd\n"));
+               return False;
+       }
+
+       p->namedpipe_close(p->np_state);
+
+       bitmap_clear(bmap, p->pnum - pipe_handle_offset);
+
+       pipes_open--;
+
+       DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n", 
+                p->name, p->pnum, pipes_open));  
+
+       DLIST_REMOVE(Pipes, p);
+
+       ZERO_STRUCTP(p);
+
+       SAFE_FREE(p);
+
+       return True;
+}
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn)
+{
+       pipes_struct *p = (pipes_struct *)np_conn;
+       if (!p) {
+               DEBUG(0,("Invalid pipe in close_internal_rpc_pipe_hnd\n"));
+               return False;
+       }
+
+       prs_mem_free(&p->out_data.rdata);
+       prs_mem_free(&p->in_data.data);
+
+       if (p->mem_ctx)
+               talloc_destroy(p->mem_ctx);
+
+       /* Free the handles database. */
+       close_policy_by_pipe(p);
+
+       delete_nt_token(&p->pipe_user.nt_user_token);
+       SAFE_FREE(p->pipe_user.groups);
+
+       DLIST_REMOVE(InternalPipes, p);
+
+       p->conn->num_files_open--;
+
+       ZERO_STRUCTP(p);
+
+       SAFE_FREE(p);
+       
+       return True;
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle in a buffer and an offset.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe_p(char *buf, int where)
+{
+       int pnum = SVAL(buf,where);
+
+       if (chain_p)
+               return chain_p;
+
+       return get_rpc_pipe(pnum);
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe(int pnum)
+{
+       smb_np_struct *p;
+
+       DEBUG(4,("search for pipe pnum=%x\n", pnum));
+
+       for (p=Pipes;p;p=p->next)
+               DEBUG(5,("pipe name %s pnum=%x (pipes_open=%d)\n", 
+                         p->name, p->pnum, pipes_open));  
+
+       for (p=Pipes;p;p=p->next) {
+               if (p->pnum == pnum) {
+                       chain_p = p;
+                       return p;
+               }
+       }
+
+       return NULL;
+}
diff --git a/source4/rpc_server/srv_reg.c b/source4/rpc_server/srv_reg.c
new file mode 100644 (file)
index 0000000..8fc1d42
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Marc Jacobsen                            2000,
+ *  Copyright (C) Jeremy Allison                   2001,
+ *  Copyright (C) Gerald Carter                    2002,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *
+ *  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.
+ */
+
+/* This is the interface for the registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_reg_close
+ ********************************************************************/
+
+static BOOL api_reg_close(pipes_struct *p)
+{
+       REG_Q_CLOSE q_u;
+       REG_R_CLOSE r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg unknown 1 */
+       if(!reg_io_q_close("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_close(p, &q_u, &r_u);
+
+       if(!reg_io_r_close("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_open_khlm
+ ********************************************************************/
+
+static BOOL api_reg_open_hklm(pipes_struct *p)
+{
+       REG_Q_OPEN_HKLM q_u;
+       REG_R_OPEN_HKLM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg open */
+       if(!reg_io_q_open_hklm("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_open_hklm(p, &q_u, &r_u);
+
+       if(!reg_io_r_open_hklm("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_open_khu
+ ********************************************************************/
+
+static BOOL api_reg_open_hku(pipes_struct *p)
+{
+       REG_Q_OPEN_HKU q_u;
+       REG_R_OPEN_HKU r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg open */
+       if(!reg_io_q_open_hku("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_open_hku(p, &q_u, &r_u);
+
+       if(!reg_io_r_open_hku("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_open_khcr
+ ********************************************************************/
+
+static BOOL api_reg_open_hkcr(pipes_struct *p)
+{
+       REG_Q_OPEN_HKCR q_u;
+       REG_R_OPEN_HKCR r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg open */
+       if(!reg_io_q_open_hkcr("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_open_hkcr(p, &q_u, &r_u);
+
+       if(!reg_io_r_open_hkcr("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ api_reg_open_entry
+ ********************************************************************/
+
+static BOOL api_reg_open_entry(pipes_struct *p)
+{
+       REG_Q_OPEN_ENTRY q_u;
+       REG_R_OPEN_ENTRY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg open entry */
+       if(!reg_io_q_open_entry("", &q_u, data, 0))
+               return False;
+
+       /* construct reply. */
+       r_u.status = _reg_open_entry(p, &q_u, &r_u);
+
+       if(!reg_io_r_open_entry("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_info
+ ********************************************************************/
+
+static BOOL api_reg_info(pipes_struct *p)
+{
+       REG_Q_INFO q_u;
+       REG_R_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg unknown 0x11*/
+       if(!reg_io_q_info("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_info(p, &q_u, &r_u);
+
+       if(!reg_io_r_info("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_shutdown(pipes_struct *p)
+{
+       REG_Q_SHUTDOWN q_u;
+       REG_R_SHUTDOWN r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg shutdown */
+       if(!reg_io_q_shutdown("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_shutdown(p, &q_u, &r_u);
+
+       if(!reg_io_r_shutdown("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_abort_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_abort_shutdown(pipes_struct *p)
+{
+       REG_Q_ABORT_SHUTDOWN q_u;
+       REG_R_ABORT_SHUTDOWN r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the reg shutdown */
+       if(!reg_io_q_abort_shutdown("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_abort_shutdown(p, &q_u, &r_u);
+
+       if(!reg_io_r_abort_shutdown("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ api_reg_query_key
+ ********************************************************************/
+
+static BOOL api_reg_query_key(pipes_struct *p)
+{
+       REG_Q_QUERY_KEY q_u;
+       REG_R_QUERY_KEY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!reg_io_q_query_key("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_query_key(p, &q_u, &r_u);
+
+       if(!reg_io_r_query_key("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_unknown_1a
+ ********************************************************************/
+
+static BOOL api_reg_unknown_1a(pipes_struct *p)
+{
+       REG_Q_UNKNOWN_1A q_u;
+       REG_R_UNKNOWN_1A r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!reg_io_q_unknown_1a("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_unknown_1a(p, &q_u, &r_u);
+
+       if(!reg_io_r_unknown_1a("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_enum_key
+ ********************************************************************/
+
+static BOOL api_reg_enum_key(pipes_struct *p)
+{
+       REG_Q_ENUM_KEY q_u;
+       REG_R_ENUM_KEY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!reg_io_q_enum_key("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _reg_enum_key(p, &q_u, &r_u);
+
+       if(!reg_io_r_enum_key("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_enum_value
+ ********************************************************************/
+
+static BOOL api_reg_enum_value(pipes_struct *p)
+{
+       REG_Q_ENUM_VALUE q_u;
+       REG_R_ENUM_VALUE r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!reg_io_q_enum_val("", &q_u, data, 0))
+               return False;
+               
+       r_u.status = _reg_enum_value(p, &q_u, &r_u);
+
+       if(!reg_io_r_enum_val("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_reg_save_key
+ ********************************************************************/
+
+static BOOL api_reg_save_key(pipes_struct *p)
+{
+       REG_Q_SAVE_KEY q_u;
+       REG_R_SAVE_KEY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!reg_io_q_save_key("", &q_u, data, 0))
+               return False;
+               
+       r_u.status = _reg_save_key(p, &q_u, &r_u);
+
+       if(!reg_io_r_save_key("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+
+
+/*******************************************************************
+ array of \PIPE\reg operations
+ ********************************************************************/
+
+#ifdef RPC_REG_DYNAMIC
+int init_module(void)
+#else
+int rpc_reg_init(void)
+#endif
+{
+  static struct api_struct api_reg_cmds[] =
+    {
+      { "REG_CLOSE"              , REG_CLOSE              , api_reg_close            },
+      { "REG_OPEN_ENTRY"         , REG_OPEN_ENTRY         , api_reg_open_entry       },
+      { "REG_OPEN_HKCR"          , REG_OPEN_HKCR          , api_reg_open_hkcr        },
+      { "REG_OPEN_HKLM"          , REG_OPEN_HKLM          , api_reg_open_hklm        },
+      { "REG_OPEN_HKU"           , REG_OPEN_HKU           , api_reg_open_hku         },
+      { "REG_ENUM_KEY"           , REG_ENUM_KEY           , api_reg_enum_key         },
+      { "REG_ENUM_VALUE"         , REG_ENUM_VALUE         , api_reg_enum_value       },
+      { "REG_QUERY_KEY"          , REG_QUERY_KEY          , api_reg_query_key        },
+      { "REG_INFO"               , REG_INFO               , api_reg_info             },
+      { "REG_SHUTDOWN"           , REG_SHUTDOWN           , api_reg_shutdown         },
+      { "REG_ABORT_SHUTDOWN"     , REG_ABORT_SHUTDOWN     , api_reg_abort_shutdown   },
+      { "REG_UNKNOWN_1A"         , REG_UNKNOWN_1A         , api_reg_unknown_1a       },
+      { "REG_SAVE_KEY"           , REG_SAVE_KEY           , api_reg_save_key         }
+    };
+  return rpc_pipe_register_commands("winreg", "winreg", api_reg_cmds,
+                                   sizeof(api_reg_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_reg_nt.c b/source4/rpc_server/srv_reg_nt.c
new file mode 100644 (file)
index 0000000..5632544
--- /dev/null
@@ -0,0 +1,664 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell               1992-1997.
+ *  Copyright (C) Luke Kenneth Casson Leighton  1996-1997.
+ *  Copyright (C) Paul Ashton                        1997.
+ *  Copyright (C) Jeremy Allison                     2001.
+ *  Copyright (C) Gerald Carter                      2002.
+ *
+ *  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.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define REGSTR_PRODUCTTYPE             "ProductType"
+#define REG_PT_WINNT                   "WinNT"
+#define REG_PT_LANMANNT                        "LanmanNT"
+#define REG_PT_SERVERNT                        "ServerNT"
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+
+static REGISTRY_KEY *regkeys_list;
+
+
+/******************************************************************
+ free() function for REGISTRY_KEY
+ *****************************************************************/
+static void free_regkey_info(void *ptr)
+{
+       REGISTRY_KEY *info = (REGISTRY_KEY*)ptr;
+       
+       DLIST_REMOVE(regkeys_list, info);
+
+       SAFE_FREE(info);
+}
+
+/******************************************************************
+ Find a registry key handle and return a REGISTRY_KEY
+ *****************************************************************/
+
+static REGISTRY_KEY *find_regkey_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+       REGISTRY_KEY *regkey = NULL;
+
+       if(!find_policy_by_hnd(p,hnd,(void **)&regkey)) {
+               DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: "));
+               return NULL;
+       }
+
+       return regkey;
+}
+
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle 
+ Note that P should be valid & hnd should already have space
+ When we open a key, we store the full path to the key as 
+ HK[LM|U]\<key>\<key>\...
+ *******************************************************************/
+static NTSTATUS open_registry_key(pipes_struct *p, POLICY_HND *hnd, REGISTRY_KEY *parent,
+                               const char *subkeyname, uint32 access_granted  )
+{
+       REGISTRY_KEY    *regkey = NULL;
+       NTSTATUS        result = NT_STATUS_OK;
+       REGSUBKEY_CTR   subkeys;
+       pstring         subkeyname2;
+       int             subkey_len;
+       
+       DEBUG(7,("open_registry_key: name = [%s][%s]\n", 
+               parent ? parent->name : "NULL", subkeyname));
+
+       /* strip any trailing '\'s */
+       pstrcpy( subkeyname2, subkeyname );
+       subkey_len = strlen ( subkeyname2 );
+       if ( subkey_len && subkeyname2[subkey_len-1] == '\\' )
+               subkeyname2[subkey_len-1] = '\0';
+
+       if ((regkey=(REGISTRY_KEY*)malloc(sizeof(REGISTRY_KEY))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+               
+       ZERO_STRUCTP( regkey );
+       
+       /* 
+        * very crazy, but regedit.exe on Win2k will attempt to call 
+        * REG_OPEN_ENTRY with a keyname of "".  We should return a new 
+        * (second) handle here on the key->name.  regedt32.exe does 
+        * not do this stupidity.   --jerry
+        */
+       
+       if ( !subkey_len ) {
+               pstrcpy( regkey->name, parent->name );  
+       }
+       else {
+               pstrcpy( regkey->name, "" );
+               if ( parent ) {
+                       pstrcat( regkey->name, parent->name );
+                       pstrcat( regkey->name, "\\" );
+               }
+               pstrcat( regkey->name, subkeyname2 );
+       }
+       
+       /* Look up the table of registry I/O operations */
+
+       if ( !(regkey->hook = reghook_cache_find( regkey->name )) ) {
+               DEBUG(0,("open_registry_key: Failed to assigned a REGISTRY_HOOK to [%s]\n",
+                       regkey->name ));
+               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+       }
+       
+       /* check if the path really exists; failed is indicated by -1 */
+       /* if the subkey count failed, bail out */
+
+       ZERO_STRUCTP( &subkeys );
+       
+       regsubkey_ctr_init( &subkeys );
+       
+       if ( fetch_reg_keys( regkey, &subkeys ) == -1 )  {
+       
+               /* don't really know what to return here */
+               result = NT_STATUS_NO_SUCH_FILE;
+       }
+       else {
+               /* 
+                * This would previously return NT_STATUS_TOO_MANY_SECRETS
+                * that doesn't sound quite right to me  --jerry
+                */
+               
+               if ( !create_policy_hnd( p, hnd, free_regkey_info, regkey ) )
+                       result = NT_STATUS_OBJECT_NAME_NOT_FOUND; 
+       }
+       
+       /* clean up */
+
+       regsubkey_ctr_destroy( &subkeys );
+       
+       if ( ! NT_STATUS_IS_OK(result) )
+               SAFE_FREE( regkey );
+       else
+               DLIST_ADD( regkeys_list, regkey );
+
+       
+       DEBUG(7,("open_registry_key: exit\n"));
+
+       return result;
+}
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle 
+ Note that P should be valid & hnd should already have space
+ *******************************************************************/
+
+static BOOL close_registry_key(pipes_struct *p, POLICY_HND *hnd)
+{
+       REGISTRY_KEY *regkey = find_regkey_index_by_hnd(p, hnd);
+       
+       if ( !regkey ) {
+               DEBUG(2,("close_registry_key: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+               return False;
+       }
+       
+       close_policy_hnd(p, hnd);
+       
+       return True;
+}
+
+/********************************************************************
+ retrieve information about the subkeys
+ *******************************************************************/
+static BOOL get_subkey_information( REGISTRY_KEY *key, uint32 *maxnum, uint32 *maxlen )
+{
+       int             num_subkeys, i;
+       uint32          max_len;
+       REGSUBKEY_CTR   subkeys;
+       uint32          len;
+       
+       if ( !key )
+               return False;
+
+       ZERO_STRUCTP( &subkeys );
+       
+       regsubkey_ctr_init( &subkeys ); 
+          
+       if ( fetch_reg_keys( key, &subkeys ) == -1 )
+               return False;
+
+       /* find the longest string */
+       
+       max_len = 0;
+       num_subkeys = regsubkey_ctr_numkeys( &subkeys );
+       
+       for ( i=0; i<num_subkeys; i++ ) {
+               len = strlen( regsubkey_ctr_specific_key(&subkeys, i) );
+               max_len = MAX(max_len, len);
+       }
+
+       *maxnum = num_subkeys;
+       *maxlen = max_len*2;
+       
+       regsubkey_ctr_destroy( &subkeys );
+       
+       return True;
+}
+
+/********************************************************************
+ retrieve information about the values.  We don't store values 
+ here.  The registry tdb is intended to be a frontend to oether 
+ Samba tdb's (such as ntdrivers.tdb).
+ *******************************************************************/
+static BOOL get_value_information( REGISTRY_KEY *key, uint32 *maxnum, 
+                                    uint32 *maxlen, uint32 *maxsize )
+{
+       REGVAL_CTR      values;
+       REGISTRY_VALUE  *val;
+       uint32          sizemax, lenmax;
+       int             i, num_values;
+       
+       if ( !key )
+               return False;
+
+
+       ZERO_STRUCTP( &values );
+       
+       regval_ctr_init( &values );
+       
+       if ( fetch_reg_values( key, &values ) == -1 )
+               return False;
+       
+       lenmax = sizemax = 0;
+       num_values = regval_ctr_numvals( &values );
+       
+       val = regval_ctr_specific_value( &values, 0 );
+       
+       for ( i=0; i<num_values && val; i++ ) 
+       {
+               lenmax  = MAX(lenmax,  strlen(val->valuename)+1 );
+               sizemax = MAX(sizemax, val->size );
+               
+               val = regval_ctr_specific_value( &values, i );
+       }
+
+       *maxnum   = num_values;
+       *maxlen   = lenmax;
+       *maxsize  = sizemax;
+       
+       regval_ctr_destroy( &values );
+       
+       return True;
+}
+
+
+/********************************************************************
+ reg_close
+ ********************************************************************/
+
+NTSTATUS _reg_close(pipes_struct *p, REG_Q_CLOSE *q_u, REG_R_CLOSE *r_u)
+{
+       /* set up the REG unknown_1 response */
+       ZERO_STRUCT(r_u->pol);
+
+       /* close the policy handle */
+       if (!close_registry_key(p, &q_u->pol))
+               return NT_STATUS_OBJECT_NAME_INVALID;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hklm(pipes_struct *p, REG_Q_OPEN_HKLM *q_u, REG_R_OPEN_HKLM *r_u)
+{
+       return open_registry_key( p, &r_u->pol, NULL, KEY_HKLM, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hkcr(pipes_struct *p, REG_Q_OPEN_HKCR *q_u, REG_R_OPEN_HKCR *r_u)
+{
+       return open_registry_key( p, &r_u->pol, NULL, KEY_HKCR, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hku(pipes_struct *p, REG_Q_OPEN_HKU *q_u, REG_R_OPEN_HKU *r_u)
+{
+       return open_registry_key( p, &r_u->pol, NULL, KEY_HKU, 0x0 );
+}
+
+/*******************************************************************
+ reg_reply_open_entry
+ ********************************************************************/
+
+NTSTATUS _reg_open_entry(pipes_struct *p, REG_Q_OPEN_ENTRY *q_u, REG_R_OPEN_ENTRY *r_u)
+{
+       POLICY_HND pol;
+       fstring name;
+       REGISTRY_KEY *key = find_regkey_index_by_hnd(p, &q_u->pol);
+       NTSTATUS result;
+
+       DEBUG(5,("reg_open_entry: Enter\n"));
+
+       if ( !key )
+               return NT_STATUS_INVALID_HANDLE;
+
+       rpcstr_pull(name,q_u->uni_name.buffer,sizeof(name),q_u->uni_name.uni_str_len*2,0);
+       
+       result = open_registry_key( p, &pol, key, name, 0x0 );
+       
+       init_reg_r_open_entry( r_u, &pol, result );
+
+       DEBUG(5,("reg_open_entry: Exit\n"));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ reg_reply_info
+ ********************************************************************/
+
+NTSTATUS _reg_info(pipes_struct *p, REG_Q_INFO *q_u, REG_R_INFO *r_u)
+{
+       NTSTATUS                status = NT_STATUS_NO_SUCH_FILE;
+       fstring                 name;
+       const char              *value_ascii = "";
+       fstring                 value;
+       int                     value_length;
+       REGISTRY_KEY            *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       REGISTRY_VALUE          *val = NULL;
+       REGVAL_CTR              regvals;
+       int                     i;
+
+       DEBUG(5,("_reg_info: Enter\n"));
+
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;
+               
+       DEBUG(7,("_reg_info: policy key name = [%s]\n", regkey->name));
+       
+       rpcstr_pull(name, q_u->uni_type.buffer, sizeof(name), q_u->uni_type.uni_str_len*2, 0);
+
+       DEBUG(5,("reg_info: looking up value: [%s]\n", name));
+
+       ZERO_STRUCTP( &regvals );
+       
+       regval_ctr_init( &regvals );
+
+       /* couple of hard coded registry values */
+       
+       if ( strequal(name, "RefusePasswordChange") ) {
+               if ( (val = (REGISTRY_VALUE*)malloc(sizeof(REGISTRY_VALUE))) == NULL ) {
+                       DEBUG(0,("_reg_info: malloc() failed!\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ZERO_STRUCTP( val );
+       
+               goto out;
+       }
+
+       if ( strequal(name, REGSTR_PRODUCTTYPE) ) {
+               /* This makes the server look like a member server to clients */
+               /* which tells clients that we have our own local user and    */
+               /* group databases and helps with ACL support.                */
+               
+               switch (lp_server_role()) {
+                       case ROLE_DOMAIN_PDC:
+                       case ROLE_DOMAIN_BDC:
+                               value_ascii = REG_PT_LANMANNT;
+                               break;
+                       case ROLE_STANDALONE:
+                               value_ascii = REG_PT_SERVERNT;
+                               break;
+                       case ROLE_DOMAIN_MEMBER:
+                               value_ascii = REG_PT_WINNT;
+                               break;
+               }
+               value_length = push_ucs2(value, value, value_ascii,
+                                        sizeof(value),
+                                        STR_TERMINATE|STR_NOALIGN);
+               regval_ctr_addvalue(&regvals, REGSTR_PRODUCTTYPE, REG_SZ,
+                                   value, value_length);
+               
+               val = dup_registry_value( regval_ctr_specific_value( &regvals, 0 ) );
+               
+               status = NT_STATUS_OK;
+               
+               goto out;
+       }
+
+       /* else fall back to actually looking up the value */
+       
+       for ( i=0; fetch_reg_values_specific(regkey, &val, i); i++ ) 
+       {
+               DEBUG(10,("_reg_info: Testing value [%s]\n", val->valuename));
+               if ( StrCaseCmp( val->valuename, name ) == 0 ) {
+                       DEBUG(10,("_reg_info: Found match for value [%s]\n", name));
+                       status = NT_STATUS_OK;
+                       break;
+               }
+               
+               free_registry_value( val );
+       }
+
+  
+out:
+       new_init_reg_r_info(q_u->ptr_buf, r_u, val, status);
+       
+       regval_ctr_destroy( &regvals );
+       free_registry_value( val );
+
+       DEBUG(5,("_reg_info: Exit\n"));
+
+       return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_QUERY_KEY
+ ****************************************************************************/
+NTSTATUS _reg_query_key(pipes_struct *p, REG_Q_QUERY_KEY *q_u, REG_R_QUERY_KEY *r_u)
+{
+       NTSTATUS        status = NT_STATUS_OK;
+       REGISTRY_KEY    *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       
+       DEBUG(5,("_reg_query_key: Enter\n"));
+       
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;        
+       
+       if ( !get_subkey_information( regkey, &r_u->num_subkeys, &r_u->max_subkeylen ) )
+               return NT_STATUS_ACCESS_DENIED;
+               
+       if ( !get_value_information( regkey, &r_u->num_values, &r_u->max_valnamelen, &r_u->max_valbufsize ) )
+               return NT_STATUS_ACCESS_DENIED; 
+
+               
+       r_u->sec_desc = 0x00000078;     /* size for key's sec_desc */
+       
+       /* Win9x set this to 0x0 since it does not keep timestamps.
+          Doing the same here for simplicity   --jerry */
+          
+       ZERO_STRUCT(r_u->mod_time);     
+
+       DEBUG(5,("_reg_query_key: Exit\n"));
+       
+       return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_UNKNOWN_1A
+ ****************************************************************************/
+NTSTATUS _reg_unknown_1a(pipes_struct *p, REG_Q_UNKNOWN_1A *q_u, REG_R_UNKNOWN_1A *r_u)
+{
+       NTSTATUS        status = NT_STATUS_OK;
+       REGISTRY_KEY    *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       
+       DEBUG(5,("_reg_unknown_1a: Enter\n"));
+       
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;        
+       
+       r_u->unknown = 0x00000005;      /* seems to be consistent...no idea what it means */
+       
+       DEBUG(5,("_reg_unknown_1a: Exit\n"));
+       
+       return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_ENUM_KEY
+ ****************************************************************************/
+NTSTATUS _reg_enum_key(pipes_struct *p, REG_Q_ENUM_KEY *q_u, REG_R_ENUM_KEY *r_u)
+{
+       NTSTATUS        status = NT_STATUS_OK;
+       REGISTRY_KEY    *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       char            *subkey = NULL;
+       
+       
+       DEBUG(5,("_reg_enum_key: Enter\n"));
+       
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;        
+
+       DEBUG(8,("_reg_enum_key: enumerating key [%s]\n", regkey->name));
+       
+       if ( !fetch_reg_keys_specific( regkey, &subkey, q_u->key_index ) )
+       {
+               status = NT_STATUS_NO_MORE_ENTRIES;
+               goto done;
+       }
+       
+       DEBUG(10,("_reg_enum_key: retrieved subkey named [%s]\n", subkey));
+       
+       /* subkey has the string name now */
+       
+       init_reg_r_enum_key( r_u, subkey, q_u->unknown_1, q_u->unknown_2 );
+       
+       DEBUG(5,("_reg_enum_key: Exit\n"));
+       
+done:  
+       SAFE_FREE( subkey );
+       return status;
+}
+
+/*****************************************************************************
+ Implementation of REG_ENUM_VALUE
+ ****************************************************************************/
+NTSTATUS _reg_enum_value(pipes_struct *p, REG_Q_ENUM_VALUE *q_u, REG_R_ENUM_VALUE *r_u)
+{
+       NTSTATUS        status = NT_STATUS_OK;
+       REGISTRY_KEY    *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       REGISTRY_VALUE  *val;
+       
+       
+       DEBUG(5,("_reg_enum_value: Enter\n"));
+       
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;        
+
+       DEBUG(8,("_reg_enum_key: enumerating values for key [%s]\n", regkey->name));
+
+       if ( !fetch_reg_values_specific( regkey, &val, q_u->val_index ) )
+       {
+               status = NT_STATUS_NO_MORE_ENTRIES;
+               goto done;
+       }
+       
+       DEBUG(10,("_reg_enum_value: retrieved value named  [%s]\n", val->valuename));
+       
+       /* subkey has the string name now */
+       
+       init_reg_r_enum_val( r_u, val );
+
+
+       DEBUG(5,("_reg_enum_value: Exit\n"));
+       
+done:  
+       free_registry_value( val );
+       
+       return status;
+}
+
+
+/*******************************************************************
+ reg_shutdwon
+ ********************************************************************/
+
+#define SHUTDOWN_R_STRING "-r"
+#define SHUTDOWN_F_STRING "-f"
+
+
+NTSTATUS _reg_shutdown(pipes_struct *p, REG_Q_SHUTDOWN *q_u, REG_R_SHUTDOWN *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       pstring shutdown_script;
+       UNISTR2 unimsg = q_u->uni_msg;
+       pstring message;
+       pstring chkmsg;
+       fstring timeout;
+       fstring r;
+       fstring f;
+       
+       /* message */
+       rpcstr_pull (message, unimsg.buffer, sizeof(message), unimsg.uni_str_len*2,0);
+               /* security check */
+       alpha_strcpy (chkmsg, message, NULL, sizeof(message));
+       /* timeout */
+       snprintf(timeout, sizeof(timeout), "%d", q_u->timeout);
+       /* reboot */
+       snprintf(r, sizeof(r), (q_u->reboot) ? SHUTDOWN_R_STRING : "");
+       /* force */
+       snprintf(f, sizeof(f), (q_u->force) ? SHUTDOWN_F_STRING : "");
+
+       pstrcpy(shutdown_script, lp_shutdown_script());
+
+       if(*shutdown_script) {
+               int shutdown_ret;
+               all_string_sub(shutdown_script, "%m", chkmsg, sizeof(shutdown_script));
+               all_string_sub(shutdown_script, "%t", timeout, sizeof(shutdown_script));
+               all_string_sub(shutdown_script, "%r", r, sizeof(shutdown_script));
+               all_string_sub(shutdown_script, "%f", f, sizeof(shutdown_script));
+               shutdown_ret = smbrun(shutdown_script,NULL);
+               DEBUG(3,("_reg_shutdown: Running the command `%s' gave %d\n",shutdown_script,shutdown_ret));
+       }
+
+       return status;
+}
+
+/*******************************************************************
+ reg_abort_shutdwon
+ ********************************************************************/
+
+NTSTATUS _reg_abort_shutdown(pipes_struct *p, REG_Q_ABORT_SHUTDOWN *q_u, REG_R_ABORT_SHUTDOWN *r_u)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       pstring abort_shutdown_script;
+
+       pstrcpy(abort_shutdown_script, lp_abort_shutdown_script());
+
+       if(*abort_shutdown_script) {
+               int abort_shutdown_ret;
+               abort_shutdown_ret = smbrun(abort_shutdown_script,NULL);
+               DEBUG(3,("_reg_abort_shutdown: Running the command `%s' gave %d\n",abort_shutdown_script,abort_shutdown_ret));
+       }
+
+       return status;
+}
+
+/*******************************************************************
+ REG_SAVE_KEY (0x14)
+ ********************************************************************/
+
+NTSTATUS _reg_save_key(pipes_struct *p, REG_Q_SAVE_KEY  *q_u, REG_R_SAVE_KEY *r_u)
+{
+       REGISTRY_KEY    *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+       
+       DEBUG(5,("_reg_save_key: Enter\n"));
+       
+       /* 
+        * basically this is a no op function which just gverifies 
+        * that the client gave us a valid registry key handle 
+        */
+        
+       if ( !regkey )
+               return NT_STATUS_INVALID_HANDLE;        
+
+       DEBUG(8,("_reg_save_key: berifying backup of key [%s]\n", regkey->name));
+       
+
+       return NT_STATUS_OK;
+}
+
+
diff --git a/source4/rpc_server/srv_samr.c b/source4/rpc_server/srv_samr.c
new file mode 100644 (file)
index 0000000..b75195c
--- /dev/null
@@ -0,0 +1,1510 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Marc Jacobsen                            1999,
+ *  Copyright (C) Jean François Micouleau      1998-2001,
+ *  Copyright (C) Anthony Liguori              2002-2003,
+ *  Copyright (C) Jim McDonough                     2002.
+ *     
+ *     Split into interface and implementation modules by, 
+ *
+ *  Copyright (C) Jeremy Allison                    2001.
+ *  
+ *  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.
+ */
+
+/*
+ * This is the interface to the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_samr_close_hnd
+ ********************************************************************/
+
+static BOOL api_samr_close_hnd(pipes_struct *p)
+{
+       SAMR_Q_CLOSE_HND q_u;
+       SAMR_R_CLOSE_HND r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_close_hnd("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_close_hnd: unable to unmarshall SAMR_Q_CLOSE_HND.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_close_hnd(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_close_hnd("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_close_hnd: unable to marshall SAMR_R_CLOSE_HND.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_open_domain
+ ********************************************************************/
+
+static BOOL api_samr_open_domain(pipes_struct *p)
+{
+       SAMR_Q_OPEN_DOMAIN q_u;
+       SAMR_R_OPEN_DOMAIN r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_open_domain("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_open_domain: unable to unmarshall SAMR_Q_OPEN_DOMAIN.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_open_domain(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_open_domain("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_open_domain: unable to marshall SAMR_R_OPEN_DOMAIN.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_usrdom_pwinfo(pipes_struct *p)
+{
+       SAMR_Q_GET_USRDOM_PWINFO q_u;
+       SAMR_R_GET_USRDOM_PWINFO r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_get_usrdom_pwinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to unmarshall SAMR_Q_GET_USRDOM_PWINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_get_usrdom_pwinfo(p, &q_u, &r_u);
+
+       if(!samr_io_r_get_usrdom_pwinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to marshall SAMR_R_GET_USRDOM_PWINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_set_sec_obj(pipes_struct *p)
+{
+       SAMR_Q_SET_SEC_OBJ q_u;
+       SAMR_R_SET_SEC_OBJ r_u;
+       
+       prs_struct *data  = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!samr_io_q_set_sec_obj("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_sec_obj: unable to unmarshall SAMR_Q_SET_SEC_OBJ.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_set_sec_obj(p, &q_u, &r_u);
+
+       if(!samr_io_r_set_sec_obj("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_sec_obj: unable to marshall SAMR_R_SET_SEC_OBJ.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_query_sec_obj(pipes_struct *p)
+{
+       SAMR_Q_QUERY_SEC_OBJ q_u;
+       SAMR_R_QUERY_SEC_OBJ r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_query_sec_obj("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_sec_obj: unable to unmarshall SAMR_Q_QUERY_SEC_OBJ.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_sec_obj(p, &q_u, &r_u);
+
+       if(!samr_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_sec_obj: unable to marshall SAMR_R_QUERY_SEC_OBJ.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_users
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_users(pipes_struct *p)
+{
+       SAMR_Q_ENUM_DOM_USERS q_u;
+       SAMR_R_ENUM_DOM_USERS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open */
+       if(!samr_io_q_enum_dom_users("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_enum_dom_users: unable to unmarshall SAMR_Q_ENUM_DOM_USERS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_enum_dom_users(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_enum_dom_users("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_enum_dom_users: unable to marshall SAMR_R_ENUM_DOM_USERS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_groups
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_groups(pipes_struct *p)
+{
+       SAMR_Q_ENUM_DOM_GROUPS q_u;
+       SAMR_R_ENUM_DOM_GROUPS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open */
+       if(!samr_io_q_enum_dom_groups("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_enum_dom_groups: unable to unmarshall SAMR_Q_ENUM_DOM_GROUPS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_enum_dom_groups(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_enum_dom_groups("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_enum_dom_groups: unable to marshall SAMR_R_ENUM_DOM_GROUPS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_aliases
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_aliases(pipes_struct *p)
+{
+       SAMR_Q_ENUM_DOM_ALIASES q_u;
+       SAMR_R_ENUM_DOM_ALIASES r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open */
+       if(!samr_io_q_enum_dom_aliases("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_enum_dom_aliases: unable to unmarshall SAMR_Q_ENUM_DOM_ALIASES.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_enum_dom_aliases(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_enum_dom_aliases("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_enum_dom_aliases: unable to marshall SAMR_R_ENUM_DOM_ALIASES.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_dispinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_dispinfo(pipes_struct *p)
+{
+       SAMR_Q_QUERY_DISPINFO q_u;
+       SAMR_R_QUERY_DISPINFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_query_dispinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_dispinfo: unable to unmarshall SAMR_Q_QUERY_DISPINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_dispinfo(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_query_dispinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_dispinfo: unable to marshall SAMR_R_QUERY_DISPINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasinfo(pipes_struct *p)
+{
+       SAMR_Q_QUERY_ALIASINFO q_u;
+       SAMR_R_QUERY_ALIASINFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open */
+       if(!samr_io_q_query_aliasinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_aliasinfo: unable to unmarshall SAMR_Q_QUERY_ALIASINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_aliasinfo(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_query_aliasinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_aliasinfo: unable to marshall SAMR_R_QUERY_ALIASINFO.\n"));
+               return False;
+       }
+  
+       return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_names
+ ********************************************************************/
+
+static BOOL api_samr_lookup_names(pipes_struct *p)
+{
+       SAMR_Q_LOOKUP_NAMES q_u;
+       SAMR_R_LOOKUP_NAMES r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr lookup names */
+       if(!samr_io_q_lookup_names("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_lookup_names: unable to unmarshall SAMR_Q_LOOKUP_NAMES.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_lookup_names(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_lookup_names("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_lookup_names: unable to marshall SAMR_R_LOOKUP_NAMES.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_chgpasswd_user
+ ********************************************************************/
+
+static BOOL api_samr_chgpasswd_user(pipes_struct *p)
+{
+       SAMR_Q_CHGPASSWD_USER q_u;
+       SAMR_R_CHGPASSWD_USER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* unknown 38 command */
+       if (!samr_io_q_chgpasswd_user("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_chgpasswd_user: Failed to unmarshall SAMR_Q_CHGPASSWD_USER.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_chgpasswd_user(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_chgpasswd_user("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_chgpasswd_user: Failed to marshall SAMR_R_CHGPASSWD_USER.\n" ));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_rids
+ ********************************************************************/
+
+static BOOL api_samr_lookup_rids(pipes_struct *p)
+{
+       SAMR_Q_LOOKUP_RIDS q_u;
+       SAMR_R_LOOKUP_RIDS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr lookup names */
+       if(!samr_io_q_lookup_rids("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_lookup_rids: unable to unmarshall SAMR_Q_LOOKUP_RIDS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_lookup_rids(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_lookup_rids("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_lookup_rids: unable to marshall SAMR_R_LOOKUP_RIDS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_open_user
+ ********************************************************************/
+
+static BOOL api_samr_open_user(pipes_struct *p)
+{
+       SAMR_Q_OPEN_USER q_u;
+       SAMR_R_OPEN_USER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 22 */
+       if(!samr_io_q_open_user("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_open_user: unable to unmarshall SAMR_Q_OPEN_USER.\n"));
+               return False;
+       }
+
+       r_u.status = _api_samr_open_user(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_open_user("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_open_user: unable to marshall SAMR_R_OPEN_USER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_userinfo(pipes_struct *p)
+{
+       SAMR_Q_QUERY_USERINFO q_u;
+       SAMR_R_QUERY_USERINFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 24 */
+       if(!samr_io_q_query_userinfo("", &q_u, data, 0)){
+               DEBUG(0,("api_samr_query_userinfo: unable to unmarshall SAMR_Q_QUERY_USERINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_userinfo(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_query_userinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_userinfo: unable to marshall SAMR_R_QUERY_USERINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_usergroups
+ ********************************************************************/
+
+static BOOL api_samr_query_usergroups(pipes_struct *p)
+{
+       SAMR_Q_QUERY_USERGROUPS q_u;
+       SAMR_R_QUERY_USERGROUPS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 32 */
+       if(!samr_io_q_query_usergroups("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_usergroups: unable to unmarshall SAMR_Q_QUERY_USERGROUPS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_usergroups(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_query_usergroups("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_usergroups: unable to marshall SAMR_R_QUERY_USERGROUPS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_query_dom_info(pipes_struct *p)
+{
+       SAMR_Q_QUERY_DOMAIN_INFO q_u;
+       SAMR_R_QUERY_DOMAIN_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 8 command */
+       if(!samr_io_q_query_dom_info("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_dom_info: unable to unmarshall SAMR_Q_QUERY_DOMAIN_INFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_dom_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_query_dom_info("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_dom_info: unable to marshall SAMR_R_QUERY_DOMAIN_INFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_create_user
+ ********************************************************************/
+
+static BOOL api_samr_create_user(pipes_struct *p)
+{
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       SAMR_Q_CREATE_USER q_u;
+       SAMR_R_CREATE_USER r_u;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr create user */
+       if (!samr_io_q_create_user("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_create_user: Unable to unmarshall SAMR_Q_CREATE_USER.\n"));
+               return False;
+       }
+
+       r_u.status=_api_samr_create_user(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_create_user("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_create_user: Unable to marshall SAMR_R_CREATE_USER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_connect_anon
+ ********************************************************************/
+
+static BOOL api_samr_connect_anon(pipes_struct *p)
+{
+       SAMR_Q_CONNECT_ANON q_u;
+       SAMR_R_CONNECT_ANON r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open policy */
+       if(!samr_io_q_connect_anon("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_connect_anon: unable to unmarshall SAMR_Q_CONNECT_ANON.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_connect_anon(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_connect_anon("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_connect_anon: unable to marshall SAMR_R_CONNECT_ANON.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_connect
+ ********************************************************************/
+
+static BOOL api_samr_connect(pipes_struct *p)
+{
+       SAMR_Q_CONNECT q_u;
+       SAMR_R_CONNECT r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open policy */
+       if(!samr_io_q_connect("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_connect: unable to unmarshall SAMR_Q_CONNECT.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_connect(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_connect("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_connect: unable to marshall SAMR_R_CONNECT.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_connect4
+ ********************************************************************/
+
+static BOOL api_samr_connect4(pipes_struct *p)
+{
+       SAMR_Q_CONNECT4 q_u;
+       SAMR_R_CONNECT4 r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open policy */
+       if(!samr_io_q_connect4("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_connect4: unable to unmarshall SAMR_Q_CONNECT4.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_connect4(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_connect4("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_connect4: unable to marshall SAMR_R_CONNECT4.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+static BOOL api_samr_lookup_domain(pipes_struct *p)
+{
+       SAMR_Q_LOOKUP_DOMAIN q_u;
+       SAMR_R_LOOKUP_DOMAIN r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+  
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_lookup_domain("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_lookup_domain: Unable to unmarshall SAMR_Q_LOOKUP_DOMAIN.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_lookup_domain(p, &q_u, &r_u);
+       
+       if(!samr_io_r_lookup_domain("", &r_u, rdata, 0)){
+               DEBUG(0,("api_samr_lookup_domain: Unable to marshall SAMR_R_LOOKUP_DOMAIN.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+static BOOL api_samr_enum_domains(pipes_struct *p)
+{
+       SAMR_Q_ENUM_DOMAINS q_u;
+       SAMR_R_ENUM_DOMAINS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+  
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!samr_io_q_enum_domains("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_enum_domains: Unable to unmarshall SAMR_Q_ENUM_DOMAINS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_enum_domains(p, &q_u, &r_u);
+
+       if(!samr_io_r_enum_domains("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_enum_domains: Unable to marshall SAMR_R_ENUM_DOMAINS.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+static BOOL api_samr_open_alias(pipes_struct *p)
+{
+       SAMR_Q_OPEN_ALIAS q_u;
+       SAMR_R_OPEN_ALIAS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr open policy */
+       if(!samr_io_q_open_alias("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_open_alias: Unable to unmarshall SAMR_Q_OPEN_ALIAS.\n"));
+               return False;
+       }
+
+       r_u.status=_api_samr_open_alias(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_open_alias("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_open_alias: Unable to marshall SAMR_R_OPEN_ALIAS.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo(pipes_struct *p)
+{
+       SAMR_Q_SET_USERINFO q_u;
+       SAMR_R_SET_USERINFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_set_userinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_userinfo: Unable to unmarshall SAMR_Q_SET_USERINFO.\n"));
+               /* Fix for W2K SP2 */
+               if (q_u.switch_value == 0x1a) {
+                       setup_fault_pdu(p, NT_STATUS(0x1c000006));
+                       return True;
+               }
+               return False;
+       }
+
+       r_u.status = _samr_set_userinfo(p, &q_u, &r_u);
+
+       if(!samr_io_r_set_userinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_userinfo: Unable to marshall SAMR_R_SET_USERINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo2
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo2(pipes_struct *p)
+{
+       SAMR_Q_SET_USERINFO2 q_u;
+       SAMR_R_SET_USERINFO2 r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_set_userinfo2("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_userinfo2: Unable to unmarshall SAMR_Q_SET_USERINFO2.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_set_userinfo2(p, &q_u, &r_u);
+
+       if(!samr_io_r_set_userinfo2("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_userinfo2: Unable to marshall SAMR_R_SET_USERINFO2.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_useraliases
+ ********************************************************************/
+
+static BOOL api_samr_query_useraliases(pipes_struct *p)
+{
+       SAMR_Q_QUERY_USERALIASES q_u;
+       SAMR_R_QUERY_USERALIASES r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_query_useraliases("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_useraliases:  Unable to unmarshall SAMR_Q_QUERY_USERALIASES.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_useraliases(p, &q_u, &r_u);
+
+       if (! samr_io_r_query_useraliases("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_useraliases:  Unable to nmarshall SAMR_R_QUERY_USERALIASES.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasmem(pipes_struct *p)
+{
+       SAMR_Q_QUERY_ALIASMEM q_u;
+       SAMR_R_QUERY_ALIASMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_query_aliasmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_aliasmem: unable to unmarshall SAMR_Q_QUERY_ALIASMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_aliasmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_query_aliasmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_aliasmem: unable to marshall SAMR_R_QUERY_ALIASMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_query_groupmem(pipes_struct *p)
+{
+       SAMR_Q_QUERY_GROUPMEM q_u;
+       SAMR_R_QUERY_GROUPMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_query_groupmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_groupmem: unable to unmarshall SAMR_Q_QUERY_GROUPMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_groupmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_query_groupmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_groupmem: unable to marshall SAMR_R_QUERY_GROUPMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_add_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_add_aliasmem(pipes_struct *p)
+{
+       SAMR_Q_ADD_ALIASMEM q_u;
+       SAMR_R_ADD_ALIASMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_add_aliasmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_add_aliasmem: unable to unmarshall SAMR_Q_ADD_ALIASMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_add_aliasmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_add_aliasmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_add_aliasmem: unable to marshall SAMR_R_ADD_ALIASMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_del_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_del_aliasmem(pipes_struct *p)
+{
+       SAMR_Q_DEL_ALIASMEM q_u;
+       SAMR_R_DEL_ALIASMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_del_aliasmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_del_aliasmem: unable to unmarshall SAMR_Q_DEL_ALIASMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_del_aliasmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_del_aliasmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_del_aliasmem: unable to marshall SAMR_R_DEL_ALIASMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_add_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_add_groupmem(pipes_struct *p)
+{
+       SAMR_Q_ADD_GROUPMEM q_u;
+       SAMR_R_ADD_GROUPMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_add_groupmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_add_groupmem: unable to unmarshall SAMR_Q_ADD_GROUPMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_add_groupmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_add_groupmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_add_groupmem: unable to marshall SAMR_R_ADD_GROUPMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_del_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_del_groupmem(pipes_struct *p)
+{
+       SAMR_Q_DEL_GROUPMEM q_u;
+       SAMR_R_DEL_GROUPMEM r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_del_groupmem("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_del_groupmem: unable to unmarshall SAMR_Q_DEL_GROUPMEM.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_del_groupmem(p, &q_u, &r_u);
+
+       if (!samr_io_r_del_groupmem("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_del_groupmem: unable to marshall SAMR_R_DEL_GROUPMEM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_user
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_user(pipes_struct *p)
+{
+       SAMR_Q_DELETE_DOM_USER q_u;
+       SAMR_R_DELETE_DOM_USER r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_delete_dom_user("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_delete_dom_user: unable to unmarshall SAMR_Q_DELETE_DOM_USER.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_delete_dom_user(p, &q_u, &r_u);
+
+       if (!samr_io_r_delete_dom_user("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_delete_dom_user: unable to marshall SAMR_R_DELETE_DOM_USER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_group(pipes_struct *p)
+{
+       SAMR_Q_DELETE_DOM_GROUP q_u;
+       SAMR_R_DELETE_DOM_GROUP r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_delete_dom_group("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_delete_dom_group: unable to unmarshall SAMR_Q_DELETE_DOM_GROUP.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_delete_dom_group(p, &q_u, &r_u);
+
+       if (!samr_io_r_delete_dom_group("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_delete_dom_group: unable to marshall SAMR_R_DELETE_DOM_GROUP.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_alias(pipes_struct *p)
+{
+       SAMR_Q_DELETE_DOM_ALIAS q_u;
+       SAMR_R_DELETE_DOM_ALIAS r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_delete_dom_alias("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_delete_dom_alias: unable to unmarshall SAMR_Q_DELETE_DOM_ALIAS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_delete_dom_alias(p, &q_u, &r_u);
+
+       if (!samr_io_r_delete_dom_alias("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_delete_dom_alias: unable to marshall SAMR_R_DELETE_DOM_ALIAS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_group(pipes_struct *p)
+{
+       SAMR_Q_CREATE_DOM_GROUP q_u;
+       SAMR_R_CREATE_DOM_GROUP r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_create_dom_group("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_create_dom_group: unable to unmarshall SAMR_Q_CREATE_DOM_GROUP.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_create_dom_group(p, &q_u, &r_u);
+
+       if (!samr_io_r_create_dom_group("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_create_dom_group: unable to marshall SAMR_R_CREATE_DOM_GROUP.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_alias(pipes_struct *p)
+{
+       SAMR_Q_CREATE_DOM_ALIAS q_u;
+       SAMR_R_CREATE_DOM_ALIAS r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_create_dom_alias("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_create_dom_alias: unable to unmarshall SAMR_Q_CREATE_DOM_ALIAS.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_create_dom_alias(p, &q_u, &r_u);
+
+       if (!samr_io_r_create_dom_alias("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_create_dom_alias: unable to marshall SAMR_R_CREATE_DOM_ALIAS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_groupinfo(pipes_struct *p)
+{
+       SAMR_Q_QUERY_GROUPINFO q_u;
+       SAMR_R_QUERY_GROUPINFO r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_query_groupinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_query_groupinfo: unable to unmarshall SAMR_Q_QUERY_GROUPINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_query_groupinfo(p, &q_u, &r_u);
+
+       if (!samr_io_r_query_groupinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_query_groupinfo: unable to marshall SAMR_R_QUERY_GROUPINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_groupinfo(pipes_struct *p)
+{
+       SAMR_Q_SET_GROUPINFO q_u;
+       SAMR_R_SET_GROUPINFO r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_set_groupinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_groupinfo: unable to unmarshall SAMR_Q_SET_GROUPINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_set_groupinfo(p, &q_u, &r_u);
+
+       if (!samr_io_r_set_groupinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_groupinfo: unable to marshall SAMR_R_SET_GROUPINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_aliasinfo(pipes_struct *p)
+{
+       SAMR_Q_SET_ALIASINFO q_u;
+       SAMR_R_SET_ALIASINFO r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_set_aliasinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_aliasinfo: unable to unmarshall SAMR_Q_SET_ALIASINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_set_aliasinfo(p, &q_u, &r_u);
+
+       if (!samr_io_r_set_aliasinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_aliasinfo: unable to marshall SAMR_R_SET_ALIASINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_get_dom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_dom_pwinfo(pipes_struct *p)
+{
+       SAMR_Q_GET_DOM_PWINFO q_u;
+       SAMR_R_GET_DOM_PWINFO r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_get_dom_pwinfo("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_get_dom_pwinfo: unable to unmarshall SAMR_Q_GET_DOM_PWINFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_get_dom_pwinfo(p, &q_u, &r_u);
+
+       if (!samr_io_r_get_dom_pwinfo("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_get_dom_pwinfo: unable to marshall SAMR_R_GET_DOM_PWINFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_open_group
+ ********************************************************************/
+
+static BOOL api_samr_open_group(pipes_struct *p)
+{
+       SAMR_Q_OPEN_GROUP q_u;
+       SAMR_R_OPEN_GROUP r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_open_group("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_open_group: unable to unmarshall SAMR_Q_OPEN_GROUP.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_open_group(p, &q_u, &r_u);
+
+       if (!samr_io_r_open_group("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_open_group: unable to marshall SAMR_R_OPEN_GROUP.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_unknown_2d
+ ********************************************************************/
+
+static BOOL api_samr_unknown_2d(pipes_struct *p)
+{
+       SAMR_Q_UNKNOWN_2D q_u;
+       SAMR_R_UNKNOWN_2D r_u;
+
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!samr_io_q_unknown_2d("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_unknown_2d: unable to unmarshall SAMR_Q_UNKNOWN_2D.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_unknown_2d(p, &q_u, &r_u);
+
+       if (!samr_io_r_unknown_2d("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_unknown_2d: unable to marshall SAMR_R_UNKNOWN_2D.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_unknown_2e(pipes_struct *p)
+{
+       SAMR_Q_UNKNOWN_2E q_u;
+       SAMR_R_UNKNOWN_2E r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 8 command */
+       if(!samr_io_q_unknown_2e("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_unknown_2e: unable to unmarshall SAMR_Q_UNKNOWN_2E.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_unknown_2e(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_samr_unknown_2e("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_unknown_2e: unable to marshall SAMR_R_UNKNOWN_2E.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_samr_set_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_set_dom_info(pipes_struct *p)
+{
+       SAMR_Q_SET_DOMAIN_INFO q_u;
+       SAMR_R_SET_DOMAIN_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the samr unknown 8 command */
+       if(!samr_io_q_set_domain_info("", &q_u, data, 0)) {
+               DEBUG(0,("api_samr_set_dom_info: unable to unmarshall SAMR_Q_SET_DOMAIN_INFO.\n"));
+               return False;
+       }
+
+       r_u.status = _samr_set_dom_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!samr_io_r_set_domain_info("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_samr_set_dom_info: unable to marshall SAMR_R_SET_DOMAIN_INFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ array of \PIPE\samr operations
+ ********************************************************************/
+
+#ifdef RPC_SAMR_DYNAMIC
+int init_module(void)
+#else
+int rpc_samr_init(void)
+#endif
+{
+  static struct api_struct api_samr_cmds [] =
+    {
+      {"SAMR_CLOSE_HND"         , SAMR_CLOSE_HND        , api_samr_close_hnd        },
+      {"SAMR_CONNECT"           , SAMR_CONNECT          , api_samr_connect          },
+      {"SAMR_CONNECT_ANON"      , SAMR_CONNECT_ANON     , api_samr_connect_anon     },
+      {"SAMR_ENUM_DOMAINS"      , SAMR_ENUM_DOMAINS     , api_samr_enum_domains     },
+      {"SAMR_ENUM_DOM_USERS"    , SAMR_ENUM_DOM_USERS   , api_samr_enum_dom_users   },
+      
+      {"SAMR_ENUM_DOM_GROUPS"   , SAMR_ENUM_DOM_GROUPS  , api_samr_enum_dom_groups  },
+      {"SAMR_ENUM_DOM_ALIASES"  , SAMR_ENUM_DOM_ALIASES , api_samr_enum_dom_aliases },
+      {"SAMR_QUERY_USERALIASES" , SAMR_QUERY_USERALIASES, api_samr_query_useraliases},
+      {"SAMR_QUERY_ALIASMEM"    , SAMR_QUERY_ALIASMEM   , api_samr_query_aliasmem   },
+      {"SAMR_QUERY_GROUPMEM"    , SAMR_QUERY_GROUPMEM   , api_samr_query_groupmem   },
+      {"SAMR_ADD_ALIASMEM"      , SAMR_ADD_ALIASMEM     , api_samr_add_aliasmem     },
+      {"SAMR_DEL_ALIASMEM"      , SAMR_DEL_ALIASMEM     , api_samr_del_aliasmem     },
+      {"SAMR_ADD_GROUPMEM"      , SAMR_ADD_GROUPMEM     , api_samr_add_groupmem     },
+      {"SAMR_DEL_GROUPMEM"      , SAMR_DEL_GROUPMEM     , api_samr_del_groupmem     },
+      
+      {"SAMR_DELETE_DOM_USER"   , SAMR_DELETE_DOM_USER  , api_samr_delete_dom_user  },
+      {"SAMR_DELETE_DOM_GROUP"  , SAMR_DELETE_DOM_GROUP , api_samr_delete_dom_group },
+      {"SAMR_DELETE_DOM_ALIAS"  , SAMR_DELETE_DOM_ALIAS , api_samr_delete_dom_alias },
+      {"SAMR_CREATE_DOM_GROUP"  , SAMR_CREATE_DOM_GROUP , api_samr_create_dom_group },
+      {"SAMR_CREATE_DOM_ALIAS"  , SAMR_CREATE_DOM_ALIAS , api_samr_create_dom_alias },
+      {"SAMR_LOOKUP_NAMES"      , SAMR_LOOKUP_NAMES     , api_samr_lookup_names     },
+      {"SAMR_OPEN_USER"         , SAMR_OPEN_USER        , api_samr_open_user        },
+      {"SAMR_QUERY_USERINFO"    , SAMR_QUERY_USERINFO   , api_samr_query_userinfo   },
+      {"SAMR_SET_USERINFO"      , SAMR_SET_USERINFO     , api_samr_set_userinfo     },
+      {"SAMR_SET_USERINFO2"     , SAMR_SET_USERINFO2    , api_samr_set_userinfo2    },
+      
+      {"SAMR_QUERY_DOMAIN_INFO" , SAMR_QUERY_DOMAIN_INFO, api_samr_query_dom_info   },
+      {"SAMR_QUERY_USERGROUPS"  , SAMR_QUERY_USERGROUPS , api_samr_query_usergroups },
+      {"SAMR_QUERY_DISPINFO"    , SAMR_QUERY_DISPINFO   , api_samr_query_dispinfo   },
+      {"SAMR_QUERY_DISPINFO3"   , SAMR_QUERY_DISPINFO3  , api_samr_query_dispinfo   },
+      {"SAMR_QUERY_DISPINFO4"   , SAMR_QUERY_DISPINFO4  , api_samr_query_dispinfo   },
+      
+      {"SAMR_QUERY_ALIASINFO"   , SAMR_QUERY_ALIASINFO  , api_samr_query_aliasinfo  },
+      {"SAMR_QUERY_GROUPINFO"   , SAMR_QUERY_GROUPINFO  , api_samr_query_groupinfo  },
+      {"SAMR_SET_GROUPINFO"     , SAMR_SET_GROUPINFO    , api_samr_set_groupinfo    },
+      {"SAMR_SET_ALIASINFO"     , SAMR_SET_ALIASINFO    , api_samr_set_aliasinfo    },
+      {"SAMR_CREATE_USER"       , SAMR_CREATE_USER      , api_samr_create_user      },
+      {"SAMR_LOOKUP_RIDS"       , SAMR_LOOKUP_RIDS      , api_samr_lookup_rids      },
+      {"SAMR_GET_DOM_PWINFO"    , SAMR_GET_DOM_PWINFO   , api_samr_get_dom_pwinfo   },
+      {"SAMR_CHGPASSWD_USER"    , SAMR_CHGPASSWD_USER   , api_samr_chgpasswd_user   },
+      {"SAMR_OPEN_ALIAS"        , SAMR_OPEN_ALIAS       , api_samr_open_alias       },
+      {"SAMR_OPEN_GROUP"        , SAMR_OPEN_GROUP       , api_samr_open_group       },
+      {"SAMR_OPEN_DOMAIN"       , SAMR_OPEN_DOMAIN      , api_samr_open_domain      },
+      {"SAMR_UNKNOWN_2D"        , SAMR_UNKNOWN_2D       , api_samr_unknown_2d       },
+      {"SAMR_LOOKUP_DOMAIN"     , SAMR_LOOKUP_DOMAIN    , api_samr_lookup_domain    },
+      
+      {"SAMR_QUERY_SEC_OBJECT"  , SAMR_QUERY_SEC_OBJECT , api_samr_query_sec_obj    },
+      {"SAMR_SET_SEC_OBJECT"    , SAMR_SET_SEC_OBJECT   , api_samr_set_sec_obj      },
+      {"SAMR_GET_USRDOM_PWINFO" , SAMR_GET_USRDOM_PWINFO, api_samr_get_usrdom_pwinfo},
+      {"SAMR_UNKNOWN_2E"        , SAMR_UNKNOWN_2E       , api_samr_unknown_2e       },
+      {"SAMR_SET_DOMAIN_INFO"   , SAMR_SET_DOMAIN_INFO  , api_samr_set_dom_info     },
+      {"SAMR_CONNECT4"          , SAMR_CONNECT4         , api_samr_connect4         }
+    };
+  return rpc_pipe_register_commands("samr", "lsass", api_samr_cmds,
+                                   sizeof(api_samr_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_samr_nt.c b/source4/rpc_server/srv_samr_nt.c
new file mode 100644 (file)
index 0000000..fd1fb92
--- /dev/null
@@ -0,0 +1,4432 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Marc Jacobsen                            1999,
+ *  Copyright (C) Jeremy Allison               2001-2002,
+ *  Copyright (C) Jean François Micouleau      1998-2001,
+ *  Copyright (C) Anthony Liguori                   2002,
+ *  Copyright (C) Jim McDonough                     2002.
+ *
+ *  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.
+ */
+
+/*
+ * This is the implementation of the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern DOM_SID global_sid_Builtin;
+
+extern rid_name domain_group_rids[];
+extern rid_name domain_alias_rids[];
+extern rid_name builtin_alias_rids[];
+
+
+typedef struct _disp_info {
+       BOOL user_dbloaded;
+       uint32 num_user_account;
+       DISP_USER_INFO *disp_user_info;
+       BOOL group_dbloaded;
+       uint32 num_group_account;
+       DISP_GROUP_INFO *disp_group_info;
+} DISP_INFO;
+
+struct samr_info {
+       /* for use by the \PIPE\samr policy */
+       DOM_SID sid;
+       uint32 status; /* some sort of flag.  best to record it.  comes from opnum 0x39 */
+       uint32 acc_granted;
+       uint16 acb_mask;
+       BOOL all_machines;
+       DISP_INFO disp_info;
+
+       TALLOC_CTX *mem_ctx;
+};
+
+struct generic_mapping sam_generic_mapping = {GENERIC_RIGHTS_SAM_READ, GENERIC_RIGHTS_SAM_WRITE, GENERIC_RIGHTS_SAM_EXECUTE, GENERIC_RIGHTS_SAM_ALL_ACCESS};
+struct generic_mapping dom_generic_mapping = {GENERIC_RIGHTS_DOMAIN_READ, GENERIC_RIGHTS_DOMAIN_WRITE, GENERIC_RIGHTS_DOMAIN_EXECUTE, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS};
+struct generic_mapping usr_generic_mapping = {GENERIC_RIGHTS_USER_READ, GENERIC_RIGHTS_USER_WRITE, GENERIC_RIGHTS_USER_EXECUTE, GENERIC_RIGHTS_USER_ALL_ACCESS};
+struct generic_mapping grp_generic_mapping = {GENERIC_RIGHTS_GROUP_READ, GENERIC_RIGHTS_GROUP_WRITE, GENERIC_RIGHTS_GROUP_EXECUTE, GENERIC_RIGHTS_GROUP_ALL_ACCESS};
+struct generic_mapping ali_generic_mapping = {GENERIC_RIGHTS_ALIAS_READ, GENERIC_RIGHTS_ALIAS_WRITE, GENERIC_RIGHTS_ALIAS_EXECUTE, GENERIC_RIGHTS_ALIAS_ALL_ACCESS};
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size);
+
+/*******************************************************************
+ Checks if access to an object should be granted, and returns that
+ level of access for further checks.
+********************************************************************/
+
+NTSTATUS access_check_samr_object(SEC_DESC *psd, NT_USER_TOKEN *nt_user_token, uint32 des_access, 
+                                 uint32 *acc_granted, const char *debug) 
+{
+       NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+
+       if (!se_access_check(psd, nt_user_token, des_access, acc_granted, &status)) {
+               if (geteuid() == sec_initial_uid()) {
+                       DEBUG(4,("%s: ACCESS should be DENIED  (requested: %#010x)\n",
+                               debug, des_access));
+                       DEBUGADD(4,("but overritten by euid == sec_initial_uid()\n"));
+                       status = NT_STATUS_OK;
+               }
+               else {
+                       DEBUG(2,("%s: ACCESS DENIED  (requested: %#010x)\n",
+                               debug, des_access));
+               }
+       }
+       return status;
+}
+
+/*******************************************************************
+ Checks if access to a function can be granted
+********************************************************************/
+
+NTSTATUS access_check_samr_function(uint32 acc_granted, uint32 acc_required, const char *debug)
+{
+       DEBUG(5,("%s: access check ((granted: %#010x;  required: %#010x)\n",
+                       debug, acc_granted, acc_required));
+       if ((acc_granted & acc_required) != acc_required) {
+               if (geteuid() == sec_initial_uid()) {
+                       DEBUG(4,("%s: ACCESS should be DENIED (granted: %#010x;  required: %#010x)\n",
+                               debug, acc_granted, acc_required));
+                       DEBUGADD(4,("but overwritten by euid == 0\n"));
+                       return NT_STATUS_OK;
+               }
+               DEBUG(2,("%s: ACCESS DENIED (granted: %#010x;  required: %#010x)\n",
+                       debug, acc_granted, acc_required));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ Create a samr_info struct.
+********************************************************************/
+
+static struct samr_info *get_samr_info_by_sid(DOM_SID *psid)
+{
+       struct samr_info *info;
+       fstring sid_str;
+       TALLOC_CTX *mem_ctx;
+       
+       if (psid) {
+               sid_to_string(sid_str, psid);
+       } else {
+               fstrcpy(sid_str,"(NULL)");
+       }
+
+       mem_ctx = talloc_init("samr_info for domain sid %s", sid_str);
+
+       if ((info = (struct samr_info *)talloc(mem_ctx, sizeof(struct samr_info))) == NULL)
+               return NULL;
+
+       ZERO_STRUCTP(info);
+       DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str));
+       if (psid) {
+               sid_copy( &info->sid, psid);
+       } else {
+               DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n"));
+       }
+       info->mem_ctx = mem_ctx;
+       return info;
+}
+
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+static void free_samr_users(struct samr_info *info) 
+{
+       int i;
+
+       if (info->disp_info.user_dbloaded){
+               for (i=0; i<info->disp_info.num_user_account; i++) {
+                       /* Not really a free, actually a 'clear' */
+                       pdb_free_sam(&info->disp_info.disp_user_info[i].sam);
+               }
+       }
+       info->disp_info.user_dbloaded=False;
+       info->disp_info.num_user_account=0;
+}
+
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+static void free_samr_db(struct samr_info *info)
+{
+       /* Groups are talloced */
+
+       free_samr_users(info);
+
+       info->disp_info.group_dbloaded=False;
+       info->disp_info.num_group_account=0;
+}
+
+
+static void free_samr_info(void *ptr)
+{
+       struct samr_info *info=(struct samr_info *) ptr;
+
+       free_samr_db(info);
+       talloc_destroy(info->mem_ctx);
+}
+
+/*******************************************************************
+ Ensure password info is never given out. Paranioa... JRA.
+ ********************************************************************/
+
+static void samr_clear_sam_passwd(SAM_ACCOUNT *sam_pass)
+{
+       
+       if (!sam_pass)
+               return;
+
+       /* These now zero out the old password */
+
+       pdb_set_lanman_passwd(sam_pass, NULL, PDB_DEFAULT);
+       pdb_set_nt_passwd(sam_pass, NULL, PDB_DEFAULT);
+}
+
+
+static NTSTATUS load_sampwd_entries(struct samr_info *info, uint16 acb_mask, BOOL all_machines)
+{
+       SAM_ACCOUNT *pwd = NULL;
+       DISP_USER_INFO *pwd_array = NULL;
+       NTSTATUS nt_status = NT_STATUS_OK;
+       TALLOC_CTX *mem_ctx = info->mem_ctx;
+
+       DEBUG(10,("load_sampwd_entries\n"));
+
+       /* if the snapshoot is already loaded, return */
+       if ((info->disp_info.user_dbloaded==True) 
+           && (info->acb_mask == acb_mask) 
+           && (info->all_machines == all_machines)) {
+               DEBUG(10,("load_sampwd_entries: already in memory\n"));
+               return NT_STATUS_OK;
+       }
+
+       free_samr_users(info);
+
+       if (!pdb_setsampwent(False)) {
+               DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       for (; (NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, &pwd))) 
+                    && pdb_getsampwent(pwd) == True; pwd=NULL) {
+               
+               if (all_machines) {
+                       if (!((pdb_get_acct_ctrl(pwd) & ACB_WSTRUST) 
+                             || (pdb_get_acct_ctrl(pwd) & ACB_SVRTRUST))) {
+                               DEBUG(5,("load_sampwd_entries: '%s' is not a machine account - ACB: %x - skipping\n", pdb_get_username(pwd), acb_mask));
+                               pdb_free_sam(&pwd);
+                               continue;
+                       }
+               } else {
+                       if (acb_mask != 0 && !(pdb_get_acct_ctrl(pwd) & acb_mask)) {
+                               pdb_free_sam(&pwd);
+                               DEBUG(5,(" acb_mask %x reject\n", acb_mask));
+                               continue;
+                       }
+               }
+
+               /* Realloc some memory for the array of ptr to the SAM_ACCOUNT structs */
+               if (info->disp_info.num_user_account % MAX_SAM_ENTRIES == 0) {
+               
+                       DEBUG(10,("load_sampwd_entries: allocating more memory\n"));
+                       pwd_array=(DISP_USER_INFO *)talloc_realloc(mem_ctx, info->disp_info.disp_user_info, 
+                                         (info->disp_info.num_user_account+MAX_SAM_ENTRIES)*sizeof(DISP_USER_INFO));
+
+                       if (pwd_array==NULL)
+                               return NT_STATUS_NO_MEMORY;
+
+                       info->disp_info.disp_user_info=pwd_array;
+               }
+       
+               /* link the SAM_ACCOUNT to the array */
+               info->disp_info.disp_user_info[info->disp_info.num_user_account].sam=pwd;
+
+               DEBUG(10,("load_sampwd_entries: entry: %d\n", info->disp_info.num_user_account));
+
+               info->disp_info.num_user_account++;     
+       }
+
+       pdb_endsampwent();
+
+       /* the snapshoot is in memory, we're ready to enumerate fast */
+
+       info->acb_mask = acb_mask;
+       info->all_machines = all_machines;
+       info->disp_info.user_dbloaded=True;
+
+       DEBUG(10,("load_sampwd_entries: done\n"));
+
+       return nt_status;
+}
+
+static NTSTATUS load_group_domain_entries(struct samr_info *info, DOM_SID *sid)
+{
+       GROUP_MAP *map=NULL;
+       DISP_GROUP_INFO *grp_array = NULL;
+       uint32 group_entries = 0;
+       uint32 i;
+       TALLOC_CTX *mem_ctx = info->mem_ctx;
+
+       DEBUG(10,("load_group_domain_entries\n"));
+
+       /* if the snapshoot is already loaded, return */
+       if (info->disp_info.group_dbloaded==True) {
+               DEBUG(10,("load_group_domain_entries: already in memory\n"));
+               return NT_STATUS_OK;
+       }
+
+       if (!pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) {
+               DEBUG(1, ("load_group_domain_entries: pdb_enum_group_mapping() failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       info->disp_info.num_group_account=group_entries;
+
+       grp_array=(DISP_GROUP_INFO *)talloc(mem_ctx, info->disp_info.num_group_account*sizeof(DISP_GROUP_INFO));
+
+       if (group_entries!=0 && grp_array==NULL) {
+               DEBUG(1, ("load_group_domain_entries: talloc() failed for grp_array!\n"));
+               SAFE_FREE(map);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       info->disp_info.disp_group_info=grp_array;
+
+       for (i=0; i<group_entries; i++) {
+       
+               grp_array[i].grp=(DOMAIN_GRP *)talloc(mem_ctx, sizeof(DOMAIN_GRP));
+       
+               fstrcpy(grp_array[i].grp->name, map[i].nt_name);
+               fstrcpy(grp_array[i].grp->comment, map[i].comment);
+               sid_split_rid(&map[i].sid, &grp_array[i].grp->rid);
+               grp_array[i].grp->attr=SID_NAME_DOM_GRP;
+       }
+
+       SAFE_FREE(map);
+
+       /* the snapshoot is in memory, we're ready to enumerate fast */
+
+       info->disp_info.group_dbloaded=True;
+
+       DEBUG(10,("load_group_domain_entries: done\n"));
+
+       return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ _samr_close_hnd
+ ********************************************************************/
+
+NTSTATUS _samr_close_hnd(pipes_struct *p, SAMR_Q_CLOSE_HND *q_u, SAMR_R_CLOSE_HND *r_u)
+{
+       r_u->status = NT_STATUS_OK;
+
+       /* close the policy handle */
+       if (!close_policy_hnd(p, &q_u->pol))
+               return NT_STATUS_OBJECT_NAME_INVALID;
+
+       DEBUG(5,("samr_reply_close_hnd: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_open_domain
+ ********************************************************************/
+
+NTSTATUS _samr_open_domain(pipes_struct *p, SAMR_Q_OPEN_DOMAIN *q_u, SAMR_R_OPEN_DOMAIN *r_u)
+{
+       struct    samr_info *info;
+       SEC_DESC *psd = NULL;
+       uint32    acc_granted;
+       uint32    des_access = q_u->flags;
+       size_t    sd_size;
+       NTSTATUS  status;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the connection policy handle. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!NT_STATUS_IS_OK(status = access_check_samr_function(info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN,"_samr_open_domain"))) {
+               return status;
+       }
+
+       /*check if access can be granted as requested by client. */
+       samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+       se_map_generic(&des_access,&dom_generic_mapping);
+
+       if (!NT_STATUS_IS_OK(status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_open_domain"))) {
+               return status;
+       }
+
+       /* associate the domain SID with the (unique) handle. */
+       if ((info = get_samr_info_by_sid(&q_u->dom_sid.sid))==NULL)
+               return NT_STATUS_NO_MEMORY;
+       info->acc_granted = acc_granted;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->domain_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       DEBUG(5,("samr_open_domain: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ _samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u, SAMR_R_GET_USRDOM_PWINFO *r_u)
+{
+       struct samr_info *info = NULL;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->user_pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!sid_check_is_in_our_domain(&info->sid))
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       init_samr_r_get_usrdom_pwinfo(r_u, NT_STATUS_OK);
+
+       DEBUG(5,("_samr_get_usrdom_pwinfo: %d\n", __LINE__));
+
+       /* 
+        * NT sometimes return NT_STATUS_ACCESS_DENIED
+        * I don't know yet why.
+        */
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_make_dom_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+       extern DOM_SID global_sid_World;
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
+
+       SEC_ACE ace[3];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+       /*basic access for every one*/
+       init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_EXECUTE | GENERIC_RIGHTS_DOMAIN_READ);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+       init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_usr_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_usr_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size, DOM_SID *usr_sid)
+{
+       extern DOM_SID global_sid_World;
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
+
+       SEC_ACE ace[4];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+       /*basic access for every one*/
+       init_sec_access(&mask, GENERIC_RIGHTS_USER_EXECUTE | GENERIC_RIGHTS_USER_READ);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_access(&mask, GENERIC_RIGHTS_USER_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+       init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*extended access for the user*/
+       init_sec_access(&mask,READ_CONTROL_ACCESS | SA_RIGHT_USER_CHANGE_PASSWORD | SA_RIGHT_USER_SET_LOC_COM);
+       init_sec_ace(&ace[3], usr_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 4, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_grp_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_grp_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+       extern DOM_SID global_sid_World;
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
+
+       SEC_ACE ace[3];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+       /*basic access for every one*/
+       init_sec_access(&mask, GENERIC_RIGHTS_GROUP_EXECUTE | GENERIC_RIGHTS_GROUP_READ);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_access(&mask, GENERIC_RIGHTS_GROUP_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+       init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_ali_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_ali_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+       extern DOM_SID global_sid_World;
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
+
+       SEC_ACE ace[3];
+       SEC_ACCESS mask;
+
+       SEC_ACL *psa = NULL;
+
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+       /*basic access for every one*/
+       init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_EXECUTE | GENERIC_RIGHTS_ALIAS_READ);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_ALL_ACCESS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+       init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       return NT_STATUS_OK;
+}
+
+static BOOL get_lsa_policy_samr_sid(pipes_struct *p, POLICY_HND *pol, DOM_SID *sid, uint32 *acc_granted)
+{
+       struct samr_info *info = NULL;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, pol, (void **)&info))
+               return False;
+
+       if (!info)
+               return False;
+
+       *sid = info->sid;
+       *acc_granted = info->acc_granted;
+       return True;
+}
+
+/*******************************************************************
+ _samr_set_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
+{
+       DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n"));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************************************
+ _samr_query_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_QUERY_SEC_OBJ *r_u)
+{
+       DOM_SID pol_sid;
+       fstring str_sid;
+       SEC_DESC * psd = NULL;
+       size_t sd_size;
+       uint32 acc_granted;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* Get the SID. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &pol_sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+
+
+
+       DEBUG(10,("_samr_query_sec_obj: querying security on SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+
+       /* Check what typ of SID is beeing queried (e.g Domain SID, User SID, Group SID) */
+
+       /* To query the security of the SAM it self an invalid SID with S-0-0 is passed to this function */
+       if (pol_sid.sid_rev_num == 0)
+       {
+               DEBUG(5,("_samr_query_sec_obj: querying security on SAM\n"));
+               r_u->status = samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+       }
+       else if (sid_equal(&pol_sid,get_global_sam_sid()))  /* check if it is our domain SID */
+
+       {
+               DEBUG(5,("_samr_query_sec_obj: querying security on Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+               r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+       }
+       else if (sid_equal(&pol_sid,&global_sid_Builtin)) /* check if it is the Builtin  Domain */
+       {
+               /* TODO: Builtin probably needs a different SD with restricted write access*/
+               DEBUG(5,("_samr_query_sec_obj: querying security on Builtin Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+               r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+       }
+       else if (sid_check_is_in_our_domain(&pol_sid) ||
+                sid_check_is_in_builtin(&pol_sid))
+       {
+               /* TODO: different SDs have to be generated for aliases groups and users.
+                        Currently all three get a default user SD  */
+               DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+               r_u->status = samr_make_usr_obj_sd(p->mem_ctx, &psd,&sd_size, &pol_sid);
+       }
+       else return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       if ((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       if (NT_STATUS_IS_OK(r_u->status))
+               r_u->ptr = 1;
+
+       return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a user list.
+********************************************************************/
+
+static NTSTATUS make_user_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+                                        uint32 num_entries, uint32 start_idx, DISP_USER_INFO *disp_user_info,
+                                        DOM_SID *domain_sid)
+{
+       uint32 i;
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_name;
+       SAM_ACCOUNT *pwd = NULL;
+       UNISTR2 uni_temp_name;
+       const char *temp_name;
+       const DOM_SID *user_sid;
+       uint32 user_rid;
+       fstring user_sid_string;
+       fstring domain_sid_string;
+       
+       *sam_pp = NULL;
+       *uni_name_pp = NULL;
+
+       if (num_entries == 0)
+               return NT_STATUS_OK;
+
+       sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_entries);
+
+       uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_entries);
+
+       if (sam == NULL || uni_name == NULL) {
+               DEBUG(0, ("make_user_sam_entry_list: talloc_zero failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i = 0; i < num_entries; i++) {
+               pwd = disp_user_info[i+start_idx].sam;
+               temp_name = pdb_get_username(pwd);
+               init_unistr2(&uni_temp_name, temp_name, strlen(temp_name)+1);
+               user_sid = pdb_get_user_sid(pwd);
+
+               if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+                       DEBUG(0, ("make_user_sam_entry_list: User %s has SID %s, which conflicts with "
+                                 "the domain sid %s.  Failing operation.\n", 
+                                 temp_name, 
+                                 sid_to_string(user_sid_string, user_sid),
+                                 sid_to_string(domain_sid_string, domain_sid)));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+               init_sam_entry(&sam[i], uni_temp_name.uni_str_len, user_rid);
+               copy_unistr2(&uni_name[i], &uni_temp_name);
+       }
+
+       *sam_pp = sam;
+       *uni_name_pp = uni_name;
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_users
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_users(pipes_struct *p, SAMR_Q_ENUM_DOM_USERS *q_u, 
+                             SAMR_R_ENUM_DOM_USERS *r_u)
+{
+       struct samr_info *info = NULL;
+       uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+       int num_account;
+       uint32 enum_context=q_u->start_idx;
+       uint32 max_size=q_u->max_size;
+       uint32 temp_size;
+       enum remote_arch_types ra_type = get_remote_arch();
+       int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+       uint32 max_entries = max_sam_entries;
+       DOM_SID domain_sid;
+       
+       r_u->status = NT_STATUS_OK;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       domain_sid = info->sid;
+
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(info->acc_granted, 
+                                       SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, 
+                                       "_samr_enum_dom_users"))) {
+               return r_u->status;
+       }
+       
+       DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+       become_root();
+       r_u->status=load_sampwd_entries(info, q_u->acb_mask, False);
+       unbecome_root();
+       
+       if (!NT_STATUS_IS_OK(r_u->status))
+               return r_u->status;
+
+       num_account = info->disp_info.num_user_account;
+
+       if (enum_context > num_account) {
+               DEBUG(5, ("_samr_enum_dom_users: enumeration handle over total entries\n"));
+               return NT_STATUS_OK;
+       }
+
+       /* verify we won't overflow */
+       if (max_entries > num_account-enum_context) {
+               max_entries = num_account-enum_context;
+               DEBUG(5, ("_samr_enum_dom_users: only %d entries to return\n", max_entries));
+       }
+
+       /* calculate the size and limit on the number of entries we will return */
+       temp_size=max_entries*struct_size;
+       
+       if (temp_size>max_size) {
+               max_entries=MIN((max_size/struct_size),max_entries);;
+               DEBUG(5, ("_samr_enum_dom_users: buffer size limits to only %d entries\n", max_entries));
+       }
+
+       /* 
+        * Note from JRA. total_entries is not being used here. Currently if there is a
+        * large user base then it looks like NT will enumerate until get_sampwd_entries
+        * returns False due to num_entries being zero. This will cause an access denied
+        * return. I don't think this is right and needs further investigation. Note that
+        * this is also the same in the TNG code (I don't think that has been tested with
+        * a very large user list as MAX_SAM_ENTRIES is set to 600).
+        * 
+        * I also think that one of the 'num_entries' return parameters is probably
+        * the "max entries" parameter - but in the TNG code they're all currently set to the same
+        * value (again I think this is wrong).
+        */
+
+       r_u->status = make_user_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_acct_name, 
+                                              max_entries, enum_context, 
+                                              info->disp_info.disp_user_info,
+                                              &domain_sid);
+
+       if (!NT_STATUS_IS_OK(r_u->status))
+               return r_u->status;
+
+       if (enum_context+max_entries < num_account)
+               r_u->status = STATUS_MORE_ENTRIES;
+
+       DEBUG(5, ("_samr_enum_dom_users: %d\n", __LINE__));
+
+       init_samr_r_enum_dom_users(r_u, q_u->start_idx + max_entries, max_entries);
+
+       DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a group list.
+********************************************************************/
+
+static void make_group_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+                uint32 num_sam_entries, DOMAIN_GRP *grp)
+{
+       uint32 i;
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_name;
+
+       *sam_pp = NULL;
+       *uni_name_pp = NULL;
+
+       if (num_sam_entries == 0)
+               return;
+
+       sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+
+       uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+       if (sam == NULL || uni_name == NULL) {
+               DEBUG(0, ("NULL pointers in SAMR_R_QUERY_DISPINFO\n"));
+               return;
+       }
+
+       for (i = 0; i < num_sam_entries; i++) {
+               /*
+                * JRA. I think this should include the null. TNG does not.
+                */
+               int len = strlen(grp[i].name)+1;
+
+               init_sam_entry(&sam[i], len, grp[i].rid);
+               init_unistr2(&uni_name[i], grp[i].name, len);
+       }
+
+       *sam_pp = sam;
+       *uni_name_pp = uni_name;
+}
+
+/*******************************************************************
+ Get the group entries - similar to get_sampwd_entries().
+ ********************************************************************/
+
+static NTSTATUS get_group_alias_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx,
+                                   uint32 *p_num_entries, uint32 max_entries)
+{
+       fstring sid_str;
+       uint32 num_entries = 0;
+       int i;
+       GROUP_MAP smap;
+       GROUP_MAP *map = NULL;
+
+       sid_to_string(sid_str, sid);
+       DEBUG(5, ("get_group_alias_entries: enumerating aliases on SID: %s\n", sid_str));
+
+       *p_num_entries = 0;
+
+       /* well-known aliases */
+       if (sid_equal(sid, &global_sid_Builtin) && !lp_hide_local_users()) {
+               
+               pdb_enum_group_mapping(SID_NAME_WKN_GRP, &map, (int *)&num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV);
+               
+               if (num_entries != 0) {         
+                       *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP));
+                       if (*d_grp==NULL)
+                               return NT_STATUS_NO_MEMORY;
+                       
+                       for(i=0; i<num_entries && i<max_entries; i++) {
+                               fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name);
+                               sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid);
+                               
+                       }
+               }
+               SAFE_FREE(map);
+               
+       } else if (sid_equal(sid, get_global_sam_sid()) && !lp_hide_local_users()) {
+               struct sys_grent *glist;
+               struct sys_grent *grp;
+               struct passwd *pw;
+               gid_t winbind_gid_low, winbind_gid_high;
+               BOOL winbind_groups_exist = lp_winbind_gid(&winbind_gid_low, &winbind_gid_high);
+
+               /* local aliases */
+               /* we return the UNIX groups here.  This seems to be the right */
+               /* thing to do, since NT member servers return their local     */
+                /* groups in the same situation.                               */
+
+               /* use getgrent_list() to retrieve the list of groups to avoid
+                * problems with getgrent possible infinite loop by internal
+                * libc grent structures overwrites by called functions */
+               grp = glist = getgrent_list();
+               if (grp == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               
+               for (; (num_entries < max_entries) && (grp != NULL); grp = grp->next) {
+                       uint32 trid;
+                       
+                       if(!pdb_getgrgid(&smap, grp->gr_gid, MAPPING_WITHOUT_PRIV))
+                               continue;
+                       
+                       if (smap.sid_name_use!=SID_NAME_ALIAS) {
+                               continue;
+                       }
+
+                       sid_split_rid(&smap.sid, &trid);
+                       
+                       if (!sid_equal(sid, &smap.sid))
+                               continue;
+
+                       /* Don't return winbind groups as they are not local! */
+                       if (winbind_groups_exist && (grp->gr_gid >= winbind_gid_low)&&(grp->gr_gid <= winbind_gid_high)) {
+                               DEBUG(10,("get_group_alias_entries: not returing %s, not local.\n", smap.nt_name ));
+                               continue;
+                       }
+
+                       /* Don't return user private groups... */
+
+                       if ((pw = Get_Pwnam(smap.nt_name)) != 0) {
+                               DEBUG(10,("get_group_alias_entries: not returing %s, clashes with user.\n", smap.nt_name ));
+                               continue;                       
+                       }
+
+                       for( i = 0; i < num_entries; i++)
+                               if ( (*d_grp)[i].rid == trid )
+                                       break;
+
+                       if ( i < num_entries ) {
+                               continue; /* rid was there, dup! */
+                       }
+
+                       /* JRA - added this for large group db enumeration... */
+
+                       if (start_idx > 0) {
+                               /* skip the requested number of entries.
+                                       not very efficient, but hey...
+                               */
+                               start_idx--;
+                               continue;
+                       }
+
+                       *d_grp=talloc_realloc(ctx,*d_grp, (num_entries+1)*sizeof(DOMAIN_GRP));
+                       if (*d_grp==NULL) {
+                               grent_free(glist);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+
+                       fstrcpy((*d_grp)[num_entries].name, smap.nt_name);
+                       (*d_grp)[num_entries].rid = trid;
+                       num_entries++;
+                       DEBUG(10,("get_group_alias_entries: added entry %d, rid:%d\n", num_entries, trid));
+               }
+
+               grent_free(glist);
+       }
+
+       *p_num_entries = num_entries;
+
+       DEBUG(10,("get_group_alias_entries: returning %d entries\n", *p_num_entries));
+
+       if (num_entries >= max_entries)
+               return STATUS_MORE_ENTRIES;
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Get the group entries - similar to get_sampwd_entries().
+ ********************************************************************/
+
+static NTSTATUS get_group_domain_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx,
+                                    uint32 *p_num_entries, uint32 max_entries)
+{
+       GROUP_MAP *map=NULL;
+       int i;
+       uint32 group_entries = 0;
+       uint32 num_entries = 0;
+
+       *p_num_entries = 0;
+
+       pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV);
+
+       num_entries=group_entries-start_idx;
+
+       /* limit the number of entries */
+       if (num_entries>max_entries) {
+               DEBUG(5,("Limiting to %d entries\n", max_entries));
+               num_entries=max_entries;
+       }
+
+       *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP));
+       if (num_entries!=0 && *d_grp==NULL){
+               SAFE_FREE(map);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       for (i=0; i<num_entries; i++) {
+               fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name);
+               fstrcpy((*d_grp)[i].comment, map[i+start_idx].comment);
+               sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid);
+               (*d_grp)[i].attr=SID_NAME_DOM_GRP;
+       }
+
+       SAFE_FREE(map);
+
+       *p_num_entries = num_entries;
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_groups
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_groups(pipes_struct *p, SAMR_Q_ENUM_DOM_GROUPS *q_u, SAMR_R_ENUM_DOM_GROUPS *r_u)
+{
+       DOMAIN_GRP *grp=NULL;
+       uint32 num_entries;
+       DOM_SID sid;
+       uint32 acc_granted;
+
+       r_u->status = NT_STATUS_OK;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, "_samr_enum_dom_groups"))) {
+               return r_u->status;
+       }
+
+       DEBUG(5,("samr_reply_enum_dom_groups: %d\n", __LINE__));
+
+       /* the domain group array is being allocated in the function below */
+       if (!NT_STATUS_IS_OK(r_u->status = get_group_domain_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, &num_entries, MAX_SAM_ENTRIES))) {
+               return r_u->status;
+       }
+
+       make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+       init_samr_r_enum_dom_groups(r_u, q_u->start_idx, num_entries);
+
+       DEBUG(5,("samr_enum_dom_groups: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+
+/*******************************************************************
+ samr_reply_enum_dom_aliases
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_aliases(pipes_struct *p, SAMR_Q_ENUM_DOM_ALIASES *q_u, SAMR_R_ENUM_DOM_ALIASES *r_u)
+{
+       DOMAIN_GRP *grp=NULL;
+       uint32 num_entries = 0;
+       fstring sid_str;
+       DOM_SID sid;
+       NTSTATUS status;
+       uint32  acc_granted;
+       
+       r_u->status = NT_STATUS_OK;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, "_samr_enum_dom_aliases"))) {
+               return r_u->status;
+       }
+       
+       sid_to_string(sid_str, &sid);
+       DEBUG(5,("samr_reply_enum_dom_aliases: sid %s\n", sid_str));
+
+       status = get_group_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, 
+                                        &num_entries, MAX_SAM_ENTRIES);
+       if (NT_STATUS_IS_ERR(status)) return status;
+
+       make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+       /*safe_free(grp);*/
+
+       init_samr_r_enum_dom_aliases(r_u, q_u->start_idx + num_entries, num_entries);
+
+       DEBUG(5,("samr_enum_dom_aliases: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_dispinfo
+ ********************************************************************/
+NTSTATUS _samr_query_dispinfo(pipes_struct *p, SAMR_Q_QUERY_DISPINFO *q_u, 
+                             SAMR_R_QUERY_DISPINFO *r_u)
+{
+       struct samr_info *info = NULL;
+       uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+       
+       uint32 max_entries=q_u->max_entries;
+       uint32 enum_context=q_u->start_idx;
+       uint32 max_size=q_u->max_size;
+
+       SAM_DISPINFO_CTR *ctr;
+       uint32 temp_size=0, total_data_size=0;
+       NTSTATUS disp_ret;
+       uint32 num_account = 0;
+       enum remote_arch_types ra_type = get_remote_arch();
+       int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+       DOM_SID domain_sid;
+
+       DEBUG(5, ("samr_reply_query_dispinfo: %d\n", __LINE__));
+       r_u->status = NT_STATUS_OK;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       domain_sid = info->sid;
+
+       /*
+        * calculate how many entries we will return.
+        * based on 
+        * - the number of entries the client asked
+        * - our limit on that
+        * - the starting point (enumeration context)
+        * - the buffer size the client will accept
+        */
+
+       /*
+        * We are a lot more like W2K. Instead of reading the SAM
+        * each time to find the records we need to send back,
+        * we read it once and link that copy to the sam handle.
+        * For large user list (over the MAX_SAM_ENTRIES)
+        * it's a definitive win.
+        * second point to notice: between enumerations
+        * our sam is now the same as it's a snapshoot.
+        * third point: got rid of the static SAM_USER_21 struct
+        * no more intermediate.
+        * con: it uses much more memory, as a full copy is stored
+        * in memory.
+        *
+        * If you want to change it, think twice and think
+        * of the second point , that's really important.
+        *
+        * JFM, 12/20/2001
+        */
+
+       /* Get what we need from the password database */
+       switch (q_u->switch_level) {
+               case 0x1:
+                       /* When playing with usrmgr, this is necessary
+                           if you want immediate refresh after editing
+                           a user. I would like to do this after the
+                           setuserinfo2, but we do not have access to
+                           the domain handle in that call, only to the
+                           user handle. Where else does this hurt?
+                          -- Volker
+                       */
+#if 0
+                       /* We cannot do this here - it kills performace. JRA. */
+                       free_samr_users(info);
+#endif
+               case 0x2:
+               case 0x4:
+                       become_root();          
+                       /* Level 2 is for all machines, otherwise only 'normal' users */
+                       r_u->status=load_sampwd_entries(info, ACB_NORMAL, q_u->switch_level==2);
+                       unbecome_root();
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+                               return r_u->status;
+                       }
+                       num_account = info->disp_info.num_user_account;
+                       break;
+               case 0x3:
+               case 0x5:
+                       r_u->status = load_group_domain_entries(info, &info->sid);
+                       if (!NT_STATUS_IS_OK(r_u->status))
+                               return r_u->status;
+                       num_account = info->disp_info.num_group_account;
+                       break;
+               default:
+                       DEBUG(0,("_samr_query_dispinfo: Unknown info level (%u)\n", (unsigned int)q_u->switch_level ));
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       /* first limit the number of entries we will return */
+       if(max_entries > max_sam_entries) {
+               DEBUG(5, ("samr_reply_query_dispinfo: client requested %d entries, limiting to %d\n", max_entries, max_sam_entries));
+               max_entries = max_sam_entries;
+       }
+
+       if (enum_context > num_account) {
+               DEBUG(5, ("samr_reply_query_dispinfo: enumeration handle over total entries\n"));
+               return NT_STATUS_NO_MORE_ENTRIES;
+       }
+
+       /* verify we won't overflow */
+       if (max_entries > num_account-enum_context) {
+               max_entries = num_account-enum_context;
+               DEBUG(5, ("samr_reply_query_dispinfo: only %d entries to return\n", max_entries));
+       }
+
+       /* calculate the size and limit on the number of entries we will return */
+       temp_size=max_entries*struct_size;
+       
+       if (temp_size>max_size) {
+               max_entries=MIN((max_size/struct_size),max_entries);;
+               DEBUG(5, ("samr_reply_query_dispinfo: buffer size limits to only %d entries\n", max_entries));
+       }
+
+       if (!(ctr = (SAM_DISPINFO_CTR *)talloc_zero(p->mem_ctx,sizeof(SAM_DISPINFO_CTR))))
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(ctr);
+
+       /* Now create reply structure */
+       switch (q_u->switch_level) {
+       case 0x1:
+               if (max_entries) {
+                       if (!(ctr->sam.info1 = (SAM_DISPINFO_1 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_1))))
+                               return NT_STATUS_NO_MEMORY;
+               }
+               disp_ret = init_sam_dispinfo_1(p->mem_ctx, ctr->sam.info1, max_entries, enum_context, 
+                                              info->disp_info.disp_user_info, &domain_sid);
+               if (!NT_STATUS_IS_OK(disp_ret))
+                       return disp_ret;
+               break;
+       case 0x2:
+               if (max_entries) {
+                       if (!(ctr->sam.info2 = (SAM_DISPINFO_2 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_2))))
+                               return NT_STATUS_NO_MEMORY;
+               }
+               disp_ret = init_sam_dispinfo_2(p->mem_ctx, ctr->sam.info2, max_entries, enum_context, 
+                                              info->disp_info.disp_user_info, &domain_sid);
+               if (!NT_STATUS_IS_OK(disp_ret))
+                       return disp_ret;
+               break;
+       case 0x3:
+               if (max_entries) {
+                       if (!(ctr->sam.info3 = (SAM_DISPINFO_3 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_3))))
+                               return NT_STATUS_NO_MEMORY;
+               }
+               disp_ret = init_sam_dispinfo_3(p->mem_ctx, ctr->sam.info3, max_entries, enum_context, info->disp_info.disp_group_info);
+               if (!NT_STATUS_IS_OK(disp_ret))
+                       return disp_ret;
+               break;
+       case 0x4:
+               if (max_entries) {
+                       if (!(ctr->sam.info4 = (SAM_DISPINFO_4 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_4))))
+                               return NT_STATUS_NO_MEMORY;
+               }
+               disp_ret = init_sam_dispinfo_4(p->mem_ctx, ctr->sam.info4, max_entries, enum_context, info->disp_info.disp_user_info);
+               if (!NT_STATUS_IS_OK(disp_ret))
+                       return disp_ret;
+               break;
+       case 0x5:
+               if (max_entries) {
+                       if (!(ctr->sam.info5 = (SAM_DISPINFO_5 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_5))))
+                               return NT_STATUS_NO_MEMORY;
+               }
+               disp_ret = init_sam_dispinfo_5(p->mem_ctx, ctr->sam.info5, max_entries, enum_context, info->disp_info.disp_group_info);
+               if (!NT_STATUS_IS_OK(disp_ret))
+                       return disp_ret;
+               break;
+
+       default:
+               ctr->sam.info = NULL;
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       /* calculate the total size */
+       total_data_size=num_account*struct_size;
+
+       if (enum_context+max_entries < num_account)
+               r_u->status = STATUS_MORE_ENTRIES;
+
+       DEBUG(5, ("_samr_query_dispinfo: %d\n", __LINE__));
+
+       init_samr_r_query_dispinfo(r_u, max_entries, total_data_size, temp_size, q_u->switch_level, ctr, r_u->status);
+
+       return r_u->status;
+
+}
+
+/*******************************************************************
+ samr_reply_query_aliasinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAMR_R_QUERY_ALIASINFO *r_u)
+{
+       DOM_SID   sid;
+       GROUP_MAP map;
+       uint32    acc_granted;
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_LOOKUP_INFO, "_samr_query_aliasinfo"))) {
+               return r_u->status;
+       }
+
+       if (!sid_check_is_in_our_domain(&sid) &&
+           !sid_check_is_in_builtin(&sid))
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       if (!pdb_getgrsid(&map, sid, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       switch (q_u->switch_level) {
+       case 1:
+               r_u->ptr = 1;
+               r_u->ctr.switch_value1 = 1;
+               init_samr_alias_info1(&r_u->ctr.alias.info1, map.nt_name, 1, map.comment);
+               break;
+       case 3:
+               r_u->ptr = 1;
+               r_u->ctr.switch_value1 = 3;
+               init_samr_alias_info3(&r_u->ctr.alias.info3, map.comment);
+               break;
+       default:
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+#if 0
+/*******************************************************************
+ samr_reply_lookup_ids
+ ********************************************************************/
+
+ uint32 _samr_lookup_ids(pipes_struct *p, SAMR_Q_LOOKUP_IDS *q_u, SAMR_R_LOOKUP_IDS *r_u)
+{
+    uint32 rid[MAX_SAM_ENTRIES];
+    int num_rids = q_u->num_sids1;
+
+    r_u->status = NT_STATUS_OK;
+
+    DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+    if (num_rids > MAX_SAM_ENTRIES) {
+        num_rids = MAX_SAM_ENTRIES;
+        DEBUG(5,("_samr_lookup_ids: truncating entries to %d\n", num_rids));
+    }
+
+#if 0
+    int i;
+    SMB_ASSERT_ARRAY(q_u->uni_user_name, num_rids);
+
+    for (i = 0; i < num_rids && status == 0; i++)
+    {
+        struct sam_passwd *sam_pass;
+        fstring user_name;
+
+
+        fstrcpy(user_name, unistrn2(q_u->uni_user_name[i].buffer,
+                                    q_u->uni_user_name[i].uni_str_len));
+
+        /* find the user account */
+        become_root();
+        sam_pass = get_smb21pwd_entry(user_name, 0);
+        unbecome_root();
+
+        if (sam_pass == NULL)
+        {
+            status = 0xC0000000 | NT_STATUS_NO_SUCH_USER;
+            rid[i] = 0;
+        }
+        else
+        {
+            rid[i] = sam_pass->user_rid;
+        }
+    }
+#endif
+
+    num_rids = 1;
+    rid[0] = BUILTIN_ALIAS_RID_USERS;
+
+    init_samr_r_lookup_ids(&r_u, num_rids, rid, NT_STATUS_OK);
+
+    DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+    return r_u->status;
+}
+#endif
+
+/*******************************************************************
+ _samr_lookup_names
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LOOKUP_NAMES *r_u)
+{
+       uint32 rid[MAX_SAM_ENTRIES];
+       uint32 local_rid;
+       enum SID_NAME_USE type[MAX_SAM_ENTRIES];
+       enum SID_NAME_USE local_type;
+       int i;
+       int num_rids = q_u->num_names2;
+       DOM_SID pol_sid;
+       fstring sid_str;
+       uint32  acc_granted;
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+       ZERO_ARRAY(rid);
+       ZERO_ARRAY(type);
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted)) {
+               init_samr_r_lookup_names(p->mem_ctx, r_u, 0, NULL, NULL, NT_STATUS_OBJECT_TYPE_MISMATCH);
+               return r_u->status;
+       }
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, 0, "_samr_lookup_names"))) { /* Don't know the acc_bits yet */
+               return r_u->status;
+       }
+
+       if (num_rids > MAX_SAM_ENTRIES) {
+               num_rids = MAX_SAM_ENTRIES;
+               DEBUG(5,("_samr_lookup_names: truncating entries to %d\n", num_rids));
+       }
+
+       DEBUG(5,("_samr_lookup_names: looking name on SID %s\n", sid_to_string(sid_str, &pol_sid)));
+       
+       become_root(); /* local_lookup_name can require root privs */
+
+       for (i = 0; i < num_rids; i++) {
+               fstring name;
+               DOM_SID sid;
+
+               r_u->status = NT_STATUS_NONE_MAPPED;
+
+               rid [i] = 0xffffffff;
+               type[i] = SID_NAME_UNKNOWN;
+
+               rpcstr_pull(name, q_u->uni_name[i].buffer, sizeof(name), q_u->uni_name[i].uni_str_len*2, 0);
+
+               /*
+                * we are only looking for a name
+                * the SID we get back can be outside
+                * the scope of the pol_sid
+                * 
+                * in clear: it prevents to reply to domain\group: yes
+                * when only builtin\group exists.
+                *
+                * a cleaner code is to add the sid of the domain we're looking in
+                * to the local_lookup_name function.
+                */
+               if(local_lookup_name(name, &sid, &local_type)) {
+                       sid_split_rid(&sid, &local_rid);
+                               
+                       if (sid_equal(&sid, &pol_sid)) {
+                               rid[i]=local_rid;
+                               type[i]=local_type;
+                               r_u->status = NT_STATUS_OK;
+                       }
+               }
+       }
+
+       unbecome_root();
+
+       init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, (uint32 *)type, r_u->status);
+
+       DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ _samr_chgpasswd_user
+ ********************************************************************/
+
+NTSTATUS _samr_chgpasswd_user(pipes_struct *p, SAMR_Q_CHGPASSWD_USER *q_u, SAMR_R_CHGPASSWD_USER *r_u)
+{
+    fstring user_name;
+    fstring wks;
+
+    DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+    r_u->status = NT_STATUS_OK;
+
+    rpcstr_pull(user_name, q_u->uni_user_name.buffer, sizeof(user_name), q_u->uni_user_name.uni_str_len*2, 0);
+    rpcstr_pull(wks, q_u->uni_dest_host.buffer, sizeof(wks), q_u->uni_dest_host.uni_str_len*2,0);
+
+    DEBUG(5,("samr_chgpasswd_user: user: %s wks: %s\n", user_name, wks));
+
+       /*
+        * Pass the user through the NT -> unix user mapping
+        * function.
+        */
+       (void)map_username(user_name);
+       /*
+        * UNIX username case mangling not required, pass_oem_change 
+        * is case insensitive.
+        */
+
+    r_u->status = pass_oem_change(user_name, q_u->lm_newpass.pass, q_u->lm_oldhash.hash,
+                                 q_u->nt_newpass.pass, q_u->nt_oldhash.hash);
+
+    init_samr_r_chgpasswd_user(r_u, r_u->status);
+
+    DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+    return r_u->status;
+}
+
+/*******************************************************************
+makes a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+static BOOL make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names, fstring names[],
+           UNIHDR **pp_hdr_name, UNISTR2 **pp_uni_name)
+{
+       uint32 i;
+       UNIHDR *hdr_name=NULL;
+       UNISTR2 *uni_name=NULL;
+
+       *pp_uni_name = NULL;
+       *pp_hdr_name = NULL;
+
+       if (num_names != 0) {
+               hdr_name = (UNIHDR *)talloc_zero(ctx, sizeof(UNIHDR)*num_names);
+               if (hdr_name == NULL)
+                       return False;
+
+               uni_name = (UNISTR2 *)talloc_zero(ctx,sizeof(UNISTR2)*num_names);
+               if (uni_name == NULL)
+                       return False;
+       }
+
+       for (i = 0; i < num_names; i++) {
+               int len = names[i] != NULL ? strlen(names[i]) : 0;
+               DEBUG(10, ("names[%d]:%s\n", i, names[i]));
+               init_uni_hdr(&hdr_name[i], len);
+               init_unistr2(&uni_name[i], names[i], len);
+       }
+
+       *pp_uni_name = uni_name;
+       *pp_hdr_name = hdr_name;
+
+       return True;
+}
+
+/*******************************************************************
+ _samr_lookup_rids
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOKUP_RIDS *r_u)
+{
+       fstring group_names[MAX_SAM_ENTRIES];
+       uint32 *group_attrs = NULL;
+       UNIHDR *hdr_name = NULL;
+       UNISTR2 *uni_name = NULL;
+       DOM_SID pol_sid;
+       int num_rids = q_u->num_rids1;
+       int i;
+       uint32 acc_granted;
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (num_rids > MAX_SAM_ENTRIES) {
+               num_rids = MAX_SAM_ENTRIES;
+               DEBUG(5,("_samr_lookup_rids: truncating entries to %d\n", num_rids));
+       }
+
+       if (num_rids) {
+               if ((group_attrs = (uint32 *)talloc_zero(p->mem_ctx, num_rids * sizeof(uint32))) == NULL)
+                       return NT_STATUS_NO_MEMORY;
+       }
+       r_u->status = NT_STATUS_NONE_MAPPED;
+
+       become_root();  /* lookup_sid can require root privs */
+
+       for (i = 0; i < num_rids; i++) {
+               fstring tmpname;
+               fstring domname;
+               DOM_SID sid;
+               enum SID_NAME_USE type;
+
+               group_attrs[i] = SID_NAME_UNKNOWN;
+               *group_names[i] = '\0';
+
+               if (sid_equal(&pol_sid, get_global_sam_sid())) {
+                       sid_copy(&sid, &pol_sid);
+                       sid_append_rid(&sid, q_u->rid[i]);
+
+                       if (lookup_sid(&sid, domname, tmpname, &type)) {
+                               r_u->status = NT_STATUS_OK;
+                               group_attrs[i] = (uint32)type;
+                               fstrcpy(group_names[i],tmpname);
+                               DEBUG(5,("_samr_lookup_rids: %s:%d\n", group_names[i], group_attrs[i]));
+                       }
+               }
+       }
+
+       unbecome_root();
+
+       if(!make_samr_lookup_rids(p->mem_ctx, num_rids, group_names, &hdr_name, &uni_name))
+               return NT_STATUS_NO_MEMORY;
+
+       init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, group_attrs);
+
+       DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ _api_samr_open_user. Safe - gives out no passwd info.
+ ********************************************************************/
+
+NTSTATUS _api_samr_open_user(pipes_struct *p, SAMR_Q_OPEN_USER *q_u, SAMR_R_OPEN_USER *r_u)
+{
+       SAM_ACCOUNT *sampass=NULL;
+       DOM_SID sid;
+       POLICY_HND domain_pol = q_u->domain_pol;
+       POLICY_HND *user_pol = &r_u->user_pol;
+       struct samr_info *info = NULL;
+       SEC_DESC *psd = NULL;
+       uint32    acc_granted;
+       uint32    des_access = q_u->access_mask;
+       size_t    sd_size;
+       BOOL ret;
+       NTSTATUS nt_status;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the domain policy handle and get domain SID / access bits in the domain policy. */
+       if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_samr_open_user"))) {
+               return nt_status;
+       }
+
+       nt_status = pdb_init_sam_talloc(p->mem_ctx, &sampass);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       /* append the user's RID to it */
+       if (!sid_append_rid(&sid, q_u->user_rid))
+               return NT_STATUS_NO_SUCH_USER;
+       
+       /* check if access can be granted as requested by client. */
+       samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+       se_map_generic(&des_access, &usr_generic_mapping);
+       if (!NT_STATUS_IS_OK(nt_status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_open_user"))) {
+               return nt_status;
+       }
+
+       become_root();
+       ret=pdb_getsampwsid(sampass, &sid);
+       unbecome_root();
+
+       /* check that the SID exists in our domain. */
+       if (ret == False) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       pdb_free_sam(&sampass);
+
+       /* associate the user's SID and access bits with the new handle. */
+       if ((info = get_samr_info_by_sid(&sid)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+       info->acc_granted = acc_granted;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return r_u->status;
+}
+
+/*************************************************************************
+ get_user_info_10. Safe. Only gives out acb bits.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_10(TALLOC_CTX *mem_ctx, SAM_USER_INFO_10 *id10, DOM_SID *user_sid)
+{
+       SAM_ACCOUNT *smbpass=NULL;
+       BOOL ret;
+       NTSTATUS nt_status;
+
+       nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       become_root();
+       ret = pdb_getsampwsid(smbpass, user_sid);
+       unbecome_root();
+
+       if (ret==False) {
+               DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
+
+       ZERO_STRUCTP(id10);
+       init_sam_user_info10(id10, pdb_get_acct_ctrl(smbpass) );
+
+       pdb_free_sam(&smbpass);
+
+       return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_12. OK - this is the killer as it gives out password info.
+ Ensure that this is only allowed on an encrypted connection with a root
+ user. JRA. 
+ *************************************************************************/
+
+static NTSTATUS get_user_info_12(pipes_struct *p, TALLOC_CTX *mem_ctx, SAM_USER_INFO_12 * id12, DOM_SID *user_sid)
+{
+       SAM_ACCOUNT *smbpass=NULL;
+       BOOL ret;
+       NTSTATUS nt_status;
+
+       if (!p->ntlmssp_auth_validated)
+               return NT_STATUS_ACCESS_DENIED;
+
+       if (!(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) || !(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL))
+               return NT_STATUS_ACCESS_DENIED;
+
+       /*
+        * Do *NOT* do become_root()/unbecome_root() here ! JRA.
+        */
+
+       nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       ret = pdb_getsampwsid(smbpass, user_sid);
+
+       if (ret == False) {
+               DEBUG(4, ("User %s not found\n", sid_string_static(user_sid)));
+               pdb_free_sam(&smbpass);
+               return (geteuid() == (uid_t)0) ? NT_STATUS_NO_SUCH_USER : NT_STATUS_ACCESS_DENIED;
+       }
+
+       DEBUG(3,("User:[%s] 0x%x\n", pdb_get_username(smbpass), pdb_get_acct_ctrl(smbpass) ));
+
+       if ( pdb_get_acct_ctrl(smbpass) & ACB_DISABLED) {
+               pdb_free_sam(&smbpass);
+               return NT_STATUS_ACCOUNT_DISABLED;
+       }
+
+       ZERO_STRUCTP(id12);
+       init_sam_user_info12(id12, pdb_get_lanman_passwd(smbpass), pdb_get_nt_passwd(smbpass));
+       
+       pdb_free_sam(&smbpass);
+
+       return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_20
+ *************************************************************************/
+
+static NTSTATUS get_user_info_20(TALLOC_CTX *mem_ctx, SAM_USER_INFO_20 *id20, DOM_SID *user_sid)
+{
+       SAM_ACCOUNT *sampass=NULL;
+       BOOL ret;
+
+       pdb_init_sam_talloc(mem_ctx, &sampass);
+
+       become_root();
+       ret = pdb_getsampwsid(sampass, user_sid);
+       unbecome_root();
+
+       if (ret == False) {
+               DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       samr_clear_sam_passwd(sampass);
+
+       DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
+
+       ZERO_STRUCTP(id20);
+       init_sam_user_info20A(id20, sampass);
+       
+       pdb_free_sam(&sampass);
+
+       return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_21
+ *************************************************************************/
+
+static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21, 
+                                DOM_SID *user_sid, DOM_SID *domain_sid)
+{
+       SAM_ACCOUNT *sampass=NULL;
+       BOOL ret;
+       NTSTATUS nt_status;
+
+       nt_status = pdb_init_sam_talloc(mem_ctx, &sampass);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       become_root();
+       ret = pdb_getsampwsid(sampass, user_sid);
+       unbecome_root();
+
+       if (ret == False) {
+               DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       samr_clear_sam_passwd(sampass);
+
+       DEBUG(3,("User:[%s]\n",  pdb_get_username(sampass) ));
+
+       ZERO_STRUCTP(id21);
+       nt_status = init_sam_user_info21A(id21, sampass, domain_sid);
+       
+       pdb_free_sam(&sampass);
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_query_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_userinfo(pipes_struct *p, SAMR_Q_QUERY_USERINFO *q_u, SAMR_R_QUERY_USERINFO *r_u)
+{
+       SAM_USERINFO_CTR *ctr;
+       struct samr_info *info = NULL;
+       DOM_SID domain_sid;
+       uint32 rid;
+       
+       r_u->status=NT_STATUS_OK;
+
+       /* search for the handle */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       domain_sid = info->sid;
+
+       sid_split_rid(&domain_sid, &rid);
+
+       if (!sid_check_is_in_our_domain(&info->sid))
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       DEBUG(5,("_samr_query_userinfo: sid:%s\n", sid_string_static(&info->sid)));
+
+       ctr = (SAM_USERINFO_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_USERINFO_CTR));
+       if (!ctr)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(ctr);
+
+       /* ok!  user info levels (lots: see MSDEV help), off we go... */
+       ctr->switch_value = q_u->switch_value;
+
+       switch (q_u->switch_value) {
+       case 0x10:
+               ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_10));
+               if (ctr->info.id10 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+
+               if (!NT_STATUS_IS_OK(r_u->status = get_user_info_10(p->mem_ctx, ctr->info.id10, &info->sid)))
+                       return r_u->status;
+               break;
+
+#if 0
+/* whoops - got this wrong.  i think.  or don't understand what's happening. */
+        case 0x11:
+        {
+            NTTIME expire;
+            info = (void *)&id11;
+
+            expire.low = 0xffffffff;
+            expire.high = 0x7fffffff;
+
+            ctr->info.id = (SAM_USER_INFO_11 *)talloc_zero(p->mem_ctx,
+                                    sizeof
+                                    (*ctr->
+                                     info.
+                                     id11));
+           ZERO_STRUCTP(ctr->info.id11);
+            init_sam_user_info11(ctr->info.id11, &expire,
+                         "BROOKFIELDS$",    /* name */
+                         0x03ef,    /* user rid */
+                         0x201, /* group rid */
+                         0x0080);   /* acb info */
+
+            break;
+        }
+#endif
+
+       case 0x12:
+               ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_12));
+               if (ctr->info.id12 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+
+               if (!NT_STATUS_IS_OK(r_u->status = get_user_info_12(p, p->mem_ctx, ctr->info.id12, &info->sid)))
+                       return r_u->status;
+               break;
+               
+       case 20:
+               ctr->info.id20 = (SAM_USER_INFO_20 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_20));
+               if (ctr->info.id20 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               if (!NT_STATUS_IS_OK(r_u->status = get_user_info_20(p->mem_ctx, ctr->info.id20, &info->sid)))
+                       return r_u->status;
+               break;
+
+       case 21:
+               ctr->info.id21 = (SAM_USER_INFO_21 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_21));
+               if (ctr->info.id21 == NULL)
+                       return NT_STATUS_NO_MEMORY;
+               if (!NT_STATUS_IS_OK(r_u->status = get_user_info_21(p->mem_ctx, ctr->info.id21, 
+                                                                   &info->sid, &domain_sid)))
+                       return r_u->status;
+               break;
+
+       default:
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       init_samr_r_query_userinfo(r_u, ctr, r_u->status);
+
+       DEBUG(5,("_samr_query_userinfo: %d\n", __LINE__));
+       
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_usergroups
+ ********************************************************************/
+
+NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, SAMR_R_QUERY_USERGROUPS *r_u)
+{
+       SAM_ACCOUNT *sam_pass=NULL;
+       DOM_SID  sid;
+       DOM_GID *gids = NULL;
+       int num_groups = 0;
+       uint32 acc_granted;
+       BOOL ret;
+
+       /*
+        * from the SID in the request:
+        * we should send back the list of DOMAIN GROUPS
+        * the user is a member of
+        *
+        * and only the DOMAIN GROUPS
+        * no ALIASES !!! neither aliases of the domain
+        * nor aliases of the builtin SID
+        *
+        * JFM, 12/2/2001
+        */
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_USER_GET_GROUPS, "_samr_query_usergroups"))) {
+               return r_u->status;
+       }
+
+       if (!sid_check_is_in_our_domain(&sid))
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       pdb_init_sam(&sam_pass);
+       
+       become_root();
+       ret = pdb_getsampwsid(sam_pass, &sid);
+       unbecome_root();
+
+       if (ret == False) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       if(!get_domain_user_groups(p->mem_ctx, &num_groups, &gids, sam_pass)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+       
+       /* construct the response.  lkclXXXX: gids are not copied! */
+       init_samr_r_query_usergroups(r_u, num_groups, gids, r_u->status);
+       
+       DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+       
+       pdb_free_sam(&sam_pass);
+       
+       return r_u->status;
+}
+
+/*******************************************************************
+ _samr_query_dom_info
+ ********************************************************************/
+
+NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SAMR_R_QUERY_DOMAIN_INFO *r_u)
+{
+       struct samr_info *info = NULL;
+       SAM_UNK_CTR *ctr;
+       uint32 min_pass_len,pass_hist,flag;
+       time_t u_expire, u_min_age;
+       NTTIME nt_expire, nt_min_age;
+
+       time_t u_lock_duration, u_reset_time;
+       NTTIME nt_lock_duration, nt_reset_time;
+       uint32 lockout;
+       
+       time_t u_logout;
+       NTTIME nt_logout;
+
+       uint32 account_policy_temp;
+
+       uint32 num_users=0, num_groups=0, num_aliases=0;
+
+       if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(ctr);
+
+       r_u->status = NT_STATUS_OK;
+       
+       DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+       
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       switch (q_u->switch_value) {
+               case 0x01:
+                       
+                       account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+                       min_pass_len = account_policy_temp;
+
+                       account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+                       pass_hist = account_policy_temp;
+
+                       account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+                       flag = account_policy_temp;
+
+                       account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+                       u_expire = account_policy_temp;
+
+                       account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+                       u_min_age = account_policy_temp;
+                       
+                       unix_to_nt_time_abs(&nt_expire, u_expire);
+                       unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+                       init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist, 
+                                      flag, nt_expire, nt_min_age);
+                       break;
+               case 0x02:
+                       become_root();          
+                       r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+                       unbecome_root();
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+                               return r_u->status;
+                       }
+                       num_users=info->disp_info.num_user_account;
+                       free_samr_db(info);
+                       
+                       r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               DEBUG(5, ("_samr_query_dispinfo: load_group_domain_entries failed\n"));
+                               return r_u->status;
+                       }
+                       num_groups=info->disp_info.num_group_account;
+                       free_samr_db(info);
+                       
+                       /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+                       init_unk_info2(&ctr->info.inf2, lp_workgroup(), lp_netbios_name(), (uint32) time(NULL), 
+                                      num_users, num_groups, num_aliases);
+                       break;
+               case 0x03:
+                       account_policy_get(AP_TIME_TO_LOGOUT, (int *)&u_logout);
+                       unix_to_nt_time_abs(&nt_logout, u_logout);
+                       
+                       init_unk_info3(&ctr->info.inf3, nt_logout);
+                       break;
+               case 0x05:
+                       init_unk_info5(&ctr->info.inf5, lp_netbios_name());
+                       break;
+               case 0x06:
+                       init_unk_info6(&ctr->info.inf6);
+                       break;
+               case 0x07:
+                       init_unk_info7(&ctr->info.inf7);
+                       break;
+               case 0x0c:
+                       account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+                       u_lock_duration = account_policy_temp;
+
+                       account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+                       u_reset_time = account_policy_temp;
+
+                       account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+                       lockout = account_policy_temp;
+
+                       unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+                       unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+       
+                       init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+               }
+       
+       init_samr_r_query_dom_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+       
+       DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+       
+       return r_u->status;
+}
+
+/*******************************************************************
+ _api_samr_create_user
+ Create an account, can be either a normal user or a machine.
+ This funcion will need to be updated for bdc/domain trusts.
+ ********************************************************************/
+
+NTSTATUS _api_samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREATE_USER *r_u)
+{
+       SAM_ACCOUNT *sam_pass=NULL;
+       fstring account;
+       DOM_SID sid;
+       pstring add_script;
+       POLICY_HND dom_pol = q_u->domain_pol;
+       UNISTR2 user_account = q_u->uni_name;
+       uint16 acb_info = q_u->acb_info;
+       POLICY_HND *user_pol = &r_u->user_pol;
+       struct samr_info *info = NULL;
+       BOOL ret;
+       NTSTATUS nt_status;
+       struct passwd *pw;
+       uint32 acc_granted;
+       SEC_DESC *psd;
+       size_t    sd_size;
+       uint32    des_access;
+
+       /* Get the domain SID stored in the domain policy */
+       if (!get_lsa_policy_samr_sid(p, &dom_pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_CREATE_USER, "_samr_create_user"))) {
+               return nt_status;
+       }
+
+       /* find the account: tell the caller if it exists.
+         lkclXXXX i have *no* idea if this is a problem or not
+         or even if you are supposed to construct a different
+         reply if the account already exists...
+        */
+
+       rpcstr_pull(account, user_account.buffer, sizeof(account), user_account.uni_str_len*2, 0);
+       strlower(account);
+
+       pdb_init_sam(&sam_pass);
+
+       become_root();
+       ret = pdb_getsampwnam(sam_pass, account);
+       unbecome_root();
+       if (ret == True) {
+               /* this account exists: say so */
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_USER_EXISTS;
+       }
+
+       pdb_free_sam(&sam_pass);
+
+       /*
+        * NB. VERY IMPORTANT ! This call must be done as the current pipe user,
+        * *NOT* surrounded by a become_root()/unbecome_root() call. This ensures
+        * that only people with write access to the smbpasswd file will be able
+        * to create a user. JRA.
+        */
+
+       /*
+        * add the user in the /etc/passwd file or the unix authority system.
+        * We don't check if the smb_create_user() function succed or not for 2 reasons:
+        * a) local_password_change() checks for us if the /etc/passwd account really exists
+        * b) smb_create_user() would return an error if the account already exists
+        * and as it could return an error also if it can't create the account, it would be tricky.
+        *
+        * So we go the easy way, only check after if the account exists.
+        * JFM (2/3/2001), to clear any possible bad understanding (-:
+        *
+        * We now have seperate script paramaters for adding users/machines so we
+        * now have some sainity-checking to match. 
+        */
+
+       DEBUG(10,("checking account %s at pos %d for $ termination\n",account, strlen(account)-1));
+#if 0
+       if ((acb_info & ACB_WSTRUST) && (account[strlen(account)-1] == '$')) {
+               pstrcpy(add_script, lp_addmachine_script());            
+       } else if ((!(acb_info & ACB_WSTRUST)) && (account[strlen(account)-1] != '$')) {
+               pstrcpy(add_script, lp_adduser_script());
+       } else {
+               DEBUG(0, ("_api_samr_create_user: mismatch between trust flags and $ termination\n"));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+#endif
+
+       /* 
+        * we can't check both the ending $ and the acb_info.
+        * 
+        * UserManager creates trust accounts (ending in $,
+        * normal that hidden accounts) with the acb_info equals to ACB_NORMAL.
+        * JFM, 11/29/2001
+        */
+       if (account[strlen(account)-1] == '$')
+               pstrcpy(add_script, lp_addmachine_script());            
+       else 
+               pstrcpy(add_script, lp_adduser_script());
+
+       if (*add_script) {
+               int add_ret;
+               all_string_sub(add_script, "%u", account, sizeof(account));
+               add_ret = smbrun(add_script,NULL);
+               DEBUG(3,("_api_samr_create_user: Running the command `%s' gave %d\n", add_script, add_ret));
+       }
+       
+       pw = getpwnam_alloc(account);
+
+       if (pw) {
+               if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sam_pass, pw))) {
+                       passwd_free(&pw);
+                       return nt_status;
+               }
+               passwd_free(&pw); /* done with this now */
+       } else {
+               DEBUG(3,("attempting to create non-unix account %s\n", account));
+               
+               if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sam_pass))) {
+                       return nt_status;
+               }
+               
+               if (!pdb_set_username(sam_pass, account, PDB_CHANGED)) {
+                       pdb_free_sam(&sam_pass);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED);
+       if (!pdb_add_sam_account(sam_pass)) {
+               pdb_free_sam(&sam_pass);
+               DEBUG(0, ("could not add user/computer %s to passdb.  Check permissions?\n", 
+                         account));
+               return NT_STATUS_ACCESS_DENIED;         
+       }
+
+       pdb_reset_sam(sam_pass);
+       
+       if (!pdb_getsampwnam(sam_pass, account)) {
+               pdb_free_sam(&sam_pass);
+               DEBUG(0, ("could not find user/computer %s just added to passdb?!?\n", 
+                         account));
+               return NT_STATUS_ACCESS_DENIED;         
+       }
+       
+       /* Get the user's SID */
+       sid_copy(&sid, pdb_get_user_sid(sam_pass));
+       
+       samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+       se_map_generic(&des_access, &usr_generic_mapping);
+       if (!NT_STATUS_IS_OK(nt_status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_create_user"))) {
+               return nt_status;
+       }
+
+       /* associate the user's SID with the new handle. */
+       if ((info = get_samr_info_by_sid(&sid)) == NULL) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(info);
+       info->sid = sid;
+       info->acc_granted = acc_granted;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
+       r_u->user_rid=pdb_get_user_rid(sam_pass);
+
+       r_u->access_granted = acc_granted;
+
+       pdb_free_sam(&sam_pass);
+
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_connect_anon
+ ********************************************************************/
+
+NTSTATUS _samr_connect_anon(pipes_struct *p, SAMR_Q_CONNECT_ANON *q_u, SAMR_R_CONNECT_ANON *r_u)
+{
+       struct samr_info *info = NULL;
+
+       /* Access check */
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to samr_connect_anon\n"));
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               return r_u->status;
+       }
+
+       /* set up the SAMR connect_anon response */
+
+       r_u->status = NT_STATUS_OK;
+
+       /* associate the user's SID with the new handle. */
+       if ((info = get_samr_info_by_sid(NULL)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       info->status = q_u->unknown_0;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_connect
+ ********************************************************************/
+
+NTSTATUS _samr_connect(pipes_struct *p, SAMR_Q_CONNECT *q_u, SAMR_R_CONNECT *r_u)
+{
+       struct samr_info *info = NULL;
+       SEC_DESC *psd = NULL;
+       uint32    acc_granted;
+       uint32    des_access = q_u->access_mask;
+       size_t    sd_size;
+       NTSTATUS  nt_status;
+
+
+       DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+       /* Access check */
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to samr_connect\n"));
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               return r_u->status;
+       }
+
+       samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+       se_map_generic(&des_access, &sam_generic_mapping);
+       if (!NT_STATUS_IS_OK(nt_status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_connect"))) {
+               return nt_status;
+       }
+
+       r_u->status = NT_STATUS_OK;
+
+       /* associate the user's SID and access granted with the new handle. */
+       if ((info = get_samr_info_by_sid(NULL)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       info->acc_granted = acc_granted;
+       info->status = q_u->access_mask;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_connect4
+ ********************************************************************/
+
+NTSTATUS _samr_connect4(pipes_struct *p, SAMR_Q_CONNECT4 *q_u, SAMR_R_CONNECT4 *r_u)
+{
+       struct samr_info *info = NULL;
+       SEC_DESC *psd = NULL;
+       uint32    acc_granted;
+       uint32    des_access = q_u->access_mask;
+       size_t    sd_size;
+       NTSTATUS  nt_status;
+
+
+       DEBUG(5,("_samr_connect4: %d\n", __LINE__));
+
+       /* Access check */
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to samr_connect4\n"));
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               return r_u->status;
+       }
+
+       samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+       se_map_generic(&des_access, &sam_generic_mapping);
+       if (!NT_STATUS_IS_OK(nt_status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_connect"))) {
+               return nt_status;
+       }
+
+       r_u->status = NT_STATUS_OK;
+
+       /* associate the user's SID and access granted with the new handle. */
+       if ((info = get_samr_info_by_sid(NULL)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       info->acc_granted = acc_granted;
+       info->status = q_u->access_mask;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+NTSTATUS _samr_lookup_domain(pipes_struct *p, SAMR_Q_LOOKUP_DOMAIN *q_u, SAMR_R_LOOKUP_DOMAIN *r_u)
+{
+       struct samr_info *info;
+       fstring domain_name;
+       DOM_SID sid;
+
+       r_u->status = NT_STATUS_OK;
+
+       if (!find_policy_by_hnd(p, &q_u->connect_pol, (void**)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN, "_samr_lookup_domain"))) {
+               return r_u->status;
+       }
+
+       rpcstr_pull(domain_name, q_u->uni_domain.buffer, sizeof(domain_name), q_u->uni_domain.uni_str_len*2, 0);
+
+       ZERO_STRUCT(sid);
+
+       if (!secrets_fetch_domain_sid(domain_name, &sid)) {
+               r_u->status = NT_STATUS_NO_SUCH_DOMAIN;
+       }
+
+       DEBUG(2,("Returning domain sid for domain %s -> %s\n", domain_name, sid_string_static(&sid)));
+
+       init_samr_r_lookup_domain(r_u, &sid, r_u->status);
+
+       return r_u->status;
+}
+
+/******************************************************************
+makes a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+static BOOL make_enum_domains(TALLOC_CTX *ctx, SAM_ENTRY **pp_sam,
+                       UNISTR2 **pp_uni_name, uint32 num_sam_entries, fstring doms[])
+{
+       uint32 i;
+       SAM_ENTRY *sam;
+       UNISTR2 *uni_name;
+
+       DEBUG(5, ("make_enum_domains\n"));
+
+       *pp_sam = NULL;
+       *pp_uni_name = NULL;
+
+       if (num_sam_entries == 0)
+               return True;
+
+       sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+       uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+       if (sam == NULL || uni_name == NULL)
+               return False;
+
+       for (i = 0; i < num_sam_entries; i++) {
+               int len = doms[i] != NULL ? strlen(doms[i]) : 0;
+
+               init_sam_entry(&sam[i], len, 0);
+               init_unistr2(&uni_name[i], doms[i], len);
+       }
+
+       *pp_sam = sam;
+       *pp_uni_name = uni_name;
+
+       return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+NTSTATUS _samr_enum_domains(pipes_struct *p, SAMR_Q_ENUM_DOMAINS *q_u, SAMR_R_ENUM_DOMAINS *r_u)
+{
+       struct samr_info *info;
+       uint32 num_entries = 2;
+       fstring dom[2];
+       const char *name;
+
+       r_u->status = NT_STATUS_OK;
+       
+       if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(info->acc_granted, SA_RIGHT_SAM_ENUM_DOMAINS, "_samr_enum_domains"))) {
+               return r_u->status;
+       }
+
+       switch (lp_server_role()) {
+       case ROLE_DOMAIN_PDC:
+       case ROLE_DOMAIN_BDC:
+               name = lp_workgroup();
+               break;
+       default:
+               name = lp_netbios_name();
+       }
+
+       fstrcpy(dom[0],name);
+       strupper(dom[0]);
+       fstrcpy(dom[1],"Builtin");
+
+       if (!make_enum_domains(p->mem_ctx, &r_u->sam, &r_u->uni_dom_name, num_entries, dom))
+               return NT_STATUS_NO_MEMORY;
+
+       init_samr_r_enum_domains(r_u, q_u->start_idx + num_entries, num_entries);
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+NTSTATUS _api_samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_ALIAS *r_u)
+{
+       DOM_SID sid;
+       POLICY_HND domain_pol = q_u->dom_pol;
+       uint32 alias_rid = q_u->rid_alias;
+       POLICY_HND *alias_pol = &r_u->pol;
+       struct    samr_info *info = NULL;
+       SEC_DESC *psd = NULL;
+       uint32    acc_granted;
+       uint32    des_access = q_u->access_mask;
+       size_t    sd_size;
+       NTSTATUS  status;
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the domain policy and get the SID / access bits stored in the domain policy */
+       if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_samr_open_alias"))) {
+               return status;
+       }
+
+       /* append the alias' RID to it */
+       if (!sid_append_rid(&sid, alias_rid))
+               return NT_STATUS_NO_SUCH_USER;
+               
+       /*check if access can be granted as requested by client. */
+       samr_make_ali_obj_sd(p->mem_ctx, &psd, &sd_size);
+       se_map_generic(&des_access,&ali_generic_mapping);
+       if (!NT_STATUS_IS_OK(status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_open_alias"))) {
+               return status;
+       }
+
+       /*
+        * we should check if the rid really exist !!!
+        * JFM.
+        */
+
+       /* associate the user's SID with the new handle. */
+       if ((info = get_samr_info_by_sid(&sid)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+               
+       info->acc_granted = acc_granted;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, alias_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ set_user_info_10
+ ********************************************************************/
+
+static BOOL set_user_info_10(const SAM_USER_INFO_10 *id10, DOM_SID *sid)
+{
+       SAM_ACCOUNT *pwd =NULL;
+       BOOL ret;
+       
+       pdb_init_sam(&pwd);
+       
+       ret = pdb_getsampwsid(pwd, sid);
+       
+       if(ret==False) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       if (id10 == NULL) {
+               DEBUG(5, ("set_user_info_10: NULL id10\n"));
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       
+       /* FIX ME: check if the value is really changed --metze */
+       if (!pdb_set_acct_ctrl(pwd, id10->acb_info, PDB_CHANGED)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       if(!pdb_update_sam_account(pwd)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       pdb_free_sam(&pwd);
+
+       return True;
+}
+
+/*******************************************************************
+ set_user_info_12
+ ********************************************************************/
+
+static BOOL set_user_info_12(SAM_USER_INFO_12 *id12, DOM_SID *sid)
+{
+       SAM_ACCOUNT *pwd = NULL;
+
+       pdb_init_sam(&pwd);
+
+       if(!pdb_getsampwsid(pwd, sid)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       if (id12 == NULL) {
+               DEBUG(2, ("set_user_info_12: id12 is NULL\n"));
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       if (!pdb_set_lanman_passwd (pwd, id12->lm_pwd, PDB_CHANGED)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       if (!pdb_set_nt_passwd     (pwd, id12->nt_pwd, PDB_CHANGED)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       if (!pdb_set_pass_changed_now (pwd)) {
+               pdb_free_sam(&pwd);
+               return False; 
+       }
+       if(!pdb_update_sam_account(pwd)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       pdb_free_sam(&pwd);
+       return True;
+}
+
+/*******************************************************************
+ set_user_info_21
+ ********************************************************************/
+
+static BOOL set_user_info_21(SAM_USER_INFO_21 *id21, DOM_SID *sid)
+{
+       SAM_ACCOUNT *pwd = NULL;
+       if (id21 == NULL) {
+               DEBUG(5, ("set_user_info_21: NULL id21\n"));
+               return False;
+       }
+       pdb_init_sam(&pwd);
+       if (!pdb_getsampwsid(pwd, sid)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       copy_id21_to_sam_passwd(pwd, id21);
+       /*
+        * The funny part about the previous two calls is
+        * that pwd still has the password hashes from the
+        * passdb entry.  These have not been updated from
+        * id21.  I don't know if they need to be set.    --jerry
+        */
+       /* write the change out */
+       if(!pdb_update_sam_account(pwd)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       pdb_free_sam(&pwd);
+
+       return True;
+}
+
+/*******************************************************************
+ set_user_info_23
+ ********************************************************************/
+
+static BOOL set_user_info_23(SAM_USER_INFO_23 *id23, DOM_SID *sid)
+{
+       SAM_ACCOUNT *pwd = NULL;
+       pstring plaintext_buf;
+       uint32 len;
+       uint16 acct_ctrl;
+       if (id23 == NULL) {
+               DEBUG(5, ("set_user_info_23: NULL id23\n"));
+               return False;
+       }
+       pdb_init_sam(&pwd);
+       if (!pdb_getsampwsid(pwd, sid)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n",
+                 pdb_get_username(pwd)));
+
+       acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+       if (!decode_pw_buffer((char*)id23->pass, plaintext_buf, 256, &len)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+  
+       if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       copy_id23_to_sam_passwd(pwd, id23);
+       /* if it's a trust account, don't update /etc/passwd */
+       if ( (!IS_SAM_UNIX_USER(pwd)) ||
+               ( (acct_ctrl &  ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+               ( (acct_ctrl &  ACB_WSTRUST) ==  ACB_WSTRUST) ||
+               ( (acct_ctrl &  ACB_SVRTRUST) ==  ACB_SVRTRUST) ) {
+               DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+       } else  {
+               /* update the UNIX password */
+               if (lp_unix_password_sync() )
+                       if(!chgpasswd(pdb_get_username(pwd), "", plaintext_buf, True)) {
+                               pdb_free_sam(&pwd);
+                               return False;
+                       }
+       }
+       ZERO_STRUCT(plaintext_buf);
+       if(!pdb_update_sam_account(pwd)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       pdb_free_sam(&pwd);
+
+       return True;
+}
+
+/*******************************************************************
+ set_user_info_pw
+ ********************************************************************/
+
+static BOOL set_user_info_pw(char *pass, DOM_SID *sid)
+{
+       SAM_ACCOUNT *pwd = NULL;
+       uint32 len;
+       pstring plaintext_buf;
+       uint16 acct_ctrl;
+       pdb_init_sam(&pwd);
+       if (!pdb_getsampwsid(pwd, sid)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       
+       DEBUG(5, ("Attempting administrator password change for user %s\n",
+                 pdb_get_username(pwd)));
+
+       acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+       ZERO_STRUCT(plaintext_buf);
+       if (!decode_pw_buffer(pass, plaintext_buf, 256, &len)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+       /* if it's a trust account, don't update /etc/passwd */
+       if ( (!IS_SAM_UNIX_USER(pwd)) ||
+               ( (acct_ctrl &  ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+               ( (acct_ctrl &  ACB_WSTRUST) ==  ACB_WSTRUST) ||
+               ( (acct_ctrl &  ACB_SVRTRUST) ==  ACB_SVRTRUST) ) {
+               DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+       } else {
+               /* update the UNIX password */
+               if (lp_unix_password_sync()) {
+                       if(!chgpasswd(pdb_get_username(pwd), "", plaintext_buf, True)) {
+                               pdb_free_sam(&pwd);
+                               return False;
+                       }
+               }
+       }
+       ZERO_STRUCT(plaintext_buf);
+       DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n"));
+       /* update the SAMBA password */
+       if(!pdb_update_sam_account(pwd)) {
+               pdb_free_sam(&pwd);
+               return False;
+       }
+
+       pdb_free_sam(&pwd);
+
+       return True;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SET_USERINFO *r_u)
+{
+       DOM_SID sid;
+       POLICY_HND *pol = &q_u->pol;
+       uint16 switch_value = q_u->switch_value;
+       SAM_USERINFO_CTR *ctr = q_u->ctr;
+       uint32 acc_granted;
+       uint32 acc_required;
+
+       DEBUG(5, ("_samr_set_userinfo: %d\n", __LINE__));
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */   
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, acc_required, "_samr_set_userinfo"))) {
+               return r_u->status;
+       }
+               
+       DEBUG(5, ("_samr_set_userinfo: sid:%s, level:%d\n", sid_string_static(&sid), switch_value));
+
+       if (ctr == NULL) {
+               DEBUG(5, ("_samr_set_userinfo: NULL info level\n"));
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       /* ok!  user info levels (lots: see MSDEV help), off we go... */
+       switch (switch_value) {
+               case 0x12:
+                       if (!set_user_info_12(ctr->info.id12, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+
+               case 24:
+                       SamOEMhash(ctr->info.id24->pass, p->session_key, 516);
+
+                       dump_data(100, (char *)ctr->info.id24->pass, 516);
+
+                       if (!set_user_info_pw((char *)ctr->info.id24->pass, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+
+               case 25:
+#if 0
+                       /*
+                        * Currently we don't really know how to unmarshall
+                        * the level 25 struct, and the password encryption
+                        * is different. This is a placeholder for when we
+                        * do understand it. In the meantime just return INVALID
+                        * info level and W2K SP2 drops down to level 23... JRA.
+                        */
+
+                       SamOEMhash(ctr->info.id25->pass, p->session_key, 532);
+
+                       dump_data(100, (char *)ctr->info.id25->pass, 532);
+
+                       if (!set_user_info_pw(ctr->info.id25->pass, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+#endif
+                       return NT_STATUS_INVALID_INFO_CLASS;
+
+               case 23:
+                       SamOEMhash(ctr->info.id23->pass, p->session_key, 516);
+
+                       dump_data(100, (char *)ctr->info.id23->pass, 516);
+
+                       if (!set_user_info_23(ctr->info.id23, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo2
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_SET_USERINFO2 *r_u)
+{
+       DOM_SID sid;
+       SAM_USERINFO_CTR *ctr = q_u->ctr;
+       POLICY_HND *pol = &q_u->pol;
+       uint16 switch_value = q_u->switch_value;
+       uint32 acc_granted;
+       uint32 acc_required;
+
+       DEBUG(5, ("samr_reply_set_userinfo2: %d\n", __LINE__));
+
+       r_u->status = NT_STATUS_OK;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */   
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, acc_required, "_samr_set_userinfo2"))) {
+               return r_u->status;
+       }
+
+       DEBUG(5, ("samr_reply_set_userinfo2: sid:%s\n", sid_string_static(&sid)));
+
+       if (ctr == NULL) {
+               DEBUG(5, ("samr_reply_set_userinfo2: NULL info level\n"));
+               return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       switch_value=ctr->switch_value;
+
+       /* ok!  user info levels (lots: see MSDEV help), off we go... */
+       switch (switch_value) {
+               case 21:
+                       if (!set_user_info_21(ctr->info.id21, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+               case 16:
+                       if (!set_user_info_10(ctr->info.id10, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+               case 18:
+                       /* Used by AS/U JRA. */
+                       if (!set_user_info_12(ctr->info.id12, &sid))
+                               return NT_STATUS_ACCESS_DENIED;
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       return r_u->status;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_useraliases(pipes_struct *p, SAMR_Q_QUERY_USERALIASES *q_u, SAMR_R_QUERY_USERALIASES *r_u)
+{
+       int num_groups = 0, tmp_num_groups=0;
+       uint32 *rids=NULL, *new_rids=NULL, *tmp_rids=NULL;
+       struct samr_info *info = NULL;
+       int i,j;
+               
+       NTSTATUS ntstatus1;
+       NTSTATUS ntstatus2;
+
+       /* until i see a real useraliases query, we fack one up */
+
+       /* I have seen one, JFM 2/12/2001 */
+       /*
+        * Explanation of what this call does:
+        * for all the SID given in the request:
+        * return a list of alias (local groups)
+        * that have those SID as members.
+        *
+        * and that's the alias in the domain specified
+        * in the policy_handle
+        *
+        * if the policy handle is on an incorrect sid
+        * for example a user's sid
+        * we should reply NT_STATUS_OBJECT_TYPE_MISMATCH
+        */
+       
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_query_useraliases: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+               
+       ntstatus1 = access_check_samr_function(info->acc_granted, SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM, "_samr_query_useraliases");
+       ntstatus2 = access_check_samr_function(info->acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_samr_query_useraliases");
+       
+       if (!NT_STATUS_IS_OK(ntstatus1) || !NT_STATUS_IS_OK(ntstatus2)) {
+               if (!(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus2)) &&
+                   !(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus1))) {
+                       return (NT_STATUS_IS_OK(ntstatus1)) ? ntstatus2 : ntstatus1;
+               }
+       }               
+
+       if (!sid_check_is_domain(&info->sid) &&
+           !sid_check_is_builtin(&info->sid))
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+
+       for (i=0; i<q_u->num_sids1; i++) {
+
+               r_u->status=get_alias_user_groups(p->mem_ctx, &info->sid, &tmp_num_groups, &tmp_rids, &(q_u->sid[i].sid));
+
+               /*
+                * if there is an error, we just continue as
+                * it can be an unfound user or group
+                */
+               if (!NT_STATUS_IS_OK(r_u->status)) {
+                       DEBUG(10,("_samr_query_useraliases: an error occured while getting groups\n"));
+                       continue;
+               }
+
+               if (tmp_num_groups==0) {
+                       DEBUG(10,("_samr_query_useraliases: no groups found\n"));
+                       continue;
+               }
+
+               new_rids=(uint32 *)talloc_realloc(p->mem_ctx, rids, (num_groups+tmp_num_groups)*sizeof(uint32));
+               if (new_rids==NULL) {
+                       DEBUG(0,("_samr_query_useraliases: could not realloc memory\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+               rids=new_rids;
+
+               for (j=0; j<tmp_num_groups; j++)
+                       rids[j+num_groups]=tmp_rids[j];
+               
+               safe_free(tmp_rids);
+               
+               num_groups+=tmp_num_groups;
+       }
+       
+       init_samr_r_query_useraliases(r_u, num_groups, rids, NT_STATUS_OK);
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_R_QUERY_ALIASMEM *r_u)
+{
+       int i;
+
+       GROUP_MAP map;
+       int num_uids = 0;
+       DOM_SID2 *sid;
+       uid_t *uid=NULL;
+
+       DOM_SID alias_sid;
+       DOM_SID als_sid;
+       uint32 alias_rid;
+       fstring alias_sid_str;
+       DOM_SID temp_sid;
+
+       SAM_ACCOUNT *sam_user = NULL;
+       BOOL check;
+       uint32 acc_granted;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = 
+               access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_GET_MEMBERS, "_samr_query_aliasmem"))) {
+               return r_u->status;
+       }
+               
+       sid_copy(&als_sid, &alias_sid);
+       sid_to_string(alias_sid_str, &alias_sid);
+       sid_split_rid(&alias_sid, &alias_rid);
+
+       DEBUG(10, ("sid is %s\n", alias_sid_str));
+
+       if (sid_equal(&alias_sid, &global_sid_Builtin)) {
+               DEBUG(10, ("lookup on Builtin SID (S-1-5-32)\n"));
+               if(!get_builtin_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV))
+                       return NT_STATUS_NO_SUCH_ALIAS;
+       } else {
+               if (sid_equal(&alias_sid, get_global_sam_sid())) {
+                       DEBUG(10, ("lookup on Server SID\n"));
+                       if(!get_local_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV))
+                               return NT_STATUS_NO_SUCH_ALIAS;
+               }
+       }
+
+       if(!get_uid_list_of_group(map.gid, &uid, &num_uids))
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       DEBUG(10, ("sid is %s\n", alias_sid_str));
+       sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_uids); 
+       if (num_uids!=0 && sid == NULL) 
+               return NT_STATUS_NO_MEMORY;
+
+       for (i = 0; i < num_uids; i++) {
+               struct passwd *pass;
+               uint32 rid;
+
+               sid_copy(&temp_sid, get_global_sam_sid());
+
+               pass = getpwuid_alloc(uid[i]);
+               if (!pass) continue;
+
+               if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_user))) {
+                       passwd_free(&pass);
+                       continue;
+               }
+
+               become_root();
+               check = pdb_getsampwnam(sam_user, pass->pw_name);
+               unbecome_root();
+       
+               if (check != True) {
+                       pdb_free_sam(&sam_user);
+                       passwd_free(&pass);
+                       continue;
+               }
+       
+               rid = pdb_get_user_rid(sam_user);
+               if (rid == 0) {
+                       pdb_free_sam(&sam_user);
+                       passwd_free(&pass);
+                       continue;
+               }
+
+               pdb_free_sam(&sam_user);
+               passwd_free(&pass);
+
+               sid_append_rid(&temp_sid, rid);
+               
+               init_dom_sid2(&sid[i], &temp_sid);
+       }
+
+       DEBUG(10, ("sid is %s\n", alias_sid_str));
+       init_samr_r_query_aliasmem(r_u, num_uids, sid, NT_STATUS_OK);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u)
+{
+       int num_uids = 0;
+       int i;
+       DOM_SID group_sid;
+       uint32 group_rid;
+       fstring group_sid_str;
+       uid_t *uid=NULL;
+       
+       GROUP_MAP map;
+
+       uint32 *rid=NULL;
+       uint32 *attr=NULL;
+
+       SAM_ACCOUNT *sam_user = NULL;
+       BOOL check;
+       uint32 acc_granted;
+
+       /* find the policy handle.  open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_GROUP_GET_MEMBERS, "_samr_query_groupmem"))) {
+               return r_u->status;
+       }
+               
+       /* todo: change to use sid_compare_front */
+
+       sid_split_rid(&group_sid, &group_rid);
+       sid_to_string(group_sid_str, &group_sid);
+       DEBUG(10, ("sid is %s\n", group_sid_str));
+
+       /* can we get a query for an SID outside our domain ? */
+       if (!sid_equal(&group_sid, get_global_sam_sid()))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       sid_append_rid(&group_sid, group_rid);
+       DEBUG(10, ("lookup on Domain SID\n"));
+
+       if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       if(!get_uid_list_of_group(map.gid, &uid, &num_uids))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids);
+       attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids);
+       
+       if (num_uids!=0 && (rid==NULL || attr==NULL))
+               return NT_STATUS_NO_MEMORY;
+       
+       for (i=0; i<num_uids; i++) {
+               struct passwd *pass;
+               uint32 urid;
+
+               pass = getpwuid_alloc(uid[i]);
+               if (!pass) continue;
+
+               if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_user))) {
+                       passwd_free(&pass);
+                       continue;
+               }
+
+               become_root();
+               check = pdb_getsampwnam(sam_user, pass->pw_name);
+               unbecome_root();
+       
+               if (check != True) {
+                       pdb_free_sam(&sam_user);
+                       passwd_free(&pass);
+                       continue;
+               }
+       
+               urid = pdb_get_user_rid(sam_user);
+               if (urid == 0) {
+                       pdb_free_sam(&sam_user);
+                       passwd_free(&pass);
+                       continue;
+               }
+
+               pdb_free_sam(&sam_user);
+               passwd_free(&pass);
+
+               rid[i] = urid;
+               attr[i] = SID_NAME_USER;                
+       }
+
+       init_samr_r_query_groupmem(r_u, num_uids, rid, attr, NT_STATUS_OK);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_add_aliasmem(pipes_struct *p, SAMR_Q_ADD_ALIASMEM *q_u, SAMR_R_ADD_ALIASMEM *r_u)
+{
+       DOM_SID alias_sid;
+       fstring alias_sid_str;
+       uid_t uid;
+       struct passwd *pwd;
+       struct group *grp;
+       fstring grp_name;
+       GROUP_MAP map;
+       NTSTATUS ret;
+       SAM_ACCOUNT *sam_user = NULL;
+       BOOL check;
+       uint32 acc_granted;
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_ADD_MEMBER, "_samr_add_aliasmem"))) {
+               return r_u->status;
+       }
+               
+       sid_to_string(alias_sid_str, &alias_sid);
+       DEBUG(10, ("sid is %s\n", alias_sid_str));
+
+       if (sid_compare(&alias_sid, get_global_sam_sid())>0) {
+               DEBUG(10, ("adding member on Server SID\n"));
+               if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV))
+                       return NT_STATUS_NO_SUCH_ALIAS;
+       
+       } else {
+               if (sid_compare(&alias_sid, &global_sid_Builtin)>0) {
+                       DEBUG(10, ("adding member on BUILTIN SID\n"));
+                       if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV))
+                               return NT_STATUS_NO_SUCH_ALIAS;
+
+               } else
+                       return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       ret = pdb_init_sam(&sam_user);
+       if (!NT_STATUS_IS_OK(ret))
+               return ret;
+       
+       check = pdb_getsampwsid(sam_user, &q_u->sid.sid);
+       
+       if (check != True) {
+               pdb_free_sam(&sam_user);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       uid = pdb_get_uid(sam_user);
+       if (uid == -1) {
+               pdb_free_sam(&sam_user);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       pdb_free_sam(&sam_user);
+
+       if ((pwd=getpwuid_alloc(uid)) == NULL) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       if ((grp=getgrgid(map.gid)) == NULL) {
+               passwd_free(&pwd);
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       /* we need to copy the name otherwise it's overloaded in user_in_group_list */
+       fstrcpy(grp_name, grp->gr_name);
+
+       /* if the user is already in the group */
+       if(user_in_unix_group_list(pwd->pw_name, grp_name)) {
+               passwd_free(&pwd);
+               return NT_STATUS_MEMBER_IN_ALIAS;
+       }
+
+       /* 
+        * ok, the group exist, the user exist, the user is not in the group,
+        * we can (finally) add it to the group !
+        */
+       smb_add_user_group(grp_name, pwd->pw_name);
+
+       /* check if the user has been added then ... */
+       if(!user_in_unix_group_list(pwd->pw_name, grp_name)) {
+               passwd_free(&pwd);
+               return NT_STATUS_MEMBER_NOT_IN_ALIAS;   /* don't know what to reply else */
+       }
+
+       passwd_free(&pwd);
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DEL_ALIASMEM *r_u)
+{
+       DOM_SID alias_sid;
+       fstring alias_sid_str;
+       struct group *grp;
+       fstring grp_name;
+       GROUP_MAP map;
+       SAM_ACCOUNT *sam_pass=NULL;
+       uint32 acc_granted;
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_REMOVE_MEMBER, "_samr_del_aliasmem"))) {
+               return r_u->status;
+       }
+       
+       sid_to_string(alias_sid_str, &alias_sid);
+       DEBUG(10, ("_samr_del_aliasmem:sid is %s\n", alias_sid_str));
+
+       if (!sid_check_is_in_our_domain(&alias_sid) &&
+           !sid_check_is_in_builtin(&alias_sid)) {
+               DEBUG(10, ("_samr_del_aliasmem:invalid alias group\n"));
+               return NT_STATUS_NO_SUCH_ALIAS;
+       }
+
+       if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       if ((grp=getgrgid(map.gid)) == NULL)
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       /* we need to copy the name otherwise it's overloaded in user_in_unix_group_list */
+       fstrcpy(grp_name, grp->gr_name);
+
+       /* check if the user exists before trying to remove it from the group */
+       pdb_init_sam(&sam_pass);
+       if(!pdb_getsampwsid(sam_pass, &q_u->sid.sid)) {
+               DEBUG(5,("_samr_del_aliasmem:User %s doesn't exist.\n", pdb_get_username(sam_pass)));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       /* if the user is not in the group */
+       if(!user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_MEMBER_IN_ALIAS;
+       }
+
+       smb_delete_user_group(grp_name, pdb_get_username(sam_pass));
+
+       /* check if the user has been removed then ... */
+       if(user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_MEMBER_NOT_IN_ALIAS;   /* don't know what to reply else */
+       }
+
+       pdb_free_sam(&sam_pass);
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_add_groupmem(pipes_struct *p, SAMR_Q_ADD_GROUPMEM *q_u, SAMR_R_ADD_GROUPMEM *r_u)
+{
+       DOM_SID group_sid;
+       DOM_SID user_sid;
+       fstring group_sid_str;
+       struct passwd *pwd;
+       struct group *grp;
+       fstring grp_name;
+       GROUP_MAP map;
+       uid_t uid;
+       NTSTATUS ret;
+       SAM_ACCOUNT *sam_user=NULL;
+       BOOL check;
+       uint32 acc_granted;
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_GROUP_ADD_MEMBER, "_samr_add_groupmem"))) {
+               return r_u->status;
+       }
+
+       sid_to_string(group_sid_str, &group_sid);
+       DEBUG(10, ("sid is %s\n", group_sid_str));
+
+       if (sid_compare(&group_sid, get_global_sam_sid())<=0)
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       DEBUG(10, ("lookup on Domain SID\n"));
+
+       if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       sid_copy(&user_sid, get_global_sam_sid());
+       sid_append_rid(&user_sid, q_u->rid);
+
+       ret = pdb_init_sam(&sam_user);
+       if (!NT_STATUS_IS_OK(ret))
+               return ret;
+       
+       check = pdb_getsampwsid(sam_user, &user_sid);
+       
+       if (check != True) {
+               pdb_free_sam(&sam_user);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+       
+       uid = pdb_get_uid(sam_user);
+       if (uid == -1) {
+               pdb_free_sam(&sam_user);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       pdb_free_sam(&sam_user);
+
+       if ((pwd=getpwuid_alloc(uid)) == NULL) {
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       if ((grp=getgrgid(map.gid)) == NULL) {
+               passwd_free(&pwd);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       /* we need to copy the name otherwise it's overloaded in user_in_unix_group_list */
+       fstrcpy(grp_name, grp->gr_name);
+
+       /* if the user is already in the group */
+       if(user_in_unix_group_list(pwd->pw_name, grp_name)) {
+               passwd_free(&pwd);
+               return NT_STATUS_MEMBER_IN_GROUP;
+       }
+
+       /* 
+        * ok, the group exist, the user exist, the user is not in the group,
+        *
+        * we can (finally) add it to the group !
+        */
+
+       smb_add_user_group(grp_name, pwd->pw_name);
+
+       /* check if the user has been added then ... */
+       if(!user_in_unix_group_list(pwd->pw_name, grp_name)) {
+               passwd_free(&pwd);
+               return NT_STATUS_MEMBER_NOT_IN_GROUP;           /* don't know what to reply else */
+       }
+
+       passwd_free(&pwd);
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_del_groupmem(pipes_struct *p, SAMR_Q_DEL_GROUPMEM *q_u, SAMR_R_DEL_GROUPMEM *r_u)
+{
+       DOM_SID group_sid;
+       DOM_SID user_sid;
+       SAM_ACCOUNT *sam_pass=NULL;
+       GROUP_MAP map;
+       fstring grp_name;
+       struct group *grp;
+       uint32 acc_granted;
+
+       /*
+        * delete the group member named q_u->rid
+        * who is a member of the sid associated with the handle
+        * the rid is a user's rid as the group is a domain group.
+        */
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_GROUP_REMOVE_MEMBER, "_samr_del_groupmem"))) {
+               return r_u->status;
+       }
+               
+       if (!sid_check_is_in_our_domain(&group_sid))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       sid_copy(&user_sid, get_global_sam_sid());
+       sid_append_rid(&user_sid, q_u->rid);
+
+       if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       if ((grp=getgrgid(map.gid)) == NULL)
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       /* we need to copy the name otherwise it's overloaded in user_in_group_list */
+       fstrcpy(grp_name, grp->gr_name);
+
+       /* check if the user exists before trying to remove it from the group */
+       pdb_init_sam(&sam_pass);
+       if (!pdb_getsampwsid(sam_pass, &user_sid)) {
+               DEBUG(5,("User %s doesn't exist.\n", pdb_get_username(sam_pass)));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       /* if the user is not in the group */
+       if (!user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_MEMBER_NOT_IN_GROUP;
+       }
+
+       smb_delete_user_group(grp_name, pdb_get_username(sam_pass));
+
+       /* check if the user has been removed then ... */
+       if (user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_ACCESS_DENIED;         /* don't know what to reply else */
+       }
+       
+       pdb_free_sam(&sam_pass);
+       return NT_STATUS_OK;
+
+}
+
+/****************************************************************************
+ Delete a UNIX user on demand.
+****************************************************************************/
+
+static int smb_delete_user(const char *unix_user)
+{
+       pstring del_script;
+       int ret;
+
+       pstrcpy(del_script, lp_deluser_script());
+       if (! *del_script)
+               return -1;
+       all_string_sub(del_script, "%u", unix_user, sizeof(pstring));
+       ret = smbrun(del_script,NULL);
+       DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret));
+       return ret;
+}
+
+/*********************************************************************
+ _samr_delete_dom_user
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAMR_R_DELETE_DOM_USER *r_u )
+{
+       DOM_SID user_sid;
+       SAM_ACCOUNT *sam_pass=NULL;
+       uint32 acc_granted;
+
+       DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__));
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &user_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_user"))) {
+               return r_u->status;
+       }
+               
+       if (!sid_check_is_in_our_domain(&user_sid))
+               return NT_STATUS_CANNOT_DELETE;
+
+       /* check if the user exists before trying to delete */
+       pdb_init_sam(&sam_pass);
+       if(!pdb_getsampwsid(sam_pass, &user_sid)) {
+               DEBUG(5,("_samr_delete_dom_user:User %s doesn't exist.\n", pdb_get_username(sam_pass)));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       /* delete the unix side */
+       /*
+        * note: we don't check if the delete really happened
+        * as the script is not necessary present
+        * and maybe the sysadmin doesn't want to delete the unix side
+        */
+       smb_delete_user(pdb_get_username(sam_pass));
+
+       /* and delete the samba side */
+       if (!pdb_delete_sam_account(sam_pass)) {
+               DEBUG(5,("_samr_delete_dom_user:Failed to delete entry for user %s.\n", pdb_get_username(sam_pass)));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_CANNOT_DELETE;
+       }
+       
+       pdb_free_sam(&sam_pass);
+
+       if (!close_policy_hnd(p, &q_u->user_pol))
+               return NT_STATUS_OBJECT_NAME_INVALID;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_group(pipes_struct *p, SAMR_Q_DELETE_DOM_GROUP *q_u, SAMR_R_DELETE_DOM_GROUP *r_u)
+{
+       DOM_SID group_sid;
+       DOM_SID dom_sid;
+       uint32 group_rid;
+       fstring group_sid_str;
+       gid_t gid;
+       struct group *grp;
+       GROUP_MAP map;
+       uint32 acc_granted;
+
+       DEBUG(5, ("samr_delete_dom_group: %d\n", __LINE__));
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_group"))) {
+               return r_u->status;
+       }
+               
+       sid_copy(&dom_sid, &group_sid);
+       sid_to_string(group_sid_str, &dom_sid);
+       sid_split_rid(&dom_sid, &group_rid);
+
+       DEBUG(10, ("sid is %s\n", group_sid_str));
+
+       /* we check if it's our SID before deleting */
+       if (!sid_equal(&dom_sid, get_global_sam_sid()))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       DEBUG(10, ("lookup on Domain SID\n"));
+
+       if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       gid=map.gid;
+
+       /* check if group really exists */
+       if ( (grp=getgrgid(gid)) == NULL)
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       /* we can delete the UNIX group */
+       smb_delete_group(grp->gr_name);
+
+       /* check if the group has been successfully deleted */
+       if ( (grp=getgrgid(gid)) != NULL)
+               return NT_STATUS_ACCESS_DENIED;
+
+       if(!pdb_delete_group_mapping_entry(group_sid))
+               return NT_STATUS_ACCESS_DENIED;
+
+       if (!close_policy_hnd(p, &q_u->group_pol))
+               return NT_STATUS_OBJECT_NAME_INVALID;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, SAMR_R_DELETE_DOM_ALIAS *r_u)
+{
+       DOM_SID alias_sid;
+       DOM_SID dom_sid;
+       uint32 alias_rid;
+       fstring alias_sid_str;
+       gid_t gid;
+       struct group *grp;
+       GROUP_MAP map;
+       uint32 acc_granted;
+
+       DEBUG(5, ("_samr_delete_dom_alias: %d\n", __LINE__));
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_alias"))) {
+               return r_u->status;
+       }
+               
+       sid_copy(&dom_sid, &alias_sid);
+       sid_to_string(alias_sid_str, &dom_sid);
+       sid_split_rid(&dom_sid, &alias_rid);
+
+       DEBUG(10, ("sid is %s\n", alias_sid_str));
+
+       /* we check if it's our SID before deleting */
+       if (!sid_equal(&dom_sid, get_global_sam_sid()))
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       DEBUG(10, ("lookup on Local SID\n"));
+
+       if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       gid=map.gid;
+
+       /* check if group really exists */
+       if ( (grp=getgrgid(gid)) == NULL)
+               return NT_STATUS_NO_SUCH_ALIAS;
+
+       /* we can delete the UNIX group */
+       smb_delete_group(grp->gr_name);
+
+       /* check if the group has been successfully deleted */
+       if ( (grp=getgrgid(gid)) != NULL)
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* don't check if we removed it as it could be an un-mapped group */
+       pdb_delete_group_mapping_entry(alias_sid);
+
+       if (!close_policy_hnd(p, &q_u->alias_pol))
+               return NT_STATUS_OBJECT_NAME_INVALID;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_group(pipes_struct *p, SAMR_Q_CREATE_DOM_GROUP *q_u, SAMR_R_CREATE_DOM_GROUP *r_u)
+{
+       DOM_SID dom_sid;
+       DOM_SID info_sid;
+       fstring name;
+       fstring sid_string;
+       struct group *grp;
+       struct samr_info *info;
+       PRIVILEGE_SET priv_set;
+       uint32 acc_granted;
+       gid_t gid;
+
+       init_privilege(&priv_set);
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &dom_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_CREATE_GROUP, "_samr_create_dom_group"))) {
+               return r_u->status;
+       }
+               
+       if (!sid_equal(&dom_sid, get_global_sam_sid()))
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/
+
+       unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+       /* check if group already exist */
+       if ((grp=getgrnam(name)) != NULL)
+               return NT_STATUS_GROUP_EXISTS;
+
+       /* we can create the UNIX group */
+       if (smb_create_group(name, &gid) != 0)
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* check if the group has been successfully created */
+       if ((grp=getgrgid(gid)) == NULL)
+               return NT_STATUS_ACCESS_DENIED;
+
+       r_u->rid=pdb_gid_to_group_rid(grp->gr_gid);
+
+       /* add the group to the mapping table */
+       sid_copy(&info_sid, get_global_sam_sid());
+       sid_append_rid(&info_sid, r_u->rid);
+       sid_to_string(sid_string, &info_sid);
+
+       if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_DOM_GRP, name, NULL, priv_set, PR_ACCESS_FROM_NETWORK))
+               return NT_STATUS_ACCESS_DENIED;
+
+       if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, SAMR_R_CREATE_DOM_ALIAS *r_u)
+{
+       DOM_SID dom_sid;
+       DOM_SID info_sid;
+       fstring name;
+       fstring sid_string;
+       struct group *grp;
+       struct samr_info *info;
+       PRIVILEGE_SET priv_set;
+       uint32 acc_granted;
+       gid_t gid;
+
+       init_privilege(&priv_set);
+
+       /* Find the policy handle. Open a policy on it. */
+       if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &dom_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+               
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_CREATE_ALIAS, "_samr_create_alias"))) {
+               return r_u->status;
+       }
+               
+       if (!sid_equal(&dom_sid, get_global_sam_sid()))
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* TODO: check if allowed to create group  and add a become_root/unbecome_root pair.*/
+
+       unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+       /* check if group already exists */
+       if ( (grp=getgrnam(name)) != NULL)
+               return NT_STATUS_GROUP_EXISTS;
+
+       /* we can create the UNIX group */
+       if (smb_create_group(name, &gid) != 0)
+               return NT_STATUS_ACCESS_DENIED;
+
+       /* check if the group has been successfully created */
+       if ((grp=getgrgid(gid)) == NULL)
+               return NT_STATUS_ACCESS_DENIED;
+
+       r_u->rid=pdb_gid_to_group_rid(grp->gr_gid);
+
+       sid_copy(&info_sid, get_global_sam_sid());
+       sid_append_rid(&info_sid, r_u->rid);
+       sid_to_string(sid_string, &info_sid);
+
+       /* add the group to the mapping table */
+       if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_ALIAS, name, NULL, priv_set, PR_ACCESS_FROM_NETWORK))
+               return NT_STATUS_ACCESS_DENIED;
+
+       if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->alias_pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_groupinfo
+
+sends the name/comment pair of a domain group
+level 1 send also the number of users of that group
+*********************************************************************/
+
+NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAMR_R_QUERY_GROUPINFO *r_u)
+{
+       DOM_SID group_sid;
+       GROUP_MAP map;
+       uid_t *uid=NULL;
+       int num_uids=0;
+       GROUP_INFO_CTR *ctr;
+       uint32 acc_granted;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_GROUP_LOOKUP_INFO, "_samr_query_groupinfo"))) {
+               return r_u->status;
+       }
+               
+       if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_INVALID_HANDLE;
+
+       ctr=(GROUP_INFO_CTR *)talloc_zero(p->mem_ctx, sizeof(GROUP_INFO_CTR));
+       if (ctr==NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       switch (q_u->switch_level) {
+               case 1:
+                       ctr->switch_value1 = 1;
+                       if(!get_uid_list_of_group(map.gid, &uid, &num_uids))
+                               return NT_STATUS_NO_SUCH_GROUP;
+                       init_samr_group_info1(&ctr->group.info1, map.nt_name, map.comment, num_uids);
+                       SAFE_FREE(uid);
+                       break;
+               case 3:
+                       ctr->switch_value1 = 3;
+                       init_samr_group_info3(&ctr->group.info3);
+                       break;
+               case 4:
+                       ctr->switch_value1 = 4;
+                       init_samr_group_info4(&ctr->group.info4, map.comment);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       init_samr_r_query_groupinfo(r_u, ctr, NT_STATUS_OK);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_groupinfo
+ update a domain group's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_groupinfo(pipes_struct *p, SAMR_Q_SET_GROUPINFO *q_u, SAMR_R_SET_GROUPINFO *r_u)
+{
+       DOM_SID group_sid;
+       GROUP_MAP map;
+       GROUP_INFO_CTR *ctr;
+       uint32 acc_granted;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_GROUP_SET_INFO, "_samr_set_groupinfo"))) {
+               return r_u->status;
+       }
+               
+       if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+       
+       ctr=q_u->ctr;
+
+       switch (ctr->switch_value1) {
+               case 1:
+                       unistr2_to_ascii(map.comment, &(ctr->group.info1.uni_acct_desc), sizeof(map.comment)-1);
+                       break;
+               case 4:
+                       unistr2_to_ascii(map.comment, &(ctr->group.info4.uni_acct_desc), sizeof(map.comment)-1);
+                       break;
+               default:
+                       free_privilege(&map.priv_set);
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       if(!pdb_update_group_mapping_entry(&map)) {
+               free_privilege(&map.priv_set);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       free_privilege(&map.priv_set);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_aliasinfo
+ update an alias's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_SET_ALIASINFO *r_u)
+{
+       DOM_SID group_sid;
+       GROUP_MAP map;
+       ALIAS_INFO_CTR *ctr;
+       uint32 acc_granted;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &group_sid, &acc_granted))
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_SET_INFO, "_samr_set_aliasinfo"))) {
+               return r_u->status;
+       }
+               
+       if (!get_local_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+       
+       ctr=&q_u->ctr;
+
+       switch (ctr->switch_value1) {
+               case 3:
+                       unistr2_to_ascii(map.comment, &(ctr->alias.info3.uni_acct_desc), sizeof(map.comment)-1);
+                       break;
+               default:
+                       free_privilege(&map.priv_set);
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       if(!pdb_update_group_mapping_entry(&map)) {
+               free_privilege(&map.priv_set);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       free_privilege(&map.priv_set);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_get_dom_pwinfo
+*********************************************************************/
+
+NTSTATUS _samr_get_dom_pwinfo(pipes_struct *p, SAMR_Q_GET_DOM_PWINFO *q_u, SAMR_R_GET_DOM_PWINFO *r_u)
+{
+       /* Perform access check.  Since this rpc does not require a
+          policy handle it will not be caught by the access checks on
+          SAMR_CONNECT or SAMR_CONNECT_ANON. */
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to samr_get_dom_pwinfo\n"));
+               r_u->status = NT_STATUS_ACCESS_DENIED;
+               return r_u->status;
+       }
+
+       /* Actually, returning zeros here works quite well :-). */
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_open_group
+*********************************************************************/
+
+NTSTATUS _samr_open_group(pipes_struct *p, SAMR_Q_OPEN_GROUP *q_u, SAMR_R_OPEN_GROUP *r_u)
+{
+       DOM_SID sid;
+       DOM_SID info_sid;
+       GROUP_MAP map;
+       struct samr_info *info;
+       SEC_DESC         *psd = NULL;
+       uint32            acc_granted;
+       uint32            des_access;
+       size_t            sd_size;
+       NTSTATUS          status;
+       fstring sid_string;
+
+       if (!get_lsa_policy_samr_sid(p, &q_u->domain_pol, &sid, &acc_granted)) 
+               return NT_STATUS_INVALID_HANDLE;
+       
+       if (!NT_STATUS_IS_OK(status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_samr_open_group"))) {
+               return status;
+       }
+               
+       /*check if access can be granted as requested by client. */
+       samr_make_grp_obj_sd(p->mem_ctx, &psd, &sd_size);
+       se_map_generic(&des_access,&grp_generic_mapping);
+       if (!NT_STATUS_IS_OK(status = 
+                            access_check_samr_object(psd, p->pipe_user.nt_user_token, 
+                                                     des_access, &acc_granted, "_samr_open_group"))) {
+               return status;
+       }
+
+
+       /* this should not be hard-coded like this */
+       if (!sid_equal(&sid, get_global_sam_sid()))
+               return NT_STATUS_ACCESS_DENIED;
+
+       sid_copy(&info_sid, get_global_sam_sid());
+       sid_append_rid(&info_sid, q_u->rid_group);
+       sid_to_string(sid_string, &info_sid);
+
+       if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+               return NT_STATUS_NO_MEMORY;
+               
+       info->acc_granted = acc_granted;
+
+       DEBUG(10, ("_samr_open_group:Opening SID: %s\n", sid_string));
+
+       /* check if that group really exists */
+       if (!get_domain_group_from_sid(info->sid, &map, MAPPING_WITHOUT_PRIV))
+               return NT_STATUS_NO_SUCH_GROUP;
+
+       /* get a (unique) handle.  open a policy on it. */
+       if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_unknown_2d
+*********************************************************************/
+
+NTSTATUS _samr_unknown_2d(pipes_struct *p, SAMR_Q_UNKNOWN_2D *q_u, SAMR_R_UNKNOWN_2D *r_u)
+{
+       DEBUG(0,("_samr_unknown_2d: Not yet implemented.\n"));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*******************************************************************
+ _samr_unknown_2e
+ ********************************************************************/
+
+NTSTATUS _samr_unknown_2e(pipes_struct *p, SAMR_Q_UNKNOWN_2E *q_u, SAMR_R_UNKNOWN_2E *r_u)
+{
+       struct samr_info *info = NULL;
+       SAM_UNK_CTR *ctr;
+       uint32 min_pass_len,pass_hist,flag;
+       time_t u_expire, u_min_age;
+       NTTIME nt_expire, nt_min_age;
+
+       time_t u_lock_duration, u_reset_time;
+       NTTIME nt_lock_duration, nt_reset_time;
+       uint32 lockout;
+       
+       time_t u_logout;
+       NTTIME nt_logout;
+
+       uint32 num_users=0, num_groups=0, num_aliases=0;
+
+       uint32 account_policy_temp;
+
+       if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       ZERO_STRUCTP(ctr);
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+               return NT_STATUS_INVALID_HANDLE;
+
+       switch (q_u->switch_value) {
+               case 0x01:
+                       account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+                       min_pass_len = account_policy_temp;
+
+                       account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+                       pass_hist = account_policy_temp;
+
+                       account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+                       flag = account_policy_temp;
+
+                       account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+                       u_expire = account_policy_temp;
+
+                       account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+                       u_min_age = account_policy_temp;
+
+                       unix_to_nt_time_abs(&nt_expire, u_expire);
+                       unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+                       init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist, 
+                                      flag, nt_expire, nt_min_age);
+                       break;
+               case 0x02:
+                       become_root();          
+                       r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+                       unbecome_root();
+                       if (!NT_STATUS_IS_OK(r_u->status)) {
+                               DEBUG(5, ("_samr_unknown_2e: load_sampwd_entries failed\n"));
+                               return r_u->status;
+                       }
+                       num_users=info->disp_info.num_user_account;
+                       free_samr_db(info);
+                       
+                       r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+                       if (NT_STATUS_IS_ERR(r_u->status)) {
+                               DEBUG(5, ("_samr_unknown_2e: load_group_domain_entries failed\n"));
+                               return r_u->status;
+                       }
+                       num_groups=info->disp_info.num_group_account;
+                       free_samr_db(info);
+
+                       /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+                       init_unk_info2(&ctr->info.inf2, lp_workgroup(), lp_netbios_name(), (uint32) time(NULL), 
+                                      num_users, num_groups, num_aliases);
+                       break;
+               case 0x03:
+                       account_policy_get(AP_TIME_TO_LOGOUT, &account_policy_temp);
+                       u_logout = account_policy_temp;
+
+                       unix_to_nt_time_abs(&nt_logout, u_logout);
+                       
+                       init_unk_info3(&ctr->info.inf3, nt_logout);
+                       break;
+               case 0x05:
+                       init_unk_info5(&ctr->info.inf5, lp_netbios_name());
+                       break;
+               case 0x06:
+                       init_unk_info6(&ctr->info.inf6);
+                       break;
+               case 0x07:
+                       init_unk_info7(&ctr->info.inf7);
+                       break;
+               case 0x0c:
+                       account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+                       u_lock_duration = account_policy_temp;
+
+                       account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+                       u_reset_time = account_policy_temp;
+
+                       account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+                       lockout = account_policy_temp;
+       
+                       unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+                       unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+       
+                       init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       init_samr_r_samr_unknown_2e(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+
+       DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ _samr_
+ ********************************************************************/
+
+NTSTATUS _samr_set_dom_info(pipes_struct *p, SAMR_Q_SET_DOMAIN_INFO *q_u, SAMR_R_SET_DOMAIN_INFO *r_u)
+{
+       time_t u_expire, u_min_age;
+       time_t u_logout;
+       time_t u_lock_duration, u_reset_time;
+
+       r_u->status = NT_STATUS_OK;
+
+       DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+       /* find the policy handle.  open a policy on it. */
+       if (!find_policy_by_hnd(p, &q_u->domain_pol, NULL))
+               return NT_STATUS_INVALID_HANDLE;
+
+       DEBUG(5,("_samr_set_dom_info: switch_value: %d\n", q_u->switch_value));
+
+       switch (q_u->switch_value) {
+               case 0x01:
+                       u_expire=nt_time_to_unix_abs(&q_u->ctr->info.inf1.expire);
+                       u_min_age=nt_time_to_unix_abs(&q_u->ctr->info.inf1.min_passwordage);
+                       
+                       account_policy_set(AP_MIN_PASSWORD_LEN, (uint32)q_u->ctr->info.inf1.min_length_password);
+                       account_policy_set(AP_PASSWORD_HISTORY, (uint32)q_u->ctr->info.inf1.password_history);
+                       account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, (uint32)q_u->ctr->info.inf1.flag);
+                       account_policy_set(AP_MAX_PASSWORD_AGE, (int)u_expire);
+                       account_policy_set(AP_MIN_PASSWORD_AGE, (int)u_min_age);
+                       break;
+               case 0x02:
+                       break;
+               case 0x03:
+                       u_logout=nt_time_to_unix_abs(&q_u->ctr->info.inf3.logout);
+                       account_policy_set(AP_TIME_TO_LOGOUT, (int)u_logout);
+                       break;
+               case 0x05:
+                       break;
+               case 0x06:
+                       break;
+               case 0x07:
+                       break;
+               case 0x0c:
+                       u_lock_duration=nt_time_to_unix_abs(&q_u->ctr->info.inf12.duration);
+                       u_reset_time=nt_time_to_unix_abs(&q_u->ctr->info.inf12.reset_count);
+                       
+                       account_policy_set(AP_LOCK_ACCOUNT_DURATION, (int)u_lock_duration);
+                       account_policy_set(AP_RESET_COUNT_TIME, (int)u_reset_time);
+                       account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, (uint32)q_u->ctr->info.inf12.bad_attempt_lockout);
+                       break;
+               default:
+                       return NT_STATUS_INVALID_INFO_CLASS;
+       }
+
+       init_samr_r_set_domain_info(r_u, NT_STATUS_OK);
+
+       DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
diff --git a/source4/rpc_server/srv_samr_util.c b/source4/rpc_server/srv_samr_util.c
new file mode 100644 (file)
index 0000000..d7ead0d
--- /dev/null
@@ -0,0 +1,437 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAMR Pipe utility functions.
+   
+   Copyright (C) Luke Kenneth Casson Leighton  1996-1998
+   Copyright (C) Gerald (Jerry) Carter         2000-2001
+   Copyright (C) Andrew Bartlett               2001-2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define STRING_CHANGED (old_string && !new_string) ||\
+                   (!old_string && new_string) ||\
+               (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+/*************************************************************
+ Copies a SAM_USER_INFO_21 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id21_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_21 *from)
+{
+       time_t unix_time, stored_time;
+       const char *old_string, *new_string;
+
+       if (from == NULL || to == NULL) 
+               return;
+       if (!nt_time_is_zero(&from->logon_time)) {
+               unix_time=nt_time_to_unix(&from->logon_time);
+               stored_time = pdb_get_logon_time(to);
+               DEBUG(10,("INFO_21 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+       }       
+       if (!nt_time_is_zero(&from->logoff_time)) {
+               unix_time=nt_time_to_unix(&from->logoff_time);
+               stored_time = pdb_get_logoff_time(to);
+               DEBUG(10,("INFO_21 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+       }
+       
+       if (!nt_time_is_zero(&from->kickoff_time)) {
+               unix_time=nt_time_to_unix(&from->kickoff_time);
+               stored_time = pdb_get_kickoff_time(to);
+               DEBUG(10,("INFO_21 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+       }       
+
+       if (!nt_time_is_zero(&from->pass_can_change_time)) {
+               unix_time=nt_time_to_unix(&from->pass_can_change_time);
+               stored_time = pdb_get_pass_can_change_time(to);
+               DEBUG(10,("INFO_21 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+       }
+       if (!nt_time_is_zero(&from->pass_last_set_time)) {
+               unix_time=nt_time_to_unix(&from->pass_last_set_time);
+               stored_time = pdb_get_pass_last_set_time(to);
+               DEBUG(10,("INFO_21 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+       }
+
+       if (!nt_time_is_zero(&from->pass_must_change_time)) {
+               unix_time=nt_time_to_unix(&from->pass_must_change_time);
+               stored_time=pdb_get_pass_must_change_time(to);
+               DEBUG(10,("INFO_21 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+       }
+
+       /* Backend should check this for sainity */
+       if (from->hdr_user_name.buffer) {
+               old_string = pdb_get_username(to);
+               new_string = unistr2_static(&from->uni_user_name);
+               DEBUG(10,("INFO_21 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+               if (STRING_CHANGED)
+                   pdb_set_username(to      , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_full_name.buffer) {
+               old_string = pdb_get_fullname(to);
+               new_string = unistr2_static(&from->uni_full_name);
+               DEBUG(10,("INFO_21 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_fullname(to      , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_home_dir.buffer) {
+               old_string = pdb_get_homedir(to);
+               new_string = unistr2_static(&from->uni_home_dir);
+               DEBUG(10,("INFO_21 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_homedir(to       , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_dir_drive.buffer) {
+               old_string = pdb_get_dir_drive(to);
+               new_string = unistr2_static(&from->uni_dir_drive);
+               DEBUG(10,("INFO_21 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_dir_drive(to     , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_logon_script.buffer) {
+               old_string = pdb_get_logon_script(to);
+               new_string = unistr2_static(&from->uni_logon_script);
+               DEBUG(10,("INFO_21 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_logon_script(to  , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_profile_path.buffer) {
+               old_string = pdb_get_profile_path(to);
+               new_string = unistr2_static(&from->uni_profile_path);
+               DEBUG(10,("INFO_21 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_profile_path(to  , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_acct_desc.buffer) {
+               old_string = pdb_get_acct_desc(to);
+               new_string = unistr2_static(&from->uni_acct_desc);
+               DEBUG(10,("INFO_21 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_acct_desc(to     , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_workstations.buffer) {
+               old_string = pdb_get_workstations(to);
+               new_string = unistr2_static(&from->uni_workstations);
+               DEBUG(10,("INFO_21 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_workstations(to  , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_unknown_str.buffer) {
+               old_string = pdb_get_unknown_str(to);
+               new_string = unistr2_static(&from->uni_unknown_str);
+               DEBUG(10,("INFO_21 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_unknown_str(to   , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_munged_dial.buffer) {
+               old_string = pdb_get_munged_dial(to);
+               new_string = unistr2_static(&from->uni_munged_dial);
+               DEBUG(10,("INFO_21 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_munged_dial(to   , new_string, PDB_CHANGED);
+       }
+       
+       if (from->user_rid != pdb_get_user_rid(to)) {
+               DEBUG(10,("INFO_21 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+               /* we really allow this ??? metze */
+               /* pdb_set_user_sid_from_rid(to, from->user_rid, PDB_CHANGED);*/
+       }
+       
+       if (from->group_rid != pdb_get_group_rid(to)) {
+               DEBUG(10,("INFO_21 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+               pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+       }
+       
+       DEBUG(10,("INFO_21 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+       if (from->acb_info != pdb_get_acct_ctrl(to)) {
+               pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_21 UNKOWN_3: %08X -> %08X\n",pdb_get_unknown_3(to),from->unknown_3));
+       if (from->unknown_3 != pdb_get_unknown_3(to)) {
+               pdb_set_unknown_3(to, from->unknown_3, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_21 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+       if (from->logon_divs != pdb_get_logon_divs(to)) {
+               pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_21 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+       if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+               pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_21 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+/* Fix me: only update if it changes --metze */
+       pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+       DEBUG(10,("INFO_21 UNKOWN_5: %08X -> %08X\n",pdb_get_unknown_5(to),from->unknown_5));
+       if (from->unknown_5 != pdb_get_unknown_5(to)) {
+               pdb_set_unknown_5(to, from->unknown_5, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_21 UNKOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+       if (from->unknown_6 != pdb_get_unknown_6(to)) {
+               pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_21 PADDING1 %02X %02X %02X %02X %02X %02X\n",
+                 from->padding1[0],
+                 from->padding1[1],
+                 from->padding1[2],
+                 from->padding1[3],
+                 from->padding1[4],
+                 from->padding1[5]));
+
+       DEBUG(10,("INFO_21 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+       if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+               pdb_set_pass_must_change_time(to,0, PDB_CHANGED);               
+       }
+
+       DEBUG(10,("INFO_21 PADDING_2: %02X\n",from->padding2));
+
+       DEBUG(10,("INFO_21 PADDING_4: %08X\n",from->padding4));
+}
+
+
+/*************************************************************
+ Copies a SAM_USER_INFO_23 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id23_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_23 *from)
+{
+       time_t unix_time, stored_time;
+       const char *old_string, *new_string;
+
+       if (from == NULL || to == NULL) 
+               return;
+       if (!nt_time_is_zero(&from->logon_time)) {
+               unix_time=nt_time_to_unix(&from->logon_time);
+               stored_time = pdb_get_logon_time(to);
+               DEBUG(10,("INFO_23 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+       }       
+       if (!nt_time_is_zero(&from->logoff_time)) {
+               unix_time=nt_time_to_unix(&from->logoff_time);
+               stored_time = pdb_get_logoff_time(to);
+               DEBUG(10,("INFO_23 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+       }
+       
+       if (!nt_time_is_zero(&from->kickoff_time)) {
+               unix_time=nt_time_to_unix(&from->kickoff_time);
+               stored_time = pdb_get_kickoff_time(to);
+               DEBUG(10,("INFO_23 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+       }       
+
+       if (!nt_time_is_zero(&from->pass_can_change_time)) {
+               unix_time=nt_time_to_unix(&from->pass_can_change_time);
+               stored_time = pdb_get_pass_can_change_time(to);
+               DEBUG(10,("INFO_23 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+       }
+       if (!nt_time_is_zero(&from->pass_last_set_time)) {
+               unix_time=nt_time_to_unix(&from->pass_last_set_time);
+               stored_time = pdb_get_pass_last_set_time(to);
+               DEBUG(10,("INFO_23 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+       }
+
+       if (!nt_time_is_zero(&from->pass_must_change_time)) {
+               unix_time=nt_time_to_unix(&from->pass_must_change_time);
+               stored_time=pdb_get_pass_must_change_time(to);
+               DEBUG(10,("INFO_23 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+               if (stored_time != unix_time) 
+                       pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+       }
+
+       /* Backend should check this for sainity */
+       if (from->hdr_user_name.buffer) {
+               old_string = pdb_get_username(to);
+               new_string = unistr2_static(&from->uni_user_name);
+               DEBUG(10,("INFO_23 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+               if (STRING_CHANGED)
+                   pdb_set_username(to      , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_full_name.buffer) {
+               old_string = pdb_get_fullname(to);
+               new_string = unistr2_static(&from->uni_full_name);
+               DEBUG(10,("INFO_23 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_fullname(to      , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_home_dir.buffer) {
+               old_string = pdb_get_homedir(to);
+               new_string = unistr2_static(&from->uni_home_dir);
+               DEBUG(10,("INFO_23 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_homedir(to       , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_dir_drive.buffer) {
+               old_string = pdb_get_dir_drive(to);
+               new_string = unistr2_static(&from->uni_dir_drive);
+               DEBUG(10,("INFO_23 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_dir_drive(to     , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_logon_script.buffer) {
+               old_string = pdb_get_logon_script(to);
+               new_string = unistr2_static(&from->uni_logon_script);
+               DEBUG(10,("INFO_23 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_logon_script(to  , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_profile_path.buffer) {
+               old_string = pdb_get_profile_path(to);
+               new_string = unistr2_static(&from->uni_profile_path);
+               DEBUG(10,("INFO_23 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_profile_path(to  , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_acct_desc.buffer) {
+               old_string = pdb_get_acct_desc(to);
+               new_string = unistr2_static(&from->uni_acct_desc);
+               DEBUG(10,("INFO_23 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+               if (STRING_CHANGED)
+                       pdb_set_acct_desc(to     , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_workstations.buffer) {
+               old_string = pdb_get_workstations(to);
+               new_string = unistr2_static(&from->uni_workstations);
+               DEBUG(10,("INFO_23 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_workstations(to  , new_string, PDB_CHANGED);
+       }
+
+       if (from->hdr_unknown_str.buffer) {
+               old_string = pdb_get_unknown_str(to);
+               new_string = unistr2_static(&from->uni_unknown_str);
+               DEBUG(10,("INFO_23 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_unknown_str(to   , new_string, PDB_CHANGED);
+       }
+       
+       if (from->hdr_munged_dial.buffer) {
+               old_string = pdb_get_munged_dial(to);
+               new_string = unistr2_static(&from->uni_munged_dial);
+               DEBUG(10,("INFO_23 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string));
+               if (STRING_CHANGED)
+                       pdb_set_munged_dial(to   , new_string, PDB_CHANGED);
+       }
+       
+       if (from->user_rid != pdb_get_user_rid(to)) {
+               DEBUG(10,("INFO_23 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+               /* we really allow this ??? metze */
+               /* pdb_set_user_sid_from_rid(to, from->user_rid, PDB_CHANGED);*/
+       }
+       
+       if (from->group_rid != pdb_get_group_rid(to)) {
+               DEBUG(10,("INFO_23 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+               pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+       }
+       
+       DEBUG(10,("INFO_23 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+       if (from->acb_info != pdb_get_acct_ctrl(to)) {
+               pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_23 UNKOWN_3: %08X -> %08X\n",pdb_get_unknown_3(to),from->unknown_3));
+       if (from->unknown_3 != pdb_get_unknown_3(to)) {
+               pdb_set_unknown_3(to, from->unknown_3, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_23 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+       if (from->logon_divs != pdb_get_logon_divs(to)) {
+               pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_23 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+       if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+               pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+       }
+
+       DEBUG(15,("INFO_23 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+/* Fix me: only update if it changes --metze */
+       pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+       DEBUG(10,("INFO_23 UNKOWN_5: %08X -> %08X\n",pdb_get_unknown_5(to),from->unknown_5));
+       if (from->unknown_5 != pdb_get_unknown_5(to)) {
+               pdb_set_unknown_5(to, from->unknown_5, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_23 UNKOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+       if (from->unknown_6 != pdb_get_unknown_6(to)) {
+               pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+       }
+
+       DEBUG(10,("INFO_23 PADDING1 %02X %02X %02X %02X %02X %02X\n",
+                 from->padding1[0],
+                 from->padding1[1],
+                 from->padding1[2],
+                 from->padding1[3],
+                 from->padding1[4],
+                 from->padding1[5]));
+
+       DEBUG(10,("INFO_23 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+       if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+               pdb_set_pass_must_change_time(to,0, PDB_CHANGED);               
+       }
+
+       DEBUG(10,("INFO_23 PADDING_2: %02X\n",from->padding2));
+
+       DEBUG(10,("INFO_23 PADDING_4: %08X\n",from->padding4));
+}
+
+
diff --git a/source4/rpc_server/srv_spoolss.c b/source4/rpc_server/srv_spoolss.c
new file mode 100755 (executable)
index 0000000..3023922
--- /dev/null
@@ -0,0 +1,1649 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2000,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Gerald Carter                2001-2002,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *  
+ *  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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ * api_spoolss_open_printer_ex (rarely seen - older call)
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer(pipes_struct *p)
+{
+       SPOOL_Q_OPEN_PRINTER q_u;
+       SPOOL_R_OPEN_PRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_open_printer("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_open_printer: unable to unmarshall SPOOL_Q_OPEN_PRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_open_printer( p, &q_u, &r_u);
+       
+       if (!spoolss_io_r_open_printer("",&r_u,rdata,0)){
+               DEBUG(0,("spoolss_io_r_open_printer: unable to marshall SPOOL_R_OPEN_PRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_open_printer_ex
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer_ex(pipes_struct *p)
+{
+       SPOOL_Q_OPEN_PRINTER_EX q_u;
+       SPOOL_R_OPEN_PRINTER_EX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_open_printer_ex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_open_printer_ex: unable to unmarshall SPOOL_Q_OPEN_PRINTER_EX.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_open_printer_ex( p, &q_u, &r_u);
+
+       if (!spoolss_io_r_open_printer_ex("",&r_u,rdata,0)){
+               DEBUG(0,("spoolss_io_r_open_printer_ex: unable to marshall SPOOL_R_OPEN_PRINTER_EX.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdata(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTERDATA q_u;
+       SPOOL_R_GETPRINTERDATA r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* read the stream and fill the struct */
+       if (!spoolss_io_q_getprinterdata("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprinterdata: unable to unmarshall SPOOL_Q_GETPRINTERDATA.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_getprinterdata( p, &q_u, &r_u);
+
+       if (!spoolss_io_r_getprinterdata("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_getprinterdata: unable to marshall SPOOL_R_GETPRINTERDATA.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdata(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTERDATA q_u;
+       SPOOL_R_DELETEPRINTERDATA r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* read the stream and fill the struct */
+       if (!spoolss_io_q_deleteprinterdata("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinterdata: unable to unmarshall SPOOL_Q_DELETEPRINTERDATA.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_deleteprinterdata( p, &q_u, &r_u);
+
+       if (!spoolss_io_r_deleteprinterdata("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinterdata: unable to marshall SPOOL_R_DELETEPRINTERDATA.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_closeprinter(pipes_struct *p)
+{
+       SPOOL_Q_CLOSEPRINTER q_u;
+       SPOOL_R_CLOSEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_closeprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_closeprinter: unable to unmarshall SPOOL_Q_CLOSEPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_closeprinter(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_closeprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_closeprinter: unable to marshall SPOOL_R_CLOSEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_abortprinter(pipes_struct *p)
+{
+       SPOOL_Q_ABORTPRINTER q_u;
+       SPOOL_R_ABORTPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_abortprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_abortprinter: unable to unmarshall SPOOL_Q_ABORTPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_abortprinter(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_abortprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_abortprinter: unable to marshall SPOOL_R_ABORTPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinter(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTER q_u;
+       SPOOL_R_DELETEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_deleteprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinter: unable to unmarshall SPOOL_Q_DELETEPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_deleteprinter(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_deleteprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_deleteprinterdriver
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriver(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTERDRIVER q_u;
+       SPOOL_R_DELETEPRINTERDRIVER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_deleteprinterdriver("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinterdriver: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_deleteprinterdriver(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_deleteprinterdriver("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ ********************************************************************/
+
+static BOOL api_spoolss_rffpcnex(pipes_struct *p)
+{
+       SPOOL_Q_RFFPCNEX q_u;
+       SPOOL_R_RFFPCNEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_rffpcnex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_rffpcnex: unable to unmarshall SPOOL_Q_RFFPCNEX.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_rffpcnex(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_rffpcnex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_rffpcnex: unable to marshall SPOOL_R_RFFPCNEX.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rfnpcnex
+ * ReplyFindNextPrinterChangeNotifyEx
+ * called from the spoolss dispatcher
+
+ * Note - this is the *ONLY* function that breaks the RPC call
+ * symmetry in all the other calls. We need to do this to fix
+ * the massive memory allocation problem with thousands of jobs...
+ * JRA.
+ ********************************************************************/
+
+static BOOL api_spoolss_rfnpcnex(pipes_struct *p)
+{
+       SPOOL_Q_RFNPCNEX q_u;
+       SPOOL_R_RFNPCNEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_rfnpcnex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_rfnpcnex: unable to unmarshall SPOOL_Q_RFNPCNEX.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_rfnpcnex(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_rfnpcnex("", &r_u, rdata, 0)) {
+               SAFE_FREE(r_u.info.data);
+               DEBUG(0,("spoolss_io_r_rfnpcnex: unable to marshall SPOOL_R_RFNPCNEX.\n"));
+               return False;
+       }
+
+       SAFE_FREE(r_u.info.data);
+
+       return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_enumprinters(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTERS q_u;
+       SPOOL_R_ENUMPRINTERS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_enumprinters("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprinters: unable to unmarshall SPOOL_Q_ENUMPRINTERS.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enumprinters( p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumprinters("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprinters: unable to marshall SPOOL_R_ENUMPRINTERS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinter(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTER q_u;
+       SPOOL_R_GETPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_getprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprinter: unable to unmarshall SPOOL_Q_GETPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_getprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_getprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_getprinter: unable to marshall SPOOL_R_GETPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdriver2(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTERDRIVER2 q_u;
+       SPOOL_R_GETPRINTERDRIVER2 r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_getprinterdriver2("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprinterdriver2: unable to unmarshall SPOOL_Q_GETPRINTERDRIVER2.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_getprinterdriver2(p, &q_u, &r_u);
+       
+       if(!spoolss_io_r_getprinterdriver2("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_getprinterdriver2: unable to marshall SPOOL_R_GETPRINTERDRIVER2.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_startpageprinter(pipes_struct *p)
+{
+       SPOOL_Q_STARTPAGEPRINTER q_u;
+       SPOOL_R_STARTPAGEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_startpageprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_startpageprinter: unable to unmarshall SPOOL_Q_STARTPAGEPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_startpageprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_startpageprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_startpageprinter: unable to marshall SPOOL_R_STARTPAGEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_endpageprinter(pipes_struct *p)
+{
+       SPOOL_Q_ENDPAGEPRINTER q_u;
+       SPOOL_R_ENDPAGEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_endpageprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_endpageprinter: unable to unmarshall SPOOL_Q_ENDPAGEPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_endpageprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_endpageprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_endpageprinter: unable to marshall SPOOL_R_ENDPAGEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_startdocprinter(pipes_struct *p)
+{
+       SPOOL_Q_STARTDOCPRINTER q_u;
+       SPOOL_R_STARTDOCPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_startdocprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_startdocprinter: unable to unmarshall SPOOL_Q_STARTDOCPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_startdocprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_startdocprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_startdocprinter: unable to marshall SPOOL_R_STARTDOCPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_enddocprinter(pipes_struct *p)
+{
+       SPOOL_Q_ENDDOCPRINTER q_u;
+       SPOOL_R_ENDDOCPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_enddocprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enddocprinter: unable to unmarshall SPOOL_Q_ENDDOCPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enddocprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_enddocprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_enddocprinter: unable to marshall SPOOL_R_ENDDOCPRINTER.\n"));
+               return False;
+       }
+
+       return True;            
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_writeprinter(pipes_struct *p)
+{
+       SPOOL_Q_WRITEPRINTER q_u;
+       SPOOL_R_WRITEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_writeprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_writeprinter: unable to unmarshall SPOOL_Q_WRITEPRINTER.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_writeprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_writeprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_writeprinter: unable to marshall SPOOL_R_WRITEPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+static BOOL api_spoolss_setprinter(pipes_struct *p)
+{
+       SPOOL_Q_SETPRINTER q_u;
+       SPOOL_R_SETPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_setprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setprinter: unable to unmarshall SPOOL_Q_SETPRINTER.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_setprinter(p, &q_u, &r_u);
+       
+       if(!spoolss_io_r_setprinter("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_setprinter: unable to marshall SPOOL_R_SETPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_fcpn(pipes_struct *p)
+{
+       SPOOL_Q_FCPN q_u;
+       SPOOL_R_FCPN r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_fcpn("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_fcpn: unable to unmarshall SPOOL_Q_FCPN.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_fcpn(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_fcpn("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_fcpn: unable to marshall SPOOL_R_FCPN.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addjob(pipes_struct *p)
+{
+       SPOOL_Q_ADDJOB q_u;
+       SPOOL_R_ADDJOB r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_addjob("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addjob: unable to unmarshall SPOOL_Q_ADDJOB.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_addjob(p, &q_u, &r_u);
+               
+       if(!spoolss_io_r_addjob("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_addjob: unable to marshall SPOOL_R_ADDJOB.\n"));
+               return False;
+       }
+
+       return True;            
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumjobs(pipes_struct *p)
+{
+       SPOOL_Q_ENUMJOBS q_u;
+       SPOOL_R_ENUMJOBS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_enumjobs("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumjobs: unable to unmarshall SPOOL_Q_ENUMJOBS.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enumjobs(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumjobs("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_enumjobs: unable to marshall SPOOL_R_ENUMJOBS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_schedulejob(pipes_struct *p)
+{
+       SPOOL_Q_SCHEDULEJOB q_u;
+       SPOOL_R_SCHEDULEJOB r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_schedulejob("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_schedulejob: unable to unmarshall SPOOL_Q_SCHEDULEJOB.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_schedulejob(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_schedulejob("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_schedulejob: unable to marshall SPOOL_R_SCHEDULEJOB.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setjob(pipes_struct *p)
+{
+       SPOOL_Q_SETJOB q_u;
+       SPOOL_R_SETJOB r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_setjob("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setjob: unable to unmarshall SPOOL_Q_SETJOB.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_setjob(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_setjob("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_setjob: unable to marshall SPOOL_R_SETJOB.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdrivers(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTERDRIVERS q_u;
+       SPOOL_R_ENUMPRINTERDRIVERS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_enumprinterdrivers("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprinterdrivers: unable to unmarshall SPOOL_Q_ENUMPRINTERDRIVERS.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enumprinterdrivers(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumprinterdrivers("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_enumprinterdrivers: unable to marshall SPOOL_R_ENUMPRINTERDRIVERS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getform(pipes_struct *p)
+{
+       SPOOL_Q_GETFORM q_u;
+       SPOOL_R_GETFORM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_getform("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getform: unable to unmarshall SPOOL_Q_GETFORM.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_getform(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_getform("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_getform: unable to marshall SPOOL_R_GETFORM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumforms(pipes_struct *p)
+{
+       SPOOL_Q_ENUMFORMS q_u;
+       SPOOL_R_ENUMFORMS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if (!spoolss_io_q_enumforms("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumforms: unable to unmarshall SPOOL_Q_ENUMFORMS.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enumforms(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumforms("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_enumforms: unable to marshall SPOOL_R_ENUMFORMS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumports(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPORTS q_u;
+       SPOOL_R_ENUMPORTS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_enumports("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumports: unable to unmarshall SPOOL_Q_ENUMPORTS.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_enumports(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumports("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_enumports: unable to marshall SPOOL_R_ENUMPORTS.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterex(pipes_struct *p)
+{
+       SPOOL_Q_ADDPRINTEREX q_u;
+       SPOOL_R_ADDPRINTEREX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_addprinterex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addprinterex: unable to unmarshall SPOOL_Q_ADDPRINTEREX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_addprinterex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_addprinterex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_addprinterex: unable to marshall SPOOL_R_ADDPRINTEREX.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriver(pipes_struct *p)
+{
+       SPOOL_Q_ADDPRINTERDRIVER q_u;
+       SPOOL_R_ADDPRINTERDRIVER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_addprinterdriver("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addprinterdriver: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVER.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_addprinterdriver(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_addprinterdriver("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_addprinterdriver: unable to marshall SPOOL_R_ADDPRINTERDRIVER.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprinterdriverdirectory(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTERDRIVERDIR q_u;
+       SPOOL_R_GETPRINTERDRIVERDIR r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_getprinterdriverdir("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprinterdriverdir: unable to unmarshall SPOOL_Q_GETPRINTERDRIVERDIR.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_getprinterdriverdirectory(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_getprinterdriverdir("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_getprinterdriverdir: unable to marshall SPOOL_R_GETPRINTERDRIVERDIR.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdata(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTERDATA q_u;
+       SPOOL_R_ENUMPRINTERDATA r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_enumprinterdata("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprinterdata: unable to unmarshall SPOOL_Q_ENUMPRINTERDATA.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_enumprinterdata(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_enumprinterdata("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprinterdata: unable to marshall SPOOL_R_ENUMPRINTERDATA.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdata(pipes_struct *p)
+{
+       SPOOL_Q_SETPRINTERDATA q_u;
+       SPOOL_R_SETPRINTERDATA r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_setprinterdata("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_setprinterdata(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_setprinterdata("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_SETPRINTERDATA.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_reset_printer(pipes_struct *p)
+{
+       SPOOL_Q_RESETPRINTER q_u;
+       SPOOL_R_RESETPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       if(!spoolss_io_q_resetprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_resetprinter(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_resetprinter("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_RESETPRINTER.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_addform(pipes_struct *p)
+{
+       SPOOL_Q_ADDFORM q_u;
+       SPOOL_R_ADDFORM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_addform("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addform: unable to unmarshall SPOOL_Q_ADDFORM.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_addform(p, &q_u, &r_u);
+       
+       if(!spoolss_io_r_addform("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_addform: unable to marshall SPOOL_R_ADDFORM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteform(pipes_struct *p)
+{
+       SPOOL_Q_DELETEFORM q_u;
+       SPOOL_R_DELETEFORM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_deleteform("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteform: unable to unmarshall SPOOL_Q_DELETEFORM.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_deleteform(p, &q_u, &r_u);
+       
+       if(!spoolss_io_r_deleteform("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_deleteform: unable to marshall SPOOL_R_DELETEFORM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setform(pipes_struct *p)
+{
+       SPOOL_Q_SETFORM q_u;
+       SPOOL_R_SETFORM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_setform("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setform: unable to unmarshall SPOOL_Q_SETFORM.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_setform(p, &q_u, &r_u);
+                                     
+       if(!spoolss_io_r_setform("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_setform: unable to marshall SPOOL_R_SETFORM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocessors(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTPROCESSORS q_u;
+       SPOOL_R_ENUMPRINTPROCESSORS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_enumprintprocessors("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprintprocessors: unable to unmarshall SPOOL_Q_ENUMPRINTPROCESSORS.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_enumprintprocessors(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_enumprintprocessors("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprintprocessors: unable to marshall SPOOL_R_ENUMPRINTPROCESSORS.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprintprocessor(pipes_struct *p)
+{
+       SPOOL_Q_ADDPRINTPROCESSOR q_u;
+       SPOOL_R_ADDPRINTPROCESSOR r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_addprintprocessor("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addprintprocessor: unable to unmarshall SPOOL_Q_ADDPRINTPROCESSOR.\n"));
+               return False;
+       }
+       
+       /* for now, just indicate success and ignore the add.  We'll
+          automatically set the winprint processor for printer
+          entries later.  Used to debug the LexMark Optra S 1855 PCL
+          driver --jerry */
+       r_u.status = WERR_OK;
+
+       if(!spoolss_io_r_addprintprocessor("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_addprintprocessor: unable to marshall SPOOL_R_ADDPRINTPROCESSOR.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocdatatypes(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTPROCDATATYPES q_u;
+       SPOOL_R_ENUMPRINTPROCDATATYPES r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_enumprintprocdatatypes("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprintprocdatatypes: unable to unmarshall SPOOL_Q_ENUMPRINTPROCDATATYPES.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_enumprintprocdatatypes(p, &q_u, &r_u);
+
+       if(!spoolss_io_r_enumprintprocdatatypes("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprintprocdatatypes: unable to marshall SPOOL_R_ENUMPRINTPROCDATATYPES.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintmonitors(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTMONITORS q_u;
+       SPOOL_R_ENUMPRINTMONITORS r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if (!spoolss_io_q_enumprintmonitors("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprintmonitors: unable to unmarshall SPOOL_Q_ENUMPRINTMONITORS.\n"));
+               return False;
+       }
+               
+       r_u.status = _spoolss_enumprintmonitors(p, &q_u, &r_u);
+
+       if (!spoolss_io_r_enumprintmonitors("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprintmonitors: unable to marshall SPOOL_R_ENUMPRINTMONITORS.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getjob(pipes_struct *p)
+{
+       SPOOL_Q_GETJOB q_u;
+       SPOOL_R_GETJOB r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       if(!spoolss_io_q_getjob("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getjob: unable to unmarshall SPOOL_Q_GETJOB.\n"));
+               return False;
+       }
+
+       r_u.status = _spoolss_getjob(p, &q_u, &r_u);
+       
+       if(!spoolss_io_r_getjob("",&r_u,rdata,0)) {
+               DEBUG(0,("spoolss_io_r_getjob: unable to marshall SPOOL_R_GETJOB.\n"));
+               return False;
+       }
+               
+       return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdataex
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdataex(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTERDATAEX q_u;
+       SPOOL_R_GETPRINTERDATAEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* read the stream and fill the struct */
+       if (!spoolss_io_q_getprinterdataex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprinterdataex: unable to unmarshall SPOOL_Q_GETPRINTERDATAEX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_getprinterdataex( p, &q_u, &r_u);
+
+       if (!spoolss_io_r_getprinterdataex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_getprinterdataex: unable to marshall SPOOL_R_GETPRINTERDATAEX.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdataex(pipes_struct *p)
+{
+       SPOOL_Q_SETPRINTERDATAEX q_u;
+       SPOOL_R_SETPRINTERDATAEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_setprinterdataex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setprinterdataex: unable to unmarshall SPOOL_Q_SETPRINTERDATAEX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_setprinterdataex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_setprinterdataex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_setprinterdataex: unable to marshall SPOOL_R_SETPRINTERDATAEX.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterkey(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTERKEY q_u;
+       SPOOL_R_ENUMPRINTERKEY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_enumprinterkey("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_setprinterkey: unable to unmarshall SPOOL_Q_ENUMPRINTERKEY.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_enumprinterkey(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_enumprinterkey("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprinterkey: unable to marshall SPOOL_R_ENUMPRINTERKEY.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdataex(pipes_struct *p)
+{
+       SPOOL_Q_ENUMPRINTERDATAEX q_u;
+       SPOOL_R_ENUMPRINTERDATAEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_enumprinterdataex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_enumprinterdataex: unable to unmarshall SPOOL_Q_ENUMPRINTERDATAEX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_enumprinterdataex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_enumprinterdataex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_enumprinterdataex: unable to marshall SPOOL_R_ENUMPRINTERDATAEX.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprintprocessordirectory(pipes_struct *p)
+{
+       SPOOL_Q_GETPRINTPROCESSORDIRECTORY q_u;
+       SPOOL_R_GETPRINTPROCESSORDIRECTORY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_getprintprocessordirectory("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_getprintprocessordirectory: unable to unmarshall SPOOL_Q_GETPRINTPROCESSORDIRECTORY.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_getprintprocessordirectory(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_getprintprocessordirectory("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_getprintprocessordirectory: unable to marshall SPOOL_R_GETPRINTPROCESSORDIRECTORY.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdataex(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTERDATAEX q_u;
+       SPOOL_R_DELETEPRINTERDATAEX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_deleteprinterdataex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinterdataex: unable to unmarshall SPOOL_Q_DELETEPRINTERDATAEX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_deleteprinterdataex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_deleteprinterdataex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinterdataex: unable to marshall SPOOL_R_DELETEPRINTERDATAEX.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterkey(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTERKEY q_u;
+       SPOOL_R_DELETEPRINTERKEY r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_deleteprinterkey("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinterkey: unable to unmarshall SPOOL_Q_DELETEPRINTERKEY.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_deleteprinterkey(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_deleteprinterkey("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinterkey: unable to marshall SPOOL_R_DELETEPRINTERKEY.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriverex(pipes_struct *p)
+{
+       SPOOL_Q_ADDPRINTERDRIVEREX q_u;
+       SPOOL_R_ADDPRINTERDRIVEREX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_addprinterdriverex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_addprinterdriverex: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVEREX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_addprinterdriverex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_addprinterdriverex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_addprinterdriverex: unable to marshall SPOOL_R_ADDPRINTERDRIVEREX.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriverex(pipes_struct *p)
+{
+       SPOOL_Q_DELETEPRINTERDRIVEREX q_u;
+       SPOOL_R_DELETEPRINTERDRIVEREX r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_deleteprinterdriverex("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_deleteprinterdriverex: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVEREX.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_deleteprinterdriverex(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_deleteprinterdriverex("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_deleteprinterdriverex: unable to marshall SPOOL_R_DELETEPRINTERDRIVEREX.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+#if 0
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replyopenprinter(pipes_struct *p)
+{
+       SPOOL_Q_REPLYOPENPRINTER q_u;
+       SPOOL_R_REPLYOPENPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_replyopenprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_replyopenprinter: unable to unmarshall SPOOL_Q_REPLYOPENPRINTER.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_replyopenprinter(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_replyopenprinter("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_replyopenprinter: unable to marshall SPOOL_R_REPLYOPENPRINTER.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replycloseprinter(pipes_struct *p)
+{
+       SPOOL_Q_REPLYCLOSEPRINTER q_u;
+       SPOOL_R_REPLYCLOSEPRINTER r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+       
+       if(!spoolss_io_q_replycloseprinter("", &q_u, data, 0)) {
+               DEBUG(0,("spoolss_io_q_replycloseprinter: unable to unmarshall SPOOL_Q_REPLYCLOSEPRINTER.\n"));
+               return False;
+       }
+       
+       r_u.status = _spoolss_replycloseprinter(p, &q_u, &r_u);
+                               
+       if(!spoolss_io_r_replycloseprinter("", &r_u, rdata, 0)) {
+               DEBUG(0,("spoolss_io_r_replycloseprinter: unable to marshall SPOOL_R_REPLYCLOSEPRINTER.\n"));
+               return False;
+       }
+       
+       return True;
+}
+
+#endif
+
+/*******************************************************************
+\pipe\spoolss commands
+********************************************************************/
+
+#ifdef RPC_SPOOLSS_DYNAMIC
+int init_module(void)
+#else
+int rpc_spoolss_init(void)
+#endif
+{
+  struct api_struct api_spoolss_cmds[] = 
+    {
+ {"SPOOLSS_OPENPRINTER",               SPOOLSS_OPENPRINTER,               api_spoolss_open_printer              },
+ {"SPOOLSS_OPENPRINTEREX",             SPOOLSS_OPENPRINTEREX,             api_spoolss_open_printer_ex           },
+ {"SPOOLSS_GETPRINTERDATA",            SPOOLSS_GETPRINTERDATA,            api_spoolss_getprinterdata            },
+ {"SPOOLSS_CLOSEPRINTER",              SPOOLSS_CLOSEPRINTER,              api_spoolss_closeprinter              },
+ {"SPOOLSS_DELETEPRINTER",             SPOOLSS_DELETEPRINTER,             api_spoolss_deleteprinter             },
+ {"SPOOLSS_ABORTPRINTER",              SPOOLSS_ABORTPRINTER,              api_spoolss_abortprinter              },
+ {"SPOOLSS_RFFPCNEX",                  SPOOLSS_RFFPCNEX,                  api_spoolss_rffpcnex                  },
+ {"SPOOLSS_RFNPCNEX",                  SPOOLSS_RFNPCNEX,                  api_spoolss_rfnpcnex                  },
+ {"SPOOLSS_ENUMPRINTERS",              SPOOLSS_ENUMPRINTERS,              api_spoolss_enumprinters              },
+ {"SPOOLSS_GETPRINTER",                SPOOLSS_GETPRINTER,                api_spoolss_getprinter                },
+ {"SPOOLSS_GETPRINTERDRIVER2",         SPOOLSS_GETPRINTERDRIVER2,         api_spoolss_getprinterdriver2         }, 
+ {"SPOOLSS_STARTPAGEPRINTER",          SPOOLSS_STARTPAGEPRINTER,          api_spoolss_startpageprinter          },
+ {"SPOOLSS_ENDPAGEPRINTER",            SPOOLSS_ENDPAGEPRINTER,            api_spoolss_endpageprinter            }, 
+ {"SPOOLSS_STARTDOCPRINTER",           SPOOLSS_STARTDOCPRINTER,           api_spoolss_startdocprinter           },
+ {"SPOOLSS_ENDDOCPRINTER",             SPOOLSS_ENDDOCPRINTER,             api_spoolss_enddocprinter             },
+ {"SPOOLSS_WRITEPRINTER",              SPOOLSS_WRITEPRINTER,              api_spoolss_writeprinter              },
+ {"SPOOLSS_SETPRINTER",                SPOOLSS_SETPRINTER,                api_spoolss_setprinter                },
+ {"SPOOLSS_FCPN",                      SPOOLSS_FCPN,                      api_spoolss_fcpn                     },
+ {"SPOOLSS_ADDJOB",                    SPOOLSS_ADDJOB,                    api_spoolss_addjob                    },
+ {"SPOOLSS_ENUMJOBS",                  SPOOLSS_ENUMJOBS,                  api_spoolss_enumjobs                  },
+ {"SPOOLSS_SCHEDULEJOB",               SPOOLSS_SCHEDULEJOB,               api_spoolss_schedulejob               },
+ {"SPOOLSS_SETJOB",                    SPOOLSS_SETJOB,                    api_spoolss_setjob                    },
+ {"SPOOLSS_ENUMFORMS",                 SPOOLSS_ENUMFORMS,                 api_spoolss_enumforms                 },
+ {"SPOOLSS_ENUMPORTS",                 SPOOLSS_ENUMPORTS,                 api_spoolss_enumports                 },
+ {"SPOOLSS_ENUMPRINTERDRIVERS",        SPOOLSS_ENUMPRINTERDRIVERS,        api_spoolss_enumprinterdrivers        },
+ {"SPOOLSS_ADDPRINTEREX",              SPOOLSS_ADDPRINTEREX,              api_spoolss_addprinterex              },
+ {"SPOOLSS_ADDPRINTERDRIVER",          SPOOLSS_ADDPRINTERDRIVER,          api_spoolss_addprinterdriver          },
+ {"SPOOLSS_DELETEPRINTERDRIVER",       SPOOLSS_DELETEPRINTERDRIVER,       api_spoolss_deleteprinterdriver       },
+ {"SPOOLSS_GETPRINTERDRIVERDIRECTORY", SPOOLSS_GETPRINTERDRIVERDIRECTORY, api_spoolss_getprinterdriverdirectory },
+ {"SPOOLSS_ENUMPRINTERDATA",           SPOOLSS_ENUMPRINTERDATA,           api_spoolss_enumprinterdata           },
+ {"SPOOLSS_SETPRINTERDATA",            SPOOLSS_SETPRINTERDATA,            api_spoolss_setprinterdata            },
+ {"SPOOLSS_RESETPRINTER",              SPOOLSS_RESETPRINTER,              api_spoolss_reset_printer             },
+ {"SPOOLSS_DELETEPRINTERDATA",         SPOOLSS_DELETEPRINTERDATA,         api_spoolss_deleteprinterdata         },
+ {"SPOOLSS_ADDFORM",                   SPOOLSS_ADDFORM,                   api_spoolss_addform                   },
+ {"SPOOLSS_DELETEFORM",                SPOOLSS_DELETEFORM,                api_spoolss_deleteform                },
+ {"SPOOLSS_GETFORM",                   SPOOLSS_GETFORM,                   api_spoolss_getform                   },
+ {"SPOOLSS_SETFORM",                   SPOOLSS_SETFORM,                   api_spoolss_setform                   },
+ {"SPOOLSS_ADDPRINTPROCESSOR",         SPOOLSS_ADDPRINTPROCESSOR,         api_spoolss_addprintprocessor         },
+ {"SPOOLSS_ENUMPRINTPROCESSORS",       SPOOLSS_ENUMPRINTPROCESSORS,       api_spoolss_enumprintprocessors       },
+ {"SPOOLSS_ENUMMONITORS",              SPOOLSS_ENUMMONITORS,              api_spoolss_enumprintmonitors         },
+ {"SPOOLSS_GETJOB",                    SPOOLSS_GETJOB,                    api_spoolss_getjob                    },
+ {"SPOOLSS_ENUMPRINTPROCDATATYPES",    SPOOLSS_ENUMPRINTPROCDATATYPES,    api_spoolss_enumprintprocdatatypes    },
+ {"SPOOLSS_GETPRINTERDATAEX",          SPOOLSS_GETPRINTERDATAEX,          api_spoolss_getprinterdataex          },
+ {"SPOOLSS_SETPRINTERDATAEX",          SPOOLSS_SETPRINTERDATAEX,          api_spoolss_setprinterdataex          },
+ {"SPOOLSS_DELETEPRINTERDATAEX",       SPOOLSS_DELETEPRINTERDATAEX,       api_spoolss_deleteprinterdataex       },
+ {"SPOOLSS_ENUMPRINTERDATAEX",         SPOOLSS_ENUMPRINTERDATAEX,         api_spoolss_enumprinterdataex         },
+ {"SPOOLSS_ENUMPRINTERKEY",            SPOOLSS_ENUMPRINTERKEY,            api_spoolss_enumprinterkey            },
+ {"SPOOLSS_DELETEPRINTERKEY",          SPOOLSS_DELETEPRINTERKEY,          api_spoolss_deleteprinterkey          },
+ {"SPOOLSS_GETPRINTPROCESSORDIRECTORY",SPOOLSS_GETPRINTPROCESSORDIRECTORY,api_spoolss_getprintprocessordirectory},
+ {"SPOOLSS_ADDPRINTERDRIVEREX",        SPOOLSS_ADDPRINTERDRIVEREX,        api_spoolss_addprinterdriverex        },
+ {"SPOOLSS_DELETEPRINTERDRIVEREX",     SPOOLSS_DELETEPRINTERDRIVEREX,     api_spoolss_deleteprinterdriverex     },
+#if 0
+ {"SPOOLSS_REPLYOPENPRINTER",          SPOOLSS_REPLYOPENPRINTER,          api_spoolss_replyopenprinter          },
+ {"SPOOLSS_REPLYCLOSEPRINTER",         SPOOLSS_REPLYCLOSEPRINTER,         api_spoolss_replycloseprinter         }
+#endif
+    };
+  return rpc_pipe_register_commands("spoolss", "spoolss", api_spoolss_cmds,
+                                   sizeof(api_spoolss_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_spoolss_nt.c b/source4/rpc_server/srv_spoolss_nt.c
new file mode 100644 (file)
index 0000000..0c29962
--- /dev/null
@@ -0,0 +1,9079 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2000,
+ *  Copyright (C) Jeremy Allison               2001-2002,
+ *  Copyright (C) Gerald Carter                       2000-2003,
+ *  Copyright (C) Tim Potter                   2001-2002.
+ *
+ *  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.
+ */
+
+/* Since the SPOOLSS rpc routines are basically DOS 16-bit calls wrapped
+   up, all the errors returned are DOS errors, not NT status codes. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef MAX_OPEN_PRINTER_EXS
+#define MAX_OPEN_PRINTER_EXS 50
+#endif
+
+#define MAGIC_DISPLAY_FREQUENCY 0xfade2bad
+#define PHANTOM_DEVMODE_KEY "_p_f_a_n_t_0_m_"
+
+
+/* Table to map the driver version */
+/* to OS */
+static const char * drv_ver_to_os[] = {
+       "WIN9X",   /* driver version/cversion 0 */
+       "",        /* unused ? */
+       "WINNT",   /* driver version/cversion 2 */
+       "WIN2K",   /* driver version/cversion 3 */
+};
+
+struct table_node {
+       const char    *long_archi;
+       const char    *short_archi;
+       int     version;
+};
+
+static Printer_entry *printers_list;
+
+typedef struct _counter_printer_0 {
+       ubi_dlNode Next;
+       ubi_dlNode Prev;
+       
+       int snum;
+       uint32 counter;
+} counter_printer_0;
+
+static ubi_dlList counter_list;
+
+static struct cli_state notify_cli; /* print notify back-channel */
+static uint32 smb_connections=0;
+
+
+/* in printing/nt_printing.c */
+
+extern STANDARD_MAPPING printer_std_mapping, printserver_std_mapping;
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+/* translate between internal status numbers and NT status numbers */
+static int nt_printj_status(int v)
+{
+       switch (v) {
+       case LPQ_QUEUED:
+               return 0;
+       case LPQ_PAUSED:
+               return JOB_STATUS_PAUSED;
+       case LPQ_SPOOLING:
+               return JOB_STATUS_SPOOLING;
+       case LPQ_PRINTING:
+               return JOB_STATUS_PRINTING;
+       case LPQ_ERROR:
+               return JOB_STATUS_ERROR;
+       case LPQ_DELETING:
+               return JOB_STATUS_DELETING;
+       case LPQ_OFFLINE:
+               return JOB_STATUS_OFFLINE;
+       case LPQ_PAPEROUT:
+               return JOB_STATUS_PAPEROUT;
+       case LPQ_PRINTED:
+               return JOB_STATUS_PRINTED;
+       case LPQ_DELETED:
+               return JOB_STATUS_DELETED;
+       case LPQ_BLOCKED:
+               return JOB_STATUS_BLOCKED;
+       case LPQ_USER_INTERVENTION:
+               return JOB_STATUS_USER_INTERVENTION;
+       }
+       return 0;
+}
+
+static int nt_printq_status(int v)
+{
+       switch (v) {
+       case LPQ_PAUSED:
+               return PRINTER_STATUS_PAUSED;
+       case LPQ_QUEUED:
+       case LPQ_SPOOLING:
+       case LPQ_PRINTING:
+               return 0;
+       }
+       return 0;
+}
+
+/****************************************************************************
+ Functions to handle SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static void free_spool_notify_option(SPOOL_NOTIFY_OPTION **pp)
+{
+       if (*pp == NULL)
+               return;
+
+       SAFE_FREE((*pp)->ctr.type);
+       SAFE_FREE(*pp);
+}
+
+/***************************************************************************
+ Disconnect from the client
+****************************************************************************/
+
+static void srv_spoolss_replycloseprinter(int snum, POLICY_HND *handle)
+{
+       WERROR result;
+
+       /* 
+        * Tell the specific printing tdb we no longer want messages for this printer
+        * by deregistering our PID.
+        */
+
+       if (!print_notify_deregister_pid(snum))
+               DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", lp_const_servicename(snum) ));
+
+       /* weird if the test succeds !!! */
+       if (smb_connections==0) {
+               DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n"));
+               return;
+       }
+
+       result = cli_spoolss_reply_close_printer(&notify_cli, notify_cli.mem_ctx, handle);
+       
+       if (!W_ERROR_IS_OK(result))
+               DEBUG(0,("srv_spoolss_replycloseprinter: reply_close_printer failed [%s].\n",
+                       dos_errstr(result)));
+
+       /* if it's the last connection, deconnect the IPC$ share */
+       if (smb_connections==1) {
+               cli_nt_session_close(&notify_cli);
+               cli_ulogoff(&notify_cli);
+               cli_shutdown(&notify_cli);
+               message_deregister(MSG_PRINTER_NOTIFY2);
+
+               /* Tell the connections db we're no longer interested in
+                * printer notify messages. */
+
+               register_message_flags( False, FLAG_MSG_PRINTING );
+       }
+
+       smb_connections--;
+}
+
+/****************************************************************************
+ Functions to free a printer entry datastruct.
+****************************************************************************/
+
+static void free_printer_entry(void *ptr)
+{
+       Printer_entry *Printer = (Printer_entry *)ptr;
+
+       if (Printer->notify.client_connected==True) {
+               int snum = -1;
+
+               if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+                       snum = -1;
+                       srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+               } else if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) {
+                       snum = print_queue_snum(Printer->dev.handlename);
+                       if (snum != -1)
+                               srv_spoolss_replycloseprinter(snum,
+                                               &Printer->notify.client_hnd);
+               }
+       }
+
+       Printer->notify.flags=0;
+       Printer->notify.options=0;
+       Printer->notify.localmachine[0]='\0';
+       Printer->notify.printerlocal=0;
+       free_spool_notify_option(&Printer->notify.option);
+       Printer->notify.option=NULL;
+       Printer->notify.client_connected=False;
+       
+       free_nt_devicemode( &Printer->nt_devmode );
+       free_a_printer( &Printer->printer_info, 2 );
+       
+       talloc_destroy( Printer->ctx );
+
+       /* Remove from the internal list. */
+       DLIST_REMOVE(printers_list, Printer);
+
+       SAFE_FREE(Printer);
+}
+
+/****************************************************************************
+ Functions to duplicate a SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static SPOOL_NOTIFY_OPTION *dup_spool_notify_option(SPOOL_NOTIFY_OPTION *sp)
+{
+       SPOOL_NOTIFY_OPTION *new_sp = NULL;
+
+       if (!sp)
+               return NULL;
+
+       new_sp = (SPOOL_NOTIFY_OPTION *)malloc(sizeof(SPOOL_NOTIFY_OPTION));
+       if (!new_sp)
+               return NULL;
+
+       *new_sp = *sp;
+
+       if (sp->ctr.count) {
+               new_sp->ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)memdup(sp->ctr.type, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * sp->ctr.count);
+
+               if (!new_sp->ctr.type) {
+                       SAFE_FREE(new_sp);
+                       return NULL;
+               }
+       }
+
+       return new_sp;
+}
+
+/****************************************************************************
+  find printer index by handle
+****************************************************************************/
+
+static Printer_entry *find_printer_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+       Printer_entry *find_printer = NULL;
+
+       if(!find_policy_by_hnd(p,hnd,(void **)&find_printer)) {
+               DEBUG(2,("find_printer_index_by_hnd: Printer handle not found: "));
+               return NULL;
+       }
+
+       return find_printer;
+}
+
+/****************************************************************************
+  find printer index by handle
+****************************************************************************/
+
+void invalidate_printer_hnd_cache( char *printername )
+{
+       Printer_entry *p;
+       
+       DEBUG(10,("invalidate_printer_hnd_cache: printer [%s]\n", printername));
+
+       for ( p=printers_list; p; p=p->next )
+       {
+               if ( p->printer_type==PRINTER_HANDLE_IS_PRINTER 
+                       && StrCaseCmp(p->dev.handlename, printername)==0)
+               {
+                       DEBUG(10,("invalidating printer_info cache for handl:\n"));
+                       free_a_printer( &p->printer_info, 2 );
+                       p->printer_info = NULL;
+               }
+       }
+
+       return;
+}
+/****************************************************************************
+ Close printer index by handle.
+****************************************************************************/
+
+static BOOL close_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+       Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+       if (!Printer) {
+               DEBUG(2,("close_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+               return False;
+       }
+
+       close_policy_hnd(p, hnd);
+
+       return True;
+}      
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+       Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+       if (!Printer) {
+               DEBUG(2,("delete_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+               return WERR_BADFID;
+       }
+
+       /* 
+        * It turns out that Windows allows delete printer on a handle
+        * opened by an admin user, then used on a pipe handle created
+        * by an anonymous user..... but they're working on security.... riiight !
+        * JRA.
+        */
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+               DEBUG(3, ("delete_printer_handle: denied by handle\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+#if 0
+       /* Check calling user has permission to delete printer.  Note that
+          since we set the snum parameter to -1 only administrators can
+          delete the printer.  This stops people with the Full Control
+          permission from deleting the printer. */
+
+       if (!print_access_check(NULL, -1, PRINTER_ACCESS_ADMINISTER)) {
+               DEBUG(3, ("printer delete denied by security descriptor\n"));
+               return WERR_ACCESS_DENIED;
+       }
+#endif
+
+       if (del_a_printer(Printer->dev.handlename) != 0) {
+               DEBUG(3,("Error deleting printer %s\n", Printer->dev.handlename));
+               return WERR_BADFID;
+       }
+
+       if (*lp_deleteprinter_cmd()) {
+
+               char *cmd = lp_deleteprinter_cmd();
+               pstring command;
+               int ret;
+               int i;
+
+               /* Printer->dev.handlename equals portname equals sharename */
+               slprintf(command, sizeof(command)-1, "%s \"%s\"", cmd,
+                                       Printer->dev.handlename);
+
+               DEBUG(10,("Running [%s]\n", command));
+               ret = smbrun(command, NULL);
+               if (ret != 0) {
+                       return WERR_BADFID; /* What to return here? */
+               }
+               DEBUGADD(10,("returned [%d]\n", ret));
+
+               /* Send SIGHUP to process group... is there a better way? */
+               kill(0, SIGHUP);
+
+               /* go ahead and re-read the services immediately */
+               reload_services( False );
+
+               if ( ( i = lp_servicenumber( Printer->dev.handlename ) ) < 0 )
+                       return WERR_ACCESS_DENIED;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Return the snum of a printer corresponding to an handle.
+****************************************************************************/
+
+static BOOL get_printer_snum(pipes_struct *p, POLICY_HND *hnd, int *number)
+{
+       Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+               
+       if (!Printer) {
+               DEBUG(2,("get_printer_snum: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+               return False;
+       }
+       
+       switch (Printer->printer_type) {
+       case PRINTER_HANDLE_IS_PRINTER:         
+               DEBUG(4,("short name:%s\n", Printer->dev.handlename));                  
+               *number = print_queue_snum(Printer->dev.handlename);
+               return (*number != -1);
+       case PRINTER_HANDLE_IS_PRINTSERVER:
+               return False;
+       default:
+               return False;
+       }
+}
+
+/****************************************************************************
+ Set printer handle type.
+ Check if it's \\server or \\server\printer
+****************************************************************************/
+
+static BOOL set_printer_hnd_printertype(Printer_entry *Printer, char *handlename)
+{
+       DEBUG(3,("Setting printer type=%s\n", handlename));
+
+       if ( strlen(handlename) < 3 ) {
+               DEBUGADD(4,("A print server must have at least 1 char ! %s\n", handlename));
+               return False;
+       }
+
+       /* it's a print server */
+       if (*handlename=='\\' && *(handlename+1)=='\\' && !strchr_m(handlename+2, '\\')) {
+               DEBUGADD(4,("Printer is a print server\n"));
+               Printer->printer_type = PRINTER_HANDLE_IS_PRINTSERVER;          
+       }
+       /* it's a printer */
+       else {
+               DEBUGADD(4,("Printer is a printer\n"));
+               Printer->printer_type = PRINTER_HANDLE_IS_PRINTER;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+ Set printer handle name.
+****************************************************************************/
+
+static BOOL set_printer_hnd_name(Printer_entry *Printer, char *handlename)
+{
+       int snum;
+       int n_services=lp_numservices();
+       char *aprinter;
+       fstring sname;
+       BOOL found=False;
+       
+       DEBUG(4,("Setting printer name=%s (len=%d)\n", handlename, strlen(handlename)));
+
+       if (Printer->printer_type==PRINTER_HANDLE_IS_PRINTSERVER) {
+               ZERO_STRUCT(Printer->dev.printerservername);
+               strncpy(Printer->dev.printerservername, handlename, strlen(handlename));
+               return True;
+       }
+
+       if (Printer->printer_type!=PRINTER_HANDLE_IS_PRINTER)
+               return False;
+       
+       if (*handlename=='\\') {
+               aprinter=strchr_m(handlename+2, '\\');
+               aprinter++;
+       }
+       else {
+               aprinter=handlename;
+       }
+
+       DEBUGADD(5,("searching for [%s] (len=%d)\n", aprinter, strlen(aprinter)));
+
+       /*
+        * The original code allowed smbd to store a printer name that
+        * was different from the share name.  This is not possible 
+        * anymore, so I've simplified this loop greatly.  Here
+        * we are just verifying that the printer name is a valid
+        * printer service defined in smb.conf
+        *                          --jerry [Fri Feb 15 11:17:46 CST 2002]
+        */
+
+       for (snum=0; snum<n_services; snum++) {
+
+               if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+                       continue;
+               
+               fstrcpy(sname, lp_servicename(snum));
+
+               DEBUGADD(5,("share:%s\n",sname));
+               
+               if (! StrCaseCmp(sname, aprinter)) {
+                       found = True;
+                       break;
+               }
+
+       }
+
+               
+       if (!found) {
+               DEBUGADD(4,("Printer not found\n"));
+               return False;
+       }
+       
+       DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname));
+
+       ZERO_STRUCT(Printer->dev.handlename);
+       fstrcpy(Printer->dev.handlename, sname);
+
+       return True;
+}
+
+/****************************************************************************
+ Find first available printer slot. creates a printer handle for you.
+ ****************************************************************************/
+
+static BOOL open_printer_hnd(pipes_struct *p, POLICY_HND *hnd, char *name, uint32 access_granted)
+{
+       Printer_entry *new_printer;
+
+       DEBUG(10,("open_printer_hnd: name [%s]\n", name));
+
+       if((new_printer=(Printer_entry *)malloc(sizeof(Printer_entry))) == NULL)
+               return False;
+
+       ZERO_STRUCTP(new_printer);
+       
+       if ( !(new_printer->ctx = talloc_init("Printer Entry [0x%x]", (uint32)hnd)) ) {
+               DEBUG(0,("open_printer_hnd: talloc_init() failed!\n"));
+               return False;
+       }
+       
+       new_printer->notify.option=NULL;
+                               
+       /* Add to the internal list. */
+       DLIST_ADD(printers_list, new_printer);
+
+       if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) {
+               SAFE_FREE(new_printer);
+               return False;
+       }
+
+       if (!set_printer_hnd_printertype(new_printer, name)) {
+               close_printer_handle(p, hnd);
+               return False;
+       }
+       
+       if (!set_printer_hnd_name(new_printer, name)) {
+               close_printer_handle(p, hnd);
+               return False;
+       }
+
+       new_printer->access_granted = access_granted;
+
+       DEBUG(5, ("%d printer handles active\n", (int)p->pipe_handles->count ));
+
+       return True;
+}
+
+/****************************************************************************
+ Allocate more memory for a BUFFER.
+****************************************************************************/
+
+static BOOL alloc_buffer_size(NEW_BUFFER *buffer, uint32 buffer_size)
+{
+       prs_struct *ps;
+       uint32 extra_space;
+       uint32 old_offset;
+       
+       ps= &buffer->prs;
+
+       /* damn, I'm doing the reverse operation of prs_grow() :) */
+       if (buffer_size < prs_data_size(ps))
+               extra_space=0;
+       else    
+               extra_space = buffer_size - prs_data_size(ps);
+
+       /*
+        * save the offset and move to the end of the buffer
+        * prs_grow() checks the extra_space against the offset
+        */
+       old_offset=prs_offset(ps);      
+       prs_set_offset(ps, prs_data_size(ps));
+       
+       if (!prs_grow(ps, extra_space))
+               return False;
+
+       prs_set_offset(ps, old_offset);
+
+       buffer->string_at_end=prs_data_size(ps);
+
+       return True;
+}
+
+/***************************************************************************
+ check to see if the client motify handle is monitoring the notification
+ given by (notify_type, notify_field).
+ **************************************************************************/
+
+static BOOL is_monitoring_event_flags(uint32 flags, uint16 notify_type,
+                                     uint16 notify_field)
+{
+       return True;
+}
+
+static BOOL is_monitoring_event(Printer_entry *p, uint16 notify_type,
+                               uint16 notify_field)
+{
+       SPOOL_NOTIFY_OPTION *option = p->notify.option;
+       uint32 i, j;
+
+       /* 
+        * Flags should always be zero when the change notify
+        * is registered by the client's spooler.  A user Win32 app
+        * might use the flags though instead of the NOTIFY_OPTION_INFO 
+        * --jerry
+        */
+        
+       if (p->notify.flags)
+               return is_monitoring_event_flags(
+                       p->notify.flags, notify_type, notify_field);
+
+       for (i = 0; i < option->count; i++) {
+               
+               /* Check match for notify_type */
+               
+               if (option->ctr.type[i].type != notify_type)
+                       continue;
+
+               /* Check match for field */
+               
+               for (j = 0; j < option->ctr.type[i].count; j++) {
+                       if (option->ctr.type[i].fields[j] == notify_field) {
+                               return True;
+                       }
+               }
+       }
+       
+       DEBUG(10, ("%s is not monitoring 0x%02x/0x%02x\n",
+                  (p->printer_type == PRINTER_HANDLE_IS_PRINTER) ?
+                  p->dev.handlename : p->dev.printerservername,
+                  notify_type, notify_field));
+       
+       return False;
+}
+
+/* Convert a notification message to a SPOOL_NOTIFY_INFO_DATA struct */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+                            SPOOL_NOTIFY_INFO_DATA *data,
+                            TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = msg->notify.value[0];
+       data->notify_data.value[1] = 0;
+}
+
+static void notify_string(struct spoolss_notify_msg *msg,
+                         SPOOL_NOTIFY_INFO_DATA *data,
+                         TALLOC_CTX *mem_ctx)
+{
+       UNISTR2 unistr;
+       
+       /* The length of the message includes the trailing \0 */
+
+       init_unistr2(&unistr, msg->notify.data, msg->len);
+
+       data->notify_data.data.length = msg->len * 2;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, msg->len * 2);
+
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, unistr.buffer, msg->len * 2);
+}
+
+static void notify_system_time(struct spoolss_notify_msg *msg,
+                              SPOOL_NOTIFY_INFO_DATA *data,
+                              TALLOC_CTX *mem_ctx)
+{
+       SYSTEMTIME systime;
+       prs_struct ps;
+
+       if (msg->len != sizeof(time_t)) {
+               DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+                         msg->len));
+               return;
+       }
+
+       if (!prs_init(&ps, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL)) {
+               DEBUG(5, ("notify_system_time: prs_init() failed\n"));
+               return;
+       }
+
+       if (!make_systemtime(&systime, gmtime((time_t *)msg->notify.data))) {
+               DEBUG(5, ("notify_system_time: unable to make systemtime\n"));
+               return;
+       }
+
+       if (!spoolss_io_system_time("", &ps, 0, &systime))
+               return;
+
+       data->notify_data.data.length = prs_offset(&ps);
+       data->notify_data.data.string = talloc(mem_ctx, prs_offset(&ps));
+
+       prs_copy_all_data_out((char *)data->notify_data.data.string, &ps);
+
+       prs_mem_free(&ps);
+}
+
+struct notify2_message_table {
+       const char *name;
+       void (*fn)(struct spoolss_notify_msg *msg,
+                  SPOOL_NOTIFY_INFO_DATA *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+       /* 0x00 */ { "PRINTER_NOTIFY_SERVER_NAME", notify_string },
+       /* 0x01 */ { "PRINTER_NOTIFY_PRINTER_NAME", notify_string },
+       /* 0x02 */ { "PRINTER_NOTIFY_SHARE_NAME", notify_string },
+       /* 0x03 */ { "PRINTER_NOTIFY_PORT_NAME", notify_string },
+       /* 0x04 */ { "PRINTER_NOTIFY_DRIVER_NAME", notify_string },
+       /* 0x05 */ { "PRINTER_NOTIFY_COMMENT", notify_string },
+       /* 0x06 */ { "PRINTER_NOTIFY_LOCATION", notify_string },
+       /* 0x07 */ { "PRINTER_NOTIFY_DEVMODE", NULL },
+       /* 0x08 */ { "PRINTER_NOTIFY_SEPFILE", notify_string },
+       /* 0x09 */ { "PRINTER_NOTIFY_PRINT_PROCESSOR", notify_string },
+       /* 0x0a */ { "PRINTER_NOTIFY_PARAMETERS", NULL },
+       /* 0x0b */ { "PRINTER_NOTIFY_DATATYPE", notify_string },
+       /* 0x0c */ { "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+       /* 0x0d */ { "PRINTER_NOTIFY_ATTRIBUTES", notify_one_value },
+       /* 0x0e */ { "PRINTER_NOTIFY_PRIORITY", notify_one_value },
+       /* 0x0f */ { "PRINTER_NOTIFY_DEFAULT_PRIORITY", NULL },
+       /* 0x10 */ { "PRINTER_NOTIFY_START_TIME", NULL },
+       /* 0x11 */ { "PRINTER_NOTIFY_UNTIL_TIME", NULL },
+       /* 0x12 */ { "PRINTER_NOTIFY_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+       /* 0x00 */ { "JOB_NOTIFY_PRINTER_NAME", NULL },
+       /* 0x01 */ { "JOB_NOTIFY_MACHINE_NAME", NULL },
+       /* 0x02 */ { "JOB_NOTIFY_PORT_NAME", NULL },
+       /* 0x03 */ { "JOB_NOTIFY_USER_NAME", notify_string },
+       /* 0x04 */ { "JOB_NOTIFY_NOTIFY_NAME", NULL },
+       /* 0x05 */ { "JOB_NOTIFY_DATATYPE", NULL },
+       /* 0x06 */ { "JOB_NOTIFY_PRINT_PROCESSOR", NULL },
+       /* 0x07 */ { "JOB_NOTIFY_PARAMETERS", NULL },
+       /* 0x08 */ { "JOB_NOTIFY_DRIVER_NAME", NULL },
+       /* 0x09 */ { "JOB_NOTIFY_DEVMODE", NULL },
+       /* 0x0a */ { "JOB_NOTIFY_STATUS", notify_one_value },
+       /* 0x0b */ { "JOB_NOTIFY_STATUS_STRING", NULL },
+       /* 0x0c */ { "JOB_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+       /* 0x0d */ { "JOB_NOTIFY_DOCUMENT", notify_string },
+       /* 0x0e */ { "JOB_NOTIFY_PRIORITY", NULL },
+       /* 0x0f */ { "JOB_NOTIFY_POSITION", NULL },
+       /* 0x10 */ { "JOB_NOTIFY_SUBMITTED", notify_system_time },
+       /* 0x11 */ { "JOB_NOTIFY_START_TIME", NULL },
+       /* 0x12 */ { "JOB_NOTIFY_UNTIL_TIME", NULL },
+       /* 0x13 */ { "JOB_NOTIFY_TIME", NULL },
+       /* 0x14 */ { "JOB_NOTIFY_TOTAL_PAGES", notify_one_value },
+       /* 0x15 */ { "JOB_NOTIFY_PAGES_PRINTED", NULL },
+       /* 0x16 */ { "JOB_NOTIFY_TOTAL_BYTES", notify_one_value },
+       /* 0x17 */ { "JOB_NOTIFY_BYTES_PRINTED", NULL },
+};
+
+
+/***********************************************************************
+ Allocate talloc context for container object
+ **********************************************************************/
+static void notify_msg_ctr_init( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+       if ( !ctr )
+               return;
+
+       ctr->ctx = talloc_init("notify_msg_ctr_init %p", ctr);
+               
+       return;
+}
+
+/***********************************************************************
+ release all allocated memory and zero out structure
+ **********************************************************************/
+static void notify_msg_ctr_destroy( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+       if ( !ctr )
+               return;
+
+       if ( ctr->ctx )
+               talloc_destroy(ctr->ctx);
+               
+       ZERO_STRUCTP(ctr);
+               
+       return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+static TALLOC_CTX* notify_ctr_getctx( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+       if ( !ctr )
+               return NULL;
+               
+       return ctr->ctx;
+}
+
+/***********************************************************************
+ **********************************************************************/
+static SPOOLSS_NOTIFY_MSG_GROUP* notify_ctr_getgroup( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+       if ( !ctr || !ctr->msg_groups )
+               return NULL;
+       
+       if ( idx >= ctr->num_groups )
+               return NULL;
+               
+       return &ctr->msg_groups[idx];
+
+}
+
+/***********************************************************************
+ How many groups of change messages do we have ?
+ **********************************************************************/
+static int notify_msg_ctr_numgroups( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+       if ( !ctr )
+               return 0;
+               
+       return ctr->num_groups;
+}
+
+/***********************************************************************
+ Add a SPOOLSS_NOTIFY_MSG_CTR to the correct group
+ **********************************************************************/
+static int notify_msg_ctr_addmsg( SPOOLSS_NOTIFY_MSG_CTR *ctr, SPOOLSS_NOTIFY_MSG *msg )
+{
+       SPOOLSS_NOTIFY_MSG_GROUP        *groups = NULL;
+       SPOOLSS_NOTIFY_MSG_GROUP        *msg_grp = NULL;
+       SPOOLSS_NOTIFY_MSG              *msg_list = NULL;
+       int                             i, new_slot;
+       
+       if ( !ctr || !msg )
+               return 0;
+       
+       /* loop over all groups looking for a matching printer name */
+       
+       for ( i=0; i<ctr->num_groups; i++ ) {
+               if ( strcmp(ctr->msg_groups[i].printername, msg->printer) == 0 )
+                       break;
+       }
+       
+       /* add a new group? */
+       
+       if ( i == ctr->num_groups ) {
+               ctr->num_groups++;
+
+               if ( !(groups = talloc_realloc( ctr->ctx, ctr->msg_groups, sizeof(SPOOLSS_NOTIFY_MSG_GROUP)*ctr->num_groups)) ) {
+                       DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed!\n"));
+                       return 0;
+               }
+               ctr->msg_groups = groups;
+
+               /* clear the new entry and set the printer name */
+               
+               ZERO_STRUCT( ctr->msg_groups[ctr->num_groups-1] );
+               fstrcpy( ctr->msg_groups[ctr->num_groups-1].printername, msg->printer );
+       }
+       
+       /* add the change messages; 'i' is the correct index now regardless */
+       
+       msg_grp = &ctr->msg_groups[i];
+       
+       msg_grp->num_msgs++;
+       
+       if ( !(msg_list =  talloc_realloc( ctr->ctx, msg_grp->msgs, sizeof(SPOOLSS_NOTIFY_MSG)*msg_grp->num_msgs )) ) {
+               DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed for new message [%d]!\n", msg_grp->num_msgs));
+               return 0;
+       }
+       msg_grp->msgs = msg_list;
+       
+       new_slot = msg_grp->num_msgs-1;
+       memcpy( &msg_grp->msgs[new_slot], msg, sizeof(SPOOLSS_NOTIFY_MSG) );
+       
+       /* need to allocate own copy of data */
+       
+       if ( msg->len != 0 ) 
+               msg_grp->msgs[new_slot].notify.data = talloc_memdup( ctr->ctx, msg->notify.data, msg->len );
+       
+       return ctr->num_groups;
+}
+
+/***********************************************************************
+ Send a change notication message on all handles which have a call 
+ back registered
+ **********************************************************************/
+
+static void send_notify2_changes( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+       Printer_entry            *p;
+       TALLOC_CTX               *mem_ctx = notify_ctr_getctx( ctr );
+       SPOOLSS_NOTIFY_MSG_GROUP *msg_group = notify_ctr_getgroup( ctr, idx );
+       SPOOLSS_NOTIFY_MSG       *messages;
+       
+       
+       if ( !msg_group ) {
+               DEBUG(5,("send_notify2_changes() called with no msg group!\n"));
+               return;
+       }
+       
+       messages = msg_group->msgs;
+       
+       if ( !messages ) {
+               DEBUG(5,("send_notify2_changes() called with no messages!\n"));
+               return;
+       }
+       
+       DEBUG(8,("send_notify2_changes: Enter...[%s]\n", msg_group->printername));
+       
+       /* loop over all printers */
+       
+       for (p = printers_list; p; p = p->next) {
+               SPOOL_NOTIFY_INFO_DATA *data;
+               uint32  data_len = 0;
+               uint32  id;
+               int     i, event_index;
+
+               /* Is there notification on this handle? */
+
+               if ( !p->notify.client_connected )
+                       continue;
+
+               DEBUG(10,("Client connected! [%s]\n", p->dev.handlename));
+
+               /* For this printer?  Print servers always receive 
+                   notifications. */
+
+               if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER )  &&
+                   ( !strequal(msg_group->printername, p->dev.handlename) ) )
+                       continue;
+
+               DEBUG(10,("Our printer\n"));
+               
+               /* allocate the max entries possible */
+               
+               data = talloc( mem_ctx, msg_group->num_msgs*sizeof(SPOOL_NOTIFY_INFO_DATA) );
+               ZERO_STRUCTP(data);
+               
+               event_index = 0;
+               
+               /* build the array of change notifications */
+               
+               for ( i=0; i<msg_group->num_msgs; i++ ) {
+                       SPOOLSS_NOTIFY_MSG      *msg = &messages[i];
+                       
+                       /* Are we monitoring this event? */
+
+                       if (!is_monitoring_event(p, msg->type, msg->field))
+                               continue;
+
+                       
+                       DEBUG(10,("process_notify2_message: Sending message type [%x] field [%x] for printer [%s]\n",
+                               msg->type, msg->field, p->dev.handlename));
+
+                       /* 
+                        * if the is a printer notification handle and not a job notification 
+                        * type, then set the id to 0.  Other wise just use what was specified
+                        * in the message.  
+                        *
+                        * When registering change notification on a print server handle 
+                        * we always need to send back the id (snum) matching the printer
+                        * for which the change took place.  For change notify registered
+                        * on a printer handle, this does not matter and the id should be 0.
+                        *
+                        * --jerry
+                        */
+
+                       if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER ) && ( msg->type == PRINTER_NOTIFY_TYPE ) )
+                               id = 0;
+                       else
+                               id = msg->id;
+
+
+                       /* Convert unix jobid to smb jobid */
+
+                       if (msg->flags & SPOOLSS_NOTIFY_MSG_UNIX_JOBID) {
+                               id = sysjob_to_jobid(msg->id);
+
+                               if (id == -1) {
+                                       DEBUG(3, ("no such unix jobid %d\n", msg->id));
+                                       goto done;
+                               }
+                       }
+
+                       construct_info_data( &data[data_len], msg->type, msg->field, id );
+
+                       switch(msg->type) {
+                       case PRINTER_NOTIFY_TYPE:
+                               if ( printer_notify_table[msg->field].fn )
+                                       printer_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+                               break;
+                       
+                       case JOB_NOTIFY_TYPE:
+                               if ( job_notify_table[msg->field].fn )
+                                       job_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+                               break;
+
+                       default:
+                               DEBUG(5, ("Unknown notification type %d\n", msg->type));
+                               goto done;
+                       }
+
+                       data_len++;
+               }
+
+               cli_spoolss_rrpcn( &notify_cli, mem_ctx, &p->notify.client_hnd, 
+                               data_len, data, p->notify.change, 0 );
+       }
+       
+done:
+       DEBUG(8,("send_notify2_changes: Exit...\n"));
+       return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static BOOL notify2_unpack_msg( SPOOLSS_NOTIFY_MSG *msg, void *buf, size_t len )
+{
+
+       size_t offset = 0;
+
+       /* Unpack message */
+
+       offset += tdb_unpack((char *)buf + offset, len - offset, "f",
+                            msg->printer);
+       
+       offset += tdb_unpack((char *)buf + offset, len - offset, "ddddd",
+                            &msg->type, &msg->field, &msg->id, &msg->len, &msg->flags);
+
+       if (msg->len == 0)
+               tdb_unpack((char *)buf + offset, len - offset, "dd",
+                          &msg->notify.value[0], &msg->notify.value[1]);
+       else
+               tdb_unpack((char *)buf + offset, len - offset, "B", 
+                          &msg->len, &msg->notify.data);
+
+       DEBUG(3, ("notify2_unpack_msg: got NOTIFY2 message, type %d, field 0x%02x, flags 0x%04x\n",
+                 msg->type, msg->field, msg->flags));
+
+       if (msg->len == 0)
+               DEBUG(3, ("notify2_unpack_msg: value1 = %d, value2 = %d\n", msg->notify.value[0],
+                         msg->notify.value[1]));
+       else
+               dump_data(3, msg->notify.data, msg->len);
+
+       return True;
+}
+
+/********************************************************************
+ Receive a notify2 message list
+ ********************************************************************/
+
+static void receive_notify2_message_list(int msg_type, pid_t src, void *msg, size_t len)
+{
+       size_t                  msg_count, i;
+       char                    *buf = (char *)msg;
+       char                    *msg_ptr;
+       size_t                  msg_len;
+       SPOOLSS_NOTIFY_MSG      notify;
+       SPOOLSS_NOTIFY_MSG_CTR  messages;
+       int                     num_groups;
+
+       if (len < 4) {
+               DEBUG(0,("receive_notify2_message_list: bad message format (len < 4)!\n"));
+               return;
+       }
+       
+       msg_count = IVAL(buf, 0);
+       msg_ptr = buf + 4;
+
+       DEBUG(5, ("receive_notify2_message_list: got %d messages in list\n", msg_count));
+
+       if (msg_count == 0) {
+               DEBUG(0,("receive_notify2_message_list: bad message format (msg_count == 0) !\n"));
+               return;
+       }
+
+       /* initialize the container */
+       
+       ZERO_STRUCT( messages );
+       notify_msg_ctr_init( &messages );
+       
+       /* 
+        * build message groups for each printer identified
+        * in a change_notify msg.  Remember that a PCN message
+        * includes the handle returned for the srv_spoolss_replyopenprinter()
+        * call.  Therefore messages are grouped according to printer handle.
+        */
+        
+       for ( i=0; i<msg_count; i++ ) 
+       {
+               if (msg_ptr + 4 - buf > len) {
+                       DEBUG(0,("receive_notify2_message_list: bad message format (len > buf_size) !\n"));
+                       return;
+               }
+
+               msg_len = IVAL(msg_ptr,0);
+               msg_ptr += 4;
+
+               if (msg_ptr + msg_len - buf > len) {
+                       DEBUG(0,("receive_notify2_message_list: bad message format (bad len) !\n"));
+                       return;
+               }
+               
+               /* unpack messages */
+               
+               ZERO_STRUCT( notify );
+               notify2_unpack_msg( &notify, msg_ptr, msg_len );
+               msg_ptr += msg_len;
+               
+               /* add to correct list in container */
+               
+               notify_msg_ctr_addmsg( &messages, &notify );
+               
+               /* free memory that might have been allocated by notify2_unpack_msg() */
+               
+               if ( notify.len != 0 )
+                       SAFE_FREE( notify.notify.data );
+       }
+       
+       /* process each group of messages */
+       
+       num_groups = notify_msg_ctr_numgroups( &messages );
+       for ( i=0; i<num_groups; i++ )
+               send_notify2_changes( &messages, i );
+       
+       
+       /* cleanup */
+               
+       DEBUG(10,("receive_notify2_message_list: processed %u messages\n", (uint32)msg_count ));
+               
+       notify_msg_ctr_destroy( &messages );
+       
+       return;
+}
+
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+static BOOL srv_spoolss_drv_upgrade_printer(char* drivername)
+{
+       int len = strlen(drivername);
+       
+       if (!len)
+               return False;
+
+       DEBUG(10,("srv_spoolss_drv_upgrade_printer: Sending message about driver upgrade [%s]\n",
+               drivername));
+               
+       message_send_pid(sys_getpid(), MSG_PRINTER_DRVUPGRADE, drivername, len+1, False);
+
+       return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and interate
+ over all printers, upgrading ones as neessary 
+ **********************************************************************/
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+       fstring drivername;
+       int snum;
+       int n_services = lp_numservices();
+       
+       len = MIN(len,sizeof(drivername)-1);
+       strncpy(drivername, buf, len);
+       
+       DEBUG(10,("do_drv_upgrade_printer: Got message for new driver [%s]\n", drivername ));
+
+       /* Iterate the printer list */
+       
+       for (snum=0; snum<n_services; snum++)
+       {
+               if (lp_snum_ok(snum) && lp_print_ok(snum) ) 
+               {
+                       WERROR result;
+                       NT_PRINTER_INFO_LEVEL *printer = NULL;
+                       
+                       result = get_a_printer(NULL, &printer, 2, lp_const_servicename(snum));
+                       if (!W_ERROR_IS_OK(result))
+                               continue;
+                               
+                       if (printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername)) 
+                       {
+                               DEBUG(6,("Updating printer [%s]\n", printer->info_2->printername));
+                               
+                               /* all we care about currently is the change_id */
+                               
+                               result = mod_a_printer(*printer, 2);
+                               if (!W_ERROR_IS_OK(result)) {
+                                       DEBUG(3,("do_drv_upgrade_printer: mod_a_printer() failed with status [%s]\n", 
+                                               dos_errstr(result)));
+                               }
+                       }
+                       
+                       free_a_printer(&printer, 2);                    
+               }
+       }
+       
+       /* all done */  
+}
+
+/********************************************************************
+ Update the cahce for all printq's with a registered client 
+ connection
+ ********************************************************************/
+
+void update_monitored_printq_cache( void )
+{
+       Printer_entry *printer = printers_list;
+       int snum;
+       
+       /* loop through all printers and update the cache where 
+          client_connected == True */
+       while ( printer ) 
+       {
+               if ( (printer->printer_type == PRINTER_HANDLE_IS_PRINTER) 
+                       && printer->notify.client_connected ) 
+               {
+                       snum = print_queue_snum(printer->dev.handlename);
+                       print_queue_status( snum, NULL, NULL );
+               }
+               
+               printer = printer->next;
+       }
+       
+       return;
+}
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+static BOOL srv_spoolss_reset_printerdata(char* drivername)
+{
+       int len = strlen(drivername);
+       
+       if (!len)
+               return False;
+
+       DEBUG(10,("srv_spoolss_reset_printerdata: Sending message about resetting printerdata [%s]\n",
+               drivername));
+               
+       message_send_pid(sys_getpid(), MSG_PRINTERDATA_INIT_RESET, drivername, len+1, False);
+
+       return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTERDATA_INIT_RESET message and interate
+ over all printers, resetting printer data as neessary 
+ **********************************************************************/
+void reset_all_printerdata(int msg_type, pid_t src, void *buf, size_t len)
+{
+       fstring drivername;
+       int snum;
+       int n_services = lp_numservices();
+       
+       len = MIN( len, sizeof(drivername)-1 );
+       strncpy( drivername, buf, len );
+       
+       DEBUG(10,("reset_all_printerdata: Got message for new driver [%s]\n", drivername ));
+
+       /* Iterate the printer list */
+       
+       for ( snum=0; snum<n_services; snum++ )
+       {
+               if ( lp_snum_ok(snum) && lp_print_ok(snum) ) 
+               {
+                       WERROR result;
+                       NT_PRINTER_INFO_LEVEL *printer = NULL;
+                       
+                       result = get_a_printer( NULL, &printer, 2, lp_const_servicename(snum) );
+                       if ( !W_ERROR_IS_OK(result) )
+                               continue;
+                               
+                       /* 
+                        * if the printer is bound to the driver, 
+                        * then reset to the new driver initdata 
+                        */
+                       
+                       if ( printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername) ) 
+                       {
+                               DEBUG(6,("reset_all_printerdata: Updating printer [%s]\n", printer->info_2->printername));
+                               
+                               if ( !set_driver_init(printer, 2) ) {
+                                       DEBUG(5,("reset_all_printerdata: Error resetting printer data for printer [%s], driver [%s]!\n",
+                                               printer->info_2->printername, printer->info_2->drivername));
+                               }       
+                               
+                               result = mod_a_printer( *printer, 2 );
+                               if ( !W_ERROR_IS_OK(result) ) {
+                                       DEBUG(3,("reset_all_printerdata: mod_a_printer() failed!  (%s)\n", 
+                                               get_dos_error_msg(result)));
+                               }
+                       }
+                       
+                       free_a_printer( &printer, 2 );
+               }
+       }
+       
+       /* all done */  
+       
+       return;
+}
+
+/********************************************************************
+ Copy routines used by convert_to_openprinterex()
+ *******************************************************************/
+
+static DEVICEMODE* dup_devicemode(TALLOC_CTX *ctx, DEVICEMODE *devmode)
+{
+       DEVICEMODE *d;
+       int len;
+
+       if (!devmode)
+               return NULL;
+               
+       DEBUG (8,("dup_devmode\n"));
+       
+       /* bulk copy first */
+       
+       d = talloc_memdup(ctx, devmode, sizeof(DEVICEMODE));
+       if (!d)
+               return NULL;
+               
+       /* dup the pointer members separately */
+       
+       len = unistrlen(devmode->devicename.buffer);
+       if (len != -1) {
+               d->devicename.buffer = talloc(ctx, len*2);
+               if (unistrcpy(d->devicename.buffer, devmode->devicename.buffer) != len)
+                       return NULL;
+       }
+               
+
+       len = unistrlen(devmode->formname.buffer);
+       if (len != -1) {
+               d->devicename.buffer = talloc(ctx, len*2);
+               if (unistrcpy(d->formname.buffer, devmode->formname.buffer) != len)
+                       return NULL;
+       }
+
+       d->private = talloc_memdup(ctx, devmode->private, devmode->driverextra);
+       
+       return d;
+}
+
+static void copy_devmode_ctr(TALLOC_CTX *ctx, DEVMODE_CTR *new_ctr, DEVMODE_CTR *ctr)
+{
+       if (!new_ctr || !ctr)
+               return;
+               
+       DEBUG(8,("copy_devmode_ctr\n"));
+       
+       new_ctr->size = ctr->size;
+       new_ctr->devmode_ptr = ctr->devmode_ptr;
+       
+       if(ctr->devmode_ptr)
+               new_ctr->devmode = dup_devicemode(ctx, ctr->devmode);
+}
+
+static void copy_printer_default(TALLOC_CTX *ctx, PRINTER_DEFAULT *new_def, PRINTER_DEFAULT *def)
+{
+       if (!new_def || !def)
+               return;
+       
+       DEBUG(8,("copy_printer_defaults\n"));
+       
+       new_def->datatype_ptr = def->datatype_ptr;
+       
+       if (def->datatype_ptr)
+               copy_unistr2(&new_def->datatype, &def->datatype);
+       
+       copy_devmode_ctr(ctx, &new_def->devmode_cont, &def->devmode_cont);
+       
+       new_def->access_required = def->access_required;
+}
+
+/********************************************************************
+ * Convert a SPOOL_Q_OPEN_PRINTER structure to a 
+ * SPOOL_Q_OPEN_PRINTER_EX structure
+ ********************************************************************/
+
+static void convert_to_openprinterex(TALLOC_CTX *ctx, SPOOL_Q_OPEN_PRINTER_EX *q_u_ex, SPOOL_Q_OPEN_PRINTER *q_u)
+{
+       if (!q_u_ex || !q_u)
+               return;
+
+       DEBUG(8,("convert_to_openprinterex\n"));
+                               
+       q_u_ex->printername_ptr = q_u->printername_ptr;
+       
+       if (q_u->printername_ptr)
+               copy_unistr2(&q_u_ex->printername, &q_u->printername);
+       
+       copy_printer_default(ctx, &q_u_ex->printer_default, &q_u->printer_default);
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+WERROR _spoolss_open_printer(pipes_struct *p, SPOOL_Q_OPEN_PRINTER *q_u, SPOOL_R_OPEN_PRINTER *r_u)
+{
+       SPOOL_Q_OPEN_PRINTER_EX q_u_ex;
+       SPOOL_R_OPEN_PRINTER_EX r_u_ex;
+       
+       if (!q_u || !r_u)
+               return WERR_NOMEM;
+       
+       ZERO_STRUCT(q_u_ex);
+       ZERO_STRUCT(r_u_ex);
+       
+       /* convert the OpenPrinter() call to OpenPrinterEx() */
+       
+       convert_to_openprinterex(p->mem_ctx, &q_u_ex, q_u);
+       
+       r_u_ex.status = _spoolss_open_printer_ex(p, &q_u_ex, &r_u_ex);
+       
+       /* convert back to OpenPrinter() */
+       
+       memcpy(r_u, &r_u_ex, sizeof(*r_u));
+       
+       return r_u->status;
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * If the openprinterex rpc call contains a devmode,
+ * it's a per-user one. This per-user devmode is derivated
+ * from the global devmode. Openprinterex() contains a per-user 
+ * devmode for when you do EMF printing and spooling.
+ * In the EMF case, the NT workstation is only doing half the job
+ * of rendering the page. The other half is done by running the printer
+ * driver on the server.
+ * The EMF file doesn't contain the page description (paper size, orientation, ...).
+ * The EMF file only contains what is to be printed on the page.
+ * So in order for the server to know how to print, the NT client sends
+ * a devicemode attached to the openprinterex call.
+ * But this devicemode is short lived, it's only valid for the current print job.
+ *
+ * If Samba would have supported EMF spooling, this devicemode would
+ * have been attached to the handle, to sent it to the driver to correctly
+ * rasterize the EMF file.
+ *
+ * As Samba only supports RAW spooling, we only receive a ready-to-print file,
+ * we just act as a pass-thru between windows and the printer.
+ *
+ * In order to know that Samba supports only RAW spooling, NT has to call
+ * getprinter() at level 2 (attribute field) or NT has to call startdoc()
+ * and until NT sends a RAW job, we refuse it.
+ *
+ * But to call getprinter() or startdoc(), you first need a valid handle,
+ * and to get an handle you have to call openprintex(). Hence why you have
+ * a devicemode in the openprinterex() call.
+ *
+ *
+ * Differences between NT4 and NT 2000.
+ * NT4:
+ * ---
+ * On NT4, you only have a global devicemode. This global devicemode can be changed
+ * by the administrator (or by a user with enough privs). Everytime a user
+ * wants to print, the devicemode is resetted to the default. In Word, everytime
+ * you print, the printer's characteristics are always reset to the global devicemode.
+ *
+ * NT 2000:
+ * -------
+ * In W2K, there is the notion of per-user devicemode. The first time you use
+ * a printer, a per-user devicemode is build from the global devicemode.
+ * If you change your per-user devicemode, it is saved in the registry, under the
+ * H_KEY_CURRENT_KEY sub_tree. So that everytime you print, you have your default
+ * printer preferences available.
+ *
+ * To change the per-user devicemode: it's the "Printing Preferences ..." button
+ * on the General Tab of the printer properties windows.
+ *
+ * To change the global devicemode: it's the "Printing Defaults..." button
+ * on the Advanced Tab of the printer properties window.
+ *
+ * JFM.
+ ********************************************************************/
+
+WERROR _spoolss_open_printer_ex( pipes_struct *p, SPOOL_Q_OPEN_PRINTER_EX *q_u, SPOOL_R_OPEN_PRINTER_EX *r_u)
+{
+       UNISTR2                 *printername = NULL;
+       PRINTER_DEFAULT         *printer_default = &q_u->printer_default;
+       POLICY_HND              *handle = &r_u->handle;
+
+       fstring name;
+       int snum;
+       struct current_user user;
+       Printer_entry *Printer=NULL;
+
+       if (q_u->printername_ptr != 0)
+               printername = &q_u->printername;
+
+       if (printername == NULL)
+               return WERR_INVALID_PRINTER_NAME;
+
+       /* some sanity check because you can open a printer or a print server */
+       /* aka: \\server\printer or \\server */
+       unistr2_to_ascii(name, printername, sizeof(name)-1);
+
+       DEBUGADD(3,("checking name: %s\n",name));
+
+       if (!open_printer_hnd(p, handle, name, 0))
+               return WERR_INVALID_PRINTER_NAME;
+       
+       Printer=find_printer_index_by_hnd(p, handle);
+       if (!Printer) {
+               DEBUG(0,(" _spoolss_open_printer_ex: logic error. \
+Can't find printer handle we created for printer %s\n", name ));
+               close_printer_handle(p,handle);
+               return WERR_INVALID_PRINTER_NAME;
+       }
+
+       get_current_user(&user, p);
+
+       /*
+        * First case: the user is opening the print server:
+        *
+        * Disallow MS AddPrinterWizard if parameter disables it. A Win2k
+        * client 1st tries an OpenPrinterEx with access==0, MUST be allowed.
+        *
+        * Then both Win2k and WinNT clients try an OpenPrinterEx with
+        * SERVER_ALL_ACCESS, which we allow only if the user is root (uid=0)
+        * or if the user is listed in the smb.conf printer admin parameter.
+        *
+        * Then they try OpenPrinterEx with SERVER_READ which we allow. This lets the
+        * client view printer folder, but does not show the MSAPW.
+        *
+        * Note: this test needs code to check access rights here too. Jeremy
+        * could you look at this?
+        * 
+        * Second case: the user is opening a printer:
+        * NT doesn't let us connect to a printer if the connecting user
+        * doesn't have print permission.
+        */
+
+       if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) 
+       {
+               /* Printserver handles use global struct... */
+
+               snum = -1;
+
+               /* Map standard access rights to object specific access rights */
+               
+               se_map_standard(&printer_default->access_required, 
+                               &printserver_std_mapping);
+       
+               /* Deny any object specific bits that don't apply to print
+                  servers (i.e printer and job specific bits) */
+
+               printer_default->access_required &= SPECIFIC_RIGHTS_MASK;
+
+               if (printer_default->access_required &
+                   ~(SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)) {
+                       DEBUG(3, ("access DENIED for non-printserver bits"));
+                       close_printer_handle(p, handle);
+                       return WERR_ACCESS_DENIED;
+               }
+
+               /* Allow admin access */
+
+               if ( printer_default->access_required & SERVER_ACCESS_ADMINISTER ) 
+               {
+                       if (!lp_ms_add_printer_wizard()) {
+                               close_printer_handle(p, handle);
+                               return WERR_ACCESS_DENIED;
+                       }
+
+                       /* if the user is not root and not a printer admin, then fail */
+                       
+                       if ( user.uid != 0
+                            && !user_in_list(uidtoname(user.uid), lp_printer_admin(snum), user.groups, user.ngroups) )
+                       {
+                               close_printer_handle(p, handle);
+                               return WERR_ACCESS_DENIED;
+                       }
+                       
+                       printer_default->access_required = SERVER_ACCESS_ADMINISTER;
+               }
+               else
+               {
+                       printer_default->access_required = SERVER_ACCESS_ENUMERATE;
+               }
+
+               DEBUG(4,("Setting print server access = %s\n", (printer_default->access_required == SERVER_ACCESS_ADMINISTER) 
+                       ? "SERVER_ACCESS_ADMINISTER" : "SERVER_ACCESS_ENUMERATE" ));
+                       
+               /* We fall through to return WERR_OK */
+               
+       }
+       else
+       {
+               /* NT doesn't let us connect to a printer if the connecting user
+                  doesn't have print permission.  */
+
+               if (!get_printer_snum(p, handle, &snum))
+                       return WERR_BADFID;
+
+               se_map_standard(&printer_default->access_required, &printer_std_mapping);
+               
+               /* map an empty access mask to the minimum access mask */
+               if (printer_default->access_required == 0x0)
+                       printer_default->access_required = PRINTER_ACCESS_USE;
+
+               /*
+                * If we are not serving the printer driver for this printer,
+                * map PRINTER_ACCESS_ADMINISTER to PRINTER_ACCESS_USE.  This
+                * will keep NT clients happy  --jerry  
+                */
+                
+               if (lp_use_client_driver(snum) 
+                       && (printer_default->access_required & PRINTER_ACCESS_ADMINISTER))
+               {
+                       printer_default->access_required = PRINTER_ACCESS_USE;
+               }
+
+               /* check smb.conf parameters and the the sec_desc */
+               
+               if (!user_ok(uidtoname(user.uid), snum, user.groups, user.ngroups) || !print_access_check(&user, snum, printer_default->access_required)) {
+                       DEBUG(3, ("access DENIED for printer open\n"));
+                       close_printer_handle(p, handle);
+                       return WERR_ACCESS_DENIED;
+               }
+
+               if ((printer_default->access_required & SPECIFIC_RIGHTS_MASK)& ~(PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)) {
+                       DEBUG(3, ("access DENIED for printer open - unknown bits\n"));
+                       close_printer_handle(p, handle);
+                       return WERR_ACCESS_DENIED;
+               }
+
+               if (printer_default->access_required & PRINTER_ACCESS_ADMINISTER)
+                       printer_default->access_required = PRINTER_ACCESS_ADMINISTER;
+               else
+                       printer_default->access_required = PRINTER_ACCESS_USE;
+
+               DEBUG(4,("Setting printer access = %s\n", (printer_default->access_required == PRINTER_ACCESS_ADMINISTER) 
+                       ? "PRINTER_ACCESS_ADMINISTER" : "PRINTER_ACCESS_USE" ));
+
+       }
+       
+       Printer->access_granted = printer_default->access_required;
+       
+       /* 
+        * If the client sent a devmode in the OpenPrinter() call, then
+        * save it here in case we get a job submission on this handle
+        */
+       
+        if ( (Printer->printer_type != PRINTER_HANDLE_IS_PRINTSERVER)
+               && q_u->printer_default.devmode_cont.devmode_ptr )
+        { 
+               convert_devicemode( Printer->dev.handlename, q_u->printer_default.devmode_cont.devmode,
+                       &Printer->nt_devmode );
+        }
+
+       /* HACK ALERT!!! Sleep for 1/3 of a second to try trigger a LAN/WAN 
+          optimization in Windows 2000 clients  --jerry */
+
+       if ( RA_WIN2K == get_remote_arch() )
+               usleep( 384000 );
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL convert_printer_info(const SPOOL_PRINTER_INFO_LEVEL *uni,
+                               NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+       BOOL ret = True;
+
+       switch (level) {
+               case 2:
+                       ret = uni_2_asc_printer_info_2(uni->info_2, &printer->info_2);
+                       break;
+               default:
+                       break;
+       }
+
+       return ret;
+}
+
+static BOOL convert_printer_driver_info(const SPOOL_PRINTER_DRIVER_INFO_LEVEL *uni,
+                                       NT_PRINTER_DRIVER_INFO_LEVEL *printer, uint32 level)
+{
+       BOOL result = True;
+
+       switch (level) {
+               case 3:
+                       printer->info_3=NULL;
+                       if (!uni_2_asc_printer_driver_3(uni->info_3, &printer->info_3))
+                               result = False;
+                       break;
+               case 6:
+                       printer->info_6=NULL;
+                       if (!uni_2_asc_printer_driver_6(uni->info_6, &printer->info_6))
+                               result = False;
+                       break;
+               default:
+                       break;
+       }
+
+       return result;
+}
+
+BOOL convert_devicemode(const char *printername, const DEVICEMODE *devmode,
+                               NT_DEVICEMODE **pp_nt_devmode)
+{
+       NT_DEVICEMODE *nt_devmode = *pp_nt_devmode;
+
+       /*
+        * Ensure nt_devmode is a valid pointer
+        * as we will be overwriting it.
+        */
+               
+       if (nt_devmode == NULL) {
+               DEBUG(5, ("convert_devicemode: allocating a generic devmode\n"));
+               if ((nt_devmode = construct_nt_devicemode(printername)) == NULL)
+                       return False;
+       }
+
+       rpcstr_pull(nt_devmode->devicename,devmode->devicename.buffer, 31, -1, 0);
+       rpcstr_pull(nt_devmode->formname,devmode->formname.buffer, 31, -1, 0);
+
+       nt_devmode->specversion=devmode->specversion;
+       nt_devmode->driverversion=devmode->driverversion;
+       nt_devmode->size=devmode->size;
+       nt_devmode->fields=devmode->fields;
+       nt_devmode->orientation=devmode->orientation;
+       nt_devmode->papersize=devmode->papersize;
+       nt_devmode->paperlength=devmode->paperlength;
+       nt_devmode->paperwidth=devmode->paperwidth;
+       nt_devmode->scale=devmode->scale;
+       nt_devmode->copies=devmode->copies;
+       nt_devmode->defaultsource=devmode->defaultsource;
+       nt_devmode->printquality=devmode->printquality;
+       nt_devmode->color=devmode->color;
+       nt_devmode->duplex=devmode->duplex;
+       nt_devmode->yresolution=devmode->yresolution;
+       nt_devmode->ttoption=devmode->ttoption;
+       nt_devmode->collate=devmode->collate;
+
+       nt_devmode->logpixels=devmode->logpixels;
+       nt_devmode->bitsperpel=devmode->bitsperpel;
+       nt_devmode->pelswidth=devmode->pelswidth;
+       nt_devmode->pelsheight=devmode->pelsheight;
+       nt_devmode->displayflags=devmode->displayflags;
+       nt_devmode->displayfrequency=devmode->displayfrequency;
+       nt_devmode->icmmethod=devmode->icmmethod;
+       nt_devmode->icmintent=devmode->icmintent;
+       nt_devmode->mediatype=devmode->mediatype;
+       nt_devmode->dithertype=devmode->dithertype;
+       nt_devmode->reserved1=devmode->reserved1;
+       nt_devmode->reserved2=devmode->reserved2;
+       nt_devmode->panningwidth=devmode->panningwidth;
+       nt_devmode->panningheight=devmode->panningheight;
+
+       /*
+        * Only change private and driverextra if the incoming devmode
+        * has a new one. JRA.
+        */
+
+       if ((devmode->driverextra != 0) && (devmode->private != NULL)) {
+               SAFE_FREE(nt_devmode->private);
+               nt_devmode->driverextra=devmode->driverextra;
+               if((nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra * sizeof(uint8))) == NULL)
+                       return False;
+               memcpy(nt_devmode->private, devmode->private, nt_devmode->driverextra);
+       }
+
+       *pp_nt_devmode = nt_devmode;
+
+       return True;
+}
+
+/********************************************************************
+ * _spoolss_enddocprinter_internal.
+ ********************************************************************/
+
+static WERROR _spoolss_enddocprinter_internal(pipes_struct *p, POLICY_HND *handle)
+{
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+       int snum;
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_enddocprinter_internal: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+       
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       Printer->document_started=False;
+       print_job_end(snum, Printer->jobid,True);
+       /* error codes unhandled so far ... */
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ ********************************************************************/
+
+WERROR _spoolss_closeprinter(pipes_struct *p, SPOOL_Q_CLOSEPRINTER *q_u, SPOOL_R_CLOSEPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+       if (Printer && Printer->document_started)
+               _spoolss_enddocprinter_internal(p, handle);          /* print job was not closed */
+
+       if (!close_printer_handle(p, handle))
+               return WERR_BADFID;     
+               
+       /* clear the returned printer handle.  Observed behavior 
+          from Win2k server.  Don't think this really matters.
+          Previous code just copied the value of the closed
+          handle.    --jerry */
+
+       memset(&r_u->handle, '\0', sizeof(r_u->handle));
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinter(pipes_struct *p, SPOOL_Q_DELETEPRINTER *q_u, SPOOL_R_DELETEPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+       WERROR result;
+
+       if (Printer && Printer->document_started)
+               _spoolss_enddocprinter_internal(p, handle);  /* print job was not closed */
+
+       memcpy(&r_u->handle, &q_u->handle, sizeof(r_u->handle));
+
+       result = delete_printer_handle(p, handle);
+
+       update_c_setprinter(False);
+
+       return result;
+}
+
+/*******************************************************************
+ * static function to lookup the version id corresponding to an
+ * long architecture string
+ ******************************************************************/
+
+static int get_version_id (char * arch)
+{
+       int i;
+       struct table_node archi_table[]= {
+               {"Windows 4.0",          "WIN40",       0 },
+               {"Windows NT x86",       "W32X86",      2 },
+               {"Windows NT R4000",     "W32MIPS",     2 },    
+               {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
+               {"Windows NT PowerPC",   "W32PPC",      2 },
+               {NULL,                   "",            -1 }
+       };
+       for (i=0; archi_table[i].long_archi != NULL; i++)
+       {
+               if (strcmp(arch, archi_table[i].long_archi) == 0)
+                       return (archi_table[i].version);
+        }
+       
+       return -1;
+}
+
+/********************************************************************
+ * _spoolss_deleteprinterdriver
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, SPOOL_R_DELETEPRINTERDRIVER *r_u)
+{
+       fstring                         driver;
+       fstring                         arch;
+       NT_PRINTER_DRIVER_INFO_LEVEL    info;
+       NT_PRINTER_DRIVER_INFO_LEVEL    info_win2k;
+       int                             version;
+       struct current_user             user;
+       WERROR                          status;
+       WERROR                          status_win2k = WERR_ACCESS_DENIED;
+       
+       get_current_user(&user, p);
+        
+       unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+       unistr2_to_ascii(arch,   &q_u->arch,   sizeof(arch)-1   );
+       
+       /* check that we have a valid driver name first */
+       
+       if ((version=get_version_id(arch)) == -1) 
+               return WERR_INVALID_ENVIRONMENT;
+                               
+       ZERO_STRUCT(info);
+       ZERO_STRUCT(info_win2k);
+       
+       if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) 
+       {
+               /* try for Win2k driver if "Windows NT x86" */
+               
+               if ( version == 2 ) {
+                       version = 3;
+                       if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+                               status = WERR_UNKNOWN_PRINTER_DRIVER;
+                               goto done;
+                       }
+               }
+               /* otherwise it was a failure */
+               else {
+                       status = WERR_UNKNOWN_PRINTER_DRIVER;
+                       goto done;
+               }
+               
+       }
+       
+       if (printer_driver_in_use(info.info_3)) {
+               status = WERR_PRINTER_DRIVER_IN_USE;
+               goto done;
+       }
+       
+       if ( version == 2 )
+       {               
+               if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+               {
+                       /* if we get to here, we now have 2 driver info structures to remove */
+                       /* remove the Win2k driver first*/
+               
+                       status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, False );
+                       free_a_printer_driver( info_win2k, 3 );
+               
+                       /* this should not have failed---if it did, report to client */
+                       if ( !W_ERROR_IS_OK(status_win2k) )
+                               goto done;
+               }
+       }
+       
+       status = delete_printer_driver(info.info_3, &user, version, False);
+       
+       /* if at least one of the deletes succeeded return OK */
+       
+       if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+               status = WERR_OK;
+       
+done:
+       free_a_printer_driver( info, 3 );
+
+       return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriverex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, SPOOL_R_DELETEPRINTERDRIVEREX *r_u)
+{
+       fstring                         driver;
+       fstring                         arch;
+       NT_PRINTER_DRIVER_INFO_LEVEL    info;
+       NT_PRINTER_DRIVER_INFO_LEVEL    info_win2k;
+       int                             version;
+       uint32                          flags = q_u->delete_flags;
+       BOOL                            delete_files;
+       struct current_user             user;
+       WERROR                          status;
+       WERROR                          status_win2k = WERR_ACCESS_DENIED;
+       
+       get_current_user(&user, p);
+       
+       unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+       unistr2_to_ascii(arch,   &q_u->arch,   sizeof(arch)-1   );
+
+       /* check that we have a valid driver name first */
+       if ((version=get_version_id(arch)) == -1) {
+               /* this is what NT returns */
+               return WERR_INVALID_ENVIRONMENT;
+       }
+       
+       if ( flags & DPD_DELETE_SPECIFIC_VERSION )
+               version = q_u->version;
+               
+       ZERO_STRUCT(info);
+       ZERO_STRUCT(info_win2k);
+               
+       status = get_a_printer_driver(&info, 3, driver, arch, version);
+       
+       if ( !W_ERROR_IS_OK(status) ) 
+       {
+               /* 
+                * if the client asked for a specific version, 
+                * or this is something other than Windows NT x86,
+                * then we've failed 
+                */
+               
+               if ( (flags&DPD_DELETE_SPECIFIC_VERSION) || (version !=2) )
+                       goto done;
+                       
+               /* try for Win2k driver if "Windows NT x86" */
+               
+               version = 3;
+               if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+                       status = WERR_UNKNOWN_PRINTER_DRIVER;
+                       goto done;
+               }
+       }
+               
+       if ( printer_driver_in_use(info.info_3) ) {
+               status = WERR_PRINTER_DRIVER_IN_USE;
+               goto done;
+       }
+       
+       /* 
+        * we have a couple of cases to consider. 
+        * (1) Are any files in use?  If so and DPD_DELTE_ALL_FILE is set,
+        *     then the delete should fail if **any** files overlap with 
+        *     other drivers 
+        * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
+        *     non-overlapping files 
+        * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
+        *     is set, the do not delete any files
+        * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+        */
+       
+       delete_files = flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
+       
+       /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+               
+       if ( delete_files && printer_driver_files_in_use(info.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+               /* no idea of the correct error here */
+               status = WERR_ACCESS_DENIED;    
+               goto done;
+       }
+
+                       
+       /* also check for W32X86/3 if necessary; maybe we already have? */
+               
+       if ( (version == 2) && ((flags&DPD_DELETE_SPECIFIC_VERSION) != DPD_DELETE_SPECIFIC_VERSION)  ) {
+               if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3))) 
+               {
+                       
+                       if ( delete_files && printer_driver_files_in_use(info_win2k.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+                               /* no idea of the correct error here */
+                               free_a_printer_driver( info_win2k, 3 );
+                               status = WERR_ACCESS_DENIED;    
+                               goto done;
+                       }
+               
+                       /* if we get to here, we now have 2 driver info structures to remove */
+                       /* remove the Win2k driver first*/
+               
+                       status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, delete_files);
+                       free_a_printer_driver( info_win2k, 3 );
+                               
+                       /* this should not have failed---if it did, report to client */
+                               
+                       if ( !W_ERROR_IS_OK(status_win2k) )
+                               goto done;
+               }
+       }
+
+       status = delete_printer_driver(info.info_3, &user, version, delete_files);
+
+       if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+               status = WERR_OK;
+done:
+       free_a_printer_driver( info, 3 );
+       
+       return status;
+}
+
+
+/****************************************************************************
+ Internal routine for retreiving printerdata
+ ***************************************************************************/
+
+static WERROR get_printer_dataex( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL *printer, 
+                                  const char *key, const char *value, uint32 *type, uint8 **data, 
+                                 uint32 *needed, uint32 in_size  )
+{
+       REGISTRY_VALUE          *val;
+       int                     size, data_len;
+       
+       if ( !(val = get_printer_data( printer->info_2, key, value)) )
+               return WERR_BADFILE;
+       
+       *type = regval_type( val );
+
+       DEBUG(5,("get_printer_dataex: allocating %d\n", in_size));
+
+       size = regval_size( val );
+       
+       /* copy the min(in_size, len) */
+       
+       if ( in_size ) {
+               data_len = (size > in_size) ? in_size : size*sizeof(uint8);
+               
+               /* special case for 0 length values */
+               if ( data_len ) {
+                       if ( (*data  = (uint8 *)talloc_memdup(ctx, regval_data_p(val), data_len)) == NULL )
+                               return WERR_NOMEM;
+               }
+               else {
+                       if ( (*data  = (uint8 *)talloc_zero(ctx, in_size)) == NULL )
+                               return WERR_NOMEM;
+               }
+       }
+       else
+               *data = NULL;
+
+       *needed = size;
+       
+       DEBUG(5,("get_printer_dataex: copy done\n"));
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Internal routine for removing printerdata
+ ***************************************************************************/
+
+static WERROR delete_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value )
+{
+       return delete_printer_data( printer->info_2, key, value );
+}
+
+/****************************************************************************
+ Internal routine for storing printerdata
+ ***************************************************************************/
+
+static WERROR set_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value, 
+                                  uint32 type, uint8 *data, int real_len  )
+{
+       delete_printer_data( printer->info_2, key, value );
+       
+       return add_printer_data( printer->info_2, key, value, type, data, real_len );
+}
+
+/********************************************************************
+ GetPrinterData on a printer server Handle.
+********************************************************************/
+
+static WERROR getprinterdata_printer_server(TALLOC_CTX *ctx, fstring value, uint32 *type, uint8 **data, uint32 *needed, uint32 in_size)
+{              
+       int i;
+       
+       DEBUG(8,("getprinterdata_printer_server:%s\n", value));
+               
+       if (!StrCaseCmp(value, "W3SvcInstalled")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc_zero(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+               *needed = 0x4;
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "BeepEnabled")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+               SIVAL(*data, 0, 0x00);
+               *needed = 0x4;                  
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "EventLog")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+               /* formally was 0x1b */
+               SIVAL(*data, 0, 0x0);
+               *needed = 0x4;                  
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "NetPopup")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+               SIVAL(*data, 0, 0x00);
+               *needed = 0x4;
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "MajorVersion")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+#ifdef HAVE_ADS
+               SIVAL(*data, 0, 3);
+#else
+               SIVAL(*data, 0, 2);
+#endif
+               *needed = 0x4;
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "DefaultSpoolDirectory")) {
+               fstring string;
+
+               fstrcpy(string, string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+               *type = 0x1;                    
+               *needed = 2*(strlen(string)+1);         
+               if((*data  = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+                       return WERR_NOMEM;
+               memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+               
+               /* it's done by hand ready to go on the wire */
+               for (i=0; i<strlen(string); i++) {
+                       (*data)[2*i]=string[i];
+                       (*data)[2*i+1]='\0';
+               }                       
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "Architecture")) {                       
+               pstring string="Windows NT x86";
+               *type = 0x1;                    
+               *needed = 2*(strlen(string)+1); 
+               if((*data  = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+                       return WERR_NOMEM;
+               memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+               for (i=0; i<strlen(string); i++) {
+                       (*data)[2*i]=string[i];
+                       (*data)[2*i+1]='\0';
+               }                       
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "DsPresent")) {
+               *type = 0x4;
+               if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+                       return WERR_NOMEM;
+               SIVAL(*data, 0, 0x01);
+               *needed = 0x4;
+               return WERR_OK;
+       }
+
+       if (!StrCaseCmp(value, "DNSMachineName")) {                     
+               pstring hostname;
+               
+               if (!get_myfullname(hostname))
+                       return WERR_BADFILE;
+               *type = 0x1;                    
+               *needed = 2*(strlen(hostname)+1);       
+               if((*data  = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+                       return WERR_NOMEM;
+               memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+               for (i=0; i<strlen(hostname); i++) {
+                       (*data)[2*i]=hostname[i];
+                       (*data)[2*i+1]='\0';
+               }                       
+               return WERR_OK;
+       }
+
+
+       return WERR_BADFILE;
+}
+
+/********************************************************************
+ * spoolss_getprinterdata
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdata(pipes_struct *p, SPOOL_Q_GETPRINTERDATA *q_u, SPOOL_R_GETPRINTERDATA *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       UNISTR2         *valuename = &q_u->valuename;
+       uint32          in_size = q_u->size;
+       uint32          *type = &r_u->type;
+       uint32          *out_size = &r_u->size;
+       uint8           **data = &r_u->data;
+       uint32          *needed = &r_u->needed;
+       WERROR          status;
+       fstring         value;
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int             snum = 0;
+       
+       /*
+        * Reminder: when it's a string, the length is in BYTES
+        * even if UNICODE is negociated.
+        *
+        * JFM, 4/19/1999
+        */
+
+       *out_size = in_size;
+
+       /* in case of problem, return some default values */
+       
+       *needed = 0;
+       *type   = 0;
+       
+       DEBUG(4,("_spoolss_getprinterdata\n"));
+       
+       if ( !Printer ) {
+               DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               status = WERR_BADFID;
+               goto done;
+       }
+       
+       unistr2_to_ascii(value, valuename, sizeof(value)-1);
+       
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER )
+               status = getprinterdata_printer_server( p->mem_ctx, value, type, data, needed, *out_size );
+       else
+       {
+               if ( !get_printer_snum(p,handle, &snum) ) {
+                       status = WERR_BADFID;
+                       goto done;
+               }
+
+               status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+               if ( !W_ERROR_IS_OK(status) )
+                       goto done;
+
+               /* XP sends this and wants to change id value from the PRINTER_INFO_0 */
+
+               if ( strequal(value, "ChangeId") ) {
+                       *type = REG_DWORD;
+                       *needed = sizeof(uint32);
+                       if ( (*data = (uint8*)talloc(p->mem_ctx, sizeof(uint32))) == NULL) {
+                               status = WERR_NOMEM;
+                               goto done;
+                       }
+                       **data = printer->info_2->changeid;
+                       status = WERR_OK;
+               }
+               else
+                       status = get_printer_dataex( p->mem_ctx, printer, SPOOL_PRINTERDATA_KEY, value, type, data, needed, *out_size );
+       }
+
+       if (*needed > *out_size)
+               status = WERR_MORE_DATA;
+       
+done:
+       if ( !W_ERROR_IS_OK(status) ) 
+       {
+               DEBUG(5, ("error %d: allocating %d\n", W_ERROR_V(status),*out_size));
+               
+               /* reply this param doesn't exist */
+               
+               if ( *out_size ) {
+                       if((*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL) {
+                               if ( printer ) 
+                                       free_a_printer( &printer, 2 );
+                               return WERR_NOMEM;
+               } 
+               } 
+               else {
+                       *data = NULL;
+               }
+       }
+       
+       /* cleanup & exit */
+
+       if ( printer )
+               free_a_printer( &printer, 2 );
+       
+       return status;
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static BOOL spoolss_connect_to_client(struct cli_state *the_cli, const char *remote_machine)
+{
+       ZERO_STRUCTP(the_cli);
+       if(cli_initialise(the_cli) == NULL) {
+               DEBUG(0,("connect_to_client: unable to initialize client connection.\n"));
+               return False;
+       }
+
+       if(!resolve_name( remote_machine, &the_cli->dest_ip, 0x20)) {
+               DEBUG(0,("connect_to_client: Can't resolve address for %s\n", remote_machine));
+               cli_shutdown(the_cli);
+       return False;
+       }
+
+       if (ismyip(the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       if (!cli_connect(the_cli, remote_machine, &the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: unable to connect to SMB server on machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+  
+       if (!attempt_netbios_session_request(the_cli, lp_netbios_name(), remote_machine, &the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the NetBIOS session request.\n", 
+                       remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       the_cli->protocol = PROTOCOL_NT1;
+    
+       if (!cli_negprot(the_cli)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the negotiate protocol. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       if (the_cli->protocol != PROTOCOL_NT1) {
+               DEBUG(0,("connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       /*
+        * Do an anonymous session setup.
+        */
+    
+       if (!cli_session_setup(the_cli, "", "", 0, "", 0, "")) {
+               DEBUG(0,("connect_to_client: machine %s rejected the session setup. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       if (!(the_cli->sec_mode & 1)) {
+               DEBUG(0,("connect_to_client: machine %s isn't in user level security mode\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       if (!cli_send_tconX(the_cli, "IPC$", "IPC", "", 1)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       /*
+        * Ok - we have an anonymous connection to the IPC$ share.
+        * Now start the NT Domain stuff :-).
+        */
+
+       if(cli_nt_session_open(the_cli, PI_SPOOLSS) == False) {
+               DEBUG(0,("connect_to_client: unable to open the domain client session to machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli)));
+               cli_nt_session_close(the_cli);
+               cli_ulogoff(the_cli);
+               cli_shutdown(the_cli);
+               return False;
+       } 
+
+       return True;
+}
+
+/***************************************************************************
+ Connect to the client.
+****************************************************************************/
+
+static BOOL srv_spoolss_replyopenprinter(int snum, const char *printer, uint32 localprinter, uint32 type, POLICY_HND *handle)
+{
+       WERROR result;
+
+       /*
+        * If it's the first connection, contact the client
+        * and connect to the IPC$ share anonumously
+        */
+       if (smb_connections==0) {
+               fstring unix_printer;
+
+               fstrcpy(unix_printer, printer+2); /* the +2 is to strip the leading 2 backslashs */
+
+               if(!spoolss_connect_to_client(&notify_cli, unix_printer))
+                       return False;
+                       
+               message_register(MSG_PRINTER_NOTIFY2, receive_notify2_message_list);
+               /* Tell the connections db we're now interested in printer
+                * notify messages. */
+               register_message_flags( True, FLAG_MSG_PRINTING );
+       }
+
+       /* 
+        * Tell the specific printing tdb we want messages for this printer
+        * by registering our PID.
+        */
+
+       if (!print_notify_register_pid(snum))
+               DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", printer ));
+
+       smb_connections++;
+
+       result = cli_spoolss_reply_open_printer(&notify_cli, notify_cli.mem_ctx, printer, localprinter, 
+                       type, handle);
+                       
+       if (!W_ERROR_IS_OK(result))
+               DEBUG(5,("srv_spoolss_reply_open_printer: Client RPC returned [%s]\n",
+                       dos_errstr(result)));
+
+       return (W_ERROR_IS_OK(result)); 
+}
+
+/********************************************************************
+ * _spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ *
+ * before replying OK: status=0 a rpc call is made to the workstation
+ * asking ReplyOpenPrinter 
+ *
+ * in fact ReplyOpenPrinter is the changenotify equivalent on the spoolss pipe
+ * called from api_spoolss_rffpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNEX *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 flags = q_u->flags;
+       uint32 options = q_u->options;
+       UNISTR2 *localmachine = &q_u->localmachine;
+       uint32 printerlocal = q_u->printerlocal;
+       int snum = -1;
+       SPOOL_NOTIFY_OPTION *option = q_u->option;
+
+       /* store the notify value in the printer struct */
+
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_rffpcnex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       Printer->notify.flags=flags;
+       Printer->notify.options=options;
+       Printer->notify.printerlocal=printerlocal;
+
+       if (Printer->notify.option)
+               free_spool_notify_option(&Printer->notify.option);
+
+       Printer->notify.option=dup_spool_notify_option(option);
+
+       unistr2_to_ascii(Printer->notify.localmachine, localmachine, 
+                      sizeof(Printer->notify.localmachine)-1);
+
+       /* Connect to the client machine and send a ReplyOpenPrinter */
+
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+               snum = -1;
+       else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+                       !get_printer_snum(p, handle, &snum) )
+               return WERR_BADFID;
+
+       if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine,
+                                       Printer->notify.printerlocal, 1,
+                                       &Printer->notify.client_hnd))
+               return WERR_SERVER_UNAVAILABLE;
+
+       Printer->notify.client_connected=True;
+
+       return WERR_OK;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servername
+ ********************************************************************/
+
+void spoolss_notify_server_name(int snum, 
+                                      SPOOL_NOTIFY_INFO_DATA *data, 
+                                      print_queue_struct *queue,
+                                      NT_PRINTER_INFO_LEVEL *printer,
+                                      TALLOC_CTX *mem_ctx) 
+{
+       pstring temp_name, temp;
+       uint32 len;
+
+       slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+       len = rpcstr_push(temp, temp_name, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername (not including the servername).
+ ********************************************************************/
+
+void spoolss_notify_printer_name(int snum, 
+                                       SPOOL_NOTIFY_INFO_DATA *data, 
+                                       print_queue_struct *queue,
+                                       NT_PRINTER_INFO_LEVEL *printer,
+                                       TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+               
+       /* the notify name should not contain the \\server\ part */
+       char *p = strrchr(printer->info_2->printername, '\\');
+
+       if (!p) {
+               p = printer->info_2->printername;
+       } else {
+               p++;
+       }
+
+       len = rpcstr_push(temp, p, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servicename
+ ********************************************************************/
+
+void spoolss_notify_share_name(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data, 
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, lp_servicename(snum), sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the port name
+ ********************************************************************/
+
+void spoolss_notify_port_name(int snum, 
+                                    SPOOL_NOTIFY_INFO_DATA *data, 
+                                    print_queue_struct *queue,
+                                    NT_PRINTER_INFO_LEVEL *printer,
+                                    TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       /* even if it's strange, that's consistant in all the code */
+
+       len = rpcstr_push(temp, printer->info_2->portname, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername
+ * but it doesn't exist, have to see what to do
+ ********************************************************************/
+
+void spoolss_notify_driver_name(int snum, 
+                                      SPOOL_NOTIFY_INFO_DATA *data,
+                                      print_queue_struct *queue,
+                                      NT_PRINTER_INFO_LEVEL *printer,
+                                      TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, printer->info_2->drivername, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ ********************************************************************/
+
+void spoolss_notify_comment(int snum, 
+                                  SPOOL_NOTIFY_INFO_DATA *data,
+                                  print_queue_struct *queue,
+                                  NT_PRINTER_INFO_LEVEL *printer,
+                                  TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       if (*printer->info_2->comment == '\0')
+               len = rpcstr_push(temp, lp_comment(snum), sizeof(temp)-2, STR_TERMINATE);
+       else
+               len = rpcstr_push(temp, printer->info_2->comment, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ * location = "Room 1, floor 2, building 3"
+ ********************************************************************/
+
+void spoolss_notify_location(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, printer->info_2->location,sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the device mode
+ * jfm:xxxx don't to it for know but that's a real problem !!!
+ ********************************************************************/
+
+static void spoolss_notify_devmode(int snum, 
+                                  SPOOL_NOTIFY_INFO_DATA *data,
+                                  print_queue_struct *queue,
+                                  NT_PRINTER_INFO_LEVEL *printer,
+                                  TALLOC_CTX *mem_ctx)
+{
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the separator file name
+ ********************************************************************/
+
+void spoolss_notify_sepfile(int snum, 
+                                  SPOOL_NOTIFY_INFO_DATA *data, 
+                                  print_queue_struct *queue,
+                                  NT_PRINTER_INFO_LEVEL *printer,
+                                  TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, printer->info_2->sepfile, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor
+ * jfm:xxxx return always winprint to indicate we don't do anything to it
+ ********************************************************************/
+
+void spoolss_notify_print_processor(int snum, 
+                                          SPOOL_NOTIFY_INFO_DATA *data,
+                                          print_queue_struct *queue,
+                                          NT_PRINTER_INFO_LEVEL *printer,
+                                          TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp,  printer->info_2->printprocessor, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor options
+ * jfm:xxxx send an empty string
+ ********************************************************************/
+
+void spoolss_notify_parameters(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data,
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp,  printer->info_2->parameters, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the data type
+ * jfm:xxxx always send RAW as data type
+ ********************************************************************/
+
+void spoolss_notify_datatype(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, printer->info_2->datatype, sizeof(pstring)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the security descriptor
+ * jfm:xxxx send an null pointer to say no security desc
+ * have to implement security before !
+ ********************************************************************/
+
+static void spoolss_notify_security_desc(int snum, 
+                                        SPOOL_NOTIFY_INFO_DATA *data,
+                                        print_queue_struct *queue,
+                                        NT_PRINTER_INFO_LEVEL *printer,
+                                        TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.sd.size = printer->info_2->secdesc_buf->len;
+       data->notify_data.sd.desc = dup_sec_desc( mem_ctx, printer->info_2->secdesc_buf->sec ) ;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the attributes
+ * jfm:xxxx a samba printer is always shared
+ ********************************************************************/
+
+void spoolss_notify_attributes(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data,
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = printer->info_2->attributes;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the priority
+ ********************************************************************/
+
+static void spoolss_notify_priority(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = printer->info_2->priority;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the default priority
+ ********************************************************************/
+
+static void spoolss_notify_default_priority(int snum, 
+                                           SPOOL_NOTIFY_INFO_DATA *data,
+                                           print_queue_struct *queue,
+                                           NT_PRINTER_INFO_LEVEL *printer,
+                                           TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = printer->info_2->default_priority;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the start time
+ ********************************************************************/
+
+static void spoolss_notify_start_time(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data,
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = printer->info_2->starttime;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the until time
+ ********************************************************************/
+
+static void spoolss_notify_until_time(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data,
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = printer->info_2->untiltime;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the status
+ ********************************************************************/
+
+static void spoolss_notify_status(int snum, 
+                                 SPOOL_NOTIFY_INFO_DATA *data,
+                                 print_queue_struct *queue,
+                                 NT_PRINTER_INFO_LEVEL *printer,
+                                 TALLOC_CTX *mem_ctx)
+{
+       print_status_struct status;
+
+       print_queue_length(snum, &status);
+       data->notify_data.value[0]=(uint32) status.status;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the number of jobs queued
+ ********************************************************************/
+
+void spoolss_notify_cjobs(int snum, 
+                                SPOOL_NOTIFY_INFO_DATA *data,
+                                print_queue_struct *queue,
+                                NT_PRINTER_INFO_LEVEL *printer, 
+                                TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = print_queue_length(snum, NULL);
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the average ppm
+ ********************************************************************/
+
+static void spoolss_notify_average_ppm(int snum, 
+                                      SPOOL_NOTIFY_INFO_DATA *data,
+                                      print_queue_struct *queue,
+                                      NT_PRINTER_INFO_LEVEL *printer,
+                                      TALLOC_CTX *mem_ctx)
+{
+       /* always respond 8 pages per minutes */
+       /* a little hard ! */
+       data->notify_data.value[0] = printer->info_2->averageppm;
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with username
+ ********************************************************************/
+
+static void spoolss_notify_username(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, queue->fs_user, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status(int snum, 
+                                     SPOOL_NOTIFY_INFO_DATA *data,
+                                     print_queue_struct *queue,
+                                     NT_PRINTER_INFO_LEVEL *printer,
+                                     TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=nt_printj_status(queue->status);
+       data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job name
+ ********************************************************************/
+
+static void spoolss_notify_job_name(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       pstring temp;
+       uint32 len;
+
+       len = rpcstr_push(temp, queue->fs_file, sizeof(temp)-2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status_string(int snum, 
+                                            SPOOL_NOTIFY_INFO_DATA *data,
+                                            print_queue_struct *queue,
+                                            NT_PRINTER_INFO_LEVEL *printer, 
+                                            TALLOC_CTX *mem_ctx)
+{
+       /*
+        * Now we're returning job status codes we just return a "" here. JRA.
+        */
+
+       const char *p = "";
+       pstring temp;
+       uint32 len;
+
+#if 0 /* NO LONGER NEEDED - JRA. 02/22/2001 */
+       p = "unknown";
+
+       switch (queue->status) {
+       case LPQ_QUEUED:
+               p = "Queued";
+               break;
+       case LPQ_PAUSED:
+               p = "";    /* NT provides the paused string */
+               break;
+       case LPQ_SPOOLING:
+               p = "Spooling";
+               break;
+       case LPQ_PRINTING:
+               p = "Printing";
+               break;
+       }
+#endif /* NO LONGER NEEDED. */
+
+       len = rpcstr_push(temp, p, sizeof(temp) - 2, STR_TERMINATE);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+       
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job time
+ ********************************************************************/
+
+static void spoolss_notify_job_time(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=0x0;
+       data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job size
+ ********************************************************************/
+
+static void spoolss_notify_job_size(int snum, 
+                                   SPOOL_NOTIFY_INFO_DATA *data,
+                                   print_queue_struct *queue,
+                                   NT_PRINTER_INFO_LEVEL *printer,
+                                   TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=queue->size;
+       data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with page info
+ ********************************************************************/
+static void spoolss_notify_total_pages(int snum,
+                               SPOOL_NOTIFY_INFO_DATA *data,
+                               print_queue_struct *queue,
+                               NT_PRINTER_INFO_LEVEL *printer,
+                               TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=queue->page_count;
+       data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with pages printed info.
+ ********************************************************************/
+static void spoolss_notify_pages_printed(int snum,
+                               SPOOL_NOTIFY_INFO_DATA *data,
+                               print_queue_struct *queue,
+                               NT_PRINTER_INFO_LEVEL *printer,
+                               TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=0;  /* Add code when back-end tracks this */
+       data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with job position.
+ ********************************************************************/
+
+static void spoolss_notify_job_position(int snum, 
+                                       SPOOL_NOTIFY_INFO_DATA *data,
+                                       print_queue_struct *queue,
+                                       NT_PRINTER_INFO_LEVEL *printer,
+                                       TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0]=queue->job;
+       data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with submitted time.
+ ********************************************************************/
+
+static void spoolss_notify_submitted_time(int snum, 
+                                         SPOOL_NOTIFY_INFO_DATA *data,
+                                         print_queue_struct *queue,
+                                         NT_PRINTER_INFO_LEVEL *printer,
+                                         TALLOC_CTX *mem_ctx)
+{
+       struct tm *t;
+       uint32 len;
+       SYSTEMTIME st;
+       char *p;
+
+       t=gmtime(&queue->time);
+
+       len = sizeof(SYSTEMTIME);
+
+       data->notify_data.data.length = len;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
+       }
+       
+       make_systemtime(&st, t);
+
+       /*
+        * Systemtime must be linearized as a set of UINT16's. 
+        * Fix from Benjamin (Bj) Kuit bj@it.uts.edu.au
+        */
+
+       p = (char *)data->notify_data.data.string;
+       SSVAL(p, 0, st.year);
+       SSVAL(p, 2, st.month);
+       SSVAL(p, 4, st.dayofweek);
+       SSVAL(p, 6, st.day);
+       SSVAL(p, 8, st.hour);
+        SSVAL(p, 10, st.minute);
+       SSVAL(p, 12, st.second);
+       SSVAL(p, 14, st.milliseconds);
+}
+
+struct s_notify_info_data_table
+{
+       uint16 type;
+       uint16 field;
+       const char *name;
+       uint32 size;
+       void (*fn) (int snum, SPOOL_NOTIFY_INFO_DATA *data,
+                   print_queue_struct *queue,
+                   NT_PRINTER_INFO_LEVEL *printer, TALLOC_CTX *mem_ctx);
+};
+
+/* A table describing the various print notification constants and
+   whether the notification data is a pointer to a variable sized
+   buffer, a one value uint32 or a two value uint32. */
+
+static const struct s_notify_info_data_table notify_info_data_table[] =
+{
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME,         "PRINTER_NOTIFY_SERVER_NAME",         NOTIFY_STRING,   spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME,        "PRINTER_NOTIFY_PRINTER_NAME",        NOTIFY_STRING,   spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,          "PRINTER_NOTIFY_SHARE_NAME",          NOTIFY_STRING,   spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,           "PRINTER_NOTIFY_PORT_NAME",           NOTIFY_STRING,   spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,         "PRINTER_NOTIFY_DRIVER_NAME",         NOTIFY_STRING,   spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,             "PRINTER_NOTIFY_COMMENT",             NOTIFY_STRING,   spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,            "PRINTER_NOTIFY_LOCATION",            NOTIFY_STRING,   spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE,             "PRINTER_NOTIFY_DEVMODE",             NOTIFY_POINTER,   spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE,             "PRINTER_NOTIFY_SEPFILE",             NOTIFY_STRING,   spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR,     "PRINTER_NOTIFY_PRINT_PROCESSOR",     NOTIFY_STRING,   spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS,          "PRINTER_NOTIFY_PARAMETERS",          NOTIFY_STRING,   spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE,            "PRINTER_NOTIFY_DATATYPE",            NOTIFY_STRING,   spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_SECDESC,   spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES,          "PRINTER_NOTIFY_ATTRIBUTES",          NOTIFY_ONE_VALUE, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY,            "PRINTER_NOTIFY_PRIORITY",            NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY,    "PRINTER_NOTIFY_DEFAULT_PRIORITY",    NOTIFY_ONE_VALUE, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME,          "PRINTER_NOTIFY_START_TIME",          NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME,          "PRINTER_NOTIFY_UNTIL_TIME",          NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS,              "PRINTER_NOTIFY_STATUS",              NOTIFY_ONE_VALUE, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING,       "PRINTER_NOTIFY_STATUS_STRING",       NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS,               "PRINTER_NOTIFY_CJOBS",               NOTIFY_ONE_VALUE, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM,         "PRINTER_NOTIFY_AVERAGE_PPM",         NOTIFY_ONE_VALUE, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES,         "PRINTER_NOTIFY_TOTAL_PAGES",         NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED,       "PRINTER_NOTIFY_PAGES_PRINTED",       NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES,         "PRINTER_NOTIFY_TOTAL_BYTES",         NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED,       "PRINTER_NOTIFY_BYTES_PRINTED",       NOTIFY_POINTER,   NULL },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINTER_NAME,            "JOB_NOTIFY_PRINTER_NAME",            NOTIFY_STRING,   spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_MACHINE_NAME,            "JOB_NOTIFY_MACHINE_NAME",            NOTIFY_STRING,   spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PORT_NAME,               "JOB_NOTIFY_PORT_NAME",               NOTIFY_STRING,   spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_USER_NAME,               "JOB_NOTIFY_USER_NAME",               NOTIFY_STRING,   spoolss_notify_username },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_NOTIFY_NAME,             "JOB_NOTIFY_NOTIFY_NAME",             NOTIFY_STRING,   spoolss_notify_username },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DATATYPE,                "JOB_NOTIFY_DATATYPE",                NOTIFY_STRING,   spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINT_PROCESSOR,         "JOB_NOTIFY_PRINT_PROCESSOR",         NOTIFY_STRING,   spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PARAMETERS,              "JOB_NOTIFY_PARAMETERS",              NOTIFY_STRING,   spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DRIVER_NAME,             "JOB_NOTIFY_DRIVER_NAME",             NOTIFY_STRING,   spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DEVMODE,                 "JOB_NOTIFY_DEVMODE",                 NOTIFY_POINTER,   spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS,                  "JOB_NOTIFY_STATUS",                  NOTIFY_ONE_VALUE, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS_STRING,           "JOB_NOTIFY_STATUS_STRING",           NOTIFY_STRING,   spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SECURITY_DESCRIPTOR,     "JOB_NOTIFY_SECURITY_DESCRIPTOR",     NOTIFY_POINTER,   NULL },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DOCUMENT,                "JOB_NOTIFY_DOCUMENT",                NOTIFY_STRING,   spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRIORITY,                "JOB_NOTIFY_PRIORITY",                NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_POSITION,                "JOB_NOTIFY_POSITION",                NOTIFY_ONE_VALUE, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SUBMITTED,               "JOB_NOTIFY_SUBMITTED",               NOTIFY_POINTER,   spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_START_TIME,              "JOB_NOTIFY_START_TIME",              NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_UNTIL_TIME,              "JOB_NOTIFY_UNTIL_TIME",              NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TIME,                    "JOB_NOTIFY_TIME",                    NOTIFY_ONE_VALUE, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_PAGES,             "JOB_NOTIFY_TOTAL_PAGES",             NOTIFY_ONE_VALUE, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PAGES_PRINTED,           "JOB_NOTIFY_PAGES_PRINTED",           NOTIFY_ONE_VALUE, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_BYTES,             "JOB_NOTIFY_TOTAL_BYTES",             NOTIFY_ONE_VALUE, spoolss_notify_job_size },
+};
+
+/*******************************************************************
+ Return the size of info_data structure.
+********************************************************************/
+
+static uint32 size_of_notify_info_data(uint16 type, uint16 field)
+{
+       int i=0;
+
+       for (i = 0; i < sizeof(notify_info_data_table); i++) 
+       {
+               if ( (notify_info_data_table[i].type == type)
+                       && (notify_info_data_table[i].field == field) ) 
+               {
+                       switch(notify_info_data_table[i].size) 
+                       {
+                       case NOTIFY_ONE_VALUE:
+                       case NOTIFY_TWO_VALUE:
+                               return 1;
+                       case NOTIFY_STRING:
+                               return 2;
+
+                       /* The only pointer notify data I have seen on
+                          the wire is the submitted time and this has
+                          the notify size set to 4. -tpot */
+
+                       case NOTIFY_POINTER:
+                               return 4;
+                                       
+                               case NOTIFY_SECDESC:
+                                       return 5;
+                       }
+               }
+       }
+
+       DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+       return 0;
+}
+
+/*******************************************************************
+ Return the type of notify_info_data.
+********************************************************************/
+
+static int type_of_notify_info_data(uint16 type, uint16 field)
+{
+       int i=0;
+
+       for (i = 0; i < sizeof(notify_info_data_table); i++) {
+               if (notify_info_data_table[i].type == type &&
+                   notify_info_data_table[i].field == field)
+                       return notify_info_data_table[i].size;
+       }
+
+       return False;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static int search_notify(uint16 type, uint16 field, int *value)
+{      
+       int i;
+
+       for (i = 0; i < sizeof(notify_info_data_table); i++) {
+               if (notify_info_data_table[i].type == type &&
+                   notify_info_data_table[i].field == field &&
+                   notify_info_data_table[i].fn != NULL) {
+                       *value = i;
+                       return True;
+               }
+       }
+       
+       return False;   
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void construct_info_data(SPOOL_NOTIFY_INFO_DATA *info_data, uint16 type, uint16 field, int id)
+{
+       info_data->type     = type;
+       info_data->field    = field;
+       info_data->reserved = 0;
+
+       info_data->size     = size_of_notify_info_data(type, field);
+       info_data->enc_type = type_of_notify_info_data(type, field);
+
+       info_data->id = id;
+
+}
+
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_printer_info(Printer_entry *print_hnd, SPOOL_NOTIFY_INFO *info, int
+                                         snum, SPOOL_NOTIFY_OPTION_TYPE
+                                         *option_type, uint32 id,
+                                         TALLOC_CTX *mem_ctx) 
+{
+       int field_num,j;
+       uint16 type;
+       uint16 field;
+
+       SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       print_queue_struct *queue=NULL;
+
+       type=option_type->type;
+
+       DEBUG(4,("construct_notify_printer_info: Notify type: [%s], number of notify info: [%d] on printer: [%s]\n",
+               (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+               option_type->count, lp_servicename(snum)));
+       
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &printer, 2, lp_const_servicename(snum))))
+               return False;
+
+       for(field_num=0; field_num<option_type->count; field_num++) {
+               field = option_type->fields[field_num];
+               
+               DEBUG(4,("construct_notify_printer_info: notify [%d]: type [%x], field [%x]\n", field_num, type, field));
+
+               if (!search_notify(type, field, &j) )
+                       continue;
+
+               if((tid=(SPOOL_NOTIFY_INFO_DATA *)Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+                       DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n"));
+                       return False;
+               } else 
+                       info->data = tid;
+
+               current_data = &info->data[info->count];
+
+               construct_info_data(current_data, type, field, id);
+
+               DEBUG(10,("construct_notify_printer_info: calling [%s]  snum=%d  printername=[%s])\n",
+                               notify_info_data_table[j].name, snum, printer->info_2->printername ));
+
+               notify_info_data_table[j].fn(snum, current_data, queue,
+                                            printer, mem_ctx);
+
+               info->count++;
+       }
+
+       free_a_printer(&printer, 2);
+       return True;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_jobs_info(print_queue_struct *queue,
+                                      SPOOL_NOTIFY_INFO *info,
+                                      NT_PRINTER_INFO_LEVEL *printer,
+                                      int snum, SPOOL_NOTIFY_OPTION_TYPE
+                                      *option_type, uint32 id,
+                                      TALLOC_CTX *mem_ctx) 
+{
+       int field_num,j;
+       uint16 type;
+       uint16 field;
+
+       SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+       
+       DEBUG(4,("construct_notify_jobs_info\n"));
+       
+       type = option_type->type;
+
+       DEBUGADD(4,("Notify type: [%s], number of notify info: [%d]\n",
+               (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+               option_type->count));
+
+       for(field_num=0; field_num<option_type->count; field_num++) {
+               field = option_type->fields[field_num];
+
+               if (!search_notify(type, field, &j) )
+                       continue;
+
+               if((tid=Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+                       DEBUG(2,("construct_notify_jobs_info: failed to enlarg buffer info->data!\n"));
+                       return False;
+               }
+               else info->data = tid;
+
+               current_data=&(info->data[info->count]);
+
+               construct_info_data(current_data, type, field, id);
+               notify_info_data_table[j].fn(snum, current_data, queue,
+                                            printer, mem_ctx);
+               info->count++;
+       }
+
+       return True;
+}
+
+/*
+ * JFM: The enumeration is not that simple, it's even non obvious.
+ *
+ * let's take an example: I want to monitor the PRINTER SERVER for
+ * the printer's name and the number of jobs currently queued.
+ * So in the NOTIFY_OPTION, I have one NOTIFY_OPTION_TYPE structure.
+ * Its type is PRINTER_NOTIFY_TYPE and it has 2 fields NAME and CJOBS.
+ *
+ * I have 3 printers on the back of my server.
+ *
+ * Now the response is a NOTIFY_INFO structure, with 6 NOTIFY_INFO_DATA
+ * structures.
+ *   Number    Data                    Id
+ *     1       printer 1 name          1
+ *     2       printer 1 cjob          1
+ *     3       printer 2 name          2
+ *     4       printer 2 cjob          2
+ *     5       printer 3 name          3
+ *     6       printer 3 name          3
+ *
+ * that's the print server case, the printer case is even worse.
+ */
+
+/*******************************************************************
+ *
+ * enumerate all printers on the printserver
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printserver_notify_info(pipes_struct *p, POLICY_HND *hnd, 
+                                     SPOOL_NOTIFY_INFO *info,
+                                     TALLOC_CTX *mem_ctx)
+{
+       int snum;
+       Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+       int n_services=lp_numservices();
+       int i;
+       uint32 id;
+       SPOOL_NOTIFY_OPTION *option;
+       SPOOL_NOTIFY_OPTION_TYPE *option_type;
+
+       DEBUG(4,("printserver_notify_info\n"));
+       
+       if (!Printer)
+               return WERR_BADFID;
+
+       option=Printer->notify.option;
+       id=1;
+       info->version=2;
+       info->data=NULL;
+       info->count=0;
+
+       for (i=0; i<option->count; i++) {
+               option_type=&(option->ctr.type[i]);
+               
+               if (option_type->type!=PRINTER_NOTIFY_TYPE)
+                       continue;
+               
+               for (snum=0; snum<n_services; snum++)
+               {
+                       if ( lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) )
+                               construct_notify_printer_info ( Printer, info, snum, option_type, snum, mem_ctx );
+               }
+       }
+                       
+#if 0                  
+       /*
+        * Debugging information, don't delete.
+        */
+
+       DEBUG(1,("dumping the NOTIFY_INFO\n"));
+       DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+       DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+       
+       for (i=0; i<info->count; i++) {
+               DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+               i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+               info->data[i].id, info->data[i].size, info->data[i].enc_type));
+       }
+#endif
+       
+       return WERR_OK;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printer_notify_info(pipes_struct *p, POLICY_HND *hnd, SPOOL_NOTIFY_INFO *info,
+                                 TALLOC_CTX *mem_ctx)
+{
+       int snum;
+       Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+       int i;
+       uint32 id;
+       SPOOL_NOTIFY_OPTION *option;
+       SPOOL_NOTIFY_OPTION_TYPE *option_type;
+       int count,j;
+       print_queue_struct *queue=NULL;
+       print_status_struct status;
+       
+       DEBUG(4,("printer_notify_info\n"));
+
+       if (!Printer)
+               return WERR_BADFID;
+
+       option=Printer->notify.option;
+       id = 0x0;
+       info->version=2;
+       info->data=NULL;
+       info->count=0;
+
+       get_printer_snum(p, hnd, &snum);
+
+       for (i=0; i<option->count; i++) {
+               option_type=&option->ctr.type[i];
+               
+               switch ( option_type->type ) {
+               case PRINTER_NOTIFY_TYPE:
+                       if(construct_notify_printer_info(Printer, info, snum, 
+                                                        option_type, id,
+                                                        mem_ctx))  
+                               id--;
+                       break;
+                       
+               case JOB_NOTIFY_TYPE: {
+                       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+                       count = print_queue_status(snum, &queue, &status);
+
+                       if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))))
+                               goto done;
+
+                       for (j=0; j<count; j++) {
+                               construct_notify_jobs_info(&queue[j], info,
+                                                          printer, snum,
+                                                          option_type,
+                                                          queue[j].job,
+                                                          mem_ctx); 
+                       }
+
+                       free_a_printer(&printer, 2);
+                       
+               done:
+                       SAFE_FREE(queue);
+                       break;
+               }
+               }
+       }
+       
+       /*
+        * Debugging information, don't delete.
+        */
+       /*
+       DEBUG(1,("dumping the NOTIFY_INFO\n"));
+       DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+       DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+       
+       for (i=0; i<info->count; i++) {
+               DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+               i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+               info->data[i].id, info->data[i].size, info->data[i].enc_type));
+       }
+       */
+       return WERR_OK;
+}
+
+/********************************************************************
+ * spoolss_rfnpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCNEX *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       SPOOL_NOTIFY_INFO *info = &r_u->info;
+
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+       WERROR result = WERR_BADFID;
+
+       /* we always have a NOTIFY_INFO struct */
+       r_u->info_ptr=0x1;
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_rfnpcnex: Invalid handle (%s:%u:%u).\n",
+                        OUR_HANDLE(handle)));
+               goto done;
+       }
+
+       DEBUG(4,("Printer type %x\n",Printer->printer_type));
+
+       /*
+        *      We are now using the change value, and 
+        *      I should check for PRINTER_NOTIFY_OPTIONS_REFRESH but as
+        *      I don't have a global notification system, I'm sending back all the
+        *      informations even when _NOTHING_ has changed.
+        */
+
+       /* We need to keep track of the change value to send back in 
+           RRPCN replies otherwise our updates are ignored. */
+
+       Printer->notify.fnpcn = True;
+
+       if (Printer->notify.client_connected) {
+               DEBUG(10,("_spoolss_rfnpcnex: Saving change value in request [%x]\n", q_u->change));
+               Printer->notify.change = q_u->change;
+       }
+
+       /* just ignore the SPOOL_NOTIFY_OPTION */
+       
+       switch (Printer->printer_type) {
+               case PRINTER_HANDLE_IS_PRINTSERVER:
+                       result = printserver_notify_info(p, handle, info, p->mem_ctx);
+                       break;
+                       
+               case PRINTER_HANDLE_IS_PRINTER:
+                       result = printer_notify_info(p, handle, info, p->mem_ctx);
+                       break;
+       }
+       
+       Printer->notify.fnpcn = False;
+       
+done:
+       return result;
+}
+
+/********************************************************************
+ * construct_printer_info_0
+ * fill a printer_info_0 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_0(Printer_entry *print_hnd, PRINTER_INFO_0 *printer, int snum)
+{
+       pstring chaine;
+       int count;
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+       counter_printer_0 *session_counter;
+       uint32 global_counter;
+       struct tm *t;
+       time_t setuptime;
+       print_status_struct status;
+       
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+
+       count = print_queue_length(snum, &status);
+
+       /* check if we already have a counter for this printer */       
+       session_counter = (counter_printer_0 *)ubi_dlFirst(&counter_list);
+
+       for(; session_counter; session_counter = (counter_printer_0 *)ubi_dlNext(session_counter)) {
+               if (session_counter->snum == snum)
+                       break;
+       }
+
+       /* it's the first time, add it to the list */
+       if (session_counter==NULL) {
+               if((session_counter=(counter_printer_0 *)malloc(sizeof(counter_printer_0))) == NULL) {
+                       free_a_printer(&ntprinter, 2);
+                       return False;
+               }
+               ZERO_STRUCTP(session_counter);
+               session_counter->snum=snum;
+               session_counter->counter=0;
+               ubi_dlAddHead( &counter_list, (ubi_dlNode *)session_counter);
+       }
+       
+       /* increment it */
+       session_counter->counter++;
+       
+       /* JFM:
+        * the global_counter should be stored in a TDB as it's common to all the clients
+        * and should be zeroed on samba startup
+        */
+       global_counter=session_counter->counter;
+       
+       pstrcpy(chaine,ntprinter->info_2->printername);
+
+       init_unistr(&printer->printername, chaine);
+       
+       slprintf(chaine,sizeof(chaine)-1,"\\\\%s", get_called_name());
+       init_unistr(&printer->servername, chaine);
+       
+       printer->cjobs = count;
+       printer->total_jobs = 0;
+       printer->total_bytes = 0;
+
+       setuptime = (time_t)ntprinter->info_2->setuptime;
+       t=gmtime(&setuptime);
+
+       printer->year = t->tm_year+1900;
+       printer->month = t->tm_mon+1;
+       printer->dayofweek = t->tm_wday;
+       printer->day = t->tm_mday;
+       printer->hour = t->tm_hour;
+       printer->minute = t->tm_min;
+       printer->second = t->tm_sec;
+       printer->milliseconds = 0;
+
+       printer->global_counter = global_counter;
+       printer->total_pages = 0;
+#ifdef HAVE_ADS
+       printer->major_version = 0x0005;        /* NT 5 */
+       printer->build_version = 0x0893;        /* build 2195 */
+#else
+       printer->major_version = 0x0004;        /* NT 4 */
+       printer->build_version = 0x0565;        /* build 1381 */
+#endif
+       printer->unknown7 = 0x1;
+       printer->unknown8 = 0x0;
+       printer->unknown9 = 0x0;
+       printer->session_counter = session_counter->counter;
+       printer->unknown11 = 0x0;
+       printer->printer_errors = 0x0;          /* number of print failure */
+       printer->unknown13 = 0x0;
+       printer->unknown14 = 0x1;
+       printer->unknown15 = 0x024a;            /* 586 Pentium ? */
+       printer->unknown16 =  0x0;
+       printer->change_id = ntprinter->info_2->changeid; /* ChangeID in milliseconds*/
+       printer->unknown18 =  0x0;
+       printer->status = nt_printq_status(status.status);
+       printer->unknown20 =  0x0;
+       printer->c_setprinter = get_c_setprinter(); /* monotonically increasing sum of delta printer counts */
+       printer->unknown22 = 0x0;
+       printer->unknown23 = 0x6;               /* 6  ???*/
+       printer->unknown24 = 0;                 /* unknown 24 to 26 are always 0 */
+       printer->unknown25 = 0;
+       printer->unknown26 = 0;
+       printer->unknown27 = 0;
+       printer->unknown28 = 0;
+       printer->unknown29 = 0;
+       
+       free_a_printer(&ntprinter,2);
+       return (True);  
+}
+
+/********************************************************************
+ * construct_printer_info_1
+ * fill a printer_info_1 struct
+ ********************************************************************/
+static BOOL construct_printer_info_1(Printer_entry *print_hnd, uint32 flags, PRINTER_INFO_1 *printer, int snum)
+{
+       pstring chaine;
+       pstring chaine2;
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+
+       printer->flags=flags;
+
+       if (*ntprinter->info_2->comment == '\0') {
+               init_unistr(&printer->comment, lp_comment(snum));
+               slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+                       ntprinter->info_2->drivername, lp_comment(snum));
+       }
+       else {
+               init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+               slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+                       ntprinter->info_2->drivername, ntprinter->info_2->comment);
+       }
+               
+       slprintf(chaine2,sizeof(chaine)-1,"%s", ntprinter->info_2->printername);
+
+       init_unistr(&printer->description, chaine);
+       init_unistr(&printer->name, chaine2);   
+       
+       free_a_printer(&ntprinter,2);
+
+       return True;
+}
+
+/****************************************************************************
+ Free a DEVMODE struct.
+****************************************************************************/
+
+static void free_dev_mode(DEVICEMODE *dev)
+{
+       if (dev == NULL)
+               return;
+
+               SAFE_FREE(dev->private);
+       SAFE_FREE(dev); 
+}
+
+
+/****************************************************************************
+ Convert an NT_DEVICEMODE to a DEVICEMODE structure.  Both pointers 
+ should be valid upon entry
+****************************************************************************/
+
+static BOOL convert_nt_devicemode( DEVICEMODE *devmode, NT_DEVICEMODE *ntdevmode )
+{
+       if ( !devmode || !ntdevmode )
+               return False;
+               
+       init_unistr(&devmode->devicename, ntdevmode->devicename);
+
+       init_unistr(&devmode->formname, ntdevmode->formname);
+
+       devmode->specversion      = ntdevmode->specversion;
+       devmode->driverversion    = ntdevmode->driverversion;
+       devmode->size             = ntdevmode->size;
+       devmode->driverextra      = ntdevmode->driverextra;
+       devmode->fields           = ntdevmode->fields;
+                               
+       devmode->orientation      = ntdevmode->orientation;     
+       devmode->papersize        = ntdevmode->papersize;
+       devmode->paperlength      = ntdevmode->paperlength;
+       devmode->paperwidth       = ntdevmode->paperwidth;
+       devmode->scale            = ntdevmode->scale;
+       devmode->copies           = ntdevmode->copies;
+       devmode->defaultsource    = ntdevmode->defaultsource;
+       devmode->printquality     = ntdevmode->printquality;
+       devmode->color            = ntdevmode->color;
+       devmode->duplex           = ntdevmode->duplex;
+       devmode->yresolution      = ntdevmode->yresolution;
+       devmode->ttoption         = ntdevmode->ttoption;
+       devmode->collate          = ntdevmode->collate;
+       devmode->icmmethod        = ntdevmode->icmmethod;
+       devmode->icmintent        = ntdevmode->icmintent;
+       devmode->mediatype        = ntdevmode->mediatype;
+       devmode->dithertype       = ntdevmode->dithertype;
+
+       if (ntdevmode->private != NULL) {
+               if ((devmode->private=(uint8 *)memdup(ntdevmode->private, ntdevmode->driverextra)) == NULL)
+                       return False;
+       }
+       
+       return True;
+}
+
+/****************************************************************************
+ Create a DEVMODE struct. Returns malloced memory.
+****************************************************************************/
+
+DEVICEMODE *construct_dev_mode(int snum)
+{
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       DEVICEMODE              *devmode = NULL;
+       
+       DEBUG(7,("construct_dev_mode\n"));
+       
+       DEBUGADD(8,("getting printer characteristics\n"));
+
+       if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum)))) 
+               return NULL;
+
+       if ( !printer->info_2->devmode ) {
+               DEBUG(5, ("BONG! There was no device mode!\n"));
+               goto done;
+       }
+
+       if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) == NULL) {
+               DEBUG(2,("construct_dev_mode: malloc fail.\n"));
+               goto done;
+       }
+
+       ZERO_STRUCTP(devmode);  
+       
+       DEBUGADD(8,("loading DEVICEMODE\n"));
+
+       if ( !convert_nt_devicemode( devmode, printer->info_2->devmode ) ) {
+               free_dev_mode( devmode );
+               devmode = NULL;
+       }
+
+done:
+       free_a_printer(&printer,2);
+
+       return devmode;
+}
+
+/********************************************************************
+ * construct_printer_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_2(Printer_entry *print_hnd, PRINTER_INFO_2 *printer, int snum)
+{
+       int count;
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+       print_status_struct status;
+
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+               
+       count = print_queue_length(snum, &status);
+
+       init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+       init_unistr(&printer->printername, ntprinter->info_2->printername);                             /* printername*/
+       init_unistr(&printer->sharename, lp_servicename(snum));                 /* sharename */
+       init_unistr(&printer->portname, ntprinter->info_2->portname);                   /* port */      
+       init_unistr(&printer->drivername, ntprinter->info_2->drivername);       /* drivername */
+
+       if (*ntprinter->info_2->comment == '\0')
+               init_unistr(&printer->comment, lp_comment(snum));                       /* comment */   
+       else
+               init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+
+       init_unistr(&printer->location, ntprinter->info_2->location);           /* location */  
+       init_unistr(&printer->sepfile, ntprinter->info_2->sepfile);             /* separator file */
+       init_unistr(&printer->printprocessor, ntprinter->info_2->printprocessor);/* print processor */
+       init_unistr(&printer->datatype, ntprinter->info_2->datatype);           /* datatype */  
+       init_unistr(&printer->parameters, ntprinter->info_2->parameters);       /* parameters (of print processor) */   
+
+       printer->attributes = ntprinter->info_2->attributes;
+
+       printer->priority = ntprinter->info_2->priority;                                /* priority */  
+       printer->defaultpriority = ntprinter->info_2->default_priority;         /* default priority */
+       printer->starttime = ntprinter->info_2->starttime;                      /* starttime */
+       printer->untiltime = ntprinter->info_2->untiltime;                      /* untiltime */
+       printer->status = nt_printq_status(status.status);                      /* status */
+       printer->cjobs = count;                                                 /* jobs */
+       printer->averageppm = ntprinter->info_2->averageppm;                    /* average pages per minute */
+                       
+       if((printer->devmode = construct_dev_mode(snum)) == NULL) {
+               DEBUG(8, ("Returning NULL Devicemode!\n"));
+       }
+
+       if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+               /* steal the printer info sec_desc structure.  [badly done]. */
+               printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+               ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen memory. */
+               ntprinter->info_2->secdesc_buf->len = 0; /* Stolen memory. */
+               ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen memory. */
+       }
+       else {
+               printer->secdesc = NULL;
+       }
+
+       free_a_printer(&ntprinter, 2);
+       return True;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_3(Printer_entry *print_hnd, PRINTER_INFO_3 **pp_printer, int snum)
+{
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+       PRINTER_INFO_3 *printer = NULL;
+
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+
+       *pp_printer = NULL;
+       if ((printer = (PRINTER_INFO_3 *)malloc(sizeof(PRINTER_INFO_3))) == NULL) {
+               DEBUG(2,("construct_printer_info_3: malloc fail.\n"));
+               return False;
+       }
+
+       ZERO_STRUCTP(printer);
+       
+       printer->flags = 4; /* These are the components of the SD we are returning. */
+       if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+               /* steal the printer info sec_desc structure.  [badly done]. */
+               printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+
+#if 0
+               /*
+                * Set the flags for the components we are returning.
+                */
+
+               if (printer->secdesc->owner_sid)
+                       printer->flags |= OWNER_SECURITY_INFORMATION;
+
+               if (printer->secdesc->grp_sid)
+                       printer->flags |= GROUP_SECURITY_INFORMATION;
+
+               if (printer->secdesc->dacl)
+                       printer->flags |= DACL_SECURITY_INFORMATION;
+
+               if (printer->secdesc->sacl)
+                       printer->flags |= SACL_SECURITY_INFORMATION;
+#endif
+
+               ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen the malloced memory. */
+               ntprinter->info_2->secdesc_buf->len = 0; /* Stolen the malloced memory. */
+               ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen the malloced memory. */
+       }
+
+       free_a_printer(&ntprinter, 2);
+
+       *pp_printer = printer;
+       return True;
+}
+
+/********************************************************************
+ * construct_printer_info_4
+ * fill a printer_info_4 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_4(Printer_entry *print_hnd, PRINTER_INFO_4 *printer, int snum)
+{
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+               
+       init_unistr(&printer->printername, ntprinter->info_2->printername);                             /* printername*/
+       init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+       printer->attributes = ntprinter->info_2->attributes;
+
+       free_a_printer(&ntprinter, 2);
+       return True;
+}
+
+/********************************************************************
+ * construct_printer_info_5
+ * fill a printer_info_5 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_5(Printer_entry *print_hnd, PRINTER_INFO_5 *printer, int snum)
+{
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+       if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+               return False;
+               
+       init_unistr(&printer->printername, ntprinter->info_2->printername);
+       init_unistr(&printer->portname, ntprinter->info_2->portname); 
+       printer->attributes = ntprinter->info_2->attributes;
+
+       /* these two are not used by NT+ according to MSDN */
+
+       printer->device_not_selected_timeout = 0x0;  /* have seen 0x3a98 */
+       printer->transmission_retry_timeout  = 0x0;  /* have seen 0xafc8 */
+
+       free_a_printer(&ntprinter, 2);
+
+       return True;
+}
+
+/********************************************************************
+ * construct_printer_info_7
+ * fill a printer_info_7 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_7(Printer_entry *print_hnd, PRINTER_INFO_7 *printer, int snum)
+{
+       char *guid_str = NULL;
+       GUID guid;
+       TALLOC_CTX *mem_ctx;
+       
+       mem_ctx = talloc_init("dump_guid");
+       if (!mem_ctx) return;
+       
+       if (is_printer_published(print_hnd, snum, &guid)) {
+               asprintf(&guid_str, "{%s}", uuid_string(mem_ctx, guid));
+               strupper(guid_str);
+               init_unistr(&printer->guid, guid_str);
+               printer->action = SPOOL_DS_PUBLISH;
+       } else {
+               init_unistr(&printer->guid, "");
+               printer->action = SPOOL_DS_UNPUBLISH;
+       }
+       talloc_destroy(mem_ctx);
+
+       return True;
+}
+
+/********************************************************************
+ Spoolss_enumprinters.
+********************************************************************/
+
+static WERROR enum_all_printers_info_1(uint32 flags, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       int snum;
+       int i;
+       int n_services=lp_numservices();
+       PRINTER_INFO_1 *tp, *printers=NULL;
+       PRINTER_INFO_1 current_prt;
+       
+       DEBUG(4,("enum_all_printers_info_1\n"));        
+
+       for (snum=0; snum<n_services; snum++) {
+               if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+                       DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+                       if (construct_printer_info_1(NULL, flags, &current_prt, snum)) {
+                               if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_1))) == NULL) {
+                                       DEBUG(2,("enum_all_printers_info_1: failed to enlarge printers buffer!\n"));
+                                       SAFE_FREE(printers);
+                                       *returned=0;
+                                       return WERR_NOMEM;
+                               }
+                               else printers = tp;
+                               DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_1\n", *returned));             
+
+                               memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_1));
+                               (*returned)++;
+                       }
+               }
+       }
+               
+       /* check the required size. */  
+       for (i=0; i<*returned; i++)
+               (*needed) += spoolss_size_printer_info_1(&printers[i]);
+
+       if (!alloc_buffer_size(buffer, *needed))
+               return WERR_INSUFFICIENT_BUFFER;
+
+       /* fill the buffer with the structures */
+       for (i=0; i<*returned; i++)
+               smb_io_printer_info_1("", buffer, &printers[i], 0);     
+
+       /* clear memory */
+       SAFE_FREE(printers);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+       else
+               return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_local.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_local(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       DEBUG(4,("enum_all_printers_info_1_local\n"));  
+       
+       return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_name.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_name(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       char *s = name;
+       
+       DEBUG(4,("enum_all_printers_info_1_name\n"));   
+       
+       if ((name[0] == '\\') && (name[1] == '\\'))
+               s = name + 2;
+               
+       if (is_myname_or_ipaddr(s)) {
+               return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+       }
+       else
+               return WERR_INVALID_NAME;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_remote.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_remote(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PRINTER_INFO_1 *printer;
+       fstring printername;
+       fstring desc;
+       fstring comment;
+       DEBUG(4,("enum_all_printers_info_1_remote\n")); 
+
+       /* JFM: currently it's more a place holder than anything else.
+        * In the spooler world there is a notion of server registration.
+        * the print servers are registring (sp ?) on the PDC (in the same domain)
+        *
+        * We should have a TDB here. The registration is done thru an undocumented RPC call.
+        */
+       
+       if((printer=(PRINTER_INFO_1 *)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+               return WERR_NOMEM;
+
+       *returned=1;
+       
+       slprintf(printername, sizeof(printername)-1,"Windows NT Remote Printers!!\\\\%s", get_called_name());           
+       slprintf(desc, sizeof(desc)-1,"%s", get_called_name());
+       slprintf(comment, sizeof(comment)-1, "Logged on Domain");
+
+       init_unistr(&printer->description, desc);
+       init_unistr(&printer->name, printername);       
+       init_unistr(&printer->comment, comment);
+       printer->flags=PRINTER_ENUM_ICON3|PRINTER_ENUM_CONTAINER;
+               
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_1(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_1("", buffer, printer, 0);  
+
+       /* clear memory */
+       SAFE_FREE(printer);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+       else
+               return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_network.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_network(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       char *s = name;
+
+       DEBUG(4,("enum_all_printers_info_1_network\n"));        
+       
+       /* If we respond to a enum_printers level 1 on our name with flags
+          set to PRINTER_ENUM_REMOTE with a list of printers then these
+          printers incorrectly appear in the APW browse list.
+          Specifically the printers for the server appear at the workgroup
+          level where all the other servers in the domain are
+          listed. Windows responds to this call with a
+          WERR_CAN_NOT_COMPLETE so we should do the same. */ 
+
+       if (name[0] == '\\' && name[1] == '\\')
+                s = name + 2;
+
+       if (is_myname_or_ipaddr(s))
+                return WERR_CAN_NOT_COMPLETE;
+
+       return enum_all_printers_info_1(PRINTER_ENUM_UNKNOWN_8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+static WERROR enum_all_printers_info_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       int snum;
+       int i;
+       int n_services=lp_numservices();
+       PRINTER_INFO_2 *tp, *printers=NULL;
+       PRINTER_INFO_2 current_prt;
+
+       for (snum=0; snum<n_services; snum++) {
+               if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+                       DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+                               
+                       if (construct_printer_info_2(NULL, &current_prt, snum)) {
+                               if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_2))) == NULL) {
+                                       DEBUG(2,("enum_all_printers_info_2: failed to enlarge printers buffer!\n"));
+                                       SAFE_FREE(printers);
+                                       *returned = 0;
+                                       return WERR_NOMEM;
+                               }
+                               else printers = tp;
+                               DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_2\n", *returned));             
+                               memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_2));
+                               (*returned)++;
+                       }
+               }
+       }
+       
+       /* check the required size. */  
+       for (i=0; i<*returned; i++) 
+               (*needed) += spoolss_size_printer_info_2(&printers[i]);
+       
+       if (!alloc_buffer_size(buffer, *needed)) {
+               for (i=0; i<*returned; i++) {
+                       free_devmode(printers[i].devmode);
+               }
+               SAFE_FREE(printers);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       for (i=0; i<*returned; i++)
+               smb_io_printer_info_2("", buffer, &(printers[i]), 0);   
+       
+       /* clear memory */
+       for (i=0; i<*returned; i++) {
+               free_devmode(printers[i].devmode);
+       }
+       SAFE_FREE(printers);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+       else
+               return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 1
+ ********************************************************************/
+
+static WERROR enumprinters_level1( uint32 flags, fstring name,
+                                NEW_BUFFER *buffer, uint32 offered,
+                                uint32 *needed, uint32 *returned)
+{
+       /* Not all the flags are equals */
+
+       if (flags & PRINTER_ENUM_LOCAL)
+               return enum_all_printers_info_1_local(buffer, offered, needed, returned);
+
+       if (flags & PRINTER_ENUM_NAME)
+               return enum_all_printers_info_1_name(name, buffer, offered, needed, returned);
+
+       if (flags & PRINTER_ENUM_REMOTE)
+               return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned);
+
+       if (flags & PRINTER_ENUM_NETWORK)
+               return enum_all_printers_info_1_network(name, buffer, offered, needed, returned);
+
+       return WERR_OK; /* NT4sp5 does that */
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 2
+ ********************************************************************/
+
+static WERROR enumprinters_level2( uint32 flags, fstring servername,
+                                NEW_BUFFER *buffer, uint32 offered,
+                                uint32 *needed, uint32 *returned)
+{
+       char *s = servername;
+
+       if (flags & PRINTER_ENUM_LOCAL) {
+                       return enum_all_printers_info_2(buffer, offered, needed, returned);
+       }
+
+       if (flags & PRINTER_ENUM_NAME) {
+               if ((servername[0] == '\\') && (servername[1] == '\\'))
+                       s = servername + 2;
+               if (is_myname_or_ipaddr(s))
+                       return enum_all_printers_info_2(buffer, offered, needed, returned);
+               else
+                       return WERR_INVALID_NAME;
+       }
+
+       if (flags & PRINTER_ENUM_REMOTE)
+               return WERR_UNKNOWN_LEVEL;
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 5
+ ********************************************************************/
+
+static WERROR enumprinters_level5( uint32 flags, fstring servername,
+                                NEW_BUFFER *buffer, uint32 offered,
+                                uint32 *needed, uint32 *returned)
+{
+/*     return enum_all_printers_info_5(buffer, offered, needed, returned);*/
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+WERROR _spoolss_enumprinters( pipes_struct *p, SPOOL_Q_ENUMPRINTERS *q_u, SPOOL_R_ENUMPRINTERS *r_u)
+{
+       uint32 flags = q_u->flags;
+       UNISTR2 *servername = &q_u->servername;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       fstring name;
+       
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_enumprinters\n"));
+
+       *needed=0;
+       *returned=0;
+       
+       /*
+        * Level 1:
+        *          flags==PRINTER_ENUM_NAME
+        *           if name=="" then enumerates all printers
+        *           if name!="" then enumerate the printer
+        *          flags==PRINTER_ENUM_REMOTE
+        *          name is NULL, enumerate printers
+        * Level 2: name!="" enumerates printers, name can't be NULL
+        * Level 3: doesn't exist
+        * Level 4: does a local registry lookup
+        * Level 5: same as Level 2
+        */
+
+       unistr2_to_ascii(name, servername, sizeof(name)-1);
+       strupper(name);
+
+       switch (level) {
+       case 1:
+               return enumprinters_level1(flags, name, buffer, offered, needed, returned);
+       case 2:
+               return enumprinters_level2(flags, name, buffer, offered, needed, returned);
+       case 5:
+               return enumprinters_level5(flags, name, buffer, offered, needed, returned);
+       case 3:
+       case 4:
+               break;
+       }
+       return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_0(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_0 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_0*)malloc(sizeof(PRINTER_INFO_0))) == NULL)
+               return WERR_NOMEM;
+
+       construct_printer_info_0(print_hnd, printer, snum);
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_0(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_0("", buffer, printer, 0);  
+       
+       /* clear memory */
+       SAFE_FREE(printer);
+
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_1(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_1 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_1*)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+               return WERR_NOMEM;
+
+       construct_printer_info_1(print_hnd, PRINTER_ENUM_ICON8, printer, snum);
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_1(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_1("", buffer, printer, 0);  
+       
+       /* clear memory */
+       SAFE_FREE(printer);
+
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_2(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_2 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_2*)malloc(sizeof(PRINTER_INFO_2)))==NULL)
+               return WERR_NOMEM;
+       
+       construct_printer_info_2(print_hnd, printer, snum);
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_2(printer);
+       
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_info_2(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       if (!smb_io_printer_info_2("", buffer, printer, 0)) {
+               free_printer_info_2(printer);
+               return WERR_NOMEM;
+       }
+       
+       /* clear memory */
+       free_printer_info_2(printer);
+
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_3(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_3 *printer=NULL;
+
+       if (!construct_printer_info_3(print_hnd, &printer, snum))
+               return WERR_NOMEM;
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_3(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_info_3(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_3("", buffer, printer, 0);  
+       
+       /* clear memory */
+       free_printer_info_3(printer);
+       
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_4(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_4 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_4*)malloc(sizeof(PRINTER_INFO_4)))==NULL)
+               return WERR_NOMEM;
+
+       if (!construct_printer_info_4(print_hnd, printer, snum))
+               return WERR_NOMEM;
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_4(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_info_4(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_4("", buffer, printer, 0);  
+       
+       /* clear memory */
+       free_printer_info_4(printer);
+       
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_5(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_5 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_5*)malloc(sizeof(PRINTER_INFO_5)))==NULL)
+               return WERR_NOMEM;
+
+       if (!construct_printer_info_5(print_hnd, printer, snum))
+               return WERR_NOMEM;
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_5(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_info_5(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_5("", buffer, printer, 0);  
+       
+       /* clear memory */
+       free_printer_info_5(printer);
+       
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+static WERROR getprinter_level_7(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       PRINTER_INFO_7 *printer=NULL;
+
+       if((printer=(PRINTER_INFO_7*)malloc(sizeof(PRINTER_INFO_7)))==NULL)
+               return WERR_NOMEM;
+
+       if (!construct_printer_info_7(print_hnd, printer, snum))
+               return WERR_NOMEM;
+       
+       /* check the required size. */  
+       *needed += spoolss_size_printer_info_7(printer);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_info_7(printer);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_info_7("", buffer, printer, 0);  
+       
+       /* clear memory */
+       free_printer_info_7(printer);
+       
+       if (*needed > offered) {
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK; 
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinter(pipes_struct *p, SPOOL_Q_GETPRINTER *q_u, SPOOL_R_GETPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+       int snum;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       *needed=0;
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       switch (level) {
+       case 0:
+               return getprinter_level_0(Printer, snum, buffer, offered, needed);
+       case 1:
+               return getprinter_level_1(Printer, snum, buffer, offered, needed);
+       case 2:         
+               return getprinter_level_2(Printer, snum, buffer, offered, needed);
+       case 3:         
+               return getprinter_level_3(Printer, snum, buffer, offered, needed);
+       case 4:         
+               return getprinter_level_4(Printer, snum, buffer, offered, needed);
+       case 5:         
+               return getprinter_level_5(Printer, snum, buffer, offered, needed);
+       case 7:
+               return getprinter_level_7(Printer, snum, buffer, offered, needed);
+       }
+       return WERR_UNKNOWN_LEVEL;
+}      
+               
+/********************************************************************
+ * fill a DRIVER_INFO_1 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_1(DRIVER_INFO_1 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername, fstring architecture)
+{
+       init_unistr( &info->name, driver.info_3->name);
+}
+
+/********************************************************************
+ * construct_printer_driver_info_1
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_1(DRIVER_INFO_1 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{      
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+       ZERO_STRUCT(driver);
+
+       if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+               return WERR_INVALID_PRINTER_NAME;
+
+       if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+               return WERR_UNKNOWN_PRINTER_DRIVER;
+
+       fill_printer_driver_info_1(info, driver, servername, architecture);
+
+       free_a_printer(&printer,2);
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_2(DRIVER_INFO_2 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+       pstring temp;
+
+       info->version=driver.info_3->cversion;
+
+       init_unistr( &info->name, driver.info_3->name );
+       init_unistr( &info->architecture, driver.info_3->environment );
+
+
+    if (strlen(driver.info_3->driverpath)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+               init_unistr( &info->driverpath, temp );
+    } else
+        init_unistr( &info->driverpath, "" );
+
+       if (strlen(driver.info_3->datafile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+               init_unistr( &info->datafile, temp );
+       } else
+               init_unistr( &info->datafile, "" );
+       
+       if (strlen(driver.info_3->configfile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+               init_unistr( &info->configfile, temp ); 
+       } else
+               init_unistr( &info->configfile, "" );
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_2(DRIVER_INFO_2 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+       ZERO_STRUCT(printer);
+       ZERO_STRUCT(driver);
+
+       if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+               return WERR_INVALID_PRINTER_NAME;
+
+       if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+               return WERR_UNKNOWN_PRINTER_DRIVER;
+
+       fill_printer_driver_info_2(info, driver, servername);
+
+       free_a_printer(&printer,2);
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * copy a strings array and convert to UNICODE
+ *
+ * convert an array of ascii string to a UNICODE string
+ ********************************************************************/
+
+static uint32 init_unistr_array(uint16 **uni_array, fstring *char_array, const char *servername)
+{
+       int i=0;
+       int j=0;
+       const char *v;
+       pstring line;
+       uint16 *tuary;
+
+       DEBUG(6,("init_unistr_array\n"));
+       *uni_array=NULL;
+
+       while (True) 
+       {
+               if ( !char_array )
+                       v = "";
+               else 
+               {
+                       v = char_array[i];
+                       if (!v) 
+                               v = ""; /* hack to handle null lists */
+               }
+               
+               /* hack to allow this to be used in places other than when generating 
+                  the list of dependent files */
+                  
+               if ( servername )
+                       slprintf( line, sizeof(line)-1, "\\\\%s%s", servername, v );
+               else
+                       pstrcpy( line, v );
+                       
+               DEBUGADD(6,("%d:%s:%d\n", i, line, strlen(line)));
+
+               /* add one extra unit16 for the second terminating NULL */
+               
+               if ( (tuary=Realloc(*uni_array, (j+1+strlen(line)+2)*sizeof(uint16))) == NULL ) {
+                       DEBUG(2,("init_unistr_array: Realloc error\n" ));
+                       return 0;
+               } else
+                       *uni_array = tuary;
+                       
+               if ( !strlen(v) ) 
+                       break;
+               
+               j += (rpcstr_push((*uni_array+j), line, sizeof(uint16)*strlen(line)+2, STR_TERMINATE) / sizeof(uint16));
+               i++;
+       }
+       
+       if (*uni_array) {
+               /* special case for ""; we need to add both NULL's here */
+               if (!j)
+                       (*uni_array)[j++]=0x0000;       
+               (*uni_array)[j]=0x0000;
+       }
+       
+       DEBUGADD(6,("last one:done\n"));
+
+       /* return size of array in uint16's */
+               
+       return j+1;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_3(DRIVER_INFO_3 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+       pstring temp;
+
+       ZERO_STRUCTP(info);
+
+       info->version=driver.info_3->cversion;
+
+       init_unistr( &info->name, driver.info_3->name );        
+       init_unistr( &info->architecture, driver.info_3->environment );
+
+       if (strlen(driver.info_3->driverpath)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);              
+               init_unistr( &info->driverpath, temp );
+       } else
+               init_unistr( &info->driverpath, "" );
+    
+       if (strlen(driver.info_3->datafile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+               init_unistr( &info->datafile, temp );
+       } else
+               init_unistr( &info->datafile, "" );
+
+       if (strlen(driver.info_3->configfile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+               init_unistr( &info->configfile, temp ); 
+       } else
+               init_unistr( &info->configfile, "" );
+
+       if (strlen(driver.info_3->helpfile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+               init_unistr( &info->helpfile, temp );
+       } else
+               init_unistr( &info->helpfile, "" );
+
+       init_unistr( &info->monitorname, driver.info_3->monitorname );
+       init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+       info->dependentfiles=NULL;
+       init_unistr_array(&info->dependentfiles, driver.info_3->dependentfiles, servername);
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_3(DRIVER_INFO_3 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{      
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+       WERROR status;
+       ZERO_STRUCT(driver);
+
+       status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+       DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+       if (!W_ERROR_IS_OK(status))
+               return WERR_INVALID_PRINTER_NAME;
+
+       status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);    
+       DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+
+#if 0  /* JERRY */
+
+       /* 
+        * I put this code in during testing.  Helpful when commenting out the 
+        * support for DRIVER_INFO_6 in regards to win2k.  Not needed in general
+        * as win2k always queries the driver using an infor level of 6.
+        * I've left it in (but ifdef'd out) because I'll probably
+        * use it in experimentation again in the future.   --jerry 22/01/2002
+        */
+
+       if (!W_ERROR_IS_OK(status)) {
+               /*
+                * Is this a W2k client ?
+                */
+               if (version == 3) {
+                       /* Yes - try again with a WinNT driver. */
+                       version = 2;
+                       status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);    
+                       DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+               }
+#endif
+
+               if (!W_ERROR_IS_OK(status)) {
+                       free_a_printer(&printer,2);
+                       return WERR_UNKNOWN_PRINTER_DRIVER;
+               }
+               
+#if 0  /* JERRY */
+       }
+#endif
+       
+
+       fill_printer_driver_info_3(info, driver, servername);
+
+       free_a_printer(&printer,2);
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct - we know that driver is really level 3. This sucks. JRA.
+ ********************************************************************/
+
+static void fill_printer_driver_info_6(DRIVER_INFO_6 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+       pstring temp;
+       fstring nullstr;
+
+       ZERO_STRUCTP(info);
+       memset(&nullstr, '\0', sizeof(fstring));
+
+       info->version=driver.info_3->cversion;
+
+       init_unistr( &info->name, driver.info_3->name );        
+       init_unistr( &info->architecture, driver.info_3->environment );
+
+       if (strlen(driver.info_3->driverpath)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);              
+               init_unistr( &info->driverpath, temp );
+       } else
+               init_unistr( &info->driverpath, "" );
+
+       if (strlen(driver.info_3->datafile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+               init_unistr( &info->datafile, temp );
+       } else
+               init_unistr( &info->datafile, "" );
+
+       if (strlen(driver.info_3->configfile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+               init_unistr( &info->configfile, temp ); 
+       } else
+               init_unistr( &info->configfile, "" );
+
+       if (strlen(driver.info_3->helpfile)) {
+               slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+               init_unistr( &info->helpfile, temp );
+       } else
+               init_unistr( &info->helpfile, "" );
+       
+       init_unistr( &info->monitorname, driver.info_3->monitorname );
+       init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+       info->dependentfiles = NULL;
+       init_unistr_array( &info->dependentfiles, driver.info_3->dependentfiles, servername );
+
+       info->previousdrivernames=NULL;
+       init_unistr_array(&info->previousdrivernames, &nullstr, servername);
+
+       info->driver_date.low=0;
+       info->driver_date.high=0;
+
+       info->padding=0;
+       info->driver_version_low=0;
+       info->driver_version_high=0;
+
+       init_unistr( &info->mfgname, "");
+       init_unistr( &info->oem_url, "");
+       init_unistr( &info->hardware_id, "");
+       init_unistr( &info->provider, "");
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_6(DRIVER_INFO_6 *info, int snum, 
+              fstring servername, fstring architecture, uint32 version)
+{      
+       NT_PRINTER_INFO_LEVEL           *printer = NULL;
+       NT_PRINTER_DRIVER_INFO_LEVEL    driver;
+       WERROR                          status;
+       
+       ZERO_STRUCT(driver);
+
+       status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+       
+       DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+       
+       if (!W_ERROR_IS_OK(status))
+               return WERR_INVALID_PRINTER_NAME;
+
+       status = get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+               
+       DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+       
+       if (!W_ERROR_IS_OK(status)) 
+       {
+               /*
+                * Is this a W2k client ?
+                */
+
+               if (version < 3) {
+                       free_a_printer(&printer,2);
+                       return WERR_UNKNOWN_PRINTER_DRIVER;
+               }
+
+               /* Yes - try again with a WinNT driver. */
+               version = 2;
+               status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);    
+               DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+               if (!W_ERROR_IS_OK(status)) {
+                       free_a_printer(&printer,2);
+                       return WERR_UNKNOWN_PRINTER_DRIVER;
+               }
+       }
+
+       fill_printer_driver_info_6(info, driver, servername);
+
+       free_a_printer(&printer,2);
+       free_a_printer_driver(driver, 3);
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+       SAFE_FREE(info->dependentfiles);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+       SAFE_FREE(info->dependentfiles);
+       
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level1(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       DRIVER_INFO_1 *info=NULL;
+       WERROR status;
+       
+       if((info=(DRIVER_INFO_1 *)malloc(sizeof(DRIVER_INFO_1))) == NULL)
+               return WERR_NOMEM;
+       
+       status=construct_printer_driver_info_1(info, snum, servername, architecture, version);
+       if (!W_ERROR_IS_OK(status)) {
+               SAFE_FREE(info);
+               return status;
+       }
+
+       /* check the required size. */  
+       *needed += spoolss_size_printer_driver_info_1(info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_driver_info_1("", buffer, info, 0);      
+
+       /* clear memory */
+       SAFE_FREE(info);
+
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level2(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       DRIVER_INFO_2 *info=NULL;
+       WERROR status;
+       
+       if((info=(DRIVER_INFO_2 *)malloc(sizeof(DRIVER_INFO_2))) == NULL)
+               return WERR_NOMEM;
+       
+       status=construct_printer_driver_info_2(info, snum, servername, architecture, version);
+       if (!W_ERROR_IS_OK(status)) {
+               SAFE_FREE(info);
+               return status;
+       }
+
+       /* check the required size. */  
+       *needed += spoolss_size_printer_driver_info_2(info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_driver_info_2("", buffer, info, 0);      
+
+       /* clear memory */
+       SAFE_FREE(info);
+
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+       
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level3(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       DRIVER_INFO_3 info;
+       WERROR status;
+
+       ZERO_STRUCT(info);
+
+       status=construct_printer_driver_info_3(&info, snum, servername, architecture, version);
+       if (!W_ERROR_IS_OK(status)) {
+               return status;
+       }
+
+       /* check the required size. */  
+       *needed += spoolss_size_printer_driver_info_3(&info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_driver_info_3(&info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_driver_info_3("", buffer, &info, 0);
+
+       free_printer_driver_info_3(&info);
+
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level6(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       DRIVER_INFO_6 info;
+       WERROR status;
+
+       ZERO_STRUCT(info);
+
+       status=construct_printer_driver_info_6(&info, snum, servername, architecture, version);
+       if (!W_ERROR_IS_OK(status)) {
+               return status;
+       }
+
+       /* check the required size. */  
+       *needed += spoolss_size_printer_driver_info_6(&info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               free_printer_driver_info_6(&info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       smb_io_printer_driver_info_6("", buffer, &info, 0);
+
+       free_printer_driver_info_6(&info);
+
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+       
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriver2(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVER2 *q_u, SPOOL_R_GETPRINTERDRIVER2 *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       UNISTR2 *uni_arch = &q_u->architecture;
+       uint32 level = q_u->level;
+       uint32 clientmajorversion = q_u->clientmajorversion;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *servermajorversion = &r_u->servermajorversion;
+       uint32 *serverminorversion = &r_u->serverminorversion;
+
+       fstring servername;
+       fstring architecture;
+       int snum;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_getprinterdriver2\n"));
+
+       *needed = 0;
+       *servermajorversion = 0;
+       *serverminorversion = 0;
+
+       fstrcpy(servername, get_called_name());
+       unistr2_to_ascii(architecture, uni_arch, sizeof(architecture)-1);
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       switch (level) {
+       case 1:
+               return getprinterdriver2_level1(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+       case 2:
+               return getprinterdriver2_level2(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+       case 3:
+               return getprinterdriver2_level3(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+       case 6:
+               return getprinterdriver2_level6(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+       }
+
+       return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_startpageprinter(pipes_struct *p, SPOOL_Q_STARTPAGEPRINTER *q_u, SPOOL_R_STARTPAGEPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       if (!Printer) {
+               DEBUG(3,("Error in startpageprinter printer handle\n"));
+               return WERR_BADFID;
+       }
+
+       Printer->page_started=True;
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_endpageprinter(pipes_struct *p, SPOOL_Q_ENDPAGEPRINTER *q_u, SPOOL_R_ENDPAGEPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       int snum;
+
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_endpageprinter: Invalid handle (%s:%u:%u).\n",OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+       
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       Printer->page_started=False;
+       print_job_endpage(snum, Printer->jobid);
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_startdocprinter(pipes_struct *p, SPOOL_Q_STARTDOCPRINTER *q_u, SPOOL_R_STARTDOCPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       DOC_INFO *docinfo = &q_u->doc_info_container.docinfo;
+       uint32 *jobid = &r_u->jobid;
+
+       DOC_INFO_1 *info_1 = &docinfo->doc_info_1;
+       int snum;
+       pstring jobname;
+       fstring datatype;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       struct current_user user;
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_startdocprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       get_current_user(&user, p);
+
+       /*
+        * a nice thing with NT is it doesn't listen to what you tell it.
+        * when asked to send _only_ RAW datas, it tries to send datas
+        * in EMF format.
+        *
+        * So I add checks like in NT Server ...
+        */
+       
+       if (info_1->p_datatype != 0) {
+               unistr2_to_ascii(datatype, &info_1->datatype, sizeof(datatype));
+               if (strcmp(datatype, "RAW") != 0) {
+                       (*jobid)=0;
+                       return WERR_INVALID_DATATYPE;
+               }               
+       }               
+       
+       /* get the share number of the printer */
+       if (!get_printer_snum(p, handle, &snum)) {
+               return WERR_BADFID;
+       }
+
+       unistr2_to_ascii(jobname, &info_1->docname, sizeof(jobname));
+       
+       Printer->jobid = print_job_start(&user, snum, jobname, Printer->nt_devmode);
+
+       /* An error occured in print_job_start() so return an appropriate
+          NT error code. */
+
+       if (Printer->jobid == -1) {
+               return map_werror_from_unix(errno);
+       }
+       
+       Printer->document_started=True;
+       (*jobid) = Printer->jobid;
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_enddocprinter(pipes_struct *p, SPOOL_Q_ENDDOCPRINTER *q_u, SPOOL_R_ENDDOCPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+
+       return _spoolss_enddocprinter_internal(p, handle);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_writeprinter(pipes_struct *p, SPOOL_Q_WRITEPRINTER *q_u, SPOOL_R_WRITEPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 buffer_size = q_u->buffer_size;
+       uint8 *buffer = q_u->buffer;
+       uint32 *buffer_written = &q_u->buffer_size2;
+       int snum;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_writeprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+               r_u->buffer_written = q_u->buffer_size2;
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       (*buffer_written) = print_job_write(snum, Printer->jobid, (char *)buffer, buffer_size);
+       if (*buffer_written == -1) {
+               r_u->buffer_written = 0;
+               if (errno == ENOSPC)
+                       return WERR_NO_SPOOL_SPACE;
+               else
+                       return WERR_ACCESS_DENIED;
+       }
+
+       r_u->buffer_written = q_u->buffer_size2;
+
+       return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static WERROR control_printer(POLICY_HND *handle, uint32 command,
+                             pipes_struct *p)
+{
+       struct current_user user;
+       int snum;
+       WERROR errcode = WERR_BADFUNC;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       get_current_user(&user, p);
+
+       if (!Printer) {
+               DEBUG(2,("control_printer: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       switch (command) {
+       case PRINTER_CONTROL_PAUSE:
+               if (print_queue_pause(&user, snum, &errcode)) {
+                       errcode = WERR_OK;
+               }
+               break;
+       case PRINTER_CONTROL_RESUME:
+       case PRINTER_CONTROL_UNPAUSE:
+               if (print_queue_resume(&user, snum, &errcode)) {
+                       errcode = WERR_OK;
+               }
+               break;
+       case PRINTER_CONTROL_PURGE:
+               if (print_queue_purge(&user, snum, &errcode)) {
+                       errcode = WERR_OK;
+               }
+               break;
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+       return errcode;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ * From MSDN: "Deletes printer's spool file if printer is configured
+ * for spooling"
+ ********************************************************************/
+
+WERROR _spoolss_abortprinter(pipes_struct *p, SPOOL_Q_ABORTPRINTER *q_u, SPOOL_R_ABORTPRINTER *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       int             snum;
+       struct          current_user user;
+       WERROR          errcode = WERR_OK;
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_abortprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+       
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+       
+       get_current_user( &user, p );   
+       
+       print_job_delete( &user, snum, Printer->jobid, &errcode );      
+       
+       return errcode;
+}
+
+/********************************************************************
+ * called by spoolss_api_setprinter
+ * when updating a printer description
+ ********************************************************************/
+
+static WERROR update_printer_sec(POLICY_HND *handle, uint32 level,
+                                const SPOOL_PRINTER_INFO_LEVEL *info,
+                                pipes_struct *p, SEC_DESC_BUF *secdesc_ctr)
+{
+       SEC_DESC_BUF *new_secdesc_ctr = NULL, *old_secdesc_ctr = NULL;
+       struct current_user user;
+       WERROR result;
+       int snum;
+
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       if (!Printer || !get_printer_snum(p, handle, &snum)) {
+               DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+                        OUR_HANDLE(handle)));
+
+               result = WERR_BADFID;
+               goto done;
+       }
+
+       /* NT seems to like setting the security descriptor even though
+          nothing may have actually changed.  This causes annoying
+          dialog boxes when the user doesn't have permission to change
+          the security descriptor. */
+
+       nt_printing_getsec(p->mem_ctx, Printer->dev.handlename, &old_secdesc_ctr);
+
+       if (DEBUGLEVEL >= 10) {
+               SEC_ACL *the_acl;
+               int i;
+
+               the_acl = old_secdesc_ctr->sec->dacl;
+               DEBUG(10, ("old_secdesc_ctr for %s has %d aces:\n", 
+                          PRINTERNAME(snum), the_acl->num_aces));
+
+               for (i = 0; i < the_acl->num_aces; i++) {
+                       fstring sid_str;
+
+                       sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+                       DEBUG(10, ("%s 0x%08x\n", sid_str, 
+                                 the_acl->ace[i].info.mask));
+               }
+
+               the_acl = secdesc_ctr->sec->dacl;
+
+               if (the_acl) {
+                       DEBUG(10, ("secdesc_ctr for %s has %d aces:\n", 
+                                  PRINTERNAME(snum), the_acl->num_aces));
+
+                       for (i = 0; i < the_acl->num_aces; i++) {
+                               fstring sid_str;
+                               
+                               sid_to_string(sid_str, &the_acl->ace[i].trustee);
+                               
+                               DEBUG(10, ("%s 0x%08x\n", sid_str, 
+                                          the_acl->ace[i].info.mask));
+                       }
+               } else {
+                       DEBUG(10, ("dacl for secdesc_ctr is NULL\n"));
+               }
+       }
+
+       new_secdesc_ctr = sec_desc_merge(p->mem_ctx, secdesc_ctr, old_secdesc_ctr);
+
+       if (sec_desc_equal(new_secdesc_ctr->sec, old_secdesc_ctr->sec)) {
+               result = WERR_OK;
+               goto done;
+       }
+
+       /* Work out which user is performing the operation */
+
+       get_current_user(&user, p);
+
+       /* Check the user has permissions to change the security
+          descriptor.  By experimentation with two NT machines, the user
+          requires Full Access to the printer to change security
+          information. */
+
+       if (!print_access_check(&user, snum, PRINTER_ACCESS_ADMINISTER)) {
+               result = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       result = nt_printing_setsec(Printer->dev.handlename, new_secdesc_ctr);
+
+ done:
+
+       return result;
+}
+
+/********************************************************************
+ Do Samba sanity checks on a printer info struct.
+ this has changed purpose: it now "canonicalises" printer
+ info from a client rather than just checking it is correct
+ ********************************************************************/
+
+static BOOL check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, int snum)
+{
+       DEBUG(5,("check_printer_ok: servername=%s printername=%s sharename=%s portname=%s drivername=%s comment=%s location=%s\n",
+                info->servername, info->printername, info->sharename, info->portname, info->drivername, info->comment, info->location));
+
+       /* we force some elements to "correct" values */
+       slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", get_called_name());
+       fstrcpy(info->sharename, lp_servicename(snum));
+       slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+                get_called_name(), info->sharename);
+       info->attributes = PRINTER_ATTRIBUTE_SAMBA;
+       
+       
+       return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
+{
+       extern userdom_struct current_user_info;
+       char *cmd = lp_addprinter_cmd();
+       char **qlines;
+       pstring command;
+       int numlines;
+       int ret;
+       int fd;
+       fstring remote_machine = "%m";
+
+       standard_sub_basic(current_user_info.smb_name, remote_machine,sizeof(remote_machine));
+       
+       slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
+                       cmd, printer->info_2->printername, printer->info_2->sharename,
+                       printer->info_2->portname, printer->info_2->drivername,
+                       printer->info_2->location, printer->info_2->comment, remote_machine);
+
+       DEBUG(10,("Running [%s]\n", command));
+       ret = smbrun(command, &fd);
+       DEBUGADD(10,("returned [%d]\n", ret));
+
+       if ( ret != 0 ) {
+               if (fd != -1)
+                       close(fd);
+               return False;
+       }
+
+       numlines = 0;
+       /* Get lines and convert them back to dos-codepage */
+       qlines = fd_lines_load(fd, &numlines);
+       DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+       close(fd);
+
+       if(numlines) {
+               /* Set the portname to what the script says the portname should be. */
+               strncpy(printer->info_2->portname, qlines[0], sizeof(printer->info_2->portname));
+               DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
+
+               /* Send SIGHUP to process group... is there a better way? */
+               kill(0, SIGHUP);
+               
+               /* reload our services immediately */
+               reload_services( False );
+       }
+
+       file_lines_free(qlines);
+       return True;
+}
+
+/********************************************************************
+ * Called by spoolss_api_setprinter
+ * when updating a printer description.
+ ********************************************************************/
+
+static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
+                           const SPOOL_PRINTER_INFO_LEVEL *info,
+                           DEVICEMODE *devmode)
+{
+       int snum;
+       NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       WERROR result;
+       UNISTR2 buffer;
+       fstring asc_buffer;
+
+       DEBUG(8,("update_printer\n"));
+
+       result = WERR_OK;
+
+       if (!Printer) {
+               result = WERR_BADFID;
+               goto done;
+       }
+
+       if (!get_printer_snum(p, handle, &snum)) {
+               result = WERR_BADFID;
+               goto done;
+       }
+
+       if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))) ||
+           (!W_ERROR_IS_OK(get_a_printer(Printer, &old_printer, 2, lp_const_servicename(snum))))) {
+               result = WERR_BADFID;
+               goto done;
+       }
+
+       DEBUGADD(8,("Converting info_2 struct\n"));
+
+       /*
+        * convert_printer_info converts the incoming
+        * info from the client and overwrites the info
+        * just read from the tdb in the pointer 'printer'.
+        */
+
+       if (!convert_printer_info(info, printer, level)) {
+               result =  WERR_NOMEM;
+               goto done;
+       }
+
+       if (devmode) {
+               /* we have a valid devmode
+                  convert it and link it*/
+
+               DEBUGADD(8,("update_printer: Converting the devicemode struct\n"));
+               if (!convert_devicemode(printer->info_2->printername, devmode,
+                               &printer->info_2->devmode)) {
+                       result =  WERR_NOMEM;
+                       goto done;
+               }
+       }
+
+       /* Do sanity check on the requested changes for Samba */
+
+       if (!check_printer_ok(printer->info_2, snum)) {
+               result = WERR_INVALID_PARAM;
+               goto done;
+       }
+
+       /* Check calling user has permission to update printer description */
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+               DEBUG(3, ("update_printer: printer property change denied by handle\n"));
+               result = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       /* Call addprinter hook */
+       /* Check changes to see if this is really needed */
+       
+       if ( *lp_addprinter_cmd() 
+               && (!strequal(printer->info_2->drivername, old_printer->info_2->drivername)
+                       || !strequal(printer->info_2->comment, old_printer->info_2->comment)
+                       || !strequal(printer->info_2->portname, old_printer->info_2->portname)
+                       || !strequal(printer->info_2->location, old_printer->info_2->location)) )
+       {
+               if ( !add_printer_hook(printer) ) {
+                       result = WERR_ACCESS_DENIED;
+                       goto done;
+               }
+
+               /* 
+                * make sure we actually reload the services after 
+                * this as smb.conf could have a new section in it 
+                * .... shouldn't .... but could
+                */
+               reload_services(False); 
+       }
+       
+       /*
+        * When a *new* driver is bound to a printer, the drivername is used to
+        * lookup previously saved driver initialization info, which is then
+        * bound to the printer, simulating what happens in the Windows arch.
+        */
+       if (!strequal(printer->info_2->drivername, old_printer->info_2->drivername))
+       {
+               if (!set_driver_init(printer, 2)) 
+               {
+                       DEBUG(5,("update_printer: Error restoring driver initialization data for driver [%s]!\n",
+                               printer->info_2->drivername));
+               }
+               
+               DEBUG(10,("update_printer: changing driver [%s]!  Sending event!\n",
+                       printer->info_2->drivername));
+                       
+               notify_printer_driver(snum, printer->info_2->drivername);
+       }
+
+       /* 
+        * flag which changes actually occured.  This is a small subset of 
+        * all the possible changes.  We also have to update things in the 
+        * DsSpooler key.
+        */
+
+       if (!strequal(printer->info_2->comment, old_printer->info_2->comment)) {
+               init_unistr2( &buffer, printer->info_2->comment, strlen(printer->info_2->comment)+1 );
+               set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "description",
+                       REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+               notify_printer_comment(snum, printer->info_2->comment);
+       }
+
+       if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename)) {
+               init_unistr2( &buffer, printer->info_2->sharename, strlen(printer->info_2->sharename)+1 );
+               set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "printerName",
+                       REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+               set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shareName",
+                       REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+               notify_printer_sharename(snum, printer->info_2->sharename);
+       }
+
+       if (!strequal(printer->info_2->portname, old_printer->info_2->portname)) {
+               init_unistr2( &buffer, printer->info_2->portname, strlen(printer->info_2->portname)+1 );
+               set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "portName",
+                       REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+               notify_printer_port(snum, printer->info_2->portname);
+       }
+
+       if (!strequal(printer->info_2->location, old_printer->info_2->location)) {
+               init_unistr2( &buffer, printer->info_2->location, strlen(printer->info_2->location)+1 );
+               set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "location",
+                       REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+               notify_printer_location(snum, printer->info_2->location);
+       }
+       
+       /* here we need to update some more DsSpooler keys */
+       /* uNCName, serverName, shortServerName */
+       
+       init_unistr2( &buffer, lp_netbios_name(), strlen(lp_netbios_name())+1 );
+       set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "serverName",
+               REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+       set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shortServerName",
+               REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+       slprintf( asc_buffer, sizeof(asc_buffer)-1, "\\\\%s\\%s",
+                 lp_netbios_name(), printer->info_2->sharename );
+       init_unistr2( &buffer, asc_buffer, strlen(asc_buffer)+1 );
+       set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "uNCName",
+               REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+       /* Update printer info */
+       result = mod_a_printer(*printer, 2);
+
+done:
+       free_a_printer(&printer, 2);
+       free_a_printer(&old_printer, 2);
+
+
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR publish_or_unpublish_printer(pipes_struct *p, POLICY_HND *handle,
+                                  const SPOOL_PRINTER_INFO_LEVEL *info)
+{
+#ifdef HAVE_ADS
+       SPOOL_PRINTER_INFO_LEVEL_7 *info7 = info->info_7;
+       int snum;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       WERROR result;
+
+       DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action));
+
+       result = WERR_OK;
+
+       if (!Printer)
+               return WERR_BADFID;
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+       
+       nt_printer_publish(Printer, snum, info7->action);
+       
+       return WERR_OK;
+#else
+       return WERR_UNKNOWN_LEVEL;
+#endif
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SETPRINTER *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 level = q_u->level;
+       SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+       DEVMODE_CTR devmode_ctr = q_u->devmode_ctr;
+       SEC_DESC_BUF *secdesc_ctr = q_u->secdesc_ctr;
+       uint32 command = q_u->command;
+
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_setprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       /* check the level */   
+       switch (level) {
+               case 0:
+                       return control_printer(handle, command, p);
+               case 2:
+                       return update_printer(p, handle, level, info, devmode_ctr.devmode);
+               case 3:
+                       return update_printer_sec(handle, level, info, p,
+                                                 secdesc_ctr);
+               case 7:
+                       return publish_or_unpublish_printer(p, handle, info);
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       Printer_entry *Printer= find_printer_index_by_hnd(p, handle);
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_fcpn: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (Printer->notify.client_connected==True) {
+               int snum = -1;
+
+               if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+                       snum = -1;
+               else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+                               !get_printer_snum(p, handle, &snum) )
+                       return WERR_BADFID;
+
+               srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+       }
+
+       Printer->notify.flags=0;
+       Printer->notify.options=0;
+       Printer->notify.localmachine[0]='\0';
+       Printer->notify.printerlocal=0;
+       if (Printer->notify.option)
+               free_spool_notify_option(&Printer->notify.option);
+       Printer->notify.client_connected=False;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addjob(pipes_struct *p, SPOOL_Q_ADDJOB *q_u, SPOOL_R_ADDJOB *r_u)
+{
+       /* that's an [in out] buffer (despite appearences to the contrary) */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+
+       r_u->needed = 0;
+       return WERR_INVALID_PARAM; /* this is what a NT server
+                                           returns for AddJob. AddJob
+                                           must fail on non-local
+                                           printers */
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_job_info_1(JOB_INFO_1 *job_info, print_queue_struct *queue,
+                            int position, int snum)
+{
+       pstring temp_name;
+       
+       struct tm *t;
+       
+       t=gmtime(&queue->time);
+       slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+       job_info->jobid=queue->job;     
+       init_unistr(&job_info->printername, lp_servicename(snum));
+       init_unistr(&job_info->machinename, temp_name);
+       init_unistr(&job_info->username, queue->fs_user);
+       init_unistr(&job_info->document, queue->fs_file);
+       init_unistr(&job_info->datatype, "RAW");
+       init_unistr(&job_info->text_status, "");
+       job_info->status=nt_printj_status(queue->status);
+       job_info->priority=queue->priority;
+       job_info->position=position;
+       job_info->totalpages=queue->page_count;
+       job_info->pagesprinted=0;
+
+       make_systemtime(&job_info->submitted, t);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL fill_job_info_2(JOB_INFO_2 *job_info, print_queue_struct *queue,
+                            int position, int snum, 
+                           NT_PRINTER_INFO_LEVEL *ntprinter,
+                           DEVICEMODE *devmode)
+{
+       pstring temp_name;
+       struct tm *t;
+
+       t=gmtime(&queue->time);
+       slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+       job_info->jobid=queue->job;
+       
+       init_unistr(&job_info->printername, ntprinter->info_2->printername);
+       
+       init_unistr(&job_info->machinename, temp_name);
+       init_unistr(&job_info->username, queue->fs_user);
+       init_unistr(&job_info->document, queue->fs_file);
+       init_unistr(&job_info->notifyname, queue->fs_user);
+       init_unistr(&job_info->datatype, "RAW");
+       init_unistr(&job_info->printprocessor, "winprint");
+       init_unistr(&job_info->parameters, "");
+       init_unistr(&job_info->drivername, ntprinter->info_2->drivername);
+       init_unistr(&job_info->text_status, "");
+       
+/* and here the security descriptor */
+
+       job_info->status=nt_printj_status(queue->status);
+       job_info->priority=queue->priority;
+       job_info->position=position;
+       job_info->starttime=0;
+       job_info->untiltime=0;
+       job_info->totalpages=queue->page_count;
+       job_info->size=queue->size;
+       make_systemtime(&(job_info->submitted), t);
+       job_info->timeelapsed=0;
+       job_info->pagesprinted=0;
+
+       job_info->devmode = devmode;
+
+       return (True);
+}
+
+/****************************************************************************
+ Enumjobs at level 1.
+****************************************************************************/
+
+static WERROR enumjobs_level1(print_queue_struct *queue, int snum,
+                             NEW_BUFFER *buffer, uint32 offered,
+                             uint32 *needed, uint32 *returned)
+{
+       JOB_INFO_1 *info;
+       int i;
+       
+       info=(JOB_INFO_1 *)malloc(*returned*sizeof(JOB_INFO_1));
+       if (info==NULL) {
+               SAFE_FREE(queue);
+               *returned=0;
+               return WERR_NOMEM;
+       }
+       
+       for (i=0; i<*returned; i++)
+               fill_job_info_1(&info[i], &queue[i], i, snum);
+
+       SAFE_FREE(queue);
+
+       /* check the required size. */  
+       for (i=0; i<*returned; i++)
+               (*needed) += spoolss_size_job_info_1(&info[i]);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the structures */
+       for (i=0; i<*returned; i++)
+               smb_io_job_info_1("", buffer, &info[i], 0);     
+
+       /* clear memory */
+       SAFE_FREE(info);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Enumjobs at level 2.
+****************************************************************************/
+
+static WERROR enumjobs_level2(print_queue_struct *queue, int snum,
+                             NEW_BUFFER *buffer, uint32 offered,
+                             uint32 *needed, uint32 *returned)
+{
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+       JOB_INFO_2 *info = NULL;
+       int i;
+       WERROR result;
+       DEVICEMODE *devmode = NULL;
+       
+       info=(JOB_INFO_2 *)malloc(*returned*sizeof(JOB_INFO_2));
+       if (info==NULL) {
+               *returned=0;
+               result = WERR_NOMEM;
+               goto done;
+       }
+
+       result = get_a_printer(NULL, &ntprinter, 2, lp_servicename(snum));
+       if (!W_ERROR_IS_OK(result)) {
+               *returned = 0;
+               goto done;
+       }
+               
+       /* this should not be a failure condition if the devmode is NULL */
+       
+       devmode = construct_dev_mode(snum);
+
+       for (i=0; i<*returned; i++)
+               fill_job_info_2(&(info[i]), &queue[i], i, snum, ntprinter,
+                               devmode);
+
+       free_a_printer(&ntprinter, 2);
+       SAFE_FREE(queue);
+
+       /* check the required size. */  
+       for (i=0; i<*returned; i++)
+               (*needed) += spoolss_size_job_info_2(&info[i]);
+
+       if (*needed > offered) {
+               *returned=0;
+               result = WERR_INSUFFICIENT_BUFFER;
+               goto done;
+       }
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info);
+               result = WERR_INSUFFICIENT_BUFFER;
+               goto done;
+       }
+
+       /* fill the buffer with the structures */
+       for (i=0; i<*returned; i++)
+               smb_io_job_info_2("", buffer, &info[i], 0);     
+
+       result = WERR_OK;
+
+ done:
+       free_a_printer(&ntprinter, 2);
+       free_devmode(devmode);
+       SAFE_FREE(queue);
+       SAFE_FREE(info);
+
+       return result;
+
+}
+
+/****************************************************************************
+ Enumjobs.
+****************************************************************************/
+
+WERROR _spoolss_enumjobs( pipes_struct *p, SPOOL_Q_ENUMJOBS *q_u, SPOOL_R_ENUMJOBS *r_u)
+{      
+       POLICY_HND *handle = &q_u->handle;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       int snum;
+       print_status_struct prt_status;
+       print_queue_struct *queue=NULL;
+       int max_rep_jobs;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_enumjobs\n"));
+
+       *needed=0;
+       *returned=0;
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       max_rep_jobs = lp_max_reported_jobs(snum);
+
+       *returned = print_queue_status(snum, &queue, &prt_status);
+       DEBUGADD(4,("count:[%d], status:[%d], [%s]\n", *returned, prt_status.status, prt_status.message));
+
+       if (*returned == 0) {
+               SAFE_FREE(queue);
+               return WERR_OK;
+       }
+
+       if (max_rep_jobs && (*returned > max_rep_jobs))
+               *returned = max_rep_jobs;
+
+       switch (level) {
+       case 1:
+               return enumjobs_level1(queue, snum, buffer, offered, needed, returned);
+       case 2:
+               return enumjobs_level2(queue, snum, buffer, offered, needed, returned);
+       default:
+               SAFE_FREE(queue);
+               *returned=0;
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_schedulejob( pipes_struct *p, SPOOL_Q_SCHEDULEJOB *q_u, SPOOL_R_SCHEDULEJOB *r_u)
+{
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setjob(pipes_struct *p, SPOOL_Q_SETJOB *q_u, SPOOL_R_SETJOB *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 jobid = q_u->jobid;
+       uint32 command = q_u->command;
+
+       struct current_user user;
+       int snum;
+       WERROR errcode = WERR_BADFUNC;
+               
+       if (!get_printer_snum(p, handle, &snum)) {
+               return WERR_BADFID;
+       }
+
+       if (!print_job_exists(snum, jobid)) {
+               return WERR_INVALID_PRINTER_NAME;
+       }
+
+       get_current_user(&user, p);     
+
+       switch (command) {
+       case JOB_CONTROL_CANCEL:
+       case JOB_CONTROL_DELETE:
+               if (print_job_delete(&user, snum, jobid, &errcode)) {
+                       errcode = WERR_OK;
+               }
+               break;
+       case JOB_CONTROL_PAUSE:
+               if (print_job_pause(&user, snum, jobid, &errcode)) {
+                       errcode = WERR_OK;
+               }               
+               break;
+       case JOB_CONTROL_RESTART:
+       case JOB_CONTROL_RESUME:
+               if (print_job_resume(&user, snum, jobid, &errcode)) {
+                       errcode = WERR_OK;
+               }
+               break;
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+       return errcode;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 1.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level1(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       int i;
+       int ndrivers;
+       uint32 version;
+       fstring *list = NULL;
+
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+       DRIVER_INFO_1 *tdi1, *driver_info_1=NULL;
+
+       *returned=0;
+
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
+               list=NULL;
+               ndrivers=get_ntdrivers(&list, architecture, version);
+               DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+               if(ndrivers == -1)
+                       return WERR_NOMEM;
+
+               if(ndrivers != 0) {
+                       if((tdi1=(DRIVER_INFO_1 *)Realloc(driver_info_1, (*returned+ndrivers) * sizeof(DRIVER_INFO_1))) == NULL) {
+                               DEBUG(0,("enumprinterdrivers_level1: failed to enlarge driver info buffer!\n"));
+                               SAFE_FREE(driver_info_1);
+                               SAFE_FREE(list);
+                               return WERR_NOMEM;
+                       }
+                       else driver_info_1 = tdi1;
+               }
+
+               for (i=0; i<ndrivers; i++) {
+                       WERROR status;
+                       DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+                       ZERO_STRUCT(driver);
+                       status = get_a_printer_driver(&driver, 3, list[i], 
+                                                     architecture, version);
+                       if (!W_ERROR_IS_OK(status)) {
+                               SAFE_FREE(list);
+                               return status;
+                       }
+                       fill_printer_driver_info_1(&driver_info_1[*returned+i], driver, servername, architecture );             
+                       free_a_printer_driver(driver, 3);
+               }       
+
+               *returned+=ndrivers;
+               SAFE_FREE(list);
+       }
+       
+       /* check the required size. */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d]'s size\n",i));
+               *needed += spoolss_size_printer_driver_info_1(&driver_info_1[i]);
+       }
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(driver_info_1);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the driver structures */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+               smb_io_printer_driver_info_1("", buffer, &driver_info_1[i], 0);
+       }
+
+       SAFE_FREE(driver_info_1);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 2.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level2(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       int i;
+       int ndrivers;
+       uint32 version;
+       fstring *list = NULL;
+
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+       DRIVER_INFO_2 *tdi2, *driver_info_2=NULL;
+
+       *returned=0;
+
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
+               list=NULL;
+               ndrivers=get_ntdrivers(&list, architecture, version);
+               DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+               if(ndrivers == -1)
+                       return WERR_NOMEM;
+
+               if(ndrivers != 0) {
+                       if((tdi2=(DRIVER_INFO_2 *)Realloc(driver_info_2, (*returned+ndrivers) * sizeof(DRIVER_INFO_2))) == NULL) {
+                               DEBUG(0,("enumprinterdrivers_level2: failed to enlarge driver info buffer!\n"));
+                               SAFE_FREE(driver_info_2);
+                               SAFE_FREE(list);
+                               return WERR_NOMEM;
+                       }
+                       else driver_info_2 = tdi2;
+               }
+               
+               for (i=0; i<ndrivers; i++) {
+                       WERROR status;
+
+                       DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+                       ZERO_STRUCT(driver);
+                       status = get_a_printer_driver(&driver, 3, list[i], 
+                                                     architecture, version);
+                       if (!W_ERROR_IS_OK(status)) {
+                               SAFE_FREE(list);
+                               return status;
+                       }
+                       fill_printer_driver_info_2(&driver_info_2[*returned+i], driver, servername);            
+                       free_a_printer_driver(driver, 3);
+               }       
+
+               *returned+=ndrivers;
+               SAFE_FREE(list);
+       }
+       
+       /* check the required size. */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d]'s size\n",i));
+               *needed += spoolss_size_printer_driver_info_2(&(driver_info_2[i]));
+       }
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(driver_info_2);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the form structures */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+               smb_io_printer_driver_info_2("", buffer, &(driver_info_2[i]), 0);
+       }
+
+       SAFE_FREE(driver_info_2);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 3.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level3(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       int i;
+       int ndrivers;
+       uint32 version;
+       fstring *list = NULL;
+
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+       DRIVER_INFO_3 *tdi3, *driver_info_3=NULL;
+
+       *returned=0;
+
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
+               list=NULL;
+               ndrivers=get_ntdrivers(&list, architecture, version);
+               DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+               if(ndrivers == -1)
+                       return WERR_NOMEM;
+
+               if(ndrivers != 0) {
+                       if((tdi3=(DRIVER_INFO_3 *)Realloc(driver_info_3, (*returned+ndrivers) * sizeof(DRIVER_INFO_3))) == NULL) {
+                               DEBUG(0,("enumprinterdrivers_level3: failed to enlarge driver info buffer!\n"));
+                               SAFE_FREE(driver_info_3);
+                               SAFE_FREE(list);
+                               return WERR_NOMEM;
+                       }
+                       else driver_info_3 = tdi3;
+               }
+
+               for (i=0; i<ndrivers; i++) {
+                       WERROR status;
+
+                       DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+                       ZERO_STRUCT(driver);
+                       status = get_a_printer_driver(&driver, 3, list[i], 
+                                                     architecture, version);
+                       if (!W_ERROR_IS_OK(status)) {
+                               SAFE_FREE(list);
+                               return status;
+                       }
+                       fill_printer_driver_info_3(&driver_info_3[*returned+i], driver, servername);            
+                       free_a_printer_driver(driver, 3);
+               }       
+
+               *returned+=ndrivers;
+               SAFE_FREE(list);
+       }
+
+       /* check the required size. */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d]'s size\n",i));
+               *needed += spoolss_size_printer_driver_info_3(&driver_info_3[i]);
+       }
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(driver_info_3);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+       
+       /* fill the buffer with the driver structures */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+               smb_io_printer_driver_info_3("", buffer, &driver_info_3[i], 0);
+       }
+
+       for (i=0; i<*returned; i++)
+               SAFE_FREE(driver_info_3[i].dependentfiles);
+       
+       SAFE_FREE(driver_info_3);
+       
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers.
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdrivers( pipes_struct *p, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, SPOOL_R_ENUMPRINTERDRIVERS *r_u)
+{
+       UNISTR2 *environment = &q_u->environment;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       fstring *list = NULL;
+       fstring servername;
+       fstring architecture;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_enumprinterdrivers\n"));
+       fstrcpy(servername, get_called_name());
+       *needed=0;
+       *returned=0;
+
+       unistr2_to_ascii(architecture, environment, sizeof(architecture)-1);
+
+       switch (level) {
+       case 1:
+               return enumprinterdrivers_level1(servername, architecture, buffer, offered, needed, returned);
+       case 2:
+               return enumprinterdrivers_level2(servername, architecture, buffer, offered, needed, returned);
+       case 3:
+               return enumprinterdrivers_level3(servername, architecture, buffer, offered, needed, returned);
+       default:
+               *returned=0;
+               SAFE_FREE(list);
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_form_1(FORM_1 *form, nt_forms_struct *list)
+{
+       form->flag=list->flag;
+       init_unistr(&form->name, list->name);
+       form->width=list->width;
+       form->length=list->length;
+       form->left=list->left;
+       form->top=list->top;
+       form->right=list->right;
+       form->bottom=list->bottom;      
+}
+       
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumforms(pipes_struct *p, SPOOL_Q_ENUMFORMS *q_u, SPOOL_R_ENUMFORMS *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *numofforms = &r_u->numofforms;
+       uint32 numbuiltinforms;
+
+       nt_forms_struct *list=NULL;
+       nt_forms_struct *builtinlist=NULL;
+       FORM_1 *forms_1;
+       int buffer_size=0;
+       int i;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_enumforms\n"));
+       DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+       DEBUGADD(5,("Info level [%d]\n",          level));
+
+       numbuiltinforms = get_builtin_ntforms(&builtinlist);
+       DEBUGADD(5,("Number of builtin forms [%d]\n",     numbuiltinforms));
+       *numofforms = get_ntforms(&list);
+       DEBUGADD(5,("Number of user forms [%d]\n",     *numofforms));
+       *numofforms += numbuiltinforms;
+
+       if (*numofforms == 0) return WERR_NO_MORE_ITEMS;
+
+       switch (level) {
+       case 1:
+               if ((forms_1=(FORM_1 *)malloc(*numofforms * sizeof(FORM_1))) == NULL) {
+                       *numofforms=0;
+                       return WERR_NOMEM;
+               }
+
+               /* construct the list of form structures */
+               for (i=0; i<numbuiltinforms; i++) {
+                       DEBUGADD(6,("Filling form number [%d]\n",i));
+                       fill_form_1(&forms_1[i], &builtinlist[i]);
+               }
+               
+               SAFE_FREE(builtinlist);
+
+               for (; i<*numofforms; i++) {
+                       DEBUGADD(6,("Filling form number [%d]\n",i));
+                       fill_form_1(&forms_1[i], &list[i-numbuiltinforms]);
+               }
+               
+               SAFE_FREE(list);
+
+               /* check the required size. */
+               for (i=0; i<numbuiltinforms; i++) {
+                       DEBUGADD(6,("adding form [%d]'s size\n",i));
+                       buffer_size += spoolss_size_form_1(&forms_1[i]);
+               }
+               for (; i<*numofforms; i++) {
+                       DEBUGADD(6,("adding form [%d]'s size\n",i));
+                       buffer_size += spoolss_size_form_1(&forms_1[i]);
+               }
+
+               *needed=buffer_size;            
+               
+               if (!alloc_buffer_size(buffer, buffer_size)){
+                       SAFE_FREE(forms_1);
+                       return WERR_INSUFFICIENT_BUFFER;
+               }
+
+               /* fill the buffer with the form structures */
+               for (i=0; i<numbuiltinforms; i++) {
+                       DEBUGADD(6,("adding form [%d] to buffer\n",i));
+                       smb_io_form_1("", buffer, &forms_1[i], 0);
+               }
+               for (; i<*numofforms; i++) {
+                       DEBUGADD(6,("adding form [%d] to buffer\n",i));
+                       smb_io_form_1("", buffer, &forms_1[i], 0);
+               }
+
+               SAFE_FREE(forms_1);
+
+               if (*needed > offered) {
+                       *numofforms=0;
+                       return WERR_INSUFFICIENT_BUFFER;
+               }
+               else
+                       return WERR_OK;
+                       
+       default:
+               SAFE_FREE(list);
+               SAFE_FREE(builtinlist);
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getform(pipes_struct *p, SPOOL_Q_GETFORM *q_u, SPOOL_R_GETFORM *r_u)
+{
+       uint32 level = q_u->level;
+       UNISTR2 *uni_formname = &q_u->formname;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+
+       nt_forms_struct *list=NULL;
+       nt_forms_struct builtin_form;
+       BOOL foundBuiltin;
+       FORM_1 form_1;
+       fstring form_name;
+       int buffer_size=0;
+       int numofforms=0, i=0;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+
+       DEBUG(4,("_spoolss_getform\n"));
+       DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+       DEBUGADD(5,("Info level [%d]\n",          level));
+
+       foundBuiltin = get_a_builtin_ntform(uni_formname,&builtin_form);
+       if (!foundBuiltin) {
+               numofforms = get_ntforms(&list);
+               DEBUGADD(5,("Number of forms [%d]\n",     numofforms));
+
+               if (numofforms == 0)
+                       return WERR_BADFID;
+       }
+
+       switch (level) {
+       case 1:
+               if (foundBuiltin) {
+                       fill_form_1(&form_1, &builtin_form);
+               } else {
+
+                       /* Check if the requested name is in the list of form structures */
+                       for (i=0; i<numofforms; i++) {
+
+                               DEBUG(4,("_spoolss_getform: checking form %s (want %s)\n", list[i].name, form_name));
+
+                               if (strequal(form_name, list[i].name)) {
+                                       DEBUGADD(6,("Found form %s number [%d]\n", form_name, i));
+                                       fill_form_1(&form_1, &list[i]);
+                                       break;
+                               }
+                       }
+                       
+                       SAFE_FREE(list);
+                       if (i == numofforms) {
+                               return WERR_BADFID;
+                       }
+               }
+               /* check the required size. */
+
+               *needed=spoolss_size_form_1(&form_1);
+               
+               if (!alloc_buffer_size(buffer, buffer_size)){
+                       return WERR_INSUFFICIENT_BUFFER;
+               }
+
+               if (*needed > offered) {
+                       return WERR_INSUFFICIENT_BUFFER;
+               }
+
+               /* fill the buffer with the form structures */
+               DEBUGADD(6,("adding form %s [%d] to buffer\n", form_name, i));
+               smb_io_form_1("", buffer, &form_1, 0);
+
+               return WERR_OK;
+                       
+       default:
+               SAFE_FREE(list);
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_1(PORT_INFO_1 *port, const char *name)
+{
+       init_unistr(&port->port_name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_2(PORT_INFO_2 *port, const char *name)
+{
+       init_unistr(&port->port_name, name);
+       init_unistr(&port->monitor_name, "Local Monitor");
+       init_unistr(&port->description, "Local Port");
+#define PORT_TYPE_WRITE 1
+       port->port_type=PORT_TYPE_WRITE;
+       port->reserved=0x0;     
+}
+
+/****************************************************************************
+ enumports level 1.
+****************************************************************************/
+
+static WERROR enumports_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PORT_INFO_1 *ports=NULL;
+       int i=0;
+
+       if (*lp_enumports_cmd()) {
+               char *cmd = lp_enumports_cmd();
+               char **qlines;
+               pstring command;
+               int numlines;
+               int ret;
+               int fd;
+
+               slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 1);
+
+               DEBUG(10,("Running [%s]\n", command));
+               ret = smbrun(command, &fd);
+               DEBUG(10,("Returned [%d]\n", ret));
+               if (ret != 0) {
+                       if (fd != -1)
+                               close(fd);
+                       /* Is this the best error to return here? */
+                       return WERR_ACCESS_DENIED;
+               }
+
+               numlines = 0;
+               qlines = fd_lines_load(fd, &numlines);
+               DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+               close(fd);
+
+               if(numlines) {
+                       if((ports=(PORT_INFO_1 *)malloc( numlines * sizeof(PORT_INFO_1) )) == NULL) {
+                               DEBUG(10,("Returning WERR_NOMEM [%s]\n", 
+                                         dos_errstr(WERR_NOMEM)));
+                               file_lines_free(qlines);
+                               return WERR_NOMEM;
+                       }
+
+                       for (i=0; i<numlines; i++) {
+                               DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+                               fill_port_1(&ports[i], qlines[i]);
+                       }
+
+                       file_lines_free(qlines);
+               }
+
+               *returned = numlines;
+
+       } else {
+               *returned = 1; /* Sole Samba port returned. */
+
+               if((ports=(PORT_INFO_1 *)malloc( sizeof(PORT_INFO_1) )) == NULL)
+                       return WERR_NOMEM;
+       
+               DEBUG(10,("enumports_level_1: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+               fill_port_1(&ports[0], SAMBA_PRINTER_PORT_NAME);
+       }
+
+       /* check the required size. */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding port [%d]'s size\n", i));
+               *needed += spoolss_size_port_info_1(&ports[i]);
+       }
+               
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(ports);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the ports structures */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding port [%d] to buffer\n", i));
+               smb_io_port_1("", buffer, &ports[i], 0);
+       }
+
+       SAFE_FREE(ports);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 2.
+****************************************************************************/
+
+static WERROR enumports_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PORT_INFO_2 *ports=NULL;
+       int i=0;
+
+       if (*lp_enumports_cmd()) {
+               char *cmd = lp_enumports_cmd();
+               char *path;
+               char **qlines;
+               pstring tmp_file;
+               pstring command;
+               int numlines;
+               int ret;
+               int fd;
+
+               if (*lp_pathname(lp_servicenumber(PRINTERS_NAME)))
+                       path = lp_pathname(lp_servicenumber(PRINTERS_NAME));
+               else
+                       path = lp_lockdir();
+
+               slprintf(tmp_file, sizeof(tmp_file)-1, "%s/smbcmd.%u.", path, (unsigned int)sys_getpid());
+               slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 2);
+
+               unlink(tmp_file);
+               DEBUG(10,("Running [%s > %s]\n", command,tmp_file));
+               ret = smbrun(command, &fd);
+               DEBUGADD(10,("returned [%d]\n", ret));
+               if (ret != 0) {
+                       if (fd != -1)
+                               close(fd);
+                       /* Is this the best error to return here? */
+                       return WERR_ACCESS_DENIED;
+               }
+
+               numlines = 0;
+               qlines = fd_lines_load(fd, &numlines);
+               DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+               close(fd);
+
+               if(numlines) {
+                       if((ports=(PORT_INFO_2 *)malloc( numlines * sizeof(PORT_INFO_2) )) == NULL) {
+                               file_lines_free(qlines);
+                               return WERR_NOMEM;
+                       }
+
+                       for (i=0; i<numlines; i++) {
+                               DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+                               fill_port_2(&(ports[i]), qlines[i]);
+                       }
+
+                       file_lines_free(qlines);
+               }
+
+               *returned = numlines;
+
+       } else {
+
+               *returned = 1;
+
+               if((ports=(PORT_INFO_2 *)malloc( sizeof(PORT_INFO_2) )) == NULL)
+                       return WERR_NOMEM;
+       
+               DEBUG(10,("enumports_level_2: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+               fill_port_2(&ports[0], SAMBA_PRINTER_PORT_NAME);
+       }
+
+       /* check the required size. */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding port [%d]'s size\n", i));
+               *needed += spoolss_size_port_info_2(&ports[i]);
+       }
+               
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(ports);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       /* fill the buffer with the ports structures */
+       for (i=0; i<*returned; i++) {
+               DEBUGADD(6,("adding port [%d] to buffer\n", i));
+               smb_io_port_2("", buffer, &ports[i], 0);
+       }
+
+       SAFE_FREE(ports);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ enumports.
+****************************************************************************/
+
+WERROR _spoolss_enumports( pipes_struct *p, SPOOL_Q_ENUMPORTS *q_u, SPOOL_R_ENUMPORTS *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_enumports\n"));
+       
+       *returned=0;
+       *needed=0;
+       
+       switch (level) {
+       case 1:
+               return enumports_level_1(buffer, offered, needed, returned);
+       case 2:
+               return enumports_level_2(buffer, offered, needed, returned);
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR spoolss_addprinterex_level_2( pipes_struct *p, const UNISTR2 *uni_srv_name,
+                               const SPOOL_PRINTER_INFO_LEVEL *info,
+                               DEVICEMODE *devmode, SEC_DESC_BUF *sec_desc_buf,
+                               uint32 user_switch, const SPOOL_USER_CTR *user,
+                               POLICY_HND *handle)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       fstring name;
+       int     snum;
+       WERROR err = WERR_OK;
+
+       if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+               DEBUG(0,("spoolss_addprinterex_level_2: malloc fail.\n"));
+               return WERR_NOMEM;
+       }
+
+       ZERO_STRUCTP(printer);
+
+       /* convert from UNICODE to ASCII - this allocates the info_2 struct inside *printer.*/
+       if (!convert_printer_info(info, printer, 2)) {
+               free_a_printer(&printer, 2);
+               return WERR_NOMEM;
+       }
+
+       /* check to see if the printer already exists */
+
+       if ((snum = print_queue_snum(printer->info_2->sharename)) != -1) {
+               DEBUG(5, ("_spoolss_addprinterex: Attempted to add a printer named [%s] when one already existed!\n", 
+                       printer->info_2->sharename));
+               free_a_printer(&printer, 2);
+               return WERR_PRINTER_ALREADY_EXISTS;
+       }
+
+       if (*lp_addprinter_cmd() ) {
+               if ( !add_printer_hook(printer) ) {
+                       free_a_printer(&printer,2);
+                       return WERR_ACCESS_DENIED;
+       }
+       }
+
+       slprintf(name, sizeof(name)-1, "\\\\%s\\%s", get_called_name(),
+             printer->info_2->sharename);
+
+       
+       if ((snum = print_queue_snum(printer->info_2->sharename)) == -1) {
+               free_a_printer(&printer,2);
+               return WERR_ACCESS_DENIED;
+       }
+
+       /* you must be a printer admin to add a new printer */
+       if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
+               free_a_printer(&printer,2);
+               return WERR_ACCESS_DENIED;              
+       }
+       
+       /*
+        * Do sanity check on the requested changes for Samba.
+        */
+
+       if (!check_printer_ok(printer->info_2, snum)) {
+               free_a_printer(&printer,2);
+               return WERR_INVALID_PARAM;
+       }
+
+       /*
+        * When a printer is created, the drivername bound to the printer is used
+        * to lookup previously saved driver initialization info, which is then 
+        * bound to the new printer, simulating what happens in the Windows arch.
+        */
+
+       if (!devmode)
+       {
+               set_driver_init(printer, 2);
+       }
+       else 
+       {
+               /* A valid devmode was included, convert and link it
+               */
+               DEBUGADD(10, ("spoolss_addprinterex_level_2: devmode included, converting\n"));
+
+               if (!convert_devicemode(printer->info_2->printername, devmode,
+                               &printer->info_2->devmode))
+                       return  WERR_NOMEM;
+       }
+
+       /* write the ASCII on disk */
+       err = mod_a_printer(*printer, 2);
+       if (!W_ERROR_IS_OK(err)) {
+               free_a_printer(&printer,2);
+               return err;
+       }
+
+       if (!open_printer_hnd(p, handle, name, PRINTER_ACCESS_ADMINISTER)) {
+               /* Handle open failed - remove addition. */
+               del_a_printer(printer->info_2->sharename);
+               free_a_printer(&printer,2);
+               return WERR_ACCESS_DENIED;
+       }
+
+       update_c_setprinter(False);
+       free_a_printer(&printer,2);
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterex( pipes_struct *p, SPOOL_Q_ADDPRINTEREX *q_u, SPOOL_R_ADDPRINTEREX *r_u)
+{
+       UNISTR2 *uni_srv_name = &q_u->server_name;
+       uint32 level = q_u->level;
+       SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+       DEVICEMODE *devmode = q_u->devmode_ctr.devmode;
+       SEC_DESC_BUF *sdb = q_u->secdesc_ctr;
+       uint32 user_switch = q_u->user_switch;
+       SPOOL_USER_CTR *user = &q_u->user_ctr;
+       POLICY_HND *handle = &r_u->handle;
+
+       switch (level) {
+               case 1:
+                       /* we don't handle yet */
+                       /* but I know what to do ... */
+                       return WERR_UNKNOWN_LEVEL;
+               case 2:
+                       return spoolss_addprinterex_level_2(p, uni_srv_name, info,
+                                                           devmode, sdb,
+                                                           user_switch, user, handle);
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u, SPOOL_R_ADDPRINTERDRIVER *r_u)
+{
+       uint32 level = q_u->level;
+       SPOOL_PRINTER_DRIVER_INFO_LEVEL *info = &q_u->info;
+       WERROR err = WERR_OK;
+       NT_PRINTER_DRIVER_INFO_LEVEL driver;
+       struct current_user user;
+       fstring driver_name;
+       uint32 version;
+
+       ZERO_STRUCT(driver);
+
+       get_current_user(&user, p);
+       
+       if (!convert_printer_driver_info(info, &driver, level)) {
+               err = WERR_NOMEM;
+               goto done;
+       }
+
+       DEBUG(5,("Cleaning driver's information\n"));
+       err = clean_up_driver_struct(driver, level, &user);
+       if (!W_ERROR_IS_OK(err))
+               goto done;
+
+       DEBUG(5,("Moving driver to final destination\n"));
+       if(!move_driver_to_download_area(driver, level, &user, &err)) {
+               if (W_ERROR_IS_OK(err))
+                       err = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       if (add_a_printer_driver(driver, level)!=0) {
+               err = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       /* BEGIN_ADMIN_LOG */
+        switch(level) {
+           case 3:
+               sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+                       driver.info_3->name,drv_ver_to_os[driver.info_3->cversion],uidtoname(user.uid));
+               fstrcpy(driver_name, driver.info_3->name);
+               break;
+           case 6:   
+               sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+                       driver.info_6->name,drv_ver_to_os[driver.info_6->version],uidtoname(user.uid));
+               fstrcpy(driver_name, driver.info_6->name);
+               break;
+        }
+       /* END_ADMIN_LOG */
+
+       /* 
+        * I think this is where he DrvUpgradePrinter() hook would be
+        * be called in a driver's interface DLL on a Windows NT 4.0/2k
+        * server.  Right now, we just need to send ourselves a message
+        * to update each printer bound to this driver.   --jerry       
+        */
+        
+       if (!srv_spoolss_drv_upgrade_printer(driver_name)) {
+               DEBUG(0,("_spoolss_addprinterdriver: Failed to send message about upgrading driver [%s]!\n",
+                       driver_name));
+       }
+
+       /*
+        * Based on the version (e.g. driver destination dir: 0=9x,2=Nt/2k,3=2k/Xp),
+        * decide if the driver init data should be deleted. The rules are:
+        *  1) never delete init data if it is a 9x driver, they don't use it anyway
+        *  2) delete init data only if there is no 2k/Xp driver
+        *  3) always delete init data
+        * The generalized rule is always use init data from the highest order driver.
+        * It is necessary to follow the driver install by an initialization step to
+        * finish off this process.
+       */
+       if (level == 3)
+               version = driver.info_3->cversion;
+       else if (level == 6)
+               version = driver.info_6->version;
+       else
+               version = -1;
+       switch (version) {
+               /*
+                * 9x printer driver - never delete init data
+               */
+               case 0: 
+                       DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for 9x driver [%s]\n",
+                                       driver_name));
+                       break;
+               
+               /*
+                * Nt or 2k (compatiblity mode) printer driver - only delete init data if
+                * there is no 2k/Xp driver init data for this driver name.
+               */
+               case 2:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL driver1;
+
+                       if (!W_ERROR_IS_OK(get_a_printer_driver(&driver1, 3, driver_name, "Windows NT x86", 3))) {
+                               /*
+                                * No 2k/Xp driver found, delete init data (if any) for the new Nt driver.
+                               */
+                               if (!del_driver_init(driver_name))
+                                       DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) Nt failed!\n", driver_name));
+                       } else {
+                               /*
+                                * a 2k/Xp driver was found, don't delete init data because Nt driver will use it.
+                               */
+                               free_a_printer_driver(driver1,3);
+                               DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for Nt driver [%s]\n", 
+                                               driver_name));
+                       }
+               }
+               break;
+
+               /*
+                * 2k or Xp printer driver - always delete init data
+               */
+               case 3: 
+                       if (!del_driver_init(driver_name))
+                               DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) 2k/Xp failed!\n", driver_name));
+                       break;
+
+               default:
+                       DEBUG(0,("_spoolss_addprinterdriver: invalid level=%d\n", level));
+                       break;
+       }
+
+       
+done:
+       free_a_printer_driver(driver, level);
+       return err;
+}
+
+/********************************************************************
+ * spoolss_addprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_addprinterdriverex(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, SPOOL_R_ADDPRINTERDRIVEREX *r_u)
+{
+       SPOOL_Q_ADDPRINTERDRIVER q_u_local;
+       SPOOL_R_ADDPRINTERDRIVER r_u_local;
+       
+       /* 
+        * we only support the semantics of AddPrinterDriver()
+        * i.e. only copy files that are newer than existing ones
+        */
+       
+       if ( q_u->copy_flags != APD_COPY_NEW_FILES )
+               return WERR_ACCESS_DENIED;
+       
+       ZERO_STRUCT(q_u_local);
+       ZERO_STRUCT(r_u_local);
+
+       /* just pass the information off to _spoolss_addprinterdriver() */
+       q_u_local.server_name_ptr = q_u->server_name_ptr;
+       copy_unistr2(&q_u_local.server_name, &q_u->server_name);
+       q_u_local.level = q_u->level;
+       memcpy( &q_u_local.info, &q_u->info, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL) );
+       
+       return _spoolss_addprinterdriver( p, &q_u_local, &r_u_local );
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_driverdir_1(DRIVER_DIRECTORY_1 *info, char *name)
+{
+       init_unistr(&info->name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriverdir_level_1(UNISTR2 *name, UNISTR2 *uni_environment, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       pstring path;
+       pstring long_archi;
+       pstring short_archi;
+       DRIVER_DIRECTORY_1 *info=NULL;
+
+       unistr2_to_ascii(long_archi, uni_environment, sizeof(long_archi)-1);
+
+       if (get_short_archi(short_archi, long_archi)==False)
+               return WERR_INVALID_ENVIRONMENT;
+
+       if((info=(DRIVER_DIRECTORY_1 *)malloc(sizeof(DRIVER_DIRECTORY_1))) == NULL)
+               return WERR_NOMEM;
+
+       slprintf(path, sizeof(path)-1, "\\\\%s\\print$\\%s", get_called_name(), short_archi);
+
+       DEBUG(4,("printer driver directory: [%s]\n", path));
+
+       fill_driverdir_1(info, path);
+       
+       *needed += spoolss_size_driverdir_info_1(info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       smb_io_driverdir_1("", buffer, info, 0);
+
+       SAFE_FREE(info);
+       
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriverdirectory(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, SPOOL_R_GETPRINTERDRIVERDIR *r_u)
+{
+       UNISTR2 *name = &q_u->name;
+       UNISTR2 *uni_environment = &q_u->environment;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(4,("_spoolss_getprinterdriverdirectory\n"));
+
+       *needed=0;
+
+       switch(level) {
+       case 1:
+               return getprinterdriverdir_level_1(name, uni_environment, buffer, offered, needed);
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+       
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, SPOOL_R_ENUMPRINTERDATA *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 idx               = q_u->index;
+       uint32 in_value_len      = q_u->valuesize;
+       uint32 in_data_len       = q_u->datasize;
+       uint32 *out_max_value_len = &r_u->valuesize;
+       uint16 **out_value       = &r_u->value;
+       uint32 *out_value_len    = &r_u->realvaluesize;
+       uint32 *out_type         = &r_u->type;
+       uint32 *out_max_data_len = &r_u->datasize;
+       uint8  **data_out        = &r_u->data;
+       uint32 *out_data_len     = &r_u->realdatasize;
+
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       
+       uint32          param_index;
+       uint32          biggest_valuesize;
+       uint32          biggest_datasize;
+       uint32          data_len;
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       int             snum;
+       WERROR          result;
+       REGISTRY_VALUE  *val;
+       NT_PRINTER_DATA *p_data;
+       int             i, key_index, num_values;
+       int             name_length;
+       
+       ZERO_STRUCT( printer );
+       
+       *out_type = 0;
+
+       *out_max_data_len = 0;
+       *data_out         = NULL;
+       *out_data_len     = 0;
+
+       DEBUG(5,("spoolss_enumprinterdata\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_enumprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p,handle, &snum))
+               return WERR_BADFID;
+       
+       result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(result))
+               return result;
+               
+       p_data = &printer->info_2->data;        
+       key_index = lookup_printerkey( p_data, SPOOL_PRINTERDATA_KEY );
+
+       result = WERR_OK;
+
+       /*
+        * The NT machine wants to know the biggest size of value and data
+        *
+        * cf: MSDN EnumPrinterData remark section
+        */
+        
+       if ( !in_value_len && !in_data_len ) 
+       {
+               DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
+
+               param_index       = 0;
+               biggest_valuesize = 0;
+               biggest_datasize  = 0;
+                               
+               num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+               
+               for ( i=0; i<num_values; i++ )
+               {
+                       val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+                       
+                       name_length = strlen(val->valuename);
+                       if ( strlen(val->valuename) > biggest_valuesize ) 
+                               biggest_valuesize = name_length;
+                               
+                       if ( val->size > biggest_datasize )
+                               biggest_datasize = val->size;
+                               
+                       DEBUG(6,("current values: [%d], [%d]\n", biggest_valuesize, 
+                               biggest_datasize));
+               }
+
+               /* the value is an UNICODE string but real_value_size is the length 
+                  in bytes including the trailing 0 */
+                  
+               *out_value_len = 2 * (1+biggest_valuesize);
+               *out_data_len  = biggest_datasize;
+
+               DEBUG(6,("final values: [%d], [%d]\n", *out_value_len, *out_data_len));
+
+               goto done;
+       }
+       
+       /*
+        * the value len is wrong in NT sp3
+        * that's the number of bytes not the number of unicode chars
+        */
+        
+       val = regval_ctr_specific_value( &p_data->keys[key_index].values, idx );
+
+       if ( !val ) 
+       {
+
+               /* out_value should default to "" or else NT4 has
+                  problems unmarshalling the response */
+
+               *out_max_value_len=(in_value_len/sizeof(uint16));
+               
+               if((*out_value=(uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL)
+               {
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+
+               *out_value_len = (uint32)rpcstr_push((char *)*out_value, "", in_value_len, 0);
+
+               /* the data is counted in bytes */
+               
+               *out_max_data_len = in_data_len;
+               *out_data_len     = in_data_len;
+               
+               /* only allocate when given a non-zero data_len */
+               
+               if ( in_data_len && ((*data_out=(uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) )
+               {
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+
+               result = WERR_NO_MORE_ITEMS;
+       }
+       else 
+       {
+               /*
+                * the value is:
+                * - counted in bytes in the request
+                * - counted in UNICODE chars in the max reply
+                * - counted in bytes in the real size
+                *
+                * take a pause *before* coding not *during* coding
+                */
+       
+               /* name */
+               *out_max_value_len=(in_value_len/sizeof(uint16));
+               if ( (*out_value = (uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL ) 
+               {
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+       
+               *out_value_len = (uint32)rpcstr_push((char *)*out_value, regval_name(val), in_value_len, 0);
+
+               /* type */
+               
+               *out_type = regval_type( val );
+
+               /* data - counted in bytes */
+
+               *out_max_data_len = in_data_len;
+               if ( (*data_out = (uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) 
+               {
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+               data_len = (size_t)regval_size(val);
+               memcpy( *data_out, regval_data_p(val), data_len );
+               *out_data_len = data_len;
+       }
+
+done:
+       free_a_printer(&printer, 2);
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinterdata( pipes_struct *p, SPOOL_Q_SETPRINTERDATA *q_u, SPOOL_R_SETPRINTERDATA *r_u)
+{
+       POLICY_HND              *handle = &q_u->handle;
+       UNISTR2                 *value = &q_u->value;
+       uint32                  type = q_u->type;
+       uint8                   *data = q_u->data;
+       uint32                  real_len = q_u->real_len;
+
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int                     snum=0;
+       WERROR                  status = WERR_OK;
+       Printer_entry           *Printer=find_printer_index_by_hnd(p, handle);
+       fstring                 valuename;
+       
+       DEBUG(5,("spoolss_setprinterdata\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p,handle, &snum))
+               return WERR_BADFID;
+
+       /* 
+        * Access check : NT returns "access denied" if you make a 
+        * SetPrinterData call without the necessary privildge.
+        * we were originally returning OK if nothing changed
+        * which made Win2k issue **a lot** of SetPrinterData
+        * when connecting to a printer  --jerry
+        */
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) 
+       {
+               DEBUG(3, ("_spoolss_setprinterdata: change denied by handle access permissions\n"));
+               status = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+
+       unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+       
+       /*
+        * When client side code sets a magic printer data key, detect it and save
+        * the current printer data and the magic key's data (its the DEVMODE) for
+        * future printer/driver initializations.
+        */
+       if ( (type == REG_BINARY) && strequal( valuename, PHANTOM_DEVMODE_KEY)) 
+       {
+               /* Set devmode and printer initialization info */
+               status = save_driver_init( printer, 2, data, real_len );
+       
+               srv_spoolss_reset_printerdata( printer->info_2->drivername );
+       }
+       else 
+       {
+       status = set_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename, 
+                                       type, data, real_len );
+               if ( W_ERROR_IS_OK(status) )
+                       status = mod_a_printer(*printer, 2);
+       }
+
+done:
+       free_a_printer(&printer, 2);
+
+       return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_resetprinter(pipes_struct *p, SPOOL_Q_RESETPRINTER *q_u, SPOOL_R_RESETPRINTER *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       Printer_entry   *Printer=find_printer_index_by_hnd(p, handle);
+       int             snum;
+       
+       DEBUG(5,("_spoolss_resetprinter\n"));
+
+       /*
+        * All we do is to check to see if the handle and queue is valid.
+        * This call really doesn't mean anything to us because we only
+        * support RAW printing.   --jerry
+        */
+        
+       if (!Printer) {
+               DEBUG(2,("_spoolss_resetprinter: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p,handle, &snum))
+               return WERR_BADFID;
+
+
+       /* blindly return success */    
+       return WERR_OK;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteprinterdata(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATA *q_u, SPOOL_R_DELETEPRINTERDATA *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       UNISTR2         *value = &q_u->valuename;
+
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int             snum=0;
+       WERROR          status = WERR_OK;
+       Printer_entry   *Printer=find_printer_index_by_hnd(p, handle);
+       pstring         valuename;
+       
+       DEBUG(5,("spoolss_deleteprinterdata\n"));
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+               DEBUG(3, ("_spoolss_deleteprinterdata: printer properties change denied by handle\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+
+       unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+
+       status = delete_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename );
+
+       free_a_printer(&printer, 2);
+
+       return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addform( pipes_struct *p, SPOOL_Q_ADDFORM *q_u, SPOOL_R_ADDFORM *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       FORM *form = &q_u->form;
+       nt_forms_struct tmpForm;
+       int snum;
+       WERROR status = WERR_OK;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+       int count=0;
+       nt_forms_struct *list=NULL;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       DEBUG(5,("spoolss_addform\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_addform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+       
+       
+       /* forms can be added on printer of on the print server handle */
+       
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+       {
+               if (!get_printer_snum(p,handle, &snum))
+                       return WERR_BADFID;
+        
+               status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+               if (!W_ERROR_IS_OK(status))
+                       goto done;
+       }
+
+       if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+               DEBUG(2,("_spoolss_addform: denied by handle permissions.\n"));
+               status = WERR_ACCESS_DENIED;
+               goto done;
+       }
+       
+       /* can't add if builtin */
+       
+       if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+               status = WERR_ALREADY_EXISTS;
+               goto done;
+       }
+
+       count = get_ntforms(&list);
+       
+       if(!add_a_form(&list, form, &count)) {
+               status =  WERR_NOMEM;
+               goto done;
+       }
+       
+       write_ntforms(&list, count);
+       
+       /*
+        * ChangeID must always be set if this is a printer
+        */
+        
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+               status = mod_a_printer(*printer, 2);
+       
+done:
+       if ( printer )
+               free_a_printer(&printer, 2);
+       SAFE_FREE(list);
+
+       return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteform( pipes_struct *p, SPOOL_Q_DELETEFORM *q_u, SPOOL_R_DELETEFORM *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       UNISTR2 *form_name = &q_u->name;
+       nt_forms_struct tmpForm;
+       int count=0;
+       nt_forms_struct *list=NULL;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+       int snum;
+       WERROR status = WERR_OK;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+       DEBUG(5,("spoolss_deleteform\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_deleteform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       /* forms can be deleted on printer of on the print server handle */
+       
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+       {
+               if (!get_printer_snum(p,handle, &snum))
+                       return WERR_BADFID;
+        
+               status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+               if (!W_ERROR_IS_OK(status))
+                       goto done;
+       }
+
+       if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+               DEBUG(2,("_spoolss_deleteform: denied by handle permissions.\n"));
+               status = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       /* can't delete if builtin */
+       
+       if (get_a_builtin_ntform(form_name,&tmpForm)) {
+               status = WERR_INVALID_PARAM;
+               goto done;
+       }
+
+       count = get_ntforms(&list);
+       
+       if ( !delete_a_form(&list, form_name, &count, &status ))
+               goto done;
+
+       /*
+        * ChangeID must always be set if this is a printer
+        */
+        
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+               status = mod_a_printer(*printer, 2);
+       
+done:
+       if ( printer )
+               free_a_printer(&printer, 2);
+       SAFE_FREE(list);
+
+       return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       FORM *form = &q_u->form;
+       nt_forms_struct tmpForm;
+       int snum;
+       WERROR status = WERR_OK;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+       int count=0;
+       nt_forms_struct *list=NULL;
+       Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+       DEBUG(5,("spoolss_setform\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_setform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       /* forms can be modified on printer of on the print server handle */
+       
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+       {
+               if (!get_printer_snum(p,handle, &snum))
+                       return WERR_BADFID;
+        
+               status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+               if (!W_ERROR_IS_OK(status))
+                       goto done;
+       }
+
+       if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+               DEBUG(2,("_spoolss_setform: denied by handle permissions\n"));
+               status = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       /* can't set if builtin */
+       if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+               status = WERR_INVALID_PARAM;
+               goto done;
+       }
+
+       count = get_ntforms(&list);
+       update_a_form(&list, form, count);
+       write_ntforms(&list, count);
+
+       /*
+        * ChangeID must always be set if this is a printer
+        */
+        
+       if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+               status = mod_a_printer(*printer, 2);
+       
+       
+done:
+       if ( printer )
+               free_a_printer(&printer, 2);
+       SAFE_FREE(list);
+
+       return status;
+}
+
+/****************************************************************************
+ enumprintprocessors level 1.
+****************************************************************************/
+
+static WERROR enumprintprocessors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PRINTPROCESSOR_1 *info_1=NULL;
+       
+       if((info_1 = (PRINTPROCESSOR_1 *)malloc(sizeof(PRINTPROCESSOR_1))) == NULL)
+               return WERR_NOMEM;
+
+       (*returned) = 0x1;
+       
+       init_unistr(&info_1->name, "winprint");
+
+       *needed += spoolss_size_printprocessor_info_1(info_1);
+
+       if (!alloc_buffer_size(buffer, *needed))
+               return WERR_INSUFFICIENT_BUFFER;
+
+       smb_io_printprocessor_info_1("", buffer, info_1, 0);
+
+       SAFE_FREE(info_1);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(5,("spoolss_enumprintprocessors\n"));
+
+       /*
+        * Enumerate the print processors ...
+        *
+        * Just reply with "winprint", to keep NT happy
+        * and I can use my nice printer checker.
+        */
+       
+       *returned=0;
+       *needed=0;
+       
+       switch (level) {
+       case 1:
+               return enumprintprocessors_level_1(buffer, offered, needed, returned);
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+ enumprintprocdatatypes level 1.
+****************************************************************************/
+
+static WERROR enumprintprocdatatypes_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PRINTPROCDATATYPE_1 *info_1=NULL;
+       
+       if((info_1 = (PRINTPROCDATATYPE_1 *)malloc(sizeof(PRINTPROCDATATYPE_1))) == NULL)
+               return WERR_NOMEM;
+
+       (*returned) = 0x1;
+       
+       init_unistr(&info_1->name, "RAW");
+
+       *needed += spoolss_size_printprocdatatype_info_1(info_1);
+
+       if (!alloc_buffer_size(buffer, *needed))
+               return WERR_INSUFFICIENT_BUFFER;
+
+       smb_io_printprocdatatype_info_1("", buffer, info_1, 0);
+
+       SAFE_FREE(info_1);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(5,("_spoolss_enumprintprocdatatypes\n"));
+       
+       *returned=0;
+       *needed=0;
+       
+       switch (level) {
+       case 1:
+               return enumprintprocdatatypes_level_1(buffer, offered, needed, returned);
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+ enumprintmonitors level 1.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PRINTMONITOR_1 *info_1=NULL;
+       
+       if((info_1 = (PRINTMONITOR_1 *)malloc(sizeof(PRINTMONITOR_1))) == NULL)
+               return WERR_NOMEM;
+
+       (*returned) = 0x1;
+       
+       init_unistr(&info_1->name, "Local Port");
+
+       *needed += spoolss_size_printmonitor_info_1(info_1);
+
+       if (!alloc_buffer_size(buffer, *needed))
+               return WERR_INSUFFICIENT_BUFFER;
+
+       smb_io_printmonitor_info_1("", buffer, info_1, 0);
+
+       SAFE_FREE(info_1);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintmonitors level 2.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+       PRINTMONITOR_2 *info_2=NULL;
+       
+       if((info_2 = (PRINTMONITOR_2 *)malloc(sizeof(PRINTMONITOR_2))) == NULL)
+               return WERR_NOMEM;
+
+       (*returned) = 0x1;
+       
+       init_unistr(&info_2->name, "Local Port");
+       init_unistr(&info_2->environment, "Windows NT X86");
+       init_unistr(&info_2->dll_name, "localmon.dll");
+
+       *needed += spoolss_size_printmonitor_info_2(info_2);
+
+       if (!alloc_buffer_size(buffer, *needed))
+               return WERR_INSUFFICIENT_BUFFER;
+
+       smb_io_printmonitor_info_2("", buffer, info_2, 0);
+
+       SAFE_FREE(info_2);
+
+       if (*needed > offered) {
+               *returned=0;
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       uint32 *returned = &r_u->returned;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(5,("spoolss_enumprintmonitors\n"));
+
+       /*
+        * Enumerate the print monitors ...
+        *
+        * Just reply with "Local Port", to keep NT happy
+        * and I can use my nice printer checker.
+        */
+       
+       *returned=0;
+       *needed=0;
+       
+       switch (level) {
+       case 1:
+               return enumprintmonitors_level_1(buffer, offered, needed, returned);
+       case 2:
+               return enumprintmonitors_level_2(buffer, offered, needed, returned);
+       default:
+               return WERR_UNKNOWN_LEVEL;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_1(print_queue_struct *queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       int i=0;
+       BOOL found=False;
+       JOB_INFO_1 *info_1=NULL;
+
+       info_1=(JOB_INFO_1 *)malloc(sizeof(JOB_INFO_1));
+
+       if (info_1 == NULL) {
+               SAFE_FREE(queue);
+               return WERR_NOMEM;
+       }
+               
+       for (i=0; i<count && found==False; i++) { 
+               if (queue[i].job==(int)jobid)
+                       found=True;
+       }
+       
+       if (found==False) {
+               SAFE_FREE(queue);
+               SAFE_FREE(info_1);
+               /* NT treats not found as bad param... yet another bad choice */
+               return WERR_INVALID_PARAM;
+       }
+       
+       fill_job_info_1(info_1, &(queue[i-1]), i, snum);
+       
+       *needed += spoolss_size_job_info_1(info_1);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               SAFE_FREE(info_1);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       smb_io_job_info_1("", buffer, info_1, 0);
+
+       SAFE_FREE(info_1);
+
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+
+       return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_2(print_queue_struct *queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+       int             i = 0;
+       BOOL            found = False;
+       JOB_INFO_2      *info_2;
+       NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+       WERROR          ret;
+       DEVICEMODE      *devmode = NULL;
+       NT_DEVICEMODE   *nt_devmode = NULL;
+
+       info_2=(JOB_INFO_2 *)malloc(sizeof(JOB_INFO_2));
+
+       ZERO_STRUCTP(info_2);
+
+       if (info_2 == NULL) {
+               ret = WERR_NOMEM;
+               goto done;
+       }
+
+       for ( i=0; i<count && found==False; i++ ) 
+       {
+               if (queue[i].job == (int)jobid)
+                       found = True;
+       }
+       
+       if ( !found ) 
+       {
+               /* NT treats not found as bad param... yet another bad
+                  choice */
+               ret = WERR_INVALID_PARAM;
+               goto done;
+       }
+       
+       ret = get_a_printer(NULL, &ntprinter, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(ret))
+               goto done;
+       
+       /* 
+        * if the print job does not have a DEVMODE associated with it, 
+        * just use the one for the printer. A NULL devicemode is not
+        *  a failure condition
+        */
+        
+       if ( !(nt_devmode=print_job_devmode( snum, jobid )) )
+               devmode = construct_dev_mode(snum);
+       else {
+               if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) != NULL) {
+                       ZERO_STRUCTP( devmode );
+                       convert_nt_devicemode( devmode, nt_devmode );
+               }
+       }
+       
+       fill_job_info_2(info_2, &(queue[i-1]), i, snum, ntprinter, devmode);
+       
+       *needed += spoolss_size_job_info_2(info_2);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               ret = WERR_INSUFFICIENT_BUFFER;
+               goto done;
+       }
+
+       smb_io_job_info_2("", buffer, info_2, 0);
+
+       if (*needed > offered) {
+               ret = WERR_INSUFFICIENT_BUFFER;
+               goto done;
+       }
+
+       ret = WERR_OK;
+       
+ done:
+       /* Cleanup allocated memory */
+
+       free_job_info_2(info_2);        /* Also frees devmode */
+       SAFE_FREE(info_2);
+       free_a_printer(&ntprinter, 2);
+
+       return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getjob( pipes_struct *p, SPOOL_Q_GETJOB *q_u, SPOOL_R_GETJOB *r_u)
+{
+       POLICY_HND *handle = &q_u->handle;
+       uint32 jobid = q_u->jobid;
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       WERROR          wstatus = WERR_OK;
+
+       int snum;
+       int count;
+       print_queue_struct      *queue = NULL;
+       print_status_struct prt_status;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(5,("spoolss_getjob\n"));
+       
+       *needed = 0;
+       
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+       
+       count = print_queue_status(snum, &queue, &prt_status);
+       
+       DEBUGADD(4,("count:[%d], prt_status:[%d], [%s]\n",
+                    count, prt_status.status, prt_status.message));
+               
+       switch ( level ) {
+       case 1:
+                       wstatus = getjob_level_1(queue, count, snum, jobid, 
+                               buffer, offered, needed);
+                       break;
+       case 2:
+                       wstatus = getjob_level_2(queue, count, snum, jobid, 
+                               buffer, offered, needed);
+                       break;
+       default:
+                       wstatus = WERR_UNKNOWN_LEVEL;
+                       break;
+       }
+       
+       SAFE_FREE(queue);
+       return wstatus;
+}
+
+/********************************************************************
+ spoolss_getprinterdataex
+ From MSDN documentation of GetPrinterDataEx: pass request
+ to GetPrinterData if key is "PrinterDriverData".
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdataex(pipes_struct *p, SPOOL_Q_GETPRINTERDATAEX *q_u, SPOOL_R_GETPRINTERDATAEX *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       uint32          in_size = q_u->size;
+       uint32          *type = &r_u->type;
+       uint32          *out_size = &r_u->size;
+       uint8           **data = &r_u->data;
+       uint32          *needed = &r_u->needed;
+       fstring         keyname, valuename;
+       
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int                     snum = 0;
+       WERROR                  status = WERR_OK;
+
+       DEBUG(4,("_spoolss_getprinterdataex\n"));
+
+        unistr2_to_ascii(keyname, &q_u->keyname, sizeof(keyname) - 1);
+        unistr2_to_ascii(valuename, &q_u->valuename, sizeof(valuename) - 1);
+       
+       DEBUG(10, ("_spoolss_getprinterdataex: key => [%s], value => [%s]\n", 
+               keyname, valuename));
+
+       /* in case of problem, return some default values */
+       
+       *needed   = 0;
+       *type     = 0;
+       *out_size = in_size;
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_getprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               status = WERR_BADFID;
+               goto done;
+       }
+
+       /* Is the handle to a printer or to the server? */
+
+       if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+               DEBUG(10,("_spoolss_getprinterdatex: Not implemented for server handles yet\n"));
+               status = WERR_INVALID_PARAM;
+               goto done;
+       }
+       
+       if ( !get_printer_snum(p,handle, &snum) )
+               return WERR_BADFID;
+
+       status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+       if ( !W_ERROR_IS_OK(status) )
+               goto done;
+
+       /* check to see if the keyname is valid */
+       if ( !strlen(keyname) ) {
+               status = WERR_INVALID_PARAM;
+               goto done;
+       }
+       
+       if ( lookup_printerkey( &printer->info_2->data, keyname ) == -1 ) {
+               DEBUG(4,("_spoolss_getprinterdataex: Invalid keyname [%s]\n", keyname ));
+               free_a_printer( &printer, 2 );
+               status = WERR_BADFILE;
+               goto done;
+       }
+       
+       /* When given a new keyname, we should just create it */
+
+       status = get_printer_dataex( p->mem_ctx, printer, keyname, valuename, type, data, needed, in_size );
+       
+       if (*needed > *out_size)
+               status = WERR_MORE_DATA;
+
+done:
+       if ( !W_ERROR_IS_OK(status) ) 
+       {
+               DEBUG(5, ("error: allocating %d\n", *out_size));
+               
+               /* reply this param doesn't exist */
+               
+               if ( *out_size ) 
+               {
+                       if( (*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL ) {
+                               status = WERR_NOMEM;
+                               goto done;
+                       }
+               } 
+               else {
+                       *data = NULL;
+       }
+       }
+       
+       if ( printer )
+       free_a_printer( &printer, 2 );
+       
+       return status;
+}
+
+/********************************************************************
+ * spoolss_setprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u, SPOOL_R_SETPRINTERDATAEX *r_u)
+{
+       POLICY_HND              *handle = &q_u->handle; 
+       uint32                  type = q_u->type;
+       uint8                   *data = q_u->data;
+       uint32                  real_len = q_u->real_len;
+
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int                     snum = 0;
+       WERROR                  status = WERR_OK;
+       Printer_entry           *Printer = find_printer_index_by_hnd(p, handle);
+       fstring                 valuename;
+       fstring                 keyname;
+       char                    *oid_string;
+       
+       DEBUG(4,("_spoolss_setprinterdataex\n"));
+
+        /* From MSDN documentation of SetPrinterDataEx: pass request to
+           SetPrinterData if key is "PrinterDriverData" */
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if ( !get_printer_snum(p,handle, &snum) )
+               return WERR_BADFID;
+
+       /* 
+        * Access check : NT returns "access denied" if you make a 
+        * SetPrinterData call without the necessary privildge.
+        * we were originally returning OK if nothing changed
+        * which made Win2k issue **a lot** of SetPrinterData
+        * when connecting to a printer  --jerry
+        */
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) 
+       {
+               DEBUG(3, ("_spoolss_setprinterdataex: change denied by handle access permissions\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+
+        unistr2_to_ascii( valuename, &q_u->value, sizeof(valuename) - 1);
+        unistr2_to_ascii( keyname, &q_u->key, sizeof(keyname) - 1);
+       
+       /* check for OID in valuename */
+       
+       if ( (oid_string = strchr( valuename, ',' )) != NULL )
+       {
+               *oid_string = '\0';
+               oid_string++;
+       }
+
+       /* save the registry data */
+       
+       status = set_printer_dataex( printer, keyname, valuename, type, data, real_len ); 
+       
+       if ( W_ERROR_IS_OK(status) )
+       {
+               /* save the OID if one was specified */
+               if ( oid_string ) {
+               fstrcat( keyname, "\\" );
+               fstrcat( keyname, SPOOL_OID_KEY );
+               
+               /* 
+                * I'm not checking the status here on purpose.  Don't know 
+                * if this is right, but I'm returning the status from the 
+                * previous set_printer_dataex() call.  I have no idea if 
+                * this is right.    --jerry
+                */
+                
+               set_printer_dataex( printer, keyname, valuename, 
+                                   REG_SZ, (void*)oid_string, strlen(oid_string)+1 );          
+       }
+       
+               status = mod_a_printer(*printer, 2);
+       }
+               
+       free_a_printer(&printer, 2);
+
+       return status;
+}
+
+
+/********************************************************************
+ * spoolss_deleteprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdataex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATAEX *q_u, SPOOL_R_DELETEPRINTERDATAEX *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle;
+       UNISTR2         *value = &q_u->valuename;
+       UNISTR2         *key = &q_u->keyname;
+
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int             snum=0;
+       WERROR          status = WERR_OK;
+       Printer_entry   *Printer=find_printer_index_by_hnd(p, handle);
+       pstring         valuename, keyname;
+       
+       DEBUG(5,("spoolss_deleteprinterdataex\n"));
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+               DEBUG(3, ("_spoolss_deleteprinterdataex: printer properties change denied by handle\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+
+       unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+       unistr2_to_ascii( keyname, key, sizeof(keyname)-1 );
+
+       status = delete_printer_dataex( printer, keyname, valuename );
+
+       free_a_printer(&printer, 2);
+
+       return status;
+}
+
+/********************************************************************
+ * spoolss_enumprinterkey
+ ********************************************************************/
+
+
+WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPOOL_R_ENUMPRINTERKEY *r_u)
+{
+       fstring         key;
+       fstring         *keynames = NULL;
+       uint16          *enumkeys = NULL;
+       int             num_keys;
+       int             printerkey_len;
+       POLICY_HND      *handle = &q_u->handle;
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       NT_PRINTER_DATA *data;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int             snum = 0;
+       WERROR          status = WERR_BADFILE;
+       
+       
+       DEBUG(4,("_spoolss_enumprinterkey\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_enumprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       if ( !get_printer_snum(p,handle, &snum) )
+               return WERR_BADFID;
+
+       status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+               
+       /* get the list of subkey names */
+       
+       unistr2_to_ascii( key, &q_u->key, sizeof(key)-1 );
+       data = &printer->info_2->data;
+
+       num_keys = get_printer_subkeys( data, key, &keynames );
+
+       if ( num_keys == -1 ) {
+               status = WERR_BADFILE;
+               goto done;
+       }
+
+       printerkey_len = init_unistr_array( &enumkeys,  keynames, NULL );
+
+       r_u->needed = printerkey_len*2;
+
+       if ( q_u->size < r_u->needed ) {
+               status = WERR_MORE_DATA;
+               goto done;
+       }
+
+       if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, printerkey_len, enumkeys)) {
+               status = WERR_NOMEM;
+               goto done;
+       }
+                       
+       status = WERR_OK;
+
+       if ( q_u->size < r_u->needed ) 
+               status = WERR_MORE_DATA;
+
+done:
+       free_a_printer( &printer, 2 );
+       SAFE_FREE( keynames );
+       
+        return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterkey
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterkey(pipes_struct *p, SPOOL_Q_DELETEPRINTERKEY *q_u, SPOOL_R_DELETEPRINTERKEY *r_u)
+{
+       POLICY_HND              *handle = &q_u->handle;
+       Printer_entry           *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+       fstring                 key;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       int                     snum=0;
+       WERROR                  status;
+       
+       DEBUG(5,("spoolss_deleteprinterkey\n"));
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_deleteprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       /* if keyname == NULL, return error */
+       
+       if ( !q_u->keyname.buffer )
+               return WERR_INVALID_PARAM;
+               
+       if (!get_printer_snum(p, handle, &snum))
+               return WERR_BADFID;
+
+       if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+               DEBUG(3, ("_spoolss_deleteprinterkey: printer properties change denied by handle\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(status))
+               return status;
+       
+       /* delete the key and all subneys */
+       
+        unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1);
+       status = delete_all_printer_data( printer->info_2, key );       
+
+       if ( W_ERROR_IS_OK(status) )
+               status = mod_a_printer(*printer, 2);
+       
+       free_a_printer( &printer, 2 );
+       
+       return status;
+}
+
+
+/********************************************************************
+ * spoolss_enumprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_enumprinterdataex(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATAEX *q_u, SPOOL_R_ENUMPRINTERDATAEX *r_u)
+{
+       POLICY_HND      *handle = &q_u->handle; 
+       uint32          in_size = q_u->size;
+       uint32          num_entries, 
+                       needed;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       PRINTER_ENUM_VALUES     *enum_values = NULL;
+       NT_PRINTER_DATA         *p_data;
+       fstring         key;
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, handle);
+       int             snum;
+       WERROR          result;
+       int             key_index;
+       int             i;
+       REGISTRY_VALUE  *val;
+       char            *value_name;
+       int             data_len;
+       
+
+       DEBUG(4,("_spoolss_enumprinterdataex\n"));
+
+       if (!Printer) {
+               DEBUG(2,("_spoolss_enumprinterdataex: Invalid handle (%s:%u:%u1<).\n", OUR_HANDLE(handle)));
+               return WERR_BADFID;
+       }
+
+       /* 
+        * first check for a keyname of NULL or "".  Win2k seems to send 
+        * this a lot and we should send back WERR_INVALID_PARAM
+        * no need to spend time looking up the printer in this case.
+        * --jerry
+        */
+        
+       unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+       if ( !strlen(key) ) {
+               result = WERR_INVALID_PARAM;
+               goto done;
+       }
+
+       /* get the printer off of disk */
+       
+       if (!get_printer_snum(p,handle, &snum))
+               return WERR_BADFID;
+       
+       ZERO_STRUCT(printer);
+       result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+       if (!W_ERROR_IS_OK(result))
+               return result;
+       
+       /* now look for a match on the key name */
+       
+       p_data = &printer->info_2->data;
+       
+       unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+       if ( (key_index = lookup_printerkey( p_data, key)) == -1  )
+       {
+               DEBUG(10,("_spoolss_enumprinterdataex: Unknown keyname [%s]\n", key));
+               result = WERR_INVALID_PARAM;
+               goto done;
+       }
+       
+       result = WERR_OK;
+       needed = 0;
+       
+       /* allocate the memory for the array of pointers -- if necessary */
+       
+       num_entries = regval_ctr_numvals( &p_data->keys[key_index].values );
+       if ( num_entries )
+       {
+               if ( (enum_values=talloc(p->mem_ctx, num_entries*sizeof(PRINTER_ENUM_VALUES))) == NULL )
+               {
+                       DEBUG(0,("_spoolss_enumprinterdataex: talloc() failed to allocate memory for [%d] bytes!\n",
+                               num_entries*sizeof(PRINTER_ENUM_VALUES)));
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+
+               memset( enum_values, 0x0, num_entries*sizeof(PRINTER_ENUM_VALUES) );
+       }
+               
+       /* 
+        * loop through all params and build the array to pass 
+        * back to the  client 
+        */
+        
+       for ( i=0; i<num_entries; i++ )
+       {
+               /* lookup the registry value */
+               
+               val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+               DEBUG(10,("retrieved value number [%d] [%s]\n", i, regval_name(val) ));
+
+               /* copy the data */
+               
+               value_name = regval_name( val );
+               init_unistr( &enum_values[i].valuename, value_name );
+               enum_values[i].value_len = (strlen(value_name)+1) * 2;
+               enum_values[i].type      = regval_type( val );
+               
+               data_len = regval_size( val );
+               if ( data_len ) {
+                       if ( !(enum_values[i].data = talloc_memdup(p->mem_ctx, regval_data_p(val), data_len)) ) 
+                       {
+                               DEBUG(0,("talloc_memdup failed to allocate memory [data_len=%d] for data!\n", 
+                                       data_len ));
+                               result = WERR_NOMEM;
+                               goto done;
+                       }
+               }
+               enum_values[i].data_len = data_len;
+
+               /* keep track of the size of the array in bytes */
+               
+               needed += spoolss_size_printer_enum_values(&enum_values[i]);
+       }
+       
+       /* housekeeping information in the reply */
+       
+       r_u->needed     = needed;
+       r_u->returned   = num_entries;
+
+       if (needed > in_size) {
+               result = WERR_MORE_DATA;
+               goto done;
+       }
+               
+       /* copy data into the reply */
+       
+       r_u->ctr.size           = r_u->needed;
+       r_u->ctr.size_of_array  = r_u->returned;
+       r_u->ctr.values         = enum_values;
+       
+       
+               
+done:  
+       if ( printer )
+       free_a_printer(&printer, 2);
+
+       return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_printprocessordirectory_1(PRINTPROCESSOR_DIRECTORY_1 *info, char *name)
+{
+       init_unistr(&info->name, name);
+}
+
+static WERROR getprintprocessordirectory_level_1(UNISTR2 *name, 
+                                                UNISTR2 *environment, 
+                                                NEW_BUFFER *buffer, 
+                                                uint32 offered, 
+                                                uint32 *needed)
+{
+       pstring path;
+       pstring long_archi;
+       pstring short_archi;
+       PRINTPROCESSOR_DIRECTORY_1 *info=NULL;
+
+       unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1);
+
+       if (get_short_archi(short_archi, long_archi)==False)
+               return WERR_INVALID_ENVIRONMENT;
+
+       if((info=(PRINTPROCESSOR_DIRECTORY_1 *)malloc(sizeof(PRINTPROCESSOR_DIRECTORY_1))) == NULL)
+               return WERR_NOMEM;
+
+       pstrcpy(path, "C:\\WINNT\\System32\\spool\\PRTPROCS\\W32X86");
+
+       fill_printprocessordirectory_1(info, path);
+       
+       *needed += spoolss_size_printprocessordirectory_info_1(info);
+
+       if (!alloc_buffer_size(buffer, *needed)) {
+               safe_free(info);
+               return WERR_INSUFFICIENT_BUFFER;
+       }
+
+       smb_io_printprocessordirectory_1("", buffer, info, 0);
+
+       safe_free(info);
+       
+       if (*needed > offered)
+               return WERR_INSUFFICIENT_BUFFER;
+       else
+               return WERR_OK;
+}
+
+WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u)
+{
+       uint32 level = q_u->level;
+       NEW_BUFFER *buffer = NULL;
+       uint32 offered = q_u->offered;
+       uint32 *needed = &r_u->needed;
+       WERROR result;
+
+       /* that's an [in out] buffer */
+       spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+       buffer = r_u->buffer;
+
+       DEBUG(5,("_spoolss_getprintprocessordirectory\n"));
+       
+       *needed=0;
+
+       switch(level) {
+       case 1:
+               result = getprintprocessordirectory_level_1
+                 (&q_u->name, &q_u->environment, buffer, offered, needed);
+               break;
+       default:
+               result = WERR_UNKNOWN_LEVEL;
+       }
+
+       return result;
+}
+
+#if 0
+
+WERROR _spoolss_replyopenprinter(pipes_struct *p, SPOOL_Q_REPLYOPENPRINTER *q_u, 
+                                SPOOL_R_REPLYOPENPRINTER *r_u)
+{
+       DEBUG(5,("_spoolss_replyopenprinter\n"));
+
+       DEBUG(10, ("replyopenprinter for localprinter %d\n", q_u->printer));
+
+       return WERR_OK;
+}
+
+WERROR _spoolss_replycloseprinter(pipes_struct *p, SPOOL_Q_REPLYCLOSEPRINTER *q_u, 
+                                 SPOOL_R_REPLYCLOSEPRINTER *r_u)
+{
+       DEBUG(5,("_spoolss_replycloseprinter\n"));
+       return WERR_OK;
+}
+
+#endif
diff --git a/source4/rpc_server/srv_srvsvc.c b/source4/rpc_server/srv_srvsvc.c
new file mode 100644 (file)
index 0000000..7c5e317
--- /dev/null
@@ -0,0 +1,557 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Jeremy Allison                    2001,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *  
+ *  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.
+ */
+
+/* This is the interface to the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_get_info(pipes_struct *p)
+{
+       SRV_Q_NET_SRV_GET_INFO q_u;
+       SRV_R_NET_SRV_GET_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net server get info */
+       if (!srv_io_q_net_srv_get_info("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _srv_net_srv_get_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if (!srv_io_r_net_srv_get_info("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_set_info(pipes_struct *p)
+{
+       SRV_Q_NET_SRV_SET_INFO q_u;
+       SRV_R_NET_SRV_SET_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net server set info */
+       if (!srv_io_q_net_srv_set_info("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _srv_net_srv_set_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if (!srv_io_r_net_srv_set_info("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_srv_net_file_enum
+********************************************************************/
+
+static BOOL api_srv_net_file_enum(pipes_struct *p)
+{
+       SRV_Q_NET_FILE_ENUM q_u;
+       SRV_R_NET_FILE_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net file enum */
+       if (!srv_io_q_net_file_enum("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _srv_net_file_enum(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!srv_io_r_net_file_enum("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ api_srv_net_conn_enum
+********************************************************************/
+
+static BOOL api_srv_net_conn_enum(pipes_struct *p)
+{
+       SRV_Q_NET_CONN_ENUM q_u;
+       SRV_R_NET_CONN_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net server get enum */
+       if (!srv_io_q_net_conn_enum("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _srv_net_conn_enum(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if (!srv_io_r_net_conn_enum("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ Enumerate sessions.
+********************************************************************/
+
+static BOOL api_srv_net_sess_enum(pipes_struct *p)
+{
+       SRV_Q_NET_SESS_ENUM q_u;
+       SRV_R_NET_SESS_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net server get enum */
+       if (!srv_io_q_net_sess_enum("", &q_u, data, 0))
+               return False;
+
+       /* construct reply.  always indicate success */
+       r_u.status = _srv_net_sess_enum(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if (!srv_io_r_net_sess_enum("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum_all(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_ENUM q_u;
+       SRV_R_NET_SHARE_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server get enum. */
+       if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_enum_all: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_enum_all(p, &q_u, &r_u);
+
+       if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_enum_all: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_ENUM q_u;
+       SRV_R_NET_SHARE_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server get enum. */
+       if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_enum: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_enum(p, &q_u, &r_u);
+
+       if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_enum: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to return share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_get_info(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_GET_INFO q_u;
+       SRV_R_NET_SHARE_GET_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server get info. */
+       if(!srv_io_q_net_share_get_info("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_get_info: Failed to unmarshall SRV_Q_NET_SHARE_GET_INFO.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_get_info(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_share_get_info("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_get_info: Failed to marshall SRV_R_NET_SHARE_GET_INFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to set share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_set_info(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_SET_INFO q_u;
+       SRV_R_NET_SHARE_SET_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server set info. */
+       if(!srv_io_q_net_share_set_info("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_set_info: Failed to unmarshall SRV_Q_NET_SHARE_SET_INFO.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_set_info(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_share_set_info("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_set_info: Failed to marshall SRV_R_NET_SHARE_SET_INFO.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to add share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_add(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_ADD q_u;
+       SRV_R_NET_SHARE_ADD r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server add info. */
+       if(!srv_io_q_net_share_add("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_add: Failed to unmarshall SRV_Q_NET_SHARE_ADD.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_add(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_share_add("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_add: Failed to marshall SRV_R_NET_SHARE_ADD.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_DEL q_u;
+       SRV_R_NET_SHARE_DEL r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server del info. */
+       if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_del: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_del(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_del: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del_sticky(pipes_struct *p)
+{
+       SRV_Q_NET_SHARE_DEL q_u;
+       SRV_R_NET_SHARE_DEL r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server del info. */
+       if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_share_del_sticky: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_share_del_sticky(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_share_del_sticky: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ api_srv_net_remote_tod
+********************************************************************/
+
+static BOOL api_srv_net_remote_tod(pipes_struct *p)
+{
+       SRV_Q_NET_REMOTE_TOD q_u;
+       SRV_R_NET_REMOTE_TOD r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net server get enum */
+       if(!srv_io_q_net_remote_tod("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _srv_net_remote_tod(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!srv_io_r_net_remote_tod("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+/*******************************************************************
+ RPC to enumerate disks available on a server e.g. C:, D: ...
+*******************************************************************/
+
+static BOOL api_srv_net_disk_enum(pipes_struct *p) 
+{
+       SRV_Q_NET_DISK_ENUM q_u;
+       SRV_R_NET_DISK_ENUM r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net server disk enum. */
+       if(!srv_io_q_net_disk_enum("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_disk_enum: Failed to unmarshall SRV_Q_NET_DISK_ENUM.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_disk_enum(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_disk_enum("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_disk_enum: Failed to marshall SRV_R_NET_DISK_ENUM.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ NetValidateName (opnum 0x21) 
+*******************************************************************/
+
+static BOOL api_srv_net_name_validate(pipes_struct *p) 
+{
+       SRV_Q_NET_NAME_VALIDATE q_u;
+       SRV_R_NET_NAME_VALIDATE r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+  
+       /* Unmarshall the net server disk enum. */
+       if(!srv_io_q_net_name_validate("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_name_validate: Failed to unmarshall SRV_Q_NET_NAME_VALIDATE.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_name_validate(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_name_validate("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_name_validate: Failed to marshall SRV_R_NET_NAME_VALIDATE.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ NetFileQuerySecdesc (opnum 0x27)
+*******************************************************************/
+
+static BOOL api_srv_net_file_query_secdesc(pipes_struct *p)
+{
+       SRV_Q_NET_FILE_QUERY_SECDESC q_u;
+       SRV_R_NET_FILE_QUERY_SECDESC r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net file get info from Win9x */
+       if(!srv_io_q_net_file_query_secdesc("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_file_query_secdesc: Failed to unmarshall SRV_Q_NET_FILE_QUERY_SECDESC.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_file_query_secdesc(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_file_query_secdesc("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_file_query_secdesc: Failed to marshall SRV_R_NET_FILE_QUERY_SECDESC.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ NetFileSetSecdesc (opnum 0x28)
+*******************************************************************/
+
+static BOOL api_srv_net_file_set_secdesc(pipes_struct *p)
+{
+       SRV_Q_NET_FILE_SET_SECDESC q_u;
+       SRV_R_NET_FILE_SET_SECDESC r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* Unmarshall the net file set info from Win9x */
+       if(!srv_io_q_net_file_set_secdesc("", &q_u, data, 0)) {
+               DEBUG(0,("api_srv_net_file_set_secdesc: Failed to unmarshall SRV_Q_NET_FILE_SET_SECDESC.\n"));
+               return False;
+       }
+
+       r_u.status = _srv_net_file_set_secdesc(p, &q_u, &r_u);
+
+       if(!srv_io_r_net_file_set_secdesc("", &r_u, rdata, 0)) {
+               DEBUG(0,("api_srv_net_file_set_secdesc: Failed to marshall SRV_R_NET_FILE_SET_SECDESC.\n"));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+\PIPE\srvsvc commands
+********************************************************************/
+
+#ifdef RPC_SVC_DYNAMIC
+int init_module(void)
+#else
+int rpc_srv_init(void)
+#endif
+{
+  static const struct api_struct api_srv_cmds[] =
+    {
+      { "SRV_NET_CONN_ENUM"         , SRV_NET_CONN_ENUM         , api_srv_net_conn_enum          },
+      { "SRV_NET_SESS_ENUM"         , SRV_NET_SESS_ENUM         , api_srv_net_sess_enum          },
+      { "SRV_NET_SHARE_ENUM_ALL"    , SRV_NET_SHARE_ENUM_ALL    , api_srv_net_share_enum_all     },
+      { "SRV_NET_SHARE_ENUM"        , SRV_NET_SHARE_ENUM        , api_srv_net_share_enum         },
+      { "SRV_NET_SHARE_ADD"         , SRV_NET_SHARE_ADD         , api_srv_net_share_add          },
+      { "SRV_NET_SHARE_DEL"         , SRV_NET_SHARE_DEL         , api_srv_net_share_del          },
+      { "SRV_NET_SHARE_DEL_STICKY"  , SRV_NET_SHARE_DEL_STICKY  , api_srv_net_share_del_sticky   },
+      { "SRV_NET_SHARE_GET_INFO"    , SRV_NET_SHARE_GET_INFO    , api_srv_net_share_get_info     },
+      { "SRV_NET_SHARE_SET_INFO"    , SRV_NET_SHARE_SET_INFO    , api_srv_net_share_set_info     },
+      { "SRV_NET_FILE_ENUM"         , SRV_NET_FILE_ENUM         , api_srv_net_file_enum          },
+      { "SRV_NET_SRV_GET_INFO"      , SRV_NET_SRV_GET_INFO      , api_srv_net_srv_get_info       },
+      { "SRV_NET_SRV_SET_INFO"      , SRV_NET_SRV_SET_INFO      , api_srv_net_srv_set_info       },
+      { "SRV_NET_REMOTE_TOD"        , SRV_NET_REMOTE_TOD        , api_srv_net_remote_tod         },
+      { "SRV_NET_DISK_ENUM"         , SRV_NET_DISK_ENUM         , api_srv_net_disk_enum          },
+      { "SRV_NET_NAME_VALIDATE"     , SRV_NET_NAME_VALIDATE     , api_srv_net_name_validate      },
+      { "SRV_NET_FILE_QUERY_SECDESC", SRV_NET_FILE_QUERY_SECDESC, api_srv_net_file_query_secdesc },
+      { "SRV_NET_FILE_SET_SECDESC"  , SRV_NET_FILE_SET_SECDESC  , api_srv_net_file_set_secdesc   }
+    };
+  return rpc_pipe_register_commands("srvsvc", "ntsvcs", api_srv_cmds,
+                                   sizeof(api_srv_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_srvsvc_nt.c b/source4/rpc_server/srv_srvsvc_nt.c
new file mode 100644 (file)
index 0000000..44a63f2
--- /dev/null
@@ -0,0 +1,2138 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Jeremy Allison                                       2001.
+ *  Copyright (C) Nigel Williams                                       2001.
+ *  
+ *  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.
+ */
+
+/* This is the implementation of the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ Utility function to get the 'type' of a share from an snum.
+ ********************************************************************/
+static uint32 get_share_type(int snum) 
+{
+       char *net_name = lp_servicename(snum);
+       int len_net_name = strlen(net_name);
+       
+       /* work out the share type */
+       uint32 type = STYPE_DISKTREE;
+
+       if (lp_print_ok(snum))
+               type = STYPE_PRINTQ;
+       if (strequal(lp_fstype(snum), "IPC"))
+               type = STYPE_IPC;
+       if (net_name[len_net_name] == '$')
+               type |= STYPE_HIDDEN;
+
+       return type;
+}
+       
+/*******************************************************************
+ Fill in a share info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_0(pipes_struct *p, SRV_SHARE_INFO_0 *sh0, int snum)
+{
+       pstring net_name;
+
+       pstrcpy(net_name, lp_servicename(snum));
+
+       init_srv_share_info0(&sh0->info_0, net_name);
+       init_srv_share_info0_str(&sh0->info_0_str, net_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1(pipes_struct *p, SRV_SHARE_INFO_1 *sh1, int snum)
+{
+       pstring remark;
+
+       char *net_name = lp_servicename(snum);
+       pstrcpy(remark, lp_comment(snum));
+       standard_sub_conn(p->conn, remark,sizeof(remark));
+
+       init_srv_share_info1(&sh1->info_1, net_name, get_share_type(snum), remark);
+       init_srv_share_info1_str(&sh1->info_1_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 2 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_2(pipes_struct *p, SRV_SHARE_INFO_2 *sh2, int snum)
+{
+       pstring remark;
+       pstring path;
+       pstring passwd;
+
+       char *net_name = lp_servicename(snum);
+       pstrcpy(remark, lp_comment(snum));
+       standard_sub_conn(p->conn, remark,sizeof(remark));
+       pstrcpy(path, "C:");
+       pstrcat(path, lp_pathname(snum));
+
+       /*
+        * Change / to \\ so that win2k will see it as a valid path.  This was added to
+        * enable use of browsing in win2k add share dialog.
+        */ 
+
+       string_replace(path, '/', '\\');
+
+       pstrcpy(passwd, "");
+
+       init_srv_share_info2(&sh2->info_2, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd);
+       init_srv_share_info2_str(&sh2->info_2_str, net_name, remark, path, passwd);
+}
+
+/*******************************************************************
+ What to do when smb.conf is updated.
+ ********************************************************************/
+
+static void smb_conf_updated(int msg_type, pid_t src, void *buf, size_t len)
+{
+       DEBUG(10,("smb_conf_updated: Got message saying smb.conf was updated. Reloading.\n"));
+       reload_services(False);
+}
+
+/*******************************************************************
+ Create the share security tdb.
+ ********************************************************************/
+
+static TDB_CONTEXT *share_tdb; /* used for share security descriptors */
+#define SHARE_DATABASE_VERSION_V1 1
+#define SHARE_DATABASE_VERSION_V2 2 /* version id in little endian. */
+
+BOOL share_info_db_init(void)
+{
+       static pid_t local_pid;
+       const char *vstring = "INFO/version";
+       int32 vers_id;
+       if (share_tdb && local_pid == sys_getpid())
+               return True;
+       share_tdb = tdb_open_log(lock_path("share_info.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+       if (!share_tdb) {
+               DEBUG(0,("Failed to open share info database %s (%s)\n",
+                       lock_path("share_info.tdb"), strerror(errno) ));
+               return False;
+       }
+       local_pid = sys_getpid();
+       /* handle a Samba upgrade */
+       tdb_lock_bystring(share_tdb, vstring, 0);
+
+       /* Cope with byte-reversed older versions of the db. */
+       vers_id = tdb_fetch_int32(share_tdb, vstring);
+       if ((vers_id == SHARE_DATABASE_VERSION_V1) || (IREV(vers_id) == SHARE_DATABASE_VERSION_V1)) {
+               /* Written on a bigendian machine with old fetch_int code. Save as le. */
+               tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+               vers_id = SHARE_DATABASE_VERSION_V2;
+       }
+
+       if (vers_id != SHARE_DATABASE_VERSION_V2) {
+               tdb_traverse(share_tdb, tdb_traverse_delete_fn, NULL);
+               tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+       }
+       tdb_unlock_bystring(share_tdb, vstring);
+
+       message_register(MSG_SMB_CONF_UPDATED, smb_conf_updated);
+       return True;
+}
+
+/*******************************************************************
+ Fake up a Everyone, full access as a default.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security_default( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+       extern DOM_SID global_sid_World;
+       extern struct generic_mapping file_generic_mapping;
+       SEC_ACCESS sa;
+       SEC_ACE ace;
+       SEC_ACL *psa = NULL;
+       SEC_DESC *psd = NULL;
+       uint32 def_access = GENERIC_ALL_ACCESS;
+
+       se_map_generic(&def_access, &file_generic_mapping);
+
+       init_sec_access(&sa, GENERIC_ALL_ACCESS | def_access );
+       init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) {
+               psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, psize);
+       }
+
+       if (!psd) {
+               DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+               return NULL;
+       }
+
+       return psd;
+}
+
+/*******************************************************************
+ Pull a security descriptor from the share tdb.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+       prs_struct ps;
+       fstring key;
+       SEC_DESC *psd = NULL;
+
+       *psize = 0;
+
+       /* Fetch security descriptor from tdb */
+       slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+       if (tdb_prs_fetch(share_tdb, key, &ps, ctx)!=0 ||
+               !sec_io_desc("get_share_security", &psd, &ps, 1)) {
+               DEBUG(4,("get_share_security: using default secdesc for %s\n", lp_servicename(snum) ));
+               return get_share_security_default(ctx, snum, psize);
+       }
+
+       if (psd)
+               *psize = sec_desc_size(psd);
+
+       prs_mem_free(&ps);
+       return psd;
+}
+
+/*******************************************************************
+ Store a security descriptor in the share db.
+ ********************************************************************/
+
+static BOOL set_share_security(TALLOC_CTX *ctx, const char *share_name, SEC_DESC *psd)
+{
+       prs_struct ps;
+       TALLOC_CTX *mem_ctx = NULL;
+       fstring key;
+       BOOL ret = False;
+
+       mem_ctx = talloc_init("set_share_security");
+       if (mem_ctx == NULL)
+               return False;
+
+       prs_init(&ps, (uint32)sec_desc_size(psd), mem_ctx, MARSHALL);
+       if (!sec_io_desc("share_security", &psd, &ps, 1))
+               goto out;
+       slprintf(key, sizeof(key)-1, "SECDESC/%s", share_name);
+       if (tdb_prs_store(share_tdb, key, &ps)==0) {
+               ret = True;
+               DEBUG(5,("set_share_security: stored secdesc for %s\n", share_name ));
+       } else {
+               DEBUG(1,("set_share_security: Failed to store secdesc for %s\n", share_name ));
+       } 
+
+       /* Free malloc'ed memory */
+out:
+       prs_mem_free(&ps);
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+       return ret;
+}
+
+/*******************************************************************
+ Delete a security descriptor.
+********************************************************************/
+
+static BOOL delete_share_security(int snum)
+{
+       TDB_DATA kbuf;
+       fstring key;
+
+       slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+
+       if (tdb_delete(share_tdb, kbuf) != 0) {
+               DEBUG(0,("delete_share_security: Failed to delete entry for share %s\n",
+                               lp_servicename(snum) ));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Map any generic bits to file specific bits.
+********************************************************************/
+
+void map_generic_share_sd_bits(SEC_DESC *psd)
+{
+       extern struct generic_mapping file_generic_mapping;
+       int i;
+       SEC_ACL *ps_dacl = NULL;
+
+       if (!psd)
+               return;
+
+       ps_dacl = psd->dacl;
+       if (!ps_dacl)
+               return;
+
+       for (i = 0; i < ps_dacl->num_aces; i++) {
+               SEC_ACE *psa = &ps_dacl->ace[i];
+               uint32 orig_mask = psa->info.mask;
+
+               se_map_generic(&psa->info.mask, &file_generic_mapping);
+               psa->info.mask |= orig_mask;
+       }       
+}
+
+/*******************************************************************
+ Can this user access with share with the required permissions ?
+********************************************************************/
+
+BOOL share_access_check(struct request_context *req, struct tcon_context *conn, int snum, uint32 desired_access)
+{
+       uint32 granted;
+       NTSTATUS status;
+       SEC_DESC *psd = NULL;
+       size_t sd_size;
+       NT_USER_TOKEN *token = NULL;
+       BOOL ret = True;
+       struct tcon_context *conn = req->conn;
+
+       psd = get_share_security(req->mem_ctx, snum, &sd_size);
+
+       if (!psd)
+               goto out;
+
+       if (conn->nt_user_token)
+               token = conn->nt_user_token;
+       else 
+               token = req->user_ctx->nt_user_token;
+
+       ret = se_access_check(psd, token, desired_access, &granted, &status);
+
+       return ret;
+}
+
+/*******************************************************************
+ Fill in a share info level 501 structure.
+********************************************************************/
+
+static void init_srv_share_info_501(pipes_struct *p, SRV_SHARE_INFO_501 *sh501, int snum)
+{
+       int len_net_name;
+       pstring remark;
+
+       char *net_name = lp_servicename(snum);
+       pstrcpy(remark, lp_comment(snum));
+       standard_sub_conn(p->conn, remark, sizeof(remark));
+
+       len_net_name = strlen(net_name);
+
+       init_srv_share_info501(&sh501->info_501, net_name, get_share_type(snum), remark, (lp_csc_policy(snum) << 4));
+       init_srv_share_info501_str(&sh501->info_501_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 502 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_502(pipes_struct *p, SRV_SHARE_INFO_502 *sh502, int snum)
+{
+       int len_net_name;
+       pstring net_name;
+       pstring remark;
+       pstring path;
+       pstring passwd;
+       SEC_DESC *sd;
+       size_t sd_size;
+       TALLOC_CTX *ctx = p->mem_ctx;
+
+
+       ZERO_STRUCTP(sh502);
+
+       pstrcpy(net_name, lp_servicename(snum));
+       pstrcpy(remark, lp_comment(snum));
+       standard_sub_conn(p->conn, remark,sizeof(remark));
+       pstrcpy(path, "C:");
+       pstrcat(path, lp_pathname(snum));
+
+       /*
+        * Change / to \\ so that win2k will see it as a valid path.  This was added to
+        * enable use of browsing in win2k add share dialog.
+        */ 
+
+       string_replace(path, '/', '\\');
+
+       pstrcpy(passwd, "");
+       len_net_name = strlen(net_name);
+
+       sd = get_share_security(ctx, snum, &sd_size);
+
+       init_srv_share_info502(&sh502->info_502, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd, sd, sd_size);
+       init_srv_share_info502_str(&sh502->info_502_str, net_name, remark, path, passwd, sd, sd_size);
+}
+
+/***************************************************************************
+ Fill in a share info level 1004 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1004(pipes_struct *p, SRV_SHARE_INFO_1004* sh1004, int snum)
+{
+        pstring remark;
+
+       pstrcpy(remark, lp_comment(snum));
+       standard_sub_conn(p->conn, remark, sizeof(remark));
+
+       ZERO_STRUCTP(sh1004);
+  
+       init_srv_share_info1004(&sh1004->info_1004, remark);
+       init_srv_share_info1004_str(&sh1004->info_1004_str, remark);
+}
+
+/***************************************************************************
+ Fill in a share info level 1005 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1005(pipes_struct *p, SRV_SHARE_INFO_1005* sh1005, int snum)
+{
+       sh1005->dfs_root_flag = 0;
+
+       if(lp_host_msdfs() && lp_msdfs_root(snum))
+               sh1005->dfs_root_flag = 3;
+}
+/***************************************************************************
+ Fill in a share info level 1006 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1006(pipes_struct *p, SRV_SHARE_INFO_1006* sh1006, int snum)
+{
+       sh1006->max_uses = -1;
+}
+
+/***************************************************************************
+ Fill in a share info level 1007 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1007(pipes_struct *p, SRV_SHARE_INFO_1007* sh1007, int snum)
+{
+        pstring alternate_directory_name = "";
+       uint32 flags = 0;
+
+       ZERO_STRUCTP(sh1007);
+  
+       init_srv_share_info1007(&sh1007->info_1007, flags, alternate_directory_name);
+       init_srv_share_info1007_str(&sh1007->info_1007_str, alternate_directory_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1501 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1501(pipes_struct *p, SRV_SHARE_INFO_1501 *sh1501, int snum)
+{
+       SEC_DESC *sd;
+       size_t sd_size;
+       TALLOC_CTX *ctx = p->mem_ctx;
+
+       ZERO_STRUCTP(sh1501);
+
+       sd = get_share_security(ctx, snum, &sd_size);
+
+       sh1501->sdb = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+}
+
+/*******************************************************************
+ True if it ends in '$'.
+ ********************************************************************/
+
+static BOOL is_hidden_share(int snum)
+{
+       pstring net_name;
+
+       pstrcpy(net_name, lp_servicename(snum));
+       return (net_name[strlen(net_name)] == '$') ? True : False;
+}
+
+/*******************************************************************
+ Fill in a share info structure.
+ ********************************************************************/
+
+static BOOL init_srv_share_info_ctr(pipes_struct *p, SRV_SHARE_INFO_CTR *ctr,
+              uint32 info_level, uint32 *resume_hnd, uint32 *total_entries, BOOL all_shares)
+{
+       int num_entries = 0;
+       int num_services = lp_numservices();
+       int snum;
+       TALLOC_CTX *ctx = p->mem_ctx;
+
+       DEBUG(5,("init_srv_share_info_ctr\n"));
+
+       ZERO_STRUCTPN(ctr);
+
+       ctr->info_level = ctr->switch_value = info_level;
+       *resume_hnd = 0;
+
+       /* Count the number of entries. */
+       for (snum = 0; snum < num_services; snum++) {
+               if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) )
+                       num_entries++;
+       }
+
+       *total_entries = num_entries;
+       ctr->num_entries2 = ctr->num_entries = num_entries;
+       ctr->ptr_share_info = ctr->ptr_entries = 1;
+
+       if (!num_entries)
+               return True;
+
+       switch (info_level) {
+       case 0:
+       {
+               SRV_SHARE_INFO_0 *info0;
+               int i = 0;
+
+               info0 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_0));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_0(p, &info0[i++], snum);
+                       }
+               }
+
+               ctr->share.info0 = info0;
+               break;
+
+       }
+
+       case 1:
+       {
+               SRV_SHARE_INFO_1 *info1;
+               int i = 0;
+
+               info1 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1(p, &info1[i++], snum);
+                       }
+               }
+
+               ctr->share.info1 = info1;
+               break;
+       }
+
+       case 2:
+       {
+               SRV_SHARE_INFO_2 *info2;
+               int i = 0;
+
+               info2 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_2));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_2(p, &info2[i++], snum);
+                       }
+               }
+
+               ctr->share.info2 = info2;
+               break;
+       }
+
+       case 501:
+       {
+               SRV_SHARE_INFO_501 *info501;
+               int i = 0;
+       
+               info501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_501));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_501(p, &info501[i++], snum);
+                       }
+               }
+       
+               ctr->share.info501 = info501;
+               break;
+       }
+
+       case 502:
+       {
+               SRV_SHARE_INFO_502 *info502;
+               int i = 0;
+
+               info502 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_502));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_502(p, &info502[i++], snum);
+                       }
+               }
+
+               ctr->share.info502 = info502;
+               break;
+       }
+
+       /* here for completeness but not currently used with enum (1004 - 1501)*/
+       
+       case 1004:
+       {
+               SRV_SHARE_INFO_1004 *info1004;
+               int i = 0;
+
+               info1004 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1004));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1004(p, &info1004[i++], snum);
+                       }
+               }
+
+               ctr->share.info1004 = info1004;
+               break;
+       }
+
+       case 1005:
+       {
+               SRV_SHARE_INFO_1005 *info1005;
+               int i = 0;
+
+               info1005 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1005));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1005(p, &info1005[i++], snum);
+                       }
+               }
+
+               ctr->share.info1005 = info1005;
+               break;
+       }
+
+       case 1006:
+       {
+               SRV_SHARE_INFO_1006 *info1006;
+               int i = 0;
+
+               info1006 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1006));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1006(p, &info1006[i++], snum);
+                       }
+               }
+
+               ctr->share.info1006 = info1006;
+               break;
+       }
+
+       case 1007:
+       {
+               SRV_SHARE_INFO_1007 *info1007;
+               int i = 0;
+
+               info1007 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1007));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1007(p, &info1007[i++], snum);
+                       }
+               }
+
+               ctr->share.info1007 = info1007;
+               break;
+       }
+
+       case 1501:
+       {
+               SRV_SHARE_INFO_1501 *info1501;
+               int i = 0;
+
+               info1501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1501));
+
+               for (snum = *resume_hnd; snum < num_services; snum++) {
+                       if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+                               init_srv_share_info_1501(p, &info1501[i++], snum);
+                       }
+               }
+
+               ctr->share.info1501 = info1501;
+               break;
+       }
+       default:
+               DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n", info_level));
+               return False;
+       }
+
+       return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_share_enum(pipes_struct *p, SRV_R_NET_SHARE_ENUM *r_n,
+                                     uint32 info_level, uint32 resume_hnd, BOOL all)  
+{
+       DEBUG(5,("init_srv_r_net_share_enum: %d\n", __LINE__));
+
+       if (init_srv_share_info_ctr(p, &r_n->ctr, info_level,
+                                   &resume_hnd, &r_n->total_entries, all)) {
+               r_n->status = WERR_OK;
+       } else {
+               r_n->status = WERR_UNKNOWN_LEVEL;
+       }
+
+       init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_GET_INFO structure.
+********************************************************************/
+
+static void init_srv_r_net_share_get_info(pipes_struct *p, SRV_R_NET_SHARE_GET_INFO *r_n,
+                                 char *share_name, uint32 info_level)
+{
+       WERROR status = WERR_OK;
+       int snum;
+
+       DEBUG(5,("init_srv_r_net_share_get_info: %d\n", __LINE__));
+
+       r_n->info.switch_value = info_level;
+
+       snum = find_service(share_name);
+
+       if (snum >= 0) {
+               switch (info_level) {
+               case 0:
+                       init_srv_share_info_0(p, &r_n->info.share.info0, snum);
+                       break;
+               case 1:
+                       init_srv_share_info_1(p, &r_n->info.share.info1, snum);
+                       break;
+               case 2:
+                       init_srv_share_info_2(p, &r_n->info.share.info2, snum);
+                       break;
+               case 501:
+                       init_srv_share_info_501(p, &r_n->info.share.info501, snum);
+                       break;
+               case 502:
+                       init_srv_share_info_502(p, &r_n->info.share.info502, snum);
+                       break;
+
+                       /* here for completeness */
+               case 1004:
+                       init_srv_share_info_1004(p, &r_n->info.share.info1004, snum);
+                       break;
+               case 1005:
+                       init_srv_share_info_1005(p, &r_n->info.share.info1005, snum);
+                       break;
+
+                       /* here for completeness 1006 - 1501 */
+               case 1006:
+                       init_srv_share_info_1006(p, &r_n->info.share.info1006, snum);
+                       break;
+               case 1007:
+                       init_srv_share_info_1007(p, &r_n->info.share.info1007, snum);
+                       break;
+               case 1501:
+                       init_srv_share_info_1501(p, &r_n->info.share.info1501, snum);
+                       break;
+               default:
+                       DEBUG(5,("init_srv_net_share_get_info: unsupported switch value %d\n", info_level));
+                       status = WERR_UNKNOWN_LEVEL;
+                       break;
+               }
+       } else {
+               status = WERR_INVALID_NAME;
+       }
+
+       r_n->info.ptr_share_ctr = W_ERROR_IS_OK(status) ? 1 : 0;
+       r_n->status = status;
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_0_info(SESS_INFO_0 *se0, SESS_INFO_0_STR *str0, char *name)
+{
+       init_srv_sess_info0(se0, name);
+       init_srv_sess_info0_str(str0, name);
+}
+
+/*******************************************************************
+ fill in a sess info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_0(SRV_SESS_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+       struct sessionid *session_list;
+       uint32 num_entries = 0;
+       (*stot) = list_sessions(&session_list);
+
+       if (ss0 == NULL) {
+               (*snum) = 0;
+               SAFE_FREE(session_list);
+               return;
+       }
+
+       DEBUG(5,("init_srv_sess_0_ss0\n"));
+
+       if (snum) {
+               for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+                       init_srv_sess_0_info(&ss0->info_0[num_entries],
+                                                                &ss0->info_0_str[num_entries], session_list[(*snum)].remote_machine);
+
+                       /* move on to creating next session */
+                       /* move on to creating next sess */
+                       num_entries++;
+               }
+
+               ss0->num_entries_read  = num_entries;
+               ss0->ptr_sess_info     = num_entries > 0 ? 1 : 0;
+               ss0->num_entries_read2 = num_entries;
+               
+               if ((*snum) >= (*stot)) {
+                       (*snum) = 0;
+               }
+
+       } else {
+               ss0->num_entries_read = 0;
+               ss0->ptr_sess_info = 0;
+               ss0->num_entries_read2 = 0;
+       }
+       SAFE_FREE(session_list);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_1_info(SESS_INFO_1 *se1, SESS_INFO_1_STR *str1,
+                               char *name, char *user,
+                               uint32 num_opens,
+                               uint32 open_time, uint32 idle_time,
+                               uint32 usr_flgs)
+{
+       init_srv_sess_info1(se1 , name, user, num_opens, open_time, idle_time, usr_flgs);
+       init_srv_sess_info1_str(str1, name, user);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_1(SRV_SESS_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+       struct sessionid *session_list;
+       uint32 num_entries = 0;
+       (*stot) = list_sessions(&session_list);
+
+       if (ss1 == NULL) {
+               (*snum) = 0;
+               SAFE_FREE(session_list);
+               return;
+       }
+
+       DEBUG(5,("init_srv_sess_1_ss1\n"));
+
+       if (snum) {
+               for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+                       init_srv_sess_1_info(&ss1->info_1[num_entries],
+                                            &ss1->info_1_str[num_entries],
+                                           session_list[*snum].remote_machine,
+                                            session_list[*snum].username,
+                                            1, 10, 5, 0);
+
+                       /* move on to creating next session */
+                       /* move on to creating next sess */
+                       num_entries++;
+               }
+
+               ss1->num_entries_read  = num_entries;
+               ss1->ptr_sess_info     = num_entries > 0 ? 1 : 0;
+               ss1->num_entries_read2 = num_entries;
+               
+               if ((*snum) >= (*stot)) {
+                       (*snum) = 0;
+               }
+
+       } else {
+               ss1->num_entries_read = 0;
+               ss1->ptr_sess_info = 0;
+               ss1->num_entries_read2 = 0;
+               
+               (*stot) = 0;
+       }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_sess_info_ctr(SRV_SESS_INFO_CTR *ctr,
+                               int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+       WERROR status = WERR_OK;
+       DEBUG(5,("init_srv_sess_info_ctr: %d\n", __LINE__));
+
+       ctr->switch_value = switch_value;
+
+       switch (switch_value) {
+       case 0:
+               init_srv_sess_info_0(&(ctr->sess.info0), resume_hnd, total_entries);
+               ctr->ptr_sess_ctr = 1;
+               break;
+       case 1:
+               init_srv_sess_info_1(&(ctr->sess.info1), resume_hnd, total_entries);
+               ctr->ptr_sess_ctr = 1;
+               break;
+       default:
+               DEBUG(5,("init_srv_sess_info_ctr: unsupported switch value %d\n", switch_value));
+               (*resume_hnd) = 0;
+               (*total_entries) = 0;
+               ctr->ptr_sess_ctr = 0;
+               status = WERR_UNKNOWN_LEVEL;
+               break;
+       }
+
+       return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_sess_enum(SRV_R_NET_SESS_ENUM *r_n,
+                               uint32 resume_hnd, int sess_level, int switch_value)  
+{
+       DEBUG(5,("init_srv_r_net_sess_enum: %d\n", __LINE__));
+
+       r_n->sess_level  = sess_level;
+
+       if (sess_level == -1)
+               r_n->status = WERR_UNKNOWN_LEVEL;
+       else
+               r_n->status = init_srv_sess_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+       if (!W_ERROR_IS_OK(r_n->status))
+               resume_hnd = 0;
+
+       init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ fill in a conn info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_0(SRV_CONN_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+       uint32 num_entries = 0;
+       (*stot) = 1;
+
+       if (ss0 == NULL) {
+               (*snum) = 0;
+               return;
+       }
+
+       DEBUG(5,("init_srv_conn_0_ss0\n"));
+
+       if (snum) {
+               for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+
+                       init_srv_conn_info0(&ss0->info_0[num_entries], (*stot));
+
+                       /* move on to creating next connection */
+                       /* move on to creating next conn */
+                       num_entries++;
+               }
+
+               ss0->num_entries_read  = num_entries;
+               ss0->ptr_conn_info     = num_entries > 0 ? 1 : 0;
+               ss0->num_entries_read2 = num_entries;
+               
+               if ((*snum) >= (*stot)) {
+                       (*snum) = 0;
+               }
+
+       } else {
+               ss0->num_entries_read = 0;
+               ss0->ptr_conn_info = 0;
+               ss0->num_entries_read2 = 0;
+
+               (*stot) = 0;
+       }
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_1_info(CONN_INFO_1 *se1, CONN_INFO_1_STR *str1,
+                               uint32 id, uint32 type,
+                               uint32 num_opens, uint32 num_users, uint32 open_time,
+                               const char *usr_name, const char *net_name)
+{
+       init_srv_conn_info1(se1 , id, type, num_opens, num_users, open_time, usr_name, net_name);
+       init_srv_conn_info1_str(str1, usr_name, net_name);
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_1(SRV_CONN_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+       uint32 num_entries = 0;
+       (*stot) = 1;
+
+       if (ss1 == NULL) {
+               (*snum) = 0;
+               return;
+       }
+
+       DEBUG(5,("init_srv_conn_1_ss1\n"));
+
+       if (snum) {
+               for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+                       init_srv_conn_1_info(&ss1->info_1[num_entries],
+                                                                &ss1->info_1_str[num_entries],
+                                            (*stot), 0x3, 1, 1, 3,"dummy_user", "IPC$");
+
+                       /* move on to creating next connection */
+                       /* move on to creating next conn */
+                       num_entries++;
+               }
+
+               ss1->num_entries_read  = num_entries;
+               ss1->ptr_conn_info     = num_entries > 0 ? 1 : 0;
+               ss1->num_entries_read2 = num_entries;
+               
+
+               if ((*snum) >= (*stot)) {
+                       (*snum) = 0;
+               }
+
+       } else {
+               ss1->num_entries_read = 0;
+               ss1->ptr_conn_info = 0;
+               ss1->num_entries_read2 = 0;
+               
+               (*stot) = 0;
+       }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_conn_info_ctr(SRV_CONN_INFO_CTR *ctr,
+                               int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+       WERROR status = WERR_OK;
+       DEBUG(5,("init_srv_conn_info_ctr: %d\n", __LINE__));
+
+       ctr->switch_value = switch_value;
+
+       switch (switch_value) {
+       case 0:
+               init_srv_conn_info_0(&ctr->conn.info0, resume_hnd, total_entries);
+               ctr->ptr_conn_ctr = 1;
+               break;
+       case 1:
+               init_srv_conn_info_1(&ctr->conn.info1, resume_hnd, total_entries);
+               ctr->ptr_conn_ctr = 1;
+               break;
+       default:
+               DEBUG(5,("init_srv_conn_info_ctr: unsupported switch value %d\n", switch_value));
+               (*resume_hnd = 0);
+               (*total_entries) = 0;
+               ctr->ptr_conn_ctr = 0;
+               status = WERR_UNKNOWN_LEVEL;
+               break;
+       }
+
+       return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_conn_enum(SRV_R_NET_CONN_ENUM *r_n,
+                               uint32 resume_hnd, int conn_level, int switch_value)  
+{
+       DEBUG(5,("init_srv_r_net_conn_enum: %d\n", __LINE__));
+
+       r_n->conn_level  = conn_level;
+       if (conn_level == -1)
+               r_n->status = WERR_UNKNOWN_LEVEL;
+       else
+               r_n->status = init_srv_conn_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+       if (!W_ERROR_IS_OK(r_n->status))
+               resume_hnd = 0;
+
+       init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_file_info_ctr(pipes_struct *p, SRV_FILE_INFO_CTR *ctr,
+                                    int switch_value, uint32 *resume_hnd, 
+                                    uint32 *total_entries)  
+{
+       WERROR status = WERR_OK;
+       TALLOC_CTX *ctx = p->mem_ctx;
+       DEBUG(5,("init_srv_file_info_ctr: %d\n", __LINE__));
+       *total_entries = 1; /* dummy entries only, for */
+
+       ctr->switch_value = switch_value;
+       ctr->num_entries = *total_entries - *resume_hnd;
+       ctr->num_entries2 = ctr->num_entries;
+
+       switch (switch_value) {
+       case 3: {
+               int i;
+               if (*total_entries > 0) {
+                       ctr->ptr_entries = 1;
+                       ctr->file.info3 = talloc(ctx, ctr->num_entries * 
+                                                sizeof(SRV_FILE_INFO_3));
+               }
+               for (i=0 ;i<ctr->num_entries;i++) {
+                       init_srv_file_info3(&ctr->file.info3[i].info_3, i+*resume_hnd, 0x35, 0, "\\PIPE\\samr", "dummy user");
+                       init_srv_file_info3_str(&ctr->file.info3[i].info_3_str,  "\\PIPE\\samr", "dummy user");
+                       
+               }
+               ctr->ptr_file_info = 1;
+               *resume_hnd = 0;
+               break;
+       }
+       default:
+               DEBUG(5,("init_srv_file_info_ctr: unsupported switch value %d\n", switch_value));
+               (*resume_hnd = 0);
+               (*total_entries) = 0;
+               ctr->ptr_entries = 0;
+               status = WERR_UNKNOWN_LEVEL;
+               break;
+       }
+
+       return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_file_enum(pipes_struct *p, SRV_R_NET_FILE_ENUM *r_n,
+                               uint32 resume_hnd, int file_level, int switch_value)  
+{
+       DEBUG(5,("init_srv_r_net_file_enum: %d\n", __LINE__));
+
+       r_n->file_level  = file_level;
+       if (file_level == 0)
+               r_n->status = WERR_UNKNOWN_LEVEL;
+       else
+               r_n->status = init_srv_file_info_ctr(p, &r_n->ctr, switch_value, &resume_hnd, &(r_n->total_entries));
+
+       if (!W_ERROR_IS_OK(r_n->status))
+               resume_hnd = 0;
+
+       init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+net server get info
+********************************************************************/
+
+WERROR _srv_net_srv_get_info(pipes_struct *p, SRV_Q_NET_SRV_GET_INFO *q_u, SRV_R_NET_SRV_GET_INFO *r_u)
+{
+       WERROR status = WERR_OK;
+       SRV_INFO_CTR *ctr = (SRV_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_INFO_CTR));
+
+       if (!ctr)
+               return WERR_NOMEM;
+
+       ZERO_STRUCTP(ctr);
+
+       DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to srv_net_srv_get_info\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       switch (q_u->switch_value) {
+
+               /* Technically level 102 should only be available to
+                  Administrators but there isn't anything super-secret
+                  here, as most of it is made up. */
+
+       case 102:
+               init_srv_info_102(&ctr->srv.sv102,
+                                 500, lp_netbios_name(), 
+                                 string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH),
+                                 lp_major_announce_version(), lp_minor_announce_version(),
+                                 lp_default_server_announce(),
+                                 0xffffffff, /* users */
+                                 0xf, /* disc */
+                                 0, /* hidden */
+                                 240, /* announce */
+                                 3000, /* announce delta */
+                                 100000, /* licenses */
+                                 "c:\\"); /* user path */
+               break;
+       case 101:
+               init_srv_info_101(&ctr->srv.sv101,
+                                 500, lp_netbios_name(),
+                                 lp_major_announce_version(), lp_minor_announce_version(),
+                                 lp_default_server_announce(),
+                                 string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+               break;
+       case 100:
+               init_srv_info_100(&ctr->srv.sv100, 500, lp_netbios_name());
+               break;
+       default:
+               status = WERR_UNKNOWN_LEVEL;
+               break;
+       }
+
+       /* set up the net server get info structure */
+       init_srv_r_net_srv_get_info(r_u, q_u->switch_value, ctr, status);
+
+       DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+net server set info
+********************************************************************/
+
+WERROR _srv_net_srv_set_info(pipes_struct *p, SRV_Q_NET_SRV_SET_INFO *q_u, SRV_R_NET_SRV_SET_INFO *r_u)
+{
+       WERROR status = WERR_OK;
+
+       DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+       /* Set up the net server set info structure. */
+
+       init_srv_r_net_srv_set_info(r_u, 0x0, status);
+
+       DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+net file enum
+********************************************************************/
+
+WERROR _srv_net_file_enum(pipes_struct *p, SRV_Q_NET_FILE_ENUM *q_u, SRV_R_NET_FILE_ENUM *r_u)
+{
+       DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+       /* set up the */
+       init_srv_r_net_file_enum(p, r_u,
+                               get_enum_hnd(&q_u->enum_hnd),
+                               q_u->file_level,
+                               q_u->ctr.switch_value);
+
+       DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+net conn enum
+********************************************************************/
+
+WERROR _srv_net_conn_enum(pipes_struct *p, SRV_Q_NET_CONN_ENUM *q_u, SRV_R_NET_CONN_ENUM *r_u)
+{
+       DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+       r_u->ctr = (SRV_CONN_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_CONN_INFO_CTR));
+       if (!r_u->ctr)
+               return WERR_NOMEM;
+
+       ZERO_STRUCTP(r_u->ctr);
+
+       /* set up the */
+       init_srv_r_net_conn_enum(r_u,
+                               get_enum_hnd(&q_u->enum_hnd),
+                               q_u->conn_level,
+                               q_u->ctr->switch_value);
+
+       DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+net sess enum
+********************************************************************/
+
+WERROR _srv_net_sess_enum(pipes_struct *p, SRV_Q_NET_SESS_ENUM *q_u, SRV_R_NET_SESS_ENUM *r_u)
+{
+       DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+       r_u->ctr = (SRV_SESS_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_SESS_INFO_CTR));
+       if (!r_u->ctr)
+               return WERR_NOMEM;
+
+       ZERO_STRUCTP(r_u->ctr);
+
+       /* set up the */
+       init_srv_r_net_sess_enum(r_u,
+                               get_enum_hnd(&q_u->enum_hnd),
+                               q_u->sess_level,
+                               q_u->ctr->switch_value);
+
+       DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum all.
+********************************************************************/
+
+WERROR _srv_net_share_enum_all(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+       DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to srv_net_share_enum_all\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       /* Create the list of shares for the response. */
+       init_srv_r_net_share_enum(p, r_u,
+                               q_u->ctr.info_level,
+                               get_enum_hnd(&q_u->enum_hnd), True);
+
+       DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum.
+********************************************************************/
+
+WERROR _srv_net_share_enum(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+       DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+       if (!pipe_access_check(p)) {
+               DEBUG(3, ("access denied to srv_net_share_enum\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       /* Create the list of shares for the response. */
+       init_srv_r_net_share_enum(p, r_u,
+                                 q_u->ctr.info_level,
+                                 get_enum_hnd(&q_u->enum_hnd), False);
+
+       DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ Net share get info.
+********************************************************************/
+
+WERROR _srv_net_share_get_info(pipes_struct *p, SRV_Q_NET_SHARE_GET_INFO *q_u, SRV_R_NET_SHARE_GET_INFO *r_u)
+{
+       fstring share_name;
+
+       DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+       /* Create the list of shares for the response. */
+       unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+       init_srv_r_net_share_get_info(p, r_u, share_name, q_u->info_level);
+
+       DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/*******************************************************************
+ Check a given DOS pathname is valid for a share.
+********************************************************************/
+
+static char *valid_share_pathname(char *dos_pathname)
+{
+       pstring saved_pathname;
+       pstring unix_pathname;
+       char *ptr;
+       int ret;
+
+       /* Convert any '\' paths to '/' */
+       unix_format(dos_pathname);
+       unix_clean_name(dos_pathname);
+
+       /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */
+       ptr = dos_pathname;
+       if (strlen(dos_pathname) > 2 && ptr[1] == ':' && ptr[0] != '/')
+               ptr += 2;
+
+       /* Only abolute paths allowed. */
+       if (*ptr != '/')
+               return NULL;
+
+       /* Can we cd to it ? */
+
+       /* First save our current directory. */
+       if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL)
+               return False;
+
+       pstrcpy(unix_pathname, ptr);
+       
+       ret = chdir(unix_pathname);
+
+       /* We *MUST* be able to chdir back. Abort if we can't. */
+       if (chdir(saved_pathname) == -1)
+               smb_panic("valid_share_pathname: Unable to restore current directory.\n");
+
+       return (ret != -1) ? ptr : NULL;
+}
+
+/*******************************************************************
+ Net share set info. Modify share details.
+********************************************************************/
+
+WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, SRV_R_NET_SHARE_SET_INFO *r_u)
+{
+       struct current_user user;
+       pstring command;
+       fstring share_name;
+       fstring comment;
+       pstring pathname;
+       int type;
+       int snum;
+       int ret;
+       char *ptr;
+       SEC_DESC *psd = NULL;
+
+       DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+       unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+       r_u->parm_error = 0;
+
+       if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+               return WERR_ACCESS_DENIED;
+
+       snum = find_service(share_name);
+
+       /* Does this share exist ? */
+       if (snum < 0)
+               return WERR_INVALID_NAME;
+
+       /* No change to printer shares. */
+       if (lp_print_ok(snum))
+               return WERR_ACCESS_DENIED;
+
+       get_current_user(&user,p);
+
+       if (user.uid != sec_initial_uid())
+               return WERR_ACCESS_DENIED;
+
+       switch (q_u->info_level) {
+       case 1:
+               pstrcpy(pathname, lp_pathname(snum));
+               unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+               type = q_u->info.share.info2.info_2.type;
+               psd = NULL;
+               break;
+       case 2:
+               unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+               unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(pathname));
+               type = q_u->info.share.info2.info_2.type;
+               psd = NULL;
+               break;
+#if 0
+               /* not supported on set but here for completeness */
+       case 501:
+               unistr2_to_ascii(comment, &q_u->info.share.info501.info_501_str.uni_remark, sizeof(comment));
+               type = q_u->info.share.info501.info_501.type;
+               psd = NULL;
+               break;
+#endif
+       case 502:
+               unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(comment));
+               unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(pathname));
+               type = q_u->info.share.info502.info_502.type;
+               psd = q_u->info.share.info502.info_502_str.sd;
+               map_generic_share_sd_bits(psd);
+               break;
+       case 1004:
+               pstrcpy(pathname, lp_pathname(snum));
+               unistr2_to_ascii(comment, &q_u->info.share.info1004.info_1004_str.uni_remark, sizeof(comment));
+               type = STYPE_DISKTREE;
+               break;
+       case 1005:
+       case 1006:
+       case 1007:
+               return WERR_ACCESS_DENIED;
+               break;
+       case 1501:
+               pstrcpy(pathname, lp_pathname(snum));
+               fstrcpy(comment, lp_comment(snum));
+               psd = q_u->info.share.info1501.sdb->sec;
+               map_generic_share_sd_bits(psd);
+               type = STYPE_DISKTREE;
+               break;
+       default:
+               DEBUG(5,("_srv_net_share_set_info: unsupported switch value %d\n", q_u->info_level));
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+       /* We can only modify disk shares. */
+       if (type != STYPE_DISKTREE)
+               return WERR_ACCESS_DENIED;
+               
+       /* Check if the pathname is valid. */
+       if (!(ptr = valid_share_pathname( pathname )))
+               return WERR_OBJECT_PATH_INVALID;
+
+       /* Ensure share name, pathname and comment don't contain '"' characters. */
+       string_replace(share_name, '"', ' ');
+       string_replace(ptr, '"', ' ');
+       string_replace(comment, '"', ' ');
+
+       DEBUG(10,("_srv_net_share_set_info: change share command = %s\n",
+               lp_change_share_cmd() ? lp_change_share_cmd() : "NULL" ));
+
+       /* Only call modify function if something changed. */
+
+       if (strcmp(ptr, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) {
+               if (!lp_change_share_cmd() || !*lp_change_share_cmd())
+                       return WERR_ACCESS_DENIED;
+
+               slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+                               lp_change_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment);
+
+               DEBUG(10,("_srv_net_share_set_info: Running [%s]\n", command ));
+               if ((ret = smbrun(command, NULL)) != 0) {
+                       DEBUG(0,("_srv_net_share_set_info: Running [%s] returned (%d)\n", command, ret ));
+                       return WERR_ACCESS_DENIED;
+               }
+
+               /* Tell everyone we updated smb.conf. */
+               message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+       } else {
+               DEBUG(10,("_srv_net_share_set_info: No change to share name (%s)\n", share_name ));
+       }
+
+       /* Replace SD if changed. */
+       if (psd) {
+               SEC_DESC *old_sd;
+               size_t sd_size;
+
+               old_sd = get_share_security(p->mem_ctx, snum, &sd_size);
+
+               if (old_sd && !sec_desc_equal(old_sd, psd)) {
+                       if (!set_share_security(p->mem_ctx, share_name, psd))
+                               DEBUG(0,("_srv_net_share_set_info: Failed to change security info in share %s.\n",
+                                       share_name ));
+               }
+       }
+
+       DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+       return WERR_OK;
+}
+
+/*******************************************************************
+ Net share add. Call 'add_share_command "sharename" "pathname" "comment" "read only = xxx"'
+********************************************************************/
+
+WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_SHARE_ADD *r_u)
+{
+       struct current_user user;
+       pstring command;
+       fstring share_name;
+       fstring comment;
+       pstring pathname;
+       int type;
+       int snum;
+       int ret;
+       char *ptr;
+       SEC_DESC *psd = NULL;
+
+       DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+       r_u->parm_error = 0;
+
+       get_current_user(&user,p);
+
+       if (user.uid != sec_initial_uid()) {
+               DEBUG(10,("_srv_net_share_add: uid != sec_initial_uid(). Access denied.\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       if (!lp_add_share_cmd() || !*lp_add_share_cmd()) {
+               DEBUG(10,("_srv_net_share_add: No add share command\n"));
+               return WERR_ACCESS_DENIED;
+       }
+
+       switch (q_u->info_level) {
+       case 0:
+               /* No path. Not enough info in a level 0 to do anything. */
+               return WERR_ACCESS_DENIED;
+       case 1:
+               /* Not enough info in a level 1 to do anything. */
+               return WERR_ACCESS_DENIED;
+       case 2:
+               unistr2_to_ascii(share_name, &q_u->info.share.info2.info_2_str.uni_netname, sizeof(share_name));
+               unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(share_name));
+               unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(share_name));
+               type = q_u->info.share.info2.info_2.type;
+               break;
+       case 501:
+               /* No path. Not enough info in a level 501 to do anything. */
+               return WERR_ACCESS_DENIED;
+       case 502:
+               unistr2_to_ascii(share_name, &q_u->info.share.info502.info_502_str.uni_netname, sizeof(share_name));
+               unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(share_name));
+               unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(share_name));
+               type = q_u->info.share.info502.info_502.type;
+               psd = q_u->info.share.info502.info_502_str.sd;
+               map_generic_share_sd_bits(psd);
+               break;
+
+               /* none of the following contain share names.  NetShareAdd does not have a separate parameter for the share name */ 
+
+       case 1004:
+       case 1005:
+       case 1006:
+       case 1007:
+               return WERR_ACCESS_DENIED;
+               break;
+       case 1501:
+               /* DFS only level. */
+               return WERR_ACCESS_DENIED;
+       default:
+               DEBUG(5,("_srv_net_share_add: unsupported switch value %d\n", q_u->info_level));
+               return WERR_UNKNOWN_LEVEL;
+       }
+
+       if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+               return WERR_ACCESS_DENIED;
+
+       snum = find_service(share_name);
+
+       /* Share already exists. */
+       if (snum >= 0)
+               return WERR_ALREADY_EXISTS;
+
+       /* We can only add disk shares. */
+       if (type != STYPE_DISKTREE)
+               return WERR_ACCESS_DENIED;
+               
+       /* Check if the pathname is valid. */
+       if (!(ptr = valid_share_pathname( pathname )))
+               return WERR_OBJECT_PATH_INVALID;
+
+       /* Ensure share name, pathname and comment don't contain '"' characters. */
+       string_replace(share_name, '"', ' ');
+       string_replace(ptr, '"', ' ');
+       string_replace(comment, '"', ' ');
+
+       slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+                       lp_add_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment);
+
+       DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+       if ((ret = smbrun(command, NULL)) != 0) {
+               DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+               return WERR_ACCESS_DENIED;
+       }
+
+       if (psd) {
+               if (!set_share_security(p->mem_ctx, share_name, psd))
+                       DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n",
+                               share_name ));
+       }
+
+       /* Tell everyone we updated smb.conf. */
+       message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+       /*
+        * We don't call reload_services() here, the message will
+        * cause this to be done before the next packet is read
+        * from the client. JRA.
+        */
+
+       DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+       return WERR_OK;
+}
+
+/*******************************************************************
+ Net share delete. Call "delete share command" with the share name as
+ a parameter.
+********************************************************************/
+
+WERROR _srv_net_share_del(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+       struct current_user user;
+       pstring command;
+       fstring share_name;
+       int ret;
+       int snum;
+
+       DEBUG(5,("_srv_net_share_del: %d\n", __LINE__));
+
+       unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+       if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+               return WERR_ACCESS_DENIED;
+
+       snum = find_service(share_name);
+
+       if (snum < 0)
+               return WERR_NO_SUCH_SHARE;
+
+       /* No change to printer shares. */
+       if (lp_print_ok(snum))
+               return WERR_ACCESS_DENIED;
+
+       get_current_user(&user,p);
+
+       if (user.uid != sec_initial_uid())
+               return WERR_ACCESS_DENIED;
+
+       if (!lp_delete_share_cmd() || !*lp_delete_share_cmd())
+               return WERR_ACCESS_DENIED;
+
+       slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+                       lp_delete_share_cmd(), dyn_CONFIGFILE, lp_servicename(snum));
+
+       DEBUG(10,("_srv_net_share_del: Running [%s]\n", command ));
+       if ((ret = smbrun(command, NULL)) != 0) {
+               DEBUG(0,("_srv_net_share_del: Running [%s] returned (%d)\n", command, ret ));
+               return WERR_ACCESS_DENIED;
+       }
+
+       /* Delete the SD in the database. */
+       delete_share_security(snum);
+
+       /* Tell everyone we updated smb.conf. */
+       message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+       lp_killservice(snum);
+
+       return WERR_OK;
+}
+
+WERROR _srv_net_share_del_sticky(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+       DEBUG(5,("_srv_net_share_del_stick: %d\n", __LINE__));
+
+       return _srv_net_share_del(p, q_u, r_u);
+}
+
+/*******************************************************************
+time of day
+********************************************************************/
+
+WERROR _srv_net_remote_tod(pipes_struct *p, SRV_Q_NET_REMOTE_TOD *q_u, SRV_R_NET_REMOTE_TOD *r_u)
+{
+       TIME_OF_DAY_INFO *tod;
+       struct tm *t;
+       time_t unixdate = time(NULL);
+
+       tod = (TIME_OF_DAY_INFO *)talloc(p->mem_ctx, sizeof(TIME_OF_DAY_INFO));
+       if (!tod)
+               return WERR_NOMEM;
+
+       ZERO_STRUCTP(tod);
+       r_u->tod = tod;
+       r_u->ptr_srv_tod = 0x1;
+       r_u->status = WERR_OK;
+
+       DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+       t = gmtime(&unixdate);
+
+       /* set up the */
+       init_time_of_day_info(tod,
+                             unixdate,
+                             0,
+                             t->tm_hour,
+                             t->tm_min,
+                             t->tm_sec,
+                             0,
+                             TimeDiff(unixdate)/60,
+                             10000,
+                             t->tm_mday,
+                             t->tm_mon + 1,
+                             1900+t->tm_year,
+                             t->tm_wday);
+       
+       DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+       return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools get security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC *q_u,
+                       SRV_R_NET_FILE_QUERY_SECDESC *r_u)
+{
+       SEC_DESC *psd = NULL;
+       size_t sd_size;
+       DATA_BLOB null_pw;
+       pstring filename;
+       pstring qualname;
+       files_struct *fsp = NULL;
+       SMB_STRUCT_STAT st;
+       BOOL bad_path;
+       int access_mode;
+       int action;
+       NTSTATUS nt_status;
+       struct current_user user;
+       struct tcon_context *conn = NULL;
+       BOOL became_user = False; 
+
+       ZERO_STRUCT(st);
+
+       r_u->status = WERR_OK;
+
+       unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+       /* Null password is ok - we are already an authenticated user... */
+       null_pw = data_blob(NULL, 0);
+
+       get_current_user(&user, p);
+
+       become_root();
+       conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+       unbecome_root();
+
+       if (conn == NULL) {
+               DEBUG(3,("_srv_net_file_query_secdesc: Unable to connect to %s\n", qualname));
+               r_u->status = ntstatus_to_werror(nt_status);
+               goto error_exit;
+       }
+
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("_srv_net_file_query_secdesc: Can't become connected user!\n"));
+               r_u->status = WERR_ACCESS_DENIED;
+               goto error_exit;
+       }
+    became_user = True;
+
+       unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+       unix_convert(filename, conn, NULL, &bad_path, &st);
+       fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                               (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &action);
+
+       if (!fsp) {
+               /* Perhaps it is a directory */
+               if (errno == EISDIR)
+                       fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+                                       (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, &action);
+
+               if (!fsp) {
+                       DEBUG(3,("_srv_net_file_query_secdesc: Unable to open file %s\n", filename));
+                       r_u->status = WERR_ACCESS_DENIED;
+                       goto error_exit;
+               }
+       }
+
+       sd_size = conn->vfs_ops.get_nt_acl(fsp, fsp->fsp_name, &psd);
+
+       if (sd_size == 0) {
+               DEBUG(3,("_srv_net_file_query_secdesc: Unable to get NT ACL for file %s\n", filename));
+               r_u->status = WERR_ACCESS_DENIED;
+               goto error_exit;
+       }
+
+       r_u->ptr_response = 1;
+       r_u->size_response = sd_size;
+       r_u->ptr_secdesc = 1;
+       r_u->size_secdesc = sd_size;
+       r_u->sec_desc = psd;
+
+       psd->dacl->revision = (uint16) NT4_ACL_REVISION;
+
+       close_file(fsp, True);
+       unbecome_user();
+       close_cnum(conn, user.vuid);
+       return r_u->status;
+
+error_exit:
+
+       if(fsp) {
+               close_file(fsp, True);
+       }
+
+       if (became_user)
+               unbecome_user();
+
+       if (conn) 
+               close_cnum(conn, user.vuid);
+
+       return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools set security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_u,
+                                                                       SRV_R_NET_FILE_SET_SECDESC *r_u)
+{
+       BOOL ret;
+       pstring filename;
+       pstring qualname;
+       DATA_BLOB null_pw;
+       files_struct *fsp = NULL;
+       SMB_STRUCT_STAT st;
+       BOOL bad_path;
+       int access_mode;
+       int action;
+       NTSTATUS nt_status;
+       struct current_user user;
+       struct tcon_context *conn = NULL;
+       BOOL became_user = False;
+
+       ZERO_STRUCT(st);
+
+       r_u->status = WERR_OK;
+
+       unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+       /* Null password is ok - we are already an authenticated user... */
+       null_pw = data_blob(NULL, 0);
+
+       get_current_user(&user, p);
+
+       become_root();
+       conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+       unbecome_root();
+
+       if (conn == NULL) {
+               DEBUG(3,("_srv_net_file_set_secdesc: Unable to connect to %s\n", qualname));
+               r_u->status = ntstatus_to_werror(nt_status);
+               goto error_exit;
+       }
+
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("_srv_net_file_set_secdesc: Can't become connected user!\n"));
+               r_u->status = WERR_ACCESS_DENIED;
+               goto error_exit;
+       }
+       became_user = True;
+
+       unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+       unix_convert(filename, conn, NULL, &bad_path, &st);
+
+       fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR),
+                       (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &action);
+
+       if (!fsp) {
+               /* Perhaps it is a directory */
+               if (errno == EISDIR)
+                       fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+                                               (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, &action);
+
+               if (!fsp) {
+                       DEBUG(3,("_srv_net_file_set_secdesc: Unable to open file %s\n", filename));
+                       r_u->status = WERR_ACCESS_DENIED;
+                       goto error_exit;
+               }
+       }
+
+       ret = conn->vfs_ops.set_nt_acl(fsp, fsp->fsp_name, q_u->sec_info, q_u->sec_desc);
+
+       if (ret == False) {
+               DEBUG(3,("_srv_net_file_set_secdesc: Unable to set NT ACL on file %s\n", filename));
+               r_u->status = WERR_ACCESS_DENIED;
+               goto error_exit;
+       }
+
+       close_file(fsp, True);
+       unbecome_user();
+       close_cnum(conn, user.vuid);
+       return r_u->status;
+
+error_exit:
+
+       if(fsp) {
+               close_file(fsp, True);
+       }
+
+       if (became_user)
+               unbecome_user();
+
+       if (conn) 
+               close_cnum(conn, user.vuid);
+
+       return r_u->status;
+}
+
+/***********************************************************************************
+ It may be that we want to limit users to creating shares on certain areas of the UNIX file area.
+ We could define areas by mapping Windows style disks to points on the UNIX directory hierarchy.
+ These disks would the disks listed by this function.
+ Users could then create shares relative to these disks.  Watch out for moving these disks around.
+ "Nigel Williams" <nigel@veritas.com>.
+***********************************************************************************/
+
+static const char *server_disks[] = {"C:"};
+
+static uint32 get_server_disk_count(void)
+{
+       return sizeof(server_disks)/sizeof(server_disks[0]);
+}
+
+static uint32 init_server_disk_enum(uint32 *resume)
+{
+       uint32 server_disk_count = get_server_disk_count();
+
+       /*resume can be an offset into the list for now*/
+
+       if(*resume & 0x80000000)
+               *resume = 0;
+
+       if(*resume > server_disk_count)
+               *resume = server_disk_count;
+
+       return server_disk_count - *resume;
+}
+
+static const char *next_server_disk_enum(uint32 *resume)
+{
+       const char *disk;
+
+       if(init_server_disk_enum(resume) == 0)
+               return NULL;
+
+       disk = server_disks[*resume];
+
+       (*resume)++;
+
+       DEBUG(10, ("next_server_disk_enum: reporting disk %s. resume handle %d.\n", disk, *resume));
+
+       return disk;
+}
+
+WERROR _srv_net_disk_enum(pipes_struct *p, SRV_Q_NET_DISK_ENUM *q_u, SRV_R_NET_DISK_ENUM *r_u)
+{
+       uint32 i;
+       const char *disk_name;
+       TALLOC_CTX *ctx = p->mem_ctx;
+       uint32 resume=get_enum_hnd(&q_u->enum_hnd);
+
+       r_u->status=WERR_OK;
+
+       r_u->total_entries = init_server_disk_enum(&resume);
+
+       r_u->disk_enum_ctr.unknown = 0; 
+
+       {
+               DISK_INFO *dinfo;
+
+               int dinfo_size = MAX_SERVER_DISK_ENTRIES * sizeof(*dinfo);
+         
+               if(!(dinfo =  talloc(ctx, dinfo_size))) {
+                       return WERR_NOMEM;
+               }
+
+               r_u->disk_enum_ctr.disk_info = dinfo;
+       }
+
+       r_u->disk_enum_ctr.disk_info_ptr = r_u->disk_enum_ctr.disk_info ? 1 : 0;
+
+       /*allow one DISK_INFO for null terminator*/
+
+       for(i = 0; i < MAX_SERVER_DISK_ENTRIES -1 && (disk_name = next_server_disk_enum(&resume)); i++) {
+
+               r_u->disk_enum_ctr.entries_read++;
+
+               /*copy disk name into a unicode string*/
+
+               init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, disk_name);    
+       }
+
+       /* add a terminating null string.  Is this there if there is more data to come? */
+
+       r_u->disk_enum_ctr.entries_read++;
+
+       init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, "");
+
+       init_enum_hnd(&r_u->enum_hnd, resume);
+
+       return r_u->status;
+}
+
+WERROR _srv_net_name_validate(pipes_struct *p, SRV_Q_NET_NAME_VALIDATE *q_u, SRV_R_NET_NAME_VALIDATE *r_u)
+{
+       int snum;
+       fstring share_name;
+
+       r_u->status=WERR_OK;
+
+       switch(q_u->type) {
+
+       case 0x9:
+
+               /*check if share name is ok*/
+               /*also check if we already have a share with this name*/
+
+               unistr2_to_ascii(share_name, &q_u->uni_name, sizeof(share_name));
+               snum = find_service(share_name);
+
+               /* Share already exists. */
+               if (snum >= 0)
+                       r_u->status = WERR_ALREADY_EXISTS;
+               break;
+
+       default:
+               /*unsupported type*/
+               r_u->status = WERR_UNKNOWN_LEVEL;
+               break;
+       }
+
+       return r_u->status;
+}
diff --git a/source4/rpc_server/srv_util.c b/source4/rpc_server/srv_util.c
new file mode 100644 (file)
index 0000000..4eba9c7
--- /dev/null
@@ -0,0 +1,546 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1998
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ *  Copyright (C) Paul Ashton                  1997-1998.
+ *  
+ *  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.
+ */
+
+/*  this module apparently provides an implementation of DCE/RPC over a
+ *  named pipe (IPC$ connection using SMBtrans).  details of DCE/RPC
+ *  documentation are available (in on-line form) from the X-Open group.
+ *
+ *  this module should provide a level of abstraction between SMB
+ *  and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ *  data copies, and network traffic.
+ *
+ *  in this version, which takes a "let's learn what's going on and
+ *  get something running" approach, there is additional network
+ *  traffic generated, but the code should be easier to understand...
+ *
+ *  ... if you read the docs.  or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*
+ * A list of the rids of well known BUILTIN and Domain users
+ * and groups.
+ */
+
+rid_name builtin_alias_rids[] =
+{  
+    { BUILTIN_ALIAS_RID_ADMINS       , "Administrators" },
+    { BUILTIN_ALIAS_RID_USERS        , "Users" },
+    { BUILTIN_ALIAS_RID_GUESTS       , "Guests" },
+    { BUILTIN_ALIAS_RID_POWER_USERS  , "Power Users" },
+   
+    { BUILTIN_ALIAS_RID_ACCOUNT_OPS  , "Account Operators" },
+    { BUILTIN_ALIAS_RID_SYSTEM_OPS   , "System Operators" },
+    { BUILTIN_ALIAS_RID_PRINT_OPS    , "Print Operators" },
+    { BUILTIN_ALIAS_RID_BACKUP_OPS   , "Backup Operators" },
+    { BUILTIN_ALIAS_RID_REPLICATOR   , "Replicator" },
+    { 0                             , NULL }
+};
+
+/* array lookup of well-known Domain RID users. */
+rid_name domain_user_rids[] =
+{  
+    { DOMAIN_USER_RID_ADMIN         , "Administrator" },
+    { DOMAIN_USER_RID_GUEST         , "Guest" },
+    { 0                             , NULL }
+};
+
+/* array lookup of well-known Domain RID groups. */
+rid_name domain_group_rids[] =
+{  
+    { DOMAIN_GROUP_RID_ADMINS       , "Domain Admins" },
+    { DOMAIN_GROUP_RID_USERS        , "Domain Users" },
+    { DOMAIN_GROUP_RID_GUESTS       , "Domain Guests" },
+    { 0                             , NULL }
+};
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, uint32 **prids, DOM_SID *q_sid)
+{
+       SAM_ACCOUNT *sam_pass=NULL;
+       int i, cur_rid=0;
+       gid_t gid;
+       gid_t *groups = NULL;
+       int num_groups;
+       GROUP_MAP map;
+       DOM_SID tmp_sid;
+       fstring user_name;
+       fstring str_domsid, str_qsid;
+       uint32 rid,grid;
+       uint32 *rids=NULL, *new_rids=NULL;
+       gid_t winbind_gid_low, winbind_gid_high;
+       BOOL ret;
+       BOOL winbind_groups_exist;
+
+       /*
+        * this code is far from perfect.
+        * first it enumerates the full /etc/group and that can be slow.
+        * second, it works only with users' SIDs
+        * whereas the day we support nested groups, it will have to
+        * support both users's SIDs and domain groups' SIDs
+        *
+        * having our own ldap backend would be so much faster !
+        * we're far from that, but hope one day ;-) JFM.
+        */
+
+       *prids=NULL;
+       *numgroups=0;
+
+       winbind_groups_exist = lp_winbind_gid(&winbind_gid_low, &winbind_gid_high);
+
+
+       DEBUG(10,("get_alias_user_groups: looking if SID %s is a member of groups in the SID domain %s\n", 
+                 sid_to_string(str_qsid, q_sid), sid_to_string(str_domsid, sid)));
+
+       pdb_init_sam(&sam_pass);
+       become_root();
+       ret = pdb_getsampwsid(sam_pass, q_sid);
+       unbecome_root();
+       if (ret == False) {
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       fstrcpy(user_name, pdb_get_username(sam_pass));
+       grid=pdb_get_group_rid(sam_pass);
+       gid=pdb_get_gid(sam_pass);
+
+       become_root();
+       /* on some systems this must run as root */
+       num_groups = getgroups_user(user_name, &groups);        
+       unbecome_root();
+       if (num_groups == -1) {
+               /* this should never happen */
+               DEBUG(2,("get_alias_user_groups: getgroups_user failed\n"));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       for (i=0;i<num_groups;i++) {
+               if(!get_group_from_gid(groups[i], &map, MAPPING_WITHOUT_PRIV)) {
+                       DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)groups[i]));
+                       continue;
+               }
+               
+               /* if it's not an alias, continue */
+               if (map.sid_name_use!=SID_NAME_ALIAS) {
+                       DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+                       continue;
+               }
+
+               sid_copy(&tmp_sid, &map.sid);
+               sid_split_rid(&tmp_sid, &rid);
+               
+               /* if the sid is not in the correct domain, continue */
+               if (!sid_equal(&tmp_sid, sid)) {
+                       DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+                       continue;
+               }
+
+               /* Don't return winbind groups as they are not local! */
+               if (winbind_groups_exist && (groups[i] >= winbind_gid_low) && (groups[i] <= winbind_gid_high)) {
+                       DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name));
+                       continue;
+               }
+
+               /* Don't return user private groups... */
+               if (Get_Pwnam(map.nt_name) != 0) {
+                       DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name));
+                       continue;                       
+               }
+               
+               new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+               if (new_rids==NULL) {
+                       DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+                       pdb_free_sam(&sam_pass);
+                       free(groups);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               rids=new_rids;
+               
+               sid_peek_rid(&map.sid, &(rids[cur_rid]));
+               cur_rid++;
+               break;
+       }
+
+       free(groups);
+
+       /* now check for the user's gid (the primary group rid) */
+       for (i=0; i<cur_rid && grid!=rids[i]; i++)
+               ;
+
+       /* the user's gid is already there */
+       if (i!=cur_rid) {
+               DEBUG(10,("get_alias_user_groups: user is already in the list. good.\n"));
+               goto done;
+       }
+
+       DEBUG(10,("get_alias_user_groups: looking for gid %d of user %s\n", (int)gid, user_name));
+
+       if(!get_group_from_gid(gid, &map, MAPPING_WITHOUT_PRIV)) {
+               DEBUG(0,("get_alias_user_groups: gid of user %s doesn't exist. Check your /etc/passwd and /etc/group files\n", user_name));
+               goto done;
+       }       
+
+       /* the primary group isn't an alias */
+       if (map.sid_name_use!=SID_NAME_ALIAS) {
+               DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+               goto done;
+       }
+
+       sid_copy(&tmp_sid, &map.sid);
+       sid_split_rid(&tmp_sid, &rid);
+
+       /* if the sid is not in the correct domain, continue */
+       if (!sid_equal(&tmp_sid, sid)) {
+               DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+               goto done;
+       }
+
+       /* Don't return winbind groups as they are not local! */
+       if (winbind_groups_exist && (gid >= winbind_gid_low) && (gid <= winbind_gid_high)) {
+               DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name ));
+               goto done;
+       }
+
+       /* Don't return user private groups... */
+       if (Get_Pwnam(map.nt_name) != 0) {
+               DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name ));
+               goto done;                      
+       }
+
+       new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+       if (new_rids==NULL) {
+               DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+               pdb_free_sam(&sam_pass);
+               return NT_STATUS_NO_MEMORY;
+       }
+       rids=new_rids;
+
+       sid_peek_rid(&map.sid, &(rids[cur_rid]));
+       cur_rid++;
+
+done:
+       *prids=rids;
+       *numgroups=cur_rid;
+       pdb_free_sam(&sam_pass);
+
+       return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+BOOL get_domain_user_groups(TALLOC_CTX *ctx, int *numgroups, DOM_GID **pgids, SAM_ACCOUNT *sam_pass)
+{
+       GROUP_MAP *map=NULL;
+       int i, num, num_entries, cur_gid=0;
+       struct group *grp;
+       DOM_GID *gids;
+       fstring user_name;
+       uint32 grid;
+       uint32 tmp_rid;
+
+       *numgroups= 0;
+
+       fstrcpy(user_name, pdb_get_username(sam_pass));
+       grid=pdb_get_group_rid(sam_pass);
+
+       DEBUG(10,("get_domain_user_groups: searching domain groups [%s] is a member of\n", user_name));
+
+       /* first get the list of the domain groups */
+       if (!pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV))
+               return False;
+       DEBUG(10,("get_domain_user_groups: there are %d mapped groups\n", num_entries));
+
+       /* 
+        * alloc memory. In the worse case, we alloc memory for nothing.
+        * but I prefer to alloc for nothing
+        * than reallocing everytime.
+        */
+       gids = (DOM_GID *)talloc(ctx, sizeof(DOM_GID) *  num_entries);  
+
+       /* for each group, check if the user is a member of*/
+       for(i=0; i<num_entries; i++) {
+               if ((grp=getgrgid(map[i].gid)) == NULL) {
+                       /* very weird !!! */
+                       DEBUG(5,("get_domain_user_groups: gid %d doesn't exist anymore !\n", (int)map[i].gid));
+                       continue;
+               }
+
+               for(num=0; grp->gr_mem[num]!=NULL; num++) {
+                       if(strcmp(grp->gr_mem[num], user_name)==0) {
+                               /* we found the user, add the group to the list */
+                               sid_peek_rid(&map[i].sid, &(gids[cur_gid].g_rid));
+                               gids[cur_gid].attr=7;
+                               DEBUG(10,("get_domain_user_groups: user found in group %s\n", map[i].nt_name));
+                               cur_gid++;
+                               break;
+                       }
+               }
+       }
+
+       /* we have checked the groups */
+       /* we must now check the gid of the user or the primary group rid, that's the same */
+       for (i=0; i<cur_gid && grid!=gids[i].g_rid; i++)
+               ;
+       
+       /* the user's gid is already there */
+       if (i!=cur_gid) {
+               /* 
+                * the primary group of the user but be the first one in the list
+                * don't ask ! JFM.
+                */
+               gids[i].g_rid=gids[0].g_rid;
+               gids[0].g_rid=grid;
+               goto done;
+       }
+
+       for(i=0; i<num_entries; i++) {
+               sid_peek_rid(&map[i].sid, &tmp_rid);
+               if (tmp_rid==grid) {
+                       /* 
+                        * the primary group of the user but be the first one in the list
+                        * don't ask ! JFM.
+                        */
+                       gids[cur_gid].g_rid=gids[0].g_rid;
+                       gids[0].g_rid=tmp_rid;
+                       gids[cur_gid].attr=7;
+                       DEBUG(10,("get_domain_user_groups: primary gid of user found in group %s\n", map[i].nt_name));
+                       cur_gid++;
+                       goto done; /* leave the loop early */
+               }
+       }
+
+       DEBUG(0,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", user_name));
+       DEBUGADD(0,("get_domain_user_groups: You should fix it, NT doesn't like that\n"));
+
+
+ done:
+       *pgids=gids;
+       *numgroups=cur_gid;
+       safe_free(map);
+
+       return True;
+}
+
+/*******************************************************************
+ gets a domain user's groups from their already-calculated NT_USER_TOKEN
+ ********************************************************************/
+NTSTATUS nt_token_to_group_list(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid, 
+                               const NT_USER_TOKEN *nt_token,
+                               int *numgroups, DOM_GID **pgids) 
+{
+       DOM_GID *gids;
+       int i;
+
+       gids = (DOM_GID *)talloc(mem_ctx, sizeof(*gids) * nt_token->num_sids);
+
+       if (!gids) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *numgroups=0;
+
+       for (i=PRIMARY_GROUP_SID_INDEX; i < nt_token->num_sids; i++) {
+               if (sid_compare_domain(domain_sid, &nt_token->user_sids[i])==0) {
+                       sid_peek_rid(&nt_token->user_sids[i], &(gids[*numgroups].g_rid));
+                       gids[*numgroups].attr=7;
+                       (*numgroups)++;
+               }
+       }
+       *pgids = gids; 
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Look up a local (domain) rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_group_name(uint32 rid, char *group_name, uint32 *type)
+{
+       int i = 0; 
+       (*type) = SID_NAME_DOM_GRP;
+
+       DEBUG(5,("lookup_group_name: rid: %d", rid));
+
+       while (domain_group_rids[i].rid != rid && domain_group_rids[i].rid != 0)
+       {
+               i++;
+       }
+
+       if (domain_group_rids[i].rid != 0)
+       {
+               fstrcpy(group_name, domain_group_rids[i].name);
+               DEBUG(5,(" = %s\n", group_name));
+               return NT_STATUS_OK;
+       }
+
+       DEBUG(5,(" none mapped\n"));
+       return NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local alias rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_alias_name(uint32 rid, char *alias_name, uint32 *type)
+{
+       int i = 0; 
+       (*type) = SID_NAME_WKN_GRP;
+
+       DEBUG(5,("lookup_alias_name: rid: %d", rid));
+
+       while (builtin_alias_rids[i].rid != rid && builtin_alias_rids[i].rid != 0)
+       {
+               i++;
+       }
+
+       if (builtin_alias_rids[i].rid != 0)
+       {
+               fstrcpy(alias_name, builtin_alias_rids[i].name);
+               DEBUG(5,(" = %s\n", alias_name));
+               return NT_STATUS_OK;
+       }
+
+       DEBUG(5,(" none mapped\n"));
+       return NT_STATUS_NONE_MAPPED;
+}
+
+
+#if 0 /*Nobody uses this function just now*/
+/*******************************************************************
+ Look up a local user rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_user_name(uint32 rid, char *user_name, uint32 *type)
+{
+       SAM_ACCOUNT *sampwd=NULL;
+       int i = 0;
+       BOOL ret;
+       
+       (*type) = SID_NAME_USER;
+
+       DEBUG(5,("lookup_user_name: rid: %d", rid));
+
+       /* look up the well-known domain user rids first */
+       while (domain_user_rids[i].rid != rid && domain_user_rids[i].rid != 0)
+       {
+               i++;
+       }
+
+       if (domain_user_rids[i].rid != 0) {
+               fstrcpy(user_name, domain_user_rids[i].name);
+               DEBUG(5,(" = %s\n", user_name));
+               return NT_STATUS_OK;
+       }
+
+       pdb_init_sam(&sampwd);
+
+       /* ok, it's a user.  find the user account */
+       become_root();
+       ret = pdb_getsampwrid(sampwd, rid);
+       unbecome_root();
+
+       if (ret == True) {
+               fstrcpy(user_name, pdb_get_username(sampwd) );
+               DEBUG(5,(" = %s\n", user_name));
+               pdb_free_sam(&sampwd);
+               return NT_STATUS_OK;
+       }
+
+       DEBUG(5,(" none mapped\n"));
+       pdb_free_sam(&sampwd);
+       return NT_STATUS_NONE_MAPPED;
+}
+
+#endif
+
+/*******************************************************************
+ Look up a local (domain) group name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_group_rid(char *group_name, uint32 *rid)
+{
+       const char *grp_name;
+       int i = -1; /* start do loop at -1 */
+
+       do /* find, if it exists, a group rid for the group name*/
+       {
+               i++;
+               (*rid) = domain_group_rids[i].rid;
+               grp_name = domain_group_rids[i].name;
+
+       } while (grp_name != NULL && !strequal(grp_name, group_name));
+
+       return (grp_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local (BUILTIN) alias name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_alias_rid(const char *alias_name, uint32 *rid)
+{
+       const char *als_name;
+       int i = -1; /* start do loop at -1 */
+
+       do /* find, if it exists, a alias rid for the alias name*/
+       {
+               i++;
+               (*rid) = builtin_alias_rids[i].rid;
+               als_name = builtin_alias_rids[i].name;
+
+       } while (als_name != NULL && !strequal(als_name, alias_name));
+
+       return (als_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local user name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_user_rid(char *user_name, uint32 *rid)
+{
+       SAM_ACCOUNT *sampass=NULL;
+       BOOL ret;
+
+       (*rid) = 0;
+
+       pdb_init_sam(&sampass);
+
+       /* find the user account */
+       become_root();
+       ret = pdb_getsampwnam(sampass, user_name);
+       unbecome_root();
+
+       if (ret == True) {
+               (*rid) = pdb_get_user_rid(sampass);
+               pdb_free_sam(&sampass);
+               return NT_STATUS_OK;
+       }
+
+       pdb_free_sam(&sampass);
+       return NT_STATUS_NONE_MAPPED;
+}
diff --git a/source4/rpc_server/srv_wkssvc.c b/source4/rpc_server/srv_wkssvc.c
new file mode 100644 (file)
index 0000000..e0d662e
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997,
+ *  Copyright (C) Anthony Liguori                   2003.
+ *  
+ *  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.
+ */
+
+/* This is the interface to the wks pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_wks_query_info
+ ********************************************************************/
+
+static BOOL api_wks_query_info(pipes_struct *p)
+{
+       WKS_Q_QUERY_INFO q_u;
+       WKS_R_QUERY_INFO r_u;
+       prs_struct *data = &p->in_data.data;
+       prs_struct *rdata = &p->out_data.rdata;
+
+       ZERO_STRUCT(q_u);
+       ZERO_STRUCT(r_u);
+
+       /* grab the net share enum */
+       if(!wks_io_q_query_info("", &q_u, data, 0))
+               return False;
+
+       r_u.status = _wks_query_info(p, &q_u, &r_u);
+
+       /* store the response in the SMB stream */
+       if(!wks_io_r_query_info("", &r_u, rdata, 0))
+               return False;
+
+       return True;
+}
+
+
+/*******************************************************************
+ \PIPE\wkssvc commands
+ ********************************************************************/
+
+#ifdef RPC_WKS_DYNAMIC
+int init_module(void)
+#else
+int rpc_wks_init(void)
+#endif
+{
+  static struct api_struct api_wks_cmds[] =
+    {
+      { "WKS_Q_QUERY_INFO", WKS_QUERY_INFO, api_wks_query_info }
+    };
+  return rpc_pipe_register_commands("wkssvc", "ntsvcs", api_wks_cmds,
+                                   sizeof(api_wks_cmds) / sizeof(struct api_struct));
+}
diff --git a/source4/rpc_server/srv_wkssvc_nt.c b/source4/rpc_server/srv_wkssvc_nt.c
new file mode 100644 (file)
index 0000000..2ca43e5
--- /dev/null
@@ -0,0 +1,79 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-1997,
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ *  Copyright (C) Paul Ashton                       1997.
+ *  Copyright (C) Jeremy Allison                                       2001.
+ *  
+ *  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.
+ */
+
+/* This is the implementation of the wks interface. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ create_wks_info_100
+ ********************************************************************/
+
+static void create_wks_info_100(WKS_INFO_100 *inf)
+{
+       pstring my_name;
+       pstring domain;
+
+       DEBUG(5,("create_wks_info_100: %d\n", __LINE__));
+
+       pstrcpy (my_name, lp_netbios_name());
+       strupper(my_name);
+
+       pstrcpy (domain, lp_workgroup());
+       strupper(domain);
+
+       init_wks_info_100(inf,
+                         0x000001f4, /* platform id info */
+                         lp_major_announce_version(),
+                         lp_minor_announce_version(),
+                         my_name, domain);
+}
+
+/*******************************************************************
+ wks_reply_query_info
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+NTSTATUS _wks_query_info(pipes_struct *p, WKS_Q_QUERY_INFO *q_u, WKS_R_QUERY_INFO *r_u)
+{
+       WKS_INFO_100 *wks100 = NULL;
+
+       DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+       wks100 = (WKS_INFO_100 *)talloc_zero(p->mem_ctx, sizeof(WKS_INFO_100));
+
+       if (!wks100)
+               return NT_STATUS_NO_MEMORY;
+
+       create_wks_info_100(wks100);
+       init_wks_r_query_info(r_u, q_u->switch_value, wks100, NT_STATUS_OK);
+
+       DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+       return r_u->status;
+}
diff --git a/source4/rpcclient/cmd_dfs.c b/source4/rpcclient/cmd_dfs.c
new file mode 100644 (file)
index 0000000..715174c
--- /dev/null
@@ -0,0 +1,237 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Tim Potter 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 "rpcclient.h"
+
+/* Check DFS is supported by the remote server */
+
+static NTSTATUS cmd_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                              int argc, const char **argv)
+{
+       BOOL dfs_exists;
+       NTSTATUS result;
+
+       if (argc != 1) {
+               printf("Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_dfs_exist(cli, mem_ctx, &dfs_exists);
+
+       if (NT_STATUS_IS_OK(result))
+               printf("dfs is %spresent\n", dfs_exists ? "" : "not ");
+
+       return result;
+}
+
+static NTSTATUS cmd_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                            int argc, const char **argv)
+{
+       NTSTATUS result;
+       const char *entrypath, *servername, *sharename, *comment;
+       uint32 flags = 0;
+
+       if (argc != 5) {
+               printf("Usage: %s entrypath servername sharename comment\n", 
+                      argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       entrypath = argv[1];
+       servername = argv[2];
+       sharename = argv[3];
+       comment = argv[4];
+
+       result = cli_dfs_add(cli, mem_ctx, entrypath, servername, 
+                            sharename, comment, flags);
+
+       return result;
+}
+
+static NTSTATUS cmd_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               int argc, const char **argv)
+{
+       NTSTATUS result;
+       const char *entrypath, *servername, *sharename;
+
+       if (argc != 4) {
+               printf("Usage: %s entrypath servername sharename\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       entrypath = argv[1];
+       servername = argv[2];
+       sharename = argv[3];
+
+       result = cli_dfs_remove(cli, mem_ctx, entrypath, servername, 
+                               sharename);
+
+       return result;
+}
+
+/* Display a DFS_INFO_1 structure */
+
+static void display_dfs_info_1(DFS_INFO_1 *info1)
+{
+       fstring temp;
+
+       unistr2_to_ascii(temp, &info1->entrypath, sizeof(temp) - 1);
+       printf("entrypath: %s\n", temp);
+}
+
+/* Display a DFS_INFO_2 structure */
+
+static void display_dfs_info_2(DFS_INFO_2 *info2)
+{
+       fstring temp;
+
+       unistr2_to_ascii(temp, &info2->entrypath, sizeof(temp) - 1);
+       printf("entrypath: %s\n", temp);
+
+       unistr2_to_ascii(temp, &info2->comment, sizeof(temp) - 1);
+       printf("\tcomment: %s\n", temp);
+
+       printf("\tstate: %d\n", info2->state);
+       printf("\tnum_storages: %d\n", info2->num_storages);
+}
+
+/* Display a DFS_INFO_3 structure */
+
+static void display_dfs_info_3(DFS_INFO_3 *info3)
+{
+       fstring temp;
+       int i;
+
+       unistr2_to_ascii(temp, &info3->entrypath, sizeof(temp) - 1);
+       printf("entrypath: %s\n", temp);
+
+       unistr2_to_ascii(temp, &info3->comment, sizeof(temp) - 1);
+       printf("\tcomment: %s\n", temp);
+
+       printf("\tstate: %d\n", info3->state);
+       printf("\tnum_storages: %d\n", info3->num_storages);
+
+       for (i = 0; i < info3->num_storages; i++) {
+               DFS_STORAGE_INFO *dsi = &info3->storages[i];
+
+               unistr2_to_ascii(temp, &dsi->servername, sizeof(temp) - 1);
+               printf("\t\tstorage[%d] servername: %s\n", i, temp);
+
+               unistr2_to_ascii(temp, &dsi->sharename, sizeof(temp) - 1);
+               printf("\t\tstorage[%d] sharename: %s\n", i, temp);
+       }
+}
+
+/* Display a DFS_INFO_CTR structure */
+
+static void display_dfs_info_ctr(DFS_INFO_CTR *ctr)
+{
+       int i;
+
+       for (i = 0; i < ctr->num_entries; i++) {
+               switch (ctr->switch_value) {
+               case 0x01:
+                       display_dfs_info_1(&ctr->dfs.info1[i]);
+                       break;
+               case 0x02:
+                       display_dfs_info_2(&ctr->dfs.info2[i]);
+                       break;
+               case 0x03:
+                       display_dfs_info_3(&ctr->dfs.info3[i]);
+                       break;
+               default:
+                       printf("unsupported info level %d\n", 
+                              ctr->switch_value);
+                       break;
+               }
+       }
+}
+
+/* Enumerate dfs shares */
+
+static NTSTATUS cmd_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                             int argc, const char **argv)
+{
+       DFS_INFO_CTR ctr;
+       NTSTATUS result;
+       uint32 info_level = 1;
+
+       if (argc > 2) {
+               printf("Usage: %s [info_level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       result = cli_dfs_enum(cli, mem_ctx, info_level, &ctr);
+
+       if (NT_STATUS_IS_OK(result))
+               display_dfs_info_ctr(&ctr);
+
+       return result;
+}
+
+static NTSTATUS cmd_dfs_getinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                int argc, const char **argv)
+{
+       NTSTATUS result;
+       const char *entrypath, *servername, *sharename;
+       uint32 info_level = 1;
+       DFS_INFO_CTR ctr;
+
+       if (argc < 4 || argc > 5) {
+               printf("Usage: %s entrypath servername sharename "
+                       "[info_level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       entrypath = argv[1];
+       servername = argv[2];
+       sharename = argv[3];
+
+       if (argc == 5)
+               info_level = atoi(argv[4]);
+
+       result = cli_dfs_get_info(cli, mem_ctx, entrypath, servername, 
+                                 sharename, info_level, &ctr);
+
+       if (NT_STATUS_IS_OK(result))
+               display_dfs_info_ctr(&ctr);
+
+       return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set dfs_commands[] = {
+
+       { "DFS" },
+
+       { "dfsexist",   cmd_dfs_exist,   PI_NETDFS, "Query DFS support",    "" },
+       { "dfsadd",     cmd_dfs_add,     PI_NETDFS, "Add a DFS share",      "" },
+       { "dfsremove",  cmd_dfs_remove,  PI_NETDFS, "Remove a DFS share",   "" },
+       { "dfsgetinfo", cmd_dfs_getinfo, PI_NETDFS, "Query DFS share info", "" },
+       { "dfsenum",    cmd_dfs_enum,    PI_NETDFS, "Enumerate dfs shares", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_ds.c b/source4/rpcclient/cmd_ds.c
new file mode 100644 (file)
index 0000000..9de6d6a
--- /dev/null
@@ -0,0 +1,59 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Gerald Carter 2002
+
+   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 "rpcclient.h"
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_ds_dsrole_getprimarydominfo(struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx, int argc, 
+                                    const char **argv) 
+{
+       NTSTATUS result;
+       DS_DOMINFO_CTR  ctr;
+       
+       result = cli_ds_getprimarydominfo( cli, mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr );
+       if ( NT_STATUS_IS_OK(result) )
+       {
+               printf ("Machine Role = [%d]\n", ctr.basic->machine_role);
+               
+               if ( ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING )     {
+                       printf( "Directory Service is running.\n");
+                       printf( "Domain is in %s mode.\n", (ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ? "mixed" : "native" );
+               }
+               else
+                       printf( "Directory Service not running on server\n");
+       }
+       
+       return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set ds_commands[] = {
+
+       { "LSARPC-DS" },
+
+       { "dsroledominfo",      cmd_ds_dsrole_getprimarydominfo,       PI_LSARPC_DS, "Get Primary Domain Information", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_lsarpc.c b/source4/rpcclient/cmd_lsarpc.c
new file mode 100644 (file)
index 0000000..fab6a89
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Tim Potter              2000
+   Copyright (C) Rafal Szczesniak        2002
+
+   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 "rpcclient.h"
+
+
+/* useful function to allow entering a name instead of a SID and
+ * looking it up automatically */
+static NTSTATUS name_to_sid(struct cli_state *cli, 
+                           TALLOC_CTX *mem_ctx,
+                           DOM_SID *sid, const char *name)
+{
+       POLICY_HND pol;
+       uint32 *sid_types;
+       NTSTATUS result;
+       DOM_SID *sids;
+
+       /* maybe its a raw SID */
+       if (strncmp(name, "S-", 2) == 0 &&
+           string_to_sid(sid, name)) {
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_lookup_names(cli, mem_ctx, &pol, 1, &name, &sids, &sid_types);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       cli_lsa_close(cli, mem_ctx, &pol);
+
+       *sid = sids[0];
+
+done:
+       return result;
+}
+
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_lsa_query_info_policy(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx, int argc, 
+                                          const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID dom_sid;
+       GUID dom_guid;
+       fstring sid_str, domain_name="", dns_name="", forest_name="";
+       uint32 info_class = 3;
+
+       if (argc > 2) {
+               printf("Usage: %s [info_class]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_class = atoi(argv[1]);
+       
+       /* Lookup info policy */
+       switch (info_class) {
+       case 12:
+               result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                            SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                            &pol);
+
+               if (!NT_STATUS_IS_OK(result))
+                       goto done;
+               result = cli_lsa_query_info_policy2(cli, mem_ctx, &pol,
+                                                   info_class, domain_name,
+                                                   dns_name, forest_name,
+                                                   &dom_guid, &dom_sid);
+               break;
+       default:
+               result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+               if (!NT_STATUS_IS_OK(result))
+                       goto done;
+               result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, 
+                                                  info_class, domain_name, 
+                                                  &dom_sid);
+       }
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       sid_to_string(sid_str, &dom_sid);
+
+       if (domain_name[0])
+               printf("domain %s has sid %s\n", domain_name, sid_str);
+       else
+               printf("could not query info for level %d\n", info_class);
+
+       if (dns_name[0])
+               printf("domain dns name is %s\n", dns_name);
+       if (forest_name[0])
+               printf("forest name is %s\n", forest_name);
+
+       if (info_class == 12) {
+               printf("domain GUID is ");
+               print_guid(&dom_guid);
+       }
+ done:
+       return result;
+}
+
+/* Resolve a list of names to a list of sids */
+
+static NTSTATUS cmd_lsa_lookup_names(struct cli_state *cli, 
+                                     TALLOC_CTX *mem_ctx, int argc, 
+                                     const char **argv)
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID *sids;
+       uint32 *types;
+       int i;
+
+       if (argc == 1) {
+               printf("Usage: %s [name1 [name2 [...]]]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_lookup_names(cli, mem_ctx, &pol, argc - 1, 
+                                     (const char**)(argv + 1), &sids, &types);
+
+       if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != 
+           NT_STATUS_V(STATUS_SOME_UNMAPPED))
+               goto done;
+
+       result = NT_STATUS_OK;
+
+       /* Print results */
+
+       for (i = 0; i < (argc - 1); i++) {
+               fstring sid_str;
+               sid_to_string(sid_str, &sids[i]);
+               printf("%s %s (%s: %d)\n", argv[i + 1], sid_str,
+                      sid_type_lookup(types[i]), types[i]);
+       }
+
+ done:
+       return result;
+}
+
+/* Resolve a list of SIDs to a list of names */
+
+static NTSTATUS cmd_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                    int argc, const char **argv)
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID *sids;
+       char **domains;
+       char **names;
+       uint32 *types;
+       int i;
+
+       if (argc == 1) {
+               printf("Usage: %s [sid1 [sid2 [...]]]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Convert arguments to sids */
+
+       sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * (argc - 1));
+
+       if (!sids) {
+               printf("could not allocate memory for %d sids\n", argc - 1);
+               goto done;
+       }
+
+       for (i = 0; i < argc - 1; i++) 
+               if (!string_to_sid(&sids[i], argv[i + 1])) {
+                       result = NT_STATUS_INVALID_SID;
+                       goto done;
+               }
+
+       /* Lookup the SIDs */
+
+       result = cli_lsa_lookup_sids(cli, mem_ctx, &pol, argc - 1, sids, 
+                                    &domains, &names, &types);
+
+       if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != 
+           NT_STATUS_V(STATUS_SOME_UNMAPPED))
+               goto done;
+
+       result = NT_STATUS_OK;
+
+       /* Print results */
+
+       for (i = 0; i < (argc - 1); i++) {
+               fstring sid_str;
+
+               sid_to_string(sid_str, &sids[i]);
+               printf("%s %s\\%s (%d)\n", sid_str, 
+                      domains[i] ? domains[i] : "*unknown*", 
+                      names[i] ? names[i] : "*unknown*", types[i]);
+       }
+
+ done:
+       return result;
+}
+
+/* Enumerate list of trusted domains */
+
+static NTSTATUS cmd_lsa_enum_trust_dom(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc, 
+                                       const char **argv)
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID *domain_sids;
+       char **domain_names;
+
+       /* defaults, but may be changed using params */
+       uint32 enum_ctx = 0;
+       uint32 num_domains = 0;
+       int i;
+
+       if (argc > 2) {
+               printf("Usage: %s [enum context (0)]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2 && argv[1]) {
+               enum_ctx = atoi(argv[2]);
+       }       
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    POLICY_VIEW_LOCAL_INFORMATION,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Lookup list of trusted domains */
+
+       result = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx,
+                                       &num_domains,
+                                       &domain_names, &domain_sids);
+       if (!NT_STATUS_IS_OK(result) &&
+           !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+           !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+           goto done;
+
+       /* Print results: list of names and sids returned in this response. */   
+       for (i = 0; i < num_domains; i++) {
+               fstring sid_str;
+
+               sid_to_string(sid_str, &domain_sids[i]);
+               printf("%s %s\n", domain_names[i] ? domain_names[i] : 
+                      "*unknown*", sid_str);
+       }
+
+ done:
+       return result;
+}
+
+/* Enumerates privileges */
+
+static NTSTATUS cmd_lsa_enum_privilege(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc, 
+                                      const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       uint32 enum_context=0;
+       uint32 pref_max_length=0x1000;
+       uint32 count=0;
+       char   **privs_name;
+       uint32 *privs_high;
+       uint32 *privs_low;
+       int i;
+
+       if (argc > 3) {
+               printf("Usage: %s [enum context] [max length]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc>=2)
+               enum_context=atoi(argv[1]);
+
+       if (argc==3)
+               pref_max_length=atoi(argv[2]);
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_enum_privilege(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+                                       &count, &privs_name, &privs_high, &privs_low);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+       printf("found %d privileges\n\n", count);
+
+       for (i = 0; i < count; i++) {
+               printf("%s \t\t%d:%d (0x%x:0x%x)\n", privs_name[i] ? privs_name[i] : "*unknown*",
+                      privs_high[i], privs_low[i], privs_high[i], privs_low[i]);
+       }
+
+ done:
+       return result;
+}
+
+/* Get privilege name */
+
+static NTSTATUS cmd_lsa_get_dispname(struct cli_state *cli, 
+                                     TALLOC_CTX *mem_ctx, int argc, 
+                                     const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       uint16 lang_id=0;
+       uint16 lang_id_sys=0;
+       uint16 lang_id_desc;
+       fstring description;
+
+       if (argc != 2) {
+               printf("Usage: %s privilege name\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_get_dispname(cli, mem_ctx, &pol, argv[1], lang_id, lang_id_sys, description, &lang_id_desc);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+       printf("%s -> %s (language: 0x%x)\n", argv[1], description, lang_id_desc);
+
+ done:
+       return result;
+}
+
+/* Enumerate the LSA SIDS */
+
+static NTSTATUS cmd_lsa_enum_sids(struct cli_state *cli, 
+                                 TALLOC_CTX *mem_ctx, int argc, 
+                                 const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       uint32 enum_context=0;
+       uint32 pref_max_length=0x1000;
+       DOM_SID *sids;
+       uint32 count=0;
+       int i;
+
+       if (argc > 3) {
+               printf("Usage: %s [enum context] [max length]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc>=2)
+               enum_context=atoi(argv[1]);
+
+       if (argc==3)
+               pref_max_length=atoi(argv[2]);
+
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_enum_sids(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+                                       &count, &sids);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+       printf("found %d SIDs\n\n", count);
+
+       for (i = 0; i < count; i++) {
+               fstring sid_str;
+
+               sid_to_string(sid_str, &sids[i]);
+               printf("%s\n", sid_str);
+       }
+
+ done:
+       return result;
+}
+
+/* Enumerate the privileges of an SID */
+
+static NTSTATUS cmd_lsa_enum_privsaccounts(struct cli_state *cli, 
+                                           TALLOC_CTX *mem_ctx, int argc, 
+                                           const char **argv) 
+{
+       POLICY_HND dom_pol;
+       POLICY_HND user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 access_desired = 0x000f000f;
+       
+       DOM_SID sid;
+       uint32 count=0;
+       LUID_ATTR *set;
+       int i;
+
+       if (argc != 2 ) {
+               printf("Usage: %s SID\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;      
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &dom_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_open_account(cli, mem_ctx, &dom_pol, &sid, access_desired, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_enum_privsaccount(cli, mem_ctx, &user_pol, &count, &set);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+       printf("found %d privileges for SID %s\n\n", count, argv[1]);
+       printf("high\tlow\tattribute\n");
+
+       for (i = 0; i < count; i++) {
+               printf("%u\t%u\t%u\n", set[i].luid.high, set[i].luid.low, set[i].attr);
+       }
+
+ done:
+       return result;
+}
+
+
+/* Enumerate the privileges of an SID via LsaEnumerateAccountRights */
+
+static NTSTATUS cmd_lsa_enum_acct_rights(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx, int argc, 
+                                        const char **argv) 
+{
+       POLICY_HND dom_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       DOM_SID sid;
+       uint32 count;
+       char **rights;
+
+       int i;
+
+       if (argc != 2 ) {
+               printf("Usage: %s SID\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;      
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &dom_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_enum_account_rights(cli, mem_ctx, &dom_pol, sid, &count, &rights);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       printf("found %d privileges for SID %s\n", count, sid_string_static(&sid));
+
+       for (i = 0; i < count; i++) {
+               printf("\t%s\n", rights[i]);
+       }
+
+ done:
+       return result;
+}
+
+
+/* Enumerate the accounts with a specific right */
+
+static NTSTATUS cmd_lsa_enum_acct_with_right(struct cli_state *cli, 
+                                            TALLOC_CTX *mem_ctx, int argc, 
+                                            const char **argv) 
+{
+       POLICY_HND dom_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       DOM_SID *sids;
+       uint32 count;
+       const char *right;
+
+       int i;
+
+       if (argc != 2 ) {
+               printf("Usage: %s <RIGHT>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       right = argv[1];
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &dom_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_enum_account_with_right(cli, mem_ctx, &dom_pol, right, &count, &sids);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       printf("found %d SIDs for '%s'\n", count, right);
+
+       for (i = 0; i < count; i++) {
+               printf("\t%s\n", sid_string_static(&sids[i]));
+       }
+
+ done:
+       return result;
+}
+
+
+/* add some privileges to a SID via LsaAddAccountRights */
+
+static NTSTATUS cmd_lsa_add_acct_rights(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc, 
+                                       const char **argv) 
+{
+       POLICY_HND dom_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       DOM_SID sid;
+
+       if (argc < 3 ) {
+               printf("Usage: %s SID [rights...]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;      
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &dom_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_add_account_rights(cli, mem_ctx, &dom_pol, sid, 
+                                           argc-2, argv+2);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+ done:
+       return result;
+}
+
+
+/* remove some privileges to a SID via LsaRemoveAccountRights */
+
+static NTSTATUS cmd_lsa_remove_acct_rights(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc, 
+                                       const char **argv) 
+{
+       POLICY_HND dom_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       DOM_SID sid;
+
+       if (argc < 3 ) {
+               printf("Usage: %s SID [rights...]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;      
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &dom_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_remove_account_rights(cli, mem_ctx, &dom_pol, sid, 
+                                              False, argc-2, argv+2);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+ done:
+       return result;
+}
+
+
+/* Get a privilege value given its name */
+
+static NTSTATUS cmd_lsa_lookupprivvalue(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc, 
+                                       const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       LUID luid;
+
+       if (argc != 2 ) {
+               printf("Usage: %s name\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_lookupprivvalue(cli, mem_ctx, &pol, argv[1], &luid);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+
+       printf("%u:%u (0x%x:0x%x)\n", luid.high, luid.low, luid.high, luid.low);
+
+ done:
+       return result;
+}
+
+/* Query LSA security object */
+
+static NTSTATUS cmd_lsa_query_secobj(struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx, int argc, 
+                                    const char **argv) 
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       SEC_DESC_BUF *sdb;
+       uint32 sec_info = 0x00000004; /* ??? */
+
+       if (argc != 1 ) {
+               printf("Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_lsa_open_policy2(cli, mem_ctx, True, 
+                                     SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                     &pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_lsa_query_secobj(cli, mem_ctx, &pol, sec_info, &sdb);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Print results */
+
+       display_sec_desc(sdb->sec);
+
+ done:
+       return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set lsarpc_commands[] = {
+
+       { "LSARPC" },
+
+       { "lsaquery",            cmd_lsa_query_info_policy,  PI_LSARPC, "Query info policy",                    "" },
+       { "lookupsids",          cmd_lsa_lookup_sids,        PI_LSARPC, "Convert SIDs to names",                "" },
+       { "lookupnames",         cmd_lsa_lookup_names,       PI_LSARPC, "Convert names to SIDs",                "" },
+       { "enumtrust",           cmd_lsa_enum_trust_dom,     PI_LSARPC, "Enumerate trusted domains",            "Usage: [preferred max number] [enum context (0)]" },
+       { "enumprivs",           cmd_lsa_enum_privilege,     PI_LSARPC, "Enumerate privileges",                 "" },
+       { "getdispname",         cmd_lsa_get_dispname,       PI_LSARPC, "Get the privilege name",               "" },
+       { "lsaenumsid",          cmd_lsa_enum_sids,          PI_LSARPC, "Enumerate the LSA SIDS",               "" },
+       { "lsaenumprivsaccount", cmd_lsa_enum_privsaccounts, PI_LSARPC, "Enumerate the privileges of an SID",   "" },
+       { "lsaenumacctrights",   cmd_lsa_enum_acct_rights,   PI_LSARPC, "Enumerate the rights of an SID",   "" },
+       { "lsaenumacctwithright",cmd_lsa_enum_acct_with_right,PI_LSARPC,"Enumerate accounts with a right",   "" },
+       { "lsaaddacctrights",    cmd_lsa_add_acct_rights,    PI_LSARPC, "Add rights to an account",   "" },
+       { "lsaremoveacctrights", cmd_lsa_remove_acct_rights, PI_LSARPC, "Remove rights from an account",   "" },
+       { "lsalookupprivvalue",  cmd_lsa_lookupprivvalue,    PI_LSARPC, "Get a privilege value given its name", "" },
+       { "lsaquerysecobj",      cmd_lsa_query_secobj,       PI_LSARPC, "Query LSA security object", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_netlogon.c b/source4/rpcclient/cmd_netlogon.c
new file mode 100644 (file)
index 0000000..407bff3
--- /dev/null
@@ -0,0 +1,342 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Tim Potter 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 "rpcclient.h"
+
+static NTSTATUS cmd_netlogon_logon_ctrl2(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx, int argc, 
+                                         const char **argv)
+{
+       uint32 query_level = 1;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       if (argc > 1) {
+               fprintf(stderr, "Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_netlogon_logon_ctrl2(cli, mem_ctx, query_level);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+ done:
+       return result;
+}
+
+static NTSTATUS cmd_netlogon_logon_ctrl(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx, int argc, 
+                                        const char **argv)
+{
+#if 0
+       uint32 query_level = 1;
+#endif
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       if (argc > 1) {
+               fprintf(stderr, "Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+#if 0
+       result = cli_netlogon_logon_ctrl(cli, mem_ctx, query_level);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+#endif
+
+       /* Display results */
+
+       return result;
+}
+
+/* Display sam synchronisation information */
+
+static void display_sam_sync(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas,
+                             SAM_DELTA_CTR *deltas)
+{
+        fstring name;
+        uint32 i, j;
+
+        for (i = 0; i < num_deltas; i++) {
+                switch (hdr_deltas[i].type) {
+                case SAM_DELTA_DOMAIN_INFO:
+                        unistr2_to_ascii(name,
+                                         &deltas[i].domain_info.uni_dom_name,
+                                         sizeof(name) - 1);
+                        printf("Domain: %s\n", name);
+                        break;
+                case SAM_DELTA_GROUP_INFO:
+                        unistr2_to_ascii(name,
+                                         &deltas[i].group_info.uni_grp_name,
+                                         sizeof(name) - 1);
+                        printf("Group: %s\n", name);
+                        break;
+                case SAM_DELTA_ACCOUNT_INFO:
+                        unistr2_to_ascii(name, 
+                                         &deltas[i].account_info.uni_acct_name,
+                                         sizeof(name) - 1);
+                        printf("Account: %s\n", name);
+                        break;
+                case SAM_DELTA_ALIAS_INFO:
+                        unistr2_to_ascii(name, 
+                                         &deltas[i].alias_info.uni_als_name,
+                                         sizeof(name) - 1);
+                        printf("Alias: %s\n", name);
+                        break;
+                case SAM_DELTA_ALIAS_MEM: {
+                        SAM_ALIAS_MEM_INFO *alias = &deltas[i].als_mem_info;
+
+                        for (j = 0; j < alias->num_members; j++) {
+                                fstring sid_str;
+
+                                sid_to_string(sid_str, &alias->sids[j].sid);
+
+                                printf("%s\n", sid_str);
+                        }
+                        break;
+                }
+                case SAM_DELTA_GROUP_MEM: {
+                        SAM_GROUP_MEM_INFO *group = &deltas[i].grp_mem_info;
+
+                        for (j = 0; j < group->num_members; j++)
+                                printf("rid 0x%x, attrib 0x%08x\n", 
+                                          group->rids[j], group->attribs[j]);
+                        break;
+                }
+                case SAM_DELTA_MODIFIED_COUNT: {
+                        SAM_DELTA_MOD_COUNT *mc = &deltas[i].mod_count;
+
+                        printf("sam sequence update: 0x%04x\n", mc->seqnum);
+                        break;
+                }                                  
+                default:
+                        printf("unknown delta type 0x%02x\n", 
+                                  hdr_deltas[i].type);
+                        break;
+                }
+        }
+}
+
+/* Perform sam synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_sync(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc,
+                                      const char **argv)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        unsigned char trust_passwd[16];
+        uint32 database_id = 0, num_deltas;
+        SAM_DELTA_HDR *hdr_deltas;
+        SAM_DELTA_CTR *deltas;
+       DOM_CRED ret_creds;
+       uint32 neg_flags = 0x000001ff;
+
+        if (argc > 2) {
+                fprintf(stderr, "Usage: %s [database_id]\n", argv[0]);
+                return NT_STATUS_OK;
+        }
+
+        if (argc == 2)
+                database_id = atoi(argv[1]);
+
+        if (!secrets_init()) {
+                fprintf(stderr, "Unable to initialise secrets database\n");
+                return result;
+        }
+
+        /* Initialise session credentials */
+
+       if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd,
+                                                  NULL)) {
+               fprintf(stderr, "could not fetch trust account password\n");
+               goto done;
+       }        
+
+        result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                fprintf(stderr, "Error initialising session creds\n");
+                goto done;
+        }
+
+       /* on first call the returnAuthenticator is empty */
+       memset(&ret_creds, 0, sizeof(ret_creds));
+        /* Synchronise sam database */
+
+       result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, database_id,
+                                      0, &num_deltas, &hdr_deltas, &deltas);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+        /* Display results */
+
+        display_sam_sync(num_deltas, hdr_deltas, deltas);
+
+ done:
+        return result;
+}
+
+/* Perform sam delta synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_deltas(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx, int argc,
+                                        const char **argv)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        unsigned char trust_passwd[16];
+        uint32 database_id, num_deltas, tmp;
+        SAM_DELTA_HDR *hdr_deltas;
+        SAM_DELTA_CTR *deltas;
+        UINT64_S seqnum;
+       uint32 neg_flags = 0x000001ff;
+
+        if (argc != 3) {
+                fprintf(stderr, "Usage: %s database_id seqnum\n", argv[0]);
+                return NT_STATUS_OK;
+        }
+
+        database_id = atoi(argv[1]);
+        tmp = atoi(argv[2]);
+
+        seqnum.low = tmp & 0xffff;
+        seqnum.high = 0;
+
+        if (!secrets_init()) {
+                fprintf(stderr, "Unable to initialise secrets database\n");
+                goto done;
+        }
+
+        /* Initialise session credentials */
+
+       if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd,
+                                                  NULL)) {
+               fprintf(stderr, "could not fetch trust account password\n");
+               goto done;
+       }        
+
+        result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                fprintf(stderr, "Error initialising session creds\n");
+                goto done;
+        }
+
+        /* Synchronise sam database */
+
+       result = cli_netlogon_sam_deltas(cli, mem_ctx, database_id,
+                                        seqnum, &num_deltas, 
+                                        &hdr_deltas, &deltas);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+        /* Display results */
+
+        display_sam_sync(num_deltas, hdr_deltas, deltas);
+        
+ done:
+        return result;
+}
+
+/* Log on a domain user */
+
+static NTSTATUS cmd_netlogon_sam_logon(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc,
+                                       const char **argv)
+{
+        unsigned char trust_passwd[16];
+        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        int logon_type = NET_LOGON_TYPE;
+        const char *username, *password;
+       uint32 neg_flags = 0x000001ff;
+       int auth_level = 2;
+
+        /* Check arguments */
+
+        if (argc < 3 || argc > 6) {
+                fprintf(stderr, "Usage: samlogon <username> <password> "
+                        "[logon_type] [neg flags] [auth level (2 or 3)]\n"
+                       "neg flags being 0x000001ff or 0x6007ffff\n");
+                return NT_STATUS_OK;
+        }
+
+        username = argv[1];
+        password = argv[2];
+
+        if (argc == 4)
+                sscanf(argv[3], "%i", &logon_type);
+
+       if (argc == 5)
+                sscanf(argv[4], "%i", &neg_flags);
+
+       if (argc == 6)
+                sscanf(argv[5], "%i", &auth_level);
+
+        /* Authenticate ourselves with the domain controller */
+
+        if (!secrets_init()) {
+                fprintf(stderr, "Unable to initialise secrets database\n");
+                return result;
+        }
+
+       if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, NULL)) {
+               fprintf(stderr, "could not fetch trust account password\n");
+               goto done;
+       }        
+
+        result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, auth_level);
+
+        if (!NT_STATUS_IS_OK(result)) {
+                fprintf(stderr, "Error initialising session creds\n");
+                goto done;
+        }
+
+        /* Perform the sam logon */
+
+        result = cli_netlogon_sam_logon(cli, mem_ctx, username, password, logon_type);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+ done:
+        return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set netlogon_commands[] = {
+
+       { "NETLOGON" },
+
+       { "logonctrl2", cmd_netlogon_logon_ctrl2, PI_NETLOGON, "Logon Control 2",     "" },
+       { "logonctrl",  cmd_netlogon_logon_ctrl,  PI_NETLOGON, "Logon Control",       "" },
+       { "samsync",    cmd_netlogon_sam_sync,    PI_NETLOGON, "Sam Synchronisation", "" },
+       { "samdeltas",  cmd_netlogon_sam_deltas,  PI_NETLOGON, "Query Sam Deltas",    "" },
+        { "samlogon",   cmd_netlogon_sam_logon,   PI_NETLOGON, "Sam Logon",           "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_reg.c b/source4/rpcclient/cmd_reg.c
new file mode 100644 (file)
index 0000000..19c0e7f
--- /dev/null
@@ -0,0 +1,1007 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT Domain Authentication SMB / MSRPC client
+   Copyright (C) Andrew Tridgell 1994-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   Copyright (C) Simo Sorce 2001
+   
+   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 "rpcclient.h"
+
+/*
+ * keys.  of the form:
+ * ----
+ *
+ * [HKLM]|[HKU]\[parent_keyname_components]\[subkey]|[value]
+ *
+ * reg_getsubkey() splits this down into:
+ * [HKLM]|[HKU]\[parent_keyname_components] and [subkey]|[value]
+ *
+ * do_reg_connect() splits the left side down further into:
+ * [HKLM]|[HKU] and [parent_keyname_components].
+ *
+ * HKLM is short for HKEY_LOCAL_MACHINE
+ * HKU  is short for HKEY_USERS
+ *
+ * oh, and HKEY stands for "Hive Key".
+ *
+ */
+
+#if 0 /* Simo: reg functions need to be updated to the new cmd interface */
+
+/****************************************************************************
+nt registry enum
+****************************************************************************/
+static void cmd_reg_enum(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res1 = True;
+       BOOL res2 = True;
+       int i;
+
+       POLICY_HND key_pol;
+       fstring full_keyname;
+       fstring key_name;
+
+       /*
+        * query key info
+        */
+
+       fstring key_class;
+       uint32 max_class_len = 0;
+       uint32 num_subkeys;
+       uint32 max_subkeylen;
+       uint32 max_subkeysize; 
+       uint32 num_values;
+       uint32 max_valnamelen;
+       uint32 max_valbufsize;
+       uint32 sec_desc;
+       NTTIME mod_time;
+
+       /*
+        * unknown 0x1a request
+        */
+
+       uint32 unk_1a_response;
+
+       DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regenum <key_name>\n");
+               return;
+       }
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*key_name) != 0)
+       {
+               /* open an entry */
+               res1 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        key_name, 0x02000000, &key_pol) : False;
+       }
+       else
+       {
+               memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+       }
+
+       res1 = res1 ? do_reg_query_key(smb_cli,
+                               &key_pol,
+                               key_class, &max_class_len,
+                               &num_subkeys, &max_subkeylen, &max_subkeysize,
+                               &num_values, &max_valnamelen, &max_valbufsize,
+                               &sec_desc, &mod_time) : False;
+
+       if (res1 && num_subkeys > 0)
+       {
+               fprintf(out_hnd,"Subkeys\n");
+               fprintf(out_hnd,"-------\n");
+       }
+
+       for (i = 0; i < num_subkeys; i++)
+       {
+               /*
+                * enumerate key
+                */
+
+               fstring enum_name;
+               uint32 enum_unk1;
+               uint32 enum_unk2;
+               time_t key_mod_time;
+
+               /* unknown 1a it */
+               res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+                                       &unk_1a_response) : False;
+
+               if (res2 && unk_1a_response != 5)
+               {
+                       fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+               }
+
+               /* enum key */
+               res2 = res2 ? do_reg_enum_key(smb_cli, &key_pol,
+                                       i, enum_name,
+                                       &enum_unk1, &enum_unk2,
+                                       &key_mod_time) : False;
+               
+               if (res2)
+               {
+                       display_reg_key_info(out_hnd, ACTION_HEADER   , enum_name, key_mod_time);
+                       display_reg_key_info(out_hnd, ACTION_ENUMERATE, enum_name, key_mod_time);
+                       display_reg_key_info(out_hnd, ACTION_FOOTER   , enum_name, key_mod_time);
+               }
+
+       }
+
+       if (num_values > 0)
+       {
+               fprintf(out_hnd,"Key Values\n");
+               fprintf(out_hnd,"----------\n");
+       }
+
+       for (i = 0; i < num_values; i++)
+       {
+               /*
+                * enumerate key
+                */
+
+               uint32 val_type;
+               BUFFER2 value;
+               fstring val_name;
+
+               /* unknown 1a it */
+               res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+                                       &unk_1a_response) : False;
+
+               if (res2 && unk_1a_response != 5)
+               {
+                       fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+               }
+
+               /* enum key */
+               res2 = res2 ? do_reg_enum_val(smb_cli, &key_pol,
+                                       i, max_valnamelen, max_valbufsize,
+                                       val_name, &val_type, &value) : False;
+               
+               if (res2)
+               {
+                       display_reg_value_info(out_hnd, ACTION_HEADER   , val_name, val_type, &value);
+                       display_reg_value_info(out_hnd, ACTION_ENUMERATE, val_name, val_type, &value);
+                       display_reg_value_info(out_hnd, ACTION_FOOTER   , val_name, val_type, &value);
+               }
+       }
+
+       /* close the handles */
+       if ((*key_name) != 0)
+       {
+               res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+       }
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res1 && res2)
+       {
+               DEBUG(5,("cmd_reg_enum: query succeeded\n"));
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_enum: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry query key
+****************************************************************************/
+static void cmd_reg_query_key(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res1 = True;
+
+       POLICY_HND key_pol;
+       fstring full_keyname;
+       fstring key_name;
+
+       /*
+        * query key info
+        */
+
+       fstring key_class;
+       uint32 key_class_len = 0;
+       uint32 num_subkeys;
+       uint32 max_subkeylen;
+       uint32 max_subkeysize; 
+       uint32 num_values;
+       uint32 max_valnamelen;
+       uint32 max_valbufsize;
+       uint32 sec_desc;
+       NTTIME mod_time;
+
+       DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regquery key_name\n");
+               return;
+       }
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*key_name) != 0)
+       {
+               /* open an entry */
+               res1 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        key_name, 0x02000000, &key_pol) : False;
+       }
+       else
+       {
+               memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+       }
+
+       res1 = res1 ? do_reg_query_key(smb_cli,
+                               &key_pol,
+                               key_class, &key_class_len,
+                               &num_subkeys, &max_subkeylen, &max_subkeysize,
+                               &num_values, &max_valnamelen, &max_valbufsize,
+                               &sec_desc, &mod_time) : False;
+
+       if (res1 && key_class_len != 0)
+       {
+               res1 = res1 ? do_reg_query_key(smb_cli,
+                               &key_pol,
+                               key_class, &key_class_len,
+                               &num_subkeys, &max_subkeylen, &max_subkeysize,
+                               &num_values, &max_valnamelen, &max_valbufsize,
+                               &sec_desc, &mod_time) : False;
+       }
+
+       if (res1)
+       {
+               fprintf(out_hnd,"Registry Query Info Key\n");
+               fprintf(out_hnd,"key class: %s\n", key_class);
+               fprintf(out_hnd,"subkeys, max_len, max_size: %d %d %d\n", num_subkeys, max_subkeylen, max_subkeysize);
+               fprintf(out_hnd,"vals, max_len, max_size: 0x%x 0x%x 0x%x\n", num_values, max_valnamelen, max_valbufsize);
+               fprintf(out_hnd,"sec desc: 0x%x\n", sec_desc);
+               fprintf(out_hnd,"mod time: %s\n", http_timestring(nt_time_to_unix(&mod_time)));
+       }
+
+       /* close the handles */
+       if ((*key_name) != 0)
+       {
+               res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+       }
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res1)
+       {
+               DEBUG(5,("cmd_reg_query: query succeeded\n"));
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_query: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry create value
+****************************************************************************/
+static void cmd_reg_create_val(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND parent_pol;
+       fstring full_keyname;
+       fstring keyname;
+       fstring parent_name;
+       fstring val_name;
+       fstring tmp;
+       uint32 val_type;
+       BUFFER3 value;
+
+#if 0
+       uint32 unk_0;
+       uint32 unk_1;
+       /* query it */
+       res1 = res1 ? do_reg_query_info(smb_cli, &val_pol,
+                               val_name, *val_type) : False;
+#endif
+
+       DEBUG(5, ("cmd_reg_create_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regcreate <val_name> <val_type> <val>\n");
+               return;
+       }
+
+       reg_get_subkey(full_keyname, keyname, val_name);
+
+       if (keyname[0] == 0 || val_name[0] == 0)
+       {
+               fprintf(out_hnd, "invalid key name\n");
+               return;
+       }
+       
+       if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+       {
+               fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+               return;
+       }
+
+       val_type = atoi(tmp);
+
+       if (val_type != 1 && val_type != 3 && val_type != 4)
+       {
+               fprintf(out_hnd, "val_type 1=UNISTR, 3=BYTES, 4=DWORD supported\n");
+               return;
+       }
+
+       if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+       {
+               fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+               return;
+       }
+
+       switch (val_type)
+       {
+               case 0x01: /* UNISTR */
+               {
+                       init_buffer3_str(&value, tmp, strlen(tmp)+1);
+                       break;
+               }
+               case 0x03: /* BYTES */
+               {
+                       init_buffer3_hex(&value, tmp);
+                       break;
+               }
+               case 0x04: /* DWORD */
+               {
+                       uint32 tmp_val;
+                       if (strnequal(tmp, "0x", 2))
+                       {
+                               tmp_val = strtol(tmp, (char**)NULL, 16);
+                       }
+                       else
+                       {
+                               tmp_val = strtol(tmp, (char**)NULL, 10);
+                       }
+                       init_buffer3_uint32(&value, tmp_val);
+                       break;
+               }
+               default:
+               {
+                       fprintf(out_hnd, "i told you i only deal with UNISTR, DWORD and BYTES!\n");
+                       return;
+               }
+       }
+               
+       DEBUG(10,("key data:\n"));
+       dump_data(10, (char *)value.buffer, value.buf_len);
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*val_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        parent_name, 0x02000000, &parent_pol) : False;
+       }
+       else
+       {
+               memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+       }
+
+       /* create an entry */
+       res4 = res3 ? do_reg_create_val(smb_cli, &parent_pol,
+                                val_name, val_type, &value) : False;
+
+       /* flush the modified key */
+       res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+       /* close the val handle */
+       if ((*val_name) != 0)
+       {
+               res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+       }
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_create_val: query succeeded\n"));
+               fprintf(out_hnd,"OK\n");
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_create_val: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry delete value
+****************************************************************************/
+static void cmd_reg_delete_val(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND parent_pol;
+       fstring full_keyname;
+       fstring keyname;
+       fstring parent_name;
+       fstring val_name;
+
+       DEBUG(5, ("cmd_reg_delete_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regdelete <val_name>\n");
+               return;
+       }
+
+       reg_get_subkey(full_keyname, keyname, val_name);
+
+       if (keyname[0] == 0 || val_name[0] == 0)
+       {
+               fprintf(out_hnd, "invalid key name\n");
+               return;
+       }
+       
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*val_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        parent_name, 0x02000000, &parent_pol) : False;
+       }
+       else
+       {
+               memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+       }
+
+       /* delete an entry */
+       res4 = res3 ? do_reg_delete_val(smb_cli, &parent_pol, val_name) : False;
+
+       /* flush the modified key */
+       res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+       /* close the key handle */
+       res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_delete_val: query succeeded\n"));
+               fprintf(out_hnd,"OK\n");
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_delete_val: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry delete key
+****************************************************************************/
+static void cmd_reg_delete_key(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND parent_pol;
+       fstring full_keyname;
+       fstring parent_name;
+       fstring key_name;
+       fstring subkey_name;
+
+       DEBUG(5, ("cmd_reg_delete_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regdeletekey <key_name>\n");
+               return;
+       }
+
+       reg_get_subkey(full_keyname, parent_name, subkey_name);
+
+       if (parent_name[0] == 0 || subkey_name[0] == 0)
+       {
+               fprintf(out_hnd, "invalid key name\n");
+               return;
+       }
+       
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, parent_name, key_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*key_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        key_name, 0x02000000, &parent_pol) : False;
+       }
+       else
+       {
+               memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+       }
+
+       /* create an entry */
+       res4 = res3 ? do_reg_delete_key(smb_cli, &parent_pol, subkey_name) : False;
+
+       /* flush the modified key */
+       res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+       /* close the key handle */
+       if ((*key_name) != 0)
+       {
+               res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+       }
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_delete_key: query succeeded\n"));
+               fprintf(out_hnd,"OK\n");
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_delete_key: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry create key
+****************************************************************************/
+static void cmd_reg_create_key(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND parent_pol;
+       POLICY_HND key_pol;
+       fstring full_keyname;
+       fstring parent_key;
+       fstring parent_name;
+       fstring key_name;
+       fstring key_class;
+       SEC_ACCESS sam_access;
+
+       DEBUG(5, ("cmd_reg_create_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "regcreate <key_name> [key_class]\n");
+               return;
+       }
+
+       reg_get_subkey(full_keyname, parent_key, key_name);
+
+       if (parent_key[0] == 0 || key_name[0] == 0)
+       {
+               fprintf(out_hnd, "invalid key name\n");
+               return;
+       }
+       
+       if (!next_token_nr(NULL, key_class, NULL, sizeof(key_class)))
+       {
+               memset(key_class, 0, sizeof(key_class));
+       }
+
+       /* set access permissions */
+       sam_access.mask = SEC_RIGHTS_READ;
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, parent_key, parent_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*parent_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        parent_name, 0x02000000, &parent_pol) : False;
+       }
+       else
+       {
+               memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+       }
+
+       /* create an entry */
+       res4 = res3 ? do_reg_create_key(smb_cli, &parent_pol,
+                                key_name, key_class, &sam_access, &key_pol) : False;
+
+       /* flush the modified key */
+       res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+       /* close the key handle */
+       res4 = res4 ? do_reg_close(smb_cli, &key_pol) : False;
+
+       /* close the key handle */
+       if ((*parent_name) != 0)
+       {
+               res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+       }
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_create_key: query succeeded\n"));
+               fprintf(out_hnd,"OK\n");
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_create_key: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_test_key_sec(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND key_pol;
+       fstring full_keyname;
+       fstring key_name;
+
+       /*
+        * security info
+        */
+
+       uint32 sec_buf_size;
+       SEC_DESC_BUF *psdb;
+
+       DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "reggetsec <key_name>\n");
+               return;
+       }
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*key_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        key_name, 0x02000000, &key_pol) : False;
+       }
+       else
+       {
+               memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+       }
+
+       /* open an entry */
+       res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                key_name, 0x02000000, &key_pol) : False;
+
+       /* query key sec info.  first call sets sec_buf_size. */
+
+       sec_buf_size = 0;
+       res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+                               &sec_buf_size, &psdb) : False;
+       
+       free_sec_desc_buf(&psdb);
+
+       res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+                               &sec_buf_size, &psdb) : False;
+
+       if (res4 && psdb->len > 0 && psdb->sec != NULL)
+       {
+               display_sec_desc(out_hnd, ACTION_HEADER   , psdb->sec);
+               display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+               display_sec_desc(out_hnd, ACTION_FOOTER   , psdb->sec);
+
+               res4 = res4 ? do_reg_set_key_sec(smb_cli, &key_pol, psdb) : False;
+       }
+
+       free_sec_desc_buf(&psdb);
+
+       /* close the key handle */
+       if ((*key_name) != 0)
+       {
+               res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+       }
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_test2: query succeeded\n"));
+               fprintf(out_hnd,"Registry Test2\n");
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_test2: query failed\n"));
+       }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_get_key_sec(struct client_info *info)
+{
+       BOOL res = True;
+       BOOL res3 = True;
+       BOOL res4 = True;
+
+       POLICY_HND key_pol;
+       fstring full_keyname;
+       fstring key_name;
+
+       /*
+        * security info
+        */
+
+       uint32 sec_buf_size;
+       SEC_DESC_BUF *psdb;
+
+       DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+       if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+       {
+               fprintf(out_hnd, "reggetsec <key_name>\n");
+               return;
+       }
+
+       /* open WINREG session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+       /* open registry receive a policy handle */
+       res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+                               &info->dom.reg_pol_connect) : False;
+
+       if ((*key_name) != 0)
+       {
+               /* open an entry */
+               res3 = res  ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                        key_name, 0x02000000, &key_pol) : False;
+       }
+       else
+       {
+               memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+       }
+
+       /* open an entry */
+       res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+                                key_name, 0x02000000, &key_pol) : False;
+
+       /* Get the size. */
+       sec_buf_size = 0;
+       res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+                               &sec_buf_size, &psdb) : False;
+       
+       free_sec_desc_buf(&psdb);
+
+       res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+                               &sec_buf_size, &psdb) : False;
+
+       if (res4 && psdb->len > 0 && psdb->sec != NULL)
+       {
+               display_sec_desc(out_hnd, ACTION_HEADER   , psdb->sec);
+               display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+               display_sec_desc(out_hnd, ACTION_FOOTER   , psdb->sec);
+       }
+
+       free_sec_desc_buf(&psdb);
+
+       /* close the key handle */
+       if ((*key_name) != 0)
+       {
+               res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+       }
+
+       /* close the registry handles */
+       res  = res  ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res && res3 && res4)
+       {
+               DEBUG(5,("cmd_reg_get_key_sec: query succeeded\n"));
+       }
+       else
+       {
+               DEBUG(5,("cmd_reg_get_key_sec: query failed\n"));
+       }
+}
+
+#endif /* 0 */
+
+/****************************************************************************
+nt registry shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_shutdown(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                 int argc, const char **argv)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       fstring msg;
+       uint32 timeout = 20;
+       BOOL force = False;
+       BOOL reboot = False;
+       int opt;
+
+       *msg = 0;
+       optind = 0; /* TODO: test if this hack works on other systems too --simo */
+
+       while ((opt = getopt(argc, argv, "m:t:rf")) != EOF)
+       {
+               /*fprintf (stderr, "[%s]\n", argv[argc-1]);*/
+       
+               switch (opt)
+               {
+                       case 'm':
+                               safe_strcpy(msg, optarg, sizeof(msg)-1);
+                               /*fprintf (stderr, "[%s|%s]\n", optarg, msg);*/
+                               break;
+
+                       case 't':
+                               timeout = atoi(optarg);
+                               /*fprintf (stderr, "[%s|%d]\n", optarg, timeout);*/
+                               break;
+
+                       case 'r':
+                               reboot = True;
+                               break;
+
+                       case 'f':
+                               force = True;
+                               break;
+
+               }
+       }
+
+       /* create an entry */
+       result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, reboot, force);
+
+       if (NT_STATUS_IS_OK(result))
+               DEBUG(5,("cmd_reg_shutdown: query succeeded\n"));
+       else
+               DEBUG(5,("cmd_reg_shutdown: query failed\n"));
+
+       return result;
+}
+
+/****************************************************************************
+abort a shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_abort_shutdown(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx, int argc, 
+                                       const char **argv)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+       result = cli_reg_abort_shutdown(cli, mem_ctx);
+
+       if (NT_STATUS_IS_OK(result))
+               DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+       else
+               DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+       return result;
+}
+
+
+/* List of commands exported by this module */
+struct cmd_set reg_commands[] = {
+
+       { "REG"  },
+
+       { "shutdown",           cmd_reg_shutdown,               PI_WINREG, "Remote Shutdown",
+                               "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" },
+                               
+       { "abortshutdown",      cmd_reg_abort_shutdown,         PI_WINREG, "Abort Shutdown",
+                               "syntax: abortshutdown" },
+/*
+       { "regenum",            cmd_reg_enum,                   "Registry Enumeration",
+                               "<keyname>" },
+                               
+       { "regdeletekey",       cmd_reg_delete_key,             "Registry Key Delete",
+                               "<keyname>" },
+                               
+       { "regcreatekey",       cmd_reg_create_key,             "Registry Key Create",
+                               "<keyname> [keyclass]" },
+                               
+       { "regqueryval",        cmd_reg_query_info,             "Registry Value Query",
+                               "<valname>" },
+                               
+       { "regquerykey",        cmd_reg_query_key,              "Registry Key Query",
+                               "<keyname>" },
+                               
+       { "regdeleteval",       cmd_reg_delete_val,             "Registry Value Delete",
+                               "<valname>" },
+       
+       { "regcreateval",       cmd_reg_create_val,             "Registry Key Create",
+                               "<valname> <valtype> <value>" },
+       
+       { "reggetsec",          cmd_reg_get_key_sec,            "Registry Key Security",
+                               "<keyname>" },
+       
+       { "regtestsec",         cmd_reg_test_key_sec,           "Test Registry Key Security",
+                               "<keyname>" },
+*/
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_samr.c b/source4/rpcclient/cmd_samr.c
new file mode 100644 (file)
index 0000000..cec6b16
--- /dev/null
@@ -0,0 +1,1517 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Andrew Tridgell              1992-2000,
+   Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+   Copyright (C) Elrond                            2000,
+   Copyright (C) Tim Potter                        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 "rpcclient.h"
+
+extern DOM_SID domain_sid;
+
+/****************************************************************************
+ display sam_user_info_21 structure
+ ****************************************************************************/
+static void display_sam_user_info_21(SAM_USER_INFO_21 *usr)
+{
+       fstring temp;
+
+       unistr2_to_ascii(temp, &usr->uni_user_name, sizeof(temp)-1);
+       printf("\tUser Name   :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_full_name, sizeof(temp)-1);
+       printf("\tFull Name   :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_home_dir, sizeof(temp)-1);
+       printf("\tHome Drive  :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_dir_drive, sizeof(temp)-1);
+       printf("\tDir Drive   :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_profile_path, sizeof(temp)-1);
+       printf("\tProfile Path:\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_logon_script, sizeof(temp)-1);
+       printf("\tLogon Script:\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_acct_desc, sizeof(temp)-1);
+       printf("\tDescription :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_workstations, sizeof(temp)-1);
+       printf("\tWorkstations:\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_unknown_str, sizeof(temp)-1);
+       printf("\tUnknown Str :\t%s\n", temp);
+       
+       unistr2_to_ascii(temp, &usr->uni_munged_dial, sizeof(temp)-1);
+       printf("\tRemote Dial :\t%s\n", temp);
+       
+       printf("\tLogon Time               :\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->logon_time)));
+       printf("\tLogoff Time              :\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->logoff_time)));
+       printf("\tKickoff Time             :\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->kickoff_time)));
+       printf("\tPassword last set Time   :\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->pass_last_set_time)));
+       printf("\tPassword can change Time :\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->pass_can_change_time)));
+       printf("\tPassword must change Time:\t%s\n", 
+              http_timestring(nt_time_to_unix(&usr->pass_must_change_time)));
+       
+       printf("\tunknown_2[0..31]...\n"); /* user passwords? */
+       
+       printf("\tuser_rid :\t0x%x\n"  , usr->user_rid ); /* User ID */
+       printf("\tgroup_rid:\t0x%x\n"  , usr->group_rid); /* Group ID */
+       printf("\tacb_info :\t0x%04x\n", usr->acb_info ); /* Account Control Info */
+       
+       printf("\tunknown_3:\t0x%08x\n", usr->unknown_3); /* 0x00ff ffff */
+       printf("\tlogon_divs:\t%d\n", usr->logon_divs); /* 0x0000 00a8 which is 168 which is num hrs in a week */
+       printf("\tunknown_5:\t0x%08x\n", usr->unknown_5); /* 0x0002 0000 */
+       
+       printf("\tpadding1[0..7]...\n");
+       
+       if (usr->ptr_logon_hrs) {
+               printf("\tlogon_hrs[0..%d]...\n", usr->logon_hrs.len);
+       }
+}
+
+static const char *display_time(NTTIME nttime)
+{
+       static fstring string;
+
+       float high;
+       float low;
+       int sec;
+       int days, hours, mins, secs;
+
+       if (nttime.high==0 && nttime.low==0)
+               return "Now";
+
+       if (nttime.high==0x80000000 && nttime.low==0)
+               return "Never";
+
+       high = 65536;   
+       high = high/10000;
+       high = high*65536;
+       high = high/1000;
+       high = high * (~nttime.high);
+
+       low = ~nttime.low;      
+       low = low/(1000*1000*10);
+
+       sec=high+low;
+
+       days=sec/(60*60*24);
+       hours=(sec - (days*60*60*24)) / (60*60);
+       mins=(sec - (days*60*60*24) - (hours*60*60) ) / 60;
+       secs=sec - (days*60*60*24) - (hours*60*60) - (mins*60);
+
+       snprintf(string, sizeof(string)-1, "%u days, %u hours, %u minutes, %u seconds", days, hours, mins, secs);
+       return (string);
+}
+
+static void display_sam_unk_info_1(SAM_UNK_INFO_1 *info1)
+{
+       
+       printf("Minimum password length:                     %d\n", info1->min_length_password);
+       printf("Password uniqueness (remember x passwords):  %d\n", info1->password_history);
+       printf("flag:                                        ");
+       if(info1->flag&&2==2) printf("users must open a session to change password ");
+       printf("\n");
+
+       printf("password expire in:                          %s\n", display_time(info1->expire));
+       printf("Min password age (allow changing in x days): %s\n", display_time(info1->min_passwordage));
+}
+
+static void display_sam_unk_info_2(SAM_UNK_INFO_2 *info2)
+{
+       fstring name;
+
+       unistr2_to_ascii(name, &info2->uni_domain, sizeof(name) - 1); 
+       printf("Domain:\t%s\n", name);
+
+       unistr2_to_ascii(name, &info2->uni_server, sizeof(name) - 1); 
+       printf("Server:\t%s\n", name);
+
+       printf("Total Users:\t%d\n", info2->num_domain_usrs);
+       printf("Total Groups:\t%d\n", info2->num_domain_grps);
+       printf("Total Aliases:\t%d\n", info2->num_local_grps);
+       
+       printf("Sequence No:\t%d\n", info2->seq_num);
+       
+       printf("Unknown 0:\t0x%x\n", info2->unknown_0);
+       printf("Unknown 1:\t0x%x\n", info2->unknown_1);
+       printf("Unknown 2:\t0x%x\n", info2->unknown_2);
+       printf("Unknown 3:\t0x%x\n", info2->unknown_3);
+       printf("Unknown 4:\t0x%x\n", info2->unknown_4);
+       printf("Unknown 5:\t0x%x\n", info2->unknown_5);
+       printf("Unknown 6:\t0x%x\n", info2->unknown_6);
+}
+
+static void display_sam_info_1(SAM_ENTRY1 *e1, SAM_STR1 *s1)
+{
+       fstring tmp;
+
+       printf("index: 0x%x ", e1->user_idx);
+       printf("RID: 0x%x ", e1->rid_user);
+       printf("acb: 0x%x ", e1->acb_info);
+
+       unistr2_to_ascii(tmp, &s1->uni_acct_name, sizeof(tmp)-1);
+       printf("Account: %s\t", tmp);
+
+       unistr2_to_ascii(tmp, &s1->uni_full_name, sizeof(tmp)-1);
+       printf("Name: %s\t", tmp);
+
+       unistr2_to_ascii(tmp, &s1->uni_acct_desc, sizeof(tmp)-1);
+       printf("Desc: %s\n", tmp);
+}
+
+static void display_sam_info_2(SAM_ENTRY2 *e2, SAM_STR2 *s2)
+{
+       fstring tmp;
+
+       printf("index: 0x%x ", e2->user_idx);
+       printf("RID: 0x%x ", e2->rid_user);
+       printf("acb: 0x%x ", e2->acb_info);
+       
+       unistr2_to_ascii(tmp, &s2->uni_srv_name, sizeof(tmp)-1);
+       printf("Account: %s\t", tmp);
+
+       unistr2_to_ascii(tmp, &s2->uni_srv_desc, sizeof(tmp)-1);
+       printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_3(SAM_ENTRY3 *e3, SAM_STR3 *s3)
+{
+       fstring tmp;
+
+       printf("index: 0x%x ", e3->grp_idx);
+       printf("RID: 0x%x ", e3->rid_grp);
+       printf("attr: 0x%x ", e3->attr);
+       
+       unistr2_to_ascii(tmp, &s3->uni_grp_name, sizeof(tmp)-1);
+       printf("Account: %s\t", tmp);
+
+       unistr2_to_ascii(tmp, &s3->uni_grp_desc, sizeof(tmp)-1);
+       printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_4(SAM_ENTRY4 *e4, SAM_STR4 *s4)
+{
+       int i;
+
+       printf("index: %d ", e4->user_idx);
+       
+       printf("Account: ");
+       for (i=0; i<s4->acct_name.str_str_len; i++)
+               printf("%c", s4->acct_name.buffer[i]);
+       printf("\n");
+
+}
+
+static void display_sam_info_5(SAM_ENTRY5 *e5, SAM_STR5 *s5)
+{
+       int i;
+
+       printf("index: 0x%x ", e5->grp_idx);
+       
+       printf("Account: ");
+       for (i=0; i<s5->grp_name.str_str_len; i++)
+               printf("%c", s5->grp_name.buffer[i]);
+       printf("\n");
+
+}
+
+/****************************************************************************
+ Try samr_connect4 first, then samr_conenct if it fails
+ ****************************************************************************/
+static NTSTATUS try_samr_connects(struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                 uint32 access_mask, POLICY_HND *connect_pol)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       
+       result = cli_samr_connect4(cli, mem_ctx, access_mask, connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               result = cli_samr_connect(cli, mem_ctx, access_mask,
+                                         connect_pol);
+       }
+       return result;
+}
+
+/**********************************************************************
+ * Query user information 
+ */
+static NTSTATUS cmd_samr_query_user(struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx,
+                                    int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 info_level = 21;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       SAM_USERINFO_CTR *user_ctr;
+       fstring server;
+       uint32 user_rid;
+       
+       if ((argc < 2) || (argc > 4)) {
+               printf("Usage: %s rid [info level] [access mask] \n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       sscanf(argv[1], "%i", &user_rid);
+       
+       if (argc > 2)
+               sscanf(argv[2], "%i", &info_level);
+               
+       if (argc > 3)
+               sscanf(argv[3], "%x", &access_mask);
+       
+
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+       
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                   access_mask,
+                                   user_rid, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       ZERO_STRUCT(user_ctr);
+
+       result = cli_samr_query_userinfo(cli, mem_ctx, &user_pol, 
+                                        info_level, &user_ctr);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       display_sam_user_info_21(user_ctr->info.id21);
+
+done:
+       return result;
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info1(GROUP_INFO1 *info1)
+{
+       fstring temp;
+
+       unistr2_to_ascii(temp, &info1->uni_acct_name, sizeof(temp)-1);
+       printf("\tGroup Name:\t%s\n", temp);
+       unistr2_to_ascii(temp, &info1->uni_acct_desc, sizeof(temp)-1);
+       printf("\tDescription:\t%s\n", temp);
+       printf("\tunk1:%d\n", info1->unknown_1);
+       printf("\tNum Members:%d\n", info1->num_members);
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info4(GROUP_INFO4 *info4)
+{
+       fstring desc;
+
+       unistr2_to_ascii(desc, &info4->uni_acct_desc, sizeof(desc)-1);
+       printf("\tGroup Description:%s\n", desc);
+}
+
+/****************************************************************************
+ display sam sync structure
+ ****************************************************************************/
+static void display_group_info_ctr(GROUP_INFO_CTR *ctr)
+{
+       switch (ctr->switch_value1) {
+           case 1: {
+                   display_group_info1(&ctr->group.info1);
+                   break;
+           }
+           case 4: {
+                   display_group_info4(&ctr->group.info4);
+                   break;
+           }
+       }
+}
+
+/***********************************************************************
+ * Query group information 
+ */
+static NTSTATUS cmd_samr_query_group(struct cli_state *cli, 
+                                     TALLOC_CTX *mem_ctx,
+                                     int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, group_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 info_level = 1;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       GROUP_INFO_CTR *group_ctr;
+       fstring                 server; 
+       uint32 group_rid;
+       
+       if ((argc < 2) || (argc > 4)) {
+               printf("Usage: %s rid [info level] [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+        sscanf(argv[1], "%i", &group_rid);
+       
+       if (argc > 2)
+               sscanf(argv[2], "%i", &info_level);
+       
+       if (argc > 3)
+               sscanf(argv[3], "%x", &access_mask);
+
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+                                    access_mask,
+                                    group_rid, &group_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_query_groupinfo(cli, mem_ctx, &group_pol, 
+                                         info_level, &group_ctr);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       display_group_info_ctr(group_ctr);
+
+done:
+       return result;
+}
+
+/* Query groups a user is a member of */
+
+static NTSTATUS cmd_samr_query_usergroups(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv) 
+{
+       POLICY_HND              connect_pol, 
+                               domain_pol, 
+                               user_pol;
+       NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
+       uint32                  num_groups, 
+                               user_rid;
+       uint32                  access_mask = MAXIMUM_ALLOWED_ACCESS;
+       DOM_GID                 *user_gids;
+       int                     i;
+       fstring                 server;
+       
+       if ((argc < 2) || (argc > 3)) {
+               printf("Usage: %s rid [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       sscanf(argv[1], "%i", &user_rid);
+       
+       if (argc > 2)
+               sscanf(argv[2], "%x", &access_mask);
+
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+               
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                   access_mask,
+                                   user_rid, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+                                          &num_groups, &user_gids);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < num_groups; i++) {
+               printf("\tgroup rid:[0x%x] attr:[0x%x]\n", 
+                      user_gids[i].g_rid, user_gids[i].attr);
+       }
+
+ done:
+       return result;
+}
+
+/* Query aliases a user is a member of */
+
+static NTSTATUS cmd_samr_query_useraliases(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv) 
+{
+       POLICY_HND              connect_pol, domain_pol;
+       NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
+       uint32                  user_rid, num_aliases, *alias_rids;
+       uint32                  access_mask = MAXIMUM_ALLOWED_ACCESS;
+       int                     i;
+       fstring                 server;
+       DOM_SID                 tmp_sid;
+       DOM_SID2                sid;
+       DOM_SID global_sid_Builtin;
+
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       if ((argc < 3) || (argc > 4)) {
+               printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       sscanf(argv[2], "%i", &user_rid);
+       
+       if (argc > 3)
+               sscanf(argv[3], "%x", &access_mask);
+
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+               
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       if (StrCaseCmp(argv[1], "domain")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             access_mask,
+                                             &domain_sid, &domain_pol);
+       else if (StrCaseCmp(argv[1], "builtin")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             access_mask,
+                                             &global_sid_Builtin, &domain_pol);
+       else
+               return NT_STATUS_OK;
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       sid_copy(&tmp_sid, &domain_sid);
+       sid_append_rid(&tmp_sid, user_rid);
+       init_dom_sid2(&sid, &tmp_sid);
+
+       result = cli_samr_query_useraliases(cli, mem_ctx, &domain_pol, 1, &sid, &num_aliases, &alias_rids);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < num_aliases; i++) {
+               printf("\tgroup rid:[0x%x]\n", alias_rids[i]);
+       }
+
+ done:
+       return result;
+}
+
+/* Query members of a group */
+
+static NTSTATUS cmd_samr_query_groupmem(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx,
+                                        int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, group_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 num_members, *group_rids, *group_attrs, group_rid;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       int i;
+       fstring                 server;
+       
+       if ((argc < 2) || (argc > 3)) {
+               printf("Usage: %s rid [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       sscanf(argv[1], "%i", &group_rid);
+       
+       if (argc > 2)
+               sscanf(argv[2], "%x", &access_mask);
+
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+                                    access_mask,
+                                    group_rid, &group_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol,
+                                        &num_members, &group_rids,
+                                        &group_attrs);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < num_members; i++) {
+               printf("\trid:[0x%x] attr:[0x%x]\n", group_rids[i],
+                      group_attrs[i]);
+       }
+
+ done:
+       return result;
+}
+
+/* Enumerate domain users */
+
+static NTSTATUS cmd_samr_enum_dom_users(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx,
+                                       int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx, size, num_dom_users, i;
+       char **dom_users;
+       uint32 *dom_rids;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       uint16 acb_mask = ACB_NORMAL;
+       BOOL got_connect_pol = False, got_domain_pol = False;
+
+       if ((argc < 1) || (argc > 2)) {
+               printf("Usage: %s [access_mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc > 1)
+               sscanf(argv[1], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_connect_pol = True;
+
+       /* Get domain policy handle */
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     access_mask,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_domain_pol = True;
+
+       /* Enumerate domain users */
+
+       start_idx = 0;
+       size = 0xffff;
+
+       do {
+               result = cli_samr_enum_dom_users(
+                       cli, mem_ctx, &domain_pol, &start_idx, acb_mask,
+                       size, &dom_users, &dom_rids, &num_dom_users);
+
+               if (NT_STATUS_IS_OK(result) ||
+                   NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+                       for (i = 0; i < num_dom_users; i++)
+                               printf("group:[%s] rid:[0x%x]\n", 
+                                      dom_users[i], dom_rids[i]);
+               }
+
+       } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+       if (got_domain_pol)
+               cli_samr_close(cli, mem_ctx, &domain_pol);
+
+       if (got_connect_pol)
+               cli_samr_close(cli, mem_ctx, &connect_pol);
+
+       return result;
+}
+
+/* Enumerate domain groups */
+
+static NTSTATUS cmd_samr_enum_dom_groups(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx, size, num_dom_groups, i;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       struct acct_info *dom_groups;
+       BOOL got_connect_pol = False, got_domain_pol = False;
+
+       if ((argc < 1) || (argc > 2)) {
+               printf("Usage: %s [access_mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc > 1)
+               sscanf(argv[1], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_connect_pol = True;
+
+       /* Get domain policy handle */
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     access_mask,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_domain_pol = True;
+
+       /* Enumerate domain groups */
+
+       start_idx = 0;
+       size = 0xffff;
+
+       do {
+               result = cli_samr_enum_dom_groups(
+                       cli, mem_ctx, &domain_pol, &start_idx, size,
+                       &dom_groups, &num_dom_groups);
+
+               if (NT_STATUS_IS_OK(result) ||
+                   NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+                       for (i = 0; i < num_dom_groups; i++)
+                               printf("group:[%s] rid:[0x%x]\n", 
+                                      dom_groups[i].acct_name,
+                                      dom_groups[i].rid);
+               }
+
+       } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+       if (got_domain_pol)
+               cli_samr_close(cli, mem_ctx, &domain_pol);
+
+       if (got_connect_pol)
+               cli_samr_close(cli, mem_ctx, &connect_pol);
+
+       return result;
+}
+
+/* Enumerate alias groups */
+
+static NTSTATUS cmd_samr_enum_als_groups(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx, size, num_als_groups, i;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       struct acct_info *als_groups;
+       DOM_SID global_sid_Builtin;
+       BOOL got_connect_pol = False, got_domain_pol = False;
+
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       if ((argc < 2) || (argc > 3)) {
+               printf("Usage: %s builtin|domain [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc > 2)
+               sscanf(argv[2], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_connect_pol = True;
+
+       /* Get domain policy handle */
+
+       if (StrCaseCmp(argv[1], "domain")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             access_mask,
+                                             &domain_sid, &domain_pol);
+       else if (StrCaseCmp(argv[1], "builtin")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             access_mask,
+                                             &global_sid_Builtin, &domain_pol);
+       else
+               return NT_STATUS_OK;
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       got_domain_pol = True;
+
+       /* Enumerate alias groups */
+
+       start_idx = 0;
+       size = 0xffff;          /* Number of groups to retrieve */
+
+       do {
+               result = cli_samr_enum_als_groups(
+                       cli, mem_ctx, &domain_pol, &start_idx, size,
+                       &als_groups, &num_als_groups);
+
+               if (NT_STATUS_IS_OK(result) ||
+                   NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+                       for (i = 0; i < num_als_groups; i++)
+                               printf("group:[%s] rid:[0x%x]\n", 
+                                      als_groups[i].acct_name,
+                                      als_groups[i].rid);
+               }
+       } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+       if (got_domain_pol)
+               cli_samr_close(cli, mem_ctx, &domain_pol);
+       
+       if (got_connect_pol)
+               cli_samr_close(cli, mem_ctx, &connect_pol);
+       
+       return result;
+}
+
+/* Query alias membership */
+
+static NTSTATUS cmd_samr_query_aliasmem(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx,
+                                        int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, alias_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 alias_rid, num_members, i;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       DOM_SID *alias_sids;
+       DOM_SID global_sid_Builtin;
+       
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       if ((argc < 3) || (argc > 4)) {
+               printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       sscanf(argv[2], "%i", &alias_rid);
+       
+       if (argc > 3)
+               sscanf(argv[3], "%x", &access_mask);
+
+       /* Open SAMR handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Open handle on domain */
+       
+       if (StrCaseCmp(argv[1], "domain")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             MAXIMUM_ALLOWED_ACCESS,
+                                             &domain_sid, &domain_pol);
+       else if (StrCaseCmp(argv[1], "builtin")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             MAXIMUM_ALLOWED_ACCESS,
+                                             &global_sid_Builtin, &domain_pol);
+       else
+               return NT_STATUS_OK;
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Open handle on alias */
+
+       result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+                                    access_mask,
+                                    alias_rid, &alias_pol);
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol,
+                                        &num_members, &alias_sids);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < num_members; i++) {
+               fstring sid_str;
+
+               sid_to_string(sid_str, &alias_sids[i]);
+               printf("\tsid:[%s]\n", sid_str);
+       }
+
+ done:
+       return result;
+}
+
+/* Query display info */
+
+static NTSTATUS cmd_samr_query_dispinfo(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx,
+                                        int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx=0, max_entries=250, max_size = 0xffff, num_entries, i;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       uint32 info_level = 1;
+       SAM_DISPINFO_CTR ctr;
+       SAM_DISPINFO_1 info1;
+       SAM_DISPINFO_2 info2;
+       SAM_DISPINFO_3 info3;
+       SAM_DISPINFO_4 info4;
+       SAM_DISPINFO_5 info5;
+       int loop_count = 0;
+       BOOL got_params = False; /* Use get_query_dispinfo_params() or not? */
+
+       if (argc > 5) {
+               printf("Usage: %s [info level] [start index] [max entries] [max size] [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc >= 2)
+                sscanf(argv[1], "%i", &info_level);
+        
+       if (argc >= 3)
+                sscanf(argv[2], "%i", &start_idx);
+        
+       if (argc >= 4) {
+                sscanf(argv[3], "%i", &max_entries);
+               got_params = True;
+       }
+       
+       if (argc >= 5) {
+                sscanf(argv[4], "%i", &max_size);
+               got_params = True;
+       }
+       
+       if (argc >= 6)
+                sscanf(argv[5], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Get domain policy handle */
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     access_mask, 
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Query display info */
+
+       ZERO_STRUCT(ctr);
+       ZERO_STRUCT(info1);
+       
+       switch (info_level) {
+       case 1:
+               ZERO_STRUCT(info1);
+               ctr.sam.info1 = &info1;
+               break;
+       case 2:
+               ZERO_STRUCT(info2);
+               ctr.sam.info2 = &info2;
+               break;
+       case 3:
+               ZERO_STRUCT(info3);
+               ctr.sam.info3 = &info3;
+               break;
+       case 4:
+               ZERO_STRUCT(info4);
+               ctr.sam.info4 = &info4;
+               break;
+       case 5:
+               ZERO_STRUCT(info5);
+               ctr.sam.info5 = &info5;
+               break;
+       }
+
+
+       while(1) {
+
+               if (!got_params)
+                       get_query_dispinfo_params(
+                               loop_count, &max_entries, &max_size);
+               
+               result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+                                                &start_idx, info_level,
+                                                &num_entries, max_entries, 
+                                                max_size, &ctr);
+
+               loop_count++;
+
+               if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) 
+                       break;
+
+               if (num_entries == 0) 
+                       break;
+
+               for (i = 0; i < num_entries; i++) {
+                       switch (info_level) {
+                       case 1:
+                               display_sam_info_1(&ctr.sam.info1->sam[i], &ctr.sam.info1->str[i]);
+                               break;
+                       case 2:
+                               display_sam_info_2(&ctr.sam.info2->sam[i], &ctr.sam.info2->str[i]);
+                               break;
+                       case 3:
+                               display_sam_info_3(&ctr.sam.info3->sam[i], &ctr.sam.info3->str[i]);
+                               break;
+                       case 4:
+                               display_sam_info_4(&ctr.sam.info4->sam[i], &ctr.sam.info4->str[i]);
+                               break;
+                       case 5:
+                               display_sam_info_5(&ctr.sam.info5->sam[i], &ctr.sam.info5->str[i]);
+                               break;
+                       }
+               }
+       }
+
+ done:
+       return result;
+}
+
+/* Query domain info */
+
+static NTSTATUS cmd_samr_query_dominfo(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx,
+                                       int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 switch_level = 2;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+       SAM_UNK_CTR ctr;
+
+       if (argc > 2) {
+               printf("Usage: %s [info level] [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc > 1)
+                sscanf(argv[1], "%i", &switch_level);
+       
+       if (argc > 2)
+                sscanf(argv[2], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Get domain policy handle */
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     access_mask,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Query domain info */
+
+       result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+                                        switch_level, &ctr);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Display domain info */
+
+       switch (switch_level) {
+       case 1:
+               display_sam_unk_info_1(&ctr.info.inf1);
+               break;
+       case 2:
+               display_sam_unk_info_2(&ctr.info.inf2);
+               break;
+       default:
+               printf("cannot display domain info for switch value %d\n",
+                      switch_level);
+               break;
+       }
+
+ done:
+       cli_samr_close(cli, mem_ctx, &domain_pol);
+       cli_samr_close(cli, mem_ctx, &connect_pol);
+       return result;
+}
+
+/* Create domain user */
+
+static NTSTATUS cmd_samr_create_dom_user(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       const char *acct_name;
+       uint16 acb_info;
+       uint32 unknown, user_rid;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+       if ((argc < 2) || (argc > 3)) {
+               printf("Usage: %s username [access mask]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       acct_name = argv[1];
+       
+       if (argc > 2)
+                sscanf(argv[2], "%x", &access_mask);
+
+       /* Get sam policy handle */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Get domain policy handle */
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     access_mask,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Create domain user */
+
+       acb_info = ACB_NORMAL;
+       unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+       result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+                                         acct_name, acb_info, unknown,
+                                         &user_pol, &user_rid);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+ done:
+       return result;
+}
+
+/* Lookup sam names */
+
+static NTSTATUS cmd_samr_lookup_names(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx,
+                                      int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND connect_pol, domain_pol;
+       uint32 flags = 0x000003e8; /* Unknown */
+       uint32 num_rids, num_names, *name_types, *rids;
+       const char **names;
+       int i;
+       DOM_SID global_sid_Builtin;
+
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       if (argc < 3) {
+               printf("Usage: %s  domain|builtin name1 [name2 [name3] [...]]\n", argv[0]);
+               printf("check on the domain SID: S-1-5-21-x-y-z\n");
+               printf("or check on the builtin SID: S-1-5-32\n");
+               return NT_STATUS_OK;
+       }
+
+       /* Get sam policy and domain handles */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       if (StrCaseCmp(argv[1], "domain")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             MAXIMUM_ALLOWED_ACCESS,
+                                             &domain_sid, &domain_pol);
+       else if (StrCaseCmp(argv[1], "builtin")==0)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             MAXIMUM_ALLOWED_ACCESS,
+                                             &global_sid_Builtin, &domain_pol);
+       else
+               return NT_STATUS_OK;
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Look up names */
+
+       num_names = argc - 2;
+       names = (const char **)talloc(mem_ctx, sizeof(char *) * num_names);
+
+       for (i = 0; i < argc - 2; i++)
+               names[i] = argv[i + 2];
+
+       result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+                                      flags, num_names, names,
+                                      &num_rids, &rids, &name_types);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+       for (i = 0; i < num_names; i++)
+               printf("name %s: 0x%x (%d)\n", names[i], rids[i], 
+                      name_types[i]);
+
+ done:
+       return result;
+}
+
+/* Lookup sam rids */
+
+static NTSTATUS cmd_samr_lookup_rids(struct cli_state *cli, 
+                                     TALLOC_CTX *mem_ctx,
+                                     int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND connect_pol, domain_pol;
+       uint32 flags = 0x000003e8; /* Unknown */
+       uint32 num_rids, num_names, *rids, *name_types;
+       char **names;
+       int i;
+
+       if (argc < 2) {
+               printf("Usage: %s rid1 [rid2 [rid3] [...]]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* Get sam policy and domain handles */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Look up rids */
+
+       num_rids = argc - 1;
+       rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+       for (i = 0; i < argc - 1; i++)
+                sscanf(argv[i + 1], "%i", &rids[i]);
+
+       result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+                                     flags, num_rids, rids,
+                                     &num_names, &names, &name_types);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+       for (i = 0; i < num_names; i++)
+               printf("rid 0x%x: %s (%d)\n", rids[i], names[i], name_types[i]);
+
+ done:
+       return result;
+}
+
+/* Delete domain user */
+
+static NTSTATUS cmd_samr_delete_dom_user(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+       if ((argc < 2) || (argc > 3)) {
+               printf("Usage: %s username\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc > 2)
+                sscanf(argv[2], "%x", &access_mask);
+
+       /* Get sam policy and domain handles */
+
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Get handle on user */
+
+       {
+               uint32 *user_rids, num_rids, *name_types;
+               uint32 flags = 0x000003e8; /* Unknown */
+
+               result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+                                              flags, 1, (const char **)&argv[1],
+                                              &num_rids, &user_rids,
+                                              &name_types);
+
+               if (!NT_STATUS_IS_OK(result))
+                       goto done;
+
+               result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                           access_mask,
+                                           user_rids[0], &user_pol);
+
+               if (!NT_STATUS_IS_OK(result))
+                       goto done;
+       }
+
+       /* Delete user */
+
+       result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+ done:
+       return result;
+}
+
+/**********************************************************************
+ * Query user security object 
+ */
+static NTSTATUS cmd_samr_query_sec_obj(struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx,
+                                    int argc, const char **argv) 
+{
+       POLICY_HND connect_pol, domain_pol, user_pol, *pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 info_level = 4;
+       fstring server;
+       uint32 user_rid = 0;
+       TALLOC_CTX *ctx = NULL;
+       SEC_DESC_BUF *sec_desc_buf=NULL;
+       BOOL domain = False;
+
+       ctx=talloc_init("cmd_samr_query_sec_obj");
+       
+       if ((argc < 1) || (argc > 2)) {
+               printf("Usage: %s [rid|-d]\n", argv[0]);
+               printf("\tSpecify rid for security on user, -d for security on domain\n");
+               return NT_STATUS_OK;
+       }
+       
+       if (argc > 1) {
+               if (strcmp(argv[1], "-d") == 0)
+                       domain = True;
+               else
+                       sscanf(argv[1], "%i", &user_rid);
+       }
+       
+       slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+       strupper (server);
+       result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                  &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       if (domain || user_rid)
+               result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                             MAXIMUM_ALLOWED_ACCESS,
+                                             &domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       if (user_rid)
+               result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                           MAXIMUM_ALLOWED_ACCESS,
+                                           user_rid, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       /* Pick which query pol to use */
+
+       pol = &connect_pol;
+
+       if (domain)
+               pol = &domain_pol;
+
+       if (user_rid)
+               pol = &user_pol;
+
+       /* Query SAM security object */
+
+       result = cli_samr_query_sec_obj(cli, mem_ctx, pol, info_level, ctx, 
+                                       &sec_desc_buf);
+
+       if (!NT_STATUS_IS_OK(result))
+               goto done;
+
+       display_sec_desc(sec_desc_buf->sec);
+       
+done:
+       talloc_destroy(ctx);
+       return result;
+}
+
+static NTSTATUS cmd_samr_get_dom_pwinfo(struct cli_state *cli, 
+                                       TALLOC_CTX *mem_ctx,
+                                       int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint16 unk_0, unk_1, unk_2;
+
+       if (argc != 1) {
+               printf("Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_samr_get_dom_pwinfo(cli, mem_ctx, &unk_0, &unk_1, &unk_2);
+       
+       if (NT_STATUS_IS_OK(result)) {
+               printf("unk_0 = 0x%08x\n", unk_0);
+               printf("unk_1 = 0x%08x\n", unk_1);
+               printf("unk_2 = 0x%08x\n", unk_2);
+       }
+
+       return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set samr_commands[] = {
+
+       { "SAMR" },
+
+       { "queryuser",          cmd_samr_query_user,            PI_SAMR,        "Query user info",         "" },
+       { "querygroup",         cmd_samr_query_group,           PI_SAMR,        "Query group info",        "" },
+       { "queryusergroups",    cmd_samr_query_usergroups,      PI_SAMR,        "Query user groups",       "" },
+       { "queryuseraliases",   cmd_samr_query_useraliases,     PI_SAMR,        "Query user aliases",      "" },
+       { "querygroupmem",      cmd_samr_query_groupmem,        PI_SAMR,        "Query group membership",  "" },
+       { "queryaliasmem",      cmd_samr_query_aliasmem,        PI_SAMR,        "Query alias membership",  "" },
+       { "querydispinfo",      cmd_samr_query_dispinfo,        PI_SAMR,        "Query display info",      "" },
+       { "querydominfo",       cmd_samr_query_dominfo,         PI_SAMR,        "Query domain info",       "" },
+       { "enumdomusers",      cmd_samr_enum_dom_users,       PI_SAMR,  "Enumerate domain users", "" },
+       { "enumdomgroups",      cmd_samr_enum_dom_groups,       PI_SAMR,        "Enumerate domain groups", "" },
+       { "enumalsgroups",      cmd_samr_enum_als_groups,       PI_SAMR,        "Enumerate alias groups",  "" },
+
+       { "createdomuser",      cmd_samr_create_dom_user,       PI_SAMR,        "Create domain user",      "" },
+       { "samlookupnames",     cmd_samr_lookup_names,          PI_SAMR,        "Look up names",           "" },
+       { "samlookuprids",      cmd_samr_lookup_rids,           PI_SAMR,        "Look up names",           "" },
+       { "deletedomuser",      cmd_samr_delete_dom_user,       PI_SAMR,        "Delete domain user",      "" },
+       { "samquerysecobj",     cmd_samr_query_sec_obj,         PI_SAMR, "Query SAMR security object",   "" },
+       { "getdompwinfo",       cmd_samr_get_dom_pwinfo,        PI_SAMR, "Retrieve domain password info", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_spoolss.c b/source4/rpcclient/cmd_spoolss.c
new file mode 100644 (file)
index 0000000..9f6f539
--- /dev/null
@@ -0,0 +1,2297 @@
+/*
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Gerald Carter                     2001
+   Copyright (C) Tim Potter                        2000
+   Copyright (C) Andrew Tridgell              1992-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+   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 "rpcclient.h"
+
+struct table_node {
+       const char      *long_archi;
+       const char      *short_archi;
+       int     version;
+};
+static const struct table_node archi_table[]= {
+
+       {"Windows 4.0",          "WIN40",       0 },
+       {"Windows NT x86",       "W32X86",      2 },
+       {"Windows NT R4000",     "W32MIPS",     2 },
+       {"Windows NT Alpha_AXP", "W32ALPHA",    2 },
+       {"Windows NT PowerPC",   "W32PPC",      2 },
+       {NULL,                   "",            -1 }
+};
+
+/**
+ * @file
+ *
+ * rpcclient module for SPOOLSS rpc pipe.
+ *
+ * This generally just parses and checks command lines, and then calls
+ * a cli_spoolss function.
+ **/
+
+/****************************************************************************
+function to do the mapping between the long architecture name and
+the short one.
+****************************************************************************/
+BOOL get_short_archi(char *short_archi, const char *long_archi)
+{
+        int i=-1;
+
+        DEBUG(107,("Getting architecture dependant directory\n"));
+        do {
+                i++;
+        } while ( (archi_table[i].long_archi!=NULL ) &&
+                  StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+        if (archi_table[i].long_archi==NULL) {
+                DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+                return False;
+        }
+
+       /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+        StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi));
+
+        DEBUGADD(108,("index: [%d]\n", i));
+        DEBUGADD(108,("long architecture: [%s]\n", long_archi));
+        DEBUGADD(108,("short architecture: [%s]\n", short_archi));
+
+        return True;
+}
+
+#if 0
+/**********************************************************************
+ * dummy function  -- placeholder
+  */
+static NTSTATUS cmd_spoolss_not_implemented(struct cli_state *cli, 
+                                            TALLOC_CTX *mem_ctx,
+                                            int argc, const char **argv)
+{
+       printf ("(*) This command is not currently implemented.\n");
+       return NT_STATUS_OK;
+}
+#endif
+
+/***********************************************************************
+ * Get printer information
+ */
+static NTSTATUS cmd_spoolss_open_printer_ex(struct cli_state *cli, 
+                                            TALLOC_CTX *mem_ctx,
+                                            int argc, const char **argv)
+{
+       WERROR          werror;
+       fstring         printername;
+       fstring         servername, user;
+       POLICY_HND      hnd;
+       
+       if (argc != 2) {
+               printf("Usage: %s <printername>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (!cli)
+               return NT_STATUS_UNSUCCESSFUL;
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       fstrcpy  (user, cli->user_name);
+       fstrcpy  (printername, argv[1]);
+
+       /* Open the printer handle */
+
+       werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", PRINTER_ALL_ACCESS, 
+                                            servername, user, &hnd);
+
+       if (W_ERROR_IS_OK(werror)) {
+               printf("Printer %s opened successfully\n", printername);
+               werror = cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+               if (!W_ERROR_IS_OK(werror)) {
+                       printf("Error closing printer handle! (%s)\n", 
+                               get_dos_error_msg(werror));
+               }
+       }
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_info_0(PRINTER_INFO_0 *i0)
+{
+       fstring name = "";
+       fstring servername = "";
+
+       if (!i0)
+               return;
+
+       rpcstr_pull(name, i0->printername.buffer, sizeof(name), -1, STR_TERMINATE);
+
+       rpcstr_pull(servername, i0->servername.buffer, sizeof(servername), -1,STR_TERMINATE);
+  
+       printf("\tprintername:[%s]\n", name);
+       printf("\tservername:[%s]\n", servername);
+       printf("\tcjobs:[0x%x]\n", i0->cjobs);
+       printf("\ttotal_jobs:[0x%x]\n", i0->total_jobs);
+       
+       printf("\t:date: [%d]-[%d]-[%d] (%d)\n", i0->year, i0->month, 
+              i0->day, i0->dayofweek);
+       printf("\t:time: [%d]-[%d]-[%d]-[%d]\n", i0->hour, i0->minute, 
+              i0->second, i0->milliseconds);
+       
+       printf("\tglobal_counter:[0x%x]\n", i0->global_counter);
+       printf("\ttotal_pages:[0x%x]\n", i0->total_pages);
+       
+       printf("\tmajorversion:[0x%x]\n", i0->major_version);
+       printf("\tbuildversion:[0x%x]\n", i0->build_version);
+       
+       printf("\tunknown7:[0x%x]\n", i0->unknown7);
+       printf("\tunknown8:[0x%x]\n", i0->unknown8);
+       printf("\tunknown9:[0x%x]\n", i0->unknown9);
+       printf("\tsession_counter:[0x%x]\n", i0->session_counter);
+       printf("\tunknown11:[0x%x]\n", i0->unknown11);
+       printf("\tprinter_errors:[0x%x]\n", i0->printer_errors);
+       printf("\tunknown13:[0x%x]\n", i0->unknown13);
+       printf("\tunknown14:[0x%x]\n", i0->unknown14);
+       printf("\tunknown15:[0x%x]\n", i0->unknown15);
+       printf("\tunknown16:[0x%x]\n", i0->unknown16);
+       printf("\tchange_id:[0x%x]\n", i0->change_id);
+       printf("\tunknown18:[0x%x]\n", i0->unknown18);
+       printf("\tstatus:[0x%x]\n", i0->status);
+       printf("\tunknown20:[0x%x]\n", i0->unknown20);
+       printf("\tc_setprinter:[0x%x]\n", i0->c_setprinter);
+       printf("\tunknown22:[0x%x]\n", i0->unknown22);
+       printf("\tunknown23:[0x%x]\n", i0->unknown23);
+       printf("\tunknown24:[0x%x]\n", i0->unknown24);
+       printf("\tunknown25:[0x%x]\n", i0->unknown25);
+       printf("\tunknown26:[0x%x]\n", i0->unknown26);
+       printf("\tunknown27:[0x%x]\n", i0->unknown27);
+       printf("\tunknown28:[0x%x]\n", i0->unknown28);
+       printf("\tunknown29:[0x%x]\n", i0->unknown29);
+
+       printf("\n");
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_info_1(PRINTER_INFO_1 *i1)
+{
+       fstring desc = "";
+       fstring name = "";
+       fstring comm = "";
+
+       rpcstr_pull(desc, i1->description.buffer, sizeof(desc), -1,
+                   STR_TERMINATE);
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       rpcstr_pull(comm, i1->comment.buffer, sizeof(comm), -1, STR_TERMINATE);
+
+       printf("\tflags:[0x%x]\n", i1->flags);
+       printf("\tname:[%s]\n", name);
+       printf("\tdescription:[%s]\n", desc);
+       printf("\tcomment:[%s]\n", comm);
+
+       printf("\n");
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_info_2(PRINTER_INFO_2 *i2)
+{
+       fstring servername = "";
+       fstring printername = "";
+       fstring sharename = "";
+       fstring portname = "";
+       fstring drivername = "";
+       fstring comment = "";
+       fstring location = "";
+       fstring sepfile = "";
+       fstring printprocessor = "";
+       fstring datatype = "";
+       fstring parameters = "";
+       
+       rpcstr_pull(servername, i2->servername.buffer,sizeof(servername), -1, STR_TERMINATE);
+
+       rpcstr_pull(printername, i2->printername.buffer,sizeof(printername), -1, STR_TERMINATE);
+
+       rpcstr_pull(sharename, i2->sharename.buffer,sizeof(sharename), -1, STR_TERMINATE);
+
+       rpcstr_pull(portname, i2->portname.buffer,sizeof(portname), -1, STR_TERMINATE);
+
+       rpcstr_pull(drivername, i2->drivername.buffer,sizeof(drivername), -1, STR_TERMINATE);
+
+       rpcstr_pull(comment, i2->comment.buffer,sizeof(comment), -1, STR_TERMINATE);
+
+       rpcstr_pull(location, i2->location.buffer,sizeof(location), -1, STR_TERMINATE);
+
+       rpcstr_pull(sepfile, i2->sepfile.buffer,sizeof(sepfile), -1, STR_TERMINATE);
+
+       rpcstr_pull(printprocessor, i2->printprocessor.buffer,sizeof(printprocessor), -1, STR_TERMINATE);
+
+       rpcstr_pull(datatype, i2->datatype.buffer,sizeof(datatype), -1, STR_TERMINATE);
+
+       rpcstr_pull(parameters, i2->parameters.buffer,sizeof(parameters), -1, STR_TERMINATE);
+
+       printf("\tservername:[%s]\n", servername);
+       printf("\tprintername:[%s]\n", printername);
+       printf("\tsharename:[%s]\n", sharename);
+       printf("\tportname:[%s]\n", portname);
+       printf("\tdrivername:[%s]\n", drivername);
+       printf("\tcomment:[%s]\n", comment);
+       printf("\tlocation:[%s]\n", location);
+       printf("\tsepfile:[%s]\n", sepfile);
+       printf("\tprintprocessor:[%s]\n", printprocessor);
+       printf("\tdatatype:[%s]\n", datatype);
+       printf("\tparameters:[%s]\n", parameters);
+       printf("\tattributes:[0x%x]\n", i2->attributes);
+       printf("\tpriority:[0x%x]\n", i2->priority);
+       printf("\tdefaultpriority:[0x%x]\n", i2->defaultpriority);
+       printf("\tstarttime:[0x%x]\n", i2->starttime);
+       printf("\tuntiltime:[0x%x]\n", i2->untiltime);
+       printf("\tstatus:[0x%x]\n", i2->status);
+       printf("\tcjobs:[0x%x]\n", i2->cjobs);
+       printf("\taverageppm:[0x%x]\n", i2->averageppm);
+
+       if (i2->secdesc) 
+               display_sec_desc(i2->secdesc);
+
+       printf("\n");
+}
+
+/****************************************************************************
+printer info level 3 display function
+****************************************************************************/
+static void display_print_info_3(PRINTER_INFO_3 *i3)
+{
+       printf("\tflags:[0x%x]\n", i3->flags);
+
+       display_sec_desc(i3->secdesc);
+
+       printf("\n");
+}
+
+/* Enumerate printers */
+
+static NTSTATUS cmd_spoolss_enum_printers(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv)
+{
+       WERROR                  result;
+       uint32                  info_level = 1;
+       PRINTER_INFO_CTR        ctr;
+       uint32                  i = 0, num_printers, needed;
+       fstring name;
+
+       if (argc > 3) 
+       {
+               printf("Usage: %s [level] [name]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       if (argc == 3)
+               fstrcpy(name, argv[2]);
+       else {
+               slprintf(name, sizeof(name)-1, "\\\\%s", cli->desthost);
+               strupper(name);
+       }
+
+       /* Enumerate printers  -- Should we enumerate types other 
+          than PRINTER_ENUM_LOCAL?  Maybe accept as a parameter?  --jerry */
+
+       ZERO_STRUCT(ctr);
+
+       result = cli_spoolss_enum_printers(
+               cli, mem_ctx, 0, &needed, name, PRINTER_ENUM_LOCAL, 
+               info_level, &num_printers, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_enum_printers(
+                       cli, mem_ctx, needed, NULL, name, PRINTER_ENUM_LOCAL, 
+                       info_level, &num_printers, &ctr);
+
+       if (W_ERROR_IS_OK(result)) {
+
+               if (!num_printers) {
+                       printf ("No printers returned.\n");
+                       goto done;
+               }
+       
+               for (i = 0; i < num_printers; i++) {
+                       switch(info_level) {
+                       case 0:
+                               display_print_info_0(&ctr.printers_0[i]);
+                               break;
+                       case 1:
+                               display_print_info_1(&ctr.printers_1[i]);
+                               break;
+                       case 2:
+                               display_print_info_2(&ctr.printers_2[i]);
+                               break;
+                       case 3:
+                               display_print_info_3(&ctr.printers_3[i]);
+                               break;
+                       default:
+                               printf("unknown info level %d\n", info_level);
+                               goto done;
+                       }
+               }
+       }
+       done:
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+port info level 1 display function
+****************************************************************************/
+static void display_port_info_1(PORT_INFO_1 *i1)
+{
+       fstring buffer;
+       
+       rpcstr_pull(buffer, i1->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+       printf("\tPort Name:\t[%s]\n", buffer);
+}
+
+/****************************************************************************
+port info level 2 display function
+****************************************************************************/
+static void display_port_info_2(PORT_INFO_2 *i2)
+{
+       fstring buffer;
+       
+       rpcstr_pull(buffer, i2->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+       printf("\tPort Name:\t[%s]\n", buffer);
+       rpcstr_pull(buffer, i2->monitor_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+       printf("\tMonitor Name:\t[%s]\n", buffer);
+       rpcstr_pull(buffer, i2->description.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+       printf("\tDescription:\t[%s]\n", buffer);
+       printf("\tPort Type:\t[%d]\n", i2->port_type);
+       printf("\tReserved:\t[%d]\n", i2->reserved);
+       printf("\n");
+}
+
+/* Enumerate ports */
+
+static NTSTATUS cmd_spoolss_enum_ports(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc, 
+                                      const char **argv)
+{
+       WERROR                  result;
+       uint32                  needed, info_level = 1;
+       PORT_INFO_CTR           ctr;
+       int                     returned;
+       
+       if (argc > 2) {
+               printf("Usage: %s [level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       /* Enumerate ports */
+
+       ZERO_STRUCT(ctr);
+
+       result = cli_spoolss_enum_ports(cli, mem_ctx, 0, &needed, info_level, 
+                                       &returned, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_enum_ports(cli, mem_ctx, needed, NULL,
+                                               info_level, &returned, &ctr);
+
+       if (W_ERROR_IS_OK(result)) {
+               int i;
+
+               for (i = 0; i < returned; i++) {
+                       switch (info_level) {
+                       case 1:
+                               display_port_info_1(&ctr.port.info_1[i]);
+                               break;
+                       case 2:
+                               display_port_info_2(&ctr.port.info_2[i]);
+                               break;
+                       default:
+                               printf("unknown info level %d\n", info_level);
+                               break;
+                       }
+               }
+       }
+       
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/***********************************************************************
+ * Set printer comment - use a level2 set.
+ */
+static NTSTATUS cmd_spoolss_setprinter(struct cli_state *cli,
+                                       TALLOC_CTX *mem_ctx,
+                                       int argc, const char **argv)
+{
+       POLICY_HND      pol;
+       WERROR          result;
+       uint32          needed;
+       uint32          info_level = 2;
+       BOOL            opened_hnd = False;
+       PRINTER_INFO_CTR ctr;
+       fstring         printername,
+                       servername,
+                       user,
+                       comment;
+
+       if (argc == 1 || argc > 3) {
+               printf("Usage: %s printername comment\n", argv[0]);
+
+               return NT_STATUS_OK;
+       }
+
+       /* Open a printer handle */
+       if (argc == 3) {
+               fstrcpy(comment, argv[2]);
+       }
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       fstrcpy (printername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+
+       /* get a printer handle */
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "", 
+                               MAXIMUM_ALLOWED_ACCESS, servername,
+                               user, &pol);
+                               
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       opened_hnd = True;
+
+       /* Get printer info */
+        result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, info_level, &ctr);
+
+        if (W_ERROR_V(result) == ERRinsufficientbuffer)
+                result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+        if (!W_ERROR_IS_OK(result))
+                goto done;
+
+
+       /* Modify the comment. */
+       init_unistr(&ctr.printers_2->comment, comment);
+       ctr.printers_2->devmode = NULL;
+       ctr.printers_2->secdesc = NULL;
+
+       result = cli_spoolss_setprinter(cli, mem_ctx, &pol, info_level, &ctr, 0);
+       if (W_ERROR_IS_OK(result))
+               printf("Success in setting comment.\n");
+
+ done:
+       if (opened_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static NTSTATUS cmd_spoolss_getprinter(struct cli_state *cli,
+                                       TALLOC_CTX *mem_ctx,
+                                       int argc, const char **argv)
+{
+       POLICY_HND      pol;
+       WERROR          result;
+       uint32          info_level = 1;
+       BOOL            opened_hnd = False;
+       PRINTER_INFO_CTR ctr;
+       fstring         printername,
+                       servername,
+                       user;
+       uint32 needed;
+
+       if (argc == 1 || argc > 3) {
+               printf("Usage: %s <printername> [level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* Open a printer handle */
+       if (argc == 3) {
+               info_level = atoi(argv[2]);
+       }
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       slprintf (printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+       
+       /* get a printer handle */
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &pol);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       opened_hnd = True;
+
+       /* Get printer info */
+
+       result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+                                       &pol, info_level, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_getprinter(
+                       cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Display printer info */
+
+       switch (info_level) {
+       case 0: 
+               display_print_info_0(ctr.printers_0);
+               break;
+       case 1:
+               display_print_info_1(ctr.printers_1);
+               break;
+       case 2:
+               display_print_info_2(ctr.printers_2);
+               break;
+       case 3:
+               display_print_info_3(ctr.printers_3);
+               break;
+       default:
+               printf("unknown info level %d\n", info_level);
+               break;
+       }
+
+ done: 
+       if (opened_hnd) 
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static void display_reg_value(REGISTRY_VALUE value)
+{
+       pstring text;
+
+       switch(value.type) {
+       case REG_DWORD:
+               printf("%s: REG_DWORD: 0x%08x\n", value.valuename, 
+                      *((uint32 *) value.data_p));
+               break;
+       case REG_SZ:
+               rpcstr_pull(text, value.data_p, sizeof(text), value.size,
+                           STR_TERMINATE);
+               printf("%s: REG_SZ: %s\n", value.valuename, text);
+               break;
+       case REG_BINARY:
+               printf("%s: REG_BINARY: unknown length value not displayed\n",
+                      value.valuename);
+               break;
+       case REG_MULTI_SZ: {
+               uint16 *curstr = (uint16 *) value.data_p;
+               uint8 *start = value.data_p;
+               printf("%s: REG_MULTI_SZ:\n", value.valuename);
+               while ((*curstr != 0) && 
+                      ((uint8 *) curstr < start + value.size)) {
+                       rpcstr_pull(text, curstr, sizeof(text), -1, 
+                                   STR_TERMINATE);
+                       printf("  %s\n", text);
+                       curstr += strlen(text) + 1;
+               }
+       }
+       break;
+       default:
+               printf("%s: unknown type %d\n", value.valuename, value.type);
+       }
+       
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static NTSTATUS cmd_spoolss_getprinterdata(struct cli_state *cli,
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv)
+{
+       POLICY_HND      pol;
+       WERROR          result;
+       BOOL            opened_hnd = False;
+       fstring         printername,
+                       servername,
+                       user;
+       uint32 needed;
+       const char *valuename;
+       REGISTRY_VALUE value;
+
+       if (argc != 3) {
+               printf("Usage: %s <printername> <valuename>\n", argv[0]);
+               printf("<printername> of . queries print server\n");
+               return NT_STATUS_OK;
+       }
+       valuename = argv[2];
+
+       /* Open a printer handle */
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       if (strncmp(argv[1], ".", sizeof(".")) == 0)
+               fstrcpy(printername, servername);
+       else
+               slprintf (printername, sizeof(servername)-1, "%s\\%s", 
+                         servername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+       
+       /* get a printer handle */
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &pol);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       opened_hnd = True;
+
+       /* Get printer info */
+
+       result = cli_spoolss_getprinterdata(cli, mem_ctx, 0, &needed,
+                                           &pol, valuename, &value);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_getprinterdata(
+                       cli, mem_ctx, needed, NULL, &pol, valuename, &value);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Display printer data */
+
+       fstrcpy(value.valuename, valuename);
+       display_reg_value(value);
+       
+
+ done: 
+       if (opened_hnd) 
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static NTSTATUS cmd_spoolss_getprinterdataex(struct cli_state *cli,
+                                            TALLOC_CTX *mem_ctx,
+                                            int argc, const char **argv)
+{
+       POLICY_HND      pol;
+       WERROR          result;
+       BOOL            opened_hnd = False;
+       fstring         printername,
+                       servername,
+                       user;
+       uint32 needed;
+       const char *valuename, *keyname;
+       REGISTRY_VALUE value;
+
+       if (argc != 4) {
+               printf("Usage: %s <printername> <keyname> <valuename>\n", 
+                      argv[0]);
+               printf("<printername> of . queries print server\n");
+               return NT_STATUS_OK;
+       }
+       valuename = argv[3];
+       keyname = argv[2];
+
+       /* Open a printer handle */
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       if (strncmp(argv[1], ".", sizeof(".")) == 0)
+               fstrcpy(printername, servername);
+       else
+               slprintf (printername, sizeof(printername)-1, "%s\\%s", 
+                         servername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+       
+       /* get a printer handle */
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &pol);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       opened_hnd = True;
+
+       /* Get printer info */
+
+       result = cli_spoolss_getprinterdataex(cli, mem_ctx, 0, &needed,
+                                             &pol, keyname, valuename, 
+                                             &value);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_getprinterdataex(cli, mem_ctx, needed, 
+                                                     NULL, &pol, keyname,
+                                                     valuename, &value);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Display printer data */
+
+       fstrcpy(value.valuename, valuename);
+       display_reg_value(value);
+       
+
+ done: 
+       if (opened_hnd) 
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_driver_1(DRIVER_INFO_1 *i1)
+{
+       fstring name;
+       if (i1 == NULL)
+               return;
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+
+       printf ("Printer Driver Info 1:\n");
+       printf ("\tDriver Name: [%s]\n\n", name);
+       
+       return;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_driver_2(DRIVER_INFO_2 *i1)
+{
+       fstring name;
+       fstring architecture;
+       fstring driverpath;
+       fstring datafile;
+       fstring configfile;
+       if (i1 == NULL)
+               return;
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+       rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+       rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+       rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+
+       printf ("Printer Driver Info 2:\n");
+       printf ("\tVersion: [%x]\n", i1->version);
+       printf ("\tDriver Name: [%s]\n", name);
+       printf ("\tArchitecture: [%s]\n", architecture);
+       printf ("\tDriver Path: [%s]\n", driverpath);
+       printf ("\tDatafile: [%s]\n", datafile);
+       printf ("\tConfigfile: [%s]\n\n", configfile);
+
+       return;
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_driver_3(DRIVER_INFO_3 *i1)
+{
+       fstring name = "";
+       fstring architecture = "";
+       fstring driverpath = "";
+       fstring datafile = "";
+       fstring configfile = "";
+       fstring helpfile = "";
+       fstring dependentfiles = "";
+       fstring monitorname = "";
+       fstring defaultdatatype = "";
+       
+       int length=0;
+       BOOL valid = True;
+       
+       if (i1 == NULL)
+               return;
+
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+       rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+       rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+       rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+       rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+       rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
+       rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
+
+       printf ("Printer Driver Info 3:\n");
+       printf ("\tVersion: [%x]\n", i1->version);
+       printf ("\tDriver Name: [%s]\n",name);
+       printf ("\tArchitecture: [%s]\n", architecture);
+       printf ("\tDriver Path: [%s]\n", driverpath);
+       printf ("\tDatafile: [%s]\n", datafile);
+       printf ("\tConfigfile: [%s]\n", configfile);
+       printf ("\tHelpfile: [%s]\n\n", helpfile);
+
+       while (valid)
+       {
+               rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+               
+               length+=strlen(dependentfiles)+1;
+               
+               if (strlen(dependentfiles) > 0)
+               {
+                       printf ("\tDependentfiles: [%s]\n", dependentfiles);
+               }
+               else
+               {
+                       valid = False;
+               }
+       }
+       
+       printf ("\n");
+
+       printf ("\tMonitorname: [%s]\n", monitorname);
+       printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
+
+       return; 
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static NTSTATUS cmd_spoolss_getdriver(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx,
+                                      int argc, const char **argv)
+{
+       POLICY_HND      pol;
+       WERROR          werror;
+       NTSTATUS        result;
+       uint32          info_level = 3;
+       BOOL            opened_hnd = False;
+       PRINTER_DRIVER_CTR      ctr;
+       fstring         printername, 
+                       servername, 
+                       user;
+       uint32          i;
+
+       if ((argc == 1) || (argc > 3)) 
+       {
+               printf("Usage: %s <printername> [level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* get the arguments need to open the printer handle */
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       fstrcpy  (user, cli->user_name);
+       fstrcpy  (printername, argv[1]);
+       if (argc == 3)
+               info_level = atoi(argv[2]);
+
+       /* Open a printer handle */
+
+       werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "", 
+                                            PRINTER_ACCESS_USE,
+                                            servername, user, &pol);
+
+       result = W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+       if (!NT_STATUS_IS_OK(result)) {
+               printf("Error opening printer handle for %s!\n", printername);
+               return result;
+       }
+
+       opened_hnd = True;
+
+       /* loop through and print driver info level for each architecture */
+
+       for (i=0; archi_table[i].long_archi!=NULL; i++) {
+               uint32 needed;
+
+               werror = cli_spoolss_getprinterdriver(
+                       cli, mem_ctx, 0, &needed, &pol, info_level, 
+                       archi_table[i].long_archi, &ctr);
+
+               if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+                       werror = cli_spoolss_getprinterdriver(
+                               cli, mem_ctx, needed, NULL, &pol, info_level, 
+                               archi_table[i].long_archi, &ctr);
+
+               if (!W_ERROR_IS_OK(werror))
+                       continue;
+                       
+               printf ("\n[%s]\n", archi_table[i].long_archi);
+
+               switch (info_level) {
+               case 1:
+                       display_print_driver_1 (ctr.info1);
+                       break;
+               case 2:
+                       display_print_driver_2 (ctr.info2);
+                       break;
+               case 3:
+                       display_print_driver_3 (ctr.info3);
+                       break;
+               default:
+                       printf("unknown info level %d\n", info_level);
+                       break;
+               }
+       }
+       
+       /* Cleanup */
+
+       if (opened_hnd)
+               cli_spoolss_close_printer (cli, mem_ctx, &pol);
+       
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static NTSTATUS cmd_spoolss_enum_drivers(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv)
+{
+       WERROR werror;
+       uint32          info_level = 1;
+       PRINTER_DRIVER_CTR      ctr;
+       uint32          i, j,
+                       returned;
+
+       if (argc > 2) 
+       {
+               printf("Usage: enumdrivers [level]\n");
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+
+       /* loop through and print driver info level for each architecture */
+       for (i=0; archi_table[i].long_archi!=NULL; i++) 
+       {
+               uint32 needed;
+
+               werror = cli_spoolss_enumprinterdrivers(
+                       cli, mem_ctx, 0, &needed, info_level, 
+                       archi_table[i].long_archi, &returned, &ctr);
+
+               if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+                       werror = cli_spoolss_enumprinterdrivers(
+                               cli, mem_ctx, needed, NULL, info_level, 
+                               archi_table[i].long_archi, &returned, &ctr);
+
+               if (returned == 0)
+                       continue;
+                       
+               if (!W_ERROR_IS_OK(werror)) {
+                       printf ("Error getting driver for environment [%s] - %d\n",
+                               archi_table[i].long_archi, W_ERROR_V(werror));
+                       continue;
+               }
+               
+               printf ("\n[%s]\n", archi_table[i].long_archi);
+               switch (info_level) 
+               {
+                       
+               case 1:
+                       for (j=0; j < returned; j++) {
+                               display_print_driver_1 (&(ctr.info1[j]));
+                       }
+                       break;
+               case 2:
+                       for (j=0; j < returned; j++) {
+                               display_print_driver_2 (&(ctr.info2[j]));
+                       }
+                       break;
+               case 3:
+                       for (j=0; j < returned; j++) {
+                               display_print_driver_3 (&(ctr.info3[j]));
+                       }
+                       break;
+               default:
+                       printf("unknown info level %d\n", info_level);
+                       break;
+               }
+       }
+       
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_printdriverdir_1(DRIVER_DIRECTORY_1 *i1)
+{
+        fstring name;
+        if (i1 == NULL)
+                return;
+       rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+       printf ("\tDirectory Name:[%s]\n", name);
+}
+
+/***********************************************************************
+ * Get printer driver directory information
+ */
+static NTSTATUS cmd_spoolss_getdriverdir(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv)
+{
+       WERROR result;
+       fstring                 env;
+       DRIVER_DIRECTORY_CTR    ctr;
+       uint32 needed;
+
+       if (argc > 2) {
+               printf("Usage: %s [environment]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* Get the arguments need to open the printer handle */
+
+       if (argc == 2)
+               fstrcpy (env, argv[1]);
+       else
+               fstrcpy (env, "Windows NT x86");
+
+       /* Get the directory.  Only use Info level 1 */
+
+       result = cli_spoolss_getprinterdriverdir(
+               cli, mem_ctx, 0, &needed, 1, env, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_getprinterdriverdir(
+                       cli, mem_ctx, needed, NULL, 1, env, &ctr);
+
+       if (W_ERROR_IS_OK(result))
+               display_printdriverdir_1(ctr.info1);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/*******************************************************************************
+ set the version and environment fields of a DRIVER_INFO_3 struct
+ ******************************************************************************/
+void set_drv_info_3_env (DRIVER_INFO_3 *info, const char *arch)
+{
+
+       int i;
+       
+       for (i=0; archi_table[i].long_archi != NULL; i++) 
+       {
+               if (strcmp(arch, archi_table[i].short_archi) == 0)
+               {
+                       info->version = archi_table[i].version;
+                       init_unistr (&info->architecture, archi_table[i].long_archi);
+                       break;
+               }
+       }
+       
+       if (archi_table[i].long_archi == NULL)
+       {
+               DEBUG(0, ("set_drv_info_3_env: Unknown arch [%s]\n", arch));
+       }
+       
+       return;
+}
+
+
+/**************************************************************************
+ wrapper for strtok to get the next parameter from a delimited list.
+ Needed to handle the empty parameter string denoted by "NULL"
+ *************************************************************************/
+static char* get_driver_3_param (const char* str, const char* delim, UNISTR* dest)
+{
+       char    *ptr;
+
+       /* get the next token */
+       ptr = strtok(str, delim);
+
+       /* a string of 'NULL' is used to represent an empty
+          parameter because two consecutive delimiters
+          will not return an empty string.  See man strtok(3)
+          for details */
+       if (StrCaseCmp(ptr, "NULL") == 0)
+               ptr = NULL;
+
+       if (dest != NULL)
+               init_unistr(dest, ptr); 
+
+       return ptr;
+}
+
+/********************************************************************************
+ fill in the members of a DRIVER_INFO_3 struct using a character 
+ string in the form of
+        <Long Printer Name>:<Driver File Name>:<Data File Name>:\
+            <Config File Name>:<Help File Name>:<Language Monitor Name>:\
+            <Default Data Type>:<Comma Separated list of Files> 
+ *******************************************************************************/
+static BOOL init_drv_info_3_members (
+       TALLOC_CTX *mem_ctx, 
+       DRIVER_INFO_3 *info, 
+       const char *args
+)
+{
+       char    *str, *str2;
+       uint32  len, i;
+       
+       /* fill in the UNISTR fields */
+       str = get_driver_3_param (args, ":", &info->name);
+       str = get_driver_3_param (NULL, ":", &info->driverpath);
+       str = get_driver_3_param (NULL, ":", &info->datafile);
+       str = get_driver_3_param (NULL, ":", &info->configfile);
+       str = get_driver_3_param (NULL, ":", &info->helpfile);
+       str = get_driver_3_param (NULL, ":", &info->monitorname);
+       str = get_driver_3_param (NULL, ":", &info->defaultdatatype);
+
+       /* <Comma Separated List of Dependent Files> */
+       str2 = get_driver_3_param (NULL, ":", NULL); /* save the beginning of the string */
+       str = str2;                     
+
+       /* begin to strip out each filename */
+       str = strtok(str, ",");         
+       len = 0;
+       while (str != NULL)
+       {
+               /* keep a cumlative count of the str lengths */
+               len += strlen(str)+1;
+               str = strtok(NULL, ",");
+       }
+
+       /* allocate the space; add one extra slot for a terminating NULL.
+          Each filename is NULL terminated and the end contains a double
+          NULL */
+       if ((info->dependentfiles=(uint16*)talloc(mem_ctx, (len+1)*sizeof(uint16))) == NULL)
+       {
+               DEBUG(0,("init_drv_info_3_members: Unable to malloc memory for dependenfiles\n"));
+               return False;
+       }
+       for (i=0; i<len; i++)
+       {
+               info->dependentfiles[i] = SSVAL(&info->dependentfiles[i], 0, str2[i]);
+       }
+       info->dependentfiles[len] = '\0';
+
+       return True;
+}
+
+
+static NTSTATUS cmd_spoolss_addprinterdriver(struct cli_state *cli, 
+                                             TALLOC_CTX *mem_ctx,
+                                             int argc, const char **argv)
+{
+       WERROR result;
+       uint32                  level = 3;
+       PRINTER_DRIVER_CTR      ctr;
+       DRIVER_INFO_3           info3;
+       fstring                 arch;
+       fstring                 driver_name;
+
+       /* parse the command arguements */
+       if (argc != 3)
+       {
+               printf ("Usage: %s <Environment>\\\n", argv[0]);
+               printf ("\t<Long Printer Name>:<Driver File Name>:<Data File Name>:\\\n");
+               printf ("\t<Config File Name>:<Help File Name>:<Language Monitor Name>:\\\n");
+               printf ("\t<Default Data Type>:<Comma Separated list of Files>\n");
+
+               return NT_STATUS_OK;
+        }
+               
+       /* Fill in the DRIVER_INFO_3 struct */
+       ZERO_STRUCT(info3);
+       if (!get_short_archi(arch, argv[1]))
+       {
+               printf ("Error Unknown architechture [%s]\n", argv[1]);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       else
+               set_drv_info_3_env(&info3, arch);
+
+       if (!init_drv_info_3_members(mem_ctx, &info3, argv[2]))
+       {
+               printf ("Error Invalid parameter list - %s.\n", argv[2]);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+
+       ctr.info3 = &info3;
+       result = cli_spoolss_addprinterdriver (cli, mem_ctx, level, &ctr);
+
+       if (W_ERROR_IS_OK(result)) {
+               rpcstr_pull(driver_name, info3.name.buffer, 
+                           sizeof(driver_name), -1, STR_TERMINATE);
+               printf ("Printer Driver %s successfully installed.\n",
+                       driver_name);
+       }
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+static NTSTATUS cmd_spoolss_addprinterex(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv)
+{
+       WERROR result;
+       uint32                  level = 2;
+       PRINTER_INFO_CTR        ctr;
+       PRINTER_INFO_2          info2;
+       fstring                 servername;
+       
+       /* parse the command arguements */
+       if (argc != 5)
+       {
+               printf ("Usage: %s <name> <shared name> <driver> <port>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+        slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+        strupper (servername);
+
+       /* Fill in the DRIVER_INFO_3 struct */
+       ZERO_STRUCT(info2);
+#if 0  /* JERRY */
+       init_unistr( &info2.servername,         servername);
+#endif
+       init_unistr( &info2.printername,        argv[1]);
+       init_unistr( &info2.sharename,          argv[2]);
+       init_unistr( &info2.drivername,         argv[3]);
+       init_unistr( &info2.portname,           argv[4]);
+       init_unistr( &info2.comment,            "Created by rpcclient");
+       init_unistr( &info2.printprocessor,     "winprint");
+       init_unistr( &info2.datatype,           "RAW");
+       info2.devmode =         NULL;
+       info2.secdesc =         NULL;
+       info2.attributes        = PRINTER_ATTRIBUTE_SHARED;
+       info2.priority          = 0;
+       info2.defaultpriority   = 0;
+       info2.starttime         = 0;
+       info2.untiltime         = 0;
+       
+       /* These three fields must not be used by AddPrinter() 
+          as defined in the MS Platform SDK documentation..  
+          --jerry
+       info2.status            = 0;
+       info2.cjobs             = 0;
+       info2.averageppm        = 0;
+       */
+
+       ctr.printers_2 = &info2;
+       result = cli_spoolss_addprinterex (cli, mem_ctx, level, &ctr);
+
+       if (W_ERROR_IS_OK(result))
+               printf ("Printer %s successfully installed.\n", argv[1]);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_spoolss_setdriver(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx,
+                                      int argc, const char **argv)
+{
+       POLICY_HND              pol;
+       WERROR                  result;
+       uint32                  level = 2;
+       BOOL                    opened_hnd = False;
+       PRINTER_INFO_CTR        ctr;
+       PRINTER_INFO_2          info2;
+       fstring                 servername,
+                               printername,
+                               user;
+       uint32 needed;
+       
+       /* parse the command arguements */
+       if (argc != 3)
+       {
+               printf ("Usage: %s <printer> <driver>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       slprintf (printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+
+       /* Get a printer handle */
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "", 
+                                            MAXIMUM_ALLOWED_ACCESS,
+                                            servername, user, &pol);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       opened_hnd = True;
+
+       /* Get printer info */
+
+       ZERO_STRUCT (info2);
+       ctr.printers_2 = &info2;
+
+       result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+                                       &pol, level, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_getprinter(
+                       cli, mem_ctx, needed, NULL, &pol, level, &ctr);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf ("Unable to retrieve printer information!\n");
+               goto done;
+       }
+
+       /* Set the printer driver */
+
+       init_unistr(&ctr.printers_2->drivername, argv[2]);
+
+       result = cli_spoolss_setprinter(cli, mem_ctx, &pol, level, &ctr, 0);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("SetPrinter call failed!\n");
+               goto done;;
+       }
+
+       printf("Succesfully set %s to driver %s.\n", argv[1], argv[2]);
+
+done:
+       /* Cleanup */
+
+       if (opened_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+static NTSTATUS cmd_spoolss_deletedriver(struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx,
+                                         int argc, const char **argv)
+{
+       WERROR result;
+       fstring                 servername;
+       int                     i;
+       
+       /* parse the command arguements */
+       if (argc != 2)
+       {
+               printf ("Usage: %s <driver>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+
+       /* delete the driver for all architectures */
+       for (i=0; archi_table[i].long_archi; i++)
+       {
+               /* make the call to remove the driver */
+               result = cli_spoolss_deleteprinterdriver(
+                       cli, mem_ctx, archi_table[i].long_archi, argv[1]);
+
+               if ( !W_ERROR_IS_OK(result) ) {
+                       if ( !W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ) {
+                               printf ("Failed to remove driver %s for arch [%s] - error 0x%x!\n", 
+                                       argv[1], archi_table[i].long_archi, 
+                                       W_ERROR_V(result));
+                       }
+               } 
+               else 
+               {
+                       printf ("Driver %s removed for arch [%s].\n", argv[1], 
+                               archi_table[i].long_archi);
+               }
+       }
+               
+       return W_ERROR_IS_OK(result) || W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_spoolss_getprintprocdir(struct cli_state *cli, 
+                                           TALLOC_CTX *mem_ctx,
+                                           int argc, const char **argv)
+{
+       WERROR result;
+       char *servername = NULL, *environment = NULL;
+       fstring procdir;
+       uint32 needed;
+       
+       /* parse the command arguements */
+       if (argc > 2) {
+               printf ("Usage: %s [environment]\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+
+       if (asprintf(&servername, "\\\\%s", cli->desthost) < 0)
+               return NT_STATUS_NO_MEMORY;
+       strupper(servername);
+
+       if (asprintf(&environment, "%s", (argc == 2) ? argv[1] : 
+                    PRINTER_DRIVER_ARCHITECTURE) < 0) {
+               SAFE_FREE(servername);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       result = cli_spoolss_getprintprocessordirectory(
+               cli, mem_ctx, 0, &needed, servername, environment, procdir);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_getprintprocessordirectory(
+                       cli, mem_ctx, needed, NULL, servername, environment, 
+                       procdir);
+
+       if (W_ERROR_IS_OK(result))
+               printf("%s\n", procdir);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(environment);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* Add a form */
+
+static NTSTATUS cmd_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   int argc, const char **argv)
+{
+       POLICY_HND handle;
+       WERROR werror;
+       char *servername = NULL, *printername = NULL;
+       FORM form;
+       BOOL got_handle = False;
+       
+       /* Parse the command arguements */
+
+       if (argc != 3) {
+               printf ("Usage: %s <printer> <formname>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+       /* Get a printer handle */
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       strupper(servername);
+       asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+       werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "", 
+                                            MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, cli->user_name, &handle);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       got_handle = True;
+
+       /* Dummy up some values for the form data */
+
+       form.flags = FORM_USER;
+       form.size_x = form.size_y = 100;
+       form.left = 0;
+       form.top = 10;
+       form.right = 20;
+       form.bottom = 30;
+
+       init_unistr2(&form.name, argv[2], strlen(argv[2]) + 1);
+
+       /* Add the form */
+
+
+       werror = cli_spoolss_addform(cli, mem_ctx, &handle, 1, &form);
+
+ done:
+       if (got_handle)
+               cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(printername);
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* Set a form */
+
+static NTSTATUS cmd_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   int argc, const char **argv)
+{
+       POLICY_HND handle;
+       WERROR werror;
+       char *servername = NULL, *printername = NULL;
+       FORM form;
+       BOOL got_handle = False;
+       
+       /* Parse the command arguements */
+
+       if (argc != 3) {
+               printf ("Usage: %s <printer> <formname>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+       /* Get a printer handle */
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       strupper(servername);
+       asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+       werror = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS, 
+               servername, cli->user_name, &handle);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       got_handle = True;
+
+       /* Dummy up some values for the form data */
+
+       form.flags = FORM_PRINTER;
+       form.size_x = form.size_y = 100;
+       form.left = 0;
+       form.top = 1000;
+       form.right = 2000;
+       form.bottom = 3000;
+
+       init_unistr2(&form.name, argv[2], strlen(argv[2]) + 1);
+
+       /* Set the form */
+
+       werror = cli_spoolss_setform(cli, mem_ctx, &handle, 1, argv[2], &form);
+
+ done:
+       if (got_handle)
+               cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(printername);
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* Get a form */
+
+static NTSTATUS cmd_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                   int argc, const char **argv)
+{
+       POLICY_HND handle;
+       WERROR werror;
+       char *servername = NULL, *printername = NULL;
+       FORM_1 form;
+       BOOL got_handle = False;
+       uint32 needed;
+       
+       /* Parse the command arguements */
+
+       if (argc != 3) {
+               printf ("Usage: %s <printer> <formname>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+       /* Get a printer handle */
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       strupper(servername);
+       asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+       werror = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS, 
+               servername, cli->user_name, &handle);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       got_handle = True;
+
+       /* Set the form */
+
+       werror = cli_spoolss_getform(cli, mem_ctx, 0, &needed,
+                                    &handle, argv[2], 1, &form);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_getform(cli, mem_ctx, needed, NULL,
+                                            &handle, argv[2], 1, &form);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       printf("width: %d\n", form.width);
+       printf("length: %d\n", form.length);
+       printf("left: %d\n", form.left);
+       printf("top: %d\n", form.top);
+       printf("right: %d\n", form.right);
+       printf("bottom: %d\n", form.bottom);
+
+ done:
+       if (got_handle)
+               cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(printername);
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* Delete a form */
+
+static NTSTATUS cmd_spoolss_deleteform(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc, 
+                                      const char **argv)
+{
+       POLICY_HND handle;
+       WERROR werror;
+       char *servername = NULL, *printername = NULL;
+       BOOL got_handle = False;
+       
+       /* Parse the command arguements */
+
+       if (argc != 3) {
+               printf ("Usage: %s <printer> <formname>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+       /* Get a printer handle */
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       strupper(servername);
+       asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+       werror = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS, 
+               servername, cli->user_name, &handle);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       got_handle = True;
+
+       /* Delete the form */
+
+       werror = cli_spoolss_deleteform(cli, mem_ctx, &handle, argv[2]);
+
+ done:
+       if (got_handle)
+               cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(printername);
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* Enumerate forms */
+
+static NTSTATUS cmd_spoolss_enum_forms(struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc, 
+                                      const char **argv)
+{
+       POLICY_HND handle;
+       WERROR werror;
+       char *servername = NULL, *printername = NULL;
+       BOOL got_handle = False;
+       uint32 needed, num_forms, level = 1, i;
+       FORM_1 *forms;
+       
+       /* Parse the command arguements */
+
+       if (argc != 2) {
+               printf ("Usage: %s <printer>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+       
+       /* Get a printer handle */
+
+       asprintf(&servername, "\\\\%s", cli->desthost);
+       strupper(servername);
+       asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+       werror = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS, 
+               servername, cli->user_name, &handle);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       got_handle = True;
+
+       /* Enumerate forms */
+
+       werror = cli_spoolss_enumforms(
+               cli, mem_ctx, 0, &needed, &handle, level, &num_forms, &forms);
+
+       if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+               werror = cli_spoolss_enumforms(
+                       cli, mem_ctx, needed, NULL, &handle, level, 
+                       &num_forms, &forms);
+
+       if (!W_ERROR_IS_OK(werror))
+               goto done;
+
+       /* Display output */
+
+       for (i = 0; i < num_forms; i++) {
+               fstring form_name;
+
+               if (forms[i].name.buffer)
+                       rpcstr_pull(form_name, forms[i].name.buffer,
+                                   sizeof(form_name), -1, STR_TERMINATE);
+
+               printf("%s\n", form_name);
+       }
+
+ done:
+       if (got_handle)
+               cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+       SAFE_FREE(servername);
+       SAFE_FREE(printername);
+
+       return W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_spoolss_setprinterdata(struct cli_state *cli,
+                                           TALLOC_CTX *mem_ctx,
+                                           int argc, const char **argv)
+{
+       WERROR result;
+       uint32 needed;
+       fstring servername, printername, user;
+       POLICY_HND pol;
+       BOOL opened_hnd = False;
+       PRINTER_INFO_CTR ctr;
+       PRINTER_INFO_0 info;
+       REGISTRY_VALUE value;
+
+       /* parse the command arguements */
+       if (argc != 4) {
+               printf ("Usage: %s <printer> <value> <data>\n", argv[0]);
+               return NT_STATUS_OK;
+        }
+
+       slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper (servername);
+       slprintf (printername, sizeof(servername)-1, "%s\\%s", servername, argv[1]);
+       fstrcpy  (user, cli->user_name);
+
+       /* get a printer handle */
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+                                            MAXIMUM_ALLOWED_ACCESS, servername, 
+                                            user, &pol);
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       opened_hnd = True;
+
+       ctr.printers_0 = &info;
+
+        result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+                                        &pol, 0, &ctr);
+
+        if (W_ERROR_V(result) == ERRinsufficientbuffer)
+                result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+        if (!W_ERROR_IS_OK(result))
+                goto done;
+               
+       printf("%s\n", timestring(True));
+       printf("\tchange_id (before set)\t:[0x%x]\n", info.change_id);
+
+       /* Set the printer data */
+       
+       fstrcpy(value.valuename, argv[2]);
+       value.type = REG_SZ;
+       value.size = strlen(argv[3]) + 1;
+       value.data_p = talloc_memdup(mem_ctx, argv[3], value.size);
+
+       result = cli_spoolss_setprinterdata(cli, mem_ctx, &pol, &value);
+               
+       if (!W_ERROR_IS_OK(result)) {
+               printf ("Unable to set [%s=%s]!\n", argv[2], argv[3]);
+               goto done;
+       }
+       printf("\tSetPrinterData succeeded [%s: %s]\n", argv[2], argv[3]);
+
+        result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, 0, &ctr);
+
+        if (W_ERROR_V(result) == ERRinsufficientbuffer)
+                result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+        if (!W_ERROR_IS_OK(result))
+                goto done;
+               
+       printf("%s\n", timestring(True));
+       printf("\tchange_id (after set)\t:[0x%x]\n", info.change_id);
+
+done:
+       /* cleanup */
+       if (opened_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static void display_job_info_1(JOB_INFO_1 *job)
+{
+       fstring username = "", document = "", text_status = "";
+
+       rpcstr_pull(username, job->username.buffer,
+                   sizeof(username), -1, STR_TERMINATE);
+
+       rpcstr_pull(document, job->document.buffer,
+                   sizeof(document), -1, STR_TERMINATE);
+
+       rpcstr_pull(text_status, job->text_status.buffer,
+                   sizeof(text_status), -1, STR_TERMINATE);
+
+       printf("%d: jobid[%d]: %s %s %s %d/%d pages\n", job->position, job->jobid,
+              username, document, text_status, job->pagesprinted,
+              job->totalpages);
+}
+
+static void display_job_info_2(JOB_INFO_2 *job)
+{
+       fstring username = "", document = "", text_status = "";
+
+       rpcstr_pull(username, job->username.buffer,
+                   sizeof(username), -1, STR_TERMINATE);
+
+       rpcstr_pull(document, job->document.buffer,
+                   sizeof(document), -1, STR_TERMINATE);
+
+       rpcstr_pull(text_status, job->text_status.buffer,
+                   sizeof(text_status), -1, STR_TERMINATE);
+
+       printf("%d: jobid[%d]: %s %s %s %d/%d pages, %d bytes\n", job->position, job->jobid,
+              username, document, text_status, job->pagesprinted,
+              job->totalpages, job->size);
+}
+
+/* Enumerate jobs */
+
+static NTSTATUS cmd_spoolss_enum_jobs(struct cli_state *cli, 
+                                     TALLOC_CTX *mem_ctx, int argc, 
+                                     const char **argv)
+{
+       WERROR result;
+       uint32 needed, level = 1, num_jobs, i;
+       BOOL got_hnd = False;
+       pstring printername;
+       fstring servername, user;
+       POLICY_HND hnd;
+       JOB_INFO_CTR ctr;
+       
+       if (argc < 2 || argc > 3) {
+               printf("Usage: %s printername [level]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc == 3)
+               level = atoi(argv[2]);
+
+       /* Open printer handle */
+
+       slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper(servername);
+       fstrcpy(user, cli->user_name);
+       slprintf(printername, sizeof(servername)-1, "\\\\%s\\", cli->desthost);
+       strupper(printername);
+       pstrcat(printername, argv[1]);
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       got_hnd = True;
+
+       /* Enumerate ports */
+
+       result = cli_spoolss_enumjobs(
+               cli, mem_ctx, 0, &needed, &hnd, level, 0, 1000,
+               &num_jobs, &ctr);
+
+       if (W_ERROR_V(result) == ERRinsufficientbuffer)
+               result = cli_spoolss_enumjobs(
+                       cli, mem_ctx, needed, NULL, &hnd, level, 0,
+                       1000, &num_jobs, &ctr);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       for (i = 0; i < num_jobs; i++) {
+               switch(level) {
+               case 1:
+                       display_job_info_1(&ctr.job.job_info_1[i]);
+                       break;
+               case 2:
+                       display_job_info_2(&ctr.job.job_info_2[i]);
+                       break;
+               default:
+                       d_printf("unknown info level %d\n", level);
+                       break;
+               }
+       }
+       
+done:
+       if (got_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* enumerate data */
+
+static NTSTATUS cmd_spoolss_enum_data( struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, int argc, 
+                                      const char **argv)
+{
+       WERROR result;
+       uint32 i=0, val_needed, data_needed;
+       BOOL got_hnd = False;
+       pstring printername;
+       fstring servername, user;
+       POLICY_HND hnd;
+
+       if (argc != 2) {
+               printf("Usage: %s printername\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       /* Open printer handle */
+
+       slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper(servername);
+       fstrcpy(user, cli->user_name);
+       slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+       strupper(printername);
+       pstrcat(printername, argv[1]);
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       got_hnd = True;
+
+       /* Enumerate data */
+
+       result = cli_spoolss_enumprinterdata(cli, mem_ctx, &hnd, i, 0, 0,
+                                            &val_needed, &data_needed,
+                                            NULL);
+       while (W_ERROR_IS_OK(result)) {
+               REGISTRY_VALUE value;
+               result = cli_spoolss_enumprinterdata(
+                       cli, mem_ctx, &hnd, i++, val_needed,
+                       data_needed, 0, 0, &value);
+               if (W_ERROR_IS_OK(result))
+                       display_reg_value(value);
+       }
+       if (W_ERROR_V(result) == ERRnomoreitems)
+               result = W_ERROR(ERRsuccess);
+
+done:
+       if (got_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* enumerate data for a given key */
+
+static NTSTATUS cmd_spoolss_enum_data_ex( struct cli_state *cli, 
+                                         TALLOC_CTX *mem_ctx, int argc, 
+                                         const char **argv)
+{
+       WERROR result;
+       uint32 needed, i;
+       BOOL got_hnd = False;
+       pstring printername;
+       fstring servername, user;
+       const char *keyname = NULL;
+       POLICY_HND hnd;
+       REGVAL_CTR ctr;
+
+       if (argc != 3) {
+               printf("Usage: %s printername <keyname>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       keyname = argv[2];
+
+       /* Open printer handle */
+
+       slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper(servername);
+       fstrcpy(user, cli->user_name);
+       slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+       strupper(printername);
+       pstrcat(printername, argv[1]);
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       got_hnd = True;
+
+       /* Enumerate subkeys */
+
+       result = cli_spoolss_enumprinterdataex(
+               cli, mem_ctx, 0, &needed, &hnd, keyname, NULL);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_enumprinterdataex(
+                       cli, mem_ctx, needed, NULL, &hnd, keyname, &ctr);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       for (i=0; i < ctr.num_values; i++) {
+               display_reg_value(*(ctr.values[i]));
+       }
+
+       regval_ctr_destroy(&ctr);
+
+done:
+       if (got_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* enumerate subkeys */
+
+static NTSTATUS cmd_spoolss_enum_printerkey( struct cli_state *cli, 
+                                            TALLOC_CTX *mem_ctx, int argc, 
+                                            const char **argv)
+{
+       WERROR result;
+       uint32 needed, returned;
+       BOOL got_hnd = False;
+       pstring printername;
+       fstring servername, user;
+       const char *keyname = NULL;
+       POLICY_HND hnd;
+       uint16 *keylist = NULL, *curkey;
+
+       if (argc < 2 || argc > 3) {
+               printf("Usage: %s printername [keyname]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+       
+       if (argc == 3)
+               keyname = argv[2];
+       else
+               keyname = "";
+
+       /* Open printer handle */
+
+       slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+       strupper(servername);
+       fstrcpy(user, cli->user_name);
+       slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+       strupper(printername);
+       pstrcat(printername, argv[1]);
+
+       result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, 
+                                            "", MAXIMUM_ALLOWED_ACCESS, 
+                                            servername, user, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+       got_hnd = True;
+
+       /* Enumerate subkeys */
+
+       result = cli_spoolss_enumprinterkey(
+               cli, mem_ctx, 0, &needed, &hnd, keyname, NULL, NULL);
+
+       if (W_ERROR_V(result) == ERRmoredata)
+               result = cli_spoolss_enumprinterkey(
+                       cli, mem_ctx, needed, NULL, &hnd, keyname, &keylist,
+                       &returned);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       curkey = keylist;
+       while (*curkey != 0) {
+               pstring subkey;
+               rpcstr_pull(subkey, curkey, sizeof(subkey), -1, 
+                           STR_TERMINATE);
+               printf("%s\n", subkey);
+               curkey += strlen(subkey) + 1;
+       }
+
+       safe_free(keylist);
+
+done:
+       if (got_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_spoolss_rffpcnex(struct cli_state *cli, 
+                                    TALLOC_CTX *mem_ctx, int argc, 
+                                    const char **argv)
+{
+       fstring servername, printername;
+       POLICY_HND hnd;
+       BOOL got_hnd = False;
+       WERROR result;
+       SPOOL_NOTIFY_OPTION option;
+
+       if (argc != 2) {
+               printf("Usage: %s printername\n", argv[0]);
+               result = WERR_OK;
+               goto done;
+       }
+
+       /* Open printer */
+
+       slprintf(servername, sizeof(servername) - 1, "\\\\%s", cli->desthost);
+       strupper(servername);
+
+       slprintf(printername, sizeof(printername) - 1, "\\\\%s\\%s", cli->desthost,
+                argv[1]);
+       strupper(printername);
+
+       result = cli_spoolss_open_printer_ex(
+               cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS, 
+               servername, cli->user_name, &hnd);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("Error opening %s\n", argv[1]);
+               goto done;
+       }
+
+       got_hnd = True;
+
+       /* Create spool options */
+
+       ZERO_STRUCT(option);
+
+       option.version = 2;
+       option.option_type_ptr = 1;
+       option.count = option.ctr.count = 2;
+
+       option.ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)talloc(
+               mem_ctx, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * 2);
+
+       ZERO_STRUCT(option.ctr.type[0]);
+       option.ctr.type[0].type = PRINTER_NOTIFY_TYPE;
+       option.ctr.type[0].count = option.ctr.type[0].count2 = 1;
+       option.ctr.type[0].fields_ptr = 1;
+       option.ctr.type[0].fields[0] = PRINTER_NOTIFY_SERVER_NAME;
+
+       ZERO_STRUCT(option.ctr.type[1]);
+       option.ctr.type[1].type = JOB_NOTIFY_TYPE;
+       option.ctr.type[1].count = option.ctr.type[1].count2 = 1;
+       option.ctr.type[1].fields_ptr = 1;
+       option.ctr.type[1].fields[0] = JOB_NOTIFY_PRINTER_NAME;
+
+       /* Send rffpcnex */
+
+       slprintf(servername, sizeof(servername) - 1, "\\\\%s", myhostname());
+       strupper(servername);
+
+       result = cli_spoolss_rffpcnex(
+               cli, mem_ctx, &hnd, 0, 0, servername, 123, &option);
+
+       if (!W_ERROR_IS_OK(result)) {
+               printf("Error rffpcnex %s\n", argv[1]);
+               goto done;
+       }
+
+done:          
+       if (got_hnd)
+               cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* List of commands exported by this module */
+struct cmd_set spoolss_commands[] = {
+
+       { "SPOOLSS"  },
+
+       { "adddriver",          cmd_spoolss_addprinterdriver,   PI_SPOOLSS, "Add a print driver",                  "" },
+       { "addprinter",         cmd_spoolss_addprinterex,       PI_SPOOLSS, "Add a printer",                       "" },
+       { "deldriver",          cmd_spoolss_deletedriver,       PI_SPOOLSS, "Delete a printer driver",             "" },
+       { "enumdata",           cmd_spoolss_enum_data,          PI_SPOOLSS, "Enumerate printer data",              "" },
+       { "enumdataex",         cmd_spoolss_enum_data_ex,       PI_SPOOLSS, "Enumerate printer data for a key",    "" },
+       { "enumkey",            cmd_spoolss_enum_printerkey,    PI_SPOOLSS, "Enumerate printer keys",              "" },
+       { "enumjobs",           cmd_spoolss_enum_jobs,          PI_SPOOLSS, "Enumerate print jobs",                "" },
+       { "enumports",          cmd_spoolss_enum_ports,         PI_SPOOLSS, "Enumerate printer ports",             "" },
+       { "enumdrivers",        cmd_spoolss_enum_drivers,       PI_SPOOLSS, "Enumerate installed printer drivers", "" },
+       { "enumprinters",       cmd_spoolss_enum_printers,      PI_SPOOLSS, "Enumerate printers",                  "" },
+       { "getdata",            cmd_spoolss_getprinterdata,     PI_SPOOLSS, "Get print driver data",               "" },
+       { "getdataex",          cmd_spoolss_getprinterdataex,   PI_SPOOLSS, "Get printer driver data with keyname", ""},
+       { "getdriver",          cmd_spoolss_getdriver,          PI_SPOOLSS, "Get print driver information",        "" },
+       { "getdriverdir",       cmd_spoolss_getdriverdir,       PI_SPOOLSS, "Get print driver upload directory",   "" },
+       { "getprinter",         cmd_spoolss_getprinter,         PI_SPOOLSS, "Get printer info",                    "" },
+       { "getprintprocdir",    cmd_spoolss_getprintprocdir,    PI_SPOOLSS, "Get print processor directory",       "" },
+       { "openprinter",        cmd_spoolss_open_printer_ex,    PI_SPOOLSS, "Open printer handle",                 "" },
+       { "setdriver",          cmd_spoolss_setdriver,          PI_SPOOLSS, "Set printer driver",                  "" },
+       { "getprintprocdir",    cmd_spoolss_getprintprocdir,    PI_SPOOLSS, "Get print processor directory",       "" },
+       { "addform",            cmd_spoolss_addform,            PI_SPOOLSS, "Add form",                            "" },
+       { "setform",            cmd_spoolss_setform,            PI_SPOOLSS, "Set form",                            "" },
+       { "getform",            cmd_spoolss_getform,            PI_SPOOLSS, "Get form",                            "" },
+       { "deleteform",         cmd_spoolss_deleteform,         PI_SPOOLSS, "Delete form",                         "" },
+       { "enumforms",          cmd_spoolss_enum_forms,         PI_SPOOLSS, "Enumerate forms",                     "" },
+       { "setprinter",         cmd_spoolss_setprinter,         PI_SPOOLSS, "Set printer comment",                 "" },
+       { "setprinterdata",     cmd_spoolss_setprinterdata,     PI_SPOOLSS, "Set REG_SZ printer data",             "" },
+       { "rffpcnex",           cmd_spoolss_rffpcnex,           PI_SPOOLSS, "Rffpcnex test", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_srvsvc.c b/source4/rpcclient/cmd_srvsvc.c
new file mode 100644 (file)
index 0000000..8597c7b
--- /dev/null
@@ -0,0 +1,361 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Andrew Tridgell 1992-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+   Copyright (C) Tim Potter 2000,2002
+
+   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 "rpcclient.h"
+
+/* Display server query info */
+
+static char *get_server_type_str(uint32 type)
+{
+       static fstring typestr;
+       int i;
+
+       if (type == SV_TYPE_ALL) {
+               fstrcpy(typestr, "All");
+               return typestr;
+       }
+               
+       typestr[0] = 0;
+
+       for (i = 0; i < 32; i++) {
+               if (type & (1 << i)) {
+                       switch (1 << i) {
+                       case SV_TYPE_WORKSTATION:
+                               fstrcat(typestr, "Wk ");
+                               break;
+                       case SV_TYPE_SERVER:
+                               fstrcat(typestr, "Sv ");
+                               break;
+                       case SV_TYPE_SQLSERVER:
+                               fstrcat(typestr, "Sql ");
+                               break;
+                       case SV_TYPE_DOMAIN_CTRL:
+                               fstrcat(typestr, "PDC ");
+                               break;
+                       case SV_TYPE_DOMAIN_BAKCTRL:
+                               fstrcat(typestr, "BDC ");
+                               break;
+                       case SV_TYPE_TIME_SOURCE:
+                               fstrcat(typestr, "Tim ");
+                               break;
+                       case SV_TYPE_AFP:
+                               fstrcat(typestr, "AFP ");
+                               break;
+                       case SV_TYPE_NOVELL:
+                               fstrcat(typestr, "Nov ");
+                               break;
+                       case SV_TYPE_DOMAIN_MEMBER:
+                               fstrcat(typestr, "Dom ");
+                               break;
+                       case SV_TYPE_PRINTQ_SERVER:
+                               fstrcat(typestr, "PrQ ");
+                               break;
+                       case SV_TYPE_DIALIN_SERVER:
+                               fstrcat(typestr, "Din ");
+                               break;
+                       case SV_TYPE_SERVER_UNIX:
+                               fstrcat(typestr, "Unx ");
+                               break;
+                       case SV_TYPE_NT:
+                               fstrcat(typestr, "NT ");
+                               break;
+                       case SV_TYPE_WFW:
+                               fstrcat(typestr, "Wfw ");
+                               break;
+                       case SV_TYPE_SERVER_MFPN:
+                               fstrcat(typestr, "Mfp ");
+                               break;
+                       case SV_TYPE_SERVER_NT:
+                               fstrcat(typestr, "SNT ");
+                               break;
+                       case SV_TYPE_POTENTIAL_BROWSER:
+                               fstrcat(typestr, "PtB ");
+                               break;
+                       case SV_TYPE_BACKUP_BROWSER:
+                               fstrcat(typestr, "BMB ");
+                               break;
+                       case SV_TYPE_MASTER_BROWSER:
+                               fstrcat(typestr, "LMB ");
+                               break;
+                       case SV_TYPE_DOMAIN_MASTER:
+                               fstrcat(typestr, "DMB ");
+                               break;
+                       case SV_TYPE_SERVER_OSF:
+                               fstrcat(typestr, "OSF ");
+                               break;
+                       case SV_TYPE_SERVER_VMS:
+                               fstrcat(typestr, "VMS ");
+                               break;
+                       case SV_TYPE_WIN95_PLUS:
+                               fstrcat(typestr, "W95 ");
+                               break;
+                       case SV_TYPE_ALTERNATE_XPORT:
+                               fstrcat(typestr, "Xpt ");
+                               break;
+                       case SV_TYPE_LOCAL_LIST_ONLY:
+                               fstrcat(typestr, "Dom ");
+                               break;
+                       case SV_TYPE_DOMAIN_ENUM:
+                               fstrcat(typestr, "Loc ");
+                               break;
+                       }
+               }
+       }
+
+       i = strlen(typestr) - 1;
+
+       if (typestr[i] == ' ')
+               typestr[i] = 0;
+       
+       return typestr;
+}
+
+static void display_server(char *sname, uint32 type, const char *comment)
+{
+       printf("\t%-15.15s%-20s %s\n", sname, get_server_type_str(type), 
+              comment);
+}
+
+static void display_srv_info_101(SRV_INFO_101 *sv101)
+{
+       fstring name;
+       fstring comment;
+
+       unistr2_to_ascii(name, &sv101->uni_name, sizeof(name) - 1);
+       unistr2_to_ascii(comment, &sv101->uni_comment, sizeof(comment) - 1);
+
+       display_server(name, sv101->srv_type, comment);
+
+       printf("\tplatform_id     :\t%d\n", sv101->platform_id);
+       printf("\tos version      :\t%d.%d\n", sv101->ver_major, 
+              sv101->ver_minor);
+
+       printf("\tserver type     :\t0x%x\n", sv101->srv_type);
+}
+
+static void display_srv_info_102(SRV_INFO_102 *sv102)
+{
+       fstring name;
+       fstring comment;
+       fstring usr_path;
+       
+       unistr2_to_ascii(name, &sv102->uni_name, sizeof(name) - 1);
+       unistr2_to_ascii(comment, &sv102->uni_comment, sizeof(comment) - 1);
+       unistr2_to_ascii(usr_path, &sv102->uni_usr_path, sizeof(usr_path) - 1);
+
+       display_server(name, sv102->srv_type, comment);
+
+       printf("\tplatform_id     :\t%d\n", sv102->platform_id);
+       printf("\tos version      :\t%d.%d\n", sv102->ver_major, 
+              sv102->ver_minor);
+
+       printf("\tusers           :\t%x\n", sv102->users);
+       printf("\tdisc, hidden    :\t%x, %x\n", sv102->disc, sv102->hidden);
+       printf("\tannounce, delta :\t%d, %d\n", sv102->announce, 
+              sv102->ann_delta);
+       printf("\tlicenses        :\t%d\n", sv102->licenses);
+       printf("\tuser path       :\t%s\n", usr_path);
+}
+
+/* Server query info */
+static NTSTATUS cmd_srvsvc_srv_query_info(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv)
+{
+       uint32 info_level = 101;
+       SRV_INFO_CTR ctr;
+       WERROR result;
+
+       if (argc > 2) {
+               printf("Usage: %s [infolevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       result = cli_srvsvc_net_srv_get_info(cli, mem_ctx, info_level,
+                                            &ctr);
+
+       if (!W_ERROR_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Display results */
+
+       switch (info_level) {
+       case 101:
+               display_srv_info_101(&ctr.srv.sv101);
+               break;
+       case 102:
+               display_srv_info_102(&ctr.srv.sv102);
+               break;
+       default:
+               printf("unsupported info level %d\n", info_level);
+               break;
+       }
+
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+       fstring netname = "", remark = "";
+
+       rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+       rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+       printf("netname: %s\n", netname);
+       printf("\tremark:\t%s\n", remark);
+}
+
+static void display_share_info_2(SRV_SHARE_INFO_2 *info2)
+{
+       fstring netname = "", remark = "", path = "", passwd = "";
+
+       rpcstr_pull_unistr2_fstring(netname, &info2->info_2_str.uni_netname);
+       rpcstr_pull_unistr2_fstring(remark, &info2->info_2_str.uni_remark);
+       rpcstr_pull_unistr2_fstring(path, &info2->info_2_str.uni_path);
+       rpcstr_pull_unistr2_fstring(passwd, &info2->info_2_str.uni_passwd);
+
+       printf("netname: %s\n", netname);
+       printf("\tremark:\t%s\n", remark);
+       printf("\tpath:\t%s\n", path);
+       printf("\tpassword:\t%s\n", passwd);
+}
+
+static NTSTATUS cmd_srvsvc_net_share_enum(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv)
+{
+       uint32 info_level = 2;
+       SRV_SHARE_INFO_CTR ctr;
+       WERROR result;
+       ENUM_HND hnd;
+       uint32 preferred_len = 0xffffffff, i;
+
+       if (argc > 2) {
+               printf("Usage: %s [infolevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       init_enum_hnd(&hnd, 0);
+
+       result = cli_srvsvc_net_share_enum(
+               cli, mem_ctx, info_level, &ctr, preferred_len, &hnd);
+
+       if (!W_ERROR_IS_OK(result) || !ctr.num_entries)
+               goto done;
+
+       /* Display results */
+
+       switch (info_level) {
+       case 1:
+               for (i = 0; i < ctr.num_entries; i++)
+                       display_share_info_1(&ctr.share.info1[i]);
+               break;
+       case 2:
+               for (i = 0; i < ctr.num_entries; i++)
+                       display_share_info_2(&ctr.share.info2[i]);
+               break;
+       default:
+               printf("unsupported info level %d\n", info_level);
+               break;
+       }
+
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_srvsvc_net_remote_tod(struct cli_state *cli, 
+                                          TALLOC_CTX *mem_ctx,
+                                          int argc, const char **argv)
+{
+       TIME_OF_DAY_INFO tod;
+       WERROR result;
+
+       if (argc > 1) {
+               printf("Usage: %s\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       result = cli_srvsvc_net_remote_tod(
+               cli, mem_ctx, cli->srv_name_slash, &tod);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS cmd_srvsvc_net_file_enum(struct cli_state *cli, 
+                                        TALLOC_CTX *mem_ctx,
+                                        int argc, const char **argv)
+{
+       uint32 info_level = 3;
+       SRV_FILE_INFO_CTR ctr;
+       WERROR result;
+       ENUM_HND hnd;
+       uint32 preferred_len = 0;
+
+       if (argc > 2) {
+               printf("Usage: %s [infolevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2)
+               info_level = atoi(argv[1]);
+
+       init_enum_hnd(&hnd, 0);
+
+       ZERO_STRUCT(ctr);
+
+       result = cli_srvsvc_net_file_enum(
+               cli, mem_ctx, info_level, NULL, &ctr, preferred_len, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set srvsvc_commands[] = {
+
+       { "SRVSVC" },
+
+       { "srvinfo",    cmd_srvsvc_srv_query_info,  PI_SRVSVC, "Server query info", "" },
+       { "netshareenum", cmd_srvsvc_net_share_enum, PI_SRVSVC, "Enumerate shares", "" },
+       { "netfileenum", cmd_srvsvc_net_file_enum, PI_SRVSVC, "Enumerate open files", "" },
+       { "netremotetod", cmd_srvsvc_net_remote_tod, PI_SRVSVC, "Fetch remote time of day", "" },
+
+       { NULL }
+};
diff --git a/source4/rpcclient/cmd_wkssvc.c b/source4/rpcclient/cmd_wkssvc.c
new file mode 100644 (file)
index 0000000..bb11823
--- /dev/null
@@ -0,0 +1,84 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NT Domain Authentication SMB / MSRPC client
+   Copyright (C) Andrew Tridgell 1994-1997
+   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+   
+   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"
+
+#define DEBUG_TESTING
+
+extern struct cli_state *smb_cli;
+
+extern FILE* out_hnd;
+
+
+/****************************************************************************
+workstation get info query
+****************************************************************************/
+void cmd_wks_query_info(struct client_info *info)
+{
+       fstring dest_wks;
+       fstring tmp;
+       WKS_INFO_100 ctr;
+       uint32 info_level = 100;
+
+       BOOL res = True;
+
+       memset((char *)&ctr, '\0', sizeof(ctr));
+
+       fstrcpy(dest_wks, "\\\\");
+       fstrcat(dest_wks, info->dest_host);
+       strupper(dest_wks);
+
+       if (next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+       {
+               info_level = (uint32)strtol(tmp, (char**)NULL, 10);
+       }
+
+       DEBUG(4,("cmd_wks_query_info: server:%s info level: %d\n",
+                               dest_wks, info_level));
+
+       DEBUG(5, ("cmd_wks_query_info: smb_cli->fd:%d\n", smb_cli->fd));
+
+       /* open LSARPC session. */
+       res = res ? cli_nt_session_open(smb_cli, PI_WKSSVC) : False;
+
+       /* send info level: receive requested info.  hopefully. */
+       res = res ? do_wks_query_info(smb_cli, 
+                               dest_wks, info_level, &ctr) : False;
+
+       /* close the session */
+       cli_nt_session_close(smb_cli);
+
+       if (res)
+       {
+               DEBUG(5,("cmd_wks_query_info: query succeeded\n"));
+
+#if 0
+               display_wks_info_100(out_hnd, ACTION_HEADER   , &ctr);
+               display_wks_info_100(out_hnd, ACTION_ENUMERATE, &ctr);
+               display_wks_info_100(out_hnd, ACTION_FOOTER   , &ctr);
+#endif
+
+       }
+       else
+       {
+               DEBUG(5,("cmd_wks_query_info: query failed\n"));
+       }
+}
diff --git a/source4/rpcclient/display_sec.c b/source4/rpcclient/display_sec.c
new file mode 100644 (file)
index 0000000..2a93c91
--- /dev/null
@@ -0,0 +1,144 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1992-1999
+   Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+   
+   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 "rpcclient.h"
+
+/****************************************************************************
+convert a security permissions into a string
+****************************************************************************/
+char *get_sec_mask_str(uint32 type)
+{
+       static fstring typestr="";
+
+       typestr[0] = 0;
+
+       if (type & GENERIC_ALL_ACCESS)
+               fstrcat(typestr, "Generic all access ");
+       if (type & GENERIC_EXECUTE_ACCESS)
+               fstrcat(typestr, "Generic execute access ");
+       if (type & GENERIC_WRITE_ACCESS)
+               fstrcat(typestr, "Generic write access ");
+       if (type & GENERIC_READ_ACCESS)
+               fstrcat(typestr, "Generic read access ");
+       if (type & MAXIMUM_ALLOWED_ACCESS)
+               fstrcat(typestr, "MAXIMUM_ALLOWED_ACCESS ");
+       if (type & SYSTEM_SECURITY_ACCESS)
+               fstrcat(typestr, "SYSTEM_SECURITY_ACCESS ");
+       if (type & SYNCHRONIZE_ACCESS)
+               fstrcat(typestr, "SYNCHRONIZE_ACCESS ");
+       if (type & WRITE_OWNER_ACCESS)
+               fstrcat(typestr, "WRITE_OWNER_ACCESS ");
+       if (type & WRITE_DAC_ACCESS)
+               fstrcat(typestr, "WRITE_DAC_ACCESS ");
+       if (type & READ_CONTROL_ACCESS)
+               fstrcat(typestr, "READ_CONTROL_ACCESS ");
+       if (type & DELETE_ACCESS)
+               fstrcat(typestr, "DELETE_ACCESS ");
+
+       printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK);
+
+       return typestr;
+}
+
+/****************************************************************************
+ display sec_access structure
+ ****************************************************************************/
+void display_sec_access(SEC_ACCESS *info)
+{
+       printf("\t\tPermissions: 0x%x: %s\n", info->mask, get_sec_mask_str(info->mask));
+}
+
+/****************************************************************************
+ display sec_ace structure
+ ****************************************************************************/
+void display_sec_ace(SEC_ACE *ace)
+{
+       fstring sid_str;
+
+       printf("\tACE\n\t\ttype: ");
+       switch (ace->type) {
+               case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                       printf("ACCESS ALLOWED");
+                       break;
+               case SEC_ACE_TYPE_ACCESS_DENIED:
+                       printf("ACCESS DENIED");
+                       break;
+               case SEC_ACE_TYPE_SYSTEM_AUDIT:
+                       printf("SYSTEM AUDIT");
+                       break;
+               case SEC_ACE_TYPE_SYSTEM_ALARM:
+                       printf("SYSTEM ALARM");
+                       break;
+               default:
+                       printf("????");
+                       break;
+       }
+       printf(" (%d) flags: %d\n", ace->type, ace->flags);
+       display_sec_access(&ace->info);
+       sid_to_string(sid_str, &ace->trustee);
+       printf("\t\tSID: %s\n\n", sid_str);
+}
+
+/****************************************************************************
+ display sec_acl structure
+ ****************************************************************************/
+void display_sec_acl(SEC_ACL *sec_acl)
+{
+       int i;
+
+       printf("\tACL\tNum ACEs:\t%d\trevision:\t%x\n",
+                        sec_acl->num_aces, sec_acl->revision); 
+       printf("\t---\n");
+
+       if (sec_acl->size != 0 && sec_acl->num_aces != 0)
+               for (i = 0; i < sec_acl->num_aces; i++)
+                       display_sec_ace(&sec_acl->ace[i]);
+                               
+}
+
+/****************************************************************************
+ display sec_desc structure
+ ****************************************************************************/
+void display_sec_desc(SEC_DESC *sec)
+{
+       fstring sid_str;
+
+       if (sec->sacl) {
+               printf("SACL\n");
+               display_sec_acl(sec->sacl);
+       }
+
+       if (sec->dacl) {
+               printf("DACL\n");
+               display_sec_acl(sec->dacl);
+       }
+
+       if (sec->owner_sid) {
+               sid_to_string(sid_str, sec->owner_sid);
+               printf("\tOwner SID:\t%s\n", sid_str);
+       }
+
+       if (sec->grp_sid) {
+               sid_to_string(sid_str, sec->grp_sid);
+               printf("\tParent SID:\t%s\n", sid_str);
+       }
+}
diff --git a/source4/rpcclient/rpcclient.c b/source4/rpcclient/rpcclient.c
new file mode 100644 (file)
index 0000000..c9d8cd9
--- /dev/null
@@ -0,0 +1,756 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Tim Potter 2000-2001
+   Copyright (C) Martin Pool 2003
+
+   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 "rpcclient.h"
+
+DOM_SID domain_sid;
+
+
+/* List to hold groups of commands.
+ *
+ * Commands are defined in a list of arrays: arrays are easy to
+ * statically declare, and lists are easier to dynamically extend.
+ */
+
+static struct cmd_list {
+       struct cmd_list *prev, *next;
+       struct cmd_set *cmd_set;
+} *cmd_list;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+       char **matches;
+       int i, count=0;
+       struct cmd_list *commands = cmd_list;
+
+#if 0  /* JERRY */
+       /* FIXME!!!  -- what to do when completing argument? */
+       /* for words not at the start of the line fallback 
+          to filename completion */
+       if (start) 
+               return NULL;
+#endif
+
+       /* make sure we have a list of valid commands */
+       if (!commands) 
+               return NULL;
+
+       matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+       if (!matches) return NULL;
+
+       matches[count++] = strdup(text);
+       if (!matches[0]) return NULL;
+
+       while (commands && count < MAX_COMPLETIONS-1) 
+       {
+               if (!commands->cmd_set)
+                       break;
+               
+               for (i=0; commands->cmd_set[i].name; i++)
+               {
+                       if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+                               commands->cmd_set[i].fn) 
+                       {
+                               matches[count] = strdup(commands->cmd_set[i].name);
+                               if (!matches[count]) 
+                                       return NULL;
+                               count++;
+                       }
+               }
+               
+               commands = commands->next;
+               
+       }
+
+       if (count == 2) {
+               SAFE_FREE(matches[0]);
+               matches[0] = strdup(matches[1]);
+       }
+       matches[count] = NULL;
+       return matches;
+}
+
+/***********************************************************************
+ * read in username/password credentials from a file
+ */
+static void read_authfile (
+       char *filename, 
+       char* username, 
+       char* password, 
+       char* domain
+)
+{
+       FILE *auth;
+        fstring buf;
+        uint16 len = 0;
+       char *ptr, *val, *param;
+                               
+       if ((auth=sys_fopen(filename, "r")) == NULL)
+       {
+               printf ("ERROR: Unable to open credentials file!\n");
+               return;
+       }
+                                
+       while (!feof(auth))
+       {  
+               /* get a line from the file */
+               if (!fgets (buf, sizeof(buf), auth))
+                       continue;
+               
+               len = strlen(buf);
+               
+               /* skip empty lines */                  
+               if ((len) && (buf[len-1]=='\n'))
+               {
+                       buf[len-1] = '\0';
+                       len--;
+               }       
+               if (len == 0)
+                       continue;
+                                       
+               /* break up the line into parameter & value.
+                  will need to eat a little whitespace possibly */
+               param = buf;
+               if (!(ptr = strchr_m(buf, '=')))
+                       continue;
+               val = ptr+1;
+               *ptr = '\0';
+                                       
+               /* eat leading white space */
+               while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+                       val++;
+                                       
+               if (strwicmp("password", param) == 0)
+                       fstrcpy (password, val);
+               else if (strwicmp("username", param) == 0)
+                       fstrcpy (username, val);
+               else if (strwicmp("domain", param) == 0)
+                       fstrcpy (domain, val);
+                                               
+               memset(buf, 0, sizeof(buf));
+       }
+       fclose(auth);
+       
+       return;
+}
+
+static char* next_command (char** cmdstr)
+{
+       static pstring          command;
+       char                    *p;
+       
+       if (!cmdstr || !(*cmdstr))
+               return NULL;
+       
+       p = strchr_m(*cmdstr, ';');
+       if (p)
+               *p = '\0';
+       pstrcpy(command, *cmdstr);
+       if (p)
+               *cmdstr = p + 1;
+       else
+               *cmdstr = NULL;
+       
+       return command;
+}
+
+
+/**
+ * Find default username from environment variables.
+ *
+ * @param username fstring to receive username; not touched if none is
+ * known.
+ **/
+static void get_username (char *username)
+{
+        if (getenv("USER"))
+                fstrcpy(username,getenv("USER"));
+        if (*username == 0 && getenv("LOGNAME"))
+                fstrcpy(username,getenv("LOGNAME"));
+        if (*username == 0) {
+                fstrcpy(username,"GUEST");
+        }
+
+       return;
+}
+
+/* Fetch the SID for this computer */
+
+static void fetch_machine_sid(struct cli_state *cli)
+{
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_OK;
+       uint32 info_class = 5;
+       fstring domain_name;
+       static BOOL got_domain_sid;
+       TALLOC_CTX *mem_ctx;
+
+       if (got_domain_sid) return;
+
+       if (!(mem_ctx=talloc_init("fetch_machine_sid")))
+       {
+               DEBUG(0,("fetch_machine_sid: talloc_init returned NULL!\n"));
+               goto error;
+       }
+
+
+       if (!cli_nt_session_open (cli, PI_LSARPC)) {
+               fprintf(stderr, "could not initialise lsa pipe\n");
+               goto error;
+       }
+       
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto error;
+       }
+
+       result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class, 
+                                          domain_name, &domain_sid);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto error;
+       }
+
+       got_domain_sid = True;
+
+       cli_lsa_close(cli, mem_ctx, &pol);
+       cli_nt_session_close(cli);
+       talloc_destroy(mem_ctx);
+
+       return;
+
+ error:
+       fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               fprintf(stderr, "error: %s\n", nt_errstr(result));
+       }
+
+       exit(1);
+}
+
+/* List the available commands on a given pipe */
+
+static NTSTATUS cmd_listcommands(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                                int argc, const char **argv)
+{
+       struct cmd_list *tmp;
+        struct cmd_set *tmp_set;
+       int i;
+
+        /* Usage */
+
+        if (argc != 2) {
+                printf("Usage: %s <pipe>\n", argv[0]);
+                return NT_STATUS_OK;
+        }
+
+        /* Help on one command */
+
+       for (tmp = cmd_list; tmp; tmp = tmp->next) 
+       {
+               tmp_set = tmp->cmd_set;
+               
+               if (!StrCaseCmp(argv[1], tmp_set->name))
+               {
+                       printf("Available commands on the %s pipe:\n\n", tmp_set->name);
+
+                       i = 0;
+                       tmp_set++;
+                       while(tmp_set->name) {
+                               printf("%20s", tmp_set->name);
+                                tmp_set++;
+                               i++;
+                               if (i%4 == 0)
+                                       printf("\n");
+                       }
+                       
+                       /* drop out of the loop */
+                       break;
+               }
+        }
+       printf("\n\n");
+
+       return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+
+static NTSTATUS cmd_help(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                         int argc, const char **argv)
+{
+       struct cmd_list *tmp;
+        struct cmd_set *tmp_set;
+
+        /* Usage */
+
+        if (argc > 2) {
+                printf("Usage: %s [command]\n", argv[0]);
+                return NT_STATUS_OK;
+        }
+
+        /* Help on one command */
+
+        if (argc == 2) {
+                for (tmp = cmd_list; tmp; tmp = tmp->next) {
+                        
+                        tmp_set = tmp->cmd_set;
+
+                        while(tmp_set->name) {
+                                if (strequal(argv[1], tmp_set->name)) {
+                                        if (tmp_set->usage &&
+                                            tmp_set->usage[0])
+                                                printf("%s\n", tmp_set->usage);
+                                        else
+                                                printf("No help for %s\n", tmp_set->name);
+
+                                        return NT_STATUS_OK;
+                                }
+
+                                tmp_set++;
+                        }
+                }
+
+                printf("No such command: %s\n", argv[1]);
+                return NT_STATUS_OK;
+        }
+
+        /* List all commands */
+
+       for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+               tmp_set = tmp->cmd_set;
+
+               while(tmp_set->name) {
+
+                       printf("%15s\t\t%s\n", tmp_set->name,
+                              tmp_set->description ? tmp_set->description:
+                              "");
+
+                       tmp_set++;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+
+static NTSTATUS cmd_debuglevel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                               int argc, const char **argv)
+{
+       if (argc > 2) {
+               printf("Usage: %s [debuglevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2) {
+               DEBUGLEVEL = atoi(argv[1]);
+       }
+
+       printf("debuglevel is %d\n", DEBUGLEVEL);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                         int argc, const char **argv)
+{
+       exit(0);
+       return NT_STATUS_OK; /* NOTREACHED */
+}
+
+/* Built in rpcclient commands */
+
+static struct cmd_set rpcclient_commands[] = {
+
+       { "GENERAL OPTIONS" },
+
+       { "help",       cmd_help,         -1,   "Get help on commands", "[command]" },
+       { "?",          cmd_help,         -1,   "Get help on commands", "[command]" },
+       { "debuglevel", cmd_debuglevel,   -1,   "Set debug level", "level" },
+       { "list",       cmd_listcommands, -1,   "List available commands on <pipe>", "pipe" },
+       { "exit",       cmd_quit,         -1,   "Exit program", "" },
+       { "quit",       cmd_quit,         -1,   "Exit program", "" },
+
+       { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+       { "---------------", NULL,      -1,     "----------------------" },
+       { NULL }
+};
+
+
+/* Various pipe commands */
+
+extern struct cmd_set lsarpc_commands[];
+extern struct cmd_set samr_commands[];
+extern struct cmd_set spoolss_commands[];
+extern struct cmd_set netlogon_commands[];
+extern struct cmd_set srvsvc_commands[];
+extern struct cmd_set dfs_commands[];
+extern struct cmd_set reg_commands[];
+extern struct cmd_set ds_commands[];
+
+static struct cmd_set *rpcclient_command_list[] = {
+       rpcclient_commands,
+       lsarpc_commands,
+       ds_commands,
+       samr_commands,
+       spoolss_commands,
+       netlogon_commands,
+       srvsvc_commands,
+       dfs_commands,
+       reg_commands,
+       NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+       struct cmd_list *entry;
+
+       if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+               DEBUG(0, ("out of memory\n"));
+               return;
+       }
+
+       ZERO_STRUCTP(entry);
+
+       entry->cmd_set = cmd_set;
+       DLIST_ADD(cmd_list, entry);
+}
+
+
+/**
+ * Call an rpcclient function, passing an argv array.
+ *
+ * @param cmd Command to run, as a single string.
+ **/
+static NTSTATUS do_cmd(struct cli_state *cli,
+                      struct cmd_set *cmd_entry,
+                      int argc, char **argv)
+{
+       NTSTATUS result;
+       
+       TALLOC_CTX *mem_ctx;
+
+       /* Create mem_ctx */
+
+       if (!(mem_ctx = talloc_init("do_cmd"))) {
+               DEBUG(0, ("talloc_init() failed\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Open pipe */
+
+       if (cmd_entry->pipe_idx != -1)
+               if (!cli_nt_session_open(cli, cmd_entry->pipe_idx)) {
+                       DEBUG(0, ("Could not initialize pipe\n"));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+       /* Run command */
+
+       result = cmd_entry->fn(cli, mem_ctx, argc, (const char **) argv);
+
+       /* Cleanup */
+
+       if (cmd_entry->pipe_idx != -1)
+               cli_nt_session_close(cli);
+
+       talloc_destroy(mem_ctx);
+
+       return result;
+}
+
+
+/**
+ * Process a command entered at the prompt or as part of -c
+ *
+ * @returns The NTSTATUS from running the command.
+ **/
+static NTSTATUS process_cmd(struct cli_state *cli, char *cmd)
+{
+       struct cmd_list *temp_list;
+       NTSTATUS result = NT_STATUS_OK;
+       int ret;
+       int argc;
+       char **argv = NULL;
+
+       if ((ret = poptParseArgvString(cmd, &argc, (const char ***) &argv)) != 0) {
+               fprintf(stderr, "rpcclient: %s\n", poptStrerror(ret));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+
+       /* Walk through a dlist of arrays of commands. */
+       for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+               struct cmd_set *temp_set = temp_list->cmd_set;
+
+               while (temp_set->name) {
+                       if (strequal(argv[0], temp_set->name)) {
+                               if (!temp_set->fn) {
+                                       fprintf (stderr, "Invalid command\n");
+                                       goto out_free;
+                               }
+
+                               result = do_cmd(cli, temp_set, argc, argv);
+
+                               goto out_free;
+                       }
+                       temp_set++;
+               }
+       }
+
+       if (argv[0]) {
+               printf("command not found: %s\n", argv[0]);
+       }
+
+out_free:
+       if (!NT_STATUS_IS_OK(result)) {
+               printf("result was %s\n", nt_errstr(result));
+       }
+
+       if (argv) {
+               /* NOTE: popt allocates the whole argv, including the
+                * strings, as a single block.  So a single free is
+                * enough to release it -- we don't free the
+                * individual strings.  rtfm. */
+               free(argv);
+       }
+       
+       return result;
+}
+
+
+/* Main function */
+
+ int main(int argc, char *argv[])
+{
+       static int              got_pass = 0;
+       BOOL                    interactive = True;
+       int                     opt;
+       static char             *cmdstr = "";
+       const char *server;
+       struct cli_state        *cli;
+       fstring                 password="",
+                               username="",
+               domain="";
+       static char             *opt_authfile=NULL,
+                               *opt_username=NULL,
+                               *opt_domain=NULL,
+                               *opt_logfile=NULL,
+                               *opt_ipaddr=NULL;
+       pstring                 logfile;
+       struct cmd_set          **cmd_set;
+       struct in_addr          server_ip;
+       NTSTATUS                nt_status;
+
+       /* make sure the vars that get altered (4th field) are in
+          a fixed location or certain compilers complain */
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               {"authfile",    'A', POPT_ARG_STRING,   &opt_authfile, 'A', "File containing user credentials", "AUTHFILE"},
+               {"nopass",      'N', POPT_ARG_NONE,     &got_pass, 'N', "Don't ask for a password"},
+               {"user", 'U', POPT_ARG_STRING,  &opt_username, 'U', "Set the network username", "USER"},
+               {"workgroup", 'W', POPT_ARG_STRING,     &opt_domain, 'W', "Set the domain name for user account", "DOMAIN"},
+               {"command",     'c', POPT_ARG_STRING,   &cmdstr, 'c', "Execute semicolon separated cmds", "COMMANDS"},
+               {"logfile",     'l', POPT_ARG_STRING,   &opt_logfile, 'l', "Logfile to use instead of stdout", "LOGFILE" },
+               {"dest-ip", 'I', POPT_ARG_STRING,   &opt_ipaddr, 'I', "Specify destination IP address", "IP"},
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { NULL }
+       };
+
+       setlinebuf(stdout);
+
+       /* Parse options */
+
+       pc = poptGetContext("rpcclient", argc, (const char **) argv,
+                           long_options, 0);
+
+       if (argc == 1) {
+               poptPrintHelp(pc, stderr, 0);
+               return 0;
+       }
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'A':
+                       /* only get the username, password, and domain from the file */
+                       read_authfile (opt_authfile, username, password, domain);
+                       if (strlen (password))
+                               got_pass = 1;
+                       break;
+                       
+               case 'l':
+                       slprintf(logfile, sizeof(logfile) - 1, "%s.client", 
+                                opt_logfile);
+                       lp_set_logfile(logfile);
+                       interactive = False;
+                       break;
+                       
+               case 'U': {
+                       char *lp;
+
+                       fstrcpy(username,opt_username);
+
+                       if ((lp=strchr_m(username,'%'))) {
+                               *lp = 0;
+                               fstrcpy(password,lp+1);
+                               got_pass = 1;
+                               memset(strchr_m(opt_username,'%') + 1, 'X',
+                                      strlen(password));
+                       }
+                       break;
+               }
+               case 'I':
+                       if ( (server_ip.s_addr=inet_addr(opt_ipaddr)) == INADDR_NONE ) {
+                               fprintf(stderr, "%s not a valid IP address\n",
+                                       opt_ipaddr);
+                               return 1;
+                       }
+               case 'W':
+                       fstrcpy(domain, opt_domain);
+                       break;
+               }
+       }
+
+       /* Get server as remaining unparsed argument.  Print usage if more
+          than one unparsed argument is present. */
+
+       server = poptGetArg(pc);
+       
+       if (!server || poptGetArg(pc)) {
+               poptPrintHelp(pc, stderr, 0);
+               return 1;
+       }
+
+       poptFreeContext(pc);
+
+       /* the following functions are part of the Samba debugging
+          facilities.  See lib/debug.c */
+       setup_logging("rpcclient", interactive);
+       if (!interactive) 
+               reopen_logs();
+       
+       /* Load smb.conf file */
+
+       if (!lp_load(dyn_CONFIGFILE,True,False,False))
+               fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+       load_interfaces();
+
+       if (!init_names())
+               return 1;
+
+       /* Resolve the IP address */
+
+       if (!opt_ipaddr && !resolve_name(server, &server_ip, 0x20))  {
+               fprintf(stderr, "Unable to resolve %s\n", server);
+               return 1;
+       }
+       
+       /*
+        * Get password
+        * from stdin if necessary
+        */
+
+       if (!got_pass) {
+               char *pass = getpass("Password:");
+               if (pass) {
+                       fstrcpy(password, pass);
+               }
+       }
+       
+       if (!strlen(username) && !got_pass)
+               get_username(username);
+               
+       nt_status = cli_full_connection(&cli, lp_netbios_name(), server, 
+                                       &server_ip, 0,
+                                       "IPC$", "IPC",  
+                                       username, domain,
+                                       password, 0, NULL);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0,("Cannot connect to server.  Error was %s\n", nt_errstr(nt_status)));
+               return 1;
+       }
+
+       memset(password,'X',sizeof(password));
+
+       /* Load command lists */
+
+       cmd_set = rpcclient_command_list;
+
+       while(*cmd_set) {
+               add_command_set(*cmd_set);
+               add_command_set(separator_command);
+               cmd_set++;
+       }
+
+       fetch_machine_sid(cli);
+       /* Do anything specified with -c */
+        if (cmdstr[0]) {
+                char    *cmd;
+                char    *p = cmdstr;
+                while((cmd=next_command(&p)) != NULL) {
+                        process_cmd(cli, cmd);
+                }
+               
+               cli_shutdown(cli);
+                return 0;
+        }
+
+       /* Loop around accepting commands */
+
+       while(1) {
+               pstring prompt;
+               char *line;
+
+               slprintf(prompt, sizeof(prompt) - 1, "rpcclient $> ");
+
+               line = smb_readline(prompt, NULL, completion_fn);
+
+               if (line == NULL)
+                       break;
+
+               if (line[0] != '\n')
+                       process_cmd(cli, line);
+       }
+       
+       cli_shutdown(cli);
+       return 0;
+}
diff --git a/source4/rpcclient/rpcclient.h b/source4/rpcclient/rpcclient.h
new file mode 100644 (file)
index 0000000..1bd3c1a
--- /dev/null
@@ -0,0 +1,34 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RPC pipe client
+
+   Copyright (C) Tim Potter 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.
+*/
+
+#ifndef RPCCLIENT_H
+#define RPCCLIENT_H
+
+struct cmd_set {
+       const char *name;
+       NTSTATUS (*fn)(struct cli_state *cli, TALLOC_CTX *mem_ctx, int argc, 
+                       const char **argv);
+        int pipe_idx;
+       const char *description;
+       const char *usage;
+};
+
+#endif /* RPCCLIENT_H */
diff --git a/source4/sam/SAM-interface_handles.txt b/source4/sam/SAM-interface_handles.txt
new file mode 100644 (file)
index 0000000..1c164bd
--- /dev/null
@@ -0,0 +1,123 @@
+SAM API \r
+\r
+NTSTATUS sam_get_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC **sd)\r
+NTSTATUS sam_set_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC *sd)\r
+\r
+NTSTATUS sam_lookup_name(NT_USER_TOKEN *access, DOM_SID *domain, char *name, DOM_SID **sid, uint32 *type)\r
+NTSTATUS sam_lookup_sid(NT_USER_TOKEN *access, DOM_SID *sid, char **name, uint32 *type)\r
+\r
+\r
+Domain API \r
+\r
+NTSTATUS sam_update_domain(SAM_DOMAIN_HANDLE *domain)\r
+\r
+NTSTATUS sam_enum_domains(NT_USER_TOKEN *access, int32 *domain_count, DOM_SID **domains, char **domain_names)\r
+NTSTATUS sam_lookup_domain(NT_USER_TOKEN *access, char *domain, DOM_SID **domainsid)\r
+\r
+NTSTATUS sam_get_domain_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)\r
+\r
+\r
+User API\r
+\r
+NTSTATUS sam_create_user(NT_USER_TOKEN *access, uint32 access_desired, SAM_USER_HANDLE **user)\r
+NTSTATUS sam_add_user(SAM_USER_HANDLE *user)\r
+NTSTATUS sam_update_user(SAM_USER_HANDLE *user)\r
+NTSTATUS sam_delete_user(SAM_USER_HANDLE * user)\r
+\r
+NTSTATUS sam_enum_users(NT_USER_TOKEN *access, DOM_SID *domain, int32 *user_count, SAM_USER_ENUM **users)\r
+\r
+NTSTATUS sam_get_user_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *usersid, SAM_USER_HANDLE **user)\r
+NTSTATUS sam_get_user_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_USER_HANDLE **user)\r
+\r
+\r
+Group API \r
+\r
+NTSTATUS sam_create_group(NT_USER_TOKEN *access, uint32 access_desired, uint32 typ, SAM_GROUP_HANDLE **group)\r
+NTSTATUS sam_add_group(SAM_GROUP_HANDLE *samgroup)\r
+NTSTATUS sam_update_group(SAM_GROUP_HANDLE *samgroup)\r
+NTSTATUS sam_delete_group(SAM_GROUP_HANDLE *groupsid)\r
+\r
+NTSTATUS sam_enum_groups(NT_USER_TOKEN *access, DOM_SID *domainsid, uint32 typ, uint32 *groups_count, SAM_GROUP_ENUM **groups)\r
+\r
+NTSTATUS sam_get_group_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *groupsid, SAM_GROUP_HANDLE **group)\r
+NTSTATUS sam_get_group_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_GROUP_HANDLE **group)\r
+\r
+NTSTATUS sam_add_member_to_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)\r
+NTSTATUS sam_delete_member_from_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)\r
+NTSTATUS sam_enum_groupmembers(SAM_GROUP_HANLDE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)\r
+\r
+NTSTATUS sam_get_groups_of_user(SAM_USER_HANDLE *user, uint32 typ, uint32 *group_count, SAM_GROUP_ENUM **groups)\r
+\r
+\r
+\r
+structures\r
+\r
+typedef _SAM_GROUP_MEMBER {\r
+ DOM_SID sid; \r
+ BOOL group; /* specifies if it is a group or a user */ \r
+\r
+} SAM_GROUP_MEMBER\r
+\r
+typedef struct sam_user_enum {\r
+ DOM_SID sid; \r
+ char *username; \r
+ char *full_name; \r
+ char *user_desc; \r
+ uint16 acc_ctrl; \r
+} SAM_USER_ENUM;\r
+\r
+typedef struct sam_group_enum {\r
+ DOM_SID sid;\r
+ char *groupname;\r
+ char *comment;\r
+} SAM_GROUP_ENUM\r
+\r
+NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, DOM_SID **sid)\r
+NTSTATUS sam_get_domain_num_users(SAM_DOMAIN_HANDLE *domain, uint32 *num_users)\r
+NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups)\r
+NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases)\r
+NTSTATUS sam_{get,set}_domain_name(SAM_DOMAIN_HANDLE *domain, char **domain_name)\r
+NTSTATUS sam_{get,set}_domain_server(SAM_DOMAIN_HANDLE *domain, char **server_name)\r
+NTSTATUS sam_{get,set}_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage)\r
+NTSTATUS sam_{get,set}_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage)\r
+NTSTATUS sam_{get,set}_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration)\r
+NTSTATUS sam_{get,set}_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count)\r
+NTSTATUS sam_{get,set}_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength)\r
+NTSTATUS sam_{get,set}_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uin16 *password_history)\r
+NTSTATUS sam_{get,set}_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count)\r
+NTSTATUS sam_{get,set}_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff)\r
+NTSTATUS sam_{get,set}_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange)\r
+\r
+NTSTATUS sam_get_user_sid(SAM_USER_HANDLE *user, DOM_SID **sid)\r
+NTSTATUS sam_{get,set}_user_pgroup(SAM_USER_HANDLE *user, DOM_SID **pgroup)\r
+NTSTATUS sam_{get,set}_user_name(SAM_USER_HANDLE *user, char **username)\r
+NTSTATUS sam_{get,set}_user_fullname(SAM_USER_HANDLE *user, char** fullname)\r
+NTSTATUS sam_{get,set}_user_description(SAM_USER_HANDLE *user, char **description)\r
+NTSTATUS sam_{get,set}_user_home_dir(SAM_USER_HANDLE *user, char **home_dir)\r
+NTSTATUS sam_{get,set}_user_dir_drive(SAM_USER_HANDLE *user, char **dir_drive)\r
+NTSTATUS sam_{get,set}_user_logon_script(SAM_USER_HANDLE *user, char **logon_script)\r
+NTSTATUS sam_{get,set}_user_profile_path(SAM_USER_HANDLE *user, char **profile_path)\r
+NTSTATUS sam_{get,set}_user_workstations(SAM_USER_HANDLE *user, char **workstations)\r
+NTSTATUS sam_{get,set}_user_munged_dial(SAM_USER_HANDLE *user, char **munged_dial)\r
+NTSTATUS sam_{get,set}_user_lm_pwd(SAM_USER_HANDLE *user, DATA_BLOB *lm_pwd)\r
+NTSTATUS sam_{get,set}_user_nt_pwd(SAM_USER_HANDLE *user, DATA_BLOB *nt_pwd)\r
+NTSTATUS sam_{get,set}_user_plain_pwd(SAM_USER_HANDLE *user, DATA_BLOB *plaintext_pwd)\r
+NTSTATUS sam_{get,set}_user_acct_ctrl(SAM_USER_HANDLE *user, uint16 *acct_ctrl)\r
+NTSTATUS sam_{get,set}_user_logon_divs(SAM_USER_HANDLE *user, uint16 *logon_divs)\r
+NTSTATUS sam_{get,set}_user_hours(SAM_USER_HANDLE *user, uint32 *hours_len, uint8 **hours)\r
+NTSTATUS sam_{get,set}_user_logon_time(SAM_USER_HANDLE *user, NTTIME *logon_time)\r
+NTSTATUS sam_{get,set}_user_logoff_time(SAM_USER_HANDLE *user, NTTIME *logoff_time)\r
+NTSTATUS sam_{get,set}_user_kickoff_time(SAM_USER_HANDLE *user, NTTIME kickoff_time)\r
+NTSTATUS sam_{get,set}_user_pwd_last_set(SAM_USER_HANDLE *user, NTTIME pwd_last_set)\r
+NTSTATUS sam_{get,set}_user_pwd_can_change(SAM_USER_HANDLE *user, NTTIME pwd_can_change)\r
+NTSTATUS sam_{get,set}_user_pwd_must_change(SAM_USER_HANDLE *user, NTTIME pwd_must_change)\r
+NTSTATUS sam_{get,set}_user_unknown_1(SAM_USER_HANDLE *user, char **unknown_1)\r
+NTSTATUS sam_{get,set}_user_unknown_2(SAM_USER_HANDLE *user, uint32 *unknown_2)\r
+NTSTATUS sam_{get,set}_user_unknown_3(SAM_USER_HANDLE *user, uint32 *unknown_3)\r
+NTSTATUS sam_{get,set}_user_unknown_4(SAM_USER_HANDLE *user, uint32 *unknown_4)\r
+\r
+NTSTATUS sam_get_group_sid(SAM_GROUP_HANDLE *group, DOM_SID **sid)\r
+NTSTATUS sam_get_group_typ(SAM_GROUP_HANDLE *group, uint32 *typ)\r
+NTSTATUS sam_{get,set}_group_name(SAM_GROUP_HANDLE *group, char **group_name)\r
+NTSTATUS sam_{get,set}_group_comment(SAM_GROUP_HANDLE *group, char **comment)\r
+NTSTATUS sam_{get,set}_group_priv_set(SAM_GROUP_HANDLE *group, PRIVILEGE_SET *priv_set)
\ No newline at end of file
diff --git a/source4/sam/account.c b/source4/sam/account.c
new file mode 100644 (file)
index 0000000..b833614
--- /dev/null
@@ -0,0 +1,305 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Jeremy Allison                1996-2001
+   Copyright (C) Luke Kenneth Casson Leighton  1996-1998
+   Copyright (C) Gerald (Jerry) Carter         2000-2001
+   Copyright (C) Andrew Bartlett               2001-2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_ACCOUNT_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_account(SAM_ACCOUNT_HANDLE *account)
+{
+       ZERO_STRUCT(account->private); /* Don't touch the talloc context */
+
+        /* Don't change these timestamp settings without a good reason.
+           They are important for NT member server compatibility. */
+
+       /* FIXME: We should actually call get_nt_time_max() or sthng 
+        * here */
+       unix_to_nt_time(&(account->private.logoff_time),get_time_t_max());
+       unix_to_nt_time(&(account->private.kickoff_time),get_time_t_max());
+       unix_to_nt_time(&(account->private.pass_must_change_time),get_time_t_max());
+       account->private.unknown_1 = 0x00ffffff;        /* don't know */
+       account->private.logon_divs = 168;      /* hours per week */
+       account->private.hours_len = 21;                /* 21 times 8 bits = 168 */
+       memset(account->private.hours, 0xff, account->private.hours_len); /* available at all hours */
+       account->private.unknown_2 = 0x00000000; /* don't know */
+       account->private.unknown_3 = 0x000004ec; /* don't know */
+}      
+
+static void destroy_sam_talloc(SAM_ACCOUNT_HANDLE **account) 
+{
+       if (*account) {
+               data_blob_clear_free(&((*account)->private.lm_pw));
+               data_blob_clear_free(&((*account)->private.nt_pw));
+               if((*account)->private.plaintext_pw!=NULL)
+                       memset((*account)->private.plaintext_pw,'\0',strlen((*account)->private.plaintext_pw));
+
+               talloc_destroy((*account)->mem_ctx);
+               *account = NULL;
+       }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_ACCOUNT_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_account_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT_HANDLE **account)
+{
+       SMB_ASSERT(*account != NULL);
+
+       if (!mem_ctx) {
+               DEBUG(0,("sam_init_account_talloc: mem_ctx was NULL!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       *account=(SAM_ACCOUNT_HANDLE *)talloc(mem_ctx, sizeof(SAM_ACCOUNT_HANDLE));
+
+       if (*account==NULL) {
+               DEBUG(0,("sam_init_account_talloc: error while allocating memory\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*account)->mem_ctx = mem_ctx;
+
+       (*account)->free_fn = NULL;
+
+       sam_fill_default_account(*account);
+       
+       return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS sam_init_account(SAM_ACCOUNT_HANDLE **account)
+{
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS nt_status;
+       
+       mem_ctx = talloc_init("sam internal SAM_ACCOUNT_HANDLE allocation");
+
+       if (!mem_ctx) {
+               DEBUG(0,("sam_init_account: error while doing talloc_init()\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_init_account_talloc(mem_ctx, account))) {
+               talloc_destroy(mem_ctx);
+               return nt_status;
+       }
+       
+       (*account)->free_fn = destroy_sam_talloc;
+
+       return NT_STATUS_OK;
+}
+
+/**
+ * Free the contents of the SAM_ACCOUNT_HANDLE, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from 
+ * memory.
+ *
+ * @param account SAM_ACCOUNT_HANDLE to free members of.
+ **/
+
+static void sam_free_account_contents(SAM_ACCOUNT_HANDLE *account)
+{
+
+       /* Kill off sensitive data.  Free()ed by the
+          talloc mechinism */
+
+       data_blob_clear_free(&(account->private.lm_pw));
+       data_blob_clear_free(&(account->private.nt_pw));
+       if (account->private.plaintext_pw)
+               memset(account->private.plaintext_pw,'\0',strlen(account->private.plaintext_pw));
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT_HANDLE and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS sam_reset_sam(SAM_ACCOUNT_HANDLE *account)
+{
+       SMB_ASSERT(account != NULL);
+       
+       sam_free_account_contents(account);
+
+       sam_fill_default_account(account);
+
+       return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_account(SAM_ACCOUNT_HANDLE **account)
+{
+       SMB_ASSERT(*account != NULL);
+
+       sam_free_account_contents(*account);
+       
+       if ((*account)->free_fn) {
+               (*account)->free_fn(account);
+       }
+
+       return NT_STATUS_OK;    
+}
+
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+       static fstring acct_str;
+       size_t i = 0;
+
+       acct_str[i++] = '[';
+
+       if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+       if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+       if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+       if (acct_ctrl & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
+       if (acct_ctrl & ACB_NORMAL   ) acct_str[i++] = 'U';
+       if (acct_ctrl & ACB_MNS      ) acct_str[i++] = 'M';
+       if (acct_ctrl & ACB_WSTRUST  ) acct_str[i++] = 'W';
+       if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+       if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+       if (acct_ctrl & ACB_PWNOEXP  ) acct_str[i++] = 'X';
+       if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+       for ( ; i < length - 2 ; i++ )
+               acct_str[i] = ' ';
+
+       i = length - 2;
+       acct_str[i++] = ']';
+       acct_str[i++] = '\0';
+
+       return acct_str;
+}     
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_acct_ctrl(const char *p)
+{
+       uint16 acct_ctrl = 0;
+       BOOL finished = False;
+
+       /*
+        * Check if the account type bits have been encoded after the
+        * NT password (in the form [NDHTUWSLXI]).
+        */
+
+       if (*p != '[')
+               return 0;
+
+       for (p++; *p && !finished; p++) {
+               switch (*p) {
+                       case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+                       case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+                       case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+                       case 'T': { acct_ctrl |= ACB_TEMPDUP  ; break; /* 'T'emp account. */ } 
+                       case 'U': { acct_ctrl |= ACB_NORMAL   ; break; /* 'U'ser account (normal). */ } 
+                       case 'M': { acct_ctrl |= ACB_MNS      ; break; /* 'M'NS logon user account. What is this ? */ } 
+                       case 'W': { acct_ctrl |= ACB_WSTRUST  ; break; /* 'W'orkstation account. */ } 
+                       case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ } 
+                       case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } 
+                       case 'X': { acct_ctrl |= ACB_PWNOEXP  ; break; /* No 'X'piry on password */ } 
+                       case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+            case ' ': { break; }
+                       case ':':
+                       case '\n':
+                       case '\0': 
+                       case ']':
+                       default:  { finished = True; }
+               }
+       }
+
+       return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void sam_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+       if (pwd != NULL) {
+               int i;
+               for (i = 0; i < 16; i++)
+                       slprintf(&p[i*2], 3, "%02X", pwd[i]);
+       } else {
+               if (acct_ctrl & ACB_PWNOTREQ)
+                       safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+               else
+                       safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+       }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL sam_gethexpwd(const char *p, unsigned char *pwd)
+{
+       int i;
+       unsigned char   lonybble, hinybble;
+       char           *hexchars = "0123456789ABCDEF";
+       char           *p1, *p2;
+       
+       if (!p)
+               return (False);
+       
+       for (i = 0; i < 32; i += 2) {
+               hinybble = toupper(p[i]);
+               lonybble = toupper(p[i + 1]);
+
+               p1 = strchr(hexchars, hinybble);
+               p2 = strchr(hexchars, lonybble);
+
+               if (!p1 || !p2)
+                       return (False);
+
+               hinybble = PTR_DIFF(p1, hexchars);
+               lonybble = PTR_DIFF(p2, hexchars);
+
+               pwd[i / 2] = (hinybble << 4) | lonybble;
+       }
+       return (True);
+}
diff --git a/source4/sam/get_set_account.c b/source4/sam/get_set_account.c
new file mode 100644 (file)
index 0000000..acac281
--- /dev/null
@@ -0,0 +1,845 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_ACCOUNT_HANDLE access routines
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Stefan (metze) Metzmacher             2002
+   Copyright (C) Jelmer Vernooij                       2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+NTSTATUS sam_get_account_domain_sid(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid)
+{
+       NTSTATUS status;
+       SAM_DOMAIN_HANDLE *domain;
+       SAM_ASSERT(!sampass || !sid);
+
+       if (!NT_STATUS_IS_OK(status = sam_get_account_domain(sampass, &domain))){
+               DEBUG(0, ("sam_get_account_domain_sid: Can't get domain for account\n"));
+               return status;
+       }
+
+       return sam_get_domain_sid(domain, sid);
+}
+
+NTSTATUS sam_get_account_domain_name(const SAM_ACCOUNT_HANDLE *sampass, const char **domain_name)
+{
+       NTSTATUS status;
+       SAM_DOMAIN_HANDLE *domain;
+       SAM_ASSERT(sampass && domain_name);
+
+       if (!NT_STATUS_IS_OK(status = sam_get_account_domain(sampass, &domain))){
+               DEBUG(0, ("sam_get_account_domain_name: Can't get domain for account\n"));
+               return status;
+       }
+
+       return sam_get_domain_name(domain, domain_name);
+}
+
+NTSTATUS sam_get_account_acct_ctrl(const SAM_ACCOUNT_HANDLE *sampass, uint16 *acct_ctrl)
+{
+       SAM_ASSERT(sampass && acct_ctrl);
+
+       *acct_ctrl = sampass->private.acct_ctrl;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_logon_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *logon_time)
+{
+       SAM_ASSERT(sampass && logon_time) ;
+
+       *logon_time = sampass->private.logon_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_logoff_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *logoff_time)
+{
+       SAM_ASSERT(sampass && logoff_time) ;
+
+       *logoff_time = sampass->private.logoff_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_kickoff_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *kickoff_time)
+{
+       SAM_ASSERT(sampass && kickoff_time);
+
+       *kickoff_time = sampass->private.kickoff_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_pass_last_set_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_last_set_time)
+{
+       SAM_ASSERT(sampass && pass_last_set_time);
+
+       *pass_last_set_time = sampass->private.pass_last_set_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_pass_can_change_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_can_change_time)
+{
+       SAM_ASSERT(sampass && pass_can_change_time);
+
+       *pass_can_change_time = sampass->private.pass_can_change_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_pass_must_change_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_must_change_time)
+{
+       SAM_ASSERT(sampass && pass_must_change_time);
+
+       *pass_must_change_time = sampass->private.pass_must_change_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_logon_divs(const SAM_ACCOUNT_HANDLE *sampass, uint16 *logon_divs)
+{
+       SAM_ASSERT(sampass && logon_divs);
+
+       *logon_divs = sampass->private.logon_divs;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_hours_len(const SAM_ACCOUNT_HANDLE *sampass, uint32 *hours_len)
+{
+       SAM_ASSERT(sampass && hours_len);
+
+       *hours_len = sampass->private.hours_len;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_hours(const SAM_ACCOUNT_HANDLE *sampass, const uint8 **hours)
+{
+       SAM_ASSERT(sampass && hours);
+
+       *hours = sampass->private.hours;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_nt_pwd(const SAM_ACCOUNT_HANDLE *sampass, DATA_BLOB *nt_pwd)
+{
+       SAM_ASSERT(sampass);
+
+       SMB_ASSERT((!sampass->private.nt_pw.data) 
+                  || sampass->private.nt_pw.length == NT_HASH_LEN);
+
+       *nt_pwd = sampass->private.nt_pw;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_lm_pwd(const SAM_ACCOUNT_HANDLE *sampass, DATA_BLOB *lm_pwd)
+{ 
+       SAM_ASSERT(sampass);
+
+       SMB_ASSERT((!sampass->private.lm_pw.data) 
+                  || sampass->private.lm_pw.length == LM_HASH_LEN);
+
+       *lm_pwd = sampass->private.lm_pw;
+
+       return NT_STATUS_OK;
+}
+
+/* Return the plaintext password if known.  Most of the time
+   it isn't, so don't assume anything magic about this function.
+   
+   Used to pass the plaintext to sam backends that might 
+   want to store more than just the NTLM hashes.
+*/
+
+NTSTATUS sam_get_account_plaintext_pwd(const SAM_ACCOUNT_HANDLE *sampass, char **plain_pwd)
+{
+       SAM_ASSERT(sampass && plain_pwd);
+
+       *plain_pwd = sampass->private.plaintext_pw;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_sid(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid)
+{
+       SAM_ASSERT(sampass);
+
+       *sid = &(sampass->private.account_sid);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_pgroup(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid)
+{
+       SAM_ASSERT(sampass);
+
+       *sid = &(sampass->private.group_sid);
+
+       return NT_STATUS_OK;
+}
+
+/**
+ * Get flags showing what is initalised in the SAM_ACCOUNT_HANDLE
+ * @param sampass the SAM_ACCOUNT_HANDLE in question
+ * @return the flags indicating the members initialised in the struct.
+ **/
+NTSTATUS sam_get_account_init_flag(const SAM_ACCOUNT_HANDLE *sampass, uint32 *initflag)
+{
+       SAM_ASSERT(sampass);
+
+       *initflag = sampass->private.init_flag;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_name(const SAM_ACCOUNT_HANDLE *sampass, char **account_name)
+{
+       SAM_ASSERT(sampass);
+
+       *account_name = sampass->private.account_name;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_domain(const SAM_ACCOUNT_HANDLE *sampass, SAM_DOMAIN_HANDLE **domain)
+{
+       SAM_ASSERT(sampass);
+
+       *domain = sampass->private.domain;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_fullname(const SAM_ACCOUNT_HANDLE *sampass, char **fullname)
+{
+       SAM_ASSERT(sampass);
+
+       *fullname = sampass->private.full_name;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_homedir(const SAM_ACCOUNT_HANDLE *sampass, char **homedir)
+{
+       SAM_ASSERT(sampass);
+
+       *homedir = sampass->private.home_dir;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_unix_home_dir(const SAM_ACCOUNT_HANDLE *sampass, char **uhomedir)
+{
+       SAM_ASSERT(sampass);
+
+       *uhomedir = sampass->private.unix_home_dir;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_dir_drive(const SAM_ACCOUNT_HANDLE *sampass, char **dirdrive)
+{
+       SAM_ASSERT(sampass);
+
+       *dirdrive = sampass->private.dir_drive;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_logon_script(const SAM_ACCOUNT_HANDLE *sampass, char **logon_script)
+{
+       SAM_ASSERT(sampass);
+
+       *logon_script = sampass->private.logon_script;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_profile_path(const SAM_ACCOUNT_HANDLE *sampass, char **profile_path)
+{
+       SAM_ASSERT(sampass);
+
+       *profile_path = sampass->private.profile_path;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_description(const SAM_ACCOUNT_HANDLE *sampass, char **description)
+{
+       SAM_ASSERT(sampass);
+
+       *description = sampass->private.acct_desc;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_workstations(const SAM_ACCOUNT_HANDLE *sampass, char **workstations)
+{
+       SAM_ASSERT(sampass);
+
+       *workstations = sampass->private.workstations;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_unknown_str(const SAM_ACCOUNT_HANDLE *sampass, char **unknown_str)
+{
+       SAM_ASSERT(sampass);
+
+       *unknown_str = sampass->private.unknown_str;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_munged_dial(const SAM_ACCOUNT_HANDLE *sampass, char **munged_dial)
+{
+       SAM_ASSERT(sampass);
+
+       *munged_dial = sampass->private.munged_dial;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_unknown_1(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown1)
+{
+       SAM_ASSERT(sampass && unknown1);
+
+       *unknown1 = sampass->private.unknown_1;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_unknown_2(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown2)
+{
+       SAM_ASSERT(sampass && unknown2);
+
+       *unknown2 = sampass->private.unknown_2;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_unknown_3(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown3)
+{
+       SAM_ASSERT(sampass && unknown3);
+
+       *unknown3 = sampass->private.unknown_3;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Collection of set...() functions for SAM_ACCOUNT_HANDLE_INFO.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_acct_ctrl(SAM_ACCOUNT_HANDLE *sampass, uint16 acct_ctrl)
+{
+       SAM_ASSERT(sampass);
+               
+       sampass->private.acct_ctrl = acct_ctrl;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_logon_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.logon_time = mytime;
+
+
+       return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS sam_set_account_logoff_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.logoff_time = mytime;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_kickoff_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.kickoff_time = mytime;
+
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_pass_can_change_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.pass_can_change_time = mytime;
+
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_pass_must_change_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.pass_must_change_time = mytime;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_pass_last_set_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.pass_last_set_time = mytime;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_hours_len(SAM_ACCOUNT_HANDLE *sampass, uint32 len)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.hours_len = len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_logon_divs(SAM_ACCOUNT_HANDLE *sampass, uint16 hours)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.logon_divs = hours;
+       return NT_STATUS_OK;
+}
+
+/**
+ * Set flags showing what is initalised in the SAM_ACCOUNT_HANDLE
+ * @param sampass the SAM_ACCOUNT_HANDLE in question
+ * @param flag The *new* flag to be set.  Old flags preserved
+ *             this flag is only added.  
+ **/
+NTSTATUS sam_set_account_init_flag(SAM_ACCOUNT_HANDLE *sampass, uint32 flag)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.init_flag |= flag;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_sid(SAM_ACCOUNT_HANDLE *sampass, const DOM_SID *u_sid)
+{
+       SAM_ASSERT(sampass && u_sid);
+       
+       sid_copy(&sampass->private.account_sid, u_sid);
+
+       DEBUG(10, ("sam_set_account_sid: setting account sid %s\n", 
+                   sid_string_static(&sampass->private.account_sid)));
+       
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_sid_from_string(SAM_ACCOUNT_HANDLE *sampass, const char *u_sid)
+{
+       DOM_SID new_sid;
+       SAM_ASSERT(sampass && u_sid);
+
+       DEBUG(10, ("sam_set_account_sid_from_string: setting account sid %s\n",
+                  u_sid));
+
+       if (!string_to_sid(&new_sid, u_sid)) { 
+               DEBUG(1, ("sam_set_account_sid_from_string: %s isn't a valid SID!\n", u_sid));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+        
+       if (!NT_STATUS_IS_OK(sam_set_account_sid(sampass, &new_sid))) {
+               DEBUG(1, ("sam_set_account_sid_from_string: could not set sid %s on SAM_ACCOUNT_HANDLE!\n", u_sid));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_pgroup_sid(SAM_ACCOUNT_HANDLE *sampass, const DOM_SID *g_sid)
+{
+       SAM_ASSERT(sampass && g_sid);
+
+       sid_copy(&sampass->private.group_sid, g_sid);
+
+       DEBUG(10, ("sam_set_group_sid: setting group sid %s\n", 
+                   sid_string_static(&sampass->private.group_sid)));
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_pgroup_string(SAM_ACCOUNT_HANDLE *sampass, const char *g_sid)
+{
+       DOM_SID new_sid;
+       SAM_ASSERT(sampass && g_sid);
+
+       DEBUG(10, ("sam_set_group_sid_from_string: setting group sid %s\n",
+                  g_sid));
+
+       if (!string_to_sid(&new_sid, g_sid)) { 
+               DEBUG(1, ("sam_set_group_sid_from_string: %s isn't a valid SID!\n", g_sid));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+        
+       if (!NT_STATUS_IS_OK(sam_set_account_pgroup_sid(sampass, &new_sid))) {
+               DEBUG(1, ("sam_set_group_sid_from_string: could not set sid %s on SAM_ACCOUNT_HANDLE!\n", g_sid));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the domain name.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_domain(SAM_ACCOUNT_HANDLE *sampass, SAM_DOMAIN_HANDLE *domain)
+{      
+       SAM_ASSERT(sampass);
+
+       sampass->private.domain = domain;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's NT name.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_name(SAM_ACCOUNT_HANDLE *sampass, const char *account_name)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_account_name: setting nt account_name %s, was %s\n", account_name, sampass->private.account_name));
+
+       sampass->private.account_name = talloc_strdup(sampass->mem_ctx, account_name);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's full name.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_fullname(SAM_ACCOUNT_HANDLE *sampass, const char *full_name)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_account_fullname: setting full name %s, was %s\n", full_name, sampass->private.full_name));
+
+       sampass->private.full_name = talloc_strdup(sampass->mem_ctx, full_name);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's logon script.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_logon_script(SAM_ACCOUNT_HANDLE *sampass, const char *logon_script, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_logon_script: from %s to %s\n", logon_script, sampass->private.logon_script));
+
+       sampass->private.logon_script = talloc_strdup(sampass->mem_ctx, logon_script);
+       
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's profile path.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_profile_path(SAM_ACCOUNT_HANDLE *sampass, const char *profile_path, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_profile_path: setting profile path %s, was %s\n", profile_path, sampass->private.profile_path));
+       sampass->private.profile_path = talloc_strdup(sampass->mem_ctx, profile_path);
+               
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's directory drive.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_dir_drive(SAM_ACCOUNT_HANDLE *sampass, const char *dir_drive, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_dir_drive: setting dir drive %s, was %s\n", dir_drive,
+                       sampass->private.dir_drive));
+       sampass->private.dir_drive = talloc_strdup(sampass->mem_ctx, dir_drive);
+               
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's home directory.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_homedir(SAM_ACCOUNT_HANDLE *sampass, const char *home_dir, BOOL store)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_homedir: setting home dir %s, was %s\n", home_dir,
+               sampass->private.home_dir));
+       sampass->private.home_dir = talloc_strdup(sampass->mem_ctx, home_dir);
+               
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's unix home directory.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_unix_homedir(SAM_ACCOUNT_HANDLE *sampass, const char *unix_home_dir)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_unix_homedir: setting home dir %s, was %s\n", unix_home_dir,
+               sampass->private.unix_home_dir));
+       sampass->private.unix_home_dir = talloc_strdup(sampass->mem_ctx, unix_home_dir);
+               
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's account description.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_acct_desc(SAM_ACCOUNT_HANDLE *sampass, const char *acct_desc)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.acct_desc = talloc_strdup(sampass->mem_ctx, acct_desc);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's workstation allowed list.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_workstations(SAM_ACCOUNT_HANDLE *sampass, const char *workstations)
+{
+       SAM_ASSERT(sampass);
+
+       DEBUG(10, ("sam_set_workstations: setting workstations %s, was %s\n", workstations,
+                       sampass->private.workstations));
+       sampass->private.workstations = talloc_strdup(sampass->mem_ctx, workstations);
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's 'unknown_str', whatever the heck this actually is...
+ ********************************************************************/
+
+NTSTATUS sam_set_account_unknown_str(SAM_ACCOUNT_HANDLE *sampass, const char *unknown_str)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.unknown_str = talloc_strdup(sampass->mem_ctx, unknown_str);
+               
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's dial string.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_munged_dial(SAM_ACCOUNT_HANDLE *sampass, const char *munged_dial)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.munged_dial = talloc_strdup(sampass->mem_ctx, munged_dial);
+       
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's NT hash.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_nt_pwd(SAM_ACCOUNT_HANDLE *sampass, const DATA_BLOB data)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.nt_pw = data;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's LM hash.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_lm_pwd(SAM_ACCOUNT_HANDLE *sampass, const DATA_BLOB data)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.lm_pw = data;
+
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's plaintext password only (base procedure, see helper
+ below)
+ ********************************************************************/
+
+NTSTATUS sam_set_account_plaintext_pwd(SAM_ACCOUNT_HANDLE *sampass, const char *plain_pwd)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.plaintext_pw = talloc_strdup(sampass->mem_ctx, plain_pwd);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_unknown_1(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.unknown_1 = unkn;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_unknown_2(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.unknown_2 = unkn;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_unknown_3(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn)
+{
+       SAM_ASSERT(sampass);
+
+       sampass->private.unknown_3 = unkn;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_account_hours(SAM_ACCOUNT_HANDLE *sampass, const uint8 *hours)
+{
+       SAM_ASSERT(sampass);
+
+       if (!hours) {
+               memset ((char *)sampass->private.hours, 0, MAX_HOURS_LEN);
+               return NT_STATUS_OK;
+       }
+       
+       memcpy(sampass->private.hours, hours, MAX_HOURS_LEN);
+
+       return NT_STATUS_OK;
+}
+
+/* Helpful interfaces to the above */
+
+/*********************************************************************
+ Sets the last changed times and must change times for a normal
+ password change.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_pass_changed_now(SAM_ACCOUNT_HANDLE *sampass)
+{
+       uint32 expire;
+       NTTIME temptime;
+
+       SAM_ASSERT(sampass);
+       
+       unix_to_nt_time(&temptime, time(NULL));
+       if (!NT_STATUS_IS_OK(sam_set_account_pass_last_set_time(sampass, temptime)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire) 
+           || (expire==(uint32)-1)) {
+
+               get_nttime_max(&temptime);
+               if (!NT_STATUS_IS_OK(sam_set_account_pass_must_change_time(sampass, temptime, False)))
+                       return NT_STATUS_UNSUCCESSFUL;
+
+       } else {
+               /* FIXME: Add expire to temptime */
+               
+               if (!NT_STATUS_IS_OK(sam_get_account_pass_last_set_time(sampass,&temptime)) || !NT_STATUS_IS_OK(sam_set_account_pass_must_change_time(sampass, temptime,True)))
+                       return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Set the account's PLAINTEXT password.  Used as an interface to the above.
+ Also sets the last change time to NOW.
+ ********************************************************************/
+
+NTSTATUS sam_set_account_passwd(SAM_ACCOUNT_HANDLE *sampass, const char *plaintext)
+{
+       DATA_BLOB data;
+       uchar new_lanman_p16[16];
+       uchar new_nt_p16[16];
+
+       SAM_ASSERT(sampass && plaintext);
+       
+       nt_lm_owf_gen(plaintext, new_nt_p16, new_lanman_p16);
+
+       data = data_blob(new_nt_p16, 16);
+       if (!NT_STATUS_IS_OK(sam_set_account_nt_pwd(sampass, data)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       data = data_blob(new_lanman_p16, 16);
+
+       if (!NT_STATUS_IS_OK(sam_set_account_lm_pwd(sampass, data)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       if (!NT_STATUS_IS_OK(sam_set_account_plaintext_pwd(sampass, plaintext)))
+               return NT_STATUS_UNSUCCESSFUL;
+       
+       if (!NT_STATUS_IS_OK(sam_set_account_pass_changed_now(sampass)))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       return NT_STATUS_OK;
+}
+
diff --git a/source4/sam/get_set_domain.c b/source4/sam/get_set_domain.c
new file mode 100644 (file)
index 0000000..c70a4a3
--- /dev/null
@@ -0,0 +1,263 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_DOMAIN access routines
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+   Copyright (C) Jelmer Vernooij                       2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, const DOM_SID **sid)
+{
+       SAM_ASSERT(domain &&sid);
+
+       *sid = &(domain->private.sid);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_num_accounts(SAM_DOMAIN_HANDLE *domain, uint32 *num_accounts)
+{
+       SAM_ASSERT(domain &&num_accounts);
+
+       *num_accounts = domain->private.num_accounts;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups)
+{
+       SAM_ASSERT(domain &&num_groups);
+
+       *num_groups = domain->private.num_groups;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases)
+{
+       SAM_ASSERT(domain &&num_aliases);
+
+       *num_aliases = domain->private.num_aliases;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_name(SAM_DOMAIN_HANDLE *domain, const char **domain_name)
+{
+       SAM_ASSERT(domain &&domain_name);
+
+       *domain_name = domain->private.name;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_server(SAM_DOMAIN_HANDLE *domain, const char **server_name)
+{
+       SAM_ASSERT(domain &&server_name);
+
+       *server_name = domain->private.servername;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage)
+{
+       SAM_ASSERT(domain &&max_passwordage);
+
+       *max_passwordage = domain->private.max_passwordage;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage)
+{
+       SAM_ASSERT(domain &&min_passwordage);
+
+       *min_passwordage = domain->private.min_passwordage;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration)
+{
+       SAM_ASSERT(domain &&lockout_duration);
+
+       *lockout_duration = domain->private.lockout_duration;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count)
+{
+       SAM_ASSERT(domain &&reset_lockout_count);
+       
+       *reset_lockout_count = domain->private.reset_count;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength)
+{
+       SAM_ASSERT(domain &&min_passwordlength);
+
+       *min_passwordlength = domain->private.min_passwordlength;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uint16 *password_history)
+{
+       SAM_ASSERT(domain &&password_history);
+
+       *password_history = domain->private.password_history;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count)
+{
+       SAM_ASSERT(domain &&lockout_count);
+
+       *lockout_count = domain->private.lockout_count;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff)
+{
+       SAM_ASSERT(domain &&force_logoff);
+
+       *force_logoff = domain->private.force_logoff;
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_get_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange)
+{
+       SAM_ASSERT(domain && login_pwdchange);
+
+       *login_pwdchange = domain->private.login_pwdchange;
+
+       return NT_STATUS_OK;
+}
+
+/* Set */
+
+NTSTATUS sam_set_domain_name(SAM_DOMAIN_HANDLE *domain, const char *domain_name)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.name = talloc_strdup(domain->mem_ctx, domain_name);
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_set_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME max_passwordage)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.max_passwordage = max_passwordage;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME min_passwordage)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.min_passwordage = min_passwordage;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME lockout_duration)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.lockout_duration = lockout_duration;
+
+       return NT_STATUS_OK;
+}
+NTSTATUS sam_set_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME reset_lockout_count)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.reset_count = reset_lockout_count;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 min_passwordlength)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.min_passwordlength = min_passwordlength;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uint16 password_history)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.password_history = password_history;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 lockout_count)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.lockout_count = lockout_count;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL force_logoff)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.force_logoff = force_logoff;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL login_pwdchange)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.login_pwdchange = login_pwdchange;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_domain_server(SAM_DOMAIN_HANDLE *domain, const char *server_name)
+{
+       SAM_ASSERT(domain);
+
+       domain->private.servername = talloc_strdup(domain->mem_ctx, server_name);
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/sam/get_set_group.c b/source4/sam/get_set_group.c
new file mode 100644 (file)
index 0000000..11ea925
--- /dev/null
@@ -0,0 +1,106 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_USER_HANDLE access routines
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Stefan (metze) Metzmacher     2002
+   Copyright (C) Jelmer Vernooij                       2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/* sam group get functions */
+
+NTSTATUS sam_get_group_sid(const SAM_GROUP_HANDLE *group, const DOM_SID **sid)
+{
+       SAM_ASSERT(group && sid);
+
+       *sid = &(group->private.sid);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_ctrl(const SAM_GROUP_HANDLE *group, uint32 *group_ctrl)
+{
+       SAM_ASSERT(group && group_ctrl);
+
+       *group_ctrl = group->private.group_ctrl;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_name(const SAM_GROUP_HANDLE *group, const char **group_name)
+{
+       SAM_ASSERT(group);
+
+       *group_name = group->private.group_name;
+
+       return NT_STATUS_OK;
+
+}
+NTSTATUS sam_get_group_comment(const SAM_GROUP_HANDLE *group, const char **group_desc)
+{
+       SAM_ASSERT(group);
+
+       *group_desc = group->private.group_desc;
+
+       return NT_STATUS_OK;
+}
+
+/* sam group set functions */
+
+NTSTATUS sam_set_group_sid(SAM_GROUP_HANDLE *group, const DOM_SID *sid)
+{
+       SAM_ASSERT(group);
+
+       if (!sid) 
+               ZERO_STRUCT(group->private.sid);
+       else 
+               sid_copy(&(group->private.sid), sid);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_group_group_ctrl(SAM_GROUP_HANDLE *group, uint32 group_ctrl)
+{
+       SAM_ASSERT(group);
+
+       group->private.group_ctrl = group_ctrl;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_group_name(SAM_GROUP_HANDLE *group, const char *group_name)
+{
+       SAM_ASSERT(group);
+
+       group->private.group_name = talloc_strdup(group->mem_ctx, group_name);
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_group_description(SAM_GROUP_HANDLE *group, const char *group_desc)
+{
+       SAM_ASSERT(group);
+
+       group->private.group_desc = talloc_strdup(group->mem_ctx, group_desc);
+
+       return NT_STATUS_OK;
+
+}
diff --git a/source4/sam/group.c b/source4/sam/group.c
new file mode 100644 (file)
index 0000000..101e3dd
--- /dev/null
@@ -0,0 +1,193 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM_GROUP_HANDLE /SAM_GROUP_ENUM helpers
+   
+   Copyright (C) Stefan (metze) Metzmacher     2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_GROUP_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_group(SAM_GROUP_HANDLE *group)
+{
+       ZERO_STRUCT(group->private); /* Don't touch the talloc context */
+
+}      
+
+static void destroy_sam_group_handle_talloc(SAM_GROUP_HANDLE **group) 
+{
+       if (*group) {
+
+               talloc_destroy((*group)->mem_ctx);
+               *group = NULL;
+       }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_GROUP_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_group_talloc(TALLOC_CTX *mem_ctx, SAM_GROUP_HANDLE **group)
+{
+       SMB_ASSERT(*group != NULL);
+
+       if (!mem_ctx) {
+               DEBUG(0,("sam_init_group_talloc: mem_ctx was NULL!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       *group=(SAM_GROUP_HANDLE *)talloc(mem_ctx, sizeof(SAM_GROUP_HANDLE));
+
+       if (*group==NULL) {
+               DEBUG(0,("sam_init_group_talloc: error while allocating memory\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*group)->mem_ctx = mem_ctx;
+
+       (*group)->free_fn = NULL;
+
+       sam_fill_default_group(*group);
+       
+       return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct SAM_GROUP_HANDLE.
+ ************************************************************/
+
+NTSTATUS sam_init_group(SAM_GROUP_HANDLE **group)
+{
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS nt_status;
+       
+       mem_ctx = talloc_init("sam internal SAM_GROUP_HANDLE allocation");
+
+       if (!mem_ctx) {
+               DEBUG(0,("sam_init_group: error while doing talloc_init()\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_init_group_talloc(mem_ctx, group))) {
+               talloc_destroy(mem_ctx);
+               return nt_status;
+       }
+       
+       (*group)->free_fn = destroy_sam_group_handle_talloc;
+
+       return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Reset the SAM_GROUP_HANDLE.
+ ***********************************************************/
+
+NTSTATUS sam_reset_group(SAM_GROUP_HANDLE *group)
+{
+       SMB_ASSERT(group != NULL);
+
+       sam_fill_default_group(group);
+
+       return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_GROUP_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_group(SAM_ACCOUNT_HANDLE **group)
+{
+       SMB_ASSERT(*group != NULL);
+
+       if ((*group)->free_fn) {
+               (*group)->free_fn(group);
+       }
+
+       return NT_STATUS_OK;    
+}
+
+
+/**********************************************************
+ Encode the group control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 group_ctrl, size_t length)
+{
+       static fstring group_str;
+       size_t i = 0;
+
+       group_str[i++] = '[';
+
+       if (group_ctrl & GCB_LOCAL_GROUP )      group_str[i++] = 'L';
+       if (group_ctrl & GCB_GLOBAL_GROUP )     group_str[i++] = 'G';
+
+       for ( ; i < length - 2 ; i++ )
+               group_str[i] = ' ';
+
+       i = length - 2;
+       group_str[i++] = ']';
+       group_str[i++] = '\0';
+
+       return group_str;
+}     
+
+/**********************************************************
+ Decode the group control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_group_ctrl(const char *p)
+{
+       uint16 group_ctrl = 0;
+       BOOL finished = False;
+
+       /*
+        * Check if the account type bits have been encoded after the
+        * NT password (in the form [NDHTUWSLXI]).
+        */
+
+       if (*p != '[')
+               return 0;
+
+       for (p++; *p && !finished; p++) {
+               switch (*p) {
+                       case 'L': { group_ctrl |= GCB_LOCAL_GROUP; break; /* 'L'ocal Aliases Group. */ } 
+                       case 'G': { group_ctrl |= GCB_GLOBAL_GROUP; break; /* 'G'lobal Domain Group. */ } 
+                       
+                       case ' ': { break; }
+                       case ':':
+                       case '\n':
+                       case '\0': 
+                       case ']':
+                       default:  { finished = True; }
+               }
+       }
+
+       return group_ctrl;
+}
+
diff --git a/source4/sam/gumm_tdb.c b/source4/sam/gumm_tdb.c
new file mode 100644 (file)
index 0000000..52eaab9
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Simo Sorce 2000-2002
+ * Copyright (C) Gerald Carter 2000
+ * Copyright (C) Jeremy Allison 2001
+ * Copyright (C) Andrew Bartlett 2002
+ * 
+ * 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 "gums.h"
+#include "tdbsam2.h"
+#include "tdbsam2_parse_info.h"
+
+static int tdbgumm_debug_level = DBGC_ALL;
+#undef DBGC_CLASS
+#define DBGC_CLASS tdbgumm_debug_level
+
+#define TDBSAM_VERSION         "20021215"
+#define TDB_FILE_NAME          "tdbsam2.tdb"
+#define DOMAINPREFIX           "DOMAIN_"
+#define OBJECTPREFIX           "OBJECT_"
+#define SIDPREFIX              "SID_"
+#define PRIVILEGEPREFIX                "PRIV_"
+
+#define TDB_FORMAT_STRING      "ddB"
+
+union tdbsam2_data {
+       struct tdbsam2_domain_data *domain;
+       struct tdbsam2_user_data *user;
+       struct tdbsam2_group_data *group;
+};
+
+struct tdbsam2_object {
+       uint32 type;
+       union tdbsam2_data data;
+};
+
+static TDB_CONTEXT *tdbsam2_db;
+
+#define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", __FUNCTION__)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define SET_OR_FAIL(func, label) do { if (NT_STATUS_IS_ERR(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", __FUNCTION__)); goto label; } } while(0)
+
+static NTSTATUS init_tdbsam2_object_from_buffer(struct tdbsam2_object *object, TALLOC_CTX *mem_ctx, char *buffer, int size) {
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_opentdb(void) {
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_get_object_by_name(struct tdbsam2_object *obj, TALLOC_CTX *mem_ctx, const char* name) {
+
+       NTSTATUS ret;
+       TDB_DATA data, key;
+       fstring keystr;
+       fstring objname;
+
+       if (!obj || !mem_ctx || !name)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (tdbsam2_db == NULL) {
+               if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) {
+                       goto done;
+               }
+       }
+
+       unix_strlower(name, -1, objname, sizeof(objname));
+
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", OBJECTPREFIX, objname);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       data = tdb_fetch(tdbsam2_db, key);
+       if (!data.dptr) {
+               DEBUG(5, ("get_domain_sid: Error fetching database, domain entry not found!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
+               DEBUGADD(5, (" Key: %s\n", keystr));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(obj, mem_ctx, data.dptr, data.dsize))) {
+               SAFE_FREE(data.dptr);
+               DEBUG(0, ("get_domain_sid: Error fetching database, malformed entry!\n"));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       SAFE_FREE(data.dptr);
+
+       ret = NT_STATUS_OK;
+
+done:
+       return ret;
+}
+
+
+static NTSTATUS tdbsam2_store(struct tdbsam2_object *object) {
+
+       NTSTATUS ret;
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_get_next_sid(TALLOC_CTX *mem_ctx, DOM_SID *sid) {
+
+       NTSTATUS ret;
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_user_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_user_data *userdata, uint32 type) {
+
+       NTSTATUS ret;
+
+       if (!object || !userdata) {
+               DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL pointers are accepted here!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* userdata->xcounter */
+       /* userdata->sec_desc */
+
+       SET_OR_FAIL(gums_set_object_sid(*object, userdata->user_sid), error);
+       SET_OR_FAIL(gums_set_object_name(*object, userdata->name), error);
+
+       SET_OR_FAIL(gums_set_user_pri_group(*object, userdata->group_sid), error);
+
+       if (userdata->description)
+               SET_OR_FAIL(gums_set_object_description(*object, userdata->description), error);
+
+       if (userdata->full_name)
+               SET_OR_FAIL(gums_set_user_fullname(*object, userdata->full_name), error);
+       
+       if (userdata->home_dir)
+               SET_OR_FAIL(gums_set_user_homedir(*object, userdata->home_dir), error);
+
+       if (userdata->dir_drive)
+               SET_OR_FAIL(gums_set_user_dir_drive(*object, userdata->dir_drive), error);
+
+       if (userdata->logon_script)
+               SET_OR_FAIL(gums_set_user_logon_script(*object, userdata->logon_script), error);
+       
+       if (userdata->profile_path) 
+               SET_OR_FAIL(gums_set_user_profile_path(*object, userdata->profile_path), error);
+
+       if (userdata->workstations)
+               SET_OR_FAIL(gums_set_user_workstations(*object, userdata->workstations), error);
+
+       if (userdata->unknown_str)
+               SET_OR_FAIL(gums_set_user_unknown_str(*object, userdata->unknown_str), error);
+
+       if (userdata->munged_dial)
+               SET_OR_FAIL(gums_set_user_munged_dial(*object, userdata->munged_dial), error);
+
+       SET_OR_FAIL(gums_set_user_logon_divs(*object, userdata->logon_divs), error);
+       SET_OR_FAIL(gums_set_user_hours_len(*object, userdata->hours_len), error);
+
+       if (userdata->hours)
+               SET_OR_FAIL(gums_set_user_hours(*object, userdata->hours), error);
+
+       SET_OR_FAIL(gums_set_user_unknown_3(*object, userdata->unknown_3), error);
+       SET_OR_FAIL(gums_set_user_unknown_5(*object, userdata->unknown_5), error);
+       SET_OR_FAIL(gums_set_user_unknown_6(*object, userdata->unknown_6), error);
+
+       SET_OR_FAIL(gums_set_user_logon_time(*object, userdata->logon_time), error);
+       SET_OR_FAIL(gums_set_user_logoff_time(*object, userdata->logoff_time), error);
+       SET_OR_FAIL(gums_set_user_kickoff_time(*object, userdata->kickoff_time), error);
+       SET_OR_FAIL(gums_set_user_pass_last_set_time(*object, userdata->pass_last_set_time), error);
+       SET_OR_FAIL(gums_set_user_pass_can_change_time(*object, userdata->pass_can_change_time), error);
+       SET_OR_FAIL(gums_set_user_pass_must_change_time(*object, userdata->pass_must_change_time), error);
+
+       ret = NT_STATUS_OK;
+       return ret;
+       
+error:
+       talloc_destroy((*object)->mem_ctx);
+       *object = NULL;
+       return ret;
+}
+
+static NTSTATUS tdbsam2_group_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_group_data *groupdata, uint32 type) {
+
+       NTSTATUS ret;
+
+       if (!object || !groupdata) {
+               DEBUG(0, ("tdbsam2_group_data_to_gums_object: no NULL pointers are accepted here!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* groupdata->xcounter */
+       /* groupdata->sec_desc */
+
+       SET_OR_FAIL(gums_set_object_sid(*object, groupdata->group_sid), error);
+       SET_OR_FAIL(gums_set_object_name(*object, groupdata->name), error);
+
+       if (groupdata->description)
+               SET_OR_FAIL(gums_set_object_description(*object, groupdata->description), error);
+
+       if (groupdata->count)
+               SET_OR_FAIL(gums_set_group_members(*object, groupdata->count, groupdata->members), error);
+
+       ret = NT_STATUS_OK;
+       return ret;
+       
+error:
+       talloc_destroy((*object)->mem_ctx);
+       *object = NULL;
+       return ret;
+}
+
+static NTSTATUS tdbsam2_domain_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_domain_data *domdata, uint32 type) {
+
+       NTSTATUS ret;
+
+       if (!object || !domdata) {
+               DEBUG(0, ("tdbsam2_domain_data_to_gums_object: no NULL pointers are accepted here!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* domdata->xcounter */
+       /* domdata->sec_desc */
+
+       SET_OR_FAIL(gums_set_object_sid(*object, domdata->dom_sid), error);
+       SET_OR_FAIL(gums_set_object_name(*object, domdata->name), error);
+
+       if (domdata->description)
+               SET_OR_FAIL(gums_set_object_description(*object, domdata->description), error);
+
+       ret = NT_STATUS_OK;
+       return ret;
+       
+error:
+       talloc_destroy((*object)->mem_ctx);
+       *object = NULL;
+       return ret;
+}
+
+static NTSTATUS tdbsam2_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_object *data) {
+
+       NTSTATUS ret;
+
+       if (!object || !data) {
+               DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL structure pointers are accepted here!\n"));
+               ret = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+
+       ret = gums_create_object(object, data->type);
+       if (NT_STATUS_IS_ERR(ret)) {
+               DEBUG(5, ("tdbsam2_user_data_to_gums_object: error creating gums object!\n"));
+               goto done;
+       }
+
+       switch (data->type) {
+               case GUMS_OBJ_DOMAIN:
+                       ret = tdbsam2_domain_data_to_gums_object(object, data->data.domain, data->type);
+                       break;
+
+               case GUMS_OBJ_NORMAL_USER:
+                       ret = tdbsam2_user_data_to_gums_object(object, data->data.user, data->type);
+                       break;
+
+               case GUMS_OBJ_GROUP:
+               case GUMS_OBJ_ALIAS:
+                       ret = tdbsam2_group_data_to_gums_object(object, data->data.group, data->type);
+                       break;
+
+               default:
+                       ret = NT_STATUS_UNSUCCESSFUL;
+       }
+
+done:
+       return ret;
+}
+
+
+
+
+
+/* GUMM object functions */
+
+static NTSTATUS get_domain_sid(DOM_SID *sid, const char* name) {
+
+       NTSTATUS ret;
+       struct tdbsam2_object obj;
+       TALLOC_CTX *mem_ctx;
+       TDB_DATA data, key;
+       fstring keystr;
+       fstring domname;
+
+       if (!sid || !name)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       mem_ctx = talloc_init("get_domain_sid");
+       if (!mem_ctx) {
+               DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (tdbsam2_db == NULL) {
+               if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) {
+                       goto done;
+               }
+       }
+
+       unix_strlower(name, -1, domname, sizeof(domname));
+
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", DOMAINPREFIX, domname);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       data = tdb_fetch(tdbsam2_db, key);
+       if (!data.dptr) {
+               DEBUG(5, ("get_domain_sid: Error fetching database, domain entry not found!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
+               DEBUGADD(5, (" Key: %s\n", keystr));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(&obj, mem_ctx, data.dptr, data.dsize))) {
+               SAFE_FREE(data.dptr);
+               DEBUG(0, ("get_domain_sid: Error fetching database, malformed entry!\n"));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+       SAFE_FREE(data.dptr);
+
+       if (obj.type != GUMS_OBJ_DOMAIN) {
+               DEBUG(5, ("get_domain_sid: Requested object is not a domain!\n"));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       sid_copy(sid, obj.data.domain->dom_sid);
+
+       ret = NT_STATUS_OK;
+
+done:
+       if (mem_ctx) talloc_destroy(mem_ctx);
+       return ret;
+}
+
+       NTSTATUS (*set_domain_sid) (const DOM_SID *sid, const char *name);
+
+       NTSTATUS (*get_sequence_number) (void);
+
+
+static NTSTATUS tdbsam2_new_object(DOM_SID **sid, const char *name, const int obj_type) {
+
+       NTSTATUS ret;
+       struct tdbsam2_object obj;
+       TALLOC_CTX *mem_ctx;
+
+       if (!sid || !name) {
+               DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       mem_ctx = talloc_init("tdbsam2_new_object");
+       if (!mem_ctx) {
+               DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       switch (obj_type) {
+               case GUMS_OBJ_NORMAL_USER:
+                       obj.data.user = (struct tdbsam2_user_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_user_data));
+                       TALLOC_CHECK(obj.data.user, ret, done);
+
+                       /*obj.data.user->sec_desc*/
+
+                       tdbsam2_get_next_sid(mem_ctx, obj.data.user->user_sid);
+                       TALLOC_CHECK(obj.data.user->user_sid, ret, done);
+
+                       obj.data.user->name = talloc_strdup(mem_ctx, name);
+                       TALLOC_CHECK(obj.data.user, ret, done);
+
+                       break;
+
+               case GUMS_OBJ_GROUP:
+               case GUMS_OBJ_ALIAS:
+                       obj.data.group = (struct tdbsam2_group_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_group_data));
+                       TALLOC_CHECK(obj.data.group, ret, done);
+
+                       /*obj.data.user->sec_desc*/
+
+                       tdbsam2_get_next_sid(mem_ctx, obj.data.group->group_sid);
+                       TALLOC_CHECK(obj.data.group->group_sid, ret, done);
+
+                       obj.data.group->name = talloc_strdup(mem_ctx, name);
+                       TALLOC_CHECK(obj.data.group, ret, done);
+
+                       break;
+
+               case GUMS_OBJ_DOMAIN:
+                       /* TODO: SHOULD WE ALLOW TO CREATE NEW DOMAINS ? */
+
+               default:
+                       ret = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+       }
+
+       ret = tdbsam2_store(&obj);
+
+done:
+       talloc_destroy(mem_ctx);
+       return ret;
+}
+
+static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid) {
+
+       NTSTATUS ret;
+       struct tdbsam2_object obj;
+       TALLOC_CTX *mem_ctx;
+       TDB_DATA data, key;
+       fstring keystr;
+       fstring sidstr;
+       char *obj_name = NULL;
+       int obj_type, obj_version, len;
+
+       if (!sid) {
+               DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       mem_ctx = talloc_init("tdbsam2_delete_object");
+       if (!mem_ctx) {
+               DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (tdbsam2_db == NULL) {
+               if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) {
+                       goto done;
+               }
+       }
+
+       sid_to_string(sidstr, sid);
+
+       slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sidstr);
+       key.dptr = keystr;
+       key.dsize = strlen(keystr) + 1;
+
+       data = tdb_fetch(tdbsam2_db, key);
+       if (!data.dptr) {
+               DEBUG(5, ("get_domain_sid: Error fetching database, SID entry not found!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
+               DEBUGADD(5, (" Key: %s\n", keystr));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       len = tdb_unpack(data.dptr, data.dsize, TDB_FORMAT_STRING,
+               &obj_version,
+               &obj_type,
+               &obj_name);
+
+       if (len == -1) {
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }
+
+       if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) {
+               DEBUG(5, ("tdbsam2_object_delete: Error deleting object!\n"));
+               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
+               DEBUGADD(5, (" Key: %s\n", keystr));
+               ret = NT_STATUS_UNSUCCESSFUL;
+               goto done;
+       }       
+
+       switch (obj_type) {
+               case GUMS_OBJ_NORMAL_USER:
+               case GUMS_OBJ_GROUP:
+               case GUMS_OBJ_ALIAS:
+                       
+                       slprintf(keystr, sizeof(keystr)-1, "%s%s", OBJECTPREFIX, obj_name);
+                       key.dptr = keystr;
+                       key.dsize = strlen(keystr) + 1;
+
+                       if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) {
+                               DEBUG(5, ("tdbsam2_object_delete: Error deleting object!\n"));
+                               DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
+                               DEBUGADD(5, (" Key: %s\n", keystr));
+                               ret = NT_STATUS_UNSUCCESSFUL;
+                               goto done;
+                       }
+                       break;
+
+               case GUMS_OBJ_DOMAIN:
+                       /* TODO: SHOULD WE ALLOW TO DELETE DOMAINS ? */
+
+               default:
+                       ret = NT_STATUS_UNSUCCESSFUL;
+                       goto done;
+       }
+
+done:
+       SAFE_FREE(obj_name);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
+
+       NTSTATUS (*get_object_from_sid) (GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type);
+       NTSTATUS (*get_sid_from_name) (GUMS_OBJECT **object, const char *name);
+       /* This function is used to get the list of all objects changed since b_time, it is
+          used to support PDC<->BDC synchronization */
+       NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+       NTSTATUS (*enumerate_objects_start) (void *handle, const DOM_SID *sid, const int obj_type);
+       NTSTATUS (*enumerate_objects_get_next) (GUMS_OBJECT **object, void *handle);
+       NTSTATUS (*enumerate_objects_stop) (void *handle);
+
+       /* This function MUST be used ONLY by PDC<->BDC replication code or recovery tools.
+          Never use this function to update an object in the database, use set_object_values() */
+       NTSTATUS (*set_object) (const GUMS_OBJECT *object);
+
+       /* set object values function */
+       NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+       /* Group related functions */
+       NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
+       NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+       NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+       NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+       NTSTATUS (*lock_sid) (const DOM_SID *sid);
+       NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+       /* privileges related functions */
+
+       NTSTATUS (*add_members_to_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
+       NTSTATUS (*delete_members_from_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
+       NTSTATUS (*enumerate_privilege_members) (DOM_SID **members, const LUID_ATTR *priv);
+       NTSTATUS (*get_sid_privileges) (DOM_SID **privs, const DOM_SID *sid);
+       /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+       NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+
+
+int gumm_init(GUMS_FUNCTIONS **storage) {
+
+       return 0;
+}
diff --git a/source4/sam/gums.c b/source4/sam/gums.c
new file mode 100644 (file)
index 0000000..3a20ef6
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+   Unix SMB/CIFS implementation.
+   Grops and Users Management System initializations.
+   Copyright (C) Simo Sorce 2002
+
+   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"
+
+/*#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_GUMS*/
+
+#define GMV_MAJOR 0
+#define GMV_MINOR 1
+
+GUMS_FUNCTIONS *gums_storage;
+static void *dl_handle;
+
+PRIVS privs[] = {
+       {PRIV_NONE,                     "no_privs",                             "No privilege"}, /* this one MUST be first */
+       {PRIV_CREATE_TOKEN,             "SeCreateToken",                        "Create Token"},
+       {PRIV_ASSIGNPRIMARYTOKEN,       "SeAssignPrimaryToken",                 "Assign Primary Token"},
+       {PRIV_LOCK_MEMORY,              "SeLockMemory",                         "Lock Memory"},
+       {PRIV_INCREASE_QUOTA,           "SeIncreaseQuotaPrivilege",             "Increase Quota Privilege"},
+       {PRIV_MACHINE_ACCOUNT,          "SeMachineAccount",                     "Machine Account"},
+       {PRIV_TCB,                      "SeTCB",                                "TCB"},
+       {PRIV_SECURITY,                 "SeSecurityPrivilege",                  "Security Privilege"},
+       {PRIV_TAKE_OWNERSHIP,           "SeTakeOwnershipPrivilege",             "Take Ownership Privilege"},
+       {PRIV_LOAD_DRIVER,              "SeLocalDriverPrivilege",               "Local Driver Privilege"},
+       {PRIV_SYSTEM_PROFILE,           "SeSystemProfilePrivilege",             "System Profile Privilege"},
+       {PRIV_SYSTEMTIME,               "SeSystemtimePrivilege",                "System Time"},
+       {PRIV_PROF_SINGLE_PROCESS,      "SeProfileSingleProcessPrivilege",      "Profile Single Process Privilege"},
+       {PRIV_INC_BASE_PRIORITY,        "SeIncreaseBasePriorityPrivilege",      "Increase Base Priority Privilege"},
+       {PRIV_CREATE_PAGEFILE,          "SeCreatePagefilePrivilege",            "Create Pagefile Privilege"},
+       {PRIV_CREATE_PERMANENT,         "SeCreatePermanent",                    "Create Permanent"},
+       {PRIV_BACKUP,                   "SeBackupPrivilege",                    "Backup Privilege"},
+       {PRIV_RESTORE,                  "SeRestorePrivilege",                   "Restore Privilege"},
+       {PRIV_SHUTDOWN,                 "SeShutdownPrivilege",                  "Shutdown Privilege"},
+       {PRIV_DEBUG,                    "SeDebugPrivilege",                     "Debug Privilege"},
+       {PRIV_AUDIT,                    "SeAudit",                              "Audit"},
+       {PRIV_SYSTEM_ENVIRONMENT,       "SeSystemEnvironmentPrivilege",         "System Environment Privilege"},
+       {PRIV_CHANGE_NOTIFY,            "SeChangeNotify",                       "Change Notify"},
+       {PRIV_REMOTE_SHUTDOWN,          "SeRemoteShutdownPrivilege",            "Remote Shutdown Privilege"},
+       {PRIV_UNDOCK,                   "SeUndock",                             "Undock"},
+       {PRIV_SYNC_AGENT,               "SeSynchronizationAgent",               "Synchronization Agent"},
+       {PRIV_ENABLE_DELEGATION,        "SeEnableDelegation",                   "Enable Delegation"},
+       {PRIV_ALL,                      "SaAllPrivs",                           "All Privileges"}
+};
+
+NTSTATUS gums_init(const char *module_name)
+{
+       int (*module_version)(int);
+       NTSTATUS (*module_init)();
+/*     gums_module_init module_init;*/
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+       DEBUG(5, ("Opening gums module %s\n", module_name));
+       dl_handle = sys_dlopen(module_name, RTLD_NOW);
+       if (!dl_handle) {
+               DEBUG(0, ("ERROR: Failed to load gums module %s, error: %s\n", module_name, sys_dlerror()));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       module_version = sys_dlsym(dl_handle, "gumm_version");
+       if (!module_version) {
+               DEBUG(0, ("ERROR: Failed to find gums module version!\n"));
+               goto error;
+       }
+
+       if (module_version(GMV_MAJOR) != GUMS_VERSION_MAJOR) {
+               DEBUG(0, ("ERROR: Module's major version does not match gums version!\n"));
+               goto error;
+       }
+
+       if (module_version(GMV_MINOR) != GUMS_VERSION_MINOR) {
+               DEBUG(1, ("WARNING: Module's minor version does not match gums version!\n"));
+       }
+
+       module_init = sys_dlsym(dl_handle, "gumm_init");
+       if (!module_init) {
+               DEBUG(0, ("ERROR: Failed to find gums module's init function!\n"));
+               goto error;
+       }
+
+       DEBUG(5, ("Initializing module %s\n", module_name));
+
+       ret = module_init(&gums_storage);
+       goto done;
+
+error:
+       ret = NT_STATUS_UNSUCCESSFUL;
+       sys_dlclose(dl_handle);
+
+done:
+       return ret;
+}
+
+NTSTATUS gums_unload(void)
+{
+       NSTATUS ret;
+       NTSTATUS (*module_finalize)();
+
+       if (!dl_handle)
+               return NT_STATUS_UNSUCCESSFUL;
+
+       module_close = sys_dlsym(dl_handle, "gumm_finalize");
+       if (!module_finalize) {
+               DEBUG(0, ("ERROR: Failed to find gums module's init function!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       DEBUG(5, ("Finalizing module %s\n", module_name));
+
+       ret = module_finalize();
+       sys_dlclose(dl_handle);
+
+       return ret;
+}
diff --git a/source4/sam/gums_api.c b/source4/sam/gums_api.c
new file mode 100644 (file)
index 0000000..75e32fa
--- /dev/null
@@ -0,0 +1,1268 @@
+/* 
+   Unix SMB/CIFS implementation.
+   GUMS structures
+   Copyright (C) Simo Sorce 2002
+   
+   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"
+
+extern GUMS_FUNCTIONS *gums_storage;
+
+/* Functions to get/set info from a GUMS object */
+
+NTSTATUS gums_get_object_type(uint32 *type, const GUMS_OBJECT *obj)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *type = obj->type;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_create_object(GUMS_OBJECT **obj, uint32 type)
+{
+       TALLOC_CTX *mem_ctx = talloc_init("gums_create_object");
+       GUMS_OBJECT *go;
+       NT_STATUS ret;
+       
+       go = talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+       go->mem_ctx = mem_ctx;
+       go->type = type;
+       go->version = GUMS_OBJECT_VERSION;
+
+       switch(type) {
+               case GUMS_OBJ_DOMAIN:
+                       break;
+
+/*
+               case GUMS_OBJ_WORKSTATION_TRUST:
+               case GUMS_OBJ_SERVER_TRUST:
+               case GUMS_OBJ_DOMAIN_TRUST:
+*/
+               case GUMS_OBJ_NORMAL_USER:
+                       go->data = (GUMS_USER *)talloc_zero(mem_ctx, sizeof(GUMS_USER));
+                       break;
+
+               case GUMS_OBJ_GROUP:
+               case GUMS_OBJ_ALIAS:
+                       go->data = (GUMS_GROUP *)talloc_zero(mem_ctx, sizeof(GUMS_GROUP));
+                       break;
+
+               default:
+                       /* TODO: throw error */
+                       ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+                       goto error;
+       }
+
+       if (!(go->data)) {
+               ret = NT_STATUS_NO_MEMORY;
+               DEBUG(0, ("gums_create_object: Out of memory!\n"));
+               goto error;
+       }
+
+       *obj = go;
+       return NT_STATUS_OK;
+       
+error:
+       talloc_destroy(go->mem_ctx);
+       *obj = NULL;
+       return ret;
+}
+
+NTSTATUS gums_get_object_seq_num(uint32 *version, const GUMS_OBJECT *obj)
+{
+       if (!version || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *version = obj->version;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_seq_num(GUMS_OBJECT *obj, uint32 version)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       obj->version = version;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_sec_desc(SEC_DESC **sec_desc, const GUMS_OBJECT *obj)
+{
+       if (!sec_desc || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *sec_desc = obj->sec_desc;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_sec_desc(GUMS_OBJECT *obj, const SEC_DESC *sec_desc)
+{
+       if (!obj || !sec_desc)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       obj->sec_desc = dup_sec_desc(obj->mem_ctx, sec_desc);
+       if (!(obj->sec_desc)) return NT_STATUS_UNSUCCESSFUL;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_object_sid(DOM_SID **sid, const GUMS_OBJECT *obj)
+{
+       if (!sid || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *sid = obj->sid;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_sid(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+       if (!obj || !sid)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       obj->sid = sid_dup_talloc(obj->mem_ctx, sid);
+       if (!(obj->sid)) return NT_STATUS_UNSUCCESSFUL;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_object_name(char **name, const GUMS_OBJECT *obj)
+{
+       if (!name || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *name = obj->name;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_name(GUMS_OBJECT *obj, const char *name)
+{
+       if (!obj || !name)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       obj->name = (char *)talloc_strdup(obj->mem_ctx, name);
+       if (!(obj->name)) return NT_STATUS_UNSUCCESSFUL;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_object_description(char **description, const GUMS_OBJECT *obj)
+{
+       if (!description || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *description = obj->description;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_description(GUMS_OBJECT *obj, const char *description)
+{
+       if (!obj || !description)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       obj->description = (char *)talloc_strdup(obj->mem_ctx, description);
+       if (!(obj->description)) return NT_STATUS_UNSUCCESSFUL;
+       return NT_STATUS_OK;
+}
+
+/* User specific functions */
+
+/*
+NTSTATUS gums_get_object_privileges(PRIVILEGE_SET **priv_set, const GUMS_OBJECT *obj)
+{
+       if (!priv_set)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *priv_set = obj->priv_set;
+       return NT_STATUS_OK;
+}
+*/
+
+NTSTATUS gums_get_user_pri_group(DOM_SID **sid, const GUMS_OBJECT *obj)
+{
+       if (!sid || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *sid = obj->data.user->group_sid;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pri_group(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+       if (!obj || !sid)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->group_sid = sid_dup_talloc(obj->mem_ctx, sid);
+       if (!(obj->data.user->group_sid)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_nt_pwd(DATA_BLOB **nt_pwd, const GUMS_OBJECT *obj)
+{
+       if (!nt_pwd || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *nt_pwd = obj->data.user->nt_pw;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_nt_pwd(GUMS_OBJECT *obj, const DATA_BLOB nt_pwd)
+{
+       if (!obj || !nt_pwd || nt_pwd != NT_HASH_LEN)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->nt_pwd = data_blob_talloc(obj->mem_ctx, nt_pwd.data, nt_pwd.lenght);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_lm_pwd(DATA_BLOB **lm_pwd, const GUMS_OBJECT *obj)
+{ 
+       if (!lm_pwd || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *lm_pwd = obj->data.user->lm_pw;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_lm_pwd(GUMS_OBJECT *obj, const DATA_BLOB lm_pwd)
+{
+       if (!obj || !lm_pwd || lm_pwd != LM_HASH_LEN)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->lm_pwd = data_blob_talloc(obj->mem_ctx, lm_pwd.data, lm_pwd.lenght);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_fullname(char **fullname, const GUMS_OBJECT *obj)
+{
+       if (!fullname || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *fullname = obj->data.user->full_name;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_fullname(GUMS_OBJECT *obj, const char *fullname)
+{
+       if (!obj || !fullname)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->full_name = (char *)talloc_strdup(obj->mem_ctx, fullname);
+       if (!(obj->data.user->full_name)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_homedir(char **homedir, const GUMS_OBJECT *obj)
+{
+       if (!homedir || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *homedir = obj->data.user->home_dir;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_homedir(GUMS_OBJECT *obj, const char *homedir)
+{
+       if (!obj || !homedir)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->home_dir = (char *)talloc_strdup(obj->mem_ctx, homedir);
+       if (!(obj->data.user->home_dir)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_dir_drive(char **dirdrive, const GUMS_OBJECT *obj)
+{
+       if (!dirdrive || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *dirdrive = obj->data.user->dir_drive;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_dir_drive(GUMS_OBJECT *obj, const char *dir_drive)
+{
+       if (!obj || !dir_drive)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->dir_drive = (char *)talloc_strdup(obj->mem_ctx, dir_drive);
+       if (!(obj->data.user->dir_drive)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_logon_script(char **logon_script, const GUMS_OBJECT *obj)
+{
+       if (!logon_script || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *logon_script = obj->data.user->logon_script;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_script(GUMS_OBJECT *obj, const char *logon_script)
+{
+       if (!obj || !logon_script)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->logon_script = (char *)talloc_strdup(obj->mem_ctx, logon_script);
+       if (!(obj->data.user->logon_script)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_profile_path(char **profile_path, const GUMS_OBJECT *obj)
+{
+       if (!profile_path || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *profile_path = obj->data.user->profile_path;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_profile_path(GUMS_OBJECT *obj, const char *profile_path)
+{
+       if (!obj || !profile_path)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->profile_path = (char *)talloc_strdup(obj->mem_ctx, profile_path);
+       if (!(obj->data.user->profile_path)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_workstations(char **workstations, const GUMS_OBJECT *obj)
+{
+       if (!workstations || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *workstations = obj->data.user->workstations;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_workstations(GUMS_OBJECT *obj, const char *workstations)
+{
+       if (!obj || !workstations)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->workstations = (char *)talloc_strdup(obj->mem_ctx, workstations);
+       if (!(obj->data.user->workstations)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_unknown_str(char **unknown_str, const GUMS_OBJECT *obj)
+{
+       if (!unknown_str || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *unknown_str = obj->data.user->unknown_str;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_str(GUMS_OBJECT *obj, const char *unknown_str)
+{
+       if (!obj || !unknown_str)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->unknown_str = (char *)talloc_strdup(obj->mem_ctx, unknown_str);
+       if (!(obj->data.user->unknown_str)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_munged_dial(char **munged_dial, const GUMS_OBJECT *obj)
+{
+       if (!munged_dial || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *munged_dial = obj->data.user->munged_dial;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_munged_dial(GUMS_OBJECT *obj, const char *munged_dial)
+{
+       if (!obj || !munged_dial)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->munged_dial = (char *)talloc_strdup(obj->mem_ctx, munged_dial);
+       if (!(obj->data.user->munged_dial)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_logon_time(NTTIME *logon_time, const GUMS_OBJECT *obj)
+{
+       if (!logon_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *logon_time = obj->data.user->logon_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_time(GUMS_OBJECT *obj, NTTIME logon_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->logon_time = logon_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_logoff_time(NTTIME *logoff_time, const GUMS_OBJECT *obj)
+{
+       if (!logoff_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *logoff_time = obj->data.user->logoff_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logoff_time(GUMS_OBJECT *obj, NTTIME logoff_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->logoff_time = logoff_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_kickoff_time(NTTIME *kickoff_time, const GUMS_OBJECT *obj)
+{
+       if (!kickoff_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *kickoff_time = obj->data.user->kickoff_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_kickoff_time(GUMS_OBJECT *obj, NTTIME kickoff_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->kickoff_time = kickoff_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_pass_last_set_time(NTTIME *pass_last_set_time, const GUMS_OBJECT *obj)
+{
+       if (!pass_last_set_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *pass_last_set_time = obj->data.user->pass_last_set_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_last_set_time(GUMS_OBJECT *obj, NTTIME pass_last_set_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->pass_last_set_time = pass_last_set_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_pass_can_change_time(NTTIME *pass_can_change_time, const GUMS_OBJECT *obj)
+{
+       if (!pass_can_change_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *pass_can_change_time = obj->data.user->pass_can_change_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_can_change_time(GUMS_OBJECT *obj, NTTIME pass_can_change_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->pass_can_change_time = pass_can_change_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_pass_must_change_time(NTTIME *pass_must_change_time, const GUMS_OBJECT *obj)
+{
+       if (!pass_must_change_time || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *pass_must_change_time = obj->data-user->pass_must_change_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_must_change_time(GUMS_OBJECT *obj, NTTIME pass_must_change_time)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->pass_must_change_time = pass_must_change_time;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_logon_divs(uint16 *logon_divs, const GUMS_OBJECT *obj)
+{
+       if (!logon_divs || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *logon_divs = obj->data.user->logon_divs;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_divs(GUMS_OBJECT *obj, uint16 logon_divs)
+{
+       if (!obj || !logon_divs)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->logon_divs = logon_divs;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_hours_len(uint32 *hours_len, const GUMS_OBJECT *obj)
+{
+       if (!hours_len || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *hours_len = obj->data.user->hours_len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_hours_len(GUMS_OBJECT *obj, uint32 hours_len)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->hours_len = hours_len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_hours(uint8 **hours, const GUMS_OBJECT *obj)
+{
+       if (!hours || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *hours = obj->data.user->hours;
+       return NT_STATUS_OK;
+}
+
+/* WARNING: always set hours_len before hours */
+NTSTATUS gums_set_user_hours(GUMS_OBJECT *obj, const uint8 *hours)
+{
+       if (!obj || !hours)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       if (obj->data.user->hours_len == 0)
+               DEBUG(10, ("gums_set_user_hours: Warning, hours_len is zero!\n"));
+
+       obj->data.user->hours = (uint8 *)talloc_memdup(obj->mem_ctx, hours, obj->data.user->hours_len);
+       if (!(obj->data.user->hours) & (obj->data.user->hours_len != 0)) return NT_STATUS_NO_MEMORY;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_unknown_3(uint32 *unknown_3, const GUMS_OBJECT *obj)
+{
+       if (!unknown_3 || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *unknown_3 = obj->data.user->unknown_3;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_3(GUMS_OBJECT *obj, uint32 unknown_3)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->unknown_3 = unknown_3;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_unknown_5(uint32 *unknown_5, const GUMS_OBJECT *obj)
+{
+       if (!unknown_5 || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *unknown_5 = obj->data.user->unknown_5;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_5(GUMS_OBJECT *obj, uint32 unknown_5)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->unknown_5 = unknown_5;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_get_user_unknown_6(uint32 *unknown_6, const GUMS_OBJECT *obj)
+{
+       if (!unknown_6 || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *unknown_6 = obj->data.user->unknown_6;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_6(GUMS_OBJECT *obj, uint32 unknown_6)
+{
+       if (!obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.user->unknown_6 = unknown_6;
+       return NT_STATUS_OK;
+}
+
+/* Group specific functions */
+
+NTSTATUS gums_get_group_members(uint32 *count, DOM_SID **members, const GUMS_OBJECT *obj)
+{
+       if (!count || !members || !obj)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_GROUP &&
+               obj->type != GUMS_OBJ_ALIAS)
+                       return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       *count = obj->data.group->count;
+       *members = obj->data.group->members;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_group_members(GUMS_OBJECT *obj, uint32 count, DOM_SID **members)
+{
+       uint32 n;
+
+       if (!obj || !members || !members)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       if (obj->type != GUMS_OBJ_GROUP &&
+               obj->type != GUMS_OBJ_ALIAS)
+                       return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+       obj->data.group->count = count;
+       n = 0;
+       do {
+               obj->data.group->members[n] = dup_sec_desc(obj->mem_ctx, members[n]);
+               if (!(obj->data.group->members[n])) return NT_STATUS_NO_MEMORY;
+               n++;
+       } while (n < count);
+       return NT_STATUS_OK;
+}
+
+/* data_store set functions */
+
+NTSTATUS gums_create_commit_set(GUMS_COMMIT_SET **com_set, TALLOC_CTX *ctx, DOM_SID *sid, uint32 type)
+{
+       TALLOC_CTX *mem_ctx;
+       GUMS_COMMIT_SET *set;
+
+       mem_ctx = talloc_init("commit_set");
+       if (mem_ctx == NULL)
+               return NT_STATUS_NO_MEMORY;
+       set = (GUMS_COMMIT_SET *)talloc(mem_ctx, sizeof(GUMS_COMMIT_SET));
+       if (set == NULL) {
+               talloc_destroy(mem_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       set->mem_ctx = mem_ctx;
+       set->type = type;
+       sid_copy(&(set->sid), sid);
+       set->count = 0;
+       set->data = NULL;
+       *com_set = set;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_sec_desc(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, SEC_DESC *sec_desc)
+{
+       GUMS_DATA_SET *data_set;
+       SEC_DESC *new_sec_desc;
+
+       if (!mem_ctx || !com_set || !sec_desc)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_SET_SEC_DESC;
+       new_sec_desc = dup_sec_desc(mem_ctx, sec_desc);
+       if (new_sec_desc == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       (SEC_DESC *)(data_set->data) = new_sec_desc;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_add_privilege(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+       GUMS_DATA_SET *data_set;
+       LUID_ATTR *new_priv;
+
+       if (!mem_ctx || !com_set)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_ADD_PRIVILEGE;
+       if (NT_STATUS_IS_ERR(dupalloc_luid_attr(mem_ctx, &new_priv, priv)))
+               return NT_STATUS_NO_MEMORY;
+
+       (SEC_DESC *)(data_set->data) = new_priv;
+
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS gums_cs_del_privilege(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+       GUMS_DATA_SET *data_set;
+       LUID_ATTR *new_priv;
+
+       if (!mem_ctx || !com_set)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_DEL_PRIVILEGE;
+       if (NT_STATUS_IS_ERR(dupalloc_luid_attr(mem_ctx, &new_priv, priv)))
+               return NT_STATUS_NO_MEMORY;
+
+       (SEC_DESC *)(data_set->data) = new_priv;
+
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS gums_cs_set_privilege_set(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, PRIVILEGE_SET *priv_set)
+{
+       GUMS_DATA_SET *data_set;
+       PRIVILEGE_SET *new_priv_set;
+
+       if (!mem_ctx || !com_set || !priv_set)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_SET_SEC_DESC;
+       if (NT_STATUS_IS_ERR(dup_priv_set(&new_priv_set, mem_ctx, priv_set)))
+               return NT_STATUS_NO_MEMORY;
+
+       (SEC_DESC *)(data_set->data) = new_priv_set;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_string(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, uint32 type, char *str)
+{
+       GUMS_DATA_SET *data_set;
+       char *new_str;
+
+       if (!mem_ctx || !com_set || !str || type < GUMS_SET_NAME || type > GUMS_SET_MUNGED_DIAL)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = type;
+       new_str = talloc_strdup(mem_ctx, str);
+       if (new_str == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       (char *)(data_set->data) = new_str;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_name(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *name)
+{
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, name);
+}
+
+NTSTATUS gums_cs_set_description(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *desc)
+{
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_DESCRIPTION, desc);
+}
+
+NTSTATUS gums_cs_set_full_name(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *full_name)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, full_name);
+}
+
+NTSTATUS gums_cs_set_home_directory(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *home_dir)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, home_dir);
+}
+
+NTSTATUS gums_cs_set_drive(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *drive)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, drive);
+}
+
+NTSTATUS gums_cs_set_logon_script(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *logon_script)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, logon_script);
+}
+
+NTSTATUS gums_cs_set_profile_path(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *prof_path)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, prof_path);
+}
+
+NTSTATUS gums_cs_set_workstations(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *wks)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, wks);
+}
+
+NTSTATUS gums_cs_set_unknown_string(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *unkn_str)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, unkn_str);
+}
+
+NTSTATUS gums_cs_set_munged_dial(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *munged_dial)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, munged_dial);
+}
+
+NTSTATUS gums_cs_set_nttime(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, uint32 type, NTTIME *nttime)
+{
+       GUMS_DATA_SET *data_set;
+       NTTIME *new_time;
+
+       if (!mem_ctx || !com_set || !nttime || type < GUMS_SET_LOGON_TIME || type > GUMS_SET_PASS_MUST_CHANGE_TIME)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = type;
+       new_time = talloc(mem_ctx, sizeof(NTTIME));
+       if (new_time == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       new_time->low = nttime->low;
+       new_time->high = nttime->high;
+       (char *)(data_set->data) = new_time;
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_logon_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *logon_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, logon_time);
+}
+
+NTSTATUS gums_cs_set_logoff_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *logoff_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGOFF_TIME, logoff_time);
+}
+
+NTSTATUS gums_cs_set_kickoff_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *kickoff_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_KICKOFF_TIME, kickoff_time);
+}
+
+NTSTATUS gums_cs_set_pass_last_set_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pls_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pls_time);
+}
+
+NTSTATUS gums_cs_set_pass_can_change_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pcc_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pcc_time);
+}
+
+NTSTATUS gums_cs_set_pass_must_change_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pmc_time)
+{
+       if (com_set->type != GUMS_OBJ_NORMAL_USER)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pmc_time);
+}
+
+NTSTATUS gums_cs_add_sids_to_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+       GUMS_DATA_SET *data_set;
+       DOM_SID **new_sids;
+       int i;
+
+       if (!mem_ctx || !com_set || !sids)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_ADD_SID_LIST;
+       new_sids = (DOM_SID **)talloc(mem_ctx, (sizeof(void *) * count));
+       if (new_sids == NULL)
+               return NT_STATUS_NO_MEMORY;
+       for (i = 0; i < count; i++) {
+               new_sids[i] = sid_dup_talloc(mem_ctx, sids[i]);
+               if (new_sids[i] == NULL)
+                       return NT_STATUS_NO_MEMORY;
+       }
+
+       (SEC_DESC *)(data_set->data) = new_sids;
+
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS gums_cs_add_users_to_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+       if (!mem_ctx || !com_set || !sids)
+               return NT_STATUS_INVALID_PARAMETER;
+       if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_add_sids_to_group(mem_ctx, com_set, sids, count);   
+}
+
+NTSTATUS gums_cs_add_groups_to_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+       if (!mem_ctx || !com_set || !sids)
+               return NT_STATUS_INVALID_PARAMETER;
+       if (com_set->type != GUMS_OBJ_ALIAS)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       return gums_add_sids_to_group(mem_ctx, com_set, sids, count);   
+}
+
+NTSTATUS gums_cs_del_sids_from_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+       GUMS_DATA_SET *data_set;
+       DOM_SID **new_sids;
+       int i;
+
+       if (!mem_ctx || !com_set || !sids)
+               return NT_STATUS_INVALID_PARAMETER;
+       if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_DEL_SID_LIST;
+       new_sids = (DOM_SID **)talloc(mem_ctx, (sizeof(void *) * count));
+       if (new_sids == NULL)
+               return NT_STATUS_NO_MEMORY;
+       for (i = 0; i < count; i++) {
+               new_sids[i] = sid_dup_talloc(mem_ctx, sids[i]);
+               if (new_sids[i] == NULL)
+                       return NT_STATUS_NO_MEMORY;
+       }
+
+       (SEC_DESC *)(data_set->data) = new_sids;
+
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS gums_ds_set_sids_in_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+       GUMS_DATA_SET *data_set;
+       DOM_SID **new_sids;
+       int i;
+
+       if (!mem_ctx || !com_set || !sids)
+               return NT_STATUS_INVALID_PARAMETER;
+       if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+               return NT_STATUS_INVALID_PARAMETER;
+
+       com_set->count = com_set->count + 1;
+       if (com_set->count == 1) { /* first data set */
+               data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET));
+       } else {
+               data_set = (GUMS_DATA_SET *)talloc_realloc(mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+       }
+       if (data_set == NULL)
+               return NT_STATUS_NO_MEMORY;
+
+       com_set->data = data_set;
+       data_set = &((com_set->data)[com_set->count - 1]);
+       
+       data_set->type = GUMS_SET_SID_LIST;
+       new_sids = (DOM_SID **)talloc(mem_ctx, (sizeof(void *) * count));
+       if (new_sids == NULL)
+               return NT_STATUS_NO_MEMORY;
+       for (i = 0; i < count; i++) {
+               new_sids[i] = sid_dup_talloc(mem_ctx, sids[i]);
+               if (new_sids[i] == NULL)
+                       return NT_STATUS_NO_MEMORY;
+       }
+
+       (SEC_DESC *)(data_set->data) = new_sids;
+
+       return NT_STATUS_OK;    
+}
+
+
+NTSTATUS gums_commit_data(GUMS_COMMIT_SET *set)
+{
+       return gums_storage->set_object_values(set->sid, set->count, set->data);
+}
+
+NTSTATUS gums_destroy_commit_set(GUMS_COMMIT_SET **com_set)
+{
+       talloc_destroy((*com_set)->mem_ctx);
+       *com_set = NULL;
+
+       return NT_STATUS_OK;
+}
+
diff --git a/source4/sam/gums_helper.c b/source4/sam/gums_helper.c
new file mode 100644 (file)
index 0000000..8526a2f
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+   Unix SMB/CIFS implementation.
+   GUMS backends helper functions
+   Copyright (C) Simo Sorce 2002
+   
+   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"
+
+extern GUMS_FUNCTIONS *gums_storage;
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Builtin_Administrators;
+extern DOM_SID global_sid_Builtin_Power_Users;
+extern DOM_SID global_sid_Builtin_Account_Operators;
+extern DOM_SID global_sid_Builtin_Server_Operators;
+extern DOM_SID global_sid_Builtin_Print_Operators;
+extern DOM_SID global_sid_Builtin_Backup_Operators;
+extern DOM_SID global_sid_Builtin_Replicator;
+extern DOM_SID global_sid_Builtin_Users;
+extern DOM_SID global_sid_Builtin_Guests;
+
+
+/* defines */
+
+#define ALLOC_CHECK(str, ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(str1, str2, err, label) do { if (NT_STATUS_IS_ERR(err)) { DEBUG(0, ("%s: %s failed!\n", str1, str2)); } } while(0)
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+   This function will check if the group SID is mapped onto a
+   system managed gid or onto a winbind manged sid.
+   In the first case it will be threated like a mapped group
+   and the backend should take the member list with a getgrgid
+   and ignore any user that have been possibly set into the group
+   object.
+
+   In the second case, the group is a fully SAM managed group
+   served back to the system through winbind. In this case the
+   members of a Local group are "unrolled" to cope with the fact
+   that unix cannot contain groups inside groups.
+   The backend MUST never call any getgr* / getpw* function or
+   loops with winbind may happen. 
+ ****************************************************************************/
+
+/*
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+       NTSTATUS result;
+       gid_t id;
+
+       /* look if mapping exist, do not make idmap alloc an uid if SID is not found * /
+       result = idmap_get_gid_from_sid(&id, sid, False);
+       if (NT_STATUS_IS_OK(result)) {
+               *mapped = gid_is_in_winbind_range(id);
+       } else {
+               *mapped = False;
+       }
+
+       return result;
+}
+*/
+
+/****************************************************************************
+ duplicate alloc luid_attr
+ ****************************************************************************/
+NTSTATUS dupalloc_luid_attr(TALLOC_CTX *ctx, LUID_ATTR **new_la, LUID_ATTR old_la)
+{
+       *new_la = (LUID_ATTR *)talloc(ctx, sizeof(LUID_ATTR));
+       if (*new_la == NULL) {
+               DEBUG(0,("dupalloc_luid_attr: could not Alloc memory to duplicate LUID_ATTR\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*new_la)->luid.high = old_la.luid.high;
+       (*new_la)->luid.low = old_la.luid.low;
+       (*new_la)->attr = old_la.attr;
+       
+       return NT_STATUS_OK;    
+}
+
+/****************************************************************************
+ initialise a privilege list
+ ****************************************************************************/
+void init_privilege(PRIVILEGE_SET *priv_set)
+{
+       priv_set->count=0;
+       priv_set->control=0;
+       priv_set->set=NULL;
+}
+
+/****************************************************************************
+ add a privilege to a privilege array
+ ****************************************************************************/
+NTSTATUS add_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx, LUID_ATTR set)
+{
+       LUID_ATTR *new_set;
+
+       /* check if the privilege is not already in the list */
+       if (check_priv_in_privilege(priv_set, set))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       /* we can allocate memory to add the new privilege */
+
+       new_set=(LUID_ATTR *)talloc_realloc(ctx, priv_set->set, (priv_set->count+1)*(sizeof(LUID_ATTR)));
+       if (new_set==NULL) {
+               DEBUG(0,("add_privilege: could not Realloc memory to add a new privilege\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       new_set[priv_set->count].luid.high=set.luid.high;
+       new_set[priv_set->count].luid.low=set.luid.low;
+       new_set[priv_set->count].attr=set.attr;
+       
+       priv_set->count++;
+       priv_set->set=new_set;
+       
+       return NT_STATUS_OK;    
+}
+
+/****************************************************************************
+ add all the privileges to a privilege array
+ ****************************************************************************/
+NTSTATUS add_all_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx)
+{
+       NTSTATUS result = NT_STATUS_OK;
+       LUID_ATTR set;
+
+       set.attr=0;
+       set.luid.high=0;
+       
+       set.luid.low=SE_PRIV_ADD_USERS;
+       result = add_privilege(priv_set, ctx, set);
+       NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done);
+       
+       set.luid.low=SE_PRIV_ADD_MACHINES;
+       result = add_privilege(priv_set, ctx, set);
+       NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done);
+
+       set.luid.low=SE_PRIV_PRINT_OPERATOR;
+       result = add_privilege(priv_set, ctx, set);
+       NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done);
+       
+done:
+       return result;
+}
+
+/****************************************************************************
+ check if the privilege list is empty
+ ****************************************************************************/
+BOOL check_empty_privilege(PRIVILEGE_SET *priv_set)
+{
+       return (priv_set->count == 0);
+}
+
+/****************************************************************************
+ check if the privilege is in the privilege list
+ ****************************************************************************/
+BOOL check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+       int i;
+
+       /* if the list is empty, obviously we can't have it */
+       if (check_empty_privilege(priv_set))
+               return False;
+
+       for (i=0; i<priv_set->count; i++) {
+               LUID_ATTR *cur_set;
+
+               cur_set=&priv_set->set[i];
+               /* check only the low and high part. Checking the attr field has no meaning */
+               if( (cur_set->luid.low==set.luid.low) && (cur_set->luid.high==set.luid.high) )
+                       return True;
+       }
+
+       return False;
+}
+
+/****************************************************************************
+ remove a privilege from a privilege array
+ ****************************************************************************/
+NTSTATUS remove_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx, LUID_ATTR set)
+{
+       LUID_ATTR *new_set;
+       LUID_ATTR *old_set;
+       int i,j;
+
+       /* check if the privilege is in the list */
+       if (!check_priv_in_privilege(priv_set, set))
+               return NT_STATUS_UNSUCCESSFUL;
+
+       /* special case if it's the only privilege in the list */
+       if (priv_set->count==1) {
+               init_privilege(priv_set);       
+               return NT_STATUS_OK;
+       }
+
+       /* 
+        * the privilege is there, create a new list,
+        * and copy the other privileges
+        */
+
+       old_set = priv_set->set;
+
+       new_set=(LUID_ATTR *)talloc(ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR)));
+       if (new_set==NULL) {
+               DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=0, j=0; i<priv_set->count; i++) {
+               if ((old_set[i].luid.low == set.luid.low) && 
+                   (old_set[i].luid.high == set.luid.high)) {
+                       continue;
+               }
+               
+               new_set[j].luid.low = old_set[i].luid.low;
+               new_set[j].luid.high = old_set[i].luid.high;
+               new_set[j].attr = old_set[i].attr;
+
+               j++;
+       }
+       
+       if (j != priv_set->count - 1) {
+               DEBUG(0,("remove_privilege: mismatch ! difference is not -1\n"));
+               DEBUGADD(0,("old count:%d, new count:%d\n", priv_set->count, j));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+               
+       /* ok everything is fine */
+       
+       priv_set->count--;
+       priv_set->set=new_set;
+       
+       return NT_STATUS_OK;    
+}
+
+/****************************************************************************
+ duplicates a privilege array
+ ****************************************************************************/
+NTSTATUS dup_priv_set(PRIVILEGE_SET **new_priv_set, TALLOC_CTX *mem_ctx, PRIVILEGE_SET *priv_set)
+{
+       LUID_ATTR *new_set;
+       LUID_ATTR *old_set;
+       int i;
+
+       *new_priv_set = (PRIVILEGE_SET *)talloc(mem_ctx, sizeof(PRIVILEGE_SET));
+       init_privilege(*new_priv_set);  
+
+       /* special case if there are no privileges in the list */
+       if (priv_set->count == 0) {
+               return NT_STATUS_OK;
+       }
+
+       /* 
+        * create a new list,
+        * and copy the other privileges
+        */
+
+       old_set = priv_set->set;
+
+       new_set = (LUID_ATTR *)talloc(mem_ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR)));
+       if (new_set==NULL) {
+               DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=0; i < priv_set->count; i++) {
+               
+               new_set[i].luid.low = old_set[i].luid.low;
+               new_set[i].luid.high = old_set[i].luid.high;
+               new_set[i].attr = old_set[i].attr;
+       }
+                       
+       (*new_priv_set)->count = priv_set->count;
+       (*new_priv_set)->control = priv_set->control;
+       (*new_priv_set)->set = new_set;
+       
+       return NT_STATUS_OK;    
+}
+
+#define ALIAS_DEFAULT_SACL_SA_RIGHTS   0x01050013
+#define ALIAS_DEFAULT_DACL_SA_RIGHTS \
+               (READ_CONTROL_ACCESS            | \
+               SA_RIGHT_ALIAS_LOOKUP_INFO      | \
+               SA_RIGHT_ALIAS_GET_MEMBERS)     /* 0x0002000c */
+
+#define ALIAS_DEFAULT_SACL_SEC_ACE_FLAG (SEC_ACE_FLAG_FAILED_ACCESS | SEC_ACE_FLAG_SUCCESSFUL_ACCESS) /* 0xc0 */
+
+NTSTATUS create_builtin_alias_default_sec_desc(SEC_DESC **sec_desc, TALLOC_CTX *ctx)
+{
+       DOM_SID *world = &global_sid_World;
+       DOM_SID *admins = &global_sid_Builtin_Administrators;
+       SEC_ACCESS sa;
+       SEC_ACE sacl_ace;
+       SEC_ACE dacl_aces[2];
+       SEC_ACL *sacl = NULL;
+       SEC_ACL *dacl = NULL;
+       size_t psize;
+
+       init_sec_access(&sa, ALIAS_DEFAULT_SACL_SA_RIGHTS);
+       init_sec_ace(&sacl_ace, world, SEC_ACE_TYPE_SYSTEM_AUDIT, sa, ALIAS_DEFAULT_SACL_SEC_ACE_FLAG);
+       
+       sacl = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &sacl_ace);
+       if (!sacl) {
+               DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       init_sec_access(&sa, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+       init_sec_ace(&(dacl_aces[0]), world, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+       init_sec_access(&sa, SA_RIGHT_ALIAS_ALL_ACCESS);
+       init_sec_ace(&(dacl_aces[1]), admins, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+       dacl = make_sec_acl(ctx, NT4_ACL_REVISION, 2, dacl_aces);
+       if (!sacl) {
+               DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *sec_desc = make_sec_desc(ctx, SEC_DESC_REVISION, admins, admins, sacl, dacl, &psize);
+       if (!(*sec_desc)) {
+               DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sec_desc_add_ace_to_dacl(SEC_DESC *sec_desc, TALLOC_CTX *ctx, DOM_SID *sid, uint32 mask)
+{
+       NTSTATUS result;
+       SEC_ACE *new_aces;
+       unsigned num_aces;
+       int i;
+
+       num_aces = sec_desc->dacl->num_aces + 1;
+       result = sec_ace_add_sid(ctx, &new_aces, sec_desc->dacl->ace, &num_aces, sid, mask);
+       if (NT_STATUS_IS_OK(result)) {
+               sec_desc->dacl->ace = new_aces;
+               sec_desc->dacl->num_aces = num_aces;
+               sec_desc->dacl->size = SEC_ACL_HEADER_SIZE;
+               for (i = 0; i < num_aces; i++) {
+                       sec_desc->dacl->size += sec_desc->dacl->ace[i].size;
+               }
+       }
+       return result;
+}
+
+NTSTATUS gums_init_builtin_groups(void)
+{
+       NTSTATUS result;
+       GUMS_OBJECT g_obj;
+       GUMS_GROUP *g_grp;
+       GUMS_PRIVILEGE g_priv;
+
+       /* Build the well known Builtin Local Groups */
+       g_obj.type = GUMS_OBJ_GROUP;
+       g_obj.version = 1;
+       g_obj.seq_num = 0;
+       g_obj.mem_ctx = talloc_init("gums_init_backend_acct");
+       if (g_obj.mem_ctx == NULL) {
+               DEBUG(0, ("gums_init_backend: Out of Memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Administrators */
+
+       /* alloc group structure */
+       g_obj.data = (void *)talloc(g_obj.mem_ctx, sizeof(GUMS_OBJ_GROUP));
+       ALLOC_CHECK("gums_init_backend", g_obj.data, result, done);
+
+       /* make admins sid */
+       g_grp = (GUMS_GROUP *)g_obj.data;
+       sid_copy(g_obj.sid, &global_sid_Builtin_Administrators);
+
+       /* make security descriptor */
+       result = create_builtin_alias_default_sec_desc(&(g_obj.sec_desc), g_obj.mem_ctx); 
+       NTSTATUS_CHECK("gums_init_backend", "create_builtin_alias_default_sec_desc", result, done);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeSecurityPrivilege
+               SeBackupPrivilege
+               SeRestorePrivilege
+               SeSystemtimePrivilege
+               SeShutdownPrivilege
+               SeRemoteShutdownPrivilege
+               SeTakeOwnershipPrivilege
+               SeDebugPrivilege
+               SeSystemEnvironmentPrivilege
+               SeSystemProfilePrivilege
+               SeProfileSingleProcessPrivilege
+               SeIncreaseBasePriorityPrivilege
+               SeLocalDriverPrivilege
+               SeCreatePagefilePrivilege
+               SeIncreaseQuotaPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Administrators");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can fully administer the computer/domain");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* numebr of group members */
+       g_grp->count = 0;
+       g_grp->members = NULL;
+
+       /* store Administrators group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Power Users */
+       /* Domain Controllers Does NOT have power Users */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Power_Users);
+
+       /* make privilege set */
+       /* SE_PRIV_??? */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Power Users");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+/* > */        g_obj.description = talloc_strdup(g_obj.mem_ctx, "Power Users");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Power Users group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Account Operators */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Account_Operators);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeShutdownPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Account Operators");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain user and group accounts");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Account Operators group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Server Operators */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Server_Operators);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeBackupPrivilege
+               SeRestorePrivilege
+               SeSystemtimePrivilege
+               SeShutdownPrivilege
+               SeRemoteShutdownPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Server Operators");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain servers");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Server Operators group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Print Operators */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Print_Operators);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeShutdownPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Print Operators");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain printers");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Print Operators group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Backup Operators */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Backup_Operators);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeBackupPrivilege
+               SeRestorePrivilege
+               SeShutdownPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Backup Operators");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can bypass file security to backup files");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Backup Operators group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Replicator */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Replicator);
+
+       /* make privilege set */
+       /* From BDC join trace:
+               SeBackupPrivilege
+               SeRestorePrivilege
+               SeShutdownPrivilege
+        */
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Replicator");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Supports file replication in a domain");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Replicator group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Users */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Users);
+
+       /* add ACE to sec dsec dacl */
+       sec_desc_add_ace_to_dacl(g_obj.sec_desc, g_obj.mem_ctx, &global_sid_Builtin_Account_Operators, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+       sec_desc_add_ace_to_dacl(g_obj.sec_desc, g_obj.mem_ctx, &global_sid_Builtin_Power_Users, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Users");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Ordinary users");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Users group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* Guests */
+
+       sid_copy(g_obj.sid, &global_sid_Builtin_Guests);
+
+       /* set name */
+       g_obj.name = talloc_strdup(g_obj.mem_ctx, "Guests");
+       ALLOC_CHECK("gums_init_backend", g_obj.name, result, done);
+
+       /* set description */
+       g_obj.description = talloc_strdup(g_obj.mem_ctx, "Users granted guest access to the computer/domain");
+       ALLOC_CHECK("gums_init_backend", g_obj.description, result, done);
+
+       /* store Guests group */
+       result = gums_storage->set_object(&g_obj);
+
+       /* set default privileges */
+       g_priv.type = GUMS_OBJ_GROUP;
+       g_priv.version = 1;
+       g_priv.seq_num = 0;
+       g_priv.mem_ctx = talloc_init("gums_init_backend_priv");
+       if (g_priv.mem_ctx == NULL) {
+               DEBUG(0, ("gums_init_backend: Out of Memory!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+               
+
+done:
+       talloc_destroy(g_obj.mem_ctx);
+       talloc_destroy(g_priv.mem_ctx);
+       return result;
+}
+
diff --git a/source4/sam/interface.c b/source4/sam/interface.c
new file mode 100644 (file)
index 0000000..51ae561
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Jelmer Vernooij                       2002
+   Copyright (C) Stefan (metze) Metzmacher             2002
+   Copyright (C) Kai Krüger                            2002
+
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+extern DOM_SID global_sid_Builtin;
+
+/** List of various built-in sam modules */
+
+const struct sam_init_function_entry builtin_sam_init_functions[] = {
+       { "plugin", sam_init_plugin },
+#ifdef HAVE_LDAP
+       { "ads", sam_init_ads },
+#endif
+       { "skel", sam_init_skel },
+       { NULL, NULL}
+};
+
+
+static NTSTATUS sam_get_methods_by_sid(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const DOM_SID *domainsid)
+{
+       SAM_METHODS     *tmp_methods;
+
+       DEBUG(5,("sam_get_methods_by_sid: %d\n", __LINE__));
+
+       /* invalid sam_context specified */
+       SAM_ASSERT(context && context->methods);
+
+       tmp_methods = context->methods;
+
+       while (tmp_methods) {
+               if (sid_equal(domainsid, &(tmp_methods->domain_sid)))
+               {
+                       (*sam_method) = tmp_methods;
+                       return NT_STATUS_OK;
+               }
+               tmp_methods = tmp_methods->next;
+       }
+
+       DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", sid_string_static(domainsid)));
+
+       return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS sam_get_methods_by_name(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const char *domainname)
+{
+       SAM_METHODS     *tmp_methods;
+
+       DEBUG(5,("sam_get_methods_by_name: %d\n", __LINE__));
+
+       /* invalid sam_context specified */
+       SAM_ASSERT(context && context->methods);
+
+       tmp_methods = context->methods;
+
+       while (tmp_methods) {
+               if (strequal(domainname, tmp_methods->domain_name))
+               {
+                       (*sam_method) = tmp_methods;
+                       return NT_STATUS_OK;
+               }
+               tmp_methods = tmp_methods->next;
+       }
+
+       DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", domainname));
+
+       return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS make_sam_methods(TALLOC_CTX *mem_ctx, SAM_METHODS **methods)
+{
+       *methods = talloc(mem_ctx, sizeof(SAM_METHODS));
+
+       if (!*methods) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*methods);
+
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+  Free and cleanup a sam context, any associated data and anything
+  that the attached modules might have associated.
+ *******************************************************************/
+
+void free_sam_context(SAM_CONTEXT **context)
+{
+       SAM_METHODS *sam_selected = (*context)->methods;
+
+       while (sam_selected) {
+               if (sam_selected->free_private_data) {
+                       sam_selected->free_private_data(&(sam_selected->private_data));
+               }
+               sam_selected = sam_selected->next;
+       }
+
+       talloc_destroy((*context)->mem_ctx);
+       *context = NULL;
+}
+
+/******************************************************************
+  Make a backend_entry from scratch
+ *******************************************************************/
+static NTSTATUS make_backend_entry(SAM_BACKEND_ENTRY *backend_entry, char *sam_backend_string)
+{
+       char *tmp = NULL;
+       char *tmp_string = sam_backend_string;
+       
+       DEBUG(5,("make_backend_entry: %d\n", __LINE__));
+       
+       SAM_ASSERT(sam_backend_string && backend_entry);
+       
+       backend_entry->module_name = sam_backend_string;
+       
+       DEBUG(5,("makeing backend_entry for %s\n", backend_entry->module_name));
+       
+       if ((tmp = strrchr(tmp_string, '|')) != NULL) {
+               DEBUGADD(20,("a domain name has been specified\n"));
+               *tmp = 0;
+               backend_entry->domain_name = smb_xstrdup(tmp + 1);
+               tmp_string = tmp + 1;
+       }
+       
+       if ((tmp = strchr(tmp_string, ':')) != NULL) {
+               DEBUG(20,("options for the backend have been specified\n"));
+               *tmp = 0;
+               backend_entry->module_params = smb_xstrdup(tmp + 1);
+               tmp_string = tmp + 1;
+       }
+               
+       if (backend_entry->domain_name == NULL) {
+               DEBUG(10,("make_backend_entry: no domain was specified for sam module %s. Using default domain %s\n",
+                       backend_entry->module_name, lp_workgroup()));
+               backend_entry->domain_name = smb_xstrdup(lp_workgroup());
+       }
+       
+       if ((backend_entry->domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID))) == NULL) {
+               DEBUG(0,("make_backend_entry: failed to malloc domain_sid\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       DEBUG(10,("looking up sid for domain %s\n", backend_entry->domain_name));
+       
+       if (!secrets_fetch_domain_sid(backend_entry->domain_name, backend_entry->domain_sid)) {
+               DEBUG(2,("make_backend_entry: There is no SID stored for domain %s. Creating a new one.\n",
+                       backend_entry->domain_name));           
+               DEBUG(0, ("FIXME in %s:%d\n", __FILE__, __LINE__));
+               ZERO_STRUCTP(backend_entry->domain_sid);
+       }
+       
+       DEBUG(5,("make_backend_entry: module name: %s, module parameters: %s, domain name: %s, domain sid: %s\n",
+               backend_entry->module_name, backend_entry->module_params, backend_entry->domain_name, sid_string_static(backend_entry->domain_sid)));
+       
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+ create sam_methods struct based on sam_backend_entry
+ *****************************************************************/
+
+static NTSTATUS make_sam_methods_backend_entry(SAM_CONTEXT *context, SAM_METHODS **methods_ptr, SAM_BACKEND_ENTRY *backend_entry)
+{
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       SAM_METHODS *methods;
+       int i;
+
+       DEBUG(5,("make_sam_methods_backend_entry: %d\n", __LINE__));
+
+       if (!NT_STATUS_IS_OK(nt_status = make_sam_methods(context->mem_ctx, methods_ptr))) {
+               return nt_status;
+       }
+
+       methods = *methods_ptr;
+       methods->backendname = talloc_strdup(context->mem_ctx, backend_entry->module_name);
+       methods->domain_name = talloc_strdup(context->mem_ctx, backend_entry->domain_name);
+       sid_copy(&methods->domain_sid, backend_entry->domain_sid);
+       methods->parent = context;
+
+       DEBUG(5,("Attempting to find sam backend %s\n", backend_entry->module_name));
+       for (i = 0; builtin_sam_init_functions[i].module_name; i++)
+       {
+               if (strequal(builtin_sam_init_functions[i].module_name, backend_entry->module_name))
+               {
+                       DEBUG(5,("Found sam backend %s (at pos %d)\n", backend_entry->module_name, i));
+                       DEBUGADD(5,("initialising it with options=%s for domain %s\n", backend_entry->module_params, sid_string_static(backend_entry->domain_sid)));
+                       nt_status = builtin_sam_init_functions[i].init(methods, backend_entry->module_params);
+                       if (NT_STATUS_IS_OK(nt_status)) {
+                               DEBUG(5,("sam backend %s has a valid init\n", backend_entry->module_name));
+                       } else {
+                               DEBUG(2,("sam backend %s did not correctly init (error was %s)\n",
+                                       backend_entry->module_name, nt_errstr(nt_status)));
+                       }
+                       return nt_status;
+               }
+       }
+       
+       DEBUG(2,("could not find backend %s\n", backend_entry->module_name));
+
+       return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS sam_context_check_default_backends(SAM_CONTEXT *context)
+{
+       SAM_BACKEND_ENTRY entry;
+       DOM_SID *global_sam_sid  = get_global_sam_sid(); /* lp_workgroup doesn't play nicely with multiple domains */
+       SAM_METHODS *methods, *tmpmethods;
+       NTSTATUS ntstatus;
+       
+       DEBUG(5,("sam_context_check_default_backends: %d\n", __LINE__));
+
+       /* Make sure domain lp_workgroup() is available */
+       
+       ntstatus = sam_get_methods_by_sid(context, &methods, &global_sid_Builtin);
+
+       if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+               DEBUG(4,("There was no backend specified for domain %s(%s); using %s\n",
+                       lp_workgroup(), sid_string_static(global_sam_sid), SAM_DEFAULT_BACKEND));
+
+               SAM_ASSERT(global_sam_sid);
+
+               entry.module_name = SAM_DEFAULT_BACKEND;
+               entry.module_params = NULL;
+               entry.domain_name = lp_workgroup();
+               entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+               sid_copy(entry.domain_sid, global_sam_sid);
+
+               if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+                       DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+                       return ntstatus;
+               }
+
+               DLIST_ADD_END(context->methods, methods, tmpmethods);
+
+       } else if (!NT_STATUS_IS_OK(ntstatus)) {
+               DEBUG(2, ("sam_get_methods_by_sid failed for %s\n", lp_workgroup()));
+               return ntstatus;
+       }
+
+       /* Make sure the BUILTIN domain is available */
+
+       ntstatus = sam_get_methods_by_sid(context, &methods, global_sam_sid);
+       
+       if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+               DEBUG(4,("There was no backend specified for domain BUILTIN; using %s\n", 
+                                SAM_DEFAULT_BACKEND));
+               entry.module_name = SAM_DEFAULT_BACKEND;
+               entry.module_params = NULL;
+               entry.domain_name = "BUILTIN";
+               entry.domain_sid    = (DOM_SID *)malloc(sizeof(DOM_SID)); 
+               sid_copy(entry.domain_sid, &global_sid_Builtin);
+
+               if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods,  &entry))) {
+                       DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+                       return ntstatus;
+               }
+
+               DLIST_ADD_END(context->methods, methods, tmpmethods);
+       } else if (!NT_STATUS_IS_OK(ntstatus)) {
+               DEBUG(2, ("sam_get_methods_by_sid failed for BUILTIN\n"));
+               return ntstatus;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS check_duplicate_backend_entries(SAM_BACKEND_ENTRY **backend_entries, int *nBackends)
+{
+       int i, j;
+       
+       DEBUG(5,("check_duplicate_backend_entries: %d\n", __LINE__));
+       
+       for (i = 0; i < *nBackends; i++) {
+               for (j = i + 1; j < *nBackends; j++) {
+                       if (sid_equal((*backend_entries)[i].domain_sid, (*backend_entries)[j].domain_sid)) {
+                               DEBUG(0,("two backend modules claim the same domain %s\n",
+                                       sid_string_static((*backend_entries)[j].domain_sid)));
+                               return NT_STATUS_INVALID_PARAMETER;                     
+                       }
+               }               
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS make_sam_context_list(SAM_CONTEXT **context, char **sam_backends_param)
+{
+       int i = 0, j = 0;
+       SAM_METHODS *curmethods, *tmpmethods;
+       int nBackends               = 0;
+       SAM_BACKEND_ENTRY *backends = NULL;
+       NTSTATUS nt_status          = NT_STATUS_UNSUCCESSFUL;
+
+       DEBUG(5,("make_sam_context_from_conf: %d\n", __LINE__));
+
+       if (!sam_backends_param) {
+               DEBUG(1, ("no SAM backeds specified!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = make_sam_context(context))) {
+               DEBUG(4,("make_sam_context failed\n"));
+               return nt_status;
+       }
+
+       while (sam_backends_param[nBackends])
+               nBackends++;
+
+       DEBUG(6,("There are %d domains listed with their backends\n", nBackends));
+
+       if ((backends = (SAM_BACKEND_ENTRY *)malloc(sizeof(*backends)*nBackends)) == NULL) {
+               DEBUG(0,("make_sam_context_list: failed to allocate backends\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       memset(backends, '\0', sizeof(*backends)*nBackends);
+
+       for (i = 0; i < nBackends; i++) {
+               DEBUG(8,("processing %s\n",sam_backends_param[i]));
+               if (!NT_STATUS_IS_OK(nt_status = make_backend_entry(&backends[i], sam_backends_param[i]))) {
+                       DEBUG(4,("make_backend_entry failed\n"));
+                       for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+                       SAFE_FREE(backends);
+                       free_sam_context(context);
+                       return nt_status;
+               }
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = check_duplicate_backend_entries(&backends, &nBackends))) {
+               DEBUG(4,("check_duplicate_backend_entries failed\n"));
+               for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+               SAFE_FREE(backends);
+               free_sam_context(context);
+               return nt_status;
+       }
+
+       for (i = 0; i < nBackends; i++) {
+               if (!NT_STATUS_IS_OK(nt_status = make_sam_methods_backend_entry(*context, &curmethods,  &backends[i]))) {
+                       DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+                       for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+                       SAFE_FREE(backends);
+                       free_sam_context(context);
+                       return nt_status;
+               }
+               DLIST_ADD_END((*context)->methods, curmethods, tmpmethods);
+       }
+       
+       for (i = 0; i < nBackends; i++) SAFE_FREE(backends[i].domain_sid);
+
+       SAFE_FREE(backends);
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+  Make a sam_context from scratch.
+ *******************************************************************/
+
+NTSTATUS make_sam_context(SAM_CONTEXT **context) 
+{
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("sam_context internal allocation context");
+
+       if (!mem_ctx) {
+               DEBUG(0, ("make_sam_context: talloc init failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }               
+
+       *context = talloc(mem_ctx, sizeof(**context));
+       if (!*context) {
+               DEBUG(0, ("make_sam_context: talloc failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(*context);
+
+       (*context)->mem_ctx = mem_ctx;
+
+       (*context)->free_fn = free_sam_context;
+
+       return NT_STATUS_OK;
+}
+
+/******************************************************************
+  Return an already initialised sam_context, to facilitate backward 
+  compatibility (see functions below).
+ *******************************************************************/
+
+static struct sam_context *sam_get_static_context(BOOL reload) 
+{
+       static SAM_CONTEXT *sam_context = NULL;
+
+       if ((sam_context) && (reload)) {
+               sam_context->free_fn(&sam_context);
+               sam_context = NULL;
+       }
+
+       if (!sam_context) {
+               if (!NT_STATUS_IS_OK(make_sam_context_list(&sam_context, lp_sam_backend()))) {
+                       DEBUG(4,("make_sam_context_list failed\n"));
+                       return NULL;
+               }
+
+               /* Make sure the required domains (default domain, builtin) are available */
+               if (!NT_STATUS_IS_OK(sam_context_check_default_backends(sam_context))) {
+                       DEBUG(4,("sam_context_check_default_backends failed\n"));
+                       return NULL;
+               }
+       }
+
+       return sam_context;
+}
+
+/***************************************************************
+  Initialize the static context (at smbd startup etc). 
+
+  If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_sam(BOOL reload)
+{      
+       return (sam_get_static_context(reload) != NULL);
+}
+
+
+/**************************************************************
+ External API.  This is what the rest of the world calls...
+***************************************************************/
+
+/******************************************************************
+  sam_* functions are used to link the external SAM interface
+  with the internal backends. These functions lookup the appropriate
+  backends for the domain and pass on to the function in sam_methods
+  in the selected backend
+
+  When the context parmater is NULL, the default is used.
+ *******************************************************************/
+
+#define SAM_SETUP_CONTEXT if (!context) \
+               context = sam_get_static_context(False);\
+       if (!context) {\
+               return NT_STATUS_UNSUCCESSFUL; \
+       }\
+       
+
+
+NTSTATUS sam_get_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_get_sec_desc: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_sec_desc) {
+               DEBUG(3, ("sam_get_sec_desc: sam_methods of the domain did not specify sam_get_sec_desc\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_sec_desc(tmp_methods, access_token, sid, sd))) {
+               DEBUG(4,("sam_get_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_set_sec_desc: %d\n", __LINE__));
+       
+       SAM_SETUP_CONTEXT;
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_set_sec_desc) {
+               DEBUG(3, ("sam_set_sec_desc: sam_methods of the domain did not specify sam_set_sec_desc\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_set_sec_desc(tmp_methods, access_token, sid, sd))) {
+               DEBUG(4,("sam_set_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_lookup_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, const char *name, DOM_SID *sid, uint32 *type)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_lookup_name: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+               DEBUG(4,("sam_get_methods_by_name failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_lookup_name) {
+               DEBUG(3, ("sam_lookup_name: sam_methods of the domain did not specify sam_lookup_name\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_name(tmp_methods, access_token, name, sid, type))) {
+               DEBUG(4,("sam_lookup_name for %s\\%s in backend %s failed\n",
+                                tmp_methods->domain_name, name, tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type)
+{
+       SAM_METHODS     *tmp_methods;
+       uint32          rid;
+       NTSTATUS        nt_status;
+       DOM_SID         domainsid;
+
+       DEBUG(5,("sam_lookup_sid: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       sid_copy(&domainsid, sid);
+       if (!sid_split_rid(&domainsid, &rid)) {
+               DEBUG(3,("sam_lookup_sid: failed to split the sid\n"));
+               return NT_STATUS_INVALID_SID;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_lookup_sid) {
+               DEBUG(3, ("sam_lookup_sid: sam_methods of the domain did not specify sam_lookup_sid\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_sid(tmp_methods, access_token, mem_ctx, sid, name, type))) {
+               DEBUG(4,("sam_lookup_name for %s in backend %s failed\n",
+                                sid_string_static(sid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_update_domain(const SAM_CONTEXT *context, const SAM_DOMAIN_HANDLE *domain)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+
+       DEBUG(5,("sam_update_domain: %d\n", __LINE__));
+       
+       SAM_SETUP_CONTEXT;
+
+       /* invalid domain specified */
+       SAM_ASSERT(domain && domain->current_sam_methods);
+       
+       tmp_methods = domain->current_sam_methods;
+       
+       if (!tmp_methods->sam_update_domain) {
+               DEBUG(3, ("sam_update_domain: sam_methods of the domain did not specify sam_update_domain\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_domain(tmp_methods, domain))){
+               DEBUG(4,("sam_update_domain in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_domains(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, int32 *domain_count, DOM_SID **domains, char ***domain_names)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS         nt_status;
+
+       SEC_DESC        *sd;
+       size_t          sd_size;
+       uint32          acc_granted;
+       int             i = 0;
+
+       DEBUG(5,("sam_enum_domains: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       /* invalid parmaters specified */
+       SAM_ASSERT(domain_count && domains && domain_names);
+
+       if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+               DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+               return nt_status;
+       }
+
+       if (!se_access_check(sd, access_token, SA_RIGHT_SAM_ENUM_DOMAINS, &acc_granted, &nt_status)) {
+               DEBUG(3,("sam_enum_domains: ACCESS DENIED\n"));
+                       return nt_status;
+       }
+
+       tmp_methods= context->methods;
+       *domain_count = 0;
+
+       while (tmp_methods) {
+               (*domain_count)++;
+               tmp_methods= tmp_methods->next;
+       }
+
+       DEBUG(6,("sam_enum_domains: enumerating %d domains\n", (*domain_count)));
+
+       tmp_methods = context->methods;
+
+       if (((*domains) = malloc( sizeof(DOM_SID) * (*domain_count))) == NULL) {
+               DEBUG(0,("sam_enum_domains: Out of memory allocating domain SID list\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (((*domain_names) = malloc( sizeof(char*) * (*domain_count))) == NULL) {
+               DEBUG(0,("sam_enum_domains: Out of memory allocating domain name list\n"));
+               SAFE_FREE((*domains));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       while (tmp_methods) {
+               DEBUGADD(7,("    [%d] %s: %s\n", i, tmp_methods->domain_name, sid_string_static(&tmp_methods->domain_sid)));
+               sid_copy(domains[i],&tmp_methods->domain_sid);
+               *domain_names[i] = smb_xstrdup(tmp_methods->domain_name);
+               i++;
+               tmp_methods= tmp_methods->next;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_domain(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, DOM_SID **domainsid)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       SEC_DESC        *sd;
+       size_t          sd_size;
+       uint32          acc_granted;
+
+       DEBUG(5,("sam_lookup_domain: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       /* invalid paramters */
+       SAM_ASSERT(access_token && domain && domainsid);
+
+       if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+               DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+               return nt_status;
+       }
+
+       if (!se_access_check(sd, access_token, SA_RIGHT_SAM_OPEN_DOMAIN, &acc_granted, &nt_status)) {
+               DEBUG(3,("sam_lookup_domain: ACCESS DENIED\n"));
+                       return nt_status;
+       }
+
+       tmp_methods= context->methods;
+
+       while (tmp_methods) {
+               if (strcmp(domain, tmp_methods->domain_name) == 0) {
+                       (*domainsid) = (DOM_SID *)malloc(sizeof(DOM_SID));
+                       sid_copy((*domainsid), &tmp_methods->domain_sid);
+                       return NT_STATUS_OK;
+               }
+               tmp_methods= tmp_methods->next;
+       }
+
+       return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+
+NTSTATUS sam_get_domain_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_get_domain_by_sid: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domainsid && domain);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_domain_handle) {
+               DEBUG(3, ("sam_get_domain_by_sid: sam_methods of the domain did not specify sam_get_domain_handle\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_domain_handle(tmp_methods, access_token, access_desired, domain))) {
+               DEBUG(4,("sam_get_domain_handle for %s in backend %s failed\n",
+                                sid_string_static(domainsid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_account(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_create_account: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       /* invalid parmaters */
+       SAM_ASSERT(access_token && domainsid && account_name && account);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_create_account) {
+               DEBUG(3, ("sam_create_account: sam_methods of the domain did not specify sam_create_account\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_account(tmp_methods, access_token, access_desired, account_name, acct_ctrl, account))) {
+               DEBUG(4,("sam_create_account in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+       DOM_SID         domainsid;
+       const DOM_SID           *accountsid;
+       SAM_METHODS     *tmp_methods;
+       uint32          rid;
+       NTSTATUS        nt_status;
+       
+       DEBUG(5,("sam_add_account: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       /* invalid parmaters */
+       SAM_ASSERT(account);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_account_sid(account, &accountsid))) {
+               DEBUG(0,("Can't get account SID\n"));
+               return nt_status;
+       }
+
+       sid_copy(&domainsid, accountsid);
+       if (!sid_split_rid(&domainsid, &rid)) {
+               DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+               return NT_STATUS_INVALID_SID;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_add_account) {
+               DEBUG(3, ("sam_add_account: sam_methods of the domain did not specify sam_add_account\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_account(tmp_methods, account))){
+               DEBUG(4,("sam_add_account in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       DEBUG(5,("sam_update_account: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       /* invalid account specified */
+       SAM_ASSERT(account && account->current_sam_methods);
+       
+       tmp_methods = account->current_sam_methods;
+               
+       if (!tmp_methods->sam_update_account) {
+               DEBUG(3, ("sam_update_account: sam_methods of the domain did not specify sam_update_account\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_account(tmp_methods, account))){
+               DEBUG(4,("sam_update_account in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       DEBUG(5,("sam_delete_account: %d\n", __LINE__));
+       
+       SAM_SETUP_CONTEXT;
+
+       /* invalid account specified */
+       SAM_ASSERT(account && account->current_sam_methods);
+       
+       tmp_methods = account->current_sam_methods;
+
+       if (!tmp_methods->sam_delete_account) {
+               DEBUG(3, ("sam_delete_account: sam_methods of the domain did not specify sam_delete_account\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_account(tmp_methods, account))){
+               DEBUG(4,("sam_delete_account in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_accounts(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 acct_ctrl, int32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_enum_accounts: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domainsid && account_count && accounts);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_enum_accounts) {
+               DEBUG(3, ("sam_enum_accounts: sam_methods of the domain did not specify sam_enum_accounts\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_accounts(tmp_methods, access_token, acct_ctrl, account_count, accounts))) {
+               DEBUG(4,("sam_enum_accounts for domain %s in backend %s failed\n",
+                                tmp_methods->domain_name, tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_get_account_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account)
+{
+       SAM_METHODS     *tmp_methods;
+       uint32          rid;
+       DOM_SID         domainsid;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_get_account_by_sid: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && accountsid && account);
+
+       sid_copy(&domainsid, accountsid);
+       if (!sid_split_rid(&domainsid, &rid)) {
+               DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+               return NT_STATUS_INVALID_SID;
+       }
+
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_account_by_sid) {
+               DEBUG(3, ("sam_get_account_by_sid: sam_methods of the domain did not specify sam_get_account_by_sid\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_sid(tmp_methods, access_token, access_desired, accountsid, account))) {
+               DEBUG(4,("sam_get_account_by_sid for %s in backend %s failed\n",
+                                sid_string_static(accountsid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_ACCOUNT_HANDLE **account)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_get_account_by_name: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domain && name && account);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+               DEBUG(4,("sam_get_methods_by_name failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_account_by_name) {
+               DEBUG(3, ("sam_get_account_by_name: sam_methods of the domain did not specify sam_get_account_by_name\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_name(tmp_methods, access_token, access_desired, name, account))) {
+               DEBUG(4,("sam_get_account_by_name for %s\\%s in backend %s failed\n",
+                                domain, name, tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_group(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_create_group: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domainsid && group_name && group);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_create_group) {
+               DEBUG(3, ("sam_create_group: sam_methods of the domain did not specify sam_create_group\n"));
+               return NT_STATUS_UNSUCCESSFUL; 
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_group(tmp_methods, access_token, access_desired, group_name, group_ctrl, group))) {
+               DEBUG(4,("sam_create_group in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+       DOM_SID         domainsid;
+       const DOM_SID           *groupsid;
+       SAM_METHODS     *tmp_methods;
+       uint32          rid;
+       NTSTATUS        nt_status;
+       
+       DEBUG(5,("sam_add_group: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(group);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_group_sid(group, &groupsid))) {
+               DEBUG(0,("Can't get group SID\n"));
+               return nt_status;
+       }
+
+       sid_copy(&domainsid, groupsid);
+       if (!sid_split_rid(&domainsid, &rid)) {
+               DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+               return NT_STATUS_INVALID_SID;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_add_group) {
+               DEBUG(3, ("sam_add_group: sam_methods of the domain did not specify sam_add_group\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_group(tmp_methods, group))){
+               DEBUG(4,("sam_add_group in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       DEBUG(5,("sam_update_group: %d\n", __LINE__));
+       
+       SAM_SETUP_CONTEXT;
+
+       /* invalid group specified */
+       SAM_ASSERT(group && group->current_sam_methods);
+       
+       tmp_methods = group->current_sam_methods;
+       
+       if (!tmp_methods->sam_update_group) {
+               DEBUG(3, ("sam_update_group: sam_methods of the domain did not specify sam_update_group\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_group(tmp_methods, group))){
+               DEBUG(4,("sam_update_group in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       DEBUG(5,("sam_delete_group: %d\n", __LINE__));
+       
+       SAM_SETUP_CONTEXT;
+
+       /* invalid group specified */
+       SAM_ASSERT(group && group->current_sam_methods);
+       
+       tmp_methods = group->current_sam_methods;
+
+       if (!tmp_methods->sam_delete_group) {
+               DEBUG(3, ("sam_delete_group: sam_methods of the domain did not specify sam_delete_group\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_group(tmp_methods, group))){
+               DEBUG(4,("sam_delete_group in backend %s failed\n",
+                                tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groups(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_enum_groups: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domainsid && groups_count && groups);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_enum_accounts) {
+               DEBUG(3, ("sam_enum_groups: sam_methods of the domain did not specify sam_enum_groups\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groups(tmp_methods, access_token, group_ctrl, groups_count, groups))) {
+               DEBUG(4,("sam_enum_groups for domain %s in backend %s failed\n",
+                                tmp_methods->domain_name, tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+       SAM_METHODS     *tmp_methods;
+       uint32          rid;
+       NTSTATUS        nt_status;
+       DOM_SID         domainsid;
+
+       DEBUG(5,("sam_get_group_by_sid: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && groupsid && group);
+
+       sid_copy(&domainsid, groupsid);
+       if (!sid_split_rid(&domainsid, &rid)) {
+               DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+               return NT_STATUS_INVALID_SID;
+       }
+
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+               DEBUG(4,("sam_get_methods_by_sid failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_group_by_sid) {
+               DEBUG(3, ("sam_get_group_by_sid: sam_methods of the domain did not specify sam_get_group_by_sid\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_sid(tmp_methods, access_token, access_desired, groupsid, group))) {
+               DEBUG(4,("sam_get_group_by_sid for %s in backend %s failed\n",
+                                sid_string_static(groupsid), tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_GROUP_HANDLE **group)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+
+       DEBUG(5,("sam_get_group_by_name: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+
+       SAM_ASSERT(access_token && domain && name && group);
+
+       if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+               DEBUG(4,("sam_get_methods_by_name failed\n"));
+               return nt_status;
+       }
+
+       if (!tmp_methods->sam_get_group_by_name) {
+               DEBUG(3, ("sam_get_group_by_name: sam_methods of the domain did not specify sam_get_group_by_name\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_name(tmp_methods, access_token, access_desired, name, group))) {
+               DEBUG(4,("sam_get_group_by_name for %s\\%s in backend %s failed\n",
+                                domain, name, tmp_methods->backendname));
+               return nt_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_member_to_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       SAM_SETUP_CONTEXT;
+       
+       /* invalid group or member specified */
+       SAM_ASSERT(group && group->current_sam_methods && member);
+       
+       tmp_methods = group->current_sam_methods;
+                       
+       if (!tmp_methods->sam_add_member_to_group) {
+               DEBUG(3, ("sam_add_member_to_group: sam_methods of the domain did not specify sam_add_member_to_group\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_member_to_group(tmp_methods, group, member))) {
+               DEBUG(4,("sam_add_member_to_group in backend %s failed\n", tmp_methods->backendname));
+               return nt_status;
+       }
+       
+       return NT_STATUS_OK;    
+       
+}
+
+NTSTATUS sam_delete_member_from_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+
+       SAM_SETUP_CONTEXT;
+       
+       /* invalid group or member specified */
+       SAM_ASSERT(group && group->current_sam_methods && member);
+       
+       tmp_methods = group->current_sam_methods;
+       
+       if (!tmp_methods->sam_delete_member_from_group) {
+               DEBUG(3, ("sam_delete_member_from_group: sam_methods of the domain did not specify sam_delete_member_from_group\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_member_from_group(tmp_methods, group, member))) {
+               DEBUG(4,("sam_delete_member_from_group in backend %s failed\n", tmp_methods->backendname));
+               return nt_status;
+       }
+       
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS sam_enum_groupmembers(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+       const SAM_METHODS *tmp_methods;
+       NTSTATUS     nt_status;
+       
+       SAM_SETUP_CONTEXT;
+       
+       /* invalid group specified */
+       SAM_ASSERT(group && group->current_sam_methods && members_count && members);
+       
+       tmp_methods = group->current_sam_methods;
+
+       if (!tmp_methods->sam_enum_groupmembers) {
+               DEBUG(3, ("sam_enum_groupmembers: sam_methods of the domain did not specify sam_enum_group_members\n"));
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+       
+       if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groupmembers(tmp_methods, group, members_count, members))) {
+               DEBUG(4,("sam_enum_groupmembers in backend %s failed\n", tmp_methods->backendname));
+               return nt_status;
+       }
+       
+       return NT_STATUS_OK;    
+}
+
+NTSTATUS sam_get_groups_of_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+       SAM_METHODS     *tmp_methods;
+       NTSTATUS        nt_status;
+       
+       uint32          tmp_group_count;
+       SAM_GROUP_ENUM *tmp_groups;
+       
+       DEBUG(5,("sam_get_groups_of_sid: %d\n", __LINE__));
+
+       SAM_SETUP_CONTEXT;
+       
+       /* invalid sam_context specified */
+       SAM_ASSERT(access_token && sids && context && context->methods);
+       
+       *group_count = 0;
+       
+       *groups = NULL;
+
+       tmp_methods= context->methods;
+
+       while (tmp_methods) {
+               DEBUG(5,("getting groups from domain \n"));
+               if (!tmp_methods->sam_get_groups_of_sid) {
+                       DEBUG(3, ("sam_get_groups_of_sid: sam_methods of domain did not specify sam_get_groups_of_sid\n"));
+                       SAFE_FREE(*groups);
+                       return NT_STATUS_NOT_IMPLEMENTED;
+               }
+               
+               if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_groups_of_sid(tmp_methods, access_token, sids, group_ctrl, &tmp_group_count, &tmp_groups))) {
+                       DEBUG(4,("sam_get_groups_of_sid in backend %s failed\n", tmp_methods->backendname));
+                       SAFE_FREE(*groups);
+                       return nt_status;
+               }
+               
+               *groups = Realloc(*groups, ((*group_count)  + tmp_group_count) * sizeof(SAM_GROUP_ENUM));
+
+               memcpy(&(*groups)[*group_count], tmp_groups, tmp_group_count);          
+               
+               SAFE_FREE(tmp_groups);
+               
+               *group_count += tmp_group_count;
+               
+               tmp_methods = tmp_methods->next;
+       }
+       
+       return NT_STATUS_OK;    
+}
+
+
diff --git a/source4/sam/sam_ads.c b/source4/sam/sam_ads.c
new file mode 100755 (executable)
index 0000000..13e0369
--- /dev/null
@@ -0,0 +1,1378 @@
+/*
+  Unix SMB/CIFS implementation.
+  Active Directory SAM backend, for simulate a W2K DC in mixed mode.
+
+  Copyright (C) Stefan (metze) Metzmacher      2002
+  Copyright (C) Andrew Bartlett                2002
+
+  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"
+
+
+#ifdef HAVE_LDAP
+
+static int sam_ads_debug_level = DBGC_SAM;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS sam_ads_debug_level
+
+#ifndef FIXME
+#define FIXME( body ) { DEBUG(0,("FIXME: "));\
+                       DEBUGADD(0,(body));}
+#endif
+
+#define ADS_STATUS_OK ADS_ERROR(0)
+#define ADS_STATUS_UNSUCCESSFUL ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL)
+#define ADS_STATUS_NOT_IMPLEMENTED ADS_ERROR_NT(NT_STATUS_NOT_IMPLEMENTED)
+
+
+#define ADS_SUBTREE_BUILTIN    "CN=Builtin,"
+#define ADS_SUBTREE_COMPUTERS  "CN=Computers,"
+#define        ADS_SUBTREE_DC          "CN=Domain Controllers,"
+#define ADS_SUBTREE_USERS      "CN=Users,"
+#define ADS_ROOT_TREE          ""
+/* Here are private module structs and functions */
+
+typedef struct sam_ads_privates {
+       ADS_STRUCT      *ads_struct;
+       TALLOC_CTX      *mem_ctx;
+       BOOL            bind_plaintext;
+       char            *ads_bind_dn;
+       char            *ads_bind_pw;
+       char            *ldap_uri;
+       /* did we need something more? */
+}SAM_ADS_PRIVATES;
+
+
+/* get only these LDAP attributes, witch we really need for an account */
+const char *account_attrs[] = {        "objectSid",
+                               "objectGUID", 
+                               "sAMAccountType",
+                               "sAMAcountName",
+                               "userPrincipalName",
+                               "accountExpires",
+                               "badPasswordTime",
+                               "badPwdCount",
+                               "lastLogoff",
+                               "lastLogon",
+                               "userWorkstations",
+                               "dBCSPwd",
+                               "unicodePwd",
+                               "pwdLastSet",
+                               "userAccountControl",
+                               "profilePath",
+                               "homeDrive",
+                               "scriptPath",
+                               "homeDirectory",
+                               "cn",
+                               "primaryGroupID",/* 513 */
+                               "nsNPAllowDialIn",/* TRUE */
+                               "userParameters",/* Dial Back number ...*/
+                               "codePage",/* 0 */
+                               "countryCode",/* 0 */
+                               "adminCount",/* 1 or 0 */
+                               "logonCount",/* 0 */
+                               "managedObjects",
+                               "memberOf",/* dn */
+                               "instanceType",/* 4 */
+                               "name", /* sync with cn */
+                               "description",
+                               /* "nTSecurityDescriptor", */
+                               NULL};
+                       
+/* get only these LDAP attributes, witch we really need for a group */                 
+const char *group_attrs[] = {"objectSid",
+                            /* "objectGUID", */ 
+                            "sAMAccountType",
+                            "sAMAcountName",
+                            "groupType",
+                            /* "member", */
+                            "description",
+                            "name", /* sync with cn */
+                            /* "nTSecurityDescriptor", */
+                            NULL};
+                       
+
+/***************************************************
+  return our ads connection. We keep the connection
+  open to make things faster
+****************************************************/
+static ADS_STATUS sam_ads_cached_connection(SAM_ADS_PRIVATES *privates)
+{
+       ADS_STRUCT      *ads_struct;
+       ADS_STATUS      ads_status;
+       
+       if (!privates->ads_struct) {
+               privates->ads_struct = ads_init_simple();
+               ads_struct = privates->ads_struct;
+               ads_struct->server.ldap_uri = smb_xstrdup(privates->ldap_uri);
+               if ((!privates->ads_bind_dn) || (!*privates->ads_bind_dn)) {
+                       ads_struct->auth.flags |= ADS_AUTH_ANON_BIND;
+               } else {
+                       ads_struct->auth.user_name 
+                               = smb_xstrdup(privates->ads_bind_dn);
+                       if (privates->ads_bind_pw) {
+                               ads_struct->auth.password 
+                                       = smb_xstrdup(privates->ads_bind_pw);
+                       }
+               }
+               if (privates->bind_plaintext) {
+                       ads_struct->auth.flags |= ADS_AUTH_SIMPLE_BIND;
+               }
+       } else {
+               ads_struct = privates->ads_struct;
+       }
+
+       if (ads_struct->ld != NULL) {           
+               /* connection has been opened. ping server. */
+               struct sockaddr_un addr;
+               socklen_t len;
+               int sd;
+               if (ldap_get_option(ads_struct->ld, LDAP_OPT_DESC, &sd) == 0 &&
+                   getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
+                       /* the other end has died. reopen. */
+                       ldap_unbind_ext(ads_struct->ld, NULL, NULL);
+                       ads_struct->ld = NULL;
+               }
+       }
+
+       if (ads_struct->ld != NULL) {
+               DEBUG(5,("sam_ads_cached_connection: allready connected to the LDAP server\n"));
+               return ADS_SUCCESS;
+       }
+
+       ads_status = ads_connect(ads_struct);
+
+       ads_status = ads_server_info(ads_struct);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(0,("Can't set server info: %s\n",ads_errstr(ads_status)));
+               /* return ads_status; */ FIXME("for now we only warn!\n");
+       }
+
+       DEBUG(2, ("sam_ads_cached_connection: succesful connection to the LDAP server\n"));
+       return ADS_SUCCESS;
+}
+
+static ADS_STATUS sam_ads_do_search(SAM_ADS_PRIVATES *privates, const char *bind_path, int scope, const char *exp, const char **attrs, void **res)
+{
+       ADS_STATUS      ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       
+       ads_status = sam_ads_cached_connection(privates);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_status;
+               
+       return ads_do_search_retry(privates->ads_struct, bind_path, scope, exp, attrs, res);            
+}
+
+
+/*********************************************
+here we have to check the update serial number
+ - this is the core of the ldap cache
+*********************************************/
+static ADS_STATUS sam_ads_usn_is_valid(SAM_ADS_PRIVATES *privates, uint32 usn_in, uint32 *usn_out)
+{
+       ADS_STATUS      ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+
+       SAM_ASSERT(privates && privates->ads_struct && usn_out);
+
+       ads_status = ads_USN(privates->ads_struct, usn_out);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_status;      
+       
+       if (*usn_out == usn_in)
+               return ADS_SUCCESS;
+               
+       return ads_status;      
+}
+
+/***********************************************
+Initialize SAM_ACCOUNT_HANDLE from an ADS query
+************************************************/
+/* not ready :-( */
+static ADS_STATUS ads_entry2sam_account_handle(SAM_ADS_PRIVATES *privates, SAM_ACCOUNT_HANDLE *account ,void *msg)
+{
+       ADS_STATUS      ads_status = ADS_ERROR_NT(NT_STATUS_NO_SUCH_USER);
+       NTSTATUS        nt_status = NT_STATUS_NO_SUCH_USER;
+       ADS_STRUCT      *ads_struct = privates->ads_struct;
+       TALLOC_CTX      *mem_ctx = account->mem_ctx;
+       char            *tmp_str = NULL;
+       
+       SAM_ASSERT(privates && ads_struct && account && mem_ctx && msg);
+
+       FIXME("should we really use ads_pull_username()(or ads_pull_string())?\n");
+       if ((account->private.account_name = ads_pull_username(ads_struct, mem_ctx, msg))==NULL) {
+               DEBUG(0,("ads_pull_username failed\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_SUCH_USER);
+       }
+       
+       if ((account->private.full_name = ads_pull_string(ads_struct, mem_ctx, msg,"name"))==NULL) {
+               DEBUG(3,("ads_pull_string for 'name' failed - skip\n"));
+       }
+       
+       if ((account->private.acct_desc = ads_pull_string(ads_struct, mem_ctx, msg,"description"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'acct_desc' failed - skip\n"));
+       }
+       
+       if ((account->private.home_dir = ads_pull_string(ads_struct, mem_ctx, msg,"homeDirectory"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'homeDirectory' failed - skip\n"));
+       }
+       
+       if ((account->private.dir_drive = ads_pull_string(ads_struct, mem_ctx, msg,"homeDrive"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'homeDrive' failed - skip\n"));
+       }
+       
+       if ((account->private.profile_path = ads_pull_string(ads_struct, mem_ctx, msg,"profilePath"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'profilePath' failed - skip\n"));
+       }
+       
+       if ((account->private.logon_script = ads_pull_string(ads_struct, mem_ctx, msg,"scriptPath"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'scriptPath' failed - skip\n"));
+       }
+       
+       FIXME("check 'nsNPAllowDialIn' for munged_dial!\n");
+       if ((account->private.munged_dial = ads_pull_string(ads_struct, mem_ctx, msg,"userParameters"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'userParameters' failed - skip\n"));
+       }
+       
+       if ((account->private.unix_home_dir = ads_pull_string(ads_struct, mem_ctx, msg,"msSFUHomeDrirectory"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'msSFUHomeDrirectory' failed - skip\n"));
+       }
+
+#if 0
+       FIXME("use function intern mem_ctx for pwdLastSet\n");
+       if ((tmp_str = ads_pull_string(ads_struct, mem_ctx, msg,"pwdLastSet"))!=NULL) {
+               DEBUG(3,("ads_pull_string for 'pwdLastSet' failed - skip\n"));
+       } else {
+               account->private.pass_last_set_time = ads_parse_nttime(tmp_str);
+               tmp_str = NULL;
+               
+       }       
+#endif
+
+#if 0
+typedef struct sam_account_handle {
+       TALLOC_CTX *mem_ctx;
+       uint32 access_granted;
+       const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */
+       void (*free_fn)(struct sam_account_handle **);
+       struct sam_account_data {
+               uint32 init_flag;
+               NTTIME logon_time; /* logon time */
+               NTTIME logoff_time; /* logoff time */
+               NTTIME kickoff_time; /* kickoff time */
+               NTTIME pass_last_set_time; /* password last set time */
+               NTTIME pass_can_change_time; /* password can change time */
+               NTTIME pass_must_change_time; /* password must change time */
+               char * account_name; /* account_name string */
+               SAM_DOMAIN_HANDLE * domain; /* domain of account */
+               char *full_name; /* account's full name string */
+               char *unix_home_dir; /* UNIX home directory string */
+               char *home_dir; /* home directory string */
+               char *dir_drive; /* home directory drive string */
+               char *logon_script; /* logon script string */
+               char *profile_path; /* profile path string */
+               char *acct_desc; /* account description string */
+               char *workstations; /* login from workstations string */
+               char *unknown_str; /* don't know what this is, yet. */
+               char *munged_dial; /* munged path name and dial-back tel number */
+               DOM_SID account_sid; /* Primary Account SID */
+               DOM_SID group_sid; /* Primary Group SID */
+               DATA_BLOB lm_pw; /* .data is Null if no password */
+               DATA_BLOB nt_pw; /* .data is Null if no password */
+               char *plaintext_pw; /* if Null not available */
+               uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+               uint32 unknown_1; /* 0x00ff ffff */
+               uint16 logon_divs; /* 168 - number of hours in a week */
+               uint32 hours_len; /* normally 21 bytes */
+               uint8 hours[MAX_HOURS_LEN];
+               uint32 unknown_2; /* 0x0002 0000 */
+               uint32 unknown_3; /* 0x0000 04ec */
+       } private;
+} SAM_ACCOUNT_HANDLE;
+#endif
+
+       return ads_status;
+}
+
+
+/***********************************************
+Initialize SAM_GROUP_ENUM from an ads entry
+************************************************/
+/* not ready :-( */
+static ADS_STATUS ads_entry2sam_group_enum(SAM_ADS_PRIVATES *privates, TALLOC_CTX *mem_ctx, SAM_GROUP_ENUM **group_enum,const void *entry)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_UNSUCCESSFUL;
+       ADS_STRUCT      *ads_struct = privates->ads_struct;
+       SAM_GROUP_ENUM  __group_enum;
+       SAM_GROUP_ENUM  *_group_enum = &__group_enum;
+       
+       SAM_ASSERT(privates && ads_struct && mem_ctx && group_enum && entry);
+       
+       *group_enum = _group_enum;
+       
+       DEBUG(3,("sam_ads: ads_entry2sam_account_handle\n"));
+
+       if (!ads_pull_sid(ads_struct, &entry, "objectSid", &(_group_enum->sid))) {
+               DEBUG(0,("No sid for!?\n"));
+               return ADS_STATUS_UNSUCCESSFUL;
+       }
+       
+       if (!(_group_enum->group_name = ads_pull_string(ads_struct, mem_ctx, &entry, "sAMAccountName"))) {
+               DEBUG(0,("No groupname found"));
+               return ADS_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!(_group_enum->group_desc = ads_pull_string(ads_struct, mem_ctx, &entry, "desciption"))) {
+               DEBUG(0,("No description found"));
+               return ADS_STATUS_UNSUCCESSFUL;
+       }       
+
+       DEBUG(0,("sAMAccountName: %s\ndescription: %s\nobjectSid: %s\n",
+                _group_enum->group_name,
+                _group_enum->group_desc,
+                sid_string_static(&(_group_enum->sid))
+                     ));
+       
+       return ads_status;
+}
+
+static ADS_STATUS sam_ads_access_check(SAM_ADS_PRIVATES *privates, const SEC_DESC *sd, const NT_USER_TOKEN *access_token, uint32 access_desired, uint32 *acc_granted)
+{
+       ADS_STATUS      ads_status = ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
+       NTSTATUS        nt_status;
+       uint32          my_acc_granted;
+
+       SAM_ASSERT(privates && sd && access_token);
+       /* acc_granted can be set to NULL */
+       
+       /* the steps you need are: 
+          1. get_sec_desc for sid 
+          2. se_map_generic(accessdesired, generic_mapping) 
+          3. se_access_check() */
+
+       if (!se_access_check(sd, access_token, access_desired, (acc_granted)?acc_granted:&my_acc_granted, &nt_status)) {
+               DEBUG(3,("sam_ads_access_check: ACCESS DENIED\n"));
+               ads_status = ADS_ERROR_NT(nt_status);
+               return ads_status;
+       }
+       ads_status = ADS_ERROR_NT(nt_status);   
+       return ads_status;
+}
+
+static ADS_STATUS sam_ads_get_tree_sec_desc(SAM_ADS_PRIVATES *privates, const char *subtree, SEC_DESC **sd)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;
+       char                    *search_path;
+       void                    *sec_desc_res;
+       void                    *sec_desc_msg;
+       const char              *sec_desc_attrs[] = {"nTSecurityDescriptor",NULL};
+               
+       SAM_ASSERT(privates && ads_struct && mem_ctx && sd);
+       *sd = NULL;
+               
+       if (subtree) {
+               asprintf(&search_path, "%s%s",subtree,ads_struct->config.bind_path);
+       } else {
+               asprintf(&search_path, "%s","");
+       }
+       ads_status = sam_ads_do_search(privates, search_path, LDAP_SCOPE_BASE, "(objectClass=*)", sec_desc_attrs, &sec_desc_res);
+       SAFE_FREE(search_path);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_status;
+               
+       if ((sec_desc_msg = ads_first_entry(ads_struct, sec_desc_res))==NULL) {
+               ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               return ads_status;              
+       }               
+                       
+       if (!ads_pull_sd(ads_struct, mem_ctx, sec_desc_msg, sec_desc_attrs[0], sd)) {
+               *sd = NULL;
+               ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               return ads_status;
+       }               
+       
+       return ads_status;      
+}
+
+static ADS_STATUS sam_ads_account_policy_get(SAM_ADS_PRIVATES *privates, int field, uint32 *value)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       void                    *ap_res;
+       void                    *ap_msg;
+       const char              *ap_attrs[] = {"minPwdLength",/* AP_MIN_PASSWORD_LEN */
+                                               "pwdHistoryLength",/* AP_PASSWORD_HISTORY */
+                                               "AP_USER_MUST_LOGON_TO_CHG_PASS",/* AP_USER_MUST_LOGON_TO_CHG_PASS */
+                                               "maxPwdAge",/* AP_MAX_PASSWORD_AGE */
+                                               "minPwdAge",/* AP_MIN_PASSWORD_AGE */
+                                               "lockoutDuration",/* AP_LOCK_ACCOUNT_DURATION */
+                                               "AP_RESET_COUNT_TIME",/* AP_RESET_COUNT_TIME */
+                                               "AP_BAD_ATTEMPT_LOCKOUT",/* AP_BAD_ATTEMPT_LOCKOUT */
+                                               "AP_TIME_TO_LOGOUT",/* AP_TIME_TO_LOGOUT */
+                                               NULL};
+                                               /*lockOutObservationWindow 
+                                               lockoutThreshold $ pwdProperties*/
+       static uint32           ap[9];
+       static uint32           ap_usn = 0;
+       uint32                  tmp_usn = 0;
+
+       SAM_ASSERT(privates && ads_struct && value);
+       
+       FIXME("We need to decode all account_policy attributes!\n");
+       
+       ads_status = sam_ads_usn_is_valid(privates,ap_usn,&tmp_usn);
+       if (!ADS_ERR_OK(ads_status)) {
+               ads_status = sam_ads_do_search(privates, ads_struct->config.bind_path, LDAP_SCOPE_BASE, "(objectClass=*)", ap_attrs, &ap_res);
+               if (!ADS_ERR_OK(ads_status))
+                       return ads_status; 
+               
+               if (ads_count_replies(ads_struct, ap_res) != 1) {
+                       ads_msgfree(ads_struct, ap_res);
+                       return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               }
+
+               if (!(ap_msg = ads_first_entry(ads_struct, ap_res))) {
+                       ads_msgfree(ads_struct, ap_res);
+                       return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               }
+               
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[0], &ap[0])) {
+                       /* AP_MIN_PASSWORD_LEN */
+                       ap[0] = MINPASSWDLENGTH;/* 5 chars minimum */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[1], &ap[1])) {
+                       /* AP_PASSWORD_HISTORY */
+                       ap[1] = 0;/* don't keep any old password */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[2], &ap[2])) {
+                       /* AP_USER_MUST_LOGON_TO_CHG_PASS */
+                       ap[2] = 0;/* don't force user to logon */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[3], &ap[3])) {
+                       /* AP_MAX_PASSWORD_AGE */
+                       ap[3] = MAX_PASSWORD_AGE;/* 21 days */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[4], &ap[4])) {
+                       /* AP_MIN_PASSWORD_AGE */
+                       ap[4] = 0;/* 0 days */
+               }               
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[5], &ap[5])) {
+                       /* AP_LOCK_ACCOUNT_DURATION */
+                       ap[5] = 0;/* lockout for 0 minutes */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[6], &ap[6])) {
+                       /* AP_RESET_COUNT_TIME */
+                       ap[6] = 0;/* reset immediatly */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[7], &ap[7])) {
+                       /* AP_BAD_ATTEMPT_LOCKOUT */
+                       ap[7] = 0;/* don't lockout */
+               }
+               if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[8], &ap[8])) {
+                       /* AP_TIME_TO_LOGOUT */
+                       ap[8] = -1;/* don't force logout */
+               }
+               
+               ads_msgfree(ads_struct, ap_res);
+               ap_usn = tmp_usn;
+       }
+
+       switch(field) {
+               case AP_MIN_PASSWORD_LEN:
+                       *value = ap[0];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_PASSWORD_HISTORY:
+                       *value = ap[1];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_USER_MUST_LOGON_TO_CHG_PASS:
+                       *value = ap[2];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_MAX_PASSWORD_AGE:
+                       *value = ap[3];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_MIN_PASSWORD_AGE:
+                       *value = ap[4];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_LOCK_ACCOUNT_DURATION:
+                       *value = ap[5];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_RESET_COUNT_TIME:
+                       *value = ap[6];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_BAD_ATTEMPT_LOCKOUT:
+                       *value = ap[7];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               case AP_TIME_TO_LOGOUT:
+                       *value = ap[8];
+                       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+                       break;
+               default: *value = 0; break;
+       }
+       
+       return ads_status;      
+}
+
+
+/**********************************
+Now the functions off the SAM API 
+***********************************/
+
+/* General API */
+static NTSTATUS sam_ads_get_sec_desc(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, 
+                             const DOM_SID *sid, SEC_DESC **sd)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx;
+       char                    *sidstr,*filter;
+       void                    *sec_desc_res;
+       void                    *sec_desc_msg;
+       const char              *sec_desc_attrs[] = {"nTSecurityDescriptor",NULL};
+       fstring                 sid_str;
+       SEC_DESC                *my_sd;
+
+       SAM_ASSERT(sam_method && access_token && sid && sd);    
+       
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL);
+
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       sidstr = sid_binstring(sid);
+       if (asprintf(&filter, "(objectSid=%s)", sidstr) == -1) {
+               SAFE_FREE(sidstr);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       SAFE_FREE(sidstr);
+
+       ads_status = sam_ads_do_search(privates,ads_struct->config.bind_path, 
+                                      LDAP_SCOPE_SUBTREE, filter, sec_desc_attrs,
+                                      &sec_desc_res);
+       SAFE_FREE(filter);
+
+       if (!ADS_ERR_OK(ads_status)) {
+               return ads_ntstatus(ads_status);
+       }
+
+       if (!(mem_ctx = talloc_init("sec_desc parse in sam_ads"))) {
+               DEBUG(1, ("talloc_init() failed for sec_desc parse context in sam_ads"));
+               ads_msgfree(ads_struct, sec_desc_res);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (ads_count_replies(ads_struct, sec_desc_res) != 1) {
+               DEBUG(1,("sam_ads_get_sec_desc: duplicate or 0 results for sid %s\n", 
+                        sid_to_string(sid_str, sid)));
+               talloc_destroy(mem_ctx);
+               ads_msgfree(ads_struct, sec_desc_res);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (!(sec_desc_msg = ads_first_entry(ads_struct, sec_desc_res))) {
+               talloc_destroy(mem_ctx);
+               ads_msgfree(ads_struct, sec_desc_res);
+               return NT_STATUS_INVALID_PARAMETER;
+       }               
+                       
+       if (!ads_pull_sd(ads_struct, mem_ctx, sec_desc_msg, sec_desc_attrs[0], sd)) {
+               ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               talloc_destroy(mem_ctx);
+               ads_msgfree(ads_struct, sec_desc_res);
+               return ads_ntstatus(ads_status);
+       }        
+       
+       /* now, were we allowed to see the SD we just got? */
+
+       ads_msgfree(ads_struct, sec_desc_res);
+       talloc_destroy(mem_ctx);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_set_sec_desc(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, 
+                             const DOM_SID *sid, const SEC_DESC *sd)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+       
+static NTSTATUS sam_ads_lookup_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, 
+                           TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, 
+                           enum SID_NAME_USE *type)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       SEC_DESC                *my_sd;
+
+       SAM_ASSERT(sam_method && access_token && mem_ctx && sid && name && type);
+
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd);        
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       return ads_sid_to_name(ads_struct, mem_ctx, sid, name, type);
+}
+
+static NTSTATUS sam_ads_lookup_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, 
+                            const char *name, DOM_SID *sid, enum SID_NAME_USE *type)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       SEC_DESC                *my_sd;
+
+       SAM_ASSERT(sam_method && access_token && name && sid && type);
+
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       return ads_name_to_sid(ads_struct, name, sid, type);
+}
+
+       
+/* Domain API */
+
+static NTSTATUS sam_ads_update_domain(const SAM_METHODS *sam_method, const SAM_DOMAIN_HANDLE *domain)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_get_domain_handle(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, 
+                                  const uint32 access_desired, SAM_DOMAIN_HANDLE **domain)
+{
+       ADS_STATUS              ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;   /*Fix me is this right??? */
+       SAM_DOMAIN_HANDLE       *dom_handle = NULL;
+       SEC_DESC                *sd;
+       uint32                  acc_granted;
+       uint32                  tmp_value;
+
+       DEBUG(5,("sam_ads_get_domain_handle: %d\n",__LINE__));
+       
+       SAM_ASSERT(sam_method && access_token && domain);
+       
+       (*domain) = NULL;
+
+       if ((dom_handle = talloc(mem_ctx, sizeof(SAM_DOMAIN_HANDLE))) == NULL) {
+               DEBUG(0,("failed to talloc dom_handle\n"));
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       return ads_ntstatus(ads_status);
+       }
+
+       ZERO_STRUCTP(dom_handle);
+
+       dom_handle->mem_ctx = mem_ctx; /*Fix me is this right??? */
+       dom_handle->free_fn = NULL;
+       dom_handle->current_sam_methods = sam_method;
+
+       /* check if access can be granted as requested */
+
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &sd);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       dom_handle->access_granted = acc_granted;
+
+       /* fill all the values of dom_handle */
+       sid_copy(&dom_handle->private.sid, &sam_method->domain_sid);
+       dom_handle->private.name       = smb_xstrdup(sam_method->domain_name);
+       dom_handle->private.servername = "WHOKNOWS"; /* what is the servername */
+
+       /*Fix me: sam_ads_account_policy_get() return ADS_STATUS! */ 
+       ads_status = sam_ads_account_policy_get(privates, AP_MAX_PASSWORD_AGE, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for max password age. Useing default\n"));
+               tmp_value = MAX_PASSWORD_AGE;
+       }
+       unix_to_nt_time_abs(&dom_handle->private.max_passwordage,tmp_value);
+
+       ads_status = sam_ads_account_policy_get(privates, AP_MIN_PASSWORD_AGE, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for min password age. Useing default\n"));
+               tmp_value = 0;
+       }
+       unix_to_nt_time_abs(&dom_handle->private.min_passwordage, tmp_value);
+
+       ads_status = sam_ads_account_policy_get(privates, AP_LOCK_ACCOUNT_DURATION, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for lockout duration. Useing default\n"));
+               tmp_value = 0;
+       }
+       unix_to_nt_time_abs(&dom_handle->private.lockout_duration, tmp_value);
+
+       ads_status = sam_ads_account_policy_get(privates, AP_RESET_COUNT_TIME, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for time till locout count is reset. Useing default\n"));
+               tmp_value = 0;
+       }
+       unix_to_nt_time_abs(&dom_handle->private.reset_count, tmp_value);
+
+       ads_status = sam_ads_account_policy_get(privates, AP_MIN_PASSWORD_LEN, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for min password length. Useing default\n"));
+               tmp_value = 0;
+       }
+       dom_handle->private.min_passwordlength = (uint16)tmp_value;
+
+       ads_status = sam_ads_account_policy_get(privates, AP_PASSWORD_HISTORY, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed password history. Useing default\n"));
+               tmp_value = 0;
+       }
+       dom_handle->private.password_history = (uint16)tmp_value;
+
+       ads_status = sam_ads_account_policy_get(privates, AP_BAD_ATTEMPT_LOCKOUT, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for bad attempts till lockout. Useing default\n"));
+               tmp_value = 0;
+       }
+       dom_handle->private.lockout_count = (uint16)tmp_value;
+
+       ads_status = sam_ads_account_policy_get(privates, AP_TIME_TO_LOGOUT, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for force logout. Useing default\n"));
+               tmp_value = -1;
+       }
+
+       ads_status = sam_ads_account_policy_get(privates, AP_USER_MUST_LOGON_TO_CHG_PASS, &tmp_value);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4,("sam_ads_account_policy_get failed for user must login to change password. Useing default\n"));
+               tmp_value = 0;
+       }
+
+       /* should the real values of num_accounts, num_groups and num_aliases be retreved?
+        * I think it is to expensive to bother
+        */
+       dom_handle->private.num_accounts = 3;
+       dom_handle->private.num_groups   = 4;
+       dom_handle->private.num_aliases  = 5;
+
+       *domain = dom_handle;
+       
+       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+       return ads_ntstatus(ads_status);
+}
+
+/* Account API */
+static NTSTATUS sam_ads_create_account(const SAM_METHODS *sam_method, 
+                               const NT_USER_TOKEN *access_token, uint32 access_desired, 
+                               const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+       ADS_STATUS              ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       SEC_DESC                *sd = NULL;
+       uint32                  acc_granted;
+
+       SAM_ASSERT(sam_method && privates && access_token && account_name && account);
+
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_SUBTREE_USERS, &sd);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = ADS_ERROR_NT(sam_init_account(account));
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);        
+
+       (*account)->access_granted = acc_granted;
+
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_add_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account)
+{
+       ADS_STATUS              ads_status = ADS_ERROR(LDAP_NO_MEMORY);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;
+       ADS_MODLIST             mods;
+       uint16                  acct_ctrl;
+       char                    *new_dn;
+       SEC_DESC                *sd;
+       uint32                  acc_granted;
+
+       SAM_ASSERT(sam_method && account);
+       
+       ads_status = ADS_ERROR_NT(sam_get_account_acct_ctrl(account,&acct_ctrl));
+       if (!ADS_ERR_OK(ads_status))
+               goto done;
+                       
+       if ((acct_ctrl & ACB_WSTRUST)||(acct_ctrl & ACB_SVRTRUST)) {
+               /* Computer account */
+               char            *name,*controlstr;
+               char            *hostname,*host_upn,*host_spn;
+               const char      *objectClass[] = {"top", "person", "organizationalPerson",
+                                                 "user", "computer", NULL};
+
+               ads_status = ADS_ERROR_NT(sam_get_account_name(account,&name));
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+
+               if (!(host_upn = talloc_asprintf(mem_ctx, "%s@%s", name, ads_struct->config.realm))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+
+               if (!(new_dn = talloc_asprintf(mem_ctx, "CN=%s,CN=Computers,%s", hostname, 
+                                              ads_struct->config.bind_path))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+                                       
+               if (!(controlstr = talloc_asprintf(mem_ctx, "%u", ads_acb2uf(acct_ctrl)))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+               
+               if (!(mods = ads_init_mods(mem_ctx))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+               
+               ads_status = ads_mod_str(mem_ctx, &mods, "cn", hostname);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_strlist(mem_ctx, &mods, "objectClass", objectClass);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", host_upn);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "displayName", hostname);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "sAMAccountName", name);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;      
+
+               ads_status = ads_mod_str(mem_ctx, &mods, "servicePrincipalName", host_spn);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "dNSHostName", hostname);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               /*      ads_status = ads_mod_str(mem_ctx, &mods, "operatingSystem", "Samba");
+                       if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               *//*    ads_status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion", VERSION);
+                       if (!ADS_ERR_OK(ads_status))
+                       goto done;
+                 */            
+               /* End Computer account */
+       } else {
+               /* User account*/
+               char            *upn, *controlstr;
+               char            *name, *fullname;
+               const char      *objectClass[] = {"top", "person", "organizationalPerson",
+                                                 "user", NULL};
+
+               ads_status = ADS_ERROR_NT(sam_get_account_name(account,&name));
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+
+               ads_status = ADS_ERROR_NT(sam_get_account_fullname(account,&fullname));
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+
+               if (!(upn = talloc_asprintf(mem_ctx, "%s@%s", name, ads_struct->config.realm))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+
+               if (!(new_dn = talloc_asprintf(mem_ctx, "CN=%s,CN=Users,%s", fullname, 
+                                              ads_struct->config.bind_path))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+                                       
+               if (!(controlstr = talloc_asprintf(mem_ctx, "%u", ads_acb2uf(acct_ctrl)))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+               
+               if (!(mods = ads_init_mods(mem_ctx))) {
+                       ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       goto done;
+               }
+               
+               ads_status = ads_mod_str(mem_ctx, &mods, "cn", fullname);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_strlist(mem_ctx, &mods, "objectClass", objectClass);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", upn);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "displayName", fullname);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "sAMAccountName", name);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;
+               ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr);
+               if (!ADS_ERR_OK(ads_status))
+                       goto done;      
+       }/* End User account */ 
+
+       /* Finally at the account */
+       ads_status = ads_gen_add(ads_struct, new_dn, mods);
+
+done:
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_update_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_delete_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+
+
+
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_enum_accounts(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+#if 0
+static NTSTATUS sam_ads_get_account_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *account_sid, SAM_ACCOUNT_HANDLE **account)
+{
+       ADS_STATUS              ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;
+       SEC_DESC                *sd = NULL;
+       uint32                  acc_granted;
+               
+       SAM_ASSERT(sam_method && privates && ads_struct && access_token && account_sid && account);
+
+       ads_status = ADS_ERROR_NT(sam_ads_get_sec_desc(sam_method, access_token, account_sid, &my_sd));
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = ADS_ERROR_NT(sam_init_account(account));
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);        
+
+       (*account)->access_granted = acc_granted;
+
+       return ads_ntstatus(ads_status);
+}
+#else
+static NTSTATUS sam_ads_get_account_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *account_sid, SAM_ACCOUNT_HANDLE **account)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+#endif
+
+#if 0
+static NTSTATUS sam_ads_get_account_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *account_name, SAM_ACCOUNT_HANDLE **account)
+{
+       ADS_STATUS      ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;
+       SEC_DESC                *sd = NULL;
+       uint32                  acc_granted;
+       
+       SAM_ASSERT(sam_method && privates && ads_struct && access_token && account_name && account);
+
+       ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &sd);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted);
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);
+
+       ads_status = ADS_ERROR_NT(sam_init_account(account));
+       if (!ADS_ERR_OK(ads_status))
+               return ads_ntstatus(ads_status);        
+
+       (*account)->access_granted = acc_granted;
+
+       return ads_ntstatus(ads_status);
+}
+#else
+static NTSTATUS sam_ads_get_account_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *account_name, SAM_ACCOUNT_HANDLE **account)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+#endif
+
+/* Group API */
+static NTSTATUS sam_ads_create_group(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_add_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_update_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_delete_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_enum_groups(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+       ADS_STATUS              ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       SAM_ADS_PRIVATES        *privates = (struct sam_ads_privates *)sam_method->private_data;
+       ADS_STRUCT              *ads_struct = privates->ads_struct;
+       TALLOC_CTX              *mem_ctx = privates->mem_ctx;
+       void                    *res = NULL;
+       void                    *msg = NULL;
+       char                    *filter = NULL;
+       int                     i = 0;
+       
+       /* get only these LDAP attributes, witch we really need for a group */                  
+       const char *group_enum_attrs[] = {"objectSid",
+                                         "description",
+                                         "sAMAcountName",
+                                         NULL};
+       
+       SAM_ASSERT(sam_method && access_token && groups_count && groups);
+       
+       *groups_count = 0;
+
+       DEBUG(3,("ads: enum_dom_groups\n"));
+
+       FIXME("get only group from the wanted Type!\n");
+       asprintf(&filter, "(&(objectClass=group)(groupType=%s))", "*");
+       ads_status = sam_ads_do_search(privates, ads_struct->config.bind_path, LDAP_SCOPE_SUBTREE, filter, group_enum_attrs, &res);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(1,("enum_groups ads_search: %s\n", ads_errstr(ads_status)));
+       }
+
+       *groups_count = ads_count_replies(ads_struct, res);
+       if (*groups_count == 0) {
+               DEBUG(1,("enum_groups: No groups found\n"));
+       }
+
+       (*groups) = talloc_zero(mem_ctx, (*groups_count) * sizeof(**groups));
+       if (!*groups) {
+               ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       for (msg = ads_first_entry(ads_struct, res); msg; msg = ads_next_entry(ads_struct, msg)) {
+               uint32          grouptype;
+
+               if (!ads_pull_uint32(ads_struct, msg, "groupType", &grouptype)) {
+                       ;
+               } else {
+                       (*groups)->group_ctrl = ads_gtype2gcb(grouptype);
+               }
+       
+               if (!((*groups)->group_name = ads_pull_string(ads_struct, mem_ctx, msg, "sAMAccountName"))) {
+                       ;
+               }
+               
+               if (!((*groups)->group_desc = ads_pull_string(ads_struct, mem_ctx, msg, "description"))) {
+                       ;
+               }
+               
+               if (!ads_pull_sid(ads_struct, msg, "objectSid", &((*groups)->sid))) {
+                       DEBUG(1,("No sid for group %s !?\n", (*groups)->group_name));
+                       continue;
+               }
+
+               i++;
+       }
+
+       (*groups_count) = i;
+
+       ads_status = ADS_ERROR_NT(NT_STATUS_OK);
+
+       DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*groups_count)));
+
+       if (res) ads_msgfree(ads_struct, res);
+
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_get_group_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_get_group_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_add_member_to_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_delete_member_from_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_enum_groupmembers(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+static NTSTATUS sam_ads_get_groups_of_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const DOM_SID **sids, const uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+       ADS_STATUS      ads_status = ADS_STATUS_NOT_IMPLEMENTED;
+       DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO));
+       SAM_ASSERT(sam_method);
+       return ads_ntstatus(ads_status);
+}
+
+/**********************************
+Free our private data
+***********************************/
+static void sam_ads_free_private_data(void **vp) 
+{
+       SAM_ADS_PRIVATES **sam_ads_state = (SAM_ADS_PRIVATES **)vp;
+
+       if ((*sam_ads_state)->ads_struct->ld) {
+               ldap_unbind((*sam_ads_state)->ads_struct->ld);
+       }
+
+       ads_destroy(&((*sam_ads_state)->ads_struct));
+       
+       talloc_destroy((*sam_ads_state)->mem_ctx);
+       FIXME("maybe we must free some other stuff here\n");
+
+       *sam_ads_state = NULL;
+}
+
+
+
+/*****************************************************
+Init the ADS SAM backend  
+******************************************************/
+NTSTATUS sam_init_ads(SAM_METHODS *sam_method, const char *module_params)
+{
+       ADS_STATUS              ads_status;
+       SAM_ADS_PRIVATES        *sam_ads_state;
+       TALLOC_CTX              *mem_ctx;
+       
+       SAM_ASSERT(sam_method && sam_method->parent);
+       
+       mem_ctx = sam_method->parent->mem_ctx;
+
+       /* Here the SAM API functions of the sam_ads module */
+
+       /* General API */
+
+       sam_method->sam_get_sec_desc = sam_ads_get_sec_desc;
+       sam_method->sam_set_sec_desc = sam_ads_set_sec_desc;
+       
+       sam_method->sam_lookup_sid = sam_ads_lookup_sid;
+       sam_method->sam_lookup_name = sam_ads_lookup_name;
+       
+       /* Domain API */
+
+       sam_method->sam_update_domain = sam_ads_update_domain;
+       sam_method->sam_get_domain_handle = sam_ads_get_domain_handle;
+
+       /* Account API */
+
+       sam_method->sam_create_account = sam_ads_create_account;
+       sam_method->sam_add_account = sam_ads_add_account;
+       sam_method->sam_update_account = sam_ads_update_account;
+       sam_method->sam_delete_account = sam_ads_delete_account;
+       sam_method->sam_enum_accounts = sam_ads_enum_accounts;
+
+       sam_method->sam_get_account_by_sid = sam_ads_get_account_by_sid;
+       sam_method->sam_get_account_by_name = sam_ads_get_account_by_name;
+
+       /* Group API */
+
+       sam_method->sam_create_group = sam_ads_create_group;
+       sam_method->sam_add_group = sam_ads_add_group;
+       sam_method->sam_update_group = sam_ads_update_group;
+       sam_method->sam_delete_group = sam_ads_delete_group;
+       sam_method->sam_enum_groups = sam_ads_enum_groups;
+       sam_method->sam_get_group_by_sid = sam_ads_get_group_by_sid;
+       sam_method->sam_get_group_by_name = sam_ads_get_group_by_name;
+
+       sam_method->sam_add_member_to_group = sam_ads_add_member_to_group;
+       sam_method->sam_delete_member_from_group = sam_ads_delete_member_from_group;
+       sam_method->sam_enum_groupmembers = sam_ads_enum_groupmembers;
+
+       sam_method->sam_get_groups_of_sid = sam_ads_get_groups_of_sid;
+
+       sam_ads_state = talloc_zero(mem_ctx, sizeof(SAM_ADS_PRIVATES));
+       if (!sam_ads_state) {
+               DEBUG(0, ("talloc() failed for sam_ads private_data!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       if (!(sam_ads_state->mem_ctx = talloc_init("sam_ads_method"))) {
+               DEBUG(0, ("talloc_init() failed for sam_ads_state->mem_ctx\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       sam_ads_state->ads_bind_dn = talloc_strdup(sam_ads_state->mem_ctx, lp_parm_string(NULL,"sam_ads","bind as"));
+       sam_ads_state->ads_bind_pw = talloc_strdup(sam_ads_state->mem_ctx, lp_parm_string(NULL,"sam_ads","bind pw"));
+
+       sam_ads_state->bind_plaintext = strequal(lp_parm_string(NULL, "sam_ads", "plaintext bind"), "yes");
+
+       if (!sam_ads_state->ads_bind_dn || !sam_ads_state->ads_bind_pw) {
+               DEBUG(0, ("talloc_strdup() failed for bind dn or password\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Maybe we should not check the result here? Server down on startup? */
+
+       if (module_params && *module_params) {
+               sam_ads_state->ldap_uri = talloc_strdup(sam_ads_state->mem_ctx, module_params);
+               if (!sam_ads_state->ldap_uri) {
+                       DEBUG(0, ("talloc_strdup() failed for bind dn or password\n"));
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               sam_ads_state->ldap_uri = "ldapi://";
+       }
+
+       ads_status = sam_ads_cached_connection(sam_ads_state);
+       if (!ADS_ERR_OK(ads_status)) {
+               return ads_ntstatus(ads_status);
+       }
+
+       sam_method->private_data = sam_ads_state;
+       sam_method->free_private_data = sam_ads_free_private_data;
+       
+       sam_ads_debug_level = debug_add_class("sam_ads");
+       if (sam_ads_debug_level == -1) {
+               sam_ads_debug_level = DBGC_ALL;
+               DEBUG(0, ("sam_ads: Couldn't register custom debugging class!\n"));
+       } else DEBUG(2, ("sam_ads: Debug class number of 'sam_ads': %d\n", sam_ads_debug_level));
+    
+       DEBUG(5, ("Initializing sam_ads\n"));
+       if (module_params)
+               DEBUG(10, ("Module Parameters for Domain %s[%s]: %s\n", sam_method->domain_name, sam_method->domain_name, module_params));
+       return NT_STATUS_OK;
+}
+
+#else /* HAVE_LDAP */
+void sam_ads_dummy(void)
+{
+       DEBUG(0,("sam_ads: not supported!\n"));
+}
+#endif /* HAVE_LDAP */
diff --git a/source4/sam/sam_plugin.c b/source4/sam/sam_plugin.c
new file mode 100644 (file)
index 0000000..fd26c4b
--- /dev/null
@@ -0,0 +1,79 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Loadable san module interface.
+   Copyright (C) Jelmer Vernooij                       2002
+   Copyright (C) Andrew Bartlett                       2002
+   Copyright (C) Stefan (metze) Metzmacher             2002
+      
+   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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+NTSTATUS sam_init_plugin(SAM_METHODS *sam_methods, const char *module_params)
+{
+       void *dl_handle;
+       char *plugin_params, *plugin_name, *p;
+       sam_init_function plugin_init;
+       int (*plugin_version)(void);
+
+       if (module_params == NULL) {
+               DEBUG(0, ("The plugin module needs an argument!\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       plugin_name = smb_xstrdup(module_params);
+       p = strchr(plugin_name, ':');
+       if (p) {
+               *p = 0;
+               plugin_params = p+1;
+               trim_string(plugin_params, " ", " ");
+       } else plugin_params = NULL;
+       trim_string(plugin_name, " ", " ");
+
+       DEBUG(5, ("Trying to load sam plugin %s\n", plugin_name));
+       dl_handle = sys_dlopen(plugin_name, RTLD_NOW);
+       if (!dl_handle) {
+               DEBUG(0, ("Failed to load sam plugin %s using sys_dlopen (%s)\n", plugin_name, sys_dlerror()));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+    
+       plugin_version = sys_dlsym(dl_handle, "sam_version");
+       if (!plugin_version) {
+               sys_dlclose(dl_handle);
+               DEBUG(0, ("Failed to find function 'sam_version' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));            
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (plugin_version()!=SAM_INTERFACE_VERSION) {
+               sys_dlclose(dl_handle);
+               DEBUG(0, ("Wrong SAM_INTERFACE_VERSION! sam plugin has version %d and version %d is needed! Please update!\n",
+                           plugin_version(),SAM_INTERFACE_VERSION));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+                                       
+       plugin_init = sys_dlsym(dl_handle, "sam_init");
+       if (!plugin_init) {
+               sys_dlclose(dl_handle);
+               DEBUG(0, ("Failed to find function 'sam_init' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));       
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       DEBUG(5, ("Starting sam plugin %s with parameters %s for domain %s\n", plugin_name, plugin_params, sam_methods->domain_name));
+       return plugin_init(sam_methods, plugin_params);
+}
diff --git a/source4/sam/sam_skel.c b/source4/sam/sam_skel.c
new file mode 100644 (file)
index 0000000..b4d64bb
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+   Unix SMB/CIFS implementation.
+   this is a skeleton for SAM backend modules.
+       
+   Copyright (C) Stefan (metze) Metzmacher             2002
+   Copyright (C) Jelmer Vernooij                       2002
+   Copyright (C) Andrew Bartlett                       2002
+
+   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"
+
+static int sam_skel_debug_level = DBGC_SAM;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS sam_skel_debug_level
+
+/* define the version of the SAM interface */ 
+SAM_MODULE_VERSIONING_MAGIC
+
+/* General API */
+
+static NTSTATUS sam_skel_get_sec_desc(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_set_sec_desc(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+       
+static NTSTATUS sam_skel_lookup_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_lookup_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const char *name, DOM_SID *sid, uint32 *type)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+       
+/* Domain API */
+
+static NTSTATUS sam_skel_update_domain(const SAM_METHODS *sam_methods, const SAM_DOMAIN_HANDLE *domain)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_get_domain_handle(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, SAM_DOMAIN_HANDLE **domain)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/* Account API */
+
+static NTSTATUS sam_skel_create_account(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_add_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_update_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_delete_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_enum_accounts(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+static NTSTATUS sam_skel_get_account_by_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_get_account_by_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_ACCOUNT_HANDLE **account)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/* Group API */
+
+static NTSTATUS sam_skel_create_group(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_add_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_update_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_delete_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_enum_groups(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_get_group_by_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_get_group_by_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+static NTSTATUS sam_skel_add_member_to_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_delete_member_from_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS sam_skel_enum_groupmembers(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+static NTSTATUS sam_skel_get_groups_of_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+       DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO));
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS sam_init_skel(SAM_METHODS *sam_methods, const char *module_params)
+{
+       /* Functions your SAM module doesn't provide should be set 
+        * to NULL */
+
+       sam_methods->sam_get_sec_desc = sam_skel_get_sec_desc;
+       sam_methods->sam_set_sec_desc = sam_skel_set_sec_desc;
+       
+       sam_methods->sam_lookup_sid = sam_skel_lookup_sid;
+       sam_methods->sam_lookup_name = sam_skel_lookup_name;
+       
+       /* Domain API */
+
+       sam_methods->sam_update_domain = sam_skel_update_domain;
+       sam_methods->sam_get_domain_handle = sam_skel_get_domain_handle;
+
+       /* Account API */
+
+       sam_methods->sam_create_account = sam_skel_create_account;
+       sam_methods->sam_add_account = sam_skel_add_account;
+       sam_methods->sam_update_account = sam_skel_update_account;
+       sam_methods->sam_delete_account = sam_skel_delete_account;
+       sam_methods->sam_enum_accounts = sam_skel_enum_accounts;
+
+       sam_methods->sam_get_account_by_sid = sam_skel_get_account_by_sid;
+       sam_methods->sam_get_account_by_name = sam_skel_get_account_by_name;
+
+       /* Group API */
+
+       sam_methods->sam_create_group = sam_skel_create_group;
+       sam_methods->sam_add_group = sam_skel_add_group;
+       sam_methods->sam_update_group = sam_skel_update_group;
+       sam_methods->sam_delete_group = sam_skel_delete_group;
+       sam_methods->sam_enum_groups = sam_skel_enum_groups;
+       sam_methods->sam_get_group_by_sid = sam_skel_get_group_by_sid;
+       sam_methods->sam_get_group_by_name = sam_skel_get_group_by_name;
+
+       sam_methods->sam_add_member_to_group = sam_skel_add_member_to_group;
+       sam_methods->sam_delete_member_from_group = sam_skel_delete_member_from_group;
+       sam_methods->sam_enum_groupmembers = sam_skel_enum_groupmembers;
+
+       sam_methods->sam_get_groups_of_sid = sam_skel_get_groups_of_sid;
+
+       sam_methods->free_private_data = NULL;
+
+
+       sam_skel_debug_level = debug_add_class("sam_skel");
+       if (sam_skel_debug_level == -1) {
+               sam_skel_debug_level = DBGC_SAM;
+               DEBUG(0, ("sam_skel: Couldn't register custom debugging class!\n"));
+       } else DEBUG(2, ("sam_skel: Debug class number of 'sam_skel': %d\n", sam_skel_debug_level));
+    
+       if(module_params)
+               DEBUG(0, ("Starting 'sam_skel' with parameters '%s' for domain %s\n", module_params, sam_methods->domain_name));
+       else
+               DEBUG(0, ("Starting 'sam_skel' for domain %s without paramters\n", sam_methods->domain_name));
+
+       return NT_STATUS_OK;
+}
diff --git a/source4/script/.cvsignore b/source4/script/.cvsignore
new file mode 100644 (file)
index 0000000..7a8114e
--- /dev/null
@@ -0,0 +1 @@
+findsmb
diff --git a/source4/script/addtosmbpass b/source4/script/addtosmbpass
new file mode 100644 (file)
index 0000000..bc82851
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/awk -f
+# edit the line above to point to your real location of awk interpreter
+
+# awk program for adding new entries in smbpasswd files
+# arguments are account names to add; feed it an existent Samba password
+# file on stdin, results will be written on stdout
+#
+# Michal Jaegermann, michal@ellpspace.math.ualberta.ca, 1995-11-09
+
+BEGIN {
+  me = "addtosmbpass";
+  count = ARGC;
+  FS = ":";
+  if (count == 1) {
+    print "Usage:", me,
+          "name1 [name2 ....] < smbpasswd.in >  smbpasswd.out";
+    ARGV[1] = "/dev/null";
+    ARGC = 2;
+    exit;
+  }
+
+  for(i = 1; i < count; i++) {
+    names[ARGV[i]] = " ";
+    delete ARGV[i];
+  }
+# sane awk should work simply with 'ARGC = 1', but not every awk
+# implementation is sane - big sigh!!
+  ARGV[1] = "-";
+  ARGC = 2;
+#
+# If you have ypmatch but is not RPC registered (some Linux systems
+# for example) comment out the next line.
+# "which ypmatch" | getline ypmatch;
+  if (1 != match(ypmatch, /^\//)) {
+    ypmatch = "";
+  }
+  pwdf = "/etc/passwd";
+}
+#check for names already present in input
+{
+  print $0;
+  for(name in names) {
+    if($1 == name) {
+      delete names[name];
+    }
+  }
+}
+END {
+  fmt = "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:";
+  fmt = fmt   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U          ]:LCT-00000000:%s:\n";
+  for(name in names) {
+    while ((getline < pwdf) > 0) {
+      if ($1 == name) {
+       printf(fmt, $1, $3, $5);
+       close(pwdf);
+       notfound = "";
+       break;
+      }
+      notfound = "n";
+    }
+    $0 = "";
+    if (notfound && ypmatch) {
+#     try to find in NIS databases
+      command = ypmatch " " name " passwd";
+      command | getline;
+      if (NF > 0) {
+       printf(fmt, $1, $3, $5);
+      }
+      close(command);
+    }
+  }
+}
+
diff --git a/source4/script/build_env.sh b/source4/script/build_env.sh
new file mode 100644 (file)
index 0000000..0000759
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+uname=`uname -a`
+date=`date`
+srcdir=$1
+builddir=$2
+compiler=$3
+
+       if [ ! "x$USER" = "x" ]; then
+           whoami=$USER
+       else 
+           if [ ! "x$LOGNAME" = "x" ]; then
+               whoami=$LOGNAME
+           else
+               whoami=`whoami || id -un`
+           fi
+       fi
+
+host=`hostname`
+
+cat <<EOF
+/* This file is automatically generated with "make build_env". DO NOT EDIT */
+
+#ifndef _BUILD_ENV_H
+#define _BUILD_ENV_H
+
+#define BUILD_ENV_UNAME "${uname}"
+#define BUILD_ENV_DATE "${date}"
+#define BUILD_ENV_SRCDIR "${srcdir}"
+#define BUILD_ENV_BUILDDIR "${builddir}"
+#define BUILD_ENV_USER "${whoami}"
+#define BUILD_ENV_HOST "${host}"
+#define BUILD_ENV_COMPILER "${compiler}"
+#endif /* _BUILD_ENV_H */
+EOF
diff --git a/source4/script/convert_smbpasswd b/source4/script/convert_smbpasswd
new file mode 100755 (executable)
index 0000000..edb775d
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# Convert a Samba 1.9.18 smbpasswd file format into
+# a Samba 2.0 smbpasswd file format.
+# Read from stdin and write to stdout for simplicity.
+# Set the last change time to 0x363F96AD to avoid problems
+# with trying to work out how to get the seconds since 1970
+# in awk or the shell. JRA.
+#
+nawk 'BEGIN {FS=":"} 
+{
+       if( $0 ~ "^#" ) {
+               print $0
+       } else {
+               printf( "%s:%s:%s:%s:[U          ]:LCT-363F96AD:\n", $1, $2, $3, $4);
+       }
+}'
diff --git a/source4/script/creategroup b/source4/script/creategroup
new file mode 100755 (executable)
index 0000000..01fb065
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Example script for 'add group command'. Handle weird NT group
+# names. First attempt to create the group directly, if that fails
+# then create a random group and print the numeric group id.
+#
+# Note that this is only an example and assumes /dev/urandom.
+# 
+# Volker
+
+GROUPNAME="$1"
+ITERS=0
+
+while ! /usr/sbin/groupadd "$GROUPNAME" > /dev/null 2>&1
+do
+    # we had difficulties creating that group. Maybe the name was
+    # too weird, or it already existed. Create a random name.
+    GROUPNAME=nt-$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | md5sum | cut -b 1-5)
+    ITERS=$(expr "$ITERS" + 1)
+    if [ "$ITERS" -gt 10 ]
+    then
+       # Too many attempts
+       exit 1
+    fi
+done
+
+getent group | grep ^"$GROUPNAME": | cut -d : -f 3
diff --git a/source4/script/extract_allparms.sh b/source4/script/extract_allparms.sh
new file mode 100755 (executable)
index 0000000..f16068b
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+grep '{".*P_[GL]' param/loadparm.c | sed -e 's/&.*$//g' -e 's/",.*P_LOCAL.*$/  S/' -e 's/",.*P_GLOBAL.*$/  G/' -e 's/^ .*{"//g' | sort -f
diff --git a/source4/script/find_missing_doc.pl b/source4/script/find_missing_doc.pl
new file mode 100755 (executable)
index 0000000..b27a405
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+my $doc_file = "/docs/docbook/manpages/smb.conf.5.sgml";
+my $source_file = "/source/param/loadparm.c";
+
+my %link,%doc,%param;
+
+# This one shouldn't be documented at all
+$doc{-valid} = "FOUND";
+
+$topdir = (shift @ARGV) or $topdir = ".";
+
+##################################################
+# Reading links from manpage
+
+open(IN,$topdir.$doc_file);
+
+while(<IN>) {
+       if( /<listitem><para><link linkend="([^"]*)"><parameter>([^<]*)<\/parameter><\/link><\/para><\/listitem>/g ){
+               $link{$2} = $1;
+               $ref{$1} = $2;
+       }
+}
+
+close(IN);
+
+##################################################
+# Reading documentation from manpage
+
+open(IN,$topdir.$doc_file) || die("Can't open $topdir$doc_file");
+
+while(<IN>) {
+       if( /<term><anchor id="([^"]*)"\/>([^<]*?)([ ]*)\(.\)([ ]*)<\/term>/g ) {
+               $key = $1;
+               $value = $2;
+               $doc{$value} = $key;
+
+               # There is a reference to this entry
+               if($ref{$key} eq $value){
+                       $ref{$key} = "FOUND";
+               } else {
+                       if($ref{$key}) {
+                               print "$key should refer to $value, but refers to " . $ref{$key} . "\n";
+                       } else {
+                               print "$key should refer to $value, but has no reference!\n";
+                       }
+                       $ref{$key} = $value;
+               }
+       }
+}
+
+close(IN);
+
+#################################################
+# Reading entries from source code
+
+open(SOURCE,$topdir.$source_file) || die("Can't open $topdir$source_file");
+
+while ($ln = <SOURCE>) {
+  last if $ln =~ m/^static\ struct\ parm_struct\ parm_table.*/;
+} #burn through the preceding lines
+
+while ($ln = <SOURCE>) {
+  last if $ln =~ m/^\s*\}\;\s*$/;
+  #pull in the param names only
+  next if $ln =~ m/.*P_SEPARATOR.*/;
+  next unless $ln =~ /.*\"(.*)\".*/;
+  
+  if($doc{lc($1)}) {
+       $doc{lc($1)} = "FOUND";
+  } else {
+       print "$1 is not documented!\n";
+  }
+}
+close SOURCE;
+
+##################################################
+# Trying to find missing references
+
+foreach (keys %ref) {
+       if($ref{$_} cmp "FOUND") {
+               print "$_ references to " . $ref{$_} . ", but " . $ref{$_} . " isn't an anchor!\n";
+       }
+}
+
+foreach (keys %doc) {
+       if($doc{$_} cmp "FOUND") {
+               print "$_ is documented but is not a configuration option!\n";
+       }
+}
diff --git a/source4/script/findsmb.in b/source4/script/findsmb.in
new file mode 100755 (executable)
index 0000000..6276bd3
--- /dev/null
@@ -0,0 +1,152 @@
+#!@PERL@
+#
+# Prints info on all smb responding machines on a subnet.
+# This script needs to be run on a machine without nmbd running and be
+# run as root to get correct info from WIN95 clients.
+#
+# syntax:
+#    findsmb [-d|-D] [-r] [subnet broadcast address]
+#
+# with no agrument it will list machines on the current subnet
+#
+# There will be a "+" in front of the workgroup name for machines that are
+# local master browsers for that workgroup. There will be an "*" in front
+# of the workgroup name for machines that are the domain master browser for
+# that workgroup.
+# 
+# Options:
+#
+# -d|-D                enable debug
+# -r           add -r option to nmblookup when finding netbios name
+#
+
+$SAMBABIN = "@prefix@/bin";
+
+for ($i = 0; $i < 2; $i++) {   # test for -d and -r options
+  $_ = shift;
+  if (m/-d|-D/) {
+    $DEBUG = 1;
+  } elsif (m/-r/) {
+    $R_OPTION = "-r";
+  }
+}
+
+if ($_) {                      # set broadcast address if it was specified
+      $BCAST = "-B $_";
+}
+
+sub ipsort                     # do numeric sort on last field of IP address
+{
+  @t1 = split(/\./,$a);
+  @t2 = split(/\./,$b);
+  @t1[3] <=> @t2[3];
+}
+
+# look for all machines that respond to a name lookup
+
+open(NMBLOOKUP,"$SAMBABIN/nmblookup $BCAST '*'|") || 
+  die("Can't run nmblookup '*'.\n");
+
+# get rid of all lines that are not a response IP address,
+# strip everything but IP address and sort by last field in address
+
+@ipaddrs = sort ipsort grep(s/ \*<00>.*$//,<NMBLOOKUP>);
+
+# print header info
+
+print "\nIP ADDR         NETBIOS NAME   WORKGROUP/OS/VERSION $BCAST\n";
+print "---------------------------------------------------------------------\n";
+
+foreach $ip (@ipaddrs)         # loop through each IP address found
+{
+  $ip =~ s/\n//;               # strip newline from IP address
+
+# find the netbios names registered by each machine
+
+  open(NMBLOOKUP,"$SAMBABIN/nmblookup $R_OPTION -A $ip|") || 
+       die("Can't get nmb name list.\n");
+  @nmblookup = <NMBLOOKUP>;
+  close NMBLOOKUP;
+
+# get the first <00> name
+
+  @name = grep(/<00>/,@nmblookup);
+  $_ = @name[0];
+  if ($_) {                     # we have a netbios name
+    if (/GROUP/) {             # is it a group name
+       ($name, $aliases, $type, $length, @addresses) = 
+       gethostbyaddr(pack('C4',split('\.',$ip)),2);
+       if (! $name) {                  # could not get name
+           $name = "unknown nis name";
+       }
+    } else {
+# The Netbios name can contain lot of characters also '<' '>'
+# and spaces. The follwing cure inside name space but not
+# names starting or ending with spaces
+       /(.{1,15})\s+<00>\s+/;
+       $name = $1;
+    }
+
+# do an smbclient command on the netbios name.
+
+    open(SMB,"$SAMBABIN/smbclient -N -L $name -I $ip -U% |") ||
+       die("Can't do smbclient command.\n");
+    @smb = <SMB>;
+    close SMB;
+
+    if ($DEBUG) {              # if -d flag print results of nmblookup and smbclient
+      print "===============================================================\n";
+      print @nmblookup;
+      print @smb;
+    }
+
+# look for the OS= string
+
+    @info = grep(/OS=/,@smb);
+    $_ = @info[0];
+    if ($_) {                          # we found response
+      s/Domain=|OS=|Server=|\n//g;     # strip out descriptions to make line shorter
+
+    } else {                           # no OS= string in response (WIN95 client)
+
+# for WIN95 clients get workgroup name from nmblookup response
+      @name = grep(/<00> - <GROUP>/,@nmblookup);
+      $_ = @name[0];
+      if ($_) {
+# Same as before for space and characters
+        /(.{1,15})\s+<00>\s+/;
+        $_ = "[$1]";
+      } else {
+       $_ = "Unknown Workgroup";
+      }
+    }
+
+# see if machine registered a local master browser name
+    if (grep(/<1d>/,@nmblookup)) {
+      $master = '+';                   # indicate local master browser
+      if (grep(/<1b>/,@nmblookup)) {   # how about domain master browser?
+        $master = '*';                 # indicate domain master browser
+      }
+    } else {
+      $master = ' ';                   # not a browse master
+    }
+
+# line up info in 3 columns
+
+    print "$ip".' 'x(16-length($ip))."$name".' 'x(14-length($name))."$master"."$_\n";
+
+  } else {                             # no netbios name found
+# try getting the host name
+    ($name, $aliases, $type, $length, @addresses) = 
+      gethostbyaddr(pack('C4',split('\.',$ip)),2);
+    if (! $name) {                     # could not get name
+      $name = "unknown nis name";
+    }
+    if ($DEBUG) {                      # if -d flag print results of nmblookup
+      print "===============================================================\n";
+      print @nmblookup;
+    }
+    print "$ip".' 'x(16-length($ip))."$name\n";
+  }
+} 
+
diff --git a/source4/script/findstatic.pl b/source4/script/findstatic.pl
new file mode 100755 (executable)
index 0000000..43a4916
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+# find a list of fns and variables in the code that could be static
+# usually called with something like this:
+#    findstatic.pl `find . -name "*.o"`
+# Andrew Tridgell <tridge@samba.org>
+
+use strict;
+
+# use nm to find the symbols
+my($saved_delim) = $/;
+undef $/;
+my($syms) = `nm -o @ARGV`;
+$/ = $saved_delim;
+
+my(@lines) = split(/\n/s, $syms);
+
+my(%def);
+my(%undef);
+my(%stype);
+
+my(%typemap) = (
+              "T" => "function",
+              "C" => "uninitialised variable",
+              "D" => "initialised variable"
+               );
+
+
+# parse the symbols into defined and undefined 
+for (my($i)=0; $i <= $#{@lines}; $i++) {
+       my($line) = $lines[$i];
+       if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
+               my($fname) = $1;
+               my($symbol) = $3;
+               push(@{$def{$fname}}, $symbol);
+               $stype{$symbol} = $2;
+       }
+       if ($line =~ /(.*):\s* U (.*)/) {
+               my($fname) = $1;
+               my($symbol) = $2;
+               push(@{$undef{$fname}}, $symbol);
+       }
+}
+
+# look for defined symbols that are never referenced outside the place they 
+# are defined
+foreach my $f (keys %def) {
+       print "Checking $f\n";
+       my($found_one) = 0;
+       foreach my $s (@{$def{$f}}) {
+               my($found) = 0;
+               foreach my $f2 (keys %undef) {
+                       if ($f2 ne $f) {
+                               foreach my $s2 (@{$undef{$f2}}) {
+                                       if ($s2 eq $s) {
+                                               $found = 1;
+                                               $found_one = 1;
+                                       }
+                               }
+                       }
+               }
+               if ($found == 0) {
+                       my($t) = $typemap{$stype{$s}};
+                       print "  '$s' is unique to $f  ($t)\n";
+               }
+       }
+       if ($found_one == 0) {
+               print "  all symbols in '$f' are unused (main program?)\n";
+       }
+}
+
diff --git a/source4/script/genstruct.pl b/source4/script/genstruct.pl
new file mode 100755 (executable)
index 0000000..081b81f
--- /dev/null
@@ -0,0 +1,298 @@
+#!/usr/bin/perl -w
+# a simple system for generating C parse info
+# this can be used to write generic C structer load/save routines
+# Copyright 2002 Andrew Tridgell <genstruct@tridgell.net>
+# released under the GNU General Public License v2 or later
+
+use strict;
+
+my(%enum_done) = ();
+my(%struct_done) = ();
+
+###################################################
+# general handler
+sub handle_general($$$$$$$$)
+{
+       my($name) = shift;
+       my($ptr_count) = shift;
+       my($size) = shift;
+       my($element) = shift;
+       my($flags) = shift;
+       my($dump_fn) = shift;
+       my($parse_fn) = shift;
+       my($tflags) = shift;
+       my($array_len) = 0;
+       my($dynamic_len) = "NULL";
+
+       # handle arrays, currently treat multidimensional arrays as 1 dimensional
+       while ($element =~ /(.*)\[(.*?)\]$/) {
+               $element = $1;
+               if ($array_len == 0) {
+                       $array_len = $2;
+               } else {
+                       $array_len = "$2 * $array_len";
+               }
+       }
+
+       if ($flags =~ /_LEN\((\w*?)\)/) {
+               $dynamic_len = "\"$1\"";
+       }
+
+       if ($flags =~ /_NULLTERM/) {
+               $tflags = "FLAG_NULLTERM";
+       }
+
+       print OFILE "{\"$element\", $ptr_count, $size, offsetof(struct $name, $element), $array_len, $dynamic_len, $tflags, $dump_fn, $parse_fn},\n";
+}
+
+
+####################################################
+# parse one element
+sub parse_one($$$$)
+{
+       my($name) = shift;
+       my($type) = shift;
+       my($element) = shift;
+       my($flags) = shift;
+       my($ptr_count) = 0;
+       my($size) = "sizeof($type)";
+       my($tflags) = "0";
+       
+       # enums get the FLAG_ALWAYS flag
+       if ($type =~ /^enum /) {
+               $tflags = "FLAG_ALWAYS";
+       }
+
+
+       # make the pointer part of the base type 
+       while ($element =~ /^\*(.*)/) {
+               $ptr_count++;
+               $element = $1;
+       }
+
+       # convert spaces to _
+       $type =~ s/ /_/g;
+
+       my($dump_fn) = "gen_dump_$type";
+       my($parse_fn) = "gen_parse_$type";
+
+       handle_general($name, $ptr_count, $size, $element, $flags, $dump_fn, $parse_fn, $tflags);
+}
+
+####################################################
+# parse one element
+sub parse_element($$$)
+{
+       my($name) = shift;
+       my($element) = shift;
+       my($flags) = shift;
+       my($type);
+       my($data);
+
+       # pull the base type
+       if ($element =~ /^struct (\S*) (.*)/) {
+               $type = "struct $1";
+               $data = $2;
+       } elsif ($element =~ /^enum (\S*) (.*)/) {
+               $type = "enum $1";
+               $data = $2;
+       } elsif ($element =~ /^unsigned (\S*) (.*)/) {
+               $type = "unsigned $1";
+               $data = $2;
+       } elsif ($element =~ /^(\S*) (.*)/) {
+               $type = $1;
+               $data = $2;
+       } else {
+               die "Can't parse element '$element'";
+       }
+
+       # handle comma separated lists 
+       while ($data =~ /(\S*),[\s]?(.*)/) {
+               parse_one($name, $type, $1, $flags);
+               $data = $2;
+       }
+       parse_one($name, $type, $data, $flags);
+}
+
+
+my($first_struct) = 1;
+
+####################################################
+# parse the elements of one structure
+sub parse_elements($$)
+{
+       my($name) = shift;
+       my($elements) = shift;
+
+       if ($first_struct) {
+               $first_struct = 0;
+               print "Parsing structs: $name";
+       } else {
+               print ", $name";
+       }
+
+       print OFILE "int gen_dump_struct_$name(struct parse_string *, const char *, unsigned);\n";
+       print OFILE "int gen_parse_struct_$name(char *, const char *);\n";
+
+       print OFILE "static const struct parse_struct pinfo_" . $name . "[] = {\n";
+
+       while ($elements =~ /^.*?([a-z].*?);\s*?(\S*?)\s*?\$(.*)/msi) {
+               my($element) = $1;
+               my($flags) = $2;
+               $elements = $3;
+               parse_element($name, $element, $flags);
+       }
+
+       print OFILE "{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};\n";
+
+       print OFILE "
+int gen_dump_struct_$name(struct parse_string *p, const char *ptr, unsigned indent) {
+       return gen_dump_struct(pinfo_$name, p, ptr, indent);
+}
+int gen_parse_struct_$name(char *ptr, const char *str) {
+       return gen_parse_struct(pinfo_$name, ptr, str);
+}
+
+";
+}
+
+my($first_enum) = 1;
+
+####################################################
+# parse out the enum declarations
+sub parse_enum_elements($$)
+{
+       my($name) = shift;
+       my($elements) = shift;
+
+       if ($first_enum) {
+               $first_enum = 0;
+               print "Parsing enums: $name";
+       } else {
+               print ", $name";
+       }
+
+       print OFILE "static const struct enum_struct einfo_" . $name . "[] = {\n";
+
+       my(@enums) = split(/,/s, $elements);
+       for (my($i)=0; $i <= $#{@enums}; $i++) {
+               my($enum) = $enums[$i];
+               if ($enum =~ /\s*(\w*)/) {
+                       my($e) = $1;
+                       print OFILE "{\"$e\", $e},\n";
+               }
+       }
+
+       print OFILE "{NULL, 0}};\n";
+
+       print OFILE "
+int gen_dump_enum_$name(struct parse_string *p, const char *ptr, unsigned indent) {
+       return gen_dump_enum(einfo_$name, p, ptr, indent);
+}
+
+int gen_parse_enum_$name(char *ptr, const char *str) {
+       return gen_parse_enum(einfo_$name, ptr, str);
+}
+
+";
+}
+
+####################################################
+# parse out the enum declarations
+sub parse_enums($)
+{
+       my($data) = shift;
+
+       while ($data =~ /^GENSTRUCT\s+enum\s+(\w*?)\s*{(.*?)}\s*;(.*)/ms) {
+               my($name) = $1;
+               my($elements) = $2;
+               $data = $3;
+
+               if (!defined($enum_done{$name})) {
+                       $enum_done{$name} = 1;
+                       parse_enum_elements($name, $elements);
+               }
+       }
+
+       if (! $first_enum) {
+               print "\n";
+       }
+}
+
+####################################################
+# parse all the structures
+sub parse_structs($)
+{
+       my($data) = shift;
+
+       # parse into structures 
+       while ($data =~ /^GENSTRUCT\s+struct\s+(\w+?)\s*{\s*(.*?)\s*}\s*;(.*)/ms) {
+               my($name) = $1;
+               my($elements) = $2;
+               $data = $3;
+               if (!defined($struct_done{$name})) {
+                       $struct_done{$name} = 1;
+                       parse_elements($name, $elements);
+               }
+       }
+
+       if (! $first_struct) {
+               print "\n";
+       } else {
+               print "No GENSTRUCT structures found?\n";
+       }
+}
+
+
+####################################################
+# parse a header file, generating a dumper structure
+sub parse_data($)
+{
+       my($data) = shift;
+
+       # collapse spaces 
+       $data =~ s/[\t ]+/ /sg;
+       $data =~ s/\s*\n\s+/\n/sg;
+       # strip debug lines
+       $data =~ s/^\#.*?\n//smg;
+
+       parse_enums($data);
+       parse_structs($data);
+}
+
+
+#########################################
+# display help text
+sub ShowHelp()
+{
+    print "
+generator for C structure dumpers
+Copyright Andrew Tridgell <genstruct\@tridgell.net>
+
+Sample usage:
+   genstruct -o output.h gcc -E -O2 -g test.h
+
+Options:
+    --help                this help page
+    -o OUTPUT             place output in OUTPUT
+";
+    exit(0);
+}
+
+########################################
+# main program
+if ($ARGV[0] ne "-o" || $#ARGV < 2) {
+       ShowHelp();
+}
+
+shift;
+my($opt_ofile)=shift;
+
+print "creating $opt_ofile\n";
+
+open(OFILE, ">$opt_ofile") || die "can't open $opt_ofile";    
+
+print OFILE "/* This is an automatically generated file - DO NOT EDIT! */\n\n";
+
+parse_data(`@ARGV -DGENSTRUCT=GENSTRUCT`);
+exit(0);
diff --git a/source4/script/installbin.sh b/source4/script/installbin.sh
new file mode 100644 (file)
index 0000000..c2f3408
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+BASEDIR=$2
+BINDIR=$3
+LIBDIR=$4
+VARDIR=$5
+shift
+shift
+shift
+shift
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+   rm -f $BINDIR/$p2.old
+   mv $BINDIR/$p2 $BINDIR/$p2.old
+ fi
+ cp $p $BINDIR/
+ chmod $INSTALLPERMS $BINDIR/$p2
+
+ # this is a special case, mount needs this in a specific location
+ if [ $p2 = smbmount ]; then
+   ln -sf $BINDIR/$p2 /sbin/mount.smbfs
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries are installed. You may restore the old binaries (if there
+were any) using the command "make revert". You may uninstall the binaries
+using the command "make uninstallbin" or "make uninstall" to uninstall
+binaries, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/installdat.sh b/source4/script/installdat.sh
new file mode 100755 (executable)
index 0000000..7ff88ac
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#fist version March 2002, Herb  Lewis
+
+DATDIR=$1
+SRCDIR=$2/
+
+echo Installing dat files in $DATDIR
+
+for f in $SRCDIR/codepages/*.dat; do
+       FNAME=$DATDIR/`basename $f`
+       echo $FNAME
+       cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+       chmod 0644 $FNAME
+done
+
+cat << EOF
+======================================================================
+The dat files have been installed. 
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source4/script/installdirs.sh b/source4/script/installdirs.sh
new file mode 100755 (executable)
index 0000000..9557b86
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+while ( test -n "$1" ); do
+       if [ ! -d $1 ]; then
+               mkdir -p $1
+       fi
+
+       if [ ! -d $1 ]; then
+               echo Failed to make directory $1
+               exit 1
+       fi
+
+       shift;
+done
+
+
+
diff --git a/source4/script/installman.sh b/source4/script/installman.sh
new file mode 100644 (file)
index 0000000..5b6bba6
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+#5 July 96 Dan.Shearer@unisa.edu.au  removed hardcoded values
+#
+# 13 Aug 2001  Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+#   modified to accomodate international man pages (inspired
+#   by Japanese edition's approach)
+
+MANDIR=$1
+SRCDIR=$2/
+langs=$3
+
+if [ $# -ge 4 ] ; then
+  GROFF=$4                    # sh cmd line, including options 
+fi
+
+
+for lang in $langs; do
+    if [ "X$lang" = Xen ]; then
+       echo Installing default man pages in $MANDIR/
+       lang=.
+    else
+       echo Installing \"$lang\" man pages in $MANDIR/lang/$lang
+    fi
+
+    langdir=$MANDIR/$lang
+    for d in $MANDIR $langdir $langdir/man1 $langdir/man5 $langdir/man7 $langdir/man8; do
+       if [ ! -d $d ]; then
+           mkdir $d
+           if [ ! -d $d ]; then
+               echo Failed to make directory $d, does $USER have privileges?
+               exit 1
+           fi
+       fi
+    done
+
+    for sect in 1 5 7 8 ; do
+       for m in $langdir/man$sect ; do
+           for s in $SRCDIR../docs/manpages/$lang/*$sect; do
+           FNAME=$m/`basename $s`
+           # Test for writability.  Involves 
+           # blowing away existing files.
+           if (rm -f $FNAME && touch $FNAME); then
+               rm $FNAME
+               if [ "x$GROFF" = x ] ; then
+                   cp $s $m            # Copy raw nroff 
+               else
+                   echo "\t$FNAME"     # groff'ing can be slow, give the user
+                                       #   a warm fuzzy.
+                   $GROFF $s > $FNAME  # Process nroff, because man(1) (on
+                                       #   this system) doesn't .
+               fi
+               chmod 0644 $FNAME
+           else
+               echo Cannot create $FNAME... does $USER have privileges?
+           fi
+           done
+       done
+    done
+done
+cat << EOF
+======================================================================
+The man pages have been installed. You may uninstall them using the command
+the command "make uninstallman" or make "uninstall" to uninstall binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source4/script/installmodules.sh b/source4/script/installmodules.sh
new file mode 100644 (file)
index 0000000..ec56919
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+BASEDIR=$2
+LIBDIR=$3
+shift
+shift
+shift
+
+for d in $BASEDIR $LIBDIR; do
+if [ ! -d $d ]; then
+mkdir $d
+if [ ! -d $d ]; then
+  echo Failed to make directory $d
+  exit 1
+fi
+fi
+done
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $LIBDIR/$p2
+ cp -f $p $LIBDIR/
+ chmod $INSTALLPERMS $LIBDIR/$p2
+done
+
+
+cat << EOF
+======================================================================
+The modules are installed.  You may uninstall the modules using the 
+command "make uninstallmodules" or "make uninstall" to uninstall
+binaries, man pages, shell scripts and modules.
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/installscripts.sh b/source4/script/installscripts.sh
new file mode 100644 (file)
index 0000000..bff5423
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh
+# this script courtesy of James_K._Foote.PARC@xerox.com
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au  Don't hardcode script names, get from Make
+
+INSTALLPERMS=$1
+BINDIR=$2
+
+shift
+shift
+
+echo Installing scripts in $BINDIR
+
+for d in $BINDIR; do
+ if [ ! -d $d ]; then
+  mkdir $d
+  if [ ! -d $d ]; then
+    echo Failed to make directory $d
+    echo Have you run installbin first?
+    exit 1
+  fi
+ fi
+done
+
+for p in $*; do
+  p2=`basename $p`
+  echo Installing $BINDIR/$p2
+  if [ -f $BINDIR/$p2 ]; then
+    rm -f $BINDIR/$p2.old
+    mv $BINDIR/$p2 $BINDIR/$p2.old
+  fi
+  cp $p $BINDIR/
+  chmod $INSTALLPERMS $BINDIR/$p2
+  if [ ! -f $BINDIR/$p2 ]; then
+    echo Cannot copy $p2... does $USER have privileges?
+  fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been installed. You may uninstall them using
+the command "make uninstallscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover the previous version (if any
+by "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/installswat.sh b/source4/script/installswat.sh
new file mode 100644 (file)
index 0000000..c66604c
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+#fist version March 1998, Andrew Tridgell
+
+SWATDIR=$1
+SRCDIR=$2/
+BOOKDIR=$SWATDIR/using_samba
+
+echo Installing SWAT in $SWATDIR
+echo Installing the Samba Web Administration Tool
+
+LANGS=". `cd $SRCDIR../swat/; /bin/echo lang/??`"
+echo Installing langs are `cd $SRCDIR../swat/lang/; /bin/echo ??`
+
+for ln in $LANGS; do 
+ SWATLANGDIR=$SWATDIR/$ln
+ for d in $SWATLANGDIR $SWATLANGDIR/help $SWATLANGDIR/images \
+       $SWATLANGDIR/include; do
+    if [ ! -d $d ]; then
+       mkdir -p $d
+       if [ ! -d $d ]; then
+           echo Failed to make directory $d, does $USER have privileges?
+           exit 1
+       fi
+    fi
+ done
+done
+
+# Install images
+for ln in $LANGS; do
+
+for f in $SRCDIR../swat/$ln/images/*.gif; do
+      FNAME=$SWATDIR/$ln/images/`basename $f`
+      echo $FNAME
+      cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+      chmod 0644 $FNAME
+done
+
+# Install html help
+
+for f in $SRCDIR../swat/$ln/help/*.html; do
+      FNAME=$SWATDIR/$ln/help/`basename $f`
+      echo $FNAME
+      if [ "x$BOOKDIR" = "x" ]; then
+        cat $f | sed 's/@BOOKDIR@.*$//' > $f.tmp
+      else
+        cat $f | sed 's/@BOOKDIR@//' > $f.tmp
+      fi
+      f=$f.tmp
+      cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+      rm -f $f
+      chmod 0644 $FNAME
+done
+
+# Install html documentation
+
+for f in $SRCDIR../docs/htmldocs/*.html; do
+      FNAME=$SWATDIR/help/`basename $f`
+      echo $FNAME
+      cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+      chmod 0644 $FNAME
+done
+
+# Install "server-side" includes
+
+for f in $SRCDIR../swat/$ln/include/*.html; do
+      FNAME=$SWATDIR/$ln/include/`basename $f`
+      echo $FNAME
+      cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+      chmod 0644 $FNAME
+done
+
+done
+
+# Install Using Samba book
+
+if [ "x$BOOKDIR" != "x" ]; then
+
+    # Create directories
+
+    for d in $BOOKDIR $BOOKDIR/figs $BOOKDIR/gifs; do
+        if [ ! -d $d ]; then
+            mkdir $d
+            if [ ! -d $d ]; then
+                echo Failed to make directory $d, does $USER have privileges?
+                exit 1
+            fi
+        fi
+    done
+
+    # HTML files
+
+    for f in $SRCDIR../docs/htmldocs/using_samba/*.html; do
+        FNAME=$BOOKDIR/`basename $f`
+        echo $FNAME
+        cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+        chmod 0644 $FNAME
+    done
+
+    # Figures
+
+    for f in $SRCDIR../docs/htmldocs/using_samba/figs/*.gif; do
+        FNAME=$BOOKDIR/figs/`basename $f`
+        echo $FNAME
+        cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+        chmod 0644 $FNAME
+    done
+
+    # Gifs
+
+    for f in $SRCDIR../docs/htmldocs/using_samba/gifs/*.gif; do
+        FNAME=$BOOKDIR/gifs/`basename $f`
+        echo $FNAME
+        cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+        chmod 0644 $FNAME
+    done
+
+fi
+
+cat << EOF
+======================================================================
+The SWAT files have been installed. Remember to read the swat/README
+for information on enabling and using SWAT
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source4/script/makeunicodecasemap.awk b/source4/script/makeunicodecasemap.awk
new file mode 100644 (file)
index 0000000..8424b6c
--- /dev/null
@@ -0,0 +1,59 @@
+function reset_vals() {
+       upperstr = "";
+       lowerstr = "";
+       flagstr = "0";
+}
+
+function print_val() {
+       upperstr = $13;
+       lowerstr = $14;
+       if ( upperstr == "" )
+               upperstr = strval;
+       if ( lowerstr == "" )
+               lowerstr = strval;
+
+       if ( $3 == "Lu" )
+               flagstr = sprintf("%s|%s", flagstr, "UNI_UPPER");
+       if ( $3 == "Ll" )
+               flagstr = sprintf("%s|%s", flagstr, "UNI_LOWER");
+       if ( val >= 48 && val <= 57)
+               flagstr = sprintf("%s|%s", flagstr, "UNI_DIGIT");
+       if ((val >= 48 && val <= 57) || (val >= 65 && val <= 70) || (val >=97 && val <= 102))
+               flagstr = sprintf("%s|%s", flagstr, "UNI_XDIGIT");
+       if ( val == 32 || (val >=9 && val <= 13))
+               flagstr = sprintf("%s|%s", flagstr, "UNI_SPACE");
+       if( index(flagstr, "0|") == 1)
+               flagstr = substr(flagstr, 3, length(flagstr) - 2);
+       printf("{ 0x%s, 0x%s, %s }, \t\t\t/* %s %s */\n", lowerstr, upperstr, flagstr, strval, $2);
+       val++;
+       strval=sprintf("%04X", val);
+       reset_vals();
+}
+
+BEGIN {
+       val=0
+       FS=";"
+       strval=sprintf("%04X", val);
+       reset_vals();
+}
+
+{
+       if ( $1 == strval ) {
+               print_val();
+       } else {
+               while ( $1 != strval) {
+                       printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+                       val++;
+                       strval=sprintf("%04X", val);
+               }
+               print_val();
+       }
+}
+
+END {
+       while ( val < 65536 ) {
+               printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+               val++;
+               strval=sprintf("%04X", val);
+       }
+}
diff --git a/source4/script/mkinstalldirs b/source4/script/mkinstalldirs
new file mode 100755 (executable)
index 0000000..f945dbf
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp" 1>&2
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+         errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/source4/script/mknissmbpasswd.sh b/source4/script/mknissmbpasswd.sh
new file mode 100755 (executable)
index 0000000..a94c963
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Script to import smbpasswd file into the smbpasswd NIS+ table. Reads
+# from stdin the smbpasswd file.
+#
+while true
+do
+  read row
+  if [ -z "$row" ]
+  then
+    break
+  fi
+
+  if [ "`echo $row | cut -c1`" = "#" ]
+  then
+    continue
+  fi
+
+  nistbladm -a \
+    name=\"`echo $row | cut -d: -f1`\" \
+    uid=\"`echo $row | cut -d: -f2`\" \
+    lmpwd=\"`echo $row | cut -d: -f3`\" \
+    ntpwd=\"`echo $row | cut -d: -f4`\" \
+    acb=\"`echo $row | cut -d: -f5`\" \
+    pwdlset_t=\"`echo $row | cut -d: -f6`\" \
+    gcos=\"`echo $row | cut -d: -f7`\" \
+    home=\"`echo $row | cut -d: -f8`\" \
+    shell=\"`echo $row | cut -d: -f9`\"  smbpasswd.org_dir.`nisdefaults -d`
+done
diff --git a/source4/script/mknissmbpwdtbl.sh b/source4/script/mknissmbpwdtbl.sh
new file mode 100755 (executable)
index 0000000..a9b34ff
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Creates smbpasswd table and smb group in NIS+
+#
+
+nistbladm \
+    -D access=og=rmcd,nw= -c \
+    -s : smbpasswd_tbl \
+       name=S,nogw=r \
+       uid=S,nogw=r \
+               user_rid=S,nogw=r \
+               smb_grpid=,nw+r \
+               group_rid=,nw+r \
+               acb=,nw+r \
+                         \
+       lmpwd=C,nw=,g=r,o=rm \
+       ntpwd=C,nw=,g=r,o=rm \
+                                    \
+               logon_t=,nw+r \
+               logoff_t=,nw+r \
+               kick_t=,nw+r \
+               pwdlset_t=,nw+r \
+               pwdlchg_t=,nw+r \
+               pwdmchg_t=,nw+r \
+                               \
+               full_name=,nw+r \
+               home_dir=,nw+r \
+               dir_drive=,nw+r \
+               logon_script=,nw+r \
+               profile_path=,nw+r \
+               acct_desc=,nw+r \
+               workstations=,nw+r \
+                                  \
+               hours=,nw+r \
+       smbpasswd.org_dir.`nisdefaults -d`
+
+nisgrpadm -c smb.`nisdefaults -d`
+
+nischgrp smb.`nisdefaults -d` smbpasswd.org_dir.`nisdefaults -d`
+
diff --git a/source4/script/mkproto.awk b/source4/script/mkproto.awk
new file mode 100644 (file)
index 0000000..999066e
--- /dev/null
@@ -0,0 +1,165 @@
+BEGIN {
+  inheader=0;
+#  use_ldap_define = 0;
+  current_file="";
+  if (headername=="") {
+    headername="_PROTO_H_";
+  }
+
+  print "#ifndef",headername
+  print "#define",headername
+  print ""
+  print "/* This file is automatically generated with \"make proto\". DO NOT EDIT */"
+  print ""
+}
+
+END {
+  print ""
+  print "#endif /* ",headername," */"
+}
+
+{
+  if (FILENAME!=current_file) {
+#    if (use_ldap_define)
+#    {
+#      print "#endif /* USE_LDAP */"
+#      use_ldap_define = 0;
+#    }
+    print ""
+    print "/* The following definitions come from",FILENAME," */"
+    print ""
+    current_file=FILENAME
+  }
+  if (inheader) {
+    if (match($0,"[)][ \t]*$")) {
+      inheader = 0;
+      printf "%s;\n",$0;
+    } else {
+      printf "%s\n",$0;
+    }
+    next;
+  }
+}
+
+# special handling for code merge of TNG to head
+/^#define OLD_NTDOMAIN 1/ {
+  printf "#if OLD_NTDOMAIN\n"
+}
+/^#undef OLD_NTDOMAIN/ {
+  printf "#endif\n"
+}
+/^#define NEW_NTDOMAIN 1/ {
+  printf "#if NEW_NTDOMAIN\n"
+}
+/^#undef NEW_NTDOMAIN/ {
+  printf "#endif\n"
+}
+
+# we handle the loadparm.c fns separately
+
+/^FN_LOCAL_BOOL/ {
+  split($0,a,"[,()]")
+  printf "BOOL %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_LIST/ {
+  split($0,a,"[,()]")
+  printf "const char **%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_STRING/ {
+  split($0,a,"[,()]")
+  printf "char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CONST_STRING/ {
+  split($0,a,"[,()]")
+  printf "const char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_INT/ {
+  split($0,a,"[,()]")
+  printf "int %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CHAR/ {
+  split($0,a,"[,()]")
+  printf "char %s(int );\n", a[2]
+}
+
+/^FN_GLOBAL_BOOL/ {
+  split($0,a,"[,()]")
+  printf "BOOL %s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_LIST/ {
+  split($0,a,"[,()]")
+  printf "const char **%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_STRING/ {
+  split($0,a,"[,()]")
+  printf "char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_CONST_STRING/ {
+  split($0,a,"[,()]")
+  printf "const char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_INT/ {
+  split($0,a,"[,()]")
+  printf "int %s(void);\n", a[2]
+}
+
+/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
+  next;
+}
+
+#
+# We have to split up the start
+# matching as we now have so many start
+# types that it can cause some versions
+# of nawk/awk to choke and fail on
+# the full match. JRA.
+#
+
+{
+  gotstart = 0;
+  if( $0 ~ /^const|^connection_struct|^pipes_struct|^smb_np_struct|^file_fd_struct|^files_struct|^connection_struct|^uid_t|^gid_t|^unsigned|^mode_t|^DIR|^user|^int|^pid_t|^ino_t|^off_t|^double/ ) {
+    gotstart = 1;
+  }
+
+  if( $0 ~ /^vuser_key|^UNISTR2|^LOCAL_GRP|^DOMAIN_GRP|^SMB_STRUCT_DIRENT|^SEC_ACL|^SEC_DESC|^SEC_DESC_BUF|^DOM_SID|^RPC_HND_NODE|^BYTE/ ) {
+    gotstart = 1;
+  }
+
+  if( $0 ~ /^ADS_STRUCT|^ADS_STATUS|^DATA_BLOB|^ASN1_DATA|^TDB_CONTEXT|^TDB_DATA|^smb_ucs2_t|^TALLOC_CTX|^hash_element|^NT_DEVICEMODE|^enum.*\(|^NT_USER_TOKEN|^SAM_ACCOUNT/ ) {
+    gotstart = 1;
+  }
+
+  if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT/ ) {
+    gotstart = 1;
+  }
+
+  if( $0 ~ /^SAM_ACCT_INFO_NODE|^SMB_ACL_T|^ADS_MODLIST|^PyObject|^SORTED_TREE|^REGISTRY_HOOK|^REGISTRY_VALUE|^NTTIME|^DEVICEMODE/ ) {
+    gotstart = 1;
+  }
+
+  if(!gotstart) {
+    next;
+  }
+}
+
+
+/[(].*[)][ \t]*$/ {
+    printf "%s;\n",$0;
+    next;
+}
+
+/[(]/ {
+  inheader=1;
+  printf "%s\n",$0;
+  next;
+}
+
diff --git a/source4/script/mkproto.sh b/source4/script/mkproto.sh
new file mode 100644 (file)
index 0000000..2bf96c9
--- /dev/null
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+LANG=C; export LANG
+LC_ALL=C; export LC_ALL
+LC_COLLATE=C; export LC_COLLATE
+
+if [ $# -lt 3 ]
+then
+  echo "Usage: $0 awk [-h headerdefine] outputheader proto_obj"
+  exit 1
+fi
+
+awk="$1"
+shift
+
+if [ x"$1" = x-h ]
+then
+  headeropt="-v headername=$2"
+  shift; shift;
+else
+  headeropt=""
+fi
+
+header="$1"
+shift
+headertmp="$header.$$.tmp~"
+
+proto_src="`echo $@ | tr ' ' '\n' | sed -e 's/\.o/\.c/g' | sort | uniq | egrep -v 'ubiqx/|wrapped'`"
+
+echo creating $header
+
+mkdir -p `dirname $header`
+
+${awk} $headeropt \
+  -f script/mkproto.awk $proto_src > $headertmp
+
+if cmp -s $header $headertmp 2>/dev/null
+then
+  echo "$header unchanged"
+  rm $headertmp
+else
+  mv $headertmp $header
+fi
diff --git a/source4/script/mksmbpasswd.sh b/source4/script/mksmbpasswd.sh
new file mode 100644 (file)
index 0000000..854e1bd
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+awk 'BEGIN {FS=":"
+       printf("#\n# SMB password file.\n#\n")
+       }
+{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U          ]:LCT-00000000:%s\n", $1, $3, $5) }
+'
diff --git a/source4/script/revert.sh b/source4/script/revert.sh
new file mode 100644 (file)
index 0000000..8df5fd2
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+BINDIR=$1
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2.old ]; then
+   echo Restoring $BINDIR/$p2.old
+   mv $BINDIR/$p2 $BINDIR/$p2.new
+   mv $BINDIR/$p2.old $BINDIR/$p2
+   rm -f $BINDIR/$p2.new
+ else
+   echo Not restoring $p
+ fi
+done
+
+exit 0
+
diff --git a/source4/script/scancvslog.pl b/source4/script/scancvslog.pl
new file mode 100755 (executable)
index 0000000..c39f911
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+require"timelocal.pl";
+
+#
+# usage scancvslog.pl logfile starttime tag
+#
+# this will extract all entries from the specified cvs log file
+# that have a date later than or equal to starttime and a tag
+# value of tag. If starttime is not specified, all entries are
+# extracted. If tag is not specified then entries for all
+# branches are extracted. starttime must be specified as
+# "monthname day, year"
+#
+# Example to extract all entries for SAMBA_2_2 branch from the
+# log file named cvs.log 
+#
+# scancvslog.pl cvs.log "" SAMBA_2_2
+#
+#
+# To extract all log entries after Jan 10, 1999 (Note month name
+# must be spelled out completely).
+#
+# scancvslog.pl cvs.log "January 10, 1999" 
+#
+
+open(INFILE,@ARGV[0]) || die "Unable to open @ARGV[0]\n";
+
+%Monthnum = (
+       "January",      0,
+       "February",     1,
+       "March",        2,
+       "April",        3,
+       "May",          4,
+       "June",         5,
+       "July",         6,
+       "August",       7,
+       "September",    8,
+       "October",      9,
+       "November",     10,
+       "December",     11,
+       "Jan",          0,
+       "Feb",          1,
+       "Mar",          2,
+       "Apr",          3,
+       "May",          4,
+       "Jun",          5,
+       "Jul",          6,
+       "Aug",          7,
+       "Sep",          8,
+       "Oct",          9,
+       "Nov",          10,
+       "Dec",          11
+);
+
+$Starttime = (@ARGV[1]) ? &make_time(@ARGV[1]) : 0;
+$Tagvalue = @ARGV[2];
+
+while (&get_entry) {
+  $_=$Entry[0];
+# get rid of extra white space
+  s/\s+/ /g;
+# get rid of any time string in date
+  s/ \d\d:\d\d:\d\d/,/;
+  s/^Date:\s*\w*\s*(\w*)\s*(\w*),\s*(\w*).*/$1 $2 $3/;
+  $Testtime = &make_time($_);
+  $Testtag = &get_tag;
+  if (($Testtime >= $Starttime) && ($Tagvalue eq $Testtag)) {
+    print join("\n",@Entry),"\n";
+  }
+}
+close(INFILE);
+
+sub make_time {
+  $_ = @_[0];
+  s/,//;
+  ($month, $day, $year) = split(" ",$_);
+  if (($year < 1900)||($day < 1)||($day > 31)||not length($Monthnum{$month})) {
+    print "Bad date format @_[0]\n";
+    print "Date needs to be specified as \"Monthname day, year\"\n";
+    print "eg: \"January 10, 1999\"\n";
+    exit 1;
+  }
+  $year = ($year == 19100) ? 2000 : $year;
+  $month = $Monthnum{$month};
+  $Mytime=&timelocal((0,0,0,$day,$month,$year));
+}
+
+sub get_tag {
+  @Mytag = grep (/Tag:/,@Entry);
+  $_ = @Mytag[0];
+  s/^.*Tag:\s*(\w*).*/$1/;
+  return $_;
+}
+
+sub get_entry {
+  @Entry=();
+  if (not eof(INFILE)) {
+    while (not eof(INFILE)) {
+      $_ = <INFILE>;
+      chomp $_;
+      next if (not ($_));
+      if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/) {
+       next if ($#Entry == -1);
+       push(Entry,$_);
+       return @Entry;
+      } else {
+       push(Entry,$_);
+      }
+    }
+  }
+  return @Entry;
+}
diff --git a/source4/script/smbtar b/source4/script/smbtar
new file mode 100644 (file)
index 0000000..f062cba
--- /dev/null
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# smbtar script - front end to smbclient
+#
+# Authors: Martin.Kraemer <Martin.Kraemer@mch.sni.de>
+#          and Ricky Poulten (ricky@logcam.co.uk)
+#
+# (May need to change shell to ksh for HPUX or OSF for better getopts)
+#
+# sandy nov 3 '98 added -a flag
+#
+# Richard Sharpe, added -c 'tarmode full' so that we back up all files to
+# fix a bug in clitar when a patch was added to stop system and hidden files
+# being backed up.
+
+case $0 in
+    # when called by absolute path, assume smbclient is in the same directory
+    /*)
+       SMBCLIENT="`dirname $0`/smbclient";;
+    *)  # you may need to edit this to show where your smbclient is
+       SMBCLIENT="smbclient";;
+esac
+
+# These are the default values. You could fill them in if you know what
+# you're doing, but beware: better not store a plain text password!
+server=""
+service="backup"            # Default: a service called "backup"
+password=""
+username=$LOGNAME           # Default: same user name as in *nix
+verbose="2>/dev/null"        # Default: no echo to stdout
+log="-d 2"
+newer=""
+newerarg=""
+blocksize=""
+blocksizearg=""
+clientargs="-c 'tarmode full'"
+tarcmd="c"
+tarargs=""
+cdcmd="\\"
+tapefile=${TAPE-tar.out}
+
+Usage(){
+    ex=$1
+    shift
+echo >&2 "Usage: `basename $0` [<options>] [<include/exclude files>]
+Function: backup/restore a Windows PC directories to a local tape file
+Options:         (Description)                 (Default)
+  -r             Restore from tape file to PC  Save from PC to tapefile
+  -i             Incremental mode              Full backup mode
+  -a             Reset archive bit mode        Don't reset archive bit
+  -v             Verbose mode: echo command    Don't echo anything
+  -s <server>    Specify PC Server             $server
+  -p <password>  Specify PC Password           $password
+  -x <share>     Specify PC Share              $service
+  -X             Exclude mode                  Include
+  -N <newer>     File for date comparison      `set -- $newer; echo $2`
+  -b <blocksize> Specify tape's blocksize      `set -- $blocksize; echo $2`
+  -d <dir>       Specify a directory in share  $cdcmd
+  -l <log>       Specify a Samba Log Level     `set -- $log; echo $2`
+  -u <user>      Specify User Name             $username
+  -t <tape>      Specify Tape device           $tapefile
+"
+  echo >&2 "$@"
+  exit $ex
+}
+
+# echo Params count: $#
+
+# DEC OSF AKA Digital UNIX does not seem to return a value in OPTIND if 
+# there are no command line params, so protect us against that ...
+if [ $# = 0 ]; then
+
+  Usage 2 "Please enter a command line parameter!"
+
+fi
+
+while getopts riavl:b:d:N:s:p:x:u:Xt: c; do
+  case $c in
+   r) # [r]estore to Windows (instead of the default "Save from Windows")
+      tarcmd="x"
+      ;;
+   i) # [i]ncremental
+      tarargs=${tarargs}ga
+      clientargs="-c 'tarmode inc'"
+      ;;
+   a) # [a]rchive
+      tarargs=${tarargs}a
+      ;;
+   l) # specify [l]og file
+      log="-d $OPTARG"
+      case "$OPTARG" in
+       [0-9]*) ;;
+       *)      echo >&2 "$0: Error, log level not numeric: -l $OPTARG"
+               exit 1
+      esac
+      ;;
+   d) # specify [d]irectory to change to in server's share
+      cdcmd="$OPTARG"
+      ;;
+   N) # compare with a file, test if [n]ewer
+      if [ -f $OPTARG ]; then
+       newer=$OPTARG
+       newerarg="N"
+      else
+       echo >&2 $0: Warning, $OPTARG not found
+      fi
+      ;;
+   X) # Add exclude flag
+      tarargs=${tarargs}X
+      ;;
+   s) # specify [s]erver's share to connect to - this MUST be given.
+      server="$OPTARG"
+      ;;
+   b) # specify [b]locksize
+      blocksize="$OPTARG"
+      case "$OPTARG" in
+       [0-9]*) ;;
+       *)      echo >&2 "$0: Error, block size not numeric: -b $OPTARG"
+               exit 1
+      esac
+      blocksizearg="b"
+      ;;
+   p) # specify [p]assword to use
+      password="$OPTARG"
+      ;;
+   x) # specify windows [s]hare to use
+      service="$OPTARG"
+      ;;
+   t) # specify [t]apefile on local host
+      tapefile="$OPTARG"
+      ;;
+   u) # specify [u]sername for connection
+      username="$OPTARG"
+      ;;
+   v) # be [v]erbose and display what's going on
+      verbose=""
+      ;;
+   '?') # any other switch
+       Usage 2 "Invalid switch specified - abort."
+      ;;
+  esac
+done
+
+shift `expr $OPTIND - 1`
+
+if [ "$server" = "" ] || [ "$service" = "" ]; then
+  Usage 1 "No server or no service specified - abort."
+fi
+
+# if the -v switch is set, the echo the current parameters
+if [ -z "$verbose" ]; then
+      echo "server    is $server"
+#     echo "share     is $service"
+      echo "share     is $service\\$cdcmd"
+      echo "tar args  is $tarargs"
+#     echo "password  is $password"  # passwords should never be sent to screen
+      echo "tape      is $tapefile"
+      echo "blocksize is $blocksize"
+fi
+
+tarargs=${tarargs}${blocksizearg}${newerarg}
+
+eval $SMBCLIENT "'\\\\$server\\$service'" "'$password'" -U "'$username'" \
+-E -N $log -D "'$cdcmd'" ${clientargs} \
+-T${tarcmd}${tarargs} $blocksize $newer $tapefile '${1+"$@"}' $verbose
diff --git a/source4/script/uninstallbin.sh b/source4/script/uninstallbin.sh
new file mode 100644 (file)
index 0000000..a8bbdea
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au   
+
+INSTALLPERMS=$1
+BASEDIR=$2
+BINDIR=$3
+LIBDIR=$4
+VARDIR=$5
+shift
+shift
+shift
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+  echo Directory $BINDIR does not exist!
+  echo Do a "make installbin" or "make install" first.
+  exit 1
+fi
+
+for p in $*; do
+  p2=`basename $p`
+  if [ -f $BINDIR/$p2 ]; then
+    echo Removing $BINDIR/$p2
+    rm -f $BINDIR/$p2
+    if [ -f $BINDIR/$p2 ]; then
+      echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+    fi
+  fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries have been uninstalled. You may restore the binaries using
+the command "make installbin" or "make install" to install binaries, 
+man pages, modules and shell scripts. You can restore a previous
+version of the binaries (if there were any) using "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/uninstallman.sh b/source4/script/uninstallman.sh
new file mode 100644 (file)
index 0000000..3126709
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+#
+# 13 Aug 2001  Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+#   modified to accomodate international man pages (inspired
+#   by Japanese edition's approach)
+
+
+MANDIR=$1
+SRCDIR=$2
+langs=$3
+
+for lang in $langs; do
+  echo Uninstalling \"$lang\" man pages from $MANDIR/$lang
+
+  for sect in 1 5 7 8 ; do
+    for m in $MANDIR/$lang/man$sect ; do
+      for s in $SRCDIR/../docs/manpages/$lang/*$sect; do
+        FNAME=$m/`basename $s`
+       if test -f $FNAME; then
+         echo Deleting $FNAME
+         rm -f $FNAME 
+         test -f $FNAME && echo Cannot remove $FNAME... does $USER have privileges?   
+        fi
+      done
+    done
+  done
+done
+
+cat << EOF
+======================================================================
+The man pages have been uninstalled. You may install them again using 
+the command "make installman" or make "install" to install binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+exit 0
diff --git a/source4/script/uninstallmodules.sh b/source4/script/uninstallmodules.sh
new file mode 100644 (file)
index 0000000..30582a3
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au   
+
+INSTALLPERMS=$1
+BASEDIR=$2
+LIBDIR=$3
+shift
+shift
+shift
+
+if [ ! -d $LIBDIR ]; then
+  echo Directory $LIBDIR does not exist!
+  echo Do a "make installmodules" or "make install" first.
+  exit 1
+fi
+
+for p in $*; do
+  p2=`basename $p`
+  if [ -f $LIBDIR/$p2 ]; then
+    echo Removing $LIBDIR/$p2
+    rm -f $LIBDIR/$p2
+    if [ -f $LIBDIR/$p2 ]; then
+      echo Cannot remove $LIBDIR/$p2 ... does $USER have privileges?
+    fi
+  fi
+done
+
+
+cat << EOF
+======================================================================
+The modules have been uninstalled. You may restore the modules using
+the command "make installmodules" or "make install" to install 
+binaries, modules, man pages and shell scripts. 
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/uninstallscripts.sh b/source4/script/uninstallscripts.sh
new file mode 100644 (file)
index 0000000..13104ac
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au  - almost identical to uninstallbin.sh
+
+INSTALLPERMS=$1
+BINDIR=$2
+
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+  echo Directory $BINDIR does not exist!
+  echo Do a "make installscripts" or "make install" first.
+  exit 1
+fi
+
+for p in $*; do
+  p2=`basename $p`
+  if [ -f $BINDIR/$p2 ]; then
+    echo Removing $BINDIR/$p2
+    rm -f $BINDIR/$p2
+    if [ -f $BINDIR/$p2 ]; then
+      echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+    fi
+  fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been uninstalled. You may reinstall them using
+the command "make installscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover a previous version (if any
+with "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/updatesmbpasswd.sh b/source4/script/updatesmbpasswd.sh
new file mode 100644 (file)
index 0000000..1d7e0d7
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+nawk 'BEGIN {FS=":"} 
+{
+       if( $0 ~ "^#" ) {
+               print $0
+       } else if( (length($4) == 32) && (($4 ~ "^[0-9A-F]*$") || ($4 ~ "^[X]*$") || ( $4 ~ "^[*]*$"))) {
+               print $0
+       } else {
+               printf( "%s:%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:", $1, $2, $3);
+               for(i = 4; i <= NF; i++)
+                       printf("%s:", $i)
+               printf("\n")
+       }
+}'
diff --git a/source4/smbadduser b/source4/smbadduser
new file mode 100755 (executable)
index 0000000..9837413
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/csh
+#
+# smbadduser - Written by Mike Zakharoff
+#
+unalias *
+set path = ($path /usr/local/samba/bin)
+
+set smbpasswd = /usr/local/samba/private/smbpasswd
+#set smbpasswd = /etc/samba/smbpasswd
+set user_map  = /usr/local/samba/lib/users.map
+#set user_map  = /etc/samba/smbusers
+#
+# Set to site specific passwd command
+#
+set passwd    = "cat /etc/passwd"
+#set passwd    = "niscat passwd.org_dir"
+#set passwd    = "ypcat passwd"
+
+set line = "----------------------------------------------------------"
+if ($#argv == 0) then
+       echo $line
+       echo "Written: Mike Zakharoff email: michael.j.zakharoff@boeing.com"
+       echo ""
+       echo "   1) Updates $smbpasswd"
+       echo "   2) Updates $user_map"
+       echo "   3) Executes smbpasswd for each new user"
+       echo ""
+       echo "smbadduser unixid:ntid unixid:ntid ..."
+       echo ""
+       echo "Example: smbadduser zak:zakharoffm johns:smithj"
+       echo $line
+       exit 1
+endif
+
+touch $smbpasswd $user_map
+set new  = ()
+foreach one ($argv)
+       echo $one | grep ':' >& /dev/null
+       if ($status != 0) then
+               echo "ERROR: Must use unixid:ntid like -> zak:zakharoffm"
+               continue
+       endif
+       set unix = `echo $one | awk -F: '{print $1}'`
+       set ntid = `echo $one | awk -F: '{print $2}'`
+
+       set usr = `eval $passwd | awk -F: '$1==USR {print $1}' USR=$unix`
+       if ($#usr != 1) then
+               echo "ERROR: $unix Not in passwd database SKIPPING..."
+               continue
+       endif
+        set tmp = `cat $smbpasswd | awk -F: '$1==USR {print $1}' USR=$unix`
+       if ($#tmp != 0) then
+               echo "ERROR: $unix is already in $smbpasswd SKIPPING..."
+               continue
+       endif
+
+       echo "Adding: $unix to $smbpasswd"
+       /usr/bin/smbpasswd -a -n $unix
+       if ($unix != $ntid) then
+               echo "Adding: {$unix = $ntid} to $user_map"
+               echo "$unix = $ntid" >> $user_map
+       endif
+       set new = ($new $unix)
+end
+
+#
+# Enter password for new users
+#
+foreach one ($new)
+       echo $line
+       echo "ENTER password for $one"
+       smbpasswd $one
+end
diff --git a/source4/smbd/.cvsignore b/source4/smbd/.cvsignore
new file mode 100644 (file)
index 0000000..5f2a5c4
--- /dev/null
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source4/smbd/build_options.c b/source4/smbd/build_options.c
new file mode 100644 (file)
index 0000000..e450fee
--- /dev/null
@@ -0,0 +1,535 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Build Options for Samba Suite
+   Copyright (C) Vance Lankhaar <vlankhaar@hotmail.com> 2001
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001
+   
+   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 "build_env.h"
+#include "dynconfig.h"
+
+static void output(BOOL screen, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+
+/*
+#define OUTPUT(x) snprintf(outstring,sizeof(outstring),x); output(screen,outstring);
+*/
+/****************************************************************************
+helper function for build_options
+****************************************************************************/
+static void output(BOOL screen, const char *format, ...)
+{
+       char *ptr;
+       va_list ap;
+       
+       va_start(ap, format);
+       vasprintf(&ptr,format,ap);
+       va_end(ap);
+
+       if (screen) {
+              d_printf("%s", ptr);
+       } else {
+              DEBUG(4,("%s", ptr));
+       }
+       
+       SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+options set at build time for the samba suite
+****************************************************************************/
+void build_options(BOOL screen)
+{
+       if ((DEBUGLEVEL < 4) && (!screen)) {
+              return;
+       }
+
+#ifdef _BUILD_ENV_H
+       /* Output information about the build environment */
+       output(screen,"Build environment:\n");
+       output(screen,"   Built by:    %s@%s\n",BUILD_ENV_USER,BUILD_ENV_HOST);
+       output(screen,"   Built on:    %s\n",BUILD_ENV_DATE);
+
+       output(screen,"   Built using: %s\n",BUILD_ENV_COMPILER);
+       output(screen,"   Build host:  %s\n",BUILD_ENV_UNAME);
+       output(screen,"   SRCDIR:      %s\n",BUILD_ENV_SRCDIR);
+       output(screen,"   BUILDDIR:    %s\n",BUILD_ENV_BUILDDIR);
+
+       
+#endif
+
+       /* Output various options (most correspond to --with options) */ 
+       output(screen,"\nBuild options:\n");
+#ifdef WITH_SMBWRAPPER 
+       output(screen,"   WITH_SMBWRAPPER\n");
+#endif
+#ifdef WITH_AFS
+       output(screen,"   WITH_AFS\n");
+#endif
+#ifdef WITH_DFS
+       output(screen,"   WITH_DFS\n");
+#endif
+#ifdef KRB4_AUTH
+       output(screen,"   KRB4_AUTH");
+#endif
+#ifdef HAVE_KRB5
+       output(screen,"   HAVE_KRB5");
+#endif
+#ifdef HAVE_GSSAPI
+       output(screen,"   HAVE_GSSAPI");
+#endif
+#ifdef HAVE_LDAP
+       output(screen,"   HAVE_LDAP");
+#endif
+#ifdef WITH_AUTOMOUNT
+       output(screen,"   WITH_AUTOMOUNT\n");
+#endif
+#ifdef WITH_SMBMOUNT
+       output(screen,"   WITH_SMBMOUNT\n");
+#endif
+#ifdef WITH_PAM
+       output(screen,"   WITH_PAM\n");
+#endif
+#ifdef WITH_TDB_SAM
+       output(screen,"   WITH_TDB_SAM\n");
+#endif
+#ifdef WITH_SMBPASSWD_SAM
+       output(screen,"   WITH_SMBPASSWD_SAM\n");
+#endif
+#ifdef WITH_NISPLUS_SAM
+       output(screen,"   WITH_NISPLUS_SAM\n");
+#endif
+#ifdef WITH_NISPLUS_HOME
+       output(screen,"   WITH_NISPLUS_HOME\n");
+#endif
+#ifdef WITH_SYSLOG
+       output(screen,"   WITH_SYSLOG\n");
+#endif
+#ifdef WITH_QUOTAS
+       output(screen,"   WITH_QUOTAS\n");
+#endif
+#ifdef WITH_VFS
+       output(screen,"   WITH_VFS\n");
+#endif
+#ifdef USE_SPINLOCKS
+       output(screen,"   USE_SPINLOCKS\n");
+#endif
+#ifdef SPARC_SPINLOCKS
+       output(screen,"   SPARC_SPINLOCKS\n");
+#endif
+#ifdef INTEL_SPINLOCKS
+       output(screen,"   INTEL_SPINLOCKS\n");
+#endif
+#ifdef MIPS_SPINLOCKS
+       output(screen,"   MIPS_SPINLOCKS\n");
+#endif
+#ifdef POWERPC_SPINLOCKS
+       output(screen,"   POWERPC_SPINLOCKS\n");
+#endif
+#ifdef HAVE_UNIXWARE_ACLS
+       output(screen,"   HAVE_UNIXWARE_ACLS\n");
+#endif
+#ifdef HAVE_SOLARIS_ACLS
+       output(screen,"   HAVE_SOLARIS_ACLS\n");
+#endif 
+#ifdef HAVE_IRIX_ACLS
+       output(screen,"   HAVE_IRIX_ACLS\n");
+#endif
+#ifdef HAVE_AIX_ACLS
+       output(screen,"   HAVE_AIX_ACLS\n");
+#endif
+#ifdef HAVE_POSIX_ACLS
+       output(screen,"   HAVE_POSIX_ACLS\n");
+#endif
+#ifdef HAVE_TRU64_ACLS
+       output(screen,"   HAVE_TRU64_ACLS\n");
+#endif
+
+#ifdef HAVE_ACL_GET_PERM_NP
+       output(screen,"   HAVE_ACL_GET_PERM_NP\n");
+#endif
+#ifdef HAVE_NO_ACLS
+       output(screen,"   HAVE_NO_ACLS\n");
+#endif
+#ifdef HAVE_LIBREADLINE
+       output(screen,"   HAVE_LIBREADLINE\n"); 
+#endif
+#ifdef WITH_LIBICONV
+       output(screen,"   WITH_LIBICONV: %s\n",WITH_LIBICONV);
+#endif
+
+
+       /* Output various paths to files and directories */
+       output(screen,"\nPaths:\n");
+       output(screen,"   CONFIGFILE: %s\n", dyn_CONFIGFILE);
+#ifdef PRIVATE_DIR
+       output(screen,"   PRIVATE_DIR: %s\n",PRIVATE_DIR);
+#endif
+#ifdef LMHOSTSFILE
+       output(screen,"   LMHOSTSFILE: %s\n",LMHOSTSFILE);
+#endif
+       output(screen,"   SBINDIR: %s\n", dyn_SBINDIR);
+       output(screen,"   BINDIR: %s\n", dyn_BINDIR);
+       output(screen,"   LOCKDIR: %s\n",dyn_LOCKDIR);
+       output(screen,"   LOGFILEBASE: %s\n", dyn_LOGFILEBASE);
+
+       /*Output various other options (most map to defines in the configure script*/
+       output(screen,"\nOther Build Options:\n");
+#ifdef HAVE_VOLATILE
+       output(screen,"   HAVE_VOLATILE\n");
+#endif
+#ifdef HAVE_SHADOW_H
+       output(screen,"   HAVE_SHADOW_H\n");
+#endif
+#ifdef HAVE_CRYPT
+       output(screen,"   HAVE_CRYPT\n");
+#endif
+#ifdef USE_BOTH_CRYPT_CALLS
+       output(screen,"   USE_BOTH_CRYPT_CALLS\n");
+#endif
+#ifdef HAVE_TRUNCATED_SALT
+       output(screen,"   HAVE_TRUNCATED_SALT\n");
+#endif
+#ifdef HAVE_CUPS
+       output(screen,"   HAVE_CUPS\n");
+#endif
+#ifdef HAVE_CUPS_CUPS_H
+       output(screen,"   HAVE_CUPS_CUPS_H\n");
+#endif
+#ifdef HAVE_CUPS_LANGUAGE_H
+       output(screen,"   HAVE_CUPS_LANGUAGE_H\n");
+#endif
+#ifdef HAVE_DLOPEN
+       output(screen,"   HAVE_DLOPEN\n");
+#endif
+#ifdef HAVE_DLCLOSE
+       output(screen,"   HAVE_DLCLOSE\n");
+#endif
+#ifdef HAVE_DLSYM
+       output(screen,"   HAVE_DLSYM\n");
+#endif
+#ifdef HAVE_DLERROR
+       output(screen,"   HAVE_DLERROR\n");
+#endif
+#ifdef HAVE_UNIXSOCKET
+       output(screen,"   HAVE_UNIXSOCKET\n");
+#endif
+#ifdef HAVE_SOCKLEN_T_TYPE
+       output(screen,"   HAVE_SOCKLEN_T_TYPE\n");
+#endif
+#ifdef HAVE_SIG_ATOMIC_T_TYPE
+       output(screen,"   HAVE_SIG_ATOMIC_T_TYPE\n");
+#endif
+#ifdef HAVE_SETRESUID
+       output(screen,"   HAVE_SETRESUID\n");
+#endif
+#ifdef HAVE_SETRESGID
+       output(screen,"   HAVE_SETRESGID\n");
+#endif
+#ifdef HAVE_CONNECT
+       output(screen,"   HAVE_CONNECT\n");
+#endif
+#ifdef HAVE_YP_GET_DEFAULT_DOMAIN
+       output(screen,"   HAVE_YP_GET_DEFAULT_DOMAIN\n");
+#endif
+#ifdef HAVE_STAT64
+       output(screen,"   HAVE_STAT64\n");
+#endif
+#ifdef HAVE_LSTAT64
+       output(screen,"   HAVE_LSTAT64\n");
+#endif
+#ifdef HAVE_FSTAT64
+       output(screen,"   HAVE_FSTAT64\n");
+#endif
+#ifdef HAVE_STRCASECMP
+       output(screen,"   HAVE_STRCASECMP\n");
+#endif
+#ifdef HAVE_MEMSET
+       output(screen,"   HAVE_MEMSET\n");
+#endif
+#ifdef HAVE_LONGLONG
+       output(screen,"   HAVE_LONGLONG\n");
+#endif
+#ifdef COMPILER_SUPPORTS_LL
+       output(screen,"   COMPILER_SUPPORTS_LL\n");
+#endif
+#ifdef SIZEOF_OFF_T
+       output(screen,"   SIZEOF_OFF_T: %d\n",SIZEOF_OFF_T);
+#endif
+#ifdef HAVE_OFF64_T
+       output(screen,"   HAVE_OFF64_T\n");
+#endif
+#ifdef SIZEOF_INO_T
+       output(screen,"   SIZEOF_INO_T: %d\n",SIZEOF_INO_T);
+#endif
+#ifdef HAVE_INO64_T
+       output(screen,"   HAVE_INO64_T\n");
+#endif
+#ifdef HAVE_STRUCT_DIRENT64
+       output(screen,"   HAVE_STRUCT_DIRENT64\n");
+#endif
+#ifdef HAVE_UNSIGNED_CHAR
+       output(screen,"   HAVE_UNSIGNED_CHAR\n");
+#endif
+#ifdef HAVE_SOCK_SIN_LEN
+       output(screen,"   HAVE_SOCK_SIN_LEN\n");
+#endif
+#ifdef SEEKDIR_RETURNS_VOID
+       output(screen,"   SEEKDIR_RETURNS_VOID\n");
+#endif
+#ifdef HAVE_FUNCTION_MACRO
+       output(screen,"   HAVE_FUNCTION_MACRO\n");
+#endif
+#ifdef HAVE_GETTIMEOFDAY
+       output(screen,"   HAVE_GETTIMEOFDAY\n");
+#endif
+#ifdef HAVE_C99_VSNPRINTF
+       output(screen,"   HAVE_C99_VSNPRINTF\n");
+#endif
+#ifdef HAVE_BROKEN_READDIR
+       output(screen,"   HAVE_BROKEN_READDIR\n");
+#endif
+#ifdef HAVE_NATIVE_ICONV
+       output(screen,"   HAVE_NATIVE_ICONV\n");
+#endif
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+       output(screen,"   HAVE_KERNEL_OPLOCKS_LINUX\n");
+#endif
+#ifdef HAVE_KERNEL_CHANGE_NOTIFY
+       output(screen,"   HAVE_KERNEL_CHANGE_NOTIFY\n");
+#endif
+#ifdef HAVE_KERNEL_SHARE_MODES
+       output(screen,"   HAVE_KERNEL_SHARE_MODES\n");
+#endif
+#ifdef HAVE_KERNEL_OPLOCKS_IRIX
+       output(screen,"   HAVE_KERNEL_OPLOCKS_IRIX\n");
+#endif
+#ifdef HAVE_IRIX_SPECIFIC_CAPABILITIES
+       output(screen,"   HAVE_IRIX_SPECIFIC_CAPABILITIES\n");
+#endif
+#ifdef HAVE_INT16_FROM_RPC_RPC_H
+       output(screen,"   HAVE_INT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_UINT16_FROM_RPC_RPC_H
+       output(screen,"   HAVE_UINT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_INT32_FROM_RPC_RPC_H
+       output(screen,"   HAVE_INT16_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_UINT32_FROM_RPC_RPC_H
+       output(screen,"   HAVE_UINT32_FROM_RPC_RPC_H\n");
+#endif
+#ifdef HAVE_RPC_AUTH_ERROR_CONFLICT
+       output(screen,"   HAVE_RPC_AUTH_ERROR_CONFLICT\n");
+#endif
+#ifdef HAVE_FTRUNCATE_EXTEND
+       output(screen,"   HAVE_FTRUNCATE_EXTEND\n");
+#endif
+#ifdef HAVE_WORKING_AF_LOCAL
+       output(screen,"   HAVE_WORKING_AF_LOCAL\n");
+#endif
+#ifdef HAVE_BROKEN_GETGROUPS
+       output(screen,"   HAVE_BROKEN_GETGROUPS\n");
+#endif
+#ifdef REPLACE_GETPASS
+       output(screen,"   REPLACE_GETPASS\n");
+#endif
+#ifdef REPLACE_INET_NTOA
+       output(screen,"   REPLACE_INET_NTOA\n");
+#endif
+#ifdef HAVE_SECURE_MKSTEMP
+       output(screen,"   HAVE_SECURE_MKSTEMP\n");
+#endif
+#ifdef SYSCONF_SC_NGROUPS_MAX
+       output(screen,"   SYSCONF_SC_NGROUPS_MAX\n");
+#endif
+#ifdef HAVE_IFACE_AIX
+       output(screen,"   HAVE_IFACE_AIX\n");
+#endif
+#ifdef HAVE_IFACE_IFCONF
+       output(screen,"   HAVE_IFACE_IFCONF\n");
+#endif
+#ifdef HAVE_IFACE_IFREQ
+       output(screen,"   HAVE_IFACE_IFREQ\n");
+#endif
+#ifdef USE_SETRESUID
+       output(screen,"   USE_SETRESUID\n");
+#endif
+#ifdef USE_SETRESGID
+       output(screen,"   USE_SETREUID\n");
+#endif
+#ifdef USE_SETEUID
+       output(screen,"   USE_SETEUID\n");
+#endif
+#ifdef USE_SETUIDX
+       output(screen,"   USE_SETUIDX\n");
+#endif
+#ifdef HAVE_MMAP
+       output(screen,"   HAVE_MMAP\n");
+#endif
+#ifdef MMAP_BLACKLIST
+       output(screen,"   MMAP_BLACKLIST\n");
+#endif
+#ifdef FTRUNCATE_NEEDS_ROOT
+       output(screen,"   FTRUNCATE_NEEDS_ROOT\n");
+#endif
+#ifdef HAVE_FCNTL_LOCK
+       output(screen,"   HAVE_FCNTL_LOCK\n");
+#endif
+#ifdef HAVE_BROKEN_FCNTL64_LOCKS
+       output(screen,"   HAVE_BROKEN_FCNTL64_LOCKS\n");
+#endif
+#ifdef HAVE_STRUCT_FLOCK64
+       output(screen,"   HAVE_STRUCT_FLOCK64\n");
+#endif
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+       output(screen,"   BROKEN_NISPLUS_INCLUDE_FILES\n");
+#endif
+#ifdef HAVE_LIBPAM
+       output(screen,"   HAVE_LIBPAM\n");
+#endif
+#ifdef STAT_STATVFS64
+       output(screen,"   STAT_STATVFS64\n");
+#endif
+#ifdef STAT_STATVFS
+       output(screen,"   STAT_STATVFS\n");
+#endif
+#ifdef STAT_STATFS3_OSF1
+       output(screen,"   STAT_STATFS3_OSF1\n");
+#endif
+#ifdef STAT_STATFS2_BSIZE
+       output(screen,"   STAT_STATFS2_BSIZE\n");
+#endif
+#ifdef STAT_STATFS4
+       output(screen,"   STAT_STATFS4\n");
+#endif
+#ifdef STAT_STATFS2_FSIZE
+       output(screen,"   STAT_STATFS2_FSIZE\n");
+#endif
+#ifdef STAT_STATFS2_FS_DATA
+       output(screen,"   STAT_STATFS2_FS_DATA\n");
+#endif
+#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
+       output(screen,"   HAVE_EXPLICIT_LARGEFILE_SUPPORT\n");
+#endif
+
+#ifdef WITH_UTMP
+       /* Output UTMP Stuff */
+       output(screen,"\nUTMP Related:\n");
+       output(screen,"   WITH_UTMP\n");
+
+#ifdef HAVE_UTIMBUF
+       output(screen,"   HAVE_UTIMBUF\n");
+#endif
+#ifdef HAVE_UT_UT_NAME
+       output(screen,"   HAVE_UT_UT_NAME\n");
+#endif
+#ifdef HAVE_UT_UT_USER
+       output(screen,"   HAVE_UT_UT_USER\n");
+#endif
+#ifdef HAVE_UT_UT_ID
+       output(screen,"   HAVE_UT_UT_ID\n");
+#endif
+#ifdef HAVE_UT_UT_HOST
+       output(screen,"   HAVE_UT_UT_HOST\n");
+#endif
+#ifdef HAVE_UT_UT_TIME
+       output(screen,"   HAVE_UT_UT_TIME\n");
+#endif
+#ifdef HAVE_UT_UT_TV
+       output(screen,"   HAVE_UT_UT_TV\n");
+#endif
+#ifdef HAVE_UT_UT_TYPE
+       output(screen,"   HAVE_UT_UT_TYPE\n");
+#endif
+#ifdef HAVE_UT_UT_PID
+       output(screen,"   HAVE_UT_UT_PID\n");
+#endif
+#ifdef HAVE_UT_UT_EXIT
+       output(screen,"   HAVE_UT_UT_EXIT\n");
+#endif
+#ifdef HAVE_UT_UT_ADDR
+       output(screen,"   HAVE_UT_UT_ADDR\n");
+#endif
+#ifdef PUTUTLINE_RETURNS_UTMP
+       output(screen,"   PUTUTLINE_RETURNS_UTMP\n");
+#endif
+#ifdef HAVE_UX_UT_SYSLEN
+       output(screen,"   HAVE_UX_UT_SYSLEN\n");
+#endif
+#endif /* WITH_UTMP */
+
+       /* Output Build OS */
+       output(screen,"\nBuilt for host os:\n");
+#ifdef LINUX
+       output(screen,"   LINUX\n");
+#endif
+#ifdef SUNOS5
+       output(screen,"   SUNOS5\n");
+#endif
+#ifdef SUNOS4
+       output(screen,"   SUNOS4\n");
+#endif
+       /* BSD Isn't Defined in the configure script, but there is something about it in include/config.h.in (and I guess acconfig.h) */
+#ifdef BSD
+       output(screen,"   BSD\n");
+#endif
+#ifdef IRIX
+       output(screen,"   IRIX\n");
+#endif
+#ifdef IRIX6
+       output(screen,"   IRIX6\n");
+#endif
+#ifdef AIX
+       output(screen,"   AIX\n");
+#endif
+#ifdef HPUX
+       output(screen,"   HPUX\n");
+#endif
+#ifdef QNX
+       output(screen,"   QNX\n");
+#endif
+#ifdef OSF1
+       output(screen,"   OSF1\n");
+#endif
+#ifdef SCO
+       output(screen,"   SCO\n");
+#endif
+#ifdef UNIXWARE
+       output(screen,"   UNIXWARE\n");
+#endif
+#ifdef NEXT2
+       output(screen,"   NEXT2\n");
+#endif
+#ifdef RELIANTUNIX
+       output(screen,"   RELIANTUNIX\n");
+#endif
+
+       /* Output the sizes of the various types */
+       output(screen,"\nType sizes:\n");
+       output(screen,"   sizeof(char):    %d\n",sizeof(char));
+       output(screen,"   sizeof(int):     %d\n",sizeof(int));
+       output(screen,"   sizeof(long):    %d\n",sizeof(long));
+       output(screen,"   sizeof(uint8):   %d\n",sizeof(uint8));
+       output(screen,"   sizeof(uint16):  %d\n",sizeof(uint16));
+       output(screen,"   sizeof(uint32):  %d\n",sizeof(uint32));
+       output(screen,"   sizeof(short):   %d\n",sizeof(short));
+       output(screen,"   sizeof(void*):   %d\n",sizeof(void*));
+}
+
+
+
diff --git a/source4/smbd/conn.c b/source4/smbd/conn.c
new file mode 100644 (file)
index 0000000..0498b3c
--- /dev/null
@@ -0,0 +1,158 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Manage connections_struct structures
+   Copyright (C) Andrew Tridgell 1998
+   Copyright (C) Alexander Bokovoy 2002
+   
+   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"
+
+/* set these to define the limits of the server. NOTE These are on a
+   per-client basis. Thus any one machine can't connect to more than
+   MAX_CONNECTIONS services, but any number of machines may connect at
+   one time. */
+#define MAX_CONNECTIONS 128
+
+/****************************************************************************
+init the conn structures
+****************************************************************************/
+void conn_init(struct server_context *smb)
+{
+       smb->tree.bmap = bitmap_allocate(MAX_CONNECTIONS);
+}
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL conn_snum_used(struct server_context *smb, int snum)
+{
+       struct tcon_context *conn;
+       for (conn=smb->tree.connections;conn;conn=conn->next) {
+               if (conn->service == snum) {
+                       return(True);
+               }
+       }
+       return(False);
+}
+
+
+/****************************************************************************
+find a conn given a cnum
+****************************************************************************/
+struct tcon_context *conn_find(struct server_context *smb, unsigned cnum)
+{
+       int count=0;
+       struct tcon_context *conn;
+
+       for (conn=smb->tree.connections;conn;conn=conn->next,count++) {
+               if (conn->cnum == cnum) {
+                       if (count > 10) {
+                               DLIST_PROMOTE(smb->tree.connections, conn);
+                       }
+                       return conn;
+               }
+       }
+
+       return NULL;
+}
+
+
+/****************************************************************************
+  find first available connection slot, starting from a random position.
+The randomisation stops problems with the server dieing and clients
+thinking the server is still available.
+****************************************************************************/
+struct tcon_context *conn_new(struct server_context *smb)
+{
+       TALLOC_CTX *mem_ctx;
+       struct tcon_context *conn;
+       int i;
+
+       i = bitmap_find(smb->tree.bmap, 1);
+       
+       if (i == -1) {
+               DEBUG(1,("ERROR! Out of connection structures\n"));            
+               return NULL;
+       }
+
+       mem_ctx = talloc_init("tcon_context[%d]", i);
+
+       conn = (struct tcon_context *)talloc(mem_ctx, sizeof(*conn));
+       if (!conn) return NULL;
+
+       ZERO_STRUCTP(conn);
+
+       conn->mem_ctx = mem_ctx;
+       conn->cnum = i;
+       conn->smb = smb;
+
+       bitmap_set(smb->tree.bmap, i);
+
+       smb->tree.num_open++;
+
+       DLIST_ADD(smb->tree.connections, conn);
+
+       return conn;
+}
+
+/****************************************************************************
+close all conn structures
+****************************************************************************/
+void conn_close_all(struct server_context *smb)
+{
+       struct tcon_context *conn, *next;
+       for (conn=smb->tree.connections;conn;conn=next) {
+               next=conn->next;
+               close_cnum(conn);
+       }
+}
+
+
+#if REWRITE_REMOVED
+/****************************************************************************
+clear a vuid out of the validity cache, and as the 'owner' of a connection.
+****************************************************************************/
+void conn_clear_vuid_cache(struct server_context *smb, uint16 vuid)
+{
+       struct tcon_context *conn;
+       unsigned int i;
+
+       for (conn=smb->tree.connections;conn;conn=conn->next) {
+               for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
+                       if (conn->vuid_cache.list[i] == vuid) {
+                               conn->vuid_cache.list[i] = UID_FIELD_INVALID;
+                       }
+               }
+       }
+}
+#endif
+
+/****************************************************************************
+ Free a conn structure.
+****************************************************************************/
+
+void conn_free(struct server_context *smb, struct tcon_context *conn)
+{
+       DLIST_REMOVE(smb->tree.connections, conn);
+
+       bitmap_clear(smb->tree.bmap, conn->cnum);
+       smb->tree.num_open--;
+
+       ZERO_STRUCTP(conn);
+       SAFE_FREE(conn);
+}
+
diff --git a/source4/smbd/connection.c b/source4/smbd/connection.c
new file mode 100644 (file)
index 0000000..35f5138
--- /dev/null
@@ -0,0 +1,223 @@
+/* 
+   Unix SMB/CIFS implementation.
+   connection claim routines
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+static TDB_CONTEXT *tdb;
+
+
+static void make_conn_key(struct tcon_context *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey)
+{
+       ZERO_STRUCTP(pkey);
+       pkey->pid = getpid();
+       pkey->cnum = conn?conn->cnum:-1;
+       fstrcpy(pkey->name, name);
+
+       pkbuf->dptr = (char *)pkey;
+       pkbuf->dsize = sizeof(*pkey);
+}
+
+/****************************************************************************
+ Delete a connection record.
+****************************************************************************/
+
+BOOL yield_connection(struct tcon_context *conn, const char *name)
+{
+       struct connections_key key;
+       TDB_DATA kbuf;
+
+       if (!tdb)
+               return False;
+
+       DEBUG(3,("Yielding connection to %s\n",name));
+
+       make_conn_key(conn, name, &kbuf, &key);
+
+       if (tdb_delete(tdb, kbuf) != 0) {
+               int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0;
+               DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n",
+                       name, tdb_errorstr(tdb) ));
+               return (False);
+       }
+
+       return(True);
+}
+
+struct count_stat {
+       pid_t mypid;
+       int curr_connections;
+       char *name;
+       BOOL Clear;
+};
+
+/****************************************************************************
+ Count the entries belonging to a service in the connection db.
+****************************************************************************/
+
+static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp)
+{
+       struct connections_data crec;
+       struct count_stat *cs = (struct count_stat *)udp;
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+       if (crec.cnum == -1)
+               return 0;
+
+       /* If the pid was not found delete the entry from connections.tdb */
+
+       if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) {
+               DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+                       (unsigned int)crec.pid, crec.cnum, crec.name));
+               if (tdb_delete(the_tdb, kbuf) != 0)
+                       DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
+               return 0;
+       }
+
+       if (strequal(crec.name, cs->name))
+               cs->curr_connections++;
+
+       return 0;
+}
+
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+BOOL claim_connection(struct tcon_context *conn, const char *name,int max_connections,BOOL Clear, uint32 msg_flags)
+{
+       struct connections_key key;
+       struct connections_data crec;
+       TDB_DATA kbuf, dbuf;
+
+       if (!tdb)
+               tdb = tdb_open_log(lock_path(conn->mem_ctx, "connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, 
+                              O_RDWR | O_CREAT, 0644);
+
+       if (!tdb)
+               return False;
+
+       /*
+        * Enforce the max connections parameter.
+        */
+
+       if (max_connections > 0) {
+               struct count_stat cs;
+
+               cs.mypid = getpid();
+               cs.curr_connections = 0;
+               cs.name = lp_servicename(SNUM(conn));
+               cs.Clear = Clear;
+
+               /*
+                * This has a race condition, but locking the chain before hand is worse
+                * as it leads to deadlock.
+                */
+
+               if (tdb_traverse(tdb, count_fn, &cs) == -1) {
+                       DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
+                               tdb_errorstr(tdb) ));
+                       return False;
+               }
+
+               if (cs.curr_connections >= max_connections) {
+                       DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n",
+                               max_connections, name ));
+                       return False;
+               }
+       }
+
+       DEBUG(5,("claiming %s %d\n",name,max_connections));
+
+       make_conn_key(conn, name, &kbuf, &key);
+
+       /* fill in the crec */
+       ZERO_STRUCT(crec);
+       crec.magic = 0x280267;
+       crec.pid = getpid();
+       crec.cnum = conn?conn->cnum:-1;
+       if (conn) {
+               crec.uid = -1;
+               crec.gid = -1;
+               StrnCpy(crec.name,
+                       lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
+       }
+       crec.start = time(NULL);
+       crec.bcast_msg_flags = msg_flags;
+       
+       StrnCpy(crec.machine,sub_get_remote_machine(),sizeof(crec.machine)-1);
+       StrnCpy(crec.addr,conn?conn->smb->socket.client_addr:"NONE",sizeof(crec.addr)-1);
+
+       dbuf.dptr = (char *)&crec;
+       dbuf.dsize = sizeof(crec);
+
+       if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+               DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
+                       tdb_errorstr(tdb) ));
+               return False;
+       }
+
+       return True;
+}
+
+BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
+{
+       struct connections_key key;
+       struct connections_data *pcrec;
+       TDB_DATA kbuf, dbuf;
+
+       if (!tdb)
+               return False;
+
+       DEBUG(10,("register_message_flags: %s flags 0x%x\n",
+               doreg ? "adding" : "removing",
+               (unsigned int)msg_flags ));
+
+       make_conn_key(NULL, "", &kbuf, &key);
+
+        dbuf = tdb_fetch(tdb, kbuf);
+        if (!dbuf.dptr) {
+               DEBUG(0,("register_message_flags: tdb_fetch failed\n"));
+               return False;
+       }
+
+       pcrec = (struct connections_data *)dbuf.dptr;
+       pcrec->bcast_msg_flags = msg_flags;
+       if (doreg)
+               pcrec->bcast_msg_flags |= msg_flags;
+       else
+               pcrec->bcast_msg_flags &= ~msg_flags;
+
+       if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+               DEBUG(0,("register_message_flags: tdb_store failed with error %s.\n",
+                       tdb_errorstr(tdb) ));
+               SAFE_FREE(dbuf.dptr);
+               return False;
+       }
+
+       DEBUG(10,("register_message_flags: new flags 0x%x\n",
+               (unsigned int)pcrec->bcast_msg_flags ));
+
+       SAFE_FREE(dbuf.dptr);
+       return True;
+}
diff --git a/source4/smbd/negprot.c b/source4/smbd/negprot.c
new file mode 100644 (file)
index 0000000..caf3ce3
--- /dev/null
@@ -0,0 +1,526 @@
+/* 
+   Unix SMB/CIFS implementation.
+   negprot reply code
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+/* initialise the auth_context for this server and return the cryptkey */
+static void get_challenge(struct server_context *smb, char buff[8]) 
+{
+       NTSTATUS nt_status;
+       const uint8 *cryptkey;
+
+       /* muliple negprots are not premitted */
+       if (smb->negotiate.auth_context) {
+               DEBUG(3,("get challenge: is this a secondary negprot?  auth_context is non-NULL!\n"));
+               smb_panic("secondary negprot");
+       }
+
+       DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
+
+       nt_status = make_auth_context_subsystem(&smb->negotiate.auth_context);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status)));
+               smb_panic("cannot make_negprot_global_auth_context!\n");
+       }
+
+       DEBUG(10, ("get challenge: getting challenge\n"));
+       cryptkey = smb->negotiate.auth_context->get_ntlm_challenge(smb->negotiate.auth_context);
+       memcpy(buff, cryptkey, 8);
+}
+
+/****************************************************************************
+ Reply for the core protocol.
+****************************************************************************/
+static void reply_corep(struct request_context *req, uint16 choice)
+{
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), choice);
+
+       req->smb->negotiate.protocol = PROTOCOL_CORE;
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the coreplus protocol.
+this is quite incomplete - we only fill in a small part of the reply, but as nobody uses
+this any more it probably doesn't matter
+****************************************************************************/
+static void reply_coreplus(struct request_context *req, uint16 choice)
+{
+       uint16 raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+
+       req_setup_reply(req, 13, 0);
+
+       /* Reply, SMBlockread, SMBwritelock supported. */
+       SCVAL(req->out.hdr,HDR_FLG,
+             CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+       SSVAL(req->out.vwv, VWV(0), choice);
+       SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */      
+
+       /* tell redirector we support
+          readbraw and writebraw (possibly) */
+       SSVAL(req->out.vwv, VWV(5), raw); 
+
+       req->smb->negotiate.protocol = PROTOCOL_COREPLUS;
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the lanman 1.0 protocol.
+****************************************************************************/
+static void reply_lanman1(struct request_context *req, uint16 choice)
+{
+       int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+       int secword=0;
+       time_t t = req->request_time.tv_sec;
+
+       req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords();
+
+       if (lp_security() != SEC_SHARE)
+               secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+
+       if (req->smb->negotiate.encrypted_passwords)
+               secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+       req->smb->negotiate.protocol = PROTOCOL_LANMAN1;
+
+       req_setup_reply(req, 13, req->smb->negotiate.encrypted_passwords ? 8 : 0);
+
+       /* SMBlockread, SMBwritelock supported. */
+       SCVAL(req->out.hdr,HDR_FLG,
+             CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+       SSVAL(req->out.vwv, VWV(0), choice);
+       SSVAL(req->out.vwv, VWV(1), secword); 
+       SSVAL(req->out.vwv, VWV(2), req->smb->negotiate.max_recv);
+       SSVAL(req->out.vwv, VWV(3), lp_maxmux());
+       SSVAL(req->out.vwv, VWV(4), 1);
+       SSVAL(req->out.vwv, VWV(5), raw); 
+       SIVAL(req->out.vwv, VWV(6), req->smb->pid);
+       put_dos_date(req->out.vwv, VWV(8), t);
+       SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60);
+
+       /* Create a token value and add it to the outgoing packet. */
+       if (req->smb->negotiate.encrypted_passwords) {
+               SSVAL(req->out.vwv, VWV(11), 8);
+               get_challenge(req->smb, req->out.data);
+       }
+
+       req_send_reply(req);    
+}
+
+/****************************************************************************
+ Reply for the lanman 2.0 protocol.
+****************************************************************************/
+static void reply_lanman2(struct request_context *req, uint16 choice)
+{
+       int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+       int secword=0;
+       time_t t = req->request_time.tv_sec;
+
+       req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords();
+  
+       if (lp_security() != SEC_SHARE)
+               secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+
+       if (req->smb->negotiate.encrypted_passwords)
+               secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+       req->smb->negotiate.protocol = PROTOCOL_LANMAN2;
+
+       req_setup_reply(req, 13, 0);
+
+       SSVAL(req->out.vwv, VWV(0), choice);
+       SSVAL(req->out.vwv, VWV(1), secword); 
+       SSVAL(req->out.vwv, VWV(2), req->smb->negotiate.max_recv);
+       SSVAL(req->out.vwv, VWV(3), lp_maxmux());
+       SSVAL(req->out.vwv, VWV(4), 1);
+       SSVAL(req->out.vwv, VWV(5), raw); 
+       SIVAL(req->out.vwv, VWV(6), req->smb->pid);
+       put_dos_date(req->out.vwv, VWV(8), t);
+       SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60);
+
+       /* Create a token value and add it to the outgoing packet. */
+       if (req->smb->negotiate.encrypted_passwords) {
+               SSVAL(req->out.vwv, VWV(11), 8);
+               req_grow_data(req, 8);
+               get_challenge(req->smb, req->out.data);
+       }
+
+       req_push_str(req, NULL, lp_workgroup(), -1, STR_TERMINATE);
+
+       req_send_reply(req);
+}
+
+
+#if 0
+/****************************************************************************
+ Generate the spnego negprot reply blob. Return the number of bytes used.
+****************************************************************************/
+static DATA_BLOB negprot_spnego(struct server_context *smb)
+{
+       DATA_BLOB blob;
+       uint8 guid[16];
+       const char *OIDs_krb5[] = {OID_KERBEROS5,
+                                  OID_KERBEROS5_OLD,
+                                  OID_NTLMSSP,
+                                  NULL};
+       const char *OIDs_plain[] = {OID_NTLMSSP, NULL};
+       char *principal;
+
+       smb->negotiate.spnego_negotiated = True;
+
+       memset(guid, 0, 16);
+       safe_strcpy((char *)guid, lp_netbios_name(), 16);
+       strlower((char *)guid);
+
+#if 0
+       /* strangely enough, NT does not send the single OID NTLMSSP when
+          not a ADS member, it sends no OIDs at all
+
+          we can't do this until we teach our sesssion setup parser to know
+          about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)
+       */
+       if (lp_security() != SEC_ADS) {
+               memcpy(p, guid, 16);
+               return 16;
+       }
+#endif
+       if (lp_security() != SEC_ADS) {
+               blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE");
+       } else {
+               asprintf(&principal, "%s$@%s", guid, lp_realm());
+               blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
+               free(principal);
+       }
+
+       return blob;
+}
+#endif
+
+/****************************************************************************
+ Reply for the nt protocol.
+****************************************************************************/
+static void reply_nt1(struct request_context *req, uint16 choice)
+{
+       /* dual names + lock_and_read + nt SMBs + remote API calls */
+       int capabilities;
+       int secword=0;
+       time_t t = req->request_time.tv_sec;
+       BOOL negotiate_spnego = False;
+
+       capabilities = 
+               CAP_NT_FIND | CAP_LOCK_AND_READ | 
+               CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+
+       req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords();
+
+       /* do spnego in user level security if the client
+          supports it and we can do encrypted passwords */
+       
+       if (req->smb->negotiate.encrypted_passwords && 
+           (lp_security() != SEC_SHARE) &&
+           lp_use_spnego() &&
+           (req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
+/* REWRITE             negotiate_spnego = True; 
+               capabilities |= CAP_EXTENDED_SECURITY;
+*/
+       }
+       
+       if (lp_unix_extensions()) {
+               capabilities |= CAP_UNIX;
+       }
+       
+       if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64)) {
+               capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS;
+       }
+       
+       if (SMB_OFF_T_BITS >= 64) {
+               capabilities |= CAP_LARGE_FILES;
+       }
+
+       if (lp_readraw() && lp_writeraw()) {
+               capabilities |= CAP_RAW_MODE;
+       }
+       
+       /* allow for disabling unicode */
+       if (lp_unicode()) {
+               capabilities |= CAP_UNICODE;
+       }
+
+       if (lp_nt_status_support()) {
+               capabilities |= CAP_STATUS32;
+       }
+       
+       if (lp_host_msdfs()) {
+               capabilities |= CAP_DFS;
+       }
+       
+       if (lp_security() != SEC_SHARE) {
+               secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+       }
+
+       if (req->smb->negotiate.encrypted_passwords) {
+               secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+       }
+       
+       req->smb->negotiate.protocol = PROTOCOL_NT1;
+
+       req_setup_reply(req, 17, 0);
+       
+       SSVAL(req->out.vwv, VWV(0), choice);
+       SCVAL(req->out.vwv, VWV(1), secword);
+
+       /* notice the strange +1 on vwv here? That's because
+          this is the one and only SMB packet that is malformed in
+          the specification - all the command words after the secword
+          are offset by 1 byte */
+       SSVAL(req->out.vwv+1, VWV(1), lp_maxmux());
+       SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */
+       SIVAL(req->out.vwv+1, VWV(3), req->smb->negotiate.max_recv);
+       SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */
+       SIVAL(req->out.vwv+1, VWV(7), req->smb->pid); /* session key */
+       SIVAL(req->out.vwv+1, VWV(9), capabilities);
+       put_long_date(req->out.vwv + VWV(11) + 1, t);
+       SSVALS(req->out.vwv+1,VWV(15), TimeDiff(t)/60);
+       
+       if (!negotiate_spnego) {
+               /* Create a token value and add it to the outgoing packet. */
+               if (req->smb->negotiate.encrypted_passwords) {
+                       req_grow_data(req, 8);
+                       /* note that we do not send a challenge at all if
+                          we are using plaintext */
+                       get_challenge(req->smb, req->out.ptr);
+                       req->out.ptr += 8;
+                       SCVAL(req->out.vwv+1, VWV(16), 8);
+               }
+               req_push_str(req, NULL, lp_workgroup(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+               DEBUG(3,("not using SPNEGO\n"));
+       } else {
+#if 0
+               DATA_BLOB blob = negprot_spnego(req->smb);
+
+               req_grow_data(req, blob.length);
+               memcpy(req->out.ptr, blob.data, blob.length);
+               DEBUG(3,("using SPNEGO\n"));
+#else
+               exit_server(req->smb, "no SPNEGO please");
+#endif
+       }
+       
+       req_send_reply(req);    
+}
+
+/* these are the protocol lists used for auto architecture detection:
+
+WinNT 3.51:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win95:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win2K:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+OS/2:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [LANMAN1.0]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+*/
+
+/*
+  * Modified to recognize the architecture of the remote machine better.
+  *
+  * This appears to be the matrix of which protocol is used by which
+  * MS product.
+       Protocol                       WfWg    Win95   WinNT  Win2K  OS/2
+       PC NETWORK PROGRAM 1.0          1       1       1      1      1
+       XENIX CORE                                      2             2
+       MICROSOFT NETWORKS 3.0          2       2       
+       DOS LM1.2X002                   3       3       
+       MICROSOFT NETWORKS 1.03                         3
+       DOS LANMAN2.1                   4       4       
+       LANMAN1.0                                       4      2      3
+       Windows for Workgroups 3.1a     5       5       5      3
+       LM1.2X002                                       6      4      4
+       LANMAN2.1                                       7      5      5
+       NT LM 0.12                              6       8      6
+  *
+  *  tim@fsg.com 09/29/95
+  *  Win2K added by matty 17/7/99
+  */
+  
+#define ARCH_WFWG     0x3      /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95    0x2
+#define ARCH_WINNT    0x4
+#define ARCH_WIN2K    0xC      /* Win2K is like NT */
+#define ARCH_OS2      0x14     /* Again OS/2 is like NT */
+#define ARCH_SAMBA    0x20
+#define ARCH_ALL      0x3F
+/* List of supported protocols, most desired first */
+static const struct {
+       const char *proto_name;
+       const char *short_name;
+       void (*proto_reply_fn)(struct request_context *req, uint16 choice);
+       int protocol_level;
+} supported_protocols[] = {
+       {"NT LANMAN 1.0",           "NT1",      reply_nt1,      PROTOCOL_NT1},
+       {"NT LM 0.12",              "NT1",      reply_nt1,      PROTOCOL_NT1},
+       {"LM1.2X002",               "LANMAN2",  reply_lanman2,  PROTOCOL_LANMAN2},
+       {"Samba",                   "LANMAN2",  reply_lanman2,  PROTOCOL_LANMAN2},
+       {"DOS LM1.2X002",           "LANMAN2",  reply_lanman2,  PROTOCOL_LANMAN2},
+       {"LANMAN1.0",               "LANMAN1",  reply_lanman1,  PROTOCOL_LANMAN1},
+       {"MICROSOFT NETWORKS 3.0",  "LANMAN1",  reply_lanman1,  PROTOCOL_LANMAN1},
+       {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+       {"PC NETWORK PROGRAM 1.0",  "CORE",     reply_corep,    PROTOCOL_CORE}, 
+       {NULL,NULL,NULL,0},
+};
+
+/****************************************************************************
+ Reply to a negprot.
+****************************************************************************/
+
+void reply_negprot(struct request_context *req)
+{
+       int Index=0;
+       int choice = -1;
+       int protocol;
+       char *p;
+       int arch = ARCH_ALL;
+
+       if (req->smb->negotiate.done_negprot) {
+               exit_server(req->smb, "multiple negprot's are not permitted");
+       }
+       req->smb->negotiate.done_negprot = True;
+
+       p = req->in.data + 1;
+
+       while (p < req->in.data + req->in.data_size) { 
+               Index++;
+               DEBUG(3,("Requested protocol [%s]\n",p));
+               if (strcmp(p,"Windows for Workgroups 3.1a") == 0)
+                       arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+               else if (strcmp(p,"DOS LM1.2X002") == 0)
+                       arch &= ( ARCH_WFWG | ARCH_WIN95 );
+               else if (strcmp(p,"DOS LANMAN2.1") == 0)
+                       arch &= ( ARCH_WFWG | ARCH_WIN95 );
+               else if (strcmp(p,"NT LM 0.12") == 0)
+                       arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+               else if (strcmp(p,"LANMAN2.1") == 0)
+                       arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+               else if (strcmp(p,"LM1.2X002") == 0)
+                       arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+               else if (strcmp(p,"MICROSOFT NETWORKS 1.03") == 0)
+                       arch &= ARCH_WINNT;
+               else if (strcmp(p,"XENIX CORE") == 0)
+                       arch &= ( ARCH_WINNT | ARCH_OS2 );
+               else if (strcmp(p,"Samba") == 0) {
+                       arch = ARCH_SAMBA;
+                       break;
+               }
+               p += strlen(p) + 2;
+       }
+    
+       switch (arch) {
+               case ARCH_SAMBA:
+                       set_remote_arch(req->smb, RA_SAMBA);
+                       break;
+               case ARCH_WFWG:
+                       set_remote_arch(req->smb, RA_WFWG);
+                       break;
+               case ARCH_WIN95:
+                       set_remote_arch(req->smb, RA_WIN95);
+                       break;
+               case ARCH_WINNT:
+                       if (req->flags2==FLAGS2_WIN2K_SIGNATURE)
+                               set_remote_arch(req->smb, RA_WIN2K);
+                       else
+                               set_remote_arch(req->smb, RA_WINNT);
+                       break;
+               case ARCH_WIN2K:
+                       set_remote_arch(req->smb, RA_WIN2K);
+                       break;
+               case ARCH_OS2:
+                       set_remote_arch(req->smb, RA_OS2);
+                       break;
+               default:
+                       set_remote_arch(req->smb, RA_UNKNOWN);
+               break;
+       }
+       /* possibly reload - change of architecture */
+       reload_services(req->smb, True);      
+    
+       /* Check for protocols, most desirable first */
+       for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
+               p = req->in.data+1;
+               Index = 0;
+               if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) &&
+                               (supported_protocols[protocol].protocol_level >= lp_minprotocol()))
+                       while (p < (req->in.data + req->in.data_size)) { 
+                               if (strequal(p,supported_protocols[protocol].proto_name))
+                                       choice = Index;
+                               Index++;
+                               p += strlen(p) + 2;
+                       }
+               if(choice != -1)
+                       break;
+       }
+  
+       if(choice != -1) {
+               sub_set_remote_proto(supported_protocols[protocol].short_name);
+               reload_services(req->smb, True);
+               supported_protocols[protocol].proto_reply_fn(req, choice);
+               DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+       } else {
+               DEBUG(0,("No protocol supported !\n"));
+       }
+  
+       DEBUG(5,("negprot index=%d\n", choice));
+}
diff --git a/source4/smbd/password.c b/source4/smbd/password.c
new file mode 100644 (file)
index 0000000..d2559ac
--- /dev/null
@@ -0,0 +1,492 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Password and authentication handling
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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"
+
+
+/****************************************************************************
+check if a uid has been validated, and return an pointer to the user_struct
+if it has. NULL if not. vuid is biased by an offset. This allows us to
+tell random client vuid's (normally zero) from valid vuids.
+****************************************************************************/
+user_struct *get_valid_user_struct(struct server_context *smb, uint16 vuid)
+{
+       user_struct *usp;
+       int count=0;
+
+       if (vuid == UID_FIELD_INVALID)
+               return NULL;
+
+       for (usp=smb->users.validated_users;usp;usp=usp->next,count++) {
+               if (vuid == usp->vuid) {
+                       if (count > 10) {
+                               DLIST_PROMOTE(smb->users.validated_users, usp);
+                       }
+                       return usp;
+               }
+       }
+
+       return NULL;
+}
+
+/****************************************************************************
+invalidate a uid
+****************************************************************************/
+void invalidate_vuid(struct server_context *smb, uint16 vuid)
+{
+       user_struct *vuser = get_valid_user_struct(smb, vuid);
+
+       if (vuser == NULL)
+               return;
+       
+       SAFE_FREE(vuser->homedir);
+       SAFE_FREE(vuser->unix_homedir);
+       SAFE_FREE(vuser->logon_script);
+       
+       session_yield(vuser);
+
+       free_server_info(&vuser->server_info);
+
+       DLIST_REMOVE(smb->users.validated_users, vuser);
+
+       /* clear the vuid from the 'cache' on each connection, and
+          from the vuid 'owner' of connections */
+       /* REWRITE: conn_clear_vuid_cache(smb, vuid); */
+
+       SAFE_FREE(vuser->groups);
+       delete_nt_token(&vuser->nt_user_token);
+       SAFE_FREE(vuser);
+       smb->users.num_validated_vuids--;
+}
+
+/****************************************************************************
+invalidate all vuid entries for this process
+****************************************************************************/
+void invalidate_all_vuids(struct server_context *smb)
+{
+       user_struct *usp, *next=NULL;
+
+       for (usp=smb->users.validated_users;usp;usp=next) {
+               next = usp->next;
+               
+               invalidate_vuid(smb, usp->vuid);
+       }
+}
+
+/**
+ *  register that a valid login has been performed, establish 'session'.
+ *  @param server_info The token returned from the authentication process. 
+ *   (now 'owned' by register_vuid)
+ *
+ *  @return Newly allocated vuid, biased by an offset. (This allows us to
+ *   tell random client vuid's (normally zero) from valid vuids.)
+ *
+ */
+
+int register_vuid(struct server_context *smb,
+                 struct auth_serversupplied_info *server_info, 
+                 const char *smb_name)
+{
+       user_struct *vuser = NULL;
+
+       /* Ensure no vuid gets registered in share level security. */
+       if(lp_security() == SEC_SHARE)
+               return UID_FIELD_INVALID;
+
+       /* Limit allowed vuids to 16bits - VUID_OFFSET. */
+       if (smb->users.num_validated_vuids >= 0xFFFF-VUID_OFFSET)
+               return UID_FIELD_INVALID;
+
+       if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) {
+               DEBUG(0,("Failed to malloc users struct!\n"));
+               return UID_FIELD_INVALID;
+       }
+
+       ZERO_STRUCTP(vuser);
+
+       /* Allocate a free vuid. Yes this is a linear search... :-) */
+       while (get_valid_user_struct(smb, smb->users.next_vuid) != NULL ) {
+               smb->users.next_vuid++;
+               /* Check for vuid wrap. */
+               if (smb->users.next_vuid == UID_FIELD_INVALID)
+                       smb->users.next_vuid = VUID_OFFSET;
+       }
+
+       DEBUG(10,("register_vuid: allocated vuid = %u\n", 
+                 (unsigned int)smb->users.next_vuid));
+
+       vuser->vuid = smb->users.next_vuid;
+
+       /* the next functions should be done by a SID mapping system (SMS) as
+        * the new real sam db won't have reference to unix uids or gids
+        */
+       if (!IS_SAM_UNIX_USER(server_info->sam_account)) {
+               DEBUG(0,("Attempted session setup with invalid user.  No uid/gid in SAM_ACCOUNT\n"));
+               free(vuser);
+               free_server_info(&server_info);
+               return UID_FIELD_INVALID;
+       }
+       
+       vuser->uid = pdb_get_uid(server_info->sam_account);
+       vuser->gid = pdb_get_gid(server_info->sam_account);
+       
+       vuser->n_groups = server_info->n_groups;
+       if (vuser->n_groups) {
+               if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) {
+                       DEBUG(0,("register_vuid: failed to memdup vuser->groups\n"));
+                       free(vuser);
+                       free_server_info(&server_info);
+                       return UID_FIELD_INVALID;
+               }
+       }
+
+       vuser->guest = server_info->guest;
+       fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account)); 
+
+       /* This is a potentially untrusted username */
+       alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", sizeof(vuser->user.smb_name));
+
+       fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account));
+       fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account));
+
+       {
+               /* Keep the homedir handy */
+               const char *homedir = pdb_get_homedir(server_info->sam_account);
+               const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account);
+               const char *logon_script = pdb_get_logon_script(server_info->sam_account);
+               if (homedir) {
+                       vuser->homedir = smb_xstrdup(homedir);
+               }
+
+               if (unix_homedir) {
+                       vuser->unix_homedir = smb_xstrdup(unix_homedir);
+               }
+
+               if (logon_script) {
+                       vuser->logon_script = smb_xstrdup(logon_script);
+               }
+       }
+
+       memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key));
+
+       DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", 
+                 (unsigned int)vuser->uid, 
+                 (unsigned int)vuser->gid,
+                 vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest ));
+
+       DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name));       
+
+       if (server_info->ptok) {
+               vuser->nt_user_token = dup_nt_token(server_info->ptok);
+       } else {
+               DEBUG(1, ("server_info does not contain a user_token - cannot continue\n"));
+               free_server_info(&server_info);
+               SAFE_FREE(vuser->homedir);
+               SAFE_FREE(vuser->unix_homedir);
+               SAFE_FREE(vuser->logon_script);
+
+               SAFE_FREE(vuser);
+               return UID_FIELD_INVALID;
+       }
+
+       /* use this to keep tabs on all our info from the authentication */
+       vuser->server_info = server_info;
+
+       DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n",(int)vuser->uid,vuser->user.unix_name, vuser->vuid));
+
+       smb->users.next_vuid++;
+       smb->users.num_validated_vuids++;
+
+       DLIST_ADD(smb->users.validated_users, vuser);
+
+       if (!session_claim(smb, vuser)) {
+               DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid));
+               invalidate_vuid(smb, vuser->vuid);
+               return -1;
+       }
+
+       /* Register a home dir service for this user */
+       if ((!vuser->guest) && vuser->unix_homedir && *(vuser->unix_homedir)) {
+               DEBUG(3, ("Adding/updating homes service for user '%s' using home direcotry: '%s'\n", 
+                         vuser->user.unix_name, vuser->unix_homedir));
+               vuser->homes_snum = add_home_service(vuser->user.unix_name, vuser->user.unix_name, vuser->unix_homedir);          
+       } else {
+               vuser->homes_snum = -1;
+       }
+       
+       return vuser->vuid;
+}
+
+
+/****************************************************************************
+add a name to the session users list
+****************************************************************************/
+void add_session_user(struct server_context *smb, const char *user)
+{
+       char *suser;
+       struct passwd *passwd;
+
+       if (!(passwd = Get_Pwnam(user))) return;
+
+       suser = strdup(passwd->pw_name);
+       if (!suser) {
+               return;
+       }
+
+       if (suser && *suser && !in_list(suser,smb->users.session_users,False)) {
+               char *p;
+               if (!smb->users.session_users) {
+                       asprintf(&p, "%s", suser);
+               } else {
+                       asprintf(&p, "%s %s", smb->users.session_users, suser);
+               }
+               SAFE_FREE(smb->users.session_users);
+               smb->users.session_users = p;
+       }
+
+       free(suser);
+}
+
+
+/****************************************************************************
+check if a username is valid
+****************************************************************************/
+BOOL user_ok(const char *user,int snum, gid_t *groups, size_t n_groups)
+{
+       char **valid, **invalid;
+       BOOL ret;
+
+       valid = invalid = NULL;
+       ret = True;
+
+       if (lp_invalid_users(snum)) {
+               str_list_copy(&invalid, lp_invalid_users(snum));
+               if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) {
+                       ret = !user_in_list(user, (const char **)invalid, groups, n_groups);
+               }
+       }
+       if (invalid)
+               str_list_free (&invalid);
+
+       if (ret && lp_valid_users(snum)) {
+               str_list_copy(&valid, lp_valid_users(snum));
+               if (valid && str_list_substitute(valid, "%S", lp_servicename(snum))) {
+                       ret = user_in_list(user, (const char **)valid, groups, n_groups);
+               }
+       }
+       if (valid)
+               str_list_free (&valid);
+
+       if (ret && lp_onlyuser(snum)) {
+               char **user_list = str_list_make (lp_username(snum), NULL);
+               if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) {
+                       ret = user_in_list(user, (const char **)user_list, groups, n_groups);
+               }
+               if (user_list) str_list_free (&user_list);
+       }
+
+       return(ret);
+}
+
+/****************************************************************************
+validate a group username entry. Return the username or NULL
+****************************************************************************/
+static const char *validate_group(struct server_context *smb, const char *group, DATA_BLOB password,int snum)
+{
+#ifdef HAVE_NETGROUP
+       {
+               char *host, *user, *domain;
+               setnetgrent(group);
+               while (getnetgrent(&host, &user, &domain)) {
+                       if (user) {
+                               if (user_ok(user, snum, NULL, 0) && 
+                                   password_ok(smb, user,password)) {
+                                       endnetgrent();
+                                       return(user);
+                               }
+                       }
+               }
+               endnetgrent();
+       }
+#endif
+  
+#ifdef HAVE_GETGRENT
+       {
+               struct group *gptr;
+               setgrent();
+               while ((gptr = (struct group *)getgrent())) {
+                       if (strequal(gptr->gr_name,group))
+                               break;
+               }
+
+               /*
+                * As user_ok can recurse doing a getgrent(), we must
+                * copy the member list into a pstring on the stack before
+                * use. Bug pointed out by leon@eatworms.swmed.edu.
+                */
+
+               if (gptr) {
+                       pstring member_list;
+                       char *member;
+                       size_t copied_len = 0;
+                       int i;
+
+                       *member_list = '\0';
+                       member = member_list;
+
+                       for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) {
+                               size_t member_len = strlen(gptr->gr_mem[i]) + 1;
+                               if( copied_len + member_len < sizeof(pstring)) { 
+
+                                       DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i]));
+
+                                       safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1);
+                                       copied_len += member_len;
+                                       member += copied_len;
+                               } else {
+                                       *member = '\0';
+                               }
+                       }
+
+                       endgrent();
+
+                       member = member_list;
+                       while (*member) {
+                               const char *name = member;
+                               if (user_ok(name,snum, NULL, 0) &&
+                                   password_ok(smb,name,password)) {
+                                       endgrent();
+                                       return(&name[0]);
+                               }
+
+                               DEBUG(10,("validate_group = member = %s\n", member));
+
+                               member += strlen(member) + 1;
+                       }
+               } else {
+                       endgrent();
+                       return NULL;
+               }
+       }
+#endif
+       return(NULL);
+}
+
+/****************************************************************************
+ Check for authority to login to a service with a given username/password.
+ Note this is *NOT* used when logging on using sessionsetup_and_X.
+****************************************************************************/
+
+BOOL authorise_login(struct server_context *smb,
+                    int snum, const char *user, DATA_BLOB password, 
+                    BOOL *guest)
+{
+       BOOL ok = False;
+       
+#if DEBUG_PASSWORD
+       DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s\n",
+                  user,password.data));
+#endif
+
+       *guest = False;
+  
+       /* there are several possibilities:
+               1) login as the given user with given password
+               2) login as a previously registered username with the given password
+               3) login as a session list username with the given password
+               4) login as a previously validated user/password pair
+               5) login as the "user =" user with given password
+               6) login as the "user =" user with no password (guest connection)
+               7) login as guest user with no password
+
+               if the service is guest_only then steps 1 to 5 are skipped
+       */
+
+       /* now check the list of session users */
+       if (!ok) {
+               char *auser;
+               char *user_list = strdup(smb->users.session_users);
+               if (!user_list)
+                       return(False);
+               
+               for (auser=strtok(user_list,LIST_SEP); !ok && auser;
+                    auser = strtok(NULL,LIST_SEP)) {
+                       const char *user2 = auser;
+
+                       if (!user_ok(user2,snum, NULL, 0))
+                               continue;
+                       
+                       if (password_ok(smb, user2,password)) {
+                               ok = True;
+                               DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \
+and given password ok\n", user2));
+                       }
+               }
+               
+               SAFE_FREE(user_list);
+       }
+       
+       /* check the user= fields and the given password */
+       if (!ok && lp_username(snum)) {
+               const char *auser;
+               pstring user_list;
+               StrnCpy(user_list,lp_username(snum),sizeof(pstring));
+               
+               pstring_sub(user_list,"%S",lp_servicename(snum));
+               
+               for (auser=strtok(user_list,LIST_SEP); auser && !ok;
+                    auser = strtok(NULL,LIST_SEP)) {
+                       if (*auser == '@') {
+                               auser = validate_group(smb, auser+1,password,snum);
+                               if (auser) {
+                                       ok = True;
+                                       DEBUG(3,("authorise_login: ACCEPTED: group username \
+and given password ok (%s)\n", auser));
+                               }
+                       } else {
+                               const char *user2 = auser;
+                               if (user_ok(user2,snum, NULL, 0) && password_ok(smb, user2,password)) {
+                                       ok = True;
+                                       DEBUG(3,("authorise_login: ACCEPTED: user list username \
+and given password ok (%s)\n", user2));
+                               }
+                       }
+               }
+       }
+
+       /* check for a normal guest connection */
+       if (!ok && GUEST_OK(snum)) {
+               const char *guestname = lp_guestaccount();
+               if (Get_Pwnam(guestname)) {
+                       ok = True;
+                       DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n", guestname));
+               } else {
+                       DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname));
+               }
+               *guest = True;
+       }
+
+       if (ok && !user_ok(user, snum, NULL, 0)) {
+               DEBUG(0,("authorise_login: rejected invalid user %s\n",user));
+               ok = False;
+       }
+
+       return(ok);
+}
diff --git a/source4/smbd/process.c b/source4/smbd/process.c
new file mode 100644 (file)
index 0000000..a4e67c7
--- /dev/null
@@ -0,0 +1,833 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process incoming packets - main loop
+   Copyright (C) Andrew Tridgell 1992-2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+SIG_ATOMIC_T reload_after_sighup = 0;
+SIG_ATOMIC_T got_sig_term = 0;
+
+/*
+  send an oplock break request to a client
+*/
+BOOL req_send_oplock_break(struct tcon_context *conn, uint16 fnum, uint8 level)
+{
+       struct request_context *req;
+
+       req = init_smb_request(conn->smb);
+
+       req_setup_reply(req, 8, 0);
+       
+       SCVAL(req->out.hdr,HDR_COM,SMBlockingX);
+       SSVAL(req->out.hdr,HDR_TID,conn->cnum);
+       SSVAL(req->out.hdr,HDR_PID,0xFFFF);
+       SSVAL(req->out.hdr,HDR_UID,0);
+       SSVAL(req->out.hdr,HDR_MID,0xFFFF);
+       SCVAL(req->out.hdr,HDR_FLG,0);
+       SSVAL(req->out.hdr,HDR_FLG2,0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), fnum);
+       SSVAL(req->out.vwv, VWV(3), level);
+       SIVAL(req->out.vwv, VWV(4), 0);
+       SSVAL(req->out.vwv, VWV(6), 0);
+       SSVAL(req->out.vwv, VWV(7), 0);
+
+       req_send_reply(req);
+       return True;
+}
+
+/****************************************************************************
+receive a SMB request from the wire, forming a request_context from the result
+****************************************************************************/
+static struct request_context *receive_smb_request(struct server_context *smb)
+{
+       ssize_t len, len2;
+       char header[4];
+       struct request_context *req;
+
+       len = read_data(smb->socket.fd, header, 4);
+       if (len != 4) {
+               return NULL;
+       }
+
+       len = smb_len(header);
+
+       req = init_smb_request(smb);
+
+       GetTimeOfDay(&req->request_time);
+       req->chained_fnum = -1;
+       
+       /* allocate the incoming buffer at the right size */
+       req->in.buffer = talloc(req->mem_ctx, len + NBT_HDR_SIZE);
+
+       /* fill in the already received header */
+       memcpy(req->in.buffer, header, 4);
+
+       len2 = read_data(smb->socket.fd, req->in.buffer + NBT_HDR_SIZE, len);
+       if (len2 != len) {
+               return NULL;
+       }
+
+       /* fill in the rest of the req->in structure */
+       req->in.size = len + NBT_HDR_SIZE;
+       req->in.allocated = req->in.size;
+       req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+       req->in.vwv = req->in.hdr + HDR_VWV;
+       req->in.wct = CVAL(req->in.hdr, HDR_WCT);
+       if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
+               req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
+               req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
+
+               /* the bcc length is only 16 bits, but some packets
+                  (such as SMBwriteX) can be much larger than 64k. We
+                  detect this by looking for a large non-chained NBT
+                  packet (at least 64k bigger than what is
+                  specified). If it is detected then the NBT size is
+                  used instead of the bcc size */
+               if (req->in.data_size + 0x10000 <= 
+                   req->in.size - PTR_DIFF(req->in.data, req->in.buffer) &&
+                   (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) {
+                       /* its an oversized packet! fun for all the family */
+                       req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
+               }
+       }
+
+       return req;
+}
+
+/*
+  setup the user_ctx element of a request
+*/
+static void setup_user_context(struct request_context *req)
+{
+       struct user_context *ctx;
+
+       ctx = talloc(req->mem_ctx, sizeof(*ctx));
+       ctx->vuid = SVAL(req->in.hdr, HDR_UID);
+       ctx->vuser = get_valid_user_struct(req->smb, ctx->vuid);
+
+       req->user_ctx = ctx;
+}
+
+
+/*
+These flags determine some of the permissions required to do an operation 
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+#define USE_MUTEX (1<<7)
+
+/* 
+   define a list of possible SMB messages and their corresponding
+   functions. Any message that has a NULL function is unimplemented -
+   please feel free to contribute implementations!
+*/
+static const struct smb_message_struct
+{
+       const char *name;
+       void (*fn)(struct request_context *);
+       int flags;
+}
+ smb_messages[256] = {
+/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+/* 0x02 */ { "SMBopen",reply_open,AS_USER },
+/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
+/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
+/* 0x05 */ { "SMBflush",reply_flush,AS_USER},
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE },
+/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE },
+/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
+/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+/* 0x0a */ { "SMBread",reply_read,AS_USER},
+/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC },
+/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
+/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
+/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER },
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, 
+/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER},
+/* 0x11 */ { "SMBexit",reply_exit,0},
+/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
+/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER},
+/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER},
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
+/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
+/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
+/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
+/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
+/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
+/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER },
+/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
+/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC},
+/* 0x27 */ { "SMBioctl",reply_ioctl,0},
+/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE },
+/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE },
+/* 0x2b */ { "SMBecho",reply_echo,0},
+/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
+/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC },
+/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | CAN_IPC },
+/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER},
+/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER},
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon",reply_tcon,USE_MUTEX},
+/* 0x71 */ { "SMBtdis",reply_tdis,0},
+/* 0x72 */ { "SMBnegprot",reply_negprot,USE_MUTEX},
+/* 0x73 */ { "SMBsesssetupX",reply_sesssetup,USE_MUTEX},
+/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",reply_tcon_and_X,USE_MUTEX},
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER},
+/* 0x81 */ { "SMBsearch",reply_search,AS_USER},
+/* 0x82 */ { "SMBffirst",reply_search,AS_USER},
+/* 0x83 */ { "SMBfunique",reply_search,AS_USER},
+/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER},
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
+/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 },
+/* 0xa5 */ { NULL, NULL, 0 },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER },
+/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER},
+/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER},
+/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER},
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST},
+/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST},
+/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
+/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
+/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
+};
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+static const char *smb_fn_name(uint8 type)
+{
+       const char *unknown_name = "SMBunknown";
+
+       if (smb_messages[type].name == NULL)
+               return unknown_name;
+
+       return smb_messages[type].name;
+}
+
+
+/****************************************************************************
+ Do a switch on the message type and call the specific reply function for this 
+message. Unlike earlier versions of Samba the reply functions are responsible
+for sending the reply themselves, rather than returning a size to this function
+The reply functions may also choose to delay the processing by pushing the message
+onto the message queue
+****************************************************************************/
+static void switch_message(int type, struct request_context *req)
+{
+       int flags;
+       uint16 session_tag;
+       struct server_context *smb = req->smb;
+
+       type &= 0xff;
+
+       errno = 0;
+
+       if (smb_messages[type].fn == NULL) {
+               DEBUG(0,("Unknown message type %d!\n",type));
+               reply_unknown(req);
+               return;
+       }
+
+       flags = smb_messages[type].flags;
+
+       /* In share mode security we must ignore the vuid. */
+       session_tag = (lp_security() == SEC_SHARE) ? 
+               UID_FIELD_INVALID : 
+               SVAL(req->in.hdr,HDR_UID);
+
+       req->conn = conn_find(req->smb, SVAL(req->in.hdr,HDR_TID));
+
+       /* setup the user context for this request */
+       setup_user_context(req);
+
+       /* Ensure this value is replaced in the incoming packet. */
+       SSVAL(req->in.hdr,HDR_UID,session_tag);
+
+       if (req->user_ctx) {
+               req->user_ctx->vuid = session_tag;
+       }
+       DEBUG(3,("switch message %s (task_id %d)\n",smb_fn_name(type), smb->model_ops->get_id(req)));
+
+       /* does this protocol need to be run as root? */
+       if (!(flags & AS_USER)) {
+               change_to_root_user();
+       }
+       
+       /* does this protocol need a valid tree connection? */
+       if ((flags & AS_USER) && !req->conn) {
+               req_reply_error(req, NT_STATUS_NETWORK_NAME_DELETED);
+               return;
+       }
+
+       /* does this protocol need to be run as the connected user? */
+#if HACK_REWRITE
+       if ((flags & AS_USER) && !change_to_user(req->conn,session_tag)) {
+               if (!(flags & AS_GUEST)) {
+                       req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+                       return;
+               }
+
+               /* we'll run it as guest */
+               flags &= ~AS_USER;
+       }
+#endif
+
+       /* this code is to work around a bug is MS client 3 without
+          introducing a security hole - it needs to be able to do
+          print queue checks as guest if it isn't logged in properly */
+       if (flags & AS_USER) {
+               flags &= ~AS_GUEST;
+       }
+       
+       /* does it need write permission? */
+       if ((flags & NEED_WRITE) && !CAN_WRITE(req->conn)) {
+               req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+       
+       /* ipc services are limited */
+       if (req->conn && req->conn->type == NTVFS_IPC && (flags & AS_USER) && !(flags & CAN_IPC)) {
+               req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+       
+       /* load service specific parameters */
+       if (req->conn && !set_current_service(req->conn,(flags & AS_USER)?True:False)) {
+               req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+       
+       /* does this protocol need to be run as guest? */
+#if HACK_REWRITE
+       if ((flags & AS_GUEST) && 
+           !change_to_guest()) {
+               req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+#endif
+       /* THREAD TESTING: use mutex to serialize calls to critical functions with global state */
+       if (flags & USE_MUTEX) {
+               MUTEX_LOCK_BY_ID(MUTEX_SMBD);
+       }
+       smb_messages[type].fn(req);
+       if (flags & USE_MUTEX) {
+               MUTEX_UNLOCK_BY_ID(MUTEX_SMBD);
+       }
+}
+
+
+/****************************************************************************
+ Construct a reply to the incoming packet.
+****************************************************************************/
+static void construct_reply(struct request_context *req)
+{
+       uint8 type = CVAL(req->in.hdr,HDR_COM);
+
+       /* see if its a special NBT packet */
+       if (CVAL(req->in.buffer,0) != 0) {
+               reply_special(req);
+               return;
+       }
+
+
+       /* Make sure this is an SMB packet */   
+       if (memcmp(req->in.hdr,"\377SMB",4) != 0) {
+               DEBUG(2,("Non-SMB packet of length %d. Terminating connection\n", 
+                        req->in.size));
+               exit_server(req->smb, "Non-SMB packet");
+               return;
+       }
+
+       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
+               DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
+               exit_server(req->smb, "Invalid SMB packet");
+               return;
+       }
+
+       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
+               DEBUG(2,("Invalid SMB buffer length count %d\n", req->in.data_size));
+               exit_server(req->smb, "Invalid SMB packet");
+               return;
+       }
+
+
+       req->smbpid = SVAL(req->in.hdr,HDR_PID);        
+       req->flags = CVAL(req->in.hdr, HDR_FLG);
+       req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+       switch_message(type, req);
+}
+
+
+/*
+  we call this when first first part of a possibly chained request has been completed
+  and we need to call the 2nd part, if any
+*/
+void chain_reply(struct request_context *req)
+{
+       uint16 chain_cmd, chain_offset;
+       char *vwv, *data;
+       uint16 wct;
+       uint16 data_size;
+
+       if (req->in.wct < 2 || req->out.wct < 2) {
+               req_reply_dos_error(req, ERRSRV, ERRerror);
+               return;
+       }
+
+       chain_cmd    = CVAL(req->in.vwv, VWV(0));
+       chain_offset = SVAL(req->in.vwv, VWV(1));
+
+       if (chain_cmd == SMB_CHAIN_NONE) {
+               /* end of chain */
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               req_send_reply(req);
+               return;
+       }
+
+       if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) {
+               goto error;
+       }
+
+       wct = CVAL(req->in.hdr, chain_offset);
+       vwv = req->in.hdr + chain_offset + 1;
+
+       if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) {
+               goto error;
+       }
+
+       data_size = SVAL(vwv, VWV(wct));
+       data = vwv + VWV(wct) + 2;
+
+       if (data + data_size > req->in.buffer + req->in.size) {
+               goto error;
+       }
+
+       /* all seems legit */
+       req->in.vwv = vwv;
+       req->in.wct = wct;
+       req->in.data = data;
+       req->in.data_size = data_size;
+       req->in.ptr = data;
+
+       req->chain_count++;
+
+       SSVAL(req->out.vwv, VWV(0), chain_cmd);
+       SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
+
+       /* the current request in the chain might have used an async reply,
+          but that doesn't mean the next element needs to */
+       ZERO_STRUCT(req->async);
+       req->control_flags &= ~REQ_CONTROL_ASYNC;
+
+       switch_message(chain_cmd, req);
+       return;
+
+error:
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       req_reply_dos_error(req, ERRSRV, ERRerror);
+}
+
+
+/*
+  close the socket and shutdown a server_context
+*/
+void server_terminate(struct server_context *smb)
+{
+       close(smb->socket.fd);
+       event_remove_fd_all(smb->events, smb->socket.fd);
+
+       conn_close_all(smb);
+
+       talloc_destroy(smb->mem_ctx);
+}
+
+
+/*
+  called when a SMB socket becomes readable
+*/
+static void smbd_read_handler(struct event_context *ev, struct fd_event *fde, 
+                             time_t t, uint16 flags)
+{
+       struct request_context *req;
+       struct server_context *smb = fde->private;
+       
+       req = receive_smb_request(smb);
+       if (!req) {
+               smb->model_ops->terminate_connection(smb, "receive error");
+               return;
+       }
+
+       construct_reply(req);
+
+       /* free up temporary memory */
+       lp_talloc_free();
+}
+
+
+/*
+  process a message from an SMB socket while still processing a
+  previous message this is used by backends who need to ensure that
+  new messages from clients are still processed while they are
+  performing long operations
+*/
+void smbd_process_async(struct server_context *smb)
+{
+       struct request_context *req;
+       
+       req = receive_smb_request(smb);
+       if (!req) {
+               smb->model_ops->terminate_connection(smb, "receive error");
+               return;
+       }
+
+       construct_reply(req);
+}
+
+
+/*
+  initialise a server_context from a open socket and register a event handler
+  for reading from that socket
+*/
+void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int fd)
+{
+       struct server_context *smb;
+       TALLOC_CTX *mem_ctx;
+       struct fd_event fde;
+
+       set_socket_options(fd,"SO_KEEPALIVE");
+       set_socket_options(fd, lp_socket_options());
+
+       mem_ctx = talloc_init("server_context");
+
+       smb = (struct server_context *)talloc(mem_ctx, sizeof(*smb));
+       if (!smb) return;
+
+       ZERO_STRUCTP(smb);
+
+       smb->mem_ctx = mem_ctx;
+       smb->socket.fd = fd;
+       smb->pid = getpid();
+
+       sub_set_context(&smb->substitute);
+
+       /* set an initial client name based on its IP address. This will be replaced with
+          the netbios name later if it gives us one */
+       sub_set_remote_machine(strdup(get_socket_addr(smb->mem_ctx, fd)));
+       smb->socket.client_addr = talloc_strdup(smb->mem_ctx, get_socket_addr(smb->mem_ctx, fd));
+
+       /* now initialise a few default values associated with this smb socket */
+       smb->negotiate.max_send = 0xFFFF;
+
+       /* this is the size that w2k uses, and it appears to be important for
+          good performance */
+       smb->negotiate.max_recv = 4356;
+
+       smb->users.next_vuid = VUID_OFFSET;
+       
+       smb->events = ev;
+       smb->model_ops = model_ops;
+
+       conn_init(smb);
+
+       /* setup a event handler for this socket. We are initially
+          only interested in reading from the socket */
+       fde.fd = fd;
+       fde.handler = smbd_read_handler;
+       fde.private = smb;
+       fde.flags = EVENT_FD_READ;
+
+       event_add_fd(ev, &fde);
+}
+
+
+/*
+ * initialize an smb process
+ */
+void smbd_process_init(void)
+{
+       TALLOC_CTX *mem_ctx;
+       
+       mem_ctx = talloc_init("smbd_process_init talloc");
+       if (!mem_ctx) {
+               DEBUG(0,("smbd_process_init: ERROR: No memory\n"));
+               exit(1);
+       }
+       namecache_enable();
+
+       if (!locking_init(0))
+               exit(1);
+
+       if (!share_info_db_init())
+               exit(1);
+
+       if (!init_registry())
+               exit(1);
+
+       if(!initialize_password_db(False))
+               exit(1);
+
+       /* possibly reload the services file. */
+       reload_services(NULL, True);
+
+       if(!get_global_sam_sid()) {
+               DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n"));
+               exit(1);
+       }
+
+       if (!init_account_policy()) {
+               DEBUG(0,("Could not open account policy tdb.\n"));
+               exit(1);
+       }
+
+       if (*lp_rootdir()) {
+               if (sys_chroot(lp_rootdir()) == 0)
+                       DEBUG(2,("Changed root to %s\n", lp_rootdir()));
+       }
+
+       /* Setup oplocks */
+       if (!init_oplocks())
+               exit(1);
+       
+       /* Setup change notify */
+       if (!init_change_notify())
+               exit(1);
+
+       /* Setup privileges database */
+       if (!privilege_init())
+               exit(1);
+
+       /* Setup the NTVFS subsystem */
+       if (!ntvfs_init())
+               exit(1);
+
+       /* re-initialise the timezone */
+       TimeInit();
+       
+       talloc_destroy(mem_ctx);
+}
+
diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c
new file mode 100644 (file)
index 0000000..06127d1
--- /dev/null
@@ -0,0 +1,85 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process model manager - main loop
+   Copyright (C) Andrew Tridgell 1992-2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+
+/* the list of currently registered process models */
+static struct {
+       const char *name;
+       struct model_ops *ops;
+} *models = NULL;
+static int num_models;
+
+/*
+  register a process model. 
+
+  The 'name' can be later used by other backends to find the operations
+  structure for this backend.  
+*/
+BOOL register_process_model(const char *name, struct model_ops *ops)
+{
+       if (process_model_byname(name) != NULL) {
+               /* its already registered! */
+               DEBUG(2,("process_model '%s' already registered\n", 
+                        name));
+               return False;
+       }
+
+       models = Realloc(models, sizeof(models[0]) * (num_models+1));
+       if (!models) {
+               smb_panic("out of memory in register_process_model");
+       }
+
+       models[num_models].name = smb_xstrdup(name);
+       models[num_models].ops = smb_xmemdup(ops, sizeof(*ops));
+
+       num_models++;
+
+       return True;
+}
+
+/*
+  return the operations structure for a named backend of the specified type
+*/
+struct model_ops *process_model_byname(const char *name)
+{
+       int i;
+
+       for (i=0;i<num_models;i++) {
+               if (strcmp(models[i].name, name) == 0) {
+                       return models[i].ops;
+               }
+       }
+
+       return NULL;
+}
+
+
+/* initialise the builtin process models */
+void process_model_init(void)
+{
+       process_model_standard_init();
+       process_model_single_init();
+#ifdef WITH_PTHREADS
+       process_model_thread_init();
+#endif
+}
diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c
new file mode 100644 (file)
index 0000000..e6bb9b6
--- /dev/null
@@ -0,0 +1,86 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process model: process (1 process handles all client connections)
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+/*
+  called when the process model is selected
+*/
+static void model_startup(void)
+{
+       smbd_process_init();
+}
+
+/*
+  called when a listening socket becomes readable
+*/
+static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags)
+{
+       int accepted_fd;
+       struct sockaddr addr;
+       socklen_t in_addrlen = sizeof(addr);
+       struct model_ops *model_ops = fde->private;
+       
+       /* accept an incoming connection. */
+       accepted_fd = accept(fde->fd,&addr,&in_addrlen);
+       if (accepted_fd == -1) {
+               DEBUG(0,("accept_connection_single: accept: %s\n",
+                        strerror(errno)));
+               return;
+       }
+
+       /* create a smb server context and add it to out event
+          handling */
+       init_smbsession(ev, model_ops, accepted_fd); 
+
+       /* return to event handling */
+}
+
+/* called when a SMB connection goes down */
+static void terminate_connection(struct server_context *server, const char *reason) 
+{
+       server_terminate(server);
+}
+
+static int get_id(struct request_context *req)
+{
+       return (int)req->smb->pid;
+}
+
+/*
+  initialise the single process model, registering ourselves with the model subsystem
+ */
+void process_model_single_init(void)
+{
+       struct model_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.model_startup = model_startup;
+       ops.accept_connection = accept_connection;
+       ops.terminate_connection = terminate_connection;
+       ops.exit_server = NULL;
+       ops.get_id = get_id;
+
+       /* register ourselves with the process model subsystem. We register under the name 'single'. */
+       register_process_model("single", &ops);
+}
diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
new file mode 100644 (file)
index 0000000..f12784b
--- /dev/null
@@ -0,0 +1,110 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process model: standard (1 process per client connection)
+   Copyright (C) Andrew Tridgell 1992-2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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"
+
+/*
+  called when the process model is selected
+*/
+static void model_startup(void)
+{
+}
+
+/*
+  called when a listening socket becomes readable
+*/
+static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags)
+{
+       int accepted_fd;
+       struct sockaddr addr;
+       socklen_t in_addrlen = sizeof(addr);
+       pid_t pid;
+       struct model_ops *model_ops = fde->private;
+
+       accepted_fd = accept(fde->fd,&addr,&in_addrlen);
+       if (accepted_fd == -1) {
+               DEBUG(0,("accept_connection_standard: accept: %s\n",
+                        strerror(errno)));
+               return;
+       }
+
+       pid = fork();
+
+       if (pid != 0) {
+               /* parent or error code ... */
+
+               close(accepted_fd);
+               /* go back to the event loop */
+               return;
+       }
+
+       /* Child code ... */
+
+       /* close all the listening sockets */
+       event_remove_fd_all_handler(ev, accept_connection);
+                       
+       /* tdb needs special fork handling */
+       if (tdb_reopen_all() == -1) {
+               DEBUG(0,("accept_connection_standard: tdb_reopen_all failed.\n"));
+       }
+
+       /* Load DSO's */
+       init_modules();
+               
+       /* initialize new process */
+       smbd_process_init();
+               
+       init_smbsession(ev, model_ops, accepted_fd); 
+
+       /* return to the event loop */
+}
+
+/* called when a SMB connection goes down */
+static void terminate_connection(struct server_context *server, const char *reason) 
+{
+       server_terminate(server);
+       /* terminate this process */
+       exit(0);
+}
+
+static int get_id(struct request_context *req)
+{
+       return (int)req->smb->pid;
+}
+
+/*
+  initialise the standard process model, registering ourselves with the model subsystem
+ */
+void process_model_standard_init(void)
+{
+       struct model_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.model_startup = model_startup;
+       ops.accept_connection = accept_connection;
+       ops.terminate_connection = terminate_connection;
+       ops.get_id = get_id;
+
+       /* register ourselves with the process model subsystem. We register under the name 'standard'. */
+       register_process_model("standard", &ops);
+}
diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c
new file mode 100644 (file)
index 0000000..cd8865f
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+   Unix SMB/CIFS implementation.
+   thread model: standard (1 thread per client connection)
+   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) James J Myers 2003 <myersjj@samba.org>
+   
+   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 "pthread.h"
+#include "execinfo.h"
+
+static void *connection_thread(void *thread_parm)
+{
+       struct event_context *ev = thread_parm;
+       /* wait for action */
+       event_loop_wait(ev);
+       
+#if 0
+       pthread_cleanup_pop(1);  /* will invoke terminate_mt_connection() */
+#endif
+       return NULL;
+}
+
+static int get_id(struct request_context *req)
+{
+       return (int)pthread_self();
+}
+
+/*
+  called when a listening socket becomes readable
+*/
+static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags)
+{
+       int accepted_fd, rc;
+       struct sockaddr addr;
+       socklen_t in_addrlen = sizeof(addr);
+       pthread_t thread_id;
+       pthread_attr_t thread_attr;
+       struct model_ops *model_ops = fde->private;
+       
+       /* accept an incoming connection */
+       accepted_fd = accept(fde->fd,&addr,&in_addrlen);
+                       
+       if (accepted_fd == -1) {
+               DEBUG(0,("accept_connection_thread: accept: %s\n",
+                        strerror(errno)));
+               return;
+       }
+       
+       /* create new detached thread for this connection.  The new
+          thread gets a new event_context with a single fd_event for
+          receiving from the new socket. We set that thread running
+          with the main event loop, then return. When we return the
+          main event_context is continued.
+       */
+       ev = event_context_init();
+       MUTEX_LOCK_BY_ID(MUTEX_SMBD);
+       init_smbsession(ev, model_ops, accepted_fd);
+       MUTEX_UNLOCK_BY_ID(MUTEX_SMBD);
+       
+       pthread_attr_init(&thread_attr);
+       pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+       rc = pthread_create(&thread_id, &thread_attr, &connection_thread, ev);
+       pthread_attr_destroy(&thread_attr);
+       if (rc == 0) {
+               DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n", 
+                       (unsigned long int)thread_id, accepted_fd));
+       } else {
+               DEBUG(0,("accept_connection_thread: thread create failed for fd=%d, rc=%d\n", accepted_fd, rc));
+       }
+}
+
+/* called when a SMB connection goes down */
+static void terminate_connection(struct server_context *server, const char *reason) 
+{
+       server_terminate(server);
+
+       /* terminate this thread */
+       pthread_exit(NULL);  /* thread cleanup routine will do actual cleanup */
+}
+
+/*
+  mutex init function for thread model
+*/
+static int thread_mutex_init(mutex_t *mutex, const char *name)
+{
+       pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+       mutex->mutex = memdup(&m, sizeof(m));
+       if (! mutex->mutex) {
+               errno = ENOMEM;
+               return -1;
+       }
+       return pthread_mutex_init((pthread_mutex_t *)mutex->mutex, NULL);
+}
+
+/*
+  mutex destroy function for thread model
+*/
+static int thread_mutex_destroy(mutex_t *mutex, const char *name)
+{
+       return pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex);
+}
+
+static void mutex_start_timer(struct timeval *tp1)
+{
+       gettimeofday(tp1,NULL);
+}
+
+static double mutex_end_timer(struct timeval tp1)
+{
+       struct timeval tp2;
+       gettimeofday(&tp2,NULL);
+       return((tp2.tv_sec - tp1.tv_sec) + 
+              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+/*
+  mutex lock function for thread model
+*/
+static int thread_mutex_lock(mutex_t *mutexP, const char *name)
+{
+       pthread_mutex_t *mutex = (pthread_mutex_t *)mutexP->mutex;
+       int rc;
+       double t;
+       struct timeval tp1;
+       /* Test below is ONLY for debugging */
+       if ((rc = pthread_mutex_trylock(mutex))) {
+               if (rc == EBUSY) {
+                       mutex_start_timer(&tp1);
+                       printf("mutex lock: thread %d, lock %s not available\n", 
+                               (uint32)pthread_self(), name);
+                       print_suspicious_usage("mutex_lock", name);
+                       pthread_mutex_lock(mutex);
+                       t = mutex_end_timer(tp1);
+                       printf("mutex lock: thread %d, lock %s now available, waited %g seconds\n", 
+                               (uint32)pthread_self(), name, t);
+                       return 0;
+               }
+               printf("mutex lock: thread %d, lock %s failed rc=%d\n", 
+                               (uint32)pthread_self(), name, rc);
+               SMB_ASSERT(errno == 0); /* force error */
+       }
+       return 0;
+}
+
+/* 
+   mutex unlock for thread model
+*/
+static int thread_mutex_unlock(mutex_t *mutex, const char *name)
+{
+       return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex);
+}
+
+/*****************************************************************
+ Read/write lock routines.
+*****************************************************************/  
+/*
+  rwlock init function for thread model
+*/
+static int thread_rwlock_init(rwlock_t *rwlock, const char *name)
+{
+       pthread_rwlock_t m = PTHREAD_RWLOCK_INITIALIZER;
+       rwlock->rwlock = memdup(&m, sizeof(m));
+       if (! rwlock->rwlock) {
+               errno = ENOMEM;
+               return -1;
+       }
+       return pthread_rwlock_init((pthread_rwlock_t *)rwlock->rwlock, NULL);
+}
+
+/*
+  rwlock destroy function for thread model
+*/
+static int thread_rwlock_destroy(rwlock_t *rwlock, const char *name)
+{
+       return pthread_rwlock_destroy((pthread_rwlock_t *)rwlock->rwlock);
+}
+
+/*
+  rwlock lock for read function for thread model
+*/
+static int thread_rwlock_lock_read(rwlock_t *rwlockP, const char *name)
+{
+       pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock;
+       int rc;
+       double t;
+       struct timeval tp1;
+       /* Test below is ONLY for debugging */
+       if ((rc = pthread_rwlock_tryrdlock(rwlock))) {
+               if (rc == EBUSY) {
+                       mutex_start_timer(&tp1);
+                       printf("rwlock lock_read: thread %d, lock %s not available\n", 
+                               (uint32)pthread_self(), name);
+                       print_suspicious_usage("rwlock_lock_read", name);
+                       pthread_rwlock_rdlock(rwlock);
+                       t = mutex_end_timer(tp1);
+                       printf("rwlock lock_read: thread %d, lock %s now available, waited %g seconds\n", 
+                               (uint32)pthread_self(), name, t);
+                       return 0;
+               }
+               printf("rwlock lock_read: thread %d, lock %s failed rc=%d\n", 
+                               (uint32)pthread_self(), name, rc);
+               SMB_ASSERT(errno == 0); /* force error */
+       }
+       return 0;
+}
+
+/*
+  rwlock lock for write function for thread model
+*/
+static int thread_rwlock_lock_write(rwlock_t *rwlockP, const char *name)
+{
+       pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock;
+       int rc;
+       double t;
+       struct timeval tp1;
+       /* Test below is ONLY for debugging */
+       if ((rc = pthread_rwlock_trywrlock(rwlock))) {
+               if (rc == EBUSY) {
+                       mutex_start_timer(&tp1);
+                       printf("rwlock lock_write: thread %d, lock %s not available\n", 
+                               (uint32)pthread_self(), name);
+                       print_suspicious_usage("rwlock_lock_write", name);
+                       pthread_rwlock_wrlock(rwlock);
+                       t = mutex_end_timer(tp1);
+                       printf("rwlock lock_write: thread %d, lock %s now available, waited %g seconds\n", 
+                               (uint32)pthread_self(), name, t);
+                       return 0;
+               }
+               printf("rwlock lock_write: thread %d, lock %s failed rc=%d\n", 
+                               (uint32)pthread_self(), name, rc);
+               SMB_ASSERT(errno == 0); /* force error */
+       }
+       return 0;
+}
+
+
+/* 
+   rwlock unlock for thread model
+*/
+static int thread_rwlock_unlock(rwlock_t *rwlock, const char *name)
+{
+       return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock->rwlock);
+}
+
+/*****************************************************************
+ Log suspicious usage (primarily for possible thread-unsafe behavior.
+*****************************************************************/  
+static void thread_log_suspicious_usage(const char* from, const char* info)
+{
+       void *addresses[10];
+       int num_addresses, i;
+       char **bt_symbols;
+       
+       DEBUG(1,("log_suspicious_usage: from %s info='%s'\n", from, info));
+       num_addresses = backtrace(addresses, 8);
+       bt_symbols = backtrace_symbols(addresses, num_addresses);
+       for (i=0; i<num_addresses; i++) {
+               DEBUG(1,("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]));
+       }
+       free(bt_symbols);
+}
+
+/*****************************************************************
+ Log suspicious usage to stdout (primarily for possible thread-unsafe behavior.
+ Used in mutex code where DEBUG calls would cause recursion.
+*****************************************************************/  
+static void thread_print_suspicious_usage(const char* from, const char* info)
+{
+       void *addresses[10];
+       int num_addresses, i;
+       char **bt_symbols;
+       
+       printf("log_suspicious_usage: from %s info='%s'\n", from, info);
+       num_addresses = backtrace(addresses, 8);
+       bt_symbols = backtrace_symbols(addresses, num_addresses);
+       for (i=0; i<num_addresses; i++) {
+               printf("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]);
+       }
+       free(bt_symbols);
+}
+
+uint32 thread_get_task_id(void)
+{
+       return (uint32)pthread_self();
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void thread_sig_fault(int sig)
+{
+       DEBUG(0,("===============================================================\n"));
+       DEBUG(0,("TERMINAL ERROR: Recursive signal %d in thread %lu (%s)\n",sig,(unsigned long int)pthread_self(),SAMBA_VERSION));
+       DEBUG(0,("===============================================================\n"));
+       exit(1); /* kill the whole server for now */
+}
+
+/*******************************************************************
+setup our recursive fault handlers
+********************************************************************/
+static void thread_fault_setup(void)
+{
+#ifdef SIGSEGV
+       CatchSignal(SIGSEGV,SIGNAL_CAST thread_sig_fault);
+#endif
+#ifdef SIGBUS
+       CatchSignal(SIGBUS,SIGNAL_CAST thread_sig_fault);
+#endif
+}
+
+/*******************************************************************
+report a fault in a thread
+********************************************************************/
+static void thread_fault_handler(int sig)
+{
+       static int counter;
+       void *addresses[10];
+       int num_addresses, i;
+       char **bt_symbols;
+       
+       /* try to catch recursive faults */
+       thread_fault_setup();
+       
+       counter++;      /* count number of faults that have occurred */
+
+       DEBUG(0,("===============================================================\n"));
+       DEBUG(0,("INTERNAL ERROR: Signal %d in thread %lu (%s)\n",sig,(unsigned long int)pthread_self(),SAMBA_VERSION));
+       DEBUG(0,("Please read the file BUGS.txt in the distribution\n"));
+       DEBUG(0,("===============================================================\n"));
+
+       num_addresses = backtrace(addresses, 10);
+       bt_symbols = backtrace_symbols(addresses, num_addresses);
+       for (i=0; i<num_addresses; i++) {
+               DEBUG(9,("fault_report:   %s\n", bt_symbols[i]));
+       }
+       free(bt_symbols);
+       pthread_exit(NULL); /* terminate failing thread only */
+}
+
+/*
+  called when the process model is selected
+*/
+static void model_startup(void)
+{
+       struct mutex_ops m_ops;
+       struct debug_ops d_ops;
+
+       ZERO_STRUCT(m_ops);
+       ZERO_STRUCT(d_ops);
+
+       smbd_process_init();
+
+       /* register mutex/rwlock handlers */
+       m_ops.mutex_init = thread_mutex_init;
+       m_ops.mutex_lock = thread_mutex_lock;
+       m_ops.mutex_unlock = thread_mutex_unlock;
+       m_ops.mutex_destroy = thread_mutex_destroy;
+       
+       m_ops.rwlock_init = thread_rwlock_init;
+       m_ops.rwlock_lock_write = thread_rwlock_lock_write;
+       m_ops.rwlock_lock_read = thread_rwlock_lock_read;
+       m_ops.rwlock_unlock = thread_rwlock_unlock;
+       m_ops.rwlock_destroy = thread_rwlock_destroy;
+
+       register_mutex_handlers("thread", &m_ops);
+
+       register_fault_handler("thread", thread_fault_handler);
+
+       d_ops.log_suspicious_usage = thread_log_suspicious_usage;
+       d_ops.print_suspicious_usage = thread_print_suspicious_usage;
+       d_ops.get_task_id = thread_get_task_id;
+
+       register_debug_handlers("thread", &d_ops);      
+}
+
+/*
+  initialise the thread process model, registering ourselves with the model subsystem
+ */
+void process_model_thread_init(void)
+{
+       struct model_ops ops;
+
+       ZERO_STRUCT(ops);
+       
+       /* fill in all the operations */
+       ops.model_startup = model_startup;
+       ops.accept_connection = accept_connection;
+       ops.terminate_connection = terminate_connection;
+       ops.exit_server = NULL;
+       ops.get_id = get_id;
+       
+       /* register ourselves with the process model subsystem. We
+          register under the name 'thread'. */
+       register_process_model("thread", &ops);
+}
diff --git a/source4/smbd/reply.c b/source4/smbd/reply.c
new file mode 100644 (file)
index 0000000..07fcf06
--- /dev/null
@@ -0,0 +1,2383 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main SMB reply routines
+   Copyright (C) Andrew Tridgell 1992-2003
+
+   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.
+*/
+/*
+   This file handles most of the reply_ calls that the server
+   makes to handle specific protocols
+*/
+
+#include "includes.h"
+
+/* useful way of catching wct errors with file and line number */
+#define REQ_CHECK_WCT(req, wcount) do { \
+       if ((req)->in.wct != (wcount)) { \
+               DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \
+                        (req)->in.wct, __FILE__, __LINE__, wcount)); \
+               req_reply_dos_error(req, ERRSRV, ERRerror); \
+               return; \
+       }} while (0)
+
+/* check req->async.status and if not OK then send an error reply */
+#define CHECK_ASYNC_STATUS do { \
+       if (!NT_STATUS_IS_OK(req->async.status)) { \
+               req_reply_error(req, req->async.status); \
+               return; \
+       }} while (0)
+       
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define REQ_TALLOC(ptr, size) do { \
+       ptr = talloc(req->mem_ctx, size); \
+       if (!ptr) { \
+               req_reply_error(req, NT_STATUS_NO_MEMORY); \
+               return; \
+       }} while (0)
+
+/* 
+   check if the backend wants to handle the request asynchronously.
+   if it wants it handled synchronously then call the send function
+   immediately
+*/
+#define REQ_ASYNC_TAIL do { \
+       if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \
+               req->async.send_fn(req); \
+       }} while (0)
+
+/* zero out some reserved fields in a reply */
+#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2)
+
+/*
+  put a NTTIME into a packet
+*/
+void push_nttime(void *base, uint16 offset, NTTIME *t)
+{
+       SIVAL(base, offset,   t->low);
+       SIVAL(base, offset+4, t->high);
+}
+
+/*
+  pull a NTTIME from a packet
+*/
+NTTIME pull_nttime(void *base, uint16 offset)
+{
+       NTTIME ret;
+       ret.low = IVAL(base, offset);
+       ret.high = IVAL(base, offset+4);
+       return ret;
+}
+
+
+/****************************************************************************
+ Reply to a simple request (async send)
+****************************************************************************/
+static void reply_simple_send(struct request_context *req)
+{
+       CHECK_ASYNC_STATUS;
+
+       req_setup_reply(req, 0, 0);
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a tcon.
+****************************************************************************/
+void reply_tcon(struct request_context *req)
+{
+       union smb_tcon con;
+       NTSTATUS status;
+       char *p;
+       
+       /* parse request */
+       REQ_CHECK_WCT(req, 0);
+
+       con.tcon.level = RAW_TCON_TCON;
+
+       p = req->in.data;       
+       p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE);
+       p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE);
+       p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE);
+
+       if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) {
+               req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       /* call backend */
+       status = tcon_backend(req, &con);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 2, 0);
+
+       SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit);
+       SSVAL(req->out.vwv, VWV(1), con.tcon.out.cnum);
+       SSVAL(req->out.hdr, HDR_TID, req->conn->cnum);
+  
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a tcon and X.
+****************************************************************************/
+void reply_tcon_and_X(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_tcon con;
+       char *p;
+       uint16 passlen;
+
+       con.tconx.level = RAW_TCON_TCONX;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 4);
+
+       con.tconx.in.flags  = SVAL(req->in.vwv, VWV(2));
+       passlen             = SVAL(req->in.vwv, VWV(3));
+
+       p = req->in.data;
+
+       if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) {
+               req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD);
+               return;
+       }
+       p += passlen;
+
+       p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII);
+
+       if (!con.tconx.in.path || !con.tconx.in.device) {
+               req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE);
+               return;
+       }
+
+       /* call backend */
+       status = tcon_backend(req, &con);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply - two varients */
+       if (req->smb->negotiate.protocol < PROTOCOL_NT1) {
+               req_setup_reply(req, 2, 0);
+
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+
+               req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
+       } else {
+               req_setup_reply(req, 3, 0);
+
+               SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+               SSVAL(req->out.vwv, VWV(1), 0);
+               SSVAL(req->out.vwv, VWV(2), con.tconx.out.options);
+
+               req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
+               req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE);
+       }
+
+       /* set the incoming and outgoing tid to the just created one */
+       SSVAL(req->in.hdr, HDR_TID, con.tconx.out.cnum);
+       SSVAL(req->out.hdr,HDR_TID, con.tconx.out.cnum);
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an unknown request
+****************************************************************************/
+void reply_unknown(struct request_context *req)
+{
+       int type;
+
+       type = CVAL(req->in.hdr, HDR_COM);
+  
+       DEBUG(0,("unknown command type %d (0x%X)\n", type, type));
+
+       req_reply_dos_error(req, ERRSRV, ERRunknownsmb);
+}
+
+
+/****************************************************************************
+ Reply to an ioctl (async reply)
+****************************************************************************/
+static void reply_ioctl_send(struct request_context *req)
+{
+       struct smb_ioctl *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* the +1 is for nicer alignment */
+       req_setup_reply(req, 8, io->out.blob.length+1);
+       SSVAL(req->out.vwv, VWV(1), io->out.blob.length);
+       SSVAL(req->out.vwv, VWV(5), io->out.blob.length);
+       SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1);
+
+       memcpy(req->out.data+1, io->out.blob.data, io->out.blob.length);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an ioctl.
+****************************************************************************/
+void reply_ioctl(struct request_context *req)
+{
+       struct smb_ioctl *io;
+
+       /* parse requst */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->in.fnum     = req_fnum(req, req->in.vwv, VWV(0));
+       io->in.request  = IVAL(req->in.vwv, VWV(1));
+
+       req->async.send_fn = reply_ioctl_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->ioctl(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a chkpth.
+****************************************************************************/
+void reply_chkpth(struct request_context *req)
+{
+       struct smb_chkpath *io;
+
+       REQ_TALLOC(io, sizeof(*io));
+
+       req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE);
+
+       req->async.send_fn = reply_simple_send;
+
+       req->async.status = req->conn->ntvfs_ops->chkpath(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a getatr (async reply)
+****************************************************************************/
+static void reply_getatr_send(struct request_context *req)
+{
+       union smb_fileinfo *st = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+       
+       /* construct reply */
+       req_setup_reply(req, 10, 0);
+
+       SSVAL(req->out.vwv,         VWV(0), st->getattr.out.attrib);
+       put_dos_date3(req->out.vwv, VWV(1), st->getattr.out.write_time);
+       SIVAL(req->out.vwv,         VWV(3), st->getattr.out.size);
+
+       REQ_VWV_RESERVED(5, 5);
+
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a getatr.
+****************************************************************************/
+void reply_getatr(struct request_context *req)
+{
+       union smb_fileinfo *st;
+
+       REQ_TALLOC(st, sizeof(*st));
+       
+       st->getattr.level = RAW_FILEINFO_GETATTR;
+
+       /* parse request */
+       req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE);
+       if (!st->getattr.in.fname) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       req->async.send_fn = reply_getatr_send;
+       req->async.private = st;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->qpathinfo(req, st);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a setatr.
+****************************************************************************/
+void reply_setatr(struct request_context *req)
+{
+       union smb_setfileinfo *st;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 8);
+       REQ_TALLOC(st, sizeof(*st));
+
+       st->setattr.level = RAW_SFILEINFO_SETATTR;
+       st->setattr.in.attrib     = SVAL(req->in.vwv, VWV(0));
+       st->setattr.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
+       
+       req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE);
+
+       if (!st->setattr.file.fname) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+       
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->setpathinfo(req, st);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a dskattr (async reply)
+****************************************************************************/
+static void reply_dskattr_send(struct request_context *req)
+{
+       union smb_fsinfo *fs = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+       
+       /* construct reply */
+       req_setup_reply(req, 5, 0);
+
+       SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total);
+       SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit);
+       SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size);
+       SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free);
+
+       REQ_VWV_RESERVED(4, 1);
+
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a dskattr.
+****************************************************************************/
+void reply_dskattr(struct request_context *req)
+{
+       union smb_fsinfo *fs;
+
+       REQ_TALLOC(fs, sizeof(*fs));
+       
+       fs->dskattr.level = RAW_QFS_DSKATTR;
+
+       req->async.send_fn = reply_dskattr_send;
+       req->async.private = fs;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->fsinfo(req, fs);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+
+/****************************************************************************
+ Reply to an open (async reply)
+****************************************************************************/
+static void reply_open_send(struct request_context *req)
+{
+       union smb_open *oi = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 7, 0);
+
+       SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum);
+       SSVAL(req->out.vwv, VWV(1), oi->open.out.attrib);
+       put_dos_date3(req->out.vwv, VWV(2), oi->open.out.write_time);
+       SIVAL(req->out.vwv, VWV(4), oi->open.out.size);
+       SSVAL(req->out.vwv, VWV(6), oi->open.out.rmode);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an open.
+****************************************************************************/
+void reply_open(struct request_context *req)
+{
+       union smb_open *oi;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 2);
+       REQ_TALLOC(oi, sizeof(*oi));
+
+       oi->open.level = RAW_OPEN_OPEN;
+       oi->open.in.flags = SVAL(req->in.vwv, VWV(0));
+       oi->open.in.search_attrs = SVAL(req->in.vwv, VWV(1));
+
+       req_pull_ascii4(req, &oi->open.in.fname, req->in.data, STR_TERMINATE);
+
+       if (!oi->open.in.fname) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       req->async.send_fn = reply_open_send;
+       req->async.private = oi;
+       
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, oi);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to an open and X (async reply)
+****************************************************************************/
+static void reply_open_and_X_send(struct request_context *req)
+{
+       union smb_open *oi = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* build the reply */
+       if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
+               req_setup_reply(req, 19, 0);
+       } else {
+               req_setup_reply(req, 15, 0);
+       }
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum);
+       SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib);
+       put_dos_date3(req->out.vwv, VWV(4), oi->openx.out.write_time);
+       SIVAL(req->out.vwv, VWV(6), oi->openx.out.size);
+       SSVAL(req->out.vwv, VWV(8), oi->openx.out.access);
+       SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype);
+       SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate);
+       SSVAL(req->out.vwv, VWV(11),oi->openx.out.action);
+       SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid);
+       SSVAL(req->out.vwv, VWV(14),0); /* reserved */
+       if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
+               SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask);
+               REQ_VWV_RESERVED(17, 2);
+       }
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an open and X.
+****************************************************************************/
+void reply_open_and_X(struct request_context *req)
+{
+       union smb_open *oi;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 15);
+       REQ_TALLOC(oi, sizeof(*oi));
+
+       oi->openx.level = RAW_OPEN_OPENX;
+       oi->openx.in.flags        = SVAL(req->in.vwv, VWV(2));
+       oi->openx.in.open_mode    = SVAL(req->in.vwv, VWV(3));
+       oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4));
+       oi->openx.in.file_attrs   = SVAL(req->in.vwv, VWV(5));
+       oi->openx.in.write_time   = make_unix_date3(req->in.vwv + VWV(6));
+       oi->openx.in.open_func    = SVAL(req->in.vwv, VWV(8));
+       oi->openx.in.size         = IVAL(req->in.vwv, VWV(9));
+       oi->openx.in.timeout      = IVAL(req->in.vwv, VWV(11));
+
+       req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE);
+
+       if (!oi->openx.in.fname) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       req->async.send_fn = reply_open_and_X_send;
+       req->async.private = oi;
+
+       /* call the backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, oi);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+static void reply_mknew_send(struct request_context *req)
+{
+       union smb_open *oi = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* build the reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum);
+
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+void reply_mknew(struct request_context *req)
+{
+       union smb_open *oi;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(oi, sizeof(*oi));
+
+       oi->mknew.level = RAW_OPEN_MKNEW;
+       oi->mknew.in.attrib  = SVAL(req->in.vwv, VWV(0));
+       oi->mknew.in.write_time  = make_unix_date3(req->in.vwv + VWV(1));
+
+       req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE);
+
+       if (!oi->mknew.in.fname) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       req->async.send_fn = reply_mknew_send;
+       req->async.private = oi;
+
+       /* call the backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, oi);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a create temporary file (async reply)
+****************************************************************************/
+static void reply_ctemp_send(struct request_context *req)
+{
+       union smb_open *oi = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* build the reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum);
+
+       /* the returned filename is relative to the directory */
+       req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a create temporary file.
+****************************************************************************/
+void reply_ctemp(struct request_context *req)
+{
+       union smb_open *oi;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(oi, sizeof(*oi));
+
+       oi->ctemp.level = RAW_OPEN_CTEMP;
+       oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0));
+       oi->ctemp.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
+
+       /* the filename is actually a directory name, the server provides a filename
+          in that directory */
+       req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE);
+
+       if (!oi->ctemp.in.directory) {
+               req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return;
+       }
+
+       req->async.send_fn = reply_ctemp_send;
+       req->async.private = oi;
+
+       /* call the backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, oi);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+void reply_unlink(struct request_context *req)
+{
+       struct smb_unlink *unl;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 1);
+       REQ_TALLOC(unl, sizeof(*unl));
+       
+       unl->in.attrib = SVAL(req->in.vwv, VWV(0));
+
+       req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE);
+       
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->unlink(req, unl);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a readbraw (core+ protocol).
+ this is a strange packet because it doesn't use a standard SMB header in the reply,
+ only the 4 byte NBT header
+ This command must be replied to synchronously
+****************************************************************************/
+void reply_readbraw(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_read io;
+
+       io.readbraw.level = RAW_READ_READBRAW;
+
+       /* there are two varients, one with 10 and one with 8 command words */
+       if (req->in.wct != 10) {
+               REQ_CHECK_WCT(req, 8);
+       }
+
+       io.readbraw.in.fnum    = req_fnum(req, req->in.vwv, VWV(0));
+       io.readbraw.in.offset  = IVAL(req->in.vwv, VWV(1));
+       io.readbraw.in.mincnt  = SVAL(req->in.vwv, VWV(3));
+       io.readbraw.in.maxcnt  = SVAL(req->in.vwv, VWV(4));
+       io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5));
+
+       /* the 64 bit varient */
+       if (req->in.wct == 10) {
+               uint32 offset_high = IVAL(req->in.vwv, VWV(8));
+#ifdef LARGE_SMB_OFF_T
+               io.readbraw.in.offset |= (((SMB_OFF_T)offset_high) << 32);
+#else
+               if (offset_high != 0) {
+                       goto failed;
+               }
+#endif
+       }
+
+       /* before calling the backend we setup the raw buffer. This
+        * saves a copy later */
+       req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE;
+       req->out.buffer = talloc(req->mem_ctx, req->out.size);
+       if (req->out.buffer == NULL) {
+               goto failed;
+       }
+
+       /* tell the backend where to put the data */
+       io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE;
+
+       /* call the backend */
+       status = req->conn->ntvfs_ops->read(req, &io);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto failed;
+       }
+
+       req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE;
+
+       req_send_reply(req);
+
+failed:
+       /* any failure in readbraw is equivalent to reading zero bytes */
+       req->out.size = 4;
+       req->out.buffer = talloc(req->mem_ctx, req->out.size);
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (async reply)
+****************************************************************************/
+static void reply_lockread_send(struct request_context *req)
+{
+       union smb_read *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* trim packet */
+       req_grow_data(req, 3 + io->lockread.out.nread);
+
+       /* construct reply */
+       SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread);
+       REQ_VWV_RESERVED(1, 4);
+
+       SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+       SSVAL(req->out.data, 1, io->lockread.out.nread);
+
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (core+ protocol).
+ note that the lock is a write lock, not a read lock!
+****************************************************************************/
+void reply_lockread(struct request_context *req)
+{
+       union smb_read *io;
+       
+       /* parse request */
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->lockread.level = RAW_READ_LOCKREAD;
+       io->lockread.in.fnum      = req_fnum(req, req->in.vwv, VWV(0));
+       io->lockread.in.count     = SVAL(req->in.vwv, VWV(1));
+       io->lockread.in.offset    = IVAL(req->in.vwv, VWV(2));
+       io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4));
+       
+       /* setup the reply packet assuming the maximum possible read */
+       req_setup_reply(req, 5, 3 + io->lockread.in.count);
+
+       /* tell the backend where to put the data */
+       io->lockread.out.data = req->out.data + 3;
+
+       req->async.send_fn = reply_lockread_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->read(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+
+/****************************************************************************
+ Reply to a read (async reply)
+****************************************************************************/
+static void reply_read_send(struct request_context *req)
+{
+       union smb_read *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* trim packet */
+       req_grow_data(req, 3 + io->read.out.nread);
+
+       /* construct reply */
+       SSVAL(req->out.vwv, VWV(0), io->read.out.nread);
+       REQ_VWV_RESERVED(1, 4);
+
+       SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+       SSVAL(req->out.data, 1, io->read.out.nread);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read.
+****************************************************************************/
+void reply_read(struct request_context *req)
+{
+       union smb_read *io;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(io, sizeof(*io));
+       
+       io->read.level = RAW_READ_READ;
+       io->read.in.fnum          = req_fnum(req, req->in.vwv, VWV(0));
+       io->read.in.count         = SVAL(req->in.vwv, VWV(1));
+       io->read.in.offset        = IVAL(req->in.vwv, VWV(2));
+       io->read.in.remaining     = SVAL(req->in.vwv, VWV(4));
+       
+       /* setup the reply packet assuming the maximum possible read */
+       req_setup_reply(req, 5, 3 + io->read.in.count);
+
+       /* tell the backend where to put the data */
+       io->lockread.out.data = req->out.data + 3;
+
+       req->async.send_fn = reply_read_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->read(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+
+/****************************************************************************
+ Reply to a read and X (async reply)
+****************************************************************************/
+static void reply_read_and_X_send(struct request_context *req)
+{
+       union smb_read *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* trim the packet to the right size */
+       req_grow_data(req, 1 + io->readx.out.nread);
+
+       /* construct reply */
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining);
+       SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode);
+       REQ_VWV_RESERVED(4, 1);
+       SSVAL(req->out.vwv, VWV(5), io->readx.out.nread);
+       SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr));
+       SCVAL(req->out.data, 0, 0); /* padding */
+       REQ_VWV_RESERVED(7, 5);
+
+       chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read and X.
+****************************************************************************/
+void reply_read_and_X(struct request_context *req)
+{
+       union smb_read *io;
+
+       /* parse request */
+       if (req->in.wct != 12) {
+               REQ_CHECK_WCT(req, 10);
+       }
+
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->readx.level = RAW_READ_READX;
+       io->readx.in.fnum          = req_fnum(req, req->in.vwv, VWV(2));
+       io->readx.in.offset        = IVAL(req->in.vwv, VWV(3));
+       io->readx.in.maxcnt        = SVAL(req->in.vwv, VWV(5));
+       io->readx.in.mincnt        = SVAL(req->in.vwv, VWV(6));
+       io->readx.in.remaining     = SVAL(req->in.vwv, VWV(9));
+       
+       /* the 64 bit varient */
+       if (req->in.wct == 12) {
+               uint32 offset_high = IVAL(req->in.vwv, VWV(10));
+#ifdef LARGE_SMB_OFF_T
+               io->readx.in.offset |= (((SMB_OFF_T)offset_high) << 32);
+#else
+               if (offset_high != 0) {
+                       req_reply_error(req, NT_STATUS_FOOBAR);
+                       return;
+               }
+#endif
+       }
+
+       /* setup the reply packet assuming the maximum possible read */
+       req_setup_reply(req, 12, 1 + io->readx.in.maxcnt);
+
+       /* tell the backend where to put the data. Notice the pad byte. */
+       io->readx.out.data = req->out.data + 1;
+
+       req->async.send_fn = reply_read_and_X_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->read(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a writebraw (core+ or LANMAN1.0 protocol).
+****************************************************************************/
+void reply_writebraw(struct request_context *req)
+{
+       /* this one is damn complex - put it off for now */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to a writeunlock (async reply)
+****************************************************************************/
+static void reply_writeunlock_send(struct request_context *req)
+{
+       union smb_write *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeunlock (core+).
+****************************************************************************/
+void reply_writeunlock(struct request_context *req)
+{
+       union smb_write *io;
+
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->writeunlock.level = RAW_WRITE_WRITEUNLOCK;
+       io->writeunlock.in.fnum        = req_fnum(req, req->in.vwv, VWV(0));
+       io->writeunlock.in.count       = SVAL(req->in.vwv, VWV(1));
+       io->writeunlock.in.offset      = IVAL(req->in.vwv, VWV(2));
+       io->writeunlock.in.remaining   = SVAL(req->in.vwv, VWV(4));
+       io->writeunlock.in.data        = req->in.data + 3;
+
+       /* make sure they gave us the data they promised */
+       if (io->writeunlock.in.count+3 > req->in.data_size) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       /* make sure the data block is big enough */
+       if (SVAL(req->in.data, 1) < io->writeunlock.in.count) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_writeunlock_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->write(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+
+/****************************************************************************
+ Reply to a write (async reply)
+****************************************************************************/
+static void reply_write_send(struct request_context *req)
+{
+       union smb_write *io = req->async.private;
+       
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write
+****************************************************************************/
+void reply_write(struct request_context *req)
+{
+       union smb_write *io;
+
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->write.level = RAW_WRITE_WRITE;
+       io->write.in.fnum        = req_fnum(req, req->in.vwv, VWV(0));
+       io->write.in.count       = SVAL(req->in.vwv, VWV(1));
+       io->write.in.offset      = IVAL(req->in.vwv, VWV(2));
+       io->write.in.remaining   = SVAL(req->in.vwv, VWV(4));
+       io->write.in.data        = req->in.data + 3;
+
+       /* make sure they gave us the data they promised */
+       if (req_data_oob(req, io->write.in.data, io->write.in.count)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       /* make sure the data block is big enough */
+       if (SVAL(req->in.data, 1) < io->write.in.count) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_write_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->write(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a write and X (async reply)
+****************************************************************************/
+static void reply_write_and_X_send(struct request_context *req)
+{
+       union smb_write *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 6, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF);
+       SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining);
+       SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16);
+       REQ_VWV_RESERVED(5, 1);
+
+       chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+****************************************************************************/
+void reply_write_and_X(struct request_context *req)
+{
+       union smb_write *io;
+       
+       if (req->in.wct != 14) {
+               REQ_CHECK_WCT(req, 12);
+       }
+
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->writex.level = RAW_WRITE_WRITEX;
+       io->writex.in.fnum      = req_fnum(req, req->in.vwv, VWV(2));
+       io->writex.in.offset    = IVAL(req->in.vwv, VWV(3));
+       io->writex.in.wmode     = SVAL(req->in.vwv, VWV(7));
+       io->writex.in.remaining = SVAL(req->in.vwv, VWV(8));
+       io->writex.in.count     = SVAL(req->in.vwv, VWV(10));
+       io->writex.in.data      = req->in.hdr + SVAL(req->in.vwv, VWV(11));
+
+       if (req->in.wct == 14) {
+               uint32 offset_high = IVAL(req->in.vwv, VWV(12));
+               uint16 count_high = SVAL(req->in.vwv, VWV(9));
+#ifdef LARGE_SMB_OFF_T
+               io->writex.in.offset |= (((SMB_OFF_T)offset_high) << 32);
+#else
+               if (offset_high != 0) {
+                       req_reply_error(req, NT_STATUS_FOOBAR);
+                       return;
+               }
+#endif
+               io->writex.in.count |= ((uint32)count_high) << 16;
+       }
+
+       /* make sure the data is in bounds */
+       if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       } 
+
+       req->async.send_fn = reply_write_and_X_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->write(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a lseek (async reply)
+****************************************************************************/
+static void reply_lseek_send(struct request_context *req)
+{
+       struct smb_seek *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 2, 0);
+
+       SIVALS(req->out.vwv, VWV(0), io->out.offset);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a lseek.
+****************************************************************************/
+void reply_lseek(struct request_context *req)
+{
+       struct smb_seek *io;
+
+       REQ_CHECK_WCT(req, 4);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->in.fnum   = req_fnum(req, req->in.vwv,  VWV(0));
+       io->in.mode   = SVAL(req->in.vwv,  VWV(1));
+       io->in.offset = IVALS(req->in.vwv, VWV(2));
+
+       req->async.send_fn = reply_lseek_send;
+       req->async.private = io;
+       
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->seek(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a flush.
+****************************************************************************/
+void reply_flush(struct request_context *req)
+{
+       struct smb_flush *io;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 1);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->in.fnum   = req_fnum(req, req->in.vwv,  VWV(0));
+       
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->flush(req, io);
+       
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a exit.
+****************************************************************************/
+void reply_exit(struct request_context *req)
+{
+       REQ_CHECK_WCT(req, 0);
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->exit(req);
+       
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a close 
+
+ Note that this has to deal with closing a directory opened by NT SMB's.
+****************************************************************************/
+void reply_close(struct request_context *req)
+{
+       union smb_close *io;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->close.level = RAW_CLOSE_CLOSE;
+       io->close.in.fnum  = req_fnum(req, req->in.vwv,  VWV(0));
+       io->close.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->close(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+
+/****************************************************************************
+ Reply to a writeclose (async reply)
+****************************************************************************/
+static void reply_writeclose_send(struct request_context *req)
+{
+       union smb_write *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeclose (Core+ protocol).
+****************************************************************************/
+void reply_writeclose(struct request_context *req)
+{
+       union smb_write *io;
+
+       /* this one is pretty weird - the wct can be 6 or 12 */
+       if (req->in.wct != 12) {
+               REQ_CHECK_WCT(req, 6);
+       }
+
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->writeclose.level = RAW_WRITE_WRITECLOSE;
+       io->writeclose.in.fnum   = req_fnum(req, req->in.vwv, VWV(0));
+       io->writeclose.in.count  = SVAL(req->in.vwv, VWV(1));
+       io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2));
+       io->writeclose.in.mtime  = make_unix_date3(req->in.vwv + VWV(4));
+       io->writeclose.in.data   = req->in.data + 1;
+
+       /* make sure they gave us the data they promised */
+       if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_writeclose_send;
+       req->async.private = io;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->write(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a lock.
+****************************************************************************/
+void reply_lock(struct request_context *req)
+{
+       union smb_lock *lck;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(lck, sizeof(*lck));
+
+       lck->lock.level     = RAW_LOCK_LOCK;
+       lck->lock.in.fnum   = req_fnum(req, req->in.vwv, VWV(0));
+       lck->lock.in.count  = IVAL(req->in.vwv, VWV(1));
+       lck->lock.in.offset = IVAL(req->in.vwv, VWV(3));
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->lock(req, lck);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a unlock.
+****************************************************************************/
+void reply_unlock(struct request_context *req)
+{
+       union smb_lock *lck;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 5);
+       REQ_TALLOC(lck, sizeof(*lck));
+
+       lck->unlock.level = RAW_LOCK_UNLOCK;
+       lck->unlock.in.fnum   = req_fnum(req, req->in.vwv, VWV(0));
+       lck->unlock.in.count  = IVAL(req->in.vwv, VWV(1));
+       lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3));
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->lock(req, lck);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a tdis.
+****************************************************************************/
+void reply_tdis(struct request_context *req)
+{
+       REQ_CHECK_WCT(req, 0);
+
+       close_cnum(req->conn);
+
+       /* construct reply */
+       req_setup_reply(req, 0, 0);
+
+       req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a echo. This is one of the few calls that is handled directly (the
+ backends don't see it at all)
+****************************************************************************/
+void reply_echo(struct request_context *req)
+{
+       uint16 count;
+       int i;
+
+       REQ_CHECK_WCT(req, 0);
+
+       count = SVAL(req->in.vwv, VWV(0));
+
+       req_setup_reply(req, 1, req->in.data_size);
+
+       memcpy(req->out.data, req->in.data, req->in.data_size);
+
+       /* we need to make sure the request isn't destroyed till the
+        * last packet */
+       req->control_flags |= REQ_CONTROL_PROTECTED;
+
+       for (i=1; i <= count;i++) {
+               if (i == count) {
+                       req->control_flags &= ~REQ_CONTROL_PROTECTED;
+               }
+
+               SSVAL(req->out.vwv, VWV(0), i);
+               req_send_reply(req);
+       }
+}
+
+
+
+/****************************************************************************
+ Reply to a printopen (async reply)
+****************************************************************************/
+static void reply_printopen_send(struct request_context *req)
+{
+       union smb_open *oi = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printopen.
+****************************************************************************/
+void reply_printopen(struct request_context *req)
+{
+       union smb_open *oi;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 2);
+       REQ_TALLOC(oi, sizeof(*oi));
+
+       oi->splopen.level = RAW_OPEN_SPLOPEN;
+       oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0));
+       oi->splopen.in.mode         = SVAL(req->in.vwv, VWV(1));
+
+       req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE);
+
+       req->async.send_fn = reply_printopen_send;
+       req->async.private = oi;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, oi);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a printclose.
+****************************************************************************/
+void reply_printclose(struct request_context *req)
+{
+       union smb_close *io;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->splclose.level = RAW_CLOSE_SPLCLOSE;
+       io->splclose.in.fnum = req_fnum(req, req->in.vwv,  VWV(0));
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->close(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+void reply_printqueue_send(struct request_context *req)
+{
+       union smb_lpq *lpq = req->async.private;
+       int i, maxcount;
+       const uint_t el_size = 28;      
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 2, 0);
+
+       /* truncate the returned list to fit in the negotiated buffer size */
+       maxcount = (req_max_data(req) - 3) / el_size;
+       if (maxcount < lpq->retq.out.count) {
+               lpq->retq.out.count = maxcount;
+       }
+
+       /* setup enough space in the reply */
+       req_grow_data(req, 3 + el_size*lpq->retq.out.count);
+       
+       /* and fill it in */
+       SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count);
+       SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx);
+
+       SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+       SSVAL(req->out.data, 1, el_size*lpq->retq.out.count);
+
+       req->out.ptr = req->out.data + 3;
+
+       for (i=0;i<lpq->retq.out.count;i++) {
+               put_dos_date2(req->out.ptr, 0 , lpq->retq.out.queue[i].time);
+               SCVAL(req->out.ptr,  4, lpq->retq.out.queue[i].status);
+               SSVAL(req->out.ptr,  5, lpq->retq.out.queue[i].job);
+               SIVAL(req->out.ptr,  7, lpq->retq.out.queue[i].size);
+               SCVAL(req->out.ptr, 11, 0); /* reserved */
+               req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII);
+               req->out.ptr += el_size;
+       }
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+void reply_printqueue(struct request_context *req)
+{
+       union smb_lpq *lpq;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 2);
+       REQ_TALLOC(lpq, sizeof(*lpq));
+
+       lpq->retq.level = RAW_LPQ_RETQ;
+       lpq->retq.in.maxcount = SVAL(req->in.vwv,  VWV(0));
+       lpq->retq.in.startidx = SVAL(req->in.vwv,  VWV(1));
+
+       req->async.send_fn = reply_printqueue_send;
+       req->async.private = lpq;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->lpq(req, lpq);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a printwrite.
+****************************************************************************/
+void reply_printwrite(struct request_context *req)
+{
+       union smb_write *io;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 1);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->splwrite.level = RAW_WRITE_SPLWRITE;
+
+       if (req->in.data_size < 3) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       io->splwrite.in.fnum  = req_fnum(req, req->in.vwv, VWV(0));
+       io->splwrite.in.count = SVAL(req->in.data, 1);
+       io->splwrite.in.data  = req->in.data + 3;
+
+       /* make sure they gave us the data they promised */
+       if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->write(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+void reply_mkdir(struct request_context *req)
+{
+       union smb_mkdir *io;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 0);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->generic.level = RAW_MKDIR_MKDIR;
+       req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE);
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->mkdir(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+void reply_rmdir(struct request_context *req)
+{
+       struct smb_rmdir *io;
+       /* parse the request */
+       REQ_CHECK_WCT(req, 0);
+       REQ_TALLOC(io, sizeof(*io));
+
+       req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE);
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->rmdir(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
+void reply_mv(struct request_context *req)
+{
+       struct smb_rename *io;
+       char *p;
+       /* parse the request */
+       REQ_CHECK_WCT(req, 1);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->in.attrib = SVAL(req->in.vwv, VWV(0));
+
+       p = req->in.data;
+       p += req_pull_ascii4(req, &io->in.pattern1, p, STR_TERMINATE);
+       p += req_pull_ascii4(req, &io->in.pattern2, p, STR_TERMINATE);
+
+       if (!io->in.pattern1 || !io->in.pattern2) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->rename(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a file copy (async reply)
+****************************************************************************/
+static void reply_copy_send(struct request_context *req)
+{
+       struct smb_copy *cp = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* build the reply */
+       req_setup_reply(req, 1, 0);
+
+       SSVAL(req->out.vwv, VWV(0), cp->out.count);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a file copy.
+****************************************************************************/
+void reply_copy(struct request_context *req)
+{
+       struct smb_copy *cp;
+       char *p;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 3);
+       REQ_TALLOC(cp, sizeof(*cp));
+
+       cp->in.tid2  = SVAL(req->in.vwv, VWV(0));
+       cp->in.ofun  = SVAL(req->in.vwv, VWV(1));
+       cp->in.flags = SVAL(req->in.vwv, VWV(2));
+
+       p = req->in.data;
+       p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE);
+       p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE);
+
+       if (!cp->in.path1 || !cp->in.path2) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_copy_send;
+       req->async.private = cp;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->copy(req, cp);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a lockingX request (async send)
+****************************************************************************/
+static void reply_lockingX_send(struct request_context *req)
+{
+       union smb_lock *lck = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* if it was an oplock break ack then we only send a reply if
+          there was an error */
+       if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) {
+               req_destroy(req);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 2, 0);
+       
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockingX request.
+****************************************************************************/
+void reply_lockingX(struct request_context *req)
+{
+       union smb_lock *lck;
+       uint_t total_locks, i;
+       uint_t lck_size;
+       char *p;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 8);
+       REQ_TALLOC(lck, sizeof(*lck));
+
+       lck->lockx.level = RAW_LOCK_LOCKX;
+       lck->lockx.in.fnum      = req_fnum(req, req->in.vwv, VWV(2));
+       lck->lockx.in.mode      = SVAL(req->in.vwv, VWV(3));
+       lck->lockx.in.timeout   = IVAL(req->in.vwv, VWV(4));
+       lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6));
+       lck->lockx.in.lock_cnt  = SVAL(req->in.vwv, VWV(7));
+
+       total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;
+
+       /* there are two varients, one with 64 bit offsets and counts */
+       if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+               lck_size = 20;
+       } else {
+               lck_size = 10;          
+       }
+
+       /* make sure we got the promised data */
+       if (req_data_oob(req, req->in.data, total_locks * lck_size)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       /* allocate the locks array */
+       if (total_locks) {
+               REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0]));
+       }
+
+       p = req->in.data;
+
+       /* construct the locks array */
+       for (i=0;i<total_locks;i++) {
+               uint32 ofs_high=0, count_high=0;
+
+               lck->lockx.in.locks[i].pid = SVAL(p, 0);
+
+               if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+                       ofs_high   = IVAL(p, 4);
+                       lck->lockx.in.locks[i].offset = IVAL(p, 8);
+                       count_high = IVAL(p, 12);
+                       lck->lockx.in.locks[i].count  = IVAL(p, 16);
+               } else {
+                       lck->lockx.in.locks[i].offset = IVAL(p, 2);
+                       lck->lockx.in.locks[i].count  = IVAL(p, 6);
+               }
+               if (ofs_high != 0 || count_high != 0) {
+#ifdef LARGE_SMB_OFF_T
+                       lck->lockx.in.locks[i].count  |= ((SMB_OFF_T)count_high) << 32;
+                       lck->lockx.in.locks[i].offset |= ((SMB_OFF_T)ofs_high) << 32;
+#else
+                       req_reply_error(req, NT_STATUS_FOOBAR);
+                       return;
+#endif
+               }
+               p += lck_size;
+       }
+
+       req->async.send_fn = reply_lockingX_send;
+       req->async.private = lck;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->lock(req, lck);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a SMBreadbmpx (read block multiplex) request.
+****************************************************************************/
+void reply_readbmpx(struct request_context *req)
+{
+       /* tell the client to not use a multiplexed read - its too broken to use */
+       req_reply_dos_error(req, ERRSRV, ERRuseSTD);
+}
+
+
+/****************************************************************************
+ Reply to a SMBsetattrE.
+****************************************************************************/
+void reply_setattrE(struct request_context *req)
+{
+       union smb_setfileinfo *info;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 7);
+       REQ_TALLOC(info, sizeof(*info));
+
+       info->setattre.level = RAW_SFILEINFO_SETATTRE;
+       info->setattre.file.fnum =      req_fnum(req, req->in.vwv,    VWV(0));
+       info->setattre.in.create_time = make_unix_date2(req->in.vwv + VWV(1));
+       info->setattre.in.access_time = make_unix_date2(req->in.vwv + VWV(3));
+       info->setattre.in.write_time  = make_unix_date2(req->in.vwv + VWV(5));
+
+       req->async.send_fn = reply_simple_send;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->setfileinfo(req, info);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+void reply_writebmpx(struct request_context *req)
+{
+       /* we will need to implement this one for OS/2, but right now I can't be bothered */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebs (write block multiplex secondary) request.
+****************************************************************************/
+void reply_writebs(struct request_context *req)
+{
+       /* see reply_writebmpx */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+
+/****************************************************************************
+ Reply to a SMBgetattrE (async reply)
+****************************************************************************/
+static void reply_getattrE_send(struct request_context *req)
+{
+       union smb_fileinfo *info = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* setup reply */
+       req_setup_reply(req, 11, 0);
+
+       put_dos_date2(req->out.vwv, VWV(0), info->getattre.out.create_time);
+       put_dos_date2(req->out.vwv, VWV(2), info->getattre.out.access_time);
+       put_dos_date2(req->out.vwv, VWV(4), info->getattre.out.write_time);
+       SIVAL(req->out.vwv,         VWV(6), info->getattre.out.size);
+       SIVAL(req->out.vwv,         VWV(8), info->getattre.out.alloc_size);
+       SSVAL(req->out.vwv,        VWV(10), info->getattre.out.attrib);
+
+       req_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a SMBgetattrE.
+****************************************************************************/
+void reply_getattrE(struct request_context *req)
+{
+       union smb_fileinfo *info;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 1);
+       REQ_TALLOC(info, sizeof(*info));
+
+       info->getattr.level = RAW_FILEINFO_GETATTRE;
+       info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
+
+       req->async.send_fn = reply_getattrE_send;
+       req->async.private = info;
+
+       /* call backend */
+       req->async.status = req->conn->ntvfs_ops->qfileinfo(req, info);
+
+       REQ_ASYNC_TAIL;
+}
+
+/****************************************************************************
+ Reply to a search.
+ Can be called from SMBsearch, SMBffirst or SMBfunique.
+****************************************************************************/
+void reply_search(struct request_context *req)
+{
+       /* do this one later */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to a fclose (stop directory search).
+****************************************************************************/
+void reply_fclose(struct request_context *req)
+{
+       /* skip this one for now too */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+reply to an old style session setup command
+****************************************************************************/
+static void reply_sesssetup_old(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_sesssetup sess;
+       char *p;
+       uint16 passlen;
+
+       sess.old.level = RAW_SESSSETUP_OLD;
+
+       /* parse request */
+       sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2));
+       sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+       sess.old.in.vc_num  = SVAL(req->in.vwv, VWV(4));
+       sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5));
+       passlen             = SVAL(req->in.vwv, VWV(7));
+
+       /* check the request isn't malformed */
+       if (req_data_oob(req, req->in.data, passlen)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       
+       p = req->in.data;
+       if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       p += passlen;
+       
+       p += req_pull_string(req, &sess.old.in.user,   p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.old.in.os,     p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE);
+
+       /* call the generic handler */
+       status = sesssetup_backend(req, &sess);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 3, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), sess.old.out.action);
+
+       SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid);
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+reply to an NT1 style session setup command
+****************************************************************************/
+static void reply_sesssetup_nt1(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_sesssetup sess;
+       char *p;
+       uint16 passlen1, passlen2;
+
+       sess.nt1.level = RAW_SESSSETUP_NT1;
+
+       /* parse request */
+       sess.nt1.in.bufsize      = SVAL(req->in.vwv, VWV(2));
+       sess.nt1.in.mpx_max      = SVAL(req->in.vwv, VWV(3));
+       sess.nt1.in.vc_num       = SVAL(req->in.vwv, VWV(4));
+       sess.nt1.in.sesskey      = IVAL(req->in.vwv, VWV(5));
+       passlen1                 = SVAL(req->in.vwv, VWV(7));
+       passlen2                 = SVAL(req->in.vwv, VWV(8));
+       sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11));
+
+       /* check the request isn't malformed */
+       if (req_data_oob(req, req->in.data, passlen1) ||
+           req_data_oob(req, req->in.data + passlen1, passlen2)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       
+       p = req->in.data;
+       if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       p += passlen1;
+       if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       p += passlen2;
+       
+       p += req_pull_string(req, &sess.nt1.in.user,   p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.nt1.in.os,     p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE);
+
+       /* call the generic handler */
+       status = sesssetup_backend(req, &sess);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 3, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action);
+
+       SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid);
+
+       req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE);
+       req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE);
+       req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE);
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+reply to an SPNEGO style session setup command
+****************************************************************************/
+static void reply_sesssetup_spnego(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_sesssetup sess;
+       char *p;
+       uint16 blob_len;
+
+       sess.spnego.level = RAW_SESSSETUP_SPNEGO;
+
+       /* parse request */
+       sess.spnego.in.bufsize      = SVAL(req->in.vwv, VWV(2));
+       sess.spnego.in.mpx_max      = SVAL(req->in.vwv, VWV(3));
+       sess.spnego.in.vc_num       = SVAL(req->in.vwv, VWV(4));
+       sess.spnego.in.sesskey      = IVAL(req->in.vwv, VWV(5));
+       blob_len                    = SVAL(req->in.vwv, VWV(7));
+       sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10));
+
+       p = req->in.data;
+       if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+       p += blob_len;
+       
+       p += req_pull_string(req, &sess.spnego.in.os,     p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE);
+       p += req_pull_string(req, &sess.spnego.in.domain, p, -1, STR_TERMINATE);
+
+       /* call the generic handler */
+       status = sesssetup_backend(req, &sess);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 4, sess.spnego.out.secblob.length);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0);
+       SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action);
+       SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length);
+
+       SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid);
+
+       memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length);
+       req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE);
+       req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE);
+
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+void reply_sesssetup(struct request_context *req)
+{
+       switch (req->in.wct) {
+       case 10:
+               /* a pre-NT1 call */
+               reply_sesssetup_old(req);
+               return;
+       case 13:
+               /* a NT1 call */
+               reply_sesssetup_nt1(req);
+               return;
+       case 12:
+               /* a SPNEGO call */
+               reply_sesssetup_spnego(req);
+               return;
+       }
+
+       /* unsupported varient */
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to a SMBulogoffX.
+****************************************************************************/
+void reply_ulogoffX(struct request_context *req)
+{
+       uint16 vuid;
+
+       vuid = SVAL(req->in.hdr, HDR_UID);
+       
+       /* in user level security we are supposed to close any files
+          open by this user */
+       if ((vuid != 0) && (lp_security() != SEC_SHARE)) {
+               DEBUG(0,("REWRITE: not closing user files\n"));
+       }
+
+       invalidate_vuid(req->smb, vuid);
+
+       req_setup_reply(req, 2, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0); 
+       
+       chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an SMBtrans request
+****************************************************************************/
+void reply_trans(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to an SMBtranss2 request
+****************************************************************************/
+void reply_transs2(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBnttrans request
+****************************************************************************/
+void reply_nttrans(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBnttranss request
+****************************************************************************/
+void reply_nttranss(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to an SMBfindclose request
+****************************************************************************/
+void reply_findclose(struct request_context *req)
+{
+       NTSTATUS status;
+       union smb_search_close io;
+
+       io.findclose.level = RAW_FINDCLOSE_CLOSE;
+
+       /* parse request */
+       REQ_CHECK_WCT(req, 1);
+
+       io.findclose.in.handle  = SVAL(req->in.vwv, VWV(0));
+       
+       /* call backend */
+       status = req->conn->ntvfs_ops->search_close(req, &io);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       /* construct reply */
+       req_setup_reply(req, 0, 0);
+
+       req_send_reply(req);    
+}
+
+/****************************************************************************
+ Reply to an SMBfindnclose request
+****************************************************************************/
+void reply_findnclose(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcreateX request (async send)
+****************************************************************************/
+static void reply_ntcreate_and_X_send(struct request_context *req)
+{
+       union smb_open *io = req->async.private;
+
+       CHECK_ASYNC_STATUS;
+
+       /* construct reply */
+       req_setup_reply(req, 34, 0);
+
+       SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+       SSVAL(req->out.vwv, VWV(1), 0); 
+       SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level);
+
+       /* the rest of the parameters are not aligned! */
+       SSVAL(req->out.vwv,        5, io->ntcreatex.out.fnum);
+       SIVAL(req->out.vwv,        7, io->ntcreatex.out.create_action);
+       push_nttime(req->out.vwv, 11, &io->ntcreatex.out.create_time);
+       push_nttime(req->out.vwv, 19, &io->ntcreatex.out.access_time);
+       push_nttime(req->out.vwv, 27, &io->ntcreatex.out.write_time);
+       push_nttime(req->out.vwv, 35, &io->ntcreatex.out.change_time);
+       SIVAL(req->out.vwv,       43, io->ntcreatex.out.attrib);
+       SBVAL(req->out.vwv,       47, io->ntcreatex.out.alloc_size);
+       SBVAL(req->out.vwv,       55, io->ntcreatex.out.size);
+       SSVAL(req->out.vwv,       63, io->ntcreatex.out.file_type);
+       SSVAL(req->out.vwv,       65, io->ntcreatex.out.ipc_state);
+       SCVAL(req->out.vwv,       67, io->ntcreatex.out.is_directory);
+
+       chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to an SMBntcreateX request
+****************************************************************************/
+void reply_ntcreate_and_X(struct request_context *req)
+{
+       union smb_open *io;
+       uint16 fname_len;
+
+       /* parse the request */
+       REQ_CHECK_WCT(req, 24);
+       REQ_TALLOC(io, sizeof(*io));
+
+       io->ntcreatex.level = RAW_OPEN_NTCREATEX;
+
+       /* notice that the word parameters are not word aligned, so we don't use VWV() */
+       fname_len =                         SVAL(req->in.vwv, 5);
+       io->ntcreatex.in.flags =            IVAL(req->in.vwv, 7);
+       io->ntcreatex.in.root_fid =         IVAL(req->in.vwv, 11);
+       io->ntcreatex.in.access_mask =      IVAL(req->in.vwv, 15);
+       io->ntcreatex.in.alloc_size =       BVAL(req->in.vwv, 19);
+       io->ntcreatex.in.file_attr =        IVAL(req->in.vwv, 27);
+       io->ntcreatex.in.share_access =     IVAL(req->in.vwv, 31);
+       io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35);
+       io->ntcreatex.in.create_options =   IVAL(req->in.vwv, 39);
+       io->ntcreatex.in.impersonation =    IVAL(req->in.vwv, 43);
+       io->ntcreatex.in.security_flags =   CVAL(req->in.vwv, 47);
+
+       /* we need a neater way to handle this alignment */
+       if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && 
+           ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) {
+               fname_len++;
+       }
+
+       req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE);
+       if (!io->ntcreatex.in.fname) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       req->async.send_fn = reply_ntcreate_and_X_send;
+       req->async.private = io;
+
+       /* call the backend */
+       req->async.status = req->conn->ntvfs_ops->open(req, io);
+
+       REQ_ASYNC_TAIL;
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcancel request
+****************************************************************************/
+void reply_ntcancel(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBsends request
+****************************************************************************/
+void reply_sends(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBsendstrt request
+****************************************************************************/
+void reply_sendstrt(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBsendend request
+****************************************************************************/
+void reply_sendend(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to an SMBsendtxt request
+****************************************************************************/
+void reply_sendtxt(struct request_context *req)
+{
+       req_reply_error(req, NT_STATUS_FOOBAR);
+}
+
+
+
+/****************************************************************************
+ Reply to a special message - a SMB packet with non zero NBT message type
+****************************************************************************/
+void reply_special(struct request_context *req)
+{
+       uint8 msg_type;
+       char buf[4];
+       
+       msg_type = CVAL(req->in.buffer,0);
+
+       SIVAL(buf, 0, 0);
+       
+       switch (msg_type) {
+       case 0x81: /* session request */
+               if (req->smb->negotiate.done_nbt_session) {
+                       exit_server(req->smb, "multiple session request not permitted");
+               }
+               
+               SCVAL(buf,0,0x82);
+               SCVAL(buf,3,0);
+               
+               DEBUG(0,("REWRITE: not parsing netbios names in NBT session request!\n"));
+               
+               req->smb->negotiate.done_nbt_session = True;
+               
+               req->out.buffer = buf;
+               req->out.size = 4;
+               req_send_reply(req);
+               return;
+               
+       case 0x89: /* session keepalive request 
+                     (some old clients produce this?) */
+               SCVAL(buf, 0, SMBkeepalive);
+               SCVAL(buf, 3, 0);
+               req->out.buffer = buf;
+               req->out.size = 4;
+               req_send_reply(req);
+               return;
+               
+       case SMBkeepalive: 
+               /* session keepalive - swallow it */
+               req_destroy(req);
+               return;
+       }
+
+       DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type));
+       req_destroy(req);
+}
diff --git a/source4/smbd/request.c b/source4/smbd/request.c
new file mode 100644 (file)
index 0000000..564bb07
--- /dev/null
@@ -0,0 +1,571 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Andrew Tridgell              2003
+   
+   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.
+*/
+
+/*
+  this file implements functions for manipulating the 'struct request_context' structure in smbd
+*/
+
+#include "includes.h"
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 256
+
+/* destroy a request structure */
+void req_destroy(struct request_context *req)
+{
+       /* the request might be marked protected. This is done by the
+        * SMBecho code for example */
+       if (req->control_flags & REQ_CONTROL_PROTECTED) {
+               return;
+       }
+
+       /* ahh, its so nice to destroy a complex structure in such a
+        * simple way! */
+       talloc_destroy(req->mem_ctx);
+}
+
+/****************************************************************************
+construct a basic request packet, mostly used to construct async packets
+such as change notify and oplock break requests
+****************************************************************************/
+struct request_context *init_smb_request(struct server_context *smb)
+{
+       struct request_context *req;
+       TALLOC_CTX *mem_ctx;
+
+       /* each request gets its own talloc context. The request
+          structure itself is also allocated inside this context, so
+          we need to allocate it before we construct the request
+       */
+       mem_ctx = talloc_init("request_context[%d]", smb->socket.pkt_count);
+       if (!mem_ctx) {
+               return NULL;
+       }
+
+       smb->socket.pkt_count++;
+
+       req = talloc(mem_ctx, sizeof(*req));
+       ZERO_STRUCTP(req);
+
+       /* setup the request context */
+       req->smb = smb;
+       req->mem_ctx = mem_ctx;
+       
+       return req;
+}
+
+
+/*
+  setup a chained reply in req->out with the given word count and initial data buffer size. 
+*/
+static void req_setup_chain_reply(struct request_context *req, unsigned wct, unsigned buflen)
+{
+       uint32 chain_base_size = req->out.size;
+
+       /* we need room for the wct value, the words, the buffer length and the buffer */
+       req->out.size += 1 + VWV(wct) + 2 + buflen;
+
+       /* over allocate by a small amount */
+       req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 
+
+       req->out.buffer = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated);
+       if (!req->out.buffer) {
+               exit_server(req->smb, "allocation failed");
+       }
+
+       req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+       req->out.vwv = req->out.buffer + chain_base_size + 1;
+       req->out.wct = wct;
+       req->out.data = req->out.vwv + VWV(wct) + 2;
+       req->out.data_size = buflen;
+       req->out.ptr = req->out.data;
+
+       SCVAL(req->out.buffer, chain_base_size, wct);
+       SSVAL(req->out.vwv, VWV(wct), buflen);
+}
+
+
+/*
+  setup a reply in req->out with the given word count and initial data buffer size. 
+  the caller will then fill in the command words and data before calling req_send_reply() to 
+  send the reply on its way
+*/
+void req_setup_reply(struct request_context *req, unsigned wct, unsigned buflen)
+{
+       if (req->chain_count != 0) {
+               req_setup_chain_reply(req, wct, buflen);
+               return;
+       }
+
+       req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen;
+
+       /* over allocate by a small amount */
+       req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 
+
+       req->out.buffer = talloc(req->mem_ctx, req->out.allocated);
+       if (!req->out.buffer) {
+               exit_server(req->smb, "allocation failed");
+       }
+
+       req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+       req->out.vwv = req->out.hdr + HDR_VWV;
+       req->out.wct = wct;
+       req->out.data = req->out.vwv + VWV(wct) + 2;
+       req->out.data_size = buflen;
+       req->out.ptr = req->out.data;
+
+       SIVAL(req->out.hdr, HDR_RCLS, 0);
+
+       SCVAL(req->out.hdr, HDR_WCT, wct);
+       SSVAL(req->out.vwv, VWV(wct), buflen);
+
+
+       memcpy(req->out.hdr, "\377SMB", 4);
+       SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES); 
+       SSVAL(req->out.hdr,HDR_FLG2, 
+             (req->flags2 & FLAGS2_UNICODE_STRINGS) |
+             FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_32_BIT_ERROR_CODES | FLAGS2_EXTENDED_SECURITY);
+
+       SSVAL(req->out.hdr,HDR_PIDHIGH,0);
+       memset(req->out.hdr + HDR_SS_FIELD, 0, 10);
+
+       if (req->in.hdr) {
+               /* copy the cmd, tid, pid, uid and mid from the request */
+               SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM));  
+               SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID));
+               SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID));
+               SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID));
+               SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID));
+       } else {
+               SSVAL(req->out.hdr,HDR_TID,0);
+               SSVAL(req->out.hdr,HDR_PID,0);
+               SSVAL(req->out.hdr,HDR_UID,0);
+               SSVAL(req->out.hdr,HDR_MID,0);
+       }
+}
+
+/*
+  work out the maximum data size we will allow for this reply, given
+  the negotiated max_xmit. The basic reply packet must be setup before
+  this call
+
+  note that this is deliberately a signed integer reply
+*/
+int req_max_data(struct request_context *req)
+{
+       int ret;
+       ret = req->smb->negotiate.max_send;
+       ret -= PTR_DIFF(req->out.data, req->out.hdr);
+       if (ret < 0) ret = 0;
+       return ret;
+}
+
+
+/*
+  grow the allocation of the data buffer portion of a reply
+  packet. Note that as this can reallocate the packet buffer this
+  invalidates any local pointers into the packet.
+
+  To cope with this req->out.ptr is supplied. This will be updated to
+  point at the same offset into the packet as before this call
+*/
+static void req_grow_allocation(struct request_context *req, unsigned new_size)
+{
+       int delta;
+       char *buf2;
+
+       delta = new_size - req->out.data_size;
+       if (delta + req->out.size <= req->out.allocated) {
+               /* it fits in the preallocation */
+               return;
+       }
+
+       /* we need to realloc */
+       req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+       buf2 = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated);
+       if (buf2 == NULL) {
+               smb_panic("out of memory in req_grow_allocation");
+       }
+
+       if (buf2 == req->out.buffer) {
+               /* the malloc library gave us the same pointer */
+               return;
+       }
+       
+       /* update the pointers into the packet */
+       req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+       req->out.ptr  = buf2 + PTR_DIFF(req->out.ptr,  req->out.buffer);
+       req->out.vwv  = buf2 + PTR_DIFF(req->out.vwv,  req->out.buffer);
+       req->out.hdr  = buf2 + PTR_DIFF(req->out.hdr,  req->out.buffer);
+
+       req->out.buffer = buf2;
+}
+
+
+/*
+  grow the data buffer portion of a reply packet. Note that as this
+  can reallocate the packet buffer this invalidates any local pointers
+  into the packet. 
+
+  To cope with this req->out.ptr is supplied. This will be updated to
+  point at the same offset into the packet as before this call
+*/
+void req_grow_data(struct request_context *req, unsigned new_size)
+{
+       int delta;
+
+       if (!(req->control_flags & REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
+               smb_panic("reply buffer too large!");
+       }
+
+       req_grow_allocation(req, new_size);
+
+       delta = new_size - req->out.data_size;
+
+       req->out.size += delta;
+       req->out.data_size += delta;
+
+       /* set the BCC to the new data size */
+       SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+/*
+  send a reply and destroy the request buffer
+
+  note that this only looks at req->out.buffer and req->out.size, allowing manually 
+  constructed packets to be sent
+*/
+void req_send_reply(struct request_context *req)
+{
+       if (req->out.size > NBT_HDR_SIZE) {
+               _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+       }
+
+       if (write_data(req->smb->socket.fd, req->out.buffer, req->out.size) != req->out.size) {
+               smb_panic("failed to send reply\n");
+       }
+
+       req_destroy(req);
+}
+
+
+
+/* 
+   construct and send an error packet with a forced DOS error code
+   this is needed to match win2000 behaviour for some parts of the protocol
+*/
+void req_reply_dos_error(struct request_context *req, uint8 eclass, uint16 ecode)
+{
+       /* if the basic packet hasn't been setup yet then do it now */
+       if (req->out.buffer == NULL) {
+               req_setup_reply(req, 0, 0);
+       }
+
+       SCVAL(req->out.hdr, HDR_RCLS, eclass);
+       SSVAL(req->out.hdr, HDR_ERR, ecode);
+
+       SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
+       
+       req_send_reply(req);
+}
+
+/* 
+   construct and send an error packet, then destroy the request 
+   auto-converts to DOS error format when appropriate
+*/
+void req_reply_error(struct request_context *req, NTSTATUS status)
+{
+       req_setup_reply(req, 0, 0);
+
+       /* error returns never have any data */
+       req_grow_data(req, 0);
+
+       if (!lp_nt_status_support() || !(req->smb->negotiate.client_caps & CAP_STATUS32)) {
+               /* convert to DOS error codes */
+               uint8 eclass;
+               uint32 ecode;
+               ntstatus_to_dos(status, &eclass, &ecode);
+               req_reply_dos_error(req, eclass, ecode);
+               return;
+       }
+
+       SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status));
+       SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES);
+       
+       req_send_reply(req);
+}
+
+
+/*
+  push a string into the data portion of the request packet, growing it if necessary
+  this gets quite tricky - please be very careful to cover all cases when modifying this
+
+  if dest is NULL, then put the string at the end of the data portion of the packet
+
+  if dest_len is -1 then no limit applies
+*/
+size_t req_push_str(struct request_context *req, char *dest, const char *str, int dest_len, unsigned flags)
+{
+       size_t len;
+       unsigned grow_size;
+       char *buf0;
+       const int max_bytes_per_char = 3;
+
+       if (!(flags & (STR_ASCII|STR_UNICODE))) {
+               flags |= (req->smb->negotiate.client_caps & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+       }
+
+       if (dest == NULL) {
+               dest = req->out.data + req->out.data_size;
+       }
+
+       if (dest_len != -1) {
+               len = dest_len;
+       } else {
+               len = (strlen(str)+2) * max_bytes_per_char;
+       }
+
+       grow_size = len + PTR_DIFF(dest, req->out.data);
+       buf0 = req->out.buffer;
+
+       req_grow_allocation(req, grow_size);
+
+       if (buf0 != req->out.buffer) {
+               dest = req->out.buffer + PTR_DIFF(dest, buf0);
+       }
+
+       len = push_string(req->out.hdr, dest, str, len, flags);
+
+       grow_size = len + PTR_DIFF(dest, req->out.data);
+
+       if (grow_size > req->out.data_size) {
+               req_grow_data(req, grow_size);
+       }
+
+       return len;
+}
+
+
+/*
+  pull a UCS2 string from a request packet, returning a talloced unix string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+static size_t req_pull_ucs2(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2, alignment=0;
+       ssize_t ret;
+
+       if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
+               src++;
+               alignment=1;
+               if (byte_len != -1) {
+                       byte_len--;
+               }
+       }
+
+       if (flags & STR_NO_RANGE_CHECK) {
+               src_len = byte_len;
+       } else {
+               src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+               if (src_len < 0) {
+                       *dest = NULL;
+                       return 0;
+               }
+
+               if (byte_len != -1 && src_len > byte_len) {
+                       src_len = byte_len;
+               }
+       }
+
+       src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
+
+       if (src_len2 <= src_len - 2) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2 += 2;
+       }
+
+       ret = convert_string_talloc(req->mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
+
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return src_len2 + alignment;
+}
+
+/*
+  pull a ascii string from a request packet, returning a talloced string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+static size_t req_pull_ascii(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags)
+{
+       int src_len, src_len2;
+       ssize_t ret;
+
+       if (flags & STR_NO_RANGE_CHECK) {
+               src_len = byte_len;
+       } else {
+               src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
+               if (src_len < 0) {
+                       *dest = NULL;
+                       return 0;
+               }
+               if (byte_len != -1 && src_len > byte_len) {
+                       src_len = byte_len;
+               }
+       }
+
+       src_len2 = strnlen(src, src_len);
+       if (src_len2 <= src_len - 1) {
+               /* include the termination if we didn't reach the end of the packet */
+               src_len2++;
+       }
+
+       ret = convert_string_talloc(req->mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
+
+       if (ret == -1) {
+               *dest = NULL;
+               return 0;
+       }
+
+       return src_len2;
+}
+
+/*
+  pull a string from a request packet, returning a talloced string
+
+  the string length is limited by the 3 things:
+   - the data size in the request (end of packet)
+   - the passed 'byte_len' if it is not -1
+   - the end of string (null termination)
+
+  Note that 'byte_len' is the number of bytes in the packet
+
+  on failure zero is returned and *dest is set to NULL, otherwise the number
+  of bytes consumed in the packet is returned
+*/
+size_t req_pull_string(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags)
+{
+       if (!(flags & STR_ASCII) && 
+           ((flags & STR_UNICODE || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
+               return req_pull_ucs2(req, dest, src, byte_len, flags);
+       }
+
+       return req_pull_ascii(req, dest, src, byte_len, flags);
+}
+
+
+/*
+  pull a ASCII4 string buffer from a request packet, returning a talloced string
+  
+  an ASCII4 buffer is a null terminated string that has a prefix
+  of the character 0x4. It tends to be used in older parts of the protocol.
+
+  on failure *dest is set to the zero length string. This seems to
+  match win2000 behaviour
+*/
+size_t req_pull_ascii4(struct request_context *req, const char **dest, const char *src, unsigned flags)
+{
+       ssize_t ret;
+
+       if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) {
+               /* win2000 treats this as the NULL string! */
+               (*dest) = talloc_strdup(req->mem_ctx, "");
+               return 0;
+       }
+
+       /* this consumes the 0x4 byte. We don't check whether the byte
+          is actually 0x4 or not. This matches win2000 server
+          behaviour */
+       src++;
+
+       ret = req_pull_string(req, dest, src, -1, flags);
+       if (ret == -1) {
+               (*dest) = talloc_strdup(req->mem_ctx, "");
+               return 1;
+       }
+       
+       return ret + 1;
+}
+
+/*
+  pull a DATA_BLOB from a request packet, returning a talloced blob
+
+  return False if any part is outside the data portion of the packet
+*/
+BOOL req_pull_blob(struct request_context *req, const char *src, int len, DATA_BLOB *blob)
+{
+       if (len != 0 && req_data_oob(req, src, len)) {
+               return False;
+       }
+
+       (*blob) = data_blob_talloc(req->mem_ctx, src, len);
+
+       return True;
+}
+
+/* check that a lump of data in a request is within the bounds of the data section of
+   the packet */
+BOOL req_data_oob(struct request_context *req, const char *ptr, uint32 count)
+{
+       if (count == 0) {
+               return False;
+       }
+       
+       /* be careful with wraparound! */
+       if (ptr < req->in.data ||
+           ptr >= req->in.data + req->in.data_size ||
+           count > req->in.data_size ||
+           ptr + count > req->in.data + req->in.data_size) {
+               return True;
+       }
+       return False;
+}
+
+
+/* 
+   pull an open file handle from a packet, taking account of the chained_fnum
+*/
+uint16 req_fnum(struct request_context *req, const char *base, unsigned offset)
+{
+       if (req->chained_fnum != -1) {
+               return req->chained_fnum;
+       }
+       return SVAL(base, offset);
+}
diff --git a/source4/smbd/rewrite.c b/source4/smbd/rewrite.c
new file mode 100644 (file)
index 0000000..c2f08e0
--- /dev/null
@@ -0,0 +1,82 @@
+#include "includes.h"
+
+/*
+
+ this is a set of temporary stub functions used during the core smbd rewrite.
+ This file will need to go away before the rewrite is complete
+*/
+
+void mangle_reset_cache(void) 
+{}
+
+void reset_stat_cache(void)
+{}
+
+
+BOOL set_current_service(void *conn, BOOL x)
+{ return True; }
+
+void change_to_root_user(void)
+{}
+
+void load_printers(void)
+{}
+
+void file_init(void)
+{}
+
+void init_rpc_pipe_hnd(void)
+{}
+
+BOOL init_oplocks(void)
+{ return True; }
+
+BOOL init_change_notify(void)
+{ return True; }
+
+
+BOOL pcap_printername_ok(const char *service, char *foo)
+{ return True; }
+
+void become_root(void)
+{}
+
+void unbecome_root(void)
+{}
+
+BOOL namecache_enable(void)
+{ return True; }
+
+BOOL locking_init(int read_only)
+{ return True; }
+
+BOOL share_info_db_init(void)
+{ return True; }
+
+BOOL init_registry(void)
+{ return True; }
+
+BOOL share_access_check(struct request_context *req, struct tcon_context *conn, int snum, uint32 desired_access)
+{ return True; }
+
+BOOL init_names(void)
+{ return True; }
+
+BOOL uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+       *sid = *get_global_sam_sid();
+       sid_append_rid(sid, uid*2);
+       return True;
+}
+
+BOOL gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+       *sid = *get_global_sam_sid();
+       sid_append_rid(sid, gid*2 + 1);
+       return True;
+}
+
+
+BOOL become_user_permanently(uid_t uid, gid_t gid)
+{ return True; }
+
diff --git a/source4/smbd/server.c b/source4/smbd/server.c
new file mode 100644 (file)
index 0000000..e33a13e
--- /dev/null
@@ -0,0 +1,340 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main SMB server routines
+   Copyright (C) Andrew Tridgell               1992-1998
+   Copyright (C) Martin Pool                   2002
+   Copyright (C) Jelmer Vernooij               2002
+   Copyright (C) James J Myers                         2003 <myersjj@samba.org>
+   
+   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"
+
+
+/*
+  called on a fatal error that should cause this server to terminate
+*/
+void exit_server(struct server_context *smb, const char *reason)
+{
+       smb->model_ops->terminate_connection(smb, reason);
+}
+
+
+/*
+  add a socket address to the list of events, one event per port
+*/
+static void add_socket(struct event_context *events, 
+                      struct model_ops *model_ops, 
+                      struct in_addr *ifip)
+{
+       char *ports = lp_smb_ports();
+       char *ptr, *tok;
+       const char *delim = ", ";
+
+       for (tok=strtok_r(ports, delim, &ptr); 
+            tok; 
+            tok=strtok_r(NULL, delim, &ptr)) {
+               unsigned port = atoi(tok);
+               struct fd_event fde;
+
+               if (port == 0) continue;
+
+               fde.fd = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True);
+               if (fde.fd == -1) {
+                       DEBUG(0,("Failed to open socket on %s:%u - %s\n",
+                                inet_ntoa(*ifip), port, strerror(errno)));
+                       continue;
+               }
+
+               /* ready to listen */
+               set_socket_options(fde.fd, "SO_KEEPALIVE"); 
+               set_socket_options(fde.fd, lp_socket_options());
+      
+               if (listen(fde.fd, 10) == -1) {
+                       DEBUG(0,("Failed to listen on %s:%d - %s\n",
+                                inet_ntoa(*ifip), port, strerror(errno)));
+                       close(fde.fd);
+                       continue;
+               }
+
+               /* we are only interested in read events on the listen socket */
+               fde.flags = EVENT_FD_READ;
+               fde.private = model_ops;
+               fde.handler = model_ops->accept_connection;
+
+               event_add_fd(events, &fde);
+       }
+}
+
+/****************************************************************************
+ Open the socket communication.
+****************************************************************************/
+static void open_sockets_smbd(struct event_context *events,
+                             struct model_ops *model_ops)
+{
+       if (lp_interfaces() && lp_bind_interfaces_only()) {
+               int num_interfaces = iface_count();
+               int i;
+
+               /* We have been given an interfaces line, and been 
+                  told to only bind to those interfaces. Create a
+                  socket per interface and bind to only these.
+               */
+               for(i = 0; i < num_interfaces; i++) {
+                       struct in_addr *ifip = iface_n_ip(i);
+
+                       if (ifip == NULL) {
+                               DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i));
+                               continue;
+                       }
+
+                       add_socket(events, model_ops, ifip);
+               }
+       } else {
+               TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd");
+               
+               struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address());
+               /* Just bind to lp_socket_address() (usually 0.0.0.0) */
+               if (!mem_ctx) {
+                       smb_panic("No memory");
+               }
+               add_socket(events, model_ops, ifip);
+               talloc_destroy(mem_ctx);
+       } 
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+BOOL reload_services(struct server_context *smb, BOOL test)
+{
+       BOOL ret;
+       
+       if (lp_loaded()) {
+               pstring fname;
+               pstrcpy(fname,lp_configfile());
+               if (file_exist(fname, NULL) &&
+                   !strcsequal(fname, dyn_CONFIGFILE)) {
+                       pstrcpy(dyn_CONFIGFILE, fname);
+                       test = False;
+               }
+       }
+
+       reopen_logs();
+
+       if (test && !lp_file_list_changed())
+               return(True);
+
+       if (smb) {
+               lp_killunused(smb, conn_snum_used);
+       }
+       
+       ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+       load_printers();
+
+       /* perhaps the config filename is now set */
+       if (!test)
+               reload_services(smb, True);
+
+       reopen_logs();
+
+       load_interfaces();
+
+       mangle_reset_cache();
+       reset_stat_cache();
+
+       /* this forces service parameters to be flushed */
+       set_current_service(NULL,True);
+
+       return(ret);
+}
+
+/****************************************************************************
+ Initialise connect, service and file structs.
+****************************************************************************/
+static BOOL init_structs(void)
+{
+       init_names();
+       file_init();
+       init_rpc_pipe_hnd();
+       secrets_init();
+
+       /* we want to re-seed early to prevent time delays causing
+           client problems at a later date. (tridge) */
+       generate_random_buffer(NULL, 0, False);
+
+       return True;
+}
+
+
+/*
+  setup the events for the chosen process model
+*/
+static void setup_process_model(struct event_context *events, 
+                               const char *model)
+{
+       struct model_ops *ops;
+
+       process_model_init();
+
+       ops = process_model_byname(model);
+       if (!ops) {
+               DEBUG(0,("Unknown process model '%s'\n", model));
+               exit(-1);
+       }
+
+       ops->model_startup();
+
+       /* now setup the listening sockets, adding 
+          event handlers to the events structure */
+       open_sockets_smbd(events, ops);
+}
+
+/****************************************************************************
+ main program.
+****************************************************************************/
+ int main(int argc,const char *argv[])
+{
+       BOOL is_daemon = False;
+       BOOL interactive = False;
+       BOOL Fork = True;
+       BOOL log_stdout = False;
+       int opt;
+       poptContext pc;
+       struct event_context *events;
+       const char *model = "standard";
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+       {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" },
+       {"interactive", 'i', POPT_ARG_VAL, &interactive, True, "Run interactive (not a daemon)"},
+       {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+       {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+       {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" },
+       {"port", 'p', POPT_ARG_STRING, NULL, 0, "Listen on the specified ports"},
+       {"model", 'M', POPT_ARG_STRING, &model, 0, "select process model"},
+       POPT_COMMON_SAMBA
+       { NULL }
+       };
+       
+       pc = poptGetContext("smbd", argc, argv, long_options, 0);
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt)  {
+               case 'b':
+                       /* Display output to screen as well as debug */
+                       build_options(True); 
+                       exit(0);
+                       break;
+               case 'p':
+                       lp_set_cmdline("smb ports", poptGetOptArg(pc));
+                       break;
+               }
+       }
+       poptFreeContext(pc);
+
+       events = event_context_init();
+
+       load_case_tables();
+
+       if (interactive) {
+               Fork = False;
+               log_stdout = True;
+       }
+
+       if (log_stdout && Fork) {
+               DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+               exit(1);
+       }
+       setup_logging(argv[0], log_stdout);
+
+       fault_setup((void (*)(void *))exit_server);
+       
+       /* we are never interested in SIGPIPE */
+       BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+       /* we are never interested in SIGFPE */
+       BlockSignals(True,SIGFPE);
+#endif
+
+#if defined(SIGUSR2)
+       /* We are no longer interested in USR2 */
+       BlockSignals(True,SIGUSR2);
+#endif
+
+       /* POSIX demands that signals are inherited. If the invoking process has
+        * these signals masked, we will have problems, as we won't recieve them. */
+       BlockSignals(False, SIGHUP);
+       BlockSignals(False, SIGUSR1);
+       BlockSignals(False, SIGTERM);
+
+       /* we want total control over the permissions on created files,
+          so set our umask to 0 */
+       umask(0);
+
+       reopen_logs();
+
+       DEBUG(0,("smbd version %s started.\n", SAMBA_VERSION));
+       DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2003\n"));
+
+       /* Output the build options to the debug log */ 
+       build_options(False);
+
+       if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+               DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+               exit(1);
+       }
+       DEBUG(0,("Using %s process model\n", model));
+                       
+       if (!reload_services(NULL, False))
+               return(-1);     
+
+       init_structs();
+
+       if (!is_daemon && !is_a_socket(0)) {
+               if (!interactive)
+                       DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+
+               /*
+                * Setting is_daemon here prevents us from eventually calling
+                * the open_sockets_inetd()
+                */
+
+               is_daemon = True;
+       }
+
+       if (is_daemon && !interactive) {
+               DEBUG(3,("Becoming a daemon.\n"));
+               become_daemon(Fork);
+       }
+
+       if (!directory_exist(lp_lockdir(), NULL)) {
+               mkdir(lp_lockdir(), 0755);
+       }
+
+       if (is_daemon) {
+               pidfile_create("smbd");
+       }
+
+       register_msg_pool_usage();
+       register_dmalloc_msgs();
+
+       setup_process_model(events, model);
+
+       /* wait for events */
+       return event_loop_wait(events);
+}
diff --git a/source4/smbd/service.c b/source4/smbd/service.c
new file mode 100644 (file)
index 0000000..d219e6c
--- /dev/null
@@ -0,0 +1,339 @@
+/* 
+   Unix SMB/CIFS implementation.
+   service (connection) handling
+   Copyright (C) Andrew Tridgell 1992-2003
+   
+   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"
+
+
+/****************************************************************************
+ Add a home service. Returns the new service number or -1 if fail.
+****************************************************************************/
+int add_home_service(const char *service, const char *username, const char *homedir)
+{
+       int iHomeService;
+
+       if (!service || !homedir)
+               return -1;
+
+       if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
+               return -1;
+
+       /*
+        * If this is a winbindd provided username, remove
+        * the domain component before adding the service.
+        * Log a warning if the "path=" parameter does not
+        * include any macros.
+        */
+
+       {
+               const char *p = strchr(service,*lp_winbind_separator());
+
+               /* We only want the 'user' part of the string */
+               if (p) {
+                       service = p + 1;
+               }
+       }
+
+       if (!lp_add_home(service, iHomeService, username, homedir)) {
+               return -1;
+       }
+       
+       return lp_servicenumber(service);
+
+}
+
+
+/**
+ * Find a service entry. service is always in dos codepage.
+ *
+ * @param service is modified (to canonical form??)
+ **/
+static int find_service(const char *service)
+{
+       int iService;
+
+       iService = lp_servicenumber(service);
+
+       /* now handle the special case of a home directory */
+       if (iService == -1) {
+               char *phome_dir = get_user_home_dir(service);
+
+               if(!phome_dir) {
+                       /*
+                        * Try mapping the servicename, it may
+                        * be a Windows to unix mapped user name.
+                        */
+/* REWRITE:
+                       if (map_username(service))
+                               phome_dir = get_user_home_dir(service);
+*/
+               }
+               
+               DEBUG(3,("checking for home directory %s gave %s\n",service,
+                        phome_dir?phome_dir:"(NULL)"));
+               
+               iService = add_home_service(service,service /* 'username' */, phome_dir);
+       }
+
+       /* If we still don't have a service, attempt to add it as a printer. */
+       if (iService == -1) {
+               int iPrinterService;
+
+               if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
+                       char *pszTemp;
+
+                       DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+                       pszTemp = lp_printcapname();
+                       if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) {
+                               DEBUG(3,("%s is a valid printer name\n", service));
+                               DEBUG(3,("adding %s as a printer service\n", service));
+                               lp_add_printer(service, iPrinterService);
+                               iService = lp_servicenumber(service);
+                               if (iService < 0)
+                                       DEBUG(0,("failed to add %s as a printer service!\n", service));
+                       } else {
+                               DEBUG(3,("%s is not a valid printer name\n", service));
+                       }
+               }
+       }
+
+       /* Check for default vfs service?  Unsure whether to implement this */
+       if (iService == -1) {
+       }
+
+       /* just possibly it's a default service? */
+       if (iService == -1) {
+               char *pdefservice = lp_defaultservice();
+               if (pdefservice && *pdefservice && 
+                   !strequal(pdefservice,service) &&
+                   !strstr(service,"..")) {
+                       /*
+                        * We need to do a local copy here as lp_defaultservice() 
+                        * returns one of the rotating lp_string buffers that
+                        * could get overwritten by the recursive find_service() call
+                        * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
+                        */
+                       pstring defservice;
+                       pstrcpy(defservice, pdefservice);
+                       iService = find_service(defservice);
+                       if (iService >= 0) {
+                               /* REWRITE: all_string_sub(service, "_","/",0); */
+                               iService = lp_add_service(service, iService);
+                       }
+               }
+       }
+
+       if (iService >= 0 && !VALID_SNUM(iService)) {
+               DEBUG(0,("Invalid snum %d for %s\n",iService, service));
+               iService = -1;
+       }
+
+       if (iService == -1) {
+               DEBUG(3,("find_service() failed to find service %s\n", service));
+       }
+
+       return iService;
+}
+
+
+/****************************************************************************
+  Make a connection, given the snum to connect to, and the vuser of the
+  connecting user if appropriate.
+****************************************************************************/
+static NTSTATUS make_connection_snum(struct request_context *req,
+                                    int snum, enum ntvfs_type type,
+                                    DATA_BLOB password, 
+                                    const char *dev)
+{
+       struct tcon_context *conn;
+       NTSTATUS status;
+
+       conn = conn_new(req->smb);
+       if (!conn) {
+               DEBUG(0,("Couldn't find free connection.\n"));
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
+       }
+       req->conn = conn;
+
+       conn->service = snum;
+       conn->type = type;
+
+       /*
+        * New code to check if there's a share security descripter
+        * added from NT server manager. This is done after the
+        * smb.conf checks are done as we need a uid and token. JRA.
+        *
+        */
+
+       if (!share_access_check(req, conn, snum, SA_RIGHT_FILE_WRITE_DATA)) {
+               if (!share_access_check(req, conn, snum, SA_RIGHT_FILE_READ_DATA)) {
+                       /* No access, read or write. */
+                       DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
+                                 lp_servicename(snum)));
+                       conn_free(req->smb, conn);
+                       return NT_STATUS_ACCESS_DENIED;
+               } else {
+                       conn->read_only = True;
+               }
+       }
+
+       /* check number of connections */
+       if (!claim_connection(conn,
+                             lp_servicename(SNUM(conn)),
+                             lp_max_connections(SNUM(conn)),
+                             False,0)) {
+               DEBUG(1,("too many connections - rejected\n"));
+               conn_free(req->smb, conn);
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
+       }  
+
+       /* init ntvfs function pointers */
+       status = ntvfs_init_connection(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("ntvfs_init_connection failed for service %s\n", lp_servicename(SNUM(conn))));
+               conn_free(req->smb, conn);
+               return status;
+       }
+       
+       /* Invoke NTVFS connection hook */
+       if (conn->ntvfs_ops->connect) {
+               status = conn->ntvfs_ops->connect(req, lp_servicename(snum));
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("make_connection: NTVFS make connection failed!\n"));
+                       conn_free(req->smb, conn);
+                       return status;
+               }
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service 
+****************************************************************************/
+static NTSTATUS make_connection(struct request_context *req,
+                               const char *service, DATA_BLOB password, 
+                               const char *dev, uint16 vuid)
+{
+       int snum;
+       enum ntvfs_type type;
+       const char *type_str;
+
+       /* the service might be of the form \\SERVER\SHARE. Should we put
+          the server name we get from this somewhere? */
+       if (strncmp(service, "\\\\", 2) == 0) {
+               char *p = strchr(service+2, '\\');
+               if (p) {
+                       service = p + 1;
+               }
+       }
+
+       snum = find_service(service);
+
+       if (snum == -1) {
+               DEBUG(0,("%s couldn't find service %s\n",
+                        sub_get_remote_machine(), service));
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+
+       /* work out what sort of connection this is */
+       if (strcmp(lp_fstype(snum), "IPC") == 0) {
+               type = NTVFS_IPC;
+               type_str = "IPC";
+       } else if (lp_print_ok(snum)) {
+               type = NTVFS_PRINT;
+               type_str = "LPT:";
+       } else {
+               type = NTVFS_DISK;
+               type_str = "A:";
+       }
+
+       if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
+               /* the client gave us the wrong device type */
+               return NT_STATUS_BAD_DEVICE_TYPE;
+       }
+
+       return make_connection_snum(req, snum, type, password, dev);
+}
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(struct tcon_context *conn)
+{
+       DEBUG(3, ("%s (%s) closed connection to service %s\n",
+                 sub_get_remote_machine(),conn->smb->socket.client_addr,
+                 lp_servicename(SNUM(conn))));
+
+       yield_connection(conn, lp_servicename(SNUM(conn)));
+
+       /* tell the ntvfs backend that we are disconnecting */
+       conn->ntvfs_ops->disconnect(conn);
+
+       conn_free(conn->smb, conn);
+}
+
+
+
+/*
+  backend for tree connect call
+*/
+NTSTATUS tcon_backend(struct request_context *req, union smb_tcon *con)
+{
+       NTSTATUS status;
+
+       /* can only do bare tcon in share level security */
+       if (req->user_ctx == NULL && lp_security() != SEC_SHARE) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (con->generic.level == RAW_TCON_TCON) {
+               DATA_BLOB password;
+               password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1);
+
+               status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev, req->user_ctx->vuid);
+               
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               con->tcon.out.max_xmit = req->smb->negotiate.max_recv;
+               con->tcon.out.cnum = req->conn->cnum;
+               
+               return status;
+       } 
+
+       status = make_connection(req, con->tconx.in.path, con->tconx.in.password, 
+                                con->tconx.in.device, req->user_ctx->vuid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       con->tconx.out.cnum = req->conn->cnum;
+       con->tconx.out.dev_type = talloc_strdup(req->mem_ctx, req->conn->dev_type);
+       con->tconx.out.fs_type = talloc_strdup(req->mem_ctx, req->conn->fs_type);
+       con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->conn->service) << 2);
+       if (lp_msdfs_root(req->conn->service) && lp_host_msdfs()) {
+               con->tconx.out.options |= SMB_SHARE_IN_DFS;
+       }
+
+       return status;
+}
diff --git a/source4/smbd/session.c b/source4/smbd/session.c
new file mode 100644 (file)
index 0000000..7f85fca
--- /dev/null
@@ -0,0 +1,42 @@
+/* 
+   Unix SMB/CIFS implementation.
+   session handling for utmp and PAM
+   Copyright (C) tridge@samba.org 2001
+   Copyright (C) abartlet@pcug.org.au 2001
+   
+   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.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+   and is yielded when the corresponding vuid is destroyed.
+
+   sessions are used to populate utmp and PAM session structures
+*/
+
+#include "includes.h"
+
+/* called when a session is created */
+BOOL session_claim(struct server_context *smb, user_struct *vuser)
+{
+       DEBUG(0,("rewrite: Not doing session claim\n"));
+       return True;
+}
+
+/* called when a session is destroyed */
+void session_yield(user_struct *vuser)
+{
+       DEBUG(0,("rewrite: Not doing session yield\n"));
+}
+
diff --git a/source4/smbd/sesssetup.c b/source4/smbd/sesssetup.c
new file mode 100644 (file)
index 0000000..c1ea446
--- /dev/null
@@ -0,0 +1,149 @@
+/* 
+   Unix SMB/CIFS implementation.
+   handle SMBsessionsetup
+   Copyright (C) Andrew Tridgell 1998-2001
+   Copyright (C) Andrew Bartlett      2001
+   Copyright (C) Jim McDonough        2002
+   Copyright (C) Luke Howard          2003
+
+   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"
+
+/*
+  setup the OS, Lanman and domain portions of a session setup reply
+*/
+static void sesssetup_common_strings(struct request_context *req,
+                                    char **os, char **lanman, char **domain)
+{
+       (*os) = talloc_asprintf(req->mem_ctx, "Unix");
+       (*lanman) = talloc_asprintf(req->mem_ctx, "Samba %s", SAMBA_VERSION);
+       (*domain) = talloc_asprintf(req->mem_ctx, "%s", lp_workgroup());
+}
+
+
+/*
+  handler for old style session setup
+*/
+static NTSTATUS sesssetup_old(struct request_context *req, union smb_sesssetup *sess)
+{
+       NTSTATUS status;
+       auth_usersupplied_info *user_info = NULL;
+       auth_serversupplied_info *server_info = NULL;
+       DATA_BLOB null_blob;
+
+       if (!req->smb->negotiate.done_sesssetup) {
+               req->smb->negotiate.max_send = sess->old.in.bufsize;
+       }
+
+       null_blob.length = 0;
+
+       status = make_user_info_for_reply_enc(&user_info, 
+                                             sess->old.in.user, sess->old.in.domain,
+                                             sess->old.in.password,
+                                             null_blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, 
+                                                                      user_info, 
+                                                                      &server_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       sess->old.out.action = 0;
+       sess->old.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user);
+       sesssetup_common_strings(req, 
+                                &sess->old.out.os,
+                                &sess->old.out.lanman,
+                                &sess->old.out.domain);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  handler for NT1 style session setup
+*/
+static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup *sess)
+{
+       NTSTATUS status;
+       auth_usersupplied_info *user_info = NULL;
+       auth_serversupplied_info *server_info = NULL;
+
+       if (!req->smb->negotiate.done_sesssetup) {
+               req->smb->negotiate.max_send = sess->nt1.in.bufsize;
+               req->smb->negotiate.client_caps = sess->nt1.in.capabilities;
+       }
+
+       status = make_user_info_for_reply_enc(&user_info, 
+                                             sess->nt1.in.user, sess->nt1.in.domain,
+                                             sess->nt1.in.password1,
+                                             sess->nt1.in.password2);
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, 
+                                                                      user_info, 
+                                                                      &server_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       sess->nt1.out.action = 0;
+       sess->nt1.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user);
+       sesssetup_common_strings(req, 
+                                &sess->nt1.out.os,
+                                &sess->nt1.out.lanman,
+                                &sess->nt1.out.domain);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  handler for SPNEGO style session setup
+*/
+static NTSTATUS sesssetup_spnego(struct request_context *req, union smb_sesssetup *sess)
+{
+       /* defer this one for now */
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+  backend for sessionsetup call - this takes all 3 varients of the call
+*/
+NTSTATUS sesssetup_backend(struct request_context *req, 
+                          union smb_sesssetup *sess)
+{
+       switch (sess->generic.level) {
+               case RAW_SESSSETUP_OLD:
+                       return sesssetup_old(req, sess);
+               case RAW_SESSSETUP_NT1:
+                       return sesssetup_nt1(req, sess);
+               case RAW_SESSSETUP_SPNEGO:
+                       return sesssetup_spnego(req, sess);
+       }
+
+       req->smb->negotiate.done_sesssetup = True;
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+
diff --git a/source4/smbd/tcon.c b/source4/smbd/tcon.c
new file mode 100644 (file)
index 0000000..b28b04f
--- /dev/null
@@ -0,0 +1,3 @@
+
+
+
diff --git a/source4/smbd/trans2.c b/source4/smbd/trans2.c
new file mode 100644 (file)
index 0000000..b26dbd5
--- /dev/null
@@ -0,0 +1,1343 @@
+/* 
+   Unix SMB/CIFS implementation.
+   transaction2 handling
+   Copyright (C) Andrew Tridgell 2003
+
+   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.
+*/
+/*
+   This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+
+
+#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
+       if ((blob)->length < (size)) { \
+               return NT_STATUS_INFO_LENGTH_MISMATCH; \
+       }} while (0)
+
+/* grow the data allocation size of a trans2 reply - this guarantees
+   that requests to grow the data size later will not change the
+   pointer */
+static void trans2_grow_data_allocation(struct request_context *req, 
+                                       struct smb_trans2 *trans,
+                                       uint16 new_size)
+{
+       if (new_size <= trans->out.data.length) {
+               return;
+       }
+       trans->out.data.data = talloc_realloc(req->mem_ctx, trans->out.data.data, new_size);
+}
+
+
+/* grow the data size of a trans2 reply */
+static void trans2_grow_data(struct request_context *req, 
+                            struct smb_trans2 *trans,
+                            uint16 new_size)
+{
+       trans2_grow_data_allocation(req, trans, new_size);
+       trans->out.data.length = new_size;
+}
+
+/* grow the data, zero filling any new bytes */
+static void trans2_grow_data_fill(struct request_context *req, 
+                                 struct smb_trans2 *trans,
+                                 uint16 new_size)
+{
+       uint16 old_size = trans->out.data.length;
+       trans2_grow_data(req, trans, new_size);
+       if (new_size > old_size) {
+               memset(trans->out.data.data + old_size, 0, new_size - old_size);
+       }
+}
+
+
+/* setup a trans2 reply, given the data and params sizes */
+static void trans2_setup_reply(struct request_context *req, 
+                              struct smb_trans2 *trans,
+                              uint16 param_size, uint16 data_size,
+                              uint16 setup_count)
+{
+       trans->out.setup_count = setup_count;
+       if (setup_count != 0) {
+               trans->out.setup = talloc_zero(req->mem_ctx, sizeof(uint16) * setup_count);
+       }
+       trans->out.params = data_blob_talloc(req->mem_ctx, NULL, param_size);
+       trans->out.data = data_blob_talloc(req->mem_ctx, NULL, data_size);
+}
+
+
+/*
+  pull a string from a blob in a trans2 request
+*/
+static size_t trans2_pull_blob_string(struct request_context *req, 
+                                     const DATA_BLOB *blob,
+                                     uint16 offset,
+                                     const char **str,
+                                     int flags)
+{
+       /* we use STR_NO_RANGE_CHECK because the params are allocated
+          separately in a DATA_BLOB, so we need to do our own range
+          checking */
+       if (offset >= blob->length) {
+               *str = NULL;
+               return 0;
+       }
+       
+       return req_pull_string(req, str, 
+                              blob->data + offset, 
+                              blob->length - offset,
+                              STR_NO_RANGE_CHECK | flags);
+}
+
+/*
+  push a string into the data section of a trans2 request
+  return the number of bytes consumed in the output
+*/
+static size_t trans2_push_data_string(struct request_context *req, 
+                                     struct smb_trans2 *trans,
+                                     uint16 len_offset,
+                                     uint16 offset,
+                                     const WIRE_STRING *str,
+                                     int dest_len,
+                                     int flags)
+{
+       int alignment = 0, ret = 0, pkt_len;
+
+       /* we use STR_NO_RANGE_CHECK because the params are allocated
+          separately in a DATA_BLOB, so we need to do our own range
+          checking */
+       if (!str->s || offset >= trans->out.data.length) {
+               if (flags & STR_LEN8BIT) {
+                       SCVAL(trans->out.data.data, len_offset, 0);
+               } else {
+                       SIVAL(trans->out.data.data, len_offset, 0);
+               }
+               return 0;
+       }
+
+       flags |= STR_NO_RANGE_CHECK;
+
+       if (dest_len == -1 || (dest_len > trans->out.data.length - offset)) {
+               dest_len = trans->out.data.length - offset;
+       }
+
+       if (!(flags & (STR_ASCII|STR_UNICODE))) {
+               flags |= (req->smb->negotiate.client_caps & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+       }
+
+       if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
+               alignment = 1;
+               if (dest_len > 0) {
+                       SCVAL(trans->out.data.data + offset, 0, 0);
+                       ret = push_string(NULL, trans->out.data.data + offset + 1, str->s, dest_len-1, flags);
+               }
+       } else {
+               ret = push_string(NULL, trans->out.data.data + offset, str->s, dest_len, flags);
+       }
+
+       /* sometimes the string needs to be terminated, but the length
+          on the wire must not include the termination! */
+       pkt_len = ret;
+
+       if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) {
+               if ((flags & STR_UNICODE) && ret >= 2) {
+                       pkt_len = ret-2;
+               }
+               if ((flags & STR_ASCII) && ret >= 1) {
+                       pkt_len = ret-1;
+               }
+       }       
+
+       if (flags & STR_LEN8BIT) {
+               SCVAL(trans->out.data.data, len_offset, pkt_len);
+       } else {
+               SIVAL(trans->out.data.data, len_offset, pkt_len);
+       }
+
+       return ret + alignment;
+}
+
+/*
+  append a string to the data section of a trans2 reply
+  len_offset points to the place in the packet where the length field
+  should go
+*/
+static void trans2_append_data_string(struct request_context *req, 
+                                       struct smb_trans2 *trans,
+                                       const WIRE_STRING *str,
+                                       uint_t len_offset,
+                                       int flags)
+{
+       size_t ret;
+       uint16 offset;
+       const int max_bytes_per_char = 3;
+
+       offset = trans->out.data.length;
+       trans2_grow_data(req, trans, offset + (2+strlen(str->s))*max_bytes_per_char);
+       ret = trans2_push_data_string(req, trans, len_offset, offset, str, -1, flags);
+       trans2_grow_data(req, trans, offset + ret);
+}
+
+
+/*
+  trans2 qfsinfo implementation
+*/
+static NTSTATUS trans2_qfsinfo(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_fsinfo fsinfo;
+       NTSTATUS status;
+       uint16 level;
+       uint_t i;
+
+       /* make sure we got enough parameters */
+       if (trans->in.params.length != 2) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       level = SVAL(trans->in.params.data, 0);
+
+       switch (level) {
+       case SMB_QFS_ALLOCATION:
+               fsinfo.allocation.level = RAW_QFS_ALLOCATION;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 18, 0);
+
+               SIVAL(trans->out.data.data,  0, fsinfo.allocation.out.fs_id);
+               SIVAL(trans->out.data.data,  4, fsinfo.allocation.out.sectors_per_unit);
+               SIVAL(trans->out.data.data,  8, fsinfo.allocation.out.total_alloc_units);
+               SIVAL(trans->out.data.data, 12, fsinfo.allocation.out.avail_alloc_units);
+               SSVAL(trans->out.data.data, 16, fsinfo.allocation.out.bytes_per_sector);
+
+               return NT_STATUS_OK;
+
+       case SMB_QFS_VOLUME:
+               fsinfo.volume.level = RAW_QFS_VOLUME;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 5, 0);
+
+               SIVAL(trans->out.data.data,       0, fsinfo.volume.out.serial_number);
+               /* w2k3 implements this incorrectly for unicode - it
+                * leaves the last byte off the string */
+               trans2_append_data_string(req, trans, 
+                                         &fsinfo.volume.out.volume_name, 
+                                         4, STR_LEN8BIT|STR_NOALIGN);
+
+               return NT_STATUS_OK;
+
+       case SMB_QFS_VOLUME_INFO:
+       case SMB_QFS_VOLUME_INFORMATION:
+               fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 18, 0);
+
+               push_nttime(trans->out.data.data, 0, &fsinfo.volume_info.out.create_time);
+               SIVAL(trans->out.data.data,       8, fsinfo.volume_info.out.serial_number);
+               trans2_append_data_string(req, trans, 
+                                         &fsinfo.volume_info.out.volume_name, 
+                                         12, STR_UNICODE);
+
+               return NT_STATUS_OK;
+
+       case SMB_QFS_SIZE_INFO:
+       case SMB_QFS_SIZE_INFORMATION:
+               fsinfo.size_info.level = RAW_QFS_SIZE_INFO;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 24, 0);
+
+               SBVAL(trans->out.data.data,  0, fsinfo.size_info.out.total_alloc_units);
+               SBVAL(trans->out.data.data,  8, fsinfo.size_info.out.avail_alloc_units);
+               SIVAL(trans->out.data.data, 16, fsinfo.size_info.out.sectors_per_unit);
+               SIVAL(trans->out.data.data, 20, fsinfo.size_info.out.bytes_per_sector);
+
+               return NT_STATUS_OK;
+
+       case SMB_QFS_DEVICE_INFO:
+       case SMB_QFS_DEVICE_INFORMATION:
+               fsinfo.device_info.level = RAW_QFS_DEVICE_INFO;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               trans2_setup_reply(req, trans, 0, 8, 0);
+               SIVAL(trans->out.data.data,      0, fsinfo.device_info.out.device_type);
+               SIVAL(trans->out.data.data,      4, fsinfo.device_info.out.characteristics);
+               return NT_STATUS_OK;
+
+
+       case SMB_QFS_ATTRIBUTE_INFO:
+       case SMB_QFS_ATTRIBUTE_INFORMATION:
+               fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 12, 0);
+
+               SIVAL(trans->out.data.data, 0, fsinfo.attribute_info.out.fs_attr);
+               SIVAL(trans->out.data.data, 4, fsinfo.attribute_info.out.max_file_component_length);
+               /* this must not be null terminated or win98 gets
+                  confused!  also note that w2k3 returns this as
+                  unicode even when ascii is negotiated */
+               trans2_append_data_string(req, trans, 
+                                         &fsinfo.attribute_info.out.fs_type,
+                                         8, STR_UNICODE);
+               return NT_STATUS_OK;
+
+
+       case SMB_QFS_QUOTA_INFORMATION:
+               fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 48, 0);
+
+               SBVAL(trans->out.data.data,   0, fsinfo.quota_information.out.unknown[0]);
+               SBVAL(trans->out.data.data,   8, fsinfo.quota_information.out.unknown[1]);
+               SBVAL(trans->out.data.data,  16, fsinfo.quota_information.out.unknown[2]);
+               SBVAL(trans->out.data.data,  24, fsinfo.quota_information.out.quota_soft);
+               SBVAL(trans->out.data.data,  32, fsinfo.quota_information.out.quota_hard);
+               SBVAL(trans->out.data.data,  40, fsinfo.quota_information.out.quota_flags);
+
+               return NT_STATUS_OK;
+
+
+       case SMB_QFS_FULL_SIZE_INFORMATION:
+               fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 32, 0);
+
+               SBVAL(trans->out.data.data,  0, fsinfo.full_size_information.out.total_alloc_units);
+               SBVAL(trans->out.data.data,  8, fsinfo.full_size_information.out.call_avail_alloc_units);
+               SBVAL(trans->out.data.data, 16, fsinfo.full_size_information.out.actual_avail_alloc_units);
+               SIVAL(trans->out.data.data, 24, fsinfo.full_size_information.out.sectors_per_unit);
+               SIVAL(trans->out.data.data, 28, fsinfo.full_size_information.out.bytes_per_sector);
+
+               return NT_STATUS_OK;
+
+       case SMB_QFS_OBJECTID_INFORMATION:
+               fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION;
+
+               status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               trans2_setup_reply(req, trans, 0, 64, 0);
+
+               memcpy(trans->out.data.data, fsinfo.objectid_information.out.guid.info, GUID_SIZE);
+               for (i=0;i<6;i++) {
+                       SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]);
+               }
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+  fill in the reply from a qpathinfo or qfileinfo call
+*/
+static NTSTATUS trans2_fileinfo_fill(struct request_context *req, struct smb_trans2 *trans,
+                                    union smb_fileinfo *st)
+{
+       uint_t i;
+       
+       switch (st->generic.level) {
+       case RAW_FILEINFO_GENERIC:
+       case RAW_FILEINFO_GETATTR:
+       case RAW_FILEINFO_GETATTRE:
+               /* handled elsewhere */
+               return NT_STATUS_INVALID_LEVEL;
+
+       case RAW_FILEINFO_BASIC_INFO:
+       case RAW_FILEINFO_BASIC_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 40, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+               push_nttime(trans->out.data.data,  0, &st->basic_info.out.create_time);
+               push_nttime(trans->out.data.data,  8, &st->basic_info.out.access_time);
+               push_nttime(trans->out.data.data, 16, &st->basic_info.out.write_time);
+               push_nttime(trans->out.data.data, 24, &st->basic_info.out.change_time);
+               SIVAL(trans->out.data.data,       32, st->basic_info.out.attrib);
+               SIVAL(trans->out.data.data,       36, 0); /* padding */
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STANDARD:
+               trans2_setup_reply(req, trans, 2, 22, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+               put_dos_date2(trans->out.data.data, 0, st->standard.out.create_time);
+               put_dos_date2(trans->out.data.data, 4, st->standard.out.access_time);
+               put_dos_date2(trans->out.data.data, 8, st->standard.out.write_time);
+               SIVAL(trans->out.data.data,        12, st->standard.out.size);
+               SIVAL(trans->out.data.data,        16, st->standard.out.alloc_size);
+               SSVAL(trans->out.data.data,        20, st->standard.out.attrib);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_SIZE:
+               trans2_setup_reply(req, trans, 2, 26, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+               put_dos_date2(trans->out.data.data, 0, st->ea_size.out.create_time);
+               put_dos_date2(trans->out.data.data, 4, st->ea_size.out.access_time);
+               put_dos_date2(trans->out.data.data, 8, st->ea_size.out.write_time);
+               SIVAL(trans->out.data.data,        12, st->ea_size.out.size);
+               SIVAL(trans->out.data.data,        16, st->ea_size.out.alloc_size);
+               SSVAL(trans->out.data.data,        20, st->ea_size.out.attrib);
+               SIVAL(trans->out.data.data,        22, st->ea_size.out.ea_size);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 56, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+               push_nttime(trans->out.data.data,  0, &st->network_open_information.out.create_time);
+               push_nttime(trans->out.data.data,  8, &st->network_open_information.out.access_time);
+               push_nttime(trans->out.data.data, 16, &st->network_open_information.out.write_time);
+               push_nttime(trans->out.data.data, 24, &st->network_open_information.out.change_time);
+               SBVAL(trans->out.data.data,       32, st->network_open_information.out.alloc_size);
+               SBVAL(trans->out.data.data,       40, st->network_open_information.out.size);
+               SIVAL(trans->out.data.data,       48, st->network_open_information.out.attrib);
+               SIVAL(trans->out.data.data,       52, 0); /* padding */
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STANDARD_INFO:
+       case RAW_FILEINFO_STANDARD_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 24, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SBVAL(trans->out.data.data,  0, st->standard_info.out.alloc_size);
+               SBVAL(trans->out.data.data,  8, st->standard_info.out.size);
+               SIVAL(trans->out.data.data, 16, st->standard_info.out.nlink);
+               SCVAL(trans->out.data.data, 20, st->standard_info.out.delete_pending);
+               SCVAL(trans->out.data.data, 21, st->standard_info.out.directory);
+               SSVAL(trans->out.data.data, 22, 0); /* padding */
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 8, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, st->attribute_tag_information.out.attrib);
+               SIVAL(trans->out.data.data,  4, st->attribute_tag_information.out.reparse_tag);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_EA_INFO:
+       case RAW_FILEINFO_EA_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, st->ea_info.out.ea_size);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_MODE_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, st->mode_information.out.mode);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, 
+                     st->alignment_information.out.alignment_requirement);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALL_EAS:
+               if (st->all_eas.out.num_eas == 0) {
+                       trans2_setup_reply(req, trans, 2, 4, 0);
+                       SSVAL(trans->out.params.data, 0, 0);
+                       SIVAL(trans->out.data.data,  0, 0);
+               } else {
+                       uint32 list_size = ea_list_size(st->all_eas.out.num_eas,
+                                                       st->all_eas.out.eas);
+                       trans2_setup_reply(req, trans, 2, list_size, 0);
+                       SSVAL(trans->out.params.data, 0, 0);
+                       ea_put_list(trans->out.data.data, 
+                                   st->all_eas.out.num_eas, st->all_eas.out.eas);
+               }
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ACCESS_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, st->access_information.out.access_flags);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_POSITION_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 8, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SBVAL(trans->out.data.data,  0, st->position_information.out.position);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_COMPRESSION_INFO:
+       case RAW_FILEINFO_COMPRESSION_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 16, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SBVAL(trans->out.data.data,  0, st->compression_info.out.compressed_size);
+               SSVAL(trans->out.data.data,  8, st->compression_info.out.format);
+               SCVAL(trans->out.data.data, 10, st->compression_info.out.unit_shift);
+               SCVAL(trans->out.data.data, 11, st->compression_info.out.chunk_shift);
+               SCVAL(trans->out.data.data, 12, st->compression_info.out.cluster_shift);
+               SSVAL(trans->out.data.data, 13, 0); /* 3 bytes padding */
+               SCVAL(trans->out.data.data, 15, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_IS_NAME_VALID:
+               trans2_setup_reply(req, trans, 2, 0, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_INTERNAL_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 8, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               SIVAL(trans->out.data.data,  0, st->internal_information.out.device);
+               SIVAL(trans->out.data.data,  4, st->internal_information.out.inode);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALL_INFO:
+       case RAW_FILEINFO_ALL_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 72, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+               push_nttime(trans->out.data.data,  0, &st->all_info.out.create_time);
+               push_nttime(trans->out.data.data,  8, &st->all_info.out.access_time);
+               push_nttime(trans->out.data.data, 16, &st->all_info.out.write_time);
+               push_nttime(trans->out.data.data, 24, &st->all_info.out.change_time);
+               SIVAL(trans->out.data.data,       32, st->all_info.out.attrib);
+               SBVAL(trans->out.data.data,       40, st->all_info.out.alloc_size);
+               SBVAL(trans->out.data.data,       48, st->all_info.out.size);
+               SIVAL(trans->out.data.data,       56, st->all_info.out.nlink);
+               SCVAL(trans->out.data.data,       60, st->all_info.out.delete_pending);
+               SCVAL(trans->out.data.data,       61, st->all_info.out.directory);
+               SSVAL(trans->out.data.data,       62, 0); /* padding */
+               SIVAL(trans->out.data.data,       64, st->all_info.out.ea_size);
+               trans2_append_data_string(req, trans, &st->all_info.out.fname, 
+                                         68, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_NAME_INFO:
+       case RAW_FILEINFO_NAME_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               trans2_append_data_string(req, trans, &st->name_info.out.fname, 0, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_ALT_NAME_INFO:
+       case RAW_FILEINFO_ALT_NAME_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 4, 0);
+               SSVAL(trans->out.params.data, 0, 0);
+               trans2_append_data_string(req, trans, &st->alt_name_info.out.fname, 0, 0);
+               return NT_STATUS_OK;
+
+       case RAW_FILEINFO_STREAM_INFO:
+       case RAW_FILEINFO_STREAM_INFORMATION:
+               trans2_setup_reply(req, trans, 2, 0, 0);
+
+               SSVAL(trans->out.params.data, 0, 0);
+
+               for (i=0;i<st->stream_info.out.num_streams;i++) {
+                       uint16 data_size = trans->out.data.length;
+                       char *data;
+
+                       trans2_grow_data(req, trans, data_size + 24);
+                       data = trans->out.data.data + data_size;
+                       SBVAL(data,  8, st->stream_info.out.streams[i].size);
+                       SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size);
+                       trans2_append_data_string(req, trans, 
+                                                 &st->stream_info.out.streams[i].stream_name, 
+                                                 data_size + 4, STR_UNICODE);
+                       if (i == st->stream_info.out.num_streams - 1) {
+                               SIVAL(trans->out.data.data, data_size, 0);
+                       } else {
+                               trans2_grow_data_fill(req, trans, (trans->out.data.length+7)&~7);
+                               SIVAL(trans->out.data.data, data_size, 
+                                     trans->out.data.length - data_size);
+                       }
+               }
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+  trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qpathinfo(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_fileinfo st;
+       NTSTATUS status;
+       uint16 level;
+
+       /* make sure we got enough parameters */
+       if (trans->in.params.length < 8) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       level = SVAL(trans->in.params.data, 0);
+
+       trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.in.fname, 0);
+       if (st.generic.in.fname == NULL) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       /* work out the backend level - we make it 1-1 in the header */
+       st.generic.level = (enum fileinfo_level)level;
+       if (st.generic.level >= RAW_FILEINFO_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* call the backend */
+       status = req->conn->ntvfs_ops->qpathinfo(req, &st);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* fill in the reply parameters */
+       status = trans2_fileinfo_fill(req, trans, &st);
+
+       return status;
+}
+
+
+/*
+  trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qfileinfo(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_fileinfo st;
+       NTSTATUS status;
+       uint16 level;
+
+       /* make sure we got enough parameters */
+       if (trans->in.params.length < 4) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       st.generic.in.fnum  = SVAL(trans->in.params.data, 0);
+       level = SVAL(trans->in.params.data, 2);
+
+       /* work out the backend level - we make it 1-1 in the header */
+       st.generic.level = (enum fileinfo_level)level;
+       if (st.generic.level >= RAW_FILEINFO_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* call the backend */
+       status = req->conn->ntvfs_ops->qfileinfo(req, &st);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* fill in the reply parameters */
+       status = trans2_fileinfo_fill(req, trans, &st);
+
+       return status;
+}
+
+
+/*
+  parse a trans2 setfileinfo/setpathinfo data blob
+*/
+static NTSTATUS trans2_parse_sfileinfo(struct request_context *req,
+                                      union smb_setfileinfo *st,
+                                      const DATA_BLOB *blob)
+{
+       uint32 len;
+
+       switch (st->generic.level) {
+       case RAW_SFILEINFO_GENERIC:
+       case RAW_SFILEINFO_SETATTR:
+       case RAW_SFILEINFO_SETATTRE:
+               /* handled elsewhere */
+               return NT_STATUS_INVALID_LEVEL;
+
+       case RAW_SFILEINFO_STANDARD:
+               CHECK_MIN_BLOB_SIZE(blob, 12);
+               st->standard.in.create_time = make_unix_date2(blob->data + 0);
+               st->standard.in.access_time = make_unix_date2(blob->data + 4);
+               st->standard.in.write_time  = make_unix_date2(blob->data + 8);
+               return NT_STATUS_OK;
+
+       case RAW_SFILEINFO_EA_SET:
+               CHECK_MIN_BLOB_SIZE(blob, 4);
+               len = IVAL(blob->data, 0);
+               if (len > blob->length || len < 4) {
+                       return NT_STATUS_INFO_LENGTH_MISMATCH;
+               }
+               {
+                       DATA_BLOB blob2;
+                       blob2.data = blob->data+4;
+                       blob2.length = len-4;
+                       len = ea_pull_struct(&blob2, req->mem_ctx, &st->ea_set.in.ea);
+               }
+               if (len == 0) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               return NT_STATUS_OK;
+
+       case SMB_SFILEINFO_BASIC_INFO:
+       case SMB_SFILEINFO_BASIC_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 36);
+               st->basic_info.in.create_time = pull_nttime(blob->data,  0);
+               st->basic_info.in.access_time = pull_nttime(blob->data,  8);
+               st->basic_info.in.write_time =  pull_nttime(blob->data, 16);
+               st->basic_info.in.change_time = pull_nttime(blob->data, 24);
+               st->basic_info.in.attrib =      IVAL(blob->data,        32);
+               return NT_STATUS_OK;
+
+       case SMB_SFILEINFO_DISPOSITION_INFO:
+       case SMB_SFILEINFO_DISPOSITION_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 1);
+               st->disposition_info.in.delete_on_close = CVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case SMB_SFILEINFO_ALLOCATION_INFO:
+       case SMB_SFILEINFO_ALLOCATION_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 8);
+               st->allocation_info.in.alloc_size = BVAL(blob->data, 0);
+               return NT_STATUS_OK;                            
+
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 8);
+               st->end_of_file_info.in.size = BVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_SFILEINFO_RENAME_INFORMATION: {
+               DATA_BLOB blob2;
+
+               CHECK_MIN_BLOB_SIZE(blob, 12);
+               st->rename_information.in.overwrite = CVAL(blob->data, 0);
+               st->rename_information.in.root_fid  = IVAL(blob->data, 4);
+               len                                 = IVAL(blob->data, 8);
+               blob2.data = blob->data+12;
+               blob2.length = MIN(blob->length, len);
+               trans2_pull_blob_string(req, &blob2, 0, 
+                                       &st->rename_information.in.new_name, 0);
+               return NT_STATUS_OK;
+       }
+
+       case RAW_SFILEINFO_POSITION_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 8);
+               st->position_information.in.position = BVAL(blob->data, 0);
+               return NT_STATUS_OK;
+
+       case RAW_SFILEINFO_MODE_INFORMATION:
+               CHECK_MIN_BLOB_SIZE(blob, 4);
+               st->mode_information.in.mode = IVAL(blob->data, 0);
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+  trans2 setfileinfo implementation
+*/
+static NTSTATUS trans2_setfileinfo(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_setfileinfo st;
+       NTSTATUS status;
+       uint16 level, fnum;
+       DATA_BLOB *blob;
+
+       /* make sure we got enough parameters */
+       if (trans->in.params.length < 4) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       fnum  = SVAL(trans->in.params.data, 0);
+       level = SVAL(trans->in.params.data, 2);
+
+       blob = &trans->in.data;
+
+       st.generic.file.fnum = fnum;
+       st.generic.level = (enum setfileinfo_level)level;
+
+       status = trans2_parse_sfileinfo(req, &st, blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = req->conn->ntvfs_ops->setfileinfo(req, &st);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       trans2_setup_reply(req, trans, 2, 0, 0);
+       SSVAL(trans->out.params.data, 0, 0);
+       return NT_STATUS_OK;
+}
+
+/*
+  trans2 setpathinfo implementation
+*/
+static NTSTATUS trans2_setpathinfo(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_setfileinfo st;
+       NTSTATUS status;
+       uint16 level;
+       DATA_BLOB *blob;
+
+       /* make sure we got enough parameters */
+       if (trans->in.params.length < 4) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       level = SVAL(trans->in.params.data, 0);
+       blob = &trans->in.data;
+       st.generic.level = (enum setfileinfo_level)level;
+
+       trans2_pull_blob_string(req, &trans->in.params, 4, &st.generic.file.fname, 0);
+       if (st.generic.file.fname == NULL) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       status = trans2_parse_sfileinfo(req, &st, blob);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       status = req->conn->ntvfs_ops->setpathinfo(req, &st);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       trans2_setup_reply(req, trans, 2, 0, 0);
+       SSVAL(trans->out.params.data, 0, 0);
+       return NT_STATUS_OK;
+}
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct find_state {
+       struct request_context *req;
+       struct smb_trans2 *trans;
+       enum search_level level;
+       uint16 last_entry_offset;
+};
+
+/*
+  fill a single entry in a trans2 find reply 
+*/
+static void find_fill_info(struct request_context *req,
+                          struct smb_trans2 *trans, 
+                          enum search_level level,
+                          union smb_search_data *file)
+{
+       char *data;
+       uint_t ofs = trans->out.data.length;
+
+       switch (level) {
+       case RAW_SEARCH_SEARCH:
+       case RAW_SEARCH_GENERIC:
+               /* handled elsewhere */
+               break;
+
+       case RAW_SEARCH_STANDARD:
+               trans2_grow_data(req, trans, ofs + 23);
+               data = trans->out.data.data + ofs;
+               put_dos_date2(data, 0, file->standard.create_time);
+               put_dos_date2(data, 4, file->standard.access_time);
+               put_dos_date2(data, 8, file->standard.write_time);
+               SIVAL(data, 12, file->standard.size);
+               SIVAL(data, 16, file->standard.alloc_size);
+               SSVAL(data, 20, file->standard.attrib);
+               trans2_append_data_string(req, trans, &file->standard.name, 
+                                         ofs + 22, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
+               break;
+
+       case RAW_SEARCH_EA_SIZE:
+               trans2_grow_data(req, trans, ofs + 27);
+               data = trans->out.data.data + ofs;
+               put_dos_date2(data, 0, file->ea_size.create_time);
+               put_dos_date2(data, 4, file->ea_size.access_time);
+               put_dos_date2(data, 8, file->ea_size.write_time);
+               SIVAL(data, 12, file->ea_size.size);
+               SIVAL(data, 16, file->ea_size.alloc_size);
+               SSVAL(data, 20, file->ea_size.attrib);
+               SIVAL(data, 22, file->ea_size.ea_size);
+               trans2_append_data_string(req, trans, &file->ea_size.name, 
+                                         ofs + 26, STR_LEN8BIT | STR_NOALIGN);
+               break;
+
+       case RAW_SEARCH_DIRECTORY_INFO:
+               trans2_grow_data(req, trans, ofs + 64);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->directory_info.file_index);
+               push_nttime(data,    8, &file->directory_info.create_time);
+               push_nttime(data,   16, &file->directory_info.access_time);
+               push_nttime(data,   24, &file->directory_info.write_time);
+               push_nttime(data,   32, &file->directory_info.change_time);
+               SBVAL(data,         40, file->directory_info.size);
+               SBVAL(data,         48, file->directory_info.alloc_size);
+               SIVAL(data,         56, file->directory_info.attrib);
+               trans2_append_data_string(req, trans, &file->directory_info.name, 
+                                         ofs + 60, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+
+       case RAW_SEARCH_FULL_DIRECTORY_INFO:
+               trans2_grow_data(req, trans, ofs + 68);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->full_directory_info.file_index);
+               push_nttime(data,    8, &file->full_directory_info.create_time);
+               push_nttime(data,   16, &file->full_directory_info.access_time);
+               push_nttime(data,   24, &file->full_directory_info.write_time);
+               push_nttime(data,   32, &file->full_directory_info.change_time);
+               SBVAL(data,         40, file->full_directory_info.size);
+               SBVAL(data,         48, file->full_directory_info.alloc_size);
+               SIVAL(data,         56, file->full_directory_info.attrib);
+               SIVAL(data,         64, file->full_directory_info.ea_size);
+               trans2_append_data_string(req, trans, &file->full_directory_info.name, 
+                                         ofs + 60, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+
+       case RAW_SEARCH_NAME_INFO:
+               trans2_grow_data(req, trans, ofs + 12);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->name_info.file_index);
+               trans2_append_data_string(req, trans, &file->name_info.name, 
+                                         ofs + 8, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+
+       case RAW_SEARCH_BOTH_DIRECTORY_INFO:
+               trans2_grow_data(req, trans, ofs + 94);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->both_directory_info.file_index);
+               push_nttime(data,    8, &file->both_directory_info.create_time);
+               push_nttime(data,   16, &file->both_directory_info.access_time);
+               push_nttime(data,   24, &file->both_directory_info.write_time);
+               push_nttime(data,   32, &file->both_directory_info.change_time);
+               SBVAL(data,         40, file->both_directory_info.size);
+               SBVAL(data,         48, file->both_directory_info.alloc_size);
+               SIVAL(data,         56, file->both_directory_info.attrib);
+               SIVAL(data,         64, file->both_directory_info.ea_size);
+               SCVAL(data,         69, 0); /* reserved */
+               memset(data+70,0,24);
+               trans2_push_data_string(req, trans, 
+                                       68 + ofs, 70 + ofs, 
+                                       &file->both_directory_info.short_name, 
+                                       24, STR_UNICODE | STR_LEN8BIT);
+               trans2_append_data_string(req, trans, &file->both_directory_info.name, 
+                                         ofs + 60, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+
+       case RAW_SEARCH_261:
+               trans2_grow_data(req, trans, ofs + 80);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->level_261.file_index);
+               push_nttime(data,    8, &file->level_261.create_time);
+               push_nttime(data,   16, &file->level_261.access_time);
+               push_nttime(data,   24, &file->level_261.write_time);
+               push_nttime(data,   32, &file->level_261.change_time);
+               SBVAL(data,         40, file->level_261.size);
+               SBVAL(data,         48, file->level_261.alloc_size);
+               SIVAL(data,         56, file->level_261.attrib);
+               SIVAL(data,         64, file->level_261.ea_size);
+               SIVAL(data,         68, file->level_261.unknown[0]);
+               SIVAL(data,         72, file->level_261.unknown[1]);
+               SIVAL(data,         76, file->level_261.unknown[2]);
+               trans2_append_data_string(req, trans, &file->level_261.name, 
+                                         ofs + 60, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+
+       case RAW_SEARCH_262:
+               trans2_grow_data(req, trans, ofs + 104);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          4, file->level_262.file_index);
+               push_nttime(data,    8, &file->level_262.create_time);
+               push_nttime(data,   16, &file->level_262.access_time);
+               push_nttime(data,   24, &file->level_262.write_time);
+               push_nttime(data,   32, &file->level_262.change_time);
+               SBVAL(data,         40, file->level_262.size);
+               SBVAL(data,         48, file->level_262.alloc_size);
+               SIVAL(data,         56, file->level_262.attrib);
+               SIVAL(data,         64, file->level_262.ea_size);
+               SCVAL(data,         69, 0); /* reserved */
+               memset(data+70,0,24);
+               trans2_push_data_string(req, trans, 
+                                       68 + ofs, 70 + ofs, 
+                                       &file->level_262.short_name, 
+                                       24, STR_UNICODE | STR_LEN8BIT);
+               SIVAL(data,         94, file->level_262.unknown[0]);
+               SIVAL(data,         98, file->level_262.unknown[1]);
+               SSVAL(data,        102, 0); /* reserved? */
+               trans2_append_data_string(req, trans, &file->level_262.name, 
+                                         ofs + 60, 0);
+               data = trans->out.data.data + ofs;
+               SIVAL(data,          0, trans->out.data.length - ofs);
+               break;
+       }
+}
+
+/* callback function for trans2 findfirst/findnext */
+static BOOL find_callback(void *private, union smb_search_data *file)
+{
+       struct find_state *state = (struct find_state *)private;
+       struct smb_trans2 *trans = state->trans;
+       uint_t old_length;
+
+       old_length = trans->out.data.length;
+
+       find_fill_info(state->req, trans, state->level, file);
+
+       /* see if we have gone beyond the user specified maximum */
+       if (trans->out.data.length > trans->in.max_data) {
+               /* restore the old length and tell the backend to stop */
+               trans2_grow_data(state->req, trans, old_length);
+               return False;
+       }
+
+       state->last_entry_offset = old_length;  
+       return True;
+}
+
+
+/*
+  trans2 findfirst implementation
+*/
+static NTSTATUS trans2_findfirst(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_search_first search;
+       NTSTATUS status;
+       uint16 level;
+       char *param;
+       struct find_state state;
+
+       /* make sure we got all the parameters */
+       if (trans->in.params.length < 14) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       search.t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0);
+       search.t2ffirst.in.max_count     = SVAL(trans->in.params.data, 2);
+       search.t2ffirst.in.flags         = SVAL(trans->in.params.data, 4);
+       level                            = SVAL(trans->in.params.data, 6);
+       search.t2ffirst.in.storage_type  = IVAL(trans->in.params.data, 8);
+
+       trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2ffirst.in.pattern, 0);
+       if (search.t2ffirst.in.pattern == NULL) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       search.t2ffirst.level = (enum search_level)level;
+       if (search.t2ffirst.level >= RAW_SEARCH_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* setup the private state structure that the backend will give us in the callback */
+       state.req = req;
+       state.trans = trans;
+       state.level = search.t2ffirst.level;
+       state.last_entry_offset = 0;
+
+       /* setup for just a header in the reply */
+       trans2_setup_reply(req, trans, 10, 0, 0);
+
+       /* call the backend */
+       status = req->conn->ntvfs_ops->search_first(req, &search, &state, find_callback);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* fill in the findfirst reply header */
+       param = trans->out.params.data;
+       SSVAL(param, VWV(0), search.t2ffirst.out.handle);
+       SSVAL(param, VWV(1), search.t2ffirst.out.count);
+       SSVAL(param, VWV(2), search.t2ffirst.out.end_of_search);
+       SSVAL(param, VWV(3), 0);
+       SSVAL(param, VWV(4), state.last_entry_offset);
+       
+       return NT_STATUS_OK;
+}
+
+
+/*
+  trans2 findnext implementation
+*/
+static NTSTATUS trans2_findnext(struct request_context *req, struct smb_trans2 *trans)
+{
+       union smb_search_next search;
+       NTSTATUS status;
+       uint16 level;
+       char *param;
+       struct find_state state;
+
+       /* make sure we got all the parameters */
+       if (trans->in.params.length < 12) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       search.t2fnext.in.handle        = SVAL(trans->in.params.data, 0);
+       search.t2fnext.in.max_count     = SVAL(trans->in.params.data, 2);
+       level                           = SVAL(trans->in.params.data, 4);
+       search.t2fnext.in.resume_key    = IVAL(trans->in.params.data, 6);
+       search.t2fnext.in.flags         = SVAL(trans->in.params.data, 10);
+
+       trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2fnext.in.last_name, 0);
+       if (search.t2fnext.in.last_name == NULL) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       search.t2fnext.level = (enum search_level)level;
+       if (search.t2fnext.level >= RAW_SEARCH_GENERIC) {
+               return NT_STATUS_INVALID_LEVEL;
+       }
+
+       /* setup the private state structure that the backend will give us in the callback */
+       state.req = req;
+       state.trans = trans;
+       state.level = search.t2fnext.level;
+       state.last_entry_offset = 0;
+
+       /* setup for just a header in the reply */
+       trans2_setup_reply(req, trans, 8, 0, 0);
+
+       /* call the backend */
+       status = req->conn->ntvfs_ops->search_next(req, &search, &state, find_callback);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* fill in the findfirst reply header */
+       param = trans->out.params.data;
+       SSVAL(param, VWV(0), search.t2fnext.out.count);
+       SSVAL(param, VWV(1), search.t2fnext.out.end_of_search);
+       SSVAL(param, VWV(2), 0);
+       SSVAL(param, VWV(3), state.last_entry_offset);
+       
+       return NT_STATUS_OK;
+}
+
+
+/*
+  backend for trans2 requests
+*/
+static NTSTATUS trans2_backend(struct request_context *req, struct smb_trans2 *trans)
+{
+       if (req->conn->ntvfs_ops->trans2 != NULL) {
+               /* direct trans2 pass thru */
+               return req->conn->ntvfs_ops->trans2(req, trans);
+       }
+
+       /* must have at least one setup word */
+       if (trans->in.setup_count < 1) {
+               return NT_STATUS_FOOBAR;
+       }
+
+       /* the trans2 command is in setup[0] */
+       switch (trans->in.setup[0]) {
+       case TRANSACT2_FINDFIRST:
+               return trans2_findfirst(req, trans);
+       case TRANSACT2_FINDNEXT:
+               return trans2_findnext(req, trans);
+       case TRANSACT2_QPATHINFO:
+               return trans2_qpathinfo(req, trans);
+       case TRANSACT2_QFILEINFO:
+               return trans2_qfileinfo(req, trans);
+       case TRANSACT2_SETFILEINFO:
+               return trans2_setfileinfo(req, trans);
+       case TRANSACT2_SETPATHINFO:
+               return trans2_setpathinfo(req, trans);
+       case TRANSACT2_QFSINFO:
+               return trans2_qfsinfo(req, trans);
+       }
+
+       /* an unknown trans2 command */
+       return NT_STATUS_FOOBAR;
+}
+
+
+/****************************************************************************
+ Reply to an SMBtrans2 request
+****************************************************************************/
+void reply_trans2(struct request_context *req)
+{
+       struct smb_trans2 trans;
+       int i;
+       uint16 param_ofs, data_ofs;
+       uint16 param_count, data_count;
+       uint16 params_left, data_left;
+       uint16 param_total, data_total;
+       char *params, *data;
+       NTSTATUS status;
+
+       /* parse request */
+       if (req->in.wct < 14) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       param_total          = SVAL(req->in.vwv, VWV(0));
+       data_total           = SVAL(req->in.vwv, VWV(1));
+       trans.in.max_param   = SVAL(req->in.vwv, VWV(2));
+       trans.in.max_data    = SVAL(req->in.vwv, VWV(3));
+       trans.in.max_setup   = CVAL(req->in.vwv, VWV(4));
+       trans.in.flags       = SVAL(req->in.vwv, VWV(5));
+       trans.in.timeout     = IVAL(req->in.vwv, VWV(6));
+       param_count          = SVAL(req->in.vwv, VWV(9));
+       param_ofs            = SVAL(req->in.vwv, VWV(10));
+       data_count           = SVAL(req->in.vwv, VWV(11));
+       data_ofs             = SVAL(req->in.vwv, VWV(12));
+       trans.in.setup_count = CVAL(req->in.vwv, VWV(13));
+
+       if (req->in.wct != 14 + trans.in.setup_count) {
+               req_reply_dos_error(req, ERRSRV, ERRerror);
+               return;
+       }
+
+       /* parse out the setup words */
+       trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16));
+       if (!trans.in.setup) {
+               req_reply_error(req, NT_STATUS_NO_MEMORY);
+               return;
+       }
+       for (i=0;i<trans.in.setup_count;i++) {
+               trans.in.setup[i] = SVAL(req->in.vwv, VWV(14+i));
+       }
+
+       if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) ||
+           !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) {
+               req_reply_error(req, NT_STATUS_FOOBAR);
+               return;
+       }
+
+       /* is it a partial request? if so, then send a 'send more' message */
+       if (param_total > param_count ||
+           data_total > data_count) {
+               DEBUG(0,("REWRITE: not handling partial trans2 requests!\n"));
+               return;
+       }
+
+       /* its a full request, give it to the backend */
+       status = trans2_backend(req, &trans);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               req_reply_error(req, status);
+               return;
+       }
+
+       params_left = trans.out.params.length;
+       data_left   = trans.out.data.length;
+       params      = trans.out.params.data;
+       data        = trans.out.data.data;
+
+       req->control_flags |= REQ_CONTROL_PROTECTED;
+
+       /* we need to divide up the reply into chunks that fit into
+          the negotiated buffer size */
+       do {
+               uint16 this_data, this_param, max_bytes;
+               uint_t align1 = 1, align2 = (params_left ? 2 : 0);
+
+               req_setup_reply(req, 10 + trans.out.setup_count, 0);
+       
+               max_bytes = req_max_data(req) - (align1 + align2);
+
+               this_param = params_left;
+               if (this_param > max_bytes) {
+                       this_param = max_bytes;
+               }
+               max_bytes -= this_param;
+
+               this_data = data_left;
+               if (this_data > max_bytes) {
+                       this_data = max_bytes;
+               }
+
+               req_grow_data(req, this_param + this_data + (align1 + align2));
+
+               SSVAL(req->out.vwv, VWV(0), trans.out.params.length);
+               SSVAL(req->out.vwv, VWV(1), trans.out.data.length);
+               SSVAL(req->out.vwv, VWV(2), 0);
+
+               SSVAL(req->out.vwv, VWV(3), this_param);
+               SSVAL(req->out.vwv, VWV(4), align1 + PTR_DIFF(req->out.data, req->out.hdr));
+               SSVAL(req->out.vwv, VWV(5), PTR_DIFF(params, trans.out.params.data));
+
+               SSVAL(req->out.vwv, VWV(6), this_data);
+               SSVAL(req->out.vwv, VWV(7), align1 + align2 + 
+                     PTR_DIFF(req->out.data + this_param, req->out.hdr));
+               SSVAL(req->out.vwv, VWV(8), PTR_DIFF(data, trans.out.data.data));
+
+               SSVAL(req->out.vwv, VWV(9), trans.out.setup_count);
+               for (i=0;i<trans.out.setup_count;i++) {
+                       SSVAL(req->out.vwv, VWV(10+i), trans.out.setup[i]);
+               }
+
+               memset(req->out.data, 0, align1);
+               if (this_param != 0) {
+                       memcpy(req->out.data + align1, params, this_param);
+               }
+               memset(req->out.data+this_param+align1, 0, align2);
+               if (this_data != 0) {
+                       memcpy(req->out.data+this_param+align1+align2, data, this_data);
+               }
+
+               params_left -= this_param;
+               data_left -= this_data;
+               params += this_param;
+               data += this_data;
+
+               /* if this is the last chunk then the request can be destroyed */
+               if (params_left == 0 && data_left == 0) {
+                       req->control_flags &= ~REQ_CONTROL_PROTECTED;
+               }
+
+               req_send_reply(req);
+       } while (params_left != 0 || data_left != 0);
+}
diff --git a/source4/smbwrapper/.cvsignore b/source4/smbwrapper/.cvsignore
new file mode 100644 (file)
index 0000000..7835612
--- /dev/null
@@ -0,0 +1,8 @@
+*.po
+*.po32
+kernel_stat.h
+smbsh
+tst
+tst.c
+wrapper.h
+xstat.c
diff --git a/source4/smbwrapper/PORTING b/source4/smbwrapper/PORTING
new file mode 100644 (file)
index 0000000..884246d
--- /dev/null
@@ -0,0 +1,77 @@
+This describes how to port the smbwrapper portion of Samba to a new
+unix-like platform. Note that porting smbwrapper is considerably
+harder than porting Samba, for Samba you generally just need to run
+configure and recompile whereas for smbwrapper some extra effort is
+generally required.
+
+
+STEP 1
+------
+
+The first step is to work out how to create a shared library on your
+OS and how to compile C code to produce position independent object
+files (PIC files). You shoud be able to find this information in the
+man pages for your compiler and loader (ld). Then modify configure.in
+to give that information to Samba.
+
+
+STEP 2
+------
+
+The next step is to work out how to preload shared objects. On many
+systems this is done using a LD_PRELOAD environment variable. On
+others (shc as IRIX) it may use a _RTL_LIST variable.
+
+To make sure it works I suggest you create two C files like this:
+
+/* first C file */
+main()
+{
+ unlink("foo.txt");
+}
+
+/* second C file */
+#include <stdio.h>
+
+int unlink(char *fname)
+{
+       fprintf(stderr,"unlink(%s) called\n",fname);
+}
+
+
+then compile the first as an ordinary C program and the second as a
+shared library. Then use LD_PRELOAD to preload the resulting shared
+library. Then run the first program. It should print "unlink(foo.txt)
+called". If it doesn't then consult your man pages till you get it
+right.
+
+Once you work this out then edit smbwrapper/smbsh.in and add a section
+if necessary to correctly set the necessary preload options for your
+OS. 
+
+
+STEP 3
+------
+
+The next step is to work out how to make direct system calls. On most
+machines this will work without any source code changes to
+smbwrapper. To test that it does work create the following C program:
+
+#include <sys/syscall.h>
+main()
+{
+ syscall(SYS_write, 1, "hello world\n", 12);
+}
+
+and try to compile/run it. If it produces "hello world" then syscall()
+works as expected. If not then work out what needs to be changed and
+then make that change in realcalls.h. For example, on IRIX 6.4 the
+system call numbers are wrong and need to be fixed up by getting an
+offset right.
+
+
+STEP 4
+------
+
+Try compiling smbwrapper! Then test it. Then debug it. Simple really :)
+
diff --git a/source4/smbwrapper/README b/source4/smbwrapper/README
new file mode 100644 (file)
index 0000000..8d5c376
--- /dev/null
@@ -0,0 +1,94 @@
+This is a prelodable shared library that provides SMB client services
+for existing executables. Using this you can simulate a smb
+filesystem.
+
+*** This is code under development. Some things don't work yet ***
+
+Currently this code has been tested on:
+
+- Linux 2.0 with glibc2 (RH5.1)
+- Linux 2.1 with glibc2
+- Solaris 2.5.1 with gcc
+- Solaris 2.6 with gcc
+- SunOS 4.1.3 with gcc
+- IRIX 6.4 with cc
+- OSF1 with gcc
+
+
+It probably won't run on other systems without some porting. If you
+have a different system then see the file PORTING.
+
+To use it you need to do this:
+
+1) build smbwrapper.so using the command "make smbwrapper"
+3) run smbsh
+
+You will be asked for a username and password. After that you will be
+returned to a shell prompt. It is actually a subshell running with
+smbwrapper enabled. 
+
+Now try to access /smb/SERVER for some SMB server name and see what
+happens. If you use the -W option to set your workgroup or have
+workgroup set in your smb.conf then listing /smb/ should list all SMB
+servers in your workgroup.
+
+
+OPTIONS
+-------
+
+-U username
+   specify the username and optional password (as user%password)
+
+-d debug level
+ This is an integer that controls the internal debug level of smbw. It
+ defaults to 0, which means no debug info.
+
+-l logfile
+ The place where smbw debug logs are put. If this is not set then
+ stderr is used.
+
+-P prefix
+ The root of the SMB filesystem. This defaults to /smb/ but you can
+ set it to any name you like.
+
+-W workgroup
+ This is the workgroup used for browsing (ie. listing machines in the
+ /smb/ directory). It defaults to the one set in smb.conf.
+
+-R resolve order
+ This allows you to override the setting of the name resolve order
+ from smb.conf
+
+
+ATTRIBUTE MAPPING
+-----------------
+
+smbwrapper does an inverse attribute maping to what Samba does. This
+means that the archive bit appears as the user execute bit, the system
+bit appears as the group execute bit and the hidden bit appears as the
+other execute bit. You can control these with chmod. The mapping can
+be enabled an disabled using the normal smb.conf controls (ie. "map
+archive", "map system" and "map hidden").
+
+Read-only files appear as non-writeable by everyone. Writeable files
+appear as writeable by the current user.
+
+
+WHAT WORKS
+----------
+
+Things that I have tried and do seem to work include:
+
+  emacs, tar, ls, cmp, cp, rsync, du, cat, rm, mv, less, more, wc, head,
+  tail, bash, tcsh, mkdir, rmdir, vim, xedit, diff
+
+things that I know don't work:
+ anything executing from the share
+ anything that uses mmap
+ redirection within shells to smbsh files
+
+If you want to help with the development of this code then join the
+samba-technical mailing list.
+
+
diff --git a/source4/smbwrapper/realcalls.c b/source4/smbwrapper/realcalls.c
new file mode 100644 (file)
index 0000000..b64f4a4
--- /dev/null
@@ -0,0 +1,48 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions for calls that syscall() can't do
+   Copyright (C) Andrew Tridgell 1998
+   
+   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 "realcalls.h"
+
+#ifdef REPLACE_UTIME
+int real_utime(const char *name, struct utimbuf *buf)
+{
+       struct timeval tv[2];
+       
+       tv[0].tv_sec = buf->actime;
+       tv[0].tv_usec = 0;
+       tv[1].tv_sec = buf->modtime;
+       tv[1].tv_usec = 0;
+       
+       return real_utimes(name, &tv[0]);
+}
+#endif
+
+#ifdef REPLACE_UTIMES
+int real_utimes(const char *name, struct timeval tv[2])
+{
+       struct utimbuf buf;
+
+       buf.actime = tv[0].tv_sec;
+       buf.modtime = tv[1].tv_sec;
+       
+       return real_utime(name, &buf);
+}
+#endif
diff --git a/source4/smbwrapper/realcalls.h b/source4/smbwrapper/realcalls.h
new file mode 100644 (file)
index 0000000..6c230db
--- /dev/null
@@ -0,0 +1,263 @@
+/* 
+   Unix SMB/CIFS implementation.
+   defintions of syscall entries
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef IRIX
+/* amazingly, IRIX gets its own syscall numbers wrong! */
+#ifdef SYSVoffset
+#if (SYSVoffset == 1)
+#undef SYSVoffset
+#define SYSVoffset 1000
+#endif
+#endif
+#endif
+
+/* this file is partly derived from zlibc by Alain Knaff */
+
+#define real_access(fn, mode)          (syscall(SYS_access, (fn), (mode)))
+#define real_chdir(fn)                 (syscall(SYS_chdir, (fn)))
+#define real_chmod(fn, mode)           (syscall(SYS_chmod,(fn), (mode)))
+#define real_chown(fn, owner, group)   (syscall(SYS_chown,(fn),(owner),(group)))
+
+#ifdef SYS_getdents
+#define real_getdents(fd, dirp, count) (syscall(SYS_getdents, (fd), (dirp), (count)))
+#endif
+
+#define real_link(fn1, fn2)            (syscall(SYS_link, (fn1), (fn2)))
+
+#define real_open(fn,flags,mode)       (syscall(SYS_open, (fn), (flags), (mode)))
+
+#ifdef SYS_open64
+#define real_open64(fn,flags,mode)     (syscall(SYS_open64, (fn), (flags), (mode)))
+#elif HAVE__OPEN64
+#define real_open64(fn,flags,mode)     (_open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#elif HAVE___OPEN64
+#define real_open64(fn,flags,mode)     (__open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#endif
+
+#ifdef HAVE__FORK
+#define real_fork()                    (_fork())
+#elif HAVE___FORK
+#define real_fork()                    (__fork())
+#elif SYS_fork
+#define real_fork()            (syscall(SYS_fork))
+#endif
+
+#ifdef HAVE__OPENDIR
+#define real_opendir(fn)               (_opendir(fn))
+#elif SYS_opendir
+#define real_opendir(fn)               (syscall(SYS_opendir,(fn)))
+#elif HAVE___OPENDIR
+#define real_opendir(fn)               (__opendir(fn))
+#endif
+
+#ifdef HAVE__READDIR
+#define real_readdir(d)                (_readdir(d))
+#elif HAVE___READDIR
+#define real_readdir(d)                (__readdir(d))
+#elif SYS_readdir
+#define real_readdir(d)                (syscall(SYS_readdir,(d)))
+#endif
+
+#ifdef HAVE__CLOSEDIR
+#define real_closedir(d)               (_closedir(d))
+#elif SYS_closedir
+#define real_closedir(d)               (syscall(SYS_closedir,(d)))
+#elif HAVE___CLOSEDIR
+#define real_closedir(d)               (__closedir(d))
+#endif
+
+#ifdef HAVE__SEEKDIR
+#define real_seekdir(d,l)              (_seekdir(d,l))
+#elif SYS_seekdir
+#define real_seekdir(d,l)              (syscall(SYS_seekdir,(d),(l)))
+#elif HAVE___SEEKDIR
+#define real_seekdir(d,l)              (__seekdir(d,l))
+#else
+#define NO_SEEKDIR_WRAPPER
+#endif
+
+#ifdef HAVE__TELLDIR
+#define real_telldir(d)                (_telldir(d))
+#elif SYS_telldir
+#define real_telldir(d)                (syscall(SYS_telldir,(d)))
+#elif HAVE___TELLDIR
+#define real_telldir(d)                (__telldir(d))
+#endif
+
+#ifdef HAVE__DUP
+#define real_dup(d)                    (_dup(d))
+#elif SYS_dup
+#define real_dup(d)            (syscall(SYS_dup,(d)))
+#elif HAVE___DUP
+#define real_dup(d)                    (__dup(d))
+#endif
+
+#ifdef HAVE__DUP2
+#define real_dup2(d1,d2)               (_dup2(d1,d2))
+#elif SYS_dup2
+#define real_dup2(d1,d2)               (syscall(SYS_dup2,(d1),(d2)))
+#elif HAVE___DUP2
+#define real_dup2(d1,d2)               (__dup2(d1,d2))
+#endif
+
+#ifdef HAVE__GETCWD
+#define real_getcwd(b,s)               ((char *)_getcwd(b,s))
+#elif SYS_getcwd
+#define real_getcwd(b,s)               ((char *)syscall(SYS_getcwd,(b),(s)))
+#elif HAVE___GETCWD
+#define real_getcwd(b,s)               ((char *)__getcwd(b,s))
+#endif
+
+#ifdef HAVE__STAT
+#define real_stat(fn,st)               (_stat(fn,st))
+#elif SYS_stat
+#define real_stat(fn,st)               (syscall(SYS_stat,(fn),(st)))
+#elif HAVE___STAT
+#define real_stat(fn,st)               (__stat(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT
+#define real_lstat(fn,st)              (_lstat(fn,st))
+#elif SYS_lstat
+#define real_lstat(fn,st)              (syscall(SYS_lstat,(fn),(st)))
+#elif HAVE___LSTAT
+#define real_lstat(fn,st)              (__lstat(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT
+#define real_fstat(fd,st)              (_fstat(fd,st))
+#elif SYS_fstat
+#define real_fstat(fd,st)              (syscall(SYS_fstat,(fd),(st)))
+#elif HAVE___FSTAT
+#define real_fstat(fd,st)              (__fstat(fd,st))
+#endif
+
+#if defined(HAVE_SYS_ACL_H) && defined(HAVE__ACL)
+#define real_acl(fn,cmd,n,buf)                 (_acl(fn,cmd,n,buf))
+#elif SYS_acl
+#define real_acl(fn,cmd,n,buf)         (syscall(SYS_acl,(fn),(cmd),(n),(buf)))
+#elif HAVE___ACL
+#define real_acl(fn,cmd,n,buf)                 (__acl(fn,cmd,n,buf))
+#else
+#define NO_ACL_WRAPPER
+#endif
+
+#ifdef HAVE__FACL
+#define real_facl(fd,cmd,n,buf)                (_facl(fd,cmd,n,buf))
+#elif SYS_facl
+#define real_facl(fd,cmd,n,buf)                (syscall(SYS_facl,(fd),(cmd),(n),(buf)))
+#elif HAVE___FACL
+#define real_facl(fd,cmd,n,buf)                (__facl(fd,cmd,n,buf))
+#else
+#define NO_FACL_WRAPPER
+#endif
+
+
+#ifdef HAVE__STAT64
+#define real_stat64(fn,st)             (_stat64(fn,st))
+#elif HAVE___STAT64
+#define real_stat64(fn,st)             (__stat64(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT64
+#define real_lstat64(fn,st)                    (_lstat64(fn,st))
+#elif HAVE___LSTAT64
+#define real_lstat64(fn,st)                    (__lstat64(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT64
+#define real_fstat64(fd,st)                    (_fstat64(fd,st))
+#elif HAVE___FSTAT64
+#define real_fstat64(fd,st)                    (__fstat64(fd,st))
+#endif
+
+#ifdef HAVE__READDIR64
+#define real_readdir64(d)              (_readdir64(d))
+#elif HAVE___READDIR64
+#define real_readdir64(d)              (__readdir64(d))
+#endif
+
+#ifdef HAVE__LLSEEK
+#define real_llseek(fd,ofs,whence)             (_llseek(fd,ofs,whence))
+#elif HAVE___LLSEEK
+#define real_llseek(fd,ofs,whence)             (__llseek(fd,ofs,whence))
+#elif HAVE___SYS_LLSEEK
+#define real_llseek(fd,ofs,whence)             (__sys_llseek(fd,ofs,whence))
+#endif
+
+
+#ifdef HAVE__PREAD
+#define real_pread(fd,buf,size,ofs)                    (_pread(fd,buf,size,ofs))
+#elif HAVE___PREAD
+#define real_pread(fd,buf,size,ofs)                    (__pread(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PREAD64
+#define real_pread64(fd,buf,size,ofs)                  (_pread64(fd,buf,size,ofs))
+#elif HAVE___PREAD64
+#define real_pread64(fd,buf,size,ofs)                  (__pread64(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE
+#define real_pwrite(fd,buf,size,ofs)                   (_pwrite(fd,buf,size,ofs))
+#elif HAVE___PWRITE
+#define real_pwrite(fd,buf,size,ofs)                   (__pwrite(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE64
+#define real_pwrite64(fd,buf,size,ofs)                 (_pwrite64(fd,buf,size,ofs))
+#elif HAVE___PWRITE64
+#define real_pwrite64(fd,buf,size,ofs)                 (__pwrite64(fd,buf,size,ofs))
+#endif
+
+
+#define real_readlink(fn,buf,len)      (syscall(SYS_readlink, (fn), (buf), (len)))
+#define real_rename(fn1, fn2)          (syscall(SYS_rename, (fn1), (fn2)))
+#define real_symlink(fn1, fn2)         (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_read(fd, buf, count )     (syscall(SYS_read, (fd), (buf), (count)))
+#define real_lseek(fd, offset, whence) (syscall(SYS_lseek, (fd), (offset), (whence)))
+#define real_write(fd, buf, count )    (syscall(SYS_write, (fd), (buf), (count)))
+#define real_close(fd)                 (syscall(SYS_close, (fd)))
+#define real_fchdir(fd)                        (syscall(SYS_fchdir, (fd)))
+#define real_fcntl(fd,cmd,arg)         (syscall(SYS_fcntl, (fd), (cmd), (arg)))
+#define real_symlink(fn1, fn2)         (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_unlink(fn)                        (syscall(SYS_unlink, (fn)))
+#define real_rmdir(fn)                 (syscall(SYS_rmdir, (fn)))
+#define real_mkdir(fn, mode)           (syscall(SYS_mkdir, (fn), (mode)))
+
+#ifdef SYS_utime
+#define real_utime(fn, buf)            (syscall(SYS_utime, (fn), (buf)))
+#else
+#define REPLACE_UTIME 1
+#endif
+
+#ifdef SYS_utimes
+#define real_utimes(fn, buf)           (syscall(SYS_utimes, (fn), (buf)))
+#else
+#define REPLACE_UTIMES 1
+#endif
diff --git a/source4/smbwrapper/shared.c b/source4/smbwrapper/shared.c
new file mode 100644 (file)
index 0000000..b4cfcf7
--- /dev/null
@@ -0,0 +1,203 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions - shared variables
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+static int shared_fd;
+static char *variables;
+static int shared_size;
+
+/***************************************************** 
+setup the shared area 
+*******************************************************/
+void smbw_setup_shared(void)
+{
+       int fd;
+       pstring name, s;
+
+       slprintf(name,sizeof(name)-1, "%s/smbw.XXXXXX",tmpdir());
+
+       fd = smb_mkstemp(name);
+
+       if (fd == -1) goto failed;
+
+       unlink(name);
+
+       shared_fd = set_maxfiles(SMBW_MAX_OPEN);
+       
+       while (shared_fd && dup2(fd, shared_fd) != shared_fd) shared_fd--;
+
+       if (shared_fd == 0) goto failed;
+
+       close(fd);
+
+       DEBUG(4,("created shared_fd=%d\n", shared_fd));
+
+       slprintf(s,sizeof(s)-1,"%d", shared_fd);
+
+       setenv("SMBW_HANDLE", s, 1);
+
+       return;
+
+ failed:
+       perror("Failed to setup shared variable area ");
+       exit(1);
+}
+
+static int locked;
+
+/***************************************************** 
+lock the shared variable area
+*******************************************************/
+static void lockit(void)
+{
+       if (shared_fd == 0) {
+               char *p = getenv("SMBW_HANDLE");
+               if (!p) {
+                       DEBUG(0,("ERROR: can't get smbw shared handle\n"));
+                       exit(1);
+               }
+               shared_fd = atoi(p);
+       }
+       if (locked==0 && 
+           fcntl_lock(shared_fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) {
+               DEBUG(0,("ERROR: can't get smbw shared lock (%s)\n", strerror(errno)));
+               exit(1);
+       }
+       locked++;
+}
+
+/***************************************************** 
+unlock the shared variable area
+*******************************************************/
+static void unlockit(void)
+{
+       locked--;
+       if (locked == 0) {
+               fcntl_lock(shared_fd,SMB_F_SETLK,0,1,F_UNLCK);
+       }
+}
+
+
+/***************************************************** 
+get a variable from the shared area
+*******************************************************/
+char *smbw_getshared(const char *name)
+{
+       int i;
+       struct stat st;
+       char *var;
+
+       lockit();
+
+       /* maybe the area has changed */
+       if (fstat(shared_fd, &st)) goto failed;
+
+       if (st.st_size != shared_size) {
+               var = (char *)Realloc(variables, st.st_size);
+               if (!var) goto failed;
+               else variables = var;
+               shared_size = st.st_size;
+               lseek(shared_fd, 0, SEEK_SET);
+               if (read(shared_fd, variables, shared_size) != shared_size) {
+                       goto failed;
+               }
+       }
+
+       unlockit();
+
+       i=0;
+       while (i < shared_size) {
+               char *n, *v;
+               int l1, l2;
+
+               l1 = SVAL(&variables[i], 0);
+               l2 = SVAL(&variables[i], 2);
+
+               n = &variables[i+4];
+               v = &variables[i+4+l1];
+               i += 4+l1+l2;
+
+               if (strcmp(name,n)) {
+                       continue;
+               }
+               return v;
+       }
+
+       return NULL;
+
+ failed:
+       DEBUG(0,("smbw: shared variables corrupt (%s)\n", strerror(errno)));
+       exit(1);
+       return NULL;
+}
+
+
+
+/***************************************************** 
+set a variable in the shared area
+*******************************************************/
+void smbw_setshared(const char *name, const char *val)
+{
+       int l1, l2;
+       char *var;
+
+       /* we don't allow variable overwrite */
+       if (smbw_getshared(name)) return;
+
+       lockit();
+
+       l1 = strlen(name)+1;
+       l2 = strlen(val)+1;
+
+       var = (char *)Realloc(variables, shared_size + l1+l2+4);
+
+       if (!var) {
+               DEBUG(0,("out of memory in smbw_setshared\n"));
+               exit(1);
+       }
+       
+       variables = var;
+
+       SSVAL(&variables[shared_size], 0, l1);
+       SSVAL(&variables[shared_size], 2, l2);
+
+       pstrcpy(&variables[shared_size] + 4, name);
+       pstrcpy(&variables[shared_size] + 4 + l1, val);
+
+       shared_size += l1+l2+4;
+
+       lseek(shared_fd, 0, SEEK_SET);
+       if (write(shared_fd, variables, shared_size) != shared_size) {
+               DEBUG(0,("smbw_setshared failed (%s)\n", strerror(errno)));
+               exit(1);
+       }
+
+       unlockit();
+}
+
+
+/*****************************************************************
+return true if the passed fd is the SMBW_HANDLE
+*****************************************************************/  
+int smbw_shared_fd(int fd)
+{
+       return (shared_fd && shared_fd == fd);
+}
diff --git a/source4/smbwrapper/smbsh.c b/source4/smbwrapper/smbsh.c
new file mode 100644 (file)
index 0000000..221c6d8
--- /dev/null
@@ -0,0 +1,127 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions - frontend
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+static void smbsh_usage(void)
+{
+       printf("smbsh [options]\n\n");
+       printf(" -W workgroup\n");
+       printf(" -U username\n");
+       printf(" -P prefix\n");
+       printf(" -R resolve order\n");
+       printf(" -d debug level\n");
+       printf(" -l logfile\n");
+       printf(" -L libdir\n");
+       exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       char *p, *u;
+       const char *libd = dyn_BINDIR;
+       pstring line, wd;
+       int opt;
+       extern char *optarg;
+       extern int optind;
+
+       dbf = x_stdout;
+       smbw_setup_shared();
+
+       while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+               switch (opt) {
+               case 'L':
+                       libd = optarg;
+                       break;
+               case 'W':
+                       smbw_setshared("WORKGROUP", optarg);
+                       break;
+               case 'l':
+                       smbw_setshared("LOGFILE", optarg);
+                       break;
+               case 'P':
+                       smbw_setshared("PREFIX", optarg);
+                       break;
+               case 'd':
+                       smbw_setshared("DEBUG", optarg);
+                       break;
+               case 'U':
+                       p = strchr_m(optarg,'%');
+                       if (p) {
+                               *p=0;
+                               smbw_setshared("PASSWORD",p+1);
+                       }
+                       smbw_setshared("USER", optarg);
+                       break;
+               case 'R':
+                       smbw_setshared("RESOLVE_ORDER",optarg);
+                       break;
+
+               case 'h':
+               default:
+                       smbsh_usage();
+               }
+       }
+
+
+       if (!smbw_getshared("USER")) {
+               printf("Username: ");
+               u = fgets_slash(line, sizeof(line)-1, x_stdin);
+               smbw_setshared("USER", u);
+       }
+
+       if (!smbw_getshared("PASSWORD")) {
+               p = getpass("Password: ");
+               smbw_setshared("PASSWORD", p);
+       }
+
+       setenv("PS1", "smbsh$ ", 1);
+
+       sys_getwd(wd);
+
+       slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+
+       smbw_setshared(line, wd);
+
+       slprintf(line,sizeof(line)-1,"%s/smbwrapper.so", libd);
+       setenv("LD_PRELOAD", line, 1);
+
+       slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so", libd);
+
+       if (file_exist(line, NULL)) {
+               slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so:DEFAULT", libd);
+               setenv("_RLD_LIST", line, 1);
+               slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+               setenv("_RLDN32_LIST", line, 1);
+       } else {
+               slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+               setenv("_RLD_LIST", line, 1);
+       }
+
+       {
+       char *shellpath = getenv("SHELL");
+               if(shellpath)
+                       execl(shellpath,"smbsh",NULL);
+               else
+                       execl("/bin/sh","smbsh",NULL);
+       }
+       printf("launch failed!\n");
+       return 1;
+}      
diff --git a/source4/smbwrapper/smbsh.in b/source4/smbwrapper/smbsh.in
new file mode 100644 (file)
index 0000000..323f091
--- /dev/null
@@ -0,0 +1,54 @@
+#! /bin/sh
+
+SMBW_LIBDIR=${SMBW_LIBDIR-@builddir@/smbwrapper}
+
+if [ ! -f ${SMBW_LIBDIR}/smbwrapper.so ]; then
+    echo You need to set LIBDIR in smbsh
+    exit
+fi
+
+# a simple launcher for the smbwrapper.so preloadde library
+
+if [ x"${SMBW_USER+set}" != x"set" ]; then
+    echo username?
+    read user
+    SMBW_USER=$user
+    export SMBW_USER
+fi
+
+# this doesn't hide the password - we need a proper launch app for that
+if [ x"${SMBW_PASSWORD+set}" != x"set" ]; then
+    echo password?
+    read pass
+    SMBW_PASSWORD=$pass
+    export SMBW_PASSWORD
+fi
+
+PWD=`pwd`
+export PWD
+PS1='smbsh$ '
+export PS1
+
+
+host_os=@HOST_OS@
+
+case "$host_os" in
+        *irix*)
+               _RLDN32_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+               _RLD_LIST=$SMBW_LIBDIR/smbwrapper.32.so:DEFAULT
+               export _RLDN32_LIST
+               export _RLD_LIST
+               ;;
+       *osf*)
+               _RLD_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+               export _RLD_LIST
+               ;;              
+       *) 
+               LD_PRELOAD=$SMBW_LIBDIR/smbwrapper.so
+               export LD_PRELOAD
+               ;;
+esac
+
+echo starting smbwrapper on $host_os
+
+exec ${SMBW_SHELL-${SHELL-/bin/sh}} ${1+"$@"}
diff --git a/source4/smbwrapper/smbw.c b/source4/smbwrapper/smbw.c
new file mode 100644 (file)
index 0000000..fbc69b2
--- /dev/null
@@ -0,0 +1,1554 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions
+   Copyright (C) Andrew Tridgell 1998
+   
+   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 "realcalls.h"
+
+pstring smbw_cwd;
+
+static struct smbw_file *smbw_files;
+static struct smbw_server *smbw_srvs;
+
+struct bitmap *smbw_file_bmap;
+
+fstring smbw_prefix = SMBW_PREFIX;
+
+int smbw_busy=0;
+
+/* needs to be here because of dumb include files on some systems */
+int creat_bits = O_WRONLY|O_CREAT|O_TRUNC;
+
+
+/***************************************************** 
+initialise structures
+*******************************************************/
+void smbw_init(void)
+{
+       extern BOOL in_client;
+       static int initialised;
+       char *p;
+       int eno;
+       pstring line;
+
+       if (initialised) return;
+       initialised = 1;
+
+       eno = errno;
+
+       smbw_busy++;
+
+       DEBUGLEVEL = 0;
+       setup_logging("smbsh",True);
+
+       dbf = x_stderr;
+
+       if ((p=smbw_getshared("LOGFILE"))) {
+               dbf = sys_fopen(p, "a");
+       }
+
+       smbw_file_bmap = bitmap_allocate(SMBW_MAX_OPEN);
+       if (!smbw_file_bmap) {
+               exit(1);
+       }
+
+       in_client = True;
+
+       load_interfaces();
+
+       if ((p=smbw_getshared("SERVICESF"))) {
+               pstrcpy(dyn_CONFIGFILE, p);
+       }
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+
+       if (!init_names())
+               exit(1);
+
+       if ((p=smbw_getshared("DEBUG"))) {
+               DEBUGLEVEL = atoi(p);
+       }
+
+       if ((p=smbw_getshared("RESOLVE_ORDER"))) {
+               lp_set_name_resolve_order(p);
+       }
+
+       if ((p=smbw_getshared("PREFIX"))) {
+               slprintf(smbw_prefix,sizeof(fstring)-1, "/%s/", p);
+               all_string_sub(smbw_prefix,"//", "/", 0);
+               DEBUG(2,("SMBW_PREFIX is %s\n", smbw_prefix));
+       }
+
+       slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+       
+       p = smbw_getshared(line);
+       if (!p) {
+               sys_getwd(smbw_cwd);
+       }
+       pstrcpy(smbw_cwd, p);
+       DEBUG(4,("Initial cwd is %s\n", smbw_cwd));
+
+       smbw_busy--;
+
+       set_maxfiles(SMBW_MAX_OPEN);
+
+       BlockSignals(True,SIGPIPE);
+
+       errno = eno;
+}
+
+/***************************************************** 
+determine if a file descriptor is a smb one
+*******************************************************/
+int smbw_fd(int fd)
+{
+       if (smbw_busy) return 0;
+       return smbw_file_bmap && bitmap_query(smbw_file_bmap, fd);
+}
+
+/***************************************************** 
+determine if a file descriptor is an internal smbw fd
+*******************************************************/
+int smbw_local_fd(int fd)
+{
+       struct smbw_server *srv;
+       
+       smbw_init();
+
+       if (smbw_busy) return 0;
+       if (smbw_shared_fd(fd)) return 1;
+
+       for (srv=smbw_srvs;srv;srv=srv->next) {
+               if (srv->cli.fd == fd) return 1;
+       }
+
+       return 0;
+}
+
+/***************************************************** 
+a crude inode number generator
+*******************************************************/
+ino_t smbw_inode(const char *name)
+{
+       if (!*name) return 2;
+       return (ino_t)str_checksum(name);
+}
+
+/***************************************************** 
+remove redundent stuff from a filename
+*******************************************************/
+void clean_fname(char *name)
+{
+       char *p, *p2;
+       int l;
+       int modified = 1;
+
+       if (!name) return;
+
+       while (modified) {
+               modified = 0;
+
+               DEBUG(5,("cleaning %s\n", name));
+
+               if ((p=strstr(name,"/./"))) {
+                       modified = 1;
+                       while (*p) {
+                               p[0] = p[2];
+                               p++;
+                       }
+               }
+
+               if ((p=strstr(name,"//"))) {
+                       modified = 1;
+                       while (*p) {
+                               p[0] = p[1];
+                               p++;
+                       }
+               }
+
+               if (strcmp(name,"/../")==0) {
+                       modified = 1;
+                       name[1] = 0;
+               }
+
+               if ((p=strstr(name,"/../"))) {
+                       modified = 1;
+                       for (p2=(p>name?p-1:p);p2>name;p2--) {
+                               if (p2[0] == '/') break;
+                       }
+                       while (*p2) {
+                               p2[0] = p2[3];
+                               p2++;
+                       }
+               }
+
+               if (strcmp(name,"/..")==0) {
+                       modified = 1;
+                       name[1] = 0;
+               }
+
+               l = strlen(name);
+               p = l>=3?(name+l-3):name;
+               if (strcmp(p,"/..")==0) {
+                       modified = 1;
+                       for (p2=p-1;p2>name;p2--) {
+                               if (p2[0] == '/') break;
+                       }
+                       if (p2==name) {
+                               p[0] = '/';
+                               p[1] = 0;
+                       } else {
+                               p2[0] = 0;
+                       }
+               }
+
+               l = strlen(name);
+               p = l>=2?(name+l-2):name;
+               if (strcmp(p,"/.")==0) {
+                       if (p == name) {
+                               p[1] = 0;
+                       } else {
+                               p[0] = 0;
+                       }
+               }
+
+               if (strncmp(p=name,"./",2) == 0) {      
+                       modified = 1;
+                       do {
+                               p[0] = p[2];
+                       } while (*p++);
+               }
+
+               l = strlen(p=name);
+               if (l > 1 && p[l-1] == '/') {
+                       modified = 1;
+                       p[l-1] = 0;
+               }
+       }
+}
+
+
+
+/***************************************************** 
+find a workgroup (any workgroup!) that has a master 
+browser on the local network
+*******************************************************/
+static char *smbw_find_workgroup(void)
+{
+       fstring server;
+       char *p;
+       struct in_addr *ip_list = NULL;
+       int count = 0;
+       int i;
+
+       /* first off see if an existing workgroup name exists */
+       p = smbw_getshared("WORKGROUP");
+       if (!p) p = lp_workgroup();
+       
+       slprintf(server, sizeof(server), "%s#1D", p);
+       if (smbw_server(server, "IPC$")) return p;
+
+       /* go looking for workgroups */
+       if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+               DEBUG(1,("No workgroups found!"));
+               return p;
+       }
+
+       for (i=0;i<count;i++) {
+               static fstring name;
+               if (name_status_find("*", 0, 0x1d, ip_list[i], name)) {
+                       slprintf(server, sizeof(server), "%s#1D", name);
+                       if (smbw_server(server, "IPC$")) {
+                               smbw_setshared("WORKGROUP", name);
+                               SAFE_FREE(ip_list);
+                               return name;
+                       }
+               }
+       }
+
+       SAFE_FREE(ip_list);
+
+       return p;
+}
+
+/***************************************************** 
+parse a smb path into its components. 
+server is one of
+  1) the name of the SMB server
+  2) WORKGROUP#1D for share listing
+  3) WORKGROUP#__ for workgroup listing
+share is the share on the server to query
+path is the SMB path on the server
+return the full path (ie. add cwd if needed)
+*******************************************************/
+char *smbw_parse_path(const char *fname, char *server, char *share, char *path)
+{
+       static pstring s;
+       char *p;
+       int len;
+       fstring workgroup;
+
+       /* add cwd if necessary */
+       if (fname[0] != '/') {
+               slprintf(s, sizeof(s), "%s/%s", smbw_cwd, fname);
+       } else {
+               pstrcpy(s, fname);
+       }
+       clean_fname(s);
+
+       /* see if it has the right prefix */
+       len = strlen(smbw_prefix)-1;
+       if (strncmp(s,smbw_prefix,len) || 
+           (s[len] != '/' && s[len] != 0)) return s;
+
+       /* ok, its for us. Now parse out the workgroup, share etc. */
+       p = s+len;
+       if (*p == '/') p++;
+       if (!next_token(&p, workgroup, "/", sizeof(fstring))) {
+               /* we're in /smb - give a list of workgroups */
+               slprintf(server,sizeof(fstring), "%s#01", smbw_find_workgroup());
+               fstrcpy(share,"IPC$");
+               pstrcpy(path,"");
+               return s;
+       }
+
+       if (!next_token(&p, server, "/", sizeof(fstring))) {
+               /* we are in /smb/WORKGROUP */
+               slprintf(server,sizeof(fstring), "%s#1D", workgroup);
+               fstrcpy(share,"IPC$");
+               pstrcpy(path,"");
+       }
+
+       if (!next_token(&p, share, "/", sizeof(fstring))) {
+               /* we are in /smb/WORKGROUP/SERVER */
+               fstrcpy(share,"IPC$");
+               pstrcpy(path,"");
+       }
+
+       pstrcpy(path, p);
+
+       all_string_sub(path, "/", "\\", 0);
+
+       return s;
+}
+
+/***************************************************** 
+determine if a path name (possibly relative) is in the 
+smb name space
+*******************************************************/
+int smbw_path(const char *path)
+{
+       fstring server, share;
+       pstring s;
+       char *cwd;
+       int len;
+
+       if(!path)
+               return 0;
+
+       /* this is needed to prevent recursion with the BSD malloc which
+          opens /etc/malloc.conf on the first call */
+       if (strncmp(path,"/etc/", 5) == 0) {
+               return 0;
+       }
+
+       smbw_init();
+
+       len = strlen(smbw_prefix)-1;
+
+       if (path[0] == '/' && strncmp(path,smbw_prefix,len)) {
+               return 0;
+       }
+
+       if (smbw_busy) return 0;
+
+       DEBUG(3,("smbw_path(%s)\n", path));
+
+       cwd = smbw_parse_path(path, server, share, s);
+
+       if (strncmp(cwd,smbw_prefix,len) == 0 &&
+           (cwd[len] == '/' || cwd[len] == 0)) {
+               return 1;
+       }
+
+       return 0;
+}
+
+/***************************************************** 
+return a unix errno from a SMB error pair
+*******************************************************/
+int smbw_errno(struct cli_state *c)
+{
+       return cli_errno(c);
+}
+
+/* Return a username and password given a server and share name */
+
+void get_envvar_auth_data(char *server, char *share, char **workgroup,
+                         char **username, char **password)
+{
+       /* Fall back to shared memory/environment variables */
+
+       *username = smbw_getshared("USER");
+       if (!*username) *username = getenv("USER");
+       if (!*username) *username = "guest";
+
+       *workgroup = smbw_getshared("WORKGROUP");
+       if (!*workgroup) *workgroup = lp_workgroup();
+
+       *password = smbw_getshared("PASSWORD");
+       if (!*password) *password = "";
+}
+
+static smbw_get_auth_data_fn get_auth_data_fn = get_envvar_auth_data;
+
+/*****************************************************
+set the get auth data function
+******************************************************/
+void smbw_set_auth_data_fn(smbw_get_auth_data_fn fn)
+{
+       get_auth_data_fn = fn;
+}
+
+/***************************************************** 
+return a connection to a server (existing or new)
+*******************************************************/
+struct smbw_server *smbw_server(char *server, char *share)
+{
+       struct smbw_server *srv=NULL;
+       struct cli_state c;
+       char *username;
+       char *password;
+       char *workgroup;
+       struct nmb_name called, calling;
+       char *p, *server_n = server;
+       fstring group;
+       pstring ipenv;
+       struct in_addr ip;
+
+        zero_ip(&ip);
+       ZERO_STRUCT(c);
+
+       get_auth_data_fn(server, share, &workgroup, &username, &password);
+
+       /* try to use an existing connection */
+       for (srv=smbw_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;
+       }
+
+       make_nmb_name(&calling, lp_netbios_name(), 0x0);
+       make_nmb_name(&called , server, 0x20);
+
+       DEBUG(4,("server_n=[%s] server=[%s]\n", server_n, server));
+
+       if ((p=strchr_m(server_n,'#')) && 
+           (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
+               struct in_addr sip;
+               pstring s;
+
+               fstrcpy(group, server_n);
+               p = strchr_m(group,'#');
+               *p = 0;
+               
+               /* cache the workgroup master lookup */
+               slprintf(s,sizeof(s)-1,"MASTER_%s", group);
+               if (!(server_n = smbw_getshared(s))) {
+                       if (!find_master_ip(group, &sip)) {
+                               errno = ENOENT;
+                               return NULL;
+                       }
+                       fstrcpy(group, inet_ntoa(sip));
+                       server_n = group;
+                       smbw_setshared(s,server_n);
+               }
+       }
+
+       DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+ again:
+       slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
+
+        zero_ip(&ip);
+       if ((p=smbw_getshared(ipenv))) {
+               ip = *(interpret_addr2(p));
+       }
+
+       /* 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 = smbw_errno(&c);
+               cli_shutdown(&c);
+               return NULL;
+       }
+
+       smbw_setshared(ipenv,inet_ntoa(ip));
+       
+       DEBUG(4,(" tconx ok\n"));
+
+       srv = (struct smbw_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;
+       }
+
+       /* some programs play with file descriptors fairly intimately. We
+          try to get out of the way by duping to a high fd number */
+       if (fcntl(SMBW_CLI_FD + srv->cli.fd, F_GETFD) && errno == EBADF) {
+               if (dup2(srv->cli.fd,SMBW_CLI_FD+srv->cli.fd) == 
+                   srv->cli.fd+SMBW_CLI_FD) {
+                       close(srv->cli.fd);
+                       srv->cli.fd += SMBW_CLI_FD;
+               }
+       }
+
+       DLIST_ADD(smbw_srvs, srv);
+
+       return srv;
+
+ failed:
+       cli_shutdown(&c);
+       if (!srv) return NULL;
+
+       SAFE_FREE(srv->server_name);
+       SAFE_FREE(srv->share_name);
+       SAFE_FREE(srv);
+       return NULL;
+}
+
+
+/***************************************************** 
+map a fd to a smbw_file structure
+*******************************************************/
+struct smbw_file *smbw_file(int fd)
+{
+       struct smbw_file *file;
+
+       for (file=smbw_files;file;file=file->next) {
+               if (file->fd == fd) return file;
+       }
+       return NULL;
+}
+
+/***************************************************** 
+a wrapper for open()
+*******************************************************/
+int smbw_open(const char *fname, int flags, mode_t mode)
+{
+       fstring server, share;
+       pstring path;
+       struct smbw_server *srv=NULL;
+       int eno=0, fd = -1;
+       struct smbw_file *file=NULL;
+
+       smbw_init();
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_busy++;    
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (path[strlen(path)-1] == '\\') {
+               fd = -1;
+       } else {
+               fd = cli_open(&srv->cli, path, flags, DENY_NONE);
+       }
+       if (fd == -1) {
+               /* it might be a directory. Maybe we should use chkpath? */
+               eno = smbw_errno(&srv->cli);
+               fd = smbw_dir_open(fname);
+               if (fd == -1) errno = eno;
+               smbw_busy--;
+               return fd;
+       }
+
+       file = (struct smbw_file *)malloc(sizeof(*file));
+       if (!file) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       ZERO_STRUCTP(file);
+
+       file->f = (struct smbw_filedes *)malloc(sizeof(*(file->f)));
+       if (!file->f) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       ZERO_STRUCTP(file->f);
+
+       file->f->cli_fd = fd;
+       file->f->fname = strdup(path);
+       if (!file->f->fname) {
+               errno = ENOMEM;
+               goto failed;
+       }
+       file->srv = srv;
+       file->fd = open(SMBW_DUMMY, O_WRONLY);
+       if (file->fd == -1) {
+               errno = EMFILE;
+               goto failed;
+       }
+
+       if (bitmap_query(smbw_file_bmap, file->fd)) {
+               DEBUG(0,("ERROR: fd used in smbw_open\n"));
+               errno = EIO;
+               goto failed;
+       }
+
+       file->f->ref_count=1;
+
+       bitmap_set(smbw_file_bmap, file->fd);
+
+       DLIST_ADD(smbw_files, file);
+
+       DEBUG(4,("opened %s\n", fname));
+
+       smbw_busy--;
+       return file->fd;
+
+ failed:
+       if (fd != -1) {
+               cli_close(&srv->cli, fd);
+       }
+       if (file) {
+               if (file->f) {
+                       SAFE_FREE(file->f->fname);
+                       SAFE_FREE(file->f);
+               }
+               SAFE_FREE(file);
+       }
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for pread()
+*******************************************************/
+ssize_t smbw_pread(int fd, void *buf, size_t count, off_t ofs)
+{
+       struct smbw_file *file;
+       int ret;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               smbw_busy--;
+               return -1;
+       }
+       
+       ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, ofs, count);
+
+       if (ret == -1) {
+               errno = smbw_errno(&file->srv->cli);
+               smbw_busy--;
+               return -1;
+       }
+
+       smbw_busy--;
+       return ret;
+}
+
+/***************************************************** 
+a wrapper for read()
+*******************************************************/
+ssize_t smbw_read(int fd, void *buf, size_t count)
+{
+       struct smbw_file *file;
+       int ret;
+
+       DEBUG(4,("smbw_read(%d, %d)\n", fd, (int)count));
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               smbw_busy--;
+               return -1;
+       }
+       
+       ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, 
+                      file->f->offset, count);
+
+       if (ret == -1) {
+               errno = smbw_errno(&file->srv->cli);
+               smbw_busy--;
+               return -1;
+       }
+
+       file->f->offset += ret;
+       
+       DEBUG(4,(" -> %d\n", ret));
+
+       smbw_busy--;
+       return ret;
+}
+
+       
+
+/***************************************************** 
+a wrapper for write()
+*******************************************************/
+ssize_t smbw_write(int fd, void *buf, size_t count)
+{
+       struct smbw_file *file;
+       int ret;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               smbw_busy--;
+               return -1;
+       }
+       
+       ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, file->f->offset, count);
+
+       if (ret == -1) {
+               errno = smbw_errno(&file->srv->cli);
+               smbw_busy--;
+               return -1;
+       }
+
+       file->f->offset += ret;
+
+       smbw_busy--;
+       return ret;
+}
+
+/***************************************************** 
+a wrapper for pwrite()
+*******************************************************/
+ssize_t smbw_pwrite(int fd, void *buf, size_t count, off_t ofs)
+{
+       struct smbw_file *file;
+       int ret;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               smbw_busy--;
+               return -1;
+       }
+       
+       ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, ofs, count);
+
+       if (ret == -1) {
+               errno = smbw_errno(&file->srv->cli);
+               smbw_busy--;
+               return -1;
+       }
+
+       smbw_busy--;
+       return ret;
+}
+
+/***************************************************** 
+a wrapper for close()
+*******************************************************/
+int smbw_close(int fd)
+{
+       struct smbw_file *file;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               int ret = smbw_dir_close(fd);
+               smbw_busy--;
+               return ret;
+       }
+       
+       if (file->f->ref_count == 1 &&
+           !cli_close(&file->srv->cli, file->f->cli_fd)) {
+               errno = smbw_errno(&file->srv->cli);
+               smbw_busy--;
+               return -1;
+       }
+
+
+       bitmap_clear(smbw_file_bmap, file->fd);
+       close(file->fd);
+       
+       DLIST_REMOVE(smbw_files, file);
+
+       file->f->ref_count--;
+       if (file->f->ref_count == 0) {
+               SAFE_FREE(file->f->fname);
+               SAFE_FREE(file->f);
+       }
+       ZERO_STRUCTP(file);
+       SAFE_FREE(file);
+       
+       smbw_busy--;
+
+       return 0;
+}
+
+
+/***************************************************** 
+a wrapper for fcntl()
+*******************************************************/
+int smbw_fcntl(int fd, int cmd, long arg)
+{
+       return 0;
+}
+
+
+/***************************************************** 
+a wrapper for access()
+*******************************************************/
+int smbw_access(const char *name, int mode)
+{
+       struct stat st;
+
+       DEBUG(4,("smbw_access(%s, 0x%x)\n", name, mode));
+
+       if (smbw_stat(name, &st)) return -1;
+
+       if (((mode & R_OK) && !(st.st_mode & S_IRUSR)) ||
+           ((mode & W_OK) && !(st.st_mode & S_IWUSR)) ||
+           ((mode & X_OK) && !(st.st_mode & S_IXUSR))) {
+               errno = EACCES;
+               return -1;
+       }
+       
+       return 0;
+}
+
+/***************************************************** 
+a wrapper for realink() - needed for correct errno setting
+*******************************************************/
+int smbw_readlink(const char *path, char *buf, size_t bufsize)
+{
+       struct stat st;
+       int ret;
+
+       ret = smbw_stat(path, &st);
+       if (ret != 0) {
+               DEBUG(4,("readlink(%s) failed\n", path));
+               return -1;
+       }
+       
+       /* it exists - say it isn't a link */
+       DEBUG(4,("readlink(%s) not a link\n", path));
+
+       errno = EINVAL;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for unlink()
+*******************************************************/
+int smbw_unlink(const char *fname)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+               int job = smbw_stat_printjob(srv, path, NULL, NULL);
+               if (job == -1) {
+                       goto failed;
+               }
+               if (cli_printjob_del(&srv->cli, job) != 0) {
+                       goto failed;
+               }
+       } else if (!cli_unlink(&srv->cli, path)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for rename()
+*******************************************************/
+int smbw_rename(const char *oldname, const char *newname)
+{
+       struct smbw_server *srv;
+       fstring server1, share1;
+       pstring path1;
+       fstring server2, share2;
+       pstring path2;
+
+       if (!oldname || !newname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       DEBUG(4,("smbw_rename(%s,%s)\n", oldname, newname));
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(oldname, server1, share1, path1);
+       smbw_parse_path(newname, server2, share2, path2);
+
+       if (strcmp(server1, server2) || strcmp(share1, share2)) {
+               /* can't cross filesystems */
+               errno = EXDEV;
+               return -1;
+       }
+
+       /* get a connection to the server */
+       srv = smbw_server(server1, share1);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (!cli_rename(&srv->cli, path1, path2)) {
+               int eno = smbw_errno(&srv->cli);
+               if (eno != EEXIST ||
+                   !cli_unlink(&srv->cli, path2) ||
+                   !cli_rename(&srv->cli, path1, path2)) {
+                       errno = eno;
+                       goto failed;
+               }
+       }
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for utime and utimes
+*******************************************************/
+static int smbw_settime(const char *fname, time_t t)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+       uint16 mode;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+
+       if (!cli_setatr(&srv->cli, path, mode, t)) {
+               /* some servers always refuse directory changes */
+               if (!(mode & aDIR)) {
+                       errno = smbw_errno(&srv->cli);
+                       goto failed;
+               }
+       }
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+/***************************************************** 
+a wrapper for utime 
+*******************************************************/
+int smbw_utime(const char *fname, void *buf)
+{
+       struct utimbuf *tbuf = (struct utimbuf *)buf;
+       return smbw_settime(fname, tbuf?tbuf->modtime:time(NULL));
+}
+
+/***************************************************** 
+a wrapper for utime 
+*******************************************************/
+int smbw_utimes(const char *fname, void *buf)
+{
+       struct timeval *tbuf = (struct timeval *)buf;
+       return smbw_settime(fname, tbuf?tbuf->tv_sec:time(NULL));
+}
+
+
+/***************************************************** 
+a wrapper for chown()
+*******************************************************/
+int smbw_chown(const char *fname, uid_t owner, gid_t group)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+       uint16 mode;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+       
+       /* assume success */
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+/***************************************************** 
+a wrapper for chmod()
+*******************************************************/
+int smbw_chmod(const char *fname, mode_t newmode)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+       uint32 mode;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       mode = 0;
+
+       if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+       if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+       if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+       if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+       if (!cli_setatr(&srv->cli, path, mode, 0)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+       
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+/***************************************************** 
+a wrapper for lseek()
+*******************************************************/
+off_t smbw_lseek(int fd, off_t offset, int whence)
+{
+       struct smbw_file *file;
+       size_t size;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               off_t ret = smbw_dir_lseek(fd, offset, whence);
+               smbw_busy--;
+               return ret;
+       }
+
+       switch (whence) {
+       case SEEK_SET:
+               file->f->offset = offset;
+               break;
+       case SEEK_CUR:
+               file->f->offset += offset;
+               break;
+       case SEEK_END:
+               if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd, 
+                                  NULL, &size, NULL, NULL, NULL, 
+                                  NULL, NULL) &&
+                   !cli_getattrE(&file->srv->cli, file->f->cli_fd, 
+                                 NULL, &size, NULL, NULL, NULL)) {
+                       errno = EINVAL;
+                       smbw_busy--;
+                       return -1;
+               }
+               file->f->offset = size + offset;
+               break;
+       }
+
+       smbw_busy--;
+       return file->f->offset;
+}
+
+
+/***************************************************** 
+a wrapper for dup()
+*******************************************************/
+int smbw_dup(int fd)
+{
+       int fd2;
+       struct smbw_file *file, *file2;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               goto failed;
+       }
+
+       fd2 = dup(file->fd);
+       if (fd2 == -1) {
+               goto failed;
+       }
+
+       if (bitmap_query(smbw_file_bmap, fd2)) {
+               DEBUG(0,("ERROR: fd already open in dup!\n"));
+               errno = EIO;
+               goto failed;
+       }
+
+       file2 = (struct smbw_file *)malloc(sizeof(*file2));
+       if (!file2) {
+               close(fd2);
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       ZERO_STRUCTP(file2);
+
+       *file2 = *file;
+       file2->fd = fd2;
+
+       file->f->ref_count++;
+
+       bitmap_set(smbw_file_bmap, fd2);
+       
+       DLIST_ADD(smbw_files, file2);
+       
+       smbw_busy--;
+       return fd2;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for dup2()
+*******************************************************/
+int smbw_dup2(int fd, int fd2)
+{
+       struct smbw_file *file, *file2;
+
+       smbw_busy++;
+
+       file = smbw_file(fd);
+       if (!file) {
+               errno = EBADF;
+               goto failed;
+       }
+
+       if (bitmap_query(smbw_file_bmap, fd2)) {
+               DEBUG(0,("ERROR: fd already open in dup2!\n"));
+               errno = EIO;
+               goto failed;
+       }
+
+       if (dup2(file->fd, fd2) != fd2) {
+               goto failed;
+       }
+
+       file2 = (struct smbw_file *)malloc(sizeof(*file2));
+       if (!file2) {
+               close(fd2);
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       ZERO_STRUCTP(file2);
+
+       *file2 = *file;
+       file2->fd = fd2;
+
+       file->f->ref_count++;
+
+       bitmap_set(smbw_file_bmap, fd2);
+       
+       DLIST_ADD(smbw_files, file2);
+       
+       smbw_busy--;
+       return fd2;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+close a connection to a server
+*******************************************************/
+static void smbw_srv_close(struct smbw_server *srv)
+{
+       smbw_busy++;
+
+       cli_shutdown(&srv->cli);
+
+       SAFE_FREE(srv->server_name);
+       SAFE_FREE(srv->share_name);
+
+       DLIST_REMOVE(smbw_srvs, srv);
+
+       ZERO_STRUCTP(srv);
+
+       SAFE_FREE(srv);
+       
+       smbw_busy--;
+}
+
+/***************************************************** 
+when we fork we have to close all connections and files
+in the child
+*******************************************************/
+int smbw_fork(void)
+{
+       pid_t child;
+       int p[2];
+       char c=0;
+       pstring line;
+
+       struct smbw_file *file, *next_file;
+       struct smbw_server *srv, *next_srv;
+
+       if (pipe(p)) return real_fork();
+
+       child = real_fork();
+
+       if (child) {
+               /* block the parent for a moment until the sockets are
+                   closed */
+               close(p[1]);
+               read(p[0], &c, 1);
+               close(p[0]);
+               return child;
+       }
+
+       close(p[0]);
+
+       /* close all files */
+       for (file=smbw_files;file;file=next_file) {
+               next_file = file->next;
+               close(file->fd);
+       }
+
+       /* close all server connections */
+       for (srv=smbw_srvs;srv;srv=next_srv) {
+               next_srv = srv->next;
+               smbw_srv_close(srv);
+       }
+
+       slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+       smbw_setshared(line,smbw_cwd);
+
+       /* unblock the parent */
+       write(p[1], &c, 1);
+       close(p[1]);
+
+       /* and continue in the child */
+       return 0;
+}
+
+#ifndef NO_ACL_WRAPPER
+/***************************************************** 
+say no to acls
+*******************************************************/
+ int smbw_acl(const char *pathp, int cmd, int nentries, aclent_t *aclbufp)
+{
+       if (cmd == GETACL || cmd == GETACLCNT) return 0;
+       errno = ENOSYS;
+       return -1;
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+/***************************************************** 
+say no to acls
+*******************************************************/
+ int smbw_facl(int fd, int cmd, int nentries, aclent_t *aclbufp)
+{
+       if (cmd == GETACL || cmd == GETACLCNT) return 0;
+       errno = ENOSYS;
+       return -1;
+}
+#endif
+
+#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
+#ifdef HAVE_STAT64
+/* this can't be in wrapped.c because of include conflicts */
+ void stat64_convert(struct stat *st, struct stat64 *st64)
+{
+       st64->st_size = st->st_size;
+       st64->st_mode = st->st_mode;
+       st64->st_ino = st->st_ino;
+       st64->st_dev = st->st_dev;
+       st64->st_rdev = st->st_rdev;
+       st64->st_nlink = st->st_nlink;
+       st64->st_uid = st->st_uid;
+       st64->st_gid = st->st_gid;
+       st64->st_atime = st->st_atime;
+       st64->st_mtime = st->st_mtime;
+       st64->st_ctime = st->st_ctime;
+       st64->st_blksize = st->st_blksize;
+       st64->st_blocks = st->st_blocks;
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void dirent64_convert(struct dirent *d, struct dirent64 *d64)
+{
+       d64->d_ino = d->d_ino;
+       d64->d_off = d->d_off;
+       d64->d_reclen = d->d_reclen;
+       pstrcpy(d64->d_name, d->d_name);
+}
+#endif
+#endif
+
+
+#ifdef HAVE___XSTAT
+/* Definition of `struct stat' used in the linux kernel..  */
+struct kernel_stat {
+       unsigned short int st_dev;
+       unsigned short int __pad1;
+       unsigned long int st_ino;
+       unsigned short int st_mode;
+       unsigned short int st_nlink;
+       unsigned short int st_uid;
+       unsigned short int st_gid;
+       unsigned short int st_rdev;
+       unsigned short int __pad2;
+       unsigned long int st_size;
+       unsigned long int st_blksize;
+       unsigned long int st_blocks;
+       unsigned long int st_atime;
+       unsigned long int __unused1;
+       unsigned long int st_mtime;
+       unsigned long int __unused2;
+       unsigned long int st_ctime;
+       unsigned long int __unused3;
+       unsigned long int __unused4;
+       unsigned long int __unused5;
+};
+
+/*
+ * Prototype for gcc in 'fussy' mode.
+ */
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf);
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf)
+{
+#ifdef _STAT_VER_LINUX_OLD
+       if (vers == _STAT_VER_LINUX_OLD) {
+               memcpy(st, kbuf, sizeof(*st));
+               return;
+       }
+#endif
+
+       ZERO_STRUCTP(st);
+
+       st->st_dev = kbuf->st_dev;
+       st->st_ino = kbuf->st_ino;
+       st->st_mode = kbuf->st_mode;
+       st->st_nlink = kbuf->st_nlink;
+       st->st_uid = kbuf->st_uid;
+       st->st_gid = kbuf->st_gid;
+       st->st_rdev = kbuf->st_rdev;
+       st->st_size = kbuf->st_size;
+       st->st_blksize = kbuf->st_blksize;
+       st->st_blocks = kbuf->st_blocks;
+       st->st_atime = kbuf->st_atime;
+       st->st_mtime = kbuf->st_mtime;
+       st->st_ctime = kbuf->st_ctime;
+}
+#endif
diff --git a/source4/smbwrapper/smbw.h b/source4/smbwrapper/smbw.h
new file mode 100644 (file)
index 0000000..3f0b1cb
--- /dev/null
@@ -0,0 +1,71 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions - definitions
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+#ifndef _SMBW_H
+#define _SMBW_H
+
+#define SMBW_PREFIX "/smb/"
+#define SMBW_DUMMY "/dev/null"
+
+#define SMBW_CLI_FD 512
+#define SMBW_MAX_OPEN 8192
+
+#define SMBW_FILE_MODE (S_IFREG | 0444)
+#define SMBW_DIR_MODE (S_IFDIR | 0555)
+
+struct smbw_server {
+       struct smbw_server *next, *prev;
+       struct cli_state cli;
+       char *server_name;
+       char *share_name;
+       char *workgroup;
+       char *username;
+       dev_t dev;
+       BOOL no_pathinfo2;
+};
+
+struct smbw_filedes {
+       int cli_fd;
+       int ref_count;
+       char *fname;
+       off_t offset;
+};
+
+struct smbw_file {
+       struct smbw_file *next, *prev;
+       struct smbw_filedes *f;
+       int fd;
+       struct smbw_server *srv;
+};
+
+struct smbw_dir {
+       struct smbw_dir *next, *prev;
+       int fd;
+       int offset, count, malloced;
+       struct smbw_server *srv;
+       struct file_info *list;
+       char *path;
+};
+
+typedef void (*smbw_get_auth_data_fn)(char *server, char *share,
+                                     char **workgroup, char **username,
+                                     char **password);
+
+#endif /* _SMBW_H */
diff --git a/source4/smbwrapper/smbw_cache.c b/source4/smbwrapper/smbw_cache.c
new file mode 100644 (file)
index 0000000..fcb0eda
--- /dev/null
@@ -0,0 +1,207 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper directory functions
+   Copyright (C) Tim Potter 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"
+
+/* We cache lists of workgroups, lists of servers in workgroups, and lists
+   of shares exported by servers. */
+
+#define CACHE_TIMEOUT 30
+
+struct name_list {
+       struct name_list *prev, *next;
+       char *name;
+       uint32 stype;
+       char *comment;
+};
+
+struct cached_names {
+       struct cached_names *prev, *next;
+       char *key;
+       struct name_list *name_list;
+       time_t cache_timeout;
+       int result;
+};
+
+static struct cached_names *cached_names = NULL;
+
+/* Find a list of cached name for a workgroup, server or share list */
+
+static struct cached_names *find_cached_names(char *key)
+{
+       struct cached_names *tmp;
+
+       for (tmp = cached_names; tmp; tmp = tmp->next) {
+               if (strequal(tmp->key, key)) {
+                       return tmp;
+               }
+       }
+
+       return NULL;
+}
+
+/* Add a name to a list stored in the state variable */
+
+static void add_cached_names(const char *name, uint32 stype, 
+                            const char *comment, void *state)
+{
+       struct name_list **name_list = (struct name_list **)state;
+       struct name_list *new_name;
+
+       new_name = (struct name_list *)malloc(sizeof(struct name_list));
+       if (!new_name) return;
+
+       ZERO_STRUCTP(new_name);
+
+       new_name->name = strdup(name);
+       new_name->stype = stype;
+       new_name->comment = strdup(comment);
+
+       DLIST_ADD(*name_list, new_name);
+}
+
+static void free_name_list(struct name_list *name_list)
+{
+       struct name_list *tmp = name_list;
+
+       while(tmp) {
+               struct name_list *next;
+
+               next = tmp->next;
+
+               SAFE_FREE(tmp->name);
+               SAFE_FREE(tmp->comment);
+               SAFE_FREE(tmp);
+               
+               tmp = next;
+       }
+}
+
+/* Wrapper for NetServerEnum function */
+
+BOOL smbw_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
+                       void (*fn)(const char *, uint32, const char *, void *),
+                       void *state)
+{
+       struct cached_names *names;
+       struct name_list *tmp;
+       time_t now = time(NULL);
+       char key[PATH_MAX];
+       BOOL result = True;
+
+       slprintf(key, PATH_MAX - 1, "%s/%s#%s", cli->desthost, 
+                workgroup, (stype == SV_TYPE_DOMAIN_ENUM ? "DOM" : "SRV"));
+
+       names = find_cached_names(key);
+
+       if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+               struct cached_names *new_names = NULL;
+
+               /* No names cached for this workgroup */
+
+               if (names == NULL) {
+                       new_names = (struct cached_names *)
+                               malloc(sizeof(struct cached_names));
+
+                       ZERO_STRUCTP(new_names);
+                       DLIST_ADD(cached_names, new_names);
+
+               } else {
+
+                       /* Dispose of out of date name list */
+
+                       free_name_list(names->name_list);
+                       names->name_list = NULL;
+
+                       new_names = names;
+               }               
+
+               result = cli_NetServerEnum(cli, workgroup, stype, 
+                                          add_cached_names, 
+                                          &new_names->name_list);
+                                          
+               new_names->cache_timeout = now;
+               new_names->result = result;
+               new_names->key = strdup(key);
+
+               names = new_names;
+       }
+
+       /* Return names by running callback function. */
+
+       for (tmp = names->name_list; tmp; tmp = tmp->next)
+               fn(tmp->name, stype, tmp->comment, state);
+       
+       return names->result;
+}
+
+/* Wrapper for RNetShareEnum function */
+
+int smbw_RNetShareEnum(struct cli_state *cli, 
+                      void (*fn)(const char *, uint32, const char *, void *), 
+                      void *state)
+{
+       struct cached_names *names;
+       struct name_list *tmp;
+       time_t now = time(NULL);
+       char key[PATH_MAX];
+
+       slprintf(key, PATH_MAX - 1, "SHARE/%s", cli->desthost);
+
+       names = find_cached_names(key);
+
+       if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+               struct cached_names *new_names = NULL;
+
+               /* No names cached for this server */
+
+               if (names == NULL) {
+                       new_names = (struct cached_names *)
+                               malloc(sizeof(struct cached_names));
+
+                       ZERO_STRUCTP(new_names);
+                       DLIST_ADD(cached_names, new_names);
+
+               } else {
+
+                       /* Dispose of out of date name list */
+
+                       free_name_list(names->name_list);
+                       names->name_list = NULL;
+
+                       new_names = names;
+               }
+
+               new_names->result = cli_RNetShareEnum(cli, add_cached_names, 
+                                                     &new_names->name_list);
+               
+               new_names->cache_timeout = now;
+               new_names->key = strdup(key);
+
+               names = new_names;
+       }
+
+       /* Return names by running callback function. */
+
+       for (tmp = names->name_list; tmp; tmp = tmp->next)
+               fn(tmp->name, tmp->stype, tmp->comment, state);
+       
+       return names->result;
+}
diff --git a/source4/smbwrapper/smbw_dir.c b/source4/smbwrapper/smbw_dir.c
new file mode 100644 (file)
index 0000000..31d81a1
--- /dev/null
@@ -0,0 +1,688 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper directory functions
+   Copyright (C) Andrew Tridgell 1998
+   
+   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 "realcalls.h"
+
+extern pstring smbw_cwd;
+extern fstring smbw_prefix;
+
+static struct smbw_dir *smbw_dirs;
+
+extern struct bitmap *smbw_file_bmap;
+
+extern int smbw_busy;
+
+/***************************************************** 
+map a fd to a smbw_dir structure
+*******************************************************/
+struct smbw_dir *smbw_dir(int fd)
+{
+       struct smbw_dir *dir;
+
+       for (dir=smbw_dirs;dir;dir=dir->next) {
+               if (dir->fd == fd) return dir;
+       }
+       return NULL;
+}
+
+/***************************************************** 
+check if a DIR* is one of ours
+*******************************************************/
+int smbw_dirp(DIR *dirp)
+{
+       struct smbw_dir *d = (struct smbw_dir *)dirp;
+       struct smbw_dir *dir;
+
+       for (dir=smbw_dirs;dir;dir=dir->next) {
+               if (dir == d) return 1;
+       }
+       return 0;
+}
+
+/***************************************************** 
+free a smbw_dir structure and all entries
+*******************************************************/
+static void free_dir(struct smbw_dir *dir)
+{
+       if(!dir) return;
+
+       SAFE_FREE(dir->list);
+       SAFE_FREE(dir->path);
+       ZERO_STRUCTP(dir);
+       SAFE_FREE(dir);
+}
+
+static struct smbw_dir *cur_dir;
+
+/***************************************************** 
+add a entry to a directory listing
+*******************************************************/
+static void smbw_dir_add(struct file_info *finfo, const char *mask, 
+                        void *state)
+{
+       struct file_info *cdl;
+
+       DEBUG(5,("%s\n", finfo->name));
+
+       if (cur_dir->malloced == cur_dir->count) {
+               cdl = (struct file_info *)Realloc(cur_dir->list, 
+                                                           sizeof(cur_dir->list[0])*
+                                                           (cur_dir->count+100));
+               if (!cdl) {
+                       /* oops */
+                       return;
+               }
+               cur_dir->list = cdl;
+               cur_dir->malloced += 100;
+       }
+
+       cur_dir->list[cur_dir->count] = *finfo;
+       cur_dir->count++;
+}
+
+/***************************************************** 
+add a entry to a directory listing
+*******************************************************/
+static void smbw_share_add(const char *share, uint32 type, 
+                          const char *comment, void *state)
+{
+       struct file_info finfo;
+
+       if (strcmp(share,"IPC$") == 0) return;
+
+       ZERO_STRUCT(finfo);
+
+       pstrcpy(finfo.name, share);
+       finfo.mode = aRONLY | aDIR;     
+
+       smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/***************************************************** 
+add a server to a directory listing
+*******************************************************/
+static void smbw_server_add(const char *name, uint32 type, 
+                           const char *comment, void *state)
+{
+       struct file_info finfo;
+
+       ZERO_STRUCT(finfo);
+
+       pstrcpy(finfo.name, name);
+       finfo.mode = aRONLY | aDIR;     
+
+       smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/***************************************************** 
+add a entry to a directory listing
+*******************************************************/
+static void smbw_printjob_add(struct print_job_info *job)
+{
+       struct file_info finfo;
+
+       ZERO_STRUCT(finfo);
+
+       pstrcpy(finfo.name, job->name);
+       finfo.mode = aRONLY | aDIR;     
+       finfo.mtime = job->t;
+       finfo.atime = job->t;
+       finfo.ctime = job->t;
+       finfo.uid = nametouid(job->user);
+       finfo.mode = aRONLY;
+       finfo.size = job->size;
+
+       smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/***************************************************** 
+open a directory on the server
+*******************************************************/
+int smbw_dir_open(const char *fname)
+{
+       fstring server, share;
+       pstring path;
+       struct smbw_server *srv=NULL;
+       struct smbw_dir *dir=NULL;
+       pstring mask;
+       int fd;
+       char *s, *p;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       /* work out what server they are after */
+       s = smbw_parse_path(fname, server, share, path);
+
+       DEBUG(4,("dir_open share=%s\n", share));
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       dir = (struct smbw_dir *)malloc(sizeof(*dir));
+       if (!dir) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       ZERO_STRUCTP(dir);
+
+       cur_dir = dir;
+
+       slprintf(mask, sizeof(mask)-1, "%s\\*", path);
+       all_string_sub(mask,"\\\\","\\",0);
+
+       if ((p=strstr(srv->server_name,"#01"))) {
+               *p = 0;
+               smbw_server_add(".",0,"", NULL);
+               smbw_server_add("..",0,"", NULL);
+               smbw_NetServerEnum(&srv->cli, srv->server_name, 
+                                  SV_TYPE_DOMAIN_ENUM, smbw_server_add, NULL);
+               *p = '#';
+       } else if ((p=strstr(srv->server_name,"#1D"))) {
+               DEBUG(4,("doing NetServerEnum\n"));
+               *p = 0;
+               smbw_server_add(".",0,"", NULL);
+               smbw_server_add("..",0,"", NULL);
+               smbw_NetServerEnum(&srv->cli, srv->server_name, SV_TYPE_ALL,
+                                  smbw_server_add, NULL);
+               *p = '#';
+       } else if (strcmp(srv->cli.dev,"IPC") == 0) {
+               DEBUG(4,("doing NetShareEnum\n"));
+               smbw_share_add(".",0,"", NULL);
+               smbw_share_add("..",0,"", NULL);
+               if (smbw_RNetShareEnum(&srv->cli, smbw_share_add, NULL) < 0) {
+                       errno = smbw_errno(&srv->cli);
+                       goto failed;
+               }
+       } else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
+               smbw_share_add(".",0,"", NULL);
+               smbw_share_add("..",0,"", NULL);
+               if (cli_print_queue(&srv->cli, smbw_printjob_add) < 0) {
+                       errno = smbw_errno(&srv->cli);
+                       goto failed;
+               }
+       } else {
+#if 0
+               if (strcmp(path,"\\") == 0) {
+                       smbw_share_add(".",0,"");
+                       smbw_share_add("..",0,"");
+               }
+#endif
+               if (cli_list(&srv->cli, mask, aHIDDEN|aSYSTEM|aDIR, 
+                            smbw_dir_add, NULL) < 0) {
+                       errno = smbw_errno(&srv->cli);
+                       goto failed;
+               }
+       }
+
+       cur_dir = NULL;
+       
+       fd = open(SMBW_DUMMY, O_WRONLY);
+       if (fd == -1) {
+               errno = EMFILE;
+               goto failed;
+       }
+
+       if (bitmap_query(smbw_file_bmap, fd)) {
+               DEBUG(0,("ERROR: fd used in smbw_dir_open\n"));
+               errno = EIO;
+               goto failed;
+       }
+
+       DLIST_ADD(smbw_dirs, dir);
+       
+       bitmap_set(smbw_file_bmap, fd);
+
+       dir->fd = fd;
+       dir->srv = srv;
+       dir->path = strdup(s);
+
+       DEBUG(4,("  -> %d\n", dir->count));
+
+       return dir->fd;
+
+ failed:
+       free_dir(dir);
+       
+       return -1;
+}
+
+/***************************************************** 
+a wrapper for fstat() on a directory
+*******************************************************/
+int smbw_dir_fstat(int fd, struct stat *st)
+{
+       struct smbw_dir *dir;
+
+       dir = smbw_dir(fd);
+       if (!dir) {
+               errno = EBADF;
+               return -1;
+       }
+
+       ZERO_STRUCTP(st);
+
+       smbw_setup_stat(st, "", dir->count*DIRP_SIZE, aDIR);
+
+       st->st_dev = dir->srv->dev;
+
+       return 0;
+}
+
+/***************************************************** 
+close a directory handle
+*******************************************************/
+int smbw_dir_close(int fd)
+{
+       struct smbw_dir *dir;
+
+       dir = smbw_dir(fd);
+       if (!dir) {
+               errno = EBADF;
+               return -1;
+       }
+
+       bitmap_clear(smbw_file_bmap, dir->fd);
+       close(dir->fd);
+       
+       DLIST_REMOVE(smbw_dirs, dir);
+
+       free_dir(dir);
+
+       return 0;
+}
+
+/***************************************************** 
+a wrapper for getdents()
+*******************************************************/
+int smbw_getdents(unsigned int fd, struct dirent *dirp, int count)
+{
+       struct smbw_dir *dir;
+       int n=0;
+
+       smbw_busy++;
+
+       dir = smbw_dir(fd);
+       if (!dir) {
+               errno = EBADF;
+               smbw_busy--;
+               return -1;
+       }
+
+       while (count>=DIRP_SIZE && (dir->offset < dir->count)) {
+#if HAVE_DIRENT_D_OFF
+               dirp->d_off = (dir->offset+1)*DIRP_SIZE;
+#endif
+               dirp->d_reclen = DIRP_SIZE;
+               fstrcpy(&dirp->d_name[0], dir->list[dir->offset].name);
+               dirp->d_ino = smbw_inode(dir->list[dir->offset].name);
+               dir->offset++;
+               count -= dirp->d_reclen;
+#if HAVE_DIRENT_D_OFF
+               if (dir->offset == dir->count) {
+                       dirp->d_off = -1;
+               }
+#endif
+               dirp = (struct dirent *)(((char *)dirp) + DIRP_SIZE);
+               n++;
+       }
+
+       smbw_busy--;
+       return n*DIRP_SIZE;
+}
+
+
+/***************************************************** 
+a wrapper for chdir()
+*******************************************************/
+int smbw_chdir(const char *name)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+       uint16 mode = aDIR;
+       char *cwd;
+       int len;
+
+       smbw_init();
+
+       len = strlen(smbw_prefix);
+
+       if (smbw_busy) return real_chdir(name);
+
+       smbw_busy++;
+
+       if (!name) {
+               errno = EINVAL;
+               goto failed;
+       }
+
+       DEBUG(4,("smbw_chdir(%s)\n", name));
+
+       /* work out what server they are after */
+       cwd = smbw_parse_path(name, server, share, path);
+
+       /* a special case - accept cd to /smb */
+       if (strncmp(cwd, smbw_prefix, len-1) == 0 &&
+           cwd[len-1] == 0) {
+               goto success1;
+       }
+
+       if (strncmp(cwd,smbw_prefix,strlen(smbw_prefix))) {
+               if (real_chdir(cwd) == 0) {
+                       goto success2;
+               }
+               goto failed;
+       }
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (strncmp(srv->cli.dev,"IPC",3) &&
+           strncmp(srv->cli.dev,"LPT",3) &&
+           !smbw_getatr(srv, path, 
+                        &mode, NULL, NULL, NULL, NULL, NULL)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+
+       if (!(mode & aDIR)) {
+               errno = ENOTDIR;
+               goto failed;
+       }
+
+ success1:
+       /* we don't want the old directory to be busy */
+       real_chdir("/");
+
+ success2:
+
+       DEBUG(4,("set SMBW_CWD to %s\n", cwd));
+
+       pstrcpy(smbw_cwd, cwd);
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for lseek() on directories
+*******************************************************/
+off_t smbw_dir_lseek(int fd, off_t offset, int whence)
+{
+       struct smbw_dir *dir;
+       off_t ret;
+
+       dir = smbw_dir(fd);
+       if (!dir) {
+               errno = EBADF;
+               return -1;
+       }
+
+       switch (whence) {
+       case SEEK_SET:
+               dir->offset = offset/DIRP_SIZE;
+               break;
+       case SEEK_CUR:
+               dir->offset += offset/DIRP_SIZE;
+               break;
+       case SEEK_END:
+               dir->offset = (dir->count * DIRP_SIZE) + offset;
+               dir->offset /= DIRP_SIZE;
+               break;
+       }
+
+       ret = dir->offset * DIRP_SIZE;
+
+       DEBUG(4,("   -> %d\n", (int)ret));
+
+       return ret;
+}
+
+
+/***************************************************** 
+a wrapper for mkdir()
+*******************************************************/
+int smbw_mkdir(const char *fname, mode_t mode)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (!cli_mkdir(&srv->cli, path)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+/***************************************************** 
+a wrapper for rmdir()
+*******************************************************/
+int smbw_rmdir(const char *fname)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       pstring path;
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+               /* smbw_server sets errno */
+               goto failed;
+       }
+
+       if (!cli_rmdir(&srv->cli, path)) {
+               errno = smbw_errno(&srv->cli);
+               goto failed;
+       }
+
+       smbw_busy--;
+       return 0;
+
+ failed:
+       smbw_busy--;
+       return -1;
+}
+
+
+/***************************************************** 
+a wrapper for getcwd()
+*******************************************************/
+char *smbw_getcwd(char *buf, size_t size)
+{
+       smbw_init();
+
+       if (smbw_busy) {
+               return (char *)real_getcwd(buf, size);
+       }
+
+       smbw_busy++;
+
+       if (!buf) {
+               if (size <= 0) size = strlen(smbw_cwd)+1;
+               buf = (char *)malloc(size);
+               if (!buf) {
+                       errno = ENOMEM;
+                       smbw_busy--;
+                       return NULL;
+               }
+       }
+
+       if (strlen(smbw_cwd) > size-1) {
+               errno = ERANGE;
+               smbw_busy--;
+               return NULL;
+       }
+
+       safe_strcpy(buf, smbw_cwd, size);
+
+       smbw_busy--;
+       return buf;
+}
+
+/***************************************************** 
+a wrapper for fchdir()
+*******************************************************/
+int smbw_fchdir(unsigned int fd)
+{
+       struct smbw_dir *dir;
+       int ret;
+
+       smbw_busy++;
+
+       dir = smbw_dir(fd);
+       if (dir) {
+               smbw_busy--;
+               return chdir(dir->path);
+       }       
+
+       ret = real_fchdir(fd);
+       if (ret == 0) {
+               sys_getwd(smbw_cwd);            
+       }
+
+       smbw_busy--;
+       return ret;
+}
+
+/***************************************************** 
+open a directory on the server
+*******************************************************/
+DIR *smbw_opendir(const char *fname)
+{
+       int fd;
+
+       smbw_busy++;
+
+       fd = smbw_dir_open(fname);
+
+       if (fd == -1) {
+               smbw_busy--;
+               return NULL;
+       }
+
+       smbw_busy--;
+
+       return (DIR *)smbw_dir(fd);
+}
+
+/***************************************************** 
+read one entry from a directory
+*******************************************************/
+struct dirent *smbw_readdir(DIR *dirp)
+{
+       struct smbw_dir *d = (struct smbw_dir *)dirp;
+       static union {
+               char buf[DIRP_SIZE];
+               struct dirent de;
+       } dbuf;
+
+       if (smbw_getdents(d->fd, &dbuf.de, DIRP_SIZE) > 0) 
+               return &dbuf.de;
+
+       return NULL;
+}
+
+/***************************************************** 
+close a DIR*
+*******************************************************/
+int smbw_closedir(DIR *dirp)
+{
+       struct smbw_dir *d = (struct smbw_dir *)dirp;
+       return smbw_close(d->fd);
+}
+
+/***************************************************** 
+seek in a directory
+*******************************************************/
+void smbw_seekdir(DIR *dirp, off_t offset)
+{
+       struct smbw_dir *d = (struct smbw_dir *)dirp;
+       smbw_dir_lseek(d->fd,offset, SEEK_SET);
+}
+
+/***************************************************** 
+current loc in a directory
+*******************************************************/
+off_t smbw_telldir(DIR *dirp)
+{
+       struct smbw_dir *d = (struct smbw_dir *)dirp;
+       return smbw_dir_lseek(d->fd,0,SEEK_CUR);
+}
diff --git a/source4/smbwrapper/smbw_stat.c b/source4/smbwrapper/smbw_stat.c
new file mode 100644 (file)
index 0000000..6c476a8
--- /dev/null
@@ -0,0 +1,250 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper stat functions
+   Copyright (C) Andrew Tridgell 1998
+   
+   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"
+
+extern int smbw_busy;
+
+/***************************************************** 
+setup basic info in a stat structure
+*******************************************************/
+void smbw_setup_stat(struct stat *st, char *fname, size_t size, int mode)
+{
+       st->st_mode = 0;
+
+       if (IS_DOS_DIR(mode)) {
+               st->st_mode = SMBW_DIR_MODE;
+       } else {
+               st->st_mode = SMBW_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 = smbw_inode(fname);
+       }
+}
+
+
+/***************************************************** 
+try to do a QPATHINFO and if that fails then do a getatr
+this is needed because win95 sometimes refuses the qpathinfo
+*******************************************************/
+BOOL smbw_getatr(struct smbw_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)
+{
+       DEBUG(4,("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;
+}
+
+
+static struct print_job_info printjob;
+
+/***************************************************** 
+gather info from a printjob listing
+*******************************************************/
+static void smbw_printjob_stat(struct print_job_info *job)
+{
+       if (strcmp(job->name, printjob.name) == 0) {
+               printjob = *job;
+       }
+}
+
+/***************************************************** 
+stat a printjob
+*******************************************************/
+int smbw_stat_printjob(struct smbw_server *srv,char *path,
+                      size_t *size, time_t *m_time)
+{
+       if (path[0] == '\\') path++;
+
+       ZERO_STRUCT(printjob);
+
+       fstrcpy(printjob.name, path);
+       cli_print_queue(&srv->cli, smbw_printjob_stat);
+
+       if (size) {
+               *size = printjob.size;
+       }
+       if (m_time) {
+               *m_time = printjob.t;
+       }
+       return printjob.id;
+}
+
+
+/***************************************************** 
+a wrapper for fstat()
+*******************************************************/
+int smbw_fstat(int fd, struct stat *st)
+{
+       struct smbw_file *file;
+       time_t c_time, a_time, m_time;
+       size_t size;
+       uint16 mode;
+       SMB_INO_T ino = 0;
+
+       smbw_busy++;
+
+       ZERO_STRUCTP(st);
+
+       file = smbw_file(fd);
+       if (!file) {
+               int ret = smbw_dir_fstat(fd, st);
+               smbw_busy--;
+               return ret;
+       }
+
+       if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd, 
+                          &mode, &size, &c_time, &a_time, &m_time, NULL,
+                          &ino) &&
+           !cli_getattrE(&file->srv->cli, file->f->cli_fd, 
+                         &mode, &size, &c_time, &a_time, &m_time)) {
+               errno = EINVAL;
+               smbw_busy--;
+               return -1;
+       }
+
+       st->st_ino = ino;
+
+       smbw_setup_stat(st, file->f->fname, size, mode);
+
+       st->st_atime = a_time;
+       st->st_ctime = c_time;
+       st->st_mtime = m_time;
+       st->st_dev = file->srv->dev;
+
+       smbw_busy--;
+       return 0;
+}
+
+
+/***************************************************** 
+a wrapper for stat()
+*******************************************************/
+int smbw_stat(const char *fname, struct stat *st)
+{
+       struct smbw_server *srv;
+       fstring server, share;
+       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;
+       int result = 0;
+
+       ZERO_STRUCTP(st);
+
+       if (!fname) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       DEBUG(4,("stat(%s)\n", fname));
+
+       smbw_init();
+
+       smbw_busy++;
+
+       /* work out what server they are after */
+       smbw_parse_path(fname, server, share, path);
+
+       /* get a connection to the server */
+       srv = smbw_server(server, share);
+       if (!srv) {
+
+               /* For shares we aren't allowed to connect to, or no master
+                  browser found, return an empty directory */
+
+               if ((server[0] && share[0] && !path[0] && errno == EACCES) ||
+                   (!path[0] && errno == ENOENT)) {
+                       mode = aDIR | aRONLY;
+                       smbw_setup_stat(st, path, size, mode);
+                       goto done;
+               }
+
+               /* smbw_server sets errno */
+               result = -1;
+               goto done;
+       }
+
+       DEBUG(4,("smbw_stat\n"));
+
+       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;
+                       smbw_stat_printjob(srv, path, &size, &m_time);
+                       c_time = a_time = m_time;
+               }
+       } else {
+               if (!smbw_getatr(srv, path, 
+                                &mode, &size, &c_time, &a_time, &m_time,
+                                &ino)) {
+                       errno = smbw_errno(&srv->cli);
+                       result = -1;
+                       goto done;
+               }
+       }
+
+       st->st_ino = ino;
+
+       smbw_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;
+
+ done:
+       smbw_busy--;
+       return result;
+}
diff --git a/source4/smbwrapper/wrapped.c b/source4/smbwrapper/wrapped.c
new file mode 100644 (file)
index 0000000..338ee0d
--- /dev/null
@@ -0,0 +1,705 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB wrapper functions
+   Copyright (C) Andrew Tridgell 1998
+   
+   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.
+*/
+
+/* NOTE: This file WILL produce compiler warnings. They are unavoidable 
+
+   Do not try and get rid of them by including other include files or
+   by including includes.h or proto.h or you will break portability. 
+  */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include "realcalls.h"
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+ int open(char *name, int flags, mode_t mode)
+{
+       if (smbw_path(name)) {
+               return smbw_open(name, flags, mode);
+       }
+
+       return real_open(name, flags, mode);
+}
+
+#ifdef HAVE__OPEN
+ int _open(char *name, int flags, mode_t mode) 
+{
+       return open(name, flags, mode);
+}
+#elif HAVE___OPEN
+ int __open(char *name, int flags, mode_t mode) 
+{
+       return open(name, flags, mode);
+}
+#endif
+
+
+#ifdef HAVE_OPEN64
+ int open64(char *name, int flags, mode_t mode)
+{
+       if (smbw_path(name)) {
+               return smbw_open(name, flags, mode);
+       }
+
+       return real_open64(name, flags, mode);
+}
+#endif
+
+#ifndef NO_OPEN64_ALIAS
+#ifdef HAVE__OPEN64
+ int _open64(char *name, int flags, mode_t mode) 
+{
+       return open64(name, flags, mode);
+}
+#elif HAVE___OPEN64
+ int __open64(char *name, int flags, mode_t mode) 
+{
+       return open64(name, flags, mode);
+}
+#endif
+#endif
+
+#ifdef HAVE_PREAD
+ ssize_t pread(int fd, void *buf, size_t size, off_t ofs)
+{
+       if (smbw_fd(fd)) {
+               return smbw_pread(fd, buf, size, ofs);
+       }
+
+       return real_pread(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PREAD64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pread64(int fd, void *buf, size_t size, off64_t ofs)
+{
+       if (smbw_fd(fd)) {
+               return smbw_pread(fd, buf, size, ofs);
+       }
+
+       return real_pread64(fd, buf, size, ofs);
+}
+#endif
+
+#ifdef HAVE_PWRITE
+ ssize_t pwrite(int fd, void *buf, size_t size, off_t ofs)
+{
+       if (smbw_fd(fd)) {
+               return smbw_pwrite(fd, buf, size, ofs);
+       }
+
+       return real_pwrite(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PWRITE64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pwrite64(int fd, void *buf, size_t size, off64_t ofs)
+{
+       if (smbw_fd(fd)) {
+               return smbw_pwrite(fd, buf, size, ofs);
+       }
+
+       return real_pwrite64(fd, buf, size, ofs);
+}
+#endif
+
+
+ int chdir(char *name)
+{
+       return smbw_chdir(name);
+}
+
+#ifdef HAVE___CHDIR
+ int __chdir(char *name)
+{
+       return chdir(name);
+}
+#elif HAVE__CHDIR
+ int _chdir(char *name)
+{
+       return chdir(name);
+}
+#endif
+
+
+ int close(int fd)
+{
+       if (smbw_fd(fd)) {
+               return smbw_close(fd);
+       }
+       if (smbw_local_fd(fd)) {
+               errno = EBADF;
+               return -1;
+       }
+
+       return real_close(fd);
+}
+
+#ifdef HAVE___CLOSE
+ int __close(int fd)
+{
+       return close(fd);
+}
+#elif HAVE__CLOSE
+ int _close(int fd)
+{
+       return close(fd);
+}
+#endif
+
+
+ int fchdir(int fd)
+{
+       return smbw_fchdir(fd);
+}
+
+#ifdef HAVE___FCHDIR
+ int __fchdir(int fd)
+{
+       return fchdir(fd);
+}
+#elif HAVE__FCHDIR
+ int _fchdir(int fd)
+{
+       return fchdir(fd);
+}
+#endif
+
+
+ int fcntl(int fd, int cmd, long arg)
+{
+       if (smbw_fd(fd)) {
+               return smbw_fcntl(fd, cmd, arg);
+       }
+
+       return real_fcntl(fd, cmd, arg);
+}
+
+
+#ifdef HAVE___FCNTL
+ int __fcntl(int fd, int cmd, long arg)
+{
+       return fcntl(fd, cmd, arg);
+}
+#elif HAVE__FCNTL
+ int _fcntl(int fd, int cmd, long arg)
+{
+       return fcntl(fd, cmd, arg);
+}
+#endif
+
+
+
+#ifdef real_getdents
+ int getdents(int fd, void *dirp, unsigned int count)
+{
+       if (smbw_fd(fd)) {
+               return smbw_getdents(fd, dirp, count);
+       }
+
+       return real_getdents(fd, dirp, count);
+}
+#endif
+
+#ifdef HAVE___GETDENTS
+ int __getdents(int fd, void *dirp, unsigned int count)
+{
+       return getdents(fd, dirp, count);
+}
+#elif HAVE__GETDENTS
+ int _getdents(int fd, void *dirp, unsigned int count)
+{
+       return getdents(fd, dirp, count);
+}
+#endif
+
+
+ off_t lseek(int fd, off_t offset, int whence)
+{
+       if (smbw_fd(fd)) {
+               return smbw_lseek(fd, offset, whence);
+       }
+
+       return real_lseek(fd, offset, whence);
+}
+
+#ifdef HAVE___LSEEK
+ off_t __lseek(int fd, off_t offset, int whence)
+{
+       return lseek(fd, offset, whence);
+}
+#elif HAVE__LSEEK
+ off_t _lseek(int fd, off_t offset, int whence)
+{
+       return lseek(fd, offset, whence);
+}
+#endif
+
+
+ ssize_t read(int fd, void *buf, size_t count)
+{
+       if (smbw_fd(fd)) {
+               return smbw_read(fd, buf, count);
+       }
+
+       return real_read(fd, buf, count);
+}
+
+#ifdef HAVE___READ
+ ssize_t __read(int fd, void *buf, size_t count)
+{
+       return read(fd, buf, count);
+}
+#elif HAVE__READ
+ ssize_t _read(int fd, void *buf, size_t count)
+{
+       return read(fd, buf, count);
+}
+#endif
+
+
+ ssize_t write(int fd, void *buf, size_t count)
+{
+       if (smbw_fd(fd)) {
+               return smbw_write(fd, buf, count);
+       }
+
+       return real_write(fd, buf, count);
+}
+
+#ifdef HAVE___WRITE
+ ssize_t __write(int fd, void *buf, size_t count)
+{
+       return write(fd, buf, count);
+}
+#elif HAVE__WRITE
+ ssize_t _write(int fd, void *buf, size_t count)
+{
+       return write(fd, buf, count);
+}
+#endif
+
+
+
+ int access(char *name, int mode)
+{
+       if (smbw_path(name)) {
+               return smbw_access(name, mode);
+       }
+
+       return real_access(name, mode);
+}
+
+
+
+ int chmod(char *name,mode_t mode)
+{
+       if (smbw_path(name)) {
+               return smbw_chmod(name, mode);
+       }
+
+       return real_chmod(name, mode);
+}
+
+
+
+ int chown(char *name,uid_t owner, gid_t group)
+{
+       if (smbw_path(name)) {
+               return smbw_chown(name, owner, group);
+       }
+
+       return real_chown(name, owner, group);
+}
+
+
+ char *getcwd(char *buf, size_t size)
+{
+       return (char *)smbw_getcwd(buf, size);
+}
+
+
+
+
+ int mkdir(char *name, mode_t mode)
+{
+       if (smbw_path(name)) {
+               return smbw_mkdir(name, mode);
+       }
+
+       return real_mkdir(name, mode);
+}
+
+
+#if HAVE___FXSTAT
+ int __fxstat(int vers, int fd, void *st)
+{
+       double xx[32];
+       int ret;
+
+       if (smbw_fd(fd)) {
+               return smbw_fstat(fd, st);
+       }
+
+       ret = real_fstat(fd, xx);
+       xstat_convert(vers, st, xx);
+       return ret;
+}
+#endif
+
+#if HAVE___XSTAT
+ int __xstat(int vers, char *name, void *st)
+{
+       double xx[32];
+       int ret;
+
+       if (smbw_path(name)) {
+               return smbw_stat(name, st);
+       }
+
+       ret = real_stat(name, xx);
+       xstat_convert(vers, st, xx);
+       return ret;
+}
+#endif
+
+
+#if HAVE___LXSTAT
+ int __lxstat(int vers, char *name, void *st)
+{
+       double xx[32];
+       int ret;
+
+       if (smbw_path(name)) {
+               return smbw_stat(name, st);
+       }
+
+       ret = real_lstat(name, xx);
+       xstat_convert(vers, st, xx);
+       return ret;
+}
+#endif
+
+
+ int stat(char *name, void *st)
+{
+#if HAVE___XSTAT
+       return __xstat(0, name, st);
+#else
+       if (smbw_path(name)) {
+               return smbw_stat(name, st);
+       }
+       return real_stat(name, st);
+#endif
+}
+
+ int lstat(char *name, void *st)
+{
+#if HAVE___LXSTAT
+       return __lxstat(0, name, st);
+#else
+       if (smbw_path(name)) {
+               return smbw_stat(name, st);
+       }
+       return real_lstat(name, st);
+#endif
+}
+
+ int fstat(int fd, void *st)
+{
+#if HAVE___LXSTAT
+       return __fxstat(0, fd, st);
+#else
+       if (smbw_fd(fd)) {
+               return smbw_fstat(fd, st);
+       }
+       return real_fstat(fd, st);
+#endif
+}
+
+
+ int unlink(char *name)
+{
+       if (smbw_path(name)) {
+               return smbw_unlink(name);
+       }
+
+       return real_unlink(name);
+}
+
+
+#ifdef HAVE_UTIME
+ int utime(char *name,void *tvp)
+{
+       if (smbw_path(name)) {
+               return smbw_utime(name, tvp);
+       }
+
+       return real_utime(name, tvp);
+}
+#endif
+
+#ifdef HAVE_UTIMES
+ int utimes(const char *name, const struct timeval *tvp)
+{
+       if (smbw_path(name)) {
+               return smbw_utimes(name, tvp);
+       }
+
+       return real_utimes(name, tvp);
+}
+#endif
+
+ int readlink(char *path, char *buf, size_t bufsize)
+{
+       if (smbw_path(path)) {
+               return smbw_readlink(path, buf, bufsize);
+       }
+
+       return real_readlink(path, buf, bufsize);
+}
+
+
+ int rename(char *oldname,char *newname)
+{
+       int p1, p2;
+       p1 = smbw_path(oldname); 
+       p2 = smbw_path(newname); 
+       if (p1 ^ p2) {
+               /* can't cross filesystem boundaries */
+               errno = EXDEV;
+               return -1;
+       }
+       if (p1 && p2) {
+               return smbw_rename(oldname, newname);
+       }
+
+       return real_rename(oldname, newname);
+}
+
+ int rmdir(char *name)
+{
+       if (smbw_path(name)) {
+               return smbw_rmdir(name);
+       }
+
+       return real_rmdir(name);
+}
+
+
+ int symlink(char *topath,char *frompath)
+{
+       int p1, p2;
+       p1 = smbw_path(topath); 
+       p2 = smbw_path(frompath); 
+       if (p1 || p2) {
+               /* can't handle symlinks */
+               errno = EPERM;
+               return -1;
+       }
+
+       return real_symlink(topath, frompath);
+}
+
+ int dup(int fd)
+{
+       if (smbw_fd(fd)) {
+               return smbw_dup(fd);
+       }
+
+       return real_dup(fd);
+}
+
+ int dup2(int oldfd, int newfd)
+{
+       if (smbw_fd(newfd)) {
+               close(newfd);
+       }
+
+       if (smbw_fd(oldfd)) {
+               return smbw_dup2(oldfd, newfd);
+       }
+
+       return real_dup2(oldfd, newfd);
+}
+
+#ifdef real_opendir
+ void *opendir(char *name)
+{
+       if (smbw_path(name)) {
+               return (void *)smbw_opendir(name);
+       }
+
+       return (void *)real_opendir(name);
+}
+#endif
+
+#ifdef real_readdir
+ void *readdir(void *dir)
+{
+       if (smbw_dirp(dir)) {
+               return (void *)smbw_readdir(dir);
+       }
+
+       return (void *)real_readdir(dir);
+}
+#endif
+
+#ifdef real_closedir
+ int closedir(void *dir)
+{
+       if (smbw_dirp(dir)) {
+               return smbw_closedir(dir);
+       }
+
+       return real_closedir(dir);
+}
+#endif
+
+#ifdef real_telldir
+ off_t telldir(void *dir)
+{
+       if (smbw_dirp(dir)) {
+               return smbw_telldir(dir);
+       }
+
+       return real_telldir(dir);
+}
+#endif
+
+#ifdef real_seekdir
+ int seekdir(void *dir, off_t offset)
+{
+       if (smbw_dirp(dir)) {
+               smbw_seekdir(dir, offset);
+               return 0;
+       }
+
+       real_seekdir(dir, offset);
+       return 0;
+}
+#endif
+
+
+#ifndef NO_ACL_WRAPPER
+ int  acl(char  *pathp,  int  cmd,  int  nentries, void *aclbufp)
+{
+       if (smbw_path(pathp)) {
+               return smbw_acl(pathp, cmd, nentries, aclbufp);
+       }
+
+       return real_acl(pathp, cmd, nentries, aclbufp);
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+ int  facl(int fd,  int  cmd,  int  nentries, void *aclbufp)
+{
+       if (smbw_fd(fd)) {
+               return smbw_facl(fd, cmd, nentries, aclbufp);
+       }
+
+       return real_facl(fd, cmd, nentries, aclbufp);
+}
+#endif
+
+ int creat(char *path, mode_t mode)
+{
+       extern int creat_bits;
+       return open(path, creat_bits, mode);
+}
+
+#ifdef HAVE_CREAT64
+ int creat64(char *path, mode_t mode)
+{
+       extern int creat_bits;
+       return open64(path, creat_bits, mode);
+}
+#endif
+
+#ifdef HAVE_STAT64
+  int stat64(char *name, void *st64)
+{
+       if (smbw_path(name)) {
+               double xx[32];
+               int ret = stat(name, xx);
+               stat64_convert(xx, st64);
+               return ret;
+       }
+       return real_stat64(name, st64);
+}
+
+  int fstat64(int fd, void *st64)
+{
+       if (smbw_fd(fd)) {
+               double xx[32];
+               int ret = fstat(fd, xx);
+               stat64_convert(xx, st64);
+               return ret;
+       }
+       return real_fstat64(fd, st64);
+}
+
+  int lstat64(char *name, void *st64)
+{
+       if (smbw_path(name)) {
+               double xx[32];
+               int ret = lstat(name, xx);
+               stat64_convert(xx, st64);
+               return ret;
+       }
+       return real_lstat64(name, st64);
+}
+#endif
+
+#ifdef HAVE_LLSEEK
+  offset_t llseek(int fd, offset_t ofs, int whence)
+{
+       if (smbw_fd(fd)) {
+               return lseek(fd, ofs, whence);
+       }
+       return real_llseek(fd, ofs, whence);
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void *readdir64(void *dir)
+{
+       if (smbw_dirp(dir)) {
+               static double xx[70];
+               void *d;
+               d = (void *)readdir(dir);
+               if (!d) return NULL;
+               dirent64_convert(d, xx);
+               return xx;
+       }
+       return (void *)real_readdir64(dir);
+}
+#endif
+
+ int fork(void)
+{
+       return smbw_fork();
+}
+
diff --git a/source4/tdb/.cvsignore b/source4/tdb/.cvsignore
new file mode 100644 (file)
index 0000000..66445fe
--- /dev/null
@@ -0,0 +1,11 @@
+*.po
+*.po32
+tdbbackup
+tdbdump
+tdbtest
+tdbtool
+tdbtorture
+test.db
+test.gdbm
+test.tdb
+torture.tdb
diff --git a/source4/tdb/Makefile b/source4/tdb/Makefile
new file mode 100644 (file)
index 0000000..59fbb07
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Makefile for tdb directory
+#
+
+CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
+CC = gcc
+
+PROGS = tdbtest tdbtool tdbtorture
+TDB_OBJ = tdb.o spinlock.o
+
+default: $(PROGS)
+
+tdbtest: tdbtest.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
+
+tdbtool: tdbtool.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
+
+tdbtorture: tdbtorture.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbdump: tdbdump.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
+
+tdbbackup: tdbbackup.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
+
+clean:
+       rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
diff --git a/source4/tdb/README b/source4/tdb/README
new file mode 100644 (file)
index 0000000..fac3eac
--- /dev/null
@@ -0,0 +1,167 @@
+tdb - a trivial database system
+tridge@linuxcare.com December 1999
+==================================
+
+This is a simple database API. It was inspired by the realisation that
+in Samba we have several ad-hoc bits of code that essentially
+implement small databases for sharing structures between parts of
+Samba. As I was about to add another I realised that a generic
+database module was called for to replace all the ad-hoc bits.
+
+I based the interface on gdbm. I couldn't use gdbm as we need to be
+able to have multiple writers to the databases at one time.
+
+Compilation
+-----------
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add TDB_DEBUG=1 for verbose debug info
+add NOLOCK=1 to disable locking code
+
+Testing
+-------
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+Interface
+---------
+
+The interface is very similar to gdbm except for the following:
+
+- different open interface. The tdb_open call is more similar to a
+  traditional open()
+- no tdbm_reorganise() function
+- no tdbm_sync() function. No operations are cached in the library anyway
+- added a tdb_traverse() function for traversing the whole database
+
+A general rule for using tdb is that the caller frees any returned
+TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
+return value called p. This is the same as gdbm.
+
+here is a full list of tdb functions with brief descriptions.
+
+
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+
+   open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the database
+   file. A flags value of O_WRONLY is invalid
+
+   The hash size is advisory, use zero for a default value. 
+
+   return is NULL on error
+
+   possible tdb_flags are:
+    TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
+    TDB_INTERNAL - don't use a file, instaed store the data in
+                   memory. The filename is ignored in this case.
+    TDB_NOLOCK - don't do any locking
+    TDB_NOMMAP - don't use mmap
+
+----------------------------------------------------------------------
+char *tdb_error(TDB_CONTEXT *tdb);
+
+     return a error string for the last tdb error
+
+----------------------------------------------------------------------
+int tdb_close(TDB_CONTEXT *tdb);
+
+   close a database
+
+----------------------------------------------------------------------
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
+
+   update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1
+
+----------------------------------------------------------------------
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   fetch an entry in the database given a key 
+   if the return value has a null dptr then a error occurred
+
+   caller must free the resulting data
+
+----------------------------------------------------------------------
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+
+----------------------------------------------------------------------
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
+                 TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
+
+   traverse the entire database - calling fn(tdb, key, data, state) on each 
+   element.
+
+   return -1 on error or the record count traversed
+
+   if fn is NULL then it is not called
+
+   a non-zero return value from fn() indicates that the traversal should stop
+
+----------------------------------------------------------------------
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+
+   find the first entry in the database and return its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   find the next entry in the database, returning its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   delete an entry in the database given a key
+
+----------------------------------------------------------------------
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+   store an element in the database, replacing any existing element
+   with the same key 
+
+   If flag==TDB_INSERT then don't overwrite an existing entry
+   If flag==TDB_MODIFY then don't create a new entry
+
+   return 0 on success, -1 on failure
+
+----------------------------------------------------------------------
+int tdb_writelock(TDB_CONTEXT *tdb);
+
+   lock the database. If we already have it locked then don't do anything
+
+----------------------------------------------------------------------
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+   unlock the database
+
+----------------------------------------------------------------------
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   lock one hash chain. This is meant to be used to reduce locking
+   contention - it cannot guarantee how many records will be locked
+
+----------------------------------------------------------------------
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   unlock one hash chain
diff --git a/source4/tdb/spinlock.c b/source4/tdb/spinlock.c
new file mode 100644 (file)
index 0000000..2370ce3
--- /dev/null
@@ -0,0 +1,430 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Anton Blanchard                   2001
+   
+   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.
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if STANDALONE
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+#else
+#include "includes.h"
+#endif
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int result;
+
+       asm volatile("ldstub    [%1], %0"
+               : "=r" (result)
+               : "r" (lock)
+               : "memory");
+
+       return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int result;
+
+       __asm__ __volatile__(
+"1:    lwarx           %0,0,%1\n\
+       cmpwi           0,%0,0\n\
+       li              %0,0\n\
+       bne-            2f\n\
+       li              %0,1\n\
+       stwcx.          %0,0,%1\n\
+       bne-            1b\n\
+       isync\n\
+2:"    : "=&r"(result)
+       : "r"(lock)
+       : "cr0", "memory");
+
+       return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("eieio":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS) 
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       int oldval;
+
+       asm volatile("xchgl %0,%1"
+               : "=r" (oldval), "=m" (*lock)
+               : "0" (0)
+               : "memory");
+
+       return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) 
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+       unsigned int res;
+
+       __asm__ __volatile__("ll\t%0,(%1)"
+               : "=r" (res)
+               : "r" (addr));
+
+       return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+       unsigned int res;
+
+       __asm__ __volatile__("sc\t%0,(%2)"
+               : "=r" (res)
+               : "0" (value), "r" (addr));
+       return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+       unsigned int mw;
+
+       do {
+               mw = load_linked(lock);
+               if (mw) 
+                       return EBUSY;
+       } while (!store_conditional(lock, 1));
+
+       asm volatile("":::"memory");
+
+       return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+       asm volatile("":::"memory");
+       *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+       *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+       return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+       struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+       sched_yield();
+#else
+       /* Linux will busy loop for delays < 2ms on real time tasks */
+       tm.tv_sec = 0;
+       tm.tv_nsec = 2000000L + 1;
+       nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+       return 0;
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+       int ntries = 0;
+
+       while(__spin_trylock(lock)) {
+               while(__spin_is_locked(lock)) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+       int ntries = 0;
+
+       while(1) {
+               __spin_lock(&rwlock->lock);
+
+               if (!(rwlock->count & RWLOCK_BIAS)) {
+                       rwlock->count++;
+                       __spin_unlock(&rwlock->lock);
+                       return;
+               }
+       
+               __spin_unlock(&rwlock->lock);
+
+               while(rwlock->count & RWLOCK_BIAS) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+       int ntries = 0;
+
+       while(1) {
+               __spin_lock(&rwlock->lock);
+
+               if (rwlock->count == 0) {
+                       rwlock->count |= RWLOCK_BIAS;
+                       __spin_unlock(&rwlock->lock);
+                       return;
+               }
+
+               __spin_unlock(&rwlock->lock);
+
+               while(rwlock->count != 0) {
+                       if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+                               continue;
+                       yield_cpu();
+               }
+       }
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+       __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+       if (!(rwlock->count & RWLOCK_BIAS))
+               fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+       rwlock->count &= ~RWLOCK_BIAS;
+       __spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+       __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+       if (!rwlock->count)
+               fprintf(stderr, "bug: read_unlock\n");
+
+       if (rwlock->count & RWLOCK_BIAS)
+               fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+       rwlock->count--;
+       __spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+       tdb_rwlock_t *rwlocks;
+
+       if (!tdb->map_ptr) return -1;
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+       switch(rw_type) {
+       case F_RDLCK:
+               __read_lock(&rwlocks[list+1]);
+               break;
+
+       case F_WRLCK:
+               __write_lock(&rwlocks[list+1]);
+               break;
+
+       default:
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+       return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+       tdb_rwlock_t *rwlocks;
+
+       if (!tdb->map_ptr) return -1;
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+       switch(rw_type) {
+       case F_RDLCK:
+               __read_unlock(&rwlocks[list+1]);
+               break;
+
+       case F_WRLCK:
+               __write_unlock(&rwlocks[list+1]);
+               break;
+
+       default:
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+
+       return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+       unsigned size, i;
+       tdb_rwlock_t *rwlocks;
+
+       size = (hash_size + 1) * sizeof(tdb_rwlock_t);
+       rwlocks = malloc(size);
+       if (!rwlocks)
+               return -1;
+
+       for(i = 0; i < hash_size+1; i++) {
+               __spin_lock_init(&rwlocks[i].lock);
+               rwlocks[i].count = 0;
+       }
+
+       /* Write it out (appending to end) */
+       if (write(fd, rwlocks, size) != size) {
+               free(rwlocks);
+               return -1;
+       }
+       smp_machine = this_is_smp();
+       free(rwlocks);
+       return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+       tdb_rwlock_t *rwlocks;
+       unsigned i;
+
+       if (tdb->header.rwlocks == 0) return 0;
+       if (!tdb->map_ptr) return -1;
+
+       /* We're mmapped here */
+       rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+       for(i = 0; i < tdb->header.hash_size+1; i++) {
+               __spin_lock_init(&rwlocks[i].lock);
+               rwlocks[i].count = 0;
+       }
+       return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+       tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+                               - (char *)&tdb->header);
+
+       tdb->header.rwlocks = 0;
+       if (lseek(tdb->fd, off, SEEK_SET) != off
+           || write(tdb->fd, (void *)&tdb->header.rwlocks,
+                    sizeof(tdb->header.rwlocks)) 
+           != sizeof(tdb->header.rwlocks))
+               return -1;
+       return 0;
+}
+#endif
diff --git a/source4/tdb/spinlock.h b/source4/tdb/spinlock.h
new file mode 100644 (file)
index 0000000..d6a2ac6
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+       spinlock_t lock;
+       volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#endif
+
+#endif
diff --git a/source4/tdb/tdb.c b/source4/tdb/tdb.c
new file mode 100644 (file)
index 0000000..097209f
--- /dev/null
@@ -0,0 +1,2020 @@
+ /* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell              1999-2000
+   Copyright (C) Luke Kenneth Casson Leighton      2000
+   Copyright (C) Paul `Rusty' Russell             2000
+   Copyright (C) Jeremy Allison                           2000-2003
+   
+   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.
+*/
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+#else
+#include "includes.h"
+#endif
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
+#define TDB_PAGE_SIZE 0x2000
+#define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+       if (tdb->flags & TDB_INTERNAL)
+               return 0;
+
+#ifdef HAVE_MMAP
+       if (tdb->map_ptr) {
+               int ret = munmap(tdb->map_ptr, tdb->map_size);
+               if (ret != 0)
+                       return ret;
+       }
+#endif
+       tdb->map_ptr = NULL;
+       return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+       if (tdb->flags & TDB_INTERNAL)
+               return;
+
+#ifdef HAVE_MMAP
+       if (!(tdb->flags & TDB_NOMMAP)) {
+               tdb->map_ptr = mmap(NULL, tdb->map_size, 
+                                   PROT_READ|(tdb->read_only? 0:PROT_WRITE), 
+                                   MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+               /*
+                * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+                */
+
+               if (tdb->map_ptr == MAP_FAILED) {
+                       tdb->map_ptr = NULL;
+                       TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", 
+                                tdb->map_size, strerror(errno)));
+               }
+       } else {
+               tdb->map_ptr = NULL;
+       }
+#else
+       tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+       u32 i, *p = buf;
+       for (i = 0; i < size / 4; i++)
+               p[i] = TDB_BYTEREV(p[i]);
+       return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
+/* the body of the database is made of one list_struct for the free space
+   plus a separate data list for each hash value */
+struct list_struct {
+       tdb_off next; /* offset of the next record in the list */
+       tdb_len rec_len; /* total byte length of record */
+       tdb_len key_len; /* byte length of key */
+       tdb_len data_len; /* byte length of data */
+       u32 full_hash; /* the full 32 bit hash of the key */
+       u32 magic;   /* try to catch errors */
+       /* the following union is implied:
+               union {
+                       char record[rec_len];
+                       struct {
+                               char key[key_len];
+                               char data[data_len];
+                       }
+                       u32 totalsize; (tailer)
+               }
+       */
+};
+
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+       palarm_fired = palarm;
+}
+
+/* a byte range locking function - return 0 on success
+   this functions locks/unlocks 1 byte at the specified offset.
+
+   On error, errno is also set so that errors are passed back properly
+   through tdb_open(). */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, 
+                     int rw_type, int lck_type, int probe)
+{
+       struct flock fl;
+       int ret;
+
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+       if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+               errno = EACCES;
+               return -1;
+       }
+
+       fl.l_type = rw_type;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = offset;
+       fl.l_len = 1;
+       fl.l_pid = 0;
+
+       do {
+               ret = fcntl(tdb->fd,lck_type,&fl);
+               if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+                       break;
+       } while (ret == -1 && errno == EINTR);
+
+       if (ret == -1) {
+               if (!probe && lck_type != F_SETLK) {
+                       /* Ensure error code is set for log fun to examine. */
+                       if (errno == EINTR && palarm_fired && *palarm_fired)
+                               tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+                       else
+                               tdb->ecode = TDB_ERR_LOCK;
+                       TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
+                                tdb->fd, offset, rw_type, lck_type));
+               }
+               /* Was it an alarm timeout ? */
+               if (errno == EINTR && palarm_fired && *palarm_fired)
+                       return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+               /* Otherwise - generic lock error. */
+               /* errno set by fcntl */
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       }
+       return 0;
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+               TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", 
+                          list, ltype));
+               return -1;
+       }
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+
+       /* Since fcntl locks don't nest, we do a lock for the first one,
+          and simply bump the count for future ones */
+       if (tdb->locked[list+1].count == 0) {
+               if (!tdb->read_only && tdb->header.rwlocks) {
+                       if (tdb_spinlock(tdb, list, ltype)) {
+                               TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list ltype=%d\n", 
+                                          list, ltype));
+                               return -1;
+                       }
+               } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+                       TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", 
+                                          list, ltype, strerror(errno)));
+                       return -1;
+               }
+               tdb->locked[list+1].ltype = ltype;
+       }
+       tdb->locked[list+1].count++;
+       return 0;
+}
+
+/* unlock the database: returns void because it's too late for errors. */
+       /* changed to return int it may be interesting to know there
+          has been an error  --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+       int ret = -1;
+
+       if (tdb->flags & TDB_NOLOCK)
+               return 0;
+
+       /* Sanity checks */
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+               TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+               return ret;
+       }
+
+       if (tdb->locked[list+1].count==0) {
+               TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+               return ret;
+       }
+
+       if (tdb->locked[list+1].count == 1) {
+               /* Down to last nested lock: unlock underneath */
+               if (!tdb->read_only && tdb->header.rwlocks) {
+                       ret = tdb_spinunlock(tdb, list, ltype);
+               } else {
+                       ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+               }
+       } else {
+               ret = 0;
+       }
+       tdb->locked[list+1].count--;
+
+       if (ret)
+               TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); 
+       return ret;
+}
+
+/* This is based on the hash algorithm from gdbm */
+static u32 tdb_hash(TDB_DATA *key)
+{
+       u32 value;      /* Used to compute the hash value.  */
+       u32   i;        /* Used to cycle through random values. */
+
+       /* Set the initial value from the key size. */
+       for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+               value = (value + (key->dptr[i] << (i*5 % 24)));
+
+       return (1103515243 * value + 12345);  
+}
+
+/* check for an out of bounds access - if it is out of bounds then
+   see if the database has been expanded by someone else and expand
+   if necessary 
+   note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
+{
+       struct stat st;
+       if (len <= tdb->map_size)
+               return 0;
+       if (tdb->flags & TDB_INTERNAL) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+                                (int)len, (int)tdb->map_size));
+               }
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+
+       if (fstat(tdb->fd, &st) == -1)
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+       if (st.st_size < (size_t)len) {
+               if (!probe) {
+                       /* Ensure ecode is set for log fn. */
+                       tdb->ecode = TDB_ERR_IO;
+                       TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+                                (int)len, (int)st.st_size));
+               }
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+
+       /* Unmap, update size, remap */
+       if (tdb_munmap(tdb) == -1)
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       tdb->map_size = st.st_size;
+       tdb_mmap(tdb);
+       return 0;
+}
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
+{
+       if (tdb_oob(tdb, off + len, 0) != 0)
+               return -1;
+
+       if (tdb->map_ptr)
+               memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+       else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+       else if (lseek(tdb->fd, off, SEEK_SET) != off
+                || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+                          off, len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+       return 0;
+}
+
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
+{
+       if (tdb_oob(tdb, off + len, 0) != 0)
+               return -1;
+
+       if (tdb->map_ptr)
+               memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+       else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+       else if (lseek(tdb->fd, off, SEEK_SET) != off
+                || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_IO;
+               TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+                          off, len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_IO, -1);
+       }
+       if (cv)
+               convert(buf, len);
+       return 0;
+}
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+       char *buf;
+
+       if (!(buf = malloc(len))) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_OOM;
+               TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+                          len, strerror(errno)));
+               return TDB_ERRCODE(TDB_ERR_OOM, buf);
+       }
+       if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       return buf;
+}
+
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
+}
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       tdb_off off = *d;
+       return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
+}
+
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+               return -1;
+       if (TDB_BAD_MAGIC(rec)) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_CORRUPT;
+               TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+               return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+       }
+       return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       struct list_struct r = *rec;
+       return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
+}
+
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
+{
+       if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
+               return -1;
+
+       if (rec->magic == TDB_MAGIC) {
+               /* this happens when a app is showdown while deleting a record - we should
+                  not completely fail when this happens */
+               TDB_LOG((tdb, 0,"rec_free_read non-free magic at offset=%d - fixing\n", 
+                        rec->magic, off));
+               rec->magic = TDB_FREE_MAGIC;
+               if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+                       return -1;
+       }
+
+       if (rec->magic != TDB_FREE_MAGIC) {
+               /* Ensure ecode is set for log fn. */
+               tdb->ecode = TDB_ERR_CORRUPT;
+               TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", 
+                          rec->magic, off));
+               return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+       }
+       if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+               return -1;
+       return 0;
+}
+
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+                        const struct list_struct *rec)
+{
+       tdb_off totalsize;
+
+       /* Offset of tailer from record header */
+       totalsize = sizeof(*rec) + rec->rec_len;
+       return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+                        &totalsize);
+}
+
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+       struct list_struct rec;
+       tdb_off tailer_ofs, tailer;
+
+       if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+               printf("ERROR: failed to read record at %u\n", offset);
+               return 0;
+       }
+
+       printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+              offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
+
+       tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+       if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+               printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+               return rec.next;
+       }
+
+       if (tailer != rec.rec_len + sizeof(rec)) {
+               printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+                               (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
+       }
+       return rec.next;
+}
+
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+       tdb_off rec_ptr, top;
+
+       top = TDB_HASH_TOP(i);
+
+       if (tdb_lock(tdb, i, F_WRLCK) != 0)
+               return -1;
+
+       if (ofs_read(tdb, top, &rec_ptr) == -1)
+               return tdb_unlock(tdb, i, F_WRLCK);
+
+       if (rec_ptr)
+               printf("hash=%d\n", i);
+
+       while (rec_ptr) {
+               rec_ptr = tdb_dump_record(tdb, rec_ptr);
+       }
+
+       return tdb_unlock(tdb, i, F_WRLCK);
+}
+
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+       int i;
+       for (i=0;i<tdb->header.hash_size;i++) {
+               tdb_dump_chain(tdb, i);
+       }
+       printf("freelist:\n");
+       tdb_dump_chain(tdb, -1);
+}
+
+int tdb_printfreelist(TDB_CONTEXT *tdb)
+{
+       int ret;
+       long total_free = 0;
+       tdb_off offset, rec_ptr;
+       struct list_struct rec;
+
+       if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+               return ret;
+
+       offset = FREELIST_TOP;
+
+       /* read in the freelist top */
+       if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+               tdb_unlock(tdb, -1, F_WRLCK);
+               return 0;
+       }
+
+       printf("freelist top=[0x%08x]\n", rec_ptr );
+       while (rec_ptr) {
+               if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return -1;
+               }
+
+               if (rec.magic != TDB_FREE_MAGIC) {
+                       printf("bad magic 0x%08x in free list\n", rec.magic);
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return -1;
+               }
+
+               printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+               total_free += rec.rec_len;
+
+               /* move to the next record */
+               rec_ptr = rec.next;
+       }
+       printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, 
+               (int)total_free);
+
+       return tdb_unlock(tdb, -1, F_WRLCK);
+}
+
+/* Remove an element from the freelist.  Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
+{
+       tdb_off last_ptr, i;
+
+       /* read in the freelist top */
+       last_ptr = FREELIST_TOP;
+       while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+               if (i == off) {
+                       /* We've found it! */
+                       return ofs_write(tdb, last_ptr, &next);
+               }
+               /* Follow chain (next offset is at start of record) */
+               last_ptr = i;
+       }
+       TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+       return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+}
+
+/* Add an element into the freelist. Merge adjacent records if
+   neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       tdb_off right, left;
+
+       /* Allocation and tailer lock */
+       if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+               return -1;
+
+       /* set an initial tailer, so if we fail we don't leave a bogus record */
+       if (update_tailer(tdb, offset, rec) != 0) {
+               TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+               goto fail;
+       }
+
+       /* Look right first (I'm an Australian, dammit) */
+       right = offset + sizeof(*rec) + rec->rec_len;
+       if (right + sizeof(*rec) <= tdb->map_size) {
+               struct list_struct r;
+
+               if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+                       goto left;
+               }
+
+               /* If it's free, expand to include it. */
+               if (r.magic == TDB_FREE_MAGIC) {
+                       if (remove_from_freelist(tdb, right, r.next) == -1) {
+                               TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+                               goto left;
+                       }
+                       rec->rec_len += sizeof(r) + r.rec_len;
+               }
+       }
+
+left:
+       /* Look left */
+       left = offset - sizeof(tdb_off);
+       if (left > TDB_HASH_TOP(tdb->header.hash_size-1)) {
+               struct list_struct l;
+               tdb_off leftsize;
+
+               /* Read in tailer and jump back to header */
+               if (ofs_read(tdb, left, &leftsize) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+                       goto update;
+               }
+               left = offset - leftsize;
+
+               /* Now read in record */
+               if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+                       goto update;
+               }
+
+               /* If it's free, expand to include it. */
+               if (l.magic == TDB_FREE_MAGIC) {
+                       if (remove_from_freelist(tdb, left, l.next) == -1) {
+                               TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+                               goto update;
+                       } else {
+                               offset = left;
+                               rec->rec_len += leftsize;
+                       }
+               }
+       }
+
+update:
+       if (update_tailer(tdb, offset, rec) == -1) {
+               TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+               goto fail;
+       }
+
+       /* Now, prepend to free list */
+       rec->magic = TDB_FREE_MAGIC;
+
+       if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+           rec_write(tdb, offset, rec) == -1 ||
+           ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+               TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+               goto fail;
+       }
+
+       /* And we're done. */
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return -1;
+}
+
+
+/* expand a file.  we prefer to use ftruncate, as that is what posix
+  says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+       char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+       if (ftruncate(tdb->fd, size+addition) != 0) {
+               TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", 
+                          size+addition, strerror(errno)));
+               return -1;
+       }
+#else
+       char b = 0;
+
+#ifdef HAVE_PWRITE
+       if (pwrite(tdb->fd,  &b, 1, (size+addition) - 1) != 1) {
+#else
+       if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || 
+           write(tdb->fd, &b, 1) != 1) {
+#endif
+               TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", 
+                          size+addition, strerror(errno)));
+               return -1;
+       }
+#endif
+
+       /* now fill the file with something. This ensures that the file isn't sparse, which would be
+          very bad if we ran out of disk. This must be done with write, not via mmap */
+       memset(buf, 0x42, sizeof(buf));
+       while (addition) {
+               int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+               int ret = pwrite(tdb->fd, buf, n, size);
+#else
+               int ret;
+               if (lseek(tdb->fd, size, SEEK_SET) != size)
+                       return -1;
+               ret = write(tdb->fd, buf, n);
+#endif
+               if (ret != n) {
+                       TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", 
+                                  n, strerror(errno)));
+                       return -1;
+               }
+               addition -= n;
+               size += n;
+       }
+       return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+   file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
+{
+       struct list_struct rec;
+       tdb_off offset;
+
+       if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+               TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+               return -1;
+       }
+
+       /* must know about any previous expansions by another process */
+       tdb_oob(tdb, tdb->map_size + 1, 1);
+
+       /* always make room for at least 10 more records, and round
+           the database up to a multiple of TDB_PAGE_SIZE */
+       size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+       if (!(tdb->flags & TDB_INTERNAL))
+               tdb_munmap(tdb);
+
+       /*
+        * We must ensure the file is unmapped before doing this
+        * to ensure consistency with systems like OpenBSD where
+        * writes and mmaps are not consistent.
+        */
+
+       /* expand the file itself */
+       if (!(tdb->flags & TDB_INTERNAL)) {
+               if (expand_file(tdb, tdb->map_size, size) != 0)
+                       goto fail;
+       }
+
+       tdb->map_size += size;
+
+       if (tdb->flags & TDB_INTERNAL)
+               tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+       else {
+               /*
+                * We must ensure the file is remapped before adding the space
+                * to ensure consistency with systems like OpenBSD where
+                * writes and mmaps are not consistent.
+                */
+
+               /* We're ok if the mmap fails as we'll fallback to read/write */
+               tdb_mmap(tdb);
+       }
+
+       /* form a new freelist record */
+       memset(&rec,'\0',sizeof(rec));
+       rec.rec_len = size - sizeof(rec);
+
+       /* link it into the free list */
+       offset = tdb->map_size - size;
+       if (tdb_free(tdb, offset, &rec) == -1)
+               goto fail;
+
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+   to a unconnected list_struct within the database with room for at
+   least length bytes of total data
+
+   0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+                           struct list_struct *rec)
+{
+       tdb_off rec_ptr, last_ptr, newrec_ptr;
+       struct list_struct newrec;
+
+       if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+               return 0;
+
+       /* Extra bytes required for tailer */
+       length += sizeof(tdb_off);
+
+ again:
+       last_ptr = FREELIST_TOP;
+
+       /* read in the freelist top */
+       if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+               goto fail;
+
+       /* keep looking until we find a freelist record big enough */
+       while (rec_ptr) {
+               if (rec_free_read(tdb, rec_ptr, rec) == -1)
+                       goto fail;
+
+               if (rec->rec_len >= length) {
+                       /* found it - now possibly split it up  */
+                       if (rec->rec_len > length + MIN_REC_SIZE) {
+                               /* Length of left piece */
+                               length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+                               /* Right piece to go on free list */
+                               newrec.rec_len = rec->rec_len
+                                       - (sizeof(*rec) + length);
+                               newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+                               /* And left record is shortened */
+                               rec->rec_len = length;
+                       } else
+                               newrec_ptr = 0;
+
+                       /* Remove allocated record from the free list */
+                       if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+                               goto fail;
+
+                       /* Update header: do this before we drop alloc
+                           lock, otherwise tdb_free() might try to
+                           merge with us, thinking we're free.
+                           (Thanks Jeremy Allison). */
+                       rec->magic = TDB_MAGIC;
+                       if (rec_write(tdb, rec_ptr, rec) == -1)
+                               goto fail;
+
+                       /* Did we create new block? */
+                       if (newrec_ptr) {
+                               /* Update allocated record tailer (we
+                                   shortened it). */
+                               if (update_tailer(tdb, rec_ptr, rec) == -1)
+                                       goto fail;
+
+                               /* Free new record */
+                               if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+                                       goto fail;
+                       }
+
+                       /* all done - return the new record offset */
+                       tdb_unlock(tdb, -1, F_WRLCK);
+                       return rec_ptr;
+               }
+               /* move to the next record */
+               last_ptr = rec_ptr;
+               rec_ptr = rec->next;
+       }
+       /* we didn't find enough space. See if we can expand the
+          database and if we can then try again */
+       if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+               goto again;
+ fail:
+       tdb_unlock(tdb, -1, F_WRLCK);
+       return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+       struct tdb_header *newdb;
+       int size, ret = -1;
+
+       /* We make it up in memory, then write it out if not internal */
+       size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+       if (!(newdb = calloc(size, 1)))
+               return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+       /* Fill in the header */
+       newdb->version = TDB_VERSION;
+       newdb->hash_size = hash_size;
+#ifdef USE_SPINLOCKS
+       newdb->rwlocks = size;
+#endif
+       if (tdb->flags & TDB_INTERNAL) {
+               tdb->map_size = size;
+               tdb->map_ptr = (char *)newdb;
+               memcpy(&tdb->header, newdb, sizeof(tdb->header));
+               /* Convert the `ondisk' version if asked. */
+               CONVERT(*newdb);
+               return 0;
+       }
+       if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+               goto fail;
+
+       if (ftruncate(tdb->fd, 0) == -1)
+               goto fail;
+
+       /* This creates an endian-converted header, as if read from disk */
+       CONVERT(*newdb);
+       memcpy(&tdb->header, newdb, sizeof(tdb->header));
+       /* Don't endian-convert the magic food! */
+       memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+       if (write(tdb->fd, newdb, size) != size)
+               ret = -1;
+       else
+               ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+  fail:
+       SAFE_FREE(newdb);
+       return ret;
+}
+
+/* Returns 0 on fail.  On success, return offset of record, and fills
+   in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+                       struct list_struct *r)
+{
+       tdb_off rec_ptr;
+       
+       /* read in the hash top */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+               return 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               if (rec_read(tdb, rec_ptr, r) == -1)
+                       return 0;
+
+               if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+                       char *k;
+                       /* a very likely hit - read the key */
+                       k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), 
+                                          r->key_len);
+                       if (!k)
+                               return 0;
+
+                       if (memcmp(key.dptr, k, key.dsize) == 0) {
+                               SAFE_FREE(k);
+                               return rec_ptr;
+                       }
+                       SAFE_FREE(k);
+               }
+               rec_ptr = r->next;
+       }
+       return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
+
+/* If they do lockkeys, check that this hash is one they locked */
+static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash)
+{
+       u32 i;
+       if (!tdb->lockedkeys)
+               return 1;
+       for (i = 0; i < tdb->lockedkeys[0]; i++)
+               if (tdb->lockedkeys[i+1] == hash)
+                       return 1;
+       return TDB_ERRCODE(TDB_ERR_NOLOCK, 0);
+}
+
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock(TDB_CONTEXT *tdb, TDB_DATA key, int locktype,
+                            struct list_struct *rec)
+{
+       u32 hash, rec_ptr;
+
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return 0;
+       if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+               return 0;
+       if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+               tdb_unlock(tdb, BUCKET(hash), locktype);
+       return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+       return tdb->ecode;
+}
+
+static struct tdb_errname {
+       enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+            {TDB_ERR_CORRUPT, "Corrupt database"},
+            {TDB_ERR_IO, "IO Error"},
+            {TDB_ERR_LOCK, "Locking error"},
+            {TDB_ERR_OOM, "Out of memory"},
+            {TDB_ERR_EXISTS, "Record exists"},
+            {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+            {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+               if (tdb->ecode == emap[i].ecode)
+                       return emap[i].estring;
+       return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1.
+*/
+
+static int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+{
+       struct list_struct rec;
+       tdb_off rec_ptr;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
+               return -1;
+
+       /* must be long enough key, data and tailer */
+       if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+               tdb->ecode = TDB_SUCCESS; /* Not really an error */
+               return -1;
+       }
+
+       if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+                     dbuf.dptr, dbuf.dsize) == -1)
+               return -1;
+
+       if (dbuf.dsize != rec.data_len) {
+               /* update size */
+               rec.data_len = dbuf.dsize;
+               return rec_write(tdb, rec_ptr, &rec);
+       }
+       return 0;
+}
+
+/* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       TDB_DATA ret;
+
+       /* find which hash bucket it is in */
+       if (!(rec_ptr = tdb_find_lock(tdb,key,F_RDLCK,&rec)))
+               return tdb_null;
+
+       if (rec.data_len)
+               ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+                                         rec.data_len);
+       else
+               ret.dptr = NULL;
+       ret.dsize = rec.data_len;
+       tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+       return ret;
+}
+
+/* check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+*/
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       struct list_struct rec;
+       
+       if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0)
+               return 0;
+       tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+       return 1;
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+  Write locks override our own fcntl readlocks, so check it here.
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       struct tdb_traverse_lock *i;
+       for (i = &tdb->travlocks; i; i = i->next)
+               if (i->off == off)
+                       return -1;
+       return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
+
+/*
+  Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+  an error to fail to get the lock here.
+*/
+
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+}
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+       struct tdb_traverse_lock *i;
+       u32 count = 0;
+
+       if (off == 0)
+               return 0;
+       for (i = &tdb->travlocks; i; i = i->next)
+               if (i->off == off)
+                       count++;
+       return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
+{
+       tdb_off last_ptr, i;
+       struct list_struct lastrec;
+
+       if (tdb->read_only) return -1;
+
+       if (write_lock_record(tdb, rec_ptr) == -1) {
+               /* Someone traversing here: mark it as dead */
+               rec->magic = TDB_DEAD_MAGIC;
+               return rec_write(tdb, rec_ptr, rec);
+       }
+       if (write_unlock_record(tdb, rec_ptr) != 0)
+               return -1;
+
+       /* find previous record in hash chain */
+       if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+               return -1;
+       for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+               if (rec_read(tdb, i, &lastrec) == -1)
+                       return -1;
+
+       /* unlink it: next ptr is at start of record. */
+       if (last_ptr == 0)
+               last_ptr = TDB_HASH_TOP(rec->full_hash);
+       if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+               return -1;
+
+       /* recover the space */
+       if (tdb_free(tdb, rec_ptr, rec) == -1)
+               return -1;
+       return 0;
+}
+
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+                        struct list_struct *rec)
+{
+       int want_next = (tlock->off != 0);
+
+       /* No traversal allows if you've called tdb_lockkeys() */
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+
+       /* Lock each chain from the start one. */
+       for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+               if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+                       return -1;
+
+               /* No previous record?  Start at top of chain. */
+               if (!tlock->off) {
+                       if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+                                    &tlock->off) == -1)
+                               goto fail;
+               } else {
+                       /* Otherwise unlock the previous record. */
+                       if (unlock_record(tdb, tlock->off) != 0)
+                               goto fail;
+               }
+
+               if (want_next) {
+                       /* We have offset of old record: grab next */
+                       if (rec_read(tdb, tlock->off, rec) == -1)
+                               goto fail;
+                       tlock->off = rec->next;
+               }
+
+               /* Iterate through chain */
+               while( tlock->off) {
+                       tdb_off current;
+                       if (rec_read(tdb, tlock->off, rec) == -1)
+                               goto fail;
+                       if (!TDB_DEAD(rec)) {
+                               /* Woohoo: we found one! */
+                               if (lock_record(tdb, tlock->off) != 0)
+                                       goto fail;
+                               return tlock->off;
+                       }
+                       /* Try to clean dead ones from old traverses */
+                       current = tlock->off;
+                       tlock->off = rec->next;
+                       if (do_delete(tdb, current, rec) != 0)
+                               goto fail;
+               }
+               tdb_unlock(tdb, tlock->hash, F_WRLCK);
+               want_next = 0;
+       }
+       /* We finished iteration without finding anything */
+       return TDB_ERRCODE(TDB_SUCCESS, 0);
+
+ fail:
+       tlock->off = 0;
+       if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
+       return -1;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+   return -1 on error or the record count traversed
+   if fn is NULL then it is not called
+   a non-zero return value from fn() indicates that the traversal should stop
+  */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state)
+{
+       TDB_DATA key, dbuf;
+       struct list_struct rec;
+       struct tdb_traverse_lock tl = { NULL, 0, 0 };
+       int ret, count = 0;
+
+       /* This was in the initializaton, above, but the IRIX compiler
+        * did not like it.  crh
+        */
+       tl.next = tdb->travlocks.next;
+
+       /* fcntl locks don't stack: beware traverse inside traverse */
+       tdb->travlocks.next = &tl;
+
+       /* tdb_next_lock places locks on the record returned, and its chain */
+       while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+               count++;
+               /* now read the full record */
+               key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), 
+                                         rec.key_len + rec.data_len);
+               if (!key.dptr) {
+                       ret = -1;
+                       if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+                               goto out;
+                       if (unlock_record(tdb, tl.off) != 0)
+                               TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+                       goto out;
+               }
+               key.dsize = rec.key_len;
+               dbuf.dptr = key.dptr + rec.key_len;
+               dbuf.dsize = rec.data_len;
+
+               /* Drop chain lock, call out */
+               if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+                       ret = -1;
+                       goto out;
+               }
+               if (fn && fn(tdb, key, dbuf, state)) {
+                       /* They want us to terminate traversal */
+                       ret = count;
+                       if (unlock_record(tdb, tl.off) != 0) {
+                               TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+                               ret = -1;
+                       }
+                       tdb->travlocks.next = tl.next;
+                       SAFE_FREE(key.dptr);
+                       return count;
+               }
+               SAFE_FREE(key.dptr);
+       }
+out:
+       tdb->travlocks.next = tl.next;
+       if (ret < 0)
+               return -1;
+       else
+               return count;
+}
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+       TDB_DATA key;
+       struct list_struct rec;
+
+       /* release any old lock */
+       if (unlock_record(tdb, tdb->travlocks.off) != 0)
+               return tdb_null;
+       tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+       if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+               return tdb_null;
+       /* now read the key */
+       key.dsize = rec.key_len;
+       key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+       if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+       return key;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+       u32 oldhash;
+       TDB_DATA key = tdb_null;
+       struct list_struct rec;
+       char *k = NULL;
+
+       /* Is locked key the old key?  If so, traverse will be reliable. */
+       if (tdb->travlocks.off) {
+               if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+                       return tdb_null;
+               if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+                   || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+                                           rec.key_len))
+                   || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+                       /* No, it wasn't: unlock it and start from scratch */
+                       if (unlock_record(tdb, tdb->travlocks.off) != 0)
+                               return tdb_null;
+                       if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+                               return tdb_null;
+                       tdb->travlocks.off = 0;
+               }
+
+               SAFE_FREE(k);
+       }
+
+       if (!tdb->travlocks.off) {
+               /* No previous element: do normal find, and lock record */
+               tdb->travlocks.off = tdb_find_lock(tdb, oldkey, F_WRLCK, &rec);
+               if (!tdb->travlocks.off)
+                       return tdb_null;
+               tdb->travlocks.hash = BUCKET(rec.full_hash);
+               if (lock_record(tdb, tdb->travlocks.off) != 0) {
+                       TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+                       return tdb_null;
+               }
+       }
+       oldhash = tdb->travlocks.hash;
+
+       /* Grab next record: locks chain and returned record,
+          unlocks old record */
+       if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+               key.dsize = rec.key_len;
+               key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+                                         key.dsize);
+               /* Unlock the chain of this new record */
+               if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+                       TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+       }
+       /* Unlock the chain of old record */
+       if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+       return key;
+}
+
+/* delete an entry in the database given a key */
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       int ret;
+
+       if (!(rec_ptr = tdb_find_lock(tdb, key, F_WRLCK, &rec)))
+               return -1;
+       ret = do_delete(tdb, rec_ptr, &rec);
+       if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+               TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
+       return ret;
+}
+
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+       struct list_struct rec;
+       u32 hash;
+       tdb_off rec_ptr;
+       char *p = NULL;
+       int ret = 0;
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return -1;
+       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+               return -1;
+
+       /* check for it existing, on insert. */
+       if (flag == TDB_INSERT) {
+               if (tdb_exists(tdb, key)) {
+                       tdb->ecode = TDB_ERR_EXISTS;
+                       goto fail;
+               }
+       } else {
+               /* first try in-place update, on modify or replace. */
+               if (tdb_update(tdb, key, dbuf) == 0)
+                       goto out;
+               if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST)
+                       goto fail;
+       }
+       /* reset the error code potentially set by the tdb_update() */
+       tdb->ecode = TDB_SUCCESS;
+
+       /* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+       if (flag != TDB_INSERT)
+               tdb_delete(tdb, key);
+
+       /* Copy key+value *before* allocating free space in case malloc
+          fails and we are left with a dead spot in the tdb. */
+
+       if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+               tdb->ecode = TDB_ERR_OOM;
+               goto fail;
+       }
+
+       memcpy(p, key.dptr, key.dsize);
+       if (dbuf.dsize)
+               memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
+
+       /* now we're into insert / modify / replace of a record which
+        * we know could not be optimised by an in-place store (for
+        * various reasons).  */
+       if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+               goto fail;
+
+       /* Read hash top into next ptr */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+               goto fail;
+
+       rec.key_len = key.dsize;
+       rec.data_len = dbuf.dsize;
+       rec.full_hash = hash;
+       rec.magic = TDB_MAGIC;
+
+       /* write out and point the top of the hash chain at it */
+       if (rec_write(tdb, rec_ptr, &rec) == -1
+           || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+           || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+               /* Need to tdb_unallocate() here */
+               goto fail;
+       }
+ out:
+       SAFE_FREE(p); 
+       tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+       return ret;
+fail:
+       ret = -1;
+       goto out;
+}
+
+/* Attempt to append data to an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+       struct list_struct rec;
+       tdb_off rec_ptr;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec)))
+               return -1;
+
+       /* Append of 0 is always ok. */
+       if (new_dbuf.dsize == 0)
+               return 0;
+
+       /* must be long enough for key, old data + new data and tailer */
+       if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+               /* No room. */
+               tdb->ecode = TDB_SUCCESS; /* Not really an error */
+               return -1;
+       }
+
+       if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+                     new_dbuf.dptr, new_dbuf.dsize) == -1)
+               return -1;
+
+       /* update size */
+       rec.data_len += new_dbuf.dsize;
+       return rec_write(tdb, rec_ptr, &rec);
+}
+
+/* Append to an entry. Create if not exist. */
+
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+       struct list_struct rec;
+       u32 hash;
+       tdb_off rec_ptr;
+       char *p = NULL;
+       int ret = 0;
+       size_t new_data_size = 0;
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+       if (!tdb_keylocked(tdb, hash))
+               return -1;
+       if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+               return -1;
+
+       /* first try in-place. */
+       if (tdb_append_inplace(tdb, key, new_dbuf) == 0)
+               goto out;
+
+       /* reset the error code potentially set by the tdb_append_inplace() */
+       tdb->ecode = TDB_SUCCESS;
+
+       /* find entry */
+       if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+               if (tdb->ecode != TDB_ERR_NOEXIST)
+                       goto fail;
+
+               /* Not found - create. */
+
+               ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+               goto out;
+       }
+
+       new_data_size = rec.data_len + new_dbuf.dsize;
+
+       /* Copy key+old_value+value *before* allocating free space in case malloc
+          fails and we are left with a dead spot in the tdb. */
+
+       if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+               tdb->ecode = TDB_ERR_OOM;
+               goto fail;
+       }
+
+       /* Copy the key in place. */
+       memcpy(p, key.dptr, key.dsize);
+
+       /* Now read the old data into place. */
+       if (rec.data_len &&
+               tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+                       goto fail;
+
+       /* Finally append the new data. */
+       if (new_dbuf.dsize)
+               memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
+
+       /* delete any existing record - if it doesn't exist we don't
+           care.  Doing this first reduces fragmentation, and avoids
+           coalescing with `allocated' block before it's updated. */
+
+       tdb_delete(tdb, key);
+
+       if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+               goto fail;
+
+       /* Read hash top into next ptr */
+       if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+               goto fail;
+
+       rec.key_len = key.dsize;
+       rec.data_len = new_data_size;
+       rec.full_hash = hash;
+       rec.magic = TDB_MAGIC;
+
+       /* write out and point the top of the hash chain at it */
+       if (rec_write(tdb, rec_ptr, &rec) == -1
+           || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+           || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+               /* Need to tdb_unallocate() here */
+               goto fail;
+       }
+
+ out:
+       SAFE_FREE(p); 
+       tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+       return ret;
+
+fail:
+       ret = -1;
+       goto out;
+}
+
+static int tdb_already_open(dev_t device,
+                           ino_t ino)
+{
+       TDB_CONTEXT *i;
+       
+       for (i = tdbs; i; i = i->next) {
+               if (i->device == device && i->inode == ino) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the
+   database file. A flags value of O_WRONLY is invalid. The hash size
+   is advisory, use zero for a default value.
+
+   Return is NULL on error, in which case errno is also set.  Don't 
+   try to call tdb_error or tdb_errname, just do strerror(errno).
+
+   @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+{
+       return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL);
+}
+
+
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+                        int open_flags, mode_t mode,
+                        tdb_log_func log_fn)
+{
+       TDB_CONTEXT *tdb;
+       struct stat st;
+       int rev = 0, locked;
+       unsigned char *vp;
+       u32 vertest;
+
+       if (!(tdb = calloc(1, sizeof *tdb))) {
+               /* Can't log this */
+               errno = ENOMEM;
+               goto fail;
+       }
+       tdb->fd = -1;
+       tdb->name = NULL;
+       tdb->map_ptr = NULL;
+       tdb->lockedkeys = NULL;
+       tdb->flags = tdb_flags;
+       tdb->open_flags = open_flags;
+       tdb->log_fn = log_fn;
+       
+       if ((open_flags & O_ACCMODE) == O_WRONLY) {
+               TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+                        name));
+               errno = EINVAL;
+               goto fail;
+       }
+       
+       if (hash_size == 0)
+               hash_size = DEFAULT_HASH_SIZE;
+       if ((open_flags & O_ACCMODE) == O_RDONLY) {
+               tdb->read_only = 1;
+               /* read only databases don't do locking or clear if first */
+               tdb->flags |= TDB_NOLOCK;
+               tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+       }
+
+       /* internal databases don't mmap or lock, and start off cleared */
+       if (tdb->flags & TDB_INTERNAL) {
+               tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+               tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+               if (tdb_new_database(tdb, hash_size) != 0) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+                       goto fail;
+               }
+               goto internal;
+       }
+
+       if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+               TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+                        name, strerror(errno)));
+               goto fail;      /* errno set by open(2) */
+       }
+
+       /* ensure there is only one process initialising at once */
+       if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+               TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+                        name, strerror(errno)));
+               goto fail;      /* errno set by tdb_brlock */
+       }
+
+       /* we need to zero database if we are the only one with it open */
+       if ((locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))
+           && (tdb_flags & TDB_CLEAR_IF_FIRST)) {
+               open_flags |= O_CREAT;
+               if (ftruncate(tdb->fd, 0) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: "
+                                "failed to truncate %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail; /* errno set by ftruncate */
+               }
+       }
+
+       if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+           || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+           || tdb->header.version != TDB_VERSION
+           || (tdb->header.hash_size != hash_size
+               && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
+               /* its not a valid database - possibly initialise it */
+               if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+                       errno = EIO; /* ie bad format or something */
+                       goto fail;
+               }
+               rev = (tdb->flags & TDB_CONVERT);
+       }
+       vp = (unsigned char *)&tdb->header.version;
+       vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+                 (((u32)vp[2]) << 8) | (u32)vp[3];
+       tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+       if (!rev)
+               tdb->flags &= ~TDB_CONVERT;
+       else {
+               tdb->flags |= TDB_CONVERT;
+               convert(&tdb->header, sizeof(tdb->header));
+       }
+       if (fstat(tdb->fd, &st) == -1)
+               goto fail;
+
+       /* Is it already in the open list?  If so, fail. */
+       if (tdb_already_open(st.st_dev, st.st_ino)) {
+               TDB_LOG((tdb, 2, "tdb_open_ex: "
+                        "%s (%d,%d) is already open in this process\n",
+                        name, st.st_dev, st.st_ino));
+               errno = EBUSY;
+               goto fail;
+       }
+
+       if (!(tdb->name = (char *)strdup(name))) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       tdb->map_size = st.st_size;
+       tdb->device = st.st_dev;
+       tdb->inode = st.st_ino;
+       tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+       if (!tdb->locked) {
+               TDB_LOG((tdb, 2, "tdb_open_ex: "
+                        "failed to allocate lock structure for %s\n",
+                        name));
+               errno = ENOMEM;
+               goto fail;
+       }
+       tdb_mmap(tdb);
+       if (locked) {
+               if (!tdb->read_only)
+                       if (tdb_clear_spinlocks(tdb) != 0) {
+                               TDB_LOG((tdb, 0, "tdb_open_ex: "
+                               "failed to clear spinlock\n"));
+                               goto fail;
+                       }
+               if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+                       TDB_LOG((tdb, 0, "tdb_open_ex: "
+                                "failed to take ACTIVE_LOCK on %s: %s\n",
+                                name, strerror(errno)));
+                       goto fail;
+               }
+       }
+       /* leave this lock in place to indicate it's in use */
+       if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+               goto fail;
+
+ internal:
+       /* Internal (memory-only) databases skip all the code above to
+        * do with disk files, and resume here by releasing their
+        * global lock and hooking into the active list. */
+       if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+               goto fail;
+       tdb->next = tdbs;
+       tdbs = tdb;
+       return tdb;
+
+ fail:
+       { int save_errno = errno;
+
+       if (!tdb)
+               return NULL;
+       
+       if (tdb->map_ptr) {
+               if (tdb->flags & TDB_INTERNAL)
+                       SAFE_FREE(tdb->map_ptr);
+               else
+                       tdb_munmap(tdb);
+       }
+       SAFE_FREE(tdb->name);
+       if (tdb->fd != -1)
+               if (close(tdb->fd) != 0)
+                       TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+       SAFE_FREE(tdb->locked);
+       SAFE_FREE(tdb);
+       errno = save_errno;
+       return NULL;
+       }
+}
+
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
+int tdb_close(TDB_CONTEXT *tdb)
+{
+       TDB_CONTEXT **i;
+       int ret = 0;
+
+       if (tdb->map_ptr) {
+               if (tdb->flags & TDB_INTERNAL)
+                       SAFE_FREE(tdb->map_ptr);
+               else
+                       tdb_munmap(tdb);
+       }
+       SAFE_FREE(tdb->name);
+       if (tdb->fd != -1)
+               ret = close(tdb->fd);
+       SAFE_FREE(tdb->locked);
+       SAFE_FREE(tdb->lockedkeys);
+
+       /* Remove from contexts list */
+       for (i = &tdbs; *i; i = &(*i)->next) {
+               if (*i == tdb) {
+                       *i = tdb->next;
+                       break;
+               }
+       }
+
+       memset(tdb, 0, sizeof(*tdb));
+       SAFE_FREE(tdb);
+
+       return ret;
+}
+
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
+{
+       u32 i;
+
+       /* There are no locks on read-only dbs */
+       if (tdb->read_only)
+               return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       for (i = 0; i < tdb->header.hash_size; i++) 
+               if (tdb_lock(tdb, i, F_WRLCK))
+                       break;
+
+       /* If error, release locks we have... */
+       if (i < tdb->header.hash_size) {
+               u32 j;
+
+               for ( j = 0; j < i; j++)
+                       tdb_unlock(tdb, j, F_WRLCK);
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       }
+
+       return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i=0; i < tdb->header.hash_size; i++)
+               tdb_unlock(tdb, i, F_WRLCK);
+}
+
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[])
+{
+       u32 i, j, hash;
+
+       /* Can't lock more keys if already locked */
+       if (tdb->lockedkeys)
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       if (!(tdb->lockedkeys = malloc(sizeof(u32) * (number+1))))
+               return TDB_ERRCODE(TDB_ERR_OOM, -1);
+       /* First number in array is # keys */
+       tdb->lockedkeys[0] = number;
+
+       /* Insertion sort by bucket */
+       for (i = 0; i < number; i++) {
+               hash = tdb_hash(&keys[i]);
+               for (j = 0; j < i && BUCKET(tdb->lockedkeys[j+1]) < BUCKET(hash); j++);
+                       memmove(&tdb->lockedkeys[j+2], &tdb->lockedkeys[j+1], sizeof(u32) * (i-j));
+               tdb->lockedkeys[j+1] = hash;
+       }
+       /* Finally, lock in order */
+       for (i = 0; i < number; i++)
+               if (tdb_lock(tdb, i, F_WRLCK))
+                       break;
+
+       /* If error, release locks we have... */
+       if (i < number) {
+               for ( j = 0; j < i; j++)
+                       tdb_unlock(tdb, j, F_WRLCK);
+               SAFE_FREE(tdb->lockedkeys);
+               return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+       }
+       return 0;
+}
+
+/* Unlock the keys previously locked by tdb_lockkeys() */
+void tdb_unlockkeys(TDB_CONTEXT *tdb)
+{
+       u32 i;
+       for (i = 0; i < tdb->lockedkeys[0]; i++)
+               tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK);
+       SAFE_FREE(tdb->lockedkeys);
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+   contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+       tdb->log_fn = fn;
+}
+
+
+/* reopen a tdb - this is used after a fork to ensure that we have an independent
+   seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+       struct stat st;
+
+       if (tdb_munmap(tdb) != 0) {
+               TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (close(tdb->fd) != 0)
+               TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+       tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+       if (tdb->fd == -1) {
+               TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (fstat(tdb->fd, &st) != 0) {
+               TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+               goto fail;
+       }
+       if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+               TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+               goto fail;
+       }
+       tdb_mmap(tdb);
+       if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) {
+               TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       tdb_close(tdb);
+       return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
+{
+       TDB_CONTEXT *tdb;
+
+       for (tdb=tdbs; tdb; tdb = tdb->next) {
+               if (tdb_reopen(tdb) != 0) return -1;
+       }
+
+       return 0;
+}
diff --git a/source4/tdb/tdb.h b/source4/tdb/tdb.h
new file mode 100644 (file)
index 0000000..6f3b1ff
--- /dev/null
@@ -0,0 +1,144 @@
+#ifndef __TDB_H__
+#define __TDB_H__
+
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell 1999
+   
+   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.
+*/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK   4 /* don't do any locking */
+#define TDB_NOMMAP   8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
+               TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT };
+
+#ifndef u32
+#define u32 unsigned
+#endif
+
+typedef struct {
+       char *dptr;
+       size_t dsize;
+} TDB_DATA;
+
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+       char magic_food[32]; /* for /etc/magic */
+       u32 version; /* version of the code */
+       u32 hash_size; /* number of hash entries */
+       tdb_off rwlocks;
+       tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+       u32 count;
+       u32 ltype;
+};
+
+struct tdb_traverse_lock {
+       struct tdb_traverse_lock *next;
+       u32 off;
+       u32 hash;
+};
+
+/* this is the context structure that is returned from a db open */
+typedef struct tdb_context {
+       char *name; /* the name of the database */
+       void *map_ptr; /* where it is currently mapped */
+       int fd; /* open file descriptor for the database */
+       tdb_len map_size; /* how much space has been mapped */
+       int read_only; /* opened read-only */
+       struct tdb_lock_type *locked; /* array of chain locks */
+       enum TDB_ERROR ecode; /* error code for last tdb error */
+       struct tdb_header header; /* a cached copy of the header */
+       u32 flags; /* the flags passed to tdb_open */
+       u32 *lockedkeys; /* array of locked keys: first is #keys */
+       struct tdb_traverse_lock travlocks; /* current traversal locks */
+       struct tdb_context *next; /* all tdbs to avoid multiple opens */
+       dev_t device;   /* uniquely identifies this tdb */
+       ino_t inode;    /* uniquely identifies this tdb */
+       void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
+       int open_flags; /* flags used in the open - needed by reopen */
+} TDB_CONTEXT;
+
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+                        int open_flags, mode_t mode,
+                        tdb_log_func log_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/source4/tdb/tdb.magic b/source4/tdb/tdb.magic
new file mode 100644 (file)
index 0000000..f5619e7
--- /dev/null
@@ -0,0 +1,10 @@
+# Magic file(1) information about tdb files.
+#
+# Install this into /etc/magic or the corresponding location for your
+# system, or pass as a -m argument to file(1).
+
+# You may use and redistribute this file without restriction.
+
+0      string  TDB\ file               TDB database
+>32    lelong  =0x2601196D             version 6, little-endian
+>>36   lelong  x                       hash size %d bytes
diff --git a/source4/tdb/tdbbackup.c b/source4/tdb/tdbbackup.c
new file mode 100644 (file)
index 0000000..7b344de
--- /dev/null
@@ -0,0 +1,315 @@
+/* 
+   Unix SMB/CIFS implementation.
+   low level tdb backup and restore utility
+   Copyright (C) Andrew Tridgell              2002
+
+   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.
+*/
+
+/*
+
+  This program is meant for backup/restore of tdb databases. Typical usage would be:
+     tdbbackup *.tdb
+  when Samba shuts down cleanly, which will make a backup of all the local databases
+  to *.bak files. Then on Samba startup you would use:
+     tdbbackup -v *.tdb
+  and this will check the databases for corruption and if corruption is detected then
+  the backup will be restored.
+
+  You may also like to do a backup on a regular basis while Samba is
+  running, perhaps using cron.
+
+  The reason this program is needed is to cope with power failures
+  while Samba is running. A power failure could lead to database
+  corruption and Samba will then not start correctly.
+
+  Note that many of the databases in Samba are transient and thus
+  don't need to be backed up, so you can optimise the above a little
+  by only running the backup on the critical databases.
+
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static int failed;
+
+static char *add_suffix(const char *name, const char *suffix)
+{
+       char *ret;
+       int len = strlen(name) + strlen(suffix) + 1;
+       ret = malloc(len);
+       if (!ret) {
+               fprintf(stderr,"Out of memory!\n");
+               exit(1);
+       }
+       strncpy(ret, name, len);
+       strncat(ret, suffix, len);
+       return ret;
+}
+
+static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
+
+       if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
+               fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
+               failed = 1;
+               return 1;
+       }
+       return 0;
+}
+
+
+static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       return 0;
+}
+
+/*
+  carefully backup a tdb, validating the contents and
+  only doing the backup if its OK
+  this function is also used for restore
+*/
+static int backup_tdb(const char *old_name, const char *new_name)
+{
+       TDB_CONTEXT *tdb;
+       TDB_CONTEXT *tdb_new;
+       char *tmp_name;
+       struct stat st;
+       int count1, count2;
+
+       tmp_name = add_suffix(new_name, ".tmp");
+
+       /* stat the old tdb to find its permissions */
+       if (stat(old_name, &st) != 0) {
+               perror(old_name);
+               return 1;
+       }
+
+       /* open the old tdb */
+       tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
+       if (!tdb) {
+               printf("Failed to open %s\n", old_name);
+               return 1;
+       }
+
+       /* create the new tdb */
+       unlink(tmp_name);
+       tdb_new = tdb_open(tmp_name, tdb->header.hash_size, 
+                          TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, 
+                          st.st_mode & 0777);
+       if (!tdb_new) {
+               perror(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* lock the old tdb */
+       if (tdb_lockall(tdb) != 0) {
+               fprintf(stderr,"Failed to lock %s\n", old_name);
+               tdb_close(tdb);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       failed = 0;
+
+       /* traverse and copy */
+       count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
+       if (count1 < 0 || failed) {
+               fprintf(stderr,"failed to copy %s\n", old_name);
+               tdb_close(tdb);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* close the old tdb */
+       tdb_close(tdb);
+
+       /* close the new tdb and re-open read-only */
+       tdb_close(tdb_new);
+       tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
+       if (!tdb_new) {
+               fprintf(stderr,"failed to reopen %s\n", tmp_name);
+               unlink(tmp_name);
+               perror(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+       
+       /* traverse the new tdb to confirm */
+       count2 = tdb_traverse(tdb_new, test_fn, 0);
+       if (count2 != count1) {
+               fprintf(stderr,"failed to copy %s\n", old_name);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* make sure the new tdb has reached stable storage */
+       fsync(tdb_new->fd);
+
+       /* close the new tdb and rename it to .bak */
+       tdb_close(tdb_new);
+       unlink(new_name);
+       if (rename(tmp_name, new_name) != 0) {
+               perror(new_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       printf("%s : %d records\n", old_name, count1);
+       free(tmp_name);
+
+       return 0;
+}
+
+
+
+/*
+  verify a tdb and if it is corrupt then restore from *.bak
+*/
+static int verify_tdb(const char *fname, const char *bak_name)
+{
+       TDB_CONTEXT *tdb;
+       int count = -1;
+
+       /* open the tdb */
+       tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+
+       /* traverse the tdb, then close it */
+       if (tdb) {
+               count = tdb_traverse(tdb, test_fn, NULL);
+               tdb_close(tdb);
+       }
+
+       /* count is < 0 means an error */
+       if (count < 0) {
+               printf("restoring %s\n", fname);
+               return backup_tdb(bak_name, fname);
+       }
+
+       printf("%s : %d records\n", fname, count);
+
+       return 0;
+}
+
+
+/*
+  see if one file is newer than another
+*/
+static int file_newer(const char *fname1, const char *fname2)
+{
+       struct stat st1, st2;
+       if (stat(fname1, &st1) != 0) {
+               return 0;
+       }
+       if (stat(fname2, &st2) != 0) {
+               return 1;
+       }
+       return (st1.st_mtime > st2.st_mtime);
+}
+
+static void usage(void)
+{
+       printf("Usage: tdbbackup [options] <fname...>\n\n");
+       printf("   -h            this help message\n");
+       printf("   -s suffix     set the backup suffix\n");
+       printf("   -v            veryify mode (restore if corrupt)\n");
+}
+               
+
+ int main(int argc, char *argv[])
+{
+       int i;
+       int ret = 0;
+       int c;
+       int verify = 0;
+       char *suffix = ".bak";
+       extern int optind;
+       extern char *optarg;
+
+       while ((c = getopt(argc, argv, "vhs:")) != -1) {
+               switch (c) {
+               case 'h':
+                       usage();
+                       exit(0);
+               case 'v':
+                       verify = 1;
+                       break;
+               case 's':
+                       suffix = optarg;
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i=0; i<argc; i++) {
+               const char *fname = argv[i];
+               char *bak_name;
+
+               bak_name = add_suffix(fname, suffix);
+
+               if (verify) {
+                       if (verify_tdb(fname, bak_name) != 0) {
+                               ret = 1;
+                       }
+               } else {
+                       if (file_newer(fname, bak_name) &&
+                           backup_tdb(fname, bak_name) != 0) {
+                               ret = 1;
+                       }
+               }
+
+               free(bak_name);
+       }
+
+       return ret;
+}
+
+#ifdef VALGRIND
+size_t valgrind_strlen(const char *s)
+{
+       size_t count;
+       for(count = 0; *s++; count++)
+               ;
+       return count;
+}
+#endif
diff --git a/source4/tdb/tdbdump.c b/source4/tdb/tdbdump.c
new file mode 100644 (file)
index 0000000..9c1dc27
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple tdb dump util
+   Copyright (C) Andrew Tridgell              2001
+
+   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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static void print_data(TDB_DATA d)
+{
+       unsigned char *p = d.dptr;
+       int len = d.dsize;
+       while (len--) {
+               if (isprint(*p) && !strchr("\"\\", *p)) {
+                       fputc(*p, stdout);
+               } else {
+                       printf("\\%02X", *p);
+               }
+               p++;
+       }
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       printf("{\n");
+       printf("key = \"");
+       print_data(key);
+       printf("\"\n");
+       printf("data = \"");
+       print_data(dbuf);
+       printf("\"\n");
+       printf("}\n");
+       return 0;
+}
+
+static int dump_tdb(const char *fname)
+{
+       TDB_CONTEXT *tdb;
+       
+       tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+       if (!tdb) {
+               printf("Failed to open %s\n", fname);
+               return 1;
+       }
+
+       tdb_traverse(tdb, traverse_fn, NULL);
+       return 0;
+}
+
+ int main(int argc, char *argv[])
+{
+       char *fname;
+
+       if (argc < 2) {
+               printf("Usage: tdbdump <fname>\n");
+               exit(1);
+       }
+
+       fname = argv[1];
+
+       return dump_tdb(fname);
+}
diff --git a/source4/tdb/tdbtest.c b/source4/tdb/tdbtest.c
new file mode 100644 (file)
index 0000000..89295a3
--- /dev/null
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "tdb.h"
+#include <gdbm.h>
+
+/* a test program for tdb - the trivial database */
+
+
+
+#define DELETE_PROB 7
+#define STORE_PROB 5
+
+static TDB_CONTEXT *db;
+static GDBM_FILE gdbm;
+
+struct timeval tp1,tp2;
+
+static void start_timer(void)
+{
+       gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+       gettimeofday(&tp2,NULL);
+       return((tp2.tv_sec - tp1.tv_sec) + 
+              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void fatal(char *why)
+{
+       perror(why);
+       exit(1);
+}
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+    
+       va_start(ap, format);
+       vfprintf(stdout, format, ap);
+       va_end(ap);
+       fflush(stdout);
+}
+
+static void compare_db(void)
+{
+       TDB_DATA d, key, nextkey;
+       datum gd, gkey, gnextkey;
+
+       key = tdb_firstkey(db);
+       while (key.dptr) {
+               d = tdb_fetch(db, key);
+               gkey.dptr = key.dptr;
+               gkey.dsize = key.dsize;
+
+               gd = gdbm_fetch(gdbm, gkey);
+
+               if (!gd.dptr) fatal("key not in gdbm");
+               if (gd.dsize != d.dsize) fatal("data sizes differ");
+               if (memcmp(gd.dptr, d.dptr, d.dsize)) {
+                       fatal("data differs");
+               }
+
+               nextkey = tdb_nextkey(db, key);
+               free(key.dptr);
+               free(d.dptr);
+               free(gd.dptr);
+               key = nextkey;
+       }
+
+       gkey = gdbm_firstkey(gdbm);
+       while (gkey.dptr) {
+               gd = gdbm_fetch(gdbm, gkey);
+               key.dptr = gkey.dptr;
+               key.dsize = gkey.dsize;
+
+               d = tdb_fetch(db, key);
+
+               if (!d.dptr) fatal("key not in db");
+               if (d.dsize != gd.dsize) fatal("data sizes differ");
+               if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
+                       fatal("data differs");
+               }
+
+               gnextkey = gdbm_nextkey(gdbm, gkey);
+               free(gkey.dptr);
+               free(gd.dptr);
+               free(d.dptr);
+               gkey = gnextkey;
+       }
+}
+
+static char *randbuf(int len)
+{
+       char *buf;
+       int i;
+       buf = (char *)malloc(len+1);
+
+       for (i=0;i<len;i++) {
+               buf[i] = 'a' + (rand() % 26);
+       }
+       buf[i] = 0;
+       return buf;
+}
+
+static void addrec_db(void)
+{
+       int klen, dlen;
+       char *k, *d;
+       TDB_DATA key, data;
+
+       klen = 1 + (rand() % 4);
+       dlen = 1 + (rand() % 100);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       if (rand() % DELETE_PROB == 0) {
+               tdb_delete(db, key);
+       } else if (rand() % STORE_PROB == 0) {
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+       } else {
+               data = tdb_fetch(db, key);
+               if (data.dptr) free(data.dptr);
+       }
+
+       free(k);
+       free(d);
+}
+
+static void addrec_gdbm(void)
+{
+       int klen, dlen;
+       char *k, *d;
+       datum key, data;
+
+       klen = 1 + (rand() % 4);
+       dlen = 1 + (rand() % 100);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       if (rand() % DELETE_PROB == 0) {
+               gdbm_delete(gdbm, key);
+       } else if (rand() % STORE_PROB == 0) {
+               if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
+                       fatal("gdbm_store failed");
+               }
+       } else {
+               data = gdbm_fetch(gdbm, key);
+               if (data.dptr) free(data.dptr);
+       }
+
+       free(k);
+       free(d);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+       printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
+#endif
+       tdb_delete(tdb, key);
+       return 0;
+}
+
+static void merge_test(void)
+{
+       int i;
+       char keys[5][2];
+       TDB_DATA key, data;
+       
+       for (i = 0; i < 5; i++) {
+               sprintf(keys[i], "%d", i);
+               key.dptr = keys[i];
+               key.dsize = 2;
+               
+               data.dptr = "test";
+               data.dsize = 4;
+               
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+       }
+
+       key.dptr = keys[0];
+       tdb_delete(db, key);
+       key.dptr = keys[4];
+       tdb_delete(db, key);
+       key.dptr = keys[2];
+       tdb_delete(db, key);
+       key.dptr = keys[1];
+       tdb_delete(db, key);
+       key.dptr = keys[3];
+       tdb_delete(db, key);
+}
+       
+int main(int argc, char *argv[])
+{
+       int i, seed=0;
+       int loops = 10000;
+
+       unlink("test.gdbm");
+
+       db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, 
+                     O_RDWR | O_CREAT | O_TRUNC, 0600);
+       gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, 
+                        0600, NULL);
+
+       if (!db || !gdbm) {
+               fatal("db open failed");
+       }
+
+       tdb_logging_function(db, tdb_log);
+       
+#if 1
+       srand(seed);
+       start_timer();
+       for (i=0;i<loops;i++) addrec_gdbm();
+       printf("gdbm got %.2f ops/sec\n", i/end_timer());
+#endif
+
+       merge_test();
+
+       srand(seed);
+       start_timer();
+       for (i=0;i<loops;i++) addrec_db();
+       printf("tdb got %.2f ops/sec\n", i/end_timer());
+
+       compare_db();
+
+       printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+       printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+
+       tdb_close(db);
+       gdbm_close(gdbm);
+
+       return 0;
+}
diff --git a/source4/tdb/tdbtool.c b/source4/tdb/tdbtool.c
new file mode 100644 (file)
index 0000000..f5e486b
--- /dev/null
@@ -0,0 +1,482 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell              1999-2000
+   Copyright (C) Paul `Rusty' Russell             2000
+   Copyright (C) Jeremy Allison                           2000
+   Copyright (C) Andrew Esh                        2001
+
+   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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+/* a tdb tool for manipulating a tdb database */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+typedef struct connections_key {
+       pid_t pid;
+       int cnum;
+       fstring name;
+} connections_key;
+
+typedef struct connections_data {
+       int magic;
+       pid_t pid;
+       int cnum;
+       uid_t uid;
+       gid_t gid;
+       char name[24];
+       char addr[24];
+       char machine[128];
+       time_t start;
+} connections_data;
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(unsigned char *buf,int len)
+{
+       int i;
+
+       /* We're probably printing ASCII strings so don't try to display
+          the trailing NULL character. */
+
+       if (buf[len - 1] == 0)
+               len--;
+
+       for (i=0;i<len;i++)
+               printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(unsigned char *buf,int len)
+{
+       int i=0;
+       if (len<=0) return;
+       printf("[%03X] ",i);
+       for (i=0;i<len;) {
+               printf("%02X ",(int)buf[i]);
+               i++;
+               if (i%8 == 0) printf(" ");
+               if (i%16 == 0) {      
+                       print_asc(&buf[i-16],8); printf(" ");
+                       print_asc(&buf[i-8],8); printf("\n");
+                       if (i<len) printf("[%03X] ",i);
+               }
+       }
+       if (i%16) {
+               int n;
+               
+               n = 16 - (i%16);
+               printf(" ");
+               if (n>8) printf(" ");
+               while (n--) printf("   ");
+               
+               n = i%16;
+               if (n > 8) n = 8;
+               print_asc(&buf[i-(i%16)],n); printf(" ");
+               n = (i%16) - n;
+               if (n>0) print_asc(&buf[i-n],n); 
+               printf("\n");    
+       }
+}
+
+static void help(void)
+{
+       printf("
+tdbtool: 
+  create    dbname     : create a database
+  open      dbname     : open an existing database
+  erase                : erase the database
+  dump                 : dump the database as strings
+  insert    key  data  : insert a record
+  store     key  data  : store a record (replace)
+  show      key        : show a record by key
+  delete    key        : delete a record by key
+  list                 : print the database hash table and freelist
+  free                 : print the database freelist
+  1 | first            : print the first record
+  n | next             : print the next record
+  q | quit             : terminate
+  \\n                   : repeat 'next' command
+");
+}
+
+static void terror(char *why)
+{
+       printf("%s\n", why);
+}
+
+static char *get_token(int startover)
+{
+       static char tmp[1024];
+       static char *cont = NULL;
+       char *insert, *start;
+       char *k = strtok(NULL, " ");
+
+       if (!k)
+         return NULL;
+
+       if (startover)
+         start = tmp;
+       else
+         start = cont;
+
+       strcpy(start, k);
+       insert = start + strlen(start) - 1;
+       while (*insert == '\\') {
+         *insert++ = ' ';
+         k = strtok(NULL, " ");
+         if (!k)
+           break;
+         strcpy(insert, k);
+         insert = start + strlen(start) - 1;
+       }
+
+       /* Get ready for next call */
+       cont = start + strlen(start) + 1;
+       return start;
+}
+
+static void create_tdb(void)
+{
+       char *tok = get_token(1);
+       if (!tok) {
+               help();
+               return;
+       }
+       if (tdb) tdb_close(tdb);
+       tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+                      O_RDWR | O_CREAT | O_TRUNC, 0600);
+       if (!tdb) {
+               printf("Could not create %s: %s\n", tok, strerror(errno));
+       }
+}
+
+static void open_tdb(void)
+{
+       char *tok = get_token(1);
+       if (!tok) {
+               help();
+               return;
+       }
+       if (tdb) tdb_close(tdb);
+       tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
+       if (!tdb) {
+               printf("Could not open %s: %s\n", tok, strerror(errno));
+       }
+}
+
+static void insert_tdb(void)
+{
+       char *k = get_token(1);
+       char *d = get_token(0);
+       TDB_DATA key, dbuf;
+
+       if (!k || !d) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+       dbuf.dptr = d;
+       dbuf.dsize = strlen(d)+1;
+
+       if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+               terror("insert failed");
+       }
+}
+
+static void store_tdb(void)
+{
+       char *k = get_token(1);
+       char *d = get_token(0);
+       TDB_DATA key, dbuf;
+
+       if (!k || !d) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+       dbuf.dptr = d;
+       dbuf.dsize = strlen(d)+1;
+
+       printf("Storing key:\n");
+       print_rec(tdb, key, dbuf, NULL);
+
+       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+               terror("store failed");
+       }
+}
+
+static void show_tdb(void)
+{
+       char *k = get_token(1);
+       TDB_DATA key, dbuf;
+
+       if (!k) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+/*     key.dsize = strlen(k)+1;*/
+       key.dsize = strlen(k);
+
+       dbuf = tdb_fetch(tdb, key);
+       if (!dbuf.dptr) {
+               terror("fetch failed");
+               return;
+       }
+       /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+       print_rec(tdb, key, dbuf, NULL);
+}
+
+static void delete_tdb(void)
+{
+       char *k = get_token(1);
+       TDB_DATA key;
+
+       if (!k) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+
+       if (tdb_delete(tdb, key) != 0) {
+               terror("delete failed");
+       }
+}
+
+#if 0
+static int print_conn_key(TDB_DATA key)
+{
+       printf( "pid    =%5d   ", ((connections_key*)key.dptr)->pid);
+       printf( "cnum   =%10d  ", ((connections_key*)key.dptr)->cnum);
+       printf( "name   =[%s]\n", ((connections_key*)key.dptr)->name);
+       return 0;
+}
+
+static int print_conn_data(TDB_DATA dbuf)
+{
+       printf( "pid    =%5d   ", ((connections_data*)dbuf.dptr)->pid);
+       printf( "cnum   =%10d  ", ((connections_data*)dbuf.dptr)->cnum);
+       printf( "name   =[%s]\n", ((connections_data*)dbuf.dptr)->name);
+       
+       printf( "uid    =%5d   ",  ((connections_data*)dbuf.dptr)->uid);
+       printf( "addr   =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
+       printf( "gid    =%5d   ",  ((connections_data*)dbuf.dptr)->gid);
+       printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
+       printf( "start  = %s\n",   ctime(&((connections_data*)dbuf.dptr)->start));
+       return 0;
+}
+#endif
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+       print_conn_key(key);
+       print_conn_data(dbuf);
+       return 0;
+#else
+       printf("\nkey %d bytes\n", key.dsize);
+       print_asc(key.dptr, key.dsize);
+       printf("\ndata %d bytes\n", dbuf.dsize);
+       print_data(dbuf.dptr, dbuf.dsize);
+       return 0;
+#endif
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       print_asc(key.dptr, key.dsize);
+       printf("\n");
+       return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       total_bytes += dbuf.dsize;
+       return 0;
+}
+
+static void info_tdb(void)
+{
+       int count;
+       total_bytes = 0;
+       if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
+               printf("Error = %s\n", tdb_errorstr(tdb));
+       else
+               printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(char *prompt)
+{
+       static char line[1024];
+       char *p;
+       fputs(prompt, stdout);
+       line[0] = 0;
+       p = fgets(line, sizeof(line)-1, stdin);
+       if (p) p = strchr(p, '\n');
+       if (p) *p = 0;
+       return p?line:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+                     void *state)
+{
+    return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+       TDB_DATA dbuf;
+       *pkey = tdb_firstkey(the_tdb);
+       
+       dbuf = tdb_fetch(the_tdb, *pkey);
+       if (!dbuf.dptr) terror("fetch failed");
+       else {
+               /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+               print_rec(the_tdb, *pkey, dbuf, NULL);
+       }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+       TDB_DATA dbuf;
+       *pkey = tdb_nextkey(the_tdb, *pkey);
+       
+       dbuf = tdb_fetch(the_tdb, *pkey);
+       if (!dbuf.dptr) 
+               terror("fetch failed");
+       else
+               /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+               print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+    int bIterate = 0;
+    char *line;
+    char *tok;
+       TDB_DATA iterate_kbuf;
+
+    if (argv[1]) {
+       static char tmp[1024];
+        sprintf(tmp, "open %s", argv[1]);
+        tok=strtok(tmp," ");
+        open_tdb();
+    }
+
+    while ((line = tdb_getline("tdb> "))) {
+
+        /* Shell command */
+        
+        if (line[0] == '!') {
+            system(line + 1);
+            continue;
+        }
+        
+        if ((tok = strtok(line," "))==NULL) {
+           if (bIterate)
+              next_record(tdb, &iterate_kbuf);
+           continue;
+        }
+        if (strcmp(tok,"create") == 0) {
+            bIterate = 0;
+            create_tdb();
+            continue;
+        } else if (strcmp(tok,"open") == 0) {
+            open_tdb();
+            continue;
+        } else if ((strcmp(tok, "q") == 0) ||
+                   (strcmp(tok, "quit") == 0)) {
+            break;
+       }
+            
+        /* all the rest require a open database */
+        if (!tdb) {
+            bIterate = 0;
+            terror("database not open");
+            help();
+            continue;
+        }
+            
+        if (strcmp(tok,"insert") == 0) {
+            bIterate = 0;
+            insert_tdb();
+        } else if (strcmp(tok,"store") == 0) {
+            bIterate = 0;
+            store_tdb();
+        } else if (strcmp(tok,"show") == 0) {
+            bIterate = 0;
+            show_tdb();
+        } else if (strcmp(tok,"erase") == 0) {
+            bIterate = 0;
+            tdb_traverse(tdb, do_delete_fn, NULL);
+        } else if (strcmp(tok,"delete") == 0) {
+            bIterate = 0;
+            delete_tdb();
+        } else if (strcmp(tok,"dump") == 0) {
+            bIterate = 0;
+            tdb_traverse(tdb, print_rec, NULL);
+        } else if (strcmp(tok,"list") == 0) {
+            tdb_dump_all(tdb);
+        } else if (strcmp(tok, "free") == 0) {
+            tdb_printfreelist(tdb);
+        } else if (strcmp(tok,"info") == 0) {
+            info_tdb();
+        } else if ( (strcmp(tok, "1") == 0) ||
+                    (strcmp(tok, "first") == 0)) {
+            bIterate = 1;
+            first_record(tdb, &iterate_kbuf);
+        } else if ((strcmp(tok, "n") == 0) ||
+                   (strcmp(tok, "next") == 0)) {
+            next_record(tdb, &iterate_kbuf);
+        } else if ((strcmp(tok, "keys") == 0)) {
+                bIterate = 0;
+                tdb_traverse(tdb, print_key, NULL);
+        } else {
+            help();
+        }
+    }
+
+    if (tdb) tdb_close(tdb);
+
+    return 0;
+}
diff --git a/source4/tdb/tdbtorture.c b/source4/tdb/tdbtorture.c
new file mode 100644 (file)
index 0000000..e27bbff
--- /dev/null
@@ -0,0 +1,226 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "tdb.h"
+
+/* this tests tdb by doing lots of ops from several simultaneous
+   writers - that stresses the locking code. Build with TDB_DEBUG=1
+   for best effect */
+
+
+
+#define REOPEN_PROB 30
+#define DELETE_PROB 8
+#define STORE_PROB 4
+#define APPEND_PROB 6
+#define LOCKSTORE_PROB 0
+#define TRAVERSE_PROB 20
+#define CULL_PROB 100
+#define KEYLEN 3
+#define DATALEN 100
+#define LOCKLEN 20
+
+static TDB_CONTEXT *db;
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+    
+       va_start(ap, format);
+       vfprintf(stdout, format, ap);
+       va_end(ap);
+       fflush(stdout);
+#if 0
+       {
+               char *ptr;
+               asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
+               system(ptr);
+               free(ptr);
+       }
+#endif 
+}
+
+static void fatal(char *why)
+{
+       perror(why);
+       exit(1);
+}
+
+static char *randbuf(int len)
+{
+       char *buf;
+       int i;
+       buf = (char *)malloc(len+1);
+
+       for (i=0;i<len;i++) {
+               buf[i] = 'a' + (rand() % 26);
+       }
+       buf[i] = 0;
+       return buf;
+}
+
+static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+                        void *state)
+{
+       if (random() % CULL_PROB == 0) {
+               tdb_delete(tdb, key);
+       }
+       return 0;
+}
+
+static void addrec_db(void)
+{
+       int klen, dlen, slen;
+       char *k, *d, *s;
+       TDB_DATA key, data, lockkey;
+
+       klen = 1 + (rand() % KEYLEN);
+       dlen = 1 + (rand() % DATALEN);
+       slen = 1 + (rand() % LOCKLEN);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+       s = randbuf(slen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       lockkey.dptr = s;
+       lockkey.dsize = slen+1;
+
+#if REOPEN_PROB
+       if (random() % REOPEN_PROB == 0) {
+               tdb_reopen_all();
+               goto next;
+       } 
+#endif
+
+#if DELETE_PROB
+       if (random() % DELETE_PROB == 0) {
+               tdb_delete(db, key);
+               goto next;
+       }
+#endif
+
+#if STORE_PROB
+       if (random() % STORE_PROB == 0) {
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+               goto next;
+       }
+#endif
+
+#if APPEND_PROB
+       if (random() % APPEND_PROB == 0) {
+               if (tdb_append(db, key, data) != 0) {
+                       fatal("tdb_append failed");
+               }
+               goto next;
+       }
+#endif
+
+#if LOCKSTORE_PROB
+       if (random() % LOCKSTORE_PROB == 0) {
+               tdb_chainlock(db, lockkey);
+               data = tdb_fetch(db, key);
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+               if (data.dptr) free(data.dptr);
+               tdb_chainunlock(db, lockkey);
+               goto next;
+       } 
+#endif
+
+#if TRAVERSE_PROB
+       if (random() % TRAVERSE_PROB == 0) {
+               tdb_traverse(db, cull_traverse, NULL);
+               goto next;
+       }
+#endif
+
+       data = tdb_fetch(db, key);
+       if (data.dptr) free(data.dptr);
+
+next:
+       free(k);
+       free(d);
+       free(s);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+                       void *state)
+{
+       tdb_delete(tdb, key);
+       return 0;
+}
+
+#ifndef NPROC
+#define NPROC 6
+#endif
+
+#ifndef NLOOPS
+#define NLOOPS 200000
+#endif
+
+int main(int argc, char *argv[])
+{
+       int i, seed=0;
+       int loops = NLOOPS;
+       pid_t pids[NPROC];
+
+       pids[0] = getpid();
+
+       for (i=0;i<NPROC-1;i++) {
+               if ((pids[i+1]=fork()) == 0) break;
+       }
+
+       db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST, 
+                     O_RDWR | O_CREAT, 0600);
+       if (!db) {
+               fatal("db open failed");
+       }
+       tdb_logging_function(db, tdb_log);
+
+       srand(seed + getpid());
+       srandom(seed + getpid() + time(NULL));
+       for (i=0;i<loops;i++) addrec_db();
+
+       tdb_traverse(db, NULL, NULL);
+       tdb_traverse(db, traverse_fn, NULL);
+       tdb_traverse(db, traverse_fn, NULL);
+
+       tdb_close(db);
+
+       if (getpid() == pids[0]) {
+               for (i=0;i<NPROC-1;i++) {
+                       int status;
+                       if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
+                               printf("failed to wait for %d\n",
+                                      (int)pids[i+1]);
+                               exit(1);
+                       }
+                       if (WEXITSTATUS(status) != 0) {
+                               printf("child %d exited with status %d\n",
+                                      (int)pids[i+1], WEXITSTATUS(status));
+                               exit(1);
+                       }
+               }
+               printf("OK\n");
+       }
+
+       return 0;
+}
diff --git a/source4/tdb/tdbutil.c b/source4/tdb/tdbutil.c
new file mode 100644 (file)
index 0000000..0d8f612
--- /dev/null
@@ -0,0 +1,687 @@
+/* 
+   Unix SMB/CIFS implementation.
+   tdb utility functions
+   Copyright (C) Andrew Tridgell 1992-1998
+   
+   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 <fnmatch.h>
+
+/* these are little tdb utility functions that are meant to make
+   dealing with a tdb database a little less cumbersome in Samba */
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+       gotalarm = 1;
+}
+
+/***************************************************************
+ Make a TDB_DATA and keep the const warning in one place
+****************************************************************/
+
+static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
+{
+       TDB_DATA ret;
+       ret.dptr = dptr;
+       ret.dsize = dsize;
+       return ret;
+}
+
+/****************************************************************************
+ Lock a chain with timeout (in seconds).
+****************************************************************************/
+
+static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
+{
+       /* Allow tdb_chainlock to be interrupted by an alarm. */
+       int ret;
+       gotalarm = 0;
+       tdb_set_lock_alarm(&gotalarm);
+
+       if (timeout) {
+               CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+               alarm(timeout);
+       }
+
+       if (rw_type == F_RDLCK)
+               ret = tdb_chainlock_read(tdb, key);
+       else
+               ret = tdb_chainlock(tdb, key);
+
+       if (timeout) {
+               alarm(0);
+               CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+               if (gotalarm) {
+                       DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
+                               timeout, key.dptr, tdb->name ));
+                       /* TODO: If we time out waiting for a lock, it might
+                        * be nice to use F_GETLK to get the pid of the
+                        * process currently holding the lock and print that
+                        * as part of the debugging message. -- mbp */
+                       return -1;
+               }
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ Write lock a chain. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
+{
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Unlock a chain by string.
+****************************************************************************/
+
+void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+       tdb_chainunlock(tdb, key);
+}
+
+/****************************************************************************
+ Read lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
+}
+
+/****************************************************************************
+ Read unlock a chain by string.
+****************************************************************************/
+
+void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+       TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+       
+       tdb_chainunlock_read(tdb, key);
+}
+
+
+/****************************************************************************
+ Fetch a int32 value by a arbitrary blob key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
+{
+       TDB_DATA key = make_tdb_data(keyval, len);
+       TDB_DATA data;
+       int32 ret;
+
+       data = tdb_fetch(tdb, key);
+       if (!data.dptr || data.dsize != sizeof(int32)) {
+               SAFE_FREE(data.dptr);
+               return -1;
+       }
+
+       ret = IVAL(data.dptr,0);
+       SAFE_FREE(data.dptr);
+       return ret;
+}
+
+/****************************************************************************
+ Fetch a int32 value by string key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
+{
+       return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
+}
+
+/****************************************************************************
+ Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
+{
+       TDB_DATA key = make_tdb_data(keystr, len);
+       TDB_DATA data;
+       int32 v_store;
+
+       SIVAL(&v_store,0,v);
+       data.dptr = (void *)&v_store;
+       data.dsize = sizeof(int32);
+
+       return tdb_store(tdb, key, data, TDB_REPLACE);
+}
+
+/****************************************************************************
+ Store a int32 value by string key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
+{
+       return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
+}
+
+/****************************************************************************
+ Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
+{
+       TDB_DATA key = make_tdb_data(keyval, len);
+       TDB_DATA data;
+
+       data = tdb_fetch(tdb, key);
+       if (!data.dptr || data.dsize != sizeof(uint32)) {
+               SAFE_FREE(data.dptr);
+               return False;
+       }
+
+       *value = IVAL(data.dptr,0);
+       SAFE_FREE(data.dptr);
+       return True;
+}
+
+/****************************************************************************
+ Fetch a uint32 value by string key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
+{
+       return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+
+/****************************************************************************
+ Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
+{
+       TDB_DATA key = make_tdb_data(keystr, len);
+       TDB_DATA data;
+       uint32 v_store;
+       BOOL ret = True;
+
+       SIVAL(&v_store, 0, value);
+       data.dptr = (void *)&v_store;
+       data.dsize = sizeof(uint32);
+
+       if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
+               ret = False;
+
+       return ret;
+}
+
+/****************************************************************************
+ Store a uint32 value by string key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
+{
+       return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+/****************************************************************************
+ Store a buffer by a null terminated string key.  Return 0 on success, -1
+ on failure.
+****************************************************************************/
+
+int tdb_store_by_string(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+       
+       return tdb_store(tdb, key, data, flags);
+}
+
+/****************************************************************************
+ Fetch a buffer using a null terminated string key.  Don't forget to call
+ free() on the result dptr.
+****************************************************************************/
+
+TDB_DATA tdb_fetch_by_string(TDB_CONTEXT *tdb, const char *keystr)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+       return tdb_fetch(tdb, key);
+}
+
+/****************************************************************************
+ Delete an entry using a null terminated string key. 
+****************************************************************************/
+
+int tdb_delete_by_string(TDB_CONTEXT *tdb, const char *keystr)
+{
+       TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+       return tdb_delete(tdb, key);
+}
+
+/****************************************************************************
+ Atomic integer change. Returns old value. To create, set initial value in *oldval. 
+****************************************************************************/
+
+int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
+{
+       int32 val;
+       int32 ret = -1;
+
+       if (tdb_lock_bystring(tdb, keystr,0) == -1)
+               return -1;
+
+       if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
+               /* The lookup failed */
+               if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+                       /* but not becouse it didn't exist */
+                       goto err_out;
+               }
+               
+               /* Start with 'old' value */
+               val = *oldval;
+
+       } else {
+               /* It worked, set return value (oldval) to tdb data */
+               *oldval = val;
+       }
+
+       /* Increment value for storage and return next time */
+       val += change_val;
+               
+       if (tdb_store_int32(tdb, keystr, val) == -1)
+               goto err_out;
+
+       ret = 0;
+
+  err_out:
+
+       tdb_unlock_bystring(tdb, keystr);
+       return ret;
+}
+
+/****************************************************************************
+ Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval. 
+****************************************************************************/
+
+BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
+{
+       uint32 val;
+       BOOL ret = False;
+
+       if (tdb_lock_bystring(tdb, keystr,0) == -1)
+               return False;
+
+       if (!tdb_fetch_uint32(tdb, keystr, &val)) {
+               /* It failed */
+               if (tdb_error(tdb) != TDB_ERR_NOEXIST) { 
+                       /* and not becouse it didn't exist */
+                       goto err_out;
+               }
+
+               /* Start with 'old' value */
+               val = *oldval;
+
+       } else {
+               /* it worked, set return value (oldval) to tdb data */
+               *oldval = val;
+
+       }
+
+       /* get a new value to store */
+       val += change_val;
+               
+       if (!tdb_store_uint32(tdb, keystr, val))
+               goto err_out;
+
+       ret = True;
+
+  err_out:
+
+       tdb_unlock_bystring(tdb, keystr);
+       return ret;
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
+{
+       va_list ap;
+       uint16 w;
+       uint32 d;
+       int i;
+       void *p;
+       int len;
+       char *s;
+       char c;
+       char *buf0 = buf;
+       const char *fmt0 = fmt;
+       int bufsize0 = bufsize;
+
+       va_start(ap, fmt);
+
+       while (*fmt) {
+               switch ((c = *fmt++)) {
+               case 'w':
+                       len = 2;
+                       w = (uint16)va_arg(ap, int);
+                       if (bufsize >= len)
+                               SSVAL(buf, 0, w);
+                       break;
+               case 'd':
+                       len = 4;
+                       d = va_arg(ap, uint32);
+                       if (bufsize >= len)
+                               SIVAL(buf, 0, d);
+                       break;
+               case 'p':
+                       len = 4;
+                       p = va_arg(ap, void *);
+                       d = p?1:0;
+                       if (bufsize >= len)
+                               SIVAL(buf, 0, d);
+                       break;
+               case 'P':
+                       s = va_arg(ap,char *);
+                       w = strlen(s);
+                       len = w + 1;
+                       if (bufsize >= len)
+                               memcpy(buf, s, len);
+                       break;
+               case 'f':
+                       s = va_arg(ap,char *);
+                       w = strlen(s);
+                       len = w + 1;
+                       if (bufsize >= len)
+                               memcpy(buf, s, len);
+                       break;
+               case 'B':
+                       i = va_arg(ap, int);
+                       s = va_arg(ap, char *);
+                       len = 4+i;
+                       if (bufsize >= len) {
+                               SIVAL(buf, 0, i);
+                               memcpy(buf+4, s, i);
+                       }
+                       break;
+               default:
+                       DEBUG(0,("Unknown tdb_pack format %c in %s\n", 
+                                c, fmt));
+                       len = 0;
+                       break;
+               }
+
+               buf += len;
+               bufsize -= len;
+       }
+
+       va_end(ap);
+
+       DEBUG(18,("tdb_pack(%s, %d) -> %d\n", 
+                fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+       
+       return PTR_DIFF(buf, buf0);
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
+{
+       va_list ap;
+       uint16 *w;
+       uint32 *d;
+       int len;
+       int *i;
+       void **p;
+       char *s, **b;
+       char c;
+       char *buf0 = buf;
+       const char *fmt0 = fmt;
+       int bufsize0 = bufsize;
+
+       va_start(ap, fmt);
+       
+       while (*fmt) {
+               switch ((c=*fmt++)) {
+               case 'w':
+                       len = 2;
+                       w = va_arg(ap, uint16 *);
+                       if (bufsize < len)
+                               goto no_space;
+                       *w = SVAL(buf, 0);
+                       break;
+               case 'd':
+                       len = 4;
+                       d = va_arg(ap, uint32 *);
+                       if (bufsize < len)
+                               goto no_space;
+                       *d = IVAL(buf, 0);
+                       break;
+               case 'p':
+                       len = 4;
+                       p = va_arg(ap, void **);
+                       if (bufsize < len)
+                               goto no_space;
+                       *p = (void *)IVAL(buf, 0);
+                       break;
+               case 'P':
+                       s = va_arg(ap,char *);
+                       len = strlen(buf) + 1;
+                       if (bufsize < len || len > sizeof(pstring))
+                               goto no_space;
+                       memcpy(s, buf, len);
+                       break;
+               case 'f':
+                       s = va_arg(ap,char *);
+                       len = strlen(buf) + 1;
+                       if (bufsize < len || len > sizeof(fstring))
+                               goto no_space;
+                       memcpy(s, buf, len);
+                       break;
+               case 'B':
+                       i = va_arg(ap, int *);
+                       b = va_arg(ap, char **);
+                       len = 4;
+                       if (bufsize < len)
+                               goto no_space;
+                       *i = IVAL(buf, 0);
+                       if (! *i) {
+                               *b = NULL;
+                               break;
+                       }
+                       len += *i;
+                       if (bufsize < len)
+                               goto no_space;
+                       *b = (char *)malloc(*i);
+                       if (! *b)
+                               goto no_space;
+                       memcpy(*b, buf+4, *i);
+                       break;
+               default:
+                       DEBUG(0,("Unknown tdb_unpack format %c in %s\n", 
+                                c, fmt));
+
+                       len = 0;
+                       break;
+               }
+
+               buf += len;
+               bufsize -= len;
+       }
+
+       va_end(ap);
+
+       DEBUG(18,("tdb_unpack(%s, %d) -> %d\n", 
+                fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+       return PTR_DIFF(buf, buf0);
+
+ no_space:
+       return -1;
+}
+
+/****************************************************************************
+ Log tdb messages via DEBUG().
+****************************************************************************/
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+       char *ptr = NULL;
+
+       va_start(ap, format);
+       vasprintf(&ptr, format, ap);
+       va_end(ap);
+       
+       if (!ptr || !*ptr)
+               return;
+
+       DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+       SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+ Like tdb_open() but also setup a logging function that redirects to
+ the samba DEBUG() system.
+****************************************************************************/
+
+TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
+                         int open_flags, mode_t mode)
+{
+       TDB_CONTEXT *tdb;
+
+       if (!lp_use_mmap())
+               tdb_flags |= TDB_NOMMAP;
+
+       tdb = tdb_open_ex(name, hash_size, tdb_flags, 
+                                   open_flags, mode, tdb_log);
+       if (!tdb)
+               return NULL;
+
+       return tdb;
+}
+
+
+/****************************************************************************
+ Allow tdb_delete to be used as a tdb_traversal_fn.
+****************************************************************************/
+
+int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+                     void *state)
+{
+    return tdb_delete(the_tdb, key);
+}
+
+
+
+/**
+ * Search across the whole tdb for keys that match the given pattern
+ * return the result as a list of keys
+ *
+ * @param tdb pointer to opened tdb file context
+ * @param pattern searching pattern used by fnmatch(3) functions
+ *
+ * @return list of keys found by looking up with given pattern
+ **/
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
+{
+       TDB_DATA key, next;
+       TDB_LIST_NODE *list = NULL;
+       TDB_LIST_NODE *rec = NULL;
+       TDB_LIST_NODE *tmp = NULL;
+       
+       for (key = tdb_firstkey(tdb); key.dptr; key = next) {
+               /* duplicate key string to ensure null-termination */
+               char *key_str = (char*) strndup(key.dptr, key.dsize);
+               if (!key_str) {
+                       DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
+                       smb_panic("strndup failed!\n");
+               }
+               
+               DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
+               
+               next = tdb_nextkey(tdb, key);
+
+               /* do the pattern checking */
+               if (fnmatch(pattern, key_str, 0) == 0) {
+                       rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
+                       ZERO_STRUCTP(rec);
+
+                       rec->node_key = key;
+       
+                       DLIST_ADD_END(list, rec, tmp);
+               
+                       DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
+               } else {
+                       free(key.dptr);
+               }
+               
+               /* free duplicated key string */
+               free(key_str);
+       }
+       
+       return list;
+
+};
+
+
+/**
+ * Free the list returned by tdb_search_keys
+ *
+ * @param node list of results found by tdb_search_keys
+ **/
+void tdb_search_list_free(TDB_LIST_NODE* node)
+{
+       TDB_LIST_NODE *next_node;
+       
+       while (node) {
+               next_node = node->next;
+               SAFE_FREE(node);
+               node = next_node;
+       };
+};
+
+
diff --git a/source4/tdb/tdbutil.h b/source4/tdb/tdbutil.h
new file mode 100644 (file)
index 0000000..0147344
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+   Unix SMB/CIFS implementation.
+   tdb utility functions
+   Copyright (C) Andrew Tridgell 1999
+   
+   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.
+*/
+
+#ifndef __TDBUTIL_H__
+#define __TDBUTIL_H__
+
+
+/* single node of a list returned by tdb_search_keys */
+typedef struct keys_node 
+{
+       struct keys_node *prev, *next;
+       TDB_DATA node_key;
+} TDB_LIST_NODE;
+
+
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
+void tdb_search_list_free(TDB_LIST_NODE*);
+
+
+#endif /* __TDBUTIL_H__ */
diff --git a/source4/tests/.cvsignore b/source4/tests/.cvsignore
new file mode 100644 (file)
index 0000000..ff5b9ca
--- /dev/null
@@ -0,0 +1,3 @@
+unixsock
+fcntl_lock_thread
+a.out
diff --git a/source4/tests/README b/source4/tests/README
new file mode 100644 (file)
index 0000000..cf1be8b
--- /dev/null
@@ -0,0 +1,10 @@
+This directory contains autoconf test programs that are too large to
+comfortably fit in configure.in.
+
+These programs should test one feature of the OS and exit(0) if it
+works or exit(1) if it doesn't work (do _not_ use return)
+
+The programs should be kept simple and to the point. Beautiful/fast
+code is not necessary
+
+
diff --git a/source4/tests/crypttest.c b/source4/tests/crypttest.c
new file mode 100644 (file)
index 0000000..efee2e5
--- /dev/null
@@ -0,0 +1,852 @@
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined(HAVE_CRYPT)
+
+/*
+   This bit of code was derived from the UFC-crypt package which
+   carries the following copyright
+
+   Modified for use by Samba by Andrew Tridgell, October 1994
+
+   Note that this routine is only faster on some machines. Under Linux 1.1.51 
+   libc 4.5.26 I actually found this routine to be slightly slower.
+
+   Under SunOS I found a huge speedup by using these routines 
+   (a factor of 20 or so)
+
+   Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+   that this crypt routine may sometimes get the wrong answer. Only
+   use UFC_CRYT if you really need it.
+
+*/
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c    2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#if (SIZEOF_INT == 4)
+#define long32 int
+#elif (SIZEOF_LONG == 4)
+#define long32 long
+#elif (SIZEOF_SHORT == 4)
+#define long32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define long32 int
+#endif
+#endif
+
+#ifndef long64
+#ifdef HAVE_LONGLONG
+#define long64 long long long
+#endif
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/* 
+ * Permutation done once on the 56 bit 
+ *  key derived from the original 8 byte ASCII key.
+ */
+static int pc1[56] = { 
+  57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
+  10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
+  63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
+  14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ *  56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = { 
+  1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 
+};
+
+/* 
+ * Permutation giving the key 
+ * of the i' DES round 
+ */
+static int pc2[48] = { 
+  14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
+  23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
+  41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+  44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[48] = { 
+  32,  1,  2,  3,  4,  5,  4,  5,  6,  7,  8,  9,
+   8,  9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
+  16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
+  24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32,  1
+};
+static int e_inverse[64];
+
+/* 
+ * Permutation done on the 
+ * result of sbox lookups 
+ */
+static int perm32[32] = {
+  16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+  2,   8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+/* 
+ * The sboxes
+ */
+static int sbox[8][4][16]= {
+        { { 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 },
+          {  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 },
+          {  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0 },
+          { 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 }
+        },
+
+        { { 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 },
+          {  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 },
+          {  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15 },
+          { 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 }
+        },
+
+        { { 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 },
+          { 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 },
+          { 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7 },
+          {  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 }
+        },
+
+        { {  7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 },
+          { 13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 },
+          { 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4 },
+          {  3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 }
+        },
+
+        { {  2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 },
+          { 14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 },
+          {  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14 },
+          { 11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 }
+        },
+
+        { { 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 },
+          { 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 },
+          {  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6 },
+          {  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 }
+        },
+
+        { {  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 },
+          { 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 },
+          {  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2 },
+          {  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 }
+        },
+
+        { { 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 },
+          {  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 },
+          {  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8 },
+          {  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 }
+        }
+};
+
+/* 
+ * This is the final 
+ * permutation matrix
+ */
+static int final_perm[64] = {
+  40,  8, 48, 16, 56, 24, 64, 32, 39,  7, 47, 15, 55, 23, 63, 31,
+  38,  6, 46, 14, 54, 22, 62, 30, 37,  5, 45, 13, 53, 21, 61, 29,
+  36,  4, 44, 12, 52, 20, 60, 28, 35,  3, 43, 11, 51, 19, 59, 27,
+  34,  2, 42, 10, 50, 18, 58, 26, 33,  1, 41,  9, 49, 17, 57, 25
+};
+
+/* 
+ * The 16 DES keys in BITMASK format 
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this  value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+/* 
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ *  -  second  -   is the value of this byte
+ *  -  third   -   selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/* 
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first   index is the byte number in the 8 byte ASCII key
+ *  -  second    -      -    the two 28 bits halfs of the result
+ *  -  third     -   selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first   index is the septet number in the two 28 bit intermediate values
+ *  -  second    -    -  -  septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ *      permutation giving the DES result.
+ * 
+ *      Invoked 6 bit a time on two 48 bit values
+ *      giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8]  = {
+  0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+  0x80000000, 0x40000000, 0x20000000, 0x10000000,
+  0x08000000, 0x04000000, 0x02000000, 0x01000000,
+  0x00800000, 0x00400000, 0x00200000, 0x00100000,
+  0x00080000, 0x00040000, 0x00020000, 0x00010000,
+  0x00008000, 0x00004000, 0x00002000, 0x00001000,
+  0x00000800, 0x00000400, 0x00000200, 0x00000100,
+  0x00000080, 0x00000040, 0x00000020, 0x00000010,
+  0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+  { while(cnt--)
+      *start++ = '\0';
+  }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+  { int comes_from_bit;
+    int bit, sg;
+    ufc_long j;
+    ufc_long mask1, mask2;
+
+    /*
+     * Create the do_pc1 table used
+     * to affect pc1 permutation
+     * when generating keys
+     */
+    for(bit = 0; bit < 56; bit++) {
+      comes_from_bit  = pc1[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 8 + 1];
+      mask2 = longmask[bit % 28 + 4];
+      for(j = 0; j < 128; j++) {
+       if(j & mask1) 
+         do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+      }
+    }
+
+    /*
+     * Create the do_pc2 table used
+     * to affect pc2 permutation when
+     * generating keys
+     */
+    for(bit = 0; bit < 48; bit++) {
+      comes_from_bit  = pc2[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 7 + 1];
+      mask2 = BITMASK(bit % 24);
+      for(j = 0; j < 128; j++) {
+       if(j & mask1)
+         do_pc2[comes_from_bit / 7][j] |= mask2;
+      }
+    }
+
+    /* 
+     * Now generate the table used to do combined
+     * 32 bit permutation and e expansion
+     *
+     * We use it because we have to permute 16384 32 bit
+     * longs into 48 bit in order to initialize sb.
+     *
+     * Looping 48 rounds per permutation becomes 
+     * just too slow...
+     *
+     */
+
+    clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+    for(bit = 0; bit < 48; bit++) {
+      ufc_long inner_mask1,comes_from;
+       
+      comes_from = perm32[esel[bit]-1]-1;
+      inner_mask1      = bytemask[comes_from % 8];
+       
+      for(j = 256; j--;) {
+       if(j & inner_mask1)
+         eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+      }
+    }
+    
+    /* 
+     * Create the sb tables:
+     *
+     * For each 12 bit segment of an 48 bit intermediate
+     * result, the sb table precomputes the two 4 bit
+     * values of the sbox lookups done with the two 6
+     * bit halves, shifts them to their proper place,
+     * sends them through perm32 and finally E expands
+     * them so that they are ready for the next
+     * DES round.
+     *
+     */
+    for(sg = 0; sg < 4; sg++) {
+      int j1, j2;
+      int s1, s2;
+    
+      for(j1 = 0; j1 < 64; j1++) {
+       s1 = s_lookup(2 * sg, j1);
+       for(j2 = 0; j2 < 64; j2++) {
+         ufc_long to_permute, inx;
+    
+         s2         = s_lookup(2 * sg + 1, j2);
+         to_permute = ((s1 << 4)  | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+         inx = ((j1 << 6)  | j2) << 1;
+         sb[sg][inx  ]  = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+         sb[sg][inx+1]  = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[2][(to_permute >>  8) & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[2][(to_permute >>  8) & 0xff][1];
+         sb[sg][inx  ] |= eperm32tab[3][(to_permute)       & 0xff][0];
+         sb[sg][inx+1] |= eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+#ifdef _UFC_64_
+         inx = ((j1 << 6)  | j2);
+         sb[sg][inx]  = 
+           ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+            (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+         sb[sg][inx] |=
+           ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+            (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+         sb[sg][inx] |= 
+           ((long64)eperm32tab[2][(to_permute >>  8) & 0xff][0] << 32) |
+            (long64)eperm32tab[2][(to_permute >>  8) & 0xff][1];
+         sb[sg][inx] |=
+           ((long64)eperm32tab[3][(to_permute)       & 0xff][0] << 32) |
+            (long64)eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+       }
+      }
+    }  
+
+    /* 
+     * Create an inverse matrix for esel telling
+     * where to plug out bits if undoing it
+     */
+    for(bit=48; bit--;) {
+      e_inverse[esel[bit] - 1     ] = bit;
+      e_inverse[esel[bit] - 1 + 32] = bit + 48;
+    }
+
+    /* 
+     * create efp: the matrix used to
+     * undo the E expansion and effect final permutation
+     */
+    clearmem((char*)efp, sizeof efp);
+    for(bit = 0; bit < 64; bit++) {
+      int o_bit, o_long;
+      ufc_long word_value, inner_mask1, inner_mask2;
+      int comes_from_f_bit, comes_from_e_bit;
+      int comes_from_word, bit_within_word;
+
+      /* See where bit i belongs in the two 32 bit long's */
+      o_long = bit / 32; /* 0..1  */
+      o_bit  = bit % 32; /* 0..31 */
+
+      /* 
+       * And find a bit in the e permutated value setting this bit.
+       *
+       * Note: the e selection may have selected the same bit several
+       * times. By the initialization of e_inverse, we only look
+       * for one specific instance.
+       */
+      comes_from_f_bit = final_perm[bit] - 1;         /* 0..63 */
+      comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+      comes_from_word  = comes_from_e_bit / 6;        /* 0..15 */
+      bit_within_word  = comes_from_e_bit % 6;        /* 0..5  */
+
+      inner_mask1 = longmask[bit_within_word + 26];
+      inner_mask2 = longmask[o_bit];
+
+      for(word_value = 64; word_value--;) {
+       if(word_value & inner_mask1)
+         efp[comes_from_word][word_value][o_long] |= inner_mask2;
+      }
+    }
+    initialized++;
+  }
+
+/* 
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+  { ufc_long j;
+    long32 x;
+    for(j=4096; j--;) {
+      x = (k[0] ^ k[1]) & (long32)saltbits;
+      *k++ ^= x;
+      *k++ ^= x;
+    }
+  }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+  { ufc_long j;
+    long64 x;
+    for(j=4096; j--;) {
+      x = ((*k >> 32) ^ *k) & (long64)saltbits;
+      *k++ ^= (x << 32) | x;
+    }
+  }
+#endif
+
+/* 
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+  { ufc_long i, j, saltbits;
+    const unsigned char *s2 = (const unsigned char *)s1;
+
+    if(!initialized)
+      ufc_init_des();
+
+    if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+      return;
+    current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+    /* 
+     * This is the only crypt change to DES:
+     * entries are swapped in the expansion table
+     * according to the bits set in the salt.
+     */
+    saltbits = 0;
+    for(i = 0; i < 2; i++) {
+      long c=ascii_to_bin(s2[i]);
+      if(c < 0 || c > 63)
+       c = 0;
+      for(j = 0; j < 6; j++) {
+       if((c >> j) & 0x1)
+         saltbits |= BITMASK(6 * i + j);
+      }
+    }
+
+    /*
+     * Permute the sb table values
+     * to reflect the changed e
+     * selection table
+     */
+    shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits); 
+    shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+    current_saltbits = saltbits;
+  }
+
+static void ufc_mk_keytab(char *key)
+  { ufc_long v1, v2, *k1;
+    int i;
+#ifdef _UFC_32_
+    long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+    long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+    v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+    for(i = 8; i--;) {
+      v1 |= k1[*key   & 0x7f]; k1 += 128;
+      v2 |= k1[*key++ & 0x7f]; k1 += 128;
+    }
+
+    for(i = 0; i < 16; i++) {
+      k1 = &do_pc2[0][0];
+
+      v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+      v  = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v1      ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+      *k2++ = v;
+      v = 0;
+#endif
+#ifdef _UFC_64_
+      v <<= 32;
+#endif
+
+      v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+      v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v2      ) & 0x7f];
+
+      *k2++ = v;
+    }
+
+    direction = 0;
+  }
+
+/* 
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+  { ufc_long v1, v2, x;
+    static ufc_long ary[2];
+
+    x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+    x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+    v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+    v1 |= efp[15][ r2         & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+    v1 |= efp[14][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+    v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+    v1 |= efp[12][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+    v1 |= efp[11][ r1         & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+    v1 |= efp[10][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+    v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+    v1 |= efp[ 8][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+    v1 |= efp[ 7][ l2         & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+    v1 |= efp[ 6][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+    v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+    v1 |= efp[ 4][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+    v1 |= efp[ 3][ l1         & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+    v1 |= efp[ 2][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+    v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+    v1 |= efp[ 0][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+    ary[0] = v1; ary[1] = v2;
+    return ary;
+  }
+
+/* 
+ * crypt only: convert from 64 bit to 11 bit ASCII 
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+  { static char outbuf[14];
+    int i, s;
+
+    outbuf[0] = salt[0];
+    outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+    for(i = 0; i < 5; i++)
+      outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+    s  = (v2 & 0xf) << 2;
+    v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+    for(i = 5; i < 10; i++)
+      outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+    outbuf[12] = bin_to_ascii(s);
+    outbuf[13] = 0;
+
+    return outbuf;
+  }
+
+/* 
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+   
+char *ufc_crypt(const char *key,const char *salt)
+  { ufc_long *s;
+    char ktab[9];
+
+    /*
+     * Hack DES tables according to salt
+     */
+    setup_salt(salt);
+
+    /*
+     * Setup key schedule
+     */
+    clearmem(ktab, sizeof ktab);
+    strncpy(ktab, key, 8);
+    ufc_mk_keytab(ktab);
+
+    /*
+     * Go for the 25 DES encryptions
+     */
+    s = _ufc_doit((ufc_long)0, (ufc_long)0, 
+                 (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+    /*
+     * And convert back to 6 bit ASCII
+     */
+    return output_conversion(s[0], s[1], salt);
+  }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+  { int i;
+    long32 s, *k;
+
+    while(itr--) {
+      k = &_ufc_keytab[0][0];
+      for(i=8; i--; ) {
+       s = *k++ ^ r1;
+       l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);  
+        l1 ^= SBA(_ufc_sb0, s >>= 16);   l2 ^= SBA(_ufc_sb0, (s)         +4); 
+        s = *k++ ^ r2; 
+        l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+        l1 ^= SBA(_ufc_sb2, s >>= 16);   l2 ^= SBA(_ufc_sb2, (s)         +4);
+
+        s = *k++ ^ l1; 
+        r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);  
+        r1 ^= SBA(_ufc_sb0, s >>= 16);   r2 ^= SBA(_ufc_sb0, (s)         +4); 
+        s = *k++ ^ l2; 
+        r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);  
+        r1 ^= SBA(_ufc_sb2, s >>= 16);   r2 ^= SBA(_ufc_sb2, (s)         +4);
+      } 
+      s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+    }
+    return _ufc_dofinalperm(l1, l2, r1, r2);
+  }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+  { int i;
+    long64 l, r, s, *k;
+
+    l = (((long64)l1) << 32) | ((long64)l2);
+    r = (((long64)r1) << 32) | ((long64)r2);
+
+    while(itr--) {
+      k = &_ufc_keytab[0];
+      for(i=8; i--; ) {
+       s = *k++ ^ r;
+       l ^= SBA(_ufc_sb3, (s >>  0) & 0xffff);
+        l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+        l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+        l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+       s = *k++ ^ l;
+       r ^= SBA(_ufc_sb3, (s >>  0) & 0xffff);
+        r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+        r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+        r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+      } 
+      s=l; l=r; r=s;
+    }
+
+    l1 = l >> 32; l2 = l & 0xffffffff;
+    r1 = r >> 32; r2 = r & 0xffffffff;
+    return _ufc_dofinalperm(l1, l2, r1, r2);
+  }
+
+#endif
+
+#define crypt ufc_crypt
+#endif
+
+main()
+{
+       char passwd[9];
+       char salt[9];
+       char c_out1[256];
+       char c_out2[256];
+
+       char expected_out[14];
+
+       strcpy(expected_out, "12yJ.Of/NQ.Pk");
+       strcpy(passwd, "12345678");
+       strcpy(salt, "12345678");
+       
+       strcpy(c_out1, crypt(passwd, salt));
+       salt[2] = '\0';
+       strcpy(c_out2, crypt(passwd, salt));
+
+       /*
+        * If the non-trucated salt fails but the
+        * truncated salt succeeds then exit 1.
+        */
+
+       if((strcmp(c_out1, expected_out) != 0) && 
+               (strcmp(c_out2, expected_out) == 0))
+               exit(1);
+
+#ifdef HAVE_BIGCRYPT
+       /*
+        * Try the same with bigcrypt...
+        */
+
+       {
+               char big_passwd[17];
+               char big_salt[17];
+               char big_c_out1[256];
+               char big_c_out2[256];
+               char big_expected_out[27];
+
+               strcpy(big_passwd, "1234567812345678");
+               strcpy(big_salt, "1234567812345678");
+               strcpy(big_expected_out, "12yJ.Of/NQ.PklfyCuHi/rwM");
+
+               strcpy(big_c_out1, bigcrypt(big_passwd, big_salt));
+               big_salt[2] = '\0';
+               strcpy(big_c_out2, bigcrypt(big_passwd, big_salt));
+
+               /*
+                * If the non-trucated salt fails but the
+                * truncated salt succeeds then exit 1.
+                */
+
+               if((strcmp(big_c_out1, big_expected_out) != 0) && 
+                       (strcmp(big_c_out2, big_expected_out) == 0))
+                       exit(1);
+
+       }
+#endif
+
+       exit(0);
+}
diff --git a/source4/tests/fcntl_lock.c b/source4/tests/fcntl_lock.c
new file mode 100644 (file)
index 0000000..3dc12a3
--- /dev/null
@@ -0,0 +1,121 @@
+/* test whether fcntl locking works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+  return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+  return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock lock;
+       int fd, ret, status=1;
+       pid_t pid;
+       char *testdir = NULL;
+
+       testdir = getenv("TESTDIR");
+       if (testdir) chdir(testdir);
+
+       alarm(10);
+
+       if (!(pid=fork())) {
+               sleep(2);
+               fd = open(DATA, O_RDONLY);
+
+               if (fd == -1) {
+                       fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                               DATA, (int)errno);
+                       exit(1);
+               }
+
+               lock.l_type = F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = 0;
+               lock.l_len = 4;
+               lock.l_pid = getpid();
+               
+               lock.l_type = F_WRLCK;
+               
+               /* check if a lock applies */
+               ret = fcntl(fd,F_GETLK,&lock);
+
+               if ((ret == -1) ||
+                   (lock.l_type == F_UNLCK)) {
+                       fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+                       exit(1);
+               } else {
+                       exit(0);
+               }
+       }
+
+       unlink(DATA);
+       fd = open(DATA, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               exit(1);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK,&lock);
+
+       sys_waitpid(pid, &status, 0);
+
+       unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if(WIFEXITED(status)) {
+        status = WEXITSTATUS(status);
+    } else {
+        status = 1;
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       if (status) {
+               fprintf(stderr,"ERROR: lock test failed with status=%d\n", 
+                       status);
+       }
+
+       exit(status);
+}
diff --git a/source4/tests/fcntl_lock64.c b/source4/tests/fcntl_lock64.c
new file mode 100644 (file)
index 0000000..e5ecd88
--- /dev/null
@@ -0,0 +1,96 @@
+/* test whether 64 bit fcntl locking really works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+  return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+  return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl64"
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock64 lock;
+       int fd, ret, status=1;
+       pid_t pid;
+
+       if (!(pid=fork())) {
+               sleep(2);
+               fd = open64(DATA, O_RDONLY);
+
+               if (fd == -1) exit(1);
+
+               lock.l_type = F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = 0;
+               lock.l_len = 4;
+               lock.l_pid = getpid();
+               
+               lock.l_type = F_WRLCK;
+               
+               /* check if a lock applies */
+               ret = fcntl(fd,F_GETLK64,&lock);
+
+               if ((ret == -1) ||
+                   (lock.l_type == F_UNLCK)) {
+/*            printf("No lock conflict\n"); */
+                       exit(1);
+               } else {
+/*            printf("lock conflict\n"); */
+                       exit(0);
+               }
+       }
+
+       fd = open64(DATA, O_RDWR|O_CREAT|O_TRUNC, 0600);
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+#if defined(COMPILER_SUPPORTS_LL)
+       lock.l_start = 0x100000000LL;
+#else
+       lock.l_start = 0x100000000;
+#endif
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK64,&lock);
+
+       sys_waitpid(pid, &status, 0);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+       if(WIFEXITED(status)) {
+               status = WEXITSTATUS(status);
+       } else {
+               status = 1;
+       }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       unlink(DATA);
+
+       exit(status);
+}
diff --git a/source4/tests/fcntl_lock_thread.c b/source4/tests/fcntl_lock_thread.c
new file mode 100644 (file)
index 0000000..f311056
--- /dev/null
@@ -0,0 +1,122 @@
+/* test whether fcntl locking works between threads on this Linux system */
+
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+
+#include <sys/fcntl.h>
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <pthread.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+  return waitpid(pid,status,options);
+}
+
+#define DATA "conftest.fcntl"
+
+#define SEEK_SET 0
+
+static void *test_thread(void *thread_parm)
+{
+       int *status = thread_parm;
+       int fd, ret;
+       struct flock lock;
+       
+       sleep(2);
+       fd = open(DATA, O_RDWR);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               pthread_exit(thread_parm);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = 0;
+               
+       /* check if a lock applies */
+       ret = fcntl(fd,F_SETLK,&lock);
+       if ((ret != -1)) {
+               fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+       } else {
+               *status = 0;  /* SUCCESS! */
+       }
+       pthread_exit(thread_parm);
+}
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+       struct flock lock;
+       int fd, ret, status=1, rc;
+       pid_t pid;
+       char *testdir = NULL;
+       pthread_t thread_id;
+       pthread_attr_t thread_attr;
+
+       testdir = getenv("TESTDIR");
+       if (testdir) chdir(testdir);
+
+       alarm(10);
+
+       pthread_attr_init(&thread_attr);
+       pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+       rc = pthread_create(&thread_id, &thread_attr, &test_thread, &status);
+       pthread_attr_destroy(&thread_attr);
+       if (rc == 0) {
+               fprintf(stderr,"created thread_id=%lu\n", 
+                       (unsigned long int)thread_id);
+       } else {
+               fprintf(stderr,"ERROR: thread create failed, rc=%d\n", rc);
+       }
+
+       unlink(DATA);
+       fd = open(DATA, O_RDWR|O_CREAT|O_RDWR, 0600);
+
+       if (fd == -1) {
+               fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", 
+                       DATA, (int)errno);
+               exit(1);
+       }
+
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       /* set a 4 byte write lock */
+       fcntl(fd,F_SETLK,&lock);
+
+       sleep(4);  /* allow thread to try getting lock */
+
+       unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if(WIFEXITED(status)) {
+        status = WEXITSTATUS(status);
+    } else {
+        status = 1;
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+       status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+       if (status) {
+               fprintf(stderr,"ERROR: lock test failed with status=%d\n", 
+                       status);
+       }
+
+       exit(status);
+}
diff --git a/source4/tests/ftruncate.c b/source4/tests/ftruncate.c
new file mode 100644 (file)
index 0000000..9328278
--- /dev/null
@@ -0,0 +1,27 @@
+/* test whether ftruncte() can extend a file */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.trunc"
+#define LEN 7663
+
+main()
+{
+       int *buf;
+       int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+
+       ftruncate(fd, LEN);
+
+       unlink(DATA);
+
+       if (lseek(fd, 0, SEEK_END) == LEN) {
+               exit(0);
+       }
+       exit(1);
+}
diff --git a/source4/tests/getgroups.c b/source4/tests/getgroups.c
new file mode 100644 (file)
index 0000000..343fd5a
--- /dev/null
@@ -0,0 +1,66 @@
+/* this tests whether getgroups actually returns lists of integers
+   rather than gid_t. The test only works if the user running
+   the test is in at least 1 group 
+
+   The test is designed to check for those broken OSes that define
+   getgroups() as returning an array of gid_t but actually return a
+   array of ints! Ultrix is one culprit
+  */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <grp.h>
+
+main()
+{
+       int i;
+       int *igroups;
+       char *cgroups;
+       int grp = 0;
+       int  ngroups = getgroups(0,&grp);
+
+       if (sizeof(gid_t) == sizeof(int)) {
+               fprintf(stderr,"gid_t and int are the same size\n");
+               exit(1);
+       }
+
+       if (ngroups <= 0)
+               ngroups = 32;
+
+       igroups = (int *)malloc(sizeof(int)*ngroups);
+
+       for (i=0;i<ngroups;i++)
+               igroups[i] = 0x42424242;
+
+       ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+       if (igroups[0] == 0x42424242)
+               ngroups = 0;
+
+       if (ngroups == 0) {
+               printf("WARNING: can't determine getgroups return type\n");
+               exit(1);
+       }
+       
+       cgroups = (char *)igroups;
+
+       if (ngroups == 1 && 
+           cgroups[2] == 0x42 && cgroups[3] == 0x42) {
+               fprintf(stderr,"getgroups returns gid_t\n");
+               exit(1);
+       }
+         
+       for (i=0;i<ngroups;i++) {
+               if (igroups[i] == 0x42424242) {
+                       fprintf(stderr,"getgroups returns gid_t\n");
+                       exit(1);
+               }
+       }
+
+       exit(0);
+}
diff --git a/source4/tests/shared_mmap.c b/source4/tests/shared_mmap.c
new file mode 100644 (file)
index 0000000..fcef75d
--- /dev/null
@@ -0,0 +1,68 @@
+/* this tests whether we can use a shared writeable mmap on a file -
+   as needed for the mmap varient of FAST_SHARE_MODES */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.mmap"
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+main()
+{
+       int *buf;
+       int i; 
+       int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+       int count=7;
+
+       if (fd == -1) exit(1);
+
+       for (i=0;i<10000;i++) {
+               write(fd,&i,sizeof(i));
+       }
+
+       close(fd);
+
+       if (fork() == 0) {
+               fd = open(DATA,O_RDWR);
+               if (fd == -1) exit(1);
+
+               buf = (int *)mmap(NULL, 10000*sizeof(int), 
+                                  (PROT_READ | PROT_WRITE), 
+                                  MAP_FILE | MAP_SHARED, 
+                                  fd, 0);
+
+               while (count-- && buf[9124] != 55732) sleep(1);
+
+               if (count <= 0) exit(1);
+
+               buf[1763] = 7268;
+               exit(0);
+       }
+
+       fd = open(DATA,O_RDWR);
+       if (fd == -1) exit(1);
+
+       buf = (int *)mmap(NULL, 10000*sizeof(int), 
+                          (PROT_READ | PROT_WRITE), 
+                          MAP_FILE | MAP_SHARED, 
+                          fd, 0);
+
+       if (buf == (int *)-1) exit(1);
+
+       buf[9124] = 55732;
+
+       while (count-- && buf[1763] != 7268) sleep(1);
+
+       unlink(DATA);
+               
+       if (count > 0) exit(0);
+       exit(1);
+}
diff --git a/source4/tests/shlib.c b/source4/tests/shlib.c
new file mode 100644 (file)
index 0000000..761d9fd
--- /dev/null
@@ -0,0 +1,6 @@
+/* a trivial function used to test building shared libraries */
+
+int foo(void)
+{
+       return 1;
+}
diff --git a/source4/tests/summary.c b/source4/tests/summary.c
new file mode 100644 (file)
index 0000000..d3708c2
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main()
+{
+#if !(defined(HAVE_FCNTL_LOCK) || defined(HAVE_STRUCT_FLOCK64))
+       printf("ERROR: No locking available. Running Samba would be unsafe\n");
+       exit(1);
+#endif
+
+#if !(defined(HAVE_IFACE_IFCONF) || defined(HAVE_IFACE_IFREQ) || defined(HAVE_IFACE_AIX))
+       printf("WARNING: No automated network interface determination\n");
+#endif
+
+#if !(defined(USE_SETEUID) || defined(USE_SETREUID) || defined(USE_SETRESUID) || defined(USE_SETUIDX))
+       printf("ERROR: no seteuid method available\n");
+       /* REWRITE: exit(1); */
+#endif
+
+#if !(defined(STAT_STATVFS) || defined(STAT_STATVFS64) || defined(STAT_STATFS3_OSF1) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS4) || defined(STAT_STATFS2_FSIZE) || defined(STAT_STATFS2_FS_DATA))
+       printf("ERROR: No disk free routine!\n");
+       exit(1);
+#endif
+
+#if !((defined(HAVE_RANDOM) || defined(HAVE_RAND)) && (defined(HAVE_SRANDOM) || defined(HAVE_SRAND)))
+    printf("ERROR: No random or srandom routine!\n");
+    exit(1);
+#endif
+
+       exit(0);
+}
diff --git a/source4/tests/trivial.c b/source4/tests/trivial.c
new file mode 100644 (file)
index 0000000..2723637
--- /dev/null
@@ -0,0 +1,4 @@
+main()
+{
+       exit(0);
+}
diff --git a/source4/tests/unixsock.c b/source4/tests/unixsock.c
new file mode 100644 (file)
index 0000000..f2765d6
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*- c-file-style: "linux" -*-
+ *
+ * Try creating a Unix-domain socket, opening it, and reading from it.
+ * The POSIX name for these is AF_LOCAL/PF_LOCAL.
+ *
+ * This is used by the Samba autoconf scripts to detect systems which
+ * don't have Unix-domain sockets, such as (probably) VMS, or systems
+ * on which they are broken under some conditions, such as RedHat 7.0
+ * (unpatched).  We can't build WinBind there at the moment.
+ *
+ * Coding standard says to always use exit() for this, not return, so
+ * we do.
+ *
+ * Martin Pool <mbp@samba.org>, June 2000. */
+
+/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#  include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_ERRNO_DECL
+# include <errno.h>
+#else
+extern int errno;
+#endif
+
+static int bind_socket(char const *filename)
+{
+       int sock_fd;
+       struct sockaddr_un name;
+       size_t size;
+       
+       /* Create the socket. */
+       if ((sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+               perror ("socket(PF_LOCAL, SOCK_STREAM)");
+               exit(1);
+       }
+     
+       /* Bind a name to the socket. */
+       name.sun_family = AF_LOCAL;
+       strncpy(name.sun_path, filename, sizeof (name.sun_path));
+     
+       /* The size of the address is
+          the offset of the start of the filename,
+          plus its length,
+          plus one for the terminating null byte.
+          Alternatively you can just do:
+          size = SUN_LEN (&name);
+      */
+       size = SUN_LEN(&name);
+       /* XXX: This probably won't work on unfriendly libcs */
+     
+       if (bind(sock_fd, (struct sockaddr *) &name, size) < 0) {
+               perror ("bind");
+               exit(1);
+       }
+
+       return sock_fd;
+}
+
+
+int main(void)
+{
+       int sock_fd;
+       int kid;
+       char const *filename = "conftest.unixsock.sock";
+
+       /* abolish hanging */
+       alarm(15);              /* secs */
+
+       if ((sock_fd = bind_socket(filename)) < 0)
+               exit(1);
+
+       /* the socket will be deleted when autoconf cleans up these
+           files. */
+
+       exit(0);
+}
diff --git a/source4/torture/.cvsignore b/source4/torture/.cvsignore
new file mode 100644 (file)
index 0000000..06cac36
--- /dev/null
@@ -0,0 +1 @@
+torturebad.c
diff --git a/source4/torture/aliases.c b/source4/torture/aliases.c
new file mode 100644 (file)
index 0000000..feb940e
--- /dev/null
@@ -0,0 +1,403 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB trans2 alias scanner
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+int create_complex_file(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *fname);
+
+struct trans2_blobs {
+       struct trans2_blobs *next, *prev;
+       uint16 level;
+       DATA_BLOB params, data;
+};
+
+/* look for aliases for a query */
+static void gen_aliases(struct cli_state *cli, struct smb_trans2 *t2, int level_offset)
+{
+       TALLOC_CTX *mem_ctx;
+       uint16 level;
+       struct trans2_blobs *alias_blobs = NULL;
+       struct trans2_blobs *t2b, *t2b2;
+       int count=0, alias_count=0;
+
+       mem_ctx = talloc_init("aliases");
+
+       for (level=0;level<2000;level++) {
+               NTSTATUS status;
+
+               SSVAL(t2->in.params.data, level_offset, level);
+               
+               status = smb_raw_trans2(cli->tree, mem_ctx, t2);
+               if (!NT_STATUS_IS_OK(status)) continue;
+
+               t2b = talloc(mem_ctx, sizeof(*t2b));
+               t2b->level = level;
+               t2b->params = t2->out.params;
+               t2b->data = t2->out.data;
+               DLIST_ADD(alias_blobs, t2b);
+               d_printf("\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", 
+                        level, level,
+                        t2b->data.length, t2b->data.length);
+               count++;
+       }
+
+       d_printf("Found %d levels with success status\n", count);
+
+       for (t2b=alias_blobs; t2b; t2b=t2b->next) {
+               for (t2b2=alias_blobs; t2b2; t2b2=t2b2->next) {
+                       if (t2b->level >= t2b2->level) continue;
+                       if (data_blob_equal(&t2b->params, &t2b2->params) &&
+                           data_blob_equal(&t2b->data, &t2b2->data)) {
+                               printf("\tLevel %u (0x%x) and level %u (0x%x) are possible aliases\n", 
+                                      t2b->level, t2b->level, t2b2->level, t2b2->level);
+                               alias_count++;
+                       }
+               }
+       }
+
+       d_printf("Found %d aliased levels\n", alias_count);
+       
+       talloc_destroy(mem_ctx);
+}
+
+/* look for qfsinfo aliases */
+static void qfsinfo_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_QFSINFO;
+
+       d_printf("\nChecking for QFSINFO aliases\n");
+
+       t2.in.max_param = 0;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob(NULL, 2);
+       t2.in.data = data_blob(NULL, 0);
+
+       gen_aliases(cli, &t2, 0);
+}
+
+/* look for qfileinfo aliases */
+static void qfileinfo_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_QFILEINFO;
+       const char *fname = "\\qfileinfo_aliases.txt";
+       int fnum;
+
+       d_printf("\nChecking for QFILEINFO aliases\n");
+
+       t2.in.max_param = 2;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob(NULL, 4);
+       t2.in.data = data_blob(NULL, 0);
+
+       cli_unlink(cli, fname);
+       fnum = create_complex_file(cli, cli->mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2));
+
+       SSVAL(t2.in.params.data, 0, fnum);
+
+       gen_aliases(cli, &t2, 2);
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+}
+
+
+/* look for qpathinfo aliases */
+static void qpathinfo_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_QPATHINFO;
+       const char *fname = "\\qpathinfo_aliases.txt";
+       int fnum;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("qpathinfo");
+
+       d_printf("\nChecking for QPATHINFO aliases\n");
+
+       t2.in.max_param = 2;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+       t2.in.data = data_blob(NULL, 0);
+
+       cli_unlink(cli, fname);
+       fnum = create_complex_file(cli, cli->mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2));
+       cli_close(cli, fnum);
+
+       SIVAL(t2.in.params.data, 2, 0);
+
+       cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, 
+                              fname, STR_TERMINATE);
+
+       gen_aliases(cli, &t2, 0);
+
+       cli_unlink(cli, fname);
+       talloc_destroy(mem_ctx);
+}
+
+
+/* look for trans2 findfirst aliases */
+static void findfirst_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_FINDFIRST;
+       const char *fname = "\\findfirst_aliases.txt";
+       int fnum;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("findfirst");
+
+       d_printf("\nChecking for FINDFIRST aliases\n");
+
+       t2.in.max_param = 16;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+       t2.in.data = data_blob(NULL, 0);
+
+       cli_unlink(cli, fname);
+       fnum = create_complex_file(cli, cli->mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2));
+       cli_close(cli, fnum);
+
+       SSVAL(t2.in.params.data, 0, 0);
+       SSVAL(t2.in.params.data, 2, 1);
+       SSVAL(t2.in.params.data, 4, FLAG_TRANS2_FIND_CLOSE);
+       SSVAL(t2.in.params.data, 6, 0);
+       SIVAL(t2.in.params.data, 8, 0);
+
+       cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, 
+                              fname, STR_TERMINATE);
+
+       gen_aliases(cli, &t2, 6);
+
+       cli_unlink(cli, fname);
+       talloc_destroy(mem_ctx);
+}
+
+
+
+/* look for aliases for a set function */
+static void gen_set_aliases(struct cli_state *cli, struct smb_trans2 *t2, int level_offset)
+{
+       TALLOC_CTX *mem_ctx;
+       uint16 level;
+       struct trans2_blobs *alias_blobs = NULL;
+       struct trans2_blobs *t2b;
+       int count=0, dsize;
+
+       mem_ctx = talloc_init("aliases");
+
+       for (level=1;level<1100;level++) {
+               NTSTATUS status, status1;
+               SSVAL(t2->in.params.data, level_offset, level);
+
+               status1 = NT_STATUS_OK;
+
+               for (dsize=2; dsize<1024; dsize += 2) {
+                       data_blob_free(&t2->in.data);
+                       t2->in.data = data_blob(NULL, dsize);
+                       data_blob_clear(&t2->in.data);
+                       status = smb_raw_trans2(cli->tree, mem_ctx, t2);
+                       /* some error codes mean that this whole level doesn't exist */
+                       if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, status) ||
+                           NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status) ||
+                           NT_STATUS_EQUAL(NT_STATUS_NOT_SUPPORTED, status)) {
+                               break;
+                       }
+                       if (NT_STATUS_IS_OK(status)) break;
+
+                       /* invalid parameter means that the level exists at this 
+                          size, but the contents are wrong (not surprising with
+                          all zeros!) */
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) break;
+
+                       /* this is the usual code for 'wrong size' */
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_INFO_LENGTH_MISMATCH)) {
+                               continue;
+                       }
+
+                       if (!NT_STATUS_EQUAL(status, status1)) {
+                               printf("level=%d size=%d %s\n", level, dsize, nt_errstr(status));
+                       }
+                       status1 = status;
+               }
+
+               if (!NT_STATUS_IS_OK(status) &&
+                   !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) continue;
+
+               t2b = talloc(mem_ctx, sizeof(*t2b));
+               t2b->level = level;
+               t2b->params = t2->out.params;
+               t2b->data = t2->out.data;
+               DLIST_ADD(alias_blobs, t2b);
+               d_printf("\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", 
+                        level, level,
+                        t2->in.data.length, t2->in.data.length);
+               count++;
+       }
+
+       d_printf("Found %d valid levels\n", count);
+       talloc_destroy(mem_ctx);
+}
+
+
+
+/* look for setfileinfo aliases */
+static void setfileinfo_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_SETFILEINFO;
+       const char *fname = "\\setfileinfo_aliases.txt";
+       int fnum;
+
+       d_printf("\nChecking for SETFILEINFO aliases\n");
+
+       t2.in.max_param = 2;
+       t2.in.max_data = 0;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob(NULL, 6);
+       t2.in.data = data_blob(NULL, 0);
+
+       cli_unlink(cli, fname);
+       fnum = create_complex_file(cli, cli->mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2));
+
+       SSVAL(t2.in.params.data, 0, fnum);
+       SSVAL(t2.in.params.data, 4, 0);
+
+       gen_set_aliases(cli, &t2, 2);
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+}
+
+/* look for setpathinfo aliases */
+static void setpathinfo_aliases(struct cli_state *cli)
+{
+       struct smb_trans2 t2;
+       uint16 setup = TRANSACT2_SETPATHINFO;
+       const char *fname = "\\setpathinfo_aliases.txt";
+       int fnum;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("findfirst");
+
+       d_printf("\nChecking for SETPATHINFO aliases\n");
+
+       t2.in.max_param = 32;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 0;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+       t2.in.data = data_blob(NULL, 0);
+
+       cli_unlink(cli, fname);
+
+       fnum = create_complex_file(cli, cli->mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2));
+       cli_close(cli, fnum);
+
+       SSVAL(t2.in.params.data, 2, 0);
+
+       cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, 
+                              fname, STR_TERMINATE);
+
+       gen_set_aliases(cli, &t2, 0);
+
+       if (!cli_unlink(cli, fname)) {
+               printf("unlink: %s\n", cli_errstr(cli));
+       }
+       talloc_destroy(mem_ctx);
+}
+
+
+/* look for aliased info levels in trans2 calls */
+BOOL torture_trans2_aliases(int dummy)
+{
+       struct cli_state *cli;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+
+       qfsinfo_aliases(cli);
+       qfileinfo_aliases(cli);
+       qpathinfo_aliases(cli);
+       findfirst_aliases(cli);
+       setfileinfo_aliases(cli);
+       setpathinfo_aliases(cli);
+
+       if (!torture_close_connection(cli)) {
+               return False;
+       }
+
+       return True;
+}
diff --git a/source4/torture/cmd_sam.c b/source4/torture/cmd_sam.c
new file mode 100644 (file)
index 0000000..3f7f7df
--- /dev/null
@@ -0,0 +1,514 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM module functions
+
+   Copyright (C) Jelmer Vernooij 2002
+
+   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 "samtest.h"
+
+static void print_account(SAM_ACCOUNT_HANDLE *a)
+{
+       /* FIXME */
+}
+
+static NTSTATUS cmd_context(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       NTSTATUS status;
+       char **plugins;
+       int i;
+
+       plugins = malloc(argc * sizeof(char *));
+
+       for(i = 1; i < argc; i++)
+               plugins[i-1] = argv[i];
+
+       plugins[argc-1] = NULL;
+
+       if(!NT_STATUS_IS_OK(status = make_sam_context_list(&st->context, plugins))) {
+               printf("make_sam_context_list failed: %s\n", nt_errstr(status));
+               SAFE_FREE(plugins);
+               return status;
+       }
+
+       SAFE_FREE(plugins);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_load_module(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char *plugin_arg[2];
+       NTSTATUS status;
+       if (argc != 2 && argc != 3) {
+               printf("Usage: load <module path> [domain-name]\n");
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 3)
+               asprintf(&plugin_arg[0], "plugin:%s|%s", argv[1], argv[2]);
+       else
+               asprintf(&plugin_arg[0], "plugin:%s", argv[1]);
+
+       plugin_arg[1] = NULL;
+       
+       if(!NT_STATUS_IS_OK(status = make_sam_context_list(&st->context, plugin_arg))) {
+               free(plugin_arg[0]);
+               return status;
+       }
+       
+       free(plugin_arg[0]);
+
+       printf("load: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_get_sec_desc(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_set_sec_desc(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_lookup_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char *name;
+       uint32 type;
+       NTSTATUS status;
+       DOM_SID sid;
+       if (argc != 2) {
+               printf("Usage: lookup_sid <sid>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!string_to_sid(&sid, argv[1])){
+               printf("Unparseable SID specified!\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_lookup_sid(st->context, st->token, mem_ctx, &sid, &name, &type))) {
+               printf("sam_lookup_sid failed!\n");
+               return status;
+       }
+
+       printf("Name: %s\n", name);
+       printf("Type: %d\n", type); /* FIXME: What kind of an integer is type ? */
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lookup_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       DOM_SID sid;
+       uint32 type;
+       NTSTATUS status;
+       if (argc != 3) {
+               printf("Usage: lookup_name <domain> <name>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_lookup_name(st->context, st->token, argv[1], argv[2], &sid, &type))) {
+               printf("sam_lookup_name failed!\n");
+               return status;
+       }
+
+       printf("SID: %s\n", sid_string_static(&sid));
+       printf("Type: %d\n", type);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lookup_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_lookup_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_lookup_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       DOM_SID *sid;
+       NTSTATUS status;
+       if (argc != 2) {
+               printf("Usage: lookup_domain <domain>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_lookup_domain(st->context, st->token, argv[1], &sid))) {
+               printf("sam_lookup_name failed!\n");
+               return status;
+       }
+
+       printf("SID: %s\n", sid_string_static(sid));
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_enum_domains(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int32 domain_count, i;
+       DOM_SID *domain_sids;
+       char **domain_names;
+       NTSTATUS status;
+
+       if (!NT_STATUS_IS_OK(status = sam_enum_domains(st->context, st->token, &domain_count, &domain_sids, &domain_names))) {
+               printf("sam_enum_domains failed!\n");
+               return status;
+       }
+
+       if (domain_count == 0) {
+               printf("No domains found!\n");
+               return NT_STATUS_OK;
+       }
+
+       for (i = 0; i < domain_count; i++) {
+               printf("%s %s\n", domain_names[i], sid_string_static(&domain_sids[i]));
+       }
+
+       SAFE_FREE(domain_sids);
+       SAFE_FREE(domain_names);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_update_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_show_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       NTSTATUS status;
+       DOM_SID sid;
+       SAM_DOMAIN_HANDLE *domain;
+       uint32 tmp_uint32;
+       uint16 tmp_uint16;
+       NTTIME tmp_nttime;
+       BOOL tmp_bool;
+       const char *tmp_string;
+
+       if (argc != 2) {
+               printf("Usage: show_domain <sid>\n");
+               return status;
+       }
+
+       if (!string_to_sid(&sid, argv[1])){
+               printf("Unparseable SID specified!\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_by_sid(st->context, st->token, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS, &sid, &domain))) {
+               printf("sam_get_domain_by_sid failed\n");
+               return status;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_num_accounts(domain, &tmp_uint32))) {
+               printf("sam_get_domain_num_accounts failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Number of accounts: %d\n", tmp_uint32);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_num_groups(domain, &tmp_uint32))) {
+               printf("sam_get_domain_num_groups failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Number of groups: %u\n", tmp_uint32);
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_num_aliases(domain, &tmp_uint32))) {
+               printf("sam_get_domain_num_aliases failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Number of aliases: %u\n", tmp_uint32);
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_name(domain, &tmp_string))) {
+               printf("sam_get_domain_name failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Domain Name: %s\n", tmp_string);
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_lockout_count(domain, &tmp_uint16))) {
+               printf("sam_get_domain_lockout_count failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Lockout Count: %u\n", tmp_uint16);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_force_logoff(domain, &tmp_bool))) {
+               printf("sam_get_domain_force_logoff failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Force Logoff: %s\n", (tmp_bool?"Yes":"No"));
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_lockout_duration(domain, &tmp_nttime))) {
+               printf("sam_get_domain_lockout_duration failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Lockout duration: %u\n", tmp_nttime.low);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_login_pwdchange(domain, &tmp_bool))) {
+               printf("sam_get_domain_login_pwdchange failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Password changing allowed: %s\n", (tmp_bool?"Yes":"No"));
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_max_pwdage(domain, &tmp_nttime))) {
+               printf("sam_get_domain_max_pwdage failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Maximum password age: %u\n", tmp_nttime.low);
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_min_pwdage(domain, &tmp_nttime))) {
+               printf("sam_get_domain_min_pwdage failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Minimal password age: %u\n", tmp_nttime.low);
+       }
+       
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_min_pwdlength(domain, &tmp_uint16))) {
+               printf("sam_get_domain_min_pwdlength: %s\n", nt_errstr(status));
+       } else {
+               printf("Minimal Password Length: %u\n", tmp_uint16);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_pwd_history(domain, &tmp_uint16))) {
+               printf("sam_get_domain_pwd_history failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Password history: %u\n", tmp_uint16);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_reset_count(domain, &tmp_nttime))) {
+               printf("sam_get_domain_reset_count failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Reset count: %u\n", tmp_nttime.low);
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_domain_server(domain, &tmp_string))) {
+               printf("sam_get_domain_server failed: %s\n", nt_errstr(status));
+       } else {
+               printf("Server: %s\n", tmp_string);
+       }
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_create_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_update_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_delete_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_enum_accounts(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       NTSTATUS status;
+       DOM_SID sid;
+       int32 account_count, i;
+       SAM_ACCOUNT_ENUM *accounts;
+
+       if (argc != 2) {
+               printf("Usage: enum_accounts <domain-sid>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!string_to_sid(&sid, argv[1])){
+               printf("Unparseable SID specified!\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_enum_accounts(st->context, st->token, &sid, 0, &account_count, &accounts))) {
+               printf("sam_enum_accounts failed: %s\n", nt_errstr(status));
+               return status;
+       }
+
+       if (account_count == 0) {
+               printf("No accounts found!\n");
+               return NT_STATUS_OK;
+       }
+
+       for (i = 0; i < account_count; i++)
+               printf("SID: %s\nName: %s\nFullname: %s\nDescription: %s\nACB_BITS: %08X\n\n", 
+                          sid_string_static(&accounts[i].sid), accounts[i].account_name,
+                          accounts[i].full_name, accounts[i].account_desc, 
+                          accounts[i].acct_ctrl);
+
+       SAFE_FREE(accounts);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lookup_account_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       NTSTATUS status;
+       DOM_SID sid;
+       SAM_ACCOUNT_HANDLE *account;
+
+       if (argc != 2) {
+               printf("Usage: lookup_account_sid <account-sid>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!string_to_sid(&sid, argv[1])){
+               printf("Unparseable SID specified!\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!NT_STATUS_IS_OK(status = sam_get_account_by_sid(st->context, st->token, GENERIC_RIGHTS_USER_ALL_ACCESS, &sid, &account))) {
+               printf("context_sam_get_account_by_sid failed: %s\n", nt_errstr(status));
+               return status;
+       }
+
+       print_account(account);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lookup_account_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       NTSTATUS status;
+       SAM_ACCOUNT_HANDLE *account;
+
+       if (argc != 3) {
+               printf("Usage: lookup_account_name <domain-name> <account-name>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+
+       if (!NT_STATUS_IS_OK(status = sam_get_account_by_name(st->context, st->token, GENERIC_RIGHTS_USER_ALL_ACCESS, argv[1], argv[2], &account))) {
+               printf("context_sam_get_account_by_sid failed: %s\n", nt_errstr(status));
+               return status;
+       }
+
+       print_account(account);
+       
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_create_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_update_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_delete_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_enum_groups(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_lookup_group_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_lookup_group_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_group_add_member(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS cmd_group_del_member(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+static NTSTATUS cmd_group_enum(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+static NTSTATUS cmd_get_sid_groups(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+struct cmd_set sam_general_commands[] = {
+
+       { "General SAM Commands" },
+
+       { "load", cmd_load_module, "Load a module", "load <module.so> [domain-sid]" },
+       { "context", cmd_context, "Load specified context", "context [DOMAIN|]backend1[:options] [DOMAIN|]backend2[:options]" },
+       { "get_sec_desc", cmd_get_sec_desc, "Get security descriptor info", "get_sec_desc <access-token> <sid>" },
+       { "set_sec_desc", cmd_set_sec_desc, "Set security descriptor info", "set_sec_desc <access-token> <sid>" },
+       { "lookup_sid", cmd_lookup_sid, "Lookup type of specified SID", "lookup_sid <sid>" },
+       { "lookup_name", cmd_lookup_name, "Lookup type of specified name", "lookup_name <sid>" },
+       { NULL }
+};
+
+struct cmd_set sam_domain_commands[] = {
+       { "Domain Commands" },
+       { "update_domain", cmd_update_domain, "Update domain information", "update_domain [domain-options] domain-name | domain-sid" },
+       { "show_domain", cmd_show_domain, "Show domain information", "show_domain domain-sid | domain-name" },
+       { "enum_domains", cmd_enum_domains, "Enumerate all domains", "enum_domains <token> <acct-ctrl>" },
+       { "lookup_domain", cmd_lookup_domain, "Lookup a domain by name", "lookup_domain domain-name" },
+       { NULL }
+};
+
+struct cmd_set sam_account_commands[] = {
+       { "Account Commands" },
+       { "create_account", cmd_create_account, "Create a new account with specified properties", "create_account [account-options]" },
+       { "update_account", cmd_update_account, "Update an existing account", "update_account [account-options] account-sid | account-name" },
+       { "delete_account", cmd_delete_account, "Delete an account", "delete_account account-sid | account-name" },
+       { "enum_accounts", cmd_enum_accounts, "Enumerate all accounts", "enum_accounts <token> <acct-ctrl>" },
+       { "lookup_account", cmd_lookup_account, "Lookup an account by either sid or name", "lookup_account account-sid | account-name" },
+       { "lookup_account_sid", cmd_lookup_account_sid, "Lookup an account by sid", "lookup_account_sid account-sid" },
+       { "lookup_account_name", cmd_lookup_account_name, "Lookup an account by name", "lookup_account_name account-name" },
+       { NULL }
+};
+
+struct cmd_set sam_group_commands[] = {
+       { "Group Commands" },
+       { "create_group", cmd_create_group, "Create a new group", "create_group [group-opts]" },
+       { "update_group", cmd_update_group, "Update an existing group", "update_group [group-opts] group-name | group-sid" },
+       { "delete_group", cmd_delete_group, "Delete an existing group", "delete_group group-name | group-sid" },
+       { "enum_groups", cmd_enum_groups, "Enumerate all groups", "enum_groups <token> <group-ctrl>" },
+       { "lookup_group", cmd_lookup_group, "Lookup a group by SID or name", "lookup_group group-sid | group-name" },
+       { "lookup_group_sid", cmd_lookup_group_sid, "Lookup a group by SID", "lookup_group_sid <sid>" },
+       { "lookup_group_name", cmd_lookup_group_name, "Lookup a group by name", "lookup_group_name <name>" },
+       { "group_add_member", cmd_group_add_member, "Add group member to group", "group_add_member <group-name | group-sid> <member-name | member-sid>" },
+       { "group_del_member", cmd_group_del_member, "Delete group member from group", "group_del_member <group-name | group-sid> <member-name | member-sid>" },
+       { "group_enum", cmd_group_enum, "Enumerate all members of specified group", "group_enum group-sid | group-name" },
+
+       { "get_sid_groups", cmd_get_sid_groups, "Get a list of groups specified sid is a member of", "group_enum <group-sid | group-name>" },
+       { NULL }
+};
diff --git a/source4/torture/cmd_vfs.c b/source4/torture/cmd_vfs.c
new file mode 100644 (file)
index 0000000..b90c53e
--- /dev/null
@@ -0,0 +1,1051 @@
+/* 
+   Unix SMB/CIFS implementation.
+   VFS module functions
+
+   Copyright (C) Simo Sorce 2002
+   Copyright (C) Eric Lorimer 2002
+
+   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 "vfstest.h"
+
+static char *null_string = "";
+
+static NTSTATUS cmd_load_module(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       struct smb_vfs_handle_struct *handle;
+       char *path = lp_vfs_path(0);
+       char name[PATH_MAX];
+       
+       if (argc != 2) {
+               printf("Usage: load <module path>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (path != NULL && *path != '\0') {
+               snprintf(name, PATH_MAX, "%s/%s", path, argv[1]);
+       } else {
+               snprintf(name, PATH_MAX, "%s", argv[1]);
+       }
+       vfs->conn->vfs_private = NULL;
+       handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct));
+       handle->handle = NULL;
+       DLIST_ADD(vfs->conn->vfs_private, handle)
+       if (!vfs_init_custom(vfs->conn, name)) {
+               DEBUG(0, ("load: error=-1 (vfs_init_custom failed for %s)\n", argv[1]));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       printf("load: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_populate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char c;
+       size_t size;
+       if (argc != 3) {
+               printf("Usage: populate <char> <size>\n");
+               return NT_STATUS_OK;
+       }
+       c = argv[1][0];
+       size = atoi(argv[2]);
+       vfs->data = (char *)talloc(mem_ctx, size);
+       if (vfs->data == NULL) {
+               printf("populate: error=-1 (not enough memory)");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       memset(vfs->data, c, size);
+       vfs->data_size = size;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_show_data(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       size_t offset;
+       size_t len;
+       if (argc != 1 && argc != 3) {
+               printf("Usage: showdata [<offset> <len>]\n");
+               return NT_STATUS_OK;
+       }
+       if (vfs->data == NULL || vfs->data_size == 0) {
+               printf("show_data: error=-1 (buffer empty)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (argc == 3) {
+               offset = atoi(argv[1]);
+               len = atoi(argv[2]);
+       } else {
+               offset = 0;
+               len = vfs->data_size;
+       }
+       if ((offset + len) > vfs->data_size) {
+               printf("show_data: error=-1 (not enough data in buffer)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       dump_data(0, (char *)(vfs->data) + offset, len);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_connect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       vfs->conn->vfs_ops.connect(vfs->conn, lp_servicename(vfs->conn->service), "vfstest");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disconnect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       vfs->conn->vfs_ops.disconnect(vfs->conn);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disk_free(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       SMB_BIG_UINT diskfree, bsize, dfree, dsize;
+       if (argc != 2) {
+               printf("Usage: disk_free <path>\n");
+               return NT_STATUS_OK;
+       }
+
+       diskfree = vfs->conn->vfs_ops.disk_free(vfs->conn, argv[1], False, &bsize, &dfree, &dsize);
+       printf("disk_free: %lu, bsize = %lu, dfree = %lu, dsize = %lu\n",
+                       (unsigned long)diskfree,
+                       (unsigned long)bsize,
+                       (unsigned long)dfree,
+                       (unsigned long)dsize);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_opendir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc != 2) {
+               printf("Usage: opendir <fname>\n");
+               return NT_STATUS_OK;
+       }
+
+       vfs->currentdir = vfs->conn->vfs_ops.opendir(vfs->conn, argv[1]);
+       if (vfs->currentdir == NULL) {
+               printf("opendir error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("opendir: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       struct dirent *dent;
+
+       if (vfs->currentdir == NULL) {
+               printf("readdir: error=-1 (no open directory)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       dent = vfs->conn->vfs_ops.readdir(vfs->conn, vfs->currentdir);
+       if (dent == NULL) {
+               printf("readdir: NULL\n");
+               return NT_STATUS_OK;
+       }
+
+       printf("readdir: %s\n", dent->d_name);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_mkdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc != 2) {
+               printf("Usage: mkdir <path>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.mkdir(vfs->conn, argv[1], 00755) == -1) {
+               printf("mkdir error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       printf("mkdir: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_closedir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int ret;
+       
+       if (vfs->currentdir == NULL) {
+               printf("closedir: failure (no directory open)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       ret = vfs->conn->vfs_ops.closedir(vfs->conn, vfs->currentdir);
+       if (ret == -1) {
+               printf("closedir failure: %s\n", strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("closedir: ok\n");
+       vfs->currentdir = NULL;
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_open(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int flags, fd;
+       mode_t mode;
+       char *flagstr;
+
+       mode = 00400;
+
+       if (argc < 3 || argc > 5) {
+               printf("Usage: open <filename> <flags> <mode>\n");
+               printf("  flags: O = O_RDONLY\n");
+               printf("         R = O_RDWR\n");
+               printf("         W = O_WRONLY\n");
+               printf("         C = O_CREAT\n");
+               printf("         E = O_EXCL\n");
+               printf("         T = O_TRUNC\n");
+               printf("         A = O_APPEND\n");
+               printf("         N = O_NONBLOCK/O_NDELAY\n");
+#ifdef O_SYNC
+               printf("         S = O_SYNC\n");
+#endif
+#ifdef O_NOFOLLOW
+               printf("         F = O_NOFOLLOW\n");
+#endif
+               printf("  mode: see open.2\n");
+               printf("        mode is ignored if C flag not present\n");
+               printf("        mode defaults to 00400\n");
+               return NT_STATUS_OK;
+       }
+       flags = 0;
+       flagstr = argv[2];
+       while (*flagstr) {
+               switch (*flagstr) {
+               case 'O':
+                       flags |= O_RDONLY;
+                       break;
+               case 'R':
+                       flags |= O_RDWR;
+                       break;
+               case 'W':
+                       flags |= O_WRONLY;
+                       break;
+               case 'C':
+                       flags |= O_CREAT;
+                       break;
+               case 'E':
+                       flags |= O_EXCL;
+                       break;
+               case 'T':
+                       flags |= O_TRUNC;
+                       break;
+               case 'A':
+                       flags |= O_APPEND;
+                       break;
+               case 'N':
+                       flags |= O_NONBLOCK;
+                       break;
+#ifdef O_SYNC
+               case 'S':
+                       flags |= O_SYNC;
+                       break;
+#endif
+#ifdef O_NOFOLLOW
+               case 'F':
+                       flags |= O_NOFOLLOW;
+                       break;
+#endif
+               default:
+                       printf("open: error=-1 (invalid flag!)\n");
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               flagstr++;
+       }
+       if ((flags & O_CREAT) && argc == 4) {
+               if (sscanf(argv[3], "%o", &mode) == 0) {
+                       printf("open: error=-1 (invalid mode!)\n");
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+       }
+
+       fd = vfs->conn->vfs_ops.open(vfs->conn, argv[1], flags, mode);
+       if (fd == -1) {
+               printf("open: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       vfs->files[fd] = (struct files_struct *)malloc(sizeof(struct files_struct));
+       vfs->files[fd]->fsp_name = strdup(argv[1]);
+       vfs->files[fd]->fd = fd;
+       vfs->files[fd]->conn = vfs->conn;
+       printf("open: fd=%d\n", fd);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_pathfunc(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int ret = -1;
+
+       if (argc != 2) {
+               printf("Usage: %s <path>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (strcmp("rmdir", argv[0]) == 0 ) {
+               ret = vfs->conn->vfs_ops.rmdir(vfs->conn, argv[1]);
+       } else if (strcmp("unlink", argv[0]) == 0 ) {
+               ret = vfs->conn->vfs_ops.unlink(vfs->conn, argv[1]);
+       } else if (strcmp("chdir", argv[0]) == 0 ) {
+               ret = vfs->conn->vfs_ops.chdir(vfs->conn, argv[1]);
+       } else {
+               printf("%s: error=%d (invalid function name!)\n", argv[0], errno);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (ret == -1) {
+               printf("%s: error=%d (%s)\n", argv[0], errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("%s: ok\n", argv[0]);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_close(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd, ret;
+
+       if (argc != 2) {
+               printf("Usage: close <fd>\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       if (vfs->files[fd] == NULL) {
+               printf("close: error=-1 (invalid file descriptor)\n");
+               return NT_STATUS_OK;
+       }
+
+       ret = vfs->conn->vfs_ops.close(vfs->files[fd], fd);
+       if (ret == -1 )
+               printf("close: error=%d (%s)\n", errno, strerror(errno));
+       else
+               printf("close: ok\n");
+
+       SAFE_FREE(vfs->files[fd]->fsp_name);
+       SAFE_FREE(vfs->files[fd]);
+       vfs->files[fd] = NULL;
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_read(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd;
+       size_t size, rsize;
+
+       if (argc != 3) {
+               printf("Usage: read <fd> <size>\n");
+               return NT_STATUS_OK;
+       }
+
+       /* do some error checking on these */
+       fd = atoi(argv[1]);
+       size = atoi(argv[2]);
+       vfs->data = (char *)talloc(mem_ctx, size);
+       if (vfs->data == NULL) {
+               printf("read: error=-1 (not enough memory)");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       vfs->data_size = size;
+       
+       rsize = vfs->conn->vfs_ops.read(vfs->files[fd], fd, vfs->data, size);
+       if (rsize == -1) {
+               printf("read: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("read: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_write(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd, size, wsize;
+
+       if (argc != 3) {
+               printf("Usage: write <fd> <size>\n");
+               return NT_STATUS_OK;
+       }
+
+       /* some error checking should go here */
+       fd = atoi(argv[1]);
+       size = atoi(argv[2]);
+       if (vfs->data == NULL) {
+               printf("write: error=-1 (buffer empty, please populate it before writing)");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (vfs->data_size < size) {
+               printf("write: error=-1 (buffer too small, please put some more data in)");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       wsize = vfs->conn->vfs_ops.write(vfs->files[fd], fd, vfs->data, size);
+
+       if (wsize == -1) {
+               printf("write: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("write: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lseek(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd, offset, whence;
+       SMB_OFF_T pos;
+
+       if (argc != 4) {
+               printf("Usage: lseek <fd> <offset> <whence>\n...where whence is 1 => SEEK_SET, 2 => SEEK_CUR, 3 => SEEK_END\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       offset = atoi(argv[2]);
+       whence = atoi(argv[3]);
+       switch (whence) {
+               case 1:         whence = SEEK_SET; break;
+               case 2:         whence = SEEK_CUR; break;
+               default:        whence = SEEK_END;
+       }
+
+       pos = vfs->conn->vfs_ops.lseek(vfs->files[fd], fd, offset, whence);
+       if (pos == (SMB_OFF_T)-1) {
+               printf("lseek: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("lseek: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_rename(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int ret;
+       if (argc != 3) {
+               printf("Usage: rename <old> <new>\n");
+               return NT_STATUS_OK;
+       }
+
+       ret = vfs->conn->vfs_ops.rename(vfs->conn, argv[1], argv[2]);
+       if (ret == -1) {
+               printf("rename: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("rename: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fsync(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int ret, fd;
+       if (argc != 2) {
+               printf("Usage: fsync <fd>\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       ret = vfs->conn->vfs_ops.fsync(vfs->files[fd], fd);
+       if (ret == -1) {
+               printf("fsync: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("fsync: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_stat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int ret;
+       char *user;
+       char *group;
+       struct passwd *pwd;
+       struct group *grp;
+       SMB_STRUCT_STAT st;
+
+       if (argc != 2) {
+               printf("Usage: stat <fname>\n");
+               return NT_STATUS_OK;
+       }
+
+       ret = vfs->conn->vfs_ops.stat(vfs->conn, argv[1], &st);
+       if (ret == -1) {
+               printf("stat: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       pwd = sys_getpwuid(st.st_uid);
+       if (pwd != NULL) user = strdup(pwd->pw_name);
+       else user = null_string;
+       grp = sys_getgrgid(st.st_gid);
+       if (grp != NULL) group = strdup(grp->gr_name);
+       else group = null_string;
+
+       printf("stat: ok\n");
+       printf("  File: %s", argv[1]);
+       if (S_ISREG(st.st_mode)) printf("  Regular File\n");
+       else if (S_ISDIR(st.st_mode)) printf("  Directory\n");
+       else if (S_ISCHR(st.st_mode)) printf("  Character Device\n");
+       else if (S_ISBLK(st.st_mode)) printf("  Block Device\n");
+       else if (S_ISFIFO(st.st_mode)) printf("  Fifo\n");
+       else if (S_ISLNK(st.st_mode)) printf("  Symbolic Link\n");
+       else if (S_ISSOCK(st.st_mode)) printf("  Socket\n");
+       printf("  Size: %10u", (unsigned int)st.st_size);
+       printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+       printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+       printf("  Device: 0x%10x", (unsigned int)st.st_dev);
+       printf(" Inode: %10u", (unsigned int)st.st_ino);
+       printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+       printf("  Access: %05o", (st.st_mode) & 007777);
+       printf(" Uid: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, st.st_gid, group);
+       printf("  Access: %s", ctime(&(st.st_atime)));
+       printf("  Modify: %s", ctime(&(st.st_mtime)));
+       printf("  Change: %s", ctime(&(st.st_ctime)));
+       if (user != null_string) SAFE_FREE(user);
+       if (group!= null_string) SAFE_FREE(group);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd;
+       char *user;
+       char *group;
+       struct passwd *pwd;
+       struct group *grp;
+       SMB_STRUCT_STAT st;
+
+       if (argc != 2) {
+               printf("Usage: fstat <fd>\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       if (fd < 0 || fd > 1024) {
+               printf("fstat: error=%d (file descriptor out of range)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->files[fd] == NULL) {
+               printf("fstat: error=%d (invalid file descriptor)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.fstat(vfs->files[fd], fd, &st) == -1) {
+               printf("fstat: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       pwd = sys_getpwuid(st.st_uid);
+       if (pwd != NULL) user = strdup(pwd->pw_name);
+       else user = null_string;
+       grp = sys_getgrgid(st.st_gid);
+       if (grp != NULL) group = strdup(grp->gr_name);
+       else group = null_string;
+
+       printf("fstat: ok\n");
+       if (S_ISREG(st.st_mode)) printf("  Regular File\n");
+       else if (S_ISDIR(st.st_mode)) printf("  Directory\n");
+       else if (S_ISCHR(st.st_mode)) printf("  Character Device\n");
+       else if (S_ISBLK(st.st_mode)) printf("  Block Device\n");
+       else if (S_ISFIFO(st.st_mode)) printf("  Fifo\n");
+       else if (S_ISLNK(st.st_mode)) printf("  Symbolic Link\n");
+       else if (S_ISSOCK(st.st_mode)) printf("  Socket\n");
+       printf("  Size: %10u", (unsigned int)st.st_size);
+       printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+       printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+       printf("  Device: 0x%10x", (unsigned int)st.st_dev);
+       printf(" Inode: %10u", (unsigned int)st.st_ino);
+       printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+       printf("  Access: %05o", (st.st_mode) & 007777);
+       printf(" Uid: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, st.st_gid, group);
+       printf("  Access: %s", ctime(&(st.st_atime)));
+       printf("  Modify: %s", ctime(&(st.st_mtime)));
+       printf("  Change: %s", ctime(&(st.st_ctime)));
+       if (user != null_string) SAFE_FREE(user);
+       if (group!= null_string) SAFE_FREE(group);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char *user;
+       char *group;
+       struct passwd *pwd;
+       struct group *grp;
+       SMB_STRUCT_STAT st;
+
+       if (argc != 2) {
+               printf("Usage: lstat <path>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.lstat(vfs->conn, argv[1], &st) == -1) {
+               printf("lstat: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       pwd = sys_getpwuid(st.st_uid);
+       if (pwd != NULL) user = strdup(pwd->pw_name);
+       else user = null_string;
+       grp = sys_getgrgid(st.st_gid);
+       if (grp != NULL) group = strdup(grp->gr_name);
+       else group = null_string;
+
+       printf("lstat: ok\n");
+       if (S_ISREG(st.st_mode)) printf("  Regular File\n");
+       else if (S_ISDIR(st.st_mode)) printf("  Directory\n");
+       else if (S_ISCHR(st.st_mode)) printf("  Character Device\n");
+       else if (S_ISBLK(st.st_mode)) printf("  Block Device\n");
+       else if (S_ISFIFO(st.st_mode)) printf("  Fifo\n");
+       else if (S_ISLNK(st.st_mode)) printf("  Symbolic Link\n");
+       else if (S_ISSOCK(st.st_mode)) printf("  Socket\n");
+       printf("  Size: %10u", (unsigned int)st.st_size);
+       printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+       printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+       printf("  Device: 0x%10x", (unsigned int)st.st_dev);
+       printf(" Inode: %10u", (unsigned int)st.st_ino);
+       printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+       printf("  Access: %05o", (st.st_mode) & 007777);
+       printf(" Uid: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, st.st_gid, group);
+       printf("  Access: %s", ctime(&(st.st_atime)));
+       printf("  Modify: %s", ctime(&(st.st_mtime)));
+       printf("  Change: %s", ctime(&(st.st_ctime)));
+       if (user != null_string) SAFE_FREE(user);
+       if (group!= null_string) SAFE_FREE(group);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       mode_t mode;
+       if (argc != 3) {
+               printf("Usage: chmod <path> <mode>\n");
+               return NT_STATUS_OK;
+       }
+
+       mode = atoi(argv[2]);
+       if (vfs->conn->vfs_ops.chmod(vfs->conn, argv[1], mode) == -1) {
+               printf("chmod: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("chmod: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd;
+       mode_t mode;
+       if (argc != 3) {
+               printf("Usage: fchmod <fd> <mode>\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       mode = atoi(argv[2]);
+       if (fd < 0 || fd > 1024) {
+               printf("fchmod: error=%d (file descriptor out of range)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+       if (vfs->files[fd] == NULL) {
+               printf("fchmod: error=%d (invalid file descriptor)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.fchmod(vfs->files[fd], fd, mode) == -1) {
+               printf("fchmod: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("fchmod: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       uid_t uid;
+       gid_t gid;
+       if (argc != 4) {
+               printf("Usage: chown <path> <uid> <gid>\n");
+               return NT_STATUS_OK;
+       }
+
+       uid = atoi(argv[2]);
+       gid = atoi(argv[3]);
+       if (vfs->conn->vfs_ops.chown(vfs->conn, argv[1], uid, gid) == -1) {
+               printf("chown: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("chown: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       uid_t uid;
+       gid_t gid;
+       int fd;
+       if (argc != 4) {
+               printf("Usage: fchown <fd> <uid> <gid>\n");
+               return NT_STATUS_OK;
+       }
+
+       uid = atoi(argv[2]);
+       gid = atoi(argv[3]);
+       fd = atoi(argv[1]);
+       if (fd < 0 || fd > 1024) {
+               printf("fchown: faliure=%d (file descriptor out of range)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+       if (vfs->files[fd] == NULL) {
+               printf("fchown: error=%d (invalid file descriptor)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+       if (vfs->conn->vfs_ops.fchown(vfs->files[fd], fd, uid, gid) == -1) {
+               printf("fchown error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("fchown: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_getwd(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char buf[PATH_MAX];
+       if (vfs->conn->vfs_ops.getwd(vfs->conn, buf) == NULL) {
+               printf("getwd: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("getwd: %s\n", buf);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_utime(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       struct utimbuf times;
+       if (argc != 4) {
+               printf("Usage: utime <path> <access> <modify>\n");
+               return NT_STATUS_OK;
+       }
+       times.actime = atoi(argv[2]);
+       times.modtime = atoi(argv[3]);
+       if (vfs->conn->vfs_ops.utime(vfs->conn, argv[1], &times) != 0) {
+               printf("utime: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("utime: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_ftruncate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       int fd;
+       SMB_OFF_T off;
+       if (argc != 3) {
+               printf("Usage: ftruncate <fd> <length>\n");
+               return NT_STATUS_OK;
+       }
+
+       fd = atoi(argv[1]);
+       off = atoi(argv[2]);
+       if (fd < 0 || fd > 1024) {
+               printf("ftruncate: error=%d (file descriptor out of range)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+       if (vfs->files[fd] == NULL) {
+               printf("ftruncate: error=%d (invalid file descriptor)\n", EBADF);
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.ftruncate(vfs->files[fd], fd, off) == -1) {
+               printf("ftruncate: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("ftruncate: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lock(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       BOOL ret;
+       int fd;
+       int op;
+       long offset;
+       long count;
+       int type;
+       char *typestr;
+       
+       if (argc != 6) {
+               printf("Usage: lock <fd> <op> <offset> <count> <type>\n");
+                printf("  ops: G = F_GETLK\n");
+                printf("       S = F_SETLK\n");
+                printf("       W = F_SETLKW\n");
+                printf("  type: R = F_RDLCK\n");
+                printf("        W = F_WRLCK\n");
+                printf("        U = F_UNLCK\n");
+               return NT_STATUS_OK;
+       }
+
+       if (sscanf(argv[1], "%d", &fd) == 0) {
+               printf("lock: error=-1 (error parsing fd)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       op = 0;
+       switch (*argv[2]) {
+       case 'G':
+               op = F_GETLK;
+               break;
+       case 'S':
+               op = F_SETLK;
+               break;
+       case 'W':
+               op = F_SETLKW;
+               break;
+       default:
+               printf("lock: error=-1 (invalid op flag!)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (sscanf(argv[3], "%ld", &offset) == 0) {
+               printf("lock: error=-1 (error parsing fd)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (sscanf(argv[4], "%ld", &count) == 0) {
+               printf("lock: error=-1 (error parsing fd)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       type = 0;
+       typestr = argv[5];
+       while(*typestr) {
+               switch (*typestr) {
+               case 'R':
+                       type |= F_RDLCK;
+                       break;
+               case 'W':
+                       type |= F_WRLCK;
+                       break;
+               case 'U':
+                       type |= F_UNLCK;
+                       break;
+               default:
+                       printf("lock: error=-1 (invalid type flag!)\n");
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+               typestr++;
+       }
+
+       printf("lock: debug lock(fd=%d, op=%d, offset=%ld, count=%ld, type=%d))\n", fd, op, offset, count, type);
+
+       if ((ret = vfs->conn->vfs_ops.lock(vfs->files[fd], fd, op, offset, count, type)) == False) {
+               printf("lock: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("lock: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_symlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc != 3) {
+               printf("Usage: symlink <path> <link>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.symlink(vfs->conn, argv[1], argv[2]) == -1) {
+               printf("symlink: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("symlink: ok\n");
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char buffer[PATH_MAX];
+       int size;
+
+       if (argc != 2) {
+               printf("Usage: readlink <path>\n");
+               return NT_STATUS_OK;
+       }
+
+       if ((size = vfs->conn->vfs_ops.readlink(vfs->conn, argv[1], buffer, PATH_MAX)) == -1) {
+               printf("readlink: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       buffer[size] = '\0';
+       printf("readlink: %s\n", buffer);
+       return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_link(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc != 3) {
+               printf("Usage: link <path> <link>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.link(vfs->conn, argv[1], argv[2]) == -1) {
+               printf("link: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("link: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_mknod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       mode_t mode;
+       unsigned int dev_val;
+       SMB_DEV_T dev;
+       
+       if (argc != 4) {
+               printf("Usage: mknod <path> <mode> <dev>\n");
+               printf("  mode is octal\n");
+               printf("  dev is hex\n");
+               return NT_STATUS_OK;
+       }
+
+       if (sscanf(argv[2], "%o", &mode) == 0) {
+               printf("open: error=-1 (invalid mode!)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (sscanf(argv[3], "%x", &dev_val) == 0) {
+               printf("open: error=-1 (invalid dev!)\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       dev = (SMB_DEV_T)dev_val;
+
+       if (vfs->conn->vfs_ops.mknod(vfs->conn, argv[1], mode, dev) == -1) {
+               printf("mknod: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("mknod: ok\n");
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_realpath(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       char respath[PATH_MAX];
+       
+       if (argc != 2) {
+               printf("Usage: realpath <path>\n");
+               return NT_STATUS_OK;
+       }
+
+       if (vfs->conn->vfs_ops.realpath(vfs->conn, argv[1], respath) == NULL) {
+               printf("realpath: error=%d (%s)\n", errno, strerror(errno));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       printf("realpath: ok\n");
+       return NT_STATUS_OK;
+}
+
+struct cmd_set vfs_commands[] = {
+
+       { "VFS Commands" },
+
+       { "load", cmd_load_module, "Load a module", "load <module.so>" },
+       { "populate", cmd_populate, "Populate a data buffer", "populate <char> <size>" },
+       { "showdata", cmd_show_data, "Show data currently in data buffer", "show_data [<offset> <len>]"},
+       { "connect",   cmd_connect,   "VFS connect()",    "connect" },
+       { "disconnect",   cmd_disconnect,   "VFS disconnect()",    "disconnect" },
+       { "disk_free",   cmd_disk_free,   "VFS disk_free()",    "disk_free <path>" },
+       { "opendir",   cmd_opendir,   "VFS opendir()",    "opendir <fname>" },
+       { "readdir",   cmd_readdir,   "VFS readdir()",    "readdir" },
+       { "mkdir",   cmd_mkdir,   "VFS mkdir()",    "mkdir <path>" },
+       { "rmdir",   cmd_pathfunc,   "VFS rmdir()",    "rmdir <path>" },
+       { "closedir",   cmd_closedir,   "VFS closedir()",    "closedir" },
+       { "open",   cmd_open,   "VFS open()",    "open <fname>" },
+       { "close",   cmd_close,   "VFS close()",    "close <fd>" },
+       { "read",   cmd_read,   "VFS read()",    "read <fd> <size>" },
+       { "write",   cmd_write,   "VFS write()",    "write <fd> <size>" },
+       { "lseek",   cmd_lseek,   "VFS lseek()",    "lseek <fd> <offset> <whence>" },
+       { "rename",   cmd_rename,   "VFS rename()",    "rename <old> <new>" },
+       { "fsync",   cmd_fsync,   "VFS fsync()",    "fsync <fd>" },
+       { "stat",   cmd_stat,   "VFS stat()",    "stat <fname>" },
+       { "fstat",   cmd_fstat,   "VFS fstat()",    "fstat <fd>" },
+       { "lstat",   cmd_lstat,   "VFS lstat()",    "lstat <fname>" },
+       { "unlink",   cmd_pathfunc,   "VFS unlink()",    "unlink <fname>" },
+       { "chmod",   cmd_chmod,   "VFS chmod()",    "chmod <path> <mode>" },
+       { "fchmod",   cmd_fchmod,   "VFS fchmod()",    "fchmod <fd> <mode>" },
+       { "chown",   cmd_chown,   "VFS chown()",    "chown <path> <uid> <gid>" },
+       { "fchown",   cmd_fchown,   "VFS fchown()",    "fchown <fd> <uid> <gid>" },
+       { "chdir",   cmd_pathfunc,   "VFS chdir()",    "chdir <path>" },
+       { "getwd",   cmd_getwd,   "VFS getwd()",    "getwd" },
+       { "utime",   cmd_utime,   "VFS utime()",    "utime <path> <access> <modify>" },
+       { "ftruncate",   cmd_ftruncate,   "VFS ftruncate()",    "ftruncate <fd> <length>" },
+       { "lock",   cmd_lock,   "VFS lock()",    "lock <f> <op> <offset> <count> <type>" },
+       { "symlink",   cmd_symlink,   "VFS symlink()",    "symlink <old> <new>" },
+       { "readlink",   cmd_readlink,   "VFS readlink()",    "readlink <path>" },
+       { "link",   cmd_link,   "VFS link()",    "link <oldpath> <newpath>" },
+       { "mknod",   cmd_mknod,   "VFS mknod()",    "mknod <path> <mode> <dev>" },
+       { "realpath",   cmd_realpath,   "VFS realpath()",    "realpath <path>" },
+       { NULL }
+};
diff --git a/source4/torture/denytest.c b/source4/torture/denytest.c
new file mode 100644 (file)
index 0000000..ea4b7e5
--- /dev/null
@@ -0,0 +1,1578 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester - deny mode scanning functions
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+extern BOOL torture_showall;
+
+enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5};
+
+static const char *denystr(int denymode)
+{
+       struct {
+               int v;
+               const char *name; 
+       } deny_modes[] = {
+               {DENY_DOS, "DENY_DOS"},
+               {DENY_ALL, "DENY_ALL"},
+               {DENY_WRITE, "DENY_WRITE"},
+               {DENY_READ, "DENY_READ"},
+               {DENY_NONE, "DENY_NONE"},
+               {DENY_FCB, "DENY_FCB"},
+               {-1, NULL}};
+       int i;
+       for (i=0;deny_modes[i].name;i++) {
+               if (deny_modes[i].v == denymode) return deny_modes[i].name;
+       }
+       return "DENY_XXX";
+}
+
+static const char *openstr(int mode)
+{
+       struct {
+               int v;
+               const char *name; 
+       } open_modes[] = {
+               {O_RDWR, "O_RDWR"},
+               {O_RDONLY, "O_RDONLY"},
+               {O_WRONLY, "O_WRONLY"},
+               {-1, NULL}};
+       int i;
+       for (i=0;open_modes[i].name;i++) {
+               if (open_modes[i].v == mode) return open_modes[i].name;
+       }
+       return "O_XXX";
+}
+
+static const char *resultstr(enum deny_result res)
+{
+       struct {
+               enum deny_result res;
+               const char *name; 
+       } results[] = {
+               {A_X, "X"},
+               {A_0, "-"},
+               {A_R, "R"},
+               {A_W, "W"},
+               {A_RW,"RW"}};
+       int i;
+       for (i=0;ARRAY_SIZE(results);i++) {
+               if (results[i].res == res) return results[i].name;
+       }
+       return "*";
+}
+
+static struct {
+       int isexe;
+       int mode1, deny1;
+       int mode2, deny2;
+       enum deny_result result;
+} denytable2[] = {
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_R},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_W},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_W},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_RW},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_W},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_RW},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_R},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_W},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_0}
+};
+
+
+static struct {
+       int isexe;
+       int mode1, deny1;
+       int mode2, deny2;
+       enum deny_result result;
+} denytable1[] = {
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{1, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{1, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{1,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{1,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{1, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{1, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{1, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{1, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{1, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_RW},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_DOS,     A_RW},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_DOS,     A_W},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_DOS,      O_RDWR,   DENY_FCB,     A_RW},
+{0, O_WRONLY,   DENY_DOS,    O_RDONLY,   DENY_FCB,     A_RW},
+{0, O_WRONLY,   DENY_DOS,    O_WRONLY,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_ALL,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,   DENY_ALL,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_READ,     A_R},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY, DENY_WRITE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY, DENY_WRITE,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0,   O_RDWR,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_W},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_RDONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_READ,     A_W},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_WRONLY,  DENY_READ,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_READ,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0,   O_RDWR,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_RW},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_W},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_RDONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_RDONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_DOS,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_READ,     A_RW},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_READ,     A_R},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_READ,     A_W},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,  DENY_NONE,     A_RW},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,  DENY_NONE,     A_R},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,  DENY_NONE,     A_W},
+{0, O_WRONLY,  DENY_NONE,      O_RDWR,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_RDONLY,   DENY_FCB,     A_0},
+{0, O_WRONLY,  DENY_NONE,    O_WRONLY,   DENY_FCB,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0,   O_RDWR,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{0,   O_RDWR,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_RDONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{0, O_RDONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{0, O_RDONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_DOS,     A_RW},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_DOS,     A_R},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_DOS,     A_W},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_ALL,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY, DENY_WRITE,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_READ,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,  DENY_NONE,     A_0},
+{0, O_WRONLY,   DENY_FCB,      O_RDWR,   DENY_FCB,     A_RW},
+{0, O_WRONLY,   DENY_FCB,    O_RDONLY,   DENY_FCB,     A_RW},
+{0, O_WRONLY,   DENY_FCB,    O_WRONLY,   DENY_FCB,     A_RW}
+};
+
+
+static void progress_bar(unsigned i, unsigned total)
+{
+       if (i % 10 != 0) return;
+       printf("%5d/%5d\r", i, total);
+       fflush(stdout);
+}
+
+/*
+  this produces a matrix of deny mode behaviour for 1 connection
+ */
+BOOL torture_denytest1(int dummy)
+{
+       static struct cli_state *cli1;
+       int fnum1, fnum2;
+       int i;
+       BOOL correct = True;
+       const char *fnames[2] = {"\\denytest1.dat", "\\denytest1.exe"};
+
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+
+       printf("starting denytest1\n");
+
+       printf("Testing deny modes with 1 connection\n");
+
+       for (i=0;i<2;i++) {
+               cli_unlink(cli1, fnames[i]);
+               fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+               cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+               cli_close(cli1, fnum1);
+       }
+
+       printf("testing %d entries\n", ARRAY_SIZE(denytable1));
+
+       for (i=0; i<ARRAY_SIZE(denytable1); i++) {
+               enum deny_result res;
+               const char *fname = fnames[denytable1[i].isexe];
+
+               progress_bar(i, ARRAY_SIZE(denytable1));
+
+               fnum1 = cli_open(cli1, fname, 
+                                denytable1[i].mode1,
+                                denytable1[i].deny1);
+               fnum2 = cli_open(cli1, fname, 
+                                denytable1[i].mode2,
+                                denytable1[i].deny2);
+
+               if (fnum1 == -1) {
+                       res = A_X;
+               } else if (fnum2 == -1) {
+                       res = A_0;
+               } else {
+                       char x = 1;
+                       res = A_0;
+                       if (cli_read(cli1, fnum2, (void *)&x, 0, 1) == 1) {
+                               res += A_R;
+                       }
+                       if (cli_write(cli1, fnum2, 0, (void *)&x, 0, 1) == 1) {
+                               res += A_W;
+                       }
+               }
+
+               if (res != denytable1[i].result) {
+                       correct = False;
+               }
+
+               if (torture_showall || res != denytable1[i].result) {
+                       printf("%s %8s %10s    %8s %10s    %s (correct=%s)\n",
+                              fname,
+                              denystr(denytable1[i].deny1),
+                              openstr(denytable1[i].mode1),
+                              denystr(denytable1[i].deny2),
+                              openstr(denytable1[i].mode2),
+                              resultstr(res),
+                              resultstr(denytable1[i].result));
+               }
+
+               cli_close(cli1, fnum1);
+               cli_close(cli1, fnum2);
+       }
+
+       for (i=0;i<2;i++) {
+               cli_unlink(cli1, fnames[i]);
+       }
+               
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       
+       printf("finshed denytest1\n");
+       return correct;
+}
+
+
+/*
+  this produces a matrix of deny mode behaviour with 2 connections
+ */
+BOOL torture_denytest2(int dummy)
+{
+       static struct cli_state *cli1, *cli2;
+       int fnum1, fnum2;
+       int i;
+       BOOL correct = True;
+       const char *fnames[2] = {"\\denytest2.dat", "\\denytest2.exe"};
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting denytest2\n");
+
+       printf("Testing deny modes with 2 connections\n");
+
+       for (i=0;i<2;i++) {
+               cli_unlink(cli1, fnames[i]);
+               fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+               cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+               cli_close(cli1, fnum1);
+       }
+
+       for (i=0; i<ARRAY_SIZE(denytable2); i++) {
+               enum deny_result res;
+               const char *fname = fnames[denytable2[i].isexe];
+
+               progress_bar(i, ARRAY_SIZE(denytable1));
+
+               fnum1 = cli_open(cli1, fname, 
+                                denytable2[i].mode1,
+                                denytable2[i].deny1);
+               fnum2 = cli_open(cli2, fname, 
+                                denytable2[i].mode2,
+                                denytable2[i].deny2);
+
+               if (fnum1 == -1) {
+                       res = A_X;
+               } else if (fnum2 == -1) {
+                       res = A_0;
+               } else {
+                       char x = 1;
+                       res = A_0;
+                       if (cli_read(cli2, fnum2, (void *)&x, 0, 1) == 1) {
+                               res += A_R;
+                       }
+                       if (cli_write(cli2, fnum2, 0, (void *)&x, 0, 1) == 1) {
+                               res += A_W;
+                       }
+               }
+
+               if (res != denytable2[i].result) {
+                       correct = False;
+               }
+
+               if (torture_showall || res != denytable2[i].result) {
+                       printf("%s %8s %10s    %8s %10s    %s (correct=%s)\n",
+                              fname,
+                              denystr(denytable2[i].deny1),
+                              openstr(denytable2[i].mode1),
+                              denystr(denytable2[i].deny2),
+                              openstr(denytable2[i].mode2),
+                              resultstr(res),
+                              resultstr(denytable2[i].result));
+               }
+
+               cli_close(cli1, fnum1);
+               cli_close(cli2, fnum2);
+       }
+               
+       for (i=0;i<2;i++) {
+               cli_unlink(cli1, fnames[i]);
+       }
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       if (!torture_close_connection(cli2)) {
+               correct = False;
+       }
+       
+       printf("finshed denytest2\n");
+       return correct;
+}
+
diff --git a/source4/torture/dfstest.c b/source4/torture/dfstest.c
new file mode 100644 (file)
index 0000000..79d49ad
--- /dev/null
@@ -0,0 +1,459 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester - DFS tests
+   Copyright (C) James J Myers 2003  <myersjj@samba.org>
+   
+   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"
+
+#define DFS_SERVER_COUNT 6
+#define DFS_FILE_COUNT 8
+extern char *host, *share, *password, *username;
+static struct cli_client context;
+static const char *sockops="TCP_NODELAY";
+
+/*
+ checks for correct DFS cluster support
+ */
+BOOL torture_dfs_basic(int dummy)
+{
+       int current_server = 0;
+       char *fname[DFS_FILE_COUNT];
+       int file_server[DFS_FILE_COUNT];
+       int fnum[DFS_FILE_COUNT];
+       int i;
+       const char *template = "\\\\%s\\%s\\dfstest%d.tmp";
+       char *filedata;
+       int server_count = 0;
+       int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS
+                               | CLI_FULL_CONNECTION_USE_DFS
+                               ;
+       
+       printf("starting dfs_basic_test\n");
+       cli_client_initialize(&context, sockops, username, password, lp_workgroup(), connection_flags);
+
+       if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags) < 0))
+               return False;
+
+       for (i=0; i < DFS_FILE_COUNT ; i++) {
+               file_server[i] = 0;
+               DEBUG(4,("host=%s share=%s cli host=%s cli share=%s\n",
+                       host, share, cli_state_get_host(context.cli[file_server[i]]),
+                       cli_state_get_share(context.cli[file_server[i]])));
+               host = cli_state_get_host(context.cli[file_server[i]]);
+               share = cli_state_get_share(context.cli[file_server[i]]);
+               asprintf(&fname[i], template, host, share, i);
+               DEBUG(3,("unlinking %s\n", fname[i]));
+               cli_nt_unlink(&context, &file_server[i], fname[i], 0);
+       }
+       
+       for (i=0; i < DFS_FILE_COUNT ; i++) {
+               host = cli_state_get_host(context.cli[file_server[i]]);
+               share = cli_state_get_share(context.cli[file_server[i]]);
+               asprintf(&fname[i], template, host, share, i);
+               DEBUG(3,("open %s on server %s(%d)\n",
+                       fname[i], host, file_server[i]));
+               fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+               if (fnum[i] == -1)
+               {
+                       printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+               asprintf(&filedata, "%s %d", fname[i], fnum[i]);
+               DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+                       strlen(filedata), filedata, fname[i], fnum[i],
+                       host, file_server[i]));
+               if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata))
+               {
+                       printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+
+               if (!cli_close(context.cli[file_server[i]], fnum[i])) {
+                       printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+       }
+       DEBUG(3,("used Dfs servers:"));
+       for (i=0; i < DFS_SERVER_COUNT ; i++) {
+               server_count++;
+               DEBUG(3,(" %s(%d)",     cli_state_get_host(context.cli[file_server[i]]), i));
+               if (!torture_close_connection(context.cli[i]))
+                       return False;
+       }
+       DEBUG(3,("\n"));
+
+       printf("Passed dfstest, found and used %d Dfs servers\n", server_count);
+       return True;
+}
+
+/*
+ Check for correct DFS rename support.
+ First test is simple rename, a la command line, explorer.
+ Second test is simulation of MS Word edit/save file.
+ */
+BOOL torture_dfs_rename(int dummy)
+{
+       int current_server = -1;
+       char *fname[DFS_FILE_COUNT];
+       int file_server[DFS_FILE_COUNT];
+       int fnum[DFS_FILE_COUNT];
+       int i;
+       const char *template = "\\\\%s\\%s\\dfstest%d.tmp";
+       const char *template2orig = "\\\\%s\\%s\\dfstestorig.txt";
+       const char *template2old = "\\\\%s\\%s\\~dfstestold.txt";
+       const char *template2new = "\\\\%s\\%s\\~dfstestnew.txt";
+       char *filedata, *newdata;
+       int server_count = 0;
+       int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS
+                               | CLI_FULL_CONNECTION_USE_DFS
+                               ;
+
+       printf("starting dfs_rename_test\n");
+       cli_client_initialize(&context, sockops, username, password,
+                             lp_workgroup(), connection_flags);
+       
+       if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0)
+               return False;
+       
+       for (i=0; i < DFS_FILE_COUNT ; i++) {
+               file_server[i] = 0;
+               slprintf(fname[i],sizeof(fstring)-1,template, host, share, i);
+               DEBUG(3,("unlinking %s\n", fname[i]));
+               cli_nt_unlink(&context, &file_server[i], fname[i], 0);
+       }
+       /* Simple rename test */
+       for (i=0; i < 1 ; i++) {
+               slprintf(fname[i],sizeof(fstring)-1,template,
+                       cli_state_get_host(context.cli[file_server[i]]),
+                       cli_state_get_share(context.cli[file_server[i]]), i);
+               DEBUG(3,("open %s on server %s(%d)\n",
+                       fname[i], cli_state_get_host(context.cli[file_server[i]]), file_server[i]));
+                       
+               fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+               if (fnum[i] == -1) {
+                       printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+               asprintf(&filedata, "%s %d", fname[i], (int)getpid());
+               DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+                       strlen(filedata), filedata, fname[i], fnum[i],
+                       cli_state_get_host(context.cli[file_server[i]]), file_server[i]));
+               if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata))
+               {
+                       printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+
+               if (!cli_close(context.cli[file_server[i]], fnum[i])) {
+                       printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+       }
+       // now attempt to rename the file
+       DEBUG(3,("rename %s to %s on server %s(%d)\n",
+                       fname[0], fname[1], cli_state_get_host(context.cli[file_server[i]]), file_server[0]));
+       if (!cli_dfs_rename(&context, &file_server[0], fname[0], fname[1])) {
+               printf("rename of %s to %s failed (%s)\n", fname[0], fname[1], cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       // clean up
+       DEBUG(3,("used Dfs servers:"));
+       for (i=0; i < DFS_SERVER_COUNT ; i++) {
+               server_count++;
+               DEBUG(3,(" %s(%d)",     cli_state_get_host(context.cli[file_server[i]]), i));
+               if (!torture_close_connection(context.cli[i]))
+                       return False;
+       }
+       DEBUG(3,("\n"));
+       printf("Dfstest: passed simple rename test\n");
+       
+       /* Now try more complicated test, a la MS Word.
+        * Open existing file (x) and read file and close.
+        * Then open, write to new temp name file (~x.new), close.
+        * Then rename old file name to old temp name file (~x.old).
+        * Then rename new temp name file to oroginal name (x). */
+       cli_client_initialize(&context, sockops, username, password,
+                             lp_workgroup(), connection_flags);
+       
+       if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0)
+               return False;    
+       slprintf(fname[0],sizeof(fname[0])-1,template2orig, host, share);
+       slprintf(fname[1],sizeof(fname[1])-1,template2old, host, share);
+       slprintf(fname[2],sizeof(fname[2])-1,template2new, host, share);
+       for (i=0; i < DFS_FILE_COUNT ; i++) {
+               file_server[i] = 0;
+               fnum[i] = 0;
+               DEBUG(3,("unlinking %s\n", fname[i]));
+               cli_nt_unlink(&context, &file_server[i], fname[i], 0);
+       }
+       asprintf(&fname[0],template2orig,
+                       cli_state_get_host(context.cli[0]),
+                       cli_state_get_share(context.cli[0]), 0);
+       asprintf(&fname[1],template2old,
+                       cli_state_get_host(context.cli[1]),
+                       cli_state_get_share(context.cli[1]), 1);
+       asprintf(&fname[2],template2new,
+                       cli_state_get_host(context.cli[2]),
+                       cli_state_get_share(context.cli[2]), 2);
+       DEBUG(3,("edit(MS Word) %s on server %s(%d)\n",
+                       fname[0], cli_state_get_host(context.cli[0]), file_server[0]));
+       DEBUG(3,("open %s on server %s(%d)\n",
+               fname[0], cli_state_get_host(context.cli[0]), file_server[0]));
+                       
+       fnum[0] = cli_dfs_open(&context, &file_server[0], fname[0], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum[0] == -1)
+       {
+               printf("open of %s failed (%s)\n", fname[0], cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       slprintf(filedata, sizeof(fstring)-1, "%s %d", fname[0], (int)getpid());
+       DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+               strlen(filedata), filedata, fname[0], fnum[0],
+               cli_state_get_host(context.cli[0]), file_server[0]));
+       if (cli_write(context.cli[file_server[0]], fnum[0], 0, filedata, 0, strlen(filedata)) != strlen(filedata))
+       {
+               printf("write failed (%s)\n", cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       // read data from original file
+       DEBUG(3,("read %s (fid %d) on server %s(%d)\n",
+               fname[0], fnum[0], cli_state_get_host(context.cli[0]), file_server[0]));
+       if (cli_read(context.cli[file_server[0]], fnum[0], filedata, 0, strlen(filedata)) != strlen(filedata))
+       {
+               printf("read failed (%s)", cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       DEBUG(3,("close %s on server %s(%d)\n",
+               fname[0], cli_state_get_host(context.cli[0]), file_server[0]));
+       if (!cli_close(context.cli[file_server[0]], fnum[0])) {
+               printf("close of %s failed (%s)\n", fname[0], cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       // open new temp file, write data
+       DEBUG(3,("open %s on server %s(%d)\n",
+               fname[2], cli_state_get_host(context.cli[2]), file_server[2]));
+       fnum[2] = cli_dfs_open(&context, &file_server[2], fname[2], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum[2] == -1)
+       {
+               printf("open of %s failed (%s)\n", fname[2], cli_errstr(context.cli[file_server[2]]));
+               return False;
+       }
+       DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+               strlen(filedata), filedata, fname[2], fnum[2],
+               cli_state_get_host(context.cli[2]), file_server[2]));
+       if (cli_write(context.cli[file_server[2]], fnum[2], 0, filedata, 0, strlen(filedata)) != strlen(filedata))
+       {
+               printf("write failed (%s)\n", cli_errstr(context.cli[file_server[2]]));
+               return False;
+       }
+       slprintf(newdata, sizeof(fstring)-1, "new data: %s %d", fname[0], (int)getpid());
+       DEBUG(3,("write new data %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+               strlen(newdata), newdata, fname[2], fnum[2],
+               cli_state_get_host(context.cli[2]), file_server[2]));
+       if (cli_write(context.cli[file_server[2]], fnum[2], 0, newdata, strlen(filedata), strlen(newdata)) != strlen(newdata))
+       {
+               printf("write failed (%s)\n", cli_errstr(context.cli[file_server[2]]));
+               return False;
+       }
+       DEBUG(3,("close %s on server %s(%d)\n",
+               fname[2], cli_state_get_host(context.cli[2]), file_server[2]));
+       if (!cli_close(context.cli[file_server[2]], fnum[2])) {
+               printf("close of %s failed (%s)\n", fname[2], cli_errstr(context.cli[file_server[2]]));
+               return False;
+       }
+       DEBUG(3,("close successful %s on server %s(%d)\n",
+               fname[2], cli_state_get_host(context.cli[2]), file_server[2]));
+       // rename original file to temp 
+       DEBUG(4,("file_server[0]=%d\n", file_server[0]));
+       DEBUG(4,("context.cli[file_server[0]].desthost=%s\n", cli_state_get_host(context.cli[0])));
+       DEBUG(3,("rename %s to %s on server %s(%d)\n",
+                       fname[0], fname[1], cli_state_get_host(context.cli[0]), file_server[0]));
+       if (!cli_dfs_rename(&context, &file_server[0], fname[0], fname[1])) {
+               printf("rename of %s to %s failed (%s)\n", fname[0], fname[1], cli_errstr(context.cli[file_server[0]]));
+               return False;
+       }
+       // name new temp file to original
+       DEBUG(3,("rename %s to %s on server %s(%d)\n",
+                       fname[2], fname[0], cli_state_get_host(context.cli[2]), file_server[2]));
+       if (!cli_dfs_rename(&context, &file_server[2], fname[2], fname[0])) {
+               printf("rename of %s to %s failed (%s)\n", fname[2], fname[0], cli_errstr(context.cli[file_server[2]]));
+               return False;
+       }
+       printf("Dfstest: passed MS Word rename test\n");
+       // clean up
+       DEBUG(3,("used Dfs servers:"));
+       for (i=0; i < DFS_SERVER_COUNT ; i++) {
+               server_count++;
+               DEBUG(3,(" %s(%d)",     cli_state_get_host(context.cli[i]), i));
+               if (!torture_close_connection(context.cli[i]))
+                       return False;
+       }
+       DEBUG(3,("\n"));
+
+       printf("Passed dfs_rename_test\n");
+       return True;
+}
+struct list_fn_parms {
+       struct cli_client *context;
+       char* rname;
+} list_fn_parms;
+
+void dfs_list_fn(file_info *finfo, const char *rname, void* parmsp);
+void delete_file(file_info *finfo, const char *rname)
+{
+       int server = 0;
+       char *fname;
+       
+       DEBUG(3,("deleting file %s in %s\n", finfo->name, rname));
+       asprintf(&fname, "%s\\%s", rname, finfo->name);
+       cli_nt_unlink(&context, &server, fname, 0);
+}
+void delete_directory(file_info *finfo, const char *rname)
+{
+       int server = 0;
+       char *dname, *rname2;
+       
+       DEBUG(3,("deleting directory %s in %s\n", finfo->name, rname));
+       asprintf(&dname, "%s%s\\*", rname, finfo->name);
+       cli_nt_unlink(&context, &server, dname, 0);
+       asprintf(&dname, "%s%s\\*", rname, finfo->name);
+       asprintf(&rname2, "%s%s", rname, finfo->name);                  
+       cli_search(context.cli[0], dname, FILE_ATTRIBUTE_DIRECTORY,
+               dfs_list_fn, (void*)rname2);
+       cli_dfs_rmdir(&context, &server, rname2);
+}
+
+void dfs_list_fn(file_info *finfo, const char *name, void* parmsp)
+{
+       struct list_fn_parms *parms = (struct list_fn_parms*)parmsp;
+       
+       DEBUG(4,("processing %s in %s\n", finfo->name, parms->rname));
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
+               delete_directory(finfo, parms->rname);
+       }
+       else {
+               delete_file(finfo, parms->rname);
+       }
+}
+
+/*
+ checks for correct DFS cluster support creating random dirs/files.
+ */
+#define DFS_RANDOM_FILE_COUNT 10
+#define DFS_RANDOM_DIR_COUNT 3
+#define DFS_RANDOM_DIR_LEVELS 2  
+BOOL torture_dfs_random(int dummy)
+{
+       char *fname[DFS_RANDOM_FILE_COUNT];
+       int file_server[DFS_RANDOM_FILE_COUNT];
+       char *dname[DFS_RANDOM_DIR_COUNT];
+       int dir_server[DFS_RANDOM_DIR_COUNT];
+       char *rname;
+       int fnum[DFS_FILE_COUNT];
+       int i;
+       const char *ftemplate = "%s\\dfsfile%d.tmp";
+       const char *alltemplate = "\\\\%s\\%s\\dfs*.tmp";
+       char *filedata;
+       int server_count = 0;
+       int file_count;
+       int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS
+                               | CLI_FULL_CONNECTION_USE_DFS
+                               ;
+       
+       printf("starting dfs_random_test\n");
+       cli_client_initialize(&context, sockops, username, password,
+                             lp_workgroup(), connection_flags);
+
+       if ((dir_server[0] = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0)
+               return False;
+
+       // get list of directories named dfsdir*.
+       // delete all files in these directories using wild card,
+       // then delete directory.
+       asprintf(&rname, "\\\\%s\\%s\\",
+                       cli_state_get_host(context.cli[0]),
+                       cli_state_get_share(context.cli[0]));
+       asprintf(&fname[0], alltemplate,
+                       cli_state_get_host(context.cli[0]),
+                       cli_state_get_share(context.cli[0]));
+       DEBUG(3,("deleting files %s in %s on server %s(%d)\n",
+               fname[0], rname, cli_state_get_host(context.cli[0]), dir_server[0]));
+       file_count = cli_search(context.cli[0], fname[0], FILE_ATTRIBUTE_DIRECTORY, dfs_list_fn, (void*)rname);
+
+       // create random directory names with 0-n levels
+       asprintf(&dname[0], "\\\\%s\\%s\\",
+                       cli_state_get_host(context.cli[0]),
+                       cli_state_get_share(context.cli[0]));
+       DEBUG(3,("creating directories in %s on server %s(%d)\n",
+               rname, cli_state_get_host(context.cli[0]), dir_server[0]));
+       for (i=1; i < DFS_RANDOM_DIR_COUNT; i++) {
+               dir_server[i] = 0;
+               asprintf(&dname[i],
+                       "\\\\%s\\%s\\dfsdir%d.tmp",
+                       cli_state_get_host(context.cli[dir_server[i]]),
+                       cli_state_get_share(context.cli[dir_server[i]]),
+                       (int)sys_random()%10000);
+               DEBUG(3,("mkdir %s on server %s(%d)\n",
+                       dname[i], cli_state_get_host(context.cli[dir_server[i]]), dir_server[i]));
+               if (!cli_dfs_mkdir(&context, &dir_server[i], dname[i])) {
+                       printf("mkdir of %s failed (%s)\n", dname[i], cli_errstr(context.cli[dir_server[i]]));
+                       return False;
+               }
+       }
+
+       for (i=0; i < DFS_RANDOM_FILE_COUNT ; i++) {
+               // select a directory randomly, create a file in it.
+               int dn = (int)sys_random()%DFS_RANDOM_DIR_COUNT;
+               file_server[i] = dir_server[dn];
+               asprintf(&fname[i], ftemplate, dname[dn], i);
+               DEBUG(3,("open %s on server %s(%d)\n",
+                       fname[i], cli_state_get_host(context.cli[dir_server[i]]), file_server[i]));
+               fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+               if (fnum[i] == -1)
+               {
+                       printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+
+               asprintf(&filedata, "%s %d", fname[i], fnum[i]);
+               DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n",
+                       strlen(filedata), filedata, fname[i], fnum[i],
+                       cli_state_get_host(context.cli[dir_server[i]]), file_server[i]));
+               if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata))
+               {
+                       printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+
+               if (!cli_close(context.cli[file_server[i]], fnum[i])) {
+                       printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]]));
+                       return False;
+               }
+       }
+       DEBUG(3,("used Dfs servers:"));
+       for (i=0; i < DFS_SERVER_COUNT ; i++) {
+               server_count++;
+               DEBUG(3,(" %s(%d)",     cli_state_get_host(context.cli[i]), i));
+               if (!torture_close_connection(context.cli[i]))
+                       return False;
+       }
+       DEBUG(3,("\n"));
+       
+       printf("Passed dfs_random_test\n");
+       return True;
+}
diff --git a/source4/torture/genbit.c b/source4/torture/genbit.c
new file mode 100644 (file)
index 0000000..6afde37
--- /dev/null
@@ -0,0 +1,83 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Gentest test definitions
+
+   Copyright (C) James Myers 2003
+
+   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.
+*/
+
+get_field_function test_field_get_file_attr;   
+get_field_function test_field_get_fid; 
+get_field_function test_field_get_filename;    
+get_field_function test_field_get_mtime;       
+get_field_function test_field_get_trans2;      
+get_field_function test_field_get_fsinfo_level;                
+
+static struct unlink_test_parms_t gen_unlink_test_parms;
+static struct close_test_parms_t gen_close_test_parms;
+static struct qfsi_test_parms_t gen_qfsi_test_parms;
+
+static struct trans2_parms trans2_qfsi_parms = {
+       testFieldTypeTrans2, 1, 2, 0, 0, TRANSACT2_QFSINFO
+};
+
+static struct field_test_spec gen_unlink_test_spec[] = {
+       {"FATTR", testFieldTypeFileAttr, NULL,
+                1, test_field_get_file_attr},
+       {"FNAME", testFieldTypeFilename, NULL,
+                -1, test_field_get_filename},
+       {"", -1, NULL, -1, NULL}
+};
+
+static struct field_test_spec gen_close_test_spec[] = {
+       {"FID", testFieldTypeFid, NULL, 1,
+               test_field_get_fid},
+       {"MTIME", testFieldTypeMtime, NULL, 2,
+               test_field_get_mtime},
+       {"", -1, NULL, -1, NULL}
+};
+
+static struct field_test_spec gen_qfsi_test_spec[] = {
+       {"TRANS2", testFieldTypeTrans2,
+               (void*)&trans2_qfsi_parms, 15,
+               test_field_get_trans2},
+       {"INFO_LEVEL", 0, NULL, 1, test_field_get_fsinfo_level},        
+       {"", -1, NULL, -1, NULL}
+};                                             
+
+static struct enum_test gen_enum_tests[] = {
+       {SMBunlink, "UNLINK", TEST_COND_TCON,
+               testTypeFilename,
+               TEST_OPTION_FILE_EXISTS | 
+                       TEST_OPTION_FILE_SYSTEM |
+                       TEST_OPTION_FILE_HIDDEN |
+                       TEST_OPTION_FILE_INVISIBLE |
+                       TEST_OPTION_FILE_WILDCARD |
+                       TEST_OPTION_FILE_NOT_EXIST,
+               1, gen_unlink_test_spec, (void*)&gen_unlink_test_parms,
+               gen_execute_unlink, gen_verify_unlink},
+       {SMBclose, "CLOSE", TEST_COND_TCON,
+               testTypeFid,
+               TEST_OPTION_FID_VALID | TEST_OPTION_FID_INVALID,
+               3, gen_close_test_spec, (void*)&gen_close_test_parms, 
+               gen_execute_close, gen_verify_close},
+       {SMBtrans2, "QUERY_FS_INFO", TEST_COND_TCON,
+               testTypeConnected,
+               1,
+               16, gen_qfsi_test_spec, (void*)&gen_qfsi_test_parms,
+               gen_execute_qfsi, gen_verify_qfsi},
+       {-1, NULL, 0, 0, 0, -1, NULL, NULL, NULL}
+};
diff --git a/source4/torture/gendefs.h b/source4/torture/gendefs.h
new file mode 100644 (file)
index 0000000..6afde37
--- /dev/null
@@ -0,0 +1,83 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Gentest test definitions
+
+   Copyright (C) James Myers 2003
+
+   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.
+*/
+
+get_field_function test_field_get_file_attr;   
+get_field_function test_field_get_fid; 
+get_field_function test_field_get_filename;    
+get_field_function test_field_get_mtime;       
+get_field_function test_field_get_trans2;      
+get_field_function test_field_get_fsinfo_level;                
+
+static struct unlink_test_parms_t gen_unlink_test_parms;
+static struct close_test_parms_t gen_close_test_parms;
+static struct qfsi_test_parms_t gen_qfsi_test_parms;
+
+static struct trans2_parms trans2_qfsi_parms = {
+       testFieldTypeTrans2, 1, 2, 0, 0, TRANSACT2_QFSINFO
+};
+
+static struct field_test_spec gen_unlink_test_spec[] = {
+       {"FATTR", testFieldTypeFileAttr, NULL,
+                1, test_field_get_file_attr},
+       {"FNAME", testFieldTypeFilename, NULL,
+                -1, test_field_get_filename},
+       {"", -1, NULL, -1, NULL}
+};
+
+static struct field_test_spec gen_close_test_spec[] = {
+       {"FID", testFieldTypeFid, NULL, 1,
+               test_field_get_fid},
+       {"MTIME", testFieldTypeMtime, NULL, 2,
+               test_field_get_mtime},
+       {"", -1, NULL, -1, NULL}
+};
+
+static struct field_test_spec gen_qfsi_test_spec[] = {
+       {"TRANS2", testFieldTypeTrans2,
+               (void*)&trans2_qfsi_parms, 15,
+               test_field_get_trans2},
+       {"INFO_LEVEL", 0, NULL, 1, test_field_get_fsinfo_level},        
+       {"", -1, NULL, -1, NULL}
+};                                             
+
+static struct enum_test gen_enum_tests[] = {
+       {SMBunlink, "UNLINK", TEST_COND_TCON,
+               testTypeFilename,
+               TEST_OPTION_FILE_EXISTS | 
+                       TEST_OPTION_FILE_SYSTEM |
+                       TEST_OPTION_FILE_HIDDEN |
+                       TEST_OPTION_FILE_INVISIBLE |
+                       TEST_OPTION_FILE_WILDCARD |
+                       TEST_OPTION_FILE_NOT_EXIST,
+               1, gen_unlink_test_spec, (void*)&gen_unlink_test_parms,
+               gen_execute_unlink, gen_verify_unlink},
+       {SMBclose, "CLOSE", TEST_COND_TCON,
+               testTypeFid,
+               TEST_OPTION_FID_VALID | TEST_OPTION_FID_INVALID,
+               3, gen_close_test_spec, (void*)&gen_close_test_parms, 
+               gen_execute_close, gen_verify_close},
+       {SMBtrans2, "QUERY_FS_INFO", TEST_COND_TCON,
+               testTypeConnected,
+               1,
+               16, gen_qfsi_test_spec, (void*)&gen_qfsi_test_parms,
+               gen_execute_qfsi, gen_verify_qfsi},
+       {-1, NULL, 0, 0, 0, -1, NULL, NULL, NULL}
+};
diff --git a/source4/torture/genparm.c b/source4/torture/genparm.c
new file mode 100644 (file)
index 0000000..4d968ba
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+   Unix SMB/CIFS implementation.
+   SMB test generator - load and parse test config
+   Copyright (C) James Myers 2003
+   
+   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 "gentest.h"
+
+static struct gentest_context_t *contextP;
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue);
+static BOOL do_section(const char *pszSectionName);
+
+/* prototypes for the special type handlers */
+static BOOL handle_tests(const char *pszParmValue, char **ptr);
+
+static BOOL handle_options(const char *pszParmValue, char **ptr);
+static BOOL handle_fields(const char *pszParmValue, char **ptr);
+
+static struct enum_list enum_command[] = {
+            {
+                SMBunlink, "SMBunlink"
+            },
+            {SMBclose, "SMBclosex"},
+            {-1, NULL}
+        };
+static struct enum_list enum_condition[] = {
+            {
+                TEST_COND_NEGPROT, "TEST_COND_NEGPROT"
+            },
+            {TEST_COND_SESSION, "TEST_COND_SESSION"},
+            {TEST_COND_TCON, "TEST_COND_TCON"},
+            {TEST_COND_FID, "TEST_COND_FID"},
+            {-1, NULL}
+        };
+static struct enum_list enum_test_type[] = {
+            {
+                testTypeConnected, "Connected"
+            },
+            {testTypeFilename, "Filename"},
+            {testTypeFid, "FID"},
+            {-1, NULL}
+        };
+static struct enum_list enum_options[] = {
+            {TEST_OPTION_FILE_EXISTS, "FILE_EXISTS"},
+            {TEST_OPTION_FILE_NOT_EXIST, "FILE_NOT_EXIST"},
+            {TEST_OPTION_FILE_HIDDEN, "FILE_HIDDEN"},
+            {TEST_OPTION_FILE_SYSTEM, "FILE_SYSTEM"},
+            {TEST_OPTION_FILE_INVISIBLE, "FILE_INVISIBLE"},
+            {TEST_OPTION_FILE_WILDCARD, "FILE_WILDCARD"},
+            {TEST_OPTION_FID_INVALID, "FID_INVALID"},
+            {TEST_OPTION_FID_VALID, "FID_VALID"},
+            {-1, NULL}
+        };
+static struct enum_list enum_execute[] = {
+            {(int)gen_execute_unlink, "gen_execute_unlink"},
+            {(int)gen_execute_close, "gen_execute_close"},
+            {-1, NULL}
+        };
+static struct enum_list enum_verify[] = {
+                                            {
+                                                (int)gen_verify_unlink, "gen_verify_unlink"
+                                            },
+                                            {(int)gen_verify_close, "gen_verify_close"},
+                                            {-1, NULL}
+                                        };
+static struct enum_list enum_field_type[] = {
+            {
+                testFieldTypeFilename, "Filename"
+            },
+            {testFieldTypeFileAttr, "FileAttr"},
+            {testFieldTypeFid, "FID"},
+            {testFieldTypeMtime, "Mtime"},
+            {testFieldTypeTrans2, "Trans2"},
+            {-1, NULL}
+        };
+static struct enum_list enum_function[] = {
+            {
+                (int)test_field_get_filename, "test_field_get_filename"
+            },
+            {(int)test_field_get_file_attr, "test_field_get_file_attr"},
+            {-1, NULL}
+        };
+
+/* Note: We do not initialise the defaults union - it is not allowed in ANSI C
+ */
+#define GEN_FLAG_GLOBAL 0x0001 /* fundamental options */
+#define GEN_FLAG_TEST  0x0002 /* test options */
+#define GEN_FLAG_FIELD         0x0004 /* field options */
+
+static struct {
+    int command;
+    char *name;
+    int debug;
+    int condition;
+    int type;
+    int options;
+    int words;
+    struct field_test_spec* fields;
+    int field_count;
+    void* execute;
+    void* verify;
+}
+test_section;
+
+static struct {
+    char *name;
+    int type;
+    BOOL random;
+    int words;
+    void * function;
+}
+field_section;
+
+static struct parm_struct parm_table[] = {
+            {"Base Options", P_SEP, P_SEPARATOR
+            },
+            /* global section parameters */
+            {"tests", P_LIST, P_GLOBAL, NULL, handle_tests, NULL, GEN_FLAG_GLOBAL},
+
+            /* test section parameters */
+            {"Test section", P_SEP, P_SEPARATOR},
+            {"command", P_ENUM, P_LOCAL, &test_section.command, NULL, enum_command, GEN_FLAG_TEST},
+            {"name", P_STRING, P_LOCAL, &test_section.name, NULL, NULL, GEN_FLAG_TEST},
+            {"debug", P_INTEGER, P_LOCAL, &test_section.debug, NULL, NULL, GEN_FLAG_TEST},
+            {"condition", P_ENUM, P_LOCAL, &test_section.condition, NULL, enum_condition, GEN_FLAG_TEST},
+            {"type", P_ENUM, P_LOCAL, &test_section.type, NULL, enum_test_type, GEN_FLAG_TEST},
+            {"options", P_LIST, P_LOCAL, &test_section.options, handle_options, NULL, GEN_FLAG_TEST},
+            {"word count", P_INTEGER, P_LOCAL, &test_section.words, NULL, NULL, GEN_FLAG_TEST},
+            {"fields", P_LIST, P_LOCAL, NULL, handle_fields, NULL, GEN_FLAG_TEST},
+            {"execute", P_ENUM, P_LOCAL, &test_section.execute, NULL, enum_execute, GEN_FLAG_TEST},
+            {"verify", P_ENUM, P_LOCAL, &test_section.verify, NULL, enum_verify, GEN_FLAG_TEST},
+
+            /* field section parameters */
+            {"Field section", P_SEP, P_SEPARATOR},
+            {"type", P_ENUM, P_LOCAL, &field_section.type, NULL, enum_field_type, GEN_FLAG_FIELD},
+            {"random", P_BOOL, P_LOCAL, &field_section.random, NULL, NULL, GEN_FLAG_FIELD},
+            {"word count", P_INTEGER, P_LOCAL, &field_section.words, NULL, NULL, GEN_FLAG_FIELD},
+            {"function", P_ENUM, P_LOCAL, &field_section.function, NULL, enum_function, GEN_FLAG_FIELD},
+
+            {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0}
+        };
+
+static BOOL handle_tests(const char *pszParmValue, char **ptr) {
+    contextP->testNames = str_list_make(pszParmValue, NULL);
+    return True;
+}
+static BOOL handle_options(const char *pszParmValue, char **ptr) {
+    /* convert option names (in enum_options) to flags */
+    char **str_array;
+
+    str_array = str_list_make(pszParmValue, NULL);
+
+    if (str_array) {
+        size_t i, j;
+        for ( j = 0; str_array[j] != NULL; j++) {
+            BOOL optionValid = False;
+            for (i = 0; enum_options[i].name; i++) {
+                if (strequal(str_array[j],
+                             enum_options[i].name)) {
+                    *(int *)ptr |= enum_options[i].value;
+                    optionValid = True;
+                    break;
+                }
+            }
+            if (!optionValid)
+                DEBUG(0,("handle_options: '%s' invalid option\n",
+                         str_array[j]));
+        }
+    }
+    DEBUG(9,("handle_options: %s -> %p\n", pszParmValue, *ptr));
+
+    return True;
+}
+
+static BOOL handle_fields(const char *pszParmValue, char **ptr) {
+    /* create initialized field structures for each name */
+    char **str_array;
+
+    str_array = str_list_make(pszParmValue, NULL);
+
+    if (str_array) {
+        size_t i;
+        for ( i = 0; str_array[i] != NULL; i++)
+            test_section.field_count++;
+        /* allocate new field array */
+        test_section.fields = talloc(contextP->mem_ctx,
+                                     test_section.field_count * sizeof(struct field_test_spec));
+        for ( i = 0; str_array[i] != NULL; i++)
+            test_section.fields[i].name = str_array[i];
+    }
+    return True;
+}
+
+/***************************************************************************
+ Map a parameter's string representation to something we can use. 
+ Returns False if the parameter string is not recognised, else TRUE.
+***************************************************************************/
+
+static int map_parameter(const char *pszParmName, int section) {
+    int iIndex;
+    unsigned validFlags = 0;
+
+    if (*pszParmName == '-')
+        return (-1);
+
+    /* Check for section-specific parameters.
+     * This allows the same parameter name to be used in 
+     * different sections with different meanings.
+     */
+    if (section == GEN_SECTION_GLOBAL)
+        validFlags |= GEN_FLAG_GLOBAL;
+    if (section == GEN_SECTION_TEST)
+        validFlags |= GEN_FLAG_TEST;
+    if (section == GEN_SECTION_FIELD)
+        validFlags |= GEN_FLAG_FIELD;
+    for (iIndex = 0; parm_table[iIndex].label; iIndex++)
+        if ((parm_table[iIndex].flags & validFlags) &&
+                strwicmp(parm_table[iIndex].label, pszParmName) == 0)
+            return (iIndex);
+
+    /* Warn only if it isn't parametric option */
+    if (strchr(pszParmName, ':') == NULL)
+        DEBUG(0, ("Unknown parameter encountered: \"%s\"\n", pszParmName));
+    /* We do return 'fail' for parametric options as well because they are
+       stored in different storage
+     */
+    return (-1);
+}
+
+/***************************************************************************
+ Set a boolean variable from the text value stored in the passed string.
+ Returns True in success, False if the passed string does not correctly 
+ represent a boolean.
+***************************************************************************/
+
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue) {
+    BOOL bRetval;
+
+    bRetval = True;
+    if (strwicmp(pszParmValue, "yes") == 0 ||
+            strwicmp(pszParmValue, "true") == 0 ||
+            strwicmp(pszParmValue, "1") == 0) {
+        *pb = True;
+    } else if (strwicmp(pszParmValue, "no") == 0 ||
+               strwicmp(pszParmValue, "False") == 0 ||
+               strwicmp(pszParmValue, "0") == 0) {
+        *pb = False;
+    } else {
+        DEBUG(0,
+              ("ERROR: Badly formed boolean in configuration file: \"%s\".\n",
+               pszParmValue));
+        bRetval = False;
+    }
+    return (bRetval);
+}
+
+/***************************************************************************
+ Process a parameter
+***************************************************************************/
+
+static BOOL gen_do_parm(struct gentest_context_t *context,
+                 const char *pszParmName, const char *pszParmValue) {
+    int parmnum, i;
+    void *parm_ptr = NULL;     /* where we are going to store the result */
+    void *def_ptr = NULL;
+
+    parmnum = map_parameter(pszParmName, context->iCurrentSectionType);
+
+    if (parmnum < 0) {
+        DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+        return (True);
+    }
+    DEBUG(19,("gen_do_parm: parm %s is valid\n", pszParmName));
+    def_ptr = parm_table[parmnum].ptr;
+
+    /* we might point at a test, a field or a global */
+    if (context->iCurrentSectionType == GEN_SECTION_GLOBAL) {
+        parm_ptr = def_ptr;
+    } else {
+        if (parm_table[parmnum].class == P_GLOBAL) {
+            DEBUG(0,
+                  ("Global parameter %s found in service section!\n",
+                   pszParmName));
+            return (True);
+        }
+        parm_ptr = def_ptr;
+    }
+
+    /* if it is a special case then go ahead */
+    if (parm_table[parmnum].special) {
+        parm_table[parmnum].special(pszParmValue, (char **)parm_ptr);
+        return (True);
+    }
+    DEBUG(19,("gen_do_parm: parm %s type=%d\n", pszParmName,
+              parm_table[parmnum].type));
+
+    /* now switch on the type of variable it is */
+    switch (parm_table[parmnum].type) {
+    case P_BOOL:
+        set_boolean(parm_ptr, pszParmValue);
+        break;
+
+    case P_INTEGER:
+        *(int *)parm_ptr = atoi(pszParmValue);
+        break;
+
+    case P_LIST:
+        *(char ***)parm_ptr = str_list_make(pszParmValue, NULL);
+        break;
+
+    case P_STRING:
+        parm_ptr = talloc_strdup(context->mem_ctx, pszParmValue);
+        break;
+
+    case P_ENUM:
+        for (i = 0; parm_table[parmnum].enum_list[i].name; i++) {
+            if (strequal
+                    (pszParmValue,
+                     parm_table[parmnum].enum_list[i].name)) {
+                *(int *)parm_ptr =
+                    parm_table[parmnum].
+                    enum_list[i].value;
+                break;
+            }
+        }
+        break;
+    case P_SEP:
+        break;
+    default:
+        break;
+    }
+
+    return (True);
+}
+/***************************************************************************
+ Process a parameter.
+***************************************************************************/
+
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue) {
+    BOOL bRetval;
+
+    DEBUG(4, ("doing parameter %s = %s\n", pszParmName, pszParmValue));
+    bRetval = gen_do_parm(contextP, pszParmName, pszParmValue);
+
+    return bRetval;
+}
+
+/***************************************************************************
+Check a test for consistency. Return False if the test is in any way
+incomplete or faulty, else True.
+***************************************************************************/
+
+static BOOL test_ok(struct gentest_context_t *context,int iTest) {
+    BOOL bRetval = True;
+
+    DEBUG(9,("test_ok: index=%d, tests@%p\n", iTest,
+             context->tests));
+    /* initialize new test section */
+    DEBUG(9,("test_ok: name=%s\n", test_section.name));
+    context->tests[iTest].name = test_section.name;
+    context->tests[iTest].debug = test_section.debug;
+    context->tests[iTest].type = test_section.type;
+    context->tests[iTest].command = test_section.command;
+    context->tests[iTest].initial_conditions = test_section.condition;
+    context->tests[iTest].options = test_section.options;
+    context->tests[iTest].word_count = test_section.words;
+    context->tests[iTest].fields = test_section.fields;
+    context->tests[iTest].field_count = test_section.field_count;
+    context->tests[iTest].execute = test_section.execute;
+    context->tests[iTest].verify = test_section.verify;
+
+    /* validate test entry */
+    DEBUG(9,("test_ok: validate name=%s\n", test_section.name));
+    if (context->tests[iTest].name[0] == '\0') {
+        DEBUG(0, ("The following message indicates an internal error:\n"));
+        DEBUG(0, ("No test name in test entry.\n"));
+        bRetval = False;
+    }
+    if (bRetval) {
+        context->tests[iTest].valid = True;
+        DEBUG(9,("added valid test %s\n",test_section.name));
+    }
+
+    return (bRetval);
+}
+/***************************************************************************
+Check a field for consistency. Return False if the field is in any way
+incomplete or faulty, else True.
+***************************************************************************/
+
+static BOOL field_ok(struct gentest_context_t *context,int iField) {
+    BOOL bRetval = True;
+
+    /* setup new field entry */
+    DEBUG(9,("field_ok: index=%d, fields@%p\n", iField,
+             context->fields));
+    context->fields[iField].name = field_section.name;
+    context->fields[iField].type = field_section.type;
+    context->fields[iField].random = field_section.random;
+    context->fields[iField].word_count = field_section.words;
+    context->fields[iField].function = field_section.function;
+
+    /* validate field */
+    if (context->fields[iField].name[0] == '\0') {
+        DEBUG(0, ("The following message indicates an internal error:\n"));
+        DEBUG(0, ("No field name in field entry.\n"));
+        bRetval = False;
+    }
+    if (bRetval) {
+        context->fields[iField].valid = True;
+        DEBUG(9,("added valid field %s\n",field_section.name));
+    }
+    
+    return (bRetval);
+}
+/***************************************************************************
+Find a test by name. Otherwise works like get_test.
+***************************************************************************/
+
+static int gettestbyname(struct gentest_context_t *context,
+                         const char *pszTestName) {
+    int iTest;
+
+    for (iTest = context->iNumTests - 1; iTest >= 0; iTest--)
+        if (context->tests[iTest].valid &&
+                strwicmp(context->tests[iTest].name, pszTestName) == 0) {
+            break;
+        }
+
+    return (iTest);
+}
+/***************************************************************************
+Find a field by name. Otherwise works like get_field.
+***************************************************************************/
+
+static int getfieldbyname(struct gentest_context_t *context,
+                          const char *pszFieldName) {
+    int iField;
+
+    for (iField = context->iNumFields - 1; iField >= 0; iField--)
+        if (context->fields[iField].valid &&
+                strwicmp(context->fields[iField].name, pszFieldName) == 0) {
+            break;
+        }
+
+    return (iField);
+}
+/***************************************************************************
+ Add a new test to the tests array initialising it with the given 
+ test. 
+***************************************************************************/
+
+static int add_a_test(struct gentest_context_t *context,
+                      const char *name) {
+    int i;
+    int num_to_alloc = context->iNumTests + 1;
+
+    DEBUG(3, ("add_a_test: %s at index %d\n", name, num_to_alloc-1));
+    /* it might already exist */
+    if (name) {
+        i = gettestbyname(context, name);
+        if (i >= 0)
+            return (i);
+    }
+
+    /* find an invalid one */
+    for (i = 0; i < context->iNumTests; i++)
+        if (!context->tests[i].valid)
+            break;
+
+    /* if not, then create one */
+    DEBUG(3, ("add_a_test: add %s at index %d\n", name, i));
+    if (i == context->iNumTests) {
+        struct enum_test *tsp;
+
+        tsp = talloc_realloc(context->mem_ctx, context->tests,
+                             sizeof(struct enum_test) *
+                             num_to_alloc);
+
+        if (!tsp) {
+            DEBUG(0,("add_a_test: failed to enlarge TestPtrs!\n"));
+            return (-1);
+        } else {
+            context->tests = tsp;
+        }
+
+        context->iNumTests++;
+        DEBUG(3, ("add_a_test: tests@%p\n", tsp));
+    } //else
+    //free_test(context->tests[i]);
+    /* reinitialize test section fields */
+    test_section.command = 0;
+    test_section.name = talloc_strdup(context->mem_ctx, name);
+    test_section.debug = 0;
+    test_section.condition = 0;
+    test_section.type = 0;
+    test_section.options = 0;
+    test_section.words = 0;
+    test_section.fields = NULL;
+    test_section.field_count = 0;
+    test_section.execute = NULL;
+    test_section.verify = NULL;
+    context->tests[i].valid = False;
+
+    if (name)
+        context->tests[i].name = test_section.name;
+    DEBUG(3, ("add_a_test: added %s at index %d\n", name, i));
+    return (i);
+}
+/***************************************************************************
+ Add a new field to the fields array initialising it with the given 
+ field. 
+***************************************************************************/
+
+static int add_a_field(struct gentest_context_t *context,
+                       const char *name) {
+    int i;
+    int num_to_alloc = context->iNumFields + 1;
+
+    DEBUG(3, ("add_a_field: %s at index %d\n", name, num_to_alloc-1));
+    /* it might already exist */
+    if (name) {
+        i = getfieldbyname(context, name);
+        if (i >= 0)
+            return (i);
+    }
+
+    /* find an invalid one */
+    for (i = 0; i < context->iNumFields; i++)
+        if (!context->fields[i].valid)
+            break;
+
+    /* if not, then create one */
+    DEBUG(3, ("add_a_field: add %s at index %d\n", name, i));
+    if (i == context->iNumFields) {
+        field_test_spec *tsp;
+
+        tsp = talloc_realloc(context->mem_ctx, context->fields,
+                             sizeof(field_test_spec) *
+                             num_to_alloc);
+
+        if (!tsp) {
+            DEBUG(0,("add_a_field: failed to enlarge FieldPtrs!\n"));
+            return (-1);
+        } else {
+            context->fields = tsp;
+        }
+
+        context->iNumFields++;
+        DEBUG(3, ("add_a_field: fields@%p\n", tsp));
+    }
+
+    /* reinitialize field section fields */
+    field_section.name = NULL;
+    field_section.type = 0;
+    field_section.random = False;
+    field_section.words = 0;
+    field_section.function = NULL;
+    context->fields[i].valid = False;
+
+    if (name)
+        field_section.name = talloc_strdup(context->mem_ctx, name);
+    DEBUG(3, ("add_a_field: added %s at index %d\n", name, i));
+    return (i);
+}
+/***************************************************************************
+ Process a new section (test or field).
+ Returns True on success, False on failure. 
+***************************************************************************/
+
+static BOOL do_section(const char *pszSectionName) {
+    BOOL bRetval;
+    BOOL isglobal = (strwicmp(pszSectionName, GLOBAL_NAME) == 0);
+    char *sectionType, *sectionName, *p;
+
+    bRetval = False;
+    DEBUG(4, ("doing section %s\n", pszSectionName));
+    /* if we've just struck a global section, note the fact. */
+    contextP->bInGlobalSection = isglobal;
+
+    /* check for multiple global sections */
+    if (contextP->bInGlobalSection) {
+        DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName));
+        contextP->iCurrentSectionType = GEN_SECTION_GLOBAL;
+        return (True);
+    } else if (contextP->iCurrentSectionType == GEN_SECTION_GLOBAL) {
+        /* just finished global section */
+        ;
+    }
+
+    /* parse section name (form <type:name> */
+    sectionType = talloc_strdup(contextP->mem_ctx, pszSectionName);
+    p = strchr_m(sectionType,':');
+    if (p) {
+        *p = 0;
+        sectionName = talloc_strdup(contextP->mem_ctx, p+1);
+    } else {
+        DEBUG(0, ("Invalid section name %s\n", pszSectionName));
+        return False;
+    }
+
+    /* if we have a current test or field, tidy it up before moving on */
+    bRetval = True;
+
+    if (contextP->iTestIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_TEST)
+        bRetval = test_ok(contextP, contextP->iTestIndex);
+    if (contextP->iFieldIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_FIELD)
+        bRetval = field_ok(contextP, contextP->iFieldIndex);
+
+    /* determine type of this section */
+    contextP->iCurrentSectionType = GEN_SECTION_INVALID;
+    if (strequal(sectionType, "test"))
+        contextP->iCurrentSectionType = GEN_SECTION_TEST;
+    if (strequal(sectionType, "field"))
+        contextP->iCurrentSectionType = GEN_SECTION_FIELD;
+    if (contextP->iCurrentSectionType == GEN_SECTION_INVALID) {
+        DEBUG(0, ("Invalid section type %s\n", sectionType));
+        return False;
+    }
+
+    /* if all is still well, move to the next record in the tests array */
+    if (bRetval) {
+        /* We put this here to avoid an odd message order if messages are */
+        /* issued by the post-processing of a previous section. */
+        DEBUG(2, ("Processing section \"[%s]\"\n", pszSectionName));
+
+        if (contextP->iCurrentSectionType == GEN_SECTION_TEST) {
+            if ((contextP->iTestIndex = add_a_test(contextP, sectionName))
+                    < 0) {
+                DEBUG(0, ("Failed to add a new test\n"));
+                return (False);
+            }
+        }
+        if (contextP->iCurrentSectionType == GEN_SECTION_FIELD) {
+            if ((contextP->iFieldIndex = add_a_field(contextP, sectionName))
+                    < 0) {
+                DEBUG(0, ("Failed to add a new field\n"));
+                return (False);
+            }
+        }
+    }
+
+    return (bRetval);
+}
+
+/***************************************************************************
+ Load the test configuration from the test config file. Return True on success, 
+ False on failure.
+***************************************************************************/
+
+BOOL gen_load_config(struct gentest_context_t *contextPTR) {
+    char *n2;
+    BOOL bRetval;
+
+    contextP = contextPTR;
+    contextP->param_opt = NULL;
+
+    n2 = talloc_strdup(contextP->mem_ctx, contextP->config_filename);
+
+    /* We get sections first, so have to start 'behind' to make up */
+    contextP->iTestIndex = -1;
+    bRetval = pm_process(n2, do_section, do_parameter);
+
+    /* finish up the last section */
+    DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval)));
+
+    /* if we have a current test or field, tidy it up before moving on */
+    if (contextP->iTestIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_TEST)
+        bRetval = test_ok(contextP, contextP->iTestIndex);
+    if (contextP->iFieldIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_FIELD)
+        bRetval = field_ok(contextP, contextP->iFieldIndex);
+
+    /* OK, we've parsed the configuration, now we need to match
+     * the field sections to fields required by tests */
+    if (bRetval) {
+        int i,j,k;
+        BOOL fieldValid;
+        for (i=0; i<contextP->iNumTests; i++) {
+            DEBUG(19,("gen_load_config: process test %d %s\n",
+                      i, contextP->tests[i].name));
+            for (j=0; j<contextP->tests[i].field_count; j++) {
+                fieldValid = False;
+                DEBUG(19,("gen_load_config: look for field %s\n",
+                          contextP->tests[i].fields[j].name));
+                for (k=0; k<contextP->iNumFields; k++) {
+                    DEBUG(19,("gen_load_config: compare field %s\n",
+                              contextP->fields[k].name));
+                    if (strequal(contextP->tests[i].fields[j].name,
+                                 contextP->fields[k].name)) {
+                        /* matching field found */
+                        fieldValid = True;
+                        contextP->tests[i].fields[j].type = contextP->fields[k].type;
+                        contextP->tests[i].fields[j].word_count = contextP->fields[k].word_count;
+                        contextP->tests[i].fields[j].function = contextP->fields[k].function;
+                        contextP->tests[i].fields[j].valid = contextP->fields[k].valid;
+                        contextP->tests[i].fields[j].random = contextP->fields[k].random;
+                        contextP->tests[i].fields[j].parms = contextP->fields[k].parms;
+                        break;
+                    }
+                    if (fieldValid)
+                        break;
+                }
+                if (!fieldValid) {
+                       contextP->tests[i].fields[j].valid = False;
+                       contextP->tests[i].fields[j].function = test_field_get_null;
+                    DEBUG(0,("missing field section: %s\n",
+                             contextP->tests[i].fields[j].name));
+                }
+            }
+        }
+    }
+
+    return (bRetval);
+}
diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c
new file mode 100644 (file)
index 0000000..abe6d05
--- /dev/null
@@ -0,0 +1,2113 @@
+/* 
+   Unix SMB/CIFS implementation.
+   generic testing tool
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define NSERVERS 2
+#define NINSTANCES 2
+
+/* global options */
+static struct gentest_options {
+       BOOL showall;
+       BOOL analyze;
+       BOOL analyze_always;
+       BOOL analyze_continuous;
+       uint_t max_open_handles;
+       uint_t seed;
+       uint_t numops;
+       BOOL use_oplocks;
+       char **ignore_patterns;
+       const char *seeds_file;
+       BOOL use_preset_seeds;
+       BOOL fast_reconnect;
+} options;
+
+/* mapping between open handles on the server and local handles */
+static struct {
+       BOOL active;
+       uint_t instance;
+       uint_t server_fnum[NSERVERS];
+       const char *name;
+} *open_handles;
+static uint_t num_open_handles;
+
+/* state information for the servers. We open NINSTANCES connections to
+   each server */
+static struct {
+       struct cli_state *cli[NINSTANCES];
+       char *server_name;
+       char *share_name;
+       char *username;
+       char *password;
+} servers[NSERVERS];
+
+/* the seeds and flags for each operation */
+static struct {
+       uint_t seed;
+       BOOL disabled;
+} *op_parms;
+
+
+/* oplock break info */
+static struct {
+       BOOL got_break;
+       uint16 fnum;
+       uint16 handle;
+       uint8 level;
+       BOOL do_close;
+} oplocks[NSERVERS][NINSTANCES];
+
+/* change notify reply info */
+static struct {
+       int notify_count;
+       NTSTATUS status;
+       struct smb_notify notify;
+} notifies[NSERVERS][NINSTANCES];
+
+/* info relevant to the current operation */
+static struct {
+       const char *name;
+       uint_t seed;
+       NTSTATUS status;
+       uint_t opnum;
+       TALLOC_CTX *mem_ctx;
+} current_op;
+
+
+
+#define BAD_HANDLE 0xFFFE
+
+static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private);
+static void idle_func(struct cli_transport *transport, void *private);
+
+/*
+  check if a string should be ignored. This is used as the basis
+  for all error ignore settings
+*/
+static BOOL ignore_pattern(const char *str)
+{
+       int i;
+       if (!options.ignore_patterns) return False;
+
+       for (i=0;options.ignore_patterns[i];i++) {
+               if (strcmp(options.ignore_patterns[i], str) == 0 ||
+                   gen_fnmatch(options.ignore_patterns[i], str) == 0) {
+                       DEBUG(2,("Ignoring '%s'\n", str));
+                       return True;
+               }
+       }
+       return False;
+}
+
+/***************************************************** 
+connect to the servers
+*******************************************************/
+static BOOL connect_servers_fast(void)
+{
+       int h, i;
+
+       /* close all open files */
+       for (h=0;h<options.max_open_handles;h++) {
+               if (!open_handles[h].active) continue;
+               for (i=0;i<NSERVERS;i++) {
+                       if (!cli_close(servers[i].cli[open_handles[h].instance],
+                                      open_handles[h].server_fnum[i])) {
+                               return False;
+                       }
+                       open_handles[h].active = False;
+               }
+       }
+
+       return True;
+}
+
+
+
+
+/***************************************************** 
+connect to the servers
+*******************************************************/
+static BOOL connect_servers(void)
+{
+       int i, j;
+
+       if (options.fast_reconnect && servers[0].cli[0]) {
+               if (connect_servers_fast()) {
+                       return True;
+               }
+       }
+
+       /* close any existing connections */
+       for (i=0;i<NSERVERS;i++) {
+               for (j=0;j<NINSTANCES;j++) {
+                       if (servers[i].cli[j]) {
+                               cli_tdis(servers[i].cli[j]);
+                               cli_shutdown(servers[i].cli[j]);
+                               servers[i].cli[j] = NULL;
+                       }
+               }
+       }
+
+       for (i=0;i<NSERVERS;i++) {
+               for (j=0;j<NINSTANCES;j++) {
+                       NTSTATUS status;
+                       printf("Connecting to \\\\%s\\%s as %s - instance %d\n",
+                              servers[i].server_name, servers[i].share_name, 
+                              servers[i].username, j);
+                       status = cli_full_connection(&servers[i].cli[j],
+                                                    "gentest",
+                                                    servers[i].server_name, NULL, 
+                                                    servers[i].share_name, "?????", 
+                                                    servers[i].username, "",
+                                                    servers[i].password, 0, NULL);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               printf("Failed to connect to \\\\%s\\%s - %s\n",
+                                      servers[i].server_name, servers[i].share_name,
+                                      nt_errstr(status));
+                               return False;
+                       }
+
+                       cli_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL);
+                       cli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 10, NULL);
+               }
+       }
+
+       return True;
+}
+
+/*
+  work out the time skew between the servers - be conservative
+*/
+static uint_t time_skew(void)
+{
+       uint_t ret;
+       ret = ABS(servers[0].cli[0]->transport->negotiate.server_time -
+                 servers[1].cli[0]->transport->negotiate.server_time);
+       return ret + 300;
+}
+
+/*
+  turn an fnum for an instance into a handle
+*/
+static uint_t fnum_to_handle(int server, int instance, uint16 fnum)
+{
+       uint_t i;
+       for (i=0;i<options.max_open_handles;i++) {
+               if (!open_handles[i].active ||
+                   instance != open_handles[i].instance) continue;
+               if (open_handles[i].server_fnum[server] == fnum) {
+                       return i;
+               }
+       }
+       printf("Invalid fnum %d in fnum_to_handle on server %d instance %d\n", 
+              fnum, server, instance);
+       return BAD_HANDLE;
+}
+
+/*
+  add some newly opened handles
+*/
+static void gen_add_handle(int instance, const char *name, uint16 fnums[NSERVERS])
+{
+       int i, h;
+       for (h=0;h<options.max_open_handles;h++) {
+               if (!open_handles[h].active) break;
+       }
+       if (h == options.max_open_handles) {
+               /* we have to force close a random handle */
+               h = random() % options.max_open_handles;
+               for (i=0;i<NSERVERS;i++) {
+                       if (!cli_close(servers[i].cli[open_handles[h].instance], 
+                                      open_handles[h].server_fnum[i])) {
+                               printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
+                                      cli_errstr(servers[i].cli[open_handles[h].instance]));
+                       }
+               }
+               printf("Recovered handle %d\n", h);
+               num_open_handles--;
+       }
+       for (i=0;i<NSERVERS;i++) {
+               open_handles[h].server_fnum[i] = fnums[i];
+               open_handles[h].instance = instance;
+               open_handles[h].active = True;
+               open_handles[h].name = name;
+       }
+       num_open_handles++;
+
+       printf("OPEN num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", 
+              num_open_handles, h, 
+              open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
+              name);
+}
+
+/*
+  remove a closed handle
+*/
+static void gen_remove_handle(int instance, uint16 fnums[NSERVERS])
+{
+       int h;
+       for (h=0;h<options.max_open_handles;h++) {
+               if (instance == open_handles[h].instance &&
+                   open_handles[h].server_fnum[0] == fnums[0]) {
+                       open_handles[h].active = False;                 
+                       num_open_handles--;
+                       printf("CLOSE num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", 
+                              num_open_handles, h, 
+                              open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
+                              open_handles[h].name);
+                       return;
+               }
+       }
+       printf("Removing invalid handle!?\n");
+       exit(1);
+}
+
+/*
+  return True with 'chance' probability as a percentage
+*/
+static BOOL gen_chance(uint_t chance)
+{
+       return ((random() % 100) <= chance);
+}
+
+/*
+  map an internal handle number to a server fnum
+*/
+static uint16 gen_lookup_fnum(int server, uint16 handle)
+{
+       if (handle == BAD_HANDLE) return handle;
+       return open_handles[handle].server_fnum[server];
+}
+
+/*
+  return a file handle
+*/
+static uint16 gen_fnum(int instance)
+{
+       uint16 h;
+       int count = 0;
+
+       if (gen_chance(20)) return BAD_HANDLE;
+
+       while (num_open_handles > 0 && count++ < 10*options.max_open_handles) {
+               h = random() % options.max_open_handles;
+               if (open_handles[h].active && 
+                   open_handles[h].instance == instance) {
+                       return h;
+               }
+       }
+       return BAD_HANDLE;
+}
+
+/*
+  return a file handle, but skewed so we don't close the last
+  couple of handles too readily
+*/
+static uint16 gen_fnum_close(int instance)
+{
+       if (num_open_handles < 3) {
+               if (gen_chance(80)) return BAD_HANDLE;
+       }
+
+       return gen_fnum(instance);
+}
+
+/*
+  generate an integer in a specified range
+*/
+static int gen_int_range(uint_t min, uint_t max)
+{
+       uint_t r = random();
+       return min + (r % (1+max-min));
+}
+
+/*
+  return a fnum for use as a root fid
+  be careful to call GEN_SET_FNUM() when you use this!
+*/
+static uint16 gen_root_fid(int instance)
+{
+       if (gen_chance(5)) return gen_fnum(instance);
+       return 0;
+}
+
+/*
+  generate a file offset
+*/
+static int gen_offset(void)
+{
+       if (gen_chance(20)) return 0;
+       return gen_int_range(0, 1024*1024);
+}
+
+/*
+  generate a io count
+*/
+static int gen_io_count(void)
+{
+       if (gen_chance(20)) return 0;
+       return gen_int_range(0, 4096);
+}
+
+/*
+  generate a filename
+*/
+static const char *gen_fname(void)
+{
+       const char *names[] = {"\\gentest\\gentest.dat", 
+                              "\\gentest\\foo", 
+                              "\\gentest\\foo2.sym", 
+                              "\\gentest\\foo3.dll", 
+                              "\\gentest\\foo4", 
+                              "\\gentest\\foo4:teststream1", 
+                              "\\gentest\\foo4:teststream2", 
+                              "\\gentest\\foo5.exe", 
+                              "\\gentest\\foo5.exe:teststream3", 
+                              "\\gentest\\foo5.exe:teststream4", 
+                              "\\gentest\\foo6.com", 
+                              "\\gentest\\blah", 
+                              "\\gentest\\blah\\blergh.txt", 
+                              "\\gentest\\blah\\blergh2", 
+                              "\\gentest\\blah\\blergh3.txt", 
+                              "\\gentest\\blah\\blergh4", 
+                              "\\gentest\\blah\\blergh5.txt", 
+                              "\\gentest\\blah\\blergh5", 
+                              "\\gentest\\blah\\.", 
+#if 0
+                              /* this causes problem with w2k3 */
+                              "\\gentest\\blah\\..", 
+#endif
+                              "\\gentest\\a_very_long_name.bin", 
+                              "\\gentest\\x.y", 
+                              "\\gentest\\blah"};
+       int i;
+
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(names)-1);
+       } while (ignore_pattern(names[i]));
+
+       return names[i];
+}
+
+/*
+  generate a filename with a higher chance of choosing an already 
+  open file
+*/
+static const char *gen_fname_open(int instance)
+{
+       uint16 h;
+       h = gen_fnum(instance);
+       if (h == BAD_HANDLE) {
+               return gen_fname();
+       }
+       return open_handles[h].name;
+}
+
+/*
+  generate a wildcard pattern
+*/
+static const char *gen_pattern(void)
+{
+       int i;
+       const char *names[] = {"\\gentest\\*.dat", 
+                              "\\gentest\\*", 
+                              "\\gentest\\*.*", 
+                              "\\gentest\\blah\\*.*", 
+                              "\\gentest\\blah\\*", 
+                              "\\gentest\\?"};
+
+       if (gen_chance(50)) return gen_fname();
+
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(names)-1);
+       } while (ignore_pattern(names[i]));
+
+       return names[i];
+}
+
+/*
+  generate a bitmask
+*/
+static uint32 gen_bits_mask(uint_t mask)
+{
+       uint_t ret = random();
+       return ret & mask;
+}
+
+/*
+  generate a bitmask with high probability of the first mask
+  and low of the second
+*/
+static uint32 gen_bits_mask2(uint32 mask1, uint32 mask2)
+{
+       if (gen_chance(10)) return gen_bits_mask(mask2);
+       return gen_bits_mask(mask1);
+}
+
+/*
+  generate a boolean
+*/
+static BOOL gen_bool(void)
+{
+       return gen_bits_mask2(0x1, 0xFF);
+}
+
+/*
+  return a lockingx lock mode
+*/
+static uint16 gen_lock_mode(void)
+{
+       if (gen_chance(5))  return gen_bits_mask(0xFFFF);
+       if (gen_chance(20)) return gen_bits_mask(0x1F);
+       return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES);
+}
+
+/*
+  generate a pid 
+*/
+static uint16 gen_pid(void)
+{
+       if (gen_chance(10)) return gen_bits_mask(0xFFFF);
+       return getpid();
+}
+
+/*
+  generate a lock count
+*/
+static SMB_OFF_T gen_lock_count(void)
+{
+       return gen_int_range(0, 3);
+}
+
+/*
+  generate a ntcreatex flags field
+*/
+static uint32 gen_ntcreatex_flags(void)
+{
+       if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED;
+       return gen_bits_mask2(0x1F, 0xFFFFFFFF);
+}
+
+/*
+  generate a NT access mask
+*/
+static uint32 gen_access_mask(void)
+{
+       if (gen_chance(50)) return SEC_RIGHT_MAXIMUM_ALLOWED;
+       if (gen_chance(20)) return GENERIC_RIGHTS_FILE_ALL_ACCESS;
+       return gen_bits_mask(0xFFFFFFFF);
+}
+
+/*
+  generate a ntcreatex create options bitfield
+*/
+static uint32 gen_create_options(void)
+{
+       if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
+       if (gen_chance(50)) return 0;
+       return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY);
+}
+
+/*
+  generate a ntcreatex open disposition
+*/
+static uint32 gen_open_disp(void)
+{
+       if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF);
+       return gen_int_range(0, 5);
+}
+
+/*
+  generate an openx open mode
+*/
+static uint16 gen_openx_mode(void)
+{
+       if (gen_chance(20)) return gen_bits_mask(0xFFFF);
+       if (gen_chance(20)) return gen_bits_mask(0xFF);
+       return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3);
+}
+
+/*
+  generate an openx flags field
+*/
+static uint16 gen_openx_flags(void)
+{
+       if (gen_chance(20)) return gen_bits_mask(0xFFFF);
+       return gen_bits_mask(0x7);
+}
+
+/*
+  generate an openx open function
+*/
+static uint16 gen_openx_func(void)
+{
+       if (gen_chance(20)) return gen_bits_mask(0xFFFF);
+       return gen_bits_mask(0x13);
+}
+
+/*
+  generate a file attrib combination
+*/
+static uint32 gen_attrib(void)
+{
+       if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
+       return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
+}
+
+/*
+  generate a unix timestamp
+*/
+static time_t gen_timet(void)
+{
+       if (gen_chance(30)) return 0;
+       return (time_t)random();
+}
+
+/*
+  generate a unix timestamp
+*/
+static NTTIME gen_nttime(void)
+{
+       NTTIME ret;
+       unix_to_nt_time(&ret, gen_timet());
+       return ret;
+}
+
+/*
+  generate a milliseconds protocol timeout
+*/
+static uint32 gen_timeout(void)
+{
+       if (gen_chance(98)) return 0;
+       return random() % 50;
+}
+
+/*
+  generate a file allocation size
+*/
+static uint_t gen_alloc_size(void)
+{
+       uint_t ret;
+
+       if (gen_chance(30)) return 0;
+
+       ret = random() % 4*1024*1024;
+       /* give a high chance of a round number */
+       if (gen_chance(60)) {
+               ret &= ~(1024*1024 - 1);
+       }
+       return ret;
+}
+
+/*
+  generate an ea_struct
+*/
+struct ea_struct gen_ea_struct(void)
+{
+       struct ea_struct ea;
+       const char *names[] = {"EAONE", 
+                              "", 
+                              "FOO!", 
+                              " WITH SPACES ", 
+                              ".", 
+                              "AVERYLONGATTRIBUTENAME"};
+       const char *values[] = {"VALUE1", 
+                              "", 
+                              "NOT MUCH FOO", 
+                              " LEADING SPACES ", 
+                              ":", 
+                              "ASOMEWHATLONGERATTRIBUTEVALUE"};
+       int i;
+
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(names)-1);
+       } while (ignore_pattern(names[i]));
+
+       ea.name.s = names[i];
+
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(values)-1);
+       } while (ignore_pattern(values[i]));
+
+       ea.value = data_blob(values[i], strlen(values[i]));
+
+       if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF);
+       ea.flags = 0;
+
+       return ea;
+}
+
+
+/*
+  this is called when a change notify reply comes in
+*/
+static void async_notify(struct cli_request *req)
+{
+       struct smb_notify notify;
+       NTSTATUS status;
+       int i, j;
+       uint16 tid;
+       struct cli_transport *transport = req->transport;
+
+       tid = SVAL(req->in.hdr, HDR_TID);
+
+       status = smb_raw_changenotify_recv(req, current_op.mem_ctx, &notify);
+       if (NT_STATUS_IS_OK(status)) {
+               printf("notify tid=%d num_changes=%d action=%d name=%s\n", 
+                      tid, 
+                      notify.out.num_changes,
+                      notify.out.changes[0].action,
+                      notify.out.changes[0].name.s);
+       }
+
+       for (i=0;i<NSERVERS;i++) {
+               for (j=0;j<NINSTANCES;j++) {
+                       if (transport == servers[i].cli[j]->transport &&
+                           tid == servers[i].cli[j]->tree->tid) {
+                               notifies[i][j].notify_count++;
+                               notifies[i][j].status = status;
+                               notifies[i][j].notify = notify;
+                       }
+               }
+       }
+}
+
+/*
+  the oplock handler will either ack the break or close the file
+*/
+static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private)
+{
+       union smb_close io;
+       NTSTATUS status;
+       int i, j;
+       BOOL do_close;
+       struct cli_tree *tree = NULL;
+
+       srandom(current_op.seed);
+       do_close = gen_chance(50);
+
+       for (i=0;i<NSERVERS;i++) {
+               for (j=0;j<NINSTANCES;j++) {
+                       if (transport == servers[i].cli[j]->transport &&
+                           tid == servers[i].cli[j]->tree->tid) {
+                               oplocks[i][j].got_break = True;
+                               oplocks[i][j].fnum = fnum;
+                               oplocks[i][j].handle = fnum_to_handle(i, j, fnum);
+                               oplocks[i][j].level = level;
+                               oplocks[i][j].do_close = do_close;
+                               tree = servers[i].cli[j]->tree;
+                       }
+               }
+       }
+
+       if (!tree) {
+               printf("Oplock break not for one of our trees!?\n");
+               return False;
+       }
+
+       if (!do_close) {
+               printf("oplock ack fnum=%d\n", fnum);
+               return cli_oplock_ack(tree, fnum, level == 1? 0x102 : 2);
+       }
+
+       printf("oplock close fnum=%d\n", fnum);
+
+       io.close.level = RAW_CLOSE_CLOSE;
+       io.close.in.fnum = fnum;
+       io.close.in.write_time = 0;
+       status = smb_raw_close(tree, &io);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("WARNING: close failed in oplock_handler_close - %s\n", nt_errstr(status));
+       }
+       return True;
+}
+
+
+/*
+  the idle function tries to cope with getting an oplock break on a connection, and
+  an operation on another connection blocking until that break is acked
+  we check for operations on all transports in the idle function
+*/
+static void idle_func(struct cli_transport *transport, void *private)
+{
+       int i, j;
+       for (i=0;i<NSERVERS;i++) {
+               for (j=0;j<NINSTANCES;j++) {
+                       if (servers[i].cli[j] &&
+                           transport != servers[i].cli[j]->transport &&
+                           cli_transport_pending(servers[i].cli[j]->transport)) {
+                               if (!cli_request_receive_next(servers[i].cli[j]->transport)) {
+                                       printf("Connection to server %d instance %d died!\n",
+                                              i, j);
+                                       exit(1);
+                               }
+                       }
+               }
+       }
+
+}
+
+
+/*
+  compare NTSTATUS, using checking ignored patterns
+*/
+static BOOL compare_status(NTSTATUS status1, NTSTATUS status2)
+{
+       if (NT_STATUS_EQUAL(status1, status2)) return True;
+
+       /* one code being an error and the other OK is always an error */
+       if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return False;
+
+       /* if we are ignoring one of the status codes then consider this a match */
+       if (ignore_pattern(nt_errstr(status1)) ||
+           ignore_pattern(nt_errstr(status2))) {
+               return True;
+       }
+       return False;
+}
+
+
+/*
+  check for pending packets on all connections
+*/
+static void check_pending(void)
+{
+       int i, j;
+
+       msleep(20);
+
+       for (j=0;j<NINSTANCES;j++) {
+               for (i=0;i<NSERVERS;i++) {
+                       if (cli_transport_pending(servers[i].cli[j]->transport)) {
+                               if (!cli_request_receive_next(servers[i].cli[j]->transport)) {
+                                       printf("Connection to server %d instance %d died!\n",
+                                              i, j);
+                                       exit(1);                                        
+                               }
+                       }
+               }
+       }       
+}
+
+/*
+  check that the same oplock breaks have been received by all instances
+*/
+static BOOL check_oplocks(const char *call)
+{
+       int i, j;
+       int tries = 0;
+
+again:
+       check_pending();
+
+       for (j=0;j<NINSTANCES;j++) {
+               for (i=1;i<NSERVERS;i++) {
+                       if (oplocks[0][j].got_break != oplocks[i][j].got_break ||
+                           oplocks[0][j].handle != oplocks[i][j].handle ||
+                           oplocks[0][j].level != oplocks[i][j].level) {
+                               if (tries++ < 10) goto again;
+                               printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n",
+                                      oplocks[0][j].got_break, 
+                                      oplocks[0][j].handle, 
+                                      oplocks[0][j].level, 
+                                      oplocks[i][j].got_break, 
+                                      oplocks[i][j].handle, 
+                                      oplocks[i][j].level);
+                               return False;
+                       }
+               }
+       }
+
+       /* if we got a break and closed then remove the handle */
+       for (j=0;j<NINSTANCES;j++) {
+               if (oplocks[0][j].got_break &&
+                   oplocks[0][j].do_close) {
+                       uint16 fnums[NSERVERS];
+                       for (i=0;i<NSERVERS;i++) {
+                               fnums[i] = oplocks[i][j].fnum;
+                       }
+                       gen_remove_handle(j, fnums);
+                       break;
+               }
+       }       
+       return True;
+}
+
+
+/*
+  check that the same change notify info has been received by all instances
+*/
+static BOOL check_notifies(const char *call)
+{
+       int i, j;
+       int tries = 0;
+
+again:
+       check_pending();
+
+       for (j=0;j<NINSTANCES;j++) {
+               for (i=1;i<NSERVERS;i++) {
+                       int n;
+                       struct smb_notify not1, not2;
+
+                       if (notifies[0][j].notify_count != notifies[i][j].notify_count) {
+                               if (tries++ < 10) goto again;
+                               printf("Notify count inconsistent %d %d\n",
+                                      notifies[0][j].notify_count,
+                                      notifies[i][j].notify_count);
+                               return False;
+                       }
+
+                       if (notifies[0][j].notify_count == 0) continue;
+
+                       if (!NT_STATUS_EQUAL(notifies[0][j].status,
+                                            notifies[i][j].status)) {
+                               printf("Notify status mismatch - %s - %s\n",
+                                      nt_errstr(notifies[0][j].status),
+                                      nt_errstr(notifies[i][j].status));
+                               return False;
+                       }
+
+                       if (!NT_STATUS_IS_OK(notifies[0][j].status)) {
+                               continue;
+                       }
+
+                       not1 = notifies[0][j].notify;
+                       not2 = notifies[i][j].notify;
+
+                       for (n=0;n<not1.out.num_changes;n++) {
+                               if (not1.out.changes[n].action != 
+                                   not2.out.changes[n].action) {
+                                       printf("Notify action %d inconsistent %d %d\n", n,
+                                              not1.out.changes[n].action,
+                                              not2.out.changes[n].action);
+                                       return False;
+                               }
+                               if (strcmp(not1.out.changes[n].name.s,
+                                          not2.out.changes[n].name.s)) {
+                                       printf("Notify name %d inconsistent %s %s\n", n,
+                                              not1.out.changes[n].name.s,
+                                              not2.out.changes[n].name.s);
+                                       return False;
+                               }
+                               if (not1.out.changes[n].name.private_length !=
+                                   not2.out.changes[n].name.private_length) {
+                                       printf("Notify name length %d inconsistent %d %d\n", n,
+                                              not1.out.changes[n].name.private_length,
+                                              not2.out.changes[n].name.private_length);
+                                       return False;
+                               }
+                       }
+               }
+       }
+
+       ZERO_STRUCT(notifies);
+
+       return True;
+}
+
+
+#define GEN_COPY_PARM do { \
+       int i; \
+       for (i=1;i<NSERVERS;i++) { \
+               parm[i] = parm[0]; \
+       } \
+} while (0)
+
+#define GEN_CALL(call) do { \
+       int i; \
+       ZERO_STRUCT(oplocks); \
+       ZERO_STRUCT(notifies); \
+       for (i=0;i<NSERVERS;i++) { \
+               struct cli_tree *tree = servers[i].cli[instance]->tree; \
+               status[i] = call; \
+       } \
+       current_op.status = status[0]; \
+       for (i=1;i<NSERVERS;i++) { \
+               if (!compare_status(status[i], status[0])) { \
+                       printf("status different in %s - %s %s\n", #call, \
+                              nt_errstr(status[0]), nt_errstr(status[i])); \
+                       return False; \
+               } \
+       } \
+       if (!check_oplocks(#call)) return False; \
+       if (!check_notifies(#call)) return False; \
+       if (!NT_STATUS_IS_OK(status[0])) { \
+               return True; \
+       } \
+} while(0)
+
+#define ADD_HANDLE(name, field) do { \
+       uint16 fnums[NSERVERS]; \
+       int i; \
+       for (i=0;i<NSERVERS;i++) { \
+               fnums[i] = parm[i].field; \
+       } \
+       gen_add_handle(instance, name, fnums); \
+} while(0)
+
+#define REMOVE_HANDLE(field) do { \
+       uint16 fnums[NSERVERS]; \
+       int i; \
+       for (i=0;i<NSERVERS;i++) { \
+               fnums[i] = parm[i].field; \
+       } \
+       gen_remove_handle(instance, fnums); \
+} while(0)
+
+#define GEN_SET_FNUM(field) do { \
+       int i; \
+       for (i=0;i<NSERVERS;i++) { \
+               parm[i].field = gen_lookup_fnum(i, parm[i].field); \
+       } \
+} while(0)
+
+#define CHECK_EQUAL(field) do { \
+       if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \
+               printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
+                      (int)parm[0].field, (int)parm[1].field); \
+               return False; \
+       } \
+} while(0)
+
+#define CHECK_WSTR_EQUAL(field) do { \
+       if (strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \
+               printf("Mismatch in %s - %s %s\n", #field, \
+                      parm[0].field.s, parm[1].field.s); \
+               return False; \
+       } \
+       CHECK_EQUAL(field.private_length); \
+} while(0)
+
+#define CHECK_BLOB_EQUAL(field) do { \
+       if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \
+               printf("Mismatch in %s\n", #field); \
+               return False; \
+       } \
+       CHECK_EQUAL(field.length); \
+} while(0)
+
+#define CHECK_TIMES_EQUAL(field) do { \
+       if (ABS(parm[0].field - parm[1].field) > time_skew() && \
+           !ignore_pattern(#field)) { \
+               printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
+                      (int)parm[0].field, (int)parm[1].field); \
+               return False; \
+       } \
+} while(0)
+
+#define CHECK_NTTIMES_EQUAL(field) do { \
+       if (ABS(nt_time_to_unix(&parm[0].field) - \
+               nt_time_to_unix(&parm[1].field)) > time_skew() && \
+           !ignore_pattern(#field)) { \
+               printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
+                      (int)nt_time_to_unix(&parm[0].field), \
+                      (int)nt_time_to_unix(&parm[1].field)); \
+               return False; \
+       } \
+} while(0)
+
+/*
+  generate openx operations
+*/
+static BOOL handler_openx(int instance)
+{
+       union smb_open parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].openx.level = RAW_OPEN_OPENX;
+       parm[0].openx.in.flags = gen_openx_flags();
+       parm[0].openx.in.open_mode = gen_openx_mode();
+       parm[0].openx.in.search_attrs = gen_attrib();
+       parm[0].openx.in.file_attrs = gen_attrib();
+       parm[0].openx.in.write_time = gen_timet();
+       parm[0].openx.in.open_func = gen_openx_func();
+       parm[0].openx.in.size = gen_io_count();
+       parm[0].openx.in.timeout = gen_timeout();
+       parm[0].openx.in.fname = gen_fname_open(instance);
+
+       if (!options.use_oplocks) {
+               /* mask out oplocks */
+               parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
+                                           OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
+       }
+       
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
+
+       CHECK_EQUAL(openx.out.attrib);
+       CHECK_EQUAL(openx.out.size);
+       CHECK_EQUAL(openx.out.access);
+       CHECK_EQUAL(openx.out.ftype);
+       CHECK_EQUAL(openx.out.devstate);
+       CHECK_EQUAL(openx.out.action);
+       CHECK_EQUAL(openx.out.access_mask);
+       CHECK_EQUAL(openx.out.unknown);
+       CHECK_TIMES_EQUAL(openx.out.write_time);
+
+       /* open creates a new file handle */
+       ADD_HANDLE(parm[0].openx.in.fname, openx.out.fnum);
+
+       return True;
+}
+
+
+/*
+  generate ntcreatex operations
+*/
+static BOOL handler_ntcreatex(int instance)
+{
+       union smb_open parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX;
+       parm[0].ntcreatex.in.flags = gen_ntcreatex_flags();
+       parm[0].ntcreatex.in.root_fid = gen_root_fid(instance);
+       parm[0].ntcreatex.in.access_mask = gen_access_mask();
+       parm[0].ntcreatex.in.alloc_size = gen_alloc_size();
+       parm[0].ntcreatex.in.file_attr = gen_attrib();
+       parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF);
+       parm[0].ntcreatex.in.open_disposition = gen_open_disp();
+       parm[0].ntcreatex.in.create_options = gen_create_options();
+       parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF);
+       parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF);
+       parm[0].ntcreatex.in.fname = gen_fname_open(instance);
+
+       if (!options.use_oplocks) {
+               /* mask out oplocks */
+               parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK|
+                                               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK);
+       }
+       
+       GEN_COPY_PARM;
+       if (parm[0].ntcreatex.in.root_fid != 0) {
+               GEN_SET_FNUM(ntcreatex.in.root_fid);
+       }
+       GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
+
+       CHECK_EQUAL(ntcreatex.out.oplock_level);
+       CHECK_EQUAL(ntcreatex.out.create_action);
+       CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time);
+       CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time);
+       CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time);
+       CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time);
+       CHECK_EQUAL(ntcreatex.out.attrib);
+       CHECK_EQUAL(ntcreatex.out.alloc_size);
+       CHECK_EQUAL(ntcreatex.out.size);
+       CHECK_EQUAL(ntcreatex.out.file_type);
+       CHECK_EQUAL(ntcreatex.out.ipc_state);
+       CHECK_EQUAL(ntcreatex.out.is_directory);
+
+       /* ntcreatex creates a new file handle */
+       ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.fnum);
+
+       return True;
+}
+
+/*
+  generate close operations
+*/
+static BOOL handler_close(int instance)
+{
+       union smb_close parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].close.level = RAW_CLOSE_CLOSE;
+       parm[0].close.in.fnum = gen_fnum_close(instance);
+       parm[0].close.in.write_time = gen_timet();
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(close.in.fnum);
+       GEN_CALL(smb_raw_close(tree, &parm[i]));
+
+       REMOVE_HANDLE(close.in.fnum);
+
+       return True;
+}
+
+/*
+  generate unlink operations
+*/
+static BOOL handler_unlink(int instance)
+{
+       struct smb_unlink parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].in.pattern = gen_pattern();
+       parm[0].in.attrib = gen_attrib();
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_unlink(tree, &parm[i]));
+
+       return True;
+}
+
+/*
+  generate chkpath operations
+*/
+static BOOL handler_chkpath(int instance)
+{
+       struct smb_chkpath parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].in.path = gen_fname_open(instance);
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_chkpath(tree, &parm[i]));
+
+       return True;
+}
+
+/*
+  generate mkdir operations
+*/
+static BOOL handler_mkdir(int instance)
+{
+       union smb_mkdir parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].mkdir.level = RAW_MKDIR_MKDIR;
+       parm[0].mkdir.in.path = gen_fname_open(instance);
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_mkdir(tree, &parm[i]));
+
+       return True;
+}
+
+/*
+  generate rmdir operations
+*/
+static BOOL handler_rmdir(int instance)
+{
+       struct smb_rmdir parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].in.path = gen_fname_open(instance);
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_rmdir(tree, &parm[i]));
+
+       return True;
+}
+
+/*
+  generate rename operations
+*/
+static BOOL handler_rename(int instance)
+{
+       struct smb_rename parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].in.pattern1 = gen_pattern();
+       parm[0].in.pattern2 = gen_pattern();
+       parm[0].in.attrib = gen_attrib();
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_rename(tree, &parm[i]));
+
+       return True;
+}
+
+
+/*
+  generate readx operations
+*/
+static BOOL handler_readx(int instance)
+{
+       union smb_read parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].readx.level = RAW_READ_READX;
+       parm[0].readx.in.fnum = gen_fnum(instance);
+       parm[0].readx.in.offset = gen_offset();
+       parm[0].readx.in.mincnt = gen_io_count();
+       parm[0].readx.in.maxcnt = gen_io_count();
+       parm[0].readx.in.remaining = gen_io_count();
+       parm[0].readx.out.data = talloc(current_op.mem_ctx,
+                                       MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt));
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(readx.in.fnum);
+       GEN_CALL(smb_raw_read(tree, &parm[i]));
+
+       CHECK_EQUAL(readx.out.remaining);
+       CHECK_EQUAL(readx.out.compaction_mode);
+       CHECK_EQUAL(readx.out.nread);
+
+       return True;
+}
+
+/*
+  generate writex operations
+*/
+static BOOL handler_writex(int instance)
+{
+       union smb_write parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].writex.level = RAW_WRITE_WRITEX;
+       parm[0].writex.in.fnum = gen_fnum(instance);
+       parm[0].writex.in.offset = gen_offset();
+       parm[0].writex.in.wmode = gen_bits_mask(0xFFFF);
+       parm[0].writex.in.remaining = gen_io_count();
+       parm[0].writex.in.count = gen_io_count();
+       parm[0].writex.in.data = talloc_zero(current_op.mem_ctx, parm[0].writex.in.count);
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(writex.in.fnum);
+       GEN_CALL(smb_raw_write(tree, &parm[i]));
+
+       CHECK_EQUAL(writex.out.nwritten);
+       CHECK_EQUAL(writex.out.remaining);
+
+       return True;
+}
+
+/*
+  generate lockingx operations
+*/
+static BOOL handler_lockingx(int instance)
+{
+       union smb_lock parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+       int n, nlocks;
+
+       parm[0].lockx.level = RAW_LOCK_LOCKX;
+       parm[0].lockx.in.fnum = gen_fnum(instance);
+       parm[0].lockx.in.mode = gen_lock_mode();
+       parm[0].lockx.in.timeout = gen_timeout();
+       do {
+               /* make sure we don't accidentially generate an oplock
+                  break ack - otherwise the server can just block forever */
+               parm[0].lockx.in.ulock_cnt = gen_lock_count();
+               parm[0].lockx.in.lock_cnt = gen_lock_count();
+               nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt;
+       } while (nlocks == 0);
+
+       if (nlocks > 0) {
+               parm[0].lockx.in.locks = talloc(current_op.mem_ctx,
+                                               sizeof(parm[0].lockx.in.locks[0]) * nlocks);
+               for (n=0;n<nlocks;n++) {
+                       parm[0].lockx.in.locks[n].pid = gen_pid();
+                       parm[0].lockx.in.locks[n].offset = gen_offset();
+                       parm[0].lockx.in.locks[n].count = gen_io_count();
+               }
+       }
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(lockx.in.fnum);
+       GEN_CALL(smb_raw_lock(tree, &parm[i]));
+
+       return True;
+}
+
+/*
+  generate a fileinfo query structure
+*/
+static void gen_fileinfo(int instance, union smb_fileinfo *info)
+{
+       int i;
+       #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v}
+       struct {
+               enum fileinfo_level level;
+               const char *name;
+       }  levels[] = {
+               LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD),
+               LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID),
+               LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO),
+               LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO),
+               LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION),
+               LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION),
+               LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION),
+               LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION),
+               LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION),
+               LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION)
+       };
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(levels)-1);
+       } while (ignore_pattern(levels[i].name));
+
+       info->generic.level = levels[i].level;
+}
+
+/*
+  compare returned fileinfo structures
+*/
+static BOOL cmp_fileinfo(int instance, 
+                        union smb_fileinfo parm[NSERVERS],
+                        NTSTATUS status[NSERVERS])
+{
+       int i;
+
+       switch (parm[0].generic.level) {
+       case RAW_FILEINFO_GENERIC:
+               return False;
+
+       case RAW_FILEINFO_GETATTR:
+               CHECK_EQUAL(getattr.out.attrib);
+               CHECK_EQUAL(getattr.out.size);
+               CHECK_TIMES_EQUAL(getattr.out.write_time);
+               break;
+
+       case RAW_FILEINFO_GETATTRE:
+               CHECK_TIMES_EQUAL(getattre.out.create_time);
+               CHECK_TIMES_EQUAL(getattre.out.access_time);
+               CHECK_TIMES_EQUAL(getattre.out.write_time);
+               CHECK_EQUAL(getattre.out.size);
+               CHECK_EQUAL(getattre.out.alloc_size);
+               CHECK_EQUAL(getattre.out.attrib);
+               break;
+
+       case RAW_FILEINFO_STANDARD:
+               CHECK_TIMES_EQUAL(standard.out.create_time);
+               CHECK_TIMES_EQUAL(standard.out.access_time);
+               CHECK_TIMES_EQUAL(standard.out.write_time);
+               CHECK_EQUAL(standard.out.size);
+               CHECK_EQUAL(standard.out.alloc_size);
+               CHECK_EQUAL(standard.out.attrib);
+               break;
+
+       case RAW_FILEINFO_EA_SIZE:
+               CHECK_TIMES_EQUAL(ea_size.out.create_time);
+               CHECK_TIMES_EQUAL(ea_size.out.access_time);
+               CHECK_TIMES_EQUAL(ea_size.out.write_time);
+               CHECK_EQUAL(ea_size.out.size);
+               CHECK_EQUAL(ea_size.out.alloc_size);
+               CHECK_EQUAL(ea_size.out.attrib);
+               CHECK_EQUAL(ea_size.out.ea_size);
+               break;
+
+       case RAW_FILEINFO_ALL_EAS:
+               CHECK_EQUAL(all_eas.out.num_eas);
+               for (i=0;i<parm[0].all_eas.out.num_eas;i++) {
+                       CHECK_EQUAL(all_eas.out.eas[i].flags);
+                       CHECK_WSTR_EQUAL(all_eas.out.eas[i].name);
+                       CHECK_BLOB_EQUAL(all_eas.out.eas[i].value);
+               }
+               break;
+
+       case RAW_FILEINFO_IS_NAME_VALID:
+               break;
+               
+       case RAW_FILEINFO_BASIC_INFO:
+       case RAW_FILEINFO_BASIC_INFORMATION:
+               CHECK_NTTIMES_EQUAL(basic_info.out.create_time);
+               CHECK_NTTIMES_EQUAL(basic_info.out.access_time);
+               CHECK_NTTIMES_EQUAL(basic_info.out.write_time);
+               CHECK_NTTIMES_EQUAL(basic_info.out.change_time);
+               CHECK_EQUAL(basic_info.out.attrib);
+               break;
+
+       case RAW_FILEINFO_STANDARD_INFO:
+       case RAW_FILEINFO_STANDARD_INFORMATION:
+               CHECK_EQUAL(standard_info.out.alloc_size);
+               CHECK_EQUAL(standard_info.out.size);
+               CHECK_EQUAL(standard_info.out.nlink);
+               CHECK_EQUAL(standard_info.out.delete_pending);
+               CHECK_EQUAL(standard_info.out.directory);
+               break;
+
+       case RAW_FILEINFO_EA_INFO:
+       case RAW_FILEINFO_EA_INFORMATION:
+               CHECK_EQUAL(ea_info.out.ea_size);
+               break;
+
+       case RAW_FILEINFO_NAME_INFO:
+       case RAW_FILEINFO_NAME_INFORMATION:
+               CHECK_WSTR_EQUAL(name_info.out.fname);
+               break;
+
+       case RAW_FILEINFO_ALL_INFO:
+       case RAW_FILEINFO_ALL_INFORMATION:
+               CHECK_NTTIMES_EQUAL(all_info.out.create_time);
+               CHECK_NTTIMES_EQUAL(all_info.out.access_time);
+               CHECK_NTTIMES_EQUAL(all_info.out.write_time);
+               CHECK_NTTIMES_EQUAL(all_info.out.change_time);
+               CHECK_EQUAL(all_info.out.attrib);
+               CHECK_EQUAL(all_info.out.alloc_size);
+               CHECK_EQUAL(all_info.out.size);
+               CHECK_EQUAL(all_info.out.nlink);
+               CHECK_EQUAL(all_info.out.delete_pending);
+               CHECK_EQUAL(all_info.out.directory);
+               CHECK_EQUAL(all_info.out.ea_size);
+               CHECK_WSTR_EQUAL(all_info.out.fname);
+               break;
+
+       case RAW_FILEINFO_ALT_NAME_INFO:
+       case RAW_FILEINFO_ALT_NAME_INFORMATION:
+               CHECK_WSTR_EQUAL(alt_name_info.out.fname);
+               break;
+
+       case RAW_FILEINFO_STREAM_INFO:
+       case RAW_FILEINFO_STREAM_INFORMATION:
+               CHECK_EQUAL(stream_info.out.num_streams);
+               for (i=0;i<parm[0].stream_info.out.num_streams;i++) {
+                       CHECK_EQUAL(stream_info.out.streams[i].size);
+                       CHECK_EQUAL(stream_info.out.streams[i].alloc_size);
+                       CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name);
+               }
+               break;
+
+       case RAW_FILEINFO_COMPRESSION_INFO:
+       case RAW_FILEINFO_COMPRESSION_INFORMATION:
+               CHECK_EQUAL(compression_info.out.compressed_size);
+               CHECK_EQUAL(compression_info.out.format);
+               CHECK_EQUAL(compression_info.out.unit_shift);
+               CHECK_EQUAL(compression_info.out.chunk_shift);
+               CHECK_EQUAL(compression_info.out.cluster_shift);
+               break;
+
+       case RAW_FILEINFO_INTERNAL_INFORMATION:
+               CHECK_EQUAL(internal_information.out.device);
+               CHECK_EQUAL(internal_information.out.inode);
+               break;
+
+       case RAW_FILEINFO_ACCESS_INFORMATION:
+               CHECK_EQUAL(access_information.out.access_flags);
+               break;
+
+       case RAW_FILEINFO_POSITION_INFORMATION:
+               CHECK_EQUAL(position_information.out.position);
+               break;
+
+       case RAW_FILEINFO_MODE_INFORMATION:
+               CHECK_EQUAL(mode_information.out.mode);
+               break;
+
+       case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+               CHECK_EQUAL(alignment_information.out.alignment_requirement);
+               break;
+
+       case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+               CHECK_NTTIMES_EQUAL(network_open_information.out.create_time);
+               CHECK_NTTIMES_EQUAL(network_open_information.out.access_time);
+               CHECK_NTTIMES_EQUAL(network_open_information.out.write_time);
+               CHECK_NTTIMES_EQUAL(network_open_information.out.change_time);
+               CHECK_EQUAL(network_open_information.out.alloc_size);
+               CHECK_EQUAL(network_open_information.out.size);
+               CHECK_EQUAL(network_open_information.out.attrib);
+               break;
+
+       case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+               CHECK_EQUAL(attribute_tag_information.out.attrib);
+               CHECK_EQUAL(attribute_tag_information.out.reparse_tag);
+               break;
+       }
+
+       return True;
+}
+
+/*
+  generate qpathinfo operations
+*/
+static BOOL handler_qpathinfo(int instance)
+{
+       union smb_fileinfo parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].generic.in.fname = gen_fname_open(instance);
+
+       gen_fileinfo(instance, &parm[0]);
+
+       GEN_COPY_PARM;
+       GEN_CALL(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i]));
+
+       return cmp_fileinfo(instance, parm, status);
+}
+
+/*
+  generate qfileinfo operations
+*/
+static BOOL handler_qfileinfo(int instance)
+{
+       union smb_fileinfo parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].generic.in.fnum = gen_fnum(instance);
+
+       gen_fileinfo(instance, &parm[0]);
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(generic.in.fnum);
+       GEN_CALL(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i]));
+
+       return cmp_fileinfo(instance, parm, status);
+}
+
+
+/*
+  generate a fileinfo query structure
+*/
+static void gen_setfileinfo(int instance, union smb_setfileinfo *info)
+{
+       int i;
+       #undef LVL
+       #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v}
+       struct {
+               enum setfileinfo_level level;
+               const char *name;
+       }  levels[] = {
+#if 0
+               /* disabled until win2003 can handle them ... */
+               LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), 
+               LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), 
+#endif
+               LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION),
+               LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), 
+               LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION),
+               LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), 
+               LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040)
+       };
+       do {
+               i = gen_int_range(0, ARRAY_SIZE(levels)-1);
+       } while (ignore_pattern(levels[i].name));
+
+       info->generic.level = levels[i].level;
+
+       switch (info->generic.level) {
+       case RAW_SFILEINFO_SETATTR:
+               info->setattr.in.attrib = gen_attrib();
+               info->setattr.in.write_time = gen_timet();
+               break;
+       case RAW_SFILEINFO_SETATTRE:
+               info->setattre.in.create_time = gen_timet();
+               info->setattre.in.access_time = gen_timet();
+               info->setattre.in.write_time = gen_timet();
+               break;
+       case RAW_SFILEINFO_STANDARD:
+               info->standard.in.create_time = gen_timet();
+               info->standard.in.access_time = gen_timet();
+               info->standard.in.write_time = gen_timet();
+               break;
+       case RAW_SFILEINFO_EA_SET:
+               info->ea_set.in.ea = gen_ea_struct();
+               break;
+       case RAW_SFILEINFO_BASIC_INFO:
+       case RAW_SFILEINFO_BASIC_INFORMATION:
+               info->basic_info.in.create_time = gen_nttime();
+               info->basic_info.in.access_time = gen_nttime();
+               info->basic_info.in.write_time = gen_nttime();
+               info->basic_info.in.change_time = gen_nttime();
+               info->basic_info.in.attrib = gen_attrib();
+               break;
+       case RAW_SFILEINFO_DISPOSITION_INFO:
+       case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+               info->disposition_info.in.delete_on_close = gen_bool();
+               break;
+       case RAW_SFILEINFO_ALLOCATION_INFO:
+       case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               info->allocation_info.in.alloc_size = gen_alloc_size();
+               break;
+       case RAW_SFILEINFO_END_OF_FILE_INFO:
+       case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               info->end_of_file_info.in.size = gen_offset();
+               break;
+       case RAW_SFILEINFO_RENAME_INFORMATION:
+               info->rename_information.in.overwrite = gen_bool();
+               info->rename_information.in.root_fid = gen_root_fid(instance);
+               info->rename_information.in.new_name = gen_fname_open(instance);
+               break;
+       case RAW_SFILEINFO_POSITION_INFORMATION:
+               info->position_information.in.position = gen_offset();
+               break;
+       case RAW_SFILEINFO_MODE_INFORMATION:
+               info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF);
+               break;
+       }
+}
+
+/*
+  generate setpathinfo operations
+*/
+static BOOL handler_spathinfo(int instance)
+{
+       union smb_setfileinfo parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].generic.file.fname = gen_fname_open(instance);
+
+       gen_setfileinfo(instance, &parm[0]);
+
+       GEN_COPY_PARM;
+
+       /* a special case for the fid in a RENAME */
+       if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION &&
+           parm[0].rename_information.in.root_fid != 0) {
+               GEN_SET_FNUM(rename_information.in.root_fid);
+       }
+
+       GEN_CALL(smb_raw_setpathinfo(tree, &parm[i]));
+
+       return True;
+}
+
+
+/*
+  generate setfileinfo operations
+*/
+static BOOL handler_sfileinfo(int instance)
+{
+       union smb_setfileinfo parm[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       parm[0].generic.file.fnum = gen_fnum(instance);
+
+       gen_setfileinfo(instance, &parm[0]);
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(generic.file.fnum);
+       GEN_CALL(smb_raw_setfileinfo(tree, &parm[i]));
+
+       return True;
+}
+
+
+/*
+  generate change notify operations
+*/
+static BOOL handler_notify(int instance)
+{
+       struct smb_notify parm[NSERVERS];
+       int n;
+
+       parm[0].in.buffer_size = gen_io_count();
+       parm[0].in.completion_filter = gen_bits_mask(0xFF);
+       parm[0].in.fnum = gen_fnum(instance);
+       parm[0].in.recursive = gen_bool();
+
+       GEN_COPY_PARM;
+       GEN_SET_FNUM(in.fnum);
+
+       for (n=0;n<NSERVERS;n++) {
+               struct cli_request *req;
+               req = smb_raw_changenotify_send(servers[n].cli[instance]->tree, &parm[n]);
+               req->async.fn = async_notify;
+       }
+
+       return True;
+}
+
+/*
+  wipe any relevant files
+*/
+static void wipe_files(void)
+{
+       int i;
+       for (i=0;i<NSERVERS;i++) {
+               int n = cli_deltree(servers[i].cli[0], "\\gentest");
+               if (n == -1) {
+                       printf("Failed to wipe tree on server %d\n", i);
+                       exit(1);
+               }
+               if (!cli_mkdir(servers[i].cli[0], "\\gentest")) {
+                       printf("Failed to create \\gentest - %s\n",
+                              cli_errstr(servers[i].cli[0]));
+                       exit(1);
+               }
+               if (n > 0) {
+                       printf("Deleted %d files on server %d\n", n, i);
+               }
+       }
+}
+
+/*
+  dump the current seeds - useful for continuing a backtrack
+*/
+static void dump_seeds(void)
+{
+       int i;
+       FILE *f;
+
+       if (!options.seeds_file) {
+               return;
+       }
+       f = fopen("seeds.tmp", "w");
+       if (!f) return;
+
+       for (i=0;i<options.numops;i++) {
+               fprintf(f, "%u\n", op_parms[i].seed);
+       }
+       fclose(f);
+       rename("seeds.tmp", options.seeds_file);
+}
+
+
+
+/*
+  the list of top-level operations that we will generate
+*/
+static struct {
+       const char *name;
+       BOOL (*handler)(int instance);
+       int count, success_count;
+} gen_ops[] = {
+       {"OPENX",      handler_openx},
+       {"NTCREATEX",  handler_ntcreatex},
+       {"CLOSE",      handler_close},
+       {"UNLINK",     handler_unlink},
+       {"MKDIR",      handler_mkdir},
+       {"RMDIR",      handler_rmdir},
+       {"RENAME",     handler_rename},
+       {"READX",      handler_readx},
+       {"WRITEX",     handler_writex},
+       {"CHKPATH",    handler_chkpath},
+       {"LOCKINGX",   handler_lockingx},
+       {"QPATHINFO",  handler_qpathinfo},
+       {"QFILEINFO",  handler_qfileinfo},
+       {"SPATHINFO",  handler_spathinfo},
+       {"SFILEINFO",  handler_sfileinfo},
+       {"NOTIFY",     handler_notify},
+};
+
+
+/*
+  run the test with the current set of op_parms parameters
+  return the number of operations that completed successfully
+*/
+static int run_test(void)
+{
+       int op, i;
+
+       if (!connect_servers()) {
+               printf("Failed to connect to servers\n");
+               exit(1);
+       }
+
+       dump_seeds();
+
+       /* wipe any leftover files from old runs */
+       wipe_files();
+
+       /* reset the open handles array */
+       memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0]));
+       num_open_handles = 0;
+
+       for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
+               gen_ops[i].count = 0;
+               gen_ops[i].success_count = 0;
+       }
+
+       for (op=0; op<options.numops; op++) {
+               int instance, which_op;
+               BOOL ret;
+
+               if (op_parms[op].disabled) continue;
+
+               srandom(op_parms[op].seed);
+
+               instance = gen_int_range(0, NINSTANCES-1);
+
+               /* generate a non-ignored operation */
+               do {
+                       which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1);
+               } while (ignore_pattern(gen_ops[which_op].name));
+
+               DEBUG(3,("Generating op %s on instance %d\n",
+                        gen_ops[which_op].name, instance));
+
+               current_op.seed = op_parms[op].seed;
+               current_op.opnum = op;
+               current_op.name = gen_ops[which_op].name;
+               current_op.status = NT_STATUS_OK;
+               current_op.mem_ctx = talloc_init(current_op.name);
+
+               ret = gen_ops[which_op].handler(instance);
+
+               talloc_destroy(current_op.mem_ctx);
+
+               gen_ops[which_op].count++;
+               if (NT_STATUS_IS_OK(current_op.status)) {
+                       gen_ops[which_op].success_count++;                      
+               }
+
+               if (!ret) {
+                       printf("Failed at operation %d - %s\n",
+                              op, gen_ops[which_op].name);
+                       return op;
+               }
+
+               if (op % 100 == 0) {
+                       printf("%d\n", op);
+               }
+       }
+
+       for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
+               printf("Op %-10s got %d/%d success\n", 
+                      gen_ops[i].name,
+                      gen_ops[i].success_count,
+                      gen_ops[i].count);
+       }
+
+       return op;
+}
+
+/* 
+   perform a backtracking analysis of the minimal set of operations
+   to generate an error
+*/
+static void backtrack_analyze(void)
+{
+       int chunk, ret;
+
+       chunk = options.numops / 2;
+
+       do {
+               int base;
+               for (base=0; 
+                    chunk > 0 && base+chunk < options.numops && options.numops > 1; ) {
+                       int i, max;
+
+                       chunk = MIN(chunk, options.numops / 2);
+
+                       /* mark this range as disabled */
+                       max = MIN(options.numops, base+chunk);
+                       for (i=base;i<max; i++) {
+                               op_parms[i].disabled = True;
+                       }
+                       printf("Testing %d ops with %d-%d disabled\n", 
+                              options.numops, base, max-1);
+                       ret = run_test();
+                       printf("Completed %d of %d ops\n", ret, options.numops);
+                       for (i=base;i<max; i++) {
+                               op_parms[i].disabled = False;
+                       }
+                       if (ret == options.numops) {
+                               /* this chunk is needed */
+                               base += chunk;
+                       } else if (ret < base) {
+                               printf("damn - inconsistent errors! found early error\n");
+                               options.numops = ret+1;
+                               base = 0;
+                       } else {
+                               /* it failed - this chunk isn't needed for a failure */
+                               memmove(&op_parms[base], &op_parms[max], 
+                                       sizeof(op_parms[0]) * (options.numops - max));
+                               options.numops = (ret+1) - (max - base);
+                       }
+               }
+
+               if (chunk == 2) {
+                       chunk = 1;
+               } else {
+                       chunk *= 0.4;
+               }
+
+               if (options.analyze_continuous && chunk == 0 && options.numops != 1) {
+                       chunk = 1;
+               }
+       } while (chunk > 0);
+
+       printf("Reduced to %d ops\n", options.numops);
+       ret = run_test();
+       if (ret != options.numops - 1) {
+               printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops);
+       }
+}
+
+/* 
+   start the main gentest process
+*/
+static BOOL start_gentest(void)
+{
+       int op;
+       int ret;
+
+       /* allocate the open_handles array */
+       open_handles = calloc(options.max_open_handles, sizeof(open_handles[0]));
+
+       srandom(options.seed);
+       op_parms = calloc(options.numops, sizeof(op_parms[0]));
+
+       /* generate the seeds - after this everything is deterministic */
+       if (options.use_preset_seeds) {
+               int numops;
+               char **preset = file_lines_load(options.seeds_file, &numops);
+               if (!preset) {
+                       printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno));
+                       exit(1);
+               }
+               if (numops < options.numops) {
+                       options.numops = numops;
+               }
+               for (op=0;op<options.numops;op++) {
+                       if (!preset[op]) {
+                               printf("Not enough seeds in %s\n", options.seeds_file);
+                               exit(1);
+                       }
+                       op_parms[op].seed = atoi(preset[op]);
+               }
+               printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file);
+       } else {
+               for (op=0; op<options.numops; op++) {
+                       op_parms[op].seed = random();
+               }
+       }
+
+       ret = run_test();
+
+       if (ret != options.numops && options.analyze) {
+               options.numops = ret+1;
+               backtrack_analyze();
+       } else if (options.analyze_always) {
+               backtrack_analyze();
+       } else if (options.analyze_continuous) {
+               while (run_test() == options.numops) ;
+       }
+
+       return ret == options.numops;
+}
+
+
+static void usage(void)
+{
+       printf(
+"Usage:\n\
+  gentest2 //server1/share1 //server2/share2 [options..]\n\
+  options:\n\
+        -U user%%pass        (must be specified twice)\n\
+        -s seed\n\
+        -o numops\n\
+        -a            (show all ops)\n\
+        -A            backtrack to find minimal ops\n\
+        -i FILE       add a list of wildcard exclusions\n\
+        -O            enable oplocks\n\
+        -S FILE       set preset seeds file\n\
+        -L            use preset seeds\n\
+        -F            fast reconnect (just close files)\n\
+        -C            continuous analysis mode\n\
+        -X            analyse even when test OK\n\
+");
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc, char *argv[])
+{
+       int opt;
+       int i;
+       BOOL ret;
+
+       setlinebuf(stdout);
+
+       setup_logging("gentest", DEBUG_STDOUT);
+
+       if (argc < 3 || argv[1][0] == '-') {
+               usage();
+               exit(1);
+       }
+
+       setup_logging(argv[0],True);
+
+       for (i=0;i<NSERVERS;i++) {
+               const char *share = argv[1+i];
+               if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) {
+                       printf("Invalid share name '%s'\n", share);
+                       return -1;
+               }
+       }
+
+       argc -= NSERVERS;
+       argv += NSERVERS;
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       options.seed = time(NULL);
+       options.numops = 1000;
+       options.max_open_handles = 20;
+       options.seeds_file = "gentest_seeds.dat";
+
+       while ((opt = getopt(argc, argv, "U:s:o:ad:i:AOhS:LFXC")) != EOF) {
+               switch (opt) {
+               case 'U':
+                       i = servers[0].username?1:0;
+                       if (!split_username(optarg, 
+                                           &servers[i].username, 
+                                           &servers[i].password)) {
+                               printf("Must supply USER%%PASS\n");
+                               return -1;
+                       }
+                       break;
+               case 'd':
+                       DEBUGLEVEL = atoi(optarg);
+                       setup_logging(NULL,True);
+                       break;
+               case 's':
+                       options.seed = atoi(optarg);
+                       break;
+               case 'S':
+                       options.seeds_file = optarg;
+                       break;
+               case 'L':
+                       options.use_preset_seeds = True;
+                       break;
+               case 'F':
+                       options.fast_reconnect = True;
+                       break;
+               case 'o':
+                       options.numops = atoi(optarg);
+                       break;
+               case 'O':
+                       options.use_oplocks = True;
+                       break;
+               case 'a':
+                       options.showall = True;
+                       break;
+               case 'A':
+                       options.analyze = True;
+                       break;
+               case 'X':
+                       options.analyze_always = True;
+                       break;
+               case 'C':
+                       options.analyze_continuous = True;
+                       break;
+               case 'i':
+                       options.ignore_patterns = file_lines_load(optarg, NULL);
+                       break;
+               case 'h':
+                       usage();
+                       exit(1);
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+       }
+
+       if (!servers[0].username || !servers[1].username) {
+               usage();
+               return -1;
+       }
+
+       printf("seed=%u\n", options.seed);
+
+       ret = start_gentest();
+
+       if (ret) {
+               printf("gentest completed - no errors\n");
+       } else {
+               printf("gentest failed\n");
+       }
+
+       return ret?0:-1;
+}
diff --git a/source4/torture/locktest.c b/source4/torture/locktest.c
new file mode 100644 (file)
index 0000000..26bf6d6
--- /dev/null
@@ -0,0 +1,566 @@
+/* 
+   Unix SMB/CIFS implementation.
+   randomised byte range lock tester
+   Copyright (C) Andrew Tridgell 1999
+   
+   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"
+
+static fstring password[2];
+static fstring username[2];
+static int got_user;
+static int got_pass;
+static BOOL use_kerberos;
+static int numops = 1000;
+static BOOL showall;
+static BOOL analyze;
+static BOOL hide_unlock_fails;
+static BOOL use_oplocks;
+static unsigned lock_range = 100;
+static unsigned lock_base = 0;
+static unsigned min_length = 0;
+static BOOL exact_error_codes;
+static BOOL zero_zero;
+
+#define FILENAME "\\locktest.dat"
+
+#define READ_PCT 50
+#define LOCK_PCT 45
+#define UNLOCK_PCT 70
+#define RANGE_MULTIPLE 1
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define NASTY_POSIX_LOCK_HACK 0
+
+enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN};
+
+struct record {
+       enum lock_op lock_op;
+       enum brl_type lock_type;
+       char conn, f;
+       SMB_BIG_UINT start, len;
+       char needed;
+};
+
+#define PRESETS 0
+
+#if PRESETS
+static struct record preset[] = {
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 3, 0, 1},
+{OP_UNLOCK, 0       , 0, 0, 2, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 3, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
+{OP_LOCK, READ_LOCK, 0, 0, 3, 1, 1},
+{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
+{OP_REOPEN, 0, 0, 0, 0, 0, 1},
+
+};
+#endif
+
+static struct record *recorded;
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share, int snum)
+{
+       struct cli_state *c;
+       fstring server, myname;
+       uint_t flags = 0;
+       NTSTATUS status;
+
+       fstrcpy(server,share+2);
+       share = strchr_m(server,'\\');
+       if (!share) return NULL;
+       *share = 0;
+       share++;
+
+       slprintf(myname,sizeof(myname), "lock-%u-%u", getpid(), snum);
+
+       if (use_kerberos)
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+       
+       status = cli_full_connection(&c, myname,
+                                    server, NULL,  
+                                    share, "?????", 
+                                    username[snum], lp_workgroup(), 
+                                    password[snum], flags, NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return NULL;
+       }
+
+       return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES],
+                     char *share[NSERVERS])
+{
+       int server, conn, f;
+
+       for (server=0;server<NSERVERS;server++)
+       for (conn=0;conn<NCONNECTIONS;conn++) {
+               if (cli[server][conn]) {
+                       for (f=0;f<NFILES;f++) {
+                               if (fnum[server][conn][f] != -1) {
+                                       cli_close(cli[server][conn], fnum[server][conn][f]);
+                                       fnum[server][conn][f] = -1;
+                               }
+                       }
+                       cli_shutdown(cli[server][conn]);
+               }
+               cli[server][conn] = connect_one(share[server], server);
+               if (!cli[server][conn]) {
+                       DEBUG(0,("Failed to connect to %s\n", share[server]));
+                       exit(1);
+               }
+       }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                    int fnum[NSERVERS][NCONNECTIONS][NFILES],
+                    struct record *rec)
+{
+       unsigned conn = rec->conn;
+       unsigned f = rec->f;
+       SMB_BIG_UINT start = rec->start;
+       SMB_BIG_UINT len = rec->len;
+       enum brl_type op = rec->lock_type;
+       int server;
+       BOOL ret[NSERVERS];
+       NTSTATUS status[NSERVERS];
+
+       switch (rec->lock_op) {
+       case OP_LOCK:
+               /* set a lock */
+               for (server=0;server<NSERVERS;server++) {
+                       ret[server] = cli_lock64(cli[server][conn], 
+                                                fnum[server][conn][f],
+                                                start, len, LOCK_TIMEOUT, op);
+                       status[server] = cli_nt_error(cli[server][conn]);
+                       if (!exact_error_codes && 
+                           NT_STATUS_EQUAL(status[server], 
+                                           NT_STATUS_FILE_LOCK_CONFLICT)) {
+                               status[server] = NT_STATUS_LOCK_NOT_GRANTED;
+                       }
+               }
+               if (showall || !NT_STATUS_EQUAL(status[0],status[1])) {
+                       printf("lock   conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n",
+                              conn, f, 
+                              (double)start, (double)len,
+                              op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+                              nt_errstr(status[0]), nt_errstr(status[1]));
+               }
+               if (!NT_STATUS_EQUAL(status[0],status[1])) return False;
+               break;
+               
+       case OP_UNLOCK:
+               /* unset a lock */
+               for (server=0;server<NSERVERS;server++) {
+                       ret[server] = cli_unlock64(cli[server][conn], 
+                                                  fnum[server][conn][f],
+                                                  start, len);
+                       status[server] = cli_nt_error(cli[server][conn]);
+               }
+               if (showall || 
+                   (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) {
+                       printf("unlock conn=%u f=%u range=%.0f(%.0f)       -> %s:%s\n",
+                              conn, f, 
+                              (double)start, (double)len,
+                              nt_errstr(status[0]), nt_errstr(status[1]));
+               }
+               if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) 
+                       return False;
+               break;
+
+       case OP_REOPEN:
+               /* reopen the file */
+               for (server=0;server<NSERVERS;server++) {
+                       cli_close(cli[server][conn], fnum[server][conn][f]);
+                       fnum[server][conn][f] = -1;
+               }
+               for (server=0;server<NSERVERS;server++) {
+                       fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME,
+                                                        O_RDWR|O_CREAT,
+                                                        DENY_NONE);
+                       if (fnum[server][conn][f] == -1) {
+                               printf("failed to reopen on share%d\n", server);
+                               return False;
+                       }
+               }
+               if (showall) {
+                       printf("reopen conn=%u f=%u\n",
+                              conn, f);
+               }
+               break;
+       }
+
+       return True;
+}
+
+static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                       int fnum[NSERVERS][NCONNECTIONS][NFILES])
+{
+       int server, conn, f; 
+
+       for (server=0;server<NSERVERS;server++)
+       for (conn=0;conn<NCONNECTIONS;conn++)
+       for (f=0;f<NFILES;f++) {
+               if (fnum[server][conn][f] != -1) {
+                       cli_close(cli[server][conn], fnum[server][conn][f]);
+                       fnum[server][conn][f] = -1;
+               }
+       }
+       for (server=0;server<NSERVERS;server++) {
+               cli_unlink(cli[server][0], FILENAME);
+       }
+}
+
+static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                      int fnum[NSERVERS][NCONNECTIONS][NFILES])
+{
+       int server, conn, f; 
+
+       for (server=0;server<NSERVERS;server++)
+       for (conn=0;conn<NCONNECTIONS;conn++)
+       for (f=0;f<NFILES;f++) {
+               fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME,
+                                                O_RDWR|O_CREAT,
+                                                DENY_NONE);
+               if (fnum[server][conn][f] == -1) {
+                       fprintf(stderr,"Failed to open fnum[%u][%u][%u]\n",
+                               server, conn, f);
+                       exit(1);
+               }
+       }
+}
+
+
+static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                  int fnum[NSERVERS][NCONNECTIONS][NFILES],
+                  int n)
+{
+       int i;
+       printf("testing %u ...\n", n);
+       for (i=0; i<n; i++) {
+               if (i && i % 100 == 0) {
+                       printf("%u\n", i);
+               }
+
+               if (recorded[i].needed &&
+                   !test_one(cli, fnum, &recorded[i])) return i;
+       }
+       return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+   descriptors open on the file - 8 file descriptors in total 
+
+   we then do random locking ops in tamdem on the 4 fnums from each
+   server and ensure that the results match
+ */
+static void test_locks(char *share[NSERVERS])
+{
+       struct cli_state *cli[NSERVERS][NCONNECTIONS];
+       int fnum[NSERVERS][NCONNECTIONS][NFILES];
+       int n, i, n1, skip, r1, r2; 
+
+       ZERO_STRUCT(fnum);
+       ZERO_STRUCT(cli);
+
+       recorded = (struct record *)malloc(sizeof(*recorded) * numops);
+
+       for (n=0; n<numops; n++) {
+#if PRESETS
+               if (n < sizeof(preset) / sizeof(preset[0])) {
+                       recorded[n] = preset[n];
+               } else {
+#endif
+                       recorded[n].conn = random() % NCONNECTIONS;
+                       recorded[n].f = random() % NFILES;
+                       recorded[n].start = lock_base + ((unsigned)random() % (lock_range-1));
+                       recorded[n].len =  min_length +
+                               random() % (lock_range-(recorded[n].start-lock_base));
+                       recorded[n].start *= RANGE_MULTIPLE;
+                       recorded[n].len *= RANGE_MULTIPLE;
+                       r1 = random() % 100;
+                       r2 = random() % 100;
+                       if (r1 < READ_PCT) {
+                               recorded[n].lock_type = READ_LOCK;
+                       } else {
+                               recorded[n].lock_type = WRITE_LOCK;
+                       }
+                       if (r2 < LOCK_PCT) {
+                               recorded[n].lock_op = OP_LOCK;
+                       } else if (r2 < UNLOCK_PCT) {
+                               recorded[n].lock_op = OP_UNLOCK;
+                       } else {
+                               recorded[n].lock_op = OP_REOPEN;
+                       }
+                       recorded[n].needed = True;
+                       if (!zero_zero && recorded[n].start==0 && recorded[n].len==0) {
+                               recorded[n].len = 1;
+                       }
+#if PRESETS
+               }
+#endif
+       }
+
+       reconnect(cli, fnum, share);
+       open_files(cli, fnum);
+       n = retest(cli, fnum, numops);
+
+       if (n == numops || !analyze) return;
+       n++;
+
+       skip = n/2;
+
+       while (1) {
+               n1 = n;
+
+               close_files(cli, fnum);
+               reconnect(cli, fnum, share);
+               open_files(cli, fnum);
+
+               for (i=0;i<n-skip;i+=skip) {
+                       int m, j;
+                       printf("excluding %d-%d\n", i, i+skip-1);
+                       for (j=i;j<i+skip;j++) {
+                               recorded[j].needed = False;
+                       }
+
+                       close_files(cli, fnum);
+                       open_files(cli, fnum);
+
+                       m = retest(cli, fnum, n);
+                       if (m == n) {
+                               for (j=i;j<i+skip;j++) {
+                                       recorded[j].needed = True;
+                               }
+                       } else {
+                               if (i+(skip-1) < m) {
+                                       memmove(&recorded[i], &recorded[i+skip],
+                                               (m-(i+skip-1))*sizeof(recorded[0]));
+                               }
+                               n = m-(skip-1);
+                               i--;
+                       }
+               }
+
+               if (skip > 1) {
+                       skip = skip/2;
+                       printf("skip=%d\n", skip);
+                       continue;
+               }
+
+               if (n1 == n) break;
+       }
+
+       close_files(cli, fnum);
+       reconnect(cli, fnum, share);
+       open_files(cli, fnum);
+       showall = True;
+       n1 = retest(cli, fnum, n);
+       if (n1 != n-1) {
+               printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+       }
+       close_files(cli, fnum);
+
+       for (i=0;i<n;i++) {
+               printf("{%d, %d, %u, %u, %.0f, %.0f, %u},\n",
+                      recorded[i].lock_op,
+                      recorded[i].lock_type,
+                      recorded[i].conn,
+                      recorded[i].f,
+                      (double)recorded[i].start,
+                      (double)recorded[i].len,
+                      recorded[i].needed);
+       }       
+}
+
+
+
+static void usage(void)
+{
+       printf(
+"Usage:\n\
+  locktest //server1/share1 //server2/share2 [options..]\n\
+  options:\n\
+        -U user%%pass        (may be specified twice)\n\
+        -k               use kerberos\n\
+        -s seed\n\
+        -o numops\n\
+        -u          hide unlock fails\n\
+        -a          (show all ops)\n\
+        -A          analyse for minimal ops\n\
+        -O          use oplocks\n\
+        -E          enable exact error code checking\n\
+        -Z          enable the zero/zero lock\n\
+        -R range    set lock range\n\
+        -B base     set lock base\n\
+        -M min      set min lock length\n\
+");
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       char *share[NSERVERS];
+       int opt;
+       char *p;
+       int seed, server;
+
+       setlinebuf(stdout);
+
+       setup_logging("locktest", DEBUG_STDOUT);
+
+       if (argc < 3 || argv[1][0] == '-') {
+               usage();
+               exit(1);
+       }
+
+       setup_logging(argv[0],True);
+
+       for (server=0;server<NSERVERS;server++) {
+               share[server] = argv[1+server];
+               all_string_sub(share[server],"/","\\",0);
+       }
+
+       argc -= NSERVERS;
+       argv += NSERVERS;
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (getenv("USER")) {
+               fstrcpy(username[0],getenv("USER"));
+               fstrcpy(username[1],getenv("USER"));
+       }
+
+       seed = time(NULL);
+
+       while ((opt = getopt(argc, argv, "U:s:ho:aAW:OkR:B:M:EZ")) != EOF) {
+               switch (opt) {
+               case 'k':
+#ifdef HAVE_KRB5
+                       use_kerberos = True;
+#else
+                       d_printf("No kerberos support compiled in\n");
+                       exit(1);
+#endif
+                       break;
+               case 'U':
+                       got_user = 1;
+                       if (got_pass == 2) {
+                               d_printf("Max of 2 usernames\n");
+                               exit(1);
+                       }
+                       fstrcpy(username[got_pass],optarg);
+                       p = strchr_m(username[got_pass],'%');
+                       if (p) {
+                               *p = 0;
+                               fstrcpy(password[got_pass], p+1);
+                               got_pass++;
+                       }
+                       break;
+               case 'R':
+                       lock_range = strtol(optarg, NULL, 0);
+                       break;
+               case 'B':
+                       lock_base = strtol(optarg, NULL, 0);
+                       break;
+               case 'M':
+                       min_length = strtol(optarg, NULL, 0);
+                       break;
+               case 's':
+                       seed = atoi(optarg);
+                       break;
+               case 'u':
+                       hide_unlock_fails = True;
+                       break;
+               case 'o':
+                       numops = atoi(optarg);
+                       break;
+               case 'O':
+                       use_oplocks = True;
+                       break;
+               case 'a':
+                       showall = True;
+                       break;
+               case 'A':
+                       analyze = True;
+                       break;
+               case 'Z':
+                       zero_zero = True;
+                       break;
+               case 'E':
+                       exact_error_codes = True;
+                       break;
+               case 'h':
+                       usage();
+                       exit(1);
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+       }
+
+       if(use_kerberos && !got_user) got_pass = True;
+
+       argc -= optind;
+       argv += optind;
+
+       DEBUG(0,("seed=%u base=%d range=%d min_length=%d\n", 
+                seed, lock_base, lock_range, min_length));
+       srandom(seed);
+
+       test_locks(share);
+
+       return(0);
+}
diff --git a/source4/torture/locktest2.c b/source4/torture/locktest2.c
new file mode 100644 (file)
index 0000000..17435af
--- /dev/null
@@ -0,0 +1,558 @@
+/* 
+   Unix SMB/CIFS implementation.
+   byte range lock tester - with local filesystem support
+   Copyright (C) Andrew Tridgell 1999
+   
+   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"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int numops = 1000;
+static BOOL showall;
+static BOOL analyze;
+static BOOL hide_unlock_fails;
+static BOOL use_oplocks;
+
+#define FILENAME "\\locktest.dat"
+#define LOCKRANGE 100
+#define LOCKBASE 0
+
+/*
+#define LOCKBASE (0x40000000 - 50)
+*/
+
+#define READ_PCT 50
+#define LOCK_PCT 25
+#define UNLOCK_PCT 65
+#define RANGE_MULTIPLE 1
+
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NUMFSTYPES 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define FSTYPE_SMB 0
+#define FSTYPE_NFS 1
+
+struct record {
+       char r1, r2;
+       char conn, f, fstype;
+       unsigned start, len;
+       char needed;
+};
+
+static struct record *recorded;
+
+static int try_open(struct cli_state *c, char *nfs, int fstype, const char *fname, int flags)
+{
+       pstring path;
+
+       switch (fstype) {
+       case FSTYPE_SMB:
+               return cli_open(c, fname, flags, DENY_NONE);
+
+       case FSTYPE_NFS:
+               slprintf(path, sizeof(path), "%s%s", nfs, fname);
+               pstring_sub(path,"\\", "/");
+               return open(path, flags, 0666);
+       }
+
+       return -1;
+}
+
+static BOOL try_close(struct cli_state *c, int fstype, int fd)
+{
+       switch (fstype) {
+       case FSTYPE_SMB:
+               return cli_close(c, fd);
+
+       case FSTYPE_NFS:
+               return close(fd) == 0;
+       }
+
+       return False;
+}
+
+static BOOL try_lock(struct cli_state *c, int fstype, 
+                    int fd, unsigned start, unsigned len,
+                    enum brl_type op)
+{
+       struct flock lock;
+
+       switch (fstype) {
+       case FSTYPE_SMB:
+               return cli_lock(c, fd, start, len, LOCK_TIMEOUT, op);
+
+       case FSTYPE_NFS:
+               lock.l_type = (op==READ_LOCK) ? F_RDLCK:F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = start;
+               lock.l_len = len;
+               lock.l_pid = getpid();
+               return fcntl(fd,F_SETLK,&lock) == 0;
+       }
+
+       return False;
+}
+
+static BOOL try_unlock(struct cli_state *c, int fstype, 
+                      int fd, unsigned start, unsigned len)
+{
+       struct flock lock;
+
+       switch (fstype) {
+       case FSTYPE_SMB:
+               return cli_unlock(c, fd, start, len);
+
+       case FSTYPE_NFS:
+               lock.l_type = F_UNLCK;
+               lock.l_whence = SEEK_SET;
+               lock.l_start = start;
+               lock.l_len = len;
+               lock.l_pid = getpid();
+               return fcntl(fd,F_SETLK,&lock) == 0;
+       }
+
+       return False;
+}      
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid, 
+                     enum brl_type lock_type,
+                     br_off start, br_off size)
+{
+       printf("%6d   %05x:%05x    %s  %.0f:%.0f(%.0f)\n", 
+              (int)pid, (int)dev, (int)ino, 
+              lock_type==READ_LOCK?"R":"W",
+              (double)start, (double)start+size-1,(double)size);
+
+}
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share)
+{
+       struct cli_state *c;
+       char *server_n;
+       fstring server;
+       fstring myname;
+       static int count;
+       NTSTATUS nt_status;
+
+       fstrcpy(server,share+2);
+       share = strchr_m(server,'\\');
+       if (!share) return NULL;
+       *share = 0;
+       share++;
+
+       server_n = server;
+       
+       if (!got_pass) {
+               char *pass = getpass("Password: ");
+               if (pass) {
+                       fstrcpy(password, pass);
+               }
+       }
+
+       slprintf(myname,sizeof(myname), "lock-%u-%u", getpid(), count++);
+
+       nt_status = cli_full_connection(&c, myname, server_n, NULL, 0, share, "?????", 
+                                       username, lp_workgroup(), password, 0,
+                                       NULL);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("cli_full_connection failed with error %s\n", nt_errstr(nt_status)));
+               return NULL;
+       }
+
+       c->use_oplocks = use_oplocks;
+
+       return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                     char *nfs[NSERVERS], 
+                     int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+                     char *share1, char *share2)
+{
+       int server, conn, f, fstype;
+       char *share[2];
+       share[0] = share1;
+       share[1] = share2;
+
+       fstype = FSTYPE_SMB;
+
+       for (server=0;server<NSERVERS;server++)
+       for (conn=0;conn<NCONNECTIONS;conn++) {
+               if (cli[server][conn]) {
+                       for (f=0;f<NFILES;f++) {
+                               cli_close(cli[server][conn], fnum[server][fstype][conn][f]);
+                       }
+                       cli_ulogoff(cli[server][conn]);
+                       cli_shutdown(cli[server][conn]);
+               }
+               cli[server][conn] = connect_one(share[server]);
+               if (!cli[server][conn]) {
+                       DEBUG(0,("Failed to connect to %s\n", share[server]));
+                       exit(1);
+               }
+       }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                    char *nfs[NSERVERS],
+                    int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+                    struct record *rec)
+{
+       unsigned conn = rec->conn;
+       unsigned f = rec->f;
+       unsigned fstype = rec->fstype;
+       unsigned start = rec->start;
+       unsigned len = rec->len;
+       unsigned r1 = rec->r1;
+       unsigned r2 = rec->r2;
+       enum brl_type op;
+       int server;
+       BOOL ret[NSERVERS];
+
+       if (r1 < READ_PCT) {
+               op = READ_LOCK; 
+       } else {
+               op = WRITE_LOCK; 
+       }
+
+       if (r2 < LOCK_PCT) {
+               /* set a lock */
+               for (server=0;server<NSERVERS;server++) {
+                       ret[server] = try_lock(cli[server][conn], fstype,
+                                              fnum[server][fstype][conn][f],
+                                              start, len, op);
+               }
+               if (showall || ret[0] != ret[1]) {
+                       printf("lock   conn=%u fstype=%u f=%u range=%u:%u(%u) op=%s -> %u:%u\n",
+                              conn, fstype, f, 
+                              start, start+len-1, len,
+                              op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+                              ret[0], ret[1]);
+               }
+               if (showall) brl_forall(print_brl);
+               if (ret[0] != ret[1]) return False;
+       } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
+               /* unset a lock */
+               for (server=0;server<NSERVERS;server++) {
+                       ret[server] = try_unlock(cli[server][conn], fstype,
+                                                fnum[server][fstype][conn][f],
+                                                start, len);
+               }
+               if (showall || (!hide_unlock_fails && (ret[0] != ret[1]))) {
+                       printf("unlock conn=%u fstype=%u f=%u range=%u:%u(%u)       -> %u:%u\n",
+                              conn, fstype, f, 
+                              start, start+len-1, len,
+                              ret[0], ret[1]);
+               }
+               if (showall) brl_forall(print_brl);
+               if (!hide_unlock_fails && ret[0] != ret[1]) return False;
+       } else {
+               /* reopen the file */
+               for (server=0;server<NSERVERS;server++) {
+                       try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+                       fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+                                                                O_RDWR|O_CREAT);
+                       if (fnum[server][fstype][conn][f] == -1) {
+                               printf("failed to reopen on share1\n");
+                               return False;
+                       }
+               }
+               if (showall) {
+                       printf("reopen conn=%u fstype=%u f=%u\n",
+                              conn, fstype, f);
+                       brl_forall(print_brl);
+               }
+       }
+       return True;
+}
+
+static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                       char *nfs[NSERVERS],
+                       int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+       int server, conn, f, fstype; 
+
+       for (server=0;server<NSERVERS;server++)
+       for (fstype=0;fstype<NUMFSTYPES;fstype++)
+       for (conn=0;conn<NCONNECTIONS;conn++)
+       for (f=0;f<NFILES;f++) {
+               if (fnum[server][fstype][conn][f] != -1) {
+                       try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+                       fnum[server][fstype][conn][f] = -1;
+               }
+       }
+       for (server=0;server<NSERVERS;server++) {
+               cli_unlink(cli[server][0], FILENAME);
+       }
+}
+
+static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                      char *nfs[NSERVERS],
+                      int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+       int server, fstype, conn, f; 
+
+       for (server=0;server<NSERVERS;server++)
+       for (fstype=0;fstype<NUMFSTYPES;fstype++)
+       for (conn=0;conn<NCONNECTIONS;conn++)
+       for (f=0;f<NFILES;f++) {
+               fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+                                                        O_RDWR|O_CREAT);
+               if (fnum[server][fstype][conn][f] == -1) {
+                       fprintf(stderr,"Failed to open fnum[%u][%u][%u][%u]\n",
+                               server, fstype, conn, f);
+                       exit(1);
+               }
+       }
+}
+
+
+static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS], 
+                 char *nfs[NSERVERS],
+                 int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+                 int n)
+{
+       int i;
+       printf("testing %u ...\n", n);
+       for (i=0; i<n; i++) {
+               if (i && i % 100 == 0) {
+                       printf("%u\n", i);
+               }
+
+               if (recorded[i].needed &&
+                   !test_one(cli, nfs, fnum, &recorded[i])) return i;
+       }
+       return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+   descriptors open on the file - 8 file descriptors in total 
+
+   we then do random locking ops in tamdem on the 4 fnums from each
+   server and ensure that the results match
+ */
+static void test_locks(char *share1, char *share2, char *nfspath1, char *nfspath2)
+{
+       struct cli_state *cli[NSERVERS][NCONNECTIONS];
+       char *nfs[NSERVERS];
+       int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES];
+       int n, i, n1; 
+
+       nfs[0] = nfspath1;
+       nfs[1] = nfspath2;
+
+       ZERO_STRUCT(fnum);
+       ZERO_STRUCT(cli);
+
+       recorded = (struct record *)malloc(sizeof(*recorded) * numops);
+
+       for (n=0; n<numops; n++) {
+               recorded[n].conn = random() % NCONNECTIONS;
+               recorded[n].fstype = random() % NUMFSTYPES;
+               recorded[n].f = random() % NFILES;
+               recorded[n].start = LOCKBASE + ((unsigned)random() % (LOCKRANGE-1));
+               recorded[n].len = 1 + 
+                       random() % (LOCKRANGE-(recorded[n].start-LOCKBASE));
+               recorded[n].start *= RANGE_MULTIPLE;
+               recorded[n].len *= RANGE_MULTIPLE;
+               recorded[n].r1 = random() % 100;
+               recorded[n].r2 = random() % 100;
+               recorded[n].needed = True;
+       }
+
+       reconnect(cli, nfs, fnum, share1, share2);
+       open_files(cli, nfs, fnum);
+       n = retest(cli, nfs, fnum, numops);
+
+       if (n == numops || !analyze) return;
+       n++;
+
+       while (1) {
+               n1 = n;
+
+               close_files(cli, nfs, fnum);
+               reconnect(cli, nfs, fnum, share1, share2);
+               open_files(cli, nfs, fnum);
+
+               for (i=0;i<n-1;i++) {
+                       int m;
+                       recorded[i].needed = False;
+
+                       close_files(cli, nfs, fnum);
+                       open_files(cli, nfs, fnum);
+
+                       m = retest(cli, nfs, fnum, n);
+                       if (m == n) {
+                               recorded[i].needed = True;
+                       } else {
+                               if (i < m) {
+                                       memmove(&recorded[i], &recorded[i+1],
+                                               (m-i)*sizeof(recorded[0]));
+                               }
+                               n = m;
+                               i--;
+                       }
+               }
+
+               if (n1 == n) break;
+       }
+
+       close_files(cli, nfs, fnum);
+       reconnect(cli, nfs, fnum, share1, share2);
+       open_files(cli, nfs, fnum);
+       showall = True;
+       n1 = retest(cli, nfs, fnum, n);
+       if (n1 != n-1) {
+               printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+       }
+       close_files(cli, nfs, fnum);
+
+       for (i=0;i<n;i++) {
+               printf("{%u, %u, %u, %u, %u, %u, %u, %u},\n",
+                      recorded[i].r1,
+                      recorded[i].r2,
+                      recorded[i].conn,
+                      recorded[i].fstype,
+                      recorded[i].f,
+                      recorded[i].start,
+                      recorded[i].len,
+                      recorded[i].needed);
+       }       
+}
+
+
+
+static void usage(void)
+{
+       printf(
+"Usage:\n\
+  locktest //server1/share1 //server2/share2 /path1 /path2 [options..]\n\
+  options:\n\
+        -U user%%pass\n\
+        -s seed\n\
+        -o numops\n\
+        -u          hide unlock fails\n\
+        -a          (show all ops)\n\
+        -O          use oplocks\n\
+");
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       char *share1, *share2, *nfspath1, *nfspath2;
+       extern char *optarg;
+       extern int optind;
+       int opt;
+       char *p;
+       int seed;
+
+       setlinebuf(stdout);
+
+       dbf = x_stderr;
+
+       if (argc < 5 || argv[1][0] == '-') {
+               usage();
+               exit(1);
+       }
+
+       share1 = argv[1];
+       share2 = argv[2];
+       nfspath1 = argv[3];
+       nfspath2 = argv[4];
+
+       all_string_sub(share1,"/","\\",0);
+       all_string_sub(share2,"/","\\",0);
+
+       setup_logging(argv[0],True);
+
+       argc -= 4;
+       argv += 4;
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (getenv("USER")) {
+               fstrcpy(username,getenv("USER"));
+       }
+
+       seed = time(NULL);
+
+       while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
+               switch (opt) {
+               case 'U':
+                       fstrcpy(username,optarg);
+                       p = strchr_m(username,'%');
+                       if (p) {
+                               *p = 0;
+                               fstrcpy(password, p+1);
+                               got_pass = 1;
+                       }
+                       break;
+               case 's':
+                       seed = atoi(optarg);
+                       break;
+               case 'u':
+                       hide_unlock_fails = True;
+                       break;
+               case 'o':
+                       numops = atoi(optarg);
+                       break;
+               case 'O':
+                       use_oplocks = True;
+                       break;
+               case 'a':
+                       showall = True;
+                       break;
+               case 'A':
+                       analyze = True;
+                       break;
+               case 'h':
+                       usage();
+                       exit(1);
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       DEBUG(0,("seed=%u\n", seed));
+       srandom(seed);
+
+       locking_init(1);
+       test_locks(share1, share2, nfspath1, nfspath2);
+
+       return(0);
+}
diff --git a/source4/torture/mangle_test.c b/source4/torture/mangle_test.c
new file mode 100644 (file)
index 0000000..fadd884
--- /dev/null
@@ -0,0 +1,205 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester - mangling test
+   Copyright (C) Andrew Tridgell 2002
+   
+   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"
+
+static TDB_CONTEXT *tdb;
+
+#define NAME_LENGTH 20
+
+static unsigned total, collisions, failures;
+
+static BOOL test_one(struct cli_state *cli, const char *name)
+{
+       int fnum;
+       char *shortname;
+       fstring name2;
+       NTSTATUS status;
+       TDB_DATA data;
+
+       total++;
+
+       fnum = cli_open(cli, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open of %s failed (%s)\n", name, cli_errstr(cli));
+               return False;
+       }
+
+       if (!cli_close(cli, fnum)) {
+               printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+               return False;
+       }
+
+       /* get the short name */
+       status = cli_qpathinfo_alt_name(cli, name, &shortname);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("query altname of %s failed (%s)\n", name, cli_errstr(cli));
+               return False;
+       }
+
+       snprintf(name2, sizeof(name2), "\\mangle_test\\%s", shortname);
+       if (!cli_unlink(cli, name2)) {
+               printf("unlink of %s  (%s) failed (%s)\n", 
+                      name2, name, cli_errstr(cli));
+               return False;
+       }
+
+       /* recreate by short name */
+       fnum = cli_open(cli, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open2 of %s failed (%s)\n", name2, cli_errstr(cli));
+               return False;
+       }
+       if (!cli_close(cli, fnum)) {
+               printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+               return False;
+       }
+
+       /* and unlink by long name */
+       if (!cli_unlink(cli, name)) {
+               printf("unlink2 of %s  (%s) failed (%s)\n", 
+                      name, name2, cli_errstr(cli));
+               failures++;
+               cli_unlink(cli, name2);
+               return True;
+       }
+
+       /* see if the short name is already in the tdb */
+       data = tdb_fetch_by_string(tdb, shortname);
+       if (data.dptr) {
+               /* maybe its a duplicate long name? */
+               if (strcasecmp(name, data.dptr) != 0) {
+                       /* we have a collision */
+                       collisions++;
+                       printf("Collision between %s and %s   ->  %s "
+                               " (coll/tot: %u/%u)\n", 
+                               name, data.dptr, shortname, collisions, total);
+               }
+               free(data.dptr);
+       } else {
+               TDB_DATA namedata;
+               /* store it for later */
+               namedata.dptr = name;
+               namedata.dsize = strlen(name)+1;
+               tdb_store_by_string(tdb, shortname, namedata, TDB_REPLACE);
+       }
+
+       return True;
+}
+
+
+static void gen_name(char *name)
+{
+       const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~...";
+       unsigned max_idx = strlen(chars);
+       unsigned len;
+       int i;
+       char *p;
+
+       fstrcpy(name, "\\mangle_test\\");
+       p = name + strlen(name);
+
+       len = 1 + random() % NAME_LENGTH;
+       
+       for (i=0;i<len;i++) {
+               p[i] = chars[random() % max_idx];
+       }
+
+       p[i] = 0;
+
+       if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
+               p[0] = '_';
+       }
+
+       /* have a high probability of a common lead char */
+       if (random() % 2 == 0) {
+               p[0] = 'A';
+       }
+
+       /* and a medium probability of a common lead string */
+       if (random() % 10 == 0) {
+               strncpy(p, "ABCDE", 5);
+       }
+
+       /* and a high probability of a good extension length */
+       if (random() % 2 == 0) {
+               char *s = strrchr(p, '.');
+               if (s) {
+                       s[4] = 0;
+               }
+       }
+}
+
+
+BOOL torture_mangle(int dummy)
+{
+       extern int torture_numops;
+       static struct cli_state *cli;
+       int i;
+
+       printf("starting mangle test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       /* we will use an internal tdb to store the names we have used */
+       tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
+       if (!tdb) {
+               printf("ERROR: Failed to open tdb\n");
+               return False;
+       }
+
+       cli_unlink(cli, "\\mangle_test\\*");
+       cli_rmdir(cli, "\\mangle_test");
+
+       if (!cli_mkdir(cli, "\\mangle_test")) {
+               printf("ERROR: Failed to make directory\n");
+               return False;
+       }
+
+       for (i=0;i<torture_numops;i++) {
+               fstring name;
+
+               gen_name(name);
+
+               if (!test_one(cli, name)) {
+                       break;
+               }
+               if (total && total % 100 == 0) {
+                       printf("collisions %u/%u  - %.2f%%   (%u failures)\r",
+                              collisions, total, (100.0*collisions) / total, failures);
+               }
+       }
+
+       cli_unlink(cli, "\\mangle_test\\*");
+       if (!cli_rmdir(cli, "\\mangle_test")) {
+               printf("ERROR: Failed to remove directory\n");
+               return False;
+       }
+
+       printf("\nTotal collisions %u/%u  - %.2f%%   (%u failures)\n",
+              collisions, total, (100.0*collisions) / total, failures);
+
+       torture_close_connection(cli);
+
+       printf("mangle test finished\n");
+       return (failures == 0);
+}
diff --git a/source4/torture/masktest.c b/source4/torture/masktest.c
new file mode 100644 (file)
index 0000000..15bc080
--- /dev/null
@@ -0,0 +1,464 @@
+/* 
+   Unix SMB/CIFS implementation.
+   mask_match tester
+   Copyright (C) Andrew Tridgell 1999
+   
+   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"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int max_protocol = PROTOCOL_NT1;
+static BOOL showall = False;
+static BOOL old_list = False;
+static const char *maskchars = "<>\"?*abc.";
+static const char *filechars = "abcdefghijklm.";
+static int verbose;
+static int die_on_error;
+static int NumLoops = 0;
+
+/* a test fn for LANMAN mask support */
+static int ms_fnmatch_lanman_core(const char *pattern, const char *string)
+{
+       const char *p = pattern, *n = string;
+       char c;
+
+       if (strcmp(p,"?")==0 && strcmp(n,".")==0) goto match;
+
+       while ((c = *p++)) {
+               switch (c) {
+               case '.':
+                       /* if (! *n && ! *p) goto match; */
+                       if (*n != '.') goto nomatch;
+                       n++;
+                       break;
+
+               case '?':
+                       if ((*n == '.' && n[1] != '.') || ! *n) goto next;
+                       n++;
+                       break;
+
+               case '>':
+                       if (n[0] == '.') {
+                               if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match;
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                               goto nomatch;
+                       }
+                       if (! *n) goto next;
+                       n++;
+                       break;
+
+               case '*':
+                       if (! *p) goto match;
+                       for (; *n; n++) {
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                       }
+                       break;
+
+               case '<':
+                       for (; *n; n++) {
+                               if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                               if (*n == '.' && !strchr_m(n+1,'.')) {
+                                       n++;
+                                       break;
+                               }
+                       }
+                       break;
+
+               case '"':
+                       if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match;
+                       if (*n != '.') goto nomatch;
+                       n++;
+                       break;
+
+               default:
+                       if (c != *n) goto nomatch;
+                       n++;
+               }
+       }
+       
+       if (! *n) goto match;
+       
+ nomatch:
+       if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+       return -1;
+
+next:
+       if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+        goto nomatch;
+
+ match:
+       if (verbose) printf("MATCH   pattern=[%s] string=[%s]\n", pattern, string);
+       return 0;
+}
+
+static int ms_fnmatch_lanman(const char *pattern, const char *string)
+{
+       if (!strpbrk(pattern, "?*<>\"")) {
+               if (strcmp(string,"..") == 0) 
+                       string = ".";
+
+               return strcmp(pattern, string);
+       }
+
+       if (strcmp(string,"..") == 0 || strcmp(string,".") == 0) {
+               return ms_fnmatch_lanman_core(pattern, "..") &&
+                       ms_fnmatch_lanman_core(pattern, ".");
+       }
+
+       return ms_fnmatch_lanman_core(pattern, string);
+}
+
+static BOOL reg_match_one(struct cli_state *cli, const char *pattern, const char *file)
+{
+       /* oh what a weird world this is */
+       if (old_list && strcmp(pattern, "*.*") == 0) return True;
+
+       if (strcmp(pattern,".") == 0) return False;
+
+       if (max_protocol <= PROTOCOL_LANMAN2) {
+               return ms_fnmatch_lanman(pattern, file)==0;
+       }
+
+       if (strcmp(file,"..") == 0) file = ".";
+
+       return ms_fnmatch(pattern, file, cli->transport->negotiate.protocol)==0;
+}
+
+static char *reg_test(struct cli_state *cli, char *pattern, char *long_name, char *short_name)
+{
+       static fstring ret;
+       fstrcpy(ret, "---");
+
+       pattern = 1+strrchr_m(pattern,'\\');
+
+       if (reg_match_one(cli, pattern, ".")) ret[0] = '+';
+       if (reg_match_one(cli, pattern, "..")) ret[1] = '+';
+       if (reg_match_one(cli, pattern, long_name) || 
+           (*short_name && reg_match_one(cli, pattern, short_name))) ret[2] = '+';
+       return ret;
+}
+
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share)
+{
+       struct cli_state *c;
+       fstring server;
+       uint_t flags = 0;
+       NTSTATUS status;
+
+       fstrcpy(server,share+2);
+       share = strchr_m(server,'\\');
+       if (!share) return NULL;
+       *share = 0;
+       share++;
+
+       status = cli_full_connection(&c, "masktest",
+                                    server, NULL, 
+                                    share, "?????", 
+                                    username, lp_workgroup(), 
+                                    password, flags, NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return NULL;
+       }
+
+       return c;
+}
+
+static char *resultp;
+static struct {
+       pstring long_name;
+       pstring short_name;
+} last_hit;
+static BOOL f_info_hit;
+
+static void listfn(file_info *f, const char *s, void *state)
+{
+       if (strcmp(f->name,".") == 0) {
+               resultp[0] = '+';
+       } else if (strcmp(f->name,"..") == 0) {
+               resultp[1] = '+';               
+       } else {
+               resultp[2] = '+';
+       }
+       pstrcpy(last_hit.long_name, f->name);
+       pstrcpy(last_hit.short_name, f->short_name);
+       f_info_hit = True;
+}
+
+static void get_real_name(struct cli_state *cli, 
+                         pstring long_name, fstring short_name)
+{
+       const char *mask;
+       if (max_protocol <= PROTOCOL_LANMAN1) {
+               mask = "\\masktest\\*.*";
+       } else {
+               mask = "\\masktest\\*";
+       }
+
+       f_info_hit = False;
+
+       cli_list_new(cli, mask, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, 
+                    listfn, NULL);
+
+       if (f_info_hit) {
+               fstrcpy(short_name, last_hit.short_name);
+               strlower(short_name);
+               pstrcpy(long_name, last_hit.long_name);
+               strlower(long_name);
+       }
+
+       if (*short_name == 0) {
+               fstrcpy(short_name, long_name);
+       }
+}
+
+static void testpair(struct cli_state *cli, char *mask, char *file)
+{
+       int fnum;
+       fstring res1;
+       char *res2;
+       static int count;
+       fstring short_name;
+       pstring long_name;
+
+       count++;
+
+       fstrcpy(res1, "---");
+
+       fnum = cli_open(cli, file, O_CREAT|O_TRUNC|O_RDWR, 0);
+       if (fnum == -1) {
+               DEBUG(0,("Can't create %s\n", file));
+               return;
+       }
+       cli_close(cli, fnum);
+
+       resultp = res1;
+       fstrcpy(short_name, "");
+       get_real_name(cli, long_name, short_name);
+       fstrcpy(res1, "---");
+       cli_list(cli, mask, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, 
+                listfn, NULL);
+
+       res2 = reg_test(cli, mask, long_name, short_name);
+
+       if (showall || strcmp(res1, res2)) {
+               d_printf("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n",
+                        res1, res2, count, mask, file, long_name, short_name);
+               if (die_on_error) exit(1);
+       }
+
+       cli_unlink(cli, file);
+
+       if (count % 100 == 0) DEBUG(0,("%d\n", count));
+}
+
+static void test_mask(int argc, char *argv[], 
+                     struct cli_state *cli)
+{
+       pstring mask, file;
+       int l1, l2, i, l;
+       int mc_len = strlen(maskchars);
+       int fc_len = strlen(filechars);
+
+       cli_mkdir(cli, "\\masktest");
+
+       cli_unlink(cli, "\\masktest\\*");
+
+       if (argc >= 2) {
+               while (argc >= 2) {
+                       pstrcpy(mask,"\\masktest\\");
+                       pstrcpy(file,"\\masktest\\");
+                       pstrcat(mask, argv[0]);
+                       pstrcat(file, argv[1]);
+                       testpair(cli, mask, file);
+                       argv += 2;
+                       argc -= 2;
+               }
+               goto finished;
+       }
+
+       while (1) {
+               l1 = 1 + random() % 20;
+               l2 = 1 + random() % 20;
+               pstrcpy(mask,"\\masktest\\");
+               pstrcpy(file,"\\masktest\\");
+               l = strlen(mask);
+               for (i=0;i<l1;i++) {
+                       mask[i+l] = maskchars[random() % mc_len];
+               }
+               mask[l+l1] = 0;
+
+               for (i=0;i<l2;i++) {
+                       file[i+l] = filechars[random() % fc_len];
+               }
+               file[l+l2] = 0;
+
+               if (strcmp(file+l,".") == 0 || 
+                   strcmp(file+l,"..") == 0 ||
+                   strcmp(mask+l,"..") == 0) continue;
+
+               if (strspn(file+l, ".") == strlen(file+l)) continue;
+
+               testpair(cli, mask, file);
+               if (NumLoops && (--NumLoops == 0))
+                       break;
+       }
+
+ finished:
+       cli_rmdir(cli, "\\masktest");
+}
+
+
+static void usage(void)
+{
+       printf(
+"Usage:\n\
+  masktest //server/share [options..]\n\
+  options:\n\
+       -d debuglevel\n\
+       -n numloops\n\
+        -W workgroup\n\
+        -U user%%pass\n\
+        -s seed\n\
+        -M max protocol\n\
+        -f filechars (default %s)\n\
+        -m maskchars (default %s)\n\
+       -v                             verbose mode\n\
+       -E                             die on error\n\
+        -a                             show all tests\n\
+\n\
+  This program tests wildcard matching between two servers. It generates\n\
+  random pairs of filenames/masks and tests that they match in the same\n\
+  way on the servers and internally\n\
+", 
+  filechars, maskchars);
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       char *share;
+       struct cli_state *cli;  
+       int opt;
+       char *p;
+       int seed;
+
+       setlinebuf(stdout);
+
+       setup_logging("masktest", DEBUG_STDOUT);
+
+       lp_set_cmdline("log level", "0");
+
+       if (argc < 2 || argv[1][0] == '-') {
+               usage();
+               exit(1);
+       }
+
+       share = argv[1];
+
+       all_string_sub(share,"/","\\",0);
+
+       setup_logging(argv[0],True);
+
+       argc -= 1;
+       argv += 1;
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (getenv("USER")) {
+               fstrcpy(username,getenv("USER"));
+       }
+
+       seed = time(NULL);
+
+       while ((opt = getopt(argc, argv, "n:d:U:s:hm:f:aoW:M:vE")) != EOF) {
+               switch (opt) {
+               case 'n':
+                       NumLoops = atoi(optarg);
+                       break;
+               case 'd':
+                       DEBUGLEVEL = atoi(optarg);
+                       break;
+               case 'E':
+                       die_on_error = 1;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'M':
+                       max_protocol = interpret_protocol(optarg, max_protocol);
+                       break;
+               case 'U':
+                       fstrcpy(username,optarg);
+                       p = strchr_m(username,'%');
+                       if (p) {
+                               *p = 0;
+                               fstrcpy(password, p+1);
+                               got_pass = 1;
+                       }
+                       break;
+               case 's':
+                       seed = atoi(optarg);
+                       break;
+               case 'h':
+                       usage();
+                       exit(1);
+               case 'm':
+                       maskchars = optarg;
+                       break;
+               case 'f':
+                       filechars = optarg;
+                       break;
+               case 'a':
+                       showall = 1;
+                       break;
+               case 'o':
+                       old_list = True;
+                       break;
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+
+       cli = connect_one(share);
+       if (!cli) {
+               DEBUG(0,("Failed to connect to %s\n", share));
+               exit(1);
+       }
+
+       /* need to init seed after connect as clientgen uses random numbers */
+       DEBUG(0,("seed=%d\n", seed));
+       srandom(seed);
+
+       test_mask(argc, argv, cli);
+
+       return(0);
+}
diff --git a/source4/torture/msgtest.c b/source4/torture/msgtest.c
new file mode 100644 (file)
index 0000000..952aa46
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Copyright (C) Andrew Tridgell 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.
+*/
+
+/*
+  test code for internal messaging
+ */
+
+#include "includes.h"
+
+static int pong_count;
+
+/****************************************************************************
+a useful function for testing the message system
+****************************************************************************/
+void pong_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+       pong_count++;
+}
+
+ int main(int argc, char *argv[])
+{
+       pid_t pid;
+       int i, n;
+       char buf[12];
+
+       setup_logging(argv[0],True);
+       
+       lp_load(dyn_CONFIGFILE,False,False,False);
+
+       message_init();
+
+       if (argc != 3) {
+               fprintf(stderr, "%s: Usage - %s pid count\n", argv[0], argv[0]);
+               exit(1);
+       }
+
+       pid = atoi(argv[1]);
+       n = atoi(argv[2]);
+
+       message_register(MSG_PONG, pong_message);
+
+       for (i=0;i<n;i++) {
+               message_send_pid(pid, MSG_PING, NULL, 0, True);
+       }
+
+       while (pong_count < i) {
+               message_dispatch();
+               msleep(1);
+       }
+
+       /* Now test that the duplicate filtering code works. */
+       pong_count = 0;
+
+       safe_strcpy(buf, "1234567890", sizeof(buf)-1);
+
+       for (i=0;i<n;i++) {
+               message_send_pid(getpid(), MSG_PING, NULL, 0, False);
+               message_send_pid(getpid(), MSG_PING, buf, 11, False);
+       }
+
+       for (i=0;i<n;i++) {
+               message_dispatch();
+               msleep(1);
+       }
+
+       if (pong_count != 2) {
+               fprintf(stderr, "Duplicate filter failed (%d).\n", pong_count);
+               exit(1);
+       }
+
+       return (0);
+}
+
diff --git a/source4/torture/nbio.c b/source4/torture/nbio.c
new file mode 100644 (file)
index 0000000..ef23147
--- /dev/null
@@ -0,0 +1,285 @@
+#define NBDEBUG 0
+
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester
+   Copyright (C) Andrew Tridgell 1997-1998
+   
+   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"
+
+#define MAX_FILES 1000
+
+static char buf[70000];
+extern int line_count;
+extern int nbio_id;
+static int nprocs;
+static BOOL bypass_io;
+
+static struct {
+       int fd;
+       int handle;
+} ftable[MAX_FILES];
+
+static struct {
+       double bytes_in, bytes_out;
+       int line;
+       int done;
+} *children;
+
+double nbio_total(void)
+{
+       int i;
+       double total = 0;
+       for (i=0;i<nprocs;i++) {
+               total += children[i].bytes_out + children[i].bytes_in;
+       }
+       return total;
+}
+
+void nb_alarm(void)
+{
+       int i;
+       int lines=0, num_clients=0;
+       if (nbio_id != -1) return;
+
+       for (i=0;i<nprocs;i++) {
+               lines += children[i].line;
+               if (!children[i].done) num_clients++;
+       }
+
+       printf("%4d  %8d  %.2f MB/sec\r", num_clients, lines/nprocs, 1.0e-6 * nbio_total() / end_timer());
+
+       signal(SIGALRM, nb_alarm);
+       alarm(1);       
+}
+
+void nbio_shmem(int n)
+{
+       nprocs = n;
+       children = shm_setup(sizeof(*children) * nprocs);
+       if (!children) {
+               printf("Failed to setup shared memory!\n");
+               exit(1);
+       }
+}
+
+static int find_handle(int handle)
+{
+       int i;
+       children[nbio_id].line = line_count;
+       for (i=0;i<MAX_FILES;i++) {
+               if (ftable[i].handle == handle) return i;
+       }
+       printf("(%d) ERROR: handle %d was not found\n", 
+              line_count, handle);
+       exit(1);
+
+       return -1;              /* Not reached */
+}
+
+
+static struct cli_state *c;
+
+static void sigsegv(int sig)
+{
+       char line[200];
+       printf("segv at line %d\n", line_count);
+       slprintf(line, sizeof(line), "/usr/X11R6/bin/xterm -e gdb /proc/%d/exe %d", 
+               (int)getpid(), (int)getpid());
+       system(line);
+       exit(1);
+}
+
+void nb_setup(struct cli_state *cli)
+{
+       signal(SIGSEGV, sigsegv);
+       c = cli;
+       start_timer();
+       children[nbio_id].done = 0;
+       if (bypass_io)
+               printf("skipping I/O\n");
+}
+
+
+void nb_unlink(const char *fname)
+{
+       if (!cli_unlink(c, fname)) {
+#if NBDEBUG
+               printf("(%d) unlink %s failed (%s)\n", 
+                      line_count, fname, cli_errstr(c));
+#endif
+       }
+}
+
+
+void nb_createx(const char *fname, 
+               unsigned create_options, unsigned create_disposition, int handle)
+{
+       int fd, i;
+       uint32 desired_access;
+
+       if (create_options & NTCREATEX_OPTIONS_DIRECTORY) {
+               desired_access = SA_RIGHT_FILE_READ_DATA;
+       } else {
+               desired_access = SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA;
+       }
+
+       fd = cli_nt_create_full(c, fname, 0, 
+                               desired_access,
+                               0x0,
+                               NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, 
+                               create_disposition, 
+                               create_options, 0);
+       if (fd == -1 && handle != -1) {
+               printf("ERROR: cli_nt_create_full failed for %s - %s\n",
+                      fname, cli_errstr(c));
+               exit(1);
+       }
+       if (fd != -1 && handle == -1) {
+               printf("ERROR: cli_nt_create_full succeeded for %s\n", fname);
+               exit(1);
+       }
+       if (fd == -1) return;
+
+       for (i=0;i<MAX_FILES;i++) {
+               if (ftable[i].handle == 0) break;
+       }
+       if (i == MAX_FILES) {
+               printf("(%d) file table full for %s\n", line_count, 
+                      fname);
+               exit(1);
+       }
+       ftable[i].handle = handle;
+       ftable[i].fd = fd;
+}
+
+void nb_writex(int handle, int offset, int size, int ret_size)
+{
+       int i;
+
+       if (buf[0] == 0) memset(buf, 1, sizeof(buf));
+
+       i = find_handle(handle);
+       if (!bypass_io && cli_write(c, ftable[i].fd, 0, buf, offset, size) != ret_size) {
+               printf("(%d) ERROR: write failed on handle %d, fd %d \
+errno %d (%s)\n", line_count, handle, ftable[i].fd, errno, strerror(errno));
+               exit(1);
+       }
+
+       children[nbio_id].bytes_out += ret_size;
+}
+
+void nb_readx(int handle, int offset, int size, int ret_size)
+{
+       int i, ret;
+
+       i = find_handle(handle);
+       if (!bypass_io && (ret=cli_read(c, ftable[i].fd, buf, offset, size)) != ret_size) {
+               printf("(%d) ERROR: read failed on handle %d ofs=%d size=%d res=%d fd %d errno %d (%s)\n",
+                       line_count, handle, offset, size, ret, ftable[i].fd, errno, strerror(errno));
+               exit(1);
+       }
+       children[nbio_id].bytes_in += ret_size;
+}
+
+void nb_close(int handle)
+{
+       int i;
+       i = find_handle(handle);
+       if (!cli_close(c, ftable[i].fd)) {
+               printf("(%d) close failed on handle %d\n", line_count, handle);
+               exit(1);
+       }
+       ftable[i].handle = 0;
+}
+
+void nb_rmdir(const char *fname)
+{
+       if (!cli_rmdir(c, fname)) {
+               printf("ERROR: rmdir %s failed (%s)\n", 
+                      fname, cli_errstr(c));
+               exit(1);
+       }
+}
+
+void nb_rename(const char *old, const char *new)
+{
+       if (!cli_rename(c, old, new)) {
+               printf("ERROR: rename %s %s failed (%s)\n", 
+                      old, new, cli_errstr(c));
+               exit(1);
+       }
+}
+
+
+void nb_qpathinfo(const char *fname)
+{
+       cli_qpathinfo(c, fname, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfileinfo(int fnum)
+{
+       int i;
+       i = find_handle(fnum);
+       cli_qfileinfo(c, ftable[i].fd, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfsinfo(int level)
+{
+       int bsize, total, avail;
+       /* this is not the right call - we need cli_qfsinfo() */
+       cli_dskattr(c, &bsize, &total, &avail);
+}
+
+static void find_fn(file_info *finfo, const char *name, void *state)
+{
+       /* noop */
+}
+
+void nb_findfirst(const char *mask)
+{
+       cli_list(c, mask, 0, find_fn, NULL);
+}
+
+void nb_flush(int fnum)
+{
+       int i;
+       i = find_handle(fnum);
+       /* hmmm, we don't have cli_flush() yet */
+}
+
+void nb_deltree(const char *dname)
+{
+       int total_deleted;
+
+       total_deleted = cli_deltree(c, dname);
+
+       if (total_deleted == -1) {
+               printf("Failed to cleanup tree %s - exiting\n", dname);
+               exit(1);
+       }
+
+       if (total_deleted > 0) printf("WARNING: Cleaned up %d files\n", total_deleted);
+}
+
+
+void nb_cleanup(void)
+{
+       cli_rmdir(c, "clients");
+       children[nbio_id].done = 1;
+}
diff --git a/source4/torture/nsstest.c b/source4/torture/nsstest.c
new file mode 100644 (file)
index 0000000..a82fa05
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+   Unix SMB/CIFS implementation.
+   nss tester for winbindd
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+static const char *so_path = "/lib/libnss_winbind.so";
+static const char *nss_name = "winbind";
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+       char s[1024];
+       static void *h;
+       void *res;
+
+       snprintf(s,sizeof(s), "_nss_%s_%s", nss_name, name);
+
+       if (!h) {
+               h = sys_dlopen(so_path, RTLD_LAZY);
+       }
+       if (!h) {
+               printf("Can't open shared library %s\n", so_path);
+               exit(1);
+       }
+       res = sys_dlsym(h, s);
+       if (!res) {
+               printf("Can't find function %s\n", s);
+               return NULL;
+       }
+       return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+       last_error = status;
+       total_errors++;
+       printf("ERROR %s: NSS_STATUS=%d  %d (nss_errno=%d)\n", 
+              who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+       NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *, 
+                                     size_t , int *) = find_fn("getpwent_r");
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+
+       status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getpwent", status);
+               return NULL;
+       }
+       return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+       NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *, 
+                                     size_t , int *) = find_fn("getpwnam_r");
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+       
+       status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getpwnam", status);
+               return NULL;
+       }
+       return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+       NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *, 
+                                     size_t , int *) = find_fn("getpwuid_r");
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+       
+       status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getpwuid", status);
+               return NULL;
+       }
+       return &pwd;
+}
+
+static void nss_setpwent(void)
+{
+       NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent");
+       NSS_STATUS status;
+       status = _nss_setpwent();
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("setpwent", status);
+       }
+}
+
+static void nss_endpwent(void)
+{
+       NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent");
+       NSS_STATUS status;
+       status = _nss_endpwent();
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("endpwent", status);
+       }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+       NSS_STATUS (*_nss_getgrent_r)(struct group *, char *, 
+                                     size_t , int *) = find_fn("getgrent_r");
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1024;
+       NSS_STATUS status;
+
+       if (!buf) buf = malloc(buflen);
+
+again: 
+       status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = realloc(buf, buflen);
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getgrent", status);
+               return NULL;
+       }
+       return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+       NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *, 
+                                     size_t , int *) = find_fn("getgrnam_r");
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1000;
+       NSS_STATUS status;
+
+       if (!buf) buf = malloc(buflen);
+again: 
+       status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = realloc(buf, buflen);
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getgrnam", status);
+               return NULL;
+       }
+       return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+       NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *, 
+                                     size_t , int *) = find_fn("getgrgid_r");
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1000;
+       NSS_STATUS status;
+       
+       if (!buf) buf = malloc(buflen);
+again: 
+       status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = realloc(buf, buflen);
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("getgrgid", status);
+               return NULL;
+       }
+       return &grp;
+}
+
+static void nss_setgrent(void)
+{
+       NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent");
+       NSS_STATUS status;
+       status = _nss_setgrent();
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("setgrent", status);
+       }
+}
+
+static void nss_endgrent(void)
+{
+       NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent");
+       NSS_STATUS status;
+       status = _nss_endgrent();
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("endgrent", status);
+       }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+       NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+                                     long int *, gid_t **, long int , int *) = 
+               find_fn("initgroups_dyn");
+       NSS_STATUS status;
+
+       if (!_nss_initgroups) return NSS_STATUS_UNAVAIL;
+
+       status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+       if (status != NSS_STATUS_SUCCESS) {
+               report_nss_error("initgroups", status);
+       }
+       return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+       printf("%s:%s:%d:%d:%s:%s:%s\n", 
+              pwd->pw_name,
+              pwd->pw_passwd,
+              pwd->pw_uid,
+              pwd->pw_gid,
+              pwd->pw_gecos,
+              pwd->pw_dir,
+              pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+       int i;
+       printf("%s:%s:%d: ", 
+              grp->gr_name,
+              grp->gr_passwd,
+              grp->gr_gid);
+       
+       if (!grp->gr_mem[0]) {
+               printf("\n");
+               return;
+       }
+       
+       for (i=0; grp->gr_mem[i+1]; i++) {
+               printf("%s, ", grp->gr_mem[i]);
+       }
+       printf("%s\n", grp->gr_mem[i]);
+}
+
+static void nss_test_initgroups(char *name, gid_t gid)
+{
+       long int size = 16;
+       long int start = 1;
+       gid_t *groups = NULL;
+       int i;
+       NSS_STATUS status;
+
+       groups = (gid_t *)malloc(size * sizeof(gid_t));
+       groups[0] = gid;
+
+       status = nss_initgroups(name, gid, &groups, &start, &size);
+       if (status == NSS_STATUS_UNAVAIL) {
+               printf("No initgroups fn\n");
+               return;
+       }
+
+       for (i=0; i<start-1; i++) {
+               printf("%d, ", groups[i]);
+       }
+       printf("%d\n", groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+       struct passwd *pwd;
+
+       nss_setpwent();
+       /* loop over all users */
+       while ((pwd = nss_getpwent())) {
+               printf("Testing user %s\n", pwd->pw_name);
+               printf("getpwent:   "); print_passwd(pwd);
+               pwd = nss_getpwuid(pwd->pw_uid);
+               if (!pwd) {
+                       total_errors++;
+                       printf("ERROR: can't getpwuid\n");
+                       continue;
+               }
+               printf("getpwuid:   "); print_passwd(pwd);
+               pwd = nss_getpwnam(pwd->pw_name);
+               if (!pwd) {
+                       total_errors++;
+                       printf("ERROR: can't getpwnam\n");
+                       continue;
+               }
+               printf("getpwnam:   "); print_passwd(pwd);
+               printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+               printf("\n");
+       }
+       nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+       struct group *grp;
+
+       nss_setgrent();
+       /* loop over all groups */
+       while ((grp = nss_getgrent())) {
+               printf("Testing group %s\n", grp->gr_name);
+               printf("getgrent: "); print_group(grp);
+               grp = nss_getgrnam(grp->gr_name);
+               if (!grp) {
+                       total_errors++;
+                       printf("ERROR: can't getgrnam\n");
+                       continue;
+               }
+               printf("getgrnam: "); print_group(grp);
+               grp = nss_getgrgid(grp->gr_gid);
+               if (!grp) {
+                       total_errors++;
+                       printf("ERROR: can't getgrgid\n");
+                       continue;
+               }
+               printf("getgrgid: "); print_group(grp);
+               printf("\n");
+       }
+       nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+       struct passwd *pwd;
+       struct group *grp;
+
+       pwd = getpwnam("nosuchname");
+       if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+               total_errors++;
+               printf("ERROR Non existant user gave error %d\n", last_error);
+       }
+
+       pwd = getpwuid(0xFFF0);
+       if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+               total_errors++;
+               printf("ERROR Non existant uid gave error %d\n", last_error);
+       }
+
+       grp = getgrnam("nosuchgroup");
+       if (grp || last_error != NSS_STATUS_NOTFOUND) {
+               total_errors++;
+               printf("ERROR Non existant group gave error %d\n", last_error);
+       }
+
+       grp = getgrgid(0xFFF0);
+       if (grp || last_error != NSS_STATUS_NOTFOUND) {
+               total_errors++;
+               printf("ERROR Non existant gid gave error %d\n", last_error);
+       }
+}
+
+ int main(int argc, char *argv[])
+{      
+       if (argc > 1) so_path = argv[1];
+       if (argc > 2) nss_name = argv[2];
+
+       nss_test_users();
+       nss_test_groups();
+       nss_test_errors();
+
+       printf("total_errors=%d\n", total_errors);
+
+       return total_errors;
+}
diff --git a/source4/torture/qfileinfo.c b/source4/torture/qfileinfo.c
new file mode 100644 (file)
index 0000000..d7136cf
--- /dev/null
@@ -0,0 +1,640 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_FILEINFO_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+static struct {
+       const char *name;
+       enum fileinfo_level level;
+       unsigned only_paths:1;
+       unsigned only_handles:1;
+       NTSTATUS fnum_status, fname_status;
+       union smb_fileinfo fnum_finfo, fname_finfo;
+} levels[] = {
+       { "GETATTR",                   RAW_FILEINFO_GETATTR,           1, 0, },
+       { "GETATTRE",                  RAW_FILEINFO_GETATTRE,          0, 1, },
+       { "STANDARD",                  RAW_FILEINFO_STANDARD, },
+       { "EA_SIZE",                   RAW_FILEINFO_EA_SIZE, },
+       { "ALL_EAS",                   RAW_FILEINFO_ALL_EAS, },
+       { "IS_NAME_VALID",             RAW_FILEINFO_IS_NAME_VALID,     1, 0, },
+       { "BASIC_INFO",                RAW_FILEINFO_BASIC_INFO, },
+       { "STANDARD_INFO",             RAW_FILEINFO_STANDARD_INFO, },
+       { "EA_INFO",                   RAW_FILEINFO_EA_INFO, },
+       { "NAME_INFO",                 RAW_FILEINFO_NAME_INFO, },
+       { "ALL_INFO",                  RAW_FILEINFO_ALL_INFO, },
+       { "ALT_NAME_INFO",             RAW_FILEINFO_ALT_NAME_INFO, },
+       { "STREAM_INFO",               RAW_FILEINFO_STREAM_INFO, },
+       { "COMPRESSION_INFO",          RAW_FILEINFO_COMPRESSION_INFO, },
+       { "BASIC_INFORMATION",         RAW_FILEINFO_BASIC_INFORMATION, },
+       { "STANDARD_INFORMATION",      RAW_FILEINFO_STANDARD_INFORMATION, },
+       { "INTERNAL_INFORMATION",      RAW_FILEINFO_INTERNAL_INFORMATION, },
+       { "EA_INFORMATION",            RAW_FILEINFO_EA_INFORMATION, },
+       { "ACCESS_INFORMATION",        RAW_FILEINFO_ACCESS_INFORMATION, },
+       { "NAME_INFORMATION",          RAW_FILEINFO_NAME_INFORMATION, },
+       { "POSITION_INFORMATION",      RAW_FILEINFO_POSITION_INFORMATION, },
+       { "MODE_INFORMATION",          RAW_FILEINFO_MODE_INFORMATION, },
+       { "ALIGNMENT_INFORMATION",     RAW_FILEINFO_ALIGNMENT_INFORMATION, },
+       { "ALL_INFORMATION",           RAW_FILEINFO_ALL_INFORMATION, },
+       { "ALT_NAME_INFORMATION",      RAW_FILEINFO_ALT_NAME_INFORMATION, },
+       { "STREAM_INFORMATION",        RAW_FILEINFO_STREAM_INFORMATION, },
+       { "COMPRESSION_INFORMATION",   RAW_FILEINFO_COMPRESSION_INFORMATION, },
+       { "NETWORK_OPEN_INFORMATION",  RAW_FILEINFO_NETWORK_OPEN_INFORMATION, },
+       { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, },
+       { NULL, }
+};
+
+/*
+  compare a dos time (2 second resolution) to a nt time
+*/
+static int dos_nt_time_cmp(time_t t, const NTTIME *nt)
+{
+       time_t t2 = nt_time_to_unix(nt);
+       if (ABS(t2 - t) <= 2) return 0;
+       return t2 - t;
+}
+
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fileinfo *fnum_find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
+                   strcmp(name, levels[i].name) == 0 && 
+                   !levels[i].only_paths) {
+                       return &levels[i].fnum_finfo;
+               }
+       }
+       return NULL;
+}
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fileinfo *fname_find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (NT_STATUS_IS_OK(levels[i].fname_status) &&
+                   strcmp(name, levels[i].name) == 0 && 
+                   !levels[i].only_handles) {
+                       return &levels[i].fname_finfo;
+               }
+       }
+       return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+        printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+               #n1, #v1, (uint_t)s1->n1.out.v1, \
+               #n2, #v2, (uint_t)s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
+                                         s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
+        printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
+               #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
+               #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+        printf("%s/%s != %s/%s at %s(%d)\n", \
+               #n1, #v1, \
+               #n2, #v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure 
+   we zero-fill */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+        printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+               #n1, #v1, \
+              (uint_t)s1->n1.out.v1, \
+              (uint_t)s1->n1.out.v1, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* basic testing of all RAW_FILEINFO_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+*/
+BOOL torture_qfileinfo(int dummy)
+{
+       struct cli_state *cli;
+       int i;
+       BOOL ret = True;
+       int count;
+       union smb_fileinfo *s1, *s2;    
+       TALLOC_CTX *mem_ctx;
+       int fnum;
+       const char *fname = "\\torture_qfileinfo.txt";
+       NTTIME correct_time;
+       large_t correct_size;
+       uint32 correct_attrib;
+       const char *correct_name;
+       BOOL skip_streams = False;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_qfileinfo");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+       
+       
+       /* scan all the fileinfo and pathinfo levels */
+       for (i=0; levels[i].name; i++) {
+               if (!levels[i].only_paths) {
+                       levels[i].fnum_finfo.generic.level = levels[i].level;
+                       levels[i].fnum_finfo.generic.in.fnum = fnum;
+                       levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx, 
+                                                                &levels[i].fnum_finfo);
+               }
+
+               if (!levels[i].only_handles) {
+                       levels[i].fname_finfo.generic.level = levels[i].level;
+                       levels[i].fname_finfo.generic.in.fname = talloc_strdup(mem_ctx, fname);
+                       levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, 
+                                                                 &levels[i].fname_finfo);
+               }
+       }
+
+       /* check for completely broken levels */
+       for (count=i=0; levels[i].name; i++) {
+               if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
+                       printf("ERROR: level %s failed - %s\n", 
+                              levels[i].name, nt_errstr(levels[i].fnum_status));
+                       count++;
+               }
+               if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
+                       printf("ERROR: level %s failed - %s\n", 
+                              levels[i].name, nt_errstr(levels[i].fname_status));
+                       count++;
+               }
+       }
+
+       if (count != 0) {
+               ret = False;
+               printf("%d levels failed\n", count);
+               if (count > 32) {
+                       printf("too many level failures - giving up\n");
+                       goto done;
+               }
+       }
+
+       /* see if we can do streams */
+       s1 = fnum_find("STREAM_INFO");
+       if (!s1 || s1->stream_info.out.num_streams == 0) {
+               printf("STREAM_INFO broken (%d) - skipping streams checks\n",
+                      s1 ? s1->stream_info.out.num_streams : -1);
+               skip_streams = True;
+       }       
+
+
+       /* this code is incredibly repititive but doesn't lend itself to loops, so
+          we use lots of macros to make it less painful */
+
+       /* first off we check the levels that are supposed to be aliases. It will be quite rare for 
+          this code to fail, but we need to check it for completeness */
+
+
+
+#define ALIAS_CHECK(sname1, sname2) \
+       do { \
+               s1 = fnum_find(sname1);  s2 = fnum_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+               s1 = fname_find(sname1); s2 = fname_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+               s1 = fnum_find(sname1);  s2 = fname_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+       } while (0)
+
+#define INFO_CHECK \
+               STRUCT_EQUAL(basic_info,  create_time, basic_info, create_time); \
+               STRUCT_EQUAL(basic_info,  access_time, basic_info, access_time); \
+               STRUCT_EQUAL(basic_info,  write_time,  basic_info, write_time); \
+               STRUCT_EQUAL(basic_info,  change_time, basic_info, change_time); \
+               VAL_EQUAL   (basic_info,  attrib,      basic_info, attrib);
+
+       ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               VAL_EQUAL(standard_info,  alloc_size,     standard_info, alloc_size); \
+               VAL_EQUAL(standard_info,  size,           standard_info, size); \
+               VAL_EQUAL(standard_info,  nlink,          standard_info, nlink); \
+               VAL_EQUAL(standard_info,  delete_pending, standard_info, delete_pending); \
+               VAL_EQUAL(standard_info,  directory,      standard_info, directory);
+
+       ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               VAL_EQUAL(ea_info,  ea_size,     ea_info, ea_size);
+
+       ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STR_EQUAL(name_info,  fname,   name_info, fname);
+
+       ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STRUCT_EQUAL(all_info,  create_time,           all_info, create_time); \
+               STRUCT_EQUAL(all_info,  access_time,           all_info, access_time); \
+               STRUCT_EQUAL(all_info,  write_time,            all_info, write_time); \
+               STRUCT_EQUAL(all_info,  change_time,           all_info, change_time); \
+                  VAL_EQUAL(all_info,  attrib,                all_info, attrib); \
+                  VAL_EQUAL(all_info,  alloc_size,            all_info, alloc_size); \
+                  VAL_EQUAL(all_info,  size,                  all_info, size); \
+                  VAL_EQUAL(all_info,  nlink,                 all_info, nlink); \
+                  VAL_EQUAL(all_info,  delete_pending,        all_info, delete_pending); \
+                  VAL_EQUAL(all_info,  directory,             all_info, directory); \
+                  VAL_EQUAL(all_info,  ea_size,               all_info, ea_size); \
+                  STR_EQUAL(all_info,  fname,                 all_info, fname);
+
+       ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
+
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STR_EQUAL(alt_name_info,  fname,   alt_name_info, fname);
+
+       ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
+
+#define TIME_CHECK_NT(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      nt_time_string(mem_ctx, &s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      nt_time_string(mem_ctx, &s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+#define TIME_CHECK_DOS(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+#define TIME_CHECK_UNX(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+       /* now check that all the times that are supposed to be equal are correct */
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.create_time;
+       printf("create_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, create_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, create_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   create_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   create_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    create_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   create_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.access_time;
+       printf("access_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, access_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, access_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   access_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   access_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    access_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   access_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.write_time;
+       printf("write_time : %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, write_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, write_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   write_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   write_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    write_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   write_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.change_time;
+       printf("change_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, change_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, change_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   change_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
+
+
+#define SIZE_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_size) { \
+               printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_size); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_size) { \
+               printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_size); \
+               ret = False; \
+       }} while (0)
+
+       s1 = fnum_find("STANDARD_INFO");
+       correct_size = s1->standard_info.out.size;
+       printf("size: %u\n", (unsigned)correct_size);
+       
+       SIZE_CHECK("GETATTR",                  getattr,                  size);
+       SIZE_CHECK("GETATTRE",                 getattre,                 size);
+       SIZE_CHECK("STANDARD",                 standard,                 size);
+       SIZE_CHECK("EA_SIZE",                  ea_size,                  size);
+       SIZE_CHECK("STANDARD_INFO",            standard_info,            size);
+       SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            size);
+       SIZE_CHECK("ALL_INFO",                 all_info,                 size);
+       SIZE_CHECK("ALL_INFORMATION",          all_info,                 size);
+       SIZE_CHECK("COMPRESSION_INFO",         compression_info,         compressed_size);
+       SIZE_CHECK("COMPRESSION_INFORMATION",  compression_info,         compressed_size);
+       SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
+       if (!skip_streams) {
+               SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].size);
+               SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].size);
+       }
+
+
+       s1 = fnum_find("STANDARD_INFO");
+       correct_size = s1->standard_info.out.alloc_size;
+       printf("alloc_size: %u\n", (unsigned)correct_size);
+       
+       SIZE_CHECK("GETATTRE",                 getattre,                 alloc_size);
+       SIZE_CHECK("STANDARD",                 standard,                 alloc_size);
+       SIZE_CHECK("EA_SIZE",                  ea_size,                  alloc_size);
+       SIZE_CHECK("STANDARD_INFO",            standard_info,            alloc_size);
+       SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            alloc_size);
+       SIZE_CHECK("ALL_INFO",                 all_info,                 alloc_size);
+       SIZE_CHECK("ALL_INFORMATION",          all_info,                 alloc_size);
+       SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
+       if (!skip_streams) {
+               SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].alloc_size);
+               SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].alloc_size);
+       }
+
+#define ATTRIB_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_attrib) { \
+               printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_attrib); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_attrib) { \
+               printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_attrib); \
+               ret = False; \
+       }} while (0)
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_attrib = s1->basic_info.out.attrib;
+       printf("attrib: 0x%x\n", (unsigned)correct_attrib);
+       
+       ATTRIB_CHECK("GETATTR",                   getattr,                   attrib);
+       ATTRIB_CHECK("GETATTRE",                  getattre,                  attrib);
+       ATTRIB_CHECK("STANDARD",                  standard,                  attrib);
+       ATTRIB_CHECK("BASIC_INFO",                basic_info,                attrib);
+       ATTRIB_CHECK("BASIC_INFORMATION",         basic_info,                attrib);
+       ATTRIB_CHECK("EA_SIZE",                   ea_size,                   attrib);
+       ATTRIB_CHECK("ALL_INFO",                  all_info,                  attrib);
+       ATTRIB_CHECK("ALL_INFORMATION",           all_info,                  attrib);
+       ATTRIB_CHECK("NETWORK_OPEN_INFORMATION",  network_open_information,  attrib);
+       ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
+
+       correct_name = fname;
+       printf("name: %s\n", correct_name);
+
+#define NAME_CHECK(sname, stype, tfield, flags) do { \
+       s1 = fnum_find(sname); \
+       if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name) != 0) || \
+                       wire_bad_flags(&s1->stype.out.tfield, flags)) { \
+               printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
+                      s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name)) != 0 || \
+                       wire_bad_flags(&s1->stype.out.tfield, flags)) { \
+               printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
+                      s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+               ret = False; \
+       }} while (0)
+
+       NAME_CHECK("NAME_INFO",        name_info, fname, STR_UNICODE);
+       NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
+
+       /* the ALL_INFO file name is the full path on the filesystem */
+       s1 = fnum_find("ALL_INFO");
+       if (s1 && !s1->all_info.out.fname.s) {
+               printf("ALL_INFO didn't give a filename\n");
+               ret = False;
+       }
+       if (s1 && s1->all_info.out.fname.s) {
+               char *p = strrchr(s1->all_info.out.fname.s, '\\');
+               if (!p) {
+                       printf("Not a full path in all_info/fname? - '%s'\n", 
+                              s1->all_info.out.fname.s);
+                       ret = False;
+               } else {
+                       if (strcmp(correct_name, p) != 0) {
+                               printf("incorrect basename in all_info/fname - '%s'\n",
+                                      s1->all_info.out.fname.s);
+                               ret = False;
+                       }
+               }
+               if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE)) {
+                       printf("Should not null terminate all_info/fname\n");
+                       ret = False;
+               }
+       }
+
+       s1 = fnum_find("ALT_NAME_INFO");
+       correct_name = s1->alt_name_info.out.fname.s;
+       printf("alt_name: %s\n", correct_name);
+
+       NAME_CHECK("ALT_NAME_INFO",        alt_name_info, fname, STR_UNICODE);
+       NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
+
+       /* and make sure we can open by alternate name */
+       cli_close(cli, fnum);
+       fnum = cli_nt_create_full(cli, correct_name, 0, NT_ACCESS_GENERIC_ALL_ACCESS, 
+                                 FILE_ATTRIBUTE_NORMAL,
+                                 NTCREATEX_SHARE_ACCESS_DELETE|
+                                 NTCREATEX_SHARE_ACCESS_READ|
+                                 NTCREATEX_SHARE_ACCESS_WRITE, 
+                                 FILE_OVERWRITE_IF, 
+                                 0, 0);
+       if (fnum == -1) {
+               printf("Unable to open by alt_name - %s\n", cli_errstr(cli));
+               ret = False;
+       }
+
+       if (!skip_streams) {
+               correct_name = "::$DATA";
+               printf("stream_name: %s\n", correct_name);
+
+               NAME_CHECK("STREAM_INFO",        stream_info, streams[0].stream_name, STR_UNICODE);
+               NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
+       }
+
+       /* make sure the EAs look right */
+       s1 = fnum_find("ALL_EAS");
+       if (s1) {
+               printf("ea_size: %d\n", s1->all_eas.out.ea_size);
+               for (i=0;i<s1->all_eas.out.num_eas;i++) {
+                       printf("  flags=%d %s=%*.*s\n", 
+                              s1->all_eas.out.eas[i].flags,
+                              s1->all_eas.out.eas[i].name.s,
+                              s1->all_eas.out.eas[i].value.length,
+                              s1->all_eas.out.eas[i].value.length,
+                              s1->all_eas.out.eas[i].value.data);
+               }
+       }
+
+
+#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
+       s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname1); s2 = fname_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fnum_find(sname1); s2 = fname_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname1); s2 = fnum_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       }} while (0)
+
+       VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, 
+                 "ALL_INFO",      all_info,      delete_pending);
+       VAL_CHECK("STANDARD_INFO", standard_info, directory, 
+                 "ALL_INFO",      all_info,      directory);
+       VAL_CHECK("STANDARD_INFO", standard_info, nlink, 
+                 "ALL_INFO",      all_info,      nlink);
+       VAL_CHECK("EA_INFO",       ea_info,       ea_size, 
+                 "ALL_INFO",      all_info,      ea_size);
+       VAL_CHECK("ALL_EAS",       all_eas,       ea_size, 
+                 "ALL_INFO",      all_info,      ea_size);
+       VAL_CHECK("EA_SIZE",       ea_size,       ea_size, 
+                 "ALL_INFO",      all_info,      ea_size);
+
+#define UNKNOWN_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != 0) { \
+               printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+                       #stype, #tfield, \
+                      (unsigned)s1->stype.out.tfield); \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != 0) { \
+               printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+                       #stype, #tfield, \
+                      (unsigned)s1->stype.out.tfield); \
+       }} while (0)
+
+       /* now get a bit fancier .... */
+       
+       /* when we set the delete disposition then the link count should drop
+          to 0 and delete_pending should be 1 */
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/qfsinfo.c b/source4/torture/qfsinfo.c
new file mode 100644 (file)
index 0000000..fc4947e
--- /dev/null
@@ -0,0 +1,286 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_QFS_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+static struct {
+       const char *name;
+       enum fsinfo_level level;
+       NTSTATUS status;
+       union smb_fsinfo fsinfo;
+} levels[] = {
+       {"DSKATTR",               RAW_QFS_DSKATTR, },
+       {"ALLOCATION",            RAW_QFS_ALLOCATION, },
+       {"VOLUME",                RAW_QFS_VOLUME, },
+       {"VOLUME_INFO",           RAW_QFS_VOLUME_INFO, },
+       {"SIZE_INFO",             RAW_QFS_SIZE_INFO, },
+       {"DEVICE_INFO",           RAW_QFS_DEVICE_INFO, },
+       {"ATTRIBUTE_INFO",        RAW_QFS_ATTRIBUTE_INFO, },
+       {"VOLUME_INFORMATION",    RAW_QFS_VOLUME_INFORMATION, },
+       {"SIZE_INFORMATION",      RAW_QFS_SIZE_INFORMATION, },
+       {"DEVICE_INFORMATION",    RAW_QFS_DEVICE_INFORMATION, },
+       {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, },
+       {"QUOTA_INFORMATION",     RAW_QFS_QUOTA_INFORMATION, },
+       {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, },
+       {"OBJECTID_INFORMATION",  RAW_QFS_OBJECTID_INFORMATION, },
+       { NULL, }
+};
+
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fsinfo *find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (strcmp(name, levels[i].name) == 0) {
+                       return &levels[i].fsinfo;
+               }
+       }
+       return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+        printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+               #n1, #v1, (uint_t)s1->n1.out.v1, \
+               #n2, #v2, (uint_t)s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do {if (!s1->n1.out.v1 && !s2->n2.out.v2) return True; \
+        if (!s1->n1.out.v1 || !s2->n2.out.v2) return False; \
+        if (strcmp(s1->n1.out.v1, s2->n2.out.v2)) { \
+          printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \
+               #n1, #v1, s1->n1.out.v1, \
+               #n2, #v2, s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+          ret = False; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+        printf("%s/%s != %s/%s at %s(%d)\n", \
+               #n1, #v1, \
+               #n2, #v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure 
+   we zero-fill */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+        printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+               #n1, #v1, \
+              (uint_t)s1->n1.out.v1, \
+              (uint_t)s1->n1.out.v1, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* basic testing of all RAW_QFS_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+
+   Some of the consistency tests assume that the target filesystem is
+   quiescent, which is sometimes hard to achieve
+*/
+BOOL torture_qfsinfo(int dummy)
+{
+       struct cli_state *cli;
+       int i;
+       BOOL ret = True;
+       int count;
+       union smb_fsinfo *s1, *s2;      
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_qfsinfo");
+       
+       /* scan all the levels, pulling the results */
+       for (i=0; levels[i].name; i++) {
+               levels[i].fsinfo.generic.level = levels[i].level;
+               levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo);
+       }
+
+       /* check for completely broken levels */
+       for (count=i=0; levels[i].name; i++) {
+               if (!NT_STATUS_IS_OK(levels[i].status)) {
+                       printf("ERROR: level %s failed - %s\n", levels[i].name, nt_errstr(levels[i].status));
+                       count++;
+               }
+       }
+
+       if (count != 0) {
+               ret = False;
+               printf("%d levels failed\n", count);
+               if (count > 10) {
+                       printf("too many level failures - giving up\n");
+                       goto done;
+               }
+       }
+
+       /* check for correct aliases */
+       s1 = find("SIZE_INFO");
+       s2 = find("SIZE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
+               VAL_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
+               VAL_EQUAL(size_info, sectors_per_unit,  size_info, sectors_per_unit);
+               VAL_EQUAL(size_info, bytes_per_sector,  size_info, bytes_per_sector);
+       }       
+
+       s1 = find("DEVICE_INFO");
+       s2 = find("DEVICE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(device_info, device_type,     device_info, device_type);
+               VAL_EQUAL(device_info, characteristics, device_info, characteristics);
+       }       
+
+       s1 = find("VOLUME_INFO");
+       s2 = find("VOLUME_INFORMATION");
+       if (s1 && s2) {
+               STRUCT_EQUAL(volume_info, create_time,    volume_info, create_time);
+               VAL_EQUAL   (volume_info, serial_number,  volume_info, serial_number);
+               STR_EQUAL   (volume_info, volume_name.s,    volume_info, volume_name.s);
+               printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
+       }       
+
+       s1 = find("ATTRIBUTE_INFO");
+       s2 = find("ATTRIBUTE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(attribute_info, fs_attr,    
+                         attribute_info, fs_attr);
+               VAL_EQUAL(attribute_info, max_file_component_length, 
+                         attribute_info, max_file_component_length);
+               STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
+               printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
+       }       
+
+       /* check for consistent disk sizes */
+       s1 = find("DSKATTR");
+       s2 = find("ALLOCATION");
+       if (s1 && s2) {
+               double size1, size2;
+               double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+               size1 = 1.0 * 
+                       s1->dskattr.out.units_total * 
+                       s1->dskattr.out.blocks_per_unit * 
+                       s1->dskattr.out.block_size / scale;
+               size2 = 1.0 *
+                       s2->allocation.out.sectors_per_unit *
+                       s2->allocation.out.total_alloc_units *
+                       s2->allocation.out.bytes_per_sector / scale;
+               if (ABS(size1 - size2) > 1) {
+                       printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
+                              size1, size2);
+                       ret = False;
+               }
+               printf("total disk = %.0f MB\n", size1*scale/1.0e6);
+       }
+
+       /* and for consistent free disk space */
+       s1 = find("DSKATTR");
+       s2 = find("ALLOCATION");
+       if (s1 && s2) {
+               double size1, size2;
+               double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+               size1 = 1.0 * 
+                       s1->dskattr.out.units_free * 
+                       s1->dskattr.out.blocks_per_unit * 
+                       s1->dskattr.out.block_size / scale;
+               size2 = 1.0 *
+                       s2->allocation.out.sectors_per_unit *
+                       s2->allocation.out.avail_alloc_units *
+                       s2->allocation.out.bytes_per_sector / scale;
+               if (ABS(size1 - size2) > 1) {
+                       printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
+                              size1, size2);
+                       ret = False;
+               }
+               printf("free disk = %.0f MB\n", size1*scale/1.0e6);
+       }
+       
+       /* volume info consistency */
+       s1 = find("VOLUME");
+       s2 = find("VOLUME_INFO");
+       if (s1 && s2) {
+               VAL_EQUAL(volume, serial_number,  volume_info, serial_number);
+               STR_EQUAL(volume, volume_name.s,  volume_info, volume_name.s);
+       }       
+
+       /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
+          available allocation units, not the total */
+       s1 = find("SIZE_INFO");
+       s2 = find("FULL_SIZE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
+               VAL_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
+               VAL_EQUAL(size_info, sectors_per_unit,  full_size_information, sectors_per_unit);
+               VAL_EQUAL(size_info, bytes_per_sector,  full_size_information, bytes_per_sector);
+       }       
+
+       /* check for non-zero unknown fields - if we find them
+          they might give us some hints */
+       s1 = find("QUOTA_INFORMATION");
+       if (s1) {
+               VAL_UNKNOWN(quota_information, unknown[0]);
+               VAL_UNKNOWN(quota_information, unknown[1]);
+               VAL_UNKNOWN(quota_information, unknown[2]);
+       }
+
+       s1 = find("OBJECTID_INFORMATION");
+       if (s1) {
+               VAL_UNKNOWN(objectid_information, unknown[0]);
+               VAL_UNKNOWN(objectid_information, unknown[1]);
+               VAL_UNKNOWN(objectid_information, unknown[2]);
+               VAL_UNKNOWN(objectid_information, unknown[3]);
+               VAL_UNKNOWN(objectid_information, unknown[4]);
+               VAL_UNKNOWN(objectid_information, unknown[5]);
+       }
+
+
+#define STR_CHECK(sname, stype, field, flags) do { \
+       s1 = find(sname); \
+       if (s1) { \
+               if (wire_bad_flags(&s1->stype.out.field, flags)) { \
+                       printf("(%d) incorrect string termination in %s/%s\n", \
+                              __LINE__, #stype, #field); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+       /* check for correct termination */
+       STR_CHECK("VOLUME",                volume,         volume_name, 0);
+       STR_CHECK("VOLUME_INFO",           volume_info,    volume_name, STR_UNICODE);
+       STR_CHECK("VOLUME_INFORMATION",    volume_info,    volume_name, STR_UNICODE);
+       STR_CHECK("ATTRIBUTE_INFO",        attribute_info, fs_type, STR_UNICODE);
+       STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
+
+done:
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c
new file mode 100644 (file)
index 0000000..3364c39
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+   Unix SMB/CIFS implementation.
+   chkpath individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\rawchkpath"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+static BOOL test_chkpath(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       struct smb_chkpath io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum = -1;
+
+       io.in.path = BASEDIR;
+
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.in.path = BASEDIR "\\nodir";
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+       fnum = create_complex_file(cli, mem_ctx, BASEDIR "\\test.txt");
+       if (fnum == -1) {
+               printf("failed to open test.txt - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       io.in.path = BASEDIR "\\test.txt";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+       
+       if (!torture_set_file_attribute(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN)) {
+               printf("failed to set basedir hidden\n");
+               ret = False;
+               goto done;
+       }
+
+       io.in.path = BASEDIR;
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.in.path = "";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.in.path = ".";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+       io.in.path = "\\";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.in.path = "\\.";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+       io.in.path = "\\..";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+       io.in.path = BASEDIR "\\..";
+       printf("testing %s\n", io.in.path);
+       status = smb_raw_chkpath(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       cli_close(cli, fnum);
+       return ret;
+}
+
+/* 
+   basic testing of chkpath calls 
+*/
+BOOL torture_raw_chkpath(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_chkpath");
+
+       if (cli_deltree(cli, BASEDIR) == -1) {
+               printf("Failed to clean " BASEDIR "\n");
+               return False;
+       }
+       if (!cli_mkdir(cli, BASEDIR)) {
+               printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!test_chkpath(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/close.c b/source4/torture/raw/close.c
new file mode 100644 (file)
index 0000000..40bb57f
--- /dev/null
@@ -0,0 +1,170 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_CLOSE_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+/* basic testing of all RAW_CLOSE_* calls 
+*/
+BOOL torture_raw_close(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+       union smb_close io;
+       struct smb_flush io_flush;
+       int fnum;
+       const char *fname = "\\torture_close.txt";
+       time_t basetime = (time(NULL) + 3*86400) & ~1;
+       union smb_fileinfo finfo, finfo2;
+       NTSTATUS status;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_close");
+
+#define REOPEN do { \
+       fnum = create_complex_file(cli, mem_ctx, fname); \
+       if (fnum == -1) { \
+               printf("(%d) Failed to create %s\n", __LINE__, fname); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+       REOPEN;
+
+       io.close.level = RAW_CLOSE_CLOSE;
+       io.close.in.fnum = fnum;
+       io.close.in.write_time = basetime;
+       status = smb_raw_close(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_close(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+       
+       printf("testing close.in.write_time\n");
+
+       /* the file should have the write time set */
+       finfo.generic.in.fname = fname;
+       finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       if (basetime != nt_time_to_unix(&finfo.all_info.out.write_time)) {
+               printf("Incorrect write time on file - %s - %s\n",
+                      time_string(mem_ctx, basetime), 
+                      nt_time_string(mem_ctx, &finfo.all_info.out.write_time));
+               dump_all_info(mem_ctx, &finfo);
+               ret = False;
+       }
+
+       printf("testing other times\n");
+
+       /* none of the other times should be set to that time */
+       if (nt_time_equal(&finfo.all_info.out.write_time, 
+                         &finfo.all_info.out.access_time) ||
+           nt_time_equal(&finfo.all_info.out.write_time, 
+                         &finfo.all_info.out.create_time) ||
+           nt_time_equal(&finfo.all_info.out.write_time, 
+                         &finfo.all_info.out.change_time)) {
+               printf("Incorrect times after close - only write time should be set\n");
+               dump_all_info(mem_ctx, &finfo);
+               ret = False;
+       }
+           
+
+       cli_unlink(cli, fname);
+       REOPEN;
+
+       finfo2.generic.in.fname = fname;
+       finfo2.generic.level = RAW_FILEINFO_ALL_INFO;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.close.level = RAW_CLOSE_CLOSE;
+       io.close.in.fnum = fnum;
+       io.close.in.write_time = 0;
+       status = smb_raw_close(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* the file should have the write time set equal to access time */
+       finfo.generic.in.fname = fname;
+       finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       if (!nt_time_equal(&finfo.all_info.out.write_time, 
+                          &finfo2.all_info.out.write_time)) {
+               printf("Incorrect write time on file - 0 time should be ignored\n");
+               dump_all_info(mem_ctx, &finfo);
+               ret = False;
+       }
+
+       printf("testing splclose\n");
+
+       /* check splclose on a file */
+       REOPEN;
+       io.splclose.level = RAW_CLOSE_SPLCLOSE;
+       io.splclose.in.fnum = fnum;
+       status = smb_raw_close(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+       printf("testing flush\n");
+       cli_close(cli, fnum);
+
+       io_flush.in.fnum = fnum;
+       status = smb_raw_flush(cli->tree, &io_flush);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       io_flush.in.fnum = 0xffff;
+       status = smb_raw_flush(cli->tree, &io_flush);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       REOPEN;
+
+       io_flush.in.fnum = fnum;
+       status = smb_raw_flush(cli->tree, &io_flush);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Testing SMBexit\n");
+       smb_raw_exit(cli->session);
+
+       io_flush.in.fnum = fnum;
+       status = smb_raw_flush(cli->tree, &io_flush);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c
new file mode 100644 (file)
index 0000000..c19fea4
--- /dev/null
@@ -0,0 +1,388 @@
+/* 
+   Unix SMB/CIFS implementation.
+   test suite for session setup operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\rawcontext"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+/*
+  test session ops
+*/
+static BOOL test_session(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       NTSTATUS status;
+       BOOL ret = True;
+       char *username, *domain, *password;
+       struct cli_session *session;
+       struct cli_tree *tree;
+       union smb_sesssetup setup;
+       union smb_open io;
+       union smb_write wr;
+       union smb_close cl;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+       char c = 1;
+
+       printf("TESTING SESSION HANDLING\n");
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       username = lp_parm_string(-1, "torture", "username");
+       password = lp_parm_string(-1, "torture", "password");
+       domain = lp_workgroup();
+
+       printf("create a second security context on the same transport\n");
+       session = cli_session_init(cli->transport);
+       setup.generic.level = RAW_SESSSETUP_GENERIC;
+       setup.generic.in.sesskey = cli->transport->negotiate.sesskey;
+       setup.generic.in.capabilities = 0; /* ignored in secondary session setup */
+       setup.generic.in.password = password;
+       setup.generic.in.user = username;
+       setup.generic.in.domain = domain;
+
+       status = smb_raw_session_setup(session, mem_ctx, &setup);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       session->vuid = setup.generic.out.vuid;
+
+       printf("use the same tree as the existing connection\n");
+       tree = cli_tree_init(session);
+       tree->tid = cli->tree->tid;
+       cli->tree->reference_count++;
+
+       printf("vuid1=%d vuid2=%d\n", cli->session->vuid, session->vuid);
+
+       printf("create a file using the new vuid\n");
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.flags = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+       status = smb_raw_open(tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       printf("write using the old vuid\n");
+       wr.generic.level = RAW_WRITE_WRITEX;
+       wr.writex.in.fnum = fnum;
+       wr.writex.in.offset = 0;
+       wr.writex.in.wmode = 0;
+       wr.writex.in.remaining = 0;
+       wr.writex.in.count = 1;
+       wr.writex.in.data = &c;
+
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("write with the new vuid\n");
+       status = smb_raw_write(tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+       printf("logoff the new vuid\n");
+       status = smb_raw_ulogoff(session);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("the new vuid should not now be accessible\n");
+       status = smb_raw_write(tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("the fnum should have been auto-closed\n");
+       cl.close.level = RAW_CLOSE_CLOSE;
+       cl.close.in.fnum = fnum;
+       cl.close.in.write_time = 0;
+       status = smb_raw_close(cli->tree, &cl);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       /* close down the new tree, which will also close the session
+          as the reference count will be 0 */
+       cli_tree_close(tree);
+       
+done:
+       return ret;
+}
+
+
+/*
+  test tree ops
+*/
+static BOOL test_tree(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       NTSTATUS status;
+       BOOL ret = True;
+       char *share;
+       struct cli_tree *tree;
+       union smb_tcon tcon;
+       union smb_open io;
+       union smb_write wr;
+       union smb_close cl;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+       char c = 1;
+
+       printf("TESTING TREE HANDLING\n");
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       share = lp_parm_string(-1, "torture", "share");
+
+       printf("create a second tree context on the same session\n");
+       tree = cli_tree_init(cli->session);
+
+       tcon.generic.level = RAW_TCON_TCONX;
+       tcon.tconx.in.flags = 0;
+       tcon.tconx.in.password = data_blob(NULL, 0);
+       tcon.tconx.in.path = share;
+       tcon.tconx.in.device = "A:";    
+       status = smb_tree_connect(tree, mem_ctx, &tcon);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       tree->tid = tcon.tconx.out.cnum;
+       printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
+
+       printf("try a tconx with a bad device type\n");
+       tcon.tconx.in.device = "FOO";   
+       status = smb_tree_connect(tree, mem_ctx, &tcon);
+       CHECK_STATUS(status, NT_STATUS_BAD_DEVICE_TYPE);
+
+
+       printf("create a file using the new tid\n");
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.flags = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+       status = smb_raw_open(tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       printf("write using the old tid\n");
+       wr.generic.level = RAW_WRITE_WRITEX;
+       wr.writex.in.fnum = fnum;
+       wr.writex.in.offset = 0;
+       wr.writex.in.wmode = 0;
+       wr.writex.in.remaining = 0;
+       wr.writex.in.count = 1;
+       wr.writex.in.data = &c;
+
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("write with the new tid\n");
+       status = smb_raw_write(tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+       printf("disconnect the new tid\n");
+       status = smb_tree_disconnect(tree);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("the new tid should not now be accessible\n");
+       status = smb_raw_write(tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("the fnum should have been auto-closed\n");
+       cl.close.level = RAW_CLOSE_CLOSE;
+       cl.close.in.fnum = fnum;
+       cl.close.in.write_time = 0;
+       status = smb_raw_close(cli->tree, &cl);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       /* close down the new tree */
+       cli_tree_close(tree);
+       
+done:
+       return ret;
+}
+
+
+/*
+  test pid ops
+*/
+static BOOL test_pid(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       NTSTATUS status;
+       BOOL ret = True;
+       union smb_open io;
+       union smb_write wr;
+       union smb_close cl;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+       char c = 1;
+       uint16 pid1, pid2;
+
+       printf("TESTING PID HANDLING\n");
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("create a second pid\n");
+       pid1 = cli->session->pid;
+       pid2 = pid1+1;
+
+       printf("pid1=%d pid2=%d\n", pid1, pid2);
+
+       printf("create a file using the new pid\n");
+       cli->session->pid = pid2;
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.flags = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       printf("write using the old pid\n");
+       cli->session->pid = pid1;
+       wr.generic.level = RAW_WRITE_WRITEX;
+       wr.writex.in.fnum = fnum;
+       wr.writex.in.offset = 0;
+       wr.writex.in.wmode = 0;
+       wr.writex.in.remaining = 0;
+       wr.writex.in.count = 1;
+       wr.writex.in.data = &c;
+
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+       printf("write with the new pid\n");
+       cli->session->pid = pid2;
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+       printf("exit the old pid\n");
+       cli->session->pid = pid1;
+       status = smb_raw_exit(cli->session);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("the fnum should still be accessible\n");
+       cli->session->pid = pid1;
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(wr.writex.out.nwritten, 1);
+
+       printf("exit the new pid\n");
+       cli->session->pid = pid2;
+       status = smb_raw_exit(cli->session);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("the fnum should not now be accessible\n");
+       cli->session->pid = pid1;
+       status = smb_raw_write(cli->tree, &wr);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("the fnum should have been auto-closed\n");
+       cl.close.level = RAW_CLOSE_CLOSE;
+       cl.close.in.fnum = fnum;
+       cl.close.in.write_time = 0;
+       status = smb_raw_close(cli->tree, &cl);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+done:
+       return ret;
+}
+
+
+/* 
+   basic testing of session/tree context calls
+*/
+BOOL torture_raw_context(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_context");
+
+       if (!test_session(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_tree(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_pid(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/ioctl.c b/source4/torture/raw/ioctl.c
new file mode 100644 (file)
index 0000000..d55db4c
--- /dev/null
@@ -0,0 +1,156 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ioctl individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\rawioctl"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+/* test some ioctls */
+static BOOL test_ioctl(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       struct smb_ioctl ctl;
+       int fnum;
+       NTSTATUS status;
+       BOOL ret = True;
+       const char *fname = BASEDIR "\\test.dat";
+
+       printf("TESTING IOCTL FUNCTIONS\n");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("Failed to create test.dat - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying QUERY_JOB_INFO\n");
+       ctl.in.fnum = fnum;
+       ctl.in.request = IOCTL_QUERY_JOB_INFO;
+
+       status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl);
+       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+       printf("Trying bad handle\n");
+       ctl.in.fnum = fnum+1;
+       status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl);
+       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+done:
+       cli_close(cli, fnum);
+       return ret;
+}
+
+/* test some filesystem control functions */
+static BOOL test_fsctl(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       int fnum;
+       NTSTATUS status;
+       BOOL ret = True;
+       const char *fname = BASEDIR "\\test.dat";
+       struct smb_ntioctl nt;
+
+       printf("\nTESTING FSCTL FUNCTIONS\n");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("Failed to create test.dat - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("trying sparse file\n");
+       nt.in.function = FSCTL_SET_SPARSE;
+       nt.in.fnum = fnum;
+       nt.in.fsctl = True;
+       nt.in.filter = 0;
+
+       status = smb_raw_ntioctl(cli->tree, &nt);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Trying bad handle\n");
+       nt.in.fnum = fnum+1;
+       status = smb_raw_ntioctl(cli->tree, &nt);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+#if 0
+       nt.in.fnum = fnum;
+       for (i=0;i<100;i++) {
+               nt.in.function = FSCTL_FILESYSTEM + (i<<2);
+               status = smb_raw_ntioctl(cli->tree, &nt);
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+                       printf("filesystem fsctl 0x%x - %s\n",
+                              i, nt_errstr(status));
+               }
+       }
+#endif
+
+done:
+       cli_close(cli, fnum);
+       return ret;
+}
+
+/* 
+   basic testing of some ioctl calls 
+*/
+BOOL torture_raw_ioctl(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_ioctl");
+
+       if (cli_deltree(cli, BASEDIR) == -1) {
+               printf("Failed to clean " BASEDIR "\n");
+               return False;
+       }
+       if (!cli_mkdir(cli, BASEDIR)) {
+               printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!test_ioctl(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_fsctl(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c
new file mode 100644 (file)
index 0000000..b0b0b56
--- /dev/null
@@ -0,0 +1,216 @@
+/* 
+   Unix SMB/CIFS implementation.
+   test suite for various lock operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define BASEDIR "\\testlock"
+
+
+/*
+  test SMBlock and SMBunlock ops
+*/
+static BOOL test_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_LOCK_LOCK\n");
+       io.generic.level = RAW_LOCK_LOCK;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying 0/0 lock\n");
+       io.lock.level = RAW_LOCK_LOCK;
+       io.lock.in.fnum = fnum;
+       io.lock.in.count = 0;
+       io.lock.in.offset = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli->session->pid++;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli->session->pid--;
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Trying 0/1 lock\n");
+       io.lock.level = RAW_LOCK_LOCK;
+       io.lock.in.fnum = fnum;
+       io.lock.in.count = 1;
+       io.lock.in.offset = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli->session->pid++;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+       cli->session->pid--;
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       printf("Trying max lock\n");
+       io.lock.level = RAW_LOCK_LOCK;
+       io.lock.in.fnum = fnum;
+       io.lock.in.count = 4000;
+       io.lock.in.offset = ~0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli->session->pid++;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       cli->session->pid--;
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       printf("Trying wrong pid unlock\n");
+       io.lock.level = RAW_LOCK_LOCK;
+       io.lock.in.fnum = fnum;
+       io.lock.in.count = 4002;
+       io.lock.in.offset = 10001;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli->session->pid++;
+       io.lock.level = RAW_LOCK_UNLOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+       cli->session->pid--;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test locking&X ops
+*/
+static BOOL test_lockx(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_lock io;
+       struct smb_lock_entry lock[1];
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_LOCK_LOCKX\n");
+       io.generic.level = RAW_LOCK_LOCKX;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.fnum = fnum;
+       io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 0;
+       lock[0].count = 0xFFFFFFFF;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of lock calls
+*/
+BOOL torture_raw_lock(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_lock");
+
+       if (!test_lockx(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_lock(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/missing.txt b/source4/torture/raw/missing.txt
new file mode 100644 (file)
index 0000000..8e026b7
--- /dev/null
@@ -0,0 +1,157 @@
+- all messaging commands
+
+- writebraw
+
+- writebmpx
+
+- acl ops
+
+- readbmpx
+
+- rap commands
+
+- rpc commands
+
+- SMBcopy
+
+- SMBtcon
+
+- SMBecho
+
+- SMBfunique
+
+- SMBsearch vs SMBffirst?
+
+- SMBfclose
+
+- SMBkeepalive
+
+- secondary trans2 and nttrans
+
+- trans2 ioctl
+
+- trans2 session setup
+
+- trans2 DFS ops
+
+- unix ops
+
+--------------
+done:
+
+mkdir
+rmdir
+open
+create
+close
+flush
+unlink
+mv
+getatr
+setatr
+read
+write
+lock
+unlock
+ctemp
+mknew
+chkpath
+exit
+lseek
+tconX
+tdis
+negprot
+dskattr
+search
+lockread
+writeunlock
+readbraw
+setattrE
+getattrE
+lockingX
+ioctl
+openX
+readX
+writeX
+sesssetupX
+trans2
+findclose
+ulogoffX
+nttrans
+ntcreateX
+ntcancel
+trans2_open
+trans2_findfirst
+trans2_findnext?
+trans2_qfsinfo
+trans2_setfsinfo
+trans2_qpathinfo
+trans2_setpathinfo
+trans2_qfileinfo
+trans2_setfileinfo
+trans2_fsctl
+trans2_mkdir
+trans2_findnext
+SMB_QFS_ALLOCATION
+SMB_QFS_VOLUME
+SMB_QFS_VOLUME_INFO
+SMB_QFS_SIZE_INFO
+SMB_QFS_DEVICE_INFO
+SMB_QFS_ATTRIBUTE_INFO
+SMB_QFS_VOLUME_INFORMATION
+SMB_QFS_SIZE_INFORMATION
+SMB_QFS_DEVICE_INFORMATION
+SMB_QFS_ATTRIBUTE_INFORMATION
+SMB_QFS_QUOTA_INFORMATION
+SMB_QFS_FULL_SIZE_INFORMATION
+SMB_QFS_OBJECTID_INFORMATION
+SMB_QFILEINFO_STANDARD
+SMB_QFILEINFO_EA_SIZE
+SMB_QFILEINFO_ALL_EAS
+SMB_QFILEINFO_IS_NAME_VALID
+SMB_QFILEINFO_BASIC_INFO
+SMB_QFILEINFO_STANDARD_INFO
+SMB_QFILEINFO_EA_INFO
+SMB_QFILEINFO_NAME_INFO
+SMB_QFILEINFO_ALL_INFO
+SMB_QFILEINFO_ALT_NAME_INFO
+SMB_QFILEINFO_STREAM_INFO
+SMB_QFILEINFO_COMPRESSION_INFO
+SMB_QFILEINFO_BASIC_INFORMATION
+SMB_QFILEINFO_STANDARD_INFORMATION
+SMB_QFILEINFO_INTERNAL_INFORMATION
+SMB_QFILEINFO_EA_INFORMATION
+SMB_QFILEINFO_ACCESS_INFORMATION
+SMB_QFILEINFO_NAME_INFORMATION
+SMB_QFILEINFO_POSITION_INFORMATION
+SMB_QFILEINFO_MODE_INFORMATION
+SMB_QFILEINFO_ALIGNMENT_INFORMATION
+SMB_QFILEINFO_ALL_INFORMATION
+SMB_QFILEINFO_ALT_NAME_INFORMATION
+SMB_QFILEINFO_STREAM_INFORMATION
+SMB_QFILEINFO_COMPRESSION_INFORMATION
+SMB_QFILEINFO_NETWORK_OPEN_INFORMATION
+SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION
+SMB_SFILEINFO_STANDARD
+SMB_SFILEINFO_EA_SET
+SMB_SFILEINFO_BASIC_INFO
+SMB_SFILEINFO_DISPOSITION_INFO
+SMB_SFILEINFO_ALLOCATION_INFO
+SMB_SFILEINFO_END_OF_FILE_INFO
+SMB_SFILEINFO_UNIX_BASIC
+SMB_SFILEINFO_UNIX_LINK
+SMB_SFILEINFO_BASIC_INFORMATION
+SMB_SFILEINFO_RENAME_INFORMATION
+SMB_SFILEINFO_DISPOSITION_INFORMATION
+SMB_SFILEINFO_POSITION_INFORMATION
+SMB_SFILEINFO_MODE_INFORMATION
+SMB_SFILEINFO_ALLOCATION_INFORMATION
+SMB_SFILEINFO_END_OF_FILE_INFORMATION
+SMB_FIND_STANDARD
+SMB_FIND_EA_SIZE
+SMB_FIND_DIRECTORY_INFO
+SMB_FIND_FULL_DIRECTORY_INFO
+SMB_FIND_NAME_INFO
+SMB_FIND_BOTH_DIRECTORY_INFO
+SMB_FIND_261
+SMB_FIND_262
diff --git a/source4/torture/raw/mkdir.c b/source4/torture/raw/mkdir.c
new file mode 100644 (file)
index 0000000..52120f0
--- /dev/null
@@ -0,0 +1,141 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_MKDIR_* and RAW_RMDIR_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+/*
+  test mkdir ops
+*/
+static BOOL test_mkdir(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_mkdir md;
+       struct smb_rmdir rd;
+       const char *path = "\\test_mkdir.dir";
+       NTSTATUS status;
+       BOOL ret = True;
+
+       /* cleanup */
+       cli_rmdir(cli, path);
+       cli_unlink(cli, path);
+
+       /* 
+          basic mkdir
+       */
+       md.mkdir.level = RAW_MKDIR_MKDIR;
+       md.mkdir.in.path = path;
+
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("testing mkdir collision\n");
+
+       /* 2nd create */
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       /* basic rmdir */
+       rd.in.path = path;
+       status = smb_raw_rmdir(cli->tree, &rd);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_rmdir(cli->tree, &rd);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+       printf("testing mkdir collision with file\n");
+
+       /* name collision with a file */
+       cli_close(cli, create_complex_file(cli, mem_ctx, path));
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       printf("testing rmdir with file\n");
+
+       /* delete a file with rmdir */
+       status = smb_raw_rmdir(cli->tree, &rd);
+       CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY);
+
+       cli_unlink(cli, path);
+
+       printf("testing invalid dir\n");
+
+       /* create an invalid dir */
+       md.mkdir.in.path = "..\\..\\..";
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+       
+       printf("testing t2mkdir\n");
+
+       /* try a t2mkdir - need to work out why this fails! */
+       md.t2mkdir.level = RAW_MKDIR_T2MKDIR;
+       md.t2mkdir.in.path = path;
+       md.t2mkdir.in.num_eas = 0;      
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+       printf("testing t2mkdir with EAs\n");
+
+       /* with EAs */
+       md.t2mkdir.in.num_eas = 1;
+       md.t2mkdir.in.eas = talloc(mem_ctx, sizeof(md.t2mkdir.in.eas[0]));
+       md.t2mkdir.in.eas[0].flags = 0;
+       md.t2mkdir.in.eas[0].name.s = "EAONE";
+       md.t2mkdir.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1);
+       status = smb_raw_mkdir(cli->tree, &md);
+       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+
+done:
+       cli_rmdir(cli, path);
+       cli_unlink(cli, path);
+       return ret;
+}
+
+
+/* 
+   basic testing of all RAW_MKDIR_* calls 
+*/
+BOOL torture_raw_mkdir(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_mkdir");
+
+       if (!test_mkdir(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c
new file mode 100644 (file)
index 0000000..05c5e3d
--- /dev/null
@@ -0,0 +1,298 @@
+/* 
+   Unix SMB/CIFS implementation.
+   basic raw test suite for multiplexing
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\test_mux"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+/*
+  test the delayed reply to a open that leads to a sharing violation
+*/
+static BOOL test_mux_open(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       struct cli_request *req;
+
+       printf("testing multiplexed open/open/close\n");
+
+       /*
+         file open with no share access
+       */
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.flags = 0;
+       io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = 0;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = BASEDIR "\\open.dat";
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       /* send an open that will conflict */
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+       /*
+         same request, but async
+       */
+       req = smb_raw_open_send(cli->tree, &io);
+       
+       /* and close the file */
+       cli_close(cli, fnum);
+
+       /* see if the async open succeeded */
+       status = smb_raw_open_recv(req, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       cli_close(cli, io.ntcreatex.out.fnum);
+
+done:
+       return ret;
+}
+
+
+/*
+  test a write that hits a byte range lock and send the close after the write
+*/
+static BOOL test_mux_write(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_write io;
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       struct cli_request *req;
+
+       printf("testing multiplexed lock/write/close\n");
+
+       fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("open failed in mux_write - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       cli->session->pid = 1;
+
+       /* lock a range */
+       if (!cli_lock(cli, fnum, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock failed in mux_write - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       cli->session->pid = 2;
+
+       /* send an async write */
+       io.generic.level = RAW_WRITE_WRITEX;
+       io.writex.in.fnum = fnum;
+       io.writex.in.offset = 0;
+       io.writex.in.wmode = 0;
+       io.writex.in.remaining = 0;
+       io.writex.in.count = 4;
+       io.writex.in.data = (void *)&fnum;      
+       req = smb_raw_write_send(cli->tree, &io);
+
+       /* unlock the range */
+       cli->session->pid = 1;
+       cli_unlock(cli, fnum, 0, 4);
+
+       /* and recv the async write reply */
+       status = smb_raw_write_recv(req, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       cli_close(cli, fnum);
+
+done:
+       return ret;
+}
+
+
+/*
+  test a lock that conflicts with an existing lock
+*/
+static BOOL test_mux_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       struct cli_request *req;
+       struct smb_lock_entry lock[1];
+
+       printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n");
+
+       fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("open failed in mux_write - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("establishing a lock\n");
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.fnum = fnum;
+       io.lockx.in.mode = 0;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.ulock_cnt = 0;
+       lock[0].pid = 1;
+       lock[0].offset = 0;
+       lock[0].count = 4;
+       io.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("the second lock will conflict with the first\n");
+       lock[0].pid = 2;
+       io.lockx.in.timeout = 1000;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       printf("this will too, but we'll unlock while waiting\n");
+       req = smb_raw_lock_send(cli->tree, &io);
+
+       printf("unlock the first range\n");
+       lock[0].pid = 1;
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.timeout = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("recv the async reply\n");
+       status = cli_request_simple_recv(req);
+       CHECK_STATUS(status, NT_STATUS_OK);     
+
+       printf("reopening with an exit\n");
+       smb_raw_exit(cli->session);
+       fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+
+       printf("Now trying with a cancel\n");
+
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.fnum = fnum;
+       io.lockx.in.mode = 0;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.ulock_cnt = 0;
+       lock[0].pid = 1;
+       lock[0].offset = 0;
+       lock[0].count = 4;
+       io.lockx.in.locks = &lock[0];
+
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       lock[0].pid = 2;
+       io.lockx.in.timeout = 1000;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       req = smb_raw_lock_send(cli->tree, &io);
+
+       /* cancel the blocking lock */
+       smb_raw_ntcancel(req);
+
+       lock[0].pid = 1;
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.timeout = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = cli_request_simple_recv(req);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);     
+
+       cli_close(cli, fnum);
+
+done:
+       return ret;
+}
+
+
+
+/* 
+   basic testing of multiplexing notify
+*/
+BOOL torture_raw_mux(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+               
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_mux");
+
+       /* cleanup */
+       if (cli_deltree(cli, BASEDIR) == -1) {
+               printf("Failed to cleanup " BASEDIR "\n");
+               ret = False;
+               goto done;
+       }
+
+
+       if (!cli_mkdir(cli, BASEDIR)) {
+               printf("Failed to create %s\n", BASEDIR);
+               ret = False;
+               goto done;
+       }
+
+       if (!test_mux_open(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_mux_write(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_mux_lock(cli, mem_ctx)) {
+               ret = False;
+       }
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c
new file mode 100644 (file)
index 0000000..a123dc6
--- /dev/null
@@ -0,0 +1,140 @@
+/* 
+   Unix SMB/CIFS implementation.
+   basic raw test suite for change notify
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\test_notify"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+#define CHECK_VAL(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) wrong value for %s  0x%x - 0x%x\n", \
+                      __LINE__, #v, (int)v, (int)correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_WSTR(field, value, flags) do { \
+       if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags)) { \
+               printf("(%d) %s [%s] != %s\n",  __LINE__, #field, field.s, value); \
+                       ret = False; \
+               goto done; \
+       }} while (0)
+
+
+/* 
+   basic testing of change notify
+*/
+BOOL torture_raw_notify(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+       struct smb_notify notify;
+       union smb_open io;
+       int fnum = -1;
+       struct cli_request *req;
+               
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_notify");
+
+       /* cleanup */
+       if (cli_deltree(cli, BASEDIR) == -1) {
+               printf("Failed to cleanup " BASEDIR "\n");
+               ret = False;
+               goto done;
+       }
+
+       /*
+         get a handle on the directory
+       */
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.flags = 0;
+       io.ntcreatex.in.access_mask = SA_RIGHT_FILE_ALL_ACCESS;
+       io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = BASEDIR;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       /* ask for a change notify */
+       notify.in.buffer_size = 4096;
+       notify.in.completion_filter = 0xFF;
+       notify.in.fnum = fnum;
+       notify.in.recursive = True;
+
+       printf("testing notify mkdir\n");
+
+       req = smb_raw_changenotify_send(cli->tree, &notify);
+       cli_mkdir(cli, BASEDIR "\\subdir-name");
+
+       status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       CHECK_VAL(notify.out.num_changes, 1);
+       CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED);
+       CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+       printf("testing notify rmdir\n");
+
+       req = smb_raw_changenotify_send(cli->tree, &notify);
+       cli_rmdir(cli, BASEDIR "\\subdir-name");
+
+       status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VAL(notify.out.num_changes, 1);
+       CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+       CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+       printf("testing notify cancel\n");
+
+       req = smb_raw_changenotify_send(cli->tree, &notify);
+       smb_raw_ntcancel(req);
+       cli_mkdir(cli, BASEDIR "\\subdir-name");
+       status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+       CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c
new file mode 100644 (file)
index 0000000..9f77eb3
--- /dev/null
@@ -0,0 +1,895 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_OPEN_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/* enum for whether reads/writes are possible on a file */
+enum rdwr_mode {RDWR_NONE, RDWR_RDONLY, RDWR_WRONLY, RDWR_RDWR};
+
+#define BASEDIR "\\rawopen"
+
+/*
+  check if a open file can be read/written
+*/
+static enum rdwr_mode check_rdwr(struct cli_state *cli, int fnum)
+{
+       char c = 1;
+       BOOL can_read  = (cli_read(cli, fnum, &c, 0, 1) == 1);
+       BOOL can_write = (cli_write(cli, fnum, 0, &c, 0, 1) == 1);
+       if ( can_read &&  can_write) return RDWR_RDWR;
+       if ( can_read && !can_write) return RDWR_RDONLY;
+       if (!can_read &&  can_write) return RDWR_WRONLY;
+       return RDWR_NONE;
+}
+
+/*
+  describe a RDWR mode as a string
+*/
+static const char *rdwr_string(enum rdwr_mode m)
+{
+       switch (m) {
+       case RDWR_NONE: return "NONE";
+       case RDWR_RDONLY: return "RDONLY";
+       case RDWR_WRONLY: return "WRONLY";
+       case RDWR_RDWR: return "RDWR";
+       }
+       return "-";
+}
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CREATE_FILE do { \
+       fnum = create_complex_file(cli, mem_ctx, fname); \
+       if (fnum == -1) { \
+               printf("(%d) Failed to create %s - %s\n", __LINE__, fname, cli_errstr(cli)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_RDWR(fnum, correct) do { \
+       enum rdwr_mode m = check_rdwr(cli, fnum); \
+       if (m != correct) { \
+               printf("(%d) Incorrect readwrite mode %s - expected %s\n", \
+                      __LINE__, rdwr_string(m), rdwr_string(correct)); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_TIME(t, field) do { \
+       time_t t1, t2; \
+       finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+       finfo.all_info.in.fname = fname; \
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \
+       CHECK_STATUS(status, NT_STATUS_OK); \
+       t1 = t & ~1; \
+       t2 = nt_time_to_unix(&finfo.all_info.out.field) & ~1; \
+       if (ABS(t1-t2) > 2) { \
+               printf("(%d) wrong time for field %s  %s - %s\n", \
+                      __LINE__, #field, \
+                      time_string(mem_ctx, t1), \
+                      time_string(mem_ctx, t2)); \
+               dump_all_info(mem_ctx, &finfo); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_NTTIME(t, field) do { \
+       NTTIME t2; \
+       finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+       finfo.all_info.in.fname = fname; \
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \
+       CHECK_STATUS(status, NT_STATUS_OK); \
+       t2 = finfo.all_info.out.field; \
+       if (!nt_time_equal(&t, &t2)) { \
+               printf("(%d) wrong time for field %s  %s - %s\n", \
+                      __LINE__, #field, \
+                      nt_time_string(mem_ctx, &t), \
+                      nt_time_string(mem_ctx, &t2)); \
+               dump_all_info(mem_ctx, &finfo); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_ALL_INFO(v, field) do { \
+       finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+       finfo.all_info.in.fname = fname; \
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \
+       CHECK_STATUS(status, NT_STATUS_OK); \
+       if ((v) != finfo.all_info.out.field) { \
+               printf("(%d) wrong value for field %s  0x%x - 0x%x\n", \
+                      __LINE__, #field, (int)v, (int)finfo.all_info.out.field); \
+               dump_all_info(mem_ctx, &finfo); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_VAL(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) wrong value for %s  0x%x - 0x%x\n", \
+                      __LINE__, #v, (int)v, (int)correct); \
+               ret = False; \
+       }} while (0)
+
+#define SET_ATTRIB(sattrib) do { \
+       union smb_setfileinfo sfinfo; \
+       sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; \
+       sfinfo.generic.file.fname = fname; \
+       ZERO_STRUCT(sfinfo.basic_info.in); \
+       sfinfo.basic_info.in.attrib = sattrib; \
+       status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+       if (!NT_STATUS_IS_OK(status)) { \
+               printf("(%d) Failed to set attrib 0x%x on %s\n", \
+                      __LINE__, sattrib, fname); \
+       }} while (0)
+
+/*
+  test RAW_OPEN_OPEN
+*/
+static BOOL test_open(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       union smb_fileinfo finfo;
+       const char *fname = BASEDIR "\\torture_open.txt";
+       NTSTATUS status;
+       int fnum, fnum2;
+       BOOL ret = True;
+
+       printf("Checking RAW_OPEN_OPEN\n");
+
+       io.open.level = RAW_OPEN_OPEN;
+       io.open.in.fname = fname;
+       io.open.in.flags = OPEN_FLAGS_FCB;
+       io.open.in.search_attrs = 0;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+       fnum = io.open.out.fnum;
+
+       cli_unlink(cli, fname);
+       CREATE_FILE;
+       cli_close(cli, fnum);
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+       CHECK_RDWR(fnum, RDWR_RDWR);
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = io.open.out.fnum;
+       CHECK_RDWR(fnum2, RDWR_RDWR);
+       cli_close(cli, fnum2);
+       cli_close(cli, fnum);
+
+       /* check the read/write modes */
+       io.open.level = RAW_OPEN_OPEN;
+       io.open.in.fname = fname;
+       io.open.in.search_attrs = 0;
+
+       io.open.in.flags = OPEN_FLAGS_OPEN_READ;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+       CHECK_RDWR(fnum, RDWR_RDONLY);
+       cli_close(cli, fnum);
+
+       io.open.in.flags = OPEN_FLAGS_OPEN_WRITE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+       CHECK_RDWR(fnum, RDWR_WRONLY);
+       cli_close(cli, fnum);
+
+       io.open.in.flags = OPEN_FLAGS_OPEN_RDWR;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+       CHECK_RDWR(fnum, RDWR_RDWR);
+       cli_close(cli, fnum);
+
+       /* check the share modes roughly - not a complete matrix */
+       io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_WRITE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+       CHECK_RDWR(fnum, RDWR_RDWR);
+       
+       if (io.open.in.flags != io.open.out.rmode) {
+               printf("(%d) rmode should equal flags - 0x%x 0x%x\n",
+                      __LINE__, io.open.out.rmode, io.open.in.flags);
+       }
+
+       io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_NONE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+       io.open.in.flags = OPEN_FLAGS_OPEN_READ | OPEN_FLAGS_DENY_NONE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = io.open.out.fnum;
+       CHECK_RDWR(fnum2, RDWR_RDONLY);
+       cli_close(cli, fnum);
+       cli_close(cli, fnum2);
+
+
+       /* check the returned write time */
+       io.open.level = RAW_OPEN_OPEN;
+       io.open.in.fname = fname;
+       io.open.in.search_attrs = 0;
+       io.open.in.flags = OPEN_FLAGS_OPEN_READ;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.open.out.fnum;
+
+       /* check other reply fields */
+       CHECK_TIME(io.open.out.write_time, write_time);
+       CHECK_ALL_INFO(io.open.out.size, size);
+       CHECK_ALL_INFO(io.open.out.attrib, attrib);
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+
+
+/*
+  test RAW_OPEN_OPENX
+*/
+static BOOL test_openx(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       union smb_fileinfo finfo;
+       const char *fname = BASEDIR "\\torture_openx.txt";
+       NTSTATUS status;
+       int fnum, fnum2;
+       BOOL ret = True;
+       int i;
+       struct {
+               uint16 open_func;
+               BOOL with_file;
+               NTSTATUS correct_status;
+       } open_funcs[] = {
+               { OPENX_OPEN_FUNC_OPEN,                           True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_OPEN,                           False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { OPENX_OPEN_FUNC_OPEN  | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_OPEN  | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_FAIL,                           True,  NT_STATUS_INVALID_LOCK_SEQUENCE },
+               { OPENX_OPEN_FUNC_FAIL,                           False, NT_STATUS_INVALID_LOCK_SEQUENCE },
+               { OPENX_OPEN_FUNC_FAIL  | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_OBJECT_NAME_COLLISION },
+               { OPENX_OPEN_FUNC_FAIL  | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_TRUNC,                          True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_TRUNC,                          False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK },
+       };
+
+       printf("Checking RAW_OPEN_OPENX\n");
+       cli_unlink(cli, fname);
+
+       io.openx.level = RAW_OPEN_OPENX;
+       io.openx.in.fname = fname;
+       io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+       io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+       io.openx.in.search_attrs = 0;
+       io.openx.in.file_attrs = 0;
+       io.openx.in.write_time = 0;
+       io.openx.in.size = 1024*1024;
+       io.openx.in.timeout = 0;
+
+       /* check all combinations of open_func */
+       for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+               if (open_funcs[i].with_file) {
+                       fnum = create_complex_file(cli, mem_ctx, fname);
+                       if (fnum == -1) {
+                               d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli));
+                               ret = False;
+                               goto done;
+                       }
+                       cli_close(cli, fnum);
+               }
+               io.openx.in.open_func = open_funcs[i].open_func;
+               status = smb_raw_open(cli->tree, mem_ctx, &io);
+               if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+                       printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", 
+                              __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status),
+                              i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func);
+                       ret = False;
+               }
+               if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+                       cli_close(cli, io.openx.out.fnum);
+                       cli_unlink(cli, fname);
+               }
+       }
+
+       /* check the basic return fields */
+       io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.openx.out.fnum;
+
+       CHECK_ALL_INFO(io.openx.out.size, size);
+       CHECK_VAL(io.openx.out.size, 1024*1024);
+       CHECK_ALL_INFO(io.openx.in.size, size);
+       CHECK_TIME(io.openx.out.write_time, write_time);
+       CHECK_ALL_INFO(io.openx.out.attrib, attrib);
+       CHECK_VAL(io.openx.out.access, OPENX_MODE_ACCESS_RDWR);
+       CHECK_VAL(io.openx.out.ftype, 0);
+       CHECK_VAL(io.openx.out.devstate, 0);
+       CHECK_VAL(io.openx.out.action, OPENX_ACTION_CREATED);
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       /* check the fields when the file already existed */
+       fnum2 = create_complex_file(cli, mem_ctx, fname);
+       if (fnum2 == -1) {
+               ret = False;
+               goto done;
+       }
+       cli_close(cli, fnum2);
+
+       io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.openx.out.fnum;
+
+       CHECK_ALL_INFO(io.openx.out.size, size);
+       CHECK_TIME(io.openx.out.write_time, write_time);
+       CHECK_VAL(io.openx.out.action, OPENX_ACTION_EXISTED);
+       CHECK_VAL(io.openx.out.unknown, 0);
+       CHECK_ALL_INFO(io.openx.out.attrib, attrib);
+       cli_close(cli, fnum);
+
+       /* now check the search attrib for hidden files - win2003 ignores this? */
+       SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN);
+       CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib);
+
+       io.openx.in.search_attrs = FILE_ATTRIBUTE_HIDDEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli_close(cli, io.openx.out.fnum);
+
+       io.openx.in.search_attrs = 0;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli_close(cli, io.openx.out.fnum);
+
+       SET_ATTRIB(FILE_ATTRIBUTE_NORMAL);
+       cli_unlink(cli, fname);
+
+       /* and check attrib on create */
+       io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE;
+       io.openx.in.search_attrs = 0;
+       io.openx.in.file_attrs = FILE_ATTRIBUTE_SYSTEM;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE, attrib);
+       cli_close(cli, io.openx.out.fnum);
+       cli_unlink(cli, fname);
+
+       /* check timeout on create - win2003 ignores the timeout! */
+       io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       io.openx.in.file_attrs = 0;
+       io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.openx.out.fnum;
+
+       io.openx.in.timeout = 20000;
+       start_timer();
+       io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+       if (end_timer() > 3) {
+               printf("(%d) Incorrect timing in openx with timeout - waited %d seconds\n",
+                      __LINE__, (int)end_timer());
+               ret = False;
+       }
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       /* now this is a really weird one - open for execute implies create?! */
+       io.openx.in.fname = fname;
+       io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
+       io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE;
+       io.openx.in.search_attrs = 0;
+       io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL;
+       io.openx.in.file_attrs = 0;
+       io.openx.in.write_time = 0;
+       io.openx.in.size = 0;
+       io.openx.in.timeout = 0;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli_close(cli, io.openx.out.fnum);
+
+       /* check the extended return flag */
+       io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | OPENX_FLAGS_EXTENDED_RETURN;
+       io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VAL(io.openx.out.access_mask, STD_RIGHT_ALL_ACCESS);
+       cli_close(cli, io.openx.out.fnum);
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+
+
+/*
+  test RAW_OPEN_T2OPEN
+
+  I can't work out how to get win2003 to accept a create file via TRANS2_OPEN, which
+  is why you see all the ACCESS_DENIED results below. When we finally work this out then this
+  test will make more sense
+*/
+static BOOL test_t2open(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       union smb_fileinfo finfo;
+       const char *fname = BASEDIR "\\torture_t2open.txt";
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       int i;
+       struct {
+               uint16 open_func;
+               BOOL with_file;
+               NTSTATUS correct_status;
+       } open_funcs[] = {
+               { OPENX_OPEN_FUNC_OPEN,                           True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_OPEN,                           False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { OPENX_OPEN_FUNC_OPEN  | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_OK },
+               { OPENX_OPEN_FUNC_OPEN  | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED },
+               { OPENX_OPEN_FUNC_FAIL,                           True,  NT_STATUS_OBJECT_NAME_COLLISION },
+               { OPENX_OPEN_FUNC_FAIL,                           False, NT_STATUS_ACCESS_DENIED },
+               { OPENX_OPEN_FUNC_FAIL  | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_OBJECT_NAME_COLLISION },
+               { OPENX_OPEN_FUNC_FAIL  | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED },
+               { OPENX_OPEN_FUNC_TRUNC,                          True,  NT_STATUS_ACCESS_DENIED },
+               { OPENX_OPEN_FUNC_TRUNC,                          False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, True,  NT_STATUS_ACCESS_DENIED },
+               { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED },
+       };
+
+       printf("Checking RAW_OPEN_T2OPEN\n");
+
+       io.t2open.level = RAW_OPEN_T2OPEN;
+       io.t2open.in.fname = fname;
+       io.t2open.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | 
+               OPENX_FLAGS_EA_LEN | OPENX_FLAGS_EXTENDED_RETURN;
+       io.t2open.in.open_mode = OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR;
+       io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       io.t2open.in.file_attrs = 0;
+       io.t2open.in.write_time = 0;
+       io.t2open.in.size = 0;
+       io.t2open.in.timeout = 0;
+
+       io.t2open.in.eas = talloc(mem_ctx, sizeof(io.t2open.in.eas[0]));
+       io.t2open.in.num_eas = 1;
+       io.t2open.in.eas[0].flags = 0;
+       io.t2open.in.eas[0].name.s = "EAONE";
+       io.t2open.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1);
+
+       /* check all combinations of open_func */
+       for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+               if (open_funcs[i].with_file) {
+                       fnum = create_complex_file(cli, mem_ctx, fname);
+                       if (fnum == -1) {
+                               d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli));
+                               ret = False;
+                               goto done;
+                       }
+                       cli_close(cli, fnum);
+               }
+               io.t2open.in.open_func = open_funcs[i].open_func;
+               status = smb_raw_open(cli->tree, mem_ctx, &io);
+               if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+                       printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", 
+                              __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status),
+                              i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func);
+                       ret = False;
+               }
+               if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+                       cli_close(cli, io.t2open.out.fnum);
+                       cli_unlink(cli, fname);
+               }
+       }
+
+       /* check the basic return fields */
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       cli_close(cli, fnum);
+       io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.t2open.out.fnum;
+
+       CHECK_ALL_INFO(io.t2open.out.size, size);
+       CHECK_VAL(io.t2open.out.write_time, 0);
+       CHECK_ALL_INFO(io.t2open.out.attrib, attrib);
+       CHECK_VAL(io.t2open.out.access, OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR);
+       CHECK_VAL(io.t2open.out.ftype, 0);
+       CHECK_VAL(io.t2open.out.devstate, 0);
+       CHECK_VAL(io.t2open.out.action, OPENX_ACTION_EXISTED);
+       cli_close(cli, fnum);
+
+       /* now check the search attrib for hidden files - win2003 ignores this? */
+       SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN);
+       CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib);
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli_close(cli, io.t2open.out.fnum);
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       cli_close(cli, io.t2open.out.fnum);
+
+       SET_ATTRIB(FILE_ATTRIBUTE_NORMAL);
+       cli_unlink(cli, fname);
+
+       /* and check attrib on create */
+       io.t2open.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE;
+       io.t2open.in.file_attrs = FILE_ATTRIBUTE_SYSTEM;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+       /* check timeout on create - win2003 ignores the timeout! */
+       io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       io.t2open.in.file_attrs = 0;
+       io.t2open.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+       
+
+/*
+  test RAW_OPEN_NTCREATEX
+*/
+static BOOL test_ntcreatex(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       union smb_fileinfo finfo;
+       const char *fname = BASEDIR "\\torture_ntcreatex.txt";
+       const char *dname = BASEDIR "\\torture_ntcreatex.dir";
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       int i;
+       struct {
+               uint32 open_disp;
+               BOOL with_file;
+               NTSTATUS correct_status;
+       } open_funcs[] = {
+               { NTCREATEX_DISP_SUPERSEDE,     True,  NT_STATUS_OK },
+               { NTCREATEX_DISP_SUPERSEDE,     False, NT_STATUS_OK },
+               { NTCREATEX_DISP_OPEN,          True,  NT_STATUS_OK },
+               { NTCREATEX_DISP_OPEN,          False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { NTCREATEX_DISP_CREATE,        True,  NT_STATUS_OBJECT_NAME_COLLISION },
+               { NTCREATEX_DISP_CREATE,        False, NT_STATUS_OK },
+               { NTCREATEX_DISP_OPEN_IF,       True,  NT_STATUS_OK },
+               { NTCREATEX_DISP_OPEN_IF,       False, NT_STATUS_OK },
+               { NTCREATEX_DISP_OVERWRITE,     True,  NT_STATUS_OK },
+               { NTCREATEX_DISP_OVERWRITE,     False, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+               { NTCREATEX_DISP_OVERWRITE_IF,  True,  NT_STATUS_OK },
+               { NTCREATEX_DISP_OVERWRITE_IF,  False, NT_STATUS_OK },
+               { 6,                            True,  NT_STATUS_INVALID_PARAMETER },
+               { 6,                            False, NT_STATUS_INVALID_PARAMETER },
+       };
+
+       printf("Checking RAW_OPEN_NTCREATEX\n");
+
+       /* reasonable default parameters */
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
+       io.ntcreatex.in.alloc_size = 1024*1024;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+
+       /* test the open disposition */
+       for (i=0; i<ARRAY_SIZE(open_funcs); i++) {
+               if (open_funcs[i].with_file) {
+                       fnum = cli_open(cli, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE);
+                       if (fnum == -1) {
+                               d_printf("Failed to create file %s - %s\n", fname, cli_errstr(cli));
+                               ret = False;
+                               goto done;
+                       }
+                       cli_close(cli, fnum);
+               }
+               io.ntcreatex.in.open_disposition = open_funcs[i].open_disp;
+               status = smb_raw_open(cli->tree, mem_ctx, &io);
+               if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) {
+                       printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_disp=%d)\n", 
+                              __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status),
+                              i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_disp);
+                       ret = False;
+               }
+               if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) {
+                       cli_close(cli, io.ntcreatex.out.fnum);
+                       cli_unlink(cli, fname);
+               }
+       }
+
+       /* basic field testing */
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+       CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+       CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+       CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+       CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+       CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+       CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+       CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+       CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+       CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+       CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+
+       /* check fields when the file already existed */
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               ret = False;
+               goto done;
+       }
+       cli_close(cli, fnum);
+
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+       CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED);
+       CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+       CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+       CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+       CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+       CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+       CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+       CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+       CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+       CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+
+       /* create a directory */
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.fname = dname;
+       fname = dname;
+
+       cli_rmdir(cli, fname);
+       cli_unlink(cli, fname);
+
+       io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED;
+       io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+
+       CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+       CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED);
+       CHECK_NTTIME(io.ntcreatex.out.create_time, create_time);
+       CHECK_NTTIME(io.ntcreatex.out.access_time, access_time);
+       CHECK_NTTIME(io.ntcreatex.out.write_time, write_time);
+       CHECK_NTTIME(io.ntcreatex.out.change_time, change_time);
+       CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib);
+       CHECK_VAL(io.ntcreatex.out.attrib, FILE_ATTRIBUTE_DIRECTORY);
+       CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size);
+       CHECK_ALL_INFO(io.ntcreatex.out.size, size);
+       CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory);
+       CHECK_VAL(io.ntcreatex.out.is_directory, 1);
+       CHECK_VAL(io.ntcreatex.out.size, 0);
+       CHECK_VAL(io.ntcreatex.out.alloc_size, 0);
+       CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+       cli_unlink(cli, fname);
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+
+
+/*
+  test RAW_OPEN_MKNEW
+*/
+static BOOL test_mknew(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       const char *fname = BASEDIR "\\torture_mknew.txt";
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       time_t basetime = (time(NULL) + 3600*24*3) & ~1;
+       union smb_fileinfo finfo;
+
+       printf("Checking RAW_OPEN_MKNEW\n");
+
+       io.mknew.level = RAW_OPEN_MKNEW;
+       io.mknew.in.attrib = 0;
+       io.mknew.in.write_time = 0;
+       io.mknew.in.fname = fname;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.mknew.out.fnum;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       /* make sure write_time works */
+       io.mknew.in.write_time = basetime;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.mknew.out.fnum;
+       CHECK_TIME(basetime, write_time);
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       /* make sure file_attrs works */
+       io.mknew.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.mknew.out.fnum;
+       CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, attrib);
+       
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+
+
+/*
+  test RAW_OPEN_CTEMP
+*/
+static BOOL test_ctemp(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_open io;
+       NTSTATUS status;
+       int fnum;
+       BOOL ret = True;
+       time_t basetime = (time(NULL) + 3600*24*3) & ~1;
+       union smb_fileinfo finfo;
+       const char *name, *fname = NULL;
+
+       printf("Checking RAW_OPEN_CTEMP\n");
+
+       io.ctemp.level = RAW_OPEN_CTEMP;
+       io.ctemp.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       io.ctemp.in.write_time = basetime;
+       io.ctemp.in.directory = BASEDIR;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ctemp.out.fnum;
+
+       name = io.ctemp.out.name;
+
+       finfo.generic.level = RAW_FILEINFO_NAME_INFO;
+       finfo.generic.in.fnum = fnum;
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       fname = finfo.name_info.out.fname.s;
+       d_printf("ctemp name=%s  real name=%s\n", name, fname);
+
+       CHECK_TIME(basetime, write_time);
+
+done:
+       cli_close(cli, fnum);
+       if (fname) {
+               cli_unlink(cli, fname);
+       }
+
+       return ret;
+}
+
+/* basic testing of all RAW_OPEN_* calls 
+*/
+BOOL torture_raw_open(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_open");
+
+       if (cli_deltree(cli, BASEDIR) == -1) {
+               printf("Failed to clean " BASEDIR "\n");
+               return False;
+       }
+       if (!cli_mkdir(cli, BASEDIR)) {
+               printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!test_open(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_openx(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_ntcreatex(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_t2open(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_mknew(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_ctemp(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
new file mode 100644 (file)
index 0000000..888fcbd
--- /dev/null
@@ -0,0 +1,288 @@
+/* 
+   Unix SMB/CIFS implementation.
+   basic raw test suite for oplocks
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_VAL(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) wrong value for %s  0x%x - 0x%x\n", \
+                      __LINE__, #v, (int)v, (int)correct); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+
+static struct {
+       int fnum;
+       unsigned char level;
+       int count;
+} break_info;
+
+/*
+  a handler function for oplock break requests
+*/
+static BOOL oplock_handler_ack(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private)
+{
+       struct cli_tree *tree = private;
+       break_info.fnum = fnum;
+       break_info.level = level;
+       break_info.count++;
+
+       printf("Acking in oplock handler\n");
+
+       return cli_oplock_ack(tree, fnum, level == 1? 0x102 : 2);
+}
+
+/*
+  a handler function for oplock break requests - close the file
+*/
+static BOOL oplock_handler_close(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private)
+{
+       union smb_close io;
+       NTSTATUS status;
+       struct cli_tree *tree = private;
+
+       break_info.fnum = fnum;
+       break_info.level = level;
+       break_info.count++;
+
+       io.close.level = RAW_CLOSE_CLOSE;
+       io.close.in.fnum = fnum;
+       io.close.in.write_time = 0;
+       status = smb_raw_close(tree, &io);
+
+       printf("Closing in oplock handler\n");
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("close failed in oplock_handler_close\n");
+               return False;
+       }
+       return True;
+}
+
+/*
+  test oplock ops
+*/
+static BOOL test_oplock(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       const char *fname = "\\test_oplock.dat";
+       NTSTATUS status;
+       BOOL ret = True;
+       union smb_open io;
+       struct smb_unlink unl;
+       union smb_read rd;
+       uint16 fnum, fnum2;
+
+       /* cleanup */
+       cli_unlink(cli, fname);
+
+       cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
+
+       /*
+         base ntcreatex parms
+       */
+       io.generic.level = RAW_OPEN_NTCREATEX;
+       io.ntcreatex.in.root_fid = 0;
+       io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS;
+       io.ntcreatex.in.alloc_size = 0;
+       io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+       io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+       io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+       io.ntcreatex.in.create_options = 0;
+       io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+       io.ntcreatex.in.security_flags = 0;
+       io.ntcreatex.in.fname = fname;
+
+       printf("open a file with a normal oplock\n");
+       ZERO_STRUCT(break_info);
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK;
+
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+       printf("unlink it - should be no break\n");
+       unl.in.pattern = fname;
+       unl.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &unl);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+       CHECK_VAL(break_info.count, 0);
+
+       cli_close(cli, fnum);
+
+       /*
+         with a batch oplock we get a break
+       */
+       printf("open with batch oplock\n");
+       ZERO_STRUCT(break_info);
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       printf("unlink should generate a break\n");
+       unl.in.pattern = fname;
+       unl.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &unl);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+       CHECK_VAL(break_info.fnum, fnum);
+       CHECK_VAL(break_info.level, 2);
+       CHECK_VAL(break_info.count, 1);
+
+
+       cli_close(cli, fnum);
+
+       printf("if we close on break then the unlink can succeed\n");
+       ZERO_STRUCT(break_info);
+       cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       unl.in.pattern = fname;
+       unl.in.attrib = 0;
+       ZERO_STRUCT(break_info);
+       status = smb_raw_unlink(cli->tree, &unl);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       CHECK_VAL(break_info.fnum, fnum);
+       CHECK_VAL(break_info.level, 2);
+       CHECK_VAL(break_info.count, 1);
+
+       printf("a self read should not cause a break\n");
+       ZERO_STRUCT(break_info);
+       cli_close(cli, fnum);
+       cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
+
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       rd.read.level = RAW_READ_READ;
+       rd.read.in.fnum = fnum;
+       rd.read.in.count = 1;
+       rd.read.in.offset = 0;
+       rd.read.in.remaining = 0;
+       status = smb_raw_read(cli->tree, &rd);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VAL(break_info.count, 0);
+
+
+       printf("a 2nd open should give a break\n");
+       ZERO_STRUCT(break_info);
+       cli_close(cli, fnum);
+       cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree);
+
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       ZERO_STRUCT(break_info);
+
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+       CHECK_VAL(break_info.count, 1);
+       CHECK_VAL(break_info.fnum, fnum);
+       CHECK_VAL(break_info.level, 2);
+
+       printf("a 2nd open should get an oplock when we close instead of ack\n");
+       ZERO_STRUCT(break_info);
+       cli_close(cli, fnum);
+       cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree);
+
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       ZERO_STRUCT(break_info);
+
+       io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | 
+               NTCREATEX_FLAGS_REQUEST_OPLOCK | 
+               NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+       status = smb_raw_open(cli->tree, mem_ctx, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = io.ntcreatex.out.fnum;
+       CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+       CHECK_VAL(break_info.count, 1);
+       CHECK_VAL(break_info.fnum, fnum2);
+       CHECK_VAL(break_info.level, 2);
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+       return ret;
+}
+
+
+/* 
+   basic testing of oplocks
+*/
+BOOL torture_raw_oplock(int dummy)
+{
+       struct cli_state *cli1;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_oplock");
+
+       if (!test_oplock(cli1, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli1);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c
new file mode 100644 (file)
index 0000000..b1f508c
--- /dev/null
@@ -0,0 +1,701 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_FILEINFO_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+static struct {
+       const char *name;
+       enum fileinfo_level level;
+       unsigned only_paths:1;
+       unsigned only_handles:1;
+       uint32 capability_mask;
+       NTSTATUS fnum_status, fname_status;
+       union smb_fileinfo fnum_finfo, fname_finfo;
+} levels[] = {
+       { "GETATTR",                   RAW_FILEINFO_GETATTR,           1, 0, },
+       { "GETATTRE",                  RAW_FILEINFO_GETATTRE,          0, 1, },
+       { "STANDARD",                  RAW_FILEINFO_STANDARD, },
+       { "EA_SIZE",                   RAW_FILEINFO_EA_SIZE, },
+       { "ALL_EAS",                   RAW_FILEINFO_ALL_EAS, },
+       { "IS_NAME_VALID",             RAW_FILEINFO_IS_NAME_VALID,     1, 0, },
+       { "BASIC_INFO",                RAW_FILEINFO_BASIC_INFO, },
+       { "STANDARD_INFO",             RAW_FILEINFO_STANDARD_INFO, },
+       { "EA_INFO",                   RAW_FILEINFO_EA_INFO, },
+       { "NAME_INFO",                 RAW_FILEINFO_NAME_INFO, },
+       { "ALL_INFO",                  RAW_FILEINFO_ALL_INFO, },
+       { "ALT_NAME_INFO",             RAW_FILEINFO_ALT_NAME_INFO, },
+       { "STREAM_INFO",               RAW_FILEINFO_STREAM_INFO, },
+       { "COMPRESSION_INFO",          RAW_FILEINFO_COMPRESSION_INFO, },
+       { "UNIX_BASIC_INFO",           RAW_FILEINFO_UNIX_BASIC, 0, 0, CAP_UNIX},
+       { "UNIX_LINK_INFO",            RAW_FILEINFO_UNIX_LINK, 0, 0, CAP_UNIX},
+       { "BASIC_INFORMATION",         RAW_FILEINFO_BASIC_INFORMATION, },
+       { "STANDARD_INFORMATION",      RAW_FILEINFO_STANDARD_INFORMATION, },
+       { "INTERNAL_INFORMATION",      RAW_FILEINFO_INTERNAL_INFORMATION, },
+       { "EA_INFORMATION",            RAW_FILEINFO_EA_INFORMATION, },
+       { "ACCESS_INFORMATION",        RAW_FILEINFO_ACCESS_INFORMATION, },
+       { "NAME_INFORMATION",          RAW_FILEINFO_NAME_INFORMATION, },
+       { "POSITION_INFORMATION",      RAW_FILEINFO_POSITION_INFORMATION, },
+       { "MODE_INFORMATION",          RAW_FILEINFO_MODE_INFORMATION, },
+       { "ALIGNMENT_INFORMATION",     RAW_FILEINFO_ALIGNMENT_INFORMATION, },
+       { "ALL_INFORMATION",           RAW_FILEINFO_ALL_INFORMATION, },
+       { "ALT_NAME_INFORMATION",      RAW_FILEINFO_ALT_NAME_INFORMATION, },
+       { "STREAM_INFORMATION",        RAW_FILEINFO_STREAM_INFORMATION, },
+       { "COMPRESSION_INFORMATION",   RAW_FILEINFO_COMPRESSION_INFORMATION, },
+       { "NETWORK_OPEN_INFORMATION",  RAW_FILEINFO_NETWORK_OPEN_INFORMATION, },
+       { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, },
+       { NULL, }
+};
+
+/*
+  compare a dos time (2 second resolution) to a nt time
+*/
+static int dos_nt_time_cmp(time_t t, const NTTIME *nt)
+{
+       time_t t2 = nt_time_to_unix(nt);
+       if (ABS(t2 - t) <= 2) return 0;
+       return t2 - t;
+}
+
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fileinfo *fnum_find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
+                   strcmp(name, levels[i].name) == 0 && 
+                   !levels[i].only_paths) {
+                       return &levels[i].fnum_finfo;
+               }
+       }
+       return NULL;
+}
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fileinfo *fname_find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (NT_STATUS_IS_OK(levels[i].fname_status) &&
+                   strcmp(name, levels[i].name) == 0 && 
+                   !levels[i].only_handles) {
+                       return &levels[i].fname_finfo;
+               }
+       }
+       return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+        printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+               #n1, #v1, (uint_t)s1->n1.out.v1, \
+               #n2, #v2, (uint_t)s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
+                                         s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
+        printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
+               #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
+               #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+        printf("%s/%s != %s/%s at %s(%d)\n", \
+               #n1, #v1, \
+               #n2, #v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure 
+   we zero-fill */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+        printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+               #n1, #v1, \
+              (uint_t)s1->n1.out.v1, \
+              (uint_t)s1->n1.out.v1, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* basic testing of all RAW_FILEINFO_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+*/
+BOOL torture_raw_qfileinfo(int dummy)
+{
+       struct cli_state *cli;
+       int i;
+       BOOL ret = True;
+       int count;
+       union smb_fileinfo *s1, *s2;    
+       TALLOC_CTX *mem_ctx;
+       int fnum;
+       const char *fname = "\\torture_qfileinfo.txt";
+       NTTIME correct_time;
+       large_t correct_size;
+       uint32 correct_attrib;
+       const char *correct_name;
+       BOOL skip_streams = False;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_qfileinfo");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+       
+       
+       /* scan all the fileinfo and pathinfo levels */
+       for (i=0; levels[i].name; i++) {
+               if (!levels[i].only_paths) {
+                       levels[i].fnum_finfo.generic.level = levels[i].level;
+                       levels[i].fnum_finfo.generic.in.fnum = fnum;
+                       levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx, 
+                                                                &levels[i].fnum_finfo);
+               }
+
+               if (!levels[i].only_handles) {
+                       levels[i].fname_finfo.generic.level = levels[i].level;
+                       levels[i].fname_finfo.generic.in.fname = talloc_strdup(mem_ctx, fname);
+                       levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, 
+                                                                 &levels[i].fname_finfo);
+               }
+       }
+
+       /* check for completely broken levels */
+       for (count=i=0; levels[i].name; i++) {
+               uint32 cap = cli->transport->negotiate.capabilities;
+               /* see if this server claims to support this level */
+               if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+                       continue;
+               }
+
+               if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
+                       printf("ERROR: level %s failed - %s\n", 
+                              levels[i].name, nt_errstr(levels[i].fnum_status));
+                       count++;
+               }
+               if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
+                       printf("ERROR: level %s failed - %s\n", 
+                              levels[i].name, nt_errstr(levels[i].fname_status));
+                       count++;
+               }
+       }
+
+       if (count != 0) {
+               ret = False;
+               printf("%d levels failed\n", count);
+               if (count > 32) {
+                       printf("too many level failures - giving up\n");
+                       goto done;
+               }
+       }
+
+       /* see if we can do streams */
+       s1 = fnum_find("STREAM_INFO");
+       if (!s1 || s1->stream_info.out.num_streams == 0) {
+               printf("STREAM_INFO broken (%d) - skipping streams checks\n",
+                      s1 ? s1->stream_info.out.num_streams : -1);
+               skip_streams = True;
+       }       
+
+
+       /* this code is incredibly repititive but doesn't lend itself to loops, so
+          we use lots of macros to make it less painful */
+
+       /* first off we check the levels that are supposed to be aliases. It will be quite rare for 
+          this code to fail, but we need to check it for completeness */
+
+
+
+#define ALIAS_CHECK(sname1, sname2) \
+       do { \
+               s1 = fnum_find(sname1);  s2 = fnum_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+               s1 = fname_find(sname1); s2 = fname_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+               s1 = fnum_find(sname1);  s2 = fname_find(sname2); \
+               if (s1 && s2) { INFO_CHECK } \
+       } while (0)
+
+#define INFO_CHECK \
+               STRUCT_EQUAL(basic_info,  create_time, basic_info, create_time); \
+               STRUCT_EQUAL(basic_info,  access_time, basic_info, access_time); \
+               STRUCT_EQUAL(basic_info,  write_time,  basic_info, write_time); \
+               STRUCT_EQUAL(basic_info,  change_time, basic_info, change_time); \
+               VAL_EQUAL   (basic_info,  attrib,      basic_info, attrib);
+
+       ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               VAL_EQUAL(standard_info,  alloc_size,     standard_info, alloc_size); \
+               VAL_EQUAL(standard_info,  size,           standard_info, size); \
+               VAL_EQUAL(standard_info,  nlink,          standard_info, nlink); \
+               VAL_EQUAL(standard_info,  delete_pending, standard_info, delete_pending); \
+               VAL_EQUAL(standard_info,  directory,      standard_info, directory);
+
+       ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               VAL_EQUAL(ea_info,  ea_size,     ea_info, ea_size);
+
+       ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STR_EQUAL(name_info,  fname,   name_info, fname);
+
+       ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STRUCT_EQUAL(all_info,  create_time,           all_info, create_time); \
+               STRUCT_EQUAL(all_info,  access_time,           all_info, access_time); \
+               STRUCT_EQUAL(all_info,  write_time,            all_info, write_time); \
+               STRUCT_EQUAL(all_info,  change_time,           all_info, change_time); \
+                  VAL_EQUAL(all_info,  attrib,                all_info, attrib); \
+                  VAL_EQUAL(all_info,  alloc_size,            all_info, alloc_size); \
+                  VAL_EQUAL(all_info,  size,                  all_info, size); \
+                  VAL_EQUAL(all_info,  nlink,                 all_info, nlink); \
+                  VAL_EQUAL(all_info,  delete_pending,        all_info, delete_pending); \
+                  VAL_EQUAL(all_info,  directory,             all_info, directory); \
+                  VAL_EQUAL(all_info,  ea_size,               all_info, ea_size); \
+                  STR_EQUAL(all_info,  fname,                 all_info, fname);
+
+       ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
+               VAL_EQUAL(compression_info, format,         compression_info, format); \
+               VAL_EQUAL(compression_info, unit_shift,     compression_info, unit_shift); \
+               VAL_EQUAL(compression_info, chunk_shift,    compression_info, chunk_shift); \
+               VAL_EQUAL(compression_info, cluster_shift,  compression_info, cluster_shift);
+
+       ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
+
+
+#undef INFO_CHECK
+#define INFO_CHECK \
+               STR_EQUAL(alt_name_info,  fname,   alt_name_info, fname);
+
+       ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
+
+
+#define TIME_CHECK_NT(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      nt_time_string(mem_ctx, &s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      nt_time_string(mem_ctx, &s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+#define TIME_CHECK_DOS(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+#define TIME_CHECK_UNX(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \
+               printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
+                      time_string(mem_ctx, s1->stype.out.tfield), \
+                      nt_time_string(mem_ctx, &correct_time)); \
+               ret = False; \
+       }} while (0)
+
+       /* now check that all the times that are supposed to be equal are correct */
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.create_time;
+       printf("create_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, create_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, create_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   create_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   create_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    create_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   create_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.access_time;
+       printf("access_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, access_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, access_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   access_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   access_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    access_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   access_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.write_time;
+       printf("write_time : %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, write_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, write_time);
+       TIME_CHECK_DOS("GETATTR",                  getattr,    write_time);
+       TIME_CHECK_DOS("GETATTRE",                 getattre,   write_time);
+       TIME_CHECK_DOS("STANDARD",                 standard,   write_time);
+       TIME_CHECK_DOS("EA_SIZE",                  ea_size,    write_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   write_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_time = s1->basic_info.out.change_time;
+       printf("change_time: %s\n", nt_time_string(mem_ctx, &correct_time));
+
+       TIME_CHECK_NT ("BASIC_INFO",               basic_info, change_time);
+       TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, change_time);
+       TIME_CHECK_NT ("ALL_INFO",                 all_info,   change_time);
+       TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
+
+
+#define SIZE_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_size) { \
+               printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_size); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_size) { \
+               printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_size); \
+               ret = False; \
+       }} while (0)
+
+       s1 = fnum_find("STANDARD_INFO");
+       correct_size = s1->standard_info.out.size;
+       printf("size: %u\n", (unsigned)correct_size);
+       
+       SIZE_CHECK("GETATTR",                  getattr,                  size);
+       SIZE_CHECK("GETATTRE",                 getattre,                 size);
+       SIZE_CHECK("STANDARD",                 standard,                 size);
+       SIZE_CHECK("EA_SIZE",                  ea_size,                  size);
+       SIZE_CHECK("STANDARD_INFO",            standard_info,            size);
+       SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            size);
+       SIZE_CHECK("ALL_INFO",                 all_info,                 size);
+       SIZE_CHECK("ALL_INFORMATION",          all_info,                 size);
+       SIZE_CHECK("COMPRESSION_INFO",         compression_info,         compressed_size);
+       SIZE_CHECK("COMPRESSION_INFORMATION",  compression_info,         compressed_size);
+       SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
+       if (!skip_streams) {
+               SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].size);
+               SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].size);
+       }
+
+
+       s1 = fnum_find("STANDARD_INFO");
+       correct_size = s1->standard_info.out.alloc_size;
+       printf("alloc_size: %u\n", (unsigned)correct_size);
+       
+       SIZE_CHECK("GETATTRE",                 getattre,                 alloc_size);
+       SIZE_CHECK("STANDARD",                 standard,                 alloc_size);
+       SIZE_CHECK("EA_SIZE",                  ea_size,                  alloc_size);
+       SIZE_CHECK("STANDARD_INFO",            standard_info,            alloc_size);
+       SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            alloc_size);
+       SIZE_CHECK("ALL_INFO",                 all_info,                 alloc_size);
+       SIZE_CHECK("ALL_INFORMATION",          all_info,                 alloc_size);
+       SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
+       if (!skip_streams) {
+               SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].alloc_size);
+               SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].alloc_size);
+       }
+
+#define ATTRIB_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_attrib) { \
+               printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_attrib); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != correct_attrib) { \
+               printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
+                      (unsigned)s1->stype.out.tfield, \
+                      (unsigned)correct_attrib); \
+               ret = False; \
+       }} while (0)
+
+       s1 = fnum_find("BASIC_INFO");
+       correct_attrib = s1->basic_info.out.attrib;
+       printf("attrib: 0x%x\n", (unsigned)correct_attrib);
+       
+       ATTRIB_CHECK("GETATTR",                   getattr,                   attrib);
+       ATTRIB_CHECK("GETATTRE",                  getattre,                  attrib);
+       ATTRIB_CHECK("STANDARD",                  standard,                  attrib);
+       ATTRIB_CHECK("BASIC_INFO",                basic_info,                attrib);
+       ATTRIB_CHECK("BASIC_INFORMATION",         basic_info,                attrib);
+       ATTRIB_CHECK("EA_SIZE",                   ea_size,                   attrib);
+       ATTRIB_CHECK("ALL_INFO",                  all_info,                  attrib);
+       ATTRIB_CHECK("ALL_INFORMATION",           all_info,                  attrib);
+       ATTRIB_CHECK("NETWORK_OPEN_INFORMATION",  network_open_information,  attrib);
+       ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
+
+       correct_name = fname;
+       printf("name: %s\n", correct_name);
+
+#define NAME_CHECK(sname, stype, tfield, flags) do { \
+       s1 = fnum_find(sname); \
+       if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name) != 0) || \
+                       wire_bad_flags(&s1->stype.out.tfield, flags)) { \
+               printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
+                      s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname); \
+       if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name)) != 0 || \
+                       wire_bad_flags(&s1->stype.out.tfield, flags)) { \
+               printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
+                      s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
+               ret = False; \
+       }} while (0)
+
+       NAME_CHECK("NAME_INFO",        name_info, fname, STR_UNICODE);
+       NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
+
+       /* the ALL_INFO file name is the full path on the filesystem */
+       s1 = fnum_find("ALL_INFO");
+       if (s1 && !s1->all_info.out.fname.s) {
+               printf("ALL_INFO didn't give a filename\n");
+               ret = False;
+       }
+       if (s1 && s1->all_info.out.fname.s) {
+               char *p = strrchr(s1->all_info.out.fname.s, '\\');
+               if (!p) {
+                       printf("Not a full path in all_info/fname? - '%s'\n", 
+                              s1->all_info.out.fname.s);
+                       ret = False;
+               } else {
+                       if (strcmp(correct_name, p) != 0) {
+                               printf("incorrect basename in all_info/fname - '%s'\n",
+                                      s1->all_info.out.fname.s);
+                               ret = False;
+                       }
+               }
+               if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE)) {
+                       printf("Should not null terminate all_info/fname\n");
+                       ret = False;
+               }
+       }
+
+       s1 = fnum_find("ALT_NAME_INFO");
+       correct_name = s1->alt_name_info.out.fname.s;
+       printf("alt_name: %s\n", correct_name);
+
+       NAME_CHECK("ALT_NAME_INFO",        alt_name_info, fname, STR_UNICODE);
+       NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
+
+       /* and make sure we can open by alternate name */
+       cli_close(cli, fnum);
+       fnum = cli_nt_create_full(cli, correct_name, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, 
+                                 FILE_ATTRIBUTE_NORMAL,
+                                 NTCREATEX_SHARE_ACCESS_DELETE|
+                                 NTCREATEX_SHARE_ACCESS_READ|
+                                 NTCREATEX_SHARE_ACCESS_WRITE, 
+                                 NTCREATEX_DISP_OVERWRITE_IF, 
+                                 0, 0);
+       if (fnum == -1) {
+               printf("Unable to open by alt_name - %s\n", cli_errstr(cli));
+               ret = False;
+       }
+
+       if (!skip_streams) {
+               correct_name = "::$DATA";
+               printf("stream_name: %s\n", correct_name);
+
+               NAME_CHECK("STREAM_INFO",        stream_info, streams[0].stream_name, STR_UNICODE);
+               NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
+       }
+
+       /* make sure the EAs look right */
+       s1 = fnum_find("ALL_EAS");
+       s2 = fnum_find("ALL_INFO");
+       if (s1) {
+               for (i=0;i<s1->all_eas.out.num_eas;i++) {
+                       printf("  flags=%d %s=%*.*s\n", 
+                              s1->all_eas.out.eas[i].flags,
+                              s1->all_eas.out.eas[i].name.s,
+                              s1->all_eas.out.eas[i].value.length,
+                              s1->all_eas.out.eas[i].value.length,
+                              s1->all_eas.out.eas[i].value.data);
+               }
+       }
+       if (s1 && s2) {
+               if (s1->all_eas.out.num_eas == 0) {
+                       if (s2->all_info.out.ea_size != 0) {
+                               printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
+                                      s2->all_info.out.ea_size);
+                       }
+               } else {
+                       if (s2->all_info.out.ea_size != 
+                           ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
+                               printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
+                                      ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
+                                      s2->all_info.out.ea_size);
+                       }
+               }
+       }
+       s2 = fname_find("ALL_EAS");
+       if (s2) {
+               VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
+               for (i=0;i<s1->all_eas.out.num_eas;i++) {
+                       VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
+                       STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
+                       VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
+               }
+       }
+
+#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
+       s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname1); s2 = fname_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fnum_find(sname1); s2 = fname_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       } \
+       s1 = fname_find(sname1); s2 = fnum_find(sname2); \
+       if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
+               printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
+                       #stype1, #tfield1, #stype2, #tfield2,  \
+                      s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
+               ret = False; \
+       }} while (0)
+
+       VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, 
+                 "ALL_INFO",      all_info,      delete_pending);
+       VAL_CHECK("STANDARD_INFO", standard_info, directory, 
+                 "ALL_INFO",      all_info,      directory);
+       VAL_CHECK("STANDARD_INFO", standard_info, nlink, 
+                 "ALL_INFO",      all_info,      nlink);
+       VAL_CHECK("EA_INFO",       ea_info,       ea_size, 
+                 "ALL_INFO",      all_info,      ea_size);
+       VAL_CHECK("EA_SIZE",       ea_size,       ea_size, 
+                 "ALL_INFO",      all_info,      ea_size);
+
+
+#define NAME_PATH_CHECK(sname, stype, field) do { \
+       s1 = fname_find(sname); s2 = fnum_find(sname); \
+       VAL_EQUAL(stype, field, stype, field); \
+} while (0)
+
+       NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, device);
+       NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, inode);
+       NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
+       NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
+       NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
+       NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
+       NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
+
+#if 0
+       /* these are expected to differ */
+       NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
+#endif
+
+#define UNKNOWN_CHECK(sname, stype, tfield) do { \
+       s1 = fnum_find(sname); \
+       if (s1 && s1->stype.out.tfield != 0) { \
+               printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+                       #stype, #tfield, \
+                      (unsigned)s1->stype.out.tfield); \
+       } \
+       s1 = fname_find(sname); \
+       if (s1 && s1->stype.out.tfield != 0) { \
+               printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
+                       #stype, #tfield, \
+                      (unsigned)s1->stype.out.tfield); \
+       }} while (0)
+
+       /* now get a bit fancier .... */
+       
+       /* when we set the delete disposition then the link count should drop
+          to 0 and delete_pending should be 1 */
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c
new file mode 100644 (file)
index 0000000..274d107
--- /dev/null
@@ -0,0 +1,295 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_QFS_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+static struct {
+       const char *name;
+       enum fsinfo_level level;
+       uint32 capability_mask;
+       NTSTATUS status;
+       union smb_fsinfo fsinfo;
+} levels[] = {
+       {"DSKATTR",               RAW_QFS_DSKATTR, },
+       {"ALLOCATION",            RAW_QFS_ALLOCATION, },
+       {"VOLUME",                RAW_QFS_VOLUME, },
+       {"VOLUME_INFO",           RAW_QFS_VOLUME_INFO, },
+       {"SIZE_INFO",             RAW_QFS_SIZE_INFO, },
+       {"DEVICE_INFO",           RAW_QFS_DEVICE_INFO, },
+       {"ATTRIBUTE_INFO",        RAW_QFS_ATTRIBUTE_INFO, },
+       {"UNIX_INFO",             RAW_QFS_UNIX_INFO,            CAP_UNIX},
+       {"VOLUME_INFORMATION",    RAW_QFS_VOLUME_INFORMATION, },
+       {"SIZE_INFORMATION",      RAW_QFS_SIZE_INFORMATION, },
+       {"DEVICE_INFORMATION",    RAW_QFS_DEVICE_INFORMATION, },
+       {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, },
+       {"QUOTA_INFORMATION",     RAW_QFS_QUOTA_INFORMATION, },
+       {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, },
+       {"OBJECTID_INFORMATION",  RAW_QFS_OBJECTID_INFORMATION, },
+       { NULL, }
+};
+
+
+/*
+  find a level in the levels[] table
+*/
+static union smb_fsinfo *find(const char *name)
+{
+       int i;
+       for (i=0; levels[i].name; i++) {
+               if (strcmp(name, levels[i].name) == 0) {
+                       return &levels[i].fsinfo;
+               }
+       }
+       return NULL;
+}
+
+/* local macros to make the code below more readable */
+#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
+        printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
+               #n1, #v1, (uint_t)s1->n1.out.v1, \
+               #n2, #v2, (uint_t)s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STR_EQUAL(n1, v1, n2, v2) do {if (!s1->n1.out.v1 && !s2->n2.out.v2) return True; \
+       if (!s1->n1.out.v1 || !s2->n2.out.v2) return False; \
+       if (strcmp(s1->n1.out.v1, s2->n2.out.v2)) { \
+         printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \
+               #n1, #v1, s1->n1.out.v1, \
+               #n2, #v2, s2->n2.out.v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
+        printf("%s/%s != %s/%s at %s(%d)\n", \
+               #n1, #v1, \
+               #n2, #v2, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* used to find hints on unknown values - and to make sure 
+   we zero-fill */
+#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
+        printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
+               #n1, #v1, \
+              (uint_t)s1->n1.out.v1, \
+              (uint_t)s1->n1.out.v1, \
+              __FILE__, __LINE__); \
+        ret = False; \
+}} while(0)
+
+/* basic testing of all RAW_QFS_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+
+   Some of the consistency tests assume that the target filesystem is
+   quiescent, which is sometimes hard to achieve
+*/
+BOOL torture_raw_qfsinfo(int dummy)
+{
+       struct cli_state *cli;
+       int i;
+       BOOL ret = True;
+       int count;
+       union smb_fsinfo *s1, *s2;      
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_qfsinfo");
+       
+       /* scan all the levels, pulling the results */
+       for (i=0; levels[i].name; i++) {
+               printf("Running level %s\n", levels[i].name);
+               levels[i].fsinfo.generic.level = levels[i].level;
+               levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo);
+       }
+
+       /* check for completely broken levels */
+       for (count=i=0; levels[i].name; i++) {
+               uint32 cap = cli->transport->negotiate.capabilities;
+               /* see if this server claims to support this level */
+               if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+                       continue;
+               }
+               
+               if (!NT_STATUS_IS_OK(levels[i].status)) {
+                       printf("ERROR: level %s failed - %s\n", 
+                              levels[i].name, nt_errstr(levels[i].status));
+                       count++;
+               }
+       }
+
+       if (count != 0) {
+               ret = False;
+               printf("%d levels failed\n", count);
+               if (count > 10) {
+                       printf("too many level failures - giving up\n");
+                       goto done;
+               }
+       }
+
+       printf("check for correct aliases\n");
+       s1 = find("SIZE_INFO");
+       s2 = find("SIZE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
+               VAL_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
+               VAL_EQUAL(size_info, sectors_per_unit,  size_info, sectors_per_unit);
+               VAL_EQUAL(size_info, bytes_per_sector,  size_info, bytes_per_sector);
+       }       
+
+       s1 = find("DEVICE_INFO");
+       s2 = find("DEVICE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(device_info, device_type,     device_info, device_type);
+               VAL_EQUAL(device_info, characteristics, device_info, characteristics);
+       }       
+
+       s1 = find("VOLUME_INFO");
+       s2 = find("VOLUME_INFORMATION");
+       if (s1 && s2) {
+               STRUCT_EQUAL(volume_info, create_time,    volume_info, create_time);
+               VAL_EQUAL   (volume_info, serial_number,  volume_info, serial_number);
+               STR_EQUAL   (volume_info, volume_name.s,    volume_info, volume_name.s);
+               printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
+       }       
+
+       s1 = find("ATTRIBUTE_INFO");
+       s2 = find("ATTRIBUTE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(attribute_info, fs_attr,    
+                         attribute_info, fs_attr);
+               VAL_EQUAL(attribute_info, max_file_component_length, 
+                         attribute_info, max_file_component_length);
+               STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
+               printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
+       }       
+
+       printf("check for consistent disk sizes\n");
+       s1 = find("DSKATTR");
+       s2 = find("ALLOCATION");
+       if (s1 && s2) {
+               double size1, size2;
+               double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+               size1 = 1.0 * 
+                       s1->dskattr.out.units_total * 
+                       s1->dskattr.out.blocks_per_unit * 
+                       s1->dskattr.out.block_size / scale;
+               size2 = 1.0 *
+                       s2->allocation.out.sectors_per_unit *
+                       s2->allocation.out.total_alloc_units *
+                       s2->allocation.out.bytes_per_sector / scale;
+               if (ABS(size1 - size2) > 1) {
+                       printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
+                              size1, size2);
+                       ret = False;
+               }
+               printf("total disk = %.0f MB\n", size1*scale/1.0e6);
+       }
+
+       printf("check consistent free disk space\n");
+       s1 = find("DSKATTR");
+       s2 = find("ALLOCATION");
+       if (s1 && s2) {
+               double size1, size2;
+               double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
+               size1 = 1.0 * 
+                       s1->dskattr.out.units_free * 
+                       s1->dskattr.out.blocks_per_unit * 
+                       s1->dskattr.out.block_size / scale;
+               size2 = 1.0 *
+                       s2->allocation.out.sectors_per_unit *
+                       s2->allocation.out.avail_alloc_units *
+                       s2->allocation.out.bytes_per_sector / scale;
+               if (ABS(size1 - size2) > 1) {
+                       printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
+                              size1, size2);
+                       ret = False;
+               }
+               printf("free disk = %.0f MB\n", size1*scale/1.0e6);
+       }
+       
+       printf("volume info consistency\n");
+       s1 = find("VOLUME");
+       s2 = find("VOLUME_INFO");
+       if (s1 && s2) {
+               VAL_EQUAL(volume, serial_number,  volume_info, serial_number);
+               STR_EQUAL(volume, volume_name.s,  volume_info, volume_name.s);
+       }       
+
+       /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
+          available allocation units, not the total */
+       s1 = find("SIZE_INFO");
+       s2 = find("FULL_SIZE_INFORMATION");
+       if (s1 && s2) {
+               VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
+               VAL_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
+               VAL_EQUAL(size_info, sectors_per_unit,  full_size_information, sectors_per_unit);
+               VAL_EQUAL(size_info, bytes_per_sector,  full_size_information, bytes_per_sector);
+       }       
+
+       printf("check for non-zero unknown fields\n");
+       s1 = find("QUOTA_INFORMATION");
+       if (s1) {
+               VAL_UNKNOWN(quota_information, unknown[0]);
+               VAL_UNKNOWN(quota_information, unknown[1]);
+               VAL_UNKNOWN(quota_information, unknown[2]);
+       }
+
+       s1 = find("OBJECTID_INFORMATION");
+       if (s1) {
+               VAL_UNKNOWN(objectid_information, unknown[0]);
+               VAL_UNKNOWN(objectid_information, unknown[1]);
+               VAL_UNKNOWN(objectid_information, unknown[2]);
+               VAL_UNKNOWN(objectid_information, unknown[3]);
+               VAL_UNKNOWN(objectid_information, unknown[4]);
+               VAL_UNKNOWN(objectid_information, unknown[5]);
+       }
+
+
+#define STR_CHECK(sname, stype, field, flags) do { \
+       s1 = find(sname); \
+       if (s1) { \
+               if (wire_bad_flags(&s1->stype.out.field, flags)) { \
+                       printf("(%d) incorrect string termination in %s/%s\n", \
+                              __LINE__, #stype, #field); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+       printf("check for correct termination\n");
+       STR_CHECK("VOLUME",                volume,         volume_name, 0);
+       STR_CHECK("VOLUME_INFO",           volume_info,    volume_name, STR_UNICODE);
+       STR_CHECK("VOLUME_INFORMATION",    volume_info,    volume_name, STR_UNICODE);
+       STR_CHECK("ATTRIBUTE_INFO",        attribute_info, fs_type, STR_UNICODE);
+       STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
+
+done:
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c
new file mode 100644 (file)
index 0000000..c231f52
--- /dev/null
@@ -0,0 +1,732 @@
+/* 
+   Unix SMB/CIFS implementation.
+   test suite for various read operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_BUFFER(buf, seed, len) do { \
+       if (!check_buffer(buf, seed, len, __LINE__)) { \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define BASEDIR "\\testread"
+
+
+/*
+  setup a random buffer based on a seed
+*/
+static void setup_buffer(char *buf, unsigned seed, int len)
+{
+       int i;
+       srandom(seed);
+       for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+  check a random buffer based on a seed
+*/
+static BOOL check_buffer(char *buf, unsigned seed, int len, int line)
+{
+       int i;
+       srandom(seed);
+       for (i=0;i<len;i++) {
+               char v = random();
+               if (buf[i] != v) {
+                       printf("Buffer incorrect at line %d! ofs=%d v1=0x%x v2=0x%x\n", 
+                              line, i, buf[i], v);
+                       return False;
+               }
+       }
+       return True;
+}
+
+/*
+  test read ops
+*/
+static BOOL test_read(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_read io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       const char *test_data = "TEST DATA";
+       unsigned seed = time(NULL);
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_READ_READ\n");
+       io.generic.level = RAW_READ_READ;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying empty file read\n");
+       io.read.in.fnum = fnum;
+       io.read.in.count = 1;
+       io.read.in.offset = 0;
+       io.read.in.remaining = 0;
+       io.read.out.data = buf;
+       status = smb_raw_read(cli->tree, &io);
+
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.read.out.nread, 0);
+
+       printf("Trying zero file read\n");
+       io.read.in.count = 0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.read.out.nread, 0);
+
+       printf("Trying bad fnum\n");
+       io.read.in.fnum = fnum+1;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+       io.read.in.fnum = fnum;
+
+       cli_write(cli, fnum, 0, test_data, 0, strlen(test_data));
+
+       printf("Trying small read\n");
+       io.read.in.fnum = fnum;
+       io.read.in.offset = 0;
+       io.read.in.remaining = 0;
+       io.read.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.read.out.nread, strlen(test_data));
+       if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+               goto done;
+       }
+
+       printf("Trying short read\n");
+       io.read.in.offset = 1;
+       io.read.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.read.out.nread, strlen(test_data)-1);
+       if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+               goto done;
+       }
+
+       printf("Trying max offset\n");
+       io.read.in.offset = ~0;
+       io.read.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.read.out.nread, 0);
+
+       setup_buffer(buf, seed, maxsize);
+       cli_write(cli, fnum, 0, buf, 0, maxsize);
+       memset(buf, 0, maxsize);
+
+       printf("Trying large read\n");
+       io.read.in.offset = 0;
+       io.read.in.count = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_BUFFER(buf, seed, io.read.out.nread);
+
+
+       printf("Trying locked region\n");
+       cli->session->pid++;
+       if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       cli->session->pid--;
+       memset(buf, 0, maxsize);
+       io.read.in.offset = 0;
+       io.read.in.count = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test lockread ops
+*/
+static BOOL test_lockread(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_read io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       const char *test_data = "TEST DATA";
+       unsigned seed = time(NULL);
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_READ_LOCKREAD\n");
+       io.generic.level = RAW_READ_LOCKREAD;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying empty file read\n");
+       io.lockread.in.fnum = fnum;
+       io.lockread.in.count = 1;
+       io.lockread.in.offset = 0;
+       io.lockread.in.remaining = 0;
+       io.lockread.out.data = buf;
+       status = smb_raw_read(cli->tree, &io);
+
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.lockread.out.nread, 0);
+
+       printf("Trying zero file read\n");
+       io.lockread.in.count = 0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       printf("Trying bad fnum\n");
+       io.lockread.in.fnum = fnum+1;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+       io.lockread.in.fnum = fnum;
+
+       cli_write(cli, fnum, 0, test_data, 0, strlen(test_data));
+
+       printf("Trying small read\n");
+       io.lockread.in.fnum = fnum;
+       io.lockread.in.offset = 0;
+       io.lockread.in.remaining = 0;
+       io.lockread.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       cli_unlock(cli, fnum, 0, 1);
+
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.lockread.out.nread, strlen(test_data));
+       if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+               goto done;
+       }
+
+       printf("Trying short read\n");
+       io.lockread.in.offset = 1;
+       io.lockread.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+       cli_unlock(cli, fnum, 0, strlen(test_data));
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       CHECK_VALUE(io.lockread.out.nread, strlen(test_data)-1);
+       if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+               goto done;
+       }
+
+       printf("Trying max offset\n");
+       io.lockread.in.offset = ~0;
+       io.lockread.in.count = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.lockread.out.nread, 0);
+
+       setup_buffer(buf, seed, maxsize);
+       cli_write(cli, fnum, 0, buf, 0, maxsize);
+       memset(buf, 0, maxsize);
+
+       printf("Trying large read\n");
+       io.lockread.in.offset = 0;
+       io.lockread.in.count = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+       cli_unlock(cli, fnum, 1, strlen(test_data));
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_BUFFER(buf, seed, io.lockread.out.nread);
+       cli_unlock(cli, fnum, 0, 0xFFFF);
+
+
+       printf("Trying locked region\n");
+       cli->session->pid++;
+       if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       cli->session->pid--;
+       memset(buf, 0, maxsize);
+       io.lockread.in.offset = 0;
+       io.lockread.in.count = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       
+
+done:
+       cli_close(cli, fnum);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test readx ops
+*/
+static BOOL test_readx(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_read io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       const char *test_data = "TEST DATA";
+       unsigned seed = time(NULL);
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_READ_READX\n");
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying empty file read\n");
+       io.generic.level = RAW_READ_READX;
+       io.readx.in.fnum = fnum;
+       io.readx.in.mincnt = 1;
+       io.readx.in.maxcnt = 1;
+       io.readx.in.offset = 0;
+       io.readx.in.remaining = 0;
+       io.readx.out.data = buf;
+       status = smb_raw_read(cli->tree, &io);
+
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, 0);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+
+       printf("Trying zero file read\n");
+       io.readx.in.mincnt = 0;
+       io.readx.in.maxcnt = 0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, 0);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+
+       printf("Trying bad fnum\n");
+       io.readx.in.fnum = fnum+1;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+       io.readx.in.fnum = fnum;
+
+       cli_write(cli, fnum, 0, test_data, 0, strlen(test_data));
+
+       printf("Trying small read\n");
+       io.readx.in.fnum = fnum;
+       io.readx.in.offset = 0;
+       io.readx.in.remaining = 0;
+       io.readx.in.mincnt = strlen(test_data);
+       io.readx.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, strlen(test_data));
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+       if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+               goto done;
+       }
+
+       printf("Trying short read\n");
+       io.readx.in.offset = 1;
+       io.readx.in.mincnt = strlen(test_data);
+       io.readx.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, strlen(test_data)-1);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+       if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+               goto done;
+       }
+
+       printf("Trying max offset\n");
+       io.readx.in.offset = 0xffffffff;
+       io.readx.in.mincnt = strlen(test_data);
+       io.readx.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, 0);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+
+       setup_buffer(buf, seed, maxsize);
+       cli_write(cli, fnum, 0, buf, 0, maxsize);
+       memset(buf, 0, maxsize);
+
+       printf("Trying large read\n");
+       io.readx.in.offset = 0;
+       io.readx.in.mincnt = ~0;
+       io.readx.in.maxcnt = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+       CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+       CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+       printf("Trying mincnt > maxcnt\n");
+       memset(buf, 0, maxsize);
+       io.readx.in.offset = 0;
+       io.readx.in.mincnt = 30000;
+       io.readx.in.maxcnt = 20000;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+       CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+       CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+       printf("Trying mincnt < maxcnt\n");
+       memset(buf, 0, maxsize);
+       io.readx.in.offset = 0;
+       io.readx.in.mincnt = 20000;
+       io.readx.in.maxcnt = 30000;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.remaining, 0xFFFF);
+       CHECK_VALUE(io.readx.out.compaction_mode, 0);
+       CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt);
+       CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+       printf("Trying locked region\n");
+       cli->session->pid++;
+       if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       cli->session->pid--;
+       memset(buf, 0, maxsize);
+       io.readx.in.offset = 0;
+       io.readx.in.mincnt = 100;
+       io.readx.in.maxcnt = 200;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);     
+
+#ifdef LARGE_SMB_OFF_T
+       printf("Trying large offset read\n");
+       io.readx.in.offset = ((SMB_BIG_UINT)0x2) << 32;
+       io.readx.in.mincnt = 10;
+       io.readx.in.maxcnt = 10;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, 0);
+
+       if (!cli_lock64(cli, fnum, io.readx.in.offset, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readx.out.nread, 0);
+#endif
+
+done:
+       cli_close(cli, fnum);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test readbraw ops
+*/
+static BOOL test_readbraw(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_read io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       const char *test_data = "TEST DATA";
+       unsigned seed = time(NULL);
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_READ_READBRAW\n");
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying empty file read\n");
+       io.generic.level = RAW_READ_READBRAW;
+       io.readbraw.in.fnum = fnum;
+       io.readbraw.in.mincnt = 1;
+       io.readbraw.in.maxcnt = 1;
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.timeout = 0;
+       io.readbraw.out.data = buf;
+       status = smb_raw_read(cli->tree, &io);
+
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+
+       printf("Trying zero file read\n");
+       io.readbraw.in.mincnt = 0;
+       io.readbraw.in.maxcnt = 0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+
+       printf("Trying bad fnum\n");
+       io.readbraw.in.fnum = fnum+1;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+       io.readbraw.in.fnum = fnum;
+
+       cli_write(cli, fnum, 0, test_data, 0, strlen(test_data));
+
+       printf("Trying small read\n");
+       io.readbraw.in.fnum = fnum;
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = strlen(test_data);
+       io.readbraw.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, strlen(test_data));
+       if (memcmp(buf, test_data, strlen(test_data)) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf);
+               goto done;
+       }
+
+       printf("Trying short read\n");
+       io.readbraw.in.offset = 1;
+       io.readbraw.in.mincnt = strlen(test_data);
+       io.readbraw.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)-1);
+       if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) {
+               ret = False;
+               printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf);
+               goto done;
+       }
+
+       printf("Trying max offset\n");
+       io.readbraw.in.offset = ~0;
+       io.readbraw.in.mincnt = strlen(test_data);
+       io.readbraw.in.maxcnt = strlen(test_data);
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+
+       setup_buffer(buf, seed, maxsize);
+       cli_write(cli, fnum, 0, buf, 0, maxsize);
+       memset(buf, 0, maxsize);
+
+       printf("Trying large read\n");
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = ~0;
+       io.readbraw.in.maxcnt = ~0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0xFFFF);
+       CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+       printf("Trying mincnt > maxcnt\n");
+       memset(buf, 0, maxsize);
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = 30000;
+       io.readbraw.in.maxcnt = 20000;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt);
+       CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+       printf("Trying mincnt < maxcnt\n");
+       memset(buf, 0, maxsize);
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = 20000;
+       io.readbraw.in.maxcnt = 30000;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt);
+       CHECK_BUFFER(buf, seed, io.readbraw.out.nread);
+
+       printf("Trying locked region\n");
+       cli->session->pid++;
+       if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       cli->session->pid--;
+       memset(buf, 0, maxsize);
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = 100;
+       io.readbraw.in.maxcnt = 200;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+
+       printf("Trying locked region with timeout\n");
+       memset(buf, 0, maxsize);
+       io.readbraw.in.offset = 0;
+       io.readbraw.in.mincnt = 100;
+       io.readbraw.in.maxcnt = 200;
+       io.readbraw.in.timeout = 10000;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+
+#ifdef LARGE_SMB_OFF_T
+       printf("Trying large offset read\n");
+       io.readbraw.in.offset = ((SMB_BIG_UINT)0x2) << 32;
+       io.readbraw.in.mincnt = 10;
+       io.readbraw.in.maxcnt = 10;
+       io.readbraw.in.timeout = 0;
+       status = smb_raw_read(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.readbraw.out.nread, 0);
+#endif
+
+done:
+       cli_close(cli, fnum);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of read calls
+*/
+BOOL torture_raw_read(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_read");
+
+       if (!test_read(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_readx(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_lockread(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_readbraw(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c
new file mode 100644 (file)
index 0000000..4cfa1c9
--- /dev/null
@@ -0,0 +1,128 @@
+/* 
+   Unix SMB/CIFS implementation.
+   rename test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define BASEDIR "\\testrename"
+
+/*
+  test SMBmv ops
+*/
+static BOOL test_mv(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       struct smb_rename io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       const char *fname1 = BASEDIR "\\test1.txt";
+       const char *fname2 = BASEDIR "\\test2.txt";
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Trying simple rename\n");
+
+       fnum = create_complex_file(cli, mem_ctx, fname1);
+       
+       io.in.pattern1 = fname1;
+       io.in.pattern2 = fname2;
+       io.in.attrib = 0;
+       
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+       
+       smb_raw_exit(cli->session);
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+
+       printf("trying wildcard rename\n");
+       io.in.pattern1 = BASEDIR "\\*.txt";
+       io.in.pattern2 = fname1;
+       
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("and again\n");
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Trying extension change\n");
+       io.in.pattern1 = BASEDIR "\\*.txt";
+       io.in.pattern2 = BASEDIR "\\*.bak";
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+       printf("Checking attrib handling\n");
+       torture_set_file_attribute(cli->tree, BASEDIR "\\test1.bak", FILE_ATTRIBUTE_HIDDEN);
+       io.in.pattern1 = BASEDIR "\\test1.bak";
+       io.in.pattern2 = BASEDIR "\\*.txt";
+       io.in.attrib = 0;
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+       io.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       status = smb_raw_rename(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of rename calls
+*/
+BOOL torture_raw_rename(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_rename");
+
+       if (!test_mv(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c
new file mode 100644 (file)
index 0000000..6cfdd2b
--- /dev/null
@@ -0,0 +1,610 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_SEARCH_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+#define BASEDIR "\\testsearch"
+
+/*
+  callback function for single_search
+*/
+static BOOL single_search_callback(void *private, union smb_search_data *file)
+{
+       union smb_search_data *data = private;
+
+       *data = *file;
+
+       return True;
+}
+
+/*
+  do a single file (non-wildcard) search 
+*/
+static NTSTATUS single_search(struct cli_state *cli, 
+                             TALLOC_CTX *mem_ctx,
+                             const char *pattern,
+                             enum search_level level,
+                             union smb_search_data *data)
+{
+       union smb_search_first io;
+       NTSTATUS status;
+
+       io.generic.level = level;
+       if (level == RAW_SEARCH_SEARCH) {
+               io.search_first.in.max_count = 1;
+               io.search_first.in.search_attrib = 0;
+               io.search_first.in.pattern = pattern;
+       } else {
+               io.t2ffirst.in.search_attrib = 0;
+               io.t2ffirst.in.max_count = 1;
+               io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
+               io.t2ffirst.in.storage_type = 0;
+               io.t2ffirst.in.pattern = pattern;
+       }
+
+       status = smb_raw_search_first(cli->tree, mem_ctx,
+                                     &io, (void *)data, single_search_callback);
+       
+       return status;
+}
+
+
+static struct {
+       const char *name;
+       enum search_level level;
+       uint32 capability_mask;
+       NTSTATUS status;
+       union smb_search_data data;
+} levels[] = {
+       {"SEARCH",              RAW_SEARCH_SEARCH, },
+       {"STANDARD",            RAW_SEARCH_STANDARD, },
+       {"EA_SIZE",             RAW_SEARCH_EA_SIZE, },
+       {"DIRECTORY_INFO",      RAW_SEARCH_DIRECTORY_INFO, },
+       {"FULL_DIRECTORY_INFO", RAW_SEARCH_FULL_DIRECTORY_INFO, },
+       {"NAME_INFO",           RAW_SEARCH_NAME_INFO, },
+       {"BOTH_DIRECTORY_INFO", RAW_SEARCH_BOTH_DIRECTORY_INFO, },
+       {"LEVEL_261",           RAW_SEARCH_261, },
+       {"LEVEL_262",           RAW_SEARCH_262, },
+       {"UNIX_INFO",           RAW_SEARCH_UNIX_INFO, CAP_UNIX}
+};
+
+/* find a level in the table by name */
+static union smb_search_data *find(const char *name)
+{
+       int i;
+       for (i=0;i<ARRAY_SIZE(levels);i++) {
+               if (NT_STATUS_IS_OK(levels[i].status) && 
+                   strcmp(levels[i].name, name) == 0) {
+                       return &levels[i].data;
+               }
+       }
+       return NULL;
+}
+
+/* 
+   basic testing of all RAW_SEARCH_* calls using a single file
+*/
+static BOOL test_one_file(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       BOOL ret = True;
+       int fnum;
+       const char *fname = "\\torture_search.txt";
+       NTSTATUS status;
+       int i;
+       union smb_fileinfo all_info, alt_info, name_info;
+       union smb_search_data *s;
+
+       printf("Testing one file searches\n");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       /* call all the levels */
+       for (i=0;i<ARRAY_SIZE(levels);i++) {
+               uint32 cap = cli->transport->negotiate.capabilities;
+
+               levels[i].status = single_search(cli, mem_ctx, fname, 
+                                                levels[i].level, &levels[i].data);
+
+               /* see if this server claims to support this level */
+               if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
+                       continue;
+               }
+
+               printf("testing %s\n", levels[i].name);
+
+               if (!NT_STATUS_IS_OK(levels[i].status)) {
+                       printf("search level %s(%d) failed - %s\n",
+                              levels[i].name, (int)levels[i].level, 
+                              nt_errstr(levels[i].status));
+                       ret = False;
+               }
+       }
+
+       /* get the all_info file into to check against */
+       all_info.generic.level = RAW_FILEINFO_ALL_INFO;
+       all_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+       alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+       alt_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+       name_info.generic.level = RAW_FILEINFO_NAME_INFO;
+       name_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (s->sname1.field1 != v.sname2.out.field2) { \
+                       printf("(%d) %s/%s [%d] != %s/%s [%d]\n", \
+                              __LINE__, \
+                               #sname1, #field1, (int)s->sname1.field1, \
+                               #sname2, #field2, (int)v.sname2.out.field2); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (s->sname1.field1 != (~1 & nt_time_to_unix(&v.sname2.out.field2))) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, time_string(mem_ctx, s->sname1.field1), \
+                               #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (memcmp(&s->sname1.field1, &v.sname2.out.field2, sizeof(NTTIME))) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, nt_time_string(mem_ctx, &s->sname1.field1), \
+                               #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1, \
+                               #sname2, #field2, v.sname2.out.field2.s); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1.s || \
+                   strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
+                   wire_bad_flags(&s->sname1.field1, flags)) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1.s, \
+                               #sname2, #field2, v.sname2.out.field2.s); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_NAME(name, sname1, field1, fname, flags) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1.s || \
+                   strcmp(s->sname1.field1.s, fname) || \
+                   wire_bad_flags(&s->sname1.field1, flags)) { \
+                       printf("(%d) %s/%s [%s] != %s\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1.s, \
+                               fname); \
+                       ret = False; \
+               } \
+       }} while (0)
+       
+       /* check that all the results are as expected */
+       CHECK_VAL("SEARCH",              search,              attrib, all_info, all_info, attrib);
+       CHECK_VAL("STANDARD",            standard,            attrib, all_info, all_info, attrib);
+       CHECK_VAL("EA_SIZE",             ea_size,             attrib, all_info, all_info, attrib);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      attrib, all_info, all_info, attrib);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
+       CHECK_VAL("LEVEL_261",           level_261,           attrib, all_info, all_info, attrib);
+       CHECK_VAL("LEVEL_262",           level_262,           attrib, all_info, all_info, attrib);
+
+       CHECK_TIME("SEARCH",             search,              write_time, all_info, all_info, write_time);
+       CHECK_TIME("STANDARD",           standard,            write_time, all_info, all_info, write_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             write_time, all_info, all_info, write_time);
+       CHECK_TIME("STANDARD",           standard,            create_time, all_info, all_info, create_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             create_time, all_info, all_info, create_time);
+       CHECK_TIME("STANDARD",           standard,            access_time, all_info, all_info, access_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             access_time, all_info, all_info, access_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           write_time, all_info, all_info, write_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           create_time, all_info, all_info, create_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           access_time, all_info, all_info, access_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           create_time, all_info, all_info, create_time);
+
+       CHECK_VAL("SEARCH",              search,              size, all_info, all_info, size);
+       CHECK_VAL("STANDARD",            standard,            size, all_info, all_info, size);
+       CHECK_VAL("EA_SIZE",             ea_size,             size, all_info, all_info, size);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      size, all_info, all_info, size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
+       CHECK_VAL("LEVEL_261",           level_261,           size, all_info, all_info, size);
+       CHECK_VAL("LEVEL_262",           level_262,           size, all_info, all_info, size);
+
+       CHECK_VAL("STANDARD",            standard,            alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("EA_SIZE",             ea_size,             alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("LEVEL_261",           level_261,           alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("LEVEL_262",           level_262,           alloc_size, all_info, all_info, alloc_size);
+
+       CHECK_VAL("EA_SIZE",             ea_size,             ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("LEVEL_261",           level_261,           ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("LEVEL_262",           level_262,           ea_size, all_info, all_info, ea_size);
+
+       CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname);
+       CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
+
+       CHECK_NAME("STANDARD",            standard,            name, fname+1, 0);
+       CHECK_NAME("EA_SIZE",             ea_size,             name, fname+1, 0);
+       CHECK_NAME("DIRECTORY_INFO",      directory_info,      name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("NAME_INFO",           name_info,           name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("LEVEL_261",           level_261,           name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("LEVEL_262",           level_262,           name, fname+1, STR_TERMINATE_ASCII);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_unlink(cli, fname);
+
+       return ret;
+}
+
+
+struct multiple_result {
+       TALLOC_CTX *mem_ctx;
+       int count;
+       union smb_search_data *list;
+};
+
+/*
+  callback function for multiple_search
+*/
+static BOOL multiple_search_callback(void *private, union smb_search_data *file)
+{
+       struct multiple_result *data = private;
+
+
+       data->count++;
+       data->list = talloc_realloc(data->mem_ctx, 
+                                   data->list, 
+                                   data->count * (sizeof(data->list[0])));
+
+       data->list[data->count-1] = *file;
+
+       return True;
+}
+
+enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY};
+
+/*
+  do a single file (non-wildcard) search 
+*/
+static NTSTATUS multiple_search(struct cli_state *cli, 
+                               TALLOC_CTX *mem_ctx,
+                               const char *pattern,
+                               enum search_level level,
+                               enum continue_type cont_type,
+                               void *data)
+{
+       union smb_search_first io;
+       union smb_search_next io2;
+       NTSTATUS status;
+       const int per_search = 300;
+       struct multiple_result *result = data;
+
+       io.generic.level = level;
+       if (level == RAW_SEARCH_SEARCH) {
+               io.search_first.in.max_count = per_search;
+               io.search_first.in.search_attrib = 0;
+               io.search_first.in.pattern = pattern;
+       } else {
+               io.t2ffirst.in.search_attrib = 0;
+               io.t2ffirst.in.max_count = per_search;
+               io.t2ffirst.in.flags = 0;
+               io.t2ffirst.in.storage_type = 0;
+               io.t2ffirst.in.pattern = pattern;
+               if (cont_type == CONT_RESUME_KEY) {
+                       io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | 
+                               FLAG_TRANS2_FIND_BACKUP_INTENT;
+               }
+       }
+
+       status = smb_raw_search_first(cli->tree, mem_ctx,
+                                     &io, data, multiple_search_callback);
+       
+
+       while (NT_STATUS_IS_OK(status)) {
+               io2.generic.level = level;
+               if (level == RAW_SEARCH_SEARCH) {
+                       io2.search_next.in.max_count = per_search;
+                       io2.search_next.in.search_attrib = 0;
+                       io2.search_next.in.search_id = result->list[result->count-1].search.search_id;
+               } else {
+                       io2.t2fnext.in.handle = io.t2ffirst.out.handle;
+                       io2.t2fnext.in.max_count = per_search;
+                       io2.t2fnext.in.resume_key = 0;
+                       io2.t2fnext.in.flags = 0;
+                       io2.t2fnext.in.last_name = "";
+                       switch (cont_type) {
+                       case CONT_RESUME_KEY:
+                               if (level == RAW_SEARCH_STANDARD) {
+                                       io2.t2fnext.in.resume_key = 
+                                               result->list[result->count-1].standard.resume_key;
+                               } else {
+                                       io2.t2fnext.in.resume_key = 
+                                               result->list[result->count-1].both_directory_info.file_index;
+                               }
+                               io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME |
+                                       FLAG_TRANS2_FIND_BACKUP_INTENT;
+                               break;
+                       case CONT_NAME:
+                               if (level == RAW_SEARCH_STANDARD) {
+                                       io2.t2fnext.in.last_name = 
+                                               result->list[result->count-1].standard.name.s;
+                               } else {
+                                       io2.t2fnext.in.last_name = 
+                                               result->list[result->count-1].both_directory_info.name.s;
+                               }
+                               break;
+                       case CONT_FLAGS:
+                               io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE;
+                               break;
+                       }
+               }
+
+               status = smb_raw_search_next(cli->tree, mem_ctx,
+                                            &io2, data, multiple_search_callback);
+               if (!NT_STATUS_IS_OK(status)) {
+                       break;
+               }
+               if (level == RAW_SEARCH_SEARCH) {
+                       if (io2.search_next.out.count == 0) {
+                               break;
+                       }
+               } else if (io2.t2fnext.out.count == 0 ||
+                          io2.t2fnext.out.end_of_search) {
+                       break;
+               }
+       }
+
+       return status;
+}
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+       }} while (0)
+
+
+static int search_both_compare(union smb_search_data *d1, union smb_search_data *d2)
+{
+       return strcmp(d1->both_directory_info.name.s, d2->both_directory_info.name.s);
+}
+
+static int search_standard_compare(union smb_search_data *d1, union smb_search_data *d2)
+{
+       return strcmp(d1->standard.name.s, d2->standard.name.s);
+}
+
+static int search_old_compare(union smb_search_data *d1, union smb_search_data *d2)
+{
+       return strcmp(d1->search.name, d2->search.name);
+}
+
+
+/* 
+   basic testing of search calls using many files
+*/
+static BOOL test_many_files(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       const int num_files = 700;
+       int i, fnum, t;
+       char *fname;
+       BOOL ret = True;
+       NTSTATUS status;
+       struct multiple_result result;
+       struct {
+               const char *name;
+               const char *cont_name;
+               enum search_level level;
+               enum continue_type cont_type;
+       } search_types[] = {
+               {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_FLAGS},
+               {"BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY},
+               {"BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_NAME},
+               {"STANDARD",            "FLAGS", RAW_SEARCH_STANDARD,            CONT_FLAGS},
+               {"STANDARD",            "KEY",   RAW_SEARCH_STANDARD,            CONT_RESUME_KEY},
+               {"STANDARD",            "NAME",  RAW_SEARCH_STANDARD,            CONT_NAME},
+               {"SEARCH",              "ID",    RAW_SEARCH_SEARCH,              CONT_RESUME_KEY}
+       };
+
+       if (cli_deltree(cli, BASEDIR) == -1 || 
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       printf("Creating %d files\n", num_files);
+
+       for (i=0;i<num_files;i++) {
+               asprintf(&fname, BASEDIR "\\test%03d.txt", i);
+               fnum = cli_open(cli, fname, O_CREAT|O_RDWR, DENY_NONE);
+               if (fnum == -1) {
+                       printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+                       ret = False;
+                       goto done;
+               }
+               free(fname);
+               cli_close(cli, fnum);
+       }
+
+
+       for (t=0;t<ARRAY_SIZE(search_types);t++) {
+               ZERO_STRUCT(result);
+               result.mem_ctx = mem_ctx;
+       
+               printf("Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
+
+               status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*", 
+                                        search_types[t].level,
+                                        search_types[t].cont_type,
+                                        &result);
+       
+               CHECK_STATUS(status, NT_STATUS_OK);
+               CHECK_VALUE(result.count, num_files);
+
+               if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) {
+                       qsort(result.list, result.count, sizeof(result.list[0]), search_both_compare);
+               } else if (search_types[t].level == RAW_SEARCH_STANDARD) {
+                       qsort(result.list, result.count, sizeof(result.list[0]), search_standard_compare);
+               } else {
+                       qsort(result.list, result.count, sizeof(result.list[0]), search_old_compare);
+               }
+
+               for (i=0;i<num_files;i++) {
+                       const char *s;
+                       if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) {
+                               s = result.list[i].both_directory_info.name.s;
+                       } else if (search_types[t].level == RAW_SEARCH_STANDARD) {
+                               s = result.list[i].standard.name.s;
+                       } else {
+                               s = result.list[i].search.name;
+                       }
+                       asprintf(&fname, "test%03d.txt", i);
+                       if (strcmp(fname, s)) {
+                               printf("Incorrect name %s at entry %d\n", s, i);
+                               ret = False;
+                               break;
+                       }
+                       free(fname);
+               }
+       }
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+
+       return ret;
+}
+
+
+/* 
+   basic testing of all RAW_SEARCH_* calls using a single file
+*/
+BOOL torture_raw_search(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_search");
+
+       if (!test_one_file(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_many_files(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       
+       return ret;
+}
diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c
new file mode 100644 (file)
index 0000000..9379b67
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+   Unix SMB/CIFS implementation.
+   seek test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define BASEDIR "\\testseek"
+
+/*
+  test seek ops
+*/
+static BOOL test_seek(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       struct smb_seek io;
+       union smb_fileinfo finfo;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       const char *fname = BASEDIR "\\test.txt";
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("Failed to open test.txt - %s\n", cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+       finfo.position_information.in.fnum = fnum;
+       
+       printf("Trying bad handle\n");
+       io.in.fnum = fnum+1;
+       io.in.mode = SEEK_MODE_START;
+       io.in.offset = 0;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("Trying simple seek\n");
+       io.in.fnum = fnum;
+       io.in.mode = SEEK_MODE_START;
+       io.in.offset = 17;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.out.offset, 17);
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(finfo.position_information.out.position, 0);
+       
+       printf("Trying relative seek\n");
+       io.in.fnum = fnum;
+       io.in.mode = SEEK_MODE_CURRENT;
+       io.in.offset = -3;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.out.offset, 14);
+
+       printf("Trying end seek\n");
+       io.in.fnum = fnum;
+       io.in.mode = SEEK_MODE_END;
+       io.in.offset = 0;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+       finfo.all_info.in.fnum = fnum;
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.out.offset, finfo.all_info.out.size);
+
+       printf("Trying max seek\n");
+       io.in.fnum = fnum;
+       io.in.mode = SEEK_MODE_START;
+       io.in.offset = -1;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.out.offset, 0xffffffff);
+
+       printf("Trying max overflow\n");
+       io.in.fnum = fnum;
+       io.in.mode = SEEK_MODE_CURRENT;
+       io.in.offset = 1000;
+       status = smb_raw_seek(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.out.offset, 999);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of seek calls
+*/
+BOOL torture_raw_seek(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_seek");
+
+       if (!test_seek(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c
new file mode 100644 (file)
index 0000000..c169895
--- /dev/null
@@ -0,0 +1,498 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_SFILEINFO_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define BASEDIR "\\testsfileinfo"
+
+/* basic testing of all RAW_SFILEINFO_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+*/
+BOOL torture_raw_sfileinfo(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+       int fnum = -1;
+       char *fnum_fname;
+       char *fnum_fname_new;
+       char *path_fname;
+       char *path_fname_new;
+       union smb_fileinfo finfo1, finfo2;
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status, status2;
+       const char *call_name;
+       time_t basetime = (time(NULL) - 86400) & ~1;
+       BOOL check_fnum;
+       int n = time(NULL) % 100;
+       
+       asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n);
+       asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n);
+       asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n);
+       asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n);
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_sfileinfo");
+
+       cli_deltree(cli, BASEDIR);
+       cli_mkdir(cli, BASEDIR);
+
+#define RECREATE_FILE(fname) do { \
+       if (fnum != -1) cli_close(cli, fnum); \
+       fnum = create_complex_file(cli, mem_ctx, fname); \
+       if (fnum == -1) { \
+               printf("(%d) ERROR: open of %s failed (%s)\n", \
+                      __LINE__, fname, cli_errstr(cli)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define RECREATE_BOTH do { \
+               RECREATE_FILE(path_fname); \
+               cli_close(cli, fnum); \
+               RECREATE_FILE(fnum_fname); \
+       } while (0)
+
+       RECREATE_BOTH;
+       
+#define CHECK_CALL_FNUM(call, rightstatus) do { \
+       check_fnum = True; \
+       call_name = #call; \
+       sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+       sfinfo.generic.file.fnum = fnum; \
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
+       if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+               printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \
+                       nt_errstr(status), nt_errstr(rightstatus)); \
+               ret = False; \
+       } \
+       finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+       finfo1.generic.in.fnum = fnum; \
+       status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1); \
+       if (!NT_STATUS_IS_OK(status2)) { \
+               printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status)); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_CALL_PATH(call, rightstatus) do { \
+       check_fnum = False; \
+       call_name = #call; \
+       sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+       sfinfo.generic.file.fname = path_fname; \
+       status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+               sfinfo.generic.file.fname = path_fname_new; \
+               status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+       } \
+       if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+               printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \
+                       nt_errstr(status), nt_errstr(rightstatus)); \
+               ret = False; \
+       } \
+       finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+       finfo1.generic.in.fname = path_fname; \
+       status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \
+       if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+               finfo1.generic.in.fname = path_fname_new; \
+               status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \
+       } \
+       if (!NT_STATUS_IS_OK(status2)) { \
+               printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status2)); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK1(call) \
+       do { if (NT_STATUS_IS_OK(status)) { \
+               finfo2.generic.level = RAW_FILEINFO_ ## call; \
+               if (check_fnum) { \
+                       finfo2.generic.in.fnum = fnum; \
+                       status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2); \
+               } else { \
+                       finfo2.generic.in.fname = path_fname; \
+                       status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \
+                       if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+                               finfo2.generic.in.fname = path_fname_new; \
+                               status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \
+                       } \
+               } \
+               if (!NT_STATUS_IS_OK(status2)) { \
+                       printf("%s - %s\n", #call, nt_errstr(status2)); \
+               } \
+       }} while (0)
+
+#define CHECK_VALUE(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && finfo2.stype.out.field != value) { \
+               printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \
+                      call_name, #stype, #field, \
+                      (uint_t)value, (uint_t)finfo2.stype.out.field); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+#define CHECK_TIME(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && nt_time_to_unix(&finfo2.stype.out.field) != value) { \
+               printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \
+                       call_name, #stype, #field, \
+                       (uint_t)value, \
+                       (uint_t)nt_time_to_unix(&finfo2.stype.out.field)); \
+               printf("\t%s", http_timestring(mem_ctx, value)); \
+               printf("\t%s\n", nt_time_string(mem_ctx, &finfo2.stype.out.field)); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+#define CHECK_STR(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && strcmp(finfo2.stype.out.field, value) != 0) { \
+               printf("(%d) %s - %s/%s should be '%s' - '%s'\n", __LINE__, \
+                       call_name, #stype, #field, \
+                       value, \
+                       finfo2.stype.out.field); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+       
+       printf("test setattr\n");
+       sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY;
+       sfinfo.setattr.in.write_time = basetime;
+       CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+       CHECK_VALUE  (ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+       CHECK_TIME   (ALL_INFO, all_info, write_time, basetime);
+
+       printf("setting to NORMAL doesn't do anything\n");
+       sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_NORMAL;
+       sfinfo.setattr.in.write_time = 0;
+       CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+       CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+       printf("a zero write_time means don't change\n");
+       sfinfo.setattr.in.attrib = 0;
+       sfinfo.setattr.in.write_time = 0;
+       CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+       CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+       printf("test setattre\n");
+       sfinfo.setattre.in.create_time = basetime + 20;
+       sfinfo.setattre.in.access_time = basetime + 30;
+       sfinfo.setattre.in.write_time  = basetime + 40;
+       CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 40);
+
+       sfinfo.setattre.in.create_time = 0;
+       sfinfo.setattre.in.access_time = 0;
+       sfinfo.setattre.in.write_time  = 0;
+       CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 40);
+
+       printf("test standard level\n");
+       sfinfo.standard.in.create_time = basetime + 100;
+       sfinfo.standard.in.access_time = basetime + 200;
+       sfinfo.standard.in.write_time  = basetime + 300;
+       CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+
+       printf("test basic_info level\n");
+       basetime += 86400;
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  basetime + 300);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
+       CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       printf("a zero time means don't change\n");
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  0);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+       CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       printf("test basic_information level\n");
+       basetime += 86400;
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  basetime + 300);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
+       CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       printf("a zero time means don't change\n");
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  0);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+       CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+
+       /* interesting - w2k3 leaves change_time as current time for 0 change time
+          in setpathinfo
+         CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       */
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       printf("test disposition_info level\n");
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       printf("test disposition_information level\n");
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+       /* this would delete the file! */
+       /*
+         CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+         CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+         CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+       */
+
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       printf("test allocation_info level\n");
+       sfinfo.allocation_info.in.alloc_size = 0;
+       CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       sfinfo.allocation_info.in.alloc_size = 4096;
+       CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       RECREATE_BOTH;
+       sfinfo.allocation_info.in.alloc_size = 0;
+       CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       sfinfo.allocation_info.in.alloc_size = 4096;
+       CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       /* setting the allocation size up via setpathinfo seems
+          to be broken in w2k3 */
+       CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       printf("test end_of_file_info level\n");
+       sfinfo.end_of_file_info.in.size = 37;
+       CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       sfinfo.end_of_file_info.in.size = 7;
+       CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+       sfinfo.end_of_file_info.in.size = 37;
+       CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       sfinfo.end_of_file_info.in.size = 7;
+       CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+       CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+       printf("test position_information level\n");
+       sfinfo.position_information.in.position = 123456;
+       CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456);
+
+       CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0);
+
+       printf("test mode_information level\n");
+       sfinfo.mode_information.in.mode = 2;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2);
+
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       sfinfo.mode_information.in.mode = 1;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+
+       sfinfo.mode_information.in.mode = 0;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       printf("finally the rename_information level\n");
+       cli_close(cli, create_complex_file(cli, mem_ctx, fnum_fname_new));
+       cli_close(cli, create_complex_file(cli, mem_ctx, path_fname_new));
+
+       sfinfo.rename_information.in.overwrite = 0;
+       sfinfo.rename_information.in.root_fid  = 0;
+       sfinfo.rename_information.in.new_name  = fnum_fname_new+strlen(BASEDIR)+1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       sfinfo.rename_information.in.new_name  = path_fname_new+strlen(BASEDIR)+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       sfinfo.rename_information.in.new_name  = fnum_fname_new+strlen(BASEDIR)+1;
+       sfinfo.rename_information.in.overwrite = 1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new);
+
+       sfinfo.rename_information.in.new_name  = path_fname_new+strlen(BASEDIR)+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new);
+
+       sfinfo.rename_information.in.new_name  = fnum_fname+strlen(BASEDIR)+1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+       sfinfo.rename_information.in.new_name  = path_fname+strlen(BASEDIR)+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, path_fname);
+
+#if 0
+       printf("test unix_basic level\n");
+       CHECK_CALL_FNUM(UNIX_BASIC, NT_STATUS_OK);
+       CHECK_CALL_PATH(UNIX_BASIC, NT_STATUS_OK);
+
+       printf("test unix_link level\n");
+       CHECK_CALL_FNUM(UNIX_LINK, NT_STATUS_OK);
+       CHECK_CALL_PATH(UNIX_LINK, NT_STATUS_OK);
+#endif
+
+done:
+       cli_close(cli, fnum);
+       if (!cli_unlink(cli, fnum_fname)) {
+               printf("Failed to delete %s - %s\n", fnum_fname, cli_errstr(cli));
+       }
+       if (!cli_unlink(cli, path_fname)) {
+               printf("Failed to delete %s - %s\n", path_fname, cli_errstr(cli));
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
+
+
+/* 
+   look for the w2k3 setpathinfo STANDARD bug
+*/
+BOOL torture_raw_sfileinfo_bug(int dummy)
+{
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       const char *fname = "\\bug3.txt";
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status;
+       int fnum;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_sfileinfo");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       cli_close(cli, fnum);
+
+       sfinfo.generic.level = RAW_SFILEINFO_STANDARD;
+       sfinfo.generic.file.fname = fname;
+
+       sfinfo.standard.in.create_time = 0;
+       sfinfo.standard.in.access_time = 0;
+       sfinfo.standard.in.write_time  = 0;
+
+       status = smb_raw_setpathinfo(cli->tree, &sfinfo);
+       printf("%s - %s\n", fname, nt_errstr(status));
+
+       printf("now try and delete %s\n", fname);
+
+       return True;
+}
diff --git a/source4/torture/raw/unlink.c b/source4/torture/raw/unlink.c
new file mode 100644 (file)
index 0000000..9cae91f
--- /dev/null
@@ -0,0 +1,148 @@
+/* 
+   Unix SMB/CIFS implementation.
+   unlink test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define BASEDIR "\\testunlink"
+
+/*
+  test unlink ops
+*/
+static BOOL test_unlink(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       struct smb_unlink io;
+       NTSTATUS status;
+       BOOL ret = True;
+       const char *fname = BASEDIR "\\test.txt";
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Trying non-existant file\n");
+       io.in.pattern = fname;
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+       cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE));
+
+       io.in.pattern = fname;
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Trying a hidden file\n");
+       cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE));
+       torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN);
+
+       io.in.pattern = fname;
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+       io.in.pattern = fname;
+       io.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       printf("Trying a directory\n");
+       io.in.pattern = BASEDIR;
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+       io.in.pattern = BASEDIR;
+       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+       printf("Trying a bad path\n");
+       io.in.pattern = "..";
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+
+       printf("Trying wildcards\n");
+       cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE));
+       io.in.pattern = BASEDIR "\\t*.t";
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+       io.in.pattern = BASEDIR "\\*";
+       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+       io.in.pattern = BASEDIR "\\*.dat";
+       io.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+       io.in.pattern = BASEDIR "\\*.tx?";
+       io.in.attrib = 0;
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_unlink(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of unlink calls
+*/
+BOOL torture_raw_unlink(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_unlink");
+
+       if (!test_unlink(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c
new file mode 100644 (file)
index 0000000..117b322
--- /dev/null
@@ -0,0 +1,702 @@
+/* 
+   Unix SMB/CIFS implementation.
+   test suite for various write operations
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+#define CHECK_STATUS(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               printf("(%d) Incorrect status %s - should be %s\n", \
+                      __LINE__, nt_errstr(status), nt_errstr(correct)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_VALUE(v, correct) do { \
+       if ((v) != (correct)) { \
+               printf("(%d) Incorrect value %s=%d - should be %d\n", \
+                      __LINE__, #v, v, correct); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_BUFFER(buf, seed, len) do { \
+       if (!check_buffer(buf, seed, len, __LINE__)) { \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define CHECK_ALL_INFO(v, field) do { \
+       finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \
+       finfo.all_info.in.fname = fname; \
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \
+       CHECK_STATUS(status, NT_STATUS_OK); \
+       if ((v) != finfo.all_info.out.field) { \
+               printf("(%d) wrong value for field %s  %.0f - %.0f\n", \
+                      __LINE__, #field, (double)v, (double)finfo.all_info.out.field); \
+               dump_all_info(mem_ctx, &finfo); \
+               ret = False; \
+       }} while (0)
+
+
+#define BASEDIR "\\testwrite"
+
+
+/*
+  setup a random buffer based on a seed
+*/
+static void setup_buffer(char *buf, unsigned seed, int len)
+{
+       int i;
+       srandom(seed);
+       for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+  check a random buffer based on a seed
+*/
+static BOOL check_buffer(char *buf, unsigned seed, int len, int line)
+{
+       int i;
+       srandom(seed);
+       for (i=0;i<len;i++) {
+               char v = random();
+               if (buf[i] != v) {
+                       printf("Buffer incorrect at line %d! ofs=%d buf=0x%x correct=0x%x\n", 
+                              line, i, buf[i], v);
+                       return False;
+               }
+       }
+       return True;
+}
+
+/*
+  test write ops
+*/
+static BOOL test_write(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_write io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       unsigned seed = time(NULL);
+       union smb_fileinfo finfo;
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_WRITE_WRITE\n");
+       io.generic.level = RAW_WRITE_WRITE;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying zero write\n");
+       io.write.in.fnum = fnum;
+       io.write.in.count = 0;
+       io.write.in.offset = 0;
+       io.write.in.remaining = 0;
+       io.write.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.write.out.nwritten, 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying small write\n");
+       io.write.in.count = 9;
+       io.write.in.offset = 4;
+       io.write.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.write.out.nwritten, io.write.in.count);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying large write\n");
+       io.write.in.count = 4000;
+       io.write.in.offset = 0;
+       io.write.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.write.out.nwritten, 4000);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+       printf("Trying bad fnum\n");
+       io.write.in.fnum = fnum+1;
+       io.write.in.count = 4000;
+       io.write.in.offset = 0;
+       io.write.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("Setting file as sparse\n");
+       status = torture_set_sparse(cli->tree, fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       printf("Trying 2^32 offset\n");
+       setup_buffer(buf, seed, maxsize);
+       io.write.in.fnum = fnum;
+       io.write.in.count = 4000;
+       io.write.in.offset = 0xFFFFFFFF - 2000;
+       io.write.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.write.out.nwritten, 4000);
+       CHECK_ALL_INFO(io.write.in.count + (SMB_BIG_UINT)io.write.in.offset, size);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, io.write.in.offset, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test writex ops
+*/
+static BOOL test_writex(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_write io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       unsigned seed = time(NULL);
+       union smb_fileinfo finfo;
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_WRITE_WRITEX\n");
+       io.generic.level = RAW_WRITE_WRITEX;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying zero write\n");
+       io.writex.in.fnum = fnum;
+       io.writex.in.offset = 0;
+       io.writex.in.wmode = 0;
+       io.writex.in.remaining = 0;
+       io.writex.in.count = 0;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying small write\n");
+       io.writex.in.count = 9;
+       io.writex.in.offset = 4;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying large write\n");
+       io.writex.in.count = 4000;
+       io.writex.in.offset = 0;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, 4000);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+       printf("Trying bad fnum\n");
+       io.writex.in.fnum = fnum+1;
+       io.writex.in.count = 4000;
+       io.writex.in.offset = 0;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("Testing wmode\n");
+       io.writex.in.fnum = fnum;
+       io.writex.in.count = 1;
+       io.writex.in.offset = 0;
+       io.writex.in.wmode = 1;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+       io.writex.in.wmode = 2;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);
+
+
+       printf("Trying locked region\n");
+       cli->session->pid++;
+       if (!cli_lock(cli, fnum, 3, 1, 0, WRITE_LOCK)) {
+               printf("Failed to lock file at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       cli->session->pid--;
+       io.writex.in.wmode = 0;
+       io.writex.in.count = 4;
+       io.writex.in.offset = 0;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       printf("Setting file as sparse\n");
+       status = torture_set_sparse(cli->tree, fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       printf("Trying 2^32 offset\n");
+       setup_buffer(buf, seed, maxsize);
+       io.writex.in.fnum = fnum;
+       io.writex.in.count = 4000;
+       io.writex.in.offset = 0xFFFFFFFF - 2000;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, 4000);
+       CHECK_ALL_INFO(io.writex.in.count + (SMB_BIG_UINT)io.writex.in.offset, size);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+       printf("Trying 2^43 offset\n");
+       setup_buffer(buf, seed+1, maxsize);
+       io.writex.in.fnum = fnum;
+       io.writex.in.count = 4000;
+       io.writex.in.offset = ((SMB_BIG_UINT)1) << 43;
+       io.writex.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writex.out.nwritten, 4000);
+       CHECK_ALL_INFO(io.writex.in.count + (SMB_BIG_UINT)io.writex.in.offset, size);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed+1, 4000);
+
+
+       setup_buffer(buf, seed, maxsize);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test write unlock ops
+*/
+static BOOL test_writeunlock(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_write io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       unsigned seed = time(NULL);
+       union smb_fileinfo finfo;
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_WRITE_WRITEUNLOCK\n");
+       io.generic.level = RAW_WRITE_WRITEUNLOCK;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying zero write\n");
+       io.writeunlock.in.fnum = fnum;
+       io.writeunlock.in.count = 0;
+       io.writeunlock.in.offset = 0;
+       io.writeunlock.in.remaining = 0;
+       io.writeunlock.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying small write\n");
+       io.writeunlock.in.count = 9;
+       io.writeunlock.in.offset = 4;
+       io.writeunlock.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+       cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 
+                0, WRITE_LOCK);
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying large write\n");
+       io.writeunlock.in.count = 4000;
+       io.writeunlock.in.offset = 0;
+       io.writeunlock.in.data = buf;
+       cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 
+                0, WRITE_LOCK);
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeunlock.out.nwritten, 4000);
+
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+       printf("Trying bad fnum\n");
+       io.writeunlock.in.fnum = fnum+1;
+       io.writeunlock.in.count = 4000;
+       io.writeunlock.in.offset = 0;
+       io.writeunlock.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("Setting file as sparse\n");
+       status = torture_set_sparse(cli->tree, fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       printf("Trying 2^32 offset\n");
+       setup_buffer(buf, seed, maxsize);
+       io.writeunlock.in.fnum = fnum;
+       io.writeunlock.in.count = 4000;
+       io.writeunlock.in.offset = 0xFFFFFFFF - 2000;
+       io.writeunlock.in.data = buf;
+       cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, 
+                0, WRITE_LOCK);
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeunlock.out.nwritten, 4000);
+       CHECK_ALL_INFO(io.writeunlock.in.count + (SMB_BIG_UINT)io.writeunlock.in.offset, size);
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test write close ops
+*/
+static BOOL test_writeclose(struct cli_state *cli, TALLOC_CTX *mem_ctx)
+{
+       union smb_write io;
+       NTSTATUS status;
+       BOOL ret = True;
+       int fnum;
+       char *buf;
+       const int maxsize = 90000;
+       const char *fname = BASEDIR "\\test.txt";
+       unsigned seed = time(NULL);
+       union smb_fileinfo finfo;
+
+       buf = talloc_zero(mem_ctx, maxsize);
+
+       if (cli_deltree(cli, BASEDIR) == -1 ||
+           !cli_mkdir(cli, BASEDIR)) {
+               printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Testing RAW_WRITE_WRITECLOSE\n");
+       io.generic.level = RAW_WRITE_WRITECLOSE;
+       
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum == -1) {
+               printf("Failed to create %s - %s\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       printf("Trying zero write\n");
+       io.writeclose.in.fnum = fnum;
+       io.writeclose.in.count = 0;
+       io.writeclose.in.offset = 0;
+       io.writeclose.in.mtime = 0;
+       io.writeclose.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying small write\n");
+       io.writeclose.in.count = 9;
+       io.writeclose.in.offset = 4;
+       io.writeclose.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       io.writeclose.in.fnum = fnum;
+
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);
+
+       fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       io.writeclose.in.fnum = fnum;
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 13) != 13) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf+4, seed, 9);
+       CHECK_VALUE(IVAL(buf,0), 0);
+
+       setup_buffer(buf, seed, maxsize);
+
+       printf("Trying large write\n");
+       io.writeclose.in.count = 4000;
+       io.writeclose.in.offset = 0;
+       io.writeclose.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeclose.out.nwritten, 4000);
+
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       io.writeclose.in.fnum = fnum;
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, 0, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+       printf("Trying bad fnum\n");
+       io.writeclose.in.fnum = fnum+1;
+       io.writeclose.in.count = 4000;
+       io.writeclose.in.offset = 0;
+       io.writeclose.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+       printf("Setting file as sparse\n");
+       status = torture_set_sparse(cli->tree, fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       
+       printf("Trying 2^32 offset\n");
+       setup_buffer(buf, seed, maxsize);
+       io.writeclose.in.fnum = fnum;
+       io.writeclose.in.count = 4000;
+       io.writeclose.in.offset = 0xFFFFFFFF - 2000;
+       io.writeclose.in.data = buf;
+       status = smb_raw_write(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(io.writeclose.out.nwritten, 4000);
+       CHECK_ALL_INFO(io.writeclose.in.count + (SMB_BIG_UINT)io.writeclose.in.offset, size);
+
+       fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       io.writeclose.in.fnum = fnum;
+
+       memset(buf, 0, maxsize);
+       if (cli_read(cli, fnum, buf, io.writeclose.in.offset, 4000) != 4000) {
+               printf("read failed at %d\n", __LINE__);
+               ret = False;
+               goto done;
+       }
+       CHECK_BUFFER(buf, seed, 4000);
+
+done:
+       smb_raw_exit(cli->session);
+       cli_deltree(cli, BASEDIR);
+       return ret;
+}
+
+
+/* 
+   basic testing of write calls
+*/
+BOOL torture_raw_write(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_raw_write");
+
+       if (!test_write(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_writeunlock(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_writeclose(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       if (!test_writex(cli, mem_ctx)) {
+               ret = False;
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/rpctorture.c b/source4/torture/rpctorture.c
new file mode 100644 (file)
index 0000000..04656e7
--- /dev/null
@@ -0,0 +1,526 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB client
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+extern file_info def_finfo;
+
+#define CNV_LANG(s) dos2unix_format(s,False)
+#define CNV_INPUT(s) unix2dos_format(s,True)
+
+static struct cli_state smbcli;
+struct cli_state *smb_cli = &smbcli;
+
+FILE *out_hnd;
+
+static pstring password; /* local copy only, if one is entered */
+
+/****************************************************************************
+initialise smb client structure
+****************************************************************************/
+void rpcclient_init(void)
+{
+       memset((char *)smb_cli, '\0', sizeof(smb_cli));
+       cli_initialise(smb_cli);
+       smb_cli->capabilities |= CAP_NT_SMBS;
+}
+
+/****************************************************************************
+make smb client connection
+****************************************************************************/
+static BOOL rpcclient_connect(struct client_info *info)
+{
+       struct nmb_name calling;
+       struct nmb_name called;
+
+       make_nmb_name(&called , dns_to_netbios_name(info->dest_host ), info->name_type);
+       make_nmb_name(&calling, dns_to_netbios_name(info->myhostname), 0x0);
+
+       if (!cli_establish_connection(smb_cli, 
+                                 info->dest_host, &info->dest_ip, 
+                                 &calling, &called,
+                                 info->share, info->svc_type,
+                                 False, True))
+       {
+               DEBUG(0,("rpcclient_connect: connection failed\n"));
+               cli_shutdown(smb_cli);
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+stop the smb connection(s?)
+****************************************************************************/
+static void rpcclient_stop(void)
+{
+       cli_shutdown(smb_cli);
+}
+
+/****************************************************************************
+  log in as an nt user, log out again. 
+****************************************************************************/
+void run_enums_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+       pstring cmd;
+       int i;
+
+       /* establish connections.  nothing to stop these being re-established. */
+       rpcclient_connect(cli_info);
+
+       DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+       if (cli->fd <= 0)
+       {
+               fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+                                cli_info->dest_host, cli_info->name_type);
+               return;
+       }
+       
+       for (i = 0; i < num_ops; i++)
+       {
+               set_first_token("");
+               cmd_srv_enum_sess(cli_info);
+               set_first_token("");
+               cmd_srv_enum_shares(cli_info);
+               set_first_token("");
+               cmd_srv_enum_files(cli_info);
+
+               if (password[0] != 0)
+               {
+                       slprintf(cmd, sizeof(cmd)-1, "1");
+                       set_first_token(cmd);
+               }
+               else
+               {
+                       set_first_token("");
+               }
+               cmd_srv_enum_conn(cli_info);
+       }
+
+       rpcclient_stop();
+
+}
+
+/****************************************************************************
+  log in as an nt user, log out again. 
+****************************************************************************/
+void run_ntlogin_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+       pstring cmd;
+       int i;
+
+       /* establish connections.  nothing to stop these being re-established. */
+       rpcclient_connect(cli_info);
+
+       DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+       if (cli->fd <= 0)
+       {
+               fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+                                cli_info->dest_host, cli_info->name_type);
+               return;
+       }
+       
+       for (i = 0; i < num_ops; i++)
+       {
+               slprintf(cmd, sizeof(cmd)-1, "%s %s", cli->user_name, password);
+               set_first_token(cmd);
+
+               cmd_netlogon_login_test(cli_info);
+       }
+
+       rpcclient_stop();
+
+}
+
+/****************************************************************************
+  runs n simultaneous functions.
+****************************************************************************/
+static void create_procs(int nprocs, int numops, 
+               struct client_info *cli_info, struct cli_state *cli,
+               void (*fn)(int, struct client_info *, struct cli_state *))
+{
+       int i, status;
+
+       for (i=0;i<nprocs;i++)
+       {
+               if (fork() == 0)
+               {
+                       pid_t mypid = getpid();
+                       sys_srandom(mypid ^ time(NULL));
+                       fn(numops, cli_info, cli);
+                       fflush(out_hnd);
+                       _exit(0);
+               }
+       }
+
+       for (i=0;i<nprocs;i++)
+       {
+               waitpid(0, &status, 0);
+       }
+}
+/****************************************************************************
+usage on the program - OUT OF DATE!
+****************************************************************************/
+static void usage(char *pname)
+{
+  fprintf(out_hnd, "Usage: %s service <password> [-d debuglevel] [-l log] ",
+          pname);
+
+  fprintf(out_hnd, "\nVersion %s\n",VERSION);
+  fprintf(out_hnd, "\t-d debuglevel         set the debuglevel\n");
+  fprintf(out_hnd, "\t-l log basename.      Basename for log/debug files\n");
+  fprintf(out_hnd, "\t-n netbios name.      Use this name as my netbios name\n");
+  fprintf(out_hnd, "\t-m max protocol       set the max protocol level\n");
+  fprintf(out_hnd, "\t-I dest IP            use this IP to connect to\n");
+  fprintf(out_hnd, "\t-E                    write messages to stderr instead of stdout\n");
+  fprintf(out_hnd, "\t-U username           set the network username\n");
+  fprintf(out_hnd, "\t-W workgroup          set the workgroup name\n");
+  fprintf(out_hnd, "\t-t terminal code      terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n");
+  fprintf(out_hnd, "\n");
+}
+
+enum client_action
+{
+       CLIENT_NONE,
+       CLIENT_IPC,
+       CLIENT_SVC
+};
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       char *pname = argv[0];
+       int opt;
+       extern char *optarg;
+       extern int optind;
+       pstring term_code;
+       BOOL got_pass = False;
+       char *cmd_str="";
+       enum client_action cli_action = CLIENT_NONE;
+       int nprocs = 1;
+       int numops = 100;
+       pstring logfile;
+
+       struct client_info cli_info;
+
+       out_hnd = stdout;
+
+       rpcclient_init();
+
+#ifdef KANJI
+       pstrcpy(term_code, KANJI);
+#else /* KANJI */
+       *term_code = 0;
+#endif /* KANJI */
+
+       if (!lp_load(dyn_CONFIGFILE,True, False, False))
+       {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+       }
+
+       DEBUGLEVEL = 0;
+
+       cli_info.put_total_size = 0;
+       cli_info.put_total_time_ms = 0;
+       cli_info.get_total_size = 0;
+       cli_info.get_total_time_ms = 0;
+
+       cli_info.dir_total = 0;
+       cli_info.newer_than = 0;
+       cli_info.archive_level = 0;
+       cli_info.print_mode = 1;
+
+       cli_info.translation = False;
+       cli_info.recurse_dir = False;
+       cli_info.lowercase = False;
+       cli_info.prompt = True;
+       cli_info.abort_mget = True;
+
+       cli_info.dest_ip.s_addr = 0;
+       cli_info.name_type = 0x20;
+
+       pstrcpy(cli_info.cur_dir , "\\");
+       pstrcpy(cli_info.file_sel, "");
+       pstrcpy(cli_info.base_dir, "");
+       pstrcpy(smb_cli->domain, "");
+       pstrcpy(smb_cli->user_name, "");
+       pstrcpy(cli_info.myhostname, "");
+       pstrcpy(cli_info.dest_host, "");
+
+       pstrcpy(cli_info.svc_type, "A:");
+       pstrcpy(cli_info.share, "");
+       pstrcpy(cli_info.service, "");
+
+       ZERO_STRUCT(cli_info.dom.level3_sid);
+       pstrcpy(cli_info.dom.level3_dom, "");
+       ZERO_STRUCT(cli_info.dom.level5_sid);
+       pstrcpy(cli_info.dom.level5_dom, "");
+
+       smb_cli->nt_pipe_fnum   = 0xffff;
+
+       setup_logging(pname, True);
+
+       password[0] = 0;
+
+       if (argc < 2)
+       {
+               usage(pname);
+               exit(1);
+       }
+
+       if (*argv[1] != '-')
+       {
+               pstrcpy(cli_info.service, argv[1]);  
+               /* Convert any '/' characters in the service name to '\' characters */
+               string_replace( cli_info.service, '/','\\');
+               argc--;
+               argv++;
+
+               DEBUG(1,("service: %s\n", cli_info.service));
+
+               if (count_chars(cli_info.service,'\\') < 3)
+               {
+                       usage(pname);
+                       printf("\n%s: Not enough '\\' characters in service\n", cli_info.service);
+                       exit(1);
+               }
+
+               /*
+               if (count_chars(cli_info.service,'\\') > 3)
+               {
+                       usage(pname);
+                       printf("\n%s: Too many '\\' characters in service\n", cli_info.service);
+                       exit(1);
+               }
+               */
+
+               if (argc > 1 && (*argv[1] != '-'))
+               {
+                       got_pass = True;
+                       pstrcpy(password,argv[1]);  
+                       memset(argv[1],'X',strlen(argv[1]));
+                       argc--;
+                       argv++;
+               }
+
+               cli_action = CLIENT_SVC;
+       }
+
+       while ((opt = getopt(argc, argv,"s:O:M:S:i:N:o:n:d:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
+       {
+               switch (opt)
+               {
+                       case 'm':
+                       {
+                               /* FIXME ... max_protocol seems to be funny here */
+
+                               int max_protocol = 0;
+                               max_protocol = interpret_protocol(optarg,max_protocol);
+                               fprintf(stderr, "max protocol not currently supported\n");
+                               break;
+                       }
+
+                       case 'O':
+                               lp_set_cmdline("socket options", optarg);
+                               break;  
+
+                       case 'S':
+                       {
+                               pstrcpy(cli_info.dest_host,optarg);
+                               strupper(cli_info.dest_host);
+                               cli_action = CLIENT_IPC;
+                               break;
+                       }
+
+                       case 'i':
+                       {
+                               pstrcpy(scope, optarg);
+                               break;
+                       }
+
+                       case 'U':
+                       {
+                               char *lp;
+                               pstrcpy(smb_cli->user_name,optarg);
+                               if ((lp=strchr_m(smb_cli->user_name,'%')))
+                               {
+                                       *lp = 0;
+                                       pstrcpy(password,lp+1);
+                                       got_pass = True;
+                                       memset(strchr_m(optarg,'%')+1,'X',strlen(password));
+                               }
+                               break;
+                       }
+
+                       case 'W':
+                       {
+                               pstrcpy(smb_cli->domain,optarg);
+                               break;
+                       }
+
+                       case 'E':
+                       {
+                               dbf = x_stderr;
+                               break;
+                       }
+
+                       case 'I':
+                       {
+                               cli_info.dest_ip = *interpret_addr2(optarg);
+                               if (is_zero_ip(cli_info.dest_ip))
+                               {
+                                       exit(1);
+                               }
+                               break;
+                       }
+
+                       case 'N':
+                       {
+                               nprocs = atoi(optarg);
+                               break;
+                       }
+
+                       case 'o':
+                       {
+                               numops = atoi(optarg);
+                               break;
+                       }
+
+                       case 'n':
+                               lp_set_cmdline("netbios name", optarg);
+                               break;
+
+                       case 'd':
+                       {
+                               if (*optarg == 'A')
+                                       DEBUGLEVEL = 10000;
+                               else
+                                       DEBUGLEVEL = atoi(optarg);
+                               break;
+                       }
+
+                       case 'l':
+                       {
+                               slprintf(logfile, sizeof(logfile)-1,
+                                        "%s.client",optarg);
+                               lp_set_logfile(logfile);
+                               break;
+                       }
+
+                       case 'c':
+                       {
+                               cmd_str = optarg;
+                               got_pass = True;
+                               break;
+                       }
+
+                       case 'h':
+                       {
+                               usage(pname);
+                               exit(0);
+                               break;
+                       }
+
+                       case 's':
+                       {
+                               pstrcpy(dyn_CONFIGFILE, optarg);
+                               break;
+                       }
+
+                       case 't':
+                       {
+                               pstrcpy(term_code, optarg);
+                               break;
+                       }
+
+                       default:
+                       {
+                               usage(pname);
+                               exit(1);
+                               break;
+                       }
+               }
+       }
+
+       if (cli_action == CLIENT_NONE)
+       {
+               usage(pname);
+               exit(1);
+       }
+
+       fstrcpy(cli_info.myhostname, lp_netbios_name());
+
+       DEBUG(3,("%s client started (version %s)\n",timestring(False),VERSION));
+
+       if (*smb_cli->domain == 0)
+       {
+               pstrcpy(smb_cli->domain,lp_workgroup());
+       }
+       strupper(smb_cli->domain);
+
+       load_interfaces();
+
+       if (cli_action == CLIENT_IPC)
+       {
+               pstrcpy(cli_info.share, "IPC$");
+               pstrcpy(cli_info.svc_type, "IPC");
+       }
+
+       fstrcpy(cli_info.mach_acct, cli_info.myhostname);
+       strupper(cli_info.mach_acct);
+       fstrcat(cli_info.mach_acct, "$");
+
+       /* set the password cache info */
+       if (got_pass)
+       {
+               if (password[0] == 0)
+               {
+                       pwd_set_nullpwd(&(smb_cli->pwd));
+               }
+               else
+               {
+                       pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+               }
+       }
+       else 
+       {
+               char *pwd = getpass("Enter Password:");
+               safe_strcpy(password, pwd, sizeof(password));
+               pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+       }
+
+       create_procs(nprocs, numops, &cli_info, smb_cli, run_enums_test);
+
+       if (password[0] != 0)
+       {
+               create_procs(nprocs, numops, &cli_info, smb_cli, run_ntlogin_test);
+       }
+
+       fflush(out_hnd);
+
+       return(0);
+}
diff --git a/source4/torture/samtest.c b/source4/torture/samtest.c
new file mode 100644 (file)
index 0000000..832f3ec
--- /dev/null
@@ -0,0 +1,451 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM module tester
+
+   Copyright (C) 2002 Jelmer Vernooij
+
+   Parts of the code stolen from vfstest by Simo Sorce and Eric Lorimer
+   Parts of the code stolen from rpcclient by Tim Potter
+
+   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 "samtest.h"
+
+struct func_entry {
+       char *name;
+       int (*fn)(struct tcon_context *conn, const char *path);
+};
+
+/* List to hold groups of commands */
+static struct cmd_list {
+       struct cmd_list *prev, *next;
+       struct cmd_set *cmd_set;
+} *cmd_list;
+
+static char* next_command (char** cmdstr)
+{
+       static pstring          command;
+       char                    *p;
+       
+       if (!cmdstr || !(*cmdstr))
+               return NULL;
+       
+       p = strchr_m(*cmdstr, ';');
+       if (p)
+               *p = '\0';
+       pstrcpy(command, *cmdstr);
+       *cmdstr = p;
+       
+       return command;
+}
+
+/* Load specified configuration file */
+static NTSTATUS cmd_conf(struct samtest_state *sam, TALLOC_CTX *mem_ctx,
+                                                int argc, char **argv)
+{
+       if (argc != 2) {
+               printf("Usage: %s <smb.conf>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (!lp_load(argv[1], False, True, False)) {
+               printf("Error loading \"%s\"\n", argv[1]);
+               return NT_STATUS_OK;
+       }
+
+       printf("\"%s\" successfully loaded\n", argv[1]);
+       return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+static NTSTATUS cmd_help(struct samtest_state *st, TALLOC_CTX *mem_ctx,
+                        int argc, const char **argv)
+{
+       struct cmd_list *tmp;
+       struct cmd_set *tmp_set;
+
+       /* Usage */
+       if (argc > 2) {
+               printf("Usage: %s [command]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* Help on one command */
+
+       if (argc == 2) {
+               for (tmp = cmd_list; tmp; tmp = tmp->next) {
+                       
+                       tmp_set = tmp->cmd_set;
+
+                       while(tmp_set->name) {
+                               if (strequal(argv[1], tmp_set->name)) {
+                                       if (tmp_set->usage &&
+                                           tmp_set->usage[0])
+                                               printf("%s\n", tmp_set->usage);
+                                       else
+                                               printf("No help for %s\n", tmp_set->name);
+
+                                       return NT_STATUS_OK;
+                               }
+
+                               tmp_set++;
+                       }
+               }
+
+               printf("No such command: %s\n", argv[1]);
+               return NT_STATUS_OK;
+       }
+
+       /* List all commands */
+
+       for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+               tmp_set = tmp->cmd_set;
+
+               while(tmp_set->name) {
+
+                       printf("%20s\t%s\n", tmp_set->name,
+                              tmp_set->description ? tmp_set->description:
+                              "");
+
+                       tmp_set++;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+static NTSTATUS cmd_debuglevel(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc > 2) {
+               printf("Usage: %s [debuglevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2) {
+               DEBUGLEVEL = atoi(argv[1]);
+       }
+
+       printf("debuglevel is %d\n", DEBUGLEVEL);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       /* Cleanup */
+       talloc_destroy(mem_ctx);
+
+       exit(0);
+       return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static struct cmd_set samtest_commands[] = {
+
+       { "GENERAL OPTIONS" },
+
+       { "help",       cmd_help,       "Get help on commands", "" },
+       { "?",          cmd_help,       "Get help on commands", "" },
+       { "conf",   cmd_conf,   "Load smb configuration file", "conf <smb.conf>" },
+       { "debuglevel", cmd_debuglevel, "Set debug level", "" },
+       { "exit",       cmd_quit,       "Exit program", "" },
+       { "quit",       cmd_quit,       "Exit program", "" },
+
+       { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+       { "---------------", NULL,      "----------------------" },
+       { NULL }
+};
+
+
+/*extern struct cmd_set sam_commands[];*/
+extern struct cmd_set sam_general_commands[];
+extern struct cmd_set sam_domain_commands[];
+extern struct cmd_set sam_account_commands[];
+extern struct cmd_set sam_group_commands[];
+static struct cmd_set *samtest_command_list[] = {
+       samtest_commands,
+       sam_general_commands,
+       sam_domain_commands,
+       sam_account_commands,
+       sam_group_commands,
+       NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+       struct cmd_list *entry;
+
+       if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+               DEBUG(0, ("out of memory\n"));
+               return;
+       }
+
+       ZERO_STRUCTP(entry);
+
+       entry->cmd_set = cmd_set;
+       DLIST_ADD(cmd_list, entry);
+}
+
+static NTSTATUS do_cmd(struct samtest_state *st, struct cmd_set *cmd_entry, char *cmd)
+{
+       char *p = cmd, **argv = NULL;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       TALLOC_CTX *mem_ctx = NULL;
+       pstring buf;
+       int argc = 0, i;
+
+       /* Count number of arguments first time through the loop then
+          allocate memory and strdup them. */
+
+ again:
+       while(next_token(&p, buf, " ", sizeof(buf))) {
+               if (argv) {
+                       argv[argc] = strdup(buf);
+               }
+               
+               argc++;
+       }
+                               
+       if (!argv) {
+
+               /* Create argument list */
+
+               argv = (char **)malloc(sizeof(char *) * argc);
+               memset(argv, 0, sizeof(char *) * argc);
+
+               if (!argv) {
+                       fprintf(stderr, "out of memory\n");
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+                                       
+               p = cmd;
+               argc = 0;
+                                       
+               goto again;
+       }
+
+       /* Call the function */
+
+       if (cmd_entry->fn) {
+
+               if (mem_ctx == NULL) {
+                       /* Create mem_ctx */
+                       if (!(mem_ctx = talloc_init("do_cmd"))) {
+                               DEBUG(0, ("talloc_init() failed\n"));
+                               goto done;
+                       }
+               }
+
+               /* Run command */
+               result = cmd_entry->fn(st, mem_ctx, argc, argv);
+
+       } else {
+               fprintf (stderr, "Invalid command\n");
+               goto done;
+       }
+
+ done:
+                                               
+       /* Cleanup */
+
+       if (argv) {
+               for (i = 0; i < argc; i++)
+                       SAFE_FREE(argv[i]);
+       
+               SAFE_FREE(argv);
+       }
+       
+       return result;
+}
+
+/* Process a command entered at the prompt or as part of -c */
+static NTSTATUS process_cmd(struct samtest_state *st, char *cmd)
+{
+       struct cmd_list *temp_list;
+       BOOL found = False;
+       pstring buf;
+       char *p = cmd;
+       NTSTATUS result = NT_STATUS_OK;
+       int len = 0;
+
+       if (cmd[strlen(cmd) - 1] == '\n')
+               cmd[strlen(cmd) - 1] = '\0';
+
+       if (!next_token(&p, buf, " ", sizeof(buf))) {
+               return NT_STATUS_OK;
+       }
+
+       /* strip the trainly \n if it exsists */
+       len = strlen(buf);
+       if (buf[len-1] == '\n')
+               buf[len-1] = '\0';
+
+       /* Search for matching commands */
+
+       for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+               struct cmd_set *temp_set = temp_list->cmd_set;
+
+               while(temp_set->name) {
+                       if (strequal(buf, temp_set->name)) {
+                               found = True;
+                               result = do_cmd(st, temp_set, cmd);
+
+                               goto done;
+                       }
+                       temp_set++;
+               }
+       }
+
+ done:
+       if (!found && buf[0]) {
+               printf("command not found: %s\n", buf);
+               return NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(result)) {
+               printf("result was %s\n", nt_errstr(result));
+       }
+
+       return result;
+}
+
+void exit_server(char *reason)
+{
+       DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+       exit(0);
+}
+
+static int server_fd = -1;
+int last_message = -1;
+
+int smbd_server_fd(void)
+{
+               return server_fd;
+}
+
+BOOL reload_services(BOOL test)
+{
+       return True;
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+       BOOL                    interactive = True;
+       int                     opt;
+       static char             *cmdstr = "";
+       static char *opt_logfile=NULL;
+       static char *config_file = dyn_CONFIGFILE;
+       pstring                 logfile;
+       struct cmd_set          **cmd_set;
+       struct samtest_state st;
+
+       /* make sure the vars that get altered (4th field) are in
+          a fixed location or certain compilers complain */
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+               {"command",     'e', POPT_ARG_STRING,   &cmdstr, 'e', "Execute semicolon seperated cmds"},
+               {"logfile",     'l', POPT_ARG_STRING,   &opt_logfile, 'l', "Logfile to use instead of stdout"},
+               {"configfile", 'c', POPT_ARG_STRING, &config_file, 0,"use different configuration file",NULL},
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { 0, 0, 0, 0}
+       };
+
+       ZERO_STRUCT(st);
+
+       st.token = get_system_token();
+
+       setlinebuf(stdout);
+
+       DEBUGLEVEL = 1;
+
+       pc = poptGetContext("samtest", argc, (const char **) argv,
+                           long_options, 0);
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'l':
+                       slprintf(logfile, sizeof(logfile) - 1, "%s.client", 
+                                opt_logfile);
+                       lp_set_logfile(logfile);
+                       interactive = False;
+                       break;
+               }
+       }
+
+       if (!lp_load(config_file,True,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", config_file);
+               exit(1);
+       }
+
+       poptFreeContext(pc);
+
+       /* the following functions are part of the Samba debugging
+          facilities.  See lib/debug.c */
+       setup_logging("samtest", interactive);
+       if (!interactive) 
+               reopen_logs();
+       
+       /* Load command lists */
+
+       cmd_set = samtest_command_list;
+
+       while(*cmd_set) {
+               add_command_set(*cmd_set);
+               add_command_set(separator_command);
+               cmd_set++;
+       }
+
+       /* Do anything specified with -c */
+       if (cmdstr[0]) {
+               char    *cmd;
+               char    *p = cmdstr;
+               while((cmd=next_command(&p)) != NULL) {
+                       process_cmd(&st, cmd);
+               }
+               
+               return 0;
+       }
+
+       /* Loop around accepting commands */
+
+       while(1) {
+               pstring prompt;
+               char *line;
+
+               slprintf(prompt, sizeof(prompt) - 1, "samtest $> ");
+
+               line = smb_readline(prompt, NULL, NULL);
+
+               if (line == NULL)
+                       break;
+
+               if (line[0] != '\n')
+                       process_cmd(&st, line);
+       }
+       
+       return 0;
+}
diff --git a/source4/torture/samtest.h b/source4/torture/samtest.h
new file mode 100644 (file)
index 0000000..a136ab1
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SAM module tester
+
+   Copyright (C) Jelmer Vernooij 2002
+
+   Most of this code was ripped off of rpcclient.
+   Copyright (C) Tim Potter 2000-2001
+
+   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.
+*/
+
+struct samtest_state {
+       SAM_CONTEXT *context;
+       NT_USER_TOKEN *token;
+};
+
+struct cmd_set {
+       char *name;
+       NTSTATUS (*fn)(struct samtest_state *sam, TALLOC_CTX *mem_ctx, int argc, 
+                       char **argv);
+       char *description;
+       char *usage;
+};
+
+
diff --git a/source4/torture/scanner.c b/source4/torture/scanner.c
new file mode 100644 (file)
index 0000000..0a92db9
--- /dev/null
@@ -0,0 +1,514 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester - scanning functions
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+#define VERBOSE 0
+#define OP_MIN 0
+#define OP_MAX 100
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+       if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+               return;
+       }
+#if VERBOSE
+       printf("possible %s hit op=%3d level=%5d status=%s\n",
+              format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existance of a trans2 call
+****************************************************************************/
+static NTSTATUS try_trans2(struct cli_state *cli, 
+                          int op,
+                          char *param, char *data,
+                          int param_len, int data_len,
+                          int *rparam_len, int *rdata_len)
+{
+       NTSTATUS status;
+       struct smb_trans2 t2;
+       uint16 setup = op;
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_init("try_trans2");
+
+       t2.in.max_param = 1024;
+       t2.in.max_data = 0x8000;
+       t2.in.max_setup = 10;
+       t2.in.flags = 0;
+       t2.in.timeout = 0;
+       t2.in.setup_count = 1;
+       t2.in.setup = &setup;
+       t2.in.params.data = param;
+       t2.in.params.length = param_len;
+       t2.in.data.data = data;
+       t2.in.data.length = data_len;
+
+       status = smb_raw_trans2(cli->tree, mem_ctx, &t2);
+
+       *rparam_len = t2.out.params.length;
+       *rdata_len = t2.out.data.length;
+
+       talloc_destroy(mem_ctx);
+       
+       return status;
+}
+
+
+static NTSTATUS try_trans2_len(struct cli_state *cli, 
+                            const char *format,
+                            int op, int level,
+                            char *param, char *data,
+                            int param_len, int *data_len,
+                            int *rparam_len, int *rdata_len)
+{
+       NTSTATUS ret=NT_STATUS_OK;
+
+       ret = try_trans2(cli, op, param, data, param_len,
+                        sizeof(pstring), rparam_len, rdata_len);
+#if VERBOSE 
+       printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+       if (!NT_STATUS_IS_OK(ret)) return ret;
+
+       *data_len = 0;
+       while (*data_len < sizeof(pstring)) {
+               ret = try_trans2(cli, op, param, data, param_len,
+                                *data_len, rparam_len, rdata_len);
+               if (NT_STATUS_IS_OK(ret)) break;
+               *data_len += 2;
+       }
+       if (NT_STATUS_IS_OK(ret)) {
+               printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+                      format, level, *data_len, *rparam_len, *rdata_len);
+       } else {
+               trans2_check_hit(format, op, level, ret);
+       }
+       return ret;
+}
+
+
+/****************************************************************************
+check whether a trans2 opnum exists at all
+****************************************************************************/
+static BOOL trans2_op_exists(struct cli_state *cli, int op)
+{
+       int data_len = 0;
+       int param_len = 0;
+       int rparam_len, rdata_len;
+       pstring param, data;
+       NTSTATUS status1, status2;
+
+       memset(data, 0, sizeof(data));
+       data_len = 4;
+
+       /* try with a info level only */
+       param_len = sizeof(param);
+       data_len = sizeof(data);
+
+       memset(param, 0xFF, sizeof(param));
+       memset(data, 0xFF, sizeof(data));
+       
+       status1 = try_trans2(cli, 0xFFFF, param, data, param_len, data_len, 
+                            &rparam_len, &rdata_len);
+
+       status2 = try_trans2(cli, op, param, data, param_len, data_len, 
+                            &rparam_len, &rdata_len);
+
+       if (NT_STATUS_EQUAL(status1, status2)) return False;
+
+       printf("Found op %d (status=%s)\n", op, nt_errstr(status2));
+
+       return True;
+}
+
+/****************************************************************************
+check for existance of a trans2 call
+****************************************************************************/
+static BOOL scan_trans2(struct cli_state *cli, int op, int level, 
+                       int fnum, int dnum, int qfnum, const char *fname)
+{
+       int data_len = 0;
+       int param_len = 0;
+       int rparam_len, rdata_len;
+       pstring param, data;
+       NTSTATUS status;
+
+       memset(data, 0, sizeof(data));
+       data_len = 4;
+
+       /* try with a info level only */
+       param_len = 2;
+       SSVAL(param, 0, level);
+       status = try_trans2_len(cli, "void", op, level, param, data, param_len, &data_len, 
+                           &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a file descriptor */
+       param_len = 6;
+       SSVAL(param, 0, fnum);
+       SSVAL(param, 2, level);
+       SSVAL(param, 4, 0);
+       status = try_trans2_len(cli, "fnum", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a quota file descriptor */
+       param_len = 6;
+       SSVAL(param, 0, qfnum);
+       SSVAL(param, 2, level);
+       SSVAL(param, 4, 0);
+       status = try_trans2_len(cli, "qfnum", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a notify style */
+       param_len = 6;
+       SSVAL(param, 0, dnum);
+       SSVAL(param, 2, dnum);
+       SSVAL(param, 4, level);
+       status = try_trans2_len(cli, "notify", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a file name */
+       param_len = 6;
+       SSVAL(param, 0, level);
+       SSVAL(param, 2, 0);
+       SSVAL(param, 4, 0);
+       param_len += push_string(NULL, &param[6], fname, sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE);
+
+       status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a new file name */
+       param_len = 6;
+       SSVAL(param, 0, level);
+       SSVAL(param, 2, 0);
+       SSVAL(param, 4, 0);
+       param_len += push_string(NULL, &param[6], "\\newfile.dat", sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE);
+
+       status = try_trans2_len(cli, "newfile", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       cli_unlink(cli, "\\newfile.dat");
+       cli_rmdir(cli, "\\newfile.dat");
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try dfs style  */
+       cli_mkdir(cli, "\\testdir");
+       param_len = 2;
+       SSVAL(param, 0, level);
+       param_len += push_string(NULL, &param[2], "\\testdir", sizeof(pstring)-3, STR_TERMINATE|STR_UNICODE);
+
+       status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       cli_rmdir(cli, "\\testdir");
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       return False;
+}
+
+
+BOOL torture_trans2_scan(int dummy)
+{
+       static struct cli_state *cli;
+       int op, level;
+       const char *fname = "\\scanner.dat";
+       int fnum, dnum, qfnum;
+
+       printf("starting trans2 scan test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       if (fnum == -1) {
+               printf("file open failed - %s\n", cli_errstr(cli));
+       }
+       dnum = cli_nt_create_full(cli, "\\", 
+                                 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL,
+                                 NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE, 
+                                 NTCREATEX_DISP_OPEN, 
+                                 NTCREATEX_OPTIONS_DIRECTORY, 0);
+       if (dnum == -1) {
+               printf("directory open failed - %s\n", cli_errstr(cli));
+       }
+       qfnum = cli_nt_create_full(cli, "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION", 
+                                  NTCREATEX_FLAGS_EXTENDED, 
+                                  SEC_RIGHTS_MAXIMUM_ALLOWED, 
+                                  0,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, 
+                                  NTCREATEX_DISP_OPEN, 
+                                  0, 0);
+       if (qfnum == -1) {
+               printf("quota open failed - %s\n", cli_errstr(cli));
+       }
+
+       for (op=OP_MIN; op<=OP_MAX; op++) {
+
+               if (!trans2_op_exists(cli, op)) {
+                       continue;
+               }
+
+               for (level = 0; level <= 50; level++) {
+                       scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
+               }
+
+               for (level = 0x100; level <= 0x130; level++) {
+                       scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
+               }
+
+               for (level = 1000; level < 1050; level++) {
+                       scan_trans2(cli, op, level, fnum, dnum, qfnum, fname);
+               }
+       }
+
+       torture_close_connection(cli);
+
+       printf("trans2 scan finished\n");
+       return True;
+}
+
+
+
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+       if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+           NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_INFO_CLASS)) {
+               return;
+       }
+#if VERBOSE
+               printf("possible %s hit op=%3d level=%5d status=%s\n",
+                      format, op, level, nt_errstr(status));
+#endif
+}
+
+/****************************************************************************
+check for existence of a nttrans call
+****************************************************************************/
+static NTSTATUS try_nttrans(struct cli_state *cli, 
+                           int op,
+                           char *param, char *data,
+                           int param_len, int data_len,
+                           int *rparam_len, int *rdata_len)
+{
+       struct smb_nttrans parms;
+       DATA_BLOB ntparam_blob, ntdata_blob;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("try_nttrans");
+
+       ntparam_blob.length = param_len;
+       ntparam_blob.data = param;
+       ntdata_blob.length = data_len;
+       ntdata_blob.data = data;
+
+       parms.in.max_param = 1024;
+       parms.in.max_data = 1024;
+       parms.in.max_setup = 0;
+       parms.in.setup_count = 0;
+       parms.in.function = op;
+       parms.in.params = ntparam_blob;
+       parms.in.data = ntdata_blob;
+       
+       status = smb_raw_nttrans(cli->tree, mem_ctx, &parms);
+       
+       if (NT_STATUS_IS_ERR(status)) {
+               DEBUG(1,("Failed to send NT_TRANS\n"));
+               talloc_destroy(mem_ctx);
+               return status;
+       }
+       *rparam_len = parms.out.params.length;
+       *rdata_len = parms.out.data.length;
+
+       talloc_destroy(mem_ctx);
+
+       return status;
+}
+
+
+static NTSTATUS try_nttrans_len(struct cli_state *cli, 
+                            const char *format,
+                            int op, int level,
+                            char *param, char *data,
+                            int param_len, int *data_len,
+                            int *rparam_len, int *rdata_len)
+{
+       NTSTATUS ret=NT_STATUS_OK;
+
+       ret = try_nttrans(cli, op, param, data, param_len,
+                        sizeof(pstring), rparam_len, rdata_len);
+#if VERBOSE 
+       printf("op=%d level=%d ret=%s\n", op, level, nt_errstr(ret));
+#endif
+       if (!NT_STATUS_IS_OK(ret)) return ret;
+
+       *data_len = 0;
+       while (*data_len < sizeof(pstring)) {
+               ret = try_nttrans(cli, op, param, data, param_len,
+                                *data_len, rparam_len, rdata_len);
+               if (NT_STATUS_IS_OK(ret)) break;
+               *data_len += 2;
+       }
+       if (NT_STATUS_IS_OK(ret)) {
+               printf("found %s level=%d data_len=%d rparam_len=%d rdata_len=%d\n",
+                      format, level, *data_len, *rparam_len, *rdata_len);
+       } else {
+               nttrans_check_hit(format, op, level, ret);
+       }
+       return ret;
+}
+
+/****************************************************************************
+check for existance of a nttrans call
+****************************************************************************/
+static BOOL scan_nttrans(struct cli_state *cli, int op, int level, 
+                       int fnum, int dnum, const char *fname)
+{
+       int data_len = 0;
+       int param_len = 0;
+       int rparam_len, rdata_len;
+       pstring param, data;
+       NTSTATUS status;
+
+       memset(data, 0, sizeof(data));
+       data_len = 4;
+
+       /* try with a info level only */
+       param_len = 2;
+       SSVAL(param, 0, level);
+       status = try_nttrans_len(cli, "void", op, level, param, data, param_len, &data_len, 
+                           &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a file descriptor */
+       param_len = 6;
+       SSVAL(param, 0, fnum);
+       SSVAL(param, 2, level);
+       SSVAL(param, 4, 0);
+       status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+
+       /* try with a notify style */
+       param_len = 6;
+       SSVAL(param, 0, dnum);
+       SSVAL(param, 2, dnum);
+       SSVAL(param, 4, level);
+       status = try_nttrans_len(cli, "notify", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a file name */
+       param_len = 6;
+       SSVAL(param, 0, level);
+       SSVAL(param, 2, 0);
+       SSVAL(param, 4, 0);
+       param_len += push_string(NULL, &param[6], fname, -1, STR_TERMINATE | STR_UNICODE);
+
+       status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try with a new file name */
+       param_len = 6;
+       SSVAL(param, 0, level);
+       SSVAL(param, 2, 0);
+       SSVAL(param, 4, 0);
+       param_len += push_string(NULL, &param[6], "\\newfile.dat", -1, STR_TERMINATE | STR_UNICODE);
+
+       status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       cli_unlink(cli, "\\newfile.dat");
+       cli_rmdir(cli, "\\newfile.dat");
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       /* try dfs style  */
+       cli_mkdir(cli, "\\testdir");
+       param_len = 2;
+       SSVAL(param, 0, level);
+       param_len += push_string(NULL, &param[2], "\\testdir", -1, STR_TERMINATE | STR_UNICODE);
+
+       status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len, 
+                               &rparam_len, &rdata_len);
+       cli_rmdir(cli, "\\testdir");
+       if (NT_STATUS_IS_OK(status)) return True;
+
+       return False;
+}
+
+
+BOOL torture_nttrans_scan(int dummy)
+{
+       static struct cli_state *cli;
+       int op, level;
+       const char *fname = "\\scanner.dat";
+       int fnum, dnum;
+
+       printf("starting nttrans scan test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC, 
+                        DENY_NONE);
+       dnum = cli_open(cli, "\\", O_RDONLY, DENY_NONE);
+
+       for (op=OP_MIN; op<=OP_MAX; op++) {
+               printf("Scanning op=%d\n", op);
+               for (level = 0; level <= 50; level++) {
+                       scan_nttrans(cli, op, level, fnum, dnum, fname);
+               }
+
+               for (level = 0x100; level <= 0x130; level++) {
+                       scan_nttrans(cli, op, level, fnum, dnum, fname);
+               }
+
+               for (level = 1000; level < 1050; level++) {
+                       scan_nttrans(cli, op, level, fnum, dnum, fname);
+               }
+       }
+
+       torture_close_connection(cli);
+
+       printf("nttrans scan finished\n");
+       return True;
+}
diff --git a/source4/torture/search.c b/source4/torture/search.c
new file mode 100644 (file)
index 0000000..cf9e28b
--- /dev/null
@@ -0,0 +1,325 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_SEARCH_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+/*
+  callback function for single_search
+*/
+static BOOL single_search_callback(void *private, union smb_search_data *file)
+{
+       union smb_search_data *data = private;
+
+       *data = *file;
+
+       return True;
+}
+
+/*
+  do a single file (non-wildcard) search 
+*/
+static NTSTATUS single_search(struct cli_state *cli, 
+                             TALLOC_CTX *mem_ctx,
+                             const char *pattern,
+                             enum search_level level,
+                             union smb_search_data *data)
+{
+       union smb_search_first io;
+       NTSTATUS status;
+
+       io.generic.level = level;
+       if (level == RAW_SEARCH_SEARCH) {
+               io.search_first.in.max_count = 1;
+               io.search_first.in.search_attrib = 0;
+               io.search_first.in.pattern = pattern;
+       } else {
+               io.t2ffirst.in.search_attrib = 0;
+               io.t2ffirst.in.max_count = 1;
+               io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
+               io.t2ffirst.in.storage_type = 0;
+               io.t2ffirst.in.pattern = pattern;
+       }
+
+       status = smb_raw_search_first(cli->tree, mem_ctx,
+                                     &io, (void *)data, single_search_callback);
+       
+       return status;
+}
+
+
+static struct {
+       const char *name;
+       enum search_level level;
+       NTSTATUS status;
+       union smb_search_data data;
+} levels[] = {
+       {"SEARCH",              RAW_SEARCH_SEARCH, },
+       {"STANDARD",            RAW_SEARCH_STANDARD, },
+       {"EA_SIZE",             RAW_SEARCH_EA_SIZE, },
+       {"DIRECTORY_INFO",      RAW_SEARCH_DIRECTORY_INFO, },
+       {"FULL_DIRECTORY_INFO", RAW_SEARCH_FULL_DIRECTORY_INFO, },
+       {"NAME_INFO",           RAW_SEARCH_NAME_INFO, },
+       {"BOTH_DIRECTORY_INFO", RAW_SEARCH_BOTH_DIRECTORY_INFO, },
+       {"LEVEL_261",           RAW_SEARCH_261, },
+       {"LEVEL_262",           RAW_SEARCH_262, }
+};
+
+/* find a level in the table by name */
+static union smb_search_data *find(const char *name)
+{
+       int i;
+       for (i=0;levels[i].name;i++) {
+               if (NT_STATUS_IS_OK(levels[i].status) && 
+                   strcmp(levels[i].name, name) == 0) {
+                       return &levels[i].data;
+               }
+       }
+       return NULL;
+}
+
+/* 
+   basic testing of all RAW_SEARCH_* calls 
+*/
+BOOL torture_search(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+       int fnum;
+       const char *fname = "\\torture_search.txt";
+       NTSTATUS status;
+       int i;
+       union smb_fileinfo all_info, alt_info, name_info;
+       union smb_search_data *s;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_search");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       if (fnum == -1) {
+               printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli));
+               ret = False;
+               goto done;
+       }
+
+       /* call all the levels */
+       for (i=0;levels[i].name;i++) {
+               levels[i].status = single_search(cli, mem_ctx, fname, 
+                                                levels[i].level, &levels[i].data);
+               if (!NT_STATUS_IS_OK(levels[i].status)) {
+                       printf("search level %s(%d) failed - %s\n",
+                              levels[i].name, (int)levels[i].level, 
+                              nt_errstr(levels[i].status));
+                       ret = False;
+               }
+       }
+
+       /* get the all_info file into to check against */
+       all_info.generic.level = RAW_FILEINFO_ALL_INFO;
+       all_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+       alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+       alt_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+       name_info.generic.level = RAW_FILEINFO_NAME_INFO;
+       name_info.generic.in.fname = fname;
+       status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status));
+               ret = False;
+               goto done;
+       }
+
+#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (s->sname1.field1 != v.sname2.out.field2) { \
+                       printf("(%d) %s/%s [%d] != %s/%s [%d]\n", \
+                              __LINE__, \
+                               #sname1, #field1, (int)s->sname1.field1, \
+                               #sname2, #field2, (int)v.sname2.out.field2); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (s->sname1.field1 != (~1 & nt_time_to_unix(&v.sname2.out.field2))) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, time_string(mem_ctx, s->sname1.field1), \
+                               #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (memcmp(&s->sname1.field1, &v.sname2.out.field2, sizeof(NTTIME))) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, nt_time_string(mem_ctx, &s->sname1.field1), \
+                               #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1, \
+                               #sname2, #field2, v.sname2.out.field2.s); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1.s || \
+                   strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
+                   wire_bad_flags(&s->sname1.field1, flags)) { \
+                       printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1.s, \
+                               #sname2, #field2, v.sname2.out.field2.s); \
+                       ret = False; \
+               } \
+       }} while (0)
+
+#define CHECK_NAME(name, sname1, field1, fname, flags) do { \
+       s = find(name); \
+       if (s) { \
+               if (!s->sname1.field1.s || \
+                   strcmp(s->sname1.field1.s, fname) || \
+                   wire_bad_flags(&s->sname1.field1, flags)) { \
+                       printf("(%d) %s/%s [%s] != %s\n", \
+                              __LINE__, \
+                               #sname1, #field1, s->sname1.field1.s, \
+                               fname); \
+                       ret = False; \
+               } \
+       }} while (0)
+       
+       /* check that all the results are as expected */
+       CHECK_VAL("SEARCH",              search,              attrib, all_info, all_info, attrib);
+       CHECK_VAL("STANDARD",            standard,            attrib, all_info, all_info, attrib);
+       CHECK_VAL("EA_SIZE",             ea_size,             attrib, all_info, all_info, attrib);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      attrib, all_info, all_info, attrib);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
+       CHECK_VAL("LEVEL_261",           level_261,           attrib, all_info, all_info, attrib);
+       CHECK_VAL("LEVEL_262",           level_262,           attrib, all_info, all_info, attrib);
+
+       CHECK_TIME("SEARCH",             search,              write_time, all_info, all_info, write_time);
+       CHECK_TIME("STANDARD",           standard,            write_time, all_info, all_info, write_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             write_time, all_info, all_info, write_time);
+       CHECK_TIME("STANDARD",           standard,            create_time, all_info, all_info, create_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             create_time, all_info, all_info, create_time);
+       CHECK_TIME("STANDARD",           standard,            access_time, all_info, all_info, access_time);
+       CHECK_TIME("EA_SIZE",            ea_size,             access_time, all_info, all_info, access_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           write_time, all_info, all_info, write_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           write_time, all_info, all_info, write_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           create_time, all_info, all_info, create_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           access_time, all_info, all_info, access_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           access_time, all_info, all_info, access_time);
+
+       CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_261",           level_261,           create_time, all_info, all_info, create_time);
+       CHECK_NTTIME("LEVEL_262",           level_262,           create_time, all_info, all_info, create_time);
+
+       CHECK_VAL("SEARCH",              search,              size, all_info, all_info, size);
+       CHECK_VAL("STANDARD",            standard,            size, all_info, all_info, size);
+       CHECK_VAL("EA_SIZE",             ea_size,             size, all_info, all_info, size);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      size, all_info, all_info, size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
+       CHECK_VAL("LEVEL_261",           level_261,           size, all_info, all_info, size);
+       CHECK_VAL("LEVEL_262",           level_262,           size, all_info, all_info, size);
+
+       CHECK_VAL("STANDARD",            standard,            alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("EA_SIZE",             ea_size,             alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("DIRECTORY_INFO",      directory_info,      alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("LEVEL_261",           level_261,           alloc_size, all_info, all_info, alloc_size);
+       CHECK_VAL("LEVEL_262",           level_262,           alloc_size, all_info, all_info, alloc_size);
+
+       CHECK_VAL("EA_SIZE",             ea_size,             ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("LEVEL_261",           level_261,           ea_size, all_info, all_info, ea_size);
+       CHECK_VAL("LEVEL_262",           level_262,           ea_size, all_info, all_info, ea_size);
+
+       CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname);
+       CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
+
+       CHECK_NAME("STANDARD",            standard,            name, fname+1, 0);
+       CHECK_NAME("EA_SIZE",             ea_size,             name, fname+1, 0);
+       CHECK_NAME("DIRECTORY_INFO",      directory_info,      name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("NAME_INFO",           name_info,           name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("LEVEL_261",           level_261,           name, fname+1, STR_TERMINATE_ASCII);
+       CHECK_NAME("LEVEL_262",           level_262,           name, fname+1, STR_TERMINATE_ASCII);
+
+done:
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
diff --git a/source4/torture/setfileinfo.c b/source4/torture/setfileinfo.c
new file mode 100644 (file)
index 0000000..b49ac18
--- /dev/null
@@ -0,0 +1,471 @@
+/* 
+   Unix SMB/CIFS implementation.
+   RAW_SFILEINFO_* individual test suite
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+/* basic testing of all RAW_SFILEINFO_* calls 
+   for each call we test that it succeeds, and where possible test 
+   for consistency between the calls. 
+*/
+BOOL torture_sfileinfo(int dummy)
+{
+       struct cli_state *cli;
+       BOOL ret = True;
+       TALLOC_CTX *mem_ctx;
+       int fnum = -1;
+       const char *fnum_fname = "\\torture_sfileinfo.txt";
+       const char *fnum_fname_new = "\\torture_sfileinfo-new.txt";
+       const char *path_fname = "\\torture_spathinfo13.txt";
+       const char *path_fname_new = "\\torture_spathinfo-new.txt";
+       union smb_fileinfo finfo1, finfo2;
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status, status2;
+       const char *call_name;
+       time_t basetime = (time(NULL) - 86400) & ~1;
+       BOOL check_fnum;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_sfileinfo");
+
+#define RECREATE_FILE(fname) do { \
+       if (fnum != -1) cli_close(cli, fnum); \
+       fnum = create_complex_file(cli, mem_ctx, fname); \
+       if (fnum == -1) { \
+               printf("(%d) ERROR: open of %s failed (%s)\n", \
+                      __LINE__, fname, cli_errstr(cli)); \
+               ret = False; \
+               goto done; \
+       }} while (0)
+
+#define RECREATE_BOTH do { \
+               RECREATE_FILE(path_fname); \
+               cli_close(cli, fnum); \
+               RECREATE_FILE(fnum_fname); \
+       } while (0)
+
+       RECREATE_BOTH;
+       
+#define CHECK_CALL_FNUM(call, rightstatus) do { \
+       check_fnum = True; \
+       call_name = #call; \
+       sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+       sfinfo.generic.file.fnum = fnum; \
+       status = smb_raw_setfileinfo(cli->tree, &sfinfo); \
+       if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+               printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \
+                       nt_errstr(status), nt_errstr(rightstatus)); \
+               ret = False; \
+       } \
+       finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+       finfo1.generic.in.fnum = fnum; \
+       status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1); \
+       if (!NT_STATUS_IS_OK(status2)) { \
+               printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status)); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK_CALL_PATH(call, rightstatus) do { \
+       check_fnum = False; \
+       call_name = #call; \
+       sfinfo.generic.level = RAW_SFILEINFO_ ## call; \
+       sfinfo.generic.file.fname = path_fname; \
+       status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+               sfinfo.generic.file.fname = path_fname_new; \
+               status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+       } \
+       if (!NT_STATUS_EQUAL(status, rightstatus)) { \
+               printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \
+                       nt_errstr(status), nt_errstr(rightstatus)); \
+               ret = False; \
+       } \
+       finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \
+       finfo1.generic.in.fname = path_fname; \
+       status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \
+       if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+               finfo1.generic.in.fname = path_fname_new; \
+               status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \
+       } \
+       if (!NT_STATUS_IS_OK(status2)) { \
+               printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status2)); \
+               ret = False; \
+       }} while (0)
+
+#define CHECK1(call) \
+       do { if (NT_STATUS_IS_OK(status)) { \
+               finfo2.generic.level = RAW_FILEINFO_ ## call; \
+               if (check_fnum) { \
+                       finfo2.generic.in.fnum = fnum; \
+                       status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2); \
+               } else { \
+                       finfo2.generic.in.fname = path_fname; \
+                       status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \
+                       if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \
+                               finfo2.generic.in.fname = path_fname_new; \
+                               status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \
+                       } \
+               } \
+               if (!NT_STATUS_IS_OK(status2)) { \
+                       printf("%s - %s\n", #call, nt_errstr(status2)); \
+               } \
+       }} while (0)
+
+#define CHECK_VALUE(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && finfo2.stype.out.field != value) { \
+               printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \
+                      call_name, #stype, #field, \
+                      (uint_t)value, (uint_t)finfo2.stype.out.field); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+#define CHECK_TIME(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && nt_time_to_unix(&finfo2.stype.out.field) != value) { \
+               printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \
+                       call_name, #stype, #field, \
+                       (uint_t)value, \
+                       (uint_t)nt_time_to_unix(&finfo2.stype.out.field)); \
+               printf("\t%s", http_timestring(value)); \
+               printf("\t%s\n", nt_time_string(mem_ctx, &finfo2.stype.out.field)); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+#define CHECK_STR(call, stype, field, value) do { \
+       CHECK1(call); \
+       if (NT_STATUS_IS_OK(status) && strcmp(finfo2.stype.out.field, value) != 0) { \
+               printf("(%d) %s - %s/%s should be '%s' - '%s'\n", __LINE__, \
+                       call_name, #stype, #field, \
+                       value, \
+                       finfo2.stype.out.field); \
+               dump_all_info(mem_ctx, &finfo1); \
+       }} while (0)
+
+       /* test setattr */
+       sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY;
+       sfinfo.setattr.in.write_time = basetime;
+       CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+       CHECK_VALUE  (ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+       CHECK_TIME   (ALL_INFO, all_info, write_time, basetime);
+
+       /* a zero write_time means don't change */
+       sfinfo.setattr.in.attrib = 0;
+       sfinfo.setattr.in.write_time = 0;
+       CHECK_CALL_PATH(SETATTR, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+       CHECK_TIME (ALL_INFO, all_info, write_time, basetime);
+
+       /* test setattre */
+       sfinfo.setattre.in.create_time = basetime + 20;
+       sfinfo.setattre.in.access_time = basetime + 30;
+       sfinfo.setattre.in.write_time  = basetime + 40;
+       CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 40);
+
+       sfinfo.setattre.in.create_time = 0;
+       sfinfo.setattre.in.access_time = 0;
+       sfinfo.setattre.in.write_time  = 0;
+       CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 40);
+
+       /* test standard level */
+       sfinfo.standard.in.create_time = basetime + 100;
+       sfinfo.standard.in.access_time = basetime + 200;
+       sfinfo.standard.in.write_time  = basetime + 300;
+       CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+
+       /* test basic_info level */
+       basetime += 86400;
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  basetime + 300);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
+       CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       /* a zero time means don't change */
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  0);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+       CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       /* test basic_information level */
+       basetime += 86400;
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  basetime + 300);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY;
+       CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_READONLY);
+
+       /* a zero time means don't change */
+       unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0);
+       unix_to_nt_time(&sfinfo.basic_info.in.write_time,  0);
+       unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0);
+       sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+       CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK);
+       CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100);
+       CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200);
+       CHECK_TIME(ALL_INFO, all_info, write_time,  basetime + 300);
+       /* interesting - w2k3 leaves change_time as current time for 0 change time
+          in setpathinfo
+         CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400);
+       */
+       CHECK_VALUE(ALL_INFO, all_info, attrib,     FILE_ATTRIBUTE_NORMAL);
+
+       /* test disposition_info level */
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       /* test disposition_information level */
+       sfinfo.disposition_info.in.delete_on_close = 1;
+       CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+
+       /* this would delete the file! */
+       /*
+         CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+         CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1);
+         CHECK_VALUE(ALL_INFO, all_info, nlink, 0);
+       */
+
+       sfinfo.disposition_info.in.delete_on_close = 0;
+       CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0);
+       CHECK_VALUE(ALL_INFO, all_info, nlink, 1);
+
+       /* test allocation_info level - this can truncate the file
+          to the rounded up size */
+       sfinfo.allocation_info.in.alloc_size = 0;
+       CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       sfinfo.allocation_info.in.alloc_size = 4096;
+       CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       RECREATE_BOTH;
+       sfinfo.allocation_info.in.alloc_size = 0;
+       CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+
+       sfinfo.allocation_info.in.alloc_size = 4096;
+       CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       /* setting the allocation size up via setpathinfo seems
+          to be broken in w2k3 */
+       CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0);
+       CHECK_VALUE(ALL_INFO, all_info, size, 0);
+
+       /* test end_of_file_info level */
+       sfinfo.end_of_file_info.in.size = 37;
+       CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       sfinfo.end_of_file_info.in.size = 7;
+       CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+       sfinfo.end_of_file_info.in.size = 37;
+       CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 37);
+
+       sfinfo.end_of_file_info.in.size = 7;
+       CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+       CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(ALL_INFO, all_info, size, 7);
+
+
+       /* test position_information level */
+       sfinfo.position_information.in.position = 123456;
+       CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456);
+
+       CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0);
+
+       /* test mode_information level */
+       sfinfo.mode_information.in.mode = 2;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2);
+
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       sfinfo.mode_information.in.mode = 1;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER);
+
+       sfinfo.mode_information.in.mode = 0;
+       CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK);
+       CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0);
+
+       /* finally the rename_information level */
+       cli_close(cli, create_complex_file(cli, mem_ctx, fnum_fname_new));
+       cli_close(cli, create_complex_file(cli, mem_ctx, path_fname_new));
+
+       sfinfo.rename_information.in.overwrite = 0;
+       sfinfo.rename_information.in.root_fid  = 0;
+       sfinfo.rename_information.in.new_name  = fnum_fname_new+1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       sfinfo.rename_information.in.new_name  = path_fname_new+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION);
+
+       sfinfo.rename_information.in.new_name  = fnum_fname_new+1;
+       sfinfo.rename_information.in.overwrite = 1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new);
+
+       sfinfo.rename_information.in.new_name  = path_fname_new+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new);
+
+       sfinfo.rename_information.in.new_name  = fnum_fname+1;
+       CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname);
+
+       sfinfo.rename_information.in.new_name  = path_fname+1;
+       CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK);
+       CHECK_STR(NAME_INFO, name_info, fname.s, path_fname);
+
+
+done:
+       cli_close(cli, fnum);
+       if (!cli_unlink(cli, fnum_fname)) {
+               printf("Failed to delete %s - %s\n", fnum_fname, cli_errstr(cli));
+       }
+       if (!cli_unlink(cli, path_fname)) {
+               printf("Failed to delete %s - %s\n", path_fname, cli_errstr(cli));
+       }
+
+       torture_close_connection(cli);
+       talloc_destroy(mem_ctx);
+       return ret;
+}
+
+
+/* 
+   look for the w2k3 setpathinfo STANDARD bug
+*/
+BOOL torture_sfileinfo_bug(int dummy)
+{
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+       const char *fname = "\\bug3.txt";
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status;
+       int fnum;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("torture_sfileinfo");
+
+       fnum = create_complex_file(cli, mem_ctx, fname);
+       cli_close(cli, fnum);
+
+       sfinfo.generic.level = RAW_SFILEINFO_STANDARD;
+       sfinfo.generic.file.fname = fname;
+
+       sfinfo.standard.in.create_time = 0;
+       sfinfo.standard.in.access_time = 0;
+       sfinfo.standard.in.write_time  = 0;
+
+       status = smb_raw_setpathinfo(cli->tree, &sfinfo);
+       printf("%s - %s\n", fname, nt_errstr(status));
+
+       printf("now try and delete %s\n", fname);
+
+       return True;
+}
diff --git a/source4/torture/t_strcmp.c b/source4/torture/t_strcmp.c
new file mode 100644 (file)
index 0000000..6227690
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ *
+ * Test harness for StrCaseCmp
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 3) {
+               fprintf(stderr, "usage: %s STRING1 STRING2\nCompares two strings\n",
+                       argv[0]);
+               return 2;
+       }
+       
+       printf("%d\n", StrCaseCmp(argv[1], argv[2]));
+       
+       return 0;
+}
diff --git a/source4/torture/torture.c b/source4/torture/torture.c
new file mode 100644 (file)
index 0000000..05a2979
--- /dev/null
@@ -0,0 +1,4183 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester
+   Copyright (C) Andrew Tridgell 1997-2003
+   
+   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"
+
+static int nprocs=4;
+int torture_numops=100;
+int torture_entries=1000;
+int torture_failures=1;
+static int procnum; /* records process count number when forking */
+static struct cli_state *current_cli;
+static char *randomfname;
+static BOOL use_oplocks;
+static BOOL use_level_II_oplocks;
+static const char *client_txt = "client_oplocks.txt";
+static BOOL use_kerberos;
+static BOOL bypass_io;
+
+BOOL torture_showall = False;
+
+static double create_procs(BOOL (*fn)(int), BOOL *result);
+
+#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0)
+
+static struct cli_state *open_nbt_connection(void)
+{
+       struct nmb_name called, calling;
+       struct in_addr ip;
+       struct cli_state *cli;
+       char *host = lp_parm_string(-1, "torture", "host");
+
+       make_nmb_name(&calling, lp_netbios_name(), 0x0);
+       make_nmb_name(&called , host, 0x20);
+
+       zero_ip(&ip);
+
+       cli = cli_state_init();
+       if (!cli) {
+               printf("Failed initialize cli_struct to connect with %s\n", host);
+               return NULL;
+       }
+
+       if (!cli_socket_connect(cli, host, &ip)) {
+               printf("Failed to connect with %s\n", host);
+               return cli;
+       }
+
+       cli->transport->socket->timeout = 120000; /* set a really long timeout (2 minutes) */
+
+       if (!cli_transport_establish(cli, &calling, &called)) {
+               /*
+                * Well, that failed, try *SMBSERVER ... 
+                * However, we must reconnect as well ...
+                */
+               if (!cli_socket_connect(cli, host, &ip)) {
+                       printf("Failed to connect with %s\n", host);
+                       return False;
+               }
+
+               make_nmb_name(&called, "*SMBSERVER", 0x20);
+               if (!cli_transport_establish(cli, &calling, &called)) {
+                       printf("%s rejected the session\n",host);
+                       printf("We tried with a called name of %s & %s\n",
+                               host, "*SMBSERVER");
+                       cli_shutdown(cli);
+                       return NULL;
+               }
+       }
+
+       return cli;
+}
+
+BOOL torture_open_connection(struct cli_state **c)
+{
+       BOOL retry;
+       int flags = 0;
+       NTSTATUS status;
+       char *host = lp_parm_string(-1, "torture", "host");
+       char *share = lp_parm_string(-1, "torture", "share");
+       char *username = lp_parm_string(-1, "torture", "username");
+       char *password = lp_parm_string(-1, "torture", "password");
+
+       if (use_kerberos)
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+       
+       status = cli_full_connection(c, lp_netbios_name(),
+                                    host, NULL, 
+                                    share, "?????", 
+                                    username, lp_workgroup(), 
+                                    password, flags, &retry);
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       (*c)->transport->options.use_oplocks = use_oplocks;
+       (*c)->transport->options.use_level2_oplocks = use_level_II_oplocks;
+       (*c)->transport->socket->timeout = 120000;
+
+       return True;
+}
+
+BOOL torture_close_connection(struct cli_state *c)
+{
+       BOOL ret = True;
+       DEBUG(9,("torture_close_connection: cli_state@%p\n", c));
+       if (!c) return True;
+       if (!cli_tdis(c)) {
+               printf("tdis failed (%s)\n", cli_errstr(c));
+               ret = False;
+       }
+       DEBUG(9,("torture_close_connection: call cli_shutdown\n"));
+       cli_shutdown(c);
+       DEBUG(9,("torture_close_connection: exit\n"));
+       return ret;
+}
+
+
+/* check if the server produced the expected error code */
+static BOOL check_error(int line, struct cli_state *c, 
+                       uint8 eclass, uint32 ecode, NTSTATUS nterr)
+{
+        if (cli_is_dos_error(c)) {
+                uint8 class;
+                uint32 num;
+
+                /* Check DOS error */
+
+                cli_dos_error(c, &class, &num);
+
+                if (eclass != class || ecode != num) {
+                        printf("unexpected error code class=%d code=%d\n", 
+                               (int)class, (int)num);
+                        printf(" expected %d/%d %s (line=%d)\n", 
+                               (int)eclass, (int)ecode, nt_errstr(nterr), line);
+                        return False;
+                }
+
+        } else {
+                NTSTATUS status;
+
+                /* Check NT error */
+
+                status = cli_nt_error(c);
+
+                if (NT_STATUS_V(nterr) != NT_STATUS_V(status)) {
+                        printf("unexpected error code %s\n", nt_errstr(status));
+                        printf(" expected %s (line=%d)\n", nt_errstr(nterr), line);
+                        return False;
+                }
+        }
+
+       return True;
+}
+
+
+static BOOL wait_lock(struct cli_state *c, int fnum, uint32 offset, uint32 len)
+{
+       while (!cli_lock(c, fnum, offset, len, -1, WRITE_LOCK)) {
+               if (!check_error(__LINE__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+       }
+       return True;
+}
+
+
+static BOOL rw_torture(struct cli_state *c)
+{
+       const char *lockfname = "\\torture.lck";
+       char *fname;
+       int fnum;
+       int fnum2;
+       pid_t pid2, pid = getpid();
+       int i, j;
+       char buf[1024];
+       BOOL correct = True;
+
+       fnum2 = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL, 
+                        DENY_NONE);
+       if (fnum2 == -1)
+               fnum2 = cli_open(c, lockfname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open of %s failed (%s)\n", lockfname, cli_errstr(c));
+               return False;
+       }
+
+
+       for (i=0;i<torture_numops;i++) {
+               unsigned n = (unsigned)sys_random()%10;
+               if (i % 10 == 0) {
+                       printf("%d\r", i); fflush(stdout);
+               }
+               asprintf(&fname, "\\torture.%u", n);
+
+               if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+                       return False;
+               }
+
+               fnum = cli_open(c, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
+               if (fnum == -1) {
+                       printf("open failed (%s)\n", cli_errstr(c));
+                       correct = False;
+                       break;
+               }
+
+               if (cli_write(c, fnum, 0, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
+                       printf("write failed (%s)\n", cli_errstr(c));
+                       correct = False;
+               }
+
+               for (j=0;j<50;j++) {
+                       if (cli_write(c, fnum, 0, (char *)buf, 
+                                     sizeof(pid)+(j*sizeof(buf)), 
+                                     sizeof(buf)) != sizeof(buf)) {
+                               printf("write failed (%s)\n", cli_errstr(c));
+                               correct = False;
+                       }
+               }
+
+               pid2 = 0;
+
+               if (cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
+                       printf("read failed (%s)\n", cli_errstr(c));
+                       correct = False;
+               }
+
+               if (pid2 != pid) {
+                       printf("data corruption!\n");
+                       correct = False;
+               }
+
+               if (!cli_close(c, fnum)) {
+                       printf("close failed (%s)\n", cli_errstr(c));
+                       correct = False;
+               }
+
+               if (!cli_unlink(c, fname)) {
+                       printf("unlink failed (%s)\n", cli_errstr(c));
+                       correct = False;
+               }
+
+               if (!cli_unlock(c, fnum2, n*sizeof(int), sizeof(int))) {
+                       printf("unlock failed (%s)\n", cli_errstr(c));
+                       correct = False;
+               }
+               free(fname);
+       }
+
+       cli_close(c, fnum2);
+       cli_unlink(c, lockfname);
+
+       printf("%d\n", i);
+
+       return correct;
+}
+
+static BOOL run_torture(int dummy)
+{
+       struct cli_state *cli;
+        BOOL ret;
+
+       cli = current_cli;
+
+       ret = rw_torture(cli);
+       
+       if (!torture_close_connection(cli)) {
+               ret = False;
+       }
+
+       return ret;
+}
+
+static BOOL rw_torture3(struct cli_state *c, char *lockfname)
+{
+       int fnum = -1;
+       unsigned int i = 0;
+       char buf[131072];
+       char buf_rd[131072];
+       unsigned count;
+       unsigned countprev = 0;
+       ssize_t sent = 0;
+       BOOL correct = True;
+
+       srandom(1);
+       for (i = 0; i < sizeof(buf); i += sizeof(uint32))
+       {
+               SIVAL(buf, i, sys_random());
+       }
+
+       if (procnum == 0)
+       {
+               fnum = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL, 
+                                DENY_NONE);
+               if (fnum == -1) {
+                       printf("first open read/write of %s failed (%s)\n",
+                                       lockfname, cli_errstr(c));
+                       return False;
+               }
+       }
+       else
+       {
+               for (i = 0; i < 500 && fnum == -1; i++)
+               {
+                       fnum = cli_open(c, lockfname, O_RDONLY, 
+                                        DENY_NONE);
+                       msleep(10);
+               }
+               if (fnum == -1) {
+                       printf("second open read-only of %s failed (%s)\n",
+                                       lockfname, cli_errstr(c));
+                       return False;
+               }
+       }
+
+       i = 0;
+       for (count = 0; count < sizeof(buf); count += sent)
+       {
+               if (count >= countprev) {
+                       printf("%d %8d\r", i, count);
+                       fflush(stdout);
+                       i++;
+                       countprev += (sizeof(buf) / 20);
+               }
+
+               if (procnum == 0)
+               {
+                       sent = ((unsigned)sys_random()%(20))+ 1;
+                       if (sent > sizeof(buf) - count)
+                       {
+                               sent = sizeof(buf) - count;
+                       }
+
+                       if (cli_write(c, fnum, 0, buf+count, count, (size_t)sent) != sent) {
+                               printf("write failed (%s)\n", cli_errstr(c));
+                               correct = False;
+                       }
+               }
+               else
+               {
+                       sent = cli_read(c, fnum, buf_rd+count, count,
+                                                 sizeof(buf)-count);
+                       if (sent < 0)
+                       {
+                               printf("read failed offset:%d size:%d (%s)\n",
+                                               count, sizeof(buf)-count,
+                                               cli_errstr(c));
+                               correct = False;
+                               sent = 0;
+                       }
+                       if (sent > 0)
+                       {
+                               if (memcmp(buf_rd+count, buf+count, sent) != 0)
+                               {
+                                       printf("read/write compare failed\n");
+                                       printf("offset: %d req %d recvd %d\n",
+                                               count, sizeof(buf)-count, sent);
+                                       correct = False;
+                                       break;
+                               }
+                       }
+               }
+
+       }
+
+       if (!cli_close(c, fnum)) {
+               printf("close failed (%s)\n", cli_errstr(c));
+               correct = False;
+       }
+
+       return correct;
+}
+
+static BOOL rw_torture2(struct cli_state *c1, struct cli_state *c2)
+{
+       const char *lockfname = "\\torture2.lck";
+       int fnum1;
+       int fnum2;
+       int i;
+       uchar buf[131072];
+       uchar buf_rd[131072];
+       BOOL correct = True;
+       ssize_t bytes_read, bytes_written;
+
+       if (cli_deltree(c1, lockfname) == -1) {
+               printf("unlink failed (%s)\n", cli_errstr(c1));
+       }
+
+       fnum1 = cli_open(c1, lockfname, O_RDWR | O_CREAT | O_EXCL, 
+                        DENY_NONE);
+       if (fnum1 == -1) {
+               printf("first open read/write of %s failed (%s)\n",
+                               lockfname, cli_errstr(c1));
+               return False;
+       }
+       fnum2 = cli_open(c2, lockfname, O_RDONLY, 
+                        DENY_NONE);
+       if (fnum2 == -1) {
+               printf("second open read-only of %s failed (%s)\n",
+                               lockfname, cli_errstr(c2));
+               cli_close(c1, fnum1);
+               return False;
+       }
+
+       printf("Checking data integrity over %d ops\n", torture_numops);
+
+       for (i=0;i<torture_numops;i++)
+       {
+               size_t buf_size = ((unsigned)sys_random()%(sizeof(buf)-1))+ 1;
+               if (i % 10 == 0) {
+                       printf("%d\r", i); fflush(stdout);
+               }
+
+               generate_random_buffer(buf, buf_size, False);
+
+               if ((bytes_written = cli_write(c1, fnum1, 0, buf, 0, buf_size)) != buf_size) {
+                       printf("write failed (%s)\n", cli_errstr(c1));
+                       printf("wrote %d, expected %d\n", bytes_written, buf_size); 
+                       correct = False;
+                       break;
+               }
+
+               if ((bytes_read = cli_read(c2, fnum2, buf_rd, 0, buf_size)) != buf_size) {
+                       printf("read failed (%s)\n", cli_errstr(c2));
+                       printf("read %d, expected %d\n", bytes_read, buf_size); 
+                       correct = False;
+                       break;
+               }
+
+               if (memcmp(buf_rd, buf, buf_size) != 0)
+               {
+                       printf("read/write compare failed\n");
+                       correct = False;
+                       break;
+               }
+       }
+
+       if (!cli_close(c2, fnum2)) {
+               printf("close failed (%s)\n", cli_errstr(c2));
+               correct = False;
+       }
+       if (!cli_close(c1, fnum1)) {
+               printf("close failed (%s)\n", cli_errstr(c1));
+               correct = False;
+       }
+
+       if (!cli_unlink(c1, lockfname)) {
+               printf("unlink failed (%s)\n", cli_errstr(c1));
+               correct = False;
+       }
+
+       return correct;
+}
+
+static BOOL run_readwritetest(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       BOOL test1, test2 = True;
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting readwritetest\n");
+
+       test1 = rw_torture2(cli1, cli2);
+       printf("Passed readwritetest v1: %s\n", BOOLSTR(test1));
+
+       if (test1) {
+               test2 = rw_torture2(cli1, cli1);
+               printf("Passed readwritetest v2: %s\n", BOOLSTR(test2));
+       }
+
+       if (!torture_close_connection(cli1)) {
+               test1 = False;
+       }
+
+       if (!torture_close_connection(cli2)) {
+               test2 = False;
+       }
+
+       return (test1 && test2);
+}
+
+static BOOL run_readwritemulti(int dummy)
+{
+       struct cli_state *cli;
+       BOOL test;
+
+       cli = current_cli;
+
+       printf("run_readwritemulti: fname %s\n", randomfname);
+       test = rw_torture3(cli, randomfname);
+
+       if (!torture_close_connection(cli)) {
+               test = False;
+       }
+       
+       return test;
+}
+
+
+int line_count = 0;
+int nbio_id;
+
+#define ival(s) strtol(s, NULL, 0)
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_netbench(int client)
+{
+       struct cli_state *cli;
+       int i;
+       pstring line;
+       char *cname;
+       FILE *f;
+       const char *params[20];
+       BOOL correct = True;
+
+       cli = current_cli;
+
+       nbio_id = client;
+
+       nb_setup(cli);
+
+       asprintf(&cname, "client%d", client);
+
+       f = fopen(client_txt, "r");
+
+       if (!f) {
+               perror(client_txt);
+               return False;
+       }
+
+       while (fgets(line, sizeof(line)-1, f)) {
+               line_count++;
+
+               line[strlen(line)-1] = 0;
+
+               /* printf("[%d] %s\n", line_count, line); */
+
+               all_string_sub(line,"client1", cname, sizeof(line));
+               
+               /* parse the command parameters */
+               params[0] = strtok(line," ");
+               i = 0;
+               while (params[i]) params[++i] = strtok(NULL," ");
+
+               params[i] = "";
+
+               if (i < 2) continue;
+
+               if (!strncmp(params[0],"SMB", 3)) {
+                       printf("ERROR: You are using a dbench 1 load file\n");
+                       exit(1);
+               }
+               DEBUG(9,("run_netbench(%d): %s %s\n", client, params[0], params[1]));
+
+               if (!strcmp(params[0],"NTCreateX")) {
+                       nb_createx(params[1], ival(params[2]), ival(params[3]), 
+                                  ival(params[4]));
+               } else if (!strcmp(params[0],"Close")) {
+                       nb_close(ival(params[1]));
+               } else if (!strcmp(params[0],"Rename")) {
+                       nb_rename(params[1], params[2]);
+               } else if (!strcmp(params[0],"Unlink")) {
+                       nb_unlink(params[1]);
+               } else if (!strcmp(params[0],"Deltree")) {
+                       nb_deltree(params[1]);
+               } else if (!strcmp(params[0],"Rmdir")) {
+                       nb_rmdir(params[1]);
+               } else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) {
+                       nb_qpathinfo(params[1]);
+               } else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) {
+                       nb_qfileinfo(ival(params[1]));
+               } else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) {
+                       nb_qfsinfo(ival(params[1]));
+               } else if (!strcmp(params[0],"FIND_FIRST")) {
+                       nb_findfirst(params[1]);
+               } else if (!strcmp(params[0],"WriteX")) {
+                       nb_writex(ival(params[1]), 
+                               ival(params[2]), ival(params[3]), ival(params[4]));
+               } else if (!strcmp(params[0],"ReadX")) {
+                       nb_readx(ival(params[1]), 
+                     ival(params[2]), ival(params[3]), ival(params[4]));
+               } else if (!strcmp(params[0],"Flush")) {
+                       nb_flush(ival(params[1]));
+               } else {
+                       printf("Unknown operation %s\n", params[0]);
+                       exit(1);
+               }
+       }
+       fclose(f);
+
+       nb_cleanup();
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+       
+       return correct;
+}
+
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_nbench(int dummy)
+{
+       double t;
+       BOOL correct = True;
+
+       nbio_shmem(nprocs);
+
+       nbio_id = -1;
+
+       signal(SIGALRM, SIGNAL_CAST nb_alarm);
+       alarm(1);
+       t = create_procs(run_netbench, &correct);
+       alarm(0);
+
+       printf("\nThroughput %g MB/sec\n", 
+              1.0e-6 * nbio_total() / t);
+       return correct;
+}
+
+
+/*
+  This test checks for two things:
+
+  1) correct support for retaining locks over a close (ie. the server
+     must not use posix semantics)
+  2) support for lock timeouts
+ */
+static BOOL run_locktest1(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       const char *fname = "\\lockt1.lck";
+       int fnum1, fnum2, fnum3;
+       time_t t1, t2;
+       unsigned lock_timeout;
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting locktest1\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       fnum2 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       fnum3 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+       if (fnum3 == -1) {
+               printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       if (!cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock1 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+
+       if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock2 succeeded! This is a locking bug\n");
+               return False;
+       } else {
+               if (!check_error(__LINE__, cli2, ERRDOS, ERRlock, 
+                                NT_STATUS_LOCK_NOT_GRANTED)) return False;
+       }
+
+
+       lock_timeout = (6 + (random() % 20));
+       printf("Testing lock timeout with timeout=%u\n", lock_timeout);
+       t1 = time(NULL);
+       if (cli_lock(cli2, fnum3, 0, 4, lock_timeout * 1000, WRITE_LOCK)) {
+               printf("lock3 succeeded! This is a locking bug\n");
+               return False;
+       } else {
+               if (!check_error(__LINE__, cli2, ERRDOS, ERRlock, 
+                                NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+       }
+       t2 = time(NULL);
+
+       if (t2 - t1 < 5) {
+               printf("error: This server appears not to support timed lock requests\n");
+       }
+       printf("server slept for %u seconds for a %u second timeout\n",
+              (unsigned int)(t2-t1), lock_timeout);
+
+       if (!cli_close(cli1, fnum2)) {
+               printf("close1 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock4 succeeded! This is a locking bug\n");
+               return False;
+       } else {
+               if (!check_error(__LINE__, cli2, ERRDOS, ERRlock, 
+                                NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_close(cli2, fnum3)) {
+               printf("close3 failed (%s)\n", cli_errstr(cli2));
+               return False;
+       }
+
+       if (!cli_unlink(cli1, fname)) {
+               printf("unlink failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+
+       if (!torture_close_connection(cli1)) {
+               return False;
+       }
+
+       if (!torture_close_connection(cli2)) {
+               return False;
+       }
+
+       printf("Passed locktest1\n");
+       return True;
+}
+
+/*
+  this checks to see if a secondary tconx can use open files from an
+  earlier tconx
+ */
+static BOOL run_tcon_test(int dummy)
+{
+       struct cli_state *cli;
+       const char *fname = "\\tcontest.tmp";
+       int fnum1;
+       uint16 cnum1, cnum2, cnum3;
+       uint16 vuid1, vuid2;
+       char buf[4];
+       BOOL ret = True;
+       struct cli_tree *tree1;
+       char *host = lp_parm_string(-1, "torture", "host");
+       char *share = lp_parm_string(-1, "torture", "share");
+       char *password = lp_parm_string(-1, "torture", "password");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("starting tcontest\n");
+
+       if (cli_deltree(cli, fname) == -1) {
+               printf("unlink of %s failed (%s)\n", fname, cli_errstr(cli));
+       }
+
+       fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       cnum1 = cli->tree->tid;
+       vuid1 = cli->session->vuid;
+
+       memset(&buf, 0, 4); /* init buf so valgrind won't complain */
+       if (cli_write(cli, fnum1, 0, buf, 130, 4) != 4) {
+               printf("initial write failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       tree1 = cli->tree;      /* save old tree connection */
+       if (!cli_send_tconX(cli, share, "?????",
+                           password)) {
+               printf("%s refused 2nd tree connect (%s)\n", host,
+                          cli_errstr(cli));
+               cli_shutdown(cli);
+               return False;
+       }
+
+       cnum2 = cli->tree->tid;
+       cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */
+       vuid2 = cli->session->vuid + 1;
+
+       /* try a write with the wrong tid */
+       cli->tree->tid = cnum2;
+
+       if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+               printf("* server allows write with wrong TID\n");
+               ret = False;
+       } else {
+               printf("server fails write with wrong TID : %s\n", cli_errstr(cli));
+       }
+
+
+       /* try a write with an invalid tid */
+       cli->tree->tid = cnum3;
+
+       if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+               printf("* server allows write with invalid TID\n");
+               ret = False;
+       } else {
+               printf("server fails write with invalid TID : %s\n", cli_errstr(cli));
+       }
+
+       /* try a write with an invalid vuid */
+       cli->session->vuid = vuid2;
+       cli->tree->tid = cnum1;
+
+       if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+               printf("* server allows write with invalid VUID\n");
+               ret = False;
+       } else {
+               printf("server fails write with invalid VUID : %s\n", cli_errstr(cli));
+       }
+
+       cli->session->vuid = vuid1;
+       cli->tree->tid = cnum1;
+
+       if (!cli_close(cli, fnum1)) {
+               printf("close failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       cli->tree->tid = cnum2;
+
+       if (!cli_tdis(cli)) {
+               printf("secondary tdis failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       cli->tree = tree1;  /* restore initial tree */
+       cli->tree->tid = cnum1;
+
+       if (!torture_close_connection(cli)) {
+               return False;
+       }
+
+       return ret;
+}
+
+
+
+static BOOL tcon_devtest(struct cli_state *cli,
+                        const char *myshare, const char *devtype,
+                        NTSTATUS expected_error)
+{
+       BOOL status;
+       BOOL ret;
+       char *password = lp_parm_string(-1, "torture", "password");
+
+       status = cli_send_tconX(cli, myshare, devtype,
+                               password);
+
+       printf("Trying share %s with devtype %s\n", myshare, devtype);
+
+       if (NT_STATUS_IS_OK(expected_error)) {
+               if (status) {
+                       ret = True;
+               } else {
+                       printf("tconX to share %s with type %s "
+                              "should have succeeded but failed\n",
+                              myshare, devtype);
+                       ret = False;
+               }
+               cli_tdis(cli);
+       } else {
+               if (status) {
+                       printf("tconx to share %s with type %s "
+                              "should have failed but succeeded\n",
+                              myshare, devtype);
+                       ret = False;
+               } else {
+                       if (NT_STATUS_EQUAL(cli_nt_error(cli),
+                                           expected_error)) {
+                               ret = True;
+                       } else {
+                               printf("Returned unexpected error\n");
+                               ret = False;
+                       }
+               }
+       }
+       return ret;
+}
+
+/*
+ checks for correct tconX support
+ */
+static BOOL run_tcon_devtype_test(int dummy)
+{
+       struct cli_state *cli1 = NULL;
+       BOOL retry;
+       int flags = 0;
+       NTSTATUS status;
+       BOOL ret = True;
+       char *host = lp_parm_string(-1, "torture", "host");
+       char *share = lp_parm_string(-1, "torture", "share");
+       char *username = lp_parm_string(-1, "torture", "username");
+       char *password = lp_parm_string(-1, "torture", "password");
+       
+       status = cli_full_connection(&cli1, lp_netbios_name(),
+                                    host, NULL, 
+                                    share, "?????",
+                                    username, lp_workgroup(),
+                                    password, flags, &retry);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("could not open connection\n");
+               return False;
+       }
+
+       if (!tcon_devtest(cli1, "IPC$", "A:", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+
+       if (!tcon_devtest(cli1, "IPC$", "?????", NT_STATUS_OK))
+               ret = False;
+
+       if (!tcon_devtest(cli1, "IPC$", "LPT:", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+
+       if (!tcon_devtest(cli1, "IPC$", "IPC", NT_STATUS_OK))
+               ret = False;
+                       
+       if (!tcon_devtest(cli1, "IPC$", "FOOBA", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+
+       if (!tcon_devtest(cli1, share, "A:", NT_STATUS_OK))
+               ret = False;
+
+       if (!tcon_devtest(cli1, share, "?????", NT_STATUS_OK))
+               ret = False;
+
+       if (!tcon_devtest(cli1, share, "LPT:", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+
+       if (!tcon_devtest(cli1, share, "IPC", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+                       
+       if (!tcon_devtest(cli1, share, "FOOBA", NT_STATUS_BAD_DEVICE_TYPE))
+               ret = False;
+
+       cli_shutdown(cli1);
+
+       if (ret)
+               printf("Passed tcondevtest\n");
+
+       return ret;
+}
+
+
+/*
+  This test checks that 
+
+  1) the server supports multiple locking contexts on the one SMB
+  connection, distinguished by PID.  
+
+  2) the server correctly fails overlapping locks made by the same PID (this
+     goes against POSIX behaviour, which is why it is tricky to implement)
+
+  3) the server denies unlock requests by an incorrect client PID
+*/
+static BOOL run_locktest2(int dummy)
+{
+       struct cli_state *cli;
+       const char *fname = "\\lockt2.lck";
+       int fnum1, fnum2, fnum3;
+       BOOL correct = True;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("starting locktest2\n");
+
+       cli_unlink(cli, fname);
+
+       printf("Testing pid context\n");
+       
+       cli->session->pid = 1;
+
+       fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       fnum2 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       cli->session->pid = 2;
+
+       fnum3 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+       if (fnum3 == -1) {
+               printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       cli->session->pid = 1;
+
+       if (!cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock1 failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+               printf("WRITE lock1 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, ERRDOS, ERRlock, 
+                                NT_STATUS_LOCK_NOT_GRANTED)) return False;
+       }
+
+       if (cli_lock(cli, fnum2, 0, 4, 0, WRITE_LOCK)) {
+               printf("WRITE lock2 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, ERRDOS, ERRlock, 
+                                NT_STATUS_LOCK_NOT_GRANTED)) return False;
+       }
+
+       if (cli_lock(cli, fnum2, 0, 4, 0, READ_LOCK)) {
+               printf("READ lock2 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, ERRDOS, ERRlock, 
+                                NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+       }
+
+       if (!cli_lock(cli, fnum1, 100, 4, 0, WRITE_LOCK)) {
+               printf("lock at 100 failed (%s)\n", cli_errstr(cli));
+       }
+
+       cli->session->pid = 2;
+
+       if (cli_unlock(cli, fnum1, 100, 4)) {
+               printf("unlock at 100 succeeded! This is a locking bug\n");
+               correct = False;
+       }
+
+       if (cli_unlock(cli, fnum1, 0, 4)) {
+               printf("unlock1 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, 
+                                ERRDOS, ERRlock, 
+                                NT_STATUS_RANGE_NOT_LOCKED)) return False;
+       }
+
+       if (cli_unlock(cli, fnum1, 0, 8)) {
+               printf("unlock2 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, 
+                                ERRDOS, ERRlock, 
+                                NT_STATUS_RANGE_NOT_LOCKED)) return False;
+       }
+
+       if (cli_lock(cli, fnum3, 0, 4, 0, WRITE_LOCK)) {
+               printf("lock3 succeeded! This is a locking bug\n");
+               correct = False;
+       } else {
+               if (!check_error(__LINE__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+       }
+
+       cli->session->pid = 1;
+
+       if (!cli_close(cli, fnum1)) {
+               printf("close1 failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!cli_close(cli, fnum2)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!cli_close(cli, fnum3)) {
+               printf("close3 failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("locktest2 finished\n");
+
+       return correct;
+}
+
+
+/*
+  This test checks that 
+
+  1) the server supports the full offset range in lock requests
+*/
+static BOOL run_locktest3(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       const char *fname = "\\lockt3.lck";
+       int fnum1, fnum2, i;
+       uint32 offset;
+       BOOL correct = True;
+
+#define NEXT_OFFSET offset += (~(uint32)0) / torture_numops
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting locktest3\n");
+
+       printf("Testing 32 bit offset ranges\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       printf("Establishing %d locks\n", torture_numops);
+
+       for (offset=i=0;i<torture_numops;i++) {
+               NEXT_OFFSET;
+               if (!cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+                       printf("lock1 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(cli1));
+                       return False;
+               }
+
+               if (!cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+                       printf("lock2 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(cli1));
+                       return False;
+               }
+       }
+
+       printf("Testing %d locks\n", torture_numops);
+
+       for (offset=i=0;i<torture_numops;i++) {
+               NEXT_OFFSET;
+
+               if (cli_lock(cli1, fnum1, offset-2, 1, 0, WRITE_LOCK)) {
+                       printf("error: lock1 %d succeeded!\n", i);
+                       return False;
+               }
+
+               if (cli_lock(cli2, fnum2, offset-1, 1, 0, WRITE_LOCK)) {
+                       printf("error: lock2 %d succeeded!\n", i);
+                       return False;
+               }
+
+               if (cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+                       printf("error: lock3 %d succeeded!\n", i);
+                       return False;
+               }
+
+               if (cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+                       printf("error: lock4 %d succeeded!\n", i);
+                       return False;
+               }
+       }
+
+       printf("Removing %d locks\n", torture_numops);
+
+       for (offset=i=0;i<torture_numops;i++) {
+               NEXT_OFFSET;
+
+               if (!cli_unlock(cli1, fnum1, offset-1, 1)) {
+                       printf("unlock1 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(cli1));
+                       return False;
+               }
+
+               if (!cli_unlock(cli2, fnum2, offset-2, 1)) {
+                       printf("unlock2 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(cli1));
+                       return False;
+               }
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close1 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_close(cli2, fnum2)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli2));
+               return False;
+       }
+
+       if (!cli_unlink(cli1, fname)) {
+               printf("unlink failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       
+       if (!torture_close_connection(cli2)) {
+               correct = False;
+       }
+
+       printf("finished locktest3\n");
+
+       return correct;
+}
+
+#define EXPECTED(ret, v) if ((ret) != (v)) { \
+        printf("** "); correct = False; \
+        }
+
+/*
+  looks at overlapping locks
+*/
+static BOOL run_locktest4(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       const char *fname = "\\lockt4.lck";
+       int fnum1, fnum2, f;
+       BOOL ret;
+       char buf[1000];
+       BOOL correct = True;
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting locktest4\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+               printf("Failed to create file\n");
+               correct = False;
+               goto fail;
+       }
+
+       ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 2, 4, 0, WRITE_LOCK);
+       EXPECTED(ret, False);
+       printf("the same process %s set overlapping write locks\n", ret?"can":"cannot");
+           
+       ret = cli_lock(cli1, fnum1, 10, 4, 0, READ_LOCK) &&
+             cli_lock(cli1, fnum1, 12, 4, 0, READ_LOCK);
+       EXPECTED(ret, True);
+       printf("the same process %s set overlapping read locks\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 20, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli2, fnum2, 22, 4, 0, WRITE_LOCK);
+       EXPECTED(ret, False);
+       printf("a different connection %s set overlapping write locks\n", ret?"can":"cannot");
+           
+       ret = cli_lock(cli1, fnum1, 30, 4, 0, READ_LOCK) &&
+             cli_lock(cli2, fnum2, 32, 4, 0, READ_LOCK);
+       EXPECTED(ret, True);
+       printf("a different connection %s set overlapping read locks\n", ret?"can":"cannot");
+       
+       ret = (cli1->session->pid = 1, cli_lock(cli1, fnum1, 40, 4, 0, WRITE_LOCK)) &&
+             (cli1->session->pid = 2, cli_lock(cli1, fnum1, 42, 4, 0, WRITE_LOCK));
+       EXPECTED(ret, False);
+       printf("a different pid %s set overlapping write locks\n", ret?"can":"cannot");
+           
+       ret = (cli1->session->pid = 1, cli_lock(cli1, fnum1, 50, 4, 0, READ_LOCK)) &&
+             (cli1->session->pid = 2, cli_lock(cli1, fnum1, 52, 4, 0, READ_LOCK));
+       EXPECTED(ret, True);
+       printf("a different pid %s set overlapping read locks\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK) &&
+             cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK);
+       EXPECTED(ret, True);
+       printf("the same process %s set the same read lock twice\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK);
+       EXPECTED(ret, False);
+       printf("the same process %s set the same write lock twice\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 80, 4, 0, READ_LOCK) &&
+             cli_lock(cli1, fnum1, 80, 4, 0, WRITE_LOCK);
+       EXPECTED(ret, False);
+       printf("the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 90, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 90, 4, 0, READ_LOCK);
+       EXPECTED(ret, True);
+       printf("the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+       ret = (cli1->session->pid = 1, cli_lock(cli1, fnum1, 100, 4, 0, WRITE_LOCK)) &&
+             (cli1->session->pid = 2, cli_lock(cli1, fnum1, 100, 4, 0, READ_LOCK));
+       EXPECTED(ret, False);
+       printf("a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 110, 4, 0, READ_LOCK) &&
+             cli_lock(cli1, fnum1, 112, 4, 0, READ_LOCK) &&
+             cli_unlock(cli1, fnum1, 110, 6);
+       EXPECTED(ret, False);
+       printf("the same process %s coalesce read locks\n", ret?"can":"cannot");
+
+
+       ret = cli_lock(cli1, fnum1, 120, 4, 0, WRITE_LOCK) &&
+             (cli_read(cli2, fnum2, buf, 120, 4) == 4);
+       EXPECTED(ret, False);
+       printf("this server %s strict write locking\n", ret?"doesn't do":"does");
+
+       ret = cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK) &&
+             (cli_write(cli2, fnum2, 0, buf, 130, 4) == 4);
+       EXPECTED(ret, False);
+       printf("this server %s strict read locking\n", ret?"doesn't do":"does");
+
+
+       ret = cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+             cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+             cli_unlock(cli1, fnum1, 140, 4) &&
+             cli_unlock(cli1, fnum1, 140, 4);
+       EXPECTED(ret, True);
+       printf("this server %s do recursive read locking\n", ret?"does":"doesn't");
+
+
+       ret = cli_lock(cli1, fnum1, 150, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 150, 4, 0, READ_LOCK) &&
+             cli_unlock(cli1, fnum1, 150, 4) &&
+             (cli_read(cli2, fnum2, buf, 150, 4) == 4) &&
+             !(cli_write(cli2, fnum2, 0, buf, 150, 4) == 4) &&
+             cli_unlock(cli1, fnum1, 150, 4);
+       EXPECTED(ret, True);
+       printf("this server %s do recursive lock overlays\n", ret?"does":"doesn't");
+
+       ret = cli_lock(cli1, fnum1, 160, 4, 0, READ_LOCK) &&
+             cli_unlock(cli1, fnum1, 160, 4) &&
+             (cli_write(cli2, fnum2, 0, buf, 160, 4) == 4) &&          
+             (cli_read(cli2, fnum2, buf, 160, 4) == 4);                
+       EXPECTED(ret, True);
+       printf("the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 170, 4, 0, WRITE_LOCK) &&
+             cli_unlock(cli1, fnum1, 170, 4) &&
+             (cli_write(cli2, fnum2, 0, buf, 170, 4) == 4) &&          
+             (cli_read(cli2, fnum2, buf, 170, 4) == 4);                
+       EXPECTED(ret, True);
+       printf("the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli1, fnum1, 190, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 190, 4, 0, READ_LOCK) &&
+             cli_unlock(cli1, fnum1, 190, 4) &&
+             !(cli_write(cli2, fnum2, 0, buf, 190, 4) == 4) &&         
+             (cli_read(cli2, fnum2, buf, 190, 4) == 4);                
+       EXPECTED(ret, True);
+       printf("the same process %s remove the first lock first\n", ret?"does":"doesn't");
+
+       cli_close(cli1, fnum1);
+       cli_close(cli2, fnum2);
+       fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       f = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+             cli_lock(cli1, f, 0, 1, 0, READ_LOCK) &&
+             cli_close(cli1, fnum1) &&
+             ((fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE)) != -1) &&
+             cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+        cli_close(cli1, f);
+       cli_close(cli1, fnum1);
+       EXPECTED(ret, True);
+       printf("the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
+
+ fail:
+       cli_close(cli1, fnum1);
+       cli_close(cli2, fnum2);
+       cli_unlink(cli1, fname);
+       torture_close_connection(cli1);
+       torture_close_connection(cli2);
+
+       printf("finished locktest4\n");
+       return correct;
+}
+
+/*
+  looks at lock upgrade/downgrade.
+*/
+static BOOL run_locktest5(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       const char *fname = "\\lockt5.lck";
+       int fnum1, fnum2, fnum3;
+       BOOL ret;
+       char buf[1000];
+       BOOL correct = True;
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting locktest5\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+       fnum3 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+               printf("Failed to create file\n");
+               correct = False;
+               goto fail;
+       }
+
+       /* Check for NT bug... */
+       ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+                 cli_lock(cli1, fnum3, 0, 1, 0, READ_LOCK);
+       cli_close(cli1, fnum1);
+       fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       ret = cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+       EXPECTED(ret, True);
+       printf("this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
+       cli_close(cli1, fnum1);
+       fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       cli_unlock(cli1, fnum3, 0, 1);
+
+       ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+             cli_lock(cli1, fnum1, 1, 1, 0, READ_LOCK);
+       EXPECTED(ret, True);
+       printf("the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
+
+       ret = cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+       EXPECTED(ret, False);
+
+       printf("a different processs %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
+
+       /* Unlock the process 2 lock. */
+       cli_unlock(cli2, fnum2, 0, 4);
+
+       ret = cli_lock(cli1, fnum3, 0, 4, 0, READ_LOCK);
+       EXPECTED(ret, False);
+
+       printf("the same processs on a different fnum %s get a read lock\n", ret?"can":"cannot");
+
+       /* Unlock the process 1 fnum3 lock. */
+       cli_unlock(cli1, fnum3, 0, 4);
+
+       /* Stack 2 more locks here. */
+       ret = cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK) &&
+                 cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK);
+
+       EXPECTED(ret, True);
+       printf("the same process %s stack read locks\n", ret?"can":"cannot");
+
+       /* Unlock the first process lock, then check this was the WRITE lock that was
+               removed. */
+
+       ret = cli_unlock(cli1, fnum1, 0, 4) &&
+                       cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+
+       EXPECTED(ret, True);
+       printf("the first unlock removes the %s lock\n", ret?"WRITE":"READ");
+
+       /* Unlock the process 2 lock. */
+       cli_unlock(cli2, fnum2, 0, 4);
+
+       /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
+
+       ret = cli_unlock(cli1, fnum1, 1, 1) &&
+                 cli_unlock(cli1, fnum1, 0, 4) &&
+                 cli_unlock(cli1, fnum1, 0, 4);
+
+       EXPECTED(ret, True);
+       printf("the same process %s unlock the stack of 4 locks\n", ret?"can":"cannot"); 
+
+       /* Ensure the next unlock fails. */
+       ret = cli_unlock(cli1, fnum1, 0, 4);
+       EXPECTED(ret, False);
+       printf("the same process %s count the lock stack\n", !ret?"can":"cannot"); 
+
+       /* Ensure connection 2 can get a write lock. */
+       ret = cli_lock(cli2, fnum2, 0, 4, 0, WRITE_LOCK);
+       EXPECTED(ret, True);
+
+       printf("a different processs %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
+
+
+ fail:
+       cli_close(cli1, fnum1);
+       cli_close(cli2, fnum2);
+       cli_unlink(cli1, fname);
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       if (!torture_close_connection(cli2)) {
+               correct = False;
+       }
+
+       printf("finished locktest5\n");
+       
+       return correct;
+}
+
+/*
+  tries the unusual lockingX locktype bits
+*/
+static BOOL run_locktest6(int dummy)
+{
+       struct cli_state *cli;
+       const char *fname[1] = { "\\lock6.txt" };
+       int i;
+       int fnum;
+       NTSTATUS status;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("starting locktest6\n");
+
+       for (i=0;i<1;i++) {
+               printf("Testing %s\n", fname[i]);
+
+               cli_unlink(cli, fname[i]);
+
+               fnum = cli_open(cli, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+               status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
+               cli_close(cli, fnum);
+               printf("CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
+
+               fnum = cli_open(cli, fname[i], O_RDWR, DENY_NONE);
+               status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
+               cli_close(cli, fnum);
+               printf("CANCEL_LOCK gave %s\n", nt_errstr(status));
+
+               cli_unlink(cli, fname[i]);
+       }
+
+       torture_close_connection(cli);
+
+       printf("finished locktest6\n");
+       return True;
+}
+
+static BOOL run_locktest7(int dummy)
+{
+       struct cli_state *cli1;
+       const char *fname = "\\lockt7.lck";
+       int fnum1;
+       char buf[200];
+       BOOL correct = False;
+
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+
+       printf("starting locktest7\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+               printf("Failed to create file\n");
+               goto fail;
+       }
+
+       cli1->session->pid = 1;
+
+       if (!cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK)) {
+               printf("Unable to apply read lock on range 130:4, error was %s\n", cli_errstr(cli1));
+               goto fail;
+       } else {
+               printf("pid1 successfully locked range 130:4 for READ\n");
+       }
+
+       if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+               printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+               goto fail;
+       } else {
+               printf("pid1 successfully read the range 130:4\n");
+       }
+
+       if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+               printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+               if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+                       printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+                       goto fail;
+               }
+       } else {
+               printf("pid1 successfully wrote to the range 130:4 (should be denied)\n");
+               goto fail;
+       }
+
+       cli1->session->pid = 2;
+
+       if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+               printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+       } else {
+               printf("pid2 successfully read the range 130:4\n");
+       }
+
+       if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+               printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+               if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+                       printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+                       goto fail;
+               }
+       } else {
+               printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+               goto fail;
+       }
+
+       cli1->session->pid = 1;
+       cli_unlock(cli1, fnum1, 130, 4);
+
+       if (!cli_lock(cli1, fnum1, 130, 4, 0, WRITE_LOCK)) {
+               printf("Unable to apply write lock on range 130:4, error was %s\n", cli_errstr(cli1));
+               goto fail;
+       } else {
+               printf("pid1 successfully locked range 130:4 for WRITE\n");
+       }
+
+       if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+               printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+               goto fail;
+       } else {
+               printf("pid1 successfully read the range 130:4\n");
+       }
+
+       if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+               printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+               goto fail;
+       } else {
+               printf("pid1 successfully wrote to the range 130:4\n");
+       }
+
+       cli1->session->pid = 2;
+
+       if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+               printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+               if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+                       printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+                       goto fail;
+               }
+       } else {
+               printf("pid2 successfully read the range 130:4 (should be denied)\n");
+               goto fail;
+       }
+
+       if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+               printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+               if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+                       printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+                       goto fail;
+               }
+       } else {
+               printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+               goto fail;
+       }
+
+       cli_unlock(cli1, fnum1, 130, 0);
+       correct = True;
+
+fail:
+       cli_close(cli1, fnum1);
+       cli_unlink(cli1, fname);
+       torture_close_connection(cli1);
+
+       printf("finished locktest7\n");
+       return correct;
+}
+
+/*
+test whether fnums and tids open on one VC are available on another (a major
+security hole)
+*/
+static BOOL run_fdpasstest(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       const char *fname = "\\fdpass.tst";
+       int fnum1, oldtid;
+       pstring buf;
+
+       if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       printf("starting fdpasstest\n");
+
+       cli_unlink(cli1, fname);
+
+       printf("Opening a file on connection 1\n");
+
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       printf("writing to file on connection 1\n");
+
+       if (cli_write(cli1, fnum1, 0, "hello world\n", 0, 13) != 13) {
+               printf("write failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       oldtid = cli2->tree->tid;
+       cli2->session->vuid = cli1->session->vuid;
+       cli2->tree->tid = cli1->tree->tid;
+       cli2->session->pid = cli1->session->pid;
+
+       printf("reading from file on connection 2\n");
+
+       if (cli_read(cli2, fnum1, buf, 0, 13) == 13) {
+               printf("read succeeded! nasty security hole [%s]\n",
+                      buf);
+               return False;
+       }
+
+       cli_close(cli1, fnum1);
+       cli_unlink(cli1, fname);
+
+       cli2->tree->tid = oldtid;
+
+       torture_close_connection(cli1);
+       torture_close_connection(cli2);
+
+       printf("finished fdpasstest\n");
+       return True;
+}
+
+
+/*
+  This test checks that 
+
+  1) the server does not allow an unlink on a file that is open
+*/
+static BOOL run_unlinktest(int dummy)
+{
+       struct cli_state *cli;
+       const char *fname = "\\unlink.tst";
+       int fnum;
+       BOOL correct = True;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("starting unlink test\n");
+
+       cli_unlink(cli, fname);
+
+       cli->session->pid = 1;
+
+       printf("Opening a file\n");
+
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       printf("Unlinking a open file\n");
+
+       if (cli_unlink(cli, fname)) {
+               printf("error: server allowed unlink on an open file\n");
+               correct = False;
+       } else {
+               correct = check_error(__LINE__, cli, ERRDOS, ERRbadshare, 
+                                     NT_STATUS_SHARING_VIOLATION);
+       }
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("unlink test finished\n");
+       
+       return correct;
+}
+
+
+/*
+test how many open files this server supports on the one socket
+*/
+static BOOL run_maxfidtest(int dummy)
+{
+       struct cli_state *cli;
+       const char *template = "\\maxfid.%d.%d";
+       char *fname;
+       int fnums[0x11000], i;
+       int retries=4;
+       BOOL correct = True;
+
+       cli = current_cli;
+
+       if (retries <= 0) {
+               printf("failed to connect\n");
+               return False;
+       }
+
+       printf("Testing maximum number of open files\n");
+
+       for (i=0; i<0x11000; i++) {
+               asprintf(&fname, template, i,(int)getpid());
+               if ((fnums[i] = cli_open(cli, fname, 
+                                       O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
+                   -1) {
+                       printf("open of %s failed (%s)\n", 
+                              fname, cli_errstr(cli));
+                       printf("maximum fnum is %d\n", i);
+                       break;
+               }
+               free(fname);
+               printf("%6d\r", i);
+       }
+       printf("%6d\n", i);
+       i--;
+
+       printf("cleaning up\n");
+       for (;i>=0;i--) {
+               asprintf(&fname, template, i,(int)getpid());
+               if (!cli_close(cli, fnums[i])) {
+                       printf("Close of fnum %d failed - %s\n", fnums[i], cli_errstr(cli));
+               }
+               if (!cli_unlink(cli, fname)) {
+                       printf("unlink of %s failed (%s)\n", 
+                              fname, cli_errstr(cli));
+                       correct = False;
+               }
+               free(fname);
+               printf("%6d\r", i);
+       }
+       printf("%6d\n", 0);
+
+       printf("maxfid test finished\n");
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+       return correct;
+}
+
+/* send smb negprot commands, not reading the response */
+static BOOL run_negprot_nowait(int dummy)
+{
+       int i;
+       struct cli_state *cli;
+       BOOL correct = True;
+
+       printf("starting negprot nowait test\n");
+
+       cli = open_nbt_connection();
+       if (!cli) {
+               return False;
+       }
+
+       printf("Establishing protocol negotiations - connect with another client\n");
+
+       for (i=0;i<50000;i++) {
+               smb_negprot_send(cli->transport, PROTOCOL_NT1);
+       }
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("finished negprot nowait test\n");
+
+       return correct;
+}
+
+
+/*
+  This checks how the getatr calls works
+*/
+static BOOL run_attrtest(int dummy)
+{
+       struct cli_state *cli;
+       int fnum;
+       time_t t, t2;
+       const char *fname = "\\attrib123456789.tst";
+       BOOL correct = True;
+
+       printf("starting attrib test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       cli_unlink(cli, fname);
+       fnum = cli_open(cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_close(cli, fnum);
+
+       if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+               printf("getatr failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       }
+
+       printf("New file time is %s", ctime(&t));
+
+       if (abs(t - time(NULL)) > 60*60*24*10) {
+               printf("ERROR: SMBgetatr bug. time is %s",
+                      ctime(&t));
+               t = time(NULL);
+               correct = False;
+       }
+
+       t2 = t-60*60*24; /* 1 day ago */
+
+       printf("Setting file time to %s", ctime(&t2));
+
+       if (!cli_setatr(cli, fname, 0, t2)) {
+               printf("setatr failed (%s)\n", cli_errstr(cli));
+               correct = True;
+       }
+
+       if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+               printf("getatr failed (%s)\n", cli_errstr(cli));
+               correct = True;
+       }
+
+       printf("Retrieved file time as %s", ctime(&t));
+
+       if (t != t2) {
+               printf("ERROR: getatr/setatr bug. times are\n%s",
+                      ctime(&t));
+               printf("%s", ctime(&t2));
+               correct = True;
+       }
+
+       cli_unlink(cli, fname);
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("attrib test finished\n");
+
+       return correct;
+}
+
+
+/*
+  This checks a couple of trans2 calls
+*/
+static BOOL run_trans2test(int dummy)
+{
+       struct cli_state *cli;
+       int fnum;
+       size_t size;
+       time_t c_time, a_time, m_time, w_time, m_time2;
+       const char *fname = "\\trans2.tst";
+       const char *dname = "\\trans2";
+       const char *fname2 = "\\trans2\\trans2.tst";
+       const char *pname;
+       BOOL correct = True;
+
+       printf("starting trans2 test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       cli_unlink(cli, fname);
+
+       printf("Testing qfileinfo\n");
+       
+       fnum = cli_open(cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       if (!cli_qfileinfo(cli, fnum, NULL, &size, &c_time, &a_time, &m_time,
+                          NULL, NULL)) {
+               printf("ERROR: qfileinfo failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       }
+
+       printf("Testing NAME_INFO\n");
+
+       if (!cli_qfilename(cli, fnum, &pname)) {
+               printf("ERROR: qfilename failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       }
+
+       if (!pname || strcmp(pname, fname)) {
+               printf("qfilename gave different name? [%s] [%s]\n",
+                      fname, pname);
+               correct = False;
+       }
+
+       cli_close(cli, fnum);
+       cli_unlink(cli, fname);
+
+       fnum = cli_open(cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       if (fnum == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+       cli_close(cli, fnum);
+
+       printf("Checking for sticky create times\n");
+
+       if (!cli_qpathinfo(cli, fname, &c_time, &a_time, &m_time, &size, NULL)) {
+               printf("ERROR: qpathinfo failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       } else {
+               if (c_time != m_time) {
+                       printf("create time=%s", ctime(&c_time));
+                       printf("modify time=%s", ctime(&m_time));
+                       printf("This system appears to have sticky create times\n");
+               }
+               if (a_time % (60*60) == 0) {
+                       printf("access time=%s", ctime(&a_time));
+                       printf("This system appears to set a midnight access time\n");
+                       correct = False;
+               }
+
+               if (abs(m_time - time(NULL)) > 60*60*24*7) {
+                       printf("ERROR: totally incorrect times - maybe word reversed? mtime=%s", ctime(&m_time));
+                       correct = False;
+               }
+       }
+
+
+       cli_unlink(cli, fname);
+       fnum = cli_open(cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_close(cli, fnum);
+       if (!cli_qpathinfo2(cli, fname, &c_time, &a_time, &m_time, 
+                           &w_time, &size, NULL, NULL)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       } else {
+               if (w_time < 60*60*24*2) {
+                       printf("write time=%s", ctime(&w_time));
+                       printf("This system appears to set a initial 0 write time\n");
+                       correct = False;
+               }
+       }
+
+       cli_unlink(cli, fname);
+
+
+       /* check if the server updates the directory modification time
+           when creating a new file */
+       if (!cli_mkdir(cli, dname)) {
+               printf("ERROR: mkdir failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       }
+       sleep(3);
+       if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time, 
+                           &w_time, &size, NULL, NULL)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       }
+
+       fnum = cli_open(cli, fname2, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_write(cli, fnum,  0, (char *)&fnum, 0, sizeof(fnum));
+       cli_close(cli, fnum);
+       if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time2, 
+                           &w_time, &size, NULL, NULL)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+               correct = False;
+       } else {
+               if (m_time2 == m_time) {
+                       printf("This system does not update directory modification times\n");
+                       correct = False;
+               }
+       }
+       cli_unlink(cli, fname2);
+       cli_rmdir(cli, dname);
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("trans2 test finished\n");
+
+       return correct;
+}
+
+/*
+  Test delete on close semantics.
+ */
+static BOOL run_deletetest(int dummy)
+{
+       struct cli_state *cli1;
+       struct cli_state *cli2 = NULL;
+       const char *fname = "\\delete.file";
+       int fnum1 = -1;
+       int fnum2 = -1;
+       BOOL correct = True;
+       
+       printf("starting delete test\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       
+       /* Test 1 - this should delete the file on close. */
+       
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, 
+                                  NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0);
+       
+       if (fnum1 == -1) {
+               printf("[1] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("[1] close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+       if (fnum1 != -1) {
+               printf("[1] open of %s succeeded (should fail)\n", fname);
+               correct = False;
+               goto fail;
+       }
+       
+       printf("first delete on close test succeeded.\n");
+       
+       /* Test 2 - this should delete the file on close. */
+       
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, 
+                                  NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+       
+       if (fnum1 == -1) {
+               printf("[2] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[2] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("[2] close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+       if (fnum1 != -1) {
+               printf("[2] open of %s succeeded should have been deleted on close !\n", fname);
+               if (!cli_close(cli1, fnum1)) {
+                       printf("[2] close failed (%s)\n", cli_errstr(cli1));
+                       correct = False;
+                       goto fail;
+               }
+               cli_unlink(cli1, fname);
+       } else
+               printf("second delete on close test succeeded.\n");
+       
+       /* Test 3 - ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("[3] open - 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should fail with a sharing violation - open for delete is only compatible
+          with SHARE_DELETE. */
+
+       fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, 
+                                  NTCREATEX_DISP_OPEN, 0, 0);
+
+       if (fnum2 != -1) {
+               printf("[3] open  - 2 of %s succeeded - should have failed.\n", fname);
+               correct = False;
+               goto fail;
+       }
+
+       /* This should succeed. */
+
+       fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL,
+                       NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("[3] open  - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[3] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("[3] close 1 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum2)) {
+               printf("[3] close 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       /* This should fail - file should no longer be there. */
+
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+       if (fnum1 != -1) {
+               printf("[3] open of %s succeeded should have been deleted on close !\n", fname);
+               if (!cli_close(cli1, fnum1)) {
+                       printf("[3] close failed (%s)\n", cli_errstr(cli1));
+               }
+               cli_unlink(cli1, fname);
+               correct = False;
+               goto fail;
+       } else
+               printf("third delete on close test succeeded.\n");
+
+       /* Test 4 ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, 
+                                  SA_RIGHT_FILE_READ_DATA  | 
+                                  SA_RIGHT_FILE_WRITE_DATA |
+                                  STD_RIGHT_DELETE_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, 
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, 
+                                  NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+                                                               
+       if (fnum1 == -1) {
+               printf("[4] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should succeed. */
+       fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ,
+                                  FILE_ATTRIBUTE_NORMAL, 
+                                  NTCREATEX_SHARE_ACCESS_READ  | 
+                                  NTCREATEX_SHARE_ACCESS_WRITE |
+                                  NTCREATEX_SHARE_ACCESS_DELETE, 
+                                  NTCREATEX_DISP_OPEN, 0, 0);
+       if (fnum2 == -1) {
+               printf("[4] open  - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum2)) {
+               printf("[4] close - 1 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[4] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       /* This should fail - no more opens once delete on close set. */
+       fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE,
+                                  NTCREATEX_DISP_OPEN, 0, 0);
+       if (fnum2 != -1) {
+               printf("[4] open  - 3 of %s succeeded ! Should have failed.\n", fname );
+               correct = False;
+               goto fail;
+       } else
+               printf("fourth delete on close test succeeded.\n");
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("[4] close - 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       /* Test 5 ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should fail - only allowed on NT opens with DELETE access. */
+
+       if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[5] setting delete_on_close on OpenX file succeeded - should fail !\n");
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("[5] close - 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       printf("fifth delete on close test succeeded.\n");
+       
+       /* Test 6 ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, 
+                                  SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA,
+                                  FILE_ATTRIBUTE_NORMAL, 
+                                  NTCREATEX_SHARE_ACCESS_READ  |
+                                  NTCREATEX_SHARE_ACCESS_WRITE |
+                                  NTCREATEX_SHARE_ACCESS_DELETE,
+                                  NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+       
+       if (fnum1 == -1) {
+               printf("[6] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       /* This should fail - only allowed on NT opens with DELETE access. */
+       
+       if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[6] setting delete_on_close on file with no delete access succeeded - should fail !\n");
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("[6] close - 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       printf("sixth delete on close test succeeded.\n");
+       
+       /* Test 7 ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, 
+                                  SA_RIGHT_FILE_READ_DATA  | 
+                                  SA_RIGHT_FILE_WRITE_DATA |
+                                  STD_RIGHT_DELETE_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, 0, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+                                                               
+       if (fnum1 == -1) {
+               printf("[7] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[7] setting delete_on_close on file failed !\n");
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_nt_delete_on_close(cli1, fnum1, False)) {
+               printf("[7] unsetting delete_on_close on file failed !\n");
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+       
+       /* This next open should succeed - we reset the flag. */
+       
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       printf("seventh delete on close test succeeded.\n");
+       
+       /* Test 7 ... */
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       if (!torture_open_connection(&cli2)) {
+               printf("[8] failed to open second connection.\n");
+               correct = False;
+               goto fail;
+       }
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE,
+                                  NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+       
+       if (fnum1 == -1) {
+               printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE,
+                                  NTCREATEX_DISP_OPEN, 0, 0);
+       
+       if (fnum2 == -1) {
+               printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+               printf("[8] setting delete_on_close on file failed !\n");
+               correct = False;
+               goto fail;
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("[8] close - 1 failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       if (!cli_close(cli2, fnum2)) {
+               printf("[8] close - 2 failed (%s)\n", cli_errstr(cli2));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should fail.. */
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+       if (fnum1 != -1) {
+               printf("[8] open of %s succeeded should have been deleted on close !\n", fname);
+               goto fail;
+               correct = False;
+       } else
+               printf("eighth delete on close test succeeded.\n");
+
+       /* This should fail - we need to set DELETE_ACCESS. */
+       fnum1 = cli_nt_create_full(cli1, fname, 0,SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0);
+       
+       if (fnum1 != -1) {
+               printf("[9] open of %s succeeded should have failed!\n", fname);
+               correct = False;
+               goto fail;
+       }
+
+       printf("ninth delete on close test succeeded.\n");
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS,
+                                  FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0);
+       if (fnum1 == -1) {
+               printf("[10] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should delete the file. */
+       if (!cli_close(cli1, fnum1)) {
+               printf("[10] close failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+               goto fail;
+       }
+
+       /* This should fail.. */
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+       if (fnum1 != -1) {
+               printf("[10] open of %s succeeded should have been deleted on close !\n", fname);
+               goto fail;
+               correct = False;
+       } else
+               printf("tenth delete on close test succeeded.\n");
+       printf("finished delete test\n");
+
+  fail:
+       /* FIXME: This will crash if we aborted before cli2 got
+        * intialized, because these functions don't handle
+        * uninitialized connections. */
+               
+       cli_close(cli1, fnum1);
+       cli_close(cli1, fnum2);
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       if (!torture_close_connection(cli2)) {
+               correct = False;
+       }
+       return correct;
+}
+
+
+/*
+  print out server properties
+ */
+static BOOL run_properties(int dummy)
+{
+       struct cli_state *cli;
+       BOOL correct = True;
+       
+       printf("starting properties test\n");
+       
+       ZERO_STRUCT(cli);
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+       
+       d_printf("Capabilities 0x%08x\n", cli->transport->negotiate.capabilities);
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       return correct;
+}
+
+
+
+/* FIRST_DESIRED_ACCESS   0xf019f */
+#define FIRST_DESIRED_ACCESS   SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_APPEND_DATA|\
+                               SA_RIGHT_FILE_READ_EA|                           /* 0xf */ \
+                               SA_RIGHT_FILE_WRITE_EA|SA_RIGHT_FILE_READ_ATTRIBUTES|     /* 0x90 */ \
+                               SA_RIGHT_FILE_WRITE_ATTRIBUTES|                  /* 0x100 */ \
+                               STD_RIGHT_DELETE_ACCESS|STD_RIGHT_READ_CONTROL_ACCESS|\
+                               STD_RIGHT_WRITE_DAC_ACCESS|STD_RIGHT_WRITE_OWNER_ACCESS     /* 0xf0000 */
+/* SECOND_DESIRED_ACCESS  0xe0080 */
+#define SECOND_DESIRED_ACCESS  SA_RIGHT_FILE_READ_ATTRIBUTES|                   /* 0x80 */ \
+                               STD_RIGHT_READ_CONTROL_ACCESS|STD_RIGHT_WRITE_DAC_ACCESS|\
+                               STD_RIGHT_WRITE_OWNER_ACCESS                      /* 0xe0000 */
+
+#if 0
+#define THIRD_DESIRED_ACCESS   FILE_READ_ATTRIBUTES|                   /* 0x80 */ \
+                               READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+                               SA_RIGHT_FILE_READ_DATA|\
+                               WRITE_OWNER_ACCESS                      /* */
+#endif
+
+/*
+  Test ntcreate calls made by xcopy
+ */
+static BOOL run_xcopy(int dummy)
+{
+       struct cli_state *cli1;
+       const char *fname = "\\test.txt";
+       BOOL correct = True;
+       int fnum1, fnum2;
+
+       printf("starting xcopy test\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0,
+                                  FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 
+                                  0x4044, 0);
+
+       if (fnum1 == -1) {
+               printf("First open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli1, fname, 0,
+                                  SECOND_DESIRED_ACCESS, 0,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN, 
+                                  0x200000, 0);
+       if (fnum2 == -1) {
+               printf("second open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+       
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       
+       return correct;
+}
+
+/*
+  Test rename on files open with share delete and no share delete.
+ */
+static BOOL run_rename(int dummy)
+{
+       struct cli_state *cli1;
+       const char *fname = "\\test.txt";
+       const char *fname1 = "\\test1.txt";
+       BOOL correct = True;
+       int fnum1;
+
+       printf("starting rename test\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       
+       cli_unlink(cli1, fname);
+       cli_unlink(cli1, fname1);
+       fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("First open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_rename(cli1, fname, fname1)) {
+               printf("First rename failed (this is correct) - %s\n", cli_errstr(cli1));
+       } else {
+               printf("First rename succeeded - this should have failed !\n");
+               correct = False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close - 1 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       cli_unlink(cli1, fname);
+       cli_unlink(cli1, fname1);
+       fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL,
+#if 0
+                                  NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+#else
+                                  NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+#endif
+
+       if (fnum1 == -1) {
+               printf("Second open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_rename(cli1, fname, fname1)) {
+               printf("Second rename failed - this should have succeeded - %s\n", cli_errstr(cli1));
+               correct = False;
+       } else {
+               printf("Second rename succeeded\n");
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close - 2 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       cli_unlink(cli1, fname);
+       cli_unlink(cli1, fname1);
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_READ_CONTROL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("Third open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+
+
+#if 0
+  {
+  int fnum2;
+
+       fnum2 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("Fourth open failed - %s\n", cli_errstr(cli1));
+               return False;
+       }
+       if (!cli_nt_delete_on_close(cli1, fnum2, True)) {
+               printf("[8] setting delete_on_close on file failed !\n");
+               return False;
+       }
+       
+       if (!cli_close(cli1, fnum2)) {
+               printf("close - 4 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+  }
+#endif
+
+       if (!cli_rename(cli1, fname, fname1)) {
+               printf("Third rename failed - this should have succeeded - %s\n", cli_errstr(cli1));
+               correct = False;
+       } else {
+               printf("Third rename succeeded\n");
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close - 3 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       cli_unlink(cli1, fname);
+       cli_unlink(cli1, fname1);
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       
+       return correct;
+}
+
+static BOOL run_pipe_number(int dummy)
+{
+       struct cli_state *cli1;
+       const char *pipe_name = "\\SPOOLSS";
+       int fnum;
+       int num_pipes = 0;
+
+       printf("starting pipenumber test\n");
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+
+       while(1) {
+               fnum = cli_nt_create_full(cli1, pipe_name, 0, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+               if (fnum == -1) {
+                       printf("Open of pipe %s failed with error (%s)\n", pipe_name, cli_errstr(cli1));
+                       break;
+               }
+               num_pipes++;
+       }
+
+       printf("pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
+       torture_close_connection(cli1);
+       return True;
+}
+
+
+/*
+  Test open mode returns on read-only files.
+ */
+ static BOOL run_opentest(int dummy)
+{
+       static struct cli_state *cli1;
+       static struct cli_state *cli2;
+       const char *fname = "\\readonly.file";
+       int fnum1, fnum2;
+       char buf[20];
+       size_t fsize;
+       BOOL correct = True;
+       char *tmp_path;
+       int failures = 0;
+
+       printf("starting open test\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+       
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+       
+       if (!cli_setatr(cli1, fname, FILE_ATTRIBUTE_READONLY, 0)) {
+               printf("cli_setatr failed (%s)\n", cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test1);
+               return False;
+       }
+       
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test1);
+               return False;
+       }
+       
+       /* This will fail - but the error should be ERRnoaccess, not ERRbadshare. */
+       fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+       
+        if (check_error(__LINE__, cli1, ERRDOS, ERRnoaccess, 
+                       NT_STATUS_ACCESS_DENIED)) {
+               printf("correct error code ERRDOS/ERRnoaccess returned\n");
+       }
+       
+       printf("finished open test 1\n");
+error_test1:
+       cli_close(cli1, fnum1);
+       
+       /* Now try not readonly and ensure ERRbadshare is returned. */
+       
+       cli_setatr(cli1, fname, 0, 0);
+       
+       fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       
+       /* This will fail - but the error should be ERRshare. */
+       fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+       
+       if (check_error(__LINE__, cli1, ERRDOS, ERRbadshare, 
+                       NT_STATUS_SHARING_VIOLATION)) {
+               printf("correct error code ERRDOS/ERRbadshare returned\n");
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+       
+       cli_unlink(cli1, fname);
+       
+       printf("finished open test 2\n");
+       
+       /* Test truncate open disposition on file opened for read. */
+       
+       fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("(3) open (1) of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       
+       /* write 20 bytes. */
+       
+       memset(buf, '\0', 20);
+
+       if (cli_write(cli1, fnum1, 0, buf, 0, 20) != 20) {
+               printf("write failed (%s)\n", cli_errstr(cli1));
+               correct = False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("(3) close1 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+       
+       /* Ensure size == 20. */
+       if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+               printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test3);
+               return False;
+       }
+       
+       if (fsize != 20) {
+               printf("(3) file size != 20\n");
+               CHECK_MAX_FAILURES(error_test3);
+               return False;
+       }
+
+       /* Now test if we can truncate a file opened for readonly. */
+       
+       fnum1 = cli_open(cli1, fname, O_RDONLY|O_TRUNC, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("(3) open (2) of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test3);
+               return False;
+       }
+       
+       if (!cli_close(cli1, fnum1)) {
+               printf("close2 failed (%s)\n", cli_errstr(cli1));
+               return False;
+       }
+
+       /* Ensure size == 0. */
+       if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+               printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test3);
+               return False;
+       }
+
+       if (fsize != 0) {
+               printf("(3) file size != 0\n");
+               CHECK_MAX_FAILURES(error_test3);
+               return False;
+       }
+       printf("finished open test 3\n");
+error_test3:   
+       cli_unlink(cli1, fname);
+
+
+       printf("testing ctemp\n");
+       fnum1 = cli_ctemp(cli1, "\\", &tmp_path);
+       if (fnum1 == -1) {
+               printf("ctemp failed (%s)\n", cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test4);
+               return False;
+       }
+       printf("ctemp gave path %s\n", tmp_path);
+       if (!cli_close(cli1, fnum1)) {
+               printf("close of temp failed (%s)\n", cli_errstr(cli1));
+       }
+       if (!cli_unlink(cli1, tmp_path)) {
+               printf("unlink of temp failed (%s)\n", cli_errstr(cli1));
+       }
+error_test4:   
+       /* Test the non-io opens... */
+
+       if (!torture_open_connection(&cli2)) {
+               return False;
+       }
+       
+       cli_setatr(cli2, fname, 0, 0);
+       cli_unlink(cli2, fname);
+       
+       printf("TEST #1 testing 2 non-io opens (no delete)\n");
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 1 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test10);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+       if (fnum2 == -1) {
+               printf("test 1 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test10);
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       if (!cli_close(cli2, fnum2)) {
+               printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       printf("non-io open test #1 passed.\n");
+error_test10:
+       cli_unlink(cli1, fname);
+
+       printf("TEST #2 testing 2 non-io opens (first with delete)\n");
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 2 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test20);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("test 2 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test20);
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       if (!cli_close(cli2, fnum2)) {
+               printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       printf("non-io open test #2 passed.\n");
+error_test20:
+       cli_unlink(cli1, fname);
+
+       printf("TEST #3 testing 2 non-io opens (second with delete)\n");
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 3 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test30);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("test 3 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test30);
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 3 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+       if (!cli_close(cli2, fnum2)) {
+               printf("test 3 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       printf("non-io open test #3 passed.\n");
+error_test30:
+       cli_unlink(cli1, fname);
+
+       printf("TEST #4 testing 2 non-io opens (both with delete)\n");
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 4 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test40);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 != -1) {
+               printf("test 4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test40);
+               return False;
+       }
+
+       printf("test 4 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 4 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       printf("non-io open test #4 passed.\n");
+error_test40:
+       cli_unlink(cli1, fname);
+
+       printf("TEST #5 testing 2 non-io opens (both with delete - both with file share delete)\n");
+       
+       fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 5 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test50);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("test 5 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test50);
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 5 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_close(cli2, fnum2)) {
+               printf("test 5 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       printf("non-io open test #5 passed.\n");
+error_test50:
+       printf("TEST #6 testing 1 non-io open, one io open\n");
+       
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 6 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test60);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 == -1) {
+               printf("test 6 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test60);
+               return False;
+       }
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 6 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       if (!cli_close(cli2, fnum2)) {
+               printf("test 6 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+               return False;
+       }
+
+       printf("non-io open test #6 passed.\n");
+error_test60:
+       printf("TEST #7 testing 1 non-io open, one io open with delete\n");
+
+       cli_unlink(cli1, fname);
+
+       fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+       if (fnum1 == -1) {
+               printf("test 7 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               CHECK_MAX_FAILURES(error_test70);
+               return False;
+       }
+
+       fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+       if (fnum2 != -1) {
+               printf("test 7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+               CHECK_MAX_FAILURES(error_test70);
+               return False;
+       }
+
+       printf("test 7 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+       if (!cli_close(cli1, fnum1)) {
+               printf("test 7 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+               return False;
+       }
+
+       printf("non-io open test #7 passed.\n");
+error_test70:
+       cli_unlink(cli1, fname);
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       if (!torture_close_connection(cli2)) {
+               correct = False;
+       }
+       
+       return correct;
+}
+
+
+static uint32 open_attrs_table[] = {
+               FILE_ATTRIBUTE_NORMAL,
+               FILE_ATTRIBUTE_ARCHIVE,
+               FILE_ATTRIBUTE_READONLY,
+               FILE_ATTRIBUTE_HIDDEN,
+               FILE_ATTRIBUTE_SYSTEM,
+
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY,
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN,
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+               FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+
+               FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+               FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+               FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+               FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM,
+};
+
+struct trunc_open_results {
+       unsigned int num;
+       uint32 init_attr;
+       uint32 trunc_attr;
+       uint32 result_attr;
+};
+
+static struct trunc_open_results attr_results[] = {
+       { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+       { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+       { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+       { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+       { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+       { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+       { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+       { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+       { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+       { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 119,  FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,  FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+       { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN },
+       { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM },
+       { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+       { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+       { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+       { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }
+};
+
+static BOOL run_openattrtest(int dummy)
+{
+       struct cli_state *cli1;
+       const char *fname = "\\openattr.file";
+       int fnum1;
+       BOOL correct = True;
+       uint16 attr;
+       unsigned int i, j, k, l;
+       int failures = 0;
+
+       printf("starting open attr test\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       
+       for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32); i++) {
+               cli_setatr(cli1, fname, 0, 0);
+               cli_unlink(cli1, fname);
+               fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_WRITE_DATA, open_attrs_table[i],
+                                  NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+
+               if (fnum1 == -1) {
+                       printf("open %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+                       return False;
+               }
+
+               if (!cli_close(cli1, fnum1)) {
+                       printf("close %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+                       return False;
+               }
+
+               for (j = 0; j < ARRAY_SIZE(open_attrs_table); j++) {
+                       fnum1 = cli_nt_create_full(cli1, fname, 0, 
+                                                  SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA, 
+                                                  open_attrs_table[j],
+                                                  NTCREATEX_SHARE_ACCESS_NONE, 
+                                                  NTCREATEX_DISP_OVERWRITE, 0, 0);
+
+                       if (fnum1 == -1) {
+                               for (l = 0; l < ARRAY_SIZE(attr_results); l++) {
+                                       if (attr_results[l].num == k) {
+                                               printf("[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(0x%x:%s)\n",
+                                                               k, open_attrs_table[i],
+                                                               open_attrs_table[j],
+                                                               fname, NT_STATUS_V(cli_nt_error(cli1)), cli_errstr(cli1));
+                                               correct = False;
+                                               CHECK_MAX_FAILURES(error_exit);
+                                       }
+                               }
+                               if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+                                       printf("[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s\n",
+                                                       k, open_attrs_table[i], open_attrs_table[j],
+                                                       cli_errstr(cli1));
+                                       correct = False;
+                                       CHECK_MAX_FAILURES(error_exit);
+                               }
+#if 0
+                               printf("[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]);
+#endif
+                               k++;
+                               continue;
+                       }
+
+                       if (!cli_close(cli1, fnum1)) {
+                               printf("close %d (2) of %s failed (%s)\n", j, fname, cli_errstr(cli1));
+                               return False;
+                       }
+
+                       if (!cli_getatr(cli1, fname, &attr, NULL, NULL)) {
+                               printf("getatr(2) failed (%s)\n", cli_errstr(cli1));
+                               return False;
+                       }
+
+#if 0
+                       printf("[%d] getatr check [0x%x] trunc [0x%x] got attr 0x%x\n",
+                                       k,  open_attrs_table[i],  open_attrs_table[j], attr );
+#endif
+
+                       for (l = 0; l < ARRAY_SIZE(attr_results); l++) {
+                               if (attr_results[l].num == k) {
+                                       if (attr != attr_results[l].result_attr ||
+                                                       open_attrs_table[i] != attr_results[l].init_attr ||
+                                                       open_attrs_table[j] != attr_results[l].trunc_attr) {
+                                               printf("[%d] getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
+                                                       k, open_attrs_table[i],
+                                                       open_attrs_table[j],
+                                                       (unsigned int)attr,
+                                                       attr_results[l].result_attr);
+                                               correct = False;
+                                               CHECK_MAX_FAILURES(error_exit);
+                                       }
+                                       break;
+                               }
+                       }
+                       k++;
+               }
+       }
+error_exit:
+       cli_setatr(cli1, fname, 0, 0);
+       cli_unlink(cli1, fname);
+
+       printf("open attr test %s.\n", correct ? "passed" : "failed");
+
+       if (!torture_close_connection(cli1)) {
+               correct = False;
+       }
+       return correct;
+}
+
+static void list_fn(file_info *finfo, const char *name, void *state)
+{
+       
+}
+
+/*
+  test directory listing speed
+ */
+static BOOL run_dirtest(int dummy)
+{
+       int i;
+       struct cli_state *cli;
+       int fnum;
+       double t1;
+       BOOL correct = True;
+
+       printf("starting directory test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("Creating %d random filenames\n", torture_numops);
+
+       srandom(0);
+       for (i=0;i<torture_numops;i++) {
+               char *fname;
+               asprintf(&fname, "\\%x", (int)random());
+               fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+               if (fnum == -1) {
+                       fprintf(stderr,"Failed to open %s\n", fname);
+                       return False;
+               }
+               cli_close(cli, fnum);
+               free(fname);
+       }
+
+       t1 = end_timer();
+
+       printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+       printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+       printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+
+       printf("dirtest core %g seconds\n", end_timer() - t1);
+
+       srandom(0);
+       for (i=0;i<torture_numops;i++) {
+               char *fname;
+               asprintf(&fname, "\\%x", (int)random());
+               cli_unlink(cli, fname);
+               free(fname);
+       }
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("finished dirtest\n");
+
+       return correct;
+}
+
+static void del_fn(file_info *finfo, const char *mask, void *state)
+{
+       struct cli_state *pcli = (struct cli_state *)state;
+       char *fname;
+       asprintf(&fname, "\\LISTDIR\\%s", finfo->name);
+
+       if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0)
+               return;
+
+       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
+               if (!cli_rmdir(pcli, fname))
+                       printf("del_fn: failed to rmdir %s, error=%s\n", fname, cli_errstr(pcli) );
+       } else {
+               if (!cli_unlink(pcli, fname))
+                       printf("del_fn: failed to unlink %s, error=%s\n", fname, cli_errstr(pcli) );
+       }
+       free(fname);
+}
+
+
+/*
+  sees what IOCTLs are supported
+ */
+BOOL torture_ioctl_test(int dummy)
+{
+       struct cli_state *cli;
+       uint16 device, function;
+       int fnum;
+       const char *fname = "\\ioctl.dat";
+       DATA_BLOB blob;
+       NTSTATUS status;
+       struct smb_ioctl parms;
+       TALLOC_CTX *mem_ctx;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       mem_ctx = talloc_init("ioctl_test");
+
+       printf("starting ioctl test\n");
+
+       cli_unlink(cli, fname);
+
+       fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+               return False;
+       }
+
+       parms.in.request = IOCTL_QUERY_JOB_INFO;
+       status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
+       printf("ioctl job info: %s\n", cli_errstr(cli));
+
+       for (device=0;device<0x100;device++) {
+               printf("testing device=0x%x\n", device);
+               for (function=0;function<0x100;function++) {
+                       parms.in.request = (device << 16) | function;
+                       status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               printf("ioctl device=0x%x function=0x%x OK : %d bytes\n", 
+                                       device, function, blob.length);
+                               data_blob_free(&parms.out.blob);
+                       }
+               }
+       }
+
+       if (!torture_close_connection(cli)) {
+               return False;
+       }
+
+       return True;
+}
+
+
+/*
+  tries variants of chkpath
+ */
+BOOL torture_chkpath_test(int dummy)
+{
+       struct cli_state *cli;
+       int fnum;
+       BOOL ret;
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("starting chkpath test\n");
+
+       printf("Testing valid and invalid paths\n");
+
+       /* cleanup from an old run */
+       cli_rmdir(cli, "\\chkpath.dir\\dir2");
+       cli_unlink(cli, "\\chkpath.dir\\*");
+       cli_rmdir(cli, "\\chkpath.dir");
+
+       if (!cli_mkdir(cli, "\\chkpath.dir")) {
+               printf("mkdir1 failed : %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       if (!cli_mkdir(cli, "\\chkpath.dir\\dir2")) {
+               printf("mkdir2 failed : %s\n", cli_errstr(cli));
+               return False;
+       }
+
+       fnum = cli_open(cli, "\\chkpath.dir\\foo.txt", O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open1 failed (%s)\n", cli_errstr(cli));
+               return False;
+       }
+       cli_close(cli, fnum);
+
+       if (!cli_chkpath(cli, "\\chkpath.dir")) {
+               printf("chkpath1 failed: %s\n", cli_errstr(cli));
+               ret = False;
+       }
+
+       if (!cli_chkpath(cli, "\\chkpath.dir\\dir2")) {
+               printf("chkpath2 failed: %s\n", cli_errstr(cli));
+               ret = False;
+       }
+
+       if (!cli_chkpath(cli, "\\chkpath.dir\\foo.txt")) {
+               ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath, 
+                                 NT_STATUS_NOT_A_DIRECTORY);
+       } else {
+               printf("* chkpath on a file should fail\n");
+               ret = False;
+       }
+
+       if (!cli_chkpath(cli, "\\chkpath.dir\\bar.txt")) {
+               ret = check_error(__LINE__, cli, ERRDOS, ERRbadfile, 
+                                 NT_STATUS_OBJECT_NAME_NOT_FOUND);
+       } else {
+               printf("* chkpath on a non existent file should fail\n");
+               ret = False;
+       }
+
+       if (!cli_chkpath(cli, "\\chkpath.dir\\dirxx\\bar.txt")) {
+               ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath, 
+                                 NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       } else {
+               printf("* chkpath on a non existent component should fail\n");
+               ret = False;
+       }
+
+       cli_rmdir(cli, "\\chkpath.dir\\dir2");
+       cli_unlink(cli, "\\chkpath.dir\\*");
+       cli_rmdir(cli, "\\chkpath.dir");
+
+       if (!torture_close_connection(cli)) {
+               return False;
+       }
+
+       return ret;
+}
+
+
+
+
+static BOOL run_dirtest1(int dummy)
+{
+       int i;
+       struct cli_state *cli;
+       int fnum, num_seen;
+       BOOL correct = True;
+
+       printf("starting directory test\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+       cli_list(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, del_fn, cli);
+       if (cli_deltree(cli, "\\LISTDIR") == -1) {
+               fprintf(stderr,"Failed to deltree %s, error=%s\n", "\\LISTDIR", cli_errstr(cli));
+               return False;
+       }
+       if (!cli_mkdir(cli, "\\LISTDIR")) {
+               fprintf(stderr,"Failed to mkdir %s, error=%s\n", "\\LISTDIR", cli_errstr(cli));
+               return False;
+       }
+
+       printf("Creating %d files\n", torture_entries);
+
+       /* Create torture_entries files and torture_entries directories. */
+       for (i=0;i<torture_entries;i++) {
+               char *fname;
+               asprintf(&fname, "\\LISTDIR\\f%d", i);
+               fnum = cli_nt_create_full(cli, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+                                  NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0);
+               if (fnum == -1) {
+                       fprintf(stderr,"Failed to open %s, error=%s\n", fname, cli_errstr(cli));
+                       return False;
+               }
+               free(fname);
+               cli_close(cli, fnum);
+       }
+       for (i=0;i<torture_entries;i++) {
+               char *fname;
+               asprintf(&fname, "\\LISTDIR\\d%d", i);
+               if (!cli_mkdir(cli, fname)) {
+                       fprintf(stderr,"Failed to open %s, error=%s\n", fname, cli_errstr(cli));
+                       return False;
+               }
+               free(fname);
+       }
+
+       /* Now ensure that doing an old list sees both files and directories. */
+       num_seen = cli_list_old(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
+       printf("num_seen = %d\n", num_seen );
+       /* We should see (torture_entries) each of files & directories + . and .. */
+       if (num_seen != (2*torture_entries)+2) {
+               correct = False;
+               fprintf(stderr,"entry count mismatch, should be %d, was %d\n",
+                       (2*torture_entries)+2, num_seen);
+       }
+               
+
+       /* Ensure if we have the "must have" bits we only see the
+        * relevant entries.
+        */
+       num_seen = cli_list_old(cli, "\\LISTDIR\\*", (FILE_ATTRIBUTE_DIRECTORY<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
+       printf("num_seen = %d\n", num_seen );
+       if (num_seen != torture_entries+2) {
+               correct = False;
+               fprintf(stderr,"entry count mismatch, should be %d, was %d\n",
+                       torture_entries+2, num_seen);
+       }
+
+       num_seen = cli_list_old(cli, "\\LISTDIR\\*", (FILE_ATTRIBUTE_ARCHIVE<<8)|FILE_ATTRIBUTE_DIRECTORY, list_fn, NULL);
+       printf("num_seen = %d\n", num_seen );
+       if (num_seen != torture_entries) {
+               correct = False;
+               fprintf(stderr,"entry count mismatch, should be %d, was %d\n",
+                       torture_entries, num_seen);
+       }
+
+       /* Delete everything. */
+       cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+       cli_list(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, del_fn, cli);
+       cli_rmdir(cli, "\\LISTDIR");
+
+#if 0
+       printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+       printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+       printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+#endif
+
+       if (!torture_close_connection(cli)) {
+               correct = False;
+       }
+
+       printf("finished dirtest1\n");
+
+       return correct;
+}
+
+
+/*
+   simple test harness for playing with deny modes
+ */
+static BOOL run_deny3test(int dummy)
+{
+       struct cli_state *cli1, *cli2;
+       int fnum1, fnum2;
+       const char *fname;
+
+       printf("starting deny3 test\n");
+
+       printf("Testing simple deny modes\n");
+       
+       if (!torture_open_connection(&cli1)) {
+               return False;
+       }
+       if (!torture_open_connection(&cli2)) {
+               return False;
+       }
+
+       fname = "\\deny_dos1.dat";
+
+       cli_unlink(cli1, fname);
+       fnum1 = cli_open(cli1, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS);
+       fnum2 = cli_open(cli1, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS);
+       if (fnum1 != -1) cli_close(cli1, fnum1);
+       if (fnum2 != -1) cli_close(cli1, fnum2);
+       cli_unlink(cli1, fname);
+       printf("fnum1=%d fnum2=%d\n", fnum1, fnum2);
+
+
+       fname = "\\deny_dos2.dat";
+
+       cli_unlink(cli1, fname);
+       fnum1 = cli_open(cli1, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS);
+       fnum2 = cli_open(cli2, fname, O_CREAT|O_TRUNC|O_WRONLY, DENY_DOS);
+       if (fnum1 != -1) cli_close(cli1, fnum1);
+       if (fnum2 != -1) cli_close(cli2, fnum2);
+       cli_unlink(cli1, fname);
+       printf("fnum1=%d fnum2=%d\n", fnum1, fnum2);
+
+
+       torture_close_connection(cli1);
+       torture_close_connection(cli2);
+
+       return True;
+}
+
+
+static double create_procs(BOOL (*fn)(int), BOOL *result)
+{
+       int i, status;
+       volatile pid_t *child_status;
+       volatile BOOL *child_status_out;
+       int synccount;
+       int tries = 8;
+
+       synccount = 0;
+
+       child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*nprocs);
+       if (!child_status) {
+               printf("Failed to setup shared memory\n");
+               return -1;
+       }
+
+       child_status_out = (volatile BOOL *)shm_setup(sizeof(BOOL)*nprocs);
+       if (!child_status_out) {
+               printf("Failed to setup result status shared memory\n");
+               return -1;
+       }
+
+       for (i = 0; i < nprocs; i++) {
+               child_status[i] = 0;
+               child_status_out[i] = True;
+       }
+
+       start_timer();
+
+       for (i=0;i<nprocs;i++) {
+               procnum = i;
+               if (fork() == 0) {
+                       char *myname;
+                       pid_t mypid = getpid();
+                       sys_srandom(((int)mypid) ^ ((int)time(NULL)));
+
+                       asprintf(&myname, "CLIENT%d", i);
+                       lp_set_cmdline("netbios name", myname);
+                       free(myname);
+
+                       while (1) {
+                               if (torture_open_connection(&current_cli)) break;
+                               if (tries-- == 0) {
+                                       printf("pid %d failed to start\n", (int)getpid());
+                                       _exit(1);
+                               }
+                               msleep(10);     
+                       }
+
+                       child_status[i] = getpid();
+
+                       while (child_status[i] && end_timer() < 5) msleep(2);
+
+                       child_status_out[i] = fn(i);
+                       _exit(0);
+               }
+       }
+
+       do {
+               synccount = 0;
+               for (i=0;i<nprocs;i++) {
+                       if (child_status[i]) synccount++;
+               }
+               if (synccount == nprocs) break;
+               msleep(10);
+       } while (end_timer() < 30);
+
+       if (synccount != nprocs) {
+               printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
+               *result = False;
+               return end_timer();
+       }
+
+       /* start the client load */
+       start_timer();
+
+       for (i=0;i<nprocs;i++) {
+               child_status[i] = 0;
+       }
+
+       printf("%d clients started\n", nprocs);
+
+       for (i=0;i<nprocs;i++) {
+               while (waitpid(0, &status, 0) == -1 && errno == EINTR) /* noop */ ;
+       }
+
+       printf("\n");
+       
+       for (i=0;i<nprocs;i++) {
+               if (!child_status_out[i]) {
+                       *result = False;
+               }
+       }
+       return end_timer();
+}
+
+#define FLAG_MULTIPROC 1
+
+static struct {
+       const char *name;
+       BOOL (*fn)(int);
+       unsigned flags;
+} torture_ops[] = {
+       {"FDPASS", run_fdpasstest, 0},
+       {"LOCK1",  run_locktest1,  0},
+       {"LOCK2",  run_locktest2,  0},
+       {"LOCK3",  run_locktest3,  0},
+       {"LOCK4",  run_locktest4,  0},
+       {"LOCK5",  run_locktest5,  0},
+       {"LOCK6",  run_locktest6,  0},
+       {"LOCK7",  run_locktest7,  0},
+       {"UNLINK", run_unlinktest, 0},
+       {"ATTR",   run_attrtest,   0},
+       {"TRANS2", run_trans2test, 0},
+       {"MAXFID", run_maxfidtest, FLAG_MULTIPROC},
+       {"TORTURE",run_torture,    FLAG_MULTIPROC},
+       {"NEGNOWAIT", run_negprot_nowait, 0},
+       {"NBENCH",  run_nbench, 0},
+       {"DIR",  run_dirtest, 0},
+       {"DIR1",  run_dirtest1, 0},
+       {"DENY1",  torture_denytest1, 0},
+       {"DENY2",  torture_denytest2, 0},
+       {"TCON",  run_tcon_test, 0},
+       {"TCONDEV",  run_tcon_devtype_test, 0},
+#if 0
+       {"DFSBASIC", torture_dfs_basic, 0},
+       {"DFSRENAME", torture_dfs_rename, 0},
+       {"DFSRANDOM", torture_dfs_random, 0},
+#endif
+       {"RW1",  run_readwritetest, 0},
+       {"RW2",  run_readwritemulti, FLAG_MULTIPROC},
+       {"OPEN", run_opentest, 0},
+       {"DENY3", run_deny3test, 0},
+#if 1
+       {"OPENATTR", run_openattrtest, 0},
+#endif
+       {"XCOPY", run_xcopy, 0},
+       {"RENAME", run_rename, 0},
+       {"DELETE", run_deletetest, 0},
+       {"PROPERTIES", run_properties, 0},
+       {"MANGLE", torture_mangle, 0},
+       {"UTABLE", torture_utable, 0},
+       {"CASETABLE", torture_casetable, 0},
+       {"PIPE_NUMBER", run_pipe_number, 0},
+       {"IOCTL",  torture_ioctl_test, 0},
+       {"CHKPATH",  torture_chkpath_test, 0},
+       {"RAW-QFSINFO", torture_raw_qfsinfo, 0},
+       {"RAW-QFILEINFO", torture_raw_qfileinfo, 0},
+       {"RAW-SFILEINFO", torture_raw_sfileinfo, 0},
+       {"RAW-SFILEINFO-BUG", torture_raw_sfileinfo_bug, 0},
+       {"RAW-SEARCH", torture_raw_search, 0},
+       {"RAW-CLOSE", torture_raw_close, 0},
+       {"RAW-OPEN", torture_raw_open, 0},
+       {"RAW-MKDIR", torture_raw_mkdir, 0},
+       {"RAW-OPLOCK", torture_raw_oplock, 0},
+       {"RAW-NOTIFY", torture_raw_notify, 0},
+       {"RAW-MUX", torture_raw_mux, 0},
+       {"RAW-IOCTL", torture_raw_ioctl, 0},
+       {"RAW-CHKPATH", torture_raw_chkpath, 0},
+       {"RAW-UNLINK", torture_raw_unlink, 0},
+       {"RAW-READ", torture_raw_read, 0},
+       {"RAW-WRITE", torture_raw_write, 0},
+       {"RAW-LOCK", torture_raw_lock, 0},
+       {"RAW-CONTEXT", torture_raw_context, 0},
+       {"RAW-RENAME", torture_raw_rename, 0},
+       {"RAW-SEEK", torture_raw_seek, 0},
+       {"SCAN-TRANS2", torture_trans2_scan, 0},
+       {"SCAN-NTTRANS", torture_nttrans_scan, 0},
+       {"SCAN-ALIASES", torture_trans2_aliases, 0},
+       {NULL, NULL, 0}};
+
+
+
+/****************************************************************************
+run a specified test or "ALL"
+****************************************************************************/
+static BOOL run_test(const char *name)
+{
+       BOOL ret = True;
+       int i;
+       BOOL matched = False;
+
+       if (strequal(name,"ALL")) {
+               for (i=0;torture_ops[i].name;i++) {
+                       if (!run_test(torture_ops[i].name)) {
+                               ret = False;
+                       }
+               }
+               return ret;
+       }
+
+       for (i=0;torture_ops[i].name;i++) {
+               asprintf(&randomfname, "\\XX%x", 
+                        (unsigned)random());
+
+               if (gen_fnmatch(name, torture_ops[i].name) == 0) {
+                       double t;
+                       matched = True;
+                       printf("Running %s\n", torture_ops[i].name);
+                       if (torture_ops[i].flags & FLAG_MULTIPROC) {
+                               BOOL result;
+                               t = create_procs(torture_ops[i].fn, &result);
+                               if (!result) { 
+                                       ret = False;
+                                       printf("TEST %s FAILED!\n", torture_ops[i].name);
+                               }
+                                        
+                       } else {
+                               start_timer();
+                               if (!torture_ops[i].fn(0)) {
+                                       ret = False;
+                                       printf("TEST %s FAILED!\n", torture_ops[i].name);
+                               }
+                               t = end_timer();
+                       }
+                       printf("%s took %g secs\n\n", torture_ops[i].name, t);
+               }
+       }
+
+       if (!matched) {
+               printf("Unknown torture operation '%s'\n", name);
+       }
+
+       return ret;
+}
+
+
+static void usage(void)
+{
+       int i;
+
+       printf("Usage: smbtorture //server/share <options> TEST1 TEST2 ...\n");
+
+       printf("\t-d debuglevel\n");
+       printf("\t-U user%%pass\n");
+       printf("\t-k use kerberos\n");
+       printf("\t-N numprocs\n");
+       printf("\t-n my_netbios_name\n");
+       printf("\t-W workgroup\n");
+       printf("\t-o num_operations\n");
+       printf("\t-e num files(entries)\n");
+       printf("\t-O socket_options\n");
+       printf("\t-m maximum protocol\n");
+       printf("\t-L use oplocks\n");
+       printf("\t-c CLIENT.TXT   specify client load file for NBENCH\n");
+       printf("\t-A showall\n");
+       printf("\t-p port\n");
+       printf("\t-s seed\n");
+       printf("\t-f max failures\n");
+       printf("\t-b bypass I/O (NBENCH)\n");
+       printf("\n\n");
+
+       printf("tests are:");
+       for (i=0;torture_ops[i].name;i++) {
+               printf(" %s", torture_ops[i].name);
+       }
+       printf("\n");
+
+       printf("default test is ALL\n");
+       
+       exit(1);
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       int opt, i;
+       char *p;
+       int gotuser = 0;
+       int gotpass = 0;
+       BOOL correct = True;
+       char *host, *share, *username, *password;
+
+       setup_logging("smbtorture", DEBUG_STDOUT);
+
+#ifdef HAVE_SETBUFFER
+       setbuffer(stdout, NULL, 0);
+#endif
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (argc < 2) {
+               usage();
+       }
+
+        for(p = argv[1]; *p; p++)
+          if(*p == '\\')
+            *p = '/';
+       if (strncmp(argv[1], "//", 2)) {
+               usage();
+       }
+
+       host = strdup(&argv[1][2]);
+       p = strchr_m(&host[2],'/');
+       if (!p) {
+               usage();
+       }
+       *p = 0;
+       share = strdup(p+1);
+
+       if (getenv("LOGNAME")) {
+               username = strdup(getenv("LOGNAME"));
+       }
+
+       lp_set_cmdline("torture:host", host);
+       lp_set_cmdline("torture:share", share);
+       lp_set_cmdline("torture:username", username);
+       lp_set_cmdline("torture:password", "");
+
+       argc--;
+       argv++;
+
+       srandom(time(NULL));
+
+       while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:e:m:Ld:Ac:ks:f:bs:")) != EOF) {
+               switch (opt) {
+               case 'p':
+                       lp_set_cmdline("smb ports", optarg);
+                       break;
+               case 'W':
+                       lp_set_cmdline("workgroup", optarg);
+                       break;
+               case 'm':
+                       lp_set_cmdline("protocol", optarg);
+                       break;
+               case 'n':
+                       lp_set_cmdline("netbios name", optarg);
+                       break;
+               case 'd':
+                       lp_set_cmdline("debug level", optarg);
+                       setup_logging(NULL,True);
+                       break;
+               case 'O':
+                       lp_set_cmdline("socket options", optarg);
+                       break;
+               case 's':
+                       srandom(atoi(optarg));
+                       break;
+               case 'N':
+                       nprocs = atoi(optarg);
+                       break;
+               case 'o':
+                       torture_numops = atoi(optarg);
+                       break;
+               case 'e':
+                       torture_entries = atoi(optarg);
+                       break;
+               case 'L':
+                       use_oplocks = True;
+                       break;
+               case 'A':
+                       torture_showall = True;
+                       break;
+               case 'c':
+                       client_txt = optarg;
+                       break;
+               case 'k':
+#ifdef HAVE_KRB5
+                       use_kerberos = True;
+#else
+                       d_printf("No kerberos support compiled in\n");
+                       exit(1);
+#endif
+                       break;
+               case 'U':
+                       gotuser = 1;
+                       username = strdup(optarg);
+                       p = strchr_m(username,'%');
+                       if (p) {
+                               *p = 0;
+                               password = strdup(p+1);
+                               gotpass = 1;
+                       }
+                       lp_set_cmdline("torture:username", username);
+                       lp_set_cmdline("torture:password", password);
+                       break;
+               case 'f':
+                       torture_failures = atoi(optarg);
+                       break;
+               case 'b':
+                       bypass_io = True;
+                       break;
+                       
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       usage();
+               }
+       }
+
+       if(use_kerberos && !gotuser) gotpass = True;
+
+       while (!gotpass) {
+               p = getpass("Password:");
+               if (p) {
+                       lp_set_cmdline("torture:password", p);
+                       gotpass = 1;
+               }
+       }
+
+       printf("host=%s share=%s user=%s myname=%s\n", 
+              host, share, username, lp_netbios_name());
+
+       if (argc == optind) {
+               printf("You must specify a test to run, or 'ALL'\n");
+       } else {
+               for (i=optind;i<argc;i++) {
+                       if (!run_test(argv[i])) {
+                               correct = False;
+                       }
+               }
+       }
+
+       if (correct) {
+               return(0);
+       } else {
+               return(1);
+       }
+}
diff --git a/source4/torture/torture_util.c b/source4/torture/torture_util.c
new file mode 100644 (file)
index 0000000..c8fb8a8
--- /dev/null
@@ -0,0 +1,306 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester utility functions
+   Copyright (C) Andrew Tridgell 2003
+   
+   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"
+
+
+static struct timeval tp1,tp2;
+
+void start_timer(void)
+{
+       gettimeofday(&tp1,NULL);
+}
+
+double end_timer(void)
+{
+       gettimeofday(&tp2,NULL);
+       return((tp2.tv_sec - tp1.tv_sec) + 
+              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+/*
+  sometimes we need a fairly complex file to work with, so we can test
+  all possible attributes. 
+*/
+int create_complex_file(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
+{
+       int fnum;
+       char buf[7] = "abc";
+       union smb_setfileinfo setfile;
+       union smb_fileinfo fileinfo;
+       time_t t = (time(NULL) & ~1);
+       NTSTATUS status;
+
+       cli_unlink(cli, fname);
+       fnum = cli_nt_create_full(cli, fname, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS,
+                                 FILE_ATTRIBUTE_NORMAL,
+                                 NTCREATEX_SHARE_ACCESS_DELETE|
+                                 NTCREATEX_SHARE_ACCESS_READ|
+                                 NTCREATEX_SHARE_ACCESS_WRITE, 
+                                 NTCREATEX_DISP_OVERWRITE_IF,
+                                 0, 0);
+       if (fnum == -1) return -1;
+
+       cli_write(cli, fnum, 0, buf, 0, sizeof(buf));
+
+       /* setup some EAs */
+       setfile.generic.level = RAW_SFILEINFO_EA_SET;
+       setfile.generic.file.fnum = fnum;
+       setfile.ea_set.in.ea.flags = 0;
+       setfile.ea_set.in.ea.name.s = "EAONE";
+       setfile.ea_set.in.ea.value = data_blob_talloc(mem_ctx, "VALUE1", 6);
+
+       status = smb_raw_setfileinfo(cli->tree, &setfile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to setup EAs\n");
+       }
+
+       setfile.ea_set.in.ea.name.s = "SECONDEA";
+       setfile.ea_set.in.ea.value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
+       status = smb_raw_setfileinfo(cli->tree, &setfile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to setup EAs\n");
+       }
+
+       /* make sure all the timestamps aren't the same */
+       setfile.generic.level = RAW_SFILEINFO_SETATTRE;
+       setfile.generic.file.fnum = fnum;
+
+       setfile.setattre.in.create_time = t + 60;
+       setfile.setattre.in.access_time = t + 120;
+       setfile.setattre.in.write_time  = t + 180;
+
+       status = smb_raw_setfileinfo(cli->tree, &setfile);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to setup file times - %s\n", nt_errstr(status));
+       }
+
+       /* make sure all the timestamps aren't the same */
+       fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
+       fileinfo.generic.in.fnum = fnum;
+
+       status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Failed to query file times - %s\n", nt_errstr(status));
+       }
+
+       if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
+               printf("create_time not setup correctly\n");
+       }
+       if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
+               printf("access_time not setup correctly\n");
+       }
+       if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
+               printf("write_time not setup correctly\n");
+       }
+
+       return fnum;
+}
+
+
+
+/* return a pointer to a anonymous shared memory segment of size "size"
+   which will persist across fork() but will disappear when all processes
+   exit 
+
+   The memory is not zeroed 
+
+   This function uses system5 shared memory. It takes advantage of a property
+   that the memory is not destroyed if it is attached when the id is removed
+   */
+void *shm_setup(int size)
+{
+       int shmid;
+       void *ret;
+
+       shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
+       if (shmid == -1) {
+               printf("can't get shared memory\n");
+               exit(1);
+       }
+       ret = (void *)shmat(shmid, 0, 0);
+       if (!ret || ret == (void *)-1) {
+               printf("can't attach to shared memory\n");
+               return NULL;
+       }
+       /* the following releases the ipc, but note that this process
+          and all its children will still have access to the memory, its
+          just that the shmid is no longer valid for other shm calls. This
+          means we don't leave behind lots of shm segments after we exit 
+
+          See Stevens "advanced programming in unix env" for details
+          */
+       shmctl(shmid, IPC_RMID, 0);
+       
+       return ret;
+}
+
+
+/*
+  check that a wire string matches the flags specified 
+  not 100% accurate, but close enough for testing
+*/
+BOOL wire_bad_flags(WIRE_STRING *str, int flags)
+{
+       int len;
+       len = strlen(str->s);
+       if (flags & STR_TERMINATE) len++;
+       if ((flags & STR_UNICODE) || !getenv("CLI_FORCE_ASCII")) {
+               len *= 2;
+       } else if (flags & STR_TERMINATE_ASCII) {
+               len++;
+       }
+       if (str->private_length != len) {
+               printf("Expected wire_length %d but got %d for '%s'\n", 
+                      len, str->private_length, str->s);
+               return True;
+       }
+       return False;
+}
+
+/*
+  return a talloced string representing a time_t for human consumption
+*/
+const char *time_string(TALLOC_CTX *mem_ctx, time_t t)
+{
+       return talloc_strdup(mem_ctx, http_timestring(mem_ctx, t));
+}
+
+/*
+  check if 2 NTTIMEs are equal
+*/
+BOOL nt_time_equal(NTTIME *t1, NTTIME *t2)
+{
+       return t1->low == t2->low && t1->high == t2->high;
+}
+
+/*
+  dump a all_info QFILEINFO structure
+*/
+void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
+{
+       d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.create_time));
+       d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.access_time));
+       d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.write_time));
+       d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.change_time));
+       d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
+       d_printf("\talloc_size:     %llu\n", (unsigned long long)finfo->all_info.out.alloc_size);
+       d_printf("\tsize:           %llu\n", (unsigned long long)finfo->all_info.out.size);
+       d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
+       d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
+       d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
+       d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
+       d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
+}
+
+/*
+  dump file infor by name
+*/
+void torture_all_info(struct cli_tree *tree, const char *fname)
+{
+       TALLOC_CTX *mem_ctx = talloc_init(fname);
+       union smb_fileinfo finfo;
+       NTSTATUS status;
+
+       finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+       finfo.generic.in.fname = fname;
+       status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("%s - %s\n", fname, nt_errstr(status));
+               return;
+       }
+
+       d_printf("%s:\n", fname);
+       dump_all_info(mem_ctx, &finfo);
+       talloc_destroy(mem_ctx);
+}
+
+
+/*
+  split a UNC name into server and share names
+*/
+BOOL split_unc_name(const char *unc, char **server, char **share)
+{
+       char *p = strdup(unc);
+       if (!p) return False;
+       all_string_sub(p, "\\", "/", 0);
+       if (strncmp(p, "//", 2) != 0) return False;
+
+       (*server) = p+2;
+       p = strchr(*server, '/');
+       if (!p) return False;
+
+       *p = 0;
+       (*share) = p+1;
+       
+       return True;
+}
+
+/*
+  split a USER%PASS pair into username and password
+*/
+BOOL split_username(const char *pair, char **user, char **pass)
+{
+       char *p = strdup(pair);
+       if (!p) return False;
+
+       (*user) = p;
+
+       p = strchr(*user, '%');
+       if (!p) return False;
+
+       *p = 0;
+       (*pass) = p+1;
+       
+       return True;
+}
+
+/*
+  set a attribute on a file
+*/
+BOOL torture_set_file_attribute(struct cli_tree *tree, const char *fname, uint16 attrib)
+{
+       union smb_setfileinfo sfinfo;
+       NTSTATUS status;
+
+       sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
+       sfinfo.generic.file.fname = fname;
+
+       ZERO_STRUCT(sfinfo.basic_info.in);
+       sfinfo.basic_info.in.attrib = attrib;
+       status = smb_raw_setpathinfo(tree, &sfinfo);
+       return NT_STATUS_IS_OK(status);
+}
+
+
+/*
+  set a file descriptor as sparse
+*/
+NTSTATUS torture_set_sparse(struct cli_tree *tree, int fnum)
+{
+       struct smb_ntioctl nt;
+
+       nt.in.function = 0x900c4;
+       nt.in.fnum = fnum;
+       nt.in.fsctl = True;
+       nt.in.filter = 0;
+
+       return smb_raw_ntioctl(tree, &nt);
+}
diff --git a/source4/torture/utable.c b/source4/torture/utable.c
new file mode 100644 (file)
index 0000000..38e381d
--- /dev/null
@@ -0,0 +1,196 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB torture tester - unicode table dumper
+   Copyright (C) Andrew Tridgell 2001
+   
+   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"
+
+BOOL torture_utable(int dummy)
+{
+       struct cli_state *cli;
+       fstring fname;
+       char *alt_name;
+       int fnum;
+       smb_ucs2_t c2;
+       int c, len, fd;
+       int chars_allowed=0, alt_allowed=0;
+       uint8 valid[0x10000];
+
+       printf("starting utable\n");
+
+       printf("Generating valid character table\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       memset(valid, 0, sizeof(valid));
+
+       cli_mkdir(cli, "\\utable");
+       cli_unlink(cli, "\\utable\\*");
+
+       for (c=1; c < 0x10000; c++) {
+               char *p;
+
+               SSVAL(&c2, 0, c);
+               fstrcpy(fname, "\\utable\\x");
+               p = fname+strlen(fname);
+               len = convert_string(CH_UCS2, CH_UNIX, 
+                                    &c2, 2, 
+                                    p, sizeof(fname)-strlen(fname));
+               p[len] = 0;
+               fstrcat(fname,"_a_long_extension");
+
+               fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC, 
+                               DENY_NONE);
+               if (fnum == -1) continue;
+
+               chars_allowed++;
+
+               cli_qpathinfo_alt_name(cli, fname, &alt_name);
+
+               if (strncmp(alt_name, "X_A_L", 5) != 0) {
+                       alt_allowed++;
+                       valid[c] = 1;
+                       d_printf("fname=[%s] alt_name=[%s]\n", fname, alt_name);
+               }
+
+               cli_close(cli, fnum);
+               cli_unlink(cli, fname);
+
+               if (c % 100 == 0) {
+                       printf("%d (%d/%d)\r", c, chars_allowed, alt_allowed);
+               }
+       }
+       printf("%d (%d/%d)\n", c, chars_allowed, alt_allowed);
+
+       cli_rmdir(cli, "\\utable");
+
+       d_printf("%d chars allowed   %d alt chars allowed\n", chars_allowed, alt_allowed);
+
+       fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd == -1) {
+               d_printf("Failed to create valid.dat - %s", strerror(errno));
+               return False;
+       }
+       write(fd, valid, 0x10000);
+       close(fd);
+       d_printf("wrote valid.dat\n");
+
+       return True;
+}
+
+
+static char *form_name(int c)
+{
+       static fstring fname;
+       smb_ucs2_t c2;
+       char *p;
+       int len;
+
+       fstrcpy(fname, "\\utable\\");
+       p = fname+strlen(fname);
+       SSVAL(&c2, 0, c);
+
+       len = convert_string(CH_UCS2, CH_UNIX, 
+                            &c2, 2, 
+                            p, sizeof(fname)-strlen(fname));
+       p[len] = 0;
+       return fname;
+}
+
+BOOL torture_casetable(int dummy)
+{
+       static struct cli_state *cli;
+       char *fname;
+       int fnum;
+       int c, i;
+#define MAX_EQUIVALENCE 8
+       smb_ucs2_t equiv[0x10000][MAX_EQUIVALENCE];
+       printf("starting casetable\n");
+
+       if (!torture_open_connection(&cli)) {
+               return False;
+       }
+
+       printf("Determining upper/lower case table\n");
+
+       memset(equiv, 0, sizeof(equiv));
+
+       cli_unlink(cli, "\\utable\\*");
+       cli_rmdir(cli, "\\utable");
+       if (!cli_mkdir(cli, "\\utable")) {
+               printf("Failed to create utable directory!\n");
+               return False;
+       }
+
+       for (c=1; c < 0x10000; c++) {
+               size_t size;
+
+               if (c == '.' || c == '\\') continue;
+
+               d_printf("%04x (%c)\n", c, isprint(c)?c:'.');
+
+               fname = form_name(c);
+               fnum = cli_nt_create_full(cli, fname, 0,
+                                         GENERIC_RIGHTS_FILE_ALL_ACCESS, 
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NTCREATEX_SHARE_ACCESS_NONE,
+                                         NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+               if (fnum == -1) {
+                       printf("Failed to create file with char %04x\n", c);
+                       continue;
+               }
+
+               size = 0;
+
+               if (!cli_qfileinfo(cli, fnum, NULL, &size, 
+                                  NULL, NULL, NULL, NULL, NULL)) continue;
+
+               if (size > 0) {
+                       /* found a character equivalence! */
+                       int c2[MAX_EQUIVALENCE];
+
+                       if (size/sizeof(int) >= MAX_EQUIVALENCE) {
+                               printf("too many chars match?? size=%d c=0x%04x\n",
+                                      size, c);
+                               cli_close(cli, fnum);
+                               return False;
+                       }
+
+                       cli_read(cli, fnum, (char *)c2, 0, size);
+                       printf("%04x: ", c);
+                       equiv[c][0] = c;
+                       for (i=0; i<size/sizeof(int); i++) {
+                               printf("%04x ", c2[i]);
+                               equiv[c][i+1] = c2[i];
+                       }
+                       printf("\n");
+                       fflush(stdout);
+               }
+
+               cli_write(cli, fnum, 0, (char *)&c, size, sizeof(c));
+               cli_close(cli, fnum);
+       }
+
+       cli_unlink(cli, "\\utable\\*");
+       cli_rmdir(cli, "\\utable");
+
+       return True;
+}
diff --git a/source4/torture/vfstest.c b/source4/torture/vfstest.c
new file mode 100644 (file)
index 0000000..6975d00
--- /dev/null
@@ -0,0 +1,591 @@
+/* 
+   Unix SMB/CIFS implementation.
+   VFS module tester
+
+   Copyright (C) Simo Sorce 2002
+   Copyright (C) Eric Lorimer 2002
+   Copyright (C) Jelmer Vernooij 2002
+
+   Most of this code was ripped off of rpcclient.
+   Copyright (C) Tim Potter 2000-2001
+
+   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 "vfstest.h"
+
+/* List to hold groups of commands */
+static struct cmd_list {
+       struct cmd_list *prev, *next;
+       struct cmd_set *cmd_set;
+} *cmd_list;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+       char **matches;
+       int i, count=0;
+       struct cmd_list *commands = cmd_list;
+
+       if (start) 
+               return NULL;
+
+       /* make sure we have a list of valid commands */
+       if (!commands) 
+               return NULL;
+
+       matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+       if (!matches) return NULL;
+
+       matches[count++] = strdup(text);
+       if (!matches[0]) return NULL;
+
+       while (commands && count < MAX_COMPLETIONS-1) 
+       {
+               if (!commands->cmd_set)
+                       break;
+               
+               for (i=0; commands->cmd_set[i].name; i++)
+               {
+                       if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+                               commands->cmd_set[i].fn) 
+                       {
+                               matches[count] = strdup(commands->cmd_set[i].name);
+                               if (!matches[count]) 
+                                       return NULL;
+                               count++;
+                       }
+               }
+               
+               commands = commands->next;
+               
+       }
+
+       if (count == 2) {
+               SAFE_FREE(matches[0]);
+               matches[0] = strdup(matches[1]);
+       }
+       matches[count] = NULL;
+       return matches;
+}
+
+static char* next_command(char** cmdstr)
+{
+       static pstring          command;
+       char                    *p;
+       
+       if (!cmdstr || !(*cmdstr))
+               return NULL;
+       
+       p = strchr_m(*cmdstr, ';');
+       if (p)
+               *p = '\0';
+       pstrcpy(command, *cmdstr);
+       *cmdstr = p;
+       
+       return command;
+}
+
+/* Load specified configuration file */
+static NTSTATUS cmd_conf(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+                       int argc, char **argv)
+{
+       if (argc != 2) {
+               printf("Usage: %s <smb.conf>\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (!lp_load(argv[1], False, True, False)) {
+               printf("Error loading \"%s\"\n", argv[1]);
+               return NT_STATUS_OK;
+       }
+
+       printf("\"%s\" successfully loaded\n", argv[1]);
+       return NT_STATUS_OK;
+}
+       
+/* Display help on commands */
+static NTSTATUS cmd_help(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+                        int argc, const char **argv)
+{
+       struct cmd_list *tmp;
+       struct cmd_set *tmp_set;
+
+       /* Usage */
+       if (argc > 2) {
+               printf("Usage: %s [command]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       /* Help on one command */
+
+       if (argc == 2) {
+               for (tmp = cmd_list; tmp; tmp = tmp->next) {
+                       
+                       tmp_set = tmp->cmd_set;
+
+                       while(tmp_set->name) {
+                               if (strequal(argv[1], tmp_set->name)) {
+                                       if (tmp_set->usage &&
+                                           tmp_set->usage[0])
+                                               printf("%s\n", tmp_set->usage);
+                                       else
+                                               printf("No help for %s\n", tmp_set->name);
+
+                                       return NT_STATUS_OK;
+                               }
+
+                               tmp_set++;
+                       }
+               }
+
+               printf("No such command: %s\n", argv[1]);
+               return NT_STATUS_OK;
+       }
+
+       /* List all commands */
+
+       for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+               tmp_set = tmp->cmd_set;
+
+               while(tmp_set->name) {
+
+                       printf("%15s\t\t%s\n", tmp_set->name,
+                              tmp_set->description ? tmp_set->description:
+                              "");
+
+                       tmp_set++;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+static NTSTATUS cmd_debuglevel(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       if (argc > 2) {
+               printf("Usage: %s [debuglevel]\n", argv[0]);
+               return NT_STATUS_OK;
+       }
+
+       if (argc == 2) {
+               DEBUGLEVEL = atoi(argv[1]);
+       }
+
+       printf("debuglevel is %d\n", DEBUGLEVEL);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_freemem(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       /* Cleanup */
+       talloc_destroy(mem_ctx);
+       mem_ctx = NULL;
+       vfs->data = NULL;
+       vfs->data_size = 0;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv)
+{
+       /* Cleanup */
+       talloc_destroy(mem_ctx);
+
+       exit(0);
+       return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static struct cmd_set vfstest_commands[] = {
+
+       { "GENERAL OPTIONS" },
+
+       { "conf",       cmd_conf,       "Load smb configuration file", "conf <smb.conf>" },
+       { "help",       cmd_help,       "Get help on commands", "" },
+       { "?",          cmd_help,       "Get help on commands", "" },
+       { "debuglevel", cmd_debuglevel, "Set debug level", "" },
+       { "freemem",    cmd_freemem,    "Free currently allocated buffers", "" },
+       { "exit",       cmd_quit,       "Exit program", "" },
+       { "quit",       cmd_quit,       "Exit program", "" },
+
+       { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+       { "---------------", NULL,      "----------------------" },
+       { NULL }
+};
+
+
+extern struct cmd_set vfs_commands[];
+static struct cmd_set *vfstest_command_list[] = {
+       vfstest_commands,
+       vfs_commands,
+       NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+       struct cmd_list *entry;
+
+       if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+               DEBUG(0, ("out of memory\n"));
+               return;
+       }
+
+       ZERO_STRUCTP(entry);
+
+       entry->cmd_set = cmd_set;
+       DLIST_ADD(cmd_list, entry);
+}
+
+static NTSTATUS do_cmd(struct vfs_state *vfs, struct cmd_set *cmd_entry, char *cmd)
+{
+       char *p = cmd, **argv = NULL;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       pstring buf;
+       TALLOC_CTX *mem_ctx = NULL;
+       int argc = 0, i;
+
+       /* Count number of arguments first time through the loop then
+          allocate memory and strdup them. */
+
+ again:
+       while(next_token(&p, buf, " ", sizeof(buf))) {
+               if (argv) {
+                       argv[argc] = strdup(buf);
+               }
+               
+               argc++;
+       }
+                               
+       if (!argv) {
+
+               /* Create argument list */
+
+               argv = (char **)malloc(sizeof(char *) * argc);
+               memset(argv, 0, sizeof(char *) * argc);
+
+               if (!argv) {
+                       fprintf(stderr, "out of memory\n");
+                       result = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+                                       
+               p = cmd;
+               argc = 0;
+                                       
+               goto again;
+       }
+
+       /* Call the function */
+
+       if (cmd_entry->fn) {
+
+               if (mem_ctx == NULL) {
+                       /* Create mem_ctx */
+                       if (!(mem_ctx = talloc_init("do_cmd"))) {
+                               DEBUG(0, ("talloc_init() failed\n"));
+                               goto done;
+                       }
+               }
+
+               /* Run command */
+               result = cmd_entry->fn(vfs, mem_ctx, argc, argv);
+
+       } else {
+               fprintf (stderr, "Invalid command\n");
+               goto done;
+       }
+
+ done:
+                                               
+       /* Cleanup */
+
+       if (argv) {
+               for (i = 0; i < argc; i++)
+                       SAFE_FREE(argv[i]);
+       
+               SAFE_FREE(argv);
+       }
+       
+       return result;
+}
+
+/* Process a command entered at the prompt or as part of -c */
+static NTSTATUS process_cmd(struct vfs_state *vfs, char *cmd)
+{
+       struct cmd_list *temp_list;
+       BOOL found = False;
+       pstring buf;
+       char *p = cmd;
+       NTSTATUS result = NT_STATUS_OK;
+       int len = 0;
+
+       if (cmd[strlen(cmd) - 1] == '\n')
+               cmd[strlen(cmd) - 1] = '\0';
+
+       if (!next_token(&p, buf, " ", sizeof(buf))) {
+               return NT_STATUS_OK;
+       }
+
+       /* strip the trainly \n if it exsists */
+       len = strlen(buf);
+       if (buf[len-1] == '\n')
+               buf[len-1] = '\0';
+
+       /* Search for matching commands */
+
+       for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+               struct cmd_set *temp_set = temp_list->cmd_set;
+
+               while(temp_set->name) {
+                       if (strequal(buf, temp_set->name)) {
+                               found = True;
+                               result = do_cmd(vfs, temp_set, cmd);
+
+                               goto done;
+                       }
+                       temp_set++;
+               }
+       }
+
+ done:
+       if (!found && buf[0]) {
+               printf("command not found: %s\n", buf);
+               return NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(result)) {
+               printf("result was %s\n", nt_errstr(result));
+       }
+
+       return result;
+}
+
+static void process_file(struct vfs_state *pvfs, char *filename) {
+       FILE *file;
+       char command[3 * PATH_MAX];
+
+       if (*filename == '-') {
+               file = stdin;
+       } else {
+               file = fopen(filename, "r");
+               if (file == NULL) {
+                       printf("vfstest: error reading file (%s)!", filename);
+                       printf("errno n.%d: %s", errno, strerror(errno));
+                       exit(-1);
+               }
+       }
+
+       while (fgets(command, 3 * PATH_MAX, file) != NULL) {
+               process_cmd(pvfs, command);
+       }
+}
+
+void exit_server(const char *reason)
+{
+       DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+       exit(0);
+}
+
+static int server_fd = -1;
+int last_message = -1;
+
+int smbd_server_fd(void)
+{
+               return server_fd;
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+
+BOOL reload_services(BOOL test)
+{
+       BOOL ret;
+       
+       if (lp_loaded()) {
+               pstring fname;
+               pstrcpy(fname,lp_configfile());
+               if (file_exist(fname, NULL) &&
+                   !strcsequal(fname, dyn_CONFIGFILE)) {
+                       pstrcpy(dyn_CONFIGFILE, fname);
+                       test = False;
+               }
+       }
+
+       reopen_logs();
+
+       if (test && !lp_file_list_changed())
+               return(True);
+
+       lp_killunused(conn_snum_used);
+       
+       ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+       load_printers();
+
+       /* perhaps the config filename is now set */
+       if (!test)
+               reload_services(True);
+
+       reopen_logs();
+
+       load_interfaces();
+
+       if (smbd_server_fd() != -1) {      
+               set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+               set_socket_options(smbd_server_fd(), lp_socket_options());
+       }
+
+       mangle_reset_cache();
+       reset_stat_cache();
+
+       /* this forces service parameters to be flushed */
+       set_current_service(NULL,True);
+
+       return (ret);
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+       BOOL                    interactive = True;
+       int                     opt;
+       static char             *cmdstr = "";
+       static char             *opt_logfile=NULL;
+       static int              opt_debuglevel;
+       pstring                 logfile;
+       struct cmd_set          **cmd_set;
+       extern BOOL             AllowDebugChange;
+       static struct vfs_state vfs;
+       int i;
+       static const char       *filename = "";
+
+       /* make sure the vars that get altered (4th field) are in
+          a fixed location or certain compilers complain */
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               {"file",        'f', POPT_ARG_STRING,   &filename, 0, },
+               {"command",     'c', POPT_ARG_STRING,   &cmdstr, 0, "Execute specified list of commands" },
+               {"logfile",     'l', POPT_ARG_STRING,   &opt_logfile, 'l', "Write output to specified logfile" },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { 0, 0, 0, 0}
+       };
+
+
+       setlinebuf(stdout);
+
+       DEBUGLEVEL = 1;
+       AllowDebugChange = False;
+
+       pc = poptGetContext("vfstest", argc, (const char **) argv,
+                           long_options, 0);
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'l':
+                       slprintf(logfile, sizeof(logfile) - 1, "%s.client", 
+                                opt_logfile);
+                       lp_set_logfile(logfile);
+                       interactive = False;
+                       break;
+                       
+               case 'd':
+                       DEBUGLEVEL = opt_debuglevel;
+                       break;
+               }
+       }
+
+
+       poptFreeContext(pc);
+
+       /* TODO: check output */
+       reload_services(False);
+
+       /* the following functions are part of the Samba debugging
+          facilities.  See lib/debug.c */
+       setup_logging("vfstest", interactive);
+       if (!interactive) 
+               reopen_logs();
+       
+       /* Load command lists */
+
+       cmd_set = vfstest_command_list;
+
+       while(*cmd_set) {
+               add_command_set(*cmd_set);
+               add_command_set(separator_command);
+               cmd_set++;
+       }
+
+       /* some basic initialization stuff */
+       conn_init();
+       vfs.conn = conn_new();
+       vfs.conn->user = "vfstest";
+       for (i=0; i < 1024; i++)
+               vfs.files[i] = NULL;
+
+       /* some advanced initiliazation stuff */
+       smbd_vfs_init(vfs.conn);
+
+       /* Do we have a file input? */
+       if (filename[0]) {
+               process_file(&vfs, filename);
+               return 0;
+       }
+
+       /* Do anything specified with -c */
+       if (cmdstr[0]) {
+               char    *cmd;
+               char    *p = cmdstr;
+               while((cmd=next_command(&p)) != NULL) {
+                       process_cmd(&vfs, cmd);
+               }
+               
+               return 0;
+       }
+
+       /* Loop around accepting commands */
+
+       while(1) {
+               pstring prompt;
+               char *line;
+
+               slprintf(prompt, sizeof(prompt) - 1, "vfstest $> ");
+
+               line = smb_readline(prompt, NULL, completion_fn);
+
+               if (line == NULL)
+                       break;
+
+               if (line[0] != '\n')
+                       process_cmd(&vfs, line);
+       }
+       
+       free(vfs.conn);
+       return 0;
+}
diff --git a/source4/torture/vfstest.h b/source4/torture/vfstest.h
new file mode 100644 (file)
index 0000000..5910c5c
--- /dev/null
@@ -0,0 +1,45 @@
+/* 
+   Unix SMB/CIFS implementation.
+   VFS module tester
+
+   Copyright (C) Simo Sorce 2002
+   Copyright (C) Eric Lorimer 2002
+
+   Most of this code was ripped off of rpcclient.
+   Copyright (C) Tim Potter 2000-2001
+
+   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.
+*/
+
+struct func_entry {
+       char *name;
+       int (*fn)(struct connection_struct *conn, const char *path);
+};
+
+struct vfs_state {
+       struct connection_struct *conn;
+       struct files_struct *files[1024];
+       DIR *currentdir;
+       void *data;
+       size_t data_size;
+};
+
+struct cmd_set {
+       const char *name;
+       NTSTATUS (*fn)(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, 
+                       char **argv);
+       const char *description;
+       const char *usage;
+};
diff --git a/source4/ubiqx/.cvsignore b/source4/ubiqx/.cvsignore
new file mode 100644 (file)
index 0000000..07da222
--- /dev/null
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source4/utils/.cvsignore b/source4/utils/.cvsignore
new file mode 100644 (file)
index 0000000..6b8749f
--- /dev/null
@@ -0,0 +1 @@
+net_proto.h
\ No newline at end of file
diff --git a/source4/utils/debug2html.c b/source4/utils/debug2html.c
new file mode 100644 (file)
index 0000000..f9a1f43
--- /dev/null
@@ -0,0 +1,253 @@
+/* ========================================================================== **
+ *                                debug2html.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * Parse Samba debug logs (2.0 & greater) and output the results as HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ *  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.
+ *
+ * -------------------------------------------------------------------------- **
+ * This program provides an example of the use of debugparse.c, and also
+ * does a decent job of converting Samba logs into HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * Revision 1.4  1998/11/13 03:37:01  tridge
+ * fixes for OSF1 compilation
+ *
+ * Revision 1.3  1998/10/28 20:33:35  crh
+ * I've moved the debugparse module files into the ubiqx directory because I
+ * know that 'make proto' will ignore them there.  The debugparse.h header
+ * file is included in includes.h, and includes.h is included in debugparse.c,
+ * so all of the pieces "see" each other.  I've compiled and tested this,
+ * and it does seem to work.  It's the same compromise model I used when
+ * adding the ubiqx modules into the system, which is why I put it all into
+ * the same directory.
+ *
+ * Chris -)-----
+ *
+ * Revision 1.1  1998/10/26 23:21:37  crh
+ * Here is the simple debug parser and the debug2html converter.  Still to do:
+ *
+ *   * Debug message filtering.
+ *   * I need to add all this to Makefile.in
+ *     (If it looks at all strange I'll ask for help.)
+ *
+ * If you want to compile debug2html, you'll need to do it by hand until I
+ * make the changes to Makefile.in.  Sorry.
+ *
+ * Chris -)-----
+ *
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * The size of the read buffer.
+ */
+
+#define DBG_BSIZE 1024
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static dbg_Token modechange( dbg_Token new, dbg_Token mode )
+  /* ------------------------------------------------------------------------ **
+   * Handle a switch between header and message printing.
+   *
+   *  Input:  new   - The token value of the current token.  This indicates
+   *                  the lexical item currently being recognized.
+   *          mode  - The current mode.  This is either dbg_null or
+   *                  dbg_message.  It could really be any toggle
+   *                  (true/false, etc.)
+   *
+   *  Output: The new mode.  This will be the same as the input mode unless
+   *          there was a transition in or out of message processing.
+   *
+   *  Notes:  The purpose of the mode value is to mark the beginning and end
+   *          of the message text block.  In order to show the text in its
+   *          correct format, it must be included within a <PRE></PRE> block.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  switch( new )
+    {
+    case dbg_null:
+    case dbg_ignore:
+      return( mode );
+    case dbg_message:
+      if( dbg_message != mode )
+        {
+        /* Switching to message mode. */
+        (void)printf( "<PRE>\n" );
+        return( dbg_message );
+        }
+      break;
+    default:
+      if( dbg_message == mode )
+        {
+        /* Switching out of message mode. */
+        (void)printf( "</PRE>\n\n" );
+        return( dbg_null );
+        }
+    }
+
+  return( mode );
+  } /* modechange */
+
+static void newblock( dbg_Token old, dbg_Token new )
+  /* ------------------------------------------------------------------------ **
+   * Handle the transition between tokens.
+   *
+   *  Input:  old - The previous token.
+   *          new - The current token.
+   *
+   *  Output: none.
+   *
+   *  Notes:  This is called whenever there is a transition from one token
+   *          type to another.  It first prints the markup tags that close
+   *          the previous token, and then the markup tags for the new
+   *          token.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  switch( old )
+    {
+    case dbg_timestamp:
+      (void)printf( ",</B>" );
+      break;
+    case dbg_level:
+      (void)printf( "</FONT>]</B>\n   " );
+      break;
+    case dbg_sourcefile:
+      (void)printf( ":" );
+      break;
+    case dbg_lineno:
+      (void)printf( ")" );
+      break;
+    }
+
+  switch( new )
+    {
+    case dbg_timestamp:
+      (void)printf( "<B>[" );
+      break;
+    case dbg_level:
+      (void)printf( " <B><FONT COLOR=MAROON>" );
+      break;
+    case dbg_lineno:
+      (void)printf( "(" );
+      break;
+    }
+  } /* newblock */
+
+static void charprint( dbg_Token tok, int c )
+  /* ------------------------------------------------------------------------ **
+   * Filter the input characters to determine what goes to output.
+   *
+   *  Input:  tok - The token value of the current character.
+   *          c   - The current character.
+   *
+   *  Output: none.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  switch( tok )
+    {
+    case dbg_ignore:
+    case dbg_header:
+      break;
+    case dbg_null:
+    case dbg_eof:
+      (void)putchar( '\n' );
+      break;
+    default:
+      switch( c )
+        {
+        case '<':
+          (void)printf( "&lt;" );
+          break;
+        case '>':
+          (void)printf( "&gt;" );
+          break;
+        case '&':
+          (void)printf( "&amp;" );
+          break;
+        case '\"':
+          (void)printf( "&#34;" );
+          break;
+        default:
+          (void)putchar( c );
+          break;
+        }
+    }
+  } /* charprint */
+
+int main( int argc, char *argv[] )
+  /* ------------------------------------------------------------------------ **
+   * This simple program scans and parses Samba debug logs, and produces HTML
+   * output.
+   *
+   *  Input:  argc  - Currently ignored.
+   *          argv  - Currently ignored.
+   *
+   *  Output: Always zero.
+   *
+   *  Notes:  The HTML output is sent to stdout.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  int       i;
+  int       len;
+  char      bufr[DBG_BSIZE];
+  dbg_Token old   = dbg_null,
+            new   = dbg_null,
+            state = dbg_null,
+            mode  = dbg_null;
+
+  (void)printf( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" );
+  (void)printf( "<HTML>\n<HEAD>\n" );
+  (void)printf( "  <TITLE>Samba Debug Output</TITLE>\n</HEAD>\n\n<BODY>\n" );
+
+  while( (!feof( stdin ))
+      && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) )
+    {
+    for( i = 0; i < len; i++ )
+      {
+      old = new;
+      new = dbg_char2token( &state, bufr[i] );
+      if( new != old )
+        {
+        mode = modechange( new, mode );
+        newblock( old, new );
+        }
+      charprint( new, bufr[i] );
+      }
+    }
+  (void)modechange( dbg_eof, mode );
+
+  (void)printf( "</BODY>\n</HTML>\n" );
+  return( 0 );
+  } /* main */
diff --git a/source4/utils/editreg.c b/source4/utils/editreg.c
new file mode 100644 (file)
index 0000000..2cf8e2c
--- /dev/null
@@ -0,0 +1,2069 @@
+/* 
+   Samba Unix/Linux SMB client utility editreg.c 
+   Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+
+   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.  */
+/*************************************************************************
+                                                       
+ A utility to edit a Windows NT/2K etc registry file.
+                                     
+ Many of the ideas in here come from other people and software. 
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from 
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. 
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset            Size      Contents
+0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
+0x00000004      D-Word      ???? //see struct REGF
+0x00000008      D-Word      ???? Always the same value as at 0x00000004
+0x0000000C      Q-Word      last modify date in WinNT date-format
+0x00000014      D-Word      1
+0x00000018      D-Word      3
+0x0000001C      D-Word      0
+0x00000020      D-Word      1
+0x00000024      D-Word      Offset of 1st key record
+0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
+0x0000002C      D-Word      1
+0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
+0x000001FB  //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset      Size      Contents
+0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
+0x0004      D-Word      Offset from the 1st hbin-Block
+0x0008      D-Word      Offset to the next hbin-Block
+0x001C      D-Word      Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset      Size      Contents
+0x0000      D-Word      Data-block size    //this size must be a
+multiple of 8. Nigel
+0x0004      ????      Data
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative! (Richard Sharpe) 
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+      The nk-record can be treated as a kombination of tree-record and
+      key-record of the win 95 registry.
+
+lf-Record
+
+      The lf-record is the counterpart to the RGKN-record (the
+      hash-function)
+
+vk-Record
+
+      The vk-record consists information to a single value.
+
+sk-Record
+
+      sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+      The value-lists contain information about which values are inside a
+      sub-key and don't have a header.
+
+Datas
+
+      The datas of the registry are (like the value-list) stored without a
+      header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"nk" = 0x6B6E
+0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
+0x0004      Q-Word      write-date/time in windows nt notation
+0x0010      D-Word      Offset of Owner/Parent key
+0x0014      D-Word      number of sub-Keys
+0x001C      D-Word      Offset of the sub-key lf-Records
+0x0024      D-Word      number of values
+0x0028      D-Word      Offset of the Value-List
+0x002C      D-Word      Offset of the sk-Record
+
+0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
+0x0048      Word      name-length
+0x004A      Word      class-name length
+0x004C      ????      key-name
+
+the Value-List
+==============
+Offset      Size      Contents
+0x0000      D-Word      Offset 1st Value
+0x0004      D-Word      Offset 2nd Value
+0x????      D-Word      Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"vk" = 0x6B76
+0x0002      Word      name length
+0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
+0x0008      D-Word      Offset of Data
+0x000C      D-Word      Type of value
+0x0010      Word      Flag
+0x0012      Word      Unused (data-trash)
+0x0014      ????      Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert      Beteutung
+0x0001      RegSZ:             character string (in UNICODE!)
+0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
+0x0003      RegBin:           raw-binary value
+0x0004      RegDWord:   Dword
+0x0007      RegMultiSZ:      multiple strings, seperated with 0
+                  (UNICODE!)
+
+The "lf"-record
+===============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"lf" = 0x666C
+0x0002      Word      number of keys
+0x0004      ????      Hash-Records
+
+Hash-Record
+===========
+Offset      Size      Contents
+0x0000      D-Word      Offset of corresponding "nk"-Record
+0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a security descriptor in the data. R Sharpe.) 
+
+
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"sk" = 0x6B73
+0x0002      Word      Unused
+0x0004      D-Word      Offset of previous "sk"-Record
+0x0008      D-Word      Offset of next "sk"-Record
+0x000C      D-Word      usage-counter
+0x0010      D-Word      Size of "sk"-record in bytes
+????                                             //standard self
+relative security desciptor. Nigel
+????  ????      Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+                      +----->+------------+
+| first sub |   |                      |      | Usage cnt. |
+| name      |   |  +-->+------------+  |      | length     |
+| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
++-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
+   +------------+  |   | value rec. |-------->+------------+        +-------+
+   v               |   +------------+         | Usage cnt. |
++-----------+      |                          | length     |
+| next rec. |      |                          | text       |------->+-------+
+| first sub |------+                          +------------+        | xxxxx |
+| name      |                                                       +-------+
+| value     |
++-----------+    
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+   
++---------------------------------------------------------------+
+|                                                               |
+v                                                               |
++---------+     +---------->+-----------+  +----->+---------+   |
+| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
+| ID      |     |           | # of keys |  |      | parent  |---+
+| Date    |     |           | 1st key   |--+      | ....    |
+| parent  |     |           +-----------+         +---------+
+| suk-keys|-----+
+| values  |--------------------->+----------+
+| SK-rec. |---------------+      | 1. value |--> +----------+
+| class   |--+            |      +----------+    | vk-rec.  |
++---------+  |            |                      | ....     |
+             v            |                      | data     |--> +-------+
+      +------------+      |                      +----------+    | xxxxx |
+      | Class name |      |                                      +-------+
+      +------------+      |
+                          v
+          +---------+    +---------+
+   +----->| next sk |--->| Next sk |--+
+   |  +---| prev sk |<---| prev sk |  |
+   |  |   | ....    |    | ...     |  |
+   |  |   +---------+    +---------+  |
+   |  |                    ^          |
+   |  |                    |          |
+   |  +--------------------+          |
+   +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps....  (Although it was "fun" for me to uncover this things,
+                  it took me several sleepless nights ;)
+
+            B.D.
+
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+
+static int verbose = 0;
+
+/* 
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+/*
+ * DateTime struct for Windows
+ */
+
+typedef struct date_time_s {
+  unsigned int low, high;
+} NTTIME;
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY  2
+#define REG_SYM_LINK 3
+
+typedef struct reg_key_s {
+  char *name;         /* Name of the key                    */
+  char *class_name;
+  int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
+  NTTIME last_mod; /* Time last modified                 */
+  struct reg_key_s *owner;
+  struct key_list_s *sub_keys;
+  struct val_list_s *values;
+  struct key_sec_desc_s *security;
+} REG_KEY;
+
+/*
+ * The KEY_LIST struct lists sub-keys.
+ */
+
+typedef struct key_list_s {
+  int key_count;
+  REG_KEY *keys[1];
+} KEY_LIST;
+
+typedef struct val_key_s {
+  char *name;
+  int has_name;
+  int data_type;
+  int data_len;
+  void *data_blk;    /* Might want a separate block */
+} VAL_KEY;
+
+typedef struct val_list_s {
+  int val_count;
+  VAL_KEY *vals[1];
+} VAL_LIST;
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15
+#endif
+
+typedef struct dom_sid_s {
+  unsigned char ver, auths;
+  unsigned char auth[6];
+  unsigned int sub_auths[MAXSUBAUTHS];
+} DOM_SID;
+
+typedef struct ace_struct_s {
+  unsigned char type, flags;
+  unsigned int perms;   /* Perhaps a better def is in order */
+  DOM_SID *trustee;
+} ACE; 
+
+typedef struct acl_struct_s {
+  unsigned short rev, refcnt;
+  unsigned short num_aces;
+  ACE *aces[1];
+} ACL;
+
+typedef struct sec_desc_s {
+  unsigned int rev, type;
+  DOM_SID *owner, *group;
+  ACL *sacl, *dacl;
+} SEC_DESC;
+
+#define SEC_DESC_NON 0
+#define SEC_DESC_RES 1
+#define SEC_DESC_OCU 2
+
+typedef struct key_sec_desc_s {
+  struct key_sec_desc_s *prev, *next;
+  int ref_cnt;
+  int state;
+  SEC_DESC *sec_desc;
+} KEY_SEC_DESC; 
+
+
+/*
+ * An API for accessing/creating/destroying items above
+ */
+
+/*
+ * Iterate over the keys, depth first, calling a function for each key
+ * and indicating if it is terminal or non-terminal and if it has values.
+ *
+ * In addition, for each value in the list, call a value list function
+ */
+
+/*
+ * There should eventually be one to deal with security keys as well
+ */
+
+typedef int (*key_print_f)(const char *path, char *key_name, char *class_name, 
+                          int root, int terminal, int values);
+
+typedef int (*val_print_f)(const char *path, char *val_name, int val_type, 
+                          int data_len, void *data_blk, int terminal,
+                          int first, int last);
+
+typedef int (*sec_print_f)(SEC_DESC *sec_desc);
+
+typedef struct regf_struct_s REGF;
+
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path, 
+                   key_print_f key_print, sec_print_f sec_print,
+                   val_print_f val_print);
+
+int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
+                        int terminal, val_print_f val_print)
+{
+  int i;
+
+  if (!val_list) return 1;
+
+  if (!val_print) return 1;
+
+  for (i=0; i<val_list->val_count; i++) {
+    if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
+                  val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
+                  terminal,
+                  (i == 0),
+                  (i == val_list->val_count))) {
+
+      return 0;
+
+    }
+  }
+
+  return 1;
+}
+
+int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, 
+                        const char *path,
+                        key_print_f key_print, sec_print_f sec_print, 
+                        val_print_f val_print)
+{
+  int i;
+
+  if (!key_list) return 1;
+
+  for (i=0; i< key_list->key_count; i++) {
+    if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print, 
+                        sec_print, val_print)) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+                   key_print_f key_print, sec_print_f sec_print,
+                   val_print_f val_print)
+{
+  int path_len = strlen(path);
+  char *new_path;
+
+  if (!regf || !key_tree)
+    return -1;
+
+  /* List the key first, then the values, then the sub-keys */
+
+  if (key_print) {
+
+    if (!(*key_print)(path, key_tree->name, 
+                     key_tree->class_name, 
+                     (key_tree->type == REG_ROOT_KEY),
+                     (key_tree->sub_keys == NULL),
+                     (key_tree->values?(key_tree->values->val_count):0)))
+      return 0;
+  }
+
+  /*
+   * If we have a security print routine, call it
+   * If the security print routine returns false, stop.
+   */
+  if (sec_print) {
+    if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
+      return 0;
+  }
+
+  new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
+  if (!new_path) return 0; /* Errors? */
+  new_path[0] = '\0';
+  strcat(new_path, path);
+  strcat(new_path, "\\");
+  strcat(new_path, key_tree->name);
+
+  /*
+   * Now, iterate through the values in the val_list 
+   */
+
+  if (key_tree->values &&
+      !nt_val_list_iterator(regf, key_tree->values, bf, new_path, 
+                           (key_tree->values!=NULL),
+                           val_print)) {
+
+    free(new_path);
+    return 0;
+  } 
+
+  /* 
+   * Now, iterate through the keys in the key list
+   */
+
+  if (key_tree->sub_keys && 
+      !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print, 
+                           sec_print, val_print)) {
+    free(new_path);
+    return 0;
+  } 
+
+  free(new_path);
+  return 1;
+}
+
+/* Make, delete keys */
+
+int nt_delete_val_key(VAL_KEY *val_key)
+{
+
+  if (val_key) {
+    if (val_key->data_blk) free(val_key->data_blk);
+    free(val_key);
+  };
+  return 1;
+}
+
+int nt_delete_val_list(VAL_LIST *vl)
+{
+  int i;
+
+  if (vl) {
+    for (i=0; i<vl->val_count; i++)
+      nt_delete_val_key(vl->vals[i]);
+    free(vl);
+  }
+  return 1;
+}
+
+int nt_delete_reg_key(REG_KEY *key);
+int nt_delete_key_list(KEY_LIST *key_list)
+{
+  int i;
+
+  if (key_list) {
+    for (i=0; i<key_list->key_count; i++) 
+      nt_delete_reg_key(key_list->keys[i]);
+    free(key_list);
+  }
+  return 1;
+}
+
+int nt_delete_sid(DOM_SID *sid)
+{
+
+  if (sid) free(sid);
+  return 1;
+
+}
+
+int nt_delete_ace(ACE *ace)
+{
+
+  if (ace) {
+    nt_delete_sid(ace->trustee);
+    free(ace);
+  }
+  return 1;
+
+}
+
+int nt_delete_acl(ACL *acl)
+{
+
+  if (acl) {
+    int i;
+
+    for (i=0; i<acl->num_aces; i++)
+      nt_delete_ace(acl->aces[i]);
+
+    free(acl);
+  }
+  return 1;
+}
+
+int nt_delete_sec_desc(SEC_DESC *sec_desc)
+{
+
+  if (sec_desc) {
+
+    nt_delete_sid(sec_desc->owner);
+    nt_delete_sid(sec_desc->group);
+    nt_delete_acl(sec_desc->sacl);
+    nt_delete_acl(sec_desc->dacl);
+    free(sec_desc);
+
+  }
+  return 1;
+}
+
+int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
+{
+
+  if (key_sec_desc) {
+    key_sec_desc->ref_cnt--;
+    if (key_sec_desc->ref_cnt<=0) {
+      /*
+       * There should always be a next and prev, even if they point to us 
+       */
+      key_sec_desc->next->prev = key_sec_desc->prev;
+      key_sec_desc->prev->next = key_sec_desc->next;
+      nt_delete_sec_desc(key_sec_desc->sec_desc);
+    }
+  }
+  return 1;
+}
+
+int nt_delete_reg_key(REG_KEY *key)
+{
+
+  if (key) {
+    if (key->name) free(key->name);
+    if (key->class_name) free(key->class_name);
+
+    /*
+     * Do not delete the owner ...
+     */
+
+    if (key->sub_keys) nt_delete_key_list(key->sub_keys);
+    if (key->values) nt_delete_val_list(key->values);
+    if (key->security) nt_delete_key_sec_desc(key->security);
+    free(key);
+  }
+  return 1;
+}
+
+/* 
+ * Create/delete key lists and add delete keys to/from a list, count the keys 
+ */
+
+
+/*
+ * Create/delete value lists, add/delete values, count them
+ */
+
+
+/*
+ * Create/delete security descriptors, add/delete SIDS, count SIDS, etc.
+ * We reference count the security descriptors. Any new reference increments 
+ * the ref count. If we modify an SD, we copy the old one, dec the ref count
+ * and make the change. We also want to be able to check for equality so
+ * we can reduce the number of SDs in use.
+ */
+
+/*
+ * Code to parse registry specification from command line or files
+ *
+ * Format:
+ * [cmd:]key:type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ */
+
+
+/*
+ * Load and unload a registry file.
+ *
+ * Load, loads it into memory as a tree, while unload sealizes/flattens it
+ */
+
+/*
+ * Get the starting record for NT Registry file 
+ */
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+  int sk_off;
+  KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+/* 
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc 
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the 
+ * appropriate free statements below.
+ */
+
+#define REGF_REGTYPE_NONE 0
+#define REGF_REGTYPE_NT   1
+#define REGF_REGTYPE_W9X  2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+                              (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000 
+
+struct regf_struct_s {
+  int reg_type;
+  char *regfile_name, *outfile_name;
+  int fd;
+  struct stat sbuf;
+  char *base;
+  int modified;
+  NTTIME last_mod_time;
+  REG_KEY *root;  /* Root of the tree for this file */
+  int sk_count, sk_map_size;
+  SK_MAP *sk_map;
+};
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+#define IVAL(buf) ((unsigned int) \
+                   (unsigned int)*((unsigned char *)(buf)+3)<<24| \
+                   (unsigned int)*((unsigned char *)(buf)+2)<<16| \
+                   (unsigned int)*((unsigned char *)(buf)+1)<<8| \
+                   (unsigned int)*((unsigned char *)(buf)+0)) 
+
+#define SVAL(buf) ((unsigned short) \
+                   (unsigned short)*((unsigned char *)(buf)+1)<<8| \
+                   (unsigned short)*((unsigned char *)(buf)+0)) 
+
+#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) 
+#define LOCN(base, f) ((base) + OFF(f))
+
+/* 
+ * All of the structures below actually have a four-byte lenght before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+  DWORD REGF_ID;     /* regf */
+  DWORD uk1;
+  DWORD uk2;
+  DWORD tim1, tim2;
+  DWORD uk3;             /* 1 */
+  DWORD uk4;             /* 3 */
+  DWORD uk5;             /* 0 */
+  DWORD uk6;             /* 1 */
+  DWORD first_key;       /* offset */
+  unsigned int dblk_size;
+  DWORD uk7[116];        /* 1 */
+  DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+  DWORD dblocksize;
+  char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+  DWORD HBIN_ID; /* hbin */
+  DWORD next_off;
+  DWORD prev_off;
+  DWORD uk1;
+  DWORD uk2;
+  DWORD uk3;
+  DWORD uk4;
+  DWORD blk_size;
+  HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+  WORD NK_ID;
+  WORD type;
+  DWORD t1, t2;
+  DWORD uk1;
+  DWORD own_off;
+  DWORD subk_num;
+  DWORD uk2;
+  DWORD lf_off;
+  DWORD uk3;
+  DWORD val_cnt;
+  DWORD val_off;
+  DWORD sk_off;
+  DWORD clsnam_off;
+  DWORD unk4[4];
+  DWORD unk5;
+  WORD nam_len;
+  WORD clsnam_len;
+  char key_nam[1];  /* Actual length determined by nam_len */
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+typedef struct sk_struct {
+  WORD SK_ID;
+  WORD uk1;
+  DWORD prev_off;
+  DWORD next_off;
+  DWORD ref_cnt;
+  DWORD rec_size;
+  char sec_desc[1];
+} SK_HDR;
+
+typedef struct ace_struct {
+    unsigned char type;
+    unsigned char flags;
+    unsigned short length;
+    unsigned int perms;
+    DOM_SID trustee;
+} REG_ACE;
+
+typedef struct acl_struct {
+  WORD rev;
+  WORD size;
+  DWORD num_aces;
+  REG_ACE *aces;   /* One or more ACEs */
+} REG_ACL;
+
+typedef struct sec_desc_rec {
+  WORD rev;
+  WORD type;
+  DWORD owner_off;
+  DWORD group_off;
+  DWORD sacl_off;
+  DWORD dacl_off;
+} REG_SEC_DESC;
+
+typedef struct hash_struct {
+  DWORD nk_off;
+  char hash[4];
+} HASH_REC;
+
+#define REG_LF_ID 0x666C
+
+typedef struct lf_struct {
+  WORD LF_ID;
+  WORD key_count;
+  struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
+} LF_HDR;
+
+typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
+
+#define REG_VK_ID 0x6B76
+
+typedef struct vk_struct {
+  WORD VK_ID;
+  WORD nam_len;
+  DWORD dat_len;    /* If top-bit set, offset contains the data */
+  DWORD dat_off;   
+  DWORD dat_type;
+  WORD flag;        /* =1, has name, else no name (=Default). */
+  WORD unk1;
+  char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+#define REG_TYPE_REGSZ     1
+#define REG_TYPE_EXPANDSZ  2
+#define REG_TYPE_BIN       3  
+#define REG_TYPE_DWORD     4
+#define REG_TYPE_MULTISZ   7
+
+typedef struct _val_str { 
+  unsigned int val;
+  const char * str;
+} VAL_STR;
+
+const VAL_STR reg_type_names[] = {
+   { 1, "REG_SZ" },
+   { 2, "REG_EXPAND_SZ" },
+   { 3, "REG_BIN" },
+   { 4, "REG_DWORD" },
+   { 7, "REG_MULTI_SZ" },
+   { 0, NULL },
+};
+
+const char *val_to_str(unsigned int val, const VAL_STR *val_array)
+{
+  int i = 0;
+
+  if (!val_array) return NULL;
+
+  while (val_array[i].val && val_array[i].str) {
+
+    if (val_array[i].val == val) return val_array[i].str;
+    i++;
+
+  }
+
+  return NULL;
+
+}
+
+/*
+ * Convert from UniCode to Ascii ... Does not take into account other lang
+ * Restrict by ascii_max if > 0
+ */
+int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max, 
+                int uni_max)
+{
+  int i = 0; 
+
+  while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
+    if (uni_max > 0 && (i*2) >= uni_max) break;
+    ascii[i] = uni[i*2];
+    i++;
+
+  }
+
+  ascii[i] = '\0';
+
+  return i;
+}
+
+/*
+ * Convert a data value to a string for display
+ */
+int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
+{ 
+  unsigned char *asciip;
+  int i;
+
+  switch (type) {
+  case REG_TYPE_REGSZ:
+    fprintf(stderr, "Len: %d\n", len);
+    return uni_to_ascii(datap, ascii, len, ascii_max);
+    break;
+
+  case REG_TYPE_EXPANDSZ:
+    return uni_to_ascii(datap, ascii, len, ascii_max);
+    break;
+
+  case REG_TYPE_BIN:
+    asciip = ascii;
+    for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) { 
+      int str_rem = ascii_max - ((int)asciip - (int)ascii);
+      asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
+      if (i < len && str_rem > 0)
+       *asciip = ' '; asciip++;        
+    }
+    *asciip = '\0';
+    return ((int)asciip - (int)ascii);
+    break;
+
+  case REG_TYPE_DWORD:
+    if (*(int *)datap == 0)
+      return snprintf(ascii, ascii_max, "0");
+    else
+      return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
+    break;
+
+  case REG_TYPE_MULTISZ:
+
+    break;
+
+  default:
+    return 0;
+    break;
+  } 
+
+  return len;
+
+}
+
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size);
+
+int nt_set_regf_input_file(REGF *regf, char *filename)
+{
+  return ((regf->regfile_name = strdup(filename)) != NULL); 
+}
+
+int nt_set_regf_output_file(REGF *regf, char *filename)
+{
+  return ((regf->outfile_name = strdup(filename)) != NULL); 
+}
+
+/* Create a regf structure and init it */
+
+REGF *nt_create_regf(void)
+{
+  REGF *tmp = (REGF *)malloc(sizeof(REGF));
+  if (!tmp) return tmp;
+  bzero(tmp, sizeof(REGF));
+  return tmp;
+} 
+
+/* Free all the bits and pieces ... Assumes regf was malloc'd */
+/* If you add stuff to REGF, add the relevant free bits here  */
+int nt_free_regf(REGF *regf)
+{
+  if (!regf) return 0;
+
+  if (regf->regfile_name) free(regf->regfile_name);
+  if (regf->outfile_name) free(regf->outfile_name);
+
+  /* Free the mmap'd area */
+
+  if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+  regf->base = NULL;
+  close(regf->fd);    /* Ignore the error :-) */
+
+  nt_delete_reg_key(regf->root); /* Free the tree */
+  free(regf->sk_map);
+  regf->sk_count = regf->sk_map_size = 0;
+
+  free(regf);
+
+  return 1;
+}
+
+/* Get the header of the registry. Return a pointer to the structure 
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+REGF_HDR *nt_get_regf_hdr(REGF *regf)
+{
+  if (!regf)
+    return NULL; /* What about errors */
+
+  if (!regf->regfile_name)
+    return NULL; /* What about errors */
+
+  if (!regf->base) { /* Try to mmap etc the file */
+
+    if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
+      return NULL; /* What about errors? */
+    }
+
+    if (fstat(regf->fd, &regf->sbuf) < 0) {
+      return NULL;
+    }
+
+    regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+    if ((int)regf->base == 1) {
+      fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
+             strerror(errno));
+      return NULL;
+    }
+  }
+
+  /* 
+   * At this point, regf->base != NULL, and we should be able to read the 
+   * header 
+   */
+
+  assert(regf->base != NULL);
+
+  return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+  if (!regf_hdr) return 0;
+
+  return 1;
+}
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the 
+ * same security descriptor. 
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+
+SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
+{
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+    regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+    if (!regf->sk_map) {
+      free(tmp);
+      return NULL;
+    }
+    regf->sk_map_size = 10;
+    regf->sk_count = 1;
+    (regf->sk_map)[0].sk_off = sk_off;
+    (regf->sk_map)[0].key_sec_desc = tmp;
+  }
+  else { /* Simply allocate a new slot, unless we have to expand the list */ 
+    int ndx = regf->sk_count;
+    if (regf->sk_count >= regf->sk_map_size) {
+      regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
+                                      (regf->sk_map_size + 10)*sizeof(SK_MAP));
+      if (!regf->sk_map) {
+       free(tmp);
+       return NULL;
+      }
+      /*
+       * ndx already points at the first entry of the new block
+       */
+      regf->sk_map_size += 10;
+    }
+    (regf->sk_map)[ndx].sk_off = sk_off;
+    (regf->sk_map)[ndx].key_sec_desc = tmp;
+    regf->sk_count++;
+  }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but dont create one if not
+ * found
+ */
+
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+  int i;
+
+  if (!sk_map) return NULL;
+
+  for (i = 0; i < count; i++) {
+
+    if (sk_map[i].sk_off == sk_off)
+      return sk_map[i].key_sec_desc;
+
+  }
+
+  return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+
+KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
+{
+  KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+  if (tmp) {
+    return tmp;
+  }
+  else { /* Allocate a new one */
+    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+    if (!tmp) {
+      return NULL;
+    }
+    tmp->state = SEC_DESC_RES;
+    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+      return NULL;
+    }
+    return tmp;
+  }
+}
+
+/*
+ * Allocate storage and duplicate a SID 
+ * We could allocate the SID to be only the size needed, but I am too lazy. 
+ */
+DOM_SID *dup_sid(DOM_SID *sid)
+{
+  DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID));
+  int i;
+  
+  if (!tmp) return NULL;
+  tmp->ver = sid->ver;
+  tmp->auths = sid->auths;
+  for (i=0; i<6; i++) {
+    tmp->auth[i] = sid->auth[i];
+  }
+  for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
+    tmp->sub_auths[i] = sid->sub_auths[i];
+  }
+  return tmp;
+}
+
+/*
+ * Allocate space for an ACE and duplicate the registry encoded one passed in
+ */
+ACE *dup_ace(REG_ACE *ace)
+{
+  ACE *tmp = NULL; 
+
+  tmp = (ACE *)malloc(sizeof(ACE));
+
+  if (!tmp) return NULL;
+
+  tmp->type = CVAL(&ace->type);
+  tmp->flags = CVAL(&ace->flags);
+  tmp->perms = IVAL(&ace->perms);
+  tmp->trustee = dup_sid(&ace->trustee);
+  return tmp;
+}
+
+/*
+ * Allocate space for an ACL and duplicate the registry encoded one passed in 
+ */
+ACL *dup_acl(REG_ACL *acl)
+{
+  ACL *tmp = NULL;
+  REG_ACE* ace;
+  int i, num_aces;
+
+  num_aces = IVAL(&acl->num_aces);
+
+  tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
+  if (!tmp) return NULL;
+
+  tmp->num_aces = num_aces;
+  tmp->refcnt = 1;
+  tmp->rev = SVAL(&acl->rev);
+  ace = (REG_ACE *)&acl->aces;
+  for (i=0; i<num_aces; i++) {
+    tmp->aces[i] = dup_ace(ace);
+    ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
+    /* XXX: FIXME, should handle malloc errors */
+  }
+
+  return tmp;
+}
+
+SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
+{
+  SEC_DESC *tmp = NULL;
+  
+  tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+  if (!tmp) {
+    return NULL;
+  }
+  
+  tmp->rev = SVAL(&sec_desc->rev);
+  tmp->type = SVAL(&sec_desc->type);
+  tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
+  if (!tmp->owner) {
+    free(tmp);
+    return NULL;
+  }
+  tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
+  if (!tmp->group) {
+    free(tmp);
+    return NULL;
+  }
+
+  /* Now pick up the SACL and DACL */
+
+  if (sec_desc->sacl_off)
+    tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
+  else
+    tmp->sacl = NULL;
+
+  if (sec_desc->dacl_off)
+    tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
+  else
+    tmp->dacl = NULL;
+
+  return tmp;
+}
+
+KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+  KEY_SEC_DESC *tmp = NULL;
+  int sk_next_off, sk_prev_off, sk_size;
+  REG_SEC_DESC *sec_desc;
+
+  if (!sk_hdr) return NULL;
+
+  if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
+    fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+           regf->regfile_name);
+    return NULL;
+  }
+
+  if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
+    fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
+           -size, sk_size, regf->regfile_name);
+    return NULL;
+  }
+
+  /* 
+   * Now, we need to look up the SK Record in the map, and return it
+   * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+   * use that
+   */
+
+  if (regf->sk_map &&
+      ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+      && (tmp->state == SEC_DESC_OCU)) {
+    tmp->ref_cnt++;
+    return tmp;
+  }
+
+  /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+  assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+  /*
+   * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+   * new KEY_SEC_DESC to the mapping structure, since the offset supplied is 
+   * the actual offset of structure. The same offset will be used by all
+   * all future references to this structure
+   * We chould put all this unpleasantness in a function.
+   */
+
+  if (!tmp) {
+    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+    if (!tmp) return NULL;
+    bzero(tmp, sizeof(KEY_SEC_DESC));
+    
+    /*
+     * Allocate an entry in the SK_MAP ...
+     * We don't need to free tmp, because that is done for us if the
+     * sm_map entry can't be expanded when we need more space in the map.
+     */
+    
+    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+      return NULL;
+    }
+  }
+
+  tmp->ref_cnt++;
+  tmp->state = SEC_DESC_OCU;
+
+  /*
+   * Now, process the actual sec desc and plug the values in
+   */
+
+  sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
+  tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+  /*
+   * Now forward and back links. Here we allocate an entry in the sk_map
+   * if it does not exist, and mark it reserved
+   */
+
+  sk_prev_off = IVAL(&sk_hdr->prev_off);
+  tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+  assert(tmp->prev != NULL);
+  sk_next_off = IVAL(&sk_hdr->next_off);
+  tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+  assert(tmp->next != NULL);
+
+  return tmp;
+}
+
+/*
+ * Process a VK header and return a value
+ */
+VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
+{
+  char val_name[1024];
+  int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+  const char *val_type;
+  VAL_KEY *tmp = NULL; 
+
+  if (!vk_hdr) return NULL;
+
+  if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
+    fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
+           vk_id, (int)vk_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  nam_len = SVAL(&vk_hdr->nam_len);
+  val_name[nam_len] = '\0';
+  flag = SVAL(&vk_hdr->flag);
+  dat_type = IVAL(&vk_hdr->dat_type);
+  dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
+  dat_off = IVAL(&vk_hdr->dat_off);
+
+  tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+  if (!tmp) {
+    goto error;
+  }
+  bzero(tmp, sizeof(VAL_KEY));
+  tmp->has_name = flag;
+  tmp->data_type = dat_type;
+
+  if (flag & 0x01) {
+    strncpy(val_name, vk_hdr->dat_name, nam_len);
+    tmp->name = strdup(val_name);
+    if (!tmp->name) {
+      goto error;
+    }
+  }
+  else
+    strncpy(val_name, "<No Name>", 10);
+
+  /*
+   * Allocate space and copy the data as a BLOB
+   */
+
+  if (dat_len) {
+    
+    char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+    
+    if (!dtmp) {
+      goto error;
+    }
+
+    tmp->data_blk = dtmp;
+
+    if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
+      char *dat_ptr = LOCN(regf->base, dat_off);
+      bcopy(dat_ptr, dtmp, dat_len);
+    }
+    else { /* The data is in the offset */
+      dat_len = dat_len & 0x7FFFFFFF;
+      bcopy(&dat_off, dtmp, dat_len);
+    }
+
+    tmp->data_len = dat_len;
+  }
+
+  val_type = val_to_str(dat_type, reg_type_names);
+
+  /*
+   * We need to save the data area as well
+   */
+
+  if (verbose) fprintf(stdout, "  %s : %s : \n", val_name, val_type);
+
+  return tmp;
+
+ error:
+  /* XXX: FIXME, free the partially allocated struct */
+  return NULL;
+
+}
+
+/*
+ * Process a VL Header and return a list of values
+ */
+VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
+{
+  int i, vk_off;
+  VK_HDR *vk_hdr;
+  VAL_LIST *tmp = NULL;
+
+  if (!vl) return NULL;
+
+  if (-size < (count+1)*sizeof(int)){
+    fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
+    return NULL;
+  }
+
+  tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
+  if (!tmp) {
+    goto error;
+  }
+
+  for (i=0; i<count; i++) {
+    vk_off = IVAL(&vl[i]);
+    vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+    tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
+    if (!tmp->vals[i]){
+      goto error;
+    }
+  }
+
+  tmp->val_count = count;
+
+  return tmp;
+
+ error:
+  /* XXX: FIXME, free the partially allocated structure */
+  return NULL;
+} 
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size)
+{
+  int count, i, nk_off;
+  unsigned int lf_id;
+  KEY_LIST *tmp;
+
+  if (!lf_hdr) return NULL;
+
+  if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
+    fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+           lf_id, (int)lf_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  assert(size < 0);
+
+  count = SVAL(&lf_hdr->key_count);
+
+  if (count <= 0) return NULL;
+
+  /* Now, we should allocate a KEY_LIST struct and fill it in ... */
+
+  tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
+  if (!tmp) {
+    goto error;
+  }
+
+  tmp->key_count = count;
+
+  for (i=0; i<count; i++) {
+    NK_HDR *nk_hdr;
+
+    nk_off = IVAL(&lf_hdr->hr[i].nk_off);
+    nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+    tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr));
+    if (!tmp->keys[i]) {
+      goto error;
+    }
+  }
+
+  return tmp;
+
+ error:
+  /* XXX: FIXME, free the partially allocated structure */
+  return NULL;
+}
+
+/*
+ * This routine is passed a NK_HDR pointer and retrieves the entire tree
+ * from there down. It return a REG_KEY *.
+ */
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size)
+{
+  REG_KEY *tmp = NULL;
+  int name_len, clsname_len, lf_off, val_off, val_count, sk_off;
+  unsigned int nk_id;
+  LF_HDR *lf_hdr;
+  VL_TYPE *vl;
+  SK_HDR *sk_hdr;
+  char key_name[1024], cls_name[1024];
+
+  if (!nk_hdr) return NULL;
+
+  if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
+    fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
+           nk_id, (int)nk_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  assert(size < 0);
+
+  name_len = SVAL(&nk_hdr->nam_len);
+  clsname_len = SVAL(&nk_hdr->clsnam_len);
+
+  /*
+   * The value of -size should be ge 
+   * (sizeof(NK_HDR) - 1 + name_len)
+   * The -1 accounts for the fact that we included the first byte of 
+   * the name in the structure. clsname_len is the length of the thing 
+   * pointed to by clsnam_off
+   */
+
+  if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+    fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
+    fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+           sizeof(NK_HDR), name_len, clsname_len);
+    /*return NULL;*/
+  }
+
+  if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
+                      name_len, clsname_len);
+
+  /* Fish out the key name and process the LF list */
+
+  assert(name_len < sizeof(key_name));
+
+  /* Allocate the key struct now */
+  tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+  if (!tmp) return tmp;
+  bzero(tmp, sizeof(REG_KEY));
+
+  tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+  
+  strncpy(key_name, nk_hdr->key_nam, name_len);
+  key_name[name_len] = '\0';
+
+  if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
+
+  tmp->name = strdup(key_name);
+  if (!tmp->name) {
+    goto error;
+  }
+
+  /*
+   * Fish out the class name, it is in UNICODE, while the key name is 
+   * ASCII :-)
+   */
+
+  if (clsname_len) { /* Just print in Ascii for now */
+    char *clsnamep;
+    int clsnam_off;
+
+    clsnam_off = IVAL(&nk_hdr->clsnam_off);
+    clsnamep = LOCN(regf->base, clsnam_off);
+    bzero(cls_name, clsname_len);
+    uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
+    
+    /*
+     * I am keeping class name as an ascii string for the moment.
+     * That means it needs to be converted on output.
+     * XXX: FIXME
+     */
+
+    tmp->class_name = strdup(cls_name);
+    if (!tmp->class_name) {
+      goto error;
+    }
+
+    if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
+
+  }
+
+  /*
+   * If there are any values, process them here
+   */
+
+  val_count = IVAL(&nk_hdr->val_cnt);
+
+  if (val_count) {
+
+    val_off = IVAL(&nk_hdr->val_off);
+    vl = (VL_TYPE *)LOCN(regf->base, val_off);
+
+    tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
+    if (!tmp->values) {
+      goto error;
+    }
+
+  }
+
+  /* 
+   * Also handle the SK header ...
+   */
+
+  sk_off = IVAL(&nk_hdr->sk_off);
+  sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+
+  if (sk_off != -1) {
+
+    tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+
+  } 
+
+  lf_off = IVAL(&nk_hdr->lf_off);
+
+  /*
+   * No more subkeys if lf_off == -1
+   */
+
+  if (lf_off != -1) {
+
+    lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+    
+    tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr));
+    if (!tmp->sub_keys){
+      goto error;
+    }
+
+  }
+
+  return tmp;
+
+ error:
+  if (tmp) nt_delete_reg_key(tmp);
+  return NULL;
+}
+
+int nt_load_registry(REGF *regf)
+{
+  REGF_HDR *regf_hdr;
+  unsigned int regf_id, hbin_id;
+  HBIN_HDR *hbin_hdr;
+  NK_HDR *first_key;
+
+  /* Get the header */
+
+  if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
+    return -1;
+  }
+
+  /* Now process that header and start to read the rest in */
+
+  if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
+    fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
+           regf_id, regf->regfile_name);
+    return -1;
+  }
+
+  /*
+   * Validate the header ...
+   */
+  if (!valid_regf_hdr(regf_hdr)) {
+    fprintf(stderr, "Registry file header does not validate: %s\n",
+           regf->regfile_name);
+    return -1;
+  }
+
+  /* Update the last mod date, and then go get the first NK record and on */
+
+  TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
+
+  /* 
+   * The hbin hdr seems to be just uninteresting garbage. Check that
+   * it is there, but that is all.
+   */
+
+  hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+  if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
+    fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
+           hbin_id, regf->regfile_name);
+    return -1;
+  } 
+
+  /*
+   * Get a pointer to the first key from the hreg_hdr
+   */
+
+  first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
+
+  /*
+   * Now, get the registry tree by processing that NK recursively
+   */
+
+  regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key));
+
+  assert(regf->root != NULL);
+
+  return 1;
+}
+
+/*
+ * Routines to parse a REGEDIT4 file
+ * 
+ * The file consists of:
+ * 
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+typedef struct command_s {
+  int cmd;
+  char *key;
+  void *val_spec_list;
+} CMD;
+
+/*
+ * We seek to offset 0, read in the required number of bytes, 
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+int regedit4_file_type(int fd)
+{
+  int cur_ofs = 0;
+
+  cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+  if (cur_ofs < 0) {
+    fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+    exit(1);
+  }
+
+  if (cur_ofs) {
+    lseek(fd, 0, SEEK_SET);
+  }
+
+  return FMT_UNREC;
+}
+
+CMD *regedit4_get_cmd(int fd)
+{
+  return NULL;
+}
+
+int regedit4_exec_cmd(CMD *cmd)
+{
+
+  return 0;
+}
+
+int editreg_1_1_file_type(int fd)
+{
+
+  return FMT_UNREC;
+}
+
+CMD *editreg_1_1_get_cmd(int fd)
+{
+  return NULL;
+}
+
+int editreg_1_1_exec_cmd(CMD *cmd)
+{
+
+  return -1;
+}
+
+typedef struct command_ops_s {
+  int type;
+  int (*file_type)(int fd);
+  CMD *(*get_cmd)(int fd);
+  int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+  {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+  {1, editreg_1_1_file_type, editreg_1_1_get_cmd, editreg_1_1_exec_cmd},
+  {-1,  NULL, NULL, NULL}
+}; 
+
+typedef struct command_file_s {
+  char *name;
+  int type, fd;
+  CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+CMD_FILE *cmd_file_create(char *file)
+{
+  CMD_FILE *tmp;
+  struct stat sbuf;
+  int i = 0;
+
+  /*
+   * Let's check if the file exists ...
+   * No use creating the cmd_file structure if the file does not exist
+   */
+
+  if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+    return NULL;
+  }
+
+  tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE)); 
+  if (!tmp) {
+    return NULL;
+  }
+
+  /*
+   * Let's fill in some of the fields;
+   */
+
+  tmp->name = strdup(file);
+
+  if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+    free(tmp);
+    return NULL;
+  }
+
+  /*
+   * Now, try to find the format by indexing through the table
+   */
+  while (default_cmd_ops[i].type != -1) {
+    if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+      tmp->cmd_ops = default_cmd_ops[i];
+      return tmp;
+    }
+    i++;
+  }
+
+  /* 
+   * If we got here, return NULL, as we could not figure out the type
+   * of command file.
+   *
+   * What about errors? 
+   */
+
+  free(tmp);
+  return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that 
+ */
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+int print_key(const char *path, char *name, char *class_name, int root, 
+             int terminal, int vals)
+{
+
+  if (terminal) fprintf(stdout, "%s\\%s\n", path, name);
+
+  return 1;
+}
+
+/*
+ * Sec Desc print functions 
+ */
+
+void print_sid(DOM_SID *sid)
+{
+  int i, comps = sid->auths;
+  fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
+
+  for (i = 0; i < comps; i++) {
+
+    fprintf(stdout, "-%u", sid->sub_auths[i]);
+
+  }
+  fprintf(stdout, "\n");
+}
+
+int print_sec(SEC_DESC *sec_desc)
+{
+
+  fprintf(stdout, "  SECURITY\n");
+  fprintf(stdout, "    Owner: ");
+  print_sid(sec_desc->owner);
+  fprintf(stdout, "    Group: ");
+  print_sid(sec_desc->group);
+  return 1;
+}
+
+/*
+ * Value print function here ...
+ */
+int print_val(const char *path, char *val_name, int val_type, int data_len, 
+             void *data_blk, int terminal, int first, int last)
+{
+  char data_asc[1024];
+
+  bzero(data_asc, sizeof(data_asc));
+  if (!terminal && first)
+    fprintf(stdout, "%s\n", path);
+  data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc, 
+               sizeof(data_asc) - 1);
+  fprintf(stdout, "  %s : %s : %s\n", (val_name?val_name:"<No Name>"), 
+                  val_to_str(val_type, reg_type_names), data_asc);
+  return 1;
+}
+
+void usage(void)
+{
+  fprintf(stderr, "Usage: editreg [-v] [-k] [-c <command-file>] <registryfile>\n");
+  fprintf(stderr, "Version: 0.1\n\n");
+  fprintf(stderr, "\n\t-v\t sets verbose mode");
+  fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
+  fprintf(stderr, "\n");
+}
+
+int main(int argc, char *argv[])
+{
+  REGF *regf;
+  extern char *optarg;
+  extern int optind;
+  int opt;
+  int commands = 0;
+  char *cmd_file = NULL;
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+  
+  /* 
+   * Now, process the arguments
+   */
+
+  while ((opt = getopt(argc, argv, "vkc:")) != EOF) {
+    switch (opt) {
+    case 'c':
+      commands = 1;
+      cmd_file = optarg;
+      break;
+
+    case 'v':
+      verbose++;
+      break;
+
+    case 'k':
+      break;
+
+    default:
+      usage();
+      exit(1);
+      break;
+    }
+  }
+
+  if ((regf = nt_create_regf()) == NULL) {
+    fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
+    exit(2);
+  }
+
+  if (!nt_set_regf_input_file(regf, argv[optind])) {
+    fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
+           argv[1], strerror(errno));
+    exit(3);
+  }
+
+  /* Now, open it, and bring it into memory :-) */
+
+  if (nt_load_registry(regf) < 0) {
+    fprintf(stderr, "Could not load registry: %s\n", argv[1]);
+    exit(4);
+  }
+
+  /*
+   * At this point, we should have a registry in memory and should be able
+   * to iterate over it.
+   */
+
+  nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
+  return 0;
+}
diff --git a/source4/utils/net.c b/source4/utils/net.c
new file mode 100644 (file)
index 0000000..5c78d4d
--- /dev/null
@@ -0,0 +1,642 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2001 Steve French  (sfrench@us.ibm.com)
+   Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+   Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+   Originally written by Steve and Jim. Largely rewritten by tridge in
+   November 2001.
+
+   Reworked again by abartlet in December 2001
+
+   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.  */
+/*****************************************************/
+/*                                                   */
+/*   Distributed SMB/CIFS Server Management Utility  */
+/*                                                   */
+/*   The intent was to make the syntax similar       */
+/*   to the NET utility (first developed in DOS      */
+/*   with additional interesting & useful functions  */
+/*   added in later SMB server network operating     */
+/*   systems).                                       */
+/*                                                   */
+/*****************************************************/
+
+#include "includes.h"
+#include "../utils/net.h"
+
+/***********************************************************************/
+/* Beginning of internationalization section.  Translatable constants  */
+/* should be kept in this area and referenced in the rest of the code. */
+/*                                                                     */
+/* No functions, outside of Samba or LSB (Linux Standards Base) should */
+/* be used (if possible).                                              */
+/***********************************************************************/
+
+#define YES_STRING              "Yes"
+#define NO_STRING               "No"
+
+/************************************************************************************/
+/*                       end of internationalization section                        */
+/************************************************************************************/
+
+/* Yes, these buggers are globals.... */
+const char *opt_requester_name = NULL;
+const char *opt_host = NULL; 
+const char *opt_password = NULL;
+const char *opt_user_name = NULL;
+BOOL opt_user_specified = False;
+const char *opt_workgroup = NULL;
+int opt_long_list_entries = 0;
+int opt_reboot = 0;
+int opt_force = 0;
+int opt_port = 0;
+int opt_maxusers = -1;
+const char *opt_comment = "";
+char *opt_container = "cn=Users";
+int opt_flags = -1;
+int opt_jobid = 0;
+int opt_timeout = 0;
+const char *opt_target_workgroup = NULL;
+static int opt_machine_pass = 0;
+
+BOOL opt_have_ip = False;
+struct in_addr opt_dest_ip;
+
+extern BOOL AllowDebugChange;
+
+/*
+  run a function from a function table. If not found then
+  call the specified usage function 
+*/
+int net_run_function(int argc, const char **argv, struct functable *table, 
+                    int (*usage_fn)(int argc, const char **argv))
+{
+       int i;
+       
+       if (argc < 1) {
+               d_printf("\nUsage: \n");
+               return usage_fn(argc, argv);
+       }
+       for (i=0; table[i].funcname; i++) {
+               if (StrCaseCmp(argv[0], table[i].funcname) == 0)
+                       return table[i].fn(argc-1, argv+1);
+       }
+       d_printf("No command: %s\n", argv[0]);
+       return usage_fn(argc, argv);
+}
+
+
+/****************************************************************************
+connect to \\server\ipc$  
+****************************************************************************/
+NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
+                                       const char *server_name)
+{
+       NTSTATUS nt_status;
+
+       if (!opt_password) {
+               char *pass = getpass("Password:");
+               if (pass) {
+                       opt_password = strdup(pass);
+               }
+       }
+       
+       nt_status = cli_full_connection(c, opt_requester_name, server_name, 
+                                       server_ip, opt_port,
+                                       "IPC$", "IPC",  
+                                       opt_user_name, opt_workgroup,
+                                       opt_password, 0, NULL);
+       
+       if (NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       } else {
+               DEBUG(1,("Cannot connect to server.  Error was %s\n", 
+                        nt_errstr(nt_status)));
+
+               /* Display a nicer message depending on the result */
+
+               if (NT_STATUS_V(nt_status) == 
+                   NT_STATUS_V(NT_STATUS_LOGON_FAILURE))
+                       d_printf("The username or password was not correct.\n");
+
+               return nt_status;
+       }
+}
+
+/****************************************************************************
+connect to \\server\ipc$ anonymously
+****************************************************************************/
+NTSTATUS connect_to_ipc_anonymous(struct cli_state **c,
+                       struct in_addr *server_ip, const char *server_name)
+{
+       NTSTATUS nt_status;
+
+       nt_status = cli_full_connection(c, opt_requester_name, server_name, 
+                                       server_ip, opt_port,
+                                       "IPC$", "IPC",  
+                                       "", "",
+                                       "", 0, NULL);
+       
+       if (NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       } else {
+               DEBUG(1,("Cannot connect to server (anonymously).  Error was %s\n", nt_errstr(nt_status)));
+               return nt_status;
+       }
+}
+
+BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_name)
+{
+
+       if (opt_host) {
+               *server_name = strdup(opt_host);
+       }               
+
+       if (opt_have_ip) {
+               *server_ip = opt_dest_ip;
+               if (!*server_name) {
+                       *server_name = strdup(inet_ntoa(opt_dest_ip));
+               }
+       } else if (*server_name) {
+               /* resolve the IP address */
+               if (!resolve_name(*server_name, server_ip, 0x20))  {
+                       DEBUG(1,("Unable to resolve server name\n"));
+                       return False;
+               }
+       } else if (flags & NET_FLAGS_PDC) {
+               struct in_addr pdc_ip;
+
+               if (get_pdc_ip(opt_target_workgroup, &pdc_ip)) {
+                       fstring dc_name;
+                       
+                       if (is_zero_ip(pdc_ip))
+                               return False;
+                       
+                       if (!lookup_dc_name(lp_netbios_name(), opt_target_workgroup, &pdc_ip, dc_name))
+                               return False;
+                               
+                       *server_name = strdup(dc_name);
+                       *server_ip = pdc_ip;
+               }
+               
+       } else if (flags & NET_FLAGS_DMB) {
+               struct in_addr msbrow_ip;
+               /*  if (!resolve_name(MSBROWSE, &msbrow_ip, 1)) */
+               if (!resolve_name(opt_target_workgroup, &msbrow_ip, 0x1B))  {
+                       DEBUG(1,("Unable to resolve domain browser via name lookup\n"));
+                       return False;
+               } else {
+                       *server_ip = msbrow_ip;
+               }
+               *server_name = strdup(inet_ntoa(opt_dest_ip));
+       } else if (flags & NET_FLAGS_MASTER) {
+               struct in_addr brow_ips;
+               if (!resolve_name(opt_target_workgroup, &brow_ips, 0x1D))  {
+                               /* go looking for workgroups */
+                       DEBUG(1,("Unable to resolve master browser via name lookup\n"));
+                       return False;
+               } else {
+                       *server_ip = brow_ips;
+               }
+               *server_name = strdup(inet_ntoa(opt_dest_ip));
+       } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) {
+               extern struct in_addr loopback_ip;
+               *server_ip = loopback_ip;
+               *server_name = strdup("127.0.0.1");
+       }
+
+       if (!server_name || !*server_name) {
+               DEBUG(1,("no server to connect to\n"));
+               return False;
+       }
+
+       return True;
+}
+
+
+BOOL net_find_dc(struct in_addr *server_ip, fstring server_name, const char *domain_name)
+{
+       if (get_pdc_ip(domain_name, server_ip)) {
+               fstring dc_name;
+                       
+               if (is_zero_ip(*server_ip))
+                       return False;
+               
+               if (!lookup_dc_name(lp_netbios_name(), domain_name, server_ip, dc_name))
+                       return False;
+                       
+               safe_strcpy(server_name, dc_name, FSTRING_LEN);
+               return True;
+       } else
+               return False;
+}
+
+
+struct cli_state *net_make_ipc_connection(unsigned flags)
+{
+       char *server_name = NULL;
+       struct in_addr server_ip;
+       struct cli_state *cli = NULL;
+       NTSTATUS nt_status;
+
+       if (!net_find_server(flags, &server_ip, &server_name)) {
+               d_printf("\nUnable to find a suitable server\n");
+               return NULL;
+       }
+
+       if (flags & NET_FLAGS_ANONYMOUS) {
+               nt_status = connect_to_ipc_anonymous(&cli, &server_ip, server_name);
+       } else {
+               nt_status = connect_to_ipc(&cli, &server_ip, server_name);
+       }
+
+       SAFE_FREE(server_name);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               return cli;
+       } else {
+               return NULL;
+       }
+}
+
+static int net_user(int argc, const char **argv)
+{
+       if (net_ads_check() == 0)
+               return net_ads_user(argc, argv);
+
+       /* if server is not specified, default to PDC? */
+       if (net_rpc_check(NET_FLAGS_PDC))
+               return net_rpc_user(argc, argv);
+
+       return net_rap_user(argc, argv);
+}
+
+static int net_group(int argc, const char **argv)
+{
+       if (net_ads_check() == 0)
+               return net_ads_group(argc, argv);
+
+       if (argc == 0 && net_rpc_check(NET_FLAGS_PDC))
+               return net_rpc_group(argc, argv);
+
+       return net_rap_group(argc, argv);
+}
+
+static int net_join(int argc, const char **argv)
+{
+       if (net_ads_check() == 0) {
+               if (net_ads_join(argc, argv) == 0)
+                       return 0;
+               else
+                       d_printf("ADS join did not work, trying RPC...\n");
+       }
+       return net_rpc_join(argc, argv);
+}
+
+static int net_share(int argc, const char **argv)
+{
+       if (net_rpc_check(0))
+               return net_rpc_share(argc, argv);
+       return net_rap_share(argc, argv);
+}
+
+static int net_file(int argc, const char **argv)
+{
+       if (net_rpc_check(0))
+               return net_rpc_file(argc, argv);
+       return net_rap_file(argc, argv);
+}
+
+/*
+ Retrieve our local SID or the SID for the specified name
+ */
+static int net_getlocalsid(int argc, const char **argv)
+{
+        DOM_SID sid;
+       const char *name;
+       fstring sid_str;
+
+       if (argc >= 1) {
+               name = argv[0];
+        }
+       else {
+               name = lp_netbios_name();
+       }
+
+       if (!secrets_fetch_domain_sid(name, &sid)) {
+               DEBUG(0, ("Can't fetch domain SID for name: %s\n", name));      
+               return 1;
+       }
+       sid_to_string(sid_str, &sid);
+       d_printf("SID for domain %s is: %s\n", name, sid_str);
+       return 0;
+}
+
+static int net_setlocalsid(int argc, const char **argv)
+{
+       DOM_SID sid;
+
+       if ( (argc != 1)
+            || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0)
+            || (!string_to_sid(&sid, argv[0]))
+            || (sid.num_auths != 4)) {
+               d_printf("usage: net setlocalsid S-1-5-21-x-y-z\n");
+               return 1;
+       }
+
+       if (!secrets_store_domain_sid(lp_netbios_name(), &sid)) {
+               DEBUG(0,("Can't store domain SID as a pdc/bdc.\n"));
+               return 1;
+       }
+
+       return 0;
+}
+
+static int net_getdomainsid(int argc, const char **argv)
+{
+       DOM_SID domain_sid;
+       fstring sid_str;
+
+       if (!secrets_fetch_domain_sid(lp_netbios_name(), &domain_sid)) {
+               d_printf("Could not fetch local SID\n");
+               return 1;
+       }
+       sid_to_string(sid_str, &domain_sid);
+       d_printf("SID for domain %s is: %s\n", lp_netbios_name(), sid_str);
+
+       if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+               d_printf("Could not fetch domain SID\n");
+               return 1;
+       }
+
+       sid_to_string(sid_str, &domain_sid);
+       d_printf("SID for domain %s is: %s\n", lp_workgroup(), sid_str);
+
+       return 0;
+}
+
+static uint32 get_maxrid(void)
+{
+       SAM_ACCOUNT *pwd = NULL;
+       uint32 max_rid = 0;
+       GROUP_MAP *map = NULL;
+       int num_entries = 0;
+       int i;
+
+       if (!pdb_setsampwent(False)) {
+               DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+               return 0;
+       }
+
+       for (; (NT_STATUS_IS_OK(pdb_init_sam(&pwd))) 
+                    && pdb_getsampwent(pwd) == True; pwd=NULL) {
+               uint32 rid;
+
+               if (!sid_peek_rid(pdb_get_user_sid(pwd), &rid)) {
+                       DEBUG(0, ("can't get RID for user '%s'\n",
+                                 pdb_get_username(pwd)));
+                       pdb_free_sam(&pwd);
+                       continue;
+               }
+
+               if (rid > max_rid)
+                       max_rid = rid;
+
+               DEBUG(1,("%d is user '%s'\n", rid, pdb_get_username(pwd)));
+               pdb_free_sam(&pwd);
+       }
+
+       pdb_endsampwent();
+       pdb_free_sam(&pwd);
+
+       if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries,
+                                   ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV))
+               return max_rid;
+
+       for (i = 0; i < num_entries; i++) {
+               uint32 rid;
+
+               if (!sid_peek_check_rid(get_global_sam_sid(), &map[i].sid,
+                                       &rid)) {
+                       DEBUG(3, ("skipping map for group '%s', SID %s\n",
+                                 map[i].nt_name,
+                                 sid_string_static(&map[i].sid)));
+                       continue;
+               }
+               DEBUG(1,("%d is group '%s'\n", rid, map[i].nt_name));
+
+               if (rid > max_rid)
+                       max_rid = rid;
+       }
+
+       SAFE_FREE(map);
+
+       return max_rid;
+}
+
+static int net_maxrid(int argc, const char **argv)
+{
+       uint32 rid;
+
+       if (argc != 0) {
+               DEBUG(0, ("usage: net initrid\n"));
+               return 1;
+       }
+
+       if ((rid = get_maxrid()) == 0) {
+               DEBUG(0, ("can't get current maximum rid\n"));
+               return 1;
+       }
+
+       d_printf("Currently used maximum rid: %d\n", rid);
+
+       return 0;
+}
+
+/* main function table */
+static struct functable net_func[] = {
+       {"RPC", net_rpc},
+       {"RAP", net_rap},
+       {"ADS", net_ads},
+
+       /* eventually these should auto-choose the transport ... */
+       {"FILE", net_file},
+       {"SHARE", net_share},
+       {"SESSION", net_rap_session},
+       {"SERVER", net_rap_server},
+       {"DOMAIN", net_rap_domain},
+       {"PRINTQ", net_rap_printq},
+       {"USER", net_user},
+       {"GROUP", net_group},
+       {"VALIDATE", net_rap_validate},
+       {"GROUPMEMBER", net_rap_groupmember},
+       {"ADMIN", net_rap_admin},
+       {"SERVICE", net_rap_service},   
+       {"PASSWORD", net_rap_password},
+       {"TIME", net_time},
+       {"LOOKUP", net_lookup},
+       {"JOIN", net_join},
+       {"CACHE", net_cache},
+       {"GETLOCALSID", net_getlocalsid},
+       {"SETLOCALSID", net_setlocalsid},
+       {"GETDOMAINSID", net_getdomainsid},
+       {"MAXRID", net_maxrid},
+
+       {"HELP", net_help},
+       {NULL, NULL}
+};
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc, const char **argv)
+{
+       int opt,i;
+       char *p;
+       int rc = 0;
+       int argc_new = 0;
+       const char ** argv_new;
+       poptContext pc;
+       static char *servicesf = dyn_CONFIGFILE;
+       static char *debuglevel = NULL;
+
+       struct poptOption long_options[] = {
+               {"help",        'h', POPT_ARG_NONE,   0, 'h'},
+               {"workgroup",   'w', POPT_ARG_STRING, &opt_target_workgroup},
+               {"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup},
+               {"user",        'U', POPT_ARG_STRING, &opt_user_name, 'U'},
+               {"ipaddress",   'I', POPT_ARG_STRING, 0,'I'},
+               {"port",        'p', POPT_ARG_INT,    &opt_port},
+               {"myname",      'n', POPT_ARG_STRING, &opt_requester_name},
+               {"conf",        's', POPT_ARG_STRING, &servicesf},
+               {"server",      'S', POPT_ARG_STRING, &opt_host},
+               {"container",   'c', POPT_ARG_STRING, &opt_container},
+               {"comment",     'C', POPT_ARG_STRING, &opt_comment},
+               {"maxusers",    'M', POPT_ARG_INT,    &opt_maxusers},
+               {"flags",       'F', POPT_ARG_INT,    &opt_flags},
+               {"jobid",       'j', POPT_ARG_INT,    &opt_jobid},
+               {"long",        'l', POPT_ARG_NONE,   &opt_long_list_entries},
+               {"reboot",      'r', POPT_ARG_NONE,   &opt_reboot},
+               {"force",       'f', POPT_ARG_NONE,   &opt_force},
+               {"timeout",     't', POPT_ARG_INT,    &opt_timeout},
+               {"machine-pass",'P', POPT_ARG_NONE,   &opt_machine_pass},
+               {"debuglevel",  'd', POPT_ARG_STRING, &debuglevel},
+               {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { 0, 0, 0, 0}
+       };
+
+       zero_ip(&opt_dest_ip);
+
+       dbf = x_stderr;
+       
+       pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'h':
+                       net_help(argc, argv);
+                       exit(0);
+                       break;
+               case 'I':
+                       opt_dest_ip = *interpret_addr2(poptGetOptArg(pc));
+                       if (is_zero_ip(opt_dest_ip))
+                               d_printf("\nInvalid ip address specified\n");
+                       else
+                               opt_have_ip = True;
+                       break;
+               case 'U':
+                       opt_user_specified = True;
+                       opt_user_name = strdup(opt_user_name);
+                       p = strchr(opt_user_name,'%');
+                       if (p) {
+                               *p = 0;
+                               opt_password = p+1;
+                       }
+                       break;
+               default:
+                       d_printf("\nInvalid option %s: %s\n", 
+                                poptBadOption(pc, 0), poptStrerror(opt));
+                       net_help(argc, argv);
+                       exit(1);
+               }
+       }
+
+       if (debuglevel) {
+               debug_parse_levels(debuglevel);
+               AllowDebugChange = False;
+       }
+
+       lp_load(servicesf,True,False,False);       
+
+       argv_new = (const char **)poptGetArgs(pc);
+
+       argc_new = argc;
+       for (i=0; i<argc; i++) {
+               if (argv_new[i] == NULL) {
+                       argc_new = i;
+                       break;
+               }
+       }
+
+       if (!opt_requester_name) {
+               opt_requester_name = get_myname();
+       }
+
+       if (!opt_user_name && getenv("LOGNAME")) {
+               opt_user_name = getenv("LOGNAME");
+       }
+
+       if (!opt_workgroup) {
+               opt_workgroup = lp_workgroup();
+       }
+       
+       if (!opt_target_workgroup) {
+               opt_target_workgroup = strdup(lp_workgroup());
+       }
+       
+       if (!init_names())
+               exit(1);
+
+       load_interfaces();
+
+       if (opt_machine_pass) {
+               char *user;
+               /* it is very useful to be able to make ads queries as the
+                  machine account for testing purposes and for domain leave */
+
+               if (!secrets_init()) {
+                       d_printf("ERROR: Unable to open secrets database\n");
+                       exit(1);
+               }
+
+               asprintf(&user,"%s$", lp_netbios_name());
+               opt_user_name = user;
+               opt_password = secrets_fetch_machine_password();
+               if (!opt_password) {
+                       d_printf("ERROR: Unable to fetch machine password\n");
+                       exit(1);
+               }
+       }
+        
+       rc = net_run_function(argc_new-1, argv_new+1, net_func, net_help);
+       
+       DEBUG(2,("return code = %d\n", rc));
+       return rc;
+}
diff --git a/source4/utils/net.h b/source4/utils/net.h
new file mode 100644 (file)
index 0000000..c1b49a9
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+   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 "../utils/net_proto.h"
+#define NET_FLAGS_MASTER 1
+#define NET_FLAGS_DMB 2
+
+/* Would it be insane to set 'localhost' as the default
+   remote host for this operation? 
+
+   For example, localhost is insane for a 'join' operation.
+*/
+#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 4 
+
+/* We want to find the PDC only */
+#define NET_FLAGS_PDC 8 
+
+/* We want an anonymous connection */
+#define NET_FLAGS_ANONYMOUS 16 
+
+
+extern int opt_maxusers;
+extern const char *opt_comment;
+extern char *opt_container;
+extern int opt_flags;
+
+extern const char *opt_comment;
+
+extern const char *opt_target_workgroup;
+extern const char *opt_workgroup;
+extern int opt_long_list_entries;
+extern int opt_reboot;
+extern int opt_force;
+extern int opt_timeout;
+extern const char *opt_host;
+extern const char *opt_user_name;
+extern const char *opt_password;
+extern BOOL opt_user_specified;
+
+extern BOOL opt_have_ip;
+extern struct in_addr opt_dest_ip;
+
+extern const char *share_type[];
+
diff --git a/source4/utils/net_ads.c b/source4/utils/net_ads.c
new file mode 100644 (file)
index 0000000..6fb6394
--- /dev/null
@@ -0,0 +1,1176 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   net ads commands
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+   Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
+   Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+   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 "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+int net_ads_usage(int argc, const char **argv)
+{
+       d_printf(
+"\nnet ads join <org_unit>"\
+"\n\tjoins the local machine to a ADS realm\n"\
+"\nnet ads leave"\
+"\n\tremoves the local machine from a ADS realm\n"\
+"\nnet ads testjoin"\
+"\n\ttests that an exiting join is OK\n"\
+"\nnet ads user"\
+"\n\tlist, add, or delete users in the realm\n"\
+"\nnet ads group"\
+"\n\tlist, add, or delete groups in the realm\n"\
+"\nnet ads info"\
+"\n\tshows some info on the server\n"\
+"\nnet ads status"\
+"\n\tdump the machine account details to stdout\n"
+"\nnet ads lookup"\
+"\n\tperform a CLDAP search on the server\n"
+"\nnet ads password <username@realm> -Uadmin_username@realm%%admin_pass"\
+"\n\tchange a user's password using an admin account"\
+"\n\t(note: use realm in UPPERCASE)\n"\
+"\nnet ads chostpass"\
+"\n\tchange the trust account password of this machine in the AD tree\n"\
+"\nnet ads printer [info | publish | remove] <printername> <servername>"\
+"\n\t lookup, add, or remove directory entry for a printer\n"\
+"\nnet ads search"\
+"\n\tperform a raw LDAP search and dump the results\n"
+"\nnet ads dn"\
+"\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+               );
+       return -1;
+}
+
+
+/*
+  this implements the CLDAP based netlogon lookup requests
+  for finding the domain controller of a ADS domain
+*/
+static int net_ads_lookup(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+
+       ads = ads_init(NULL, NULL, opt_host);
+       if (ads) {
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
+       }
+
+       ads_connect(ads);
+
+       if (!ads || !ads->config.realm) {
+               d_printf("Didn't find the cldap server!\n");
+               return -1;
+       }
+
+       return ads_cldap_netlogon(ads);
+}
+
+
+
+static int net_ads_info(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+
+       ads = ads_init(NULL, NULL, opt_host);
+
+       if (ads) {
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
+       }
+
+       ads_connect(ads);
+
+       if (!ads || !ads->config.realm) {
+               d_printf("Didn't find the ldap server!\n");
+               return -1;
+       }
+
+       d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
+       d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
+       d_printf("Realm: %s\n", ads->config.realm);
+       d_printf("Bind Path: %s\n", ads->config.bind_path);
+       d_printf("LDAP port: %d\n", ads->ldap_port);
+       d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
+
+       return 0;
+}
+
+static void use_in_memory_ccache(void) {
+       /* Use in-memory credentials cache so we do not interfere with
+        * existing credentials */
+       setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
+
+static ADS_STRUCT *ads_startup(void)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       BOOL need_password = False;
+       BOOL second_time = False;
+       
+       ads = ads_init(NULL, NULL, opt_host);
+
+       if (!opt_user_name) {
+               opt_user_name = "administrator";
+       }
+
+       if (opt_user_specified) {
+               need_password = True;
+       }
+
+retry:
+       if (!opt_password && need_password) {
+               char *prompt;
+               asprintf(&prompt,"%s password: ", opt_user_name);
+               opt_password = getpass(prompt);
+               free(prompt);
+       }
+
+       if (opt_password) {
+               use_in_memory_ccache();
+               ads->auth.password = strdup(opt_password);
+       }
+
+       ads->auth.user_name = strdup(opt_user_name);
+
+       status = ads_connect(ads);
+       if (!ADS_ERR_OK(status)) {
+               if (!need_password && !second_time) {
+                       need_password = True;
+                       second_time = True;
+                       goto retry;
+               } else {
+                       DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
+                       return NULL;
+               }
+       }
+       return ads;
+}
+
+
+/*
+  Check to see if connection can be made via ads.
+  ads_startup() stores the password in opt_password if it needs to so
+  that rpc or rap can use it without re-prompting.
+*/
+int net_ads_check(void)
+{
+       ADS_STRUCT *ads;
+
+       ads = ads_startup();
+       if (!ads)
+               return -1;
+       ads_destroy(&ads);
+       return 0;
+}
+
+/* 
+   determine the netbios workgroup name for a domain
+ */
+static int net_ads_workgroup(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       TALLOC_CTX *ctx;
+       char *workgroup;
+
+       if (!(ads = ads_startup())) return -1;
+
+       if (!(ctx = talloc_init("net_ads_workgroup"))) {
+               return -1;
+       }
+
+       if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
+               d_printf("Failed to find workgroup for realm '%s'\n", 
+                        ads->config.realm);
+               talloc_destroy(ctx);
+               return -1;
+       }
+
+       d_printf("Workgroup: %s\n", workgroup);
+
+       talloc_destroy(ctx);
+
+       return 0;
+}
+
+
+
+static BOOL usergrp_display(char *field, void **values, void *data_area)
+{
+       char **disp_fields = (char **) data_area;
+
+       if (!field) { /* must be end of record */
+               if (!strchr_m(disp_fields[0], '$')) {
+                       if (disp_fields[1])
+                               d_printf("%-21.21s %-50.50s\n", 
+                                      disp_fields[0], disp_fields[1]);
+                       else
+                               d_printf("%s\n", disp_fields[0]);
+               }
+               SAFE_FREE(disp_fields[0]);
+               SAFE_FREE(disp_fields[1]);
+               return True;
+       }
+       if (!values) /* must be new field, indicate string field */
+               return True;
+       if (StrCaseCmp(field, "sAMAccountName") == 0) {
+               disp_fields[0] = strdup((char *) values[0]);
+       }
+       if (StrCaseCmp(field, "description") == 0)
+               disp_fields[1] = strdup((char *) values[0]);
+       return True;
+}
+
+static int net_ads_user_usage(int argc, const char **argv)
+{
+       return net_help_user(argc, argv);
+} 
+
+static int ads_user_add(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       char *upn, *userdn;
+       void *res=NULL;
+       int rc = -1;
+
+       if (argc < 1) return net_ads_user_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       status = ads_find_user_acct(ads, &res, argv[0]);
+
+       if (!ADS_ERR_OK(status)) {
+               d_printf("ads_user_add: %s\n", ads_errstr(status));
+               goto done;
+       }
+       
+       if (ads_count_replies(ads, res)) {
+               d_printf("ads_user_add: User %s already exists\n", argv[0]);
+               goto done;
+       }
+
+       status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
+
+       if (!ADS_ERR_OK(status)) {
+               d_printf("Could not add user %s: %s\n", argv[0],
+                        ads_errstr(status));
+               goto done;
+       }
+
+       /* if no password is to be set, we're done */
+       if (argc == 1) { 
+               d_printf("User %s added\n", argv[0]);
+               rc = 0;
+               goto done;
+       }
+
+       /* try setting the password */
+       asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+       status = krb5_set_password(ads->auth.kdc_server, upn, argv[1], ads->auth.time_offset);
+       safe_free(upn);
+       if (ADS_ERR_OK(status)) {
+               d_printf("User %s added\n", argv[0]);
+               rc = 0;
+               goto done;
+       }
+
+       /* password didn't set, delete account */
+       d_printf("Could not add user %s.  Error setting password %s\n",
+                argv[0], ads_errstr(status));
+       ads_msgfree(ads, res);
+       status=ads_find_user_acct(ads, &res, argv[0]);
+       if (ADS_ERR_OK(status)) {
+               userdn = ads_get_dn(ads, res);
+               ads_del_dn(ads, userdn);
+               ads_memfree(ads, userdn);
+       }
+
+ done:
+       if (res)
+               ads_msgfree(ads, res);
+       ads_destroy(&ads);
+       return rc;
+}
+
+static int ads_user_info(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       void *res;
+       const char *attrs[] = {"memberOf", NULL};
+       char *searchstring=NULL;
+       char **grouplist;
+       char *escaped_user = escape_ldap_string_alloc(argv[0]);
+
+       if (argc < 1) return net_ads_user_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       if (!escaped_user) {
+               d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+               return -1;
+       }
+
+       asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
+       rc = ads_search(ads, &res, searchstring, attrs);
+       safe_free(searchstring);
+
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_search: %s\n", ads_errstr(rc));
+               return -1;
+       }
+       
+       grouplist = ldap_get_values(ads->ld, res, "memberOf");
+
+       if (grouplist) {
+               int i;
+               char **groupname;
+               for (i=0;grouplist[i];i++) {
+                       groupname = ldap_explode_dn(grouplist[i], 1);
+                       d_printf("%s\n", groupname[0]);
+                       ldap_value_free(groupname);
+               }
+               ldap_value_free(grouplist);
+       }
+       
+       ads_msgfree(ads, res);
+
+       ads_destroy(&ads);
+       return 0;
+}
+
+static int ads_user_delete(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       void *res;
+       char *userdn;
+
+       if (argc < 1) return net_ads_user_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       rc = ads_find_user_acct(ads, &res, argv[0]);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(0, ("User %s does not exist\n", argv[0]));
+               return -1;
+       }
+       userdn = ads_get_dn(ads, res);
+       ads_msgfree(ads, res);
+       rc = ads_del_dn(ads, userdn);
+       ads_memfree(ads, userdn);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("User %s deleted\n", argv[0]);
+               return 0;
+       }
+       d_printf("Error deleting user %s: %s\n", argv[0], 
+                ads_errstr(rc));
+       return -1;
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADD", ads_user_add},
+               {"INFO", ads_user_info},
+               {"DELETE", ads_user_delete},
+               {NULL, NULL}
+       };
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *shortattrs[] = {"sAMAccountName", NULL};
+       const char *longattrs[] = {"sAMAccountName", "description", NULL};
+       char *disp_fields[2] = {NULL, NULL};
+       
+       if (argc == 0) {
+               if (!(ads = ads_startup())) return -1;
+
+               if (opt_long_list_entries)
+                       d_printf("\nUser name             Comment"\
+                                "\n-----------------------------\n");
+
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
+                                         LDAP_SCOPE_SUBTREE,
+                                         "(objectclass=user)", 
+                                         opt_long_list_entries ? longattrs :
+                                         shortattrs, usergrp_display, 
+                                         disp_fields);
+               ads_destroy(&ads);
+               return 0;
+       }
+
+       return net_run_function(argc, argv, func, net_ads_user_usage);
+}
+
+static int net_ads_group_usage(int argc, const char **argv)
+{
+       return net_help_group(argc, argv);
+} 
+
+static int ads_group_add(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       void *res=NULL;
+       int rc = -1;
+
+       if (argc < 1) return net_ads_group_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       status = ads_find_user_acct(ads, &res, argv[0]);
+
+       if (!ADS_ERR_OK(status)) {
+               d_printf("ads_group_add: %s\n", ads_errstr(status));
+               goto done;
+       }
+       
+       if (ads_count_replies(ads, res)) {
+               d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+               ads_msgfree(ads, res);
+               goto done;
+       }
+
+       status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
+
+       if (ADS_ERR_OK(status)) {
+               d_printf("Group %s added\n", argv[0]);
+               rc = 0;
+       } else {
+               d_printf("Could not add group %s: %s\n", argv[0],
+                        ads_errstr(status));
+       }
+
+ done:
+       if (res)
+               ads_msgfree(ads, res);
+       ads_destroy(&ads);
+       return rc;
+}
+
+static int ads_group_delete(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       void *res;
+       char *groupdn;
+
+       if (argc < 1) return net_ads_group_usage(argc, argv);
+       
+       if (!(ads = ads_startup())) return -1;
+
+       rc = ads_find_user_acct(ads, &res, argv[0]);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(0, ("Group %s does not exist\n", argv[0]));
+               return -1;
+       }
+       groupdn = ads_get_dn(ads, res);
+       ads_msgfree(ads, res);
+       rc = ads_del_dn(ads, groupdn);
+       ads_memfree(ads, groupdn);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("Group %s deleted\n", argv[0]);
+               return 0;
+       }
+       d_printf("Error deleting group %s: %s\n", argv[0], 
+                ads_errstr(rc));
+       return -1;
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADD", ads_group_add},
+               {"DELETE", ads_group_delete},
+               {NULL, NULL}
+       };
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *shortattrs[] = {"sAMAccountName", NULL};
+       const char *longattrs[] = {"sAMAccountName", "description", NULL};
+       char *disp_fields[2] = {NULL, NULL};
+
+       if (argc == 0) {
+               if (!(ads = ads_startup())) return -1;
+
+               if (opt_long_list_entries)
+                       d_printf("\nGroup name            Comment"\
+                                "\n-----------------------------\n");
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
+                                         LDAP_SCOPE_SUBTREE, 
+                                         "(objectclass=group)", 
+                                         opt_long_list_entries ? longattrs : 
+                                         shortattrs, usergrp_display, 
+                                         disp_fields);
+
+               ads_destroy(&ads);
+               return 0;
+       }
+       return net_run_function(argc, argv, func, net_ads_group_usage);
+}
+
+static int net_ads_status(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       void *res;
+
+       if (!(ads = ads_startup())) return -1;
+
+       rc = ads_find_machine_acct(ads, &res, lp_netbios_name());
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
+               return -1;
+       }
+
+       if (ads_count_replies(ads, res) == 0) {
+               d_printf("No machine account for '%s' found\n", lp_netbios_name());
+               return -1;
+       }
+
+       ads_dump(ads, res);
+
+       return 0;
+}
+
+static int net_ads_leave(int argc, const char **argv)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS rc;
+
+       if (!secrets_init()) {
+               DEBUG(1,("Failed to initialise secrets database\n"));
+               return -1;
+       }
+
+       if (!opt_password) {
+               char *user_name;
+               asprintf(&user_name, "%s$", lp_netbios_name());
+               opt_password = secrets_fetch_machine_password();
+               opt_user_name = user_name;
+       }
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       rc = ads_leave_realm(ads, lp_netbios_name());
+       if (!ADS_ERR_OK(rc)) {
+           d_printf("Failed to delete host '%s' from the '%s' realm.\n", 
+                    lp_netbios_name(), ads->config.realm);
+           return -1;
+       }
+
+       d_printf("Removed '%s' from realm '%s'\n", lp_netbios_name(), ads->config.realm);
+
+       return 0;
+}
+
+static int net_ads_join_ok(void)
+{
+       char *user_name;
+       ADS_STRUCT *ads = NULL;
+
+       if (!secrets_init()) {
+               DEBUG(1,("Failed to initialise secrets database\n"));
+               return -1;
+       }
+
+       asprintf(&user_name, "%s$", lp_netbios_name());
+       opt_user_name = user_name;
+       opt_password = secrets_fetch_machine_password();
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       ads_destroy(&ads);
+       return 0;
+}
+
+/*
+  check that an existing join is OK
+ */
+int net_ads_testjoin(int argc, const char **argv)
+{
+       use_in_memory_ccache();
+
+       /* Display success or failure */
+       if (net_ads_join_ok() != 0) {
+               fprintf(stderr,"Join to domain is not valid\n");
+               return -1;
+       }
+
+       printf("Join is OK\n");
+       return 0;
+}
+
+/*
+  join a domain using ADS
+ */
+int net_ads_join(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       char *password;
+       char *tmp_password;
+       const char *org_unit = "Computers";
+       char *dn;
+       void *res;
+       DOM_SID dom_sid;
+       char *ou_str;
+
+       if (argc > 0) org_unit = argv[0];
+
+       if (!secrets_init()) {
+               DEBUG(1,("Failed to initialise secrets database\n"));
+               return -1;
+       }
+
+       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+       password = strdup(tmp_password);
+
+       if (!(ads = ads_startup())) return -1;
+
+       ou_str = ads_ou_string(org_unit);
+       asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
+       free(ou_str);
+
+       rc = ads_search_dn(ads, &res, dn, NULL);
+       ads_msgfree(ads, res);
+
+       if (rc.error_type == ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
+               d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n", 
+                        org_unit, dn);
+               return -1;
+       }
+       free(dn);
+
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+               return -1;
+       }       
+
+       rc = ads_join_realm(ads, lp_netbios_name(), org_unit);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+               return -1;
+       }
+
+       rc = ads_domain_sid(ads, &dom_sid);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
+               return -1;
+       }
+
+       rc = ads_set_machine_password(ads, lp_netbios_name(), password);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+               return -1;
+       }
+
+       if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
+               DEBUG(1,("Failed to save domain sid\n"));
+               return -1;
+       }
+
+       if (!secrets_store_machine_password(password)) {
+               DEBUG(1,("Failed to save machine password\n"));
+               return -1;
+       }
+
+       d_printf("Joined '%s' to realm '%s'\n", lp_netbios_name(), ads->config.realm);
+
+       free(password);
+
+       return 0;
+}
+
+int net_ads_printer_usage(int argc, const char **argv)
+{
+       d_printf(
+"\nnet ads printer info <printer> <server>"
+"\n\tlookup info in directory for printer on server"
+"\n\t(note: printer defaults to \"*\", server defaults to local)\n"
+"\nnet ads printer publish <printername>"
+"\n\tpublish printer in directory"
+"\n\t(note: printer name is required)\n"
+"\nnet ads printer remove <printername>"
+"\n\tremove printer from directory"
+"\n\t(note: printer name is required)\n");
+       return -1;
+}
+
+static int net_ads_printer_info(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *servername, *printername;
+       void *res = NULL;
+
+       if (!(ads = ads_startup())) return -1;
+
+       if (argc > 0)
+               printername = argv[0];
+       else
+               printername = "*";
+
+       if (argc > 1)
+               servername =  argv[1];
+       else
+               servername = lp_netbios_name();
+
+       rc = ads_find_printer_on_server(ads, &res, printername, servername);
+
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+               ads_msgfree(ads, res);
+               return -1;
+       }
+
+       if (ads_count_replies(ads, res) == 0) {
+               d_printf("Printer '%s' not found\n", printername);
+               ads_msgfree(ads, res);
+               return -1;
+       }
+
+       ads_dump(ads, res);
+       ads_msgfree(ads, res);
+
+       return 0;
+}
+
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+       return;
+}
+
+static int net_ads_printer_publish(int argc, const char **argv)
+{
+        ADS_STRUCT *ads;
+        ADS_STATUS rc;
+       const char *servername;
+       struct cli_state *cli;
+       struct in_addr          server_ip;
+       NTSTATUS nt_status;
+       TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
+       ADS_MODLIST mods = ads_init_mods(mem_ctx);
+       char *prt_dn, *srv_dn, **srv_cn;
+       void *res = NULL;
+
+       if (!(ads = ads_startup())) return -1;
+
+       if (argc < 1)
+               return net_ads_printer_usage(argc, argv);
+       
+       if (argc == 2)
+               servername = argv[1];
+       else
+               servername = lp_netbios_name();
+               
+       ads_find_machine_acct(ads, &res, servername);
+       srv_dn = ldap_get_dn(ads->ld, res);
+       srv_cn = ldap_explode_dn(srv_dn, 1);
+       asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], argv[0], srv_dn);
+
+       resolve_name(servername, &server_ip, 0x20);
+
+       nt_status = cli_full_connection(&cli, lp_netbios_name(), servername, 
+                                       &server_ip, 0,
+                                       "IPC$", "IPC",  
+                                       opt_user_name, opt_workgroup,
+                                       opt_password ? opt_password : "", 
+                                       CLI_FULL_CONNECTION_USE_KERBEROS, 
+                                       NULL);
+
+       cli_nt_session_open(cli, PI_SPOOLSS);
+       get_remote_printer_publishing_data(cli, mem_ctx, &mods, argv[0]);
+
+        rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
+        if (!ADS_ERR_OK(rc)) {
+                d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
+                return -1;
+        }
+        d_printf("published printer\n");
+       return 0;
+}
+
+static int net_ads_printer_remove(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *servername;
+       char *prt_dn;
+       void *res = NULL;
+
+       if (!(ads = ads_startup())) return -1;
+
+       if (argc < 1)
+               return net_ads_printer_usage(argc, argv);
+
+       if (argc > 1)
+               servername = argv[1];
+       else
+               servername = lp_netbios_name();
+
+       rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
+
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+               ads_msgfree(ads, res);
+               return -1;
+       }
+
+       if (ads_count_replies(ads, res) == 0) {
+               d_printf("Printer '%s' not found\n", argv[1]);
+               ads_msgfree(ads, res);
+               return -1;
+       }
+
+       prt_dn = ads_get_dn(ads, res);
+       ads_msgfree(ads, res);
+       rc = ads_del_dn(ads, prt_dn);
+       ads_memfree(ads, prt_dn);
+
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("ads_del_dn: %s\n", ads_errstr(rc));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int net_ads_printer(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"INFO", net_ads_printer_info},
+               {"PUBLISH", net_ads_printer_publish},
+               {"REMOVE", net_ads_printer_remove},
+               {NULL, NULL}
+       };
+       
+       return net_run_function(argc, argv, func, net_ads_printer_usage);
+}
+
+
+static int net_ads_password(int argc, const char **argv)
+{
+    ADS_STRUCT *ads;
+    const char *auth_principal = opt_user_name;
+    const char *auth_password = opt_password;
+    char *realm = NULL;
+    char *new_password = NULL;
+    char *c;
+    char *prompt;
+    ADS_STATUS ret;
+
+    
+    if ((argc != 1) || (opt_user_name == NULL) || 
+       (opt_password == NULL) || (strchr(opt_user_name, '@') == NULL) ||
+       (strchr(argv[0], '@') == NULL)) {
+       return net_ads_usage(argc, argv);
+    }
+
+    use_in_memory_ccache();    
+    c = strchr(auth_principal, '@');
+    realm = ++c;
+
+    /* use the realm so we can eventually change passwords for users 
+    in realms other than default */
+    if (!(ads = ads_init(realm, NULL, NULL))) return -1;
+
+    asprintf(&prompt, "Enter new password for %s:", argv[0]);
+
+    new_password = getpass(prompt);
+
+    ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
+                               auth_password, argv[0], new_password, ads->auth.time_offset);
+    if (!ADS_ERR_OK(ret)) {
+       d_printf("Password change failed :-( ...\n");
+       ads_destroy(&ads);
+       free(prompt);
+       return -1;
+    }
+
+    d_printf("Password change for %s completed.\n", argv[0]);
+    ads_destroy(&ads);
+    free(prompt);
+
+    return 0;
+}
+
+
+static int net_ads_change_localhost_pass(int argc, const char **argv)
+{    
+    ADS_STRUCT *ads;
+    char *host_principal;
+    char *hostname;
+    ADS_STATUS ret;
+    char *user_name;
+
+    if (!secrets_init()) {
+           DEBUG(1,("Failed to initialise secrets database\n"));
+           return -1;
+    }
+
+    asprintf(&user_name, "%s$", lp_netbios_name());
+    opt_user_name = user_name;
+
+    opt_password = secrets_fetch_machine_password();
+
+    use_in_memory_ccache();
+
+    if (!(ads = ads_startup())) {
+           return -1;
+    }
+
+    hostname = strdup(lp_netbios_name());
+    strlower(hostname);
+    asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
+    SAFE_FREE(hostname);
+    d_printf("Changing password for principal: HOST/%s\n", host_principal);
+    
+    ret = ads_change_trust_account_password(ads, host_principal);
+
+    if (!ADS_ERR_OK(ret)) {
+       d_printf("Password change failed :-( ...\n");
+       ads_destroy(&ads);
+       SAFE_FREE(host_principal);
+       return -1;
+    }
+    
+    d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
+    ads_destroy(&ads);
+    SAFE_FREE(host_principal);
+
+    return 0;
+}
+
+/*
+  help for net ads search
+*/
+static int net_ads_search_usage(int argc, const char **argv)
+{
+       d_printf(
+               "\nnet ads search <expression> <attributes...>\n"\
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+               "The expression is a standard LDAP search expression, and the\n"\
+               "attributes are a list of LDAP fields to show in the results\n\n"\
+               "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
+               );
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
+/*
+  general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_search(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *exp;
+       const char **attrs;
+       void *res = NULL;
+
+       if (argc < 1) {
+               return net_ads_search_usage(argc, argv);
+       }
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       exp = argv[0];
+       attrs = (argv + 1);
+
+       rc = ads_do_search_all(ads, ads->config.bind_path,
+                              LDAP_SCOPE_SUBTREE,
+                              exp, attrs, &res);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("search failed: %s\n", ads_errstr(rc));
+               return -1;
+       }       
+
+       d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+       /* dump the results */
+       ads_dump(ads, res);
+
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return 0;
+}
+
+
+/*
+  help for net ads search
+*/
+static int net_ads_dn_usage(int argc, const char **argv)
+{
+       d_printf(
+               "\nnet ads dn <dn> <attributes...>\n"\
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+               "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
+               "to show in the results\n\n"\
+               "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
+               );
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
+/*
+  general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_dn(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *dn;
+       const char **attrs;
+       void *res = NULL;
+
+       if (argc < 1) {
+               return net_ads_dn_usage(argc, argv);
+       }
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       dn = argv[0];
+       attrs = (argv + 1);
+
+       rc = ads_do_search_all(ads, dn, 
+                              LDAP_SCOPE_BASE,
+                              "(objectclass=*)", attrs, &res);
+       if (!ADS_ERR_OK(rc)) {
+               d_printf("search failed: %s\n", ads_errstr(rc));
+               return -1;
+       }       
+
+       d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+       /* dump the results */
+       ads_dump(ads, res);
+
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return 0;
+}
+
+
+int net_ads_help(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"USER", net_ads_user_usage},
+               {"GROUP", net_ads_group_usage},
+               {"PRINTER", net_ads_printer_usage},
+               {"SEARCH", net_ads_search_usage},
+#if 0
+               {"INFO", net_ads_info},
+               {"JOIN", net_ads_join},
+               {"LEAVE", net_ads_leave},
+               {"STATUS", net_ads_status},
+               {"PASSWORD", net_ads_password},
+               {"CHOSTPASS", net_ads_change_localhost_pass},
+#endif
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+int net_ads(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"INFO", net_ads_info},
+               {"JOIN", net_ads_join},
+               {"TESTJOIN", net_ads_testjoin},
+               {"LEAVE", net_ads_leave},
+               {"STATUS", net_ads_status},
+               {"USER", net_ads_user},
+               {"GROUP", net_ads_group},
+               {"PASSWORD", net_ads_password},
+               {"CHOSTPASS", net_ads_change_localhost_pass},
+               {"PRINTER", net_ads_printer},
+               {"SEARCH", net_ads_search},
+               {"DN", net_ads_dn},
+               {"WORKGROUP", net_ads_workgroup},
+               {"LOOKUP", net_ads_lookup},
+               {"HELP", net_ads_help},
+               {NULL, NULL}
+       };
+       
+       return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+#else
+
+static int net_ads_noads(void)
+{
+       d_printf("ADS support not compiled in\n");
+       return -1;
+}
+
+int net_ads_usage(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_help(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_join(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
+/* this one shouldn't display a message */
+int net_ads_check(void)
+{
+       return -1;
+}
+
+int net_ads(int argc, const char **argv)
+{
+       return net_ads_usage(argc, argv);
+}
+
+#endif
diff --git a/source4/utils/net_ads_cldap.c b/source4/utils/net_ads_cldap.c
new file mode 100644 (file)
index 0000000..1322385
--- /dev/null
@@ -0,0 +1,354 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   net ads cldap functions 
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+   Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com)
+
+   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 "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+struct netlogon_string {
+       uint32 comp_len;
+       char **component;
+       uint8 extra_flag;
+};
+
+struct cldap_netlogon_reply {
+       uint32 type;
+       uint32 flags;
+       GUID guid;
+
+       struct netlogon_string forest;
+       struct netlogon_string domain;
+       struct netlogon_string hostname;
+
+       struct netlogon_string netbios_domain;
+       struct netlogon_string netbios_hostname;
+
+       struct netlogon_string user_name;
+       struct netlogon_string site_name;
+
+       struct netlogon_string unk0;
+
+       uint32 version;
+       uint16 lmnt_token;
+       uint16 lm20_token;
+};
+
+/*
+  These strings are rather interesting... They are composed of a series of
+  length encoded strings, terminated by either 1) a zero length string or 2)
+  a 0xc0 byte with what appears to be a one byte flags immediately following.
+*/
+static unsigned pull_netlogon_string(struct netlogon_string *ret,const char *d)
+{
+       char *p = (char *)d;
+
+       ZERO_STRUCTP(ret);
+
+       do {
+               unsigned len = (unsigned char)*p;
+               p++;
+
+               if (len > 0 && len != 0xc0) {
+                       ret->component = realloc(ret->component,
+                                                ++ret->comp_len *
+                                                sizeof(char *));
+
+                       ret->component[ret->comp_len - 1] = 
+                               smb_xstrndup(p, len);
+                       p += len;
+               } else {
+                       if (len == 0xc0) {
+                               ret->extra_flag = *p;
+                               p++;
+                       };
+                       break;
+               }
+       } while (1);
+
+       return (p - d);
+}
+
+/*
+  do a cldap netlogon query
+*/
+static int send_cldap_netlogon(int sock, const char *domain, 
+                              const char *hostname, unsigned ntversion)
+{
+       ASN1_DATA data;
+       char ntver[4];
+
+       SIVAL(ntver, 0, ntversion);
+
+       memset(&data, 0, sizeof(data));
+
+       asn1_push_tag(&data,ASN1_SEQUENCE(0));
+       asn1_write_Integer(&data, 4);
+       asn1_push_tag(&data, ASN1_APPLICATION(3));
+       asn1_write_OctetString(&data, NULL, 0);
+       asn1_write_enumerated(&data, 0);
+       asn1_write_enumerated(&data, 0);
+       asn1_write_Integer(&data, 0);
+       asn1_write_Integer(&data, 0);
+       asn1_write_BOOLEAN2(&data, False);
+       asn1_push_tag(&data, ASN1_CONTEXT(0));
+
+       asn1_push_tag(&data, ASN1_CONTEXT(3));
+       asn1_write_OctetString(&data, "DnsDomain", 9);
+       asn1_write_OctetString(&data, domain, strlen(domain));
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(3));
+       asn1_write_OctetString(&data, "Host", 4);
+       asn1_write_OctetString(&data, hostname, strlen(hostname));
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data, ASN1_CONTEXT(3));
+       asn1_write_OctetString(&data, "NtVer", 5);
+       asn1_write_OctetString(&data, ntver, 4);
+       asn1_pop_tag(&data);
+
+       asn1_pop_tag(&data);
+
+       asn1_push_tag(&data,ASN1_SEQUENCE(0));
+       asn1_write_OctetString(&data, "NetLogon", 8);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+       asn1_pop_tag(&data);
+
+       if (data.has_error) {
+               d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs);
+               asn1_free(&data);
+               return -1;
+       }
+
+       if (write(sock, data.data, data.length) != data.length) {
+               d_printf("failed to send cldap query (%s)\n", strerror(errno));
+       }
+
+       file_save("cldap_query.dat", data.data, data.length);
+       asn1_free(&data);
+
+       return 0;
+}
+
+
+/*
+  receive a cldap netlogon reply
+*/
+static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
+{
+       int ret;
+       ASN1_DATA data;
+       DATA_BLOB blob;
+       DATA_BLOB os1, os2, os3;
+       uint32 i1;
+       char *p;
+
+       blob = data_blob(NULL, 8192);
+
+       ret = read(sock, blob.data, blob.length);
+
+       if (ret <= 0) {
+               d_printf("no reply received to cldap netlogon\n");
+               return -1;
+       }
+       blob.length = ret;
+
+       file_save("cldap_reply.dat", blob.data, blob.length);
+
+       asn1_load(&data, blob);
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_read_Integer(&data, &i1);
+       asn1_start_tag(&data, ASN1_APPLICATION(4));
+       asn1_read_OctetString(&data, &os1);
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_start_tag(&data, ASN1_SEQUENCE(0));
+       asn1_read_OctetString(&data, &os2);
+       asn1_start_tag(&data, ASN1_SET);
+       asn1_read_OctetString(&data, &os3);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+       asn1_end_tag(&data);
+
+       if (data.has_error) {
+               d_printf("Failed to parse cldap reply\n");
+               return -1;
+       }
+
+       file_save("cldap_reply_core.dat", os3.data, os3.length);
+
+       p = os3.data;
+
+       reply->type = IVAL(p, 0); p += 4;
+       reply->flags = IVAL(p, 0); p += 4;
+
+       memcpy(&reply->guid.info, p, GUID_SIZE);
+       p += GUID_SIZE;
+
+       p += pull_netlogon_string(&reply->forest, p);
+       p += pull_netlogon_string(&reply->domain, p);
+       p += pull_netlogon_string(&reply->hostname, p);
+       p += pull_netlogon_string(&reply->netbios_domain, p);
+       p += pull_netlogon_string(&reply->netbios_hostname, p);
+       p += pull_netlogon_string(&reply->user_name, p);
+       p += pull_netlogon_string(&reply->site_name, p);
+
+       p += pull_netlogon_string(&reply->unk0, p);
+
+       reply->version = IVAL(p, 0);
+       reply->lmnt_token = SVAL(p, 4);
+       reply->lm20_token = SVAL(p, 6);
+
+       data_blob_free(&os1);
+       data_blob_free(&os2);
+       data_blob_free(&os3);
+       data_blob_free(&blob);
+       
+       return 0;
+}
+
+/*
+  free a netlogon string
+*/
+static void netlogon_string_free(struct netlogon_string *str)
+{
+       int i;
+
+       for (i = 0; i < str->comp_len; ++i) {
+               SAFE_FREE(str->component[i]);
+       }
+       SAFE_FREE(str->component);
+}
+
+/*
+  free a cldap reply packet
+*/
+static void cldap_reply_free(struct cldap_netlogon_reply *reply)
+{
+       netlogon_string_free(&reply->forest);
+       netlogon_string_free(&reply->domain);
+       netlogon_string_free(&reply->hostname);
+       netlogon_string_free(&reply->netbios_domain);
+       netlogon_string_free(&reply->netbios_hostname);
+       netlogon_string_free(&reply->user_name);
+       netlogon_string_free(&reply->site_name);
+       netlogon_string_free(&reply->unk0);
+}
+
+static void d_print_netlogon_string(const char *label, 
+                                   struct netlogon_string *str)
+{
+       int i;
+
+       if (str->comp_len) {
+               d_printf("%s", label);
+               if (str->extra_flag) {
+                       d_printf("[%d]", str->extra_flag);
+               }
+               d_printf(": ");
+               for (i = 0; i < str->comp_len; ++i) {
+                       d_printf("%s%s", (i ? "." : ""), str->component[i]);
+               }
+               d_printf("\n");
+       }
+}
+
+/*
+  do a cldap netlogon query
+*/
+int ads_cldap_netlogon(ADS_STRUCT *ads)
+{
+       int sock;
+       int ret;
+       struct cldap_netlogon_reply reply;
+
+       sock = open_udp_socket(inet_ntoa(ads->ldap_ip), ads->ldap_port);
+       if (sock == -1) {
+               d_printf("Failed to open udp socket to %s:%u\n", 
+                        inet_ntoa(ads->ldap_ip), 
+                        ads->ldap_port);
+               return -1;
+       }
+
+       ret = send_cldap_netlogon(sock, ads->config.realm, lp_netbios_name(), 6);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = recv_cldap_netlogon(sock, &reply);
+       close(sock);
+
+       if (ret == -1) {
+               return -1;
+       }
+
+       d_printf("Information for Domain Controller: %s\n\n", 
+                ads->config.ldap_server_name);
+
+       d_printf("Response Type: 0x%x\n", reply.type);
+       d_printf("GUID: "); 
+       print_guid(&reply.guid);
+       d_printf("Flags:\n"
+                "\tIs a PDC:                                   %s\n"
+                "\tIs a GC of the forest:                      %s\n"
+                "\tIs an LDAP server:                          %s\n"
+                "\tSupports DS:                                %s\n"
+                "\tIs running a KDC:                           %s\n"
+                "\tIs running time services:                   %s\n"
+                "\tIs the closest DC:                          %s\n"
+                "\tIs writable:                                %s\n"
+                "\tHas a hardware clock:                       %s\n"
+                "\tIs a non-domain NC serviced by LDAP server: %s\n",
+                (reply.flags & ADS_PDC) ? "yes" : "no",
+                (reply.flags & ADS_GC) ? "yes" : "no",
+                (reply.flags & ADS_LDAP) ? "yes" : "no",
+                (reply.flags & ADS_DS) ? "yes" : "no",
+                (reply.flags & ADS_KDC) ? "yes" : "no",
+                (reply.flags & ADS_TIMESERV) ? "yes" : "no",
+                (reply.flags & ADS_CLOSEST) ? "yes" : "no",
+                (reply.flags & ADS_WRITABLE) ? "yes" : "no",
+                (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
+                (reply.flags & ADS_NDNC) ? "yes" : "no");
+
+       d_print_netlogon_string("Forest", &reply.forest);
+       d_print_netlogon_string("Domain", &reply.domain);
+       d_print_netlogon_string("Hostname", &reply.hostname);
+
+       d_print_netlogon_string("Pre-Win2k Domain", &reply.netbios_domain);
+       d_print_netlogon_string("Pre-Win2k Hostname", &reply.netbios_hostname);
+
+       d_print_netlogon_string("User name", &reply.user_name);
+       d_print_netlogon_string("Site Name", &reply.site_name);
+       d_print_netlogon_string("Unknown Field", &reply.unk0);
+
+       d_printf("NT Version: %d\n", reply.version);
+       d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
+       d_printf("LM20 Token: %.2x\n", reply.lm20_token);
+
+       cldap_reply_free(&reply);
+       
+       return ret;
+}
+
+
+#endif
diff --git a/source4/utils/net_cache.c b/source4/utils/net_cache.c
new file mode 100644 (file)
index 0000000..93c4f1a
--- /dev/null
@@ -0,0 +1,348 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) Rafal Szczesniak    2002
+
+   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 "net.h"
+
+/**
+ * @file net_cache.c
+ * @brief This is part of the net tool which is basically command
+ *        line wrapper for gencache.c functions (mainly for testing)
+ *
+ **/
+
+
+/*
+ * These routines are used via gencache_iterate() to display the cache's contents
+ * (print_cache_entry) and to flush it (delete_cache_entry).
+ * Both of them are defined by first arg of gencache_iterate() routine.
+ */
+static void print_cache_entry(const char* keystr, const char* datastr,
+                              const time_t timeout, void* dptr)
+{
+       char* timeout_str;
+       time_t now_t = time(NULL);
+       struct tm timeout_tm, *now_tm;
+       /* localtime returns statically allocated pointer, so timeout_tm
+          has to be copied somewhere else */
+       memcpy(&timeout_tm, localtime(&timeout), sizeof(struct tm));
+       now_tm = localtime(&now_t);
+
+       /* form up timeout string depending whether it's today's date or not */
+       if (timeout_tm.tm_year != now_tm->tm_year ||
+           timeout_tm.tm_mon != now_tm->tm_mon ||
+           timeout_tm.tm_mday != now_tm->tm_mday) {
+           
+           timeout_str = asctime(&timeout_tm);
+           timeout_str[strlen(timeout_str) - 1] = '\0';        /* remove tailing CR */
+       } else
+               asprintf(&timeout_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour,
+                        timeout_tm.tm_min, timeout_tm.tm_sec);
+       
+       d_printf("Key: %s\t Timeout: %s\t Value: %s  %s\n", keystr,
+                timeout_str, datastr, timeout > now_t ? "": "(expired)");
+}
+
+static void delete_cache_entry(const char* keystr, const char* datastr,
+                               const time_t timeout, void* dptr)
+{
+       if (!gencache_del(keystr))
+               d_printf("Couldn't delete entry! key = %s", keystr);
+}
+
+
+/**
+ * Parse text representation of timeout value
+ *
+ * @param timeout_str string containing text representation of the timeout
+ * @return numeric timeout of time_t type
+ **/
+static time_t parse_timeout(const char* timeout_str)
+{
+       char sign = '\0', *number = NULL, unit = '\0';
+       int len, number_begin, number_end;
+       time_t timeout;
+
+       /* sign detection */
+       if (timeout_str[0] == '!' || timeout_str[0] == '+') {
+               sign = timeout_str[0];
+               number_begin = 1;
+       } else {
+               number_begin = 0;
+       }
+       
+       /* unit detection */
+       len = strlen(timeout_str);
+       switch (timeout_str[len - 1]) {
+       case 's':
+       case 'm':
+       case 'h':
+       case 'd':
+       case 'w': unit = timeout_str[len - 1];
+       }
+       
+       /* number detection */
+       len = (sign) ? strlen(&timeout_str[number_begin]) : len;
+       number_end = (unit) ? len - 1 : len;
+       number = strndup(&timeout_str[number_begin], number_end);
+       
+       /* calculate actual timeout value */
+       timeout = (time_t)atoi(number);
+
+       switch (unit) {
+       case 'm': timeout *= 60; break;
+       case 'h': timeout *= 60*60; break;
+       case 'd': timeout *= 60*60*24; break;
+       case 'w': timeout *= 60*60*24*7; break;  /* that's fair enough, I think :) */
+       }
+       
+       switch (sign) {
+       case '!': timeout = time(NULL) - timeout; break;
+       case '+':
+       default:  timeout += time(NULL); break;
+       }
+       
+       if (number) SAFE_FREE(number);
+       return timeout; 
+}
+
+
+/**
+ * Add an entry to the cache. If it does exist, then set it.
+ * 
+ * @param argv key, value and timeout are passed in command line
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_add(int argc, const char **argv)
+{
+       const char *keystr, *datastr, *timeout_str;
+       time_t timeout;
+       
+       if (argc < 3) {
+               d_printf("\nUsage: net cache add <key string> <data string> <timeout>\n");
+               return -1;
+       }
+       
+       keystr = argv[0];
+       datastr = argv[1];
+       timeout_str = argv[2];
+       
+       /* parse timeout given in command line */
+       timeout = parse_timeout(timeout_str);
+       if (!timeout) {
+               d_printf("Invalid timeout argument.\n");
+               return -1;
+       }
+       
+       if (gencache_set(keystr, datastr, timeout)) {
+               d_printf("New cache entry stored successfully.\n");
+               gencache_shutdown();
+               return 0;
+       }
+       
+       d_printf("Entry couldn't be added. Perhaps there's already such a key.\n");
+       gencache_shutdown();
+       return -1;
+}
+
+
+/**
+ * Set new value of an existing entry in the cache. Fail If the entry doesn't
+ * exist.
+ * 
+ * @param argv key being searched and new value and timeout to set in the entry
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_set(int argc, const char **argv)
+{
+       const char *keystr, *datastr, *timeout_str;
+       time_t timeout;
+       
+       if (argc < 3) {
+               d_printf("\nUsage: net cache set <key string> <data string> <timeout>\n");
+               return -1;
+       }
+       
+       keystr = argv[0];
+       datastr = argv[1];
+       timeout_str = argv[2];
+       
+       /* parse timeout given in command line */
+       timeout = parse_timeout(timeout_str);
+       if (!timeout) {
+               d_printf("Invalid timeout argument.\n");
+               return -1;
+       }
+       
+       if (gencache_set_only(keystr, datastr, timeout)) {
+               d_printf("Cache entry set successfully.\n");
+               gencache_shutdown();
+               return 0;
+       }
+
+       d_printf("Entry couldn't be set. Perhaps there's no such a key.\n");
+       gencache_shutdown();
+       return -1;
+}
+
+
+/**
+ * Delete an entry in the cache
+ * 
+ * @param argv key to delete an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_del(int argc, const char **argv)
+{
+       const char *keystr = argv[0];
+       
+       if (argc < 1) {
+               d_printf("\nUsage: net cache add <key string>\n");
+               return -1;
+       }
+       
+       if(gencache_del(keystr)) {
+               d_printf("Entry deleted.\n");
+               return 0;
+       }
+
+       d_printf("Couldn't delete specified entry\n");
+       return -1;
+}
+
+
+/**
+ * Get and display an entry from the cache
+ * 
+ * @param argv key to search an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_get(int argc, const char **argv)
+{
+       const char* keystr = argv[0];
+       char* valuestr;
+       time_t timeout;
+
+       if (argc < 1) {
+               d_printf("\nUsage: net cache get <key>\n");
+               return -1;
+       }
+       
+       if (gencache_get(keystr, &valuestr, &timeout)) {
+               print_cache_entry(keystr, valuestr, timeout, NULL);
+               return 0;
+       }
+
+       d_printf("Failed to find entry\n");
+       return -1;
+}
+
+
+/**
+ * Search an entry/entries in the cache
+ * 
+ * @param argv key pattern to match the entries to
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_search(int argc, const char **argv)
+{
+       const char* pattern;
+       
+       if (argc < 1) {
+               d_printf("Usage: net cache search <pattern>\n");
+               return -1;
+       }
+       
+       pattern = argv[0];
+       gencache_iterate(print_cache_entry, NULL, pattern);
+       return 0;
+}
+
+
+/**
+ * List the contents of the cache
+ * 
+ * @param argv ignored in this functionailty
+ * @return always returns 0
+ **/
+static int net_cache_list(int argc, const char **argv)
+{
+       const char* pattern = "*";
+       gencache_iterate(print_cache_entry, NULL, pattern);
+       gencache_shutdown();
+       return 0;
+}
+
+
+/**
+ * Flush the whole cache
+ * 
+ * @param argv ignored in this functionality
+ * @return always returns 0
+ **/
+static int net_cache_flush(int argc, const char **argv)
+{
+       const char* pattern = "*";
+       gencache_iterate(delete_cache_entry, NULL, pattern);
+       gencache_shutdown();
+       return 0;
+}
+
+
+/**
+ * Short help
+ *
+ * @param argv ignored in this functionality
+ * @return always returns -1
+ **/
+static int net_cache_usage(int argc, const char **argv)
+{
+       d_printf("  net cache add \t add add new cache entry\n");
+       d_printf("  net cache set \t set new value for existing cache entry\n");
+       d_printf("  net cache del \t delete existing cache entry by key\n");
+       d_printf("  net cache flush \t delete all entries existing in the cache\n");
+       d_printf("  net cache get \t get cache entry by key\n");
+       d_printf("  net cache search \t search for entries in the cache, by given pattern\n");
+       d_printf("  net cache list \t list all cache entries (just like search for \"*\")\n");
+       return -1;
+}
+
+
+/**
+ * Entry point to 'net cache' subfunctionality
+ *
+ * @param argv arguments passed to further called functions
+ * @return whatever further functions return
+ **/
+int net_cache(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"add", net_cache_add},
+               {"set", net_cache_set},
+               {"del", net_cache_del},
+               {"get", net_cache_get},
+               {"search", net_cache_search},
+               {"list", net_cache_list},
+               {"flush", net_cache_flush},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, net_cache_usage);
+}
diff --git a/source4/utils/net_help.c b/source4/utils/net_help.c
new file mode 100644 (file)
index 0000000..4000a24
--- /dev/null
@@ -0,0 +1,199 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   net help commands
+   Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+   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 "../utils/net.h"
+
+int net_common_methods_usage(int argc, const char**argv)
+{
+       d_printf("Valid methods: (auto-detected if not specified)\n");
+       d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+       d_printf("\trpc\t\t\t\tDCE-RPC\n");
+       d_printf("\trap\t\t\t\tRAP (older systems)\n");
+       d_printf("\n");
+       return 0;
+}
+
+int net_common_flags_usage(int argc, const char **argv)
+{
+       d_printf("Valid targets: choose one (none defaults to localhost)\n");
+       d_printf("\t-S or --server=<server>\t\tserver name\n");
+       d_printf("\t-I or --ipaddress=<ipaddr>\taddress of target server\n");
+       d_printf("\t-w or --workgroup=<wg>\t\ttarget workgroup or domain\n");
+
+       d_printf("\n");
+       d_printf("Valid miscellaneous options are:\n"); /* misc options */
+       d_printf("\t-p or --port=<port>\t\tconnection port on target\n");
+       d_printf("\t-W or --myworkgroup=<wg>\tclient workgroup\n");
+       d_printf("\t-d or --debug=<level>\t\tdebug level (0-10)\n");
+       d_printf("\t-n or --myname=<name>\t\tclient name\n");
+       d_printf("\t-U or --user=<name>\t\tuser name\n");
+       d_printf("\t-s or --conf=<path>\t\tpathname of smb.conf file\n");
+       d_printf("\t-l or --long\t\t\tDisplay full information\n");
+       d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n");
+       return -1;
+}
+
+static int help_usage(int argc, const char **argv)
+{
+       d_printf(
+"\n"\
+"Usage: net help <function>\n"\
+"\n"\
+"Valid functions are:\n"\
+"  RPC RAP ADS FILE SHARE SESSION SERVER DOMAIN PRINTQ USER GROUP VALIDATE\n"\
+"  GROUPMEMBER ADMIN SERVICE PASSWORD TIME LOOKUP GETLOCALSID SETLOCALSID\n");
+       return -1;
+}
+
+int net_help_user(int argc, const char **argv)
+{
+       d_printf("\nnet [<method>] user [misc. options] [targets]"\
+                "\n\tList users\n\n");
+       d_printf("net [<method>] user DELETE <name> [misc. options] [targets]"\
+                "\n\tDelete specified user\n");
+       d_printf("\nnet [<method>] user INFO <name> [misc. options] [targets]"\
+                "\n\tList the domain groups of the specified user\n");
+       d_printf("\nnet [<method>] user ADD <name> [password] [-c container] "\
+                "[-F user flags] [misc. options]"\
+                " [targets]\n\tAdd specified user\n");
+
+       net_common_methods_usage(argc, argv);
+       net_common_flags_usage(argc, argv);
+       d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+       d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+       return -1;
+}
+
+int net_help_group(int argc, const char **argv)
+{
+       d_printf("net [<method>] group [misc. options] [targets]"\
+                "\n\tList user groups\n\n");
+       d_printf("net [<method>] group DELETE <name> "\
+                "[misc. options] [targets]"\
+                "\n\tDelete specified group\n");
+       d_printf("\nnet [<method>] group ADD <name> [-C comment] [-c container]"\
+                " [misc. options] [targets]\n\tCreate specified group\n");
+       net_common_methods_usage(argc, argv);
+       net_common_flags_usage(argc, argv);
+       d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+       d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+       return -1;
+}
+
+
+int net_help_join(int argc, const char **argv)
+{
+       d_printf("\nnet [<method>] join [misc. options]\n"
+                "\tjoins this server to a domain\n");
+       d_printf("Valid methods: (auto-detected if not specified)\n");
+       d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+       d_printf("\trpc\t\t\t\tDCE-RPC\n");
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+int net_help_share(int argc, const char **argv)
+{
+       d_printf(
+        "\nnet [<method>] share [misc. options] [targets] \n"
+        "\tenumerates all exported resources (network shares) "
+        "on target server\n\n"
+        "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
+        "\n\tAdds a share from a server (makes the export active)\n\n"
+        "net [<method>] share DELETE <sharename> [misc. options] [targets]\n"
+        "\n\tDeletes a share from a server (makes the export inactive)\n");
+       net_common_methods_usage(argc, argv);
+       net_common_flags_usage(argc, argv);
+       d_printf(
+        "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"
+        "\t-M or --maxusers=<num>\t\tmax users allowed for share\n");
+       return -1;
+}
+
+int net_help_file(int argc, const char **argv)
+{
+       d_printf("net [<method>] file [misc. options] [targets]\n"\
+                "\tlists all open files on file server\n\n");
+       d_printf("net [<method>] file USER <username> "\
+                "[misc. options] [targets]"\
+                "\n\tlists all files opened by username on file server\n\n");
+       d_printf("net [<method>] file CLOSE <id> [misc. options] [targets]\n"\
+                "\tcloses specified file on target server\n\n");
+       d_printf("net [rap] file INFO <id> [misc. options] [targets]\n"\
+                "\tdisplays information about the specified open file\n");
+
+       net_common_methods_usage(argc, argv);
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+static int net_usage(int argc, const char **argv)
+{
+       d_printf("  net time\t\tto view or set time information\n"\
+                "  net lookup\t\tto lookup host name or ip address\n"\
+                "  net user\t\tto manage users\n"\
+                "  net group\t\tto manage groups\n"\
+                "  net join\t\tto join a domain\n"\
+                "  net cache\t\tto operate on cache tdb file\n"\
+                "  net getlocalsid [NAME]\tto get the SID for local name\n"\
+                "  net setlocalsid SID\tto set the local domain SID\n"\
+                "\n"\
+                "  net ads <command>\tto run ADS commands\n"\
+                "  net rap <command>\tto run RAP (pre-RPC) commands\n"\
+                "  net rpc <command>\tto run RPC commands\n"\
+                "\n"\
+                "Type \"net help <option>\" to get more information on that option\n");
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+/*
+  handle "net help *" subcommands
+*/
+int net_help(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADS", net_ads_help},  
+               {"RAP", net_rap_help},
+               {"RPC", net_rpc_help},
+
+               {"FILE", net_help_file},
+               {"SHARE", net_help_share},
+               {"SESSION", net_rap_session_usage},
+               {"SERVER", net_rap_server_usage},
+               {"DOMAIN", net_rap_domain_usage},
+               {"PRINTQ", net_rap_printq_usage},
+               {"USER", net_help_user},
+               {"GROUP", net_help_group},
+               {"JOIN", net_help_join},
+               {"VALIDATE", net_rap_validate_usage},
+               {"GROUPMEMBER", net_rap_groupmember_usage},
+               {"ADMIN", net_rap_admin_usage},
+               {"SERVICE", net_rap_service_usage},
+               {"PASSWORD", net_rap_password_usage},
+               {"TIME", net_time_usage},
+               {"LOOKUP", net_lookup_usage},
+
+               {"HELP", help_usage},
+               {NULL, NULL}};
+
+       return net_run_function(argc, argv, func, net_usage);
+}
diff --git a/source4/utils/net_lookup.c b/source4/utils/net_lookup.c
new file mode 100644 (file)
index 0000000..2710944
--- /dev/null
@@ -0,0 +1,234 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   net lookup command
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+
+   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 "../utils/net.h"
+
+int net_lookup_usage(int argc, const char **argv)
+{
+       d_printf(
+"  net lookup host HOSTNAME <type>\n\tgives IP for a hostname\n\n"
+"  net lookup ldap [domain]\n\tgives IP of domain's ldap server\n\n"
+"  net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n"
+"  net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n"
+"  net lookup master [domain|wg]\n\tgive IP of master browser\n\n"
+);
+       return -1;
+}
+
+/* lookup a hostname giving an IP */
+static int net_lookup_host(int argc, const char **argv)
+{
+       struct in_addr ip;
+       int name_type = 0x20;
+
+       if (argc == 0) return net_lookup_usage(argc, argv);
+       if (argc > 1) name_type = strtol(argv[1], NULL, 0);
+
+       if (!resolve_name(argv[0], &ip, name_type)) {
+               /* we deliberately use DEBUG() here to send it to stderr 
+                  so scripts aren't mucked up */
+               DEBUG(0,("Didn't find %s#%02x\n", argv[0], name_type));
+               return -1;
+       }
+
+       d_printf("%s\n", inet_ntoa(ip));
+       return 0;
+}
+
+static void print_ldap_srvlist(char *srvlist)
+{
+       char *cur, *next;
+       struct in_addr ip;
+       BOOL printit;
+
+       cur = srvlist;
+       do {
+               next = strchr(cur,':');
+               if (next) *next++='\0';
+               printit = resolve_name(cur, &ip, 0x20);
+               cur=next;
+               next=cur ? strchr(cur,' ') :NULL;
+               if (next)
+                       *next++='\0';
+               if (printit)
+                       d_printf("%s:%s\n", inet_ntoa(ip), cur?cur:"");
+               cur = next;
+       } while (next);
+}
+               
+
+static int net_lookup_ldap(int argc, const char **argv)
+{
+#ifdef HAVE_LDAP
+       char *srvlist;
+       const char *domain;
+       int rc;
+       struct in_addr addr;
+       struct hostent *hostent;
+
+       if (argc > 0)
+               domain = argv[0];
+       else
+               domain = opt_target_workgroup;
+
+       DEBUG(9, ("Lookup up ldap for domain %s\n", domain));
+       rc = ldap_domain2hostlist(domain, &srvlist);
+       if ((rc == LDAP_SUCCESS) && srvlist) {
+               print_ldap_srvlist(srvlist);
+               return 0;
+       }
+
+       DEBUG(9, ("Looking up DC for domain %s\n", domain));
+       if (!get_pdc_ip(domain, &addr))
+               return -1;
+
+       hostent = gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr),
+                               AF_INET);
+       if (!hostent)
+               return -1;
+
+       DEBUG(9, ("Found DC with DNS name %s\n", hostent->h_name));
+       domain = strchr(hostent->h_name, '.');
+       if (!domain)
+               return -1;
+       domain++;
+
+       DEBUG(9, ("Looking up ldap for domain %s\n", domain));
+       rc = ldap_domain2hostlist(domain, &srvlist);
+       if ((rc == LDAP_SUCCESS) && srvlist) {
+               print_ldap_srvlist(srvlist);
+               return 0;
+       }
+       return -1;
+#endif
+       DEBUG(1,("No LDAP support\n"));
+       return -1;
+}
+
+static int net_lookup_dc(int argc, const char **argv)
+{
+       struct in_addr *ip_list, addr;
+       char *pdc_str = NULL;
+       const char *domain=opt_target_workgroup;
+       int count, i;
+       BOOL list_ordered;
+
+       if (argc > 0)
+               domain=argv[0];
+
+       /* first get PDC */
+       if (!get_pdc_ip(domain, &addr))
+               return -1;
+
+       asprintf(&pdc_str, "%s", inet_ntoa(addr));
+       d_printf("%s\n", pdc_str);
+
+       if (!get_dc_list(domain, &ip_list, &count, &list_ordered)) {
+               SAFE_FREE(pdc_str);
+               return 0;
+       }
+       for (i=0;i<count;i++) {
+               char *dc_str = inet_ntoa(ip_list[i]);
+               if (!strequal(pdc_str, dc_str))
+                       d_printf("%s\n", dc_str);
+       }
+       SAFE_FREE(pdc_str);
+       return 0;
+}
+
+static int net_lookup_master(int argc, const char **argv)
+{
+       struct in_addr master_ip;
+       const char *domain=opt_target_workgroup;
+
+       if (argc > 0)
+               domain=argv[0];
+
+       if (!find_master_ip(domain, &master_ip))
+               return -1;
+       d_printf("%s\n", inet_ntoa(master_ip));
+       return 0;
+}
+
+static int net_lookup_kdc(int argc, const char **argv)
+{
+#ifdef HAVE_KRB5
+       krb5_error_code rc;
+       krb5_context ctx;
+       struct sockaddr_in *addrs;
+       int num_kdcs,i;
+       krb5_data realm;
+       char **realms;
+
+       rc = krb5_init_context(&ctx);
+       if (rc) {
+               DEBUG(1,("krb5_init_context failed (%s)\n", 
+                        error_message(rc)));
+               return -1;
+       }
+
+       if (argc>0) {
+               realm.data = (krb5_pointer) argv[0];
+               realm.length = strlen(argv[0]);
+       } else if (lp_realm() && *lp_realm()) {
+               realm.data = (krb5_pointer) lp_realm();
+               realm.length = strlen(realm.data);
+       } else {
+               rc = krb5_get_host_realm(ctx, NULL, &realms);
+               if (rc) {
+                       DEBUG(1,("krb5_gethost_realm failed (%s)\n",
+                                error_message(rc)));
+                       return -1;
+               }
+               realm.data = (krb5_pointer) *realms;
+               realm.length = strlen(realm.data);
+       }
+
+       rc = krb5_locate_kdc(ctx, &realm, &addrs, &num_kdcs, 0);
+       if (rc) {
+               DEBUG(1, ("krb5_locate_kdc failed (%s)\n", error_message(rc)));
+               return -1;
+       }
+       for (i=0;i<num_kdcs;i++)
+               if (addrs[i].sin_family == AF_INET) 
+                       d_printf("%s:%hd\n", inet_ntoa(addrs[i].sin_addr),
+                                ntohs(addrs[i].sin_port));
+       return 0;
+
+#endif 
+       DEBUG(1, ("No kerberos support\n"));
+       return -1;
+}
+
+
+/* lookup hosts or IP addresses using internal samba lookup fns */
+int net_lookup(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"HOST", net_lookup_host},
+               {"LDAP", net_lookup_ldap},
+               {"DC", net_lookup_dc},
+               {"MASTER", net_lookup_master},
+               {"KDC", net_lookup_kdc},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, net_lookup_usage);
+}
diff --git a/source4/utils/net_rap.c b/source4/utils/net_rap.c
new file mode 100644 (file)
index 0000000..8f3dd53
--- /dev/null
@@ -0,0 +1,1051 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2001 Steve French  (sfrench@us.ibm.com)
+   Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+   Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+   Originally written by Steve and Jim. Largely rewritten by tridge in
+   November 2001.
+
+   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 "../utils/net.h"
+
+/* The following messages were for error checking that is not properly 
+   reported at the moment.  Which should be reinstated? */
+#define ERRMSG_TARGET_WG_NOT_VALID      "\nTarget workgroup option not valid "\
+                                       "except on net rap server command, ignored"
+#define ERRMSG_INVALID_HELP_OPTION     "\nInvalid help option\n"
+
+#define ERRMSG_BOTH_SERVER_IPADDRESS    "\nTarget server and IP address both "\
+  "specified. Do not set both at the same time.  The target IP address was used\n"
+
+const char *share_type[] = {
+  "Disk",
+  "Print",
+  "Dev",
+  "IPC"
+};
+
+static int errmsg_not_implemented(void)
+{
+       d_printf("\nNot implemented\n");
+       return 0;
+}
+
+int net_rap_file_usage(int argc, const char **argv)
+{
+       return net_help_file(argc, argv);
+}
+
+/***************************************************************************
+  list info on an open file
+***************************************************************************/
+static void file_fn(const char * pPath, const char * pUser, uint16 perms, 
+                   uint16 locks, uint32 id)
+{
+       d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+                id, pUser, perms, locks, pPath);
+}
+
+static void one_file_fn(const char *pPath, const char *pUser, uint16 perms, 
+                       uint16 locks, uint32 id)
+{
+       d_printf("File ID          %d\n"\
+                "User name        %s\n"\
+                "Locks            0x%-4.2x\n"\
+                "Path             %s\n"\
+                "Permissions      0x%x\n",
+                id, pUser, locks, pPath, perms);
+}
+
+
+static int rap_file_close(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc == 0) {
+               d_printf("\nMissing fileid of file to close\n\n");
+               return net_rap_file_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       ret = cli_NetFileClose(cli, atoi(argv[0]));
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_file_info(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc == 0)
+               return net_rap_file_usage(argc, argv);
+       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_file_user(int argc, const char **argv)
+{
+       if (argc == 0)
+               return net_rap_file_usage(argc, argv);
+
+       d_printf("net rap file user not implemented yet\n");
+       return -1;
+}
+
+int net_rap_file(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"CLOSE", rap_file_close},
+               {"USER", rap_file_user},
+               {"INFO", rap_file_info},
+               {NULL, NULL}
+       };
+       
+       if (argc == 0) {
+               struct cli_state *cli;
+               int ret;
+               
+               if (!(cli = net_make_ipc_connection(0))) 
+                        return -1;
+
+               /* list open files */
+               d_printf(
+                "\nEnumerating open files on remote server:\n\n"\
+                "\nFileId  Opened by            Perms  Locks  Path \n"\
+                "------  ---------            -----  -----  ---- \n");
+               ret = cli_NetFileEnum(cli, NULL, NULL, file_fn);
+               cli_shutdown(cli);
+               return ret;
+       }
+       
+       return net_run_function(argc, argv, func, net_rap_file_usage);
+}
+                      
+int net_rap_share_usage(int argc, const char **argv)
+{
+       return net_help_share(argc, argv);
+}
+
+static void long_share_fn(const char *share_name, uint32 type, 
+                         const char *comment, void *state)
+{
+       d_printf("%-12.12s %-8.8s %-50.50s\n",
+                share_name, share_type[type], comment);
+}
+
+static void share_fn(const char *share_name, uint32 type, 
+                    const char *comment, void *state)
+{
+       d_printf("%-12.12s\n", share_name);
+}
+
+static int rap_share_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (argc == 0) {
+               d_printf("\n\nShare name not specified\n");
+               return net_rap_share_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       ret = cli_NetShareDelete(cli, argv[0]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_share_add(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       RAP_SHARE_INFO_2 sinfo;
+       char *p;
+       char *sharename;
+
+       if (argc == 0) {
+               d_printf("\n\nShare name not specified\n");
+               return net_rap_share_usage(argc, argv);
+       }
+                       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       sharename = strdup(argv[0]);
+       p = strchr(sharename, '=');
+       *p = 0;
+       strlcpy(sinfo.share_name, sharename, sizeof(sinfo.share_name));
+       sinfo.reserved1 = '\0';
+       sinfo.share_type = 0;
+       sinfo.comment = smb_xstrdup(opt_comment);
+       sinfo.perms = 0;
+       sinfo.maximum_users = opt_maxusers;
+       sinfo.active_users = 0;
+       sinfo.path = p+1;
+       memset(sinfo.password, '\0', sizeof(sinfo.password));
+       sinfo.reserved2 = '\0';
+       
+       ret = cli_NetShareAdd(cli, &sinfo);
+       cli_shutdown(cli);
+       return ret;
+}
+
+
+int net_rap_share(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"DELETE", rap_share_delete},
+               {"CLOSE", rap_share_delete},
+               {"ADD", rap_share_add},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               struct cli_state *cli;
+               int ret;
+               
+               if (!(cli = net_make_ipc_connection(0))) 
+                       return -1;
+               
+               if (opt_long_list_entries) {
+                       d_printf(
+       "\nEnumerating shared resources (exports) on remote server:\n\n"\
+       "\nShare name   Type     Description\n"\
+       "----------   ----     -----------\n");
+                       ret = cli_RNetShareEnum(cli, long_share_fn, NULL);
+               }
+               ret = cli_RNetShareEnum(cli, share_fn, NULL);
+               cli_shutdown(cli);
+               return ret;
+       }
+
+       return net_run_function(argc, argv, func, net_rap_share_usage);
+}
+                   
+               
+int net_rap_session_usage(int argc, const char **argv)
+{
+       d_printf(
+        "\nnet rap session [misc. options] [targets]"\
+        "\n\tenumerates all active SMB/CIFS sessions on target server\n");
+       d_printf(
+        "\nnet rap session DELETE <client_name> [misc. options] [targets] \n"\
+        "\tor"\
+        "\nnet rap session CLOSE <client_name> [misc. options] [targets]"\
+        "\n\tDeletes (closes) a session from specified client to server\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+    
+static void list_sessions_func(char *wsname, char *username, uint16 conns,
+                       uint16 opens, uint16 users, uint32 sess_time,
+                       uint32 idle_time, uint32 user_flags, char *clitype)
+{
+       int hrs = idle_time / 3600;
+       int min = (idle_time / 60) % 60;
+       int sec = idle_time % 60;
+       
+       d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n",
+                wsname, username, clitype, opens, hrs, min, sec);
+}
+
+static void display_session_func(const char *wsname, const char *username, 
+                                uint16 conns, uint16 opens, uint16 users, 
+                                uint32 sess_time, uint32 idle_time, 
+                                uint32 user_flags, const char *clitype)
+{
+       int ihrs = idle_time / 3600;
+       int imin = (idle_time / 60) % 60;
+       int isec = idle_time % 60;
+       int shrs = sess_time / 3600;
+       int smin = (sess_time / 60) % 60;
+       int ssec = sess_time % 60;
+       d_printf("User name       %-20.20s\n"\
+                "Computer        %-20.20s\n"\
+                "Guest logon     %-20.20s\n"\
+                "Client Type     %-40.40s\n"\
+                "Sess time       %2.2d:%2.2d:%2.2d\n"\
+                "Idle time       %2.2d:%2.2d:%2.2d\n", 
+                username, wsname, 
+                (user_flags&0x0)?"yes":"no", clitype,
+                shrs, smin, ssec, ihrs, imin, isec);
+}
+
+static void display_conns_func(uint16 conn_id, uint16 conn_type, uint16 opens,
+                              uint16 users, uint32 conn_time,
+                              const char *username, const char *netname)
+{
+       d_printf("%-14.14s %-8.8s %5d\n",
+                netname, share_type[conn_type], opens);
+}
+
+static int rap_session_info(int argc, const char **argv)
+{
+       const char *sessname;
+       struct cli_state *cli;
+       int ret;
+       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       if (argc == 0) 
+                return net_rap_session_usage(argc, argv);
+
+       sessname = argv[0];
+
+       ret = cli_NetSessionGetInfo(cli, sessname, display_session_func);
+       if (ret < 0) {
+               cli_shutdown(cli);
+                return ret;
+       }
+
+       d_printf("Share name     Type     # Opens\n-------------------------"\
+                "-----------------------------------------------------\n");
+       ret = cli_NetConnectionEnum(cli, sessname, display_conns_func);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_session_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       if (argc == 0) 
+                return net_rap_session_usage(argc, argv);
+
+       ret = cli_NetSessionDel(cli, argv[0]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_session(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"INFO", rap_session_info},
+               {"DELETE", rap_session_delete},
+               {"CLOSE", rap_session_delete},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               struct cli_state *cli;
+               int ret;
+               
+               if (!(cli = net_make_ipc_connection(0))) 
+                       return -1;
+
+               d_printf("Computer             User name            "\
+                        "Client Type        Opens Idle time\n"\
+                        "------------------------------------------"\
+                        "------------------------------------\n");
+               ret = cli_NetSessionEnum(cli, list_sessions_func);
+
+               cli_shutdown(cli);
+               return ret;
+       }
+
+       return net_run_function(argc, argv, func, net_rap_session_usage);
+}
+       
+/****************************************************************************
+list a server name
+****************************************************************************/
+static void display_server_func(const char *name, uint32 m,
+                               const char *comment, void * reserved)
+{
+       d_printf("\t%-16.16s     %s\n", name, comment);
+}
+
+
+int net_rap_server_usage(int argc, const char **argv)
+{
+       d_printf("net rap server [misc. options] [target]\n\t"\
+                "lists the servers in the specified domain or workgroup.\n");
+       d_printf("\n\tIf domain is not specified, it uses the current"\
+                " domain or workgroup as\n\tthe default.\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+                   
+int net_rap_server(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       d_printf("\nEnumerating servers in this domain or workgroup: \n\n"\
+                "\tServer name          Server description\n"\
+                "\t-------------        ----------------------------\n");
+
+       ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, 
+                               display_server_func,NULL); 
+       cli_shutdown(cli);
+       return ret;     
+}
+                     
+int net_rap_domain_usage(int argc, const char **argv)
+{
+       d_printf("net rap domain [misc. options] [target]\n\tlists the"\
+                " domains or workgroups visible on the current network\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+                 
+int net_rap_domain(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       d_printf("\nEnumerating domains:\n\n"\
+                "\tDomain name          Server name of Browse Master\n"\
+                "\t-------------        ----------------------------\n");
+
+       ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+                               display_server_func,NULL);      
+       cli_shutdown(cli);
+       return ret;     
+}
+                     
+int net_rap_printq_usage(int argc, const char **argv)
+{
+       d_printf(
+        "net rap printq [misc. options] [targets]\n"\
+        "\tor\n"\
+        "net rap printq list [<queue_name>] [misc. options] [targets]\n"\
+        "\tlists the specified queue and jobs on the target server.\n"\
+        "\tIf the queue name is not specified, all queues are listed.\n\n");
+       d_printf(
+        "net rap printq delete [<queue name>] [misc. options] [targets]\n"\
+        "\tdeletes the specified job number on the target server, or the\n"\
+        "\tprinter queue if no job number is specified\n");
+
+       net_common_flags_usage(argc, argv);
+        d_printf("\t-j or --jobid=<job id>\t\tjob id\n");
+
+       return -1;
+}      
+
+static void enum_queue(const char *queuename, uint16 pri, uint16 start, 
+                      uint16 until, const char *sep, const char *pproc, 
+                      const char *dest, const char *qparms, 
+                      const char *qcomment, uint16 status, uint16 jobcount) 
+{
+       d_printf("%-17.17s Queue %5d jobs                      ",
+                queuename, jobcount);
+
+       switch (status) {
+       case 0:
+               d_printf("*Printer Active*\n");
+               break;
+       case 1:
+               d_printf("*Printer Paused*\n");
+               break;
+       case 2:
+               d_printf("*Printer error*\n");
+               break;
+       case 3:
+               d_printf("*Delete Pending*\n");
+               break;
+       default:
+               d_printf("**UNKNOWN STATUS**\n");
+       }
+}
+
+static void enum_jobs(uint16 jobid, const char *ownername, 
+                     const char *notifyname, const char *datatype,
+                     const char *jparms, uint16 pos, uint16 status, 
+                     const char *jstatus, unsigned int submitted, unsigned int jobsize, 
+                     const char *comment)
+{
+       d_printf("     %-23.23s %5d %9d            ",
+                ownername, jobid, jobsize);
+       switch (status) {
+       case 0:
+               d_printf("Waiting\n");
+               break;
+       case 1:
+               d_printf("Held in queue\n");
+               break;
+       case 2:
+               d_printf("Spooling\n");
+               break;
+       case 3:
+               d_printf("Printing\n");
+               break;
+       default:
+               d_printf("**UNKNOWN STATUS**\n");
+       }
+}
+
+#define PRINTQ_ENUM_DISPLAY \
+    "Print queues at \\\\%s\n\n"\
+    "Name                         Job #      Size            Status\n\n"\
+    "------------------------------------------------------------------"\
+    "-------------\n"
+
+static int rap_printq_info(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (argc == 0) 
+                return net_rap_printq_usage(argc, argv);
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+       ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_printq_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (argc == 0) 
+                return net_rap_printq_usage(argc, argv);
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       ret = cli_printjob_del(cli, atoi(argv[0]));
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_printq(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       struct functable func[] = {
+               {"INFO", rap_printq_info},
+               {"DELETE", rap_printq_delete},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               if (!(cli = net_make_ipc_connection(0))) 
+                       return -1;
+
+               d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+               ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs);
+               cli_shutdown(cli);
+               return ret;
+       }
+
+       return net_run_function(argc, argv, func, net_rap_printq_usage);
+}
+
+       
+static int net_rap_user_usage(int argc, const char **argv)
+{
+       return net_help_user(argc, argv);
+} 
+       
+static void user_fn(const char *user_name, const char *comment,
+                   const char * home_dir, const char * logon_script,
+                   void *state)
+{
+       d_printf("%-21.21s\n", user_name);
+}
+
+static void long_user_fn(const char *user_name, const char *comment,
+                        const char * home_dir, const char * logon_script, 
+                        void *state)
+{
+       d_printf("%-21.21s %-50.50s\n",
+                user_name, comment);
+}
+
+static void group_member_fn(const char *user_name, void *state)
+{
+       d_printf("%-21.21s\n", user_name);
+}
+
+static int rap_user_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (argc == 0) {
+               d_printf("\n\nUser name not specified\n");
+                return net_rap_user_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       ret = cli_NetUserDelete(cli, argv[0]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_user_add(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       RAP_USER_INFO_1 userinfo;
+
+       if (argc == 0) {
+               d_printf("\n\nUser name not specified\n");
+                return net_rap_user_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+                       
+       safe_strcpy(userinfo.user_name, argv[0], sizeof(userinfo.user_name));
+       if (opt_flags == -1) 
+                opt_flags = 0x21; 
+                       
+       userinfo.userflags = opt_flags;
+       userinfo.reserved1 = '\0';
+       userinfo.comment = smb_xstrdup(opt_comment);
+       userinfo.priv = 1; 
+       userinfo.home_dir = NULL;
+       userinfo.logon_script = NULL;
+
+       ret = cli_NetUserAdd(cli, &userinfo);
+
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_user_info(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc == 0) {
+               d_printf("\n\nUser name not specified\n");
+                return net_rap_user_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+
+       ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL);
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_user(int argc, const char **argv)
+{
+       int ret = -1;
+       struct functable func[] = {
+               {"ADD", rap_user_add},
+               {"INFO", rap_user_info},
+               {"DELETE", rap_user_delete},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               struct cli_state *cli;
+               if (!(cli = net_make_ipc_connection(0)))
+                        goto done;
+               if (opt_long_list_entries) {
+                       d_printf("\nUser name             Comment"\
+                                "\n-----------------------------\n");
+                       ret = cli_RNetUserEnum(cli, long_user_fn, NULL);
+                       cli_shutdown(cli);
+                       goto done;
+               }
+               ret = cli_RNetUserEnum(cli, user_fn, NULL); 
+               cli_shutdown(cli);
+               goto done;
+       }
+
+       ret = net_run_function(argc, argv, func, net_rap_user_usage);
+ done:
+       if (ret != 0) {
+               DEBUG(1, ("Net user returned: %d\n", ret));
+       }
+       return ret;
+}
+
+
+int net_rap_group_usage(int argc, const char **argv)
+{
+       return net_help_group(argc, argv);
+}
+
+static void long_group_fn(const char *group_name, const char *comment,
+                         void *state)
+{
+       d_printf("%-21.21s %-50.50s\n", group_name, comment);
+}
+
+static void group_fn(const char *group_name, const char *comment, void *state)
+{
+       d_printf("%-21.21s\n", group_name);
+}
+
+static int rap_group_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc == 0) {
+               d_printf("\n\nGroup name not specified\n");
+                return net_rap_group_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+
+       ret = cli_NetGroupDelete(cli, argv[0]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_group_add(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       RAP_GROUP_INFO_1 grinfo;
+
+       if (argc == 0) {
+               d_printf("\n\nGroup name not specified\n");
+                return net_rap_group_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+                       
+       /* BB check for length 21 or smaller explicitly ? BB */
+       safe_strcpy(grinfo.group_name, argv[0], sizeof(grinfo.group_name));
+       grinfo.reserved1 = '\0';
+       grinfo.comment = smb_xstrdup(opt_comment);
+       
+       ret = cli_NetGroupAdd(cli, &grinfo);
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_group(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADD", rap_group_add},
+               {"DELETE", rap_group_delete},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               struct cli_state *cli;
+               int ret;
+               if (!(cli = net_make_ipc_connection(0)))
+                        return -1;
+               if (opt_long_list_entries) {
+                       d_printf("Group name            Comment\n");
+                       d_printf("-----------------------------\n");
+                       ret = cli_RNetGroupEnum(cli, long_group_fn, NULL);
+                       cli_shutdown(cli);
+                       return ret;
+               }
+               ret = cli_RNetGroupEnum(cli, group_fn, NULL); 
+               cli_shutdown(cli);
+               return ret;
+       }
+
+       return net_run_function(argc, argv, func, net_rap_group_usage);
+}
+
+int net_rap_groupmember_usage(int argc, const char **argv)
+{
+       d_printf(
+        "net rap groupmember LIST <group> [misc. options] [targets]"\
+        "\n\t Enumerate users in a group\n"\
+        "\nnet rap groupmember DELETE <group> <user> [misc. options] "\
+        "[targets]\n\t Delete sepcified user from specified group\n"\
+        "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]"\
+        "\n\t Add specified user to specified group\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
+static int rap_groupmember_add(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc != 2) {
+               d_printf("\n\nGroup or user name not specified\n");
+                return net_rap_groupmember_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+
+       ret = cli_NetGroupAddUser(cli, argv[0], argv[1]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_groupmember_delete(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc != 2) {
+               d_printf("\n\nGroup or user name not specified\n");
+                return net_rap_groupmember_usage(argc, argv);
+       }
+       
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+
+       ret = cli_NetGroupDelUser(cli, argv[0], argv[1]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+static int rap_groupmember_list(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       if (argc == 0) {
+               d_printf("\n\nGroup name not specified\n");
+                return net_rap_groupmember_usage(argc, argv);
+       }
+
+       if (!(cli = net_make_ipc_connection(0)))
+                return -1;
+
+       ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL ); 
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_groupmember(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"ADD", rap_groupmember_add},
+               {"LIST", rap_groupmember_list},
+               {"DELETE", rap_groupmember_delete},
+               {NULL, NULL}
+       };
+       
+       return net_run_function(argc, argv, func, net_rap_groupmember_usage);
+}
+
+int net_rap_validate_usage(int argc, const char **argv)
+{
+       d_printf("net rap validate <username> [password]\n"\
+                "\tValidate user and password to check whether they"\
+                " can access target server or domain\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+int net_rap_validate(int argc, const char **argv)
+{
+       return errmsg_not_implemented();
+}
+
+int net_rap_service_usage(int argc, const char **argv)
+{
+       d_printf("net rap service [misc. options] [targets] \n"\
+                "\tlists all running service daemons on target server\n");
+       d_printf("\nnet rap service START <name> [service startup arguments]"\
+                " [misc. options] [targets]"\
+                "\n\tStart named service on remote server\n");
+       d_printf("\nnet rap service STOP <name> [misc. options] [targets]\n"\
+                "\n\tStop named service on remote server\n");
+    
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+static int rap_service_start(int argc, const char **argv)
+{
+       return errmsg_not_implemented();
+}
+
+static int rap_service_stop(int argc, const char **argv)
+{
+       return errmsg_not_implemented();
+}
+
+int net_rap_service(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"START", rap_service_start},
+               {"STOP", rap_service_stop},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               struct cli_state *cli;
+               int ret;
+               if (!(cli = net_make_ipc_connection(0))) 
+                       return -1;
+
+               if (opt_long_list_entries) {
+                       d_printf("Service name          Comment\n");
+                       d_printf("-----------------------------\n");
+                       ret = cli_RNetServiceEnum(cli, long_group_fn, NULL);
+               }
+               ret = cli_RNetServiceEnum(cli, group_fn, NULL); 
+               cli_shutdown(cli);
+               return ret;
+       }
+
+       return net_run_function(argc, argv, func, net_rap_service_usage);
+}
+
+int net_rap_password_usage(int argc, const char **argv)
+{
+       d_printf(
+        "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n"\
+        "\tchanges the password for the specified user at target\n"); 
+       
+       return -1;
+}
+
+
+int net_rap_password(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       int ret;
+       
+       if (argc < 3) 
+                return net_rap_password_usage(argc, argv);
+
+       if (!(cli = net_make_ipc_connection(0))) 
+                return -1;
+
+       /* BB Add check for password lengths? */
+       ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]);
+       cli_shutdown(cli);
+       return ret;
+}
+
+int net_rap_admin_usage(int argc, const char **argv)
+{
+       d_printf(
+   "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]"\
+   "\n\texecutes a remote command on an os/2 target server\n"); 
+       
+       return -1;
+}
+
+
+int net_rap_admin(int argc, const char **argv)
+{
+       return errmsg_not_implemented();
+}
+
+/* The help subsystem for the RAP subcommand */
+
+int net_rap_usage(int argc, const char **argv)
+{
+       d_printf("  net rap domain \tto list domains \n"\
+                "  net rap file \t\tto list open files on a server \n"\
+                "  net rap group \tto list user groups  \n"\
+                "  net rap groupmember \tto list users in a group \n"\
+                "  net rap password \tto change the password of a user\n"\
+                "  net rap printq \tto list the print queues on a server\n"\
+                "  net rap server \tto list servers in a domain\n"\
+                "  net rap session \tto list clients with open sessions to a server\n"\
+                "  net rap share \tto list shares exported by a server\n"\
+                "  net rap user \t\tto list users\n"\
+                "  net rap validate \tto check whether a user and the corresponding password are valid\n"\
+                "  net rap help\n"\
+                "\nType \"net help <option>\" to get more information on that option\n\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+/*
+  handle "net rap help *" subcommands
+*/
+int net_rap_help(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"FILE", net_rap_file_usage},
+               {"SHARE", net_rap_share_usage},
+               {"SESSION", net_rap_session_usage},
+               {"SERVER", net_rap_server_usage},
+               {"DOMAIN", net_rap_domain_usage},
+               {"PRINTQ", net_rap_printq_usage},
+               {"USER", net_rap_user_usage},
+               {"GROUP", net_rap_group_usage},
+               {"VALIDATE", net_rap_validate_usage},
+               {"GROUPMEMBER", net_rap_groupmember_usage},
+               {"ADMIN", net_rap_admin_usage},
+               {"SERVICE", net_rap_service_usage},
+               {"PASSWORD", net_rap_password_usage},
+               {NULL, NULL}};
+
+       return net_run_function(argc, argv, func, net_rap_usage);
+}
+
+/* Entry-point for all the RAP functions. */
+
+int net_rap(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"FILE", net_rap_file},
+               {"SHARE", net_rap_share},
+               {"SESSION", net_rap_session},
+               {"SERVER", net_rap_server},
+               {"DOMAIN", net_rap_domain},
+               {"PRINTQ", net_rap_printq},
+               {"USER", net_rap_user},
+               {"GROUP", net_rap_group},
+               {"VALIDATE", net_rap_validate},
+               {"GROUPMEMBER", net_rap_groupmember},
+               {"ADMIN", net_rap_admin},
+               {"SERVICE", net_rap_service},   
+               {"PASSWORD", net_rap_password},
+               {"HELP", net_rap_help},
+               {NULL, NULL}
+       };
+       
+       return net_run_function(argc, argv, func, net_rap_usage);
+}
+
diff --git a/source4/utils/net_rpc.c b/source4/utils/net_rpc.c
new file mode 100644 (file)
index 0000000..89ee34a
--- /dev/null
@@ -0,0 +1,2262 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+   Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+   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 "../utils/net.h"
+
+/**
+ * @file net_rpc.c
+ *
+ * @brief RPC based subcommands for the 'net' utility.
+ *
+ * This file should contain much of the functionality that used to
+ * be found in rpcclient, execpt that the commands should change 
+ * less often, and the fucntionality should be sane (the user is not 
+ * expected to know a rid/sid before they conduct an operation etc.)
+ *
+ * @todo Perhaps eventually these should be split out into a number
+ * of files, as this could get quite big.
+ **/
+
+
+/* A function of this type is passed to the 'run_rpc_command' wrapper */
+typedef NTSTATUS (*rpc_command_fn)(const DOM_SID *, struct cli_state *, TALLOC_CTX *, int, const char **);
+
+/**
+ * Many of the RPC functions need the domain sid.  This function gets
+ *  it at the start of every run 
+ *
+ * @param cli A cli_state already connected to the remote machine
+ *
+ * @return The Domain SID of the remote machine.
+ **/
+
+static DOM_SID *net_get_remote_domain_sid(struct cli_state *cli)
+{
+       DOM_SID *domain_sid;
+       POLICY_HND pol;
+       NTSTATUS result = NT_STATUS_OK;
+       uint32 info_class = 5;
+       fstring domain_name;
+       TALLOC_CTX *mem_ctx;
+       
+       if (!(domain_sid = malloc(sizeof(DOM_SID)))){
+               DEBUG(0,("net_get_remote_domain_sid: malloc returned NULL!\n"));
+               goto error;
+       }
+           
+       if (!(mem_ctx=talloc_init("net_get_remote_domain_sid")))
+       {
+               DEBUG(0,("net_get_remote_domain_sid: talloc_init returned NULL!\n"));
+               goto error;
+       }
+
+
+       if (!cli_nt_session_open (cli, PI_LSARPC)) {
+               fprintf(stderr, "could not initialise lsa pipe\n");
+               goto error;
+       }
+       
+       result = cli_lsa_open_policy(cli, mem_ctx, True, 
+                                    SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                    &pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto error;
+       }
+
+       result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class, 
+                                          domain_name, domain_sid);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto error;
+       }
+
+       cli_lsa_close(cli, mem_ctx, &pol);
+       cli_nt_session_close(cli);
+       talloc_destroy(mem_ctx);
+
+       return domain_sid;
+
+ error:
+       fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               fprintf(stderr, "error: %s\n", nt_errstr(result));
+       }
+
+       exit(1);
+}
+
+/**
+ * Run a single RPC command, from start to finish.
+ *
+ * @param pipe_name the pipe to connect to (usually a PIPE_ constant)
+ * @param conn_flag a NET_FLAG_ combination.  Passed to 
+ *                   net_make_ipc_connection.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ * @return A shell status integer (0 for success)
+ */
+
+static int run_rpc_command(struct cli_state *cli_arg, const int pipe_idx, int conn_flags,
+                           rpc_command_fn fn,
+                           int argc, const char **argv) 
+{
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS nt_status;
+       DOM_SID *domain_sid;
+
+       /* make use of cli_state handed over as an argument, if possible */
+       if (!cli_arg)
+               cli = net_make_ipc_connection(conn_flags);
+       else
+               cli = cli_arg;
+
+       if (!cli) {
+               return -1;
+       }
+
+       domain_sid = net_get_remote_domain_sid(cli);
+
+       /* Create mem_ctx */
+       
+       if (!(mem_ctx = talloc_init("run_rpc_command"))) {
+               DEBUG(0, ("talloc_init() failed\n"));
+               cli_shutdown(cli);
+               return -1;
+       }
+       
+       if (!cli_nt_session_open(cli, pipe_idx)) {
+               DEBUG(0, ("Could not initialise pipe\n"));
+       }
+       
+       nt_status = fn(domain_sid, cli, mem_ctx, argc, argv);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status)));
+       } else {
+               DEBUG(5, ("rpc command function succedded\n"));
+       }
+               
+           
+       if (cli->nt_pipe_fnum)
+               cli_nt_session_close(cli);
+       
+       /* close the connection only if it was opened here */
+       if (!cli_arg)
+               cli_shutdown(cli);
+       
+       talloc_destroy(mem_ctx);
+
+       return (!NT_STATUS_IS_OK(nt_status));
+}
+
+
+/****************************************************************************/
+
+
+/** 
+ * Force a change of the trust acccount password.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_changetrustpw_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                      int argc, const char **argv) {
+       
+       return trust_pw_find_change_and_store_it(cli, mem_ctx, opt_target_workgroup);
+}
+
+/** 
+ * Force a change of the trust acccount password.
+ *
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_changetrustpw(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, rpc_changetrustpw_internals,
+                              argc, argv);
+}
+
+
+/****************************************************************************/
+
+
+/** 
+ * Join a domain, the old way.
+ *
+ * This uses 'machinename' as the inital password, and changes it. 
+ *
+ * The password should be created with 'server manager' or eqiv first.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_join_oldstyle_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                      int argc, const char **argv) {
+       
+       fstring trust_passwd;
+       unsigned char orig_trust_passwd_hash[16];
+       NTSTATUS result;
+
+       fstrcpy(trust_passwd, lp_netbios_name());
+       strlower(trust_passwd);
+
+       /*
+        * Machine names can be 15 characters, but the max length on
+        * a password is 14.  --jerry
+        */
+
+       trust_passwd[14] = '\0';
+
+       E_md4hash(trust_passwd, orig_trust_passwd_hash);
+
+       result = trust_pw_change_and_store_it(cli, mem_ctx, orig_trust_passwd_hash);
+
+       if (NT_STATUS_IS_OK(result))
+               printf("Joined domain %s.\n",lp_workgroup());
+
+       return result;
+}
+
+/** 
+ * Join a domain, the old way.
+ *
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int net_rpc_join_oldstyle(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, rpc_join_oldstyle_internals,
+                              argc, argv);
+}
+
+/** 
+ * Basic usage function for 'net rpc join'
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+static int rpc_join_usage(int argc, const char **argv) 
+{      
+       d_printf("net rpc join -U <username>[%%password] [options]\n"\
+                "\t to join a domain with admin username & password\n"\
+                "\t\t password will be prompted if none is specified\n");
+       d_printf("net rpc join [options except -U]\n"\
+                "\t to join a domain created in server manager\n\n\n");
+
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+/** 
+ * 'net rpc join' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * Main 'net_rpc_join()' (where the admain username/password is used) is 
+ * in net_rpc_join.c
+ * Assume if a -U is specified, it's the new style, otherwise it's the
+ * old style.  If 'oldstyle' is specfied explicity, do it and don't prompt.
+ **/
+
+int net_rpc_join(int argc, const char **argv) 
+{
+       struct functable func[] = {
+               {"oldstyle", net_rpc_join_oldstyle},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               if ((net_rpc_join_oldstyle(argc, argv) == 0))
+                       return 0;
+               
+               return net_rpc_join_newstyle(argc, argv);
+       }
+
+       return net_run_function(argc, argv, func, rpc_join_usage);
+}
+
+
+
+/** 
+ * display info about a rpc domain
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_info_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                  TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       SAM_UNK_CTR ctr;
+       fstring sid_str;
+
+       sid_to_string(sid_str, domain_sid);
+
+       /* Get sam policy handle */     
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       
+       /* Get domain policy handle */
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       ZERO_STRUCT(ctr);
+       result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+                                        2, &ctr);
+       if (NT_STATUS_IS_OK(result)) {
+               TALLOC_CTX *ctx = talloc_init("rpc_info_internals");
+               d_printf("Domain Name: %s\n", unistr2_tdup(ctx, &ctr.info.inf2.uni_domain));
+               d_printf("Domain SID: %s\n", sid_str);
+               d_printf("Sequence number: %u\n", ctr.info.inf2.seq_num);
+               d_printf("Num users: %u\n", ctr.info.inf2.num_domain_usrs);
+               d_printf("Num domain groups: %u\n", ctr.info.inf2.num_domain_grps);
+               d_printf("Num local groups: %u\n", ctr.info.inf2.num_local_grps);
+               talloc_destroy(ctx);
+       }
+
+ done:
+       return result;
+}
+
+
+/** 
+ * 'net rpc info' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+int net_rpc_info(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, 
+                              rpc_info_internals,
+                              argc, argv);
+}
+
+
+/** 
+ * Fetch domain SID into the local secrets.tdb
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_getsid_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                  TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       fstring sid_str;
+
+       sid_to_string(sid_str, domain_sid);
+       d_printf("Storing SID %s for Domain %s in secrets.tdb\n",
+                sid_str, lp_workgroup());
+
+       if (!secrets_store_domain_sid(lp_netbios_name(), domain_sid)) {
+               DEBUG(0,("Can't store domain SID\n"));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS_OK;
+}
+
+
+/** 
+ * 'net rpc getsid' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+int net_rpc_getsid(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, 
+                              rpc_getsid_internals,
+                              argc, argv);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc user'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv.  Initial components are already
+ *             stripped.
+ **/
+
+static int rpc_user_usage(int argc, const char **argv)
+{
+       return net_help_user(argc, argv);
+}
+
+/** 
+ * Add a new user to a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_add_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                      int argc, const char **argv) {
+       
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       const char *acct_name;
+       uint16 acb_info;
+       uint32 unknown, user_rid;
+
+       if (argc != 1) {
+               d_printf("User must be specified\n");
+               rpc_user_usage(argc, argv);
+               return NT_STATUS_OK;
+       }
+
+       acct_name = argv[0];
+
+       /* Get sam policy handle */
+       
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       
+       /* Get domain policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Create domain user */
+
+       acb_info = ACB_NORMAL;
+       unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+       result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+                                         acct_name, acb_info, unknown,
+                                         &user_pol, &user_rid);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+ done:
+       if (!NT_STATUS_IS_OK(result)) {
+               d_printf("Failed to add user %s - %s\n", acct_name, 
+                        nt_errstr(result));
+       } else {
+               d_printf("Added user %s\n", acct_name);
+       }
+       return result;
+}
+
+/** 
+ * Add a new user to a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_add(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_add_internals,
+                              argc, argv);
+}
+
+/** 
+ * Delete a user from a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_del_internals(const DOM_SID *domain_sid, 
+                                      struct cli_state *cli, 
+                                      TALLOC_CTX *mem_ctx, 
+                                      int argc, const char **argv)
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       POLICY_HND connect_pol, domain_pol, user_pol;
+
+       if (argc < 1) {
+               d_printf("User must be specified\n");
+               rpc_user_usage(argc, argv);
+               return NT_STATUS_OK;
+       }
+       /* Get sam policy and domain handles */
+
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Get handle on user */
+
+       {
+               uint32 *user_rids, num_rids, *name_types;
+               uint32 flags = 0x000003e8; /* Unknown */
+
+               result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+                                              flags, 1, &argv[0],
+                                              &num_rids, &user_rids,
+                                              &name_types);
+
+               if (!NT_STATUS_IS_OK(result)) {
+                       goto done;
+               }
+
+               result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                           MAXIMUM_ALLOWED_ACCESS,
+                                           user_rids[0], &user_pol);
+
+               if (!NT_STATUS_IS_OK(result)) {
+                       goto done;
+               }
+       }
+
+       /* Delete user */
+
+       result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Display results */
+
+ done:
+       return result;
+
+}      
+
+/** 
+ * Delete a user from a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_delete(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_del_internals,
+                              argc, argv);
+}
+
+/** 
+ * List user's groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_user_info_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                       TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 *rids, num_rids, *name_types, num_names;
+       uint32 flags = 0x000003e8; /* Unknown */
+       int i;
+       char **names;
+       DOM_GID *user_gids;
+
+       if (argc < 1) {
+               d_printf("User must be specified\n");
+               rpc_user_usage(argc, argv);
+               return NT_STATUS_OK;
+       }
+       /* Get sam policy handle */
+       
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) goto done;
+       
+       /* Get domain policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) goto done;
+
+       /* Get handle on user */
+
+       result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+                                      flags, 1, &argv[0],
+                                      &num_rids, &rids, &name_types);
+
+       if (!NT_STATUS_IS_OK(result)) goto done;
+
+       result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                   MAXIMUM_ALLOWED_ACCESS,
+                                   rids[0], &user_pol);
+       if (!NT_STATUS_IS_OK(result)) goto done;
+
+       result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+                                          &num_rids, &user_gids);
+
+       /* Look up rids */
+
+       rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+       for (i = 0; i < num_rids; i++)
+                rids[i] = user_gids[i].g_rid;
+
+       result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+                                     flags, num_rids, rids,
+                                     &num_names, &names, &name_types);
+
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Display results */
+
+       for (i = 0; i < num_names; i++)
+               printf("%s\n", names[i]);
+
+ done:
+       return result;
+}
+
+/** 
+ * List a user's groups from a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_info(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_info_internals,
+                              argc, argv);
+}
+
+/** 
+ * List users on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_user_list_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                       TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx=0, num_entries, i, loop_count = 0;
+       SAM_DISPINFO_CTR ctr;
+       SAM_DISPINFO_1 info1;
+
+       /* Get sam policy handle */
+       
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       
+       /* Get domain policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Query domain users */
+       ZERO_STRUCT(ctr);
+       ZERO_STRUCT(info1);
+       ctr.sam.info1 = &info1;
+       if (opt_long_list_entries)
+               d_printf("\nUser name             Comment"\
+                        "\n-----------------------------\n");
+       do {
+               fstring user, desc;
+               uint32 max_entries, max_size;
+
+               get_query_dispinfo_params(
+                       loop_count, &max_entries, &max_size);
+
+               result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+                                                &start_idx, 1, &num_entries,
+                                                max_entries, max_size, &ctr);
+               loop_count++;
+
+               for (i = 0; i < num_entries; i++) {
+                       unistr2_to_ascii(user, &(&ctr.sam.info1->str[i])->uni_acct_name, sizeof(user)-1);
+                       if (opt_long_list_entries) 
+                               unistr2_to_ascii(desc, &(&ctr.sam.info1->str[i])->uni_acct_desc, sizeof(desc)-1);
+                       
+                       if (opt_long_list_entries)
+                               printf("%-21.21s %-50.50s\n", user, desc);
+                       else
+                               printf("%s\n", user);
+               }
+       } while (!NT_STATUS_IS_OK(result));
+
+ done:
+       return result;
+}
+
+/** 
+ * 'net rpc user' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_user(int argc, const char **argv) 
+{
+       struct functable func[] = {
+               {"add", rpc_user_add},
+               {"info", rpc_user_info},
+               {"delete", rpc_user_delete},
+               {NULL, NULL}
+       };
+       
+       if (argc == 0) {
+               if (opt_long_list_entries) {
+               } else {
+               }
+                       return run_rpc_command(NULL,PI_SAMR, 0, 
+                                              rpc_user_list_internals,
+                                              argc, argv);
+       }
+
+       return net_run_function(argc, argv, func, rpc_user_usage);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc group'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv.  Initial components are already
+ *             stripped.
+ **/
+
+static int rpc_group_usage(int argc, const char **argv)
+{
+       return net_help_group(argc, argv);
+}
+
+/** 
+ * List groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_group_list_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                        TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       POLICY_HND connect_pol, domain_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       uint32 start_idx=0, max_entries=250, num_entries, i;
+       struct acct_info *groups;
+       DOM_SID global_sid_Builtin;
+
+       string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+       /* Get sam policy handle */
+       
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, 
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       
+       /* Get domain policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Query domain groups */
+       if (opt_long_list_entries)
+               d_printf("\nGroup name            Comment"\
+                        "\n-----------------------------\n");
+       do {
+               result = cli_samr_enum_dom_groups(cli, mem_ctx, &domain_pol,
+                                                 &start_idx, max_entries,
+                                                 &groups, &num_entries);
+                                                
+               for (i = 0; i < num_entries; i++) {
+                       if (opt_long_list_entries)
+                               printf("%-21.21s %-50.50s\n", 
+                                      groups[i].acct_name,
+                                      groups[i].acct_desc);
+                       else
+                               printf("%-21.21s\n", groups[i].acct_name);
+               }
+       } while (!NT_STATUS_IS_OK(result));
+       /* query domain aliases */
+       do {
+               result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+                                                 &start_idx, max_entries,
+                                                 &groups, &num_entries);
+                                                
+               for (i = 0; i < num_entries; i++) {
+                       if (opt_long_list_entries)
+                               printf("%-21.21s %-50.50s\n", 
+                                      groups[i].acct_name,
+                                      groups[i].acct_desc);
+                       else
+                               printf("%-21.21s\n", groups[i].acct_name);
+               }
+       } while (!NT_STATUS_IS_OK(result));
+       cli_samr_close(cli, mem_ctx, &domain_pol);
+       /* Get builtin policy handle */
+       
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     &global_sid_Builtin, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       /* query builtin aliases */
+       do {
+               result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+                                                 &start_idx, max_entries,
+                                                 &groups, &num_entries);
+                                                
+               for (i = 0; i < num_entries; i++) {
+                       if (opt_long_list_entries)
+                               printf("%-21.21s %-50.50s\n", 
+                                      groups[i].acct_name,
+                                      groups[i].acct_desc);
+                       else
+                               printf("%s\n", groups[i].acct_name);
+               }
+       } while (!NT_STATUS_IS_OK(result));
+
+ done:
+       return result;
+}
+
+/** 
+ * 'net rpc group' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_group(int argc, const char **argv) 
+{
+       struct functable func[] = {
+#if 0
+               {"add", rpc_group_add},
+               {"delete", rpc_group_delete},
+#endif
+               {NULL, NULL}
+       };
+       
+       if (argc == 0) {
+               if (opt_long_list_entries) {
+               } else {
+               }
+               return run_rpc_command(NULL, PI_SAMR, 0, 
+                                      rpc_group_list_internals,
+                                      argc, argv);
+       }
+
+       return net_run_function(argc, argv, func, rpc_group_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_share_usage(int argc, const char **argv)
+{
+       return net_help_share(argc, argv);
+}
+
+/** 
+ * Add a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+rpc_share_add_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                       TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+       WERROR result;
+       char *sharename=talloc_strdup(mem_ctx, argv[0]);
+       char *path;
+       uint32 type=0; /* only allow disk shares to be added */
+       uint32 num_users=0, perms=0;
+       char *password=NULL; /* don't allow a share password */
+
+       path = strchr(sharename, '=');
+       if (!path)
+               return NT_STATUS_UNSUCCESSFUL;
+       *path++ = '\0';
+
+       result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type,
+                                         opt_comment, perms, opt_maxusers,
+                                         num_users, path, password);
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_share_add(int argc, const char **argv)
+{
+       if ((argc < 1) || !strchr(argv[0], '=')) {
+               DEBUG(1,("Sharename or path not specified on add\n"));
+               return rpc_share_usage(argc, argv);
+       }
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_share_add_internals,
+                              argc, argv);
+}
+
+/** 
+ * Delete a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+rpc_share_del_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                       TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+       WERROR result;
+
+       result = cli_srvsvc_net_share_del(cli, mem_ctx, argv[0]);
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/** 
+ * Delete a share on a remote RPC server
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_share_delete(int argc, const char **argv)
+{
+       if (argc < 1) {
+               DEBUG(1,("Sharename not specified on delete\n"));
+               return rpc_share_usage(argc, argv);
+       }
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_share_del_internals,
+                              argc, argv);
+}
+
+/**
+ * Formatted print of share info
+ *
+ * @param info1  pointer to SRV_SHARE_INFO_1 to format
+ **/
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+       fstring netname = "", remark = "";
+
+       rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+       rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+       if (opt_long_list_entries) {
+               d_printf("%-12.12s %-8.8s %-50.50s\n",
+                        netname, share_type[info1->info_1.type], remark);
+       } else {
+               d_printf("%-12.12s\n", netname);
+       }
+
+}
+
+/** 
+ * List shares on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_share_list_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                        TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       SRV_SHARE_INFO_CTR ctr;
+       WERROR result;
+       ENUM_HND hnd;
+       uint32 preferred_len = 0xffffffff, i;
+
+       init_enum_hnd(&hnd, 0);
+
+       result = cli_srvsvc_net_share_enum(
+               cli, mem_ctx, 1, &ctr, preferred_len, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+       if (opt_long_list_entries) {
+               d_printf(
+       "\nEnumerating shared resources (exports) on remote server:\n\n"\
+       "\nShare name   Type     Description\n"\
+       "----------   ----     -----------\n");
+       }
+       for (i = 0; i < ctr.num_entries; i++)
+               display_share_info_1(&ctr.share.info1[i]);
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/** 
+ * 'net rpc share' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_share(int argc, const char **argv) 
+{
+       struct functable func[] = {
+               {"add", rpc_share_add},
+               {"delete", rpc_share_delete},
+               {NULL, NULL}
+       };
+
+       if (argc == 0)
+               return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                                      rpc_share_list_internals,
+                                      argc, argv);
+
+       return net_run_function(argc, argv, func, rpc_share_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_file_usage(int argc, const char **argv)
+{
+       return net_help_file(argc, argv);
+}
+
+/** 
+ * Close a file on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS 
+rpc_file_close_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                        TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       WERROR result;
+       result = cli_srvsvc_net_file_close(cli, mem_ctx, atoi(argv[0]));
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/** 
+ * Close a file on a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_close(int argc, const char **argv)
+{
+       if (argc < 1) {
+               DEBUG(1, ("No fileid given on close\n"));
+               return(rpc_file_usage(argc, argv));
+       }
+
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_file_close_internals,
+                              argc, argv);
+}
+
+/** 
+ * Formatted print of open file info 
+ *
+ * @param info3  FILE_INFO_3 contents
+ * @param str3   strings for FILE_INFO_3
+ **/
+
+static void display_file_info_3(FILE_INFO_3 *info3, FILE_INFO_3_STR *str3)
+{
+       fstring user = "", path = "";
+
+       rpcstr_pull_unistr2_fstring(user, &str3->uni_user_name);
+       rpcstr_pull_unistr2_fstring(path, &str3->uni_path_name);
+
+       d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+                info3->id, user, info3->perms, info3->num_locks, path);
+}
+
+/** 
+ * List open files on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS 
+rpc_file_list_internals(const DOM_SID *domain_sid, struct cli_state *cli,
+                       TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+       SRV_FILE_INFO_CTR ctr;
+       WERROR result;
+       ENUM_HND hnd;
+       uint32 preferred_len = 0xffffffff, i;
+       const char *username=NULL;
+
+       init_enum_hnd(&hnd, 0);
+
+       /* if argc > 0, must be user command */
+       if (argc > 0)
+               username = smb_xstrdup(argv[0]);
+               
+       result = cli_srvsvc_net_file_enum(
+               cli, mem_ctx, 3, username, &ctr, preferred_len, &hnd);
+
+       if (!W_ERROR_IS_OK(result))
+               goto done;
+
+       /* Display results */
+
+       d_printf(
+                "\nEnumerating open files on remote server:\n\n"\
+                "\nFileId  Opened by            Perms  Locks  Path"\
+                "\n------  ---------            -----  -----  ---- \n");
+       for (i = 0; i < ctr.num_entries; i++)
+               display_file_info_3(&ctr.file.info3[i].info_3, 
+                                   &ctr.file.info3[i].info_3_str);
+ done:
+       return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/** 
+ * List files for a user on a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_user(int argc, const char **argv)
+{
+       if (argc < 1) {
+               DEBUG(1, ("No username given\n"));
+               return(rpc_file_usage(argc, argv));
+       }
+
+       return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                              rpc_file_list_internals,
+                              argc, argv);
+}
+
+
+/** 
+ * 'net rpc file' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_file(int argc, const char **argv) 
+{
+       struct functable func[] = {
+               {"close", rpc_file_close},
+               {"user", rpc_file_user},
+#if 0
+               {"info", rpc_file_info},
+#endif
+               {NULL, NULL}
+       };
+
+       if (argc == 0)
+               return run_rpc_command(NULL, PI_SRVSVC, 0, 
+                                      rpc_file_list_internals,
+                                      argc, argv);
+
+       return net_run_function(argc, argv, func, rpc_file_usage);
+}
+
+/****************************************************************************/
+
+
+
+/** 
+ * ABORT the shutdown of a remote RPC Server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_abort_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                            int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       
+       result = cli_reg_abort_shutdown(cli, mem_ctx);
+       
+       if (NT_STATUS_IS_OK(result))
+               DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+       else
+               DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+       
+       return result;
+}
+
+
+/** 
+ * ABORT the Shut down of a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown_abort(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_abort_internals,
+                              argc, argv);
+}
+
+/** 
+ * Shut down a remote RPC Server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through. 
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                      int argc, const char **argv) 
+{
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+        const char *msg = "This machine will be shutdown shortly";
+       uint32 timeout = 20;
+#if 0
+       poptContext pc;
+       int rc;
+
+       struct poptOption long_options[] = {
+               {"message",    'm', POPT_ARG_STRING, &msg},
+               {"timeout",    't', POPT_ARG_INT,    &timeout},
+               {"reboot",     'r', POPT_ARG_NONE,   &reboot},
+               {"force",      'f', POPT_ARG_NONE,   &force},
+               { 0, 0, 0, 0}
+       };
+
+       pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       rc = poptGetNextOpt(pc);
+       
+       if (rc < -1) {
+               /* an error occurred during option processing */
+               DEBUG(0, ("%s: %s\n",
+                         poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+                         poptStrerror(rc)));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+#endif
+       if (opt_comment) {
+               msg = opt_comment;
+       }
+       if (opt_timeout) {
+               timeout = opt_timeout;
+       }
+
+       /* create an entry */
+       result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, opt_reboot, opt_force);
+
+       if (NT_STATUS_IS_OK(result))
+               DEBUG(5,("Shutdown of remote machine succeeded\n"));
+       else
+               DEBUG(0,("Shutdown of remote machine failed!\n"));
+
+       return result;
+}
+
+/** 
+ * Shut down a remote RPC server
+ *
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown(int argc, const char **argv) 
+{
+       return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_internals,
+                                      argc, argv);
+}
+
+/***************************************************************************
+  NT Domain trusts code (i.e. 'net rpc trustdom' functionality)
+  
+ ***************************************************************************/
+
+/**
+ * Add interdomain trust account to the RPC server.
+ * All parameters (except for argc and argv) are passed by run_rpc_command
+ * function.
+ *
+ * @param domain_sid The domain sid acquired from the server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped
+ *
+ * @return normal NTSTATUS return code
+ */
+
+static NTSTATUS rpc_trustdom_add_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, 
+                                           int argc, const char **argv) {
+
+       POLICY_HND connect_pol, domain_pol, user_pol;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       char *acct_name;
+       uint16 acb_info;
+       uint32 unknown, user_rid;
+
+       if (argc != 1) {
+               d_printf("Usage: net rpc trustdom add <domain_name>\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* 
+        * Make valid trusting domain account (ie. uppercased and with '$' appended)
+        */
+        
+       if (asprintf(&acct_name, "%s$", argv[0]) < 0) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       strupper(acct_name);
+
+       /* Get samr policy handle */
+       result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+                                 &connect_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+       
+       /* Get domain policy handle */
+       result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     domain_sid, &domain_pol);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+       /* Create trusting domain's account */
+       acb_info = ACB_DOMTRUST;
+       unknown = 0xe005000b; /* No idea what this is - a permission mask?
+                                mimir: yes, most probably it is */
+
+       result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+                                         acct_name, acb_info, unknown,
+                                         &user_pol, &user_rid);
+       if (!NT_STATUS_IS_OK(result)) {
+               goto done;
+       }
+
+ done:
+       SAFE_FREE(acct_name);
+       return result;
+}
+
+/**
+ * Create interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_add(int argc, const char **argv)
+{
+       return run_rpc_command(NULL, PI_SAMR, 0, rpc_trustdom_add_internals,
+                              argc, argv);
+}
+
+
+/**
+ * Delete interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+static int rpc_trustdom_del(int argc, const char **argv)
+{
+       d_printf("Sorry, not yet implemented.\n");
+       return -1;
+}
+
+/**
+ * Establish trust relationship to a trusting domain.
+ * Interdomain account must already be created on remote PDC.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_establish(int argc, const char **argv)
+{
+       struct cli_state *cli;
+       struct in_addr server_ip;
+       POLICY_HND connect_hnd;
+       TALLOC_CTX *mem_ctx;
+       NTSTATUS nt_status;
+       DOM_SID domain_sid;
+       WKS_INFO_100 wks_info;
+       
+       char* domain_name;
+       char* acct_name;
+       fstring pdc_name;
+
+       /*
+        * Connect to \\server\ipc$ as 'our domain' account with password
+        */
+
+       if (argc != 1) {
+               d_printf("Usage: net rpc trustdom establish <domain_name>\n");
+               return -1;
+       }
+
+       domain_name = smb_xstrdup(argv[0]);
+       strupper(domain_name);
+
+       /* account name used at first is our domain's name with '$' */
+       asprintf(&acct_name, "%s$", lp_workgroup());
+       strupper(acct_name);
+       
+       /*
+        * opt_workgroup will be used by connection functions further,
+        * hence it should be set to remote domain name instead of ours
+        */
+       if (opt_workgroup) {
+               opt_workgroup = smb_xstrdup(domain_name);
+       };
+       
+       opt_user_name = acct_name;
+
+       /* find the domain controller */
+       if (!net_find_dc(&server_ip, pdc_name, domain_name)) {
+               DEBUG(0, ("Coulnd find domain controller for domain %s\n", domain_name));
+               return -1;
+       }
+
+       /* connect to ipc$ as username/password */
+       nt_status = connect_to_ipc(&cli, &server_ip, pdc_name);
+       if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+
+               /* Is it trusting domain account for sure ? */
+               DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       }
+       
+       /*
+        * Connect to \\server\ipc$ again (this time anonymously)
+        */
+       
+       nt_status = connect_to_ipc_anonymous(&cli, &server_ip, (char*)pdc_name);
+       
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n",
+                       domain_name, nt_errstr(nt_status)));
+       }
+
+       /*
+        * Use NetServerEnum2 to make sure we're talking to a proper server
+        */
+        
+       if (!cli_get_pdc_name(cli, domain_name, (char*)pdc_name)) {
+               DEBUG(0, ("NetServerEnum2 error: Couldn't find primary domain controller\
+                        for domain %s\n", domain_name));
+       }
+        
+       /*
+        * Call WksQueryInfo to check remote server's capabilities
+        * note: It is now used only to get unicode domain name
+        */
+       
+       if (!cli_nt_session_open(cli, PI_WKSSVC)) {
+               DEBUG(0, ("Couldn't not initialise wkssvc pipe\n"));
+               return -1;
+       }
+
+       if (!(mem_ctx = talloc_init("establishing trust relationship to domain %s",
+                       domain_name))) {
+               DEBUG(0, ("talloc_init() failed\n"));
+               cli_shutdown(cli);
+               return -1;
+       }
+       
+       nt_status = cli_wks_query_info(cli, mem_ctx, &wks_info);
+       
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("WksQueryInfo call failed.\n"));
+               return -1;
+       }
+
+       if (cli->nt_pipe_fnum)
+               cli_nt_session_close(cli);
+
+
+       /*
+        * Call LsaOpenPolicy and LsaQueryInfo
+        */
+        
+       if (!(mem_ctx = talloc_init("rpc_trustdom_establish"))) {
+               DEBUG(0, ("talloc_init() failed\n"));
+               cli_shutdown(cli);
+               return -1;
+       }
+
+       if (!cli_nt_session_open(cli, PI_LSARPC)) {
+               DEBUG(0, ("Could not initialise lsa pipe\n"));
+               cli_shutdown(cli);
+               return -1;
+       }
+
+       nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+                                        &connect_hnd);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       }
+
+       /* Querying info level 5 */
+       
+       nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+                                             5 /* info level */, domain_name,
+                                             &domain_sid);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       }
+
+
+
+
+       /* There should be actually query info level 3 (following nt serv behaviour),
+          but I still don't know if it's _really_ necessary */
+                       
+       /*
+        * Store the password in secrets db
+        */
+
+       if (!secrets_store_trusted_domain_password(domain_name, wks_info.uni_lan_grp.buffer,
+                                                  wks_info.uni_lan_grp.uni_str_len, opt_password,
+                                                  domain_sid)) {
+               DEBUG(0, ("Storing password for trusted domain failed.\n"));
+               return -1;
+       }
+       
+       /*
+        * Close the pipes and clean up
+        */
+        
+       nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       }
+
+       if (cli->nt_pipe_fnum)
+               cli_nt_session_close(cli);
+        
+       talloc_destroy(mem_ctx);
+        
+       DEBUG(0, ("Success!\n"));
+       return 0;
+}
+
+/**
+ * Revoke trust relationship to the remote domain
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_revoke(int argc, const char **argv)
+{
+       char* domain_name;
+
+       if (argc < 1) return -1;
+       
+       /* generate upper cased domain name */
+       domain_name = smb_xstrdup(argv[0]);
+       strupper(domain_name);
+
+       /* delete password of the trust */
+       if (!trusted_domain_password_delete(domain_name)) {
+               DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n",
+                         domain_name));
+               return -1;
+       };
+       
+       return 0;
+}
+
+/**
+ * Usage for 'net rpc trustdom' command
+ *
+ * @param argc standard argc
+ * @param argv standard argv without inital components
+ *
+ * @return Integer status returned to shell
+ **/
+static int rpc_trustdom_usage(int argc, const char **argv)
+{
+       d_printf("  net rpc trustdom add \t\t add trusting domain's account\n");
+       d_printf("  net rpc trustdom del \t\t delete trusting domain's account\n");
+       d_printf("  net rpc trustdom establish \t establish relationship to trusted domain\n");
+       d_printf("  net rpc trustdom revoke \t abandon relationship to trusted domain\n");
+       d_printf("  net rpc trustdom list \t show current interdomain trust relationships\n");
+       return -1;
+}
+
+
+static NTSTATUS rpc_query_domain_sid(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx,
+                              int argc, const char **argv)
+{
+       fstring str_sid;
+       sid_to_string(str_sid, domain_sid);
+       d_printf("%s\n", str_sid);
+       return NT_STATUS_OK;
+};
+
+
+static int rpc_trustdom_list(int argc, const char **argv)
+{
+       /* common variables */
+       TALLOC_CTX* mem_ctx;
+       struct cli_state *cli, *remote_cli;
+       NTSTATUS nt_status;
+       const char *domain_name = NULL;
+       DOM_SID queried_dom_sid;
+       fstring ascii_sid, padding;
+       int ascii_dom_name_len;
+       POLICY_HND connect_hnd;
+       
+       /* trusted domains listing variables */
+       int enum_ctx = 0;
+       int num_domains, i, pad_len, col_len = 20;
+       DOM_SID *domain_sids;
+       char **trusted_dom_names;
+       fstring pdc_name;
+       
+       /* trusting domains listing variables */
+       POLICY_HND domain_hnd;
+       char **trusting_dom_names;
+       uint32 *trusting_dom_rids;
+       
+       /*
+        * Listing trusted domains (stored in secrets.tdb, if local)
+        */
+
+       mem_ctx = talloc_init("trust relationships listing");
+
+       /*
+        * set domain and pdc name to local samba server (default)
+        * or to remote one given in command line
+        */
+       
+       if (StrCaseCmp(opt_workgroup, lp_workgroup())) {
+               domain_name = opt_workgroup;
+               opt_target_workgroup = opt_workgroup;
+       } else {
+               fstrcpy(pdc_name, lp_netbios_name());
+               domain_name = talloc_strdup(mem_ctx, lp_workgroup());
+               opt_target_workgroup = domain_name;
+       };
+
+       /* open \PIPE\lsarpc and open policy handle */
+       if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) {
+               DEBUG(0, ("Couldn't connect to domain controller\n"));
+               return -1;
+       };
+
+       if (!cli_nt_session_open(cli, PI_LSARPC)) {
+               DEBUG(0, ("Could not initialise lsa pipe\n"));
+               return -1;
+       };
+
+       nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+                                       &connect_hnd);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       };
+       
+       /* query info level 5 to obtain sid of a domain being queried */
+       nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+                                       5 /* info level */, domain_name, &queried_dom_sid);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       }
+               
+       /*
+        * Keep calling LsaEnumTrustdom over opened pipe until
+        * the end of enumeration is reached
+        */
+        
+       d_printf("Trusted domains list:\n\n");
+
+       do {
+               nt_status = cli_lsa_enum_trust_dom(cli, mem_ctx, &connect_hnd, &enum_ctx,
+                                                  &num_domains,
+                                                  &trusted_dom_names, &domain_sids);
+               
+               if (NT_STATUS_IS_ERR(nt_status)) {
+                       DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+                               nt_errstr(nt_status)));
+                       return -1;
+               };
+               
+               for (i = 0; i < num_domains; i++) {
+                       /* convert sid into ascii string */
+                       sid_to_string(ascii_sid, &(domain_sids[i]));
+               
+                       /* calculate padding space for d_printf to look nicer */
+                       pad_len = col_len - strlen(trusted_dom_names[i]);
+                       padding[pad_len] = 0;
+                       do padding[--pad_len] = ' '; while (pad_len);
+                       
+                       d_printf("%s%s%s\n", trusted_dom_names[i], padding, ascii_sid);
+               };
+               
+               /*
+                * in case of no trusted domains say something rather
+                * than just display blank line
+                */
+               if (!num_domains) d_printf("none\n");
+
+       } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+       /* close this connection before doing next one */
+       nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+       if (NT_STATUS_IS_ERR(nt_status)) {
+               DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       };
+       
+       cli_nt_session_close(cli);
+
+       /*
+        * Listing trusting domains (stored in passdb backend, if local)
+        */
+       
+       d_printf("\nTrusting domains list:\n\n");
+
+       /*
+        * Open \PIPE\samr and get needed policy handles
+        */
+       if (!cli_nt_session_open(cli, PI_SAMR)) {
+               DEBUG(0, ("Could not initialise samr pipe\n"));
+               return -1;
+       };
+       
+       /* SamrConnect */
+       nt_status = cli_samr_connect(cli, mem_ctx, SA_RIGHT_SAM_OPEN_DOMAIN,
+                                                                &connect_hnd);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       };
+       
+       /* SamrOpenDomain - we have to open domain policy handle in order to be
+          able to enumerate accounts*/
+       nt_status = cli_samr_open_domain(cli, mem_ctx, &connect_hnd,
+                                                                        SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+                                                                        &queried_dom_sid, &domain_hnd);                                                                         
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("Couldn't open domain object. Error was %s\n",
+                       nt_errstr(nt_status)));
+               return -1;
+       };
+       
+       /*
+        * perform actual enumeration
+        */
+        
+       enum_ctx = 0;   /* reset enumeration context from last enumeration */
+       do {
+                       
+               nt_status = cli_samr_enum_dom_users(cli, mem_ctx, &domain_hnd,
+                                                   &enum_ctx, ACB_DOMTRUST, 0xffff,
+                                                   &trusting_dom_names, &trusting_dom_rids,
+                                                   &num_domains);
+               if (NT_STATUS_IS_ERR(nt_status)) {
+                       DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n",
+                               nt_errstr(nt_status)));
+                       return -1;
+               };
+               
+               for (i = 0; i < num_domains; i++) {
+
+                       /*
+                        * get each single domain's sid (do we _really_ need this ?):
+                        *  1) connect to domain's pdc
+                        *  2) query the pdc for domain's sid
+                        */
+
+                       /* get rid of '$' tail */
+                       ascii_dom_name_len = strlen(trusting_dom_names[i]);
+                       if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN)
+                               trusting_dom_names[i][ascii_dom_name_len - 1] = '\0';
+                       
+                       /* calculate padding space for d_printf to look nicer */
+                       pad_len = col_len - strlen(trusting_dom_names[i]);
+                       padding[pad_len] = 0;
+                       do padding[--pad_len] = ' '; while (pad_len);
+
+                       /* set opt_* variables to remote domain */
+                       strupper(trusting_dom_names[i]);
+                       opt_workgroup = talloc_strdup(mem_ctx, trusting_dom_names[i]);
+                       opt_target_workgroup = opt_workgroup;
+                       
+                       d_printf("%s%s", trusting_dom_names[i], padding);
+                       
+                       /* connect to remote domain controller */
+                       remote_cli = net_make_ipc_connection(NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS);
+                       if (remote_cli) {                       
+                               /* query for domain's sid */
+                               if (run_rpc_command(remote_cli, PI_LSARPC, 0, rpc_query_domain_sid, argc, argv))
+                                       d_printf("couldn't get domain's sid\n");
+
+                               cli_shutdown(remote_cli);
+                       
+                       } else {
+                               d_printf("domain controller is not responding\n");
+                       };
+               };
+               
+               if (!num_domains) d_printf("none\n");
+               
+       } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+       /* close opened samr and domain policy handles */
+       nt_status = cli_samr_close(cli, mem_ctx, &domain_hnd);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name));
+       };
+       
+       nt_status = cli_samr_close(cli, mem_ctx, &connect_hnd);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name));
+       };
+       
+       /* close samr pipe and connection to IPC$ */
+       cli_nt_session_close(cli);
+       cli_shutdown(cli);
+
+       talloc_destroy(mem_ctx);         
+       return 0;
+}
+
+/**
+ * Entrypoint for 'net rpc trustdom' code
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ */
+
+static int rpc_trustdom(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"add", rpc_trustdom_add},
+               {"del", rpc_trustdom_del},
+               {"establish", rpc_trustdom_establish},
+               {"revoke", rpc_trustdom_revoke},
+               {"help", rpc_trustdom_usage},
+               {"list", rpc_trustdom_list},
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               rpc_trustdom_usage(argc, argv);
+               return -1;
+       }
+
+       return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+/**
+ * Check if a server will take rpc commands
+ * @param flags        Type of server to connect to (PDC, DMB, localhost)
+ *             if the host is not explicitly specified
+ * @return  BOOL (true means rpc supported)
+ */
+BOOL net_rpc_check(unsigned flags)
+{
+       struct cli_state cli;
+       BOOL ret = False;
+       struct in_addr server_ip;
+       char *server_name = NULL;
+
+       /* flags (i.e. server type) may depend on command */
+       if (!net_find_server(flags, &server_ip, &server_name))
+               return False;
+
+       ZERO_STRUCT(cli);
+       if (cli_initialise(&cli) == False)
+               return False;
+
+       if (!cli_connect(&cli, server_name, &server_ip))
+               goto done;
+       if (!attempt_netbios_session_request(&cli, lp_netbios_name(), 
+                                            server_name, &server_ip))
+               goto done;
+       if (!cli_negprot(&cli))
+               goto done;
+       if (cli.protocol < PROTOCOL_NT1)
+               goto done;
+
+       ret = True;
+ done:
+       cli_shutdown(&cli);
+       return ret;
+}
+
+
+/****************************************************************************/
+
+
+/** 
+ * Basic usage function for 'net rpc'
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_usage(int argc, const char **argv) 
+{
+       d_printf("  net rpc info \t\t\tshow basic info about a domain \n");
+       d_printf("  net rpc join \t\t\tto join a domain \n");
+       d_printf("  net rpc testjoin \t\ttests that a join is valid\n");
+       d_printf("  net rpc user \t\t\tto add, delete and list users\n");
+       d_printf("  net rpc group \t\tto list groups\n");
+       d_printf("  net rpc share \t\tto add, delete, and list shares\n");
+       d_printf("  net rpc file \t\t\tto list open files\n");
+       d_printf("  net rpc changetrustpw \tto change the trust account password\n");
+       d_printf("  net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n");
+       d_printf("  net rpc vampire \t\tsyncronise an NT PDC's users and groups into the local passdb\n");
+       d_printf("  net rpc samdump \t\tdiplay an NT PDC's users, groups and other data\n");
+       d_printf("  net rpc trustdom \t\tto create trusting domain's account\n"
+                "\t\t\t\t\tor establish trust\n");
+       d_printf("  net rpc abortshutdown \tto abort the shutdown of a remote server\n");
+       d_printf("  net rpc shutdown \t\tto shutdown a remote server\n");
+       d_printf("\n");
+       d_printf("'net rpc shutdown' also accepts the following miscellaneous options:\n"); /* misc options */
+       d_printf("\t-r or --reboot\trequest remote server reboot on shutdown\n");
+       d_printf("\t-f or --force\trequest the remote server force its shutdown\n");
+       d_printf("\t-t or --timeout=<timeout>\tnumber of seconds before shutdown\n");
+       d_printf("\t-c or --comment=<message>\ttext message to display on impending shutdown\n");
+       return -1;
+}
+
+
+/**
+ * Help function for 'net rpc'.  Calls command specific help if requested
+ * or displays usage of net rpc
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc_help(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"join", rpc_join_usage},
+               {"user", rpc_user_usage},
+               {"group", rpc_group_usage},
+               {"share", rpc_share_usage},
+               /*{"changetrustpw", rpc_changetrustpw_usage}, */
+               {"trustdom", rpc_trustdom_usage},
+               /*{"abortshutdown", rpc_shutdown_abort_usage},*/
+               /*{"shutdown", rpc_shutdown_usage}, */
+               {NULL, NULL}
+       };
+
+       if (argc == 0) {
+               net_rpc_usage(argc, argv);
+               return -1;
+       }
+
+       return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+
+/** 
+ * 'net rpc' entrypoint.
+ * @param argc  Standard main() style argc
+ * @param argv  Standard main() style argv.  Initial components are already
+ *              stripped
+ **/
+
+int net_rpc(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"info", net_rpc_info},
+               {"join", net_rpc_join},
+               {"testjoin", net_rpc_testjoin},
+               {"user", net_rpc_user},
+               {"group", net_rpc_group},
+               {"share", net_rpc_share},
+               {"file", net_rpc_file},
+               {"changetrustpw", rpc_changetrustpw},
+               {"trustdom", rpc_trustdom},
+               {"abortshutdown", rpc_shutdown_abort},
+               {"shutdown", rpc_shutdown},
+               {"samdump", rpc_samdump},
+               {"vampire", rpc_vampire},
+               {"getsid", net_rpc_getsid},
+               {"help", net_rpc_help},
+               {NULL, NULL}
+       };
+       return net_run_function(argc, argv, func, net_rpc_usage);
+}
diff --git a/source4/utils/net_rpc_join.c b/source4/utils/net_rpc_join.c
new file mode 100644 (file)
index 0000000..4f82d0b
--- /dev/null
@@ -0,0 +1,354 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+   Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+   Copyright (C) Tim Potter     2001
+
+   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 "../utils/net.h"
+
+/* Macro for checking RPC error codes to make things more readable */
+
+#define CHECK_RPC_ERR(rpc, msg) \
+        if (!NT_STATUS_IS_OK(result = rpc)) { \
+                DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+                goto done; \
+        }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+        if (!NT_STATUS_IS_OK(result = rpc)) { \
+                DEBUG(0, debug_args); \
+                goto done; \
+        }
+
+
+/**
+ * confirm that a domain join is still valid
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+int net_rpc_join_ok(const char *domain)
+{
+       struct cli_state *cli;
+       uchar stored_md4_trust_password[16];
+       int retval = 1;
+       uint32 channel;
+       NTSTATUS result;
+       uint32 neg_flags = 0x000001ff;
+
+       /* Connect to remote machine */
+       if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) {
+               return 1;
+       }
+
+       if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+               DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+               goto done;
+       }
+
+       if (!secrets_fetch_trust_account_password(domain,
+                                                 stored_md4_trust_password, NULL)) {
+               DEBUG(0,("Could not reterive domain trust secret"));
+               goto done;
+       }
+       
+       if (lp_server_role() == ROLE_DOMAIN_BDC || 
+           lp_server_role() == ROLE_DOMAIN_PDC) {
+               channel = SEC_CHAN_BDC;
+       } else {
+               channel = SEC_CHAN_WKSTA;
+       }
+
+       CHECK_RPC_ERR(cli_nt_setup_creds(cli, 
+                                        channel,
+                                        stored_md4_trust_password, &neg_flags, 2),
+                         "error in domain join verification");
+       
+       retval = 0;             /* Success! */
+       
+done:
+       /* Close down pipe - this will clean up open policy handles */
+       if (cli->nt_pipe_fnum)
+               cli_nt_session_close(cli);
+
+       cli_shutdown(cli);
+
+       return retval;
+}
+
+/**
+ * Join a domain using the administrator username and password
+ *
+ * @param argc  Standard main() style argc
+ * @param argc  Standard main() style argv.  Initial components are already
+ *              stripped.  Currently not used.
+ * @return A shell status integer (0 for success)
+ *
+ **/
+
+int net_rpc_join_newstyle(int argc, const char **argv) 
+{
+
+       /* libsmb variables */
+
+       struct cli_state *cli;
+       TALLOC_CTX *mem_ctx;
+        uint32 acb_info;
+
+       /* rpc variables */
+
+       POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol;
+       DOM_SID domain_sid;
+       uint32 user_rid;
+
+       /* Password stuff */
+
+       char *clear_trust_password = NULL;
+       fstring ucs2_trust_password;
+       int ucs2_pw_len;
+       uchar pwbuf[516], sess_key[16];
+       SAM_USERINFO_CTR ctr;
+       SAM_USER_INFO_24 p24;
+       SAM_USER_INFO_10 p10;
+
+       /* Misc */
+
+       NTSTATUS result;
+       int retval = 1;
+       fstring domain;
+       uint32 num_rids, *name_types, *user_rids;
+       uint32 flags = 0x3e8;
+       char *acct_name;
+       const char *const_acct_name;
+
+       /* Connect to remote machine */
+
+       if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) 
+               return 1;
+
+       if (!(mem_ctx = talloc_init("net_rpc_join_newstyle"))) {
+               DEBUG(0, ("Could not initialise talloc context\n"));
+               goto done;
+       }
+
+       /* Fetch domain sid */
+
+       if (!cli_nt_session_open(cli, PI_LSARPC)) {
+               DEBUG(0, ("Error connecting to SAM pipe\n"));
+               goto done;
+       }
+
+
+       CHECK_RPC_ERR(cli_lsa_open_policy(cli, mem_ctx, True,
+                                         SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                         &lsa_pol),
+                     "error opening lsa policy handle");
+
+       CHECK_RPC_ERR(cli_lsa_query_info_policy(cli, mem_ctx, &lsa_pol,
+                                               5, domain, &domain_sid),
+                     "error querying info policy");
+
+       cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+       cli_nt_session_close(cli); /* Done with this pipe */
+
+       /* Create domain user */
+       if (!cli_nt_session_open(cli, PI_SAMR)) {
+               DEBUG(0, ("Error connecting to SAM pipe\n"));
+               goto done;
+       }
+
+       CHECK_RPC_ERR(cli_samr_connect(cli, mem_ctx, 
+                                      SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                      &sam_pol),
+                     "could not connect to SAM database");
+
+       
+       CHECK_RPC_ERR(cli_samr_open_domain(cli, mem_ctx, &sam_pol,
+                                          SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                          &domain_sid, &domain_pol),
+                     "could not open domain");
+
+       /* Create domain user */
+       acct_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name()); 
+       strlower(acct_name);
+       const_acct_name = acct_name;
+
+        acb_info = ((lp_server_role() == ROLE_DOMAIN_BDC) || lp_server_role() == ROLE_DOMAIN_PDC) ? ACB_SVRTRUST : ACB_WSTRUST;
+
+       result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+                                         acct_name, acb_info,
+                                         0xe005000b, &user_pol, 
+                                         &user_rid);
+
+       if (!NT_STATUS_IS_OK(result) && 
+           !NT_STATUS_EQUAL(result, NT_STATUS_USER_EXISTS)) {
+               d_printf("Create of workstation account failed\n");
+
+               /* If NT_STATUS_ACCESS_DENIED then we have a valid
+                  username/password combo but the user does not have
+                  administrator access. */
+
+               if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED))
+                       d_printf("User specified does not have administrator privileges\n");
+
+               goto done;
+       }
+
+       /* We *must* do this.... don't ask... */
+
+       if (NT_STATUS_IS_OK(result))
+               cli_samr_close(cli, mem_ctx, &user_pol);
+
+       CHECK_RPC_ERR_DEBUG(cli_samr_lookup_names(cli, mem_ctx,
+                                                 &domain_pol, flags,
+                                                 1, &const_acct_name, 
+                                                 &num_rids,
+                                                 &user_rids, &name_types),
+                           ("error looking up rid for user %s: %s\n",
+                            acct_name, nt_errstr(result)));
+
+       if (name_types[0] != SID_NAME_USER) {
+               DEBUG(0, ("%s is not a user account\n", acct_name));
+               goto done;
+       }
+
+       user_rid = user_rids[0];
+               
+       /* Open handle on user */
+
+       CHECK_RPC_ERR_DEBUG(
+               cli_samr_open_user(cli, mem_ctx, &domain_pol,
+                                  SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                  user_rid, &user_pol),
+               ("could not re-open existing user %s: %s\n",
+                acct_name, nt_errstr(result)));
+       
+       /* Create a random machine account password */
+
+       { 
+               char *str;
+               str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+               clear_trust_password = strdup(str);
+       }
+
+       ucs2_pw_len = push_ucs2(NULL, ucs2_trust_password, 
+                               clear_trust_password, 
+                               sizeof(ucs2_trust_password), 0);
+                 
+       encode_pw_buffer((char *)pwbuf, ucs2_trust_password,
+                        ucs2_pw_len);
+
+       /* Set password on machine account */
+
+       ZERO_STRUCT(ctr);
+       ZERO_STRUCT(p24);
+
+       init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+       ctr.switch_value = 24;
+       ctr.info.id24 = &p24;
+
+       CHECK_RPC_ERR(cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24, 
+                                           cli->user_session_key, &ctr),
+                     "error setting trust account password");
+
+       /* Why do we have to try to (re-)set the ACB to be the same as what
+          we passed in the samr_create_dom_user() call?  When a NT
+          workstation is joined to a domain by an administrator the
+          acb_info is set to 0x80.  For a normal user with "Add
+          workstations to the domain" rights the acb_info is 0x84.  I'm
+          not sure whether it is supposed to make a difference or not.  NT
+          seems to cope with either value so don't bomb out if the set
+          userinfo2 level 0x10 fails.  -tpot */
+
+       ZERO_STRUCT(ctr);
+       ctr.switch_value = 0x10;
+       ctr.info.id10 = &p10;
+
+       init_sam_user_info10(&p10, acb_info);
+
+       /* Ignoring the return value is necessary for joining a domain
+          as a normal user with "Add workstation to domain" privilege. */
+
+       result = cli_samr_set_userinfo2(cli, mem_ctx, &user_pol, 0x10, 
+                                       sess_key, &ctr);
+
+       /* Now store the secret in the secrets database */
+
+       strupper(domain);
+
+       if (!secrets_store_domain_sid(domain, &domain_sid)) {
+               DEBUG(0, ("error storing domain sid for %s\n", domain));
+               goto done;
+       }
+
+       if (!secrets_store_machine_password(clear_trust_password)) {
+               DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain));
+       }
+
+       /* Now check the whole process from top-to-bottom */
+       cli_samr_close(cli, mem_ctx, &user_pol);
+       cli_nt_session_close(cli); /* Done with this pipe */
+
+       retval = net_rpc_join_ok(domain);
+       
+done:
+       /* Close down pipe - this will clean up open policy handles */
+
+       if (cli->nt_pipe_fnum)
+               cli_nt_session_close(cli);
+
+       /* Display success or failure */
+
+       if (retval != 0) {
+               trust_password_delete(domain);
+               fprintf(stderr,"Unable to join domain %s.\n",domain);
+       } else {
+               printf("Joined domain %s.\n",domain);
+       }
+       
+       cli_shutdown(cli);
+
+       SAFE_FREE(clear_trust_password);
+
+       return retval;
+}
+
+
+/**
+ * check that a join is OK
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+int net_rpc_testjoin(int argc, const char **argv) 
+{
+       char *domain = smb_xstrdup(lp_workgroup());
+
+       /* Display success or failure */
+       if (net_rpc_join_ok(domain) != 0) {
+               fprintf(stderr,"Join to domain '%s' is not valid\n",domain);
+               free(domain);
+               return -1;
+       }
+
+       printf("Join to '%s' is OK\n",domain);
+       free(domain);
+       return 0;
+}
diff --git a/source4/utils/net_rpc_samsync.c b/source4/utils/net_rpc_samsync.c
new file mode 100644 (file)
index 0000000..7d5c868
--- /dev/null
@@ -0,0 +1,725 @@
+/* 
+   Unix SMB/CIFS implementation.
+   dump the remote SAM using rpc samsync operations
+
+   Copyright (C) Andrew Tridgell 2002
+   Copyright (C) Tim Potter 2001,2002
+   Modified by Volker Lendecke 2002
+
+   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 "../utils/net.h"
+
+extern DOM_SID global_sid_Builtin; 
+
+static void display_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *g)
+{
+       int i;
+       d_printf("Group mem %u: ", rid);
+       for (i=0;i<g->num_members;i++) {
+               d_printf("%u ", g->rids[i]);
+       }
+       d_printf("\n");
+}
+
+static void display_alias_info(uint32 rid, SAM_ALIAS_INFO *a)
+{
+       d_printf("Alias '%s' ", unistr2_static(&a->uni_als_name));
+       d_printf("desc='%s' rid=%u\n", unistr2_static(&a->uni_als_desc), a->als_rid);
+}
+
+static void display_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *a)
+{
+       int i;
+       d_printf("Alias rid %u: ", rid);
+       for (i=0;i<a->num_members;i++) {
+               d_printf("%s ", sid_string_static(&a->sids[i].sid));
+       }
+       d_printf("\n");
+}
+
+static void display_account_info(uint32 rid, SAM_ACCOUNT_INFO *a)
+{
+       fstring hex_nt_passwd, hex_lm_passwd;
+       uchar lm_passwd[16], nt_passwd[16];
+       static uchar zero_buf[16];
+
+       /* Decode hashes from password hash (if they are not NULL) */
+       
+       if (memcmp(a->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+               sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd, lm_passwd, 0);
+               smbpasswd_sethexpwd(hex_lm_passwd, lm_passwd, a->acb_info);
+       } else {
+               smbpasswd_sethexpwd(hex_lm_passwd, NULL, 0);
+       }
+
+       if (memcmp(a->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+               sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd, nt_passwd, 0);
+               smbpasswd_sethexpwd(hex_nt_passwd, nt_passwd, a->acb_info);
+       } else {
+               smbpasswd_sethexpwd(hex_nt_passwd, NULL, 0);
+       }
+       
+       printf("%s:%d:%s:%s:%s:LCT-0\n", unistr2_static(&a->uni_acct_name),
+              a->user_rid, hex_lm_passwd, hex_nt_passwd,
+              smbpasswd_encode_acb_info(a->acb_info));
+}
+
+static void display_domain_info(SAM_DOMAIN_INFO *a)
+{
+       d_printf("Domain name: %s\n", unistr2_static(&a->uni_dom_name));
+}
+
+static void display_group_info(uint32 rid, SAM_GROUP_INFO *a)
+{
+       d_printf("Group '%s' ", unistr2_static(&a->uni_grp_name));
+       d_printf("desc='%s', rid=%u\n", unistr2_static(&a->uni_grp_desc), rid);
+}
+
+static void display_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta)
+{
+       switch (hdr_delta->type) {
+       case SAM_DELTA_ACCOUNT_INFO:
+               display_account_info(hdr_delta->target_rid, &delta->account_info);
+               break;
+       case SAM_DELTA_GROUP_MEM:
+               display_group_mem_info(hdr_delta->target_rid, &delta->grp_mem_info);
+               break;
+       case SAM_DELTA_ALIAS_INFO:
+               display_alias_info(hdr_delta->target_rid, &delta->alias_info);
+               break;
+       case SAM_DELTA_ALIAS_MEM:
+               display_alias_mem(hdr_delta->target_rid, &delta->als_mem_info);
+               break;
+       case SAM_DELTA_DOMAIN_INFO:
+               display_domain_info(&delta->domain_info);
+               break;
+       case SAM_DELTA_GROUP_INFO:
+               display_group_info(hdr_delta->target_rid, &delta->group_info);
+               break;
+       default:
+               d_printf("Unknown delta record type %d\n", hdr_delta->type);
+               break;
+       }
+}
+
+
+static void dump_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds)
+{
+       unsigned sync_context = 0;
+        NTSTATUS result;
+       int i;
+        TALLOC_CTX *mem_ctx;
+        SAM_DELTA_HDR *hdr_deltas;
+        SAM_DELTA_CTR *deltas;
+        uint32 num_deltas;
+
+       if (!(mem_ctx = talloc_init("dump_database"))) {
+               return;
+       }
+
+       d_printf("Dumping database %u\n", db_type);
+
+       do {
+               result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, db_type,
+                                              sync_context,
+                                              &num_deltas, &hdr_deltas, &deltas);
+               clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), ret_creds);
+                for (i = 0; i < num_deltas; i++) {
+                       display_sam_entry(&hdr_deltas[i], &deltas[i]);
+                }
+               sync_context += 1;
+       } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+       talloc_destroy(mem_ctx);
+}
+
+/* dump sam database via samsync rpc calls */
+int rpc_samdump(int argc, const char **argv)
+{
+        NTSTATUS result;
+       struct cli_state *cli = NULL;
+       uchar trust_password[16];
+       DOM_CRED ret_creds;
+       uint32 neg_flags = 0x000001ff;
+
+
+       ZERO_STRUCT(ret_creds);
+
+       /* Connect to remote machine */
+       if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) {
+               return 1;
+       }
+
+       if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+               DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+               goto fail;
+       }
+
+       if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_password, NULL)) {
+               d_printf("Could not retrieve domain trust secret\n");
+               goto fail;
+       }
+       
+       result = cli_nt_setup_creds(cli, SEC_CHAN_BDC,  trust_password, &neg_flags, 2);
+       if (!NT_STATUS_IS_OK(result)) {
+               d_printf("Failed to setup BDC creds\n");
+               goto fail;
+       }
+
+       dump_database(cli, SAM_DATABASE_DOMAIN, &ret_creds);
+       dump_database(cli, SAM_DATABASE_BUILTIN, &ret_creds);
+       dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds);
+
+       cli_nt_session_close(cli);
+        
+        return 0;
+
+fail:
+       if (cli) {
+               cli_nt_session_close(cli);
+       }
+       return -1;
+}
+
+/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */
+
+static NTSTATUS
+sam_account_from_delta(SAM_ACCOUNT *account, SAM_ACCOUNT_INFO *delta)
+{
+       fstring s;
+       uchar lm_passwd[16], nt_passwd[16];
+       static uchar zero_buf[16];
+
+       /* Username, fullname, home dir, dir drive, logon script, acct
+          desc, workstations, profile. */
+
+       unistr2_to_ascii(s, &delta->uni_acct_name, sizeof(s) - 1);
+       pdb_set_nt_username(account, s, PDB_CHANGED);
+
+       /* Unix username is the same - for sainity */
+       pdb_set_username(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_full_name, sizeof(s) - 1);
+       pdb_set_fullname(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_home_dir, sizeof(s) - 1);
+       pdb_set_homedir(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_dir_drive, sizeof(s) - 1);
+       pdb_set_dir_drive(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_logon_script, sizeof(s) - 1);
+       pdb_set_logon_script(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_acct_desc, sizeof(s) - 1);
+       pdb_set_acct_desc(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_workstations, sizeof(s) - 1);
+       pdb_set_workstations(account, s, PDB_CHANGED);
+
+       unistr2_to_ascii(s, &delta->uni_profile, sizeof(s) - 1);
+       pdb_set_profile_path(account, s, PDB_CHANGED);
+
+       /* User and group sid */
+
+       pdb_set_user_sid_from_rid(account, delta->user_rid, PDB_CHANGED);
+       pdb_set_group_sid_from_rid(account, delta->group_rid, PDB_CHANGED);
+
+       /* Logon and password information */
+
+       pdb_set_logon_time(account, nt_time_to_unix(&delta->logon_time), PDB_CHANGED);
+       pdb_set_logoff_time(account, nt_time_to_unix(&delta->logoff_time),
+                           PDB_CHANGED);
+       pdb_set_logon_divs(account, delta->logon_divs, PDB_CHANGED);
+
+       /* TODO: logon hours */
+       /* TODO: bad password count */
+       /* TODO: logon count */
+
+       pdb_set_pass_last_set_time(
+               account, nt_time_to_unix(&delta->pwd_last_set_time), PDB_CHANGED);
+
+       pdb_set_kickoff_time(account, get_time_t_max(), PDB_CHANGED);
+
+       /* Decode hashes from password hash 
+          Note that win2000 may send us all zeros for the hashes if it doesn't 
+          think this channel is secure enough - don't set the passwords at all
+          in that case
+        */
+       if (memcmp(delta->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+               sam_pwd_hash(delta->user_rid, delta->pass.buf_lm_pwd, lm_passwd, 0);
+               pdb_set_lanman_passwd(account, lm_passwd, PDB_CHANGED);
+       }
+
+       if (memcmp(delta->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+               sam_pwd_hash(delta->user_rid, delta->pass.buf_nt_pwd, nt_passwd, 0);
+               pdb_set_nt_passwd(account, nt_passwd, PDB_CHANGED);
+       }
+
+       /* TODO: account expiry time */
+
+       pdb_set_acct_ctrl(account, delta->acb_info, PDB_CHANGED);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_account_info(uint32 rid, SAM_ACCOUNT_INFO *delta)
+{
+       NTSTATUS nt_ret;
+       fstring account;
+       pstring add_script;
+       SAM_ACCOUNT *sam_account=NULL;
+       GROUP_MAP map;
+       struct group *grp;
+       DOM_SID sid;
+       BOOL try_add = False;
+
+       fstrcpy(account, unistr2_static(&delta->uni_acct_name));
+       d_printf("Creating account: %s\n", account);
+
+       if (!NT_STATUS_IS_OK(nt_ret = pdb_init_sam(&sam_account)))
+               return nt_ret;
+
+       if (!pdb_getsampwnam(sam_account, account)) {
+               /* Create appropriate user */
+               if (delta->acb_info & ACB_NORMAL) {
+                       pstrcpy(add_script, lp_adduser_script());
+               } else if ( (delta->acb_info & ACB_WSTRUST) ||
+                           (delta->acb_info & ACB_SVRTRUST) ) {
+                       pstrcpy(add_script, lp_addmachine_script());
+               } else {
+                       DEBUG(1, ("Unknown user type: %s\n",
+                                 smbpasswd_encode_acb_info(delta->acb_info)));
+                       pdb_free_sam(&sam_account);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+               if (*add_script) {
+                       int add_ret;
+                       all_string_sub(add_script, "%u", account,
+                                      sizeof(account));
+                       add_ret = smbrun(add_script,NULL);
+                       DEBUG(1,("fetch_account: Running the command `%s' "
+                                "gave %d\n", add_script, add_ret));
+               }
+
+               try_add = True;
+       }
+
+       sam_account_from_delta(sam_account, delta);
+
+       if (try_add) { 
+               if (!pdb_add_sam_account(sam_account)) {
+                       DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
+                                 account));
+               }
+       } else {
+               if (!pdb_update_sam_account(sam_account)) {
+                       DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
+                                 account));
+               }
+       }
+
+       sid = *pdb_get_group_sid(sam_account);
+
+       if (!pdb_getgrsid(&map, sid, False)) {
+               DEBUG(0, ("Primary group of %s has no mapping!\n",
+                         pdb_get_username(sam_account)));
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       if (!(grp = getgrgid(map.gid))) {
+               DEBUG(0, ("Could not find unix group %d for user %s (group SID=%s)\n", 
+                         map.gid, pdb_get_username(sam_account), sid_string_static(&sid)));
+               pdb_free_sam(&sam_account);
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
+
+       pdb_free_sam(&sam_account);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_group_info(uint32 rid, SAM_GROUP_INFO *delta)
+{
+       fstring name;
+       fstring comment;
+       struct group *grp = NULL;
+       DOM_SID group_sid;
+       fstring sid_string;
+       GROUP_MAP map;
+       BOOL insert = True;
+
+       unistr2_to_ascii(name, &delta->uni_grp_name, sizeof(name)-1);
+       unistr2_to_ascii(comment, &delta->uni_grp_desc, sizeof(comment)-1);
+
+       /* add the group to the mapping table */
+       sid_copy(&group_sid, get_global_sam_sid());
+       sid_append_rid(&group_sid, rid);
+       sid_to_string(sid_string, &group_sid);
+
+       if (pdb_getgrsid(&map, group_sid, False)) {
+               grp = getgrgid(map.gid);
+               insert = False;
+       }
+
+       if (grp == NULL)
+       {
+               gid_t gid;
+
+               /* No group found from mapping, find it from its name. */
+               if ((grp = getgrnam(name)) == NULL) {
+                               /* No appropriate group found, create one */
+                       d_printf("Creating unix group: '%s'\n", name);
+                       if (smb_create_group(name, &gid) != 0)
+                               return NT_STATUS_ACCESS_DENIED;
+                       if ((grp = getgrgid(gid)) == NULL)
+                               return NT_STATUS_ACCESS_DENIED;
+               }
+       }
+
+       map.gid = grp->gr_gid;
+       map.sid = group_sid;
+       map.sid_name_use = SID_NAME_DOM_GRP;
+       fstrcpy(map.nt_name, name);
+       fstrcpy(map.comment, comment);
+
+       map.priv_set.count = 0;
+       map.priv_set.set = NULL;
+
+       if (insert)
+               pdb_add_group_mapping_entry(&map);
+       else
+               pdb_update_group_mapping_entry(&map);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *delta)
+{
+       int i;
+       TALLOC_CTX *t = NULL;
+       char **nt_members = NULL;
+       char **unix_members;
+       DOM_SID group_sid;
+       GROUP_MAP map;
+       struct group *grp;
+
+       if (delta->num_members == 0) {
+               return NT_STATUS_OK;
+       }
+
+       sid_copy(&group_sid, get_global_sam_sid());
+       sid_append_rid(&group_sid, rid);
+
+       if (!get_domain_group_from_sid(group_sid, &map, False)) {
+               DEBUG(0, ("Could not find global group %d\n", rid));
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       if (!(grp = getgrgid(map.gid))) {
+               DEBUG(0, ("Could not find unix group %d\n", map.gid));
+               return NT_STATUS_NO_SUCH_GROUP;
+       }
+
+       d_printf("Group members of %s: ", grp->gr_name);
+
+       if (!(t = talloc_init("fetch_group_mem_info"))) {
+               DEBUG(0, ("could not talloc_init\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       nt_members = talloc_zero(t, sizeof(char *) * delta->num_members);
+
+       for (i=0; i<delta->num_members; i++) {
+               NTSTATUS nt_status;
+               SAM_ACCOUNT *member = NULL;
+               DOM_SID member_sid;
+
+               if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) {
+                       talloc_destroy(t);
+                       return nt_status;
+               }
+
+               sid_copy(&member_sid, get_global_sam_sid());
+               sid_append_rid(&member_sid, delta->rids[i]);
+
+               if (!pdb_getsampwsid(member, &member_sid)) {
+                       DEBUG(1, ("Found bogus group member: %d (member_sid=%s group=%s)\n",
+                                 delta->rids[i], sid_string_static(&member_sid), grp->gr_name));
+                       pdb_free_sam(&member);
+                       continue;
+               }
+
+               if (pdb_get_group_rid(member) == rid) {
+                       d_printf("%s(primary),", pdb_get_username(member));
+                       pdb_free_sam(&member);
+                       continue;
+               }
+               
+               d_printf("%s,", pdb_get_username(member));
+               nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+               pdb_free_sam(&member);
+       }
+
+       d_printf("\n");
+
+       unix_members = grp->gr_mem;
+
+       while (*unix_members) {
+               BOOL is_nt_member = False;
+               for (i=0; i<delta->num_members; i++) {
+                       if (nt_members[i] == NULL) {
+                               /* This was a primary group */
+                               continue;
+                       }
+
+                       if (strcmp(*unix_members, nt_members[i]) == 0) {
+                               is_nt_member = True;
+                               break;
+                       }
+               }
+               if (!is_nt_member) {
+                       /* We look at a unix group member that is not
+                          an nt group member. So, remove it. NT is
+                          boss here. */
+                       smb_delete_user_group(grp->gr_name, *unix_members);
+               }
+               unix_members += 1;
+       }
+
+       for (i=0; i<delta->num_members; i++) {
+               BOOL is_unix_member = False;
+
+               if (nt_members[i] == NULL) {
+                       /* This was the primary group */
+                       continue;
+               }
+
+               unix_members = grp->gr_mem;
+
+               while (*unix_members) {
+                       if (strcmp(*unix_members, nt_members[i]) == 0) {
+                               is_unix_member = True;
+                               break;
+                       }
+                       unix_members += 1;
+               }
+
+               if (!is_unix_member) {
+                       /* We look at a nt group member that is not a
+                           unix group member currently. So, add the nt
+                           group member. */
+                       smb_add_user_group(grp->gr_name, nt_members[i]);
+               }
+       }
+       
+       talloc_destroy(t);
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_alias_info(uint32 rid, SAM_ALIAS_INFO *delta,
+                                DOM_SID dom_sid)
+{
+       fstring name;
+       fstring comment;
+       struct group *grp = NULL;
+       DOM_SID alias_sid;
+       fstring sid_string;
+       GROUP_MAP map;
+       BOOL insert = True;
+
+       unistr2_to_ascii(name, &delta->uni_als_name, sizeof(name)-1);
+       unistr2_to_ascii(comment, &delta->uni_als_desc, sizeof(comment)-1);
+
+       /* Find out whether the group is already mapped */
+       sid_copy(&alias_sid, &dom_sid);
+       sid_append_rid(&alias_sid, rid);
+       sid_to_string(sid_string, &alias_sid);
+
+       if (pdb_getgrsid(&map, alias_sid, False)) {
+               grp = getgrgid(map.gid);
+               insert = False;
+       }
+
+       if (grp == NULL) {
+               gid_t gid;
+
+               /* No group found from mapping, find it from its name. */
+               if ((grp = getgrnam(name)) == NULL) {
+                               /* No appropriate group found, create one */
+                       d_printf("Creating unix group: '%s'\n", name);
+                       if (smb_create_group(name, &gid) != 0)
+                               return NT_STATUS_ACCESS_DENIED;
+                       if ((grp = getgrgid(gid)) == NULL)
+                               return NT_STATUS_ACCESS_DENIED;
+               }
+       }
+
+       map.gid = grp->gr_gid;
+       map.sid = alias_sid;
+
+       if (sid_equal(&dom_sid, &global_sid_Builtin))
+               map.sid_name_use = SID_NAME_WKN_GRP;
+       else
+               map.sid_name_use = SID_NAME_ALIAS;
+
+       fstrcpy(map.nt_name, name);
+       fstrcpy(map.comment, comment);
+
+       map.priv_set.count = 0;
+       map.priv_set.set = NULL;
+
+       if (insert)
+               pdb_add_group_mapping_entry(&map);
+       else
+               pdb_update_group_mapping_entry(&map);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *delta, DOM_SID dom_sid)
+{
+       
+       return NT_STATUS_OK;
+}
+
+static void
+fetch_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta,
+               DOM_SID dom_sid)
+{
+       switch(hdr_delta->type) {
+       case SAM_DELTA_ACCOUNT_INFO:
+               fetch_account_info(hdr_delta->target_rid,
+                                  &delta->account_info);
+               break;
+       case SAM_DELTA_GROUP_INFO:
+               fetch_group_info(hdr_delta->target_rid,
+                                &delta->group_info);
+               break;
+       case SAM_DELTA_GROUP_MEM:
+               fetch_group_mem_info(hdr_delta->target_rid,
+                                    &delta->grp_mem_info);
+               break;
+       case SAM_DELTA_ALIAS_INFO:
+               fetch_alias_info(hdr_delta->target_rid,
+                                &delta->alias_info, dom_sid);
+               break;
+       case SAM_DELTA_ALIAS_MEM:
+               fetch_alias_mem(hdr_delta->target_rid,
+                               &delta->als_mem_info, dom_sid);
+               break;
+       default:
+               d_printf("Unknown delta record type %d\n", hdr_delta->type);
+               break;
+       }
+}
+
+static void
+fetch_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds,
+              DOM_SID dom_sid)
+{
+       unsigned sync_context = 0;
+        NTSTATUS result;
+       int i;
+        TALLOC_CTX *mem_ctx;
+        SAM_DELTA_HDR *hdr_deltas;
+        SAM_DELTA_CTR *deltas;
+        uint32 num_deltas;
+
+       if (!(mem_ctx = talloc_init("fetch_database"))) {
+               return;
+       }
+
+       d_printf("Fetching database %u\n", db_type);
+
+       do {
+               result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds,
+                                              db_type, sync_context,
+                                              &num_deltas,
+                                              &hdr_deltas, &deltas);
+               clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred),
+                                    ret_creds);
+                for (i = 0; i < num_deltas; i++) {
+                       fetch_sam_entry(&hdr_deltas[i], &deltas[i], dom_sid);
+                }
+               sync_context += 1;
+       } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+       talloc_destroy(mem_ctx);
+}
+
+/* dump sam database via samsync rpc calls */
+int rpc_vampire(int argc, const char **argv)
+{
+        NTSTATUS result;
+       struct cli_state *cli = NULL;
+       uchar trust_password[16];
+       DOM_CRED ret_creds;
+       uint32 neg_flags = 0x000001ff;
+       DOM_SID dom_sid;
+
+       ZERO_STRUCT(ret_creds);
+
+       /* Connect to remote machine */
+       if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS |
+                                           NET_FLAGS_PDC))) {
+               return 1;
+       }
+
+       if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+               DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+               goto fail;
+       }
+
+       if (!secrets_fetch_trust_account_password(lp_workgroup(),
+                                                 trust_password, NULL)) {
+               d_printf("Could not retrieve domain trust secret\n");
+               goto fail;
+       }
+       
+       result = cli_nt_setup_creds(cli, SEC_CHAN_BDC,  trust_password,
+                                   &neg_flags, 2);
+       if (!NT_STATUS_IS_OK(result)) {
+               d_printf("Failed to setup BDC creds\n");
+               goto fail;
+       }
+
+       dom_sid = *get_global_sam_sid();
+       fetch_database(cli, SAM_DATABASE_DOMAIN, &ret_creds, dom_sid);
+
+       sid_copy(&dom_sid, &global_sid_Builtin);
+       fetch_database(cli, SAM_DATABASE_BUILTIN, &ret_creds, dom_sid);
+
+       /* Currently we crash on PRIVS somewhere in unmarshalling */
+       /* Dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); */
+
+       cli_nt_session_close(cli);
+        
+        return 0;
+
+fail:
+       if (cli) {
+               cli_nt_session_close(cli);
+       }
+       return -1;
+}
diff --git a/source4/utils/net_time.c b/source4/utils/net_time.c
new file mode 100644 (file)
index 0000000..4e6ce23
--- /dev/null
@@ -0,0 +1,180 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   net time command
+   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+
+   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 "../utils/net.h"
+
+
+/*
+  return the time on a server. This does not require any authentication
+*/
+static time_t cli_servertime(const char *host, struct in_addr *ip, int *zone)
+{
+       struct nmb_name calling, called;
+       time_t ret = 0;
+       struct cli_state *cli = NULL;
+
+       cli = cli_initialise(NULL);
+       if (!cli) goto done;
+
+       if (!cli_connect(cli, host, ip)) {
+               fprintf(stderr,"Can't contact server\n");
+               goto done;
+       }
+
+       make_nmb_name(&calling, lp_netbios_name(), 0x0);
+       if (host) {
+               make_nmb_name(&called, host, 0x20);
+       } else {
+               make_nmb_name(&called, "*SMBSERVER", 0x20);
+       }
+
+       if (!cli_session_request(cli, &calling, &called)) {
+               fprintf(stderr,"Session request failed\n");
+               goto done;
+       }
+       if (!cli_negprot(cli)) {
+               fprintf(stderr,"Protocol negotiation failed\n");
+               goto done;
+       }
+
+       ret = cli->servertime;
+       if (zone) *zone = cli->serverzone;
+
+done:
+       if (cli) cli_shutdown(cli);
+       return ret;
+}
+
+/* find the servers time on the opt_host host */
+static time_t nettime(int *zone)
+{
+       return cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, zone);
+}
+
+/* return a time as a string ready to be passed to /bin/date */
+static char *systime(time_t t)
+{
+       static char s[100];
+       struct tm *tm;
+
+       tm = localtime(&t);
+       
+       snprintf(s, sizeof(s), "%02d%02d%02d%02d%04d.%02d", 
+                tm->tm_mon+1, tm->tm_mday, tm->tm_hour, 
+                tm->tm_min, tm->tm_year + 1900, tm->tm_sec);
+       return s;
+}
+
+int net_time_usage(int argc, const char **argv)
+{
+       d_printf(
+"net time\n\tdisplays time on a server\n\n"\
+"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n"\
+"net time set\n\truns /bin/date with the time from the server\n\n"\
+"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n"\
+"\n");
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+/* try to set the system clock using /bin/date */
+static int net_time_set(int argc, const char **argv)
+{
+       time_t t = nettime(NULL);
+       char *cmd;
+
+       if (t == 0) return -1;
+       
+       /* yes, I know this is cheesy. Use "net time system" if you want to 
+          roll your own. I'm putting this in as it works on a large number
+          of systems and the user has a choice in whether its used or not */
+       asprintf(&cmd, "/bin/date %s", systime(t));
+       system(cmd);
+       free(cmd);
+
+       return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_system(int argc, const char **argv)
+{
+       time_t t = nettime(NULL);
+
+       if (t == 0) return -1;
+
+       printf("%s\n", systime(t));
+
+       return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_zone(int argc, const char **argv)
+{
+       int zone = 0;
+       int hours, mins;
+       char zsign;
+       time_t t;
+
+       t = nettime(&zone);
+
+       if (t == 0) return -1;
+
+       zsign = (zone > 0) ? '-' : '+';
+       if (zone < 0) zone = -zone;
+
+       zone /= 60;
+       hours = zone / 60;
+       mins = zone % 60;
+
+       printf("%c%02d%02d\n", zsign, hours, mins);
+
+       return 0;
+}
+
+/* display or set the time on a host */
+int net_time(int argc, const char **argv)
+{
+       time_t t;
+       struct functable func[] = {
+               {"SYSTEM", net_time_system},
+               {"SET", net_time_set},
+               {"ZONE", net_time_zone},
+               {NULL, NULL}
+       };
+
+       if (!opt_host && !opt_have_ip && 
+           !find_master_ip(opt_target_workgroup, &opt_dest_ip)) {
+               d_printf("Could not locate a time server.  Try "\
+                                "specifying a target host.\n");
+               net_time_usage(argc,argv);
+               return -1;
+       }
+
+       if (argc != 0) {
+               return net_run_function(argc, argv, func, net_time_usage);
+       }
+
+       /* default - print the time */
+       t = cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, NULL);
+       if (t == 0) return -1;
+
+       d_printf("%s", ctime(&t));
+       return 0;
+}
diff --git a/source4/utils/nmblookup.c b/source4/utils/nmblookup.c
new file mode 100644 (file)
index 0000000..143d2dd
--- /dev/null
@@ -0,0 +1,338 @@
+/* 
+   Unix SMB/CIFS implementation.
+   NBT client - used to lookup netbios names
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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"
+
+extern BOOL AllowDebugChange;
+
+static BOOL give_flags = False;
+static BOOL use_bcast = True;
+static BOOL got_bcast = False;
+static struct in_addr bcast_addr;
+static BOOL recursion_desired = False;
+static BOOL translate_addresses = False;
+static int ServerFD= -1;
+static int RootPort = False;
+static BOOL find_status=False;
+
+/****************************************************************************
+  open the socket communication
+  **************************************************************************/
+static BOOL open_sockets(void)
+{
+  ServerFD = open_socket_in( SOCK_DGRAM,
+                             (RootPort ? 137 : 0),
+                             (RootPort ?   0 : 3),
+                             interpret_addr(lp_socket_address()), True );
+
+  if (ServerFD == -1)
+    return(False);
+
+  set_socket_options( ServerFD, "SO_BROADCAST" );
+
+  DEBUG(3, ("Socket opened.\n"));
+  return True;
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+  d_printf("Usage: nmblookup [options] name\n");
+  d_printf("Version %s\n",VERSION);
+  d_printf("\t-d debuglevel         set the debuglevel\n");
+  d_printf("\t-B broadcast address  the address to use for broadcasts\n");
+  d_printf("\t-f                    list the NMB flags returned\n");
+  d_printf("\t-U unicast   address  the address to use for unicast\n");
+  d_printf("\t-M                    searches for a master browser\n");
+  d_printf("\t-R                    set recursion desired in packet\n");
+  d_printf("\t-S                    lookup node status as well\n");
+  d_printf("\t-T                    translate IP addresses into names\n");
+  d_printf("\t-r                    Use root port 137 (Win95 only replies to this)\n");
+  d_printf("\t-A                    Do a node status on <name> as an IP Address\n");
+  d_printf("\t-i NetBIOS scope      Use the given NetBIOS scope for name queries\n");
+  d_printf("\t-s smb.conf file      Use the given path to the smb.conf file\n");
+  d_printf("\t-h                    Print this help message.\n");
+  d_printf("\n  If you specify -M and name is \"-\", nmblookup looks up __MSBROWSE__<01>\n");
+  d_printf("\n");
+}
+
+/****************************************************************************
+turn a node status flags field into a string
+****************************************************************************/
+static char *node_status_flags(unsigned char flags)
+{
+       static fstring ret;
+       fstrcpy(ret,"");
+       
+       fstrcat(ret, (flags & 0x80) ? "<GROUP> " : "        ");
+       if ((flags & 0x60) == 0x00) fstrcat(ret,"B ");
+       if ((flags & 0x60) == 0x20) fstrcat(ret,"P ");
+       if ((flags & 0x60) == 0x40) fstrcat(ret,"M ");
+       if ((flags & 0x60) == 0x60) fstrcat(ret,"H ");
+       if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> ");
+       if (flags & 0x08) fstrcat(ret,"<CONFLICT> ");
+       if (flags & 0x04) fstrcat(ret,"<ACTIVE> ");
+       if (flags & 0x02) fstrcat(ret,"<PERMANENT> ");
+       
+       return ret;
+}
+
+/****************************************************************************
+turn the NMB Query flags into a string
+****************************************************************************/
+static char *query_flags(int flags)
+{
+       static fstring ret1;
+       fstrcpy(ret1, "");
+
+       if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response ");
+       if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative ");
+       if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated ");
+       if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired ");
+       if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available ");
+       if (flags & NM_FLAGS_B)  fstrcat(ret1, "Broadcast ");
+
+       return ret1;
+}
+
+/****************************************************************************
+do a node status query
+****************************************************************************/
+static void do_node_status(int fd, const char *name, int type, struct in_addr ip)
+{
+       struct nmb_name nname;
+       int count, i, j;
+       struct node_status *status;
+       fstring cleanname;
+
+       d_printf("Looking up status of %s\n",inet_ntoa(ip));
+       make_nmb_name(&nname, name, type);
+       status = node_status_query(fd,&nname,ip, &count);
+       if (status) {
+               for (i=0;i<count;i++) {
+                       fstrcpy(cleanname, status[i].name);
+                       for (j=0;cleanname[j];j++) {
+                               if (!isprint((int)cleanname[j])) cleanname[j] = '.';
+                       }
+                       d_printf("\t%-15s <%02x> - %s\n",
+                              cleanname,status[i].type,
+                              node_status_flags(status[i].flags));
+               }
+               SAFE_FREE(status);
+       }
+       d_printf("\n");
+}
+
+
+/****************************************************************************
+send out one query
+****************************************************************************/
+static BOOL query_one(const char *lookup, unsigned int lookup_type)
+{
+       int j, count, flags = 0;
+       struct in_addr *ip_list=NULL;
+
+       if (got_bcast) {
+               d_printf("querying %s on %s\n", lookup, inet_ntoa(bcast_addr));
+               ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast,
+                                    use_bcast?True:recursion_desired,
+                                    bcast_addr,&count, &flags, NULL);
+       } else {
+               struct in_addr *bcast;
+               for (j=iface_count() - 1;
+                    !ip_list && j >= 0;
+                    j--) {
+                       bcast = iface_n_bcast(j);
+                       d_printf("querying %s on %s\n", 
+                              lookup, inet_ntoa(*bcast));
+                       ip_list = name_query(ServerFD,lookup,lookup_type,
+                                            use_bcast,
+                                            use_bcast?True:recursion_desired,
+                                            *bcast,&count, &flags, NULL);
+               }
+       }
+
+       if (!ip_list) return False;
+
+       if (give_flags)
+         d_printf("Flags: %s\n", query_flags(flags));
+
+       for (j=0;j<count;j++) {
+               if (translate_addresses) {
+                       struct hostent *host = gethostbyaddr((char *)&ip_list[j], sizeof(ip_list[j]), AF_INET);
+                       if (host) {
+                               d_printf("%s, ", host -> h_name);
+                       }
+               }
+               d_printf("%s %s<%02x>\n",inet_ntoa(ip_list[j]),lookup, lookup_type);
+       }
+
+       /* We can only do find_status if the ip address returned
+          was valid - ie. name_query returned true.
+       */
+       if (find_status) {
+               do_node_status(ServerFD, lookup, lookup_type, ip_list[0]);
+       }
+
+       safe_free(ip_list);
+
+       return (ip_list != NULL);
+}
+
+
+/****************************************************************************
+  main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+  int opt;
+  unsigned int lookup_type = 0x0;
+  fstring lookup;
+  extern int optind;
+  extern char *optarg;
+  BOOL find_master=False;
+  int i;
+  BOOL lookup_by_ip = False;
+  int commandline_debuglevel = -2;
+
+  DEBUGLEVEL = 1;
+  /* Prevent smb.conf setting from overridding */
+  AllowDebugChange = False;
+
+  *lookup = 0;
+
+  setup_logging(argv[0],True);
+
+  while ((opt = getopt(argc, argv, "d:fB:U:i:s:SMrhART")) != EOF)
+    switch (opt)
+      {
+      case 'B':
+       bcast_addr = *interpret_addr2(optarg);
+       got_bcast = True;
+       use_bcast = True;
+       break;
+      case 'f':
+       give_flags = True;
+       break;
+      case 'U':
+       bcast_addr = *interpret_addr2(optarg);
+       got_bcast = True;
+       use_bcast = False;
+       break;
+      case 'T':
+        translate_addresses = !translate_addresses;
+       break;
+      case 'i':
+             lp_set_cmdline("netbios scope", optarg);
+       break;
+      case 'M':
+       find_master = True;
+       break;
+      case 'S':
+       find_status = True;
+       break;
+      case 'R':
+       recursion_desired = True;
+       break;
+      case 'd':
+       commandline_debuglevel = DEBUGLEVEL = atoi(optarg);
+       break;
+      case 's':
+       pstrcpy(dyn_CONFIGFILE, optarg);
+       break;
+      case 'r':
+        RootPort = True;
+        break;
+      case 'h':
+       usage();
+       exit(0);
+       break;
+      case 'A':
+        lookup_by_ip = True;
+        break;
+      default:
+       usage();
+       exit(1);
+      }
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+
+  if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+    fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+  }
+
+  /*
+   * Ensure we reset DEBUGLEVEL if someone specified it
+   * on the command line.
+   */
+
+  if(commandline_debuglevel != -2)
+    DEBUGLEVEL = commandline_debuglevel;
+
+  load_interfaces();
+  if (!open_sockets()) return(1);
+
+  for (i=optind;i<argc;i++)
+  {
+      char *p;
+      struct in_addr ip;
+
+      fstrcpy(lookup,argv[i]);
+
+      if(lookup_by_ip)
+      {
+        fstrcpy(lookup,"*");
+        ip = *interpret_addr2(argv[i]);
+       do_node_status(ServerFD, lookup, lookup_type, ip);
+        continue;
+      }
+
+      if (find_master) {
+       if (*lookup == '-') {
+         fstrcpy(lookup,"\01\02__MSBROWSE__\02");
+         lookup_type = 1;
+       } else {
+         lookup_type = 0x1d;
+       }
+      }
+
+      p = strchr_m(lookup,'#');
+      if (p) {
+        *p = '\0';
+        sscanf(++p,"%x",&lookup_type);
+      }
+
+      if (!query_one(lookup, lookup_type)) {
+       d_printf( "name_query failed to find name %s", lookup );
+        if( 0 != lookup_type )
+          d_printf( "#%02x", lookup_type );
+        d_printf( "\n" );
+      }
+  }
+  
+  return(0);
+}
diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c
new file mode 100644 (file)
index 0000000..b76308c
--- /dev/null
@@ -0,0 +1,551 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Winbind status program.
+
+   Copyright (C) Tim Potter      2000-2002
+   Copyright (C) Andrew Bartlett 2003
+   Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define SQUID_BUFFER_SIZE 2010
+
+enum squid_mode {
+       SQUID_2_4_BASIC,
+       SQUID_2_5_BASIC,
+       SQUID_2_5_NTLMSSP
+};
+       
+
+extern int winbindd_fd;
+
+static const char *helper_protocol;
+static const char *username;
+static const char *domain;
+static const char *workstation;
+static const char *hex_challenge;
+static const char *hex_lm_response;
+static const char *hex_nt_response;
+static unsigned char *challenge;
+static size_t challenge_len;
+static unsigned char *lm_response;
+static size_t lm_response_len;
+static unsigned char *nt_response;
+static size_t nt_response_len;
+
+static char *password;
+
+static char winbind_separator(void)
+{
+       struct winbindd_response response;
+       static BOOL got_sep;
+       static char sep;
+
+       if (got_sep)
+               return sep;
+
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+           NSS_STATUS_SUCCESS) {
+               d_printf("could not obtain winbind separator!\n");
+               return '\\';
+       }
+
+       sep = response.data.info.winbind_separator;
+       got_sep = True;
+
+       if (!sep) {
+               d_printf("winbind separator was NULL!\n");
+               return '\\';
+       }
+       
+       return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+       struct winbindd_response response;
+
+       static fstring winbind_domain;
+       if (*winbind_domain) {
+               return winbind_domain;
+       }
+
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+           NSS_STATUS_SUCCESS) {
+               d_printf("could not obtain winbind domain name!\n");
+               return NULL;
+       }
+
+       fstrcpy(winbind_domain, response.data.domain_name);
+
+       return winbind_domain;
+
+}
+
+static const char *get_winbind_netbios_name(void)
+{
+       struct winbindd_response response;
+
+       static fstring winbind_netbios_name;
+
+       if (*winbind_netbios_name) {
+               return winbind_netbios_name;
+       }
+
+       ZERO_STRUCT(response);
+
+       /* Send off request */
+
+       if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
+           NSS_STATUS_SUCCESS) {
+               d_printf("could not obtain winbind netbios name!\n");
+               return NULL;
+       }
+
+       fstrcpy(winbind_netbios_name, response.data.netbios_name);
+
+       return winbind_netbios_name;
+
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.auth.user, user);
+       fstrcpy(request.data.auth.pass, pass);
+
+       result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+       /* Display response */
+       
+       if (stdout_diagnostics) {
+               if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+                       d_printf("Reading winbind reply failed! (0x01)\n");
+               }
+               
+               d_printf("%s (0x%x)\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status);
+       } else {
+               if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+                       DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
+               }
+               
+               DEBUG(3, ("%s (0x%x)\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status));                
+       }
+               
+        return (result == NSS_STATUS_SUCCESS);
+}
+
+static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) 
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.auth_crap.user, ntlmssp_state->user);
+
+       fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain);
+       fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation);
+       
+       memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data, 
+              MIN(ntlmssp_state->chal.length, 8));
+
+       memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, 
+              MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp)));
+        
+       memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->lm_resp.data,
+              MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp)));
+        
+        request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length;
+        request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length;
+
+       result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+       /* Display response */
+
+       if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       return NT_STATUS(response.data.auth.nt_status);
+}
+
+static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
+                                        char *buf, int length) 
+{
+       static NTLMSSP_STATE *ntlmssp_state;
+       DATA_BLOB request, reply;
+       NTSTATUS nt_status;
+
+       if (!ntlmssp_state) {
+               ntlmssp_server_start(&ntlmssp_state);
+               ntlmssp_state->check_password = winbind_pw_check;
+               ntlmssp_state->get_domain = get_winbind_domain;
+               ntlmssp_state->get_global_myname = get_winbind_netbios_name;
+       }
+
+       if (strlen(buf) < 3) {
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+       
+       request = base64_decode_data_blob(buf + 3);
+       
+       DEBUG(0, ("got NTLMSSP packet:\n"));
+       dump_data(0, request.data, request.length);
+
+       nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
+       
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               char *reply_base64 = base64_encode_data_blob(reply);
+               x_fprintf(x_stdout, "TT %s\n", reply_base64);
+               SAFE_FREE(reply_base64);
+               data_blob_free(&reply);
+       } else if (!NT_STATUS_IS_OK(nt_status)) {
+               x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+       } else {
+               x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
+       }
+
+       data_blob_free(&request);
+}
+
+static void manage_squid_basic_request(enum squid_mode squid_mode, 
+                                      char *buf, int length) 
+{
+       char *user, *pass;      
+       user=buf;
+       
+       pass=memchr(buf,' ',length);
+       if (!pass) {
+               DEBUG(2, ("Password not found. Denying access\n"));
+               x_fprintf(x_stderr, "ERR\n");
+               return;
+       }
+       *pass='\0';
+       pass++;
+       
+       if (squid_mode == SQUID_2_5_BASIC) {
+               rfc1738_unescape(user);
+               rfc1738_unescape(pass);
+       }
+       
+       if (check_plaintext_auth(user, pass, False)) {
+               x_fprintf(x_stdout, "OK\n");
+       } else {
+               x_fprintf(x_stdout, "ERR\n");
+       }
+}
+
+static void manage_squid_request(enum squid_mode squid_mode) 
+{
+       char buf[SQUID_BUFFER_SIZE+1];
+       int length;
+       char *c;
+       static BOOL err;
+  
+       if (x_fgets(buf, sizeof(buf)-1, x_stdin) == NULL) {
+               DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", errno,
+                         strerror(errno)));
+               exit(1);    /* BIIG buffer */
+       }
+    
+       c=memchr(buf,'\n',sizeof(buf)-1);
+       if (c) {
+               *c = '\0';
+               length = c-buf;
+       } else {
+               err = 1;
+               return;
+       }
+       if (err) {
+               DEBUG(2, ("Oversized message\n"));
+               x_fprintf(x_stderr, "ERR\n");
+               err = 0;
+               return;
+       }
+
+       DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
+
+       if (buf[0] == '\0') {
+               DEBUG(2, ("Invalid Request\n"));
+               x_fprintf(x_stderr, "ERR\n");
+               return;
+       }
+       
+       if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
+               manage_squid_basic_request(squid_mode, buf, length);
+       } else if (squid_mode == SQUID_2_5_NTLMSSP) {
+               manage_squid_ntlmssp_request(squid_mode, buf, length);
+       }
+}
+
+
+static void squid_stream(enum squid_mode squid_mode) {
+       /* initialize FDescs */
+       x_setbuf(x_stdout, NULL);
+       x_setbuf(x_stderr, NULL);
+       while(1) {
+               manage_squid_request(squid_mode);
+       }
+}
+
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL check_auth_crap(void)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       fstrcpy(request.data.auth_crap.user, username);
+
+       fstrcpy(request.data.auth_crap.domain, domain);
+       fstrcpy(request.data.auth_crap.workstation, workstation);
+       
+       memcpy(request.data.auth_crap.chal, challenge, MIN(challenge_len, 8));
+
+       memcpy(request.data.auth_crap.lm_resp, lm_response, MIN(lm_response_len, sizeof(request.data.auth_crap.lm_resp)));
+        
+       memcpy(request.data.auth_crap.nt_resp, nt_response, MIN(nt_response_len, sizeof(request.data.auth_crap.nt_resp)));
+        
+        request.data.auth_crap.lm_resp_len = lm_response_len;
+        request.data.auth_crap.nt_resp_len = nt_response_len;
+
+       result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+       /* Display response */
+
+       if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+               d_printf("Reading winbind reply failed! (0x01)\n");
+       }
+
+       d_printf("%s (0x%x)\n", 
+                response.data.auth.nt_status_string, 
+                response.data.auth.nt_status);
+
+        return result == NSS_STATUS_SUCCESS;
+}
+
+/* Main program */
+
+enum {
+       OPT_USERNAME = 1000,
+       OPT_DOMAIN,
+       OPT_WORKSTATION,
+       OPT_CHALLENGE,
+       OPT_RESPONSE,
+       OPT_LM,
+       OPT_NT,
+       OPT_PASSWORD
+};
+
+/*************************************************************
+ Routine to set hex password characters into an allocated array.
+**************************************************************/
+
+static void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
+{
+       int i;
+       char *hex_buffer;
+
+       *out_hex_buffer = smb_xmalloc((len*2)+1);
+       hex_buffer = *out_hex_buffer;
+
+       for (i = 0; i < len; i++)
+               slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+static BOOL hex_decode(const char *hex_buf_in, unsigned char **out_buffer, size_t *size)
+{
+       int i;
+       size_t hex_buf_in_len = strlen(hex_buf_in);
+       unsigned char  partial_byte_hex;
+       unsigned char  partial_byte;
+       const char     *hexchars = "0123456789ABCDEF";
+       char           *p;
+       BOOL           high = True;
+       
+       if (!hex_buf_in) 
+               return (False);
+       
+       *size = (hex_buf_in_len + 1) / 2;
+
+       *out_buffer = smb_xmalloc(*size);
+       
+       for (i = 0; i < hex_buf_in_len; i++) {
+               partial_byte_hex = toupper(hex_buf_in[i]);
+
+               p = strchr(hexchars, partial_byte_hex);
+
+               if (!p)
+                       return (False);
+
+               partial_byte = PTR_DIFF(p, hexchars);
+
+               if (high) {
+                       (*out_buffer)[i / 2] = (partial_byte << 4);
+               } else {
+                       (*out_buffer)[i / 2] |= partial_byte;
+               }
+               high = !high;
+       }
+       return (True);
+}
+
+
+int main(int argc, const char **argv)
+{
+       int opt;
+
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+
+               { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
+               { "username", 0, POPT_ARG_STRING, &username, OPT_USERNAME, "username"},
+               { "domain", 0, POPT_ARG_STRING, &domain, OPT_DOMAIN, "domain name"},
+               { "workstation", 0, POPT_ARG_STRING, &domain, OPT_WORKSTATION, "workstation"},
+               { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
+               { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
+               { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
+               { "password", 0, POPT_ARG_STRING, &password, OPT_PASSWORD, "User's plaintext password"},                
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               { 0, 0, 0, 0 }
+       };
+
+       /* Samba client initialisation */
+
+       dbf = x_stderr;
+       
+       /* Parse options */
+
+       pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
+
+       /* Parse command line options */
+
+       if (argc == 1) {
+               poptPrintHelp(pc, stderr, 0);
+               return 1;
+       }
+
+       pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case OPT_CHALLENGE:
+                       if (!hex_decode(hex_challenge, &challenge, &challenge_len)) {
+                               fprintf(stderr, "hex decode of %s failed!\n", hex_challenge);
+                               exit(1);
+                       }
+                       break;
+               case OPT_LM: 
+                       if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
+                               fprintf(stderr, "hex decode of %s failed!\n", lm_response);
+                               exit(1);
+                       }
+                       break;
+               case OPT_NT:
+                       if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
+                               fprintf(stderr, "hex decode of %s failed!\n", lm_response);
+                               exit(1);
+                       }
+                       break;
+               }
+       }
+
+       if (helper_protocol) {
+               if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
+                       squid_stream(SQUID_2_5_NTLMSSP);
+               } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
+                       squid_stream(SQUID_2_5_BASIC);
+               } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
+                       squid_stream(SQUID_2_4_BASIC);
+               } else {
+                       fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol);
+                       exit(1);
+               }
+       }
+
+       if (domain == NULL) {
+               domain = get_winbind_domain();
+       }
+
+       if (workstation == NULL) {
+               workstation = "";
+       }
+
+       if (challenge) {
+               if (!check_auth_crap()) {
+                       exit(1);
+               }
+       } else if (password) {
+               fstring user;
+               snprintf(user, sizeof(user)-1, "%s%c%s", domain, winbind_separator(), username);
+               if (!check_plaintext_auth(user, password, True)) {
+                       exit(1);
+               }
+       }
+
+       /* Exit code */
+
+       poptFreeContext(pc);
+       return 0;
+}
diff --git a/source4/utils/pdbedit.c b/source4/utils/pdbedit.c
new file mode 100644 (file)
index 0000000..cec3e70
--- /dev/null
@@ -0,0 +1,696 @@
+/* 
+   Unix SMB/CIFS implementation.
+   passdb editing frontend
+   
+   Copyright (C) Simo Sorce      2000
+   Copyright (C) Andrew Bartlett 2001   
+   Copyright (C) Jelmer Vernooij 2002
+
+   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"
+
+#define BIT_BACKEND    0x00000004
+#define BIT_VERBOSE    0x00000008
+#define BIT_SPSTYLE    0x00000010
+#define BIT_RESERV_1   0x00000020
+#define BIT_RESERV_2   0x00000040
+#define BIT_RESERV_3   0x00000080
+#define BIT_FULLNAME   0x00000100
+#define BIT_HOMEDIR    0x00000200
+#define BIT_HDIRDRIVE  0x00000400
+#define BIT_LOGSCRIPT  0x00000800
+#define BIT_PROFILE    0x00001000
+#define BIT_MACHINE    0x00002000
+#define BIT_RESERV_4   0x00004000
+#define BIT_USER       0x00008000
+#define BIT_LIST       0x00010000
+#define BIT_MODIFY     0x00020000
+#define BIT_CREATE     0x00040000
+#define BIT_DELETE     0x00080000
+#define BIT_ACCPOLICY  0x00100000
+#define BIT_ACCPOLVAL  0x00200000
+#define BIT_ACCTCTRL   0x00400000
+#define BIT_RESERV_7   0x00800000
+#define BIT_IMPORT     0x01000000
+#define BIT_EXPORT     0x02000000
+
+#define MASK_ALWAYS_GOOD       0x0000001F
+#define MASK_USER_GOOD         0x00401F00
+
+/*********************************************************
+ Add all currently available users to another db
+ ********************************************************/
+
+static int export_database (struct pdb_context *in, struct pdb_context *out) {
+       SAM_ACCOUNT *user = NULL;
+
+       if (NT_STATUS_IS_ERR(in->pdb_setsampwent(in, 0))) {
+               fprintf(stderr, "Can't sampwent!\n");
+               return 1;
+       }
+
+       if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+               fprintf(stderr, "Can't initialize new SAM_ACCOUNT!\n");
+               return 1;
+       }
+
+       while (NT_STATUS_IS_OK(in->pdb_getsampwent(in, user))) {
+               out->pdb_add_sam_account(out, user);
+               if (!NT_STATUS_IS_OK(pdb_reset_sam(user))){
+                       fprintf(stderr, "Can't reset SAM_ACCOUNT!\n");
+                       return 1;
+               }
+       }
+
+       in->pdb_endsampwent(in);
+
+       return 0;
+}
+
+/*********************************************************
+ Print info from sam structure
+**********************************************************/
+
+static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle)
+{
+       uid_t uid;
+       gid_t gid;
+       time_t tmp;
+
+       /* TODO: chaeck if entry is a user or a workstation */
+       if (!sam_pwent) return -1;
+       
+       if (verbosity) {
+               printf ("Unix username:        %s\n", pdb_get_username(sam_pwent));
+               printf ("NT username:          %s\n", pdb_get_nt_username(sam_pwent));
+               printf ("Account Flags:        %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+               
+               if (IS_SAM_UNIX_USER(sam_pwent)) {
+                       uid = pdb_get_uid(sam_pwent);
+                       gid = pdb_get_gid(sam_pwent);
+                       printf ("User ID/Group ID:     %d/%d\n", uid, gid);
+               }
+               printf ("User SID:             %s\n",
+                       sid_string_static(pdb_get_user_sid(sam_pwent)));
+               printf ("Primary Group SID:    %s\n",
+                       sid_string_static(pdb_get_group_sid(sam_pwent)));
+               printf ("Full Name:            %s\n", pdb_get_fullname(sam_pwent));
+               printf ("Home Directory:       %s\n", pdb_get_homedir(sam_pwent));
+               printf ("HomeDir Drive:        %s\n", pdb_get_dir_drive(sam_pwent));
+               printf ("Logon Script:         %s\n", pdb_get_logon_script(sam_pwent));
+               printf ("Profile Path:         %s\n", pdb_get_profile_path(sam_pwent));
+               printf ("Domain:               %s\n", pdb_get_domain(sam_pwent));
+               printf ("Account desc:         %s\n", pdb_get_acct_desc(sam_pwent));
+               printf ("Workstations:         %s\n", pdb_get_workstations(sam_pwent));
+               printf ("Munged dial:          %s\n", pdb_get_munged_dial(sam_pwent));
+               
+               tmp = pdb_get_logon_time(sam_pwent);
+               printf ("Logon time:           %s\n", tmp ? http_timestring(tmp) : "0");
+               
+               tmp = pdb_get_logoff_time(sam_pwent);
+               printf ("Logoff time:          %s\n", tmp ? http_timestring(tmp) : "0");
+               
+               tmp = pdb_get_kickoff_time(sam_pwent);
+               printf ("Kickoff time:         %s\n", tmp ? http_timestring(tmp) : "0");
+               
+               tmp = pdb_get_pass_last_set_time(sam_pwent);
+               printf ("Password last set:    %s\n", tmp ? http_timestring(tmp) : "0");
+               
+               tmp = pdb_get_pass_can_change_time(sam_pwent);
+               printf ("Password can change:  %s\n", tmp ? http_timestring(tmp) : "0");
+               
+               tmp = pdb_get_pass_must_change_time(sam_pwent);
+               printf ("Password must change: %s\n", tmp ? http_timestring(tmp) : "0");
+               
+       } else if (smbpwdstyle) {
+               if (IS_SAM_UNIX_USER(sam_pwent)) {
+                       char lm_passwd[33];
+                       char nt_passwd[33];
+
+                       uid = pdb_get_uid(sam_pwent);
+                       pdb_sethexpwd(lm_passwd, 
+                                     pdb_get_lanman_passwd(sam_pwent), 
+                                     pdb_get_acct_ctrl(sam_pwent));
+                       pdb_sethexpwd(nt_passwd, 
+                                     pdb_get_nt_passwd(sam_pwent), 
+                                     pdb_get_acct_ctrl(sam_pwent));
+                       
+                       printf("%s:%d:%s:%s:%s:LCT-%08X:\n",
+                              pdb_get_username(sam_pwent),
+                              uid,
+                              lm_passwd,
+                              nt_passwd,
+                              pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN),
+                              (uint32)pdb_get_pass_last_set_time(sam_pwent));
+               } else {
+                       fprintf(stderr, "Can't output in smbpasswd format, no uid on this record.\n");
+               }
+       } else {
+               if (IS_SAM_UNIX_USER(sam_pwent)) {
+                       printf ("%s:%d:%s\n", pdb_get_username(sam_pwent), pdb_get_uid(sam_pwent), 
+                               pdb_get_fullname(sam_pwent));
+               } else {        
+                       printf ("%s:(null):%s\n", pdb_get_username(sam_pwent), pdb_get_fullname(sam_pwent));
+               }
+       }
+
+       return 0;       
+}
+
+/*********************************************************
+ Get an Print User Info
+**********************************************************/
+
+static int print_user_info (struct pdb_context *in, const char *username, BOOL verbosity, BOOL smbpwdstyle)
+{
+       SAM_ACCOUNT *sam_pwent=NULL;
+       BOOL ret;
+       
+       if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+               return -1;
+       }
+       
+       ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+
+       if (ret==False) {
+               fprintf (stderr, "Username not found!\n");
+               pdb_free_sam(&sam_pwent);
+               return -1;
+       }
+       
+       ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+       pdb_free_sam(&sam_pwent);
+       
+       return ret;
+}
+       
+/*********************************************************
+ List Users
+**********************************************************/
+static int print_users_list (struct pdb_context *in, BOOL verbosity, BOOL smbpwdstyle)
+{
+       SAM_ACCOUNT *sam_pwent=NULL;
+       BOOL check, ret;
+       
+       check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False));
+       if (!check) {
+               return 1;
+       }
+
+       check = True;
+       if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1;
+
+       while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) {
+               if (verbosity)
+                       printf ("---------------\n");
+               print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+               pdb_free_sam(&sam_pwent);
+               check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent));
+       }
+       if (check) pdb_free_sam(&sam_pwent);
+       
+       in->pdb_endsampwent(in);
+       return 0;
+}
+
+/*********************************************************
+ Set User Info
+**********************************************************/
+
+static int set_user_info (struct pdb_context *in, const char *username, 
+                         const char *fullname, const char *homedir, 
+                         const char *drive, const char *script, 
+                         const char *profile, const char *account_control)
+{
+       SAM_ACCOUNT *sam_pwent=NULL;
+       BOOL ret;
+       
+       pdb_init_sam(&sam_pwent);
+       
+       ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+       if (ret==False) {
+               fprintf (stderr, "Username not found!\n");
+               pdb_free_sam(&sam_pwent);
+               return -1;
+       }
+       
+       if (fullname)
+               pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+       if (homedir)
+               pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED);
+       if (drive)
+               pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED);
+       if (script)
+               pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+       if (profile)
+               pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+
+       if (account_control) {
+               uint16 not_settable = ~(ACB_DISABLED|ACB_HOMDIRREQ|ACB_PWNOTREQ|
+                                       ACB_PWNOEXP|ACB_AUTOLOCK);
+
+               uint16 newflag = pdb_decode_acct_ctrl(account_control);
+
+               if (newflag & not_settable) {
+                       fprintf(stderr, "Can only set [NDHLX] flags\n");
+                       pdb_free_sam(&sam_pwent);
+                       return -1;
+               }
+
+               pdb_set_acct_ctrl(sam_pwent,
+                                 (pdb_get_acct_ctrl(sam_pwent) & not_settable) | newflag,
+                                 PDB_CHANGED);
+       }
+       
+       if (NT_STATUS_IS_OK(in->pdb_update_sam_account (in, sam_pwent)))
+               print_user_info (in, username, True, False);
+       else {
+               fprintf (stderr, "Unable to modify entry!\n");
+               pdb_free_sam(&sam_pwent);
+               return -1;
+       }
+       pdb_free_sam(&sam_pwent);
+       return 0;
+}
+
+/*********************************************************
+ Add New User
+**********************************************************/
+static int new_user (struct pdb_context *in, const char *username, const char *fullname, const char *homedir, const char *drive, const char *script, const char *profile)
+{
+       SAM_ACCOUNT *sam_pwent=NULL;
+       struct passwd  *pwd = NULL;
+       char *password1, *password2, *staticpass;
+       
+       ZERO_STRUCT(sam_pwent);
+
+       if ((pwd = getpwnam_alloc(username))) {
+               pdb_init_sam_pw (&sam_pwent, pwd);
+               passwd_free(&pwd);
+       } else {
+               fprintf (stderr, "WARNING: user %s does not exist in system passwd\n", username);
+               pdb_init_sam(&sam_pwent);
+               if (!pdb_set_username(sam_pwent, username, PDB_CHANGED)) {
+                       return False;
+               }
+       }
+
+       staticpass = getpass("new password:");
+       password1 = strdup(staticpass);
+       memset(staticpass, 0, strlen(staticpass));
+       staticpass = getpass("retype new password:");
+       password2 = strdup(staticpass);
+       memset(staticpass, 0, strlen(staticpass));
+       if (strcmp (password1, password2)) {
+               fprintf (stderr, "Passwords does not match!\n");
+               memset(password1, 0, strlen(password1));
+               SAFE_FREE(password1);
+               memset(password2, 0, strlen(password2));
+               SAFE_FREE(password2);
+               pdb_free_sam (&sam_pwent);
+               return -1;
+       }
+
+       pdb_set_plaintext_passwd(sam_pwent, password1);
+       memset(password1, 0, strlen(password1));
+       SAFE_FREE(password1);
+       memset(password2, 0, strlen(password2));
+       SAFE_FREE(password2);
+
+       if (fullname)
+               pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+       if (homedir)
+               pdb_set_homedir (sam_pwent, homedir, PDB_CHANGED);
+       if (drive)
+               pdb_set_dir_drive (sam_pwent, drive, PDB_CHANGED);
+       if (script)
+               pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+       if (profile)
+               pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+       
+       pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL, PDB_CHANGED);
+       
+       if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) { 
+               print_user_info (in, username, True, False);
+       } else {
+               fprintf (stderr, "Unable to add user! (does it alredy exist?)\n");
+               pdb_free_sam (&sam_pwent);
+               return -1;
+       }
+       pdb_free_sam (&sam_pwent);
+       return 0;
+}
+
+/*********************************************************
+ Add New Machine
+**********************************************************/
+
+static int new_machine (struct pdb_context *in, const char *machine_in)
+{
+       SAM_ACCOUNT *sam_pwent=NULL;
+       fstring machinename;
+       struct passwd  *pwd = NULL;
+       char name[16];
+       
+       fstrcpy(machinename, machine_in); 
+
+       if (machinename[strlen (machinename) -1] == '$')
+               machinename[strlen (machinename) -1] = '\0';
+       
+       strlower_m(machinename);
+       
+       safe_strcpy (name, machinename, 16);
+       safe_strcat (name, "$", 16);
+
+       if ((pwd = getpwnam_alloc(name))) {
+               if (!NT_STATUS_IS_OK(pdb_init_sam_pw( &sam_pwent, pwd))) {
+                       fprintf(stderr, "Could not init sam from pw\n");
+                       passwd_free(&pwd);
+                       return -1;
+               }
+               passwd_free(&pwd);
+       } else {
+               if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+                       fprintf(stderr, "Could not init sam from pw\n");
+                       return -1;
+               }
+       }
+
+       pdb_set_plaintext_passwd (sam_pwent, machinename);
+
+       pdb_set_username (sam_pwent, name, PDB_CHANGED);
+       
+       pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST, PDB_CHANGED);
+       
+       pdb_set_group_sid_from_rid(sam_pwent, DOMAIN_GROUP_RID_COMPUTERS, PDB_CHANGED);
+       
+       if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) {
+               print_user_info (in, name, True, False);
+       } else {
+               fprintf (stderr, "Unable to add machine! (does it already exist?)\n");
+               pdb_free_sam (&sam_pwent);
+               return -1;
+       }
+       pdb_free_sam (&sam_pwent);
+       return 0;
+}
+
+/*********************************************************
+ Delete user entry
+**********************************************************/
+
+static int delete_user_entry (struct pdb_context *in, const char *username)
+{
+       SAM_ACCOUNT *samaccount = NULL;
+
+       if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+               return -1;
+       }
+
+       if (NT_STATUS_IS_ERR(in->pdb_getsampwnam(in, samaccount, username))) {
+               fprintf (stderr, "user %s does not exist in the passdb\n", username);
+               return -1;
+       }
+
+       return NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount));
+}
+
+/*********************************************************
+ Delete machine entry
+**********************************************************/
+
+static int delete_machine_entry (struct pdb_context *in, const char *machinename)
+{
+       char name[16];
+       SAM_ACCOUNT *samaccount = NULL;
+       
+       safe_strcpy (name, machinename, 16);
+       if (name[strlen(name)] != '$')
+               safe_strcat (name, "$", 16);
+
+       if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+               return -1;
+       }
+
+       if (NT_STATUS_IS_ERR(in->pdb_getsampwnam(in, samaccount, name))) {
+               fprintf (stderr, "machine %s does not exist in the passdb\n", name);
+               return -1;
+       }
+
+       return NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount));
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+
+int main (int argc, char **argv)
+{
+       static BOOL list_users = False;
+       static BOOL verbose = False;
+       static BOOL spstyle = False;
+       static BOOL machine = False;
+       static BOOL add_user = False;
+       static BOOL delete_user = False;
+       static BOOL modify_user = False;
+       uint32  setparms, checkparms;
+       int opt;
+       static char *full_name = NULL;
+       static const char *user_name = NULL;
+       static char *home_dir = NULL;
+       static char *home_drive = NULL;
+       static char *backend = NULL;
+       static char *backend_in = NULL;
+       static char *backend_out = NULL;
+       static char *logon_script = NULL;
+       static char *profile_path = NULL;
+       static char *account_control = NULL;
+       static char *account_policy = NULL;
+       static long int account_policy_value = 0;
+       BOOL account_policy_value_set = False;
+
+       struct pdb_context *bin;
+       struct pdb_context *bout;
+       struct pdb_context *bdef;
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               {"list",        'l', POPT_ARG_NONE, &list_users, 0, "list all users", NULL},
+               {"verbose",     'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL },
+               {"smbpasswd-style",     'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL},
+               {"user",        'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" },
+               {"fullname",    'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL},
+               {"homedir",     'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL},
+               {"drive",       'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL},
+               {"script",      'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL},
+               {"profile",     'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL},
+               {"create",      'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL},
+               {"modify",      'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL},
+               {"machine",     'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL},
+               {"delete",      'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL},
+               {"backend",     'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL},
+               {"import",      'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL},
+               {"export",      'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL},
+               {"account-policy",      'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL},
+               {"value",       'V', POPT_ARG_LONG, &account_policy_value, 'V',"set the account policy to this value", NULL},
+               {"account-control",     'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL},
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
+               { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
+               {0,0,0,0}
+       };
+       
+       setup_logging("pdbedit", True);
+       
+       pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+                           POPT_CONTEXT_KEEP_FIRST);
+       
+       while((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case 'V':
+                       account_policy_value_set = True;
+                       break;
+               }
+       }
+
+       poptGetArg(pc); /* Drop argv[0], the program name */
+
+       if (user_name == NULL)
+               user_name = poptGetArg(pc);
+
+       if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+               exit(1);
+       }
+
+       init_modules();
+       
+       if (!init_names())
+               exit(1);
+
+       setparms =      (backend ? BIT_BACKEND : 0) +
+                       (verbose ? BIT_VERBOSE : 0) +
+                       (spstyle ? BIT_SPSTYLE : 0) +
+                       (full_name ? BIT_FULLNAME : 0) +
+                       (home_dir ? BIT_HOMEDIR : 0) +
+                       (home_drive ? BIT_HDIRDRIVE : 0) +
+                       (logon_script ? BIT_LOGSCRIPT : 0) +
+                       (profile_path ? BIT_PROFILE : 0) +
+                       (machine ? BIT_MACHINE : 0) +
+                       (user_name ? BIT_USER : 0) +
+                       (list_users ? BIT_LIST : 0) +
+                       (modify_user ? BIT_MODIFY : 0) +
+                       (add_user ? BIT_CREATE : 0) +
+                       (delete_user ? BIT_DELETE : 0) +
+                       (account_control ? BIT_ACCTCTRL : 0) +
+                       (account_policy ? BIT_ACCPOLICY : 0) +
+                       (account_policy_value_set ? BIT_ACCPOLVAL : 0) +
+                       (backend_in ? BIT_IMPORT : 0) +
+                       (backend_out ? BIT_EXPORT : 0);
+
+       if (setparms & BIT_BACKEND) {
+               if (!NT_STATUS_IS_OK(make_pdb_context_string(&bdef, backend))) {
+                       fprintf(stderr, "Can't initialize passdb backend.\n");
+                       return 1;
+               }
+       } else {
+               if (!NT_STATUS_IS_OK(make_pdb_context_list(&bdef, lp_passdb_backend()))) {
+                       fprintf(stderr, "Can't initialize passdb backend.\n");
+                       return 1;
+               }
+       }
+       
+       /* the lowest bit options are always accepted */
+       checkparms = setparms & ~MASK_ALWAYS_GOOD;
+
+       /* account policy operations */
+       if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) {
+               uint32 value;
+               int field = account_policy_name_to_fieldnum(account_policy);
+               if (field == 0) {
+                       fprintf(stderr, "No account policy by that name\n");
+                       exit(1);
+               }
+               if (!account_policy_get(field, &value)) {
+                       fprintf(stderr, "valid account policy, but unable to fetch value!\n");
+                       exit(1);
+               }
+               if (account_policy_value_set) {
+                       printf("account policy value for %s was %u\n", account_policy, value);
+                       if (!account_policy_set(field, account_policy_value)) {
+                               fprintf(stderr, "valid account policy, but unable to set value!\n");
+                               exit(1);
+                       }
+                       printf("account policy value for %s is now %lu\n", account_policy, account_policy_value);
+                       exit(0);
+               } else {
+                       printf("account policy value for %s is %u\n", account_policy, value);
+                       exit(0);
+               }
+       }
+
+       /* import and export operations */
+       if (((checkparms & BIT_IMPORT) || (checkparms & BIT_EXPORT))
+                       && !(checkparms & ~(BIT_IMPORT +BIT_EXPORT))) {
+               if (backend_in) {
+                       if (!NT_STATUS_IS_OK(make_pdb_context_string(&bin, backend_in))) {
+                               fprintf(stderr, "Can't initialize passdb backend.\n");
+                               return 1;
+                       }
+               } else {
+                       bin = bdef;
+               }
+               if (backend_out) {
+                       if (!NT_STATUS_IS_OK(make_pdb_context_string(&bout, backend_out))) {
+                               fprintf(stderr, "Can't initialize %s.\n", backend_out);
+                               return 1;
+                       }
+               } else {
+                       bout = bdef;
+               }
+               return export_database(bin, bout);
+       }
+
+       /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */
+       /* fake up BIT_LIST if only BIT_USER is defined */
+       if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) {
+               checkparms += BIT_LIST;
+       }
+       
+       /* modify flag is optional to maintain backwards compatibility */
+       /* fake up BIT_MODIFY if BIT_USER  and at least one of MASK_USER_GOOD is defined */
+       if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) {
+               checkparms += BIT_MODIFY;
+       }
+
+       /* list users operations */
+       if (checkparms & BIT_LIST) {
+               if (!(checkparms & ~BIT_LIST)) {
+                       return print_users_list (bdef, verbose, spstyle);
+               }
+               if (!(checkparms & ~(BIT_USER + BIT_LIST))) {
+                       return print_user_info (bdef, user_name, verbose, spstyle);
+               }
+       }
+       
+       /* mask out users options */
+       checkparms &= ~MASK_USER_GOOD;
+       
+       /* account operation */
+       if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+               /* check use of -u option */
+               if (!(checkparms & BIT_USER)) {
+                       fprintf (stderr, "Username not specified! (use -u option)\n");
+                       return -1;
+               }
+
+               /* account creation operations */
+               if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE))) {
+                       if (checkparms & BIT_MACHINE) {
+                               return new_machine (bdef, user_name);
+                       } else {
+                               return new_user (bdef, user_name, full_name, home_dir, 
+                                                home_drive, logon_script, 
+                                                profile_path);
+                       }
+               }
+
+               /* account deletion operations */
+               if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) {
+                       if (checkparms & BIT_MACHINE) {
+                               return delete_machine_entry (bdef, user_name);
+                       } else {
+                               return delete_user_entry (bdef, user_name);
+                       }
+               }
+
+               /* account modification operations */
+               if (!(checkparms & ~(BIT_MODIFY + BIT_USER))) {
+                       return set_user_info (bdef, user_name, full_name,
+                                             home_dir,
+                                             home_drive,
+                                             logon_script,
+                                             profile_path, account_control);
+               }
+       }
+
+       if (setparms >= 0x20) {
+               fprintf (stderr, "Incompatible or insufficient options on command line!\n");
+       }
+       poptPrintHelp(pc, stderr, 0);
+
+       return 1;
+}
diff --git a/source4/utils/profiles.c b/source4/utils/profiles.c
new file mode 100644 (file)
index 0000000..4f40b93
--- /dev/null
@@ -0,0 +1,729 @@
+/* 
+   Samba Unix/Linux SMB client utility profiles.c 
+   Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+
+   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.  */
+/*************************************************************************
+                                                       
+ A utility to report and change SIDs in registry files 
+                                     
+ Many of the ideas in here come from other people and software. 
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from 
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. 
+
+The windows NT registry has 2 different blocks, where one can occure many
+times...
+
+the "regf"-Block
+================
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset            Size      Contents
+0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
+0x00000004      D-Word      ???? //see struct REGF
+0x00000008      D-Word      ???? Always the same value as at 0x00000004
+0x0000000C      Q-Word      last modify date in WinNT date-format
+0x00000014      D-Word      1
+0x00000018      D-Word      3
+0x0000001C      D-Word      0
+0x00000020      D-Word      1
+0x00000024      D-Word      Offset of 1st key record
+0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
+0x0000002C      D-Word      1
+0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
+0x000001FB  //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset      Size      Contents
+0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
+0x0004      D-Word      Offset from the 1st hbin-Block
+0x0008      D-Word      Offset to the next hbin-Block
+0x001C      D-Word      Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset      Size      Contents
+0x0000      D-Word      Data-block size    //this size must be a
+multiple of 8. Nigel
+0x0004      ????      Data
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+      The nk-record can be treated as a kombination of tree-record and
+      key-record of the win 95 registry.
+
+lf-Record
+
+      The lf-record is the counterpart to the RGKN-record (the
+      hash-function)
+
+vk-Record
+
+      The vk-record consists information to a single value.
+
+sk-Record
+
+      sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+      The value-lists contain information about which values are inside a
+      sub-key and don't have a header.
+
+Datas
+
+      The datas of the registry are (like the value-list) stored without a
+      header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"nk" = 0x6B6E
+0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
+0x0004      Q-Word      write-date/time in windows nt notation
+0x0010      D-Word      Offset of Owner/Parent key
+0x0014      D-Word      number of sub-Keys
+0x001C      D-Word      Offset of the sub-key lf-Records
+0x0024      D-Word      number of values
+0x0028      D-Word      Offset of the Value-List
+0x002C      D-Word      Offset of the sk-Record
+
+0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
+0x0048      Word      name-length
+0x004A      Word      class-name length
+0x004C      ????      key-name
+
+the Value-List
+==============
+Offset      Size      Contents
+0x0000      D-Word      Offset 1st Value
+0x0004      D-Word      Offset 2nd Value
+0x????      D-Word      Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"vk" = 0x6B76
+0x0002      Word      name length
+0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
+0x0008      D-Word      Offset of Data
+0x000C      D-Word      Type of value
+0x0010      Word      Flag
+0x0012      Word      Unused (data-trash)
+0x0014      ????      Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert      Beteutung
+0x0001      RegSZ:             character string (in UNICODE!)
+0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
+0x0003      RegBin:           raw-binary value
+0x0004      RegDWord:   Dword
+0x0007      RegMultiSZ:      multiple strings, separated with 0
+                  (UNICODE!)
+
+The "lf"-record
+===============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"lf" = 0x666C
+0x0002      Word      number of keys
+0x0004      ????      Hash-Records
+
+Hash-Record
+===========
+Offset      Size      Contents
+0x0000      D-Word      Offset of corresponding "nk"-Record
+0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"sk" = 0x6B73
+0x0002      Word      Unused
+0x0004      D-Word      Offset of previous "sk"-Record
+0x0008      D-Word      Offset of next "sk"-Record
+0x000C      D-Word      usage-counter
+0x0010      D-Word      Size of "sk"-record in bytes
+????                                             //standard self
+relative security desciptor. Nigel
+????  ????      Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+                      +----->+------------+
+| first sub |   |                      |      | Usage cnt. |
+| name      |   |  +-->+------------+  |      | length     |
+| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
++-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
+   +------------+  |   | value rec. |-------->+------------+        +-------+
+   v               |   +------------+         | Usage cnt. |
++-----------+      |                          | length     |
+| next rec. |      |                          | text       |------->+-------+
+| first sub |------+                          +------------+        | xxxxx |
+| name      |                                                       +-------+
+| value     |
++-----------+    
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+   
++---------------------------------------------------------------+
+|                                                               |
+v                                                               |
++---------+     +---------->+-----------+  +----->+---------+   |
+| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
+| ID      |     |           | # of keys |  |      | parent  |---+
+| Date    |     |           | 1st key   |--+      | ....    |
+| parent  |     |           +-----------+         +---------+
+| suk-keys|-----+
+| values  |--------------------->+----------+
+| SK-rec. |---------------+      | 1. value |--> +----------+
+| class   |--+            |      +----------+    | vk-rec.  |
++---------+  |            |                      | ....     |
+             v            |                      | data     |--> +-------+
+      +------------+      |                      +----------+    | xxxxx |
+      | Class name |      |                                      +-------+
+      +------------+      |
+                          v
+          +---------+    +---------+
+   +----->| next sk |--->| Next sk |--+
+   |  +---| prev sk |<---| prev sk |  |
+   |  |   | ....    |    | ...     |  |
+   |  |   +---------+    +---------+  |
+   |  |         ^          |          |
+      |         +----------+          |
+      +-------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps....  (Although it was "fun" for me to uncover this things,
+                  it took me several sleepless nights ;)
+
+            B.D.
+
+*************************************************************************/
+#include "includes.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+  DWORD REGF_ID;     /* regf */
+  DWORD uk1;
+  DWORD uk2;
+  DWORD tim1, tim2;
+  DWORD uk3;             /* 1 */
+  DWORD uk4;             /* 3 */
+  DWORD uk5;             /* 0 */
+  DWORD uk6;             /* 1 */
+  DWORD first_key;       /* offset */
+  unsigned int dblk_size;
+  DWORD uk7[116];        /* 1 */
+  DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+  DWORD dblocksize;
+  char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+  DWORD HBIN_ID; /* hbin */
+  DWORD next_off;
+  DWORD prev_off;
+  DWORD uk1;
+  DWORD uk2;
+  DWORD uk3;
+  DWORD uk4;
+  DWORD blk_size;
+  HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+  WORD NK_ID;
+  WORD type;
+  DWORD t1, t2;
+  DWORD uk1;
+  DWORD own_off;
+  DWORD subk_num;
+  DWORD uk2;
+  DWORD lf_off;
+  DWORD uk3;
+  DWORD val_cnt;
+  DWORD val_off;
+  DWORD sk_off;
+  DWORD clsnam_off;
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+typedef struct sk_struct {
+  WORD SK_ID;
+  WORD uk1;
+  DWORD prev_off;
+  DWORD next_off;
+  DWORD ref_cnt;
+  DWORD rec_size;
+  char sec_desc[1];
+} SK_HDR;
+
+typedef struct sec_desc_rec {
+  WORD rev;
+  WORD type;
+  DWORD owner_off;
+  DWORD group_off;
+  DWORD sacl_off;
+  DWORD dacl_off;
+} MY_SEC_DESC;
+
+typedef struct ace_struct {
+    unsigned char type;
+    unsigned char flags;
+    unsigned short length;
+    unsigned int perms;
+    DOM_SID trustee;
+} ACE;
+
+typedef struct acl_struct {
+  WORD rev;
+  WORD size;
+  DWORD num_aces;
+  ACE *aces;   /* One or more ACEs */
+} ACL;
+
+#define OFF(f) (0x1000 + (f) + 4) 
+
+static void print_sid(DOM_SID *sid);
+
+int verbose = 1;
+DOM_SID old_sid, new_sid;
+int change = 0, new = 0;
+
+/* Compare two SIDs for equality */
+static int my_sid_equal(DOM_SID *s1, DOM_SID *s2)
+{
+  int sa1, sa2;
+
+  if (s1->sid_rev_num != s2->sid_rev_num) return 0;
+
+  sa1 = s1->num_auths; sa2 = s2->num_auths;
+
+  if (sa1 != sa2) return 0;
+
+  return !memcmp((char *)&s1->id_auth, (char *)&s2->id_auth,
+               6 + sa1 * 4);
+
+}
+
+/*
+ * Quick and dirty to read a SID in S-1-5-21-x-y-z-rid format and 
+ * construct a DOM_SID
+ */
+static int get_sid(DOM_SID *sid, char *sid_str)
+{
+  int i = 0, auth;
+  char *lstr; 
+
+  if (strncmp(sid_str, "S-1-5", 5)) {
+    fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+    return 0;
+  }
+
+  /* We only allow strings of form S-1-5... */
+
+  sid->sid_rev_num = 1;
+  sid->id_auth[5] = 5;
+
+  lstr = sid_str + 5;
+
+  while (1) {
+    if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
+      if (i < 4) {
+       fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+       return 0;
+      }
+      sid->num_auths=i;
+      print_sid(sid);
+      return 1;
+    }
+
+    SIVAL(&sid->sub_auths[i], 0, auth);
+    i++;
+    lstr = strchr(lstr + 1, '-'); 
+  }
+
+  return 1;
+}
+
+/* 
+ * Replace SID1, component by component with SID2
+ * Assumes will never be called with unequal length SIDS
+ * so only touches 21-x-y-z-rid portion
+ * This routine does not need to deal with endianism as 
+ * long as the incoming SIDs are both in the same (LE) format.
+ */
+static void change_sid(DOM_SID *s1, DOM_SID *s2)
+{
+  int i;
+  
+  for (i=0; i<s1->num_auths; i++) {
+    s1->sub_auths[i] = s2->sub_auths[i];
+  }
+}
+
+static void print_sid(DOM_SID *sid)
+{
+  int i, comps = sid->num_auths;
+  fprintf(stdout, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+  for (i = 0; i < comps; i++) {
+
+    fprintf(stdout, "-%u", IVAL(&sid->sub_auths[i],0));
+
+  }
+  fprintf(stdout, "\n");
+}
+
+static void process_sid(DOM_SID *sid, DOM_SID *o_sid, DOM_SID *n_sid) 
+{
+  int i;
+  if (my_sid_equal(sid, o_sid)) {
+
+    for (i=0; i<sid->num_auths; i++) {
+      sid->sub_auths[i] = n_sid->sub_auths[i];
+
+    }
+
+  }
+
+}
+
+static void process_acl(ACL *acl, const char *prefix)
+{
+  int ace_cnt, i;
+  ACE *ace;
+
+  ace_cnt = IVAL(&acl->num_aces, 0);
+  ace = (ACE *)&acl->aces;
+  if (verbose) fprintf(stdout, "%sACEs: %u\n", prefix, ace_cnt);
+  for (i=0; i<ace_cnt; i++) {
+    if (verbose) fprintf(stdout, "%s  Perms: %08X, SID: ", prefix,
+                        IVAL(&ace->perms, 0));
+    if (change)
+      process_sid(&ace->trustee, &old_sid, &new_sid);
+    print_sid(&ace->trustee);
+    ace = (ACE *)((char *)ace + SVAL(&ace->length, 0));
+  }
+} 
+
+static void usage(void)
+{
+  fprintf(stderr, "usage: profiles [-c <OLD-SID> -n <NEW-SID>] <profilefile>\n");
+  fprintf(stderr, "Version: %s\n", VERSION);
+  fprintf(stderr, "\n\t-v\t sets verbose mode");
+  fprintf(stderr, "\n\t-c S-1-5-21-z-y-x-oldrid - provides SID to change");
+  fprintf(stderr, "\n\t-n S-1-5-21-a-b-c-newrid - provides SID to change to");
+  fprintf(stderr, "\n\t\tBoth must be present if the other is.");
+  fprintf(stderr, "\n\t\tIf neither present, just report the SIDs found\n");
+}
+
+int main(int argc, char *argv[])
+{
+  extern char *optarg;
+  extern int optind;
+  int opt;
+  int fd, start = 0;
+  char *base;
+  struct stat sbuf;
+  REGF_HDR *regf_hdr;
+  HBIN_HDR *hbin_hdr;
+  NK_HDR *nk_hdr;
+  SK_HDR *sk_hdr;
+  DWORD first_sk_off, sk_off;
+  MY_SEC_DESC *sec_desc;
+  int *ptr;
+
+  if (argc < 2) {
+    usage();
+    exit(1);
+  }
+
+  /*
+   * Now, process the arguments
+   */
+
+  while ((opt = getopt(argc, argv, "c:n:v")) != EOF) {
+    switch (opt) {
+    case 'c':
+      change = 1;
+      if (!get_sid(&old_sid, optarg)) {
+       fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
+       usage();
+       exit(254);
+      }
+      break;
+
+    case 'n':
+      new = 1;
+      if (!get_sid(&new_sid, optarg)) {
+       fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
+       usage();
+       exit(253);
+      }
+
+      break;
+
+    case 'v':
+      verbose++;
+      break;
+
+    default:
+      usage();
+      exit(255);
+    }
+  }
+
+  if ((!change & new) || (change & !new)) {
+    fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
+    usage();
+    exit(252);
+  }
+
+  fd = open(argv[optind], O_RDWR, 0000);
+
+  if (fd < 0) {
+    fprintf(stderr, "Could not open %s: %s\n", argv[optind], 
+       strerror(errno));
+    exit(2);
+  }
+
+  if (fstat(fd, &sbuf) < 0) {
+    fprintf(stderr, "Could not stat file %s, %s\n", argv[optind],
+       strerror(errno));
+    exit(3);
+  }
+
+  /*
+   * Now, mmap the file into memory, check the header and start
+   * dealing with the records. We are interested in the sk record
+   */
+  start = 0;
+  base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+  if ((int)base == -1) {
+    fprintf(stderr, "Could not mmap file: %s, %s\n", argv[optind],
+       strerror(errno));
+    exit(4);
+  }
+
+  /*
+   * In what follows, and in places above, in order to work on both LE and
+   * BE platforms, we have to use the Samba macros to extract SHORT, LONG
+   * and associated UNSIGNED quantities from the data in the mmap'd file.
+   * NOTE, however, that we do not need to do anything with memory
+   * addresses that we construct from pointers in our address space.
+   * For example, 
+   *
+   *    sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+   *
+   * is simply taking the address of a structure we already have the address
+   * of in our address space, while, the fields within it, will have to 
+   * be accessed with the macros:
+   *
+   * owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + 
+   *                         IVAL(&sec_desc->owner_off, 0));
+   *
+   * Which is pulling out an offset and adding it to an existing pointer.
+   *
+   */
+
+  regf_hdr = (REGF_HDR *)base;
+
+  if (verbose) fprintf(stdout, "Registry file size: %u\n", (unsigned int)sbuf.st_size);
+
+  if (IVAL(&regf_hdr->REGF_ID, 0) != REG_REGF_ID) {
+    fprintf(stderr, "Incorrect Registry file (doesn't have header ID): %s\n", argv[optind]);
+    exit(5);
+  }
+
+  if (verbose) fprintf(stdout, "First Key Off: %u, Data Block Size: %u\n",
+                      IVAL(&regf_hdr->first_key, 0), 
+                      IVAL(&regf_hdr->dblk_size, 0));
+
+  hbin_hdr = (HBIN_HDR *)(base + 0x1000); /* No need for Endian stuff */
+
+  /*
+   * This should be the hbin_hdr 
+   */
+
+  if (IVAL(&hbin_hdr->HBIN_ID, 0) != REG_HBIN_ID) {
+    fprintf(stderr, "Incorrect hbin hdr: %s\n", argv[optind]);
+    exit(6);
+  } 
+
+  if (verbose) fprintf(stdout, "Next Off: %u, Prev Off: %u\n", 
+                      IVAL(&hbin_hdr->next_off, 0), 
+                      IVAL(&hbin_hdr->prev_off, 0));
+
+  nk_hdr = (NK_HDR *)(base + 0x1000 + IVAL(&regf_hdr->first_key, 0) + 4);
+
+  if (SVAL(&nk_hdr->NK_ID, 0) != REG_NK_ID) {
+    fprintf(stderr, "Incorrect NK Header: %s\n", argv[optind]);
+    exit(7);
+  }
+
+  sk_off = first_sk_off = IVAL(&nk_hdr->sk_off, 0);
+  if (verbose) {
+    fprintf(stdout, "Type: %0x\n", SVAL(&nk_hdr->type, 0));
+    fprintf(stdout, "SK Off    : %o\n", (0x1000 + sk_off + 4));  
+  }
+
+  sk_hdr = (SK_HDR *)(base + 0x1000 + sk_off + 4);
+
+  do {
+    DOM_SID *owner_sid, *group_sid;
+    ACL *sacl, *dacl;
+    if (SVAL(&sk_hdr->SK_ID, 0) != REG_SK_ID) {
+      fprintf(stderr, "Incorrect SK Header format: %08X\n", 
+             (0x1000 + sk_off + 4));
+      exit(8);
+    }
+    ptr = (int *)sk_hdr;
+    if (verbose) fprintf(stdout, "Off: %08X, Refs: %u, Size: %u\n",
+                        sk_off, IVAL(&sk_hdr->ref_cnt, 0), 
+                        IVAL(&sk_hdr->rec_size, 0));
+
+    sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+    owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+                           IVAL(&sec_desc->owner_off, 0));
+    group_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + 
+                           IVAL(&sec_desc->group_off, 0));
+    sacl = (ACL *)(&sk_hdr->sec_desc[0] + 
+                  IVAL(&sec_desc->sacl_off, 0));
+    dacl = (ACL *)(&sk_hdr->sec_desc[0] + 
+                  IVAL(&sec_desc->dacl_off, 0));
+    if (verbose)fprintf(stdout, "  Owner SID: "); 
+    if (change) process_sid(owner_sid, &old_sid, &new_sid);
+    if (verbose) print_sid(owner_sid);
+    if (verbose) fprintf(stdout, "  Group SID: "); 
+    if (change) process_sid(group_sid, &old_sid, &new_sid);
+    if (verbose) print_sid(group_sid);
+    fprintf(stdout, "  SACL: ");
+    if (!sec_desc->sacl_off) { /* LE zero == BE zero */
+      if (verbose) fprintf(stdout, "NONE\n");
+    }
+    else 
+      process_acl(sacl, "    ");
+    if (verbose) fprintf(stdout, "  DACL: ");
+    if (!sec_desc->dacl_off) {
+      if (verbose) fprintf(stdout, "NONE\n");
+    }
+    else 
+      process_acl(dacl, "    ");
+    sk_off = IVAL(&sk_hdr->prev_off, 0);
+    sk_hdr = (SK_HDR *)(base + OFF(IVAL(&sk_hdr->prev_off, 0)));
+  } while (sk_off != first_sk_off);
+
+  munmap(base, sbuf.st_size); 
+
+  close(fd);
+  return 0;
+}
diff --git a/source4/utils/rewrite.c b/source4/utils/rewrite.c
new file mode 100644 (file)
index 0000000..5c0b2b6
--- /dev/null
@@ -0,0 +1,32 @@
+#include "includes.h"
+
+/*
+
+ this is a set of temporary stub functions used during the samba4 rewrite.
+ This file will need to go away before the rewrite is complete.
+*/
+
+BOOL become_user_permanently(uid_t uid, gid_t gid)
+{ return True; }
+
+BOOL is_setuid_root(void)
+{ return False; }
+
+int share_mode_forall(SHAREMODE_FN(fn))
+{ return 0; }
+
+#define BRLOCK_FN(fn) \
+       void (*fn)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \
+                                enum brl_type lock_type, \
+                                br_off start, br_off size)
+int brl_forall(BRLOCK_FN(fn))
+{ return 0; }
+
+BOOL locking_end(void)
+{ return True; }
+
+BOOL locking_init(int read_only)
+{ return True; }
+
+uid_t sec_initial_gid(void)
+{ return 0; }
diff --git a/source4/utils/rpccheck.c b/source4/utils/rpccheck.c
new file mode 100644 (file)
index 0000000..ae109f6
--- /dev/null
@@ -0,0 +1,62 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Jean François Micouleau 2001
+   
+   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"
+
+main()
+{
+       char filter[]="0123456789ABCDEF";
+
+       char s[128];
+       char d=0;
+       int x=0;
+       prs_struct ps;
+       TALLOC_CTX *ctx;
+
+       /* change that struct */
+       SAMR_R_QUERY_USERINFO rpc_stub;
+       
+       ZERO_STRUCT(rpc_stub);
+
+       setup_logging("", True);
+       DEBUGLEVEL=10;
+
+       ctx=talloc_init("main");
+       if (!ctx) exit(1);
+
+       prs_init(&ps, 1600, 4, ctx, MARSHALL);
+
+       while (scanf("%s", s)!=-1) {
+               if (strlen(s)==2 && strchr_m(filter, *s)!=NULL && strchr_m(filter, *(s+1))!=NULL) {
+                       d=strtol(s, NULL, 16);
+                       if(!prs_append_data(&ps, &d, 1))
+                               printf("error while reading data\n");
+               }
+       }
+       
+       prs_switch_type(&ps, UNMARSHALL);
+       prs_set_offset(&ps, 0);
+       
+       /* change that call */  
+       if(!samr_io_r_query_userinfo("", &rpc_stub, &ps, 0))
+               printf("error while UNMARSHALLING the data\n");
+
+       printf("\n");
+}
diff --git a/source4/utils/smbcacls.c b/source4/utils/smbcacls.c
new file mode 100644 (file)
index 0000000..4c85ea5
--- /dev/null
@@ -0,0 +1,937 @@
+/* 
+   Unix SMB/CIFS implementation.
+   ACL get/set utility
+   
+   Copyright (C) Andrew Tridgell 2000
+   Copyright (C) Tim Potter      2000
+   Copyright (C) Jeremy Allison  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"
+
+static fstring password;
+static pstring username;
+static pstring owner_username;
+static fstring server;
+static int got_pass;
+static int test_args;
+static TALLOC_CTX *ctx;
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+#define CREATE_ACCESS_WRITE (WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS)
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+   than going via LSA calls to resolve them */
+static int numeric;
+
+enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
+enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+struct perm_value {
+       const char *perm;
+       uint32 mask;
+};
+
+/* These values discovered by inspection */
+
+static const struct perm_value special_values[] = {
+       { "R", 0x00120089 },
+       { "W", 0x00120116 },
+       { "X", 0x001200a0 },
+       { "D", 0x00010000 },
+       { "P", 0x00040000 },
+       { "O", 0x00080000 },
+       { NULL, 0 },
+};
+
+static const struct perm_value standard_values[] = {
+       { "READ",   0x001200a9 },
+       { "CHANGE", 0x001301bf },
+       { "FULL",   0x001f01ff },
+       { NULL, 0 },
+};
+
+static struct cli_state *global_hack_cli;
+static POLICY_HND pol;
+static BOOL got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static BOOL cacls_open_policy_hnd(void)
+{
+       /* Initialise cli LSA connection */
+
+       if (!global_hack_cli) {
+               global_hack_cli = connect_one("IPC$");
+               if (!cli_nt_session_open (global_hack_cli, PI_LSARPC)) {
+                               return False;
+               }
+       }
+       
+       /* Open policy handle */
+
+       if (!got_policy_hnd) {
+
+               /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+                  but NT sends 0x2000000 so we might as well do it too. */
+
+               if (!NT_STATUS_IS_OK(cli_lsa_open_policy(global_hack_cli, global_hack_cli->mem_ctx, True, 
+                                                        GENERIC_EXECUTE_ACCESS, &pol))) {
+                       return False;
+               }
+
+               got_policy_hnd = True;
+       }
+       
+       return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, DOM_SID *sid)
+{
+       char **domains = NULL;
+       char **names = NULL;
+       uint32 *types = NULL;
+
+       sid_to_string(str, sid);
+
+       if (numeric) return;
+
+       /* Ask LSA to convert the sid to a name */
+
+       if (!cacls_open_policy_hnd() ||
+           !NT_STATUS_IS_OK(cli_lsa_lookup_sids(global_hack_cli, global_hack_cli->mem_ctx,  
+                                                &pol, 1, sid, &domains, 
+                                                &names, &types)) ||
+           !domains || !domains[0] || !names || !names[0]) {
+               return;
+       }
+
+       /* Converted OK */
+
+       slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+                domains[0], lp_winbind_separator(),
+                names[0]);
+       
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL StringToSid(DOM_SID *sid, const char *str)
+{
+       uint32 *types = NULL;
+       DOM_SID *sids = NULL;
+       BOOL result = True;
+
+       if (strncmp(str, "S-", 2) == 0) {
+               return string_to_sid(sid, str);
+       }
+
+       if (!cacls_open_policy_hnd() ||
+           !NT_STATUS_IS_OK(cli_lsa_lookup_names(global_hack_cli, global_hack_cli->mem_ctx, 
+                                                 &pol, 1, &str, &sids, 
+                                                 &types))) {
+               result = False;
+               goto done;
+       }
+
+       sid_copy(sid, &sids[0]);
+ done:
+
+       return result;
+}
+
+
+/* print an ACE on a FILE, using either numeric or ascii representation */
+static void print_ace(FILE *f, SEC_ACE *ace)
+{
+       const struct perm_value *v;
+       fstring sidstr;
+       int do_print = 0;
+       uint32 got_mask;
+
+       SidToString(sidstr, &ace->trustee);
+
+       fprintf(f, "%s:", sidstr);
+
+       if (numeric) {
+               fprintf(f, "%d/%d/0x%08x", 
+                       ace->type, ace->flags, ace->info.mask);
+               return;
+       }
+
+       /* Ace type */
+
+       if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+               fprintf(f, "ALLOWED");
+       } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+               fprintf(f, "DENIED");
+       } else {
+               fprintf(f, "%d", ace->type);
+       }
+
+       /* Not sure what flags can be set in a file ACL */
+
+       fprintf(f, "/%d/", ace->flags);
+
+       /* Standard permissions */
+
+       for (v = standard_values; v->perm; v++) {
+               if (ace->info.mask == v->mask) {
+                       fprintf(f, "%s", v->perm);
+                       return;
+               }
+       }
+
+       /* Special permissions.  Print out a hex value if we have
+          leftover bits in the mask. */
+
+       got_mask = ace->info.mask;
+
+ again:
+       for (v = special_values; v->perm; v++) {
+               if ((ace->info.mask & v->mask) == v->mask) {
+                       if (do_print) {
+                               fprintf(f, "%s", v->perm);
+                       }
+                       got_mask &= ~v->mask;
+               }
+       }
+
+       if (!do_print) {
+               if (got_mask != 0) {
+                       fprintf(f, "0x%08x", ace->info.mask);
+               } else {
+                       do_print = 1;
+                       goto again;
+               }
+       }
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static BOOL parse_ace(SEC_ACE *ace, char *str)
+{
+       char *p;
+       const char *cp;
+       fstring tok;
+       unsigned atype, aflags, amask;
+       DOM_SID sid;
+       SEC_ACCESS mask;
+       const struct perm_value *v;
+
+       ZERO_STRUCTP(ace);
+       p = strchr_m(str,':');
+       if (!p) return False;
+       *p = '\0';
+       p++;
+       /* Try to parse numeric form */
+
+       if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+           StringToSid(&sid, str)) {
+               goto done;
+       }
+
+       /* Try to parse text form */
+
+       if (!StringToSid(&sid, str)) {
+               return False;
+       }
+
+       cp = p;
+       if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+               return False;
+       }
+
+       if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+               atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+       } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
+               atype = SEC_ACE_TYPE_ACCESS_DENIED;
+       } else {
+               return False;
+       }
+
+       /* Only numeric form accepted for flags at present */
+
+       if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
+             sscanf(tok, "%i", &aflags))) {
+               return False;
+       }
+
+       if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+               return False;
+       }
+
+       if (strncmp(tok, "0x", 2) == 0) {
+               if (sscanf(tok, "%i", &amask) != 1) {
+                       return False;
+               }
+               goto done;
+       }
+
+       for (v = standard_values; v->perm; v++) {
+               if (strcmp(tok, v->perm) == 0) {
+                       amask = v->mask;
+                       goto done;
+               }
+       }
+
+       p = tok;
+
+       while(*p) {
+               BOOL found = False;
+
+               for (v = special_values; v->perm; v++) {
+                       if (v->perm[0] == *p) {
+                               amask |= v->mask;
+                               found = True;
+                       }
+               }
+
+               if (!found) return False;
+               p++;
+       }
+
+       if (*p) {
+               return False;
+       }
+
+ done:
+       mask.mask = amask;
+       init_sec_ace(ace, &sid, atype, mask, aflags);
+       return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
+{
+       SEC_ACL *new;
+       SEC_ACE *aces;
+       if (! *the_acl) {
+               (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+               return True;
+       }
+
+       aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
+       memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+       memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+       new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+       SAFE_FREE(aces);
+       (*the_acl) = new;
+       return True;
+}
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(char *str)
+{
+       const char *p = str;
+       fstring tok;
+       SEC_DESC *ret;
+       size_t sd_size;
+       DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+       SEC_ACL *dacl=NULL;
+       int revision=1;
+
+       while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+               if (strncmp(tok,"REVISION:", 9) == 0) {
+                       revision = strtol(tok+9, NULL, 16);
+                       continue;
+               }
+
+               if (strncmp(tok,"OWNER:", 6) == 0) {
+                       owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (!owner_sid ||
+                           !StringToSid(owner_sid, tok+6)) {
+                               printf("Failed to parse owner sid\n");
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               if (strncmp(tok,"GROUP:", 6) == 0) {
+                       grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+                       if (!grp_sid ||
+                           !StringToSid(grp_sid, tok+6)) {
+                               printf("Failed to parse group sid\n");
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               if (strncmp(tok,"ACL:", 4) == 0) {
+                       SEC_ACE ace;
+                       if (!parse_ace(&ace, tok+4)) {
+                               printf("Failed to parse ACL %s\n", tok);
+                               return NULL;
+                       }
+                       if(!add_ace(&dacl, &ace)) {
+                               printf("Failed to add ACL %s\n", tok);
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               printf("Failed to parse security descriptor\n");
+               return NULL;
+       }
+
+       ret = make_sec_desc(ctx,revision, owner_sid, grp_sid, 
+                           NULL, dacl, &sd_size);
+
+       SAFE_FREE(grp_sid);
+       SAFE_FREE(owner_sid);
+
+       return ret;
+}
+
+
+/* print a ascii version of a security descriptor on a FILE handle */
+static void sec_desc_print(FILE *f, SEC_DESC *sd)
+{
+       fstring sidstr;
+       uint32 i;
+
+       printf("REVISION:%d\n", sd->revision);
+
+       /* Print owner and group sid */
+
+       if (sd->owner_sid) {
+               SidToString(sidstr, sd->owner_sid);
+       } else {
+               fstrcpy(sidstr, "");
+       }
+
+       printf("OWNER:%s\n", sidstr);
+
+       if (sd->grp_sid) {
+               SidToString(sidstr, sd->grp_sid);
+       } else {
+               fstrcpy(sidstr, "");
+       }
+
+       fprintf(f, "GROUP:%s\n", sidstr);
+
+       /* Print aces */
+       for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+               SEC_ACE *ace = &sd->dacl->ace[i];
+               fprintf(f, "ACL:");
+               print_ace(f, ace);
+               fprintf(f, "\n");
+       }
+
+}
+
+/***************************************************** 
+dump the acls for a file
+*******************************************************/
+static int cacl_dump(struct cli_state *cli, char *filename)
+{
+       int result = EXIT_FAILED;
+       int fnum = -1;
+       SEC_DESC *sd;
+
+       if (test_args) 
+               return EXIT_OK;
+
+       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+       if (fnum == -1) {
+               printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+               goto done;
+       }
+
+       sd = cli_query_secdesc(cli, fnum, ctx);
+
+       if (!sd) {
+               printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
+               goto done;
+       }
+
+       sec_desc_print(stdout, sd);
+
+       result = EXIT_OK;
+
+done:
+       if (fnum != -1)
+               cli_close(cli, fnum);
+
+       return result;
+}
+
+/***************************************************** 
+Change the ownership or group ownership of a file. Just
+because the NT docs say this can't be done :-). JRA.
+*******************************************************/
+
+static int owner_set(struct cli_state *cli, enum chown_mode change_mode, 
+                    char *filename, char *new_username)
+{
+       int fnum;
+       DOM_SID sid;
+       SEC_DESC *sd, *old;
+       size_t sd_size;
+
+       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+       if (fnum == -1) {
+               printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+               return EXIT_FAILED;
+       }
+
+       if (!StringToSid(&sid, new_username))
+               return EXIT_PARSE_ERROR;
+
+       old = cli_query_secdesc(cli, fnum, ctx);
+
+       cli_close(cli, fnum);
+
+       if (!old) {
+               printf("owner_set: Failed to query old descriptor\n");
+               return EXIT_FAILED;
+       }
+
+       sd = make_sec_desc(ctx,old->revision,
+                               (change_mode == REQUEST_CHOWN) ? &sid : old->owner_sid,
+                               (change_mode == REQUEST_CHGRP) ? &sid : old->grp_sid,
+                          NULL, old->dacl, &sd_size);
+
+       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE);
+
+       if (fnum == -1) {
+               printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+               return EXIT_FAILED;
+       }
+
+       if (!cli_set_secdesc(cli, fnum, sd)) {
+               printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+       }
+
+       cli_close(cli, fnum);
+
+       return EXIT_OK;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+   However NT4 gives a "The information may have been modified by a
+   computer running Windows NT 5.0" if denied ACEs do not appear before
+   allowed ACEs. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+       if (sec_ace_equal(ace1, ace2)) 
+               return 0;
+
+       if (ace1->type != ace2->type) 
+               return ace2->type - ace1->type;
+
+       if (sid_compare(&ace1->trustee, &ace2->trustee)) 
+               return sid_compare(&ace1->trustee, &ace2->trustee);
+
+       if (ace1->flags != ace2->flags) 
+               return ace1->flags - ace2->flags;
+
+       if (ace1->info.mask != ace2->info.mask) 
+               return ace1->info.mask - ace2->info.mask;
+
+       if (ace1->size != ace2->size) 
+               return ace1->size - ace2->size;
+
+       return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+       uint32 i;
+       if (!the_acl) return;
+
+       qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+
+       for (i=1;i<the_acl->num_aces;) {
+               if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+                       int j;
+                       for (j=i; j<the_acl->num_aces-1; j++) {
+                               the_acl->ace[j] = the_acl->ace[j+1];
+                       }
+                       the_acl->num_aces--;
+               } else {
+                       i++;
+               }
+       }
+}
+
+/***************************************************** 
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(struct cli_state *cli, char *filename, 
+                   char *the_acl, enum acl_mode mode)
+{
+       int fnum;
+       SEC_DESC *sd, *old;
+       uint32 i, j;
+       size_t sd_size;
+       int result = EXIT_OK;
+
+       sd = sec_desc_parse(the_acl);
+
+       if (!sd) return EXIT_PARSE_ERROR;
+       if (test_args) return EXIT_OK;
+
+       /* The desired access below is the only one I could find that works
+          with NT4, W2KP and Samba */
+
+       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+       if (fnum == -1) {
+               printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+               return EXIT_FAILED;
+       }
+
+       old = cli_query_secdesc(cli, fnum, ctx);
+
+       if (!old) {
+               printf("calc_set: Failed to query old descriptor\n");
+               return EXIT_FAILED;
+       }
+
+       cli_close(cli, fnum);
+
+       /* the logic here is rather more complex than I would like */
+       switch (mode) {
+       case SMB_ACL_DELETE:
+               for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+                       BOOL found = False;
+
+                       for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+                               if (sec_ace_equal(&sd->dacl->ace[i],
+                                                 &old->dacl->ace[j])) {
+                                       uint32 k;
+                                       for (k=j; k<old->dacl->num_aces-1;k++) {
+                                               old->dacl->ace[k] = old->dacl->ace[k+1];
+                                       }
+                                       old->dacl->num_aces--;
+                                       if (old->dacl->num_aces == 0) {
+                                               SAFE_FREE(old->dacl->ace);
+                                               SAFE_FREE(old->dacl);
+                                               old->off_dacl = 0;
+                                       }
+                                       found = True;
+                                       break;
+                               }
+                       }
+
+                       if (!found) {
+                               printf("ACL for ACE:"); 
+                               print_ace(stdout, &sd->dacl->ace[i]);
+                               printf(" not found\n");
+                       }
+               }
+               break;
+
+       case SMB_ACL_MODIFY:
+               for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+                       BOOL found = False;
+
+                       for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+                               if (sid_equal(&sd->dacl->ace[i].trustee,
+                                             &old->dacl->ace[j].trustee)) {
+                                       old->dacl->ace[j] = sd->dacl->ace[i];
+                                       found = True;
+                               }
+                       }
+
+                       if (!found) {
+                               fstring str;
+
+                               SidToString(str, &sd->dacl->ace[i].trustee);
+                               printf("ACL for SID %s not found\n", str);
+                       }
+               }
+
+               break;
+
+       case SMB_ACL_ADD:
+               for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+                       add_ace(&old->dacl, &sd->dacl->ace[i]);
+               }
+               break;
+
+       case SMB_ACL_SET:
+               old = sd;
+               break;
+       }
+
+       /* Denied ACE entries must come before allowed ones */
+       sort_acl(old->dacl);
+
+       /* Create new security descriptor and set it */
+       sd = make_sec_desc(ctx,old->revision, old->owner_sid, old->grp_sid, 
+                          NULL, old->dacl, &sd_size);
+
+       fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE);
+
+       if (fnum == -1) {
+               printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+               return EXIT_FAILED;
+       }
+
+       if (!cli_set_secdesc(cli, fnum, sd)) {
+               printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+               result = EXIT_FAILED;
+       }
+
+       /* Clean up */
+
+       cli_close(cli, fnum);
+
+       return result;
+}
+
+
+/***************************************************** 
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(const char *share)
+{
+       struct cli_state *c;
+       struct in_addr ip;
+       NTSTATUS nt_status;
+       zero_ip(&ip);
+       
+       if (!got_pass) {
+               char *pass = getpass("Password: ");
+               if (pass) {
+                       fstrcpy(password, pass);
+                       got_pass = True;
+               }
+       }
+
+       if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, lp_netbios_name(), server, 
+                                                           &ip, 0,
+                                                           share, "?????",  
+                                                           username, lp_workgroup(),
+                                                           password, 0, NULL))) {
+               return c;
+       } else {
+               DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+               return NULL;
+       }
+}
+
+
+static void usage(void)
+{
+       printf(
+"Usage: smbcacls //server1/share1 filename [options]\n\
+\n\
+\t-D <acls>               delete an acl\n\
+\t-M <acls>               modify an acl\n\
+\t-A <acls>               add an acl\n\
+\t-S <acls>               set acls\n\
+\t-C username             change ownership of a file\n\
+\t-G username             change group ownership of a file\n\
+\t-n                      don't resolve sids or masks to names\n\
+\t-h                      print help\n\
+\t-d debuglevel           set debug output level\n\
+\t-U username             user to autheticate as\n\
+\n\
+The username can be of the form username%%password or\n\
+workgroup\\username%%password.\n\n\
+An acl is of the form ACL:<SID>:type/flags/mask\n\
+You can string acls together with spaces, commas or newlines\n\
+");
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       char *share;
+       pstring filename;
+       extern char *optarg;
+       extern int optind;
+       int opt;
+       char *p;
+       enum acl_mode mode = SMB_ACL_SET;
+       char *the_acl = NULL;
+       enum chown_mode change_mode = REQUEST_NONE;
+       int result;
+
+       struct cli_state *cli;
+
+       ctx=talloc_init("main");
+
+       setlinebuf(stdout);
+
+       dbf = x_stderr;
+
+       if (argc < 3 || argv[1][0] == '-') {
+               usage();
+               talloc_destroy(ctx);
+               exit(EXIT_PARSE_ERROR);
+       }
+
+       setup_logging(argv[0],True);
+
+       share = argv[1];
+       pstrcpy(filename, argv[2]);
+       all_string_sub(share,"/","\\",0);
+
+       argc -= 2;
+       argv += 2;
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (getenv("USER")) {
+               pstrcpy(username,getenv("USER"));
+
+               if ((p=strchr_m(username,'%'))) {
+                       *p = 0;
+                       fstrcpy(password,p+1);
+                       got_pass = True;
+                       memset(strchr_m(getenv("USER"), '%') + 1, 'X',
+                              strlen(password));
+               }
+       }
+
+       while ((opt = getopt(argc, argv, "U:nhS:D:A:M:C:G:td:")) != EOF) {
+               switch (opt) {
+               case 'U':
+                       pstrcpy(username,optarg);
+                       p = strchr_m(username,'%');
+                       if (p) {
+                               *p = 0;
+                               fstrcpy(password, p+1);
+                               got_pass = 1;
+                       }
+                       break;
+
+               case 'S':
+                       the_acl = optarg;
+                       mode = SMB_ACL_SET;
+                       break;
+
+               case 'D':
+                       the_acl = optarg;
+                       mode = SMB_ACL_DELETE;
+                       break;
+
+               case 'M':
+                       the_acl = optarg;
+                       mode = SMB_ACL_MODIFY;
+                       break;
+
+               case 'A':
+                       the_acl = optarg;
+                       mode = SMB_ACL_ADD;
+                       break;
+
+               case 'C':
+                       pstrcpy(owner_username,optarg);
+                       change_mode = REQUEST_CHOWN;
+                       break;
+
+               case 'G':
+                       pstrcpy(owner_username,optarg);
+                       change_mode = REQUEST_CHGRP;
+                       break;
+
+               case 'n':
+                       numeric = 1;
+                       break;
+
+               case 't':
+                       test_args = 1;
+                       break;
+
+               case 'h':
+                       usage();
+                       talloc_destroy(ctx);
+                       exit(EXIT_PARSE_ERROR);
+
+               case 'd':
+                       DEBUGLEVEL = atoi(optarg);
+                       break;
+
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       talloc_destroy(ctx);
+                       exit(EXIT_PARSE_ERROR);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc > 0) {
+               usage();
+               talloc_destroy(ctx);
+               exit(EXIT_PARSE_ERROR);
+       }
+
+       /* Make connection to server */
+
+       fstrcpy(server,share+2);
+       share = strchr_m(server,'\\');
+       if (!share) {
+               share = strchr_m(server,'/');
+               if (!share) {
+                       return -1;
+               }
+       }
+
+       *share = 0;
+       share++;
+
+       if (!test_args) {
+               cli = connect_one(share);
+               if (!cli) {
+                       talloc_destroy(ctx);
+                       exit(EXIT_FAILED);
+               }
+       } else {
+               exit(0);
+       }
+
+       all_string_sub(filename, "/", "\\", 0);
+       if (filename[0] != '\\') {
+               pstring s;
+               s[0] = '\\';
+               safe_strcpy(&s[1], filename, sizeof(pstring)-1);
+               pstrcpy(filename, s);
+       }
+
+       /* Perform requested action */
+
+       if (change_mode != REQUEST_NONE) {
+               result = owner_set(cli, change_mode, filename, owner_username);
+       } else if (the_acl) {
+               result = cacl_set(cli, filename, the_acl, mode);
+       } else {
+               result = cacl_dump(cli, filename);
+       }
+
+       talloc_destroy(ctx);
+
+       return result;
+}
+
diff --git a/source4/utils/smbcontrol.c b/source4/utils/smbcontrol.c
new file mode 100644 (file)
index 0000000..0861eb5
--- /dev/null
@@ -0,0 +1,714 @@
+/* 
+   Unix SMB/CIFS implementation.
+   program to send control messages to Samba processes
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) 2001, 2002 by Martin Pool
+   Copyright (C) Simo Sorce 2002
+   
+   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"
+
+extern BOOL AllowDebugChange;
+
+static const struct {
+       const char *name;
+       int value;
+} msg_types[] = {
+       {"debug", MSG_DEBUG},
+       {"force-election", MSG_FORCE_ELECTION},
+       {"ping", MSG_PING},
+       {"profile", MSG_PROFILE},
+       {"profilelevel", MSG_REQ_PROFILELEVEL},
+       {"debuglevel", MSG_REQ_DEBUGLEVEL},
+       {"printnotify", MSG_PRINTER_NOTIFY2 },
+       {"close-share", MSG_SMB_FORCE_TDIS},
+        {"samsync", MSG_SMB_SAM_SYNC},
+        {"samrepl", MSG_SMB_SAM_REPL},
+       {"pool-usage", MSG_REQ_POOL_USAGE },
+       {"dmalloc-mark", MSG_REQ_DMALLOC_MARK },
+       {"dmalloc-log-changed", MSG_REQ_DMALLOC_LOG_CHANGED },
+       {"shutdown", MSG_SHUTDOWN },
+       {"drvupgrade", MSG_PRINTER_DRVUPGRADE},
+       {"tallocdump", MSG_REQ_TALLOC_USAGE},
+       {NULL, -1}
+};
+
+time_t timeout_start;
+
+#define MAX_WAIT       10
+
+/* we need these because we link to printing*.o */
+
+void become_root(void) {}
+void unbecome_root(void) {}
+
+
+static void usage(BOOL doexit)
+{
+       int i;
+       if (doexit) {
+               printf("Usage: smbcontrol -i -s configfile\n");
+               printf("       smbcontrol <destination> <message-type> <parameters>\n\n");
+       } else {
+               printf("<destination> <message-type> <parameters>\n\n");
+       }
+       printf("\t<destination> is one of \"nmbd\", \"smbd\" or a process ID\n");
+       printf("\t<message-type> is one of:\n");
+       for (i=0; msg_types[i].name; i++) 
+           printf("\t\t%s\n", msg_types[i].name);
+       printf("\n");
+       if (doexit) exit(1);
+}
+
+static int pong_count;
+static BOOL got_level;
+static BOOL got_pool;
+static BOOL pong_registered = False;
+static BOOL debuglevel_registered = False;
+static BOOL poolusage_registered = False;
+static BOOL profilelevel_registered = False;
+
+
+/**
+ * Wait for replies for up to @p *max_secs seconds, or until @p
+ * max_replies are received.  max_replies may be NULL in which case it
+ * is ignored.
+ *
+ * @note This is a pretty lame timeout; all it means is that after
+ * max_secs we won't look for any more messages.
+ **/
+static void wait_for_replies(int max_secs, int *max_replies)
+{
+       time_t timeout_end = time(NULL) + max_secs;
+
+       while ((!max_replies || (*max_replies)-- > 0)
+              &&  (time(NULL) < timeout_end)) {
+               message_dispatch();
+       }
+}
+
+
+/****************************************************************************
+a useful function for testing the message system
+****************************************************************************/
+void pong_function(int msg_type, pid_t src, void *buf, size_t len)
+{
+       pong_count++;
+       printf("PONG from PID %u\n",(unsigned int)src);
+}
+
+/****************************************************************************
+ Prints out the current talloc list.
+****************************************************************************/
+void tallocdump_function(int msg_type, pid_t src, void *buf, size_t len)
+{
+       char *info = (char *)buf;
+
+       printf("Current talloc contexts for process %u\n", (unsigned int)src );
+       if (len == 0)
+               printf("None returned\n");
+       else
+               printf(info);
+       printf("\n");
+       got_pool = True;
+}
+
+/****************************************************************************
+Prints out the current Debug level returned by MSG_DEBUGLEVEL
+****************************************************************************/
+void debuglevel_function(int msg_type, pid_t src, void *buf, size_t len)
+{
+       const char *levels = (char *)buf;
+
+       printf("Current debug levels of PID %u are:\n",(unsigned int)src);
+       printf("%s\n", levels);
+       
+       got_level = True;
+}
+
+/****************************************************************************
+Prints out the current Profile level returned by MSG_PROFILELEVEL
+****************************************************************************/
+void profilelevel_function(int msg_type, pid_t src, void *buf, size_t len)
+{
+        int level;
+       const char *s=NULL;
+        memcpy(&level, buf, sizeof(int));
+
+       if (level) {
+           switch (level) {
+           case 1:
+               s = "off";
+               break;
+           case 3:
+               s = "count only";
+               break;
+           case 7:
+               s = "count and time";
+               break;
+           default:
+                   s = "BOGUS";
+                   break;
+           }
+           printf("Profiling %s on PID %u\n",s,(unsigned int)src);
+       } else {
+           printf("Profiling not available on PID %u\n",(unsigned int)src);
+       }
+       got_level = True;
+}
+
+/**
+ * Handle reply from POOL_USAGE.
+ **/
+static void pool_usage_cb(int msg_type, pid_t src_pid, void *buf, size_t len)
+{
+       printf("Got POOL_USAGE reply from pid%u:\n%.*s",
+              (unsigned int) src_pid, (int) len, (const char *) buf);
+}
+
+
+/**
+ * Send a message to a named destination
+ *
+ * @return False if an error occurred.
+ **/
+static BOOL send_message(char *dest, int msg_type, void *buf, int len, BOOL duplicates)
+{
+       pid_t pid;
+       /* "smbd" is the only broadcast operation */
+       if (strequal(dest,"smbd")) {
+               TDB_CONTEXT *tdb;
+               BOOL ret;
+               int n_sent = 0;
+
+               tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDWR, 0);
+               if (!tdb) {
+                       fprintf(stderr,"Failed to open connections database in send_message.\n");
+                       return False;
+               }
+
+               ret = message_send_all(tdb,msg_type, buf, len, duplicates,
+                                      &n_sent);
+               DEBUG(10,("smbcontrol/send_message: broadcast message to "
+                         "%d processes\n", n_sent));
+               tdb_close(tdb);
+
+               return ret;
+       } else if (strequal(dest,"nmbd")) {
+               pid = pidfile_pid(dest);
+               if (pid == 0) {
+                       fprintf(stderr,"Can't find pid for nmbd\n");
+                       return False;
+               }
+       } else if (strequal(dest,"self")) {
+               pid = sys_getpid();
+       } else {
+               pid = atoi(dest);
+               if (pid == 0) {
+                       fprintf(stderr,"Not a valid pid\n");
+                       return False;
+               }               
+       } 
+
+       DEBUG(10,("smbcontrol/send_message: send message to pid%d\n", pid));
+       return message_send_pid(pid, msg_type, buf, len, duplicates);
+}
+
+/****************************************************************************
+evaluate a message type string
+****************************************************************************/
+static int parse_type(char *mtype)
+{
+       int i;
+       for (i=0;msg_types[i].name;i++) {
+               if (strequal(mtype, msg_types[i].name)) return msg_types[i].value;
+       }
+       return -1;
+}
+
+
+static void register_all(void)
+{
+       message_register(MSG_POOL_USAGE, pool_usage_cb);
+}
+
+/* This guy is here so we can link printing/notify.c to the smbcontrol
+   binary without having to pull in tons of other crap. */
+
+TDB_CONTEXT *conn_tdb_ctx(void)
+{
+       static TDB_CONTEXT *tdb;
+
+       if (tdb)
+               return tdb;
+
+       tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+
+       if (!tdb)
+               DEBUG(3, ("Failed to open connections database in send_spoolss_notify2_msg\n"));
+
+       return tdb;
+}
+
+/****************************************************************************
+do command
+****************************************************************************/
+static BOOL do_command(char *dest, char *msg_name, int iparams, char **params)
+{
+       int i, n, v;
+       int mtype;
+       BOOL retval=False;
+       BOOL check_notify_msgs = False;
+
+       mtype = parse_type(msg_name);
+       if (mtype == -1) {
+               fprintf(stderr,"Couldn't resolve message type: %s\n", msg_name);
+               return(False);
+       }
+
+       switch (mtype) {
+       case MSG_DEBUG: {
+               char *buf, *b;
+               char **p;
+               int dim = 0;
+
+               if (!params || !params[0]) {
+                       fprintf(stderr,"MSG_DEBUG needs a parameter\n");
+                       return(False);
+               }
+
+               /* first pass retrieve total lenght */
+               for (p = params; p && *p ; p++)
+                       dim += (strnlen(*p, 1024) +1); /* lenght + space */
+               b = buf = malloc(dim);
+               if (!buf) {
+                       fprintf(stderr, "Out of memory!");
+                       return(False);
+               }
+               /* now build a single string with all parameters */
+               for(p = params; p && *p; p++) {
+                       int l = strnlen(*p, 1024);
+                       strncpy(b, *p, l);
+                       b[l] = ' ';
+                       b = b + l + 1;
+               }
+               b[-1] = '\0';
+
+               send_message(dest, MSG_DEBUG, buf, dim, False);
+
+               free(buf);
+  
+               break;
+       }
+
+       case MSG_PROFILE:
+               if (!params || !params[0]) {
+                       fprintf(stderr,"MSG_PROFILE needs a parameter\n");
+                       return(False);
+               }
+               if (strequal(params[0], "off")) {
+                       v = 0;
+               } else if (strequal(params[0], "count")) {
+                       v = 1;
+               } else if (strequal(params[0], "on")) {
+                       v = 2;
+               } else if (strequal(params[0], "flush")) {
+                       v = 3;
+               } else {
+                   fprintf(stderr,
+                       "MSG_PROFILE parameter must be off, count, on, or flush\n");
+                   return(False);
+               }
+               send_message(dest, MSG_PROFILE, &v, sizeof(int), False);
+               break;
+
+       case MSG_FORCE_ELECTION:
+               if (!strequal(dest, "nmbd")) {
+                       fprintf(stderr,"force-election can only be sent to nmbd\n");
+                       return(False);
+               }
+               send_message(dest, MSG_FORCE_ELECTION, NULL, 0, False);
+               break;
+
+       case MSG_REQ_PROFILELEVEL:
+               if (!profilelevel_registered) {
+                   message_register(MSG_PROFILELEVEL, profilelevel_function);
+                   profilelevel_registered = True;
+               }
+               got_level = False;
+               retval = send_message(dest, MSG_REQ_PROFILELEVEL, NULL, 0, True);
+               if (retval) {
+                       timeout_start = time(NULL);
+                       while (!got_level) {
+                               message_dispatch();
+                               if ((time(NULL) - timeout_start) > MAX_WAIT) {
+                                       fprintf(stderr,"profilelevel timeout\n");
+                                       break;
+                               }
+                       }
+               }
+               break;
+
+       case MSG_REQ_TALLOC_USAGE:
+               if (!poolusage_registered) {
+                       message_register(MSG_TALLOC_USAGE, tallocdump_function);
+                       poolusage_registered = True;
+               }
+               got_pool = False;
+               retval = send_message(dest, MSG_REQ_TALLOC_USAGE, NULL, 0, True);
+               if (retval) {
+                       timeout_start = time(NULL);
+                       while (!got_pool) {
+                               message_dispatch();
+                               if ((time(NULL) - timeout_start) > MAX_WAIT) {
+                                       fprintf(stderr,"tallocdump timeout\n");
+                                       break;
+                               }
+                       }
+               }
+               break;
+
+       case MSG_REQ_DEBUGLEVEL:
+               if (!debuglevel_registered) {
+                   message_register(MSG_DEBUGLEVEL, debuglevel_function);
+                   debuglevel_registered = True;
+               }
+               got_level = False;
+               retval = send_message(dest, MSG_REQ_DEBUGLEVEL, NULL, 0, True);
+               if (retval) {
+                       timeout_start = time(NULL);
+                       while (!got_level) {
+                               message_dispatch();
+                               if ((time(NULL) - timeout_start) > MAX_WAIT) {
+                                       fprintf(stderr,"debuglevel timeout\n");
+                                       break;
+                               }
+                       }
+               }
+               break;
+
+               /* Send a notification message to a printer */
+
+       case MSG_PRINTER_NOTIFY2: {
+               char *cmd;
+
+               /* Read subcommand */
+
+               if (!params || !params[0]) {
+                       fprintf(stderr, "Must specify subcommand:\n");
+                       fprintf(stderr, "\tqueuepause <printername>\n");
+                       fprintf(stderr, "\tqueueresume <printername>\n");
+                       fprintf(stderr, "\tjobpause <printername> <unix jobid>\n");
+                       fprintf(stderr, "\tjobresume <printername> <unix jobid>\n");
+                       fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n");
+                       fprintf(stderr, "\tprinter <printername> <comment|port|driver> <new value>\n");
+                       return False;
+               }
+
+               cmd = params[0];
+
+               check_notify_msgs = True;
+
+               /* Pause a print queue */
+
+               if (strequal(cmd, "queuepause")) {
+
+                       if (!params[1]) {
+                               fprintf(stderr, "queuepause command requires a printer name\n");
+                               return False;
+                       }
+
+                       //TODL: notify_printer_status_byname(params[1], PRINTER_STATUS_PAUSED);
+                       break;
+               }
+
+               /* Resume a print queue */
+
+               if (strequal(cmd, "queueresume")) {
+
+                       if (!params[1]) {
+                               fprintf(stderr, "queueresume command requires a printer name\n");
+                               return False;
+                       }
+
+                       //TODL: notify_printer_status_byname(params[1], PRINTER_STATUS_OK);
+                       break;
+               }
+
+               /* Pause a print job */
+
+               if (strequal(cmd, "jobpause")) {
+                       int jobid;
+
+                       if (!params[1] || !params[2]) {
+                               fprintf(stderr, "jobpause command requires a printer name and a jobid\n");
+                               return False;
+                       }
+
+                       jobid = atoi(params[2]);
+
+                       //TODL: notify_job_status_byname(
+                       //TODL:         params[1], jobid, JOB_STATUS_PAUSED, 
+                       //TODL:         SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+                       break;
+               }
+
+               /* Resume a print job */
+
+               if (strequal(cmd, "jobresume")) {
+                       int jobid;
+
+                       if (!params[1] || !params[2]) {
+                               fprintf(stderr, "jobresume command requires a printer name and a jobid\n");
+                               return False;
+                       }
+
+                       jobid = atoi(params[2]);
+
+                       //TODL: notify_job_status_byname(
+                       //TODL:         params[1], jobid, JOB_STATUS_QUEUED,
+                       //TODL:         SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+                       break;
+               }
+
+               /* Delete a print job */
+
+               if (strequal(cmd, "jobdelete")) {
+                       int jobid;
+
+                       if (!params[1] || !params[2]) {
+                               fprintf(stderr, "jobdelete command requires a printer name and a jobid\n");
+                               return False;
+                       }
+
+                       jobid = atoi(params[2]);
+
+                       //TODL: notify_job_status_byname(
+                       //TODL:         params[1], jobid, JOB_STATUS_DELETING,
+                       //TODL:         SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+                       //TODL: notify_job_status_byname(
+                       //TODL:         params[1], jobid, JOB_STATUS_DELETING|
+                       //TODL:         JOB_STATUS_DELETED,
+                       //TODL:         SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+               }
+               
+               /* printer change notify */
+               
+               if (strequal(cmd, "printer")) {
+                       int attribute = -1;
+                       
+                       if (!params[1] || !params[2] || !params[3]) {
+                               fprintf(stderr, "printer command requires an and attribute name and value!\n");
+                               fprintf(stderr, "supported attributes:\n");
+                               fprintf(stderr, "\tcomment:\n");
+                               fprintf(stderr, "\tport:\n");
+                               fprintf(stderr, "\tdriver:\n");
+                               return False;
+                       }
+                       if ( strequal(params[2], "comment") )
+                               attribute = PRINTER_NOTIFY_COMMENT;
+                       else if ( strequal(params[2], "port") )
+                               attribute = PRINTER_NOTIFY_PORT_NAME;
+                       else if ( strequal(params[2], "driver") )
+                               attribute = PRINTER_NOTIFY_DRIVER_NAME;
+                       
+                       if ( attribute == -1 ) {
+                               fprintf(stderr, "bad attribute!\n");
+                               return False;
+                       }
+                       
+                       //TODL: notify_printer_byname( params[1], attribute, params[3]);
+                       
+                       break;
+               }
+               
+               break;
+         }
+
+
+       case MSG_SMB_FORCE_TDIS:
+               if (!strequal(dest, "smbd")) {
+                       fprintf(stderr,"close-share can only be sent to smbd\n");
+                       return(False);
+               }
+               if (!params || !params[0]) {
+                       fprintf(stderr, "close-share needs a share name or '*'\n");
+                       return (False);
+               }
+               retval = send_message(dest, MSG_SMB_FORCE_TDIS, params[0],
+                                     strlen(params[0]) + 1, False);
+               break;
+
+        case MSG_SMB_SAM_SYNC:
+                if (!strequal(dest, "smbd")) {
+                        fprintf(stderr, "samsync can only be sent to smbd\n");
+                        return False;
+                }
+
+                if (params) {
+                        fprintf(stderr, "samsync does not take any parameters\n");
+                        return False;
+                }
+
+                retval = send_message(dest, MSG_SMB_SAM_SYNC, NULL, 0, False);
+
+                break;
+
+        case MSG_SMB_SAM_REPL: {
+                uint32 seqnum;
+
+                if (!strequal(dest, "smbd")) {
+                        fprintf(stderr, "sam repl can only be sent to smbd\n");
+                        return False;
+                }
+
+                if (!params || !params[0]) {
+                        fprintf(stderr, "SAM_REPL needs a parameter\n");
+                        return False;
+                }
+
+                seqnum = atoi(params[0]);
+
+                retval = send_message(dest, MSG_SMB_SAM_SYNC, 
+                                      (char *)&seqnum, sizeof(uint32), False); 
+
+                break;
+        }
+
+       case MSG_PING:
+               if (!pong_registered) {
+                   message_register(MSG_PONG, pong_function);
+                   pong_registered = True;
+               }
+               if (!params || !params[0]) {
+                       fprintf(stderr,"MSG_PING needs a parameter\n");
+                       return(False);
+               }
+               n = atoi(params[0]);
+               pong_count = 0;
+               for (i=0;i<n;i++) {
+                       if (iparams > 1)
+                               retval = send_message(dest, MSG_PING, params[1], strlen(params[1]) + 1, True);
+                       else
+                               retval = send_message(dest, MSG_PING, NULL, 0, True);
+                       if (retval == False)
+                               return False;
+               }
+               wait_for_replies(MAX_WAIT, &n);
+               if (n > 0) {
+                       fprintf(stderr,"PING timeout\n");
+               }
+               break;
+
+       case MSG_REQ_POOL_USAGE:
+               if (!send_message(dest, MSG_REQ_POOL_USAGE, NULL, 0, True))
+                       return False;
+               wait_for_replies(MAX_WAIT, NULL);
+               
+               break;
+
+       case MSG_REQ_DMALLOC_LOG_CHANGED:
+       case MSG_REQ_DMALLOC_MARK:
+               if (!send_message(dest, mtype, NULL, 0, False))
+                       return False;
+               break;
+
+       case MSG_SHUTDOWN:
+               if (!send_message(dest, MSG_SHUTDOWN, NULL, 0, False))
+                       return False;
+               break;
+       case MSG_PRINTER_DRVUPGRADE:
+               if (!send_message(dest, MSG_PRINTER_DRVUPGRADE, params[0], 0, False))
+                       return False;
+               break;
+       }
+
+       /* check if we have any pending print notify messages */
+
+       if ( check_notify_msgs )
+               ;//TODO: print_notify_send_messages(0);
+               
+       return (True);
+}
+
+ int main(int argc, char *argv[])
+{
+       int opt;
+       char temp[255];
+       extern int optind;
+       BOOL interactive = False;
+
+       AllowDebugChange = False;
+       DEBUGLEVEL = 0;
+
+       setup_logging(argv[0],True);
+       
+       if (argc < 2) usage(True);
+
+       while ((opt = getopt(argc, argv,"is:")) != EOF) {
+               switch (opt) {
+               case 'i':
+                       interactive = True;
+                       break;
+               case 's':
+                       pstrcpy(dyn_CONFIGFILE, optarg);
+                       break;
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       usage(True);
+               }
+       }
+
+       lp_load(dyn_CONFIGFILE,False,False,False);
+
+       if (!message_init()) exit(1);
+
+       argc -= optind;
+       argv = &argv[optind];
+
+       register_all();
+
+       if (!interactive) {
+               if (argc < 2) usage(True);
+               /* Need to invert sense of return code -- samba
+                * routines mostly return True==1 for success, but
+                * shell needs 0. */ 
+               return ! do_command(argv[0],argv[1], argc-2, argc > 2 ? &argv[2] : 0);
+       }
+
+       while (True) {
+               char *myargv[4];
+               int myargc;
+
+               printf("smbcontrol> ");
+               if (!fgets(temp, sizeof(temp)-1, stdin)) break;
+               myargc = 0;
+               while ((myargc < 4) && 
+                      (myargv[myargc] = strtok(myargc?NULL:temp," \t\n"))) {
+                       myargc++;
+               }
+               if (!myargc) break;
+               if (strequal(myargv[0],"q")) break;
+               if (myargc < 2)
+                       usage(False);
+               else if (!do_command(myargv[0],myargv[1],myargc-2,myargc > 2 ? &myargv[2] : 0))
+                       usage(False);
+       }
+       return(0);
+}
+
diff --git a/source4/utils/smbfilter.c b/source4/utils/smbfilter.c
new file mode 100644 (file)
index 0000000..1a0d639
--- /dev/null
@@ -0,0 +1,245 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB filter/socket plugin
+   Copyright (C) Andrew Tridgell 1999
+   
+   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"
+
+#define SECURITY_MASK 0
+#define SECURITY_SET  0
+
+/* this forces non-unicode */
+#define CAPABILITY_MASK 0
+#define CAPABILITY_SET  0
+
+/* and non-unicode for the client too */
+#define CLI_CAPABILITY_MASK 0
+#define CLI_CAPABILITY_SET  0
+
+static char *netbiosname;
+static char packet[BUFFER_SIZE];
+
+static void save_file(const char *fname, void *packet, size_t length)
+{
+       int fd;
+       fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+       if (fd == -1) {
+               perror(fname);
+               return;
+       }
+       if (write(fd, packet, length) != length) {
+               fprintf(stderr,"Failed to write %s\n", fname);
+               return;
+       }
+       close(fd);
+       printf("Wrote %d bytes to %s\n", length, fname);
+}
+
+static void filter_reply(char *buf)
+{
+       int msg_type = CVAL(buf,0);
+       int type = CVAL(buf,smb_com);
+       unsigned x;
+
+       if (msg_type) return;
+
+       switch (type) {
+
+       case SMBnegprot:
+               /* force the security bits */
+               x = CVAL(buf, smb_vwv1);
+               x = (x | SECURITY_SET) & ~SECURITY_MASK;
+               SCVAL(buf, smb_vwv1, x);
+
+               /* force the capabilities */
+               x = IVAL(buf,smb_vwv9+1);
+               x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK;
+               SIVAL(buf, smb_vwv9+1, x);
+               break;
+
+       }
+}
+
+static void filter_request(char *buf)
+{
+       int msg_type = CVAL(buf,0);
+       int type = CVAL(buf,smb_com);
+       pstring name1,name2;
+       unsigned x;
+
+       if (msg_type) {
+               /* it's a netbios special */
+               switch (msg_type) {
+               case 0x81:
+                       /* session request */
+                       name_extract(buf,4,name1);
+                       name_extract(buf,4 + name_len(buf + 4),name2);
+                       d_printf("sesion_request: %s -> %s\n",
+                                name1, name2);
+                       if (netbiosname) {
+                               /* replace the destination netbios name */
+                               name_mangle(netbiosname, buf+4, 0x20);
+                       }
+               }
+               return;
+       }
+
+       /* it's an ordinary SMB request */
+       switch (type) {
+       case SMBsesssetupX:
+               /* force the client capabilities */
+               x = IVAL(buf,smb_vwv11);
+               d_printf("SMBsesssetupX cap=0x%08x\n", x);
+               d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8));
+               system("mv sessionsetup.dat sessionsetup1.dat");
+               save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7));
+               x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK;
+               SIVAL(buf, smb_vwv11, x);
+               break;
+       }
+
+}
+
+
+static void filter_child(int c, struct in_addr dest_ip)
+{
+       int s;
+
+       /* we have a connection from a new client, now connect to the server */
+       s = open_socket_out(SOCK_STREAM, &dest_ip, 445, LONG_CONNECT_TIMEOUT);
+
+       if (s == -1) {
+               d_printf("Unable to connect to %s\n", inet_ntoa(dest_ip));
+               exit(1);
+       }
+
+       while (c != -1 || s != -1) {
+               fd_set fds;
+               int num;
+               
+               FD_ZERO(&fds);
+               if (s != -1) FD_SET(s, &fds);
+               if (c != -1) FD_SET(c, &fds);
+
+               num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL);
+               if (num <= 0) continue;
+               
+               if (c != -1 && FD_ISSET(c, &fds)) {
+                       if (!receive_smb(c, packet, 0)) {
+                               d_printf("client closed connection\n");
+                               exit(0);
+                       }
+                       filter_request(packet);
+                       if (!send_smb(s, packet)) {
+                               d_printf("server is dead\n");
+                               exit(1);
+                       }                       
+               }
+               if (s != -1 && FD_ISSET(s, &fds)) {
+                       if (!receive_smb(s, packet, 0)) {
+                               d_printf("server closed connection\n");
+                               exit(0);
+                       }
+                       filter_reply(packet);
+                       if (!send_smb(c, packet)) {
+                               d_printf("client is dead\n");
+                               exit(1);
+                       }                       
+               }
+       }
+       d_printf("Connection closed\n");
+       exit(0);
+}
+
+
+static void start_filter(char *desthost)
+{
+       int s, c;
+       struct in_addr dest_ip;
+
+       CatchChild();
+
+       /* start listening on port 445 locally */
+       s = open_socket_in(SOCK_STREAM, 445, 0, 0, True);
+       
+       if (s == -1) {
+               d_printf("bind failed\n");
+               exit(1);
+       }
+
+       if (listen(s, 5) == -1) {
+               d_printf("listen failed\n");
+       }
+
+       if (!resolve_name(desthost, &dest_ip, 0x20)) {
+               d_printf("Unable to resolve host %s\n", desthost);
+               exit(1);
+       }
+
+       while (1) {
+               fd_set fds;
+               int num;
+               struct sockaddr addr;
+               socklen_t in_addrlen = sizeof(addr);
+               
+               FD_ZERO(&fds);
+               FD_SET(s, &fds);
+
+               num = sys_select_intr(s+1,&fds,NULL,NULL,NULL);
+               if (num > 0) {
+                       c = accept(s, &addr, &in_addrlen);
+                       if (c != -1) {
+                               if (fork() == 0) {
+                                       close(s);
+                                       filter_child(c, dest_ip);
+                                       exit(0);
+                               } else {
+                                       close(c);
+                               }
+                       }
+               }
+       }
+}
+
+
+int main(int argc, char *argv[])
+{
+       char *desthost;
+       pstring configfile;
+
+       setup_logging(argv[0],True);
+  
+       pstrcpy(configfile,dyn_CONFIGFILE);
+       if (argc < 2) {
+               fprintf(stderr,"smbfilter <desthost> <netbiosname>\n");
+               exit(1);
+       }
+
+       desthost = argv[1];
+       if (argc > 2) {
+               netbiosname = argv[2];
+       }
+
+       if (!lp_load(configfile,True,False,False)) {
+               d_printf("Unable to load config file\n");
+       }
+
+       start_filter(desthost);
+       return 0;
+}
diff --git a/source4/utils/smbgroupedit.c b/source4/utils/smbgroupedit.c
new file mode 100644 (file)
index 0000000..69f69ae
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Andrew Tridgell              1992-2000,
+ *  Copyright (C) Jean François Micouleau      1998-2001.
+ *  
+ *  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"
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
+ */
+extern char *optarg;
+extern int optind;
+
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
+{
+       if (getuid() == 0) {
+               printf("smbgroupedit options\n");
+       } else {
+               printf("You need to be root to use this tool!\n");
+       }
+       printf("options:\n");
+       printf("  -a group             create new group\n");
+       printf("    -n group           NT group name\n");
+       printf("    -p privilege       only local\n");
+       printf("    -d description     group description\n");
+       printf("  -v                   list groups\n");
+       printf("    -l                 long list (include details)\n");
+       printf("    -s                 short list (default)\n");
+       printf("  -c SID               change group\n");
+       printf("     -u unix group\n");
+       printf("     -d description    group description\n");
+       printf("  -r rid               RID of new group\n");
+       printf("  -x group             delete this group\n");
+       printf("\n");
+       printf("    -t[b|d|l]          type: builtin, domain, local \n");
+       exit(1);
+}
+
+/*********************************************************
+ Figure out if the input was an NT group or a SID string.  
+ Return the SID.
+**********************************************************/
+static BOOL get_sid_from_input(DOM_SID *sid, char *input) 
+{
+       GROUP_MAP map;
+       
+       if (StrnCaseCmp( input, "S-", 2)) {
+               /* Perhaps its the NT group name? */
+               if (!pdb_getgrnam(&map, input, MAPPING_WITHOUT_PRIV)) {
+                       printf("NT Group %s doesn't exist in mapping DB\n", input);
+                       return False;
+               } else {
+                       *sid = map.sid;
+               }
+       } else {
+               if (!string_to_sid(sid, input)) {
+                       printf("converting sid %s from a string failed!\n", input);
+                       return False;
+               }
+       }
+       return True;
+}
+
+/*********************************************************
+ add a group.
+**********************************************************/
+static int addgroup(gid_t gid, enum SID_NAME_USE sid_type, char *ntgroup, char *ntcomment, char *privilege, uint32 rid)
+{
+       PRIVILEGE_SET se_priv;
+       DOM_SID sid;
+       fstring string_sid;
+       fstring comment;
+
+       sid_copy(&sid, get_global_sam_sid());
+       sid_append_rid(&sid, rid);
+       
+       sid_to_string(string_sid, &sid);
+       
+       if (ntcomment==NULL)
+               fstrcpy(comment, "Local Unix group");
+       else
+               fstrcpy(comment, ntcomment);
+
+       init_privilege(&se_priv);
+       if (privilege!=NULL)
+               convert_priv_from_text(&se_priv, privilege);
+
+       if(!add_initial_entry(gid, string_sid, sid_type, ntgroup,
+                             comment, se_priv, PR_ACCESS_FROM_NETWORK)) {
+               printf("adding entry for group %s failed!\n", ntgroup);
+               free_privilege(&se_priv);
+               return -1;
+       }
+
+       free_privilege(&se_priv);
+       return 0;
+}
+
+/*********************************************************
+ Change a group.
+**********************************************************/
+static int changegroup(char *sid_string, char *group, enum SID_NAME_USE sid_type, char *ntgroup, char *groupdesc, char *privilege)
+{
+       DOM_SID sid;
+       GROUP_MAP map;
+       gid_t gid;
+
+       if (!get_sid_from_input(&sid, sid_string)) {
+               return -1;
+       }
+
+       /* Get the current mapping from the database */
+       if(!pdb_getgrsid(&map, sid, MAPPING_WITH_PRIV)) {
+               printf("This SID does not exist in the database\n");
+               return -1;
+       }
+
+       /* If a new Unix group is specified, check and change */
+       if (group!=NULL) {
+               gid=nametogid(group);
+               if (gid==-1) {
+                       printf("The UNIX group does not exist\n");
+                       return -1;
+               } else
+                       map.gid=gid;
+       }
+       
+       /*
+        * Allow changing of group type only between domain and local
+        * We disallow changing Builtin groups !!! (SID problem)
+        */ 
+       if (sid_type==SID_NAME_ALIAS 
+           || sid_type==SID_NAME_DOM_GRP 
+           || sid_type==SID_NAME_UNKNOWN) {
+               if (map.sid_name_use==SID_NAME_ALIAS 
+                   || map.sid_name_use==SID_NAME_DOM_GRP
+                   || map.sid_name_use==SID_NAME_UNKNOWN) {
+                       map.sid_name_use=sid_type;
+               } else {
+                       printf("cannot change group type to builtin\n");
+               };
+       } else {
+               printf("cannot change group type from builtin\n");
+       }
+
+       if (ntgroup!=NULL)
+               fstrcpy(map.nt_name, ntgroup);
+
+       /* Change comment if new one */
+       if (groupdesc!=NULL)
+               fstrcpy(map.comment, groupdesc);
+
+       /* Change the privilege if new one */
+       if (privilege!=NULL)
+               convert_priv_from_text(&map.priv_set, privilege);
+
+       if (!pdb_update_group_mapping_entry(&map)) {
+               printf("Could not update group database\n");
+               free_privilege(&map.priv_set);
+               return -1;
+       }
+       
+       free_privilege(&map.priv_set);
+       return 0;
+}
+
+/*********************************************************
+ Delete the group.
+**********************************************************/
+static int deletegroup(char *group)
+{
+       DOM_SID sid;
+
+       if (!get_sid_from_input(&sid, group)) {
+               return -1;
+       }
+
+       if(!pdb_delete_group_mapping_entry(sid)) {
+               printf("removing group %s from the mapping db failed!\n", group);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int listgroup(enum SID_NAME_USE sid_type, BOOL long_list)
+{
+       int entries,i;
+       TALLOC_CTX *mem_ctx;
+       GROUP_MAP *map=NULL;
+       fstring string_sid;
+       fstring group_type;
+       fstring priv_text;
+
+       if (!long_list)
+               printf("NT group (SID) -> Unix group\n");
+               
+       if (!pdb_enum_group_mapping(sid_type, &map, &entries, ENUM_ALL_MAPPED, MAPPING_WITH_PRIV))
+               return -1;
+       
+       mem_ctx = talloc_init("smbgroupedit talloc");
+       if (!mem_ctx) return -1;
+       for (i=0; i<entries; i++) {
+               decode_sid_name_use(group_type, (map[i]).sid_name_use);
+               sid_to_string(string_sid, &map[i].sid);
+               convert_priv_to_text(&(map[i].priv_set), priv_text);
+               free_privilege(&(map[i].priv_set));
+               
+               if (!long_list)
+                       printf("%s (%s) -> %s\n", map[i].nt_name, string_sid, 
+                               gidtoname(mem_ctx, map[i].gid));
+               else {
+                       printf("%s\n", map[i].nt_name);
+                       printf("\tSID       : %s\n", string_sid);
+                       printf("\tUnix group: %s\n", gidtoname(mem_ctx, map[i].gid));
+                       printf("\tGroup type: %s\n", group_type);
+                       printf("\tComment   : %s\n", map[i].comment);
+                       printf("\tPrivilege : %s\n\n", priv_text);
+               }
+       }
+       talloc_destroy(mem_ctx);
+
+       return 0;
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main (int argc, char **argv)
+{
+       int ch;
+       BOOL add_group = False;
+       BOOL view_group = False;
+       BOOL change_group = False;
+       BOOL delete_group = False;
+       BOOL nt_group = False;
+       BOOL priv = False;
+       BOOL group_type = False;
+       BOOL long_list = False;
+
+       char *group = NULL;
+       char *sid = NULL;
+       char *ntgroup = NULL;
+       char *privilege = NULL;
+       char *groupt = NULL;
+       char *group_desc = NULL;
+
+       enum SID_NAME_USE sid_type;
+       uint32 rid = -1;
+
+       setup_logging("groupedit", True);
+
+       if (argc < 2) {
+               usage();
+               return 0;
+       }
+       
+       if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
+                       dyn_CONFIGFILE);
+               exit(1);
+       }
+
+       if (!init_names())
+               exit(1);
+       
+       if(!initialize_password_db(True)) {
+               fprintf(stderr, "Can't setup password database vectors.\n");
+               exit(1);
+       }
+       
+       if(get_global_sam_sid()==False) {
+               fprintf(stderr, "Can not read machine SID\n");
+               return 0;
+       }
+
+       while ((ch = getopt(argc, argv, "a:c:d:ln:p:r:st:u:vx:")) != EOF) {
+               switch(ch) {
+               case 'a':
+                       add_group = True;
+                       group=optarg;
+                       break;
+               case 'c':
+                       change_group = True;
+                       sid=optarg;
+                       break;
+               case 'd':
+                       group_desc=optarg;
+                       break;
+               case 'l':
+                       long_list = True;
+                       break;
+               case 'n':
+                       nt_group = True;
+                       ntgroup=optarg;
+                       break;
+               case 'p':
+                       priv = True;
+                       privilege=optarg;
+                       break;
+               case 'r':
+                       rid = atoi(optarg);
+                       break;
+               case 's':
+                       long_list = False;
+                       break;
+               case 't':
+                       group_type = True;
+                       groupt=optarg;
+                       break;
+               case 'u':
+                       group=optarg;
+                       break;
+               case 'v':
+                       view_group = True;
+                       break;
+               case 'x':
+                       delete_group = True;
+                       group=optarg;
+                       break;
+               /*default:
+                       usage();*/
+               }
+       }
+       
+       
+       if (((add_group?1:0) + (view_group?1:0) + (change_group?1:0) + (delete_group?1:0)) > 1) {
+               fprintf (stderr, "Incompatible options on command line!\n");
+               usage();
+               exit(1);
+       }
+
+       /* no option on command line -> list groups */  
+       if (((add_group?1:0) + (view_group?1:0) + (change_group?1:0) + (delete_group?1:0)) == 0)
+               view_group = True;
+
+       
+       if (group_type==False)
+               sid_type=SID_NAME_UNKNOWN;
+       else {
+               switch (groupt[0]) {
+                       case 'l':
+                       case 'L':
+                               sid_type=SID_NAME_ALIAS;
+                               break;
+                       case 'd':
+                       case 'D':
+                               sid_type=SID_NAME_DOM_GRP;
+                               break;
+                       case 'b':
+                       case 'B':
+                               sid_type=SID_NAME_WKN_GRP;
+                               break;
+                       default:
+                               sid_type=SID_NAME_UNKNOWN;
+                               break;
+               }
+       }
+
+       if (add_group) {
+               gid_t gid=nametogid(group);
+               if (gid==-1) {
+                       printf("unix group %s doesn't exist!\n", group);
+                       return -1;
+               }
+
+               if (rid == -1) {
+                       rid = pdb_gid_to_group_rid(gid);
+               }
+               return addgroup(gid, sid_type, ntgroup?ntgroup:group,
+                               group_desc, privilege, rid);
+       }
+
+       if (view_group)
+               return listgroup(sid_type, long_list);
+
+       if (delete_group)
+               return deletegroup(group);
+       
+       if (change_group) {             
+               return changegroup(sid, group, sid_type, ntgroup, group_desc, privilege);
+       }
+       
+       usage();
+
+       return 0;
+}
diff --git a/source4/utils/smbpasswd.c b/source4/utils/smbpasswd.c
new file mode 100644 (file)
index 0000000..577e467
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * Unix SMB/CIFS implementation. 
+ * Copyright (C) Jeremy Allison 1995-1998
+ * Copyright (C) Tim Potter     2001
+ * 
+ * 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"
+
+extern BOOL AllowDebugChange;
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
+ */
+extern char *optarg;
+extern int optind;
+
+/* forced running in root-mode */
+static BOOL got_pass = False, got_username = False;
+static BOOL stdin_passwd_get = False;
+static fstring user_name, user_password;
+static char *new_passwd = NULL;
+static const char *remote_machine = NULL;
+
+static fstring ldap_secret;
+
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
+{
+       printf("When run by root:\n");
+       printf("    smbpasswd [options] [username] [password]\n");
+       printf("otherwise:\n");
+       printf("    smbpasswd [options] [password]\n\n");
+
+       printf("options:\n");
+       printf("  -L                   local mode (must be first option)\n");
+       printf("  -h                   print this usage message\n");
+       printf("  -s                   use stdin for password prompt\n");
+       printf("  -c smb.conf file     Use the given path to the smb.conf file\n");
+       printf("  -D LEVEL             debug level\n");
+       printf("  -r MACHINE           remote machine\n");
+       printf("  -U USER              remote username\n");
+
+       printf("extra options when run by root or in local mode:\n");
+       printf("  -a                   add user\n");
+       printf("  -d                   disable user\n");
+       printf("  -e                   enable user\n");
+       printf("  -i                   interdomain trust account\n");
+       printf("  -m                   machine trust account\n");
+       printf("  -n                   set no password\n");
+       printf("  -w                   ldap admin password\n");
+       printf("  -x                   delete user\n");
+       printf("  -R ORDER             name resolve order\n");
+
+       exit(1);
+}
+
+static void set_line_buffering(FILE *f)
+{
+       setvbuf(f, NULL, _IOLBF, 0);
+}
+
+/*******************************************************************
+ Process command line options
+ ******************************************************************/
+static int process_options(int argc, char **argv, int local_flags)
+{
+       int ch;
+       pstring configfile;
+       pstrcpy(configfile, dyn_CONFIGFILE);
+
+       local_flags |= LOCAL_SET_PASSWORD;
+
+       ZERO_STRUCT(user_name);
+       ZERO_STRUCT(user_password);
+
+       user_name[0] = '\0';
+
+       while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:L")) != EOF) {
+               switch(ch) {
+               case 'L':
+                       local_flags |= LOCAL_AM_ROOT;
+                       break;
+               case 'c':
+                       pstrcpy(configfile,optarg);
+                       break;
+               case 'a':
+                       local_flags |= LOCAL_ADD_USER;
+                       break;
+               case 'x':
+                       local_flags |= LOCAL_DELETE_USER;
+                       local_flags &= ~LOCAL_SET_PASSWORD;
+                       break;
+               case 'd':
+                       local_flags |= LOCAL_DISABLE_USER;
+                       local_flags &= ~LOCAL_SET_PASSWORD;
+                       break;
+               case 'e':
+                       local_flags |= LOCAL_ENABLE_USER;
+                       local_flags &= ~LOCAL_SET_PASSWORD;
+                       break;
+               case 'm':
+                       local_flags |= LOCAL_TRUST_ACCOUNT;
+                       break;
+               case 'i':
+                       local_flags |= LOCAL_INTERDOM_ACCOUNT;
+                       break;
+               case 'j':
+                       d_printf("See 'net join' for this functionality\n");
+                       exit(1);
+                       break;
+               case 'n':
+                       local_flags |= LOCAL_SET_NO_PASSWORD;
+                       local_flags &= ~LOCAL_SET_PASSWORD;
+                       new_passwd = smb_xstrdup("NO PASSWORD");
+                       break;
+               case 'r':
+                       remote_machine = optarg;
+                       break;
+               case 's':
+                       set_line_buffering(stdin);
+                       set_line_buffering(stdout);
+                       set_line_buffering(stderr);
+                       stdin_passwd_get = True;
+                       break;
+               case 'w':
+                       local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
+                       fstrcpy(ldap_secret, optarg);
+                       break;
+               case 'R':
+                       lp_set_name_resolve_order(optarg);
+                       break;
+               case 'D':
+                       DEBUGLEVEL = atoi(optarg);
+                       break;
+               case 'U': {
+                       char *lp;
+
+                       got_username = True;
+                       fstrcpy(user_name, optarg);
+
+                       if ((lp = strchr(user_name, '%'))) {
+                               *lp = 0;
+                               fstrcpy(user_password, lp + 1);
+                               got_pass = True;
+                               memset(strchr_m(optarg, '%') + 1, 'X',
+                                      strlen(user_password));
+                       }
+
+                       break;
+               }
+               case 'h':
+               default:
+                       usage();
+               }
+       }
+       
+       argc -= optind;
+       argv += optind;
+
+       switch(argc) {
+       case 0:
+               if (!got_username)
+                       fstrcpy(user_name, "");
+               break;
+       case 1:
+               if (!(local_flags & LOCAL_AM_ROOT)) {
+                       new_passwd = argv[0];
+               } else {
+                       if (got_username) {
+                               usage();
+                       } else {
+                               fstrcpy(user_name, argv[0]);
+                       }
+               }
+               break;
+       case 2:
+               if (!(local_flags & LOCAL_AM_ROOT) || got_username || got_pass) {
+                       usage();
+               }
+
+               fstrcpy(user_name, argv[0]);
+               new_passwd = smb_xstrdup(argv[1]);
+               break;
+       default:
+               usage();
+       }
+
+       if (!lp_load(configfile,True,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", 
+                       dyn_CONFIGFILE);
+               exit(1);
+       }
+
+       return local_flags;
+}
+
+/*************************************************************
+ Utility function to prompt for passwords from stdin. Each
+ password entered must end with a newline.
+*************************************************************/
+static char *stdin_new_passwd(void)
+{
+       static fstring new_pw;
+       size_t len;
+
+       ZERO_ARRAY(new_pw);
+
+       /*
+        * if no error is reported from fgets() and string at least contains
+        * the newline that ends the password, then replace the newline with
+        * a null terminator.
+        */
+       if ( fgets(new_pw, sizeof(new_pw), stdin) != NULL) {
+               if ((len = strlen(new_pw)) > 0) {
+                       if(new_pw[len-1] == '\n')
+                               new_pw[len - 1] = 0; 
+               }
+       }
+       return(new_pw);
+}
+
+
+/*************************************************************
+ Utility function to get passwords via tty or stdin
+ Used if the '-s' option is set to silently get passwords
+ to enable scripting.
+*************************************************************/
+static char *get_pass( const char *prompt, BOOL stdin_get)
+{
+       char *p;
+       if (stdin_get) {
+               p = stdin_new_passwd();
+       } else {
+               p = getpass(prompt);
+       }
+       return smb_xstrdup(p);
+}
+
+/*************************************************************
+ Utility function to prompt for new password.
+*************************************************************/
+static char *prompt_for_new_password(BOOL stdin_get)
+{
+       char *p;
+       fstring new_pw;
+
+       ZERO_ARRAY(new_pw);
+       p = get_pass("New SMB password:", stdin_get);
+
+       fstrcpy(new_pw, p);
+       SAFE_FREE(p);
+
+       p = get_pass("Retype new SMB password:", stdin_get);
+
+       if (strcmp(p, new_pw)) {
+               fprintf(stderr, "Mismatch - password unchanged.\n");
+               ZERO_ARRAY(new_pw);
+               SAFE_FREE(p);
+               return NULL;
+       }
+
+       return p;
+}
+
+
+/*************************************************************
+ Change a password either locally or remotely.
+*************************************************************/
+
+static BOOL password_change(const char *remote_mach, char *username, 
+                           char *old_passwd, char *new_pw, int local_flags)
+{
+       BOOL ret;
+       pstring err_str;
+       pstring msg_str;
+
+       if (remote_mach != NULL) {
+               if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
+                                                       LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
+                       /* these things can't be done remotely yet */
+                       return False;
+               }
+               ret = remote_password_change(remote_mach, username, 
+                                            old_passwd, new_pw, err_str, sizeof(err_str));
+               if(*err_str)
+                       fprintf(stderr, err_str);
+               return ret;
+       }
+       
+       ret = local_password_change(username, local_flags, new_pw, 
+                                    err_str, sizeof(err_str), msg_str, sizeof(msg_str));
+
+       if(*msg_str)
+               printf(msg_str);
+       if(*err_str)
+               fprintf(stderr, err_str);
+
+       return ret;
+}
+
+/*******************************************************************
+ Store the LDAP admin password in secrets.tdb
+ ******************************************************************/
+static BOOL store_ldap_admin_pw (char* pw)
+{      
+       if (!pw) 
+               return False;
+
+       if (!secrets_init())
+               return False;
+       
+       return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
+}
+
+
+/*************************************************************
+ Handle password changing for root.
+*************************************************************/
+
+static int process_root(int local_flags)
+{
+       struct passwd  *pwd;
+       int result = 0;
+       char *old_passwd = NULL;
+
+       if (local_flags & LOCAL_SET_LDAP_ADMIN_PW)
+       {
+               printf("Setting stored password for \"%s\" in secrets.tdb\n", 
+                       lp_ldap_admin_dn());
+               if (!store_ldap_admin_pw(ldap_secret))
+                       DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
+               goto done;
+       }
+
+       /*
+        * Ensure both add/delete user are not set
+        * Ensure add/delete user and either remote machine or join domain are
+        * not both set.
+        */     
+       if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) || 
+          ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) && 
+               (remote_machine != NULL))) {
+               usage();
+       }
+       
+       /* Only load interfaces if we are doing network operations. */
+
+       if (remote_machine) {
+               load_interfaces();
+       }
+
+       if (!user_name[0] && (pwd = getpwuid_alloc(geteuid()))) {
+               fstrcpy(user_name, pwd->pw_name);
+               passwd_free(&pwd);
+       } 
+
+       if (!user_name[0]) {
+               fprintf(stderr,"You must specify a username\n");
+               exit(1);
+       }
+
+       if (local_flags & LOCAL_TRUST_ACCOUNT) {
+               /* add the $ automatically */
+               static fstring buf;
+
+               /*
+                * Remove any trailing '$' before we
+                * generate the initial machine password.
+                */
+
+               if (user_name[strlen(user_name)-1] == '$') {
+                       user_name[strlen(user_name)-1] = 0;
+               }
+
+               if (local_flags & LOCAL_ADD_USER) {
+                       SAFE_FREE(new_passwd);
+                       new_passwd = smb_xstrdup(user_name);
+                       strlower(new_passwd);
+               }
+
+               /*
+                * Now ensure the username ends in '$' for
+                * the machine add.
+                */
+
+               slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+               fstrcpy(user_name, buf);
+       } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+               static fstring buf;
+
+               if (local_flags & LOCAL_ADD_USER) {
+                       /*
+                        * Prompt for trusting domain's account password
+                        */
+                       new_passwd = prompt_for_new_password(stdin_passwd_get);
+                       if(!new_passwd) {
+                               fprintf(stderr, "Unable to get newpassword.\n");
+                               exit(1);
+                       }
+               }
+               
+               /* prepare uppercased and '$' terminated username */
+               slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
+               fstrcpy(user_name, buf);
+               
+       } else {
+               
+               if (remote_machine != NULL) {
+                       old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+               }
+               
+               if (!(local_flags & LOCAL_SET_PASSWORD)) {
+                       
+                       /*
+                        * If we are trying to enable a user, first we need to find out
+                        * if they are using a modern version of the smbpasswd file that
+                        * disables a user by just writing a flag into the file. If so
+                        * then we can re-enable a user without prompting for a new
+                        * password. If not (ie. they have a no stored password in the
+                        * smbpasswd file) then we need to prompt for a new password.
+                        */
+                       
+                       if(local_flags & LOCAL_ENABLE_USER) {
+                               SAM_ACCOUNT *sampass = NULL;
+                               BOOL ret;
+                               
+                               pdb_init_sam(&sampass);
+                               ret = pdb_getsampwnam(sampass, user_name);
+                               if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) {
+                                       local_flags |= LOCAL_SET_PASSWORD;
+                               }
+                               pdb_free_sam(&sampass);
+                       }
+               }
+               
+               if(local_flags & LOCAL_SET_PASSWORD) {
+                       new_passwd = prompt_for_new_password(stdin_passwd_get);
+                       
+                       if(!new_passwd) {
+                               fprintf(stderr, "Unable to get new password.\n");
+                               exit(1);
+                       }
+               }
+       }
+
+       if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) {
+               fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
+               result = 1;
+               goto done;
+       } 
+
+       if(remote_machine) {
+               printf("Password changed for user %s on %s.\n", user_name, remote_machine );
+       } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
+               SAM_ACCOUNT *sampass = NULL;
+               BOOL ret;
+               
+               pdb_init_sam(&sampass);
+               ret = pdb_getsampwnam(sampass, user_name);
+
+               printf("Password changed for user %s.", user_name );
+               if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) )
+                       printf(" User has disabled flag set.");
+               if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) )
+                       printf(" User has no password flag set.");
+               printf("\n");
+               pdb_free_sam(&sampass);
+       }
+
+ done:
+       SAFE_FREE(new_passwd);
+       return result;
+}
+
+
+/*************************************************************
+ Handle password changing for non-root.
+*************************************************************/
+
+static int process_nonroot(int local_flags)
+{
+       struct passwd  *pwd = NULL;
+       int result = 0;
+       char *old_pw = NULL;
+       char *new_pw = NULL;
+
+       if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) {
+               /* Extra flags that we can't honor non-root */
+               usage();
+       }
+
+       if (!user_name[0]) {
+               pwd = getpwuid_alloc(getuid());
+               if (pwd) {
+                       fstrcpy(user_name,pwd->pw_name);
+                       passwd_free(&pwd);
+               } else {
+                       fprintf(stderr, "smbpasswd: you don't exist - go away\n");
+                       exit(1);
+               }
+       }
+       
+       /*
+        * A non-root user is always setting a password
+        * via a remote machine (even if that machine is
+        * localhost).
+        */     
+
+       load_interfaces(); /* Delayed from main() */
+
+       if (remote_machine == NULL) {
+               remote_machine = "127.0.0.1";
+       }
+
+       if (remote_machine != NULL) {
+               old_pw = get_pass("Old SMB password:",stdin_passwd_get);
+       }
+       
+       if (!new_passwd) {
+               new_pw = prompt_for_new_password(stdin_passwd_get);
+       }
+       else
+               new_pw = smb_xstrdup(new_passwd);
+       
+       if (!new_pw) {
+               fprintf(stderr, "Unable to get new password.\n");
+               exit(1);
+       }
+
+       if (!password_change(remote_machine, user_name, old_pw, new_pw, 0)) {
+               fprintf(stderr,"Failed to change password for %s\n", user_name);
+               result = 1;
+               goto done;
+       }
+
+       printf("Password changed for user %s\n", user_name);
+
+ done:
+       SAFE_FREE(old_pw);
+       SAFE_FREE(new_pw);
+
+       return result;
+}
+
+
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main(int argc, char **argv)
+{      
+       int local_flags = 0;
+       
+       AllowDebugChange = False;
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+       set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+       if (getuid() == 0) {
+               local_flags = LOCAL_AM_ROOT;
+       }
+
+       local_flags = process_options(argc, argv, local_flags);
+
+       setup_logging("smbpasswd", True);
+       
+       /*
+        * Set the machine NETBIOS name if not already
+        * set from the config file. 
+        */ 
+    
+       if (!init_names())
+               return 1;
+
+       /* Check the effective uid - make sure we are not setuid */
+       if (is_setuid_root()) {
+               fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
+               exit(1);
+       }
+
+       if (local_flags & LOCAL_AM_ROOT) {
+               secrets_init();
+               return process_root(local_flags);
+       } 
+
+       return process_nonroot(local_flags);
+}
diff --git a/source4/utils/smbtree.c b/source4/utils/smbtree.c
new file mode 100644 (file)
index 0000000..ff6120e
--- /dev/null
@@ -0,0 +1,369 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Network neighbourhood browser.
+   
+   Copyright (C) Tim Potter      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"
+
+static BOOL use_bcast;
+
+struct user_auth_info {
+       pstring username;
+       pstring password;
+       pstring workgroup;
+};
+
+/* How low can we go? */
+
+enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
+static enum tree_level level = LEV_SHARE;
+
+static void usage(void)
+{
+       printf(
+"Usage: smbtree [options]\n\
+\n\
+\t-d debuglevel           set debug output level\n\
+\t-U username             user to autheticate as\n\
+\t-W workgroup            workgroup of user to authenticate as\n\
+\t-D                      list only domains (workgroups) of tree\n\
+\t-S                      list domains and servers of tree\n\
+\t-b                      use bcast instead of using the master browser\n\
+\n\
+The username can be of the form username%%password or\n\
+workgroup\\username%%password.\n\n\
+");
+}
+
+/* Holds a list of workgroups or servers */
+
+struct name_list {
+        struct name_list *prev, *next;
+        pstring name, comment;
+        uint32 server_type;
+};
+
+static struct name_list *workgroups, *servers, *shares;
+
+static void free_name_list(struct name_list *list)
+{
+        while(list)
+                DLIST_REMOVE(list, list);
+}
+
+static void add_name(const char *machine_name, uint32 server_type,
+                     const char *comment, void *state)
+{
+        struct name_list **name_list = (struct name_list **)state;
+        struct name_list *new_name;
+
+        new_name = (struct name_list *)malloc(sizeof(struct name_list));
+
+        if (!new_name)
+                return;
+
+        ZERO_STRUCTP(new_name);
+
+        pstrcpy(new_name->name, machine_name);
+        pstrcpy(new_name->comment, comment);
+        new_name->server_type = server_type;
+
+        DLIST_ADD(*name_list, new_name);
+}
+
+/* Return a cli_state pointing at the IPC$ share for the given server */
+
+static struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
+                                         struct user_auth_info *user_info)
+{
+        struct cli_state *cli;
+        char *myname;
+       NTSTATUS nt_status;
+
+    myname = get_myname();
+       
+       nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC", 
+                                       user_info->username, lp_workgroup(), user_info->password, 
+                                       CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, NULL);
+
+       free(myname);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               return cli;
+       } else {
+               return NULL;
+       }
+}
+
+/* Return the IP address and workgroup of a master browser on the 
+   network. */
+
+static BOOL find_master_ip_bcast(pstring workgroup, struct in_addr *server_ip)
+{
+       struct in_addr *ip_list;
+       int i, count;
+
+        /* Go looking for workgroups by broadcasting on the local network */ 
+
+        if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+                return False;
+        }
+
+       for (i = 0; i < count; i++) {
+               static fstring name;
+
+               if (!name_status_find("*", 0, 0x1d, ip_list[i], name))
+                       continue;
+
+                if (!find_master_ip(name, server_ip))
+                       continue;
+
+                pstrcpy(workgroup, name);
+
+                DEBUG(4, ("found master browser %s, %s\n", 
+                          name, inet_ntoa(ip_list[i])));
+
+                return True;
+       }
+
+       return False;
+}
+
+/****************************************************************************
+  display tree of smb workgroups, servers and shares
+****************************************************************************/
+static BOOL get_workgroups(struct user_auth_info *user_info)
+{
+        struct cli_state *cli;
+        struct in_addr server_ip;
+       pstring master_workgroup;
+
+        /* Try to connect to a #1d name of our current workgroup.  If that
+           doesn't work broadcast for a master browser and then jump off
+           that workgroup. */
+
+       pstrcpy(master_workgroup, lp_workgroup());
+
+        if (use_bcast || !find_master_ip(lp_workgroup(), &server_ip)) {
+                DEBUG(4, ("Unable to find master browser for workgroup %s\n", 
+                         master_workgroup));
+               if (!find_master_ip_bcast(master_workgroup, &server_ip)) {
+                       DEBUG(4, ("Unable to find master browser by "
+                                 "broadcast\n"));
+                       return False;
+               }
+        }
+
+        if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+                return False;
+
+        if (!cli_NetServerEnum(cli, master_workgroup, 
+                               SV_TYPE_DOMAIN_ENUM, add_name, &workgroups))
+                return False;
+
+        return True;
+}
+
+/* Retrieve the list of servers for a given workgroup */
+
+static BOOL get_servers(char *workgroup, struct user_auth_info *user_info)
+{
+        struct cli_state *cli;
+        struct in_addr server_ip;
+
+        /* Open an IPC$ connection to the master browser for the workgroup */
+
+        if (!find_master_ip(workgroup, &server_ip)) {
+                DEBUG(4, ("Cannot find master browser for workgroup %s\n",
+                          workgroup));
+                return False;
+        }
+
+        if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+                return False;
+
+        if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name, 
+                               &servers))
+                return False;
+
+        return True;
+}
+
+static BOOL get_shares(char *server_name, struct user_auth_info *user_info)
+{
+        struct cli_state *cli;
+
+        if (!(cli = get_ipc_connect(server_name, NULL, user_info)))
+                return False;
+
+        if (!cli_RNetShareEnum(cli, add_name, &shares))
+                return False;
+
+        return True;
+}
+
+static BOOL print_tree(struct user_auth_info *user_info)
+{
+        struct name_list *wg, *sv, *sh;
+
+        /* List workgroups */
+
+        if (!get_workgroups(user_info))
+                return False;
+
+        for (wg = workgroups; wg; wg = wg->next) {
+
+                printf("%s\n", wg->name);
+
+                /* List servers */
+
+                free_name_list(servers);
+                servers = NULL;
+
+                if (level == LEV_WORKGROUP || 
+                    !get_servers(wg->name, user_info))
+                        continue;
+
+                for (sv = servers; sv; sv = sv->next) {
+
+                        printf("\t\\\\%-15s\t\t%s\n", 
+                              sv->name, sv->comment);
+
+                        /* List shares */
+
+                        free_name_list(shares);
+                        shares = NULL;
+
+                        if (level == LEV_SERVER ||
+                            !get_shares(sv->name, user_info))
+                                continue;
+
+                        for (sh = shares; sh; sh = sh->next) {
+                                printf("\t\t\\\\%s\\%-15s\t%s\n", 
+                                      sv->name, sh->name, sh->comment);
+                        }
+                }
+        }
+
+        return True;
+}
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       int opt;
+       char *p;
+       struct user_auth_info user_info;
+       BOOL got_pass = False;
+
+       /* Initialise samba stuff */
+
+       setlinebuf(stdout);
+
+       dbf = x_stderr;
+
+       setup_logging(argv[0],True);
+
+       lp_load(dyn_CONFIGFILE,True,False,False);
+       load_interfaces();
+
+       if (getenv("USER")) {
+               pstrcpy(user_info.username, getenv("USER"));
+
+               if ((p=strchr(user_info.username, '%'))) {
+                       *p = 0;
+                       pstrcpy(user_info.password, p+1);
+                       got_pass = True;
+                       memset(strchr(getenv("USER"), '%') + 1, 'X',
+                              strlen(user_info.password));
+               }
+       }
+
+        pstrcpy(user_info.workgroup, lp_workgroup());
+
+       /* Parse command line args */
+
+       while ((opt = getopt(argc, argv, "U:hd:W:DSb")) != EOF) {
+               switch (opt) {
+               case 'U':
+                       pstrcpy(user_info.username,optarg);
+                       p = strchr(user_info.username,'%');
+                       if (p) {
+                               *p = 0;
+                               pstrcpy(user_info.password, p+1);
+                               got_pass = 1;
+                       }
+                       break;
+
+               case 'b':
+                       use_bcast = True;
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(1);
+
+               case 'd':
+                       DEBUGLEVEL = atoi(optarg);
+                       break;
+
+               case 'W':
+                       pstrcpy(user_info.workgroup, optarg);
+                       break;
+
+                case 'D':
+                        level = LEV_WORKGROUP;
+                        break;
+
+                case 'S':
+                        level = LEV_SERVER;
+                        break;
+
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       
+       if (argc > 0) {
+               usage();
+               exit(1);
+       }
+
+       if (!got_pass) {
+               char *pass = getpass("Password: ");
+               if (pass) {
+                       pstrcpy(user_info.password, pass);
+               }
+                got_pass = True;
+       }
+
+       /* Now do our stuff */
+
+        if (!print_tree(&user_info))
+                return 1;
+
+       return 0;
+}
diff --git a/source4/utils/smbw_sample.c b/source4/utils/smbw_sample.c
new file mode 100644 (file)
index 0000000..5cd792d
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static void usage(void)
+{
+       printf("
+smbw_sample - a sample program that uses smbw
+
+smbw_sample <options> path
+
+  options:
+     -W workgroup
+     -l logfile
+     -P prefix
+     -d debuglevel
+     -U username%%password
+     -R resolve order
+
+note that path must start with /smb/
+");
+}
+
+int main(int argc, char *argv[])
+{
+       DIR *dir;
+       struct dirent *dent;
+       int opt;
+       char *p;
+       extern char *optarg;
+       extern int optind;
+       char *path;
+
+       lp_load(dyn_CONFIGFILE,1,0,0);
+       smbw_setup_shared();
+
+       while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+               switch (opt) {
+               case 'W':
+                       smbw_setshared("WORKGROUP", optarg);
+                       break;
+               case 'l':
+                       smbw_setshared("LOGFILE", optarg);
+                       break;
+               case 'P':
+                       smbw_setshared("PREFIX", optarg);
+                       break;
+               case 'd':
+                       smbw_setshared("DEBUG", optarg);
+                       break;
+               case 'U':
+                       p = strchr_m(optarg,'%');
+                       if (p) {
+                               *p=0;
+                               smbw_setshared("PASSWORD",p+1);
+                       }
+                       smbw_setshared("USER", optarg);
+                       break;
+               case 'R':
+                       smbw_setshared("RESOLVE_ORDER",optarg);
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       path = argv[0];
+
+       smbw_init();
+
+       dir = smbw_opendir(path);
+       if (!dir) {
+               printf("failed to open %s\n", path);
+               exit(1);
+       }
+       
+       while ((dent = smbw_readdir(dir))) {
+               printf("%s\n", dent->d_name);
+       }
+       smbw_closedir(dir);
+       return 0;
+}
diff --git a/source4/utils/status.c b/source4/utils/status.c
new file mode 100644 (file)
index 0000000..76b9253
--- /dev/null
@@ -0,0 +1,665 @@
+/* 
+   Unix SMB/CIFS implementation.
+   status reporting
+   Copyright (C) Andrew Tridgell 1994-1998
+   
+   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.
+
+   Revision History:
+
+   12 aug 96: Erik.Devriendt@te6.siemens.be
+   added support for shared memory implementation of share mode locking
+
+   21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe)
+   Added -L (locks only) -S (shares only) flags and code
+
+*/
+
+/*
+ * This program reports current SMB connections
+ */
+
+#include "includes.h"
+
+static pstring Ucrit_username = "";                   /* added by OH */
+static pid_t   Ucrit_pid[100];  /* Ugly !!! */        /* added by OH */
+static int            Ucrit_MaxPid=0;                        /* added by OH */
+static unsigned int   Ucrit_IsActive = 0;                    /* added by OH */
+static int verbose, brief;
+static int            shares_only = 0;            /* Added by RJS */
+static int            locks_only  = 0;            /* Added by RJS */
+static BOOL processes_only=False;
+static int show_brl;
+
+/* we need these because we link to locking*.o */
+ void become_root(void) {}
+ void unbecome_root(void) {}
+
+
+/* added by OH */
+static void Ucrit_addUsername(const char *username)
+{
+       pstrcpy(Ucrit_username, username);
+       if(strlen(Ucrit_username) > 0)
+               Ucrit_IsActive = 1;
+}
+
+static unsigned int Ucrit_checkUsername(const char *username)
+{
+       if ( !Ucrit_IsActive) return 1;
+       if (strcmp(Ucrit_username,username) ==0) return 1;
+       return 0;
+}
+
+static unsigned int Ucrit_checkPid(pid_t pid)
+{
+       int i;
+       if ( !Ucrit_IsActive) return 1;
+       for (i=0;i<Ucrit_MaxPid;i++)
+               if( pid == Ucrit_pid[i] ) return 1;
+       return 0;
+}
+
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+       static int count;
+       if (count==0) {
+               d_printf("Locked files:\n");
+               d_printf("Pid    DenyMode   Access      R/W        Oplock           Name\n");
+               d_printf("--------------------------------------------------------------\n");
+       }
+       count++;
+
+       if (Ucrit_checkPid(e->pid)) {
+          d_printf("%-5d  ",(int)e->pid);
+         switch (GET_DENY_MODE(e->share_mode)) {
+         case DENY_NONE: d_printf("DENY_NONE  "); break;
+         case DENY_ALL:  d_printf("DENY_ALL   "); break;
+         case DENY_DOS:  d_printf("DENY_DOS   "); break;
+         case DENY_READ: d_printf("DENY_READ  "); break;
+         case DENY_WRITE:printf("DENY_WRITE "); break;
+         case DENY_FCB:  d_printf("DENY_FCB "); break;
+         }
+         d_printf("0x%-8x  ",(unsigned int)e->desired_access);
+         switch (e->share_mode&0xF) {
+         case 0: d_printf("RDONLY     "); break;
+         case 1: d_printf("WRONLY     "); break;
+         case 2: d_printf("RDWR       "); break;
+         }
+
+         if((e->op_type & 
+            (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) == 
+             (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+               d_printf("EXCLUSIVE+BATCH ");
+         else if (e->op_type & EXCLUSIVE_OPLOCK)
+               d_printf("EXCLUSIVE       ");
+         else if (e->op_type & BATCH_OPLOCK)
+               d_printf("BATCH           ");
+         else if (e->op_type & LEVEL_II_OPLOCK)
+               d_printf("LEVEL_II        ");
+         else
+               d_printf("NONE            ");
+
+         d_printf(" %s   %s",fname,
+             asctime(LocalTime((time_t *)&e->time.tv_sec)));
+       }
+}
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid, 
+                     enum brl_type lock_type,
+                     br_off start, br_off size)
+{
+       static int count;
+       if (count==0) {
+               d_printf("Byte range locks:\n");
+               d_printf("   Pid     dev:inode  R/W      start        size\n");
+               d_printf("------------------------------------------------\n");
+       }
+       count++;
+
+       d_printf("%6d   %05x:%05x    %s  %9.0f   %9.0f\n", 
+              (int)pid, (int)dev, (int)ino, 
+              lock_type==READ_LOCK?"R":"W",
+              (double)start, (double)size);
+}
+
+
+/*******************************************************************
+ dump the elements of the profile structure
+  ******************************************************************/
+static int profile_dump(void)
+{
+#ifdef WITH_PROFILE
+       if (!profile_setup(True)) {
+               fprintf(stderr,"Failed to initialise profile memory\n");
+               return -1;
+       }
+
+       d_printf("smb_count:                      %u\n", profile_p->smb_count);
+       d_printf("uid_changes:                    %u\n", profile_p->uid_changes);
+       d_printf("************************ System Calls ****************************\n");
+       d_printf("opendir_count:                  %u\n", profile_p->syscall_opendir_count);
+       d_printf("opendir_time:                   %u\n", profile_p->syscall_opendir_time);
+       d_printf("readdir_count:                  %u\n", profile_p->syscall_readdir_count);
+       d_printf("readdir_time:                   %u\n", profile_p->syscall_readdir_time);
+       d_printf("mkdir_count:                    %u\n", profile_p->syscall_mkdir_count);
+       d_printf("mkdir_time:                     %u\n", profile_p->syscall_mkdir_time);
+       d_printf("rmdir_count:                    %u\n", profile_p->syscall_rmdir_count);
+       d_printf("rmdir_time:                     %u\n", profile_p->syscall_rmdir_time);
+       d_printf("closedir_count:                 %u\n", profile_p->syscall_closedir_count);
+       d_printf("closedir_time:                  %u\n", profile_p->syscall_closedir_time);
+       d_printf("open_count:                     %u\n", profile_p->syscall_open_count);
+       d_printf("open_time:                      %u\n", profile_p->syscall_open_time);
+       d_printf("close_count:                    %u\n", profile_p->syscall_close_count);
+       d_printf("close_time:                     %u\n", profile_p->syscall_close_time);
+       d_printf("read_count:                     %u\n", profile_p->syscall_read_count);
+       d_printf("read_time:                      %u\n", profile_p->syscall_read_time);
+       d_printf("read_bytes:                     %u\n", profile_p->syscall_read_bytes);
+       d_printf("write_count:                    %u\n", profile_p->syscall_write_count);
+       d_printf("write_time:                     %u\n", profile_p->syscall_write_time);
+       d_printf("write_bytes:                    %u\n", profile_p->syscall_write_bytes);
+#ifdef WITH_SENDFILE
+       d_printf("sendfile_count:                 %u\n", profile_p->syscall_sendfile_count);
+       d_printf("sendfile_time:                  %u\n", profile_p->syscall_sendfile_time);
+       d_printf("sendfile_bytes:                 %u\n", profile_p->syscall_sendfile_bytes);
+#endif
+       d_printf("lseek_count:                    %u\n", profile_p->syscall_lseek_count);
+       d_printf("lseek_time:                     %u\n", profile_p->syscall_lseek_time);
+       d_printf("rename_count:                   %u\n", profile_p->syscall_rename_count);
+       d_printf("rename_time:                    %u\n", profile_p->syscall_rename_time);
+       d_printf("fsync_count:                    %u\n", profile_p->syscall_fsync_count);
+       d_printf("fsync_time:                     %u\n", profile_p->syscall_fsync_time);
+       d_printf("stat_count:                     %u\n", profile_p->syscall_stat_count);
+       d_printf("stat_time:                      %u\n", profile_p->syscall_stat_time);
+       d_printf("fstat_count:                    %u\n", profile_p->syscall_fstat_count);
+       d_printf("fstat_time:                     %u\n", profile_p->syscall_fstat_time);
+       d_printf("lstat_count:                    %u\n", profile_p->syscall_lstat_count);
+       d_printf("lstat_time:                     %u\n", profile_p->syscall_lstat_time);
+       d_printf("unlink_count:                   %u\n", profile_p->syscall_unlink_count);
+       d_printf("unlink_time:                    %u\n", profile_p->syscall_unlink_time);
+       d_printf("chmod_count:                    %u\n", profile_p->syscall_chmod_count);
+       d_printf("chmod_time:                     %u\n", profile_p->syscall_chmod_time);
+       d_printf("fchmod_count:                   %u\n", profile_p->syscall_fchmod_count);
+       d_printf("fchmod_time:                    %u\n", profile_p->syscall_fchmod_time);
+       d_printf("chown_count:                    %u\n", profile_p->syscall_chown_count);
+       d_printf("chown_time:                     %u\n", profile_p->syscall_chown_time);
+       d_printf("fchown_count:                   %u\n", profile_p->syscall_fchown_count);
+       d_printf("fchown_time:                    %u\n", profile_p->syscall_fchown_time);
+       d_printf("chdir_count:                    %u\n", profile_p->syscall_chdir_count);
+       d_printf("chdir_time:                     %u\n", profile_p->syscall_chdir_time);
+       d_printf("getwd_count:                    %u\n", profile_p->syscall_getwd_count);
+       d_printf("getwd_time:                     %u\n", profile_p->syscall_getwd_time);
+       d_printf("utime_count:                    %u\n", profile_p->syscall_utime_count);
+       d_printf("utime_time:                     %u\n", profile_p->syscall_utime_time);
+       d_printf("ftruncate_count:                %u\n", profile_p->syscall_ftruncate_count);
+       d_printf("ftruncate_time:                 %u\n", profile_p->syscall_ftruncate_time);
+       d_printf("fcntl_lock_count:               %u\n", profile_p->syscall_fcntl_lock_count);
+       d_printf("fcntl_lock_time:                %u\n", profile_p->syscall_fcntl_lock_time);
+       d_printf("readlink_count:                 %u\n", profile_p->syscall_readlink_count);
+       d_printf("readlink_time:                  %u\n", profile_p->syscall_readlink_time);
+       d_printf("symlink_count:                  %u\n", profile_p->syscall_symlink_count);
+       d_printf("symlink_time:                   %u\n", profile_p->syscall_symlink_time);
+       d_printf("************************ Statcache *******************************\n");
+       d_printf("lookups:                        %u\n", profile_p->statcache_lookups);
+       d_printf("misses:                         %u\n", profile_p->statcache_misses);
+       d_printf("hits:                           %u\n", profile_p->statcache_hits);
+       d_printf("************************ Writecache ******************************\n");
+       d_printf("read_hits:                      %u\n", profile_p->writecache_read_hits);
+       d_printf("abutted_writes:                 %u\n", profile_p->writecache_abutted_writes);
+       d_printf("total_writes:                   %u\n", profile_p->writecache_total_writes);
+       d_printf("non_oplock_writes:              %u\n", profile_p->writecache_non_oplock_writes);
+       d_printf("direct_writes:                  %u\n", profile_p->writecache_direct_writes);
+       d_printf("init_writes:                    %u\n", profile_p->writecache_init_writes);
+       d_printf("flushed_writes[SEEK]:           %u\n", profile_p->writecache_flushed_writes[SEEK_FLUSH]);
+       d_printf("flushed_writes[READ]:           %u\n", profile_p->writecache_flushed_writes[READ_FLUSH]);
+       d_printf("flushed_writes[WRITE]:          %u\n", profile_p->writecache_flushed_writes[WRITE_FLUSH]);
+       d_printf("flushed_writes[READRAW]:        %u\n", profile_p->writecache_flushed_writes[READRAW_FLUSH]);
+       d_printf("flushed_writes[OPLOCK_RELEASE]: %u\n", profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH]);
+       d_printf("flushed_writes[CLOSE]:          %u\n", profile_p->writecache_flushed_writes[CLOSE_FLUSH]);
+       d_printf("flushed_writes[SYNC]:           %u\n", profile_p->writecache_flushed_writes[SYNC_FLUSH]);
+       d_printf("flushed_writes[SIZECHANGE]:     %u\n", profile_p->writecache_flushed_writes[SIZECHANGE_FLUSH]);
+       d_printf("num_perfect_writes:             %u\n", profile_p->writecache_num_perfect_writes);
+       d_printf("num_write_caches:               %u\n", profile_p->writecache_num_write_caches);
+       d_printf("allocated_write_caches:         %u\n", profile_p->writecache_allocated_write_caches);
+       d_printf("************************ SMB Calls *******************************\n");
+       d_printf("mkdir_count:                    %u\n", profile_p->SMBmkdir_count);
+       d_printf("mkdir_time:                     %u\n", profile_p->SMBmkdir_time);
+       d_printf("rmdir_count:                    %u\n", profile_p->SMBrmdir_count);
+       d_printf("rmdir_time:                     %u\n", profile_p->SMBrmdir_time);
+       d_printf("open_count:                     %u\n", profile_p->SMBopen_count);
+       d_printf("open_time:                      %u\n", profile_p->SMBopen_time);
+       d_printf("create_count:                   %u\n", profile_p->SMBcreate_count);
+       d_printf("create_time:                    %u\n", profile_p->SMBcreate_time);
+       d_printf("close_count:                    %u\n", profile_p->SMBclose_count);
+       d_printf("close_time:                     %u\n", profile_p->SMBclose_time);
+       d_printf("flush_count:                    %u\n", profile_p->SMBflush_count);
+       d_printf("flush_time:                     %u\n", profile_p->SMBflush_time);
+       d_printf("unlink_count:                   %u\n", profile_p->SMBunlink_count);
+       d_printf("unlink_time:                    %u\n", profile_p->SMBunlink_time);
+       d_printf("mv_count:                       %u\n", profile_p->SMBmv_count);
+       d_printf("mv_time:                        %u\n", profile_p->SMBmv_time);
+       d_printf("getatr_count:                   %u\n", profile_p->SMBgetatr_count);
+       d_printf("getatr_time:                    %u\n", profile_p->SMBgetatr_time);
+       d_printf("setatr_count:                   %u\n", profile_p->SMBsetatr_count);
+       d_printf("setatr_time:                    %u\n", profile_p->SMBsetatr_time);
+       d_printf("read_count:                     %u\n", profile_p->SMBread_count);
+       d_printf("read_time:                      %u\n", profile_p->SMBread_time);
+       d_printf("write_count:                    %u\n", profile_p->SMBwrite_count);
+       d_printf("write_time:                     %u\n", profile_p->SMBwrite_time);
+       d_printf("lock_count:                     %u\n", profile_p->SMBlock_count);
+       d_printf("lock_time:                      %u\n", profile_p->SMBlock_time);
+       d_printf("unlock_count:                   %u\n", profile_p->SMBunlock_count);
+       d_printf("unlock_time:                    %u\n", profile_p->SMBunlock_time);
+       d_printf("ctemp_count:                    %u\n", profile_p->SMBctemp_count);
+       d_printf("ctemp_time:                     %u\n", profile_p->SMBctemp_time);
+       d_printf("mknew_count:                    %u\n", profile_p->SMBmknew_count);
+       d_printf("mknew_time:                     %u\n", profile_p->SMBmknew_time);
+       d_printf("chkpth_count:                   %u\n", profile_p->SMBchkpth_count);
+       d_printf("chkpth_time:                    %u\n", profile_p->SMBchkpth_time);
+       d_printf("exit_count:                     %u\n", profile_p->SMBexit_count);
+       d_printf("exit_time:                      %u\n", profile_p->SMBexit_time);
+       d_printf("lseek_count:                    %u\n", profile_p->SMBlseek_count);
+       d_printf("lseek_time:                     %u\n", profile_p->SMBlseek_time);
+       d_printf("lockread_count:                 %u\n", profile_p->SMBlockread_count);
+       d_printf("lockread_time:                  %u\n", profile_p->SMBlockread_time);
+       d_printf("writeunlock_count:              %u\n", profile_p->SMBwriteunlock_count);
+       d_printf("writeunlock_time:               %u\n", profile_p->SMBwriteunlock_time);
+       d_printf("readbraw_count:                 %u\n", profile_p->SMBreadbraw_count);
+       d_printf("readbraw_time:                  %u\n", profile_p->SMBreadbraw_time);
+       d_printf("readBmpx_count:                 %u\n", profile_p->SMBreadBmpx_count);
+       d_printf("readBmpx_time:                  %u\n", profile_p->SMBreadBmpx_time);
+       d_printf("readBs_count:                   %u\n", profile_p->SMBreadBs_count);
+       d_printf("readBs_time:                    %u\n", profile_p->SMBreadBs_time);
+       d_printf("writebraw_count:                %u\n", profile_p->SMBwritebraw_count);
+       d_printf("writebraw_time:                 %u\n", profile_p->SMBwritebraw_time);
+       d_printf("writeBmpx_count:                %u\n", profile_p->SMBwriteBmpx_count);
+       d_printf("writeBmpx_time:                 %u\n", profile_p->SMBwriteBmpx_time);
+       d_printf("writeBs_count:                  %u\n", profile_p->SMBwriteBs_count);
+       d_printf("writeBs_time:                   %u\n", profile_p->SMBwriteBs_time);
+       d_printf("writec_count:                   %u\n", profile_p->SMBwritec_count);
+       d_printf("writec_time:                    %u\n", profile_p->SMBwritec_time);
+       d_printf("setattrE_count:                 %u\n", profile_p->SMBsetattrE_count);
+       d_printf("setattrE_time:                  %u\n", profile_p->SMBsetattrE_time);
+       d_printf("getattrE_count:                 %u\n", profile_p->SMBgetattrE_count);
+       d_printf("getattrE_time:                  %u\n", profile_p->SMBgetattrE_time);
+       d_printf("lockingX_count:                 %u\n", profile_p->SMBlockingX_count);
+       d_printf("lockingX_time:                  %u\n", profile_p->SMBlockingX_time);
+       d_printf("trans_count:                    %u\n", profile_p->SMBtrans_count);
+       d_printf("trans_time:                     %u\n", profile_p->SMBtrans_time);
+       d_printf("transs_count:                   %u\n", profile_p->SMBtranss_count);
+       d_printf("transs_time:                    %u\n", profile_p->SMBtranss_time);
+       d_printf("ioctl_count:                    %u\n", profile_p->SMBioctl_count);
+       d_printf("ioctl_time:                     %u\n", profile_p->SMBioctl_time);
+       d_printf("ioctls_count:                   %u\n", profile_p->SMBioctls_count);
+       d_printf("ioctls_time:                    %u\n", profile_p->SMBioctls_time);
+       d_printf("copy_count:                     %u\n", profile_p->SMBcopy_count);
+       d_printf("copy_time:                      %u\n", profile_p->SMBcopy_time);
+       d_printf("move_count:                     %u\n", profile_p->SMBmove_count);
+       d_printf("move_time:                      %u\n", profile_p->SMBmove_time);
+       d_printf("echo_count:                     %u\n", profile_p->SMBecho_count);
+       d_printf("echo_time:                      %u\n", profile_p->SMBecho_time);
+       d_printf("writeclose_count:               %u\n", profile_p->SMBwriteclose_count);
+       d_printf("writeclose_time:                %u\n", profile_p->SMBwriteclose_time);
+       d_printf("openX_count:                    %u\n", profile_p->SMBopenX_count);
+       d_printf("openX_time:                     %u\n", profile_p->SMBopenX_time);
+       d_printf("readX_count:                    %u\n", profile_p->SMBreadX_count);
+       d_printf("readX_time:                     %u\n", profile_p->SMBreadX_time);
+       d_printf("writeX_count:                   %u\n", profile_p->SMBwriteX_count);
+       d_printf("writeX_time:                    %u\n", profile_p->SMBwriteX_time);
+       d_printf("trans2_count:                   %u\n", profile_p->SMBtrans2_count);
+       d_printf("trans2_time:                    %u\n", profile_p->SMBtrans2_time);
+       d_printf("transs2_count:                  %u\n", profile_p->SMBtranss2_count);
+       d_printf("transs2_time:                   %u\n", profile_p->SMBtranss2_time);
+       d_printf("findclose_count:                %u\n", profile_p->SMBfindclose_count);
+       d_printf("findclose_time:                 %u\n", profile_p->SMBfindclose_time);
+       d_printf("findnclose_count:               %u\n", profile_p->SMBfindnclose_count);
+       d_printf("findnclose_time:                %u\n", profile_p->SMBfindnclose_time);
+       d_printf("tcon_count:                     %u\n", profile_p->SMBtcon_count);
+       d_printf("tcon_time:                      %u\n", profile_p->SMBtcon_time);
+       d_printf("tdis_count:                     %u\n", profile_p->SMBtdis_count);
+       d_printf("tdis_time:                      %u\n", profile_p->SMBtdis_time);
+       d_printf("negprot_count:                  %u\n", profile_p->SMBnegprot_count);
+       d_printf("negprot_time:                   %u\n", profile_p->SMBnegprot_time);
+       d_printf("sesssetupX_count:               %u\n", profile_p->SMBsesssetupX_count);
+       d_printf("sesssetupX_time:                %u\n", profile_p->SMBsesssetupX_time);
+       d_printf("ulogoffX_count:                 %u\n", profile_p->SMBulogoffX_count);
+       d_printf("ulogoffX_time:                  %u\n", profile_p->SMBulogoffX_time);
+       d_printf("tconX_count:                    %u\n", profile_p->SMBtconX_count);
+       d_printf("tconX_time:                     %u\n", profile_p->SMBtconX_time);
+       d_printf("dskattr_count:                  %u\n", profile_p->SMBdskattr_count);
+       d_printf("dskattr_time:                   %u\n", profile_p->SMBdskattr_time);
+       d_printf("search_count:                   %u\n", profile_p->SMBsearch_count);
+       d_printf("search_time:                    %u\n", profile_p->SMBsearch_time);
+       d_printf("ffirst_count:                   %u\n", profile_p->SMBffirst_count);
+       d_printf("ffirst_time:                    %u\n", profile_p->SMBffirst_time);
+       d_printf("funique_count:                  %u\n", profile_p->SMBfunique_count);
+       d_printf("funique_time:                   %u\n", profile_p->SMBfunique_time);
+       d_printf("fclose_count:                   %u\n", profile_p->SMBfclose_count);
+       d_printf("fclose_time:                    %u\n", profile_p->SMBfclose_time);
+       d_printf("nttrans_count:                  %u\n", profile_p->SMBnttrans_count);
+       d_printf("nttrans_time:                   %u\n", profile_p->SMBnttrans_time);
+       d_printf("nttranss_count:                 %u\n", profile_p->SMBnttranss_count);
+       d_printf("nttranss_time:                  %u\n", profile_p->SMBnttranss_time);
+       d_printf("ntcreateX_count:                %u\n", profile_p->SMBntcreateX_count);
+       d_printf("ntcreateX_time:                 %u\n", profile_p->SMBntcreateX_time);
+       d_printf("ntcancel_count:                 %u\n", profile_p->SMBntcancel_count);
+       d_printf("ntcancel_time:                  %u\n", profile_p->SMBntcancel_time);
+       d_printf("splopen_count:                  %u\n", profile_p->SMBsplopen_count);
+       d_printf("splopen_time:                   %u\n", profile_p->SMBsplopen_time);
+       d_printf("splwr_count:                    %u\n", profile_p->SMBsplwr_count);
+       d_printf("splwr_time:                     %u\n", profile_p->SMBsplwr_time);
+       d_printf("splclose_count:                 %u\n", profile_p->SMBsplclose_count);
+       d_printf("splclose_time:                  %u\n", profile_p->SMBsplclose_time);
+       d_printf("splretq_count:                  %u\n", profile_p->SMBsplretq_count);
+       d_printf("splretq_time:                   %u\n", profile_p->SMBsplretq_time);
+       d_printf("sends_count:                    %u\n", profile_p->SMBsends_count);
+       d_printf("sends_time:                     %u\n", profile_p->SMBsends_time);
+       d_printf("sendb_count:                    %u\n", profile_p->SMBsendb_count);
+       d_printf("sendb_time:                     %u\n", profile_p->SMBsendb_time);
+       d_printf("fwdname_count:                  %u\n", profile_p->SMBfwdname_count);
+       d_printf("fwdname_time:                   %u\n", profile_p->SMBfwdname_time);
+       d_printf("cancelf_count:                  %u\n", profile_p->SMBcancelf_count);
+       d_printf("cancelf_time:                   %u\n", profile_p->SMBcancelf_time);
+       d_printf("getmac_count:                   %u\n", profile_p->SMBgetmac_count);
+       d_printf("getmac_time:                    %u\n", profile_p->SMBgetmac_time);
+       d_printf("sendstrt_count:                 %u\n", profile_p->SMBsendstrt_count);
+       d_printf("sendstrt_time:                  %u\n", profile_p->SMBsendstrt_time);
+       d_printf("sendend_count:                  %u\n", profile_p->SMBsendend_count);
+       d_printf("sendend_time:                   %u\n", profile_p->SMBsendend_time);
+       d_printf("sendtxt_count:                  %u\n", profile_p->SMBsendtxt_count);
+       d_printf("sendtxt_time:                   %u\n", profile_p->SMBsendtxt_time);
+       d_printf("invalid_count:                  %u\n", profile_p->SMBinvalid_count);
+       d_printf("invalid_time:                   %u\n", profile_p->SMBinvalid_time);
+       d_printf("************************ Pathworks Calls *************************\n");
+       d_printf("setdir_count:                   %u\n", profile_p->pathworks_setdir_count);
+       d_printf("setdir_time:                    %u\n", profile_p->pathworks_setdir_time);
+       d_printf("************************ Trans2 Calls ****************************\n");
+       d_printf("open_count:                     %u\n", profile_p->Trans2_open_count);
+       d_printf("open_time:                      %u\n", profile_p->Trans2_open_time);
+       d_printf("findfirst_count:                %u\n", profile_p->Trans2_findfirst_count);
+       d_printf("findfirst_time:                 %u\n", profile_p->Trans2_findfirst_time);
+       d_printf("findnext_count:                 %u\n", profile_p->Trans2_findnext_count);
+       d_printf("findnext_time:                  %u\n", profile_p->Trans2_findnext_time);
+       d_printf("qfsinfo_count:                  %u\n", profile_p->Trans2_qfsinfo_count);
+       d_printf("qfsinfo_time:                   %u\n", profile_p->Trans2_qfsinfo_time);
+       d_printf("setfsinfo_count:                %u\n", profile_p->Trans2_setfsinfo_count);
+       d_printf("setfsinfo_time:                 %u\n", profile_p->Trans2_setfsinfo_time);
+       d_printf("qpathinfo_count:                %u\n", profile_p->Trans2_qpathinfo_count);
+       d_printf("qpathinfo_time:                 %u\n", profile_p->Trans2_qpathinfo_time);
+       d_printf("setpathinfo_count:              %u\n", profile_p->Trans2_setpathinfo_count);
+       d_printf("setpathinfo_time:               %u\n", profile_p->Trans2_setpathinfo_time);
+       d_printf("qfileinfo_count:                %u\n", profile_p->Trans2_qfileinfo_count);
+       d_printf("qfileinfo_time:                 %u\n", profile_p->Trans2_qfileinfo_time);
+       d_printf("setfileinfo_count:              %u\n", profile_p->Trans2_setfileinfo_count);
+       d_printf("setfileinfo_time:               %u\n", profile_p->Trans2_setfileinfo_time);
+       d_printf("fsctl_count:                    %u\n", profile_p->Trans2_fsctl_count);
+       d_printf("fsctl_time:                     %u\n", profile_p->Trans2_fsctl_time);
+       d_printf("ioctl_count:                    %u\n", profile_p->Trans2_ioctl_count);
+       d_printf("ioctl_time:                     %u\n", profile_p->Trans2_ioctl_time);
+       d_printf("findnotifyfirst_count:          %u\n", profile_p->Trans2_findnotifyfirst_count);
+       d_printf("findnotifyfirst_time:           %u\n", profile_p->Trans2_findnotifyfirst_time);
+       d_printf("findnotifynext_count:           %u\n", profile_p->Trans2_findnotifynext_count);
+       d_printf("findnotifynext_time:            %u\n", profile_p->Trans2_findnotifynext_time);
+       d_printf("mkdir_count:                    %u\n", profile_p->Trans2_mkdir_count);
+       d_printf("mkdir_time:                     %u\n", profile_p->Trans2_mkdir_time);
+       d_printf("session_setup_count:            %u\n", profile_p->Trans2_session_setup_count);
+       d_printf("session_setup_time:             %u\n", profile_p->Trans2_session_setup_time);
+       d_printf("get_dfs_referral_count:         %u\n", profile_p->Trans2_get_dfs_referral_count);
+       d_printf("get_dfs_referral_time:          %u\n", profile_p->Trans2_get_dfs_referral_time);
+       d_printf("report_dfs_inconsistancy_count: %u\n", profile_p->Trans2_report_dfs_inconsistancy_count);
+       d_printf("report_dfs_inconsistancy_time:  %u\n", profile_p->Trans2_report_dfs_inconsistancy_time);
+       d_printf("************************ NT Transact Calls ***********************\n");
+       d_printf("create_count:                   %u\n", profile_p->NT_transact_create_count);
+       d_printf("create_time:                    %u\n", profile_p->NT_transact_create_time);
+       d_printf("ioctl_count:                    %u\n", profile_p->NT_transact_ioctl_count);
+       d_printf("ioctl_time:                     %u\n", profile_p->NT_transact_ioctl_time);
+       d_printf("set_security_desc_count:        %u\n", profile_p->NT_transact_set_security_desc_count);
+       d_printf("set_security_desc_time:         %u\n", profile_p->NT_transact_set_security_desc_time);
+       d_printf("notify_change_count:            %u\n", profile_p->NT_transact_notify_change_count);
+       d_printf("notify_change_time:             %u\n", profile_p->NT_transact_notify_change_time);
+       d_printf("rename_count:                   %u\n", profile_p->NT_transact_rename_count);
+       d_printf("rename_time:                    %u\n", profile_p->NT_transact_rename_time);
+       d_printf("query_security_desc_count:      %u\n", profile_p->NT_transact_query_security_desc_count);
+       d_printf("query_security_desc_time:       %u\n", profile_p->NT_transact_query_security_desc_time);
+       d_printf("************************ ACL Calls *******************************\n");
+       d_printf("get_nt_acl_count:               %u\n", profile_p->get_nt_acl_count);
+       d_printf("get_nt_acl_time:                %u\n", profile_p->get_nt_acl_time);
+       d_printf("fget_nt_acl_count:              %u\n", profile_p->fget_nt_acl_count);
+       d_printf("fget_nt_acl_time:               %u\n", profile_p->fget_nt_acl_time);
+       d_printf("set_nt_acl_count:               %u\n", profile_p->set_nt_acl_count);
+       d_printf("set_nt_acl_time:                %u\n", profile_p->set_nt_acl_time);
+       d_printf("fset_nt_acl_count:              %u\n", profile_p->fset_nt_acl_count);
+       d_printf("fset_nt_acl_time:               %u\n", profile_p->fset_nt_acl_time);
+       d_printf("chmod_acl_count:                %u\n", profile_p->chmod_acl_count);
+       d_printf("chmod_acl_time:                 %u\n", profile_p->chmod_acl_time);
+       d_printf("fchmod_acl_count:               %u\n", profile_p->fchmod_acl_count);
+       d_printf("fchmod_acl_time:                %u\n", profile_p->fchmod_acl_time);
+       d_printf("************************ NMBD Calls ****************************\n");
+       d_printf("name_release_count:             %u\n", profile_p->name_release_count);
+       d_printf("name_release_time:              %u\n", profile_p->name_release_time);
+       d_printf("name_refresh_count:             %u\n", profile_p->name_refresh_count);
+       d_printf("name_refresh_time:              %u\n", profile_p->name_refresh_time);
+       d_printf("name_registration_count:        %u\n", profile_p->name_registration_count);
+       d_printf("name_registration_time:         %u\n", profile_p->name_registration_time);
+       d_printf("node_status_count:              %u\n", profile_p->node_status_count);
+       d_printf("node_status_time:               %u\n", profile_p->node_status_time);
+       d_printf("name_query_count:               %u\n", profile_p->name_query_count);
+       d_printf("name_query_time:                %u\n", profile_p->name_query_time);
+       d_printf("host_announce_count:            %u\n", profile_p->host_announce_count);
+       d_printf("host_announce_time:             %u\n", profile_p->host_announce_time);
+       d_printf("workgroup_announce_count:       %u\n", profile_p->workgroup_announce_count);
+       d_printf("workgroup_announce_time:        %u\n", profile_p->workgroup_announce_time);
+       d_printf("local_master_announce_count:    %u\n", profile_p->local_master_announce_count);
+       d_printf("local_master_announce_time:     %u\n", profile_p->local_master_announce_time);
+       d_printf("master_browser_announce_count:  %u\n", profile_p->master_browser_announce_count);
+       d_printf("master_browser_announce_time:   %u\n", profile_p->master_browser_announce_time);
+       d_printf("lm_host_announce_count:         %u\n", profile_p->lm_host_announce_count);
+       d_printf("lm_host_announce_time:          %u\n", profile_p->lm_host_announce_time);
+       d_printf("get_backup_list_count:          %u\n", profile_p->get_backup_list_count);
+       d_printf("get_backup_list_time:           %u\n", profile_p->get_backup_list_time);
+       d_printf("reset_browser_count:            %u\n", profile_p->reset_browser_count);
+       d_printf("reset_browser_time:             %u\n", profile_p->reset_browser_time);
+       d_printf("announce_request_count:         %u\n", profile_p->announce_request_count);
+       d_printf("announce_request_time:          %u\n", profile_p->announce_request_time);
+       d_printf("lm_announce_request_count:      %u\n", profile_p->lm_announce_request_count);
+       d_printf("lm_announce_request_time:       %u\n", profile_p->lm_announce_request_time);
+       d_printf("domain_logon_count:             %u\n", profile_p->domain_logon_count);
+       d_printf("domain_logon_time:              %u\n", profile_p->domain_logon_time);
+       d_printf("sync_browse_lists_count:        %u\n", profile_p->sync_browse_lists_count);
+       d_printf("sync_browse_lists_time:         %u\n", profile_p->sync_browse_lists_time);
+       d_printf("run_elections_count:            %u\n", profile_p->run_elections_count);
+       d_printf("run_elections_time:             %u\n", profile_p->run_elections_time);
+       d_printf("election_count:                 %u\n", profile_p->election_count);
+       d_printf("election_time:                  %u\n", profile_p->election_time);
+#else /* WITH_PROFILE */
+       fprintf(stderr, "Profile data unavailable\n");
+#endif /* WITH_PROFILE */
+
+       return 0;
+}
+
+
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct connections_data crec;
+
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+       if (crec.cnum == -1)
+               return 0;
+
+       if (!process_exists(crec.pid) || !Ucrit_checkUsername(uidtoname(crec.uid))) {
+               return 0;
+       }
+
+       d_printf("%-10.10s   %5d   %-12s  %s",
+              crec.name,(int)crec.pid,
+              crec.machine,
+              asctime(LocalTime(&crec.start)));
+
+       return 0;
+}
+
+static int traverse_sessionid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+       struct sessionid sessionid;
+       TALLOC_CTX *mem_ctx;
+
+       if (dbuf.dsize != sizeof(sessionid))
+               return 0;
+
+       memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+       if (!process_exists(sessionid.pid) || !Ucrit_checkUsername(uidtoname(sessionid.uid))) {
+               return 0;
+       }
+
+       mem_ctx = talloc_init("smbgroupedit talloc");
+       if (!mem_ctx) return -1;
+       d_printf("%5d   %-12s  %-12s  %-12s (%s)\n",
+              (int)sessionid.pid, uidtoname(sessionid.uid), 
+              gidtoname(mem_ctx, sessionid.gid), 
+              sessionid.remote_machine, sessionid.hostname);
+       talloc_destroy(mem_ctx);
+       return 0;
+}
+
+
+
+
+ int main(int argc, char *argv[])
+{
+       int c;
+       static int profile_only = 0;
+       TDB_CONTEXT *tdb;
+       poptContext pc;
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               {"processes",   'p', POPT_ARG_NONE,     &processes_only, 'p', "Show processes only" },
+               {"verbose",     'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+               {"locks",       'L', POPT_ARG_NONE,     &locks_only, 'L', "Show locks only" },
+               {"shares",      'S', POPT_ARG_NONE,     &shares_only, 'S', "Show shares only" },
+               {"user", 'u', POPT_ARG_STRING,  0, 'u', "Switch to user" },
+               {"brief",       'b', POPT_ARG_NONE,     &brief, 'b', "Be brief" },
+#ifdef WITH_PROFILE
+               {"profile",     'P', POPT_ARG_NONE,     &profile_only, 'P', "Do profiling" },
+#endif /* WITH_PROFILE */
+               {"byterange",   'B', POPT_ARG_NONE,     &show_brl, 'B', "Include byte range locks"},
+               POPT_COMMON_SAMBA
+               POPT_COMMON_CONNECTION
+               POPT_COMMON_CREDENTIALS
+               POPT_TABLEEND
+       };
+
+       setup_logging(argv[0],True);
+       
+       dbf = x_stderr;
+       
+       if (getuid() != geteuid()) {
+               d_printf("smbstatus should not be run setuid\n");
+               return(1);
+       }
+
+       pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+       
+       while ((c = poptGetNextOpt(pc)) != EOF) {
+               switch (c) {
+               case 'u':                                      
+                       Ucrit_addUsername(poptGetOptArg(pc));             
+                       break;
+               }
+       }
+
+       if (verbose) {
+               d_printf("using configfile = %s\n", dyn_CONFIGFILE);
+       }
+
+       if (!lp_load(dyn_CONFIGFILE,False,False,False)) {
+               fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+               return (-1);
+       }
+       
+       if (profile_only) {
+               return profile_dump();
+       }
+       
+       tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+       if (!tdb) {
+               d_printf("sessionid.tdb not initialised\n");
+       } else {
+               if (locks_only) goto locks;
+
+               d_printf("\nSamba version %s\n",SAMBA_VERSION);
+               d_printf("PID     Username      Group         Machine                        \n");
+               d_printf("-------------------------------------------------------------------\n");
+
+               tdb_traverse(tdb, traverse_sessionid, NULL);
+               tdb_close(tdb);
+       }
+  
+       tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+       if (!tdb) {
+               d_printf("%s not initialised\n", lock_path("connections.tdb"));
+               d_printf("This is normal if an SMB client has never connected to your server.\n");
+       }  else  {
+               if (verbose) {
+                       d_printf("Opened %s\n", lock_path("connections.tdb"));
+               }
+
+               if (brief) 
+                       exit(0);
+               
+               d_printf("\nService      pid     machine       Connected at\n");
+               d_printf("-------------------------------------------------------\n");
+
+               tdb_traverse(tdb, traverse_fn1, NULL);
+               tdb_close(tdb);
+       }
+
+ locks:
+       if (processes_only) exit(0);
+
+       if (!shares_only) {
+               int ret;
+
+               if (!locking_init(1)) {
+                       d_printf("Can't initialise locking module - exiting\n");
+                       exit(1);
+               }
+               
+               ret = share_mode_forall(print_share_mode);
+
+               if (ret == 0) {
+                       d_printf("No locked files\n");
+               } else if (ret == -1) {
+                       d_printf("locked file list truncated\n");
+               }
+               
+               d_printf("\n");
+
+               if (show_brl) {
+                       brl_forall(print_brl);
+               }
+               
+               locking_end();
+       }
+
+       return (0);
+}
diff --git a/source4/utils/tdb/Makefile b/source4/utils/tdb/Makefile
new file mode 100644 (file)
index 0000000..59fbb07
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Makefile for tdb directory
+#
+
+CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
+CC = gcc
+
+PROGS = tdbtest tdbtool tdbtorture
+TDB_OBJ = tdb.o spinlock.o
+
+default: $(PROGS)
+
+tdbtest: tdbtest.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
+
+tdbtool: tdbtool.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
+
+tdbtorture: tdbtorture.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbdump: tdbdump.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
+
+tdbbackup: tdbbackup.o $(TDB_OBJ)
+       $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
+
+clean:
+       rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
diff --git a/source4/utils/tdb/README b/source4/utils/tdb/README
new file mode 100644 (file)
index 0000000..fac3eac
--- /dev/null
@@ -0,0 +1,167 @@
+tdb - a trivial database system
+tridge@linuxcare.com December 1999
+==================================
+
+This is a simple database API. It was inspired by the realisation that
+in Samba we have several ad-hoc bits of code that essentially
+implement small databases for sharing structures between parts of
+Samba. As I was about to add another I realised that a generic
+database module was called for to replace all the ad-hoc bits.
+
+I based the interface on gdbm. I couldn't use gdbm as we need to be
+able to have multiple writers to the databases at one time.
+
+Compilation
+-----------
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add TDB_DEBUG=1 for verbose debug info
+add NOLOCK=1 to disable locking code
+
+Testing
+-------
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+Interface
+---------
+
+The interface is very similar to gdbm except for the following:
+
+- different open interface. The tdb_open call is more similar to a
+  traditional open()
+- no tdbm_reorganise() function
+- no tdbm_sync() function. No operations are cached in the library anyway
+- added a tdb_traverse() function for traversing the whole database
+
+A general rule for using tdb is that the caller frees any returned
+TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
+return value called p. This is the same as gdbm.
+
+here is a full list of tdb functions with brief descriptions.
+
+
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+
+   open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the database
+   file. A flags value of O_WRONLY is invalid
+
+   The hash size is advisory, use zero for a default value. 
+
+   return is NULL on error
+
+   possible tdb_flags are:
+    TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
+    TDB_INTERNAL - don't use a file, instaed store the data in
+                   memory. The filename is ignored in this case.
+    TDB_NOLOCK - don't do any locking
+    TDB_NOMMAP - don't use mmap
+
+----------------------------------------------------------------------
+char *tdb_error(TDB_CONTEXT *tdb);
+
+     return a error string for the last tdb error
+
+----------------------------------------------------------------------
+int tdb_close(TDB_CONTEXT *tdb);
+
+   close a database
+
+----------------------------------------------------------------------
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
+
+   update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1
+
+----------------------------------------------------------------------
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   fetch an entry in the database given a key 
+   if the return value has a null dptr then a error occurred
+
+   caller must free the resulting data
+
+----------------------------------------------------------------------
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+
+----------------------------------------------------------------------
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
+                 TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
+
+   traverse the entire database - calling fn(tdb, key, data, state) on each 
+   element.
+
+   return -1 on error or the record count traversed
+
+   if fn is NULL then it is not called
+
+   a non-zero return value from fn() indicates that the traversal should stop
+
+----------------------------------------------------------------------
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+
+   find the first entry in the database and return its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   find the next entry in the database, returning its key
+
+   the caller must free the returned data
+
+----------------------------------------------------------------------
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   delete an entry in the database given a key
+
+----------------------------------------------------------------------
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+   store an element in the database, replacing any existing element
+   with the same key 
+
+   If flag==TDB_INSERT then don't overwrite an existing entry
+   If flag==TDB_MODIFY then don't create a new entry
+
+   return 0 on success, -1 on failure
+
+----------------------------------------------------------------------
+int tdb_writelock(TDB_CONTEXT *tdb);
+
+   lock the database. If we already have it locked then don't do anything
+
+----------------------------------------------------------------------
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+   unlock the database
+
+----------------------------------------------------------------------
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   lock one hash chain. This is meant to be used to reduce locking
+   contention - it cannot guarantee how many records will be locked
+
+----------------------------------------------------------------------
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+   unlock one hash chain
diff --git a/source4/utils/tdb/tdb.magic b/source4/utils/tdb/tdb.magic
new file mode 100644 (file)
index 0000000..f5619e7
--- /dev/null
@@ -0,0 +1,10 @@
+# Magic file(1) information about tdb files.
+#
+# Install this into /etc/magic or the corresponding location for your
+# system, or pass as a -m argument to file(1).
+
+# You may use and redistribute this file without restriction.
+
+0      string  TDB\ file               TDB database
+>32    lelong  =0x2601196D             version 6, little-endian
+>>36   lelong  x                       hash size %d bytes
diff --git a/source4/utils/tdb/tdbbackup.c b/source4/utils/tdb/tdbbackup.c
new file mode 100644 (file)
index 0000000..7b344de
--- /dev/null
@@ -0,0 +1,315 @@
+/* 
+   Unix SMB/CIFS implementation.
+   low level tdb backup and restore utility
+   Copyright (C) Andrew Tridgell              2002
+
+   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.
+*/
+
+/*
+
+  This program is meant for backup/restore of tdb databases. Typical usage would be:
+     tdbbackup *.tdb
+  when Samba shuts down cleanly, which will make a backup of all the local databases
+  to *.bak files. Then on Samba startup you would use:
+     tdbbackup -v *.tdb
+  and this will check the databases for corruption and if corruption is detected then
+  the backup will be restored.
+
+  You may also like to do a backup on a regular basis while Samba is
+  running, perhaps using cron.
+
+  The reason this program is needed is to cope with power failures
+  while Samba is running. A power failure could lead to database
+  corruption and Samba will then not start correctly.
+
+  Note that many of the databases in Samba are transient and thus
+  don't need to be backed up, so you can optimise the above a little
+  by only running the backup on the critical databases.
+
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static int failed;
+
+static char *add_suffix(const char *name, const char *suffix)
+{
+       char *ret;
+       int len = strlen(name) + strlen(suffix) + 1;
+       ret = malloc(len);
+       if (!ret) {
+               fprintf(stderr,"Out of memory!\n");
+               exit(1);
+       }
+       strncpy(ret, name, len);
+       strncat(ret, suffix, len);
+       return ret;
+}
+
+static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
+
+       if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
+               fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
+               failed = 1;
+               return 1;
+       }
+       return 0;
+}
+
+
+static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       return 0;
+}
+
+/*
+  carefully backup a tdb, validating the contents and
+  only doing the backup if its OK
+  this function is also used for restore
+*/
+static int backup_tdb(const char *old_name, const char *new_name)
+{
+       TDB_CONTEXT *tdb;
+       TDB_CONTEXT *tdb_new;
+       char *tmp_name;
+       struct stat st;
+       int count1, count2;
+
+       tmp_name = add_suffix(new_name, ".tmp");
+
+       /* stat the old tdb to find its permissions */
+       if (stat(old_name, &st) != 0) {
+               perror(old_name);
+               return 1;
+       }
+
+       /* open the old tdb */
+       tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
+       if (!tdb) {
+               printf("Failed to open %s\n", old_name);
+               return 1;
+       }
+
+       /* create the new tdb */
+       unlink(tmp_name);
+       tdb_new = tdb_open(tmp_name, tdb->header.hash_size, 
+                          TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, 
+                          st.st_mode & 0777);
+       if (!tdb_new) {
+               perror(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* lock the old tdb */
+       if (tdb_lockall(tdb) != 0) {
+               fprintf(stderr,"Failed to lock %s\n", old_name);
+               tdb_close(tdb);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       failed = 0;
+
+       /* traverse and copy */
+       count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
+       if (count1 < 0 || failed) {
+               fprintf(stderr,"failed to copy %s\n", old_name);
+               tdb_close(tdb);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* close the old tdb */
+       tdb_close(tdb);
+
+       /* close the new tdb and re-open read-only */
+       tdb_close(tdb_new);
+       tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
+       if (!tdb_new) {
+               fprintf(stderr,"failed to reopen %s\n", tmp_name);
+               unlink(tmp_name);
+               perror(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+       
+       /* traverse the new tdb to confirm */
+       count2 = tdb_traverse(tdb_new, test_fn, 0);
+       if (count2 != count1) {
+               fprintf(stderr,"failed to copy %s\n", old_name);
+               tdb_close(tdb_new);
+               unlink(tmp_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       /* make sure the new tdb has reached stable storage */
+       fsync(tdb_new->fd);
+
+       /* close the new tdb and rename it to .bak */
+       tdb_close(tdb_new);
+       unlink(new_name);
+       if (rename(tmp_name, new_name) != 0) {
+               perror(new_name);
+               free(tmp_name);
+               return 1;
+       }
+
+       printf("%s : %d records\n", old_name, count1);
+       free(tmp_name);
+
+       return 0;
+}
+
+
+
+/*
+  verify a tdb and if it is corrupt then restore from *.bak
+*/
+static int verify_tdb(const char *fname, const char *bak_name)
+{
+       TDB_CONTEXT *tdb;
+       int count = -1;
+
+       /* open the tdb */
+       tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+
+       /* traverse the tdb, then close it */
+       if (tdb) {
+               count = tdb_traverse(tdb, test_fn, NULL);
+               tdb_close(tdb);
+       }
+
+       /* count is < 0 means an error */
+       if (count < 0) {
+               printf("restoring %s\n", fname);
+               return backup_tdb(bak_name, fname);
+       }
+
+       printf("%s : %d records\n", fname, count);
+
+       return 0;
+}
+
+
+/*
+  see if one file is newer than another
+*/
+static int file_newer(const char *fname1, const char *fname2)
+{
+       struct stat st1, st2;
+       if (stat(fname1, &st1) != 0) {
+               return 0;
+       }
+       if (stat(fname2, &st2) != 0) {
+               return 1;
+       }
+       return (st1.st_mtime > st2.st_mtime);
+}
+
+static void usage(void)
+{
+       printf("Usage: tdbbackup [options] <fname...>\n\n");
+       printf("   -h            this help message\n");
+       printf("   -s suffix     set the backup suffix\n");
+       printf("   -v            veryify mode (restore if corrupt)\n");
+}
+               
+
+ int main(int argc, char *argv[])
+{
+       int i;
+       int ret = 0;
+       int c;
+       int verify = 0;
+       char *suffix = ".bak";
+       extern int optind;
+       extern char *optarg;
+
+       while ((c = getopt(argc, argv, "vhs:")) != -1) {
+               switch (c) {
+               case 'h':
+                       usage();
+                       exit(0);
+               case 'v':
+                       verify = 1;
+                       break;
+               case 's':
+                       suffix = optarg;
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i=0; i<argc; i++) {
+               const char *fname = argv[i];
+               char *bak_name;
+
+               bak_name = add_suffix(fname, suffix);
+
+               if (verify) {
+                       if (verify_tdb(fname, bak_name) != 0) {
+                               ret = 1;
+                       }
+               } else {
+                       if (file_newer(fname, bak_name) &&
+                           backup_tdb(fname, bak_name) != 0) {
+                               ret = 1;
+                       }
+               }
+
+               free(bak_name);
+       }
+
+       return ret;
+}
+
+#ifdef VALGRIND
+size_t valgrind_strlen(const char *s)
+{
+       size_t count;
+       for(count = 0; *s++; count++)
+               ;
+       return count;
+}
+#endif
diff --git a/source4/utils/tdb/tdbdump.c b/source4/utils/tdb/tdbdump.c
new file mode 100644 (file)
index 0000000..9c1dc27
--- /dev/null
@@ -0,0 +1,89 @@
+/* 
+   Unix SMB/CIFS implementation.
+   simple tdb dump util
+   Copyright (C) Andrew Tridgell              2001
+
+   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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static void print_data(TDB_DATA d)
+{
+       unsigned char *p = d.dptr;
+       int len = d.dsize;
+       while (len--) {
+               if (isprint(*p) && !strchr("\"\\", *p)) {
+                       fputc(*p, stdout);
+               } else {
+                       printf("\\%02X", *p);
+               }
+               p++;
+       }
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       printf("{\n");
+       printf("key = \"");
+       print_data(key);
+       printf("\"\n");
+       printf("data = \"");
+       print_data(dbuf);
+       printf("\"\n");
+       printf("}\n");
+       return 0;
+}
+
+static int dump_tdb(const char *fname)
+{
+       TDB_CONTEXT *tdb;
+       
+       tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+       if (!tdb) {
+               printf("Failed to open %s\n", fname);
+               return 1;
+       }
+
+       tdb_traverse(tdb, traverse_fn, NULL);
+       return 0;
+}
+
+ int main(int argc, char *argv[])
+{
+       char *fname;
+
+       if (argc < 2) {
+               printf("Usage: tdbdump <fname>\n");
+               exit(1);
+       }
+
+       fname = argv[1];
+
+       return dump_tdb(fname);
+}
diff --git a/source4/utils/tdb/tdbtest.c b/source4/utils/tdb/tdbtest.c
new file mode 100644 (file)
index 0000000..89295a3
--- /dev/null
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "tdb.h"
+#include <gdbm.h>
+
+/* a test program for tdb - the trivial database */
+
+
+
+#define DELETE_PROB 7
+#define STORE_PROB 5
+
+static TDB_CONTEXT *db;
+static GDBM_FILE gdbm;
+
+struct timeval tp1,tp2;
+
+static void start_timer(void)
+{
+       gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+       gettimeofday(&tp2,NULL);
+       return((tp2.tv_sec - tp1.tv_sec) + 
+              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void fatal(char *why)
+{
+       perror(why);
+       exit(1);
+}
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+    
+       va_start(ap, format);
+       vfprintf(stdout, format, ap);
+       va_end(ap);
+       fflush(stdout);
+}
+
+static void compare_db(void)
+{
+       TDB_DATA d, key, nextkey;
+       datum gd, gkey, gnextkey;
+
+       key = tdb_firstkey(db);
+       while (key.dptr) {
+               d = tdb_fetch(db, key);
+               gkey.dptr = key.dptr;
+               gkey.dsize = key.dsize;
+
+               gd = gdbm_fetch(gdbm, gkey);
+
+               if (!gd.dptr) fatal("key not in gdbm");
+               if (gd.dsize != d.dsize) fatal("data sizes differ");
+               if (memcmp(gd.dptr, d.dptr, d.dsize)) {
+                       fatal("data differs");
+               }
+
+               nextkey = tdb_nextkey(db, key);
+               free(key.dptr);
+               free(d.dptr);
+               free(gd.dptr);
+               key = nextkey;
+       }
+
+       gkey = gdbm_firstkey(gdbm);
+       while (gkey.dptr) {
+               gd = gdbm_fetch(gdbm, gkey);
+               key.dptr = gkey.dptr;
+               key.dsize = gkey.dsize;
+
+               d = tdb_fetch(db, key);
+
+               if (!d.dptr) fatal("key not in db");
+               if (d.dsize != gd.dsize) fatal("data sizes differ");
+               if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
+                       fatal("data differs");
+               }
+
+               gnextkey = gdbm_nextkey(gdbm, gkey);
+               free(gkey.dptr);
+               free(gd.dptr);
+               free(d.dptr);
+               gkey = gnextkey;
+       }
+}
+
+static char *randbuf(int len)
+{
+       char *buf;
+       int i;
+       buf = (char *)malloc(len+1);
+
+       for (i=0;i<len;i++) {
+               buf[i] = 'a' + (rand() % 26);
+       }
+       buf[i] = 0;
+       return buf;
+}
+
+static void addrec_db(void)
+{
+       int klen, dlen;
+       char *k, *d;
+       TDB_DATA key, data;
+
+       klen = 1 + (rand() % 4);
+       dlen = 1 + (rand() % 100);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       if (rand() % DELETE_PROB == 0) {
+               tdb_delete(db, key);
+       } else if (rand() % STORE_PROB == 0) {
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+       } else {
+               data = tdb_fetch(db, key);
+               if (data.dptr) free(data.dptr);
+       }
+
+       free(k);
+       free(d);
+}
+
+static void addrec_gdbm(void)
+{
+       int klen, dlen;
+       char *k, *d;
+       datum key, data;
+
+       klen = 1 + (rand() % 4);
+       dlen = 1 + (rand() % 100);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       if (rand() % DELETE_PROB == 0) {
+               gdbm_delete(gdbm, key);
+       } else if (rand() % STORE_PROB == 0) {
+               if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
+                       fatal("gdbm_store failed");
+               }
+       } else {
+               data = gdbm_fetch(gdbm, key);
+               if (data.dptr) free(data.dptr);
+       }
+
+       free(k);
+       free(d);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+       printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
+#endif
+       tdb_delete(tdb, key);
+       return 0;
+}
+
+static void merge_test(void)
+{
+       int i;
+       char keys[5][2];
+       TDB_DATA key, data;
+       
+       for (i = 0; i < 5; i++) {
+               sprintf(keys[i], "%d", i);
+               key.dptr = keys[i];
+               key.dsize = 2;
+               
+               data.dptr = "test";
+               data.dsize = 4;
+               
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+       }
+
+       key.dptr = keys[0];
+       tdb_delete(db, key);
+       key.dptr = keys[4];
+       tdb_delete(db, key);
+       key.dptr = keys[2];
+       tdb_delete(db, key);
+       key.dptr = keys[1];
+       tdb_delete(db, key);
+       key.dptr = keys[3];
+       tdb_delete(db, key);
+}
+       
+int main(int argc, char *argv[])
+{
+       int i, seed=0;
+       int loops = 10000;
+
+       unlink("test.gdbm");
+
+       db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, 
+                     O_RDWR | O_CREAT | O_TRUNC, 0600);
+       gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, 
+                        0600, NULL);
+
+       if (!db || !gdbm) {
+               fatal("db open failed");
+       }
+
+       tdb_logging_function(db, tdb_log);
+       
+#if 1
+       srand(seed);
+       start_timer();
+       for (i=0;i<loops;i++) addrec_gdbm();
+       printf("gdbm got %.2f ops/sec\n", i/end_timer());
+#endif
+
+       merge_test();
+
+       srand(seed);
+       start_timer();
+       for (i=0;i<loops;i++) addrec_db();
+       printf("tdb got %.2f ops/sec\n", i/end_timer());
+
+       compare_db();
+
+       printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+       printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+
+       tdb_close(db);
+       gdbm_close(gdbm);
+
+       return 0;
+}
diff --git a/source4/utils/tdb/tdbtool.c b/source4/utils/tdb/tdbtool.c
new file mode 100644 (file)
index 0000000..f5e486b
--- /dev/null
@@ -0,0 +1,482 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba database functions
+   Copyright (C) Andrew Tridgell              1999-2000
+   Copyright (C) Paul `Rusty' Russell             2000
+   Copyright (C) Jeremy Allison                           2000
+   Copyright (C) Andrew Esh                        2001
+
+   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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+/* a tdb tool for manipulating a tdb database */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+typedef struct connections_key {
+       pid_t pid;
+       int cnum;
+       fstring name;
+} connections_key;
+
+typedef struct connections_data {
+       int magic;
+       pid_t pid;
+       int cnum;
+       uid_t uid;
+       gid_t gid;
+       char name[24];
+       char addr[24];
+       char machine[128];
+       time_t start;
+} connections_data;
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(unsigned char *buf,int len)
+{
+       int i;
+
+       /* We're probably printing ASCII strings so don't try to display
+          the trailing NULL character. */
+
+       if (buf[len - 1] == 0)
+               len--;
+
+       for (i=0;i<len;i++)
+               printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(unsigned char *buf,int len)
+{
+       int i=0;
+       if (len<=0) return;
+       printf("[%03X] ",i);
+       for (i=0;i<len;) {
+               printf("%02X ",(int)buf[i]);
+               i++;
+               if (i%8 == 0) printf(" ");
+               if (i%16 == 0) {      
+                       print_asc(&buf[i-16],8); printf(" ");
+                       print_asc(&buf[i-8],8); printf("\n");
+                       if (i<len) printf("[%03X] ",i);
+               }
+       }
+       if (i%16) {
+               int n;
+               
+               n = 16 - (i%16);
+               printf(" ");
+               if (n>8) printf(" ");
+               while (n--) printf("   ");
+               
+               n = i%16;
+               if (n > 8) n = 8;
+               print_asc(&buf[i-(i%16)],n); printf(" ");
+               n = (i%16) - n;
+               if (n>0) print_asc(&buf[i-n],n); 
+               printf("\n");    
+       }
+}
+
+static void help(void)
+{
+       printf("
+tdbtool: 
+  create    dbname     : create a database
+  open      dbname     : open an existing database
+  erase                : erase the database
+  dump                 : dump the database as strings
+  insert    key  data  : insert a record
+  store     key  data  : store a record (replace)
+  show      key        : show a record by key
+  delete    key        : delete a record by key
+  list                 : print the database hash table and freelist
+  free                 : print the database freelist
+  1 | first            : print the first record
+  n | next             : print the next record
+  q | quit             : terminate
+  \\n                   : repeat 'next' command
+");
+}
+
+static void terror(char *why)
+{
+       printf("%s\n", why);
+}
+
+static char *get_token(int startover)
+{
+       static char tmp[1024];
+       static char *cont = NULL;
+       char *insert, *start;
+       char *k = strtok(NULL, " ");
+
+       if (!k)
+         return NULL;
+
+       if (startover)
+         start = tmp;
+       else
+         start = cont;
+
+       strcpy(start, k);
+       insert = start + strlen(start) - 1;
+       while (*insert == '\\') {
+         *insert++ = ' ';
+         k = strtok(NULL, " ");
+         if (!k)
+           break;
+         strcpy(insert, k);
+         insert = start + strlen(start) - 1;
+       }
+
+       /* Get ready for next call */
+       cont = start + strlen(start) + 1;
+       return start;
+}
+
+static void create_tdb(void)
+{
+       char *tok = get_token(1);
+       if (!tok) {
+               help();
+               return;
+       }
+       if (tdb) tdb_close(tdb);
+       tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+                      O_RDWR | O_CREAT | O_TRUNC, 0600);
+       if (!tdb) {
+               printf("Could not create %s: %s\n", tok, strerror(errno));
+       }
+}
+
+static void open_tdb(void)
+{
+       char *tok = get_token(1);
+       if (!tok) {
+               help();
+               return;
+       }
+       if (tdb) tdb_close(tdb);
+       tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
+       if (!tdb) {
+               printf("Could not open %s: %s\n", tok, strerror(errno));
+       }
+}
+
+static void insert_tdb(void)
+{
+       char *k = get_token(1);
+       char *d = get_token(0);
+       TDB_DATA key, dbuf;
+
+       if (!k || !d) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+       dbuf.dptr = d;
+       dbuf.dsize = strlen(d)+1;
+
+       if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+               terror("insert failed");
+       }
+}
+
+static void store_tdb(void)
+{
+       char *k = get_token(1);
+       char *d = get_token(0);
+       TDB_DATA key, dbuf;
+
+       if (!k || !d) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+       dbuf.dptr = d;
+       dbuf.dsize = strlen(d)+1;
+
+       printf("Storing key:\n");
+       print_rec(tdb, key, dbuf, NULL);
+
+       if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+               terror("store failed");
+       }
+}
+
+static void show_tdb(void)
+{
+       char *k = get_token(1);
+       TDB_DATA key, dbuf;
+
+       if (!k) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+/*     key.dsize = strlen(k)+1;*/
+       key.dsize = strlen(k);
+
+       dbuf = tdb_fetch(tdb, key);
+       if (!dbuf.dptr) {
+               terror("fetch failed");
+               return;
+       }
+       /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+       print_rec(tdb, key, dbuf, NULL);
+}
+
+static void delete_tdb(void)
+{
+       char *k = get_token(1);
+       TDB_DATA key;
+
+       if (!k) {
+               help();
+               return;
+       }
+
+       key.dptr = k;
+       key.dsize = strlen(k)+1;
+
+       if (tdb_delete(tdb, key) != 0) {
+               terror("delete failed");
+       }
+}
+
+#if 0
+static int print_conn_key(TDB_DATA key)
+{
+       printf( "pid    =%5d   ", ((connections_key*)key.dptr)->pid);
+       printf( "cnum   =%10d  ", ((connections_key*)key.dptr)->cnum);
+       printf( "name   =[%s]\n", ((connections_key*)key.dptr)->name);
+       return 0;
+}
+
+static int print_conn_data(TDB_DATA dbuf)
+{
+       printf( "pid    =%5d   ", ((connections_data*)dbuf.dptr)->pid);
+       printf( "cnum   =%10d  ", ((connections_data*)dbuf.dptr)->cnum);
+       printf( "name   =[%s]\n", ((connections_data*)dbuf.dptr)->name);
+       
+       printf( "uid    =%5d   ",  ((connections_data*)dbuf.dptr)->uid);
+       printf( "addr   =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
+       printf( "gid    =%5d   ",  ((connections_data*)dbuf.dptr)->gid);
+       printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
+       printf( "start  = %s\n",   ctime(&((connections_data*)dbuf.dptr)->start));
+       return 0;
+}
+#endif
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+       print_conn_key(key);
+       print_conn_data(dbuf);
+       return 0;
+#else
+       printf("\nkey %d bytes\n", key.dsize);
+       print_asc(key.dptr, key.dsize);
+       printf("\ndata %d bytes\n", dbuf.dsize);
+       print_data(dbuf.dptr, dbuf.dsize);
+       return 0;
+#endif
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       print_asc(key.dptr, key.dsize);
+       printf("\n");
+       return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+       total_bytes += dbuf.dsize;
+       return 0;
+}
+
+static void info_tdb(void)
+{
+       int count;
+       total_bytes = 0;
+       if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
+               printf("Error = %s\n", tdb_errorstr(tdb));
+       else
+               printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(char *prompt)
+{
+       static char line[1024];
+       char *p;
+       fputs(prompt, stdout);
+       line[0] = 0;
+       p = fgets(line, sizeof(line)-1, stdin);
+       if (p) p = strchr(p, '\n');
+       if (p) *p = 0;
+       return p?line:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+                     void *state)
+{
+    return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+       TDB_DATA dbuf;
+       *pkey = tdb_firstkey(the_tdb);
+       
+       dbuf = tdb_fetch(the_tdb, *pkey);
+       if (!dbuf.dptr) terror("fetch failed");
+       else {
+               /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+               print_rec(the_tdb, *pkey, dbuf, NULL);
+       }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+       TDB_DATA dbuf;
+       *pkey = tdb_nextkey(the_tdb, *pkey);
+       
+       dbuf = tdb_fetch(the_tdb, *pkey);
+       if (!dbuf.dptr) 
+               terror("fetch failed");
+       else
+               /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+               print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+    int bIterate = 0;
+    char *line;
+    char *tok;
+       TDB_DATA iterate_kbuf;
+
+    if (argv[1]) {
+       static char tmp[1024];
+        sprintf(tmp, "open %s", argv[1]);
+        tok=strtok(tmp," ");
+        open_tdb();
+    }
+
+    while ((line = tdb_getline("tdb> "))) {
+
+        /* Shell command */
+        
+        if (line[0] == '!') {
+            system(line + 1);
+            continue;
+        }
+        
+        if ((tok = strtok(line," "))==NULL) {
+           if (bIterate)
+              next_record(tdb, &iterate_kbuf);
+           continue;
+        }
+        if (strcmp(tok,"create") == 0) {
+            bIterate = 0;
+            create_tdb();
+            continue;
+        } else if (strcmp(tok,"open") == 0) {
+            open_tdb();
+            continue;
+        } else if ((strcmp(tok, "q") == 0) ||
+                   (strcmp(tok, "quit") == 0)) {
+            break;
+       }
+            
+        /* all the rest require a open database */
+        if (!tdb) {
+            bIterate = 0;
+            terror("database not open");
+            help();
+            continue;
+        }
+            
+        if (strcmp(tok,"insert") == 0) {
+            bIterate = 0;
+            insert_tdb();
+        } else if (strcmp(tok,"store") == 0) {
+            bIterate = 0;
+            store_tdb();
+        } else if (strcmp(tok,"show") == 0) {
+            bIterate = 0;
+            show_tdb();
+        } else if (strcmp(tok,"erase") == 0) {
+            bIterate = 0;
+            tdb_traverse(tdb, do_delete_fn, NULL);
+        } else if (strcmp(tok,"delete") == 0) {
+            bIterate = 0;
+            delete_tdb();
+        } else if (strcmp(tok,"dump") == 0) {
+            bIterate = 0;
+            tdb_traverse(tdb, print_rec, NULL);
+        } else if (strcmp(tok,"list") == 0) {
+            tdb_dump_all(tdb);
+        } else if (strcmp(tok, "free") == 0) {
+            tdb_printfreelist(tdb);
+        } else if (strcmp(tok,"info") == 0) {
+            info_tdb();
+        } else if ( (strcmp(tok, "1") == 0) ||
+                    (strcmp(tok, "first") == 0)) {
+            bIterate = 1;
+            first_record(tdb, &iterate_kbuf);
+        } else if ((strcmp(tok, "n") == 0) ||
+                   (strcmp(tok, "next") == 0)) {
+            next_record(tdb, &iterate_kbuf);
+        } else if ((strcmp(tok, "keys") == 0)) {
+                bIterate = 0;
+                tdb_traverse(tdb, print_key, NULL);
+        } else {
+            help();
+        }
+    }
+
+    if (tdb) tdb_close(tdb);
+
+    return 0;
+}
diff --git a/source4/utils/tdb/tdbtorture.c b/source4/utils/tdb/tdbtorture.c
new file mode 100644 (file)
index 0000000..e27bbff
--- /dev/null
@@ -0,0 +1,226 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "tdb.h"
+
+/* this tests tdb by doing lots of ops from several simultaneous
+   writers - that stresses the locking code. Build with TDB_DEBUG=1
+   for best effect */
+
+
+
+#define REOPEN_PROB 30
+#define DELETE_PROB 8
+#define STORE_PROB 4
+#define APPEND_PROB 6
+#define LOCKSTORE_PROB 0
+#define TRAVERSE_PROB 20
+#define CULL_PROB 100
+#define KEYLEN 3
+#define DATALEN 100
+#define LOCKLEN 20
+
+static TDB_CONTEXT *db;
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+       va_list ap;
+    
+       va_start(ap, format);
+       vfprintf(stdout, format, ap);
+       va_end(ap);
+       fflush(stdout);
+#if 0
+       {
+               char *ptr;
+               asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
+               system(ptr);
+               free(ptr);
+       }
+#endif 
+}
+
+static void fatal(char *why)
+{
+       perror(why);
+       exit(1);
+}
+
+static char *randbuf(int len)
+{
+       char *buf;
+       int i;
+       buf = (char *)malloc(len+1);
+
+       for (i=0;i<len;i++) {
+               buf[i] = 'a' + (rand() % 26);
+       }
+       buf[i] = 0;
+       return buf;
+}
+
+static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+                        void *state)
+{
+       if (random() % CULL_PROB == 0) {
+               tdb_delete(tdb, key);
+       }
+       return 0;
+}
+
+static void addrec_db(void)
+{
+       int klen, dlen, slen;
+       char *k, *d, *s;
+       TDB_DATA key, data, lockkey;
+
+       klen = 1 + (rand() % KEYLEN);
+       dlen = 1 + (rand() % DATALEN);
+       slen = 1 + (rand() % LOCKLEN);
+
+       k = randbuf(klen);
+       d = randbuf(dlen);
+       s = randbuf(slen);
+
+       key.dptr = k;
+       key.dsize = klen+1;
+
+       data.dptr = d;
+       data.dsize = dlen+1;
+
+       lockkey.dptr = s;
+       lockkey.dsize = slen+1;
+
+#if REOPEN_PROB
+       if (random() % REOPEN_PROB == 0) {
+               tdb_reopen_all();
+               goto next;
+       } 
+#endif
+
+#if DELETE_PROB
+       if (random() % DELETE_PROB == 0) {
+               tdb_delete(db, key);
+               goto next;
+       }
+#endif
+
+#if STORE_PROB
+       if (random() % STORE_PROB == 0) {
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+               goto next;
+       }
+#endif
+
+#if APPEND_PROB
+       if (random() % APPEND_PROB == 0) {
+               if (tdb_append(db, key, data) != 0) {
+                       fatal("tdb_append failed");
+               }
+               goto next;
+       }
+#endif
+
+#if LOCKSTORE_PROB
+       if (random() % LOCKSTORE_PROB == 0) {
+               tdb_chainlock(db, lockkey);
+               data = tdb_fetch(db, key);
+               if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+                       fatal("tdb_store failed");
+               }
+               if (data.dptr) free(data.dptr);
+               tdb_chainunlock(db, lockkey);
+               goto next;
+       } 
+#endif
+
+#if TRAVERSE_PROB
+       if (random() % TRAVERSE_PROB == 0) {
+               tdb_traverse(db, cull_traverse, NULL);
+               goto next;
+       }
+#endif
+
+       data = tdb_fetch(db, key);
+       if (data.dptr) free(data.dptr);
+
+next:
+       free(k);
+       free(d);
+       free(s);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+                       void *state)
+{
+       tdb_delete(tdb, key);
+       return 0;
+}
+
+#ifndef NPROC
+#define NPROC 6
+#endif
+
+#ifndef NLOOPS
+#define NLOOPS 200000
+#endif
+
+int main(int argc, char *argv[])
+{
+       int i, seed=0;
+       int loops = NLOOPS;
+       pid_t pids[NPROC];
+
+       pids[0] = getpid();
+
+       for (i=0;i<NPROC-1;i++) {
+               if ((pids[i+1]=fork()) == 0) break;
+       }
+
+       db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST, 
+                     O_RDWR | O_CREAT, 0600);
+       if (!db) {
+               fatal("db open failed");
+       }
+       tdb_logging_function(db, tdb_log);
+
+       srand(seed + getpid());
+       srandom(seed + getpid() + time(NULL));
+       for (i=0;i<loops;i++) addrec_db();
+
+       tdb_traverse(db, NULL, NULL);
+       tdb_traverse(db, traverse_fn, NULL);
+       tdb_traverse(db, traverse_fn, NULL);
+
+       tdb_close(db);
+
+       if (getpid() == pids[0]) {
+               for (i=0;i<NPROC-1;i++) {
+                       int status;
+                       if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
+                               printf("failed to wait for %d\n",
+                                      (int)pids[i+1]);
+                               exit(1);
+                       }
+                       if (WEXITSTATUS(status) != 0) {
+                               printf("child %d exited with status %d\n",
+                                      (int)pids[i+1], WEXITSTATUS(status));
+                               exit(1);
+                       }
+               }
+               printf("OK\n");
+       }
+
+       return 0;
+}
diff --git a/source4/utils/testparm.c b/source4/utils/testparm.c
new file mode 100644 (file)
index 0000000..b68deaa
--- /dev/null
@@ -0,0 +1,338 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Test validity of smb.conf
+   Copyright (C) Karl Auer 1993, 1994-1998
+
+   Extensively modified by Andrew Tridgell, 1995
+   Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
+   
+   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.
+*/
+
+/*
+ * Testbed for loadparm.c/params.c
+ *
+ * This module simply loads a specified configuration file and
+ * if successful, dumps it's contents to stdout. Note that the
+ * operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick 'syntax check' of a configuration file.
+ *
+ */
+
+#include "includes.h"
+
+extern BOOL AllowDebugChange;
+
+/***********************************************
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
+************************************************/
+
+static int do_global_checks(void)
+{
+       int ret = 0;
+       SMB_STRUCT_STAT st;
+
+       if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) {
+               printf("ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n");
+               ret = 1;
+       }
+
+       if (lp_wins_support() && lp_wins_server_list()) {
+               printf("ERROR: both 'wins support = true' and 'wins server = <server list>' \
+cannot be set in the smb.conf file. nmbd will abort with this setting.\n");
+               ret = 1;
+       }
+
+       if (!directory_exist(lp_lockdir(), &st)) {
+               printf("ERROR: lock directory %s does not exist\n",
+                      lp_lockdir());
+               ret = 1;
+       } else if ((st.st_mode & 0777) != 0755) {
+               printf("WARNING: lock directory %s should have permissions 0755 for browsing to work\n",
+                      lp_lockdir());
+               ret = 1;
+       }
+
+       if (!directory_exist(lp_piddir(), &st)) {
+               printf("ERROR: pid directory %s does not exist\n",
+                      lp_piddir());
+               ret = 1;
+       }
+
+       /*
+        * Password server sanity checks.
+        */
+
+       if((lp_security() == SEC_SERVER || lp_security() >= SEC_DOMAIN) && !lp_passwordserver()) {
+               pstring sec_setting;
+               if(lp_security() == SEC_SERVER)
+                       pstrcpy(sec_setting, "server");
+               else if(lp_security() == SEC_DOMAIN)
+                       pstrcpy(sec_setting, "domain");
+
+               printf("ERROR: The setting 'security=%s' requires the 'password server' parameter be set \
+to a valid password server.\n", sec_setting );
+               ret = 1;
+       }
+
+       
+       /*
+        * Check 'hosts equiv' and 'use rhosts' compatibility with 'hostname lookup' value.
+        */
+
+       if(*lp_hosts_equiv() && !lp_hostname_lookups()) {
+               printf("ERROR: The setting 'hosts equiv = %s' requires that 'hostname lookups = yes'.\n", lp_hosts_equiv());
+               ret = 1;
+       }
+
+       /*
+        * Password chat sanity checks.
+        */
+
+       if(lp_security() == SEC_USER && lp_unix_password_sync()) {
+
+               /*
+                * Check that we have a valid lp_passwd_program() if not using pam.
+                */
+
+#ifdef WITH_PAM
+               if (!lp_pam_password_change()) {
+#endif
+
+                       if(lp_passwd_program() == NULL) {
+                               printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \
+parameter.\n" );
+                               ret = 1;
+                       } else {
+                               pstring passwd_prog;
+                               pstring truncated_prog;
+                               const char *p;
+
+                               pstrcpy( passwd_prog, lp_passwd_program());
+                               p = passwd_prog;
+                               *truncated_prog = '\0';
+                               next_token(&p, truncated_prog, NULL, sizeof(pstring));
+
+                               if(access(truncated_prog, F_OK) == -1) {
+                                       printf("ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \
+cannot be executed (error was %s).\n", truncated_prog, strerror(errno) );
+                                       ret = 1;
+                               }
+                       }
+
+#ifdef WITH_PAM
+               }
+#endif
+
+               if(lp_passwd_chat() == NULL) {
+                       printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \
+parameter.\n");
+                       ret = 1;
+               }
+
+               /*
+                * Check that we have a valid script and that it hasn't
+                * been written to expect the old password.
+                */
+
+               if(lp_encrypted_passwords()) {
+                       if(strstr( lp_passwd_chat(), "%o")!=NULL) {
+                               printf("ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \
+via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() );
+                               ret = 1;
+                       }
+               }
+       }
+
+       if (strlen(lp_winbind_separator()) != 1) {
+               printf("ERROR: the 'winbind separator' parameter must be a single character.\n");
+               ret = 1;
+       }
+
+       if (*lp_winbind_separator() == '+') {
+               printf("'winbind separator = +' might cause problems with group membership.\n");
+       }
+
+       if (lp_algorithmic_rid_base() < BASE_RID) {
+               /* Try to prevent admin foot-shooting, we can't put algorithmic
+                  rids below 1000, that's the 'well known RIDs' on NT */
+               printf("'algorithmic rid base' must be equal to or above %lu\n", BASE_RID);
+       }
+
+       if (lp_algorithmic_rid_base() & 1) {
+               printf("'algorithmic rid base' must be even.\n");
+       }
+
+#ifndef HAVE_DLOPEN
+       if (lp_preload_modules()) {
+               printf("WARNING: 'preload modules = ' set while loading plugins not supported.\n");
+       }
+#endif
+
+       return ret;
+}   
+
+int main(int argc, const char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       const char *config_file = dyn_CONFIGFILE;
+       int s;
+       static BOOL silent_mode = False;
+       int ret = 0;
+       int opt;
+       poptContext pc;
+       static const char *term_code = "";
+       static char *new_local_machine = NULL;
+       const char *cname;
+       const char *caddr;
+       static int show_defaults;
+
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
+               {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
+               {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"},
+               {"encoding", 't', POPT_ARG_STRING, &term_code, 0, "Print parameters with encoding"},
+               {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+               {0,0,0,0}
+       };
+
+       pc = poptGetContext(NULL, argc, argv, long_options, 
+                           POPT_CONTEXT_KEEP_FIRST);
+       poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
+
+       while((opt = poptGetNextOpt(pc)) != -1);
+
+       setup_logging(poptGetArg(pc), True);
+
+       if (poptPeekArg(pc)) 
+               config_file = poptGetArg(pc);
+
+       cname = poptGetArg(pc);
+       caddr = poptGetArg(pc);
+       
+       if (new_local_machine) {
+               set_local_machine_name(new_local_machine);
+       }
+
+       dbf = x_stdout;
+       DEBUGLEVEL = 2;
+       AllowDebugChange = False;
+
+       printf("Load smb config files from %s\n",config_file);
+
+       if (!lp_load(config_file,False,True,False)) {
+               printf("Error loading services.\n");
+               return(1);
+       }
+
+       printf("Loaded services file OK.\n");
+
+       ret = do_global_checks();
+
+       for (s=0;s<1000;s++) {
+               if (VALID_SNUM(s))
+                       if (strlen(lp_servicename(s)) > 8) {
+                               printf("WARNING: You have some share names that are longer than 8 chars\n");
+                               printf("These may give errors while browsing or may not be accessible\nto some older clients\n");
+                               break;
+                       }
+       }
+
+       for (s=0;s<1000;s++) {
+               if (VALID_SNUM(s)) {
+                       const char **deny_list = lp_hostsdeny(s);
+                       const char **allow_list = lp_hostsallow(s);
+                       int i;
+                       if(deny_list) {
+                               for (i=0; deny_list[i]; i++) {
+                                       char *hasstar = strchr_m(deny_list[i], '*');
+                                       char *hasquery = strchr_m(deny_list[i], '?');
+                                       if(hasstar || hasquery) {
+                                               printf("Invalid character %c in hosts deny list (%s) for service %s.\n",
+                                                          hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(s) );
+                                       }
+                               }
+                       }
+
+                       if(allow_list) {
+                               for (i=0; allow_list[i]; i++) {
+                                       char *hasstar = strchr_m(allow_list[i], '*');
+                                       char *hasquery = strchr_m(allow_list[i], '?');
+                                       if(hasstar || hasquery) {
+                                               printf("Invalid character %c in hosts allow list (%s) for service %s.\n",
+                                                          hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(s) );
+                                       }
+                               }
+                       }
+
+                       if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
+                               printf("Invalid combination of parameters for service %s. \
+                                          Level II oplocks can only be set if oplocks are also set.\n",
+                                          lp_servicename(s) );
+                       }
+               }
+       }
+
+
+       if (!silent_mode) {
+               printf("Server role: ");
+               switch(lp_server_role()) {
+                       case ROLE_STANDALONE:
+                               printf("ROLE_STANDALONE\n");
+                               break;
+                       case ROLE_DOMAIN_MEMBER:
+                               printf("ROLE_DOMAIN_MEMBER\n");
+                               break;
+                       case ROLE_DOMAIN_BDC:
+                               printf("ROLE_DOMAIN_BDC\n");
+                               break;
+                       case ROLE_DOMAIN_PDC:
+                               printf("ROLE_DOMAIN_PDC\n");
+                               break;
+                       default:
+                               printf("Unknown -- internal error?\n");
+                               break;
+               }
+       }
+
+       if (!cname) {
+               if (!silent_mode) {
+                       printf("Press enter to see a dump of your service definitions\n");
+                       fflush(stdout);
+                       getc(stdin);
+               }
+               lp_dump(stdout, show_defaults, lp_numservices());
+       }
+
+       if(cname && caddr){
+               /* this is totally ugly, a real `quick' hack */
+               for (s=0;s<1000;s++) {
+                       if (VALID_SNUM(s)) {             
+                               if (allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) {
+                                       printf("Allow connection from %s (%s) to %s\n",
+                                                  cname,caddr,lp_servicename(s));
+                               } else {
+                                       printf("Deny connection from %s (%s) to %s\n",
+                                                  cname,caddr,lp_servicename(s));
+                               }
+                       }
+               }
+       }
+       return(ret);
+}
diff --git a/source4/utils/testprns.c b/source4/utils/testprns.c
new file mode 100644 (file)
index 0000000..7e52b86
--- /dev/null
@@ -0,0 +1,61 @@
+/* 
+   Unix SMB/CIFS implementation.
+   test printer setup
+   Copyright (C) Karl Auer 1993, 1994-1998
+   
+   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.
+*/
+
+/*
+ * Testbed for pcap.c
+ *
+ * This module simply checks a given printer name against the compiled-in
+ * printcap file.
+ *
+ * The operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick check of a printcap file.
+ *
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+   const char *pszTemp;
+
+   setup_logging(argv[0],True);
+
+   if (argc < 2 || argc > 3)
+      printf("Usage: testprns printername [printcapfile]\n");
+   else
+   {
+      dbf = x_fopen("test.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+      if (dbf == NULL) {
+         printf("Unable to open logfile.\n");
+      } else {
+         DEBUGLEVEL = 3;
+         pszTemp = (argc < 3) ? PRINTCAP_NAME : argv[2];
+         printf("Looking for printer %s in printcap file %s\n", 
+                 argv[1], pszTemp);
+         if (!pcap_printername_ok(argv[1], pszTemp))
+            printf("Printer name %s is not valid.\n", argv[1]);
+         else
+            printf("Printer name %s is valid.\n", argv[1]);
+         x_fclose(dbf);
+      }
+   }
+   return (0);
+}
diff --git a/source4/web/.cvsignore b/source4/web/.cvsignore
new file mode 100644 (file)
index 0000000..ed29eaf
--- /dev/null
@@ -0,0 +1 @@
+swat_proto.h
\ No newline at end of file
diff --git a/source4/web/cgi.c b/source4/web/cgi.c
new file mode 100644 (file)
index 0000000..212c288
--- /dev/null
@@ -0,0 +1,604 @@
+/* 
+   some simple CGI helper routines
+   Copyright (C) Andrew Tridgell 1997-1998
+   
+   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 "../web/swat_proto.h"
+
+#define MAX_VARIABLES 10000
+
+/* set the expiry on fixed pages */
+#define EXPIRY_TIME (60*60*24*7)
+
+#ifdef DEBUG_COMMENTS
+extern void print_title(char *fmt, ...);
+#endif
+
+struct var {
+       char *name;
+       char *value;
+};
+
+static struct var variables[MAX_VARIABLES];
+static int num_variables;
+static int content_length;
+static int request_post;
+static char *query_string;
+static const char *baseurl;
+static char *pathinfo;
+static char *C_user;
+static BOOL inetd_server;
+static BOOL got_request;
+
+static char *grab_line(FILE *f, int *cl)
+{
+       char *ret = NULL;
+       int i = 0;
+       int len = 0;
+
+       while ((*cl)) {
+               int c;
+       
+               if (i == len) {
+                       char *ret2;
+                       if (len == 0) len = 1024;
+                       else len *= 2;
+                       ret2 = (char *)Realloc(ret, len);
+                       if (!ret2) return ret;
+                       ret = ret2;
+               }
+       
+               c = fgetc(f);
+               (*cl)--;
+
+               if (c == EOF) {
+                       (*cl) = 0;
+                       break;
+               }
+               
+               if (c == '\r') continue;
+
+               if (strchr_m("\n&", c)) break;
+
+               ret[i++] = c;
+
+       }
+       
+
+       ret[i] = 0;
+       return ret;
+}
+
+/***************************************************************************
+  load all the variables passed to the CGI program. May have multiple variables
+  with the same name and the same or different values. Takes a file parameter
+  for simulating CGI invocation eg loading saved preferences.
+  ***************************************************************************/
+void cgi_load_variables(void)
+{
+       static char *line;
+       char *p, *s, *tok;
+       int len, i;
+       FILE *f = stdin;
+
+#ifdef DEBUG_COMMENTS
+       char dummy[100]="";
+       print_title(dummy);
+       d_printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
+#endif
+
+       if (!content_length) {
+               p = getenv("CONTENT_LENGTH");
+               len = p?atoi(p):0;
+       } else {
+               len = content_length;
+       }
+
+
+       if (len > 0 && 
+           (request_post ||
+            ((s=getenv("REQUEST_METHOD")) && 
+             strcasecmp(s,"POST")==0))) {
+               while (len && (line=grab_line(f, &len))) {
+                       p = strchr_m(line,'=');
+                       if (!p) continue;
+                       
+                       *p = 0;
+                       
+                       variables[num_variables].name = strdup(line);
+                       variables[num_variables].value = strdup(p+1);
+
+                       SAFE_FREE(line);
+                       
+                       if (!variables[num_variables].name || 
+                           !variables[num_variables].value)
+                               continue;
+
+                       rfc1738_unescape(variables[num_variables].value);
+                       rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+                       printf("<!== POST var %s has value \"%s\"  ==>\n",
+                              variables[num_variables].name,
+                              variables[num_variables].value);
+#endif
+                       
+                       num_variables++;
+                       if (num_variables == MAX_VARIABLES) break;
+               }
+       }
+
+       fclose(stdin);
+       open("/dev/null", O_RDWR);
+
+       if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
+               for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
+                       p = strchr_m(tok,'=');
+                       if (!p) continue;
+                       
+                       *p = 0;
+                       
+                       variables[num_variables].name = strdup(tok);
+                       variables[num_variables].value = strdup(p+1);
+
+                       if (!variables[num_variables].name || 
+                           !variables[num_variables].value)
+                               continue;
+
+                       rfc1738_unescape(variables[num_variables].value);
+                       rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+                        printf("<!== Commandline var %s has value \"%s\"  ==>\n",
+                               variables[num_variables].name,
+                               variables[num_variables].value);
+#endif                                         
+                       num_variables++;
+                       if (num_variables == MAX_VARIABLES) break;
+               }
+
+       }
+#ifdef DEBUG_COMMENTS
+        printf("<!== End dump in cgi_load_variables() ==>\n");   
+#endif
+
+       /* variables from the client are in display charset - convert them
+          to our internal charset before use */
+       for (i=0;i<num_variables;i++) {
+               pstring dest;
+
+               convert_string(CH_DISPLAY, CH_UNIX, 
+                              variables[i].name, -1, 
+                              dest, sizeof(dest));
+               free(variables[i].name);
+               variables[i].name = strdup(dest);
+
+               convert_string(CH_DISPLAY, CH_UNIX, 
+                              variables[i].value, -1,
+                              dest, sizeof(dest));
+               free(variables[i].value);
+               variables[i].value = strdup(dest);
+       }
+}
+
+
+/***************************************************************************
+  find a variable passed via CGI
+  Doesn't quite do what you think in the case of POST text variables, because
+  if they exist they might have a value of "" or even " ", depending on the 
+  browser. Also doesn't allow for variables[] containing multiple variables
+  with the same name and the same or different values.
+  ***************************************************************************/
+const char *cgi_variable(const char *name)
+{
+       int i;
+
+       for (i=0;i<num_variables;i++)
+               if (strcmp(variables[i].name, name) == 0)
+                       return variables[i].value;
+       return NULL;
+}
+
+/***************************************************************************
+tell a browser about a fatal error in the http processing
+  ***************************************************************************/
+static void cgi_setup_error(const char *err, const char *header, const char *info)
+{
+       if (!got_request) {
+               /* damn browsers don't like getting cut off before they give a request */
+               char line[1024];
+               while (fgets(line, sizeof(line)-1, stdin)) {
+                       if (strncasecmp(line,"GET ", 4)==0 || 
+                           strncasecmp(line,"POST ", 5)==0 ||
+                           strncasecmp(line,"PUT ", 4)==0) {
+                               break;
+                       }
+               }
+       }
+
+       d_printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
+       fclose(stdin);
+       fclose(stdout);
+       exit(0);
+}
+
+
+/***************************************************************************
+tell a browser about a fatal authentication error
+  ***************************************************************************/
+static void cgi_auth_error(void)
+{
+       if (inetd_server) {
+                cgi_setup_error("401 Authorization Required", 
+                                "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
+                                "You must be authenticated to use this service");
+       } else {
+               printf("Content-Type: text/html\r\n");
+
+               printf("\r\n<HTML><HEAD><TITLE>SWAT</TITLE></HEAD>\n");
+               printf("<BODY><H1>Installation Error</H1>\n");
+               printf("SWAT must be installed via inetd. It cannot be run as a CGI script<p>\n");
+               printf("</BODY></HTML>\r\n");
+       }
+       exit(0);
+}
+
+/***************************************************************************
+authenticate when we are running as a CGI
+  ***************************************************************************/
+static void cgi_web_auth(void)
+{
+       const char *user = getenv("REMOTE_USER");
+       struct passwd *pwd;
+       const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
+       const char *tail = "</BODY></HTML>\r\n";
+
+       if (!user) {
+               printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
+                      head, tail);
+               exit(0);
+       }
+
+       pwd = getpwnam_alloc(user);
+       if (!pwd) {
+               printf("%sCannot find user %s<br>%s\n", head, user, tail);
+               exit(0);
+       }
+
+       setuid(0);
+       setuid(pwd->pw_uid);
+       if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
+               printf("%sFailed to become user %s - uid=%d/%d<br>%s\n", 
+                      head, user, (int)geteuid(), (int)getuid(), tail);
+               exit(0);
+       }
+       passwd_free(&pwd);
+}
+
+
+/***************************************************************************
+handle a http authentication line
+  ***************************************************************************/
+static BOOL cgi_handle_authorization(char *line)
+{
+       char *p;
+       fstring user, user_pass;
+       struct passwd *pass = NULL;
+
+       if (strncasecmp(line,"Basic ", 6)) {
+               goto err;
+       }
+       line += 6;
+       while (line[0] == ' ') line++;
+       base64_decode_inplace(line);
+       if (!(p=strchr_m(line,':'))) {
+               /*
+                * Always give the same error so a cracker
+                * cannot tell why we fail.
+                */
+               goto err;
+       }
+       *p = 0;
+
+       convert_string(CH_DISPLAY, CH_UNIX, 
+                      line, -1, 
+                      user, sizeof(user));
+
+       convert_string(CH_DISPLAY, CH_UNIX, 
+                      p+1, -1, 
+                      user_pass, sizeof(user_pass));
+
+       /*
+        * Try and get the user from the UNIX password file.
+        */
+       
+       pass = getpwnam_alloc(user);
+       
+       /*
+        * Validate the password they have given.
+        */
+       
+       if NT_STATUS_IS_OK(pass_check(pass, user, user_pass, 
+                     strlen(user_pass), NULL, False)) {
+               
+               if (pass) {
+                       /*
+                        * Password was ok.
+                        */
+                       
+                       become_user_permanently(pass->pw_uid, pass->pw_gid);
+                       
+                       /* Save the users name */
+                       C_user = strdup(user);
+                       passwd_free(&pass);
+                       return True;
+               }
+       }
+       
+err:
+       cgi_setup_error("401 Bad Authorization", "", 
+                       "username or password incorrect");
+
+       passwd_free(&pass);
+       return False;
+}
+
+/***************************************************************************
+is this root?
+  ***************************************************************************/
+BOOL am_root(void)
+{
+       if (geteuid() == 0) {
+               return( True);
+       } else {
+               return( False);
+       }
+}
+
+/***************************************************************************
+return a ptr to the users name
+  ***************************************************************************/
+char *cgi_user_name(void)
+{
+        return(C_user);
+}
+
+
+/***************************************************************************
+handle a file download
+  ***************************************************************************/
+static void cgi_download(char *file)
+{
+       SMB_STRUCT_STAT st;
+       char buf[1024];
+       int fd, l, i;
+       char *p;
+       char *lang;
+
+       /* sanitise the filename */
+       for (i=0;file[i];i++) {
+               if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
+                       cgi_setup_error("404 File Not Found","",
+                                       "Illegal character in filename");
+               }
+       }
+
+       if (!file_exist(file, &st)) {
+               cgi_setup_error("404 File Not Found","",
+                               "The requested file was not found");
+       }
+
+       fd = web_open(file,O_RDONLY,0);
+       if (fd == -1) {
+               cgi_setup_error("404 File Not Found","",
+                               "The requested file was not found");
+       }
+       printf("HTTP/1.0 200 OK\r\n");
+       if ((p=strrchr_m(file,'.'))) {
+               if (strcmp(p,".gif")==0) {
+                       printf("Content-Type: image/gif\r\n");
+               } else if (strcmp(p,".jpg")==0) {
+                       printf("Content-Type: image/jpeg\r\n");
+               } else if (strcmp(p,".txt")==0) {
+                       printf("Content-Type: text/plain\r\n");
+               } else {
+                       printf("Content-Type: text/html\r\n");
+               }
+       }
+       printf("Expires: %s\r\n", http_timestring(time(NULL)+EXPIRY_TIME));
+
+       lang = lang_tdb_current();
+       if (lang) {
+               printf("Content-Language: %s\r\n", lang);
+       }
+
+       printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
+       while ((l=read(fd,buf,sizeof(buf)))>0) {
+               fwrite(buf, 1, l, stdout);
+       }
+       close(fd);
+       exit(0);
+}
+
+
+
+
+/**
+ * @brief Setup the CGI framework.
+ *
+ * Setup the cgi framework, handling the possibility that this program
+ * is either run as a true CGI program with a gateway to a web server, or
+ * is itself a mini web server.
+ **/
+void cgi_setup(const char *rootdir, int auth_required)
+{
+       BOOL authenticated = False;
+       char line[1024];
+       char *url=NULL;
+       char *p;
+       char *lang;
+
+       if (chdir(rootdir)) {
+               cgi_setup_error("500 Server Error", "",
+                               "chdir failed - the server is not configured correctly");
+       }
+
+       /* Handle the possibility we might be running as non-root */
+       sec_init();
+
+       if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
+               /* if running as a cgi program */
+               web_set_lang(lang);
+       }
+
+       /* maybe we are running under a web server */
+       if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
+               if (auth_required) {
+                       cgi_web_auth();
+               }
+               return;
+       }
+
+       inetd_server = True;
+
+       if (!check_access(1, lp_hostsallow(-1), lp_hostsdeny(-1))) {
+               cgi_setup_error("403 Forbidden", "",
+                               "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
+       }
+
+       /* we are a mini-web server. We need to read the request from stdin
+          and handle authentication etc */
+       while (fgets(line, sizeof(line)-1, stdin)) {
+               if (line[0] == '\r' || line[0] == '\n') break;
+               if (strncasecmp(line,"GET ", 4)==0) {
+                       got_request = True;
+                       url = strdup(&line[4]);
+               } else if (strncasecmp(line,"POST ", 5)==0) {
+                       got_request = True;
+                       request_post = 1;
+                       url = strdup(&line[5]);
+               } else if (strncasecmp(line,"PUT ", 4)==0) {
+                       got_request = True;
+                       cgi_setup_error("400 Bad Request", "",
+                                       "This server does not accept PUT requests");
+               } else if (strncasecmp(line,"Authorization: ", 15)==0) {
+                       authenticated = cgi_handle_authorization(&line[15]);
+               } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
+                       content_length = atoi(&line[16]);
+               } else if (strncasecmp(line,"Accept-Language: ", 17)==0) {
+                       web_set_lang(&line[17]);
+               }
+               /* ignore all other requests! */
+       }
+
+       if (auth_required && !authenticated) {
+               cgi_auth_error();
+       }
+
+       if (!url) {
+               cgi_setup_error("400 Bad Request", "",
+                               "You must specify a GET or POST request");
+       }
+
+       /* trim the URL */
+       if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
+               *p = 0;
+       }
+       while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
+               url[strlen(url)-1] = 0;
+       }
+
+       /* anything following a ? in the URL is part of the query string */
+       if ((p=strchr_m(url,'?'))) {
+               query_string = p+1;
+               *p = 0;
+       }
+
+       string_sub(url, "/swat/", "", 0);
+
+       if (url[0] != '/' && strstr(url,"..")==0 && file_exist(url, NULL)) {
+               cgi_download(url);
+       }
+
+       printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
+       printf("Date: %s\r\n", http_timestring(time(NULL)));
+       baseurl = "";
+       pathinfo = url+1;
+}
+
+
+/***************************************************************************
+return the current pages URL
+  ***************************************************************************/
+const char *cgi_baseurl(void)
+{
+       if (inetd_server) {
+               return baseurl;
+       }
+       return getenv("SCRIPT_NAME");
+}
+
+/***************************************************************************
+return the current pages path info
+  ***************************************************************************/
+const char *cgi_pathinfo(void)
+{
+       char *r;
+       if (inetd_server) {
+               return pathinfo;
+       }
+       r = getenv("PATH_INFO");
+       if (!r) return "";
+       if (*r == '/') r++;
+       return r;
+}
+
+/***************************************************************************
+return the hostname of the client
+  ***************************************************************************/
+char *cgi_remote_host(void)
+{
+       if (inetd_server) {
+               return get_socket_name(1,False);
+       }
+       return getenv("REMOTE_HOST");
+}
+
+/***************************************************************************
+return the hostname of the client
+  ***************************************************************************/
+char *cgi_remote_addr(void)
+{
+       if (inetd_server) {
+               return get_socket_addr(1);
+       }
+       return getenv("REMOTE_ADDR");
+}
+
+
+/***************************************************************************
+return True if the request was a POST
+  ***************************************************************************/
+BOOL cgi_waspost(void)
+{
+       if (inetd_server) {
+               return request_post;
+       }
+       return strequal(getenv("REQUEST_METHOD"), "POST");
+}
diff --git a/source4/web/diagnose.c b/source4/web/diagnose.c
new file mode 100644 (file)
index 0000000..f9a70d1
--- /dev/null
@@ -0,0 +1,83 @@
+/* 
+   Unix SMB/CIFS implementation.
+   diagnosis tools for web admin
+   Copyright (C) Andrew Tridgell 1998
+   
+   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 "../web/swat_proto.h"
+
+#ifdef WITH_WINBIND
+
+NSS_STATUS winbindd_request(int req_type,
+                       struct winbindd_request *request,
+                       struct winbindd_response *response);
+
+/* check to see if winbind is running by pinging it */
+
+BOOL winbindd_running(void)
+{
+
+       if (winbindd_request(WINBINDD_PING, NULL, NULL))
+               return False;
+
+       return True;
+}      
+#endif
+
+/* check to see if nmbd is running on localhost by looking for a __SAMBA__
+   response */
+BOOL nmbd_running(void)
+{
+       extern struct in_addr loopback_ip;
+       int fd, count, flags;
+       struct in_addr *ip_list;
+
+       if ((fd = open_socket_in(SOCK_DGRAM, 0, 3,
+                                interpret_addr("127.0.0.1"), True)) != -1) {
+               if ((ip_list = name_query(fd, "__SAMBA__", 0, 
+                                         True, True, loopback_ip,
+                                         &count, &flags, NULL)) != NULL) {
+                       SAFE_FREE(ip_list);
+                       close(fd);
+                       return True;
+               }
+               close (fd);
+       }
+
+       return False;
+}
+
+
+/* check to see if smbd is running on localhost by trying to open a connection
+   then closing it */
+BOOL smbd_running(void)
+{
+       static struct cli_state cli;
+       extern struct in_addr loopback_ip;
+
+       if (!cli_initialise(&cli))
+               return False;
+
+       if (!cli_connect(&cli, lp_netbios_name(), &loopback_ip)) {
+               cli_shutdown(&cli);
+               return False;
+       }
+
+       cli_shutdown(&cli);
+       return True;
+}
diff --git a/source4/web/neg_lang.c b/source4/web/neg_lang.c
new file mode 100644 (file)
index 0000000..da974f7
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+   Unix SMB/CIFS implementation.
+   SWAT language handling
+   
+   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.
+
+   Created by Ryo Kawahara <rkawa@lbe.co.jp> 
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+/*
+  during a file download we first check to see if there is a language
+  specific file available. If there is then use that, otherwise 
+  just open the specified file
+*/
+int web_open(const char *fname, int flags, mode_t mode)
+{
+       char *p = NULL;
+       char *lang = lang_tdb_current();
+       int fd;
+       if (lang) {
+               asprintf(&p, "lang/%s/%s", lang, fname);
+               if (p) {
+                       fd = sys_open(p, flags, mode);
+                       free(p);
+                       if (fd != -1) {
+                               return fd;
+                       }
+               }
+       }
+
+       /* fall through to default name */
+       return sys_open(fname, flags, mode);
+}
+
+
+struct pri_list {
+       float pri;
+       char *string;
+};
+
+static int qsort_cmp_list(const void *x, const void *y) {
+       struct pri_list *a = (struct pri_list *)x;
+       struct pri_list *b = (struct pri_list *)y;
+       if (a->pri > b->pri) return -1;
+       if (a->pri == b->pri) return 0;
+       return 1;
+}
+
+/*
+  choose from a list of languages. The list can be comma or space
+  separated
+  Keep choosing until we get a hit 
+  Changed to habdle priority -- Simo
+*/
+
+void web_set_lang(const char *lang_string)
+{
+       char **lang_list, **count;
+       struct pri_list *pl;
+       int lang_num, i;
+
+       /* build the lang list */
+       lang_list = str_list_make(lang_string, ", \t\r\n");
+       if (!lang_list) return;
+       
+       /* sort the list by priority */
+       lang_num = 0;
+       count = lang_list;
+       while (*count && **count) {
+               count++;
+               lang_num++;
+       }
+       pl = (struct pri_list *)malloc(sizeof(struct pri_list) * lang_num);
+       for (i = 0; i < lang_num; i++) {
+               char *pri_code;
+               if ((pri_code=strstr(lang_list[i], ";q="))) {
+                       *pri_code = '\0';
+                       pri_code += 3;
+                       sscanf(pri_code, "%f", &(pl[i].pri));
+               } else {
+                       pl[i].pri = 1;
+               }
+               pl[i].string = strdup(lang_list[i]);
+       }
+       str_list_free(&lang_list);
+
+       qsort(pl, lang_num, sizeof(struct pri_list), &qsort_cmp_list);
+
+       /* it's not an error to not initialise - we just fall back to 
+          the default */
+
+       for (i = 0; i < lang_num; i++) {
+               if (lang_tdb_init(pl[i].string)) break;
+       }
+
+       for (i = 0; i < lang_num; i++) {
+               SAFE_FREE(pl[i].string);
+       }
+       SAFE_FREE(pl);
+
+       return;
+}
diff --git a/source4/web/startstop.c b/source4/web/startstop.c
new file mode 100644 (file)
index 0000000..c6babff
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+   Unix SMB/CIFS implementation.
+   start/stop nmbd and smbd
+   Copyright (C) Andrew Tridgell 1998
+   
+   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 "../web/swat_proto.h"
+#include "dynconfig.h"
+
+/** Need to wait for daemons to startup */
+#define SLEEP_TIME 3
+
+/** Startup smbd from web interface. */
+void start_smbd(void)
+{
+       pstring binfile;
+
+       if (geteuid() != 0) return;
+
+       if (fork()) {
+               sleep(SLEEP_TIME);
+               return;
+       }
+
+       slprintf(binfile, sizeof(pstring) - 1, "%s/smbd", dyn_SBINDIR);
+
+       become_daemon(True);
+
+       execl(binfile, binfile, "-D", NULL);
+
+       exit(0);
+}
+
+/* startup nmbd */
+void start_nmbd(void)
+{
+       pstring binfile;
+
+       if (geteuid() != 0) return;
+
+       if (fork()) {
+               sleep(SLEEP_TIME);
+               return;
+       }
+
+       slprintf(binfile, sizeof(pstring) - 1, "%s/nmbd", dyn_SBINDIR);
+       
+       become_daemon(True);
+
+       execl(binfile, binfile, "-D", NULL);
+
+       exit(0);
+}
+
+/** Startup winbindd from web interface. */
+void start_winbindd(void)
+{
+       pstring binfile;
+
+       if (geteuid() != 0) return;
+
+       if (fork()) {
+               sleep(SLEEP_TIME);
+               return;
+       }
+
+       slprintf(binfile, sizeof(pstring) - 1, "%s/winbindd", dyn_SBINDIR);
+
+       become_daemon(True);
+
+       execl(binfile, binfile, NULL);
+
+       exit(0);
+}
+
+
+/* stop smbd */
+void stop_smbd(void)
+{
+       pid_t pid = pidfile_pid("smbd");
+
+       if (geteuid() != 0) return;
+
+       if (pid <= 0) return;
+
+       kill(pid, SIGTERM);
+}
+
+/* stop nmbd */
+void stop_nmbd(void)
+{
+       pid_t pid = pidfile_pid("nmbd");
+
+       if (geteuid() != 0) return;
+
+       if (pid <= 0) return;
+
+       kill(pid, SIGTERM);
+}
+#ifdef WITH_WINBIND
+/* stop winbindd */
+void stop_winbindd(void)
+{
+       pid_t pid = pidfile_pid("winbindd");
+
+       if (geteuid() != 0) return;
+
+       if (pid <= 0) return;
+
+       kill(pid, SIGTERM);
+}
+#endif
+/* kill a specified process */
+void kill_pid(pid_t pid)
+{
+       if (geteuid() != 0) return;
+
+       if (pid <= 0) return;
+
+       kill(pid, SIGTERM);
+       sleep(SLEEP_TIME);
+}
diff --git a/source4/web/statuspage.c b/source4/web/statuspage.c
new file mode 100644 (file)
index 0000000..5dadb99
--- /dev/null
@@ -0,0 +1,406 @@
+/* 
+   Unix SMB/CIFS implementation.
+   web status page
+   Copyright (C) Andrew Tridgell 1997-1998
+   
+   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 "../web/swat_proto.h"
+
+#define PIDMAP         struct PidMap
+
+PIDMAP {
+       PIDMAP  *next, *prev;
+       pid_t   pid;
+       char    *machine;
+};
+
+static PIDMAP  *pidmap;
+static int     PID_or_Machine;         /* 0 = show PID, else show Machine name */
+
+static pid_t smbd_pid;
+
+/* from 2nd call on, remove old list */
+static void initPid2Machine (void)
+{
+       /* show machine name rather PID on table "Open Files"? */
+       if (PID_or_Machine) {
+               PIDMAP *p;
+
+               for (p = pidmap; p != NULL; ) {
+                       DLIST_REMOVE(pidmap, p);
+                       SAFE_FREE(p->machine);
+                       SAFE_FREE(p);
+               }
+
+               pidmap = NULL;
+       }
+}
+
+/* add new PID <-> Machine name mapping */
+static void addPid2Machine (pid_t pid, char *machine)
+{
+       /* show machine name rather PID on table "Open Files"? */
+       if (PID_or_Machine) {
+               PIDMAP *newmap;
+
+               if ((newmap = (PIDMAP *) malloc (sizeof (PIDMAP))) == NULL) {
+                       /* XXX need error message for this?
+                          if malloc fails, PID is always shown */
+                       return;
+               }
+
+               newmap->pid = pid;
+               newmap->machine = strdup (machine);
+
+               DLIST_ADD(pidmap, newmap);
+       }
+}
+
+/* lookup PID <-> Machine name mapping */
+static char *mapPid2Machine (pid_t pid)
+{
+       static char pidbuf [64];
+       PIDMAP *map;
+
+       /* show machine name rather PID on table "Open Files"? */
+       if (PID_or_Machine) {
+               for (map = pidmap; map != NULL; map = map->next) {
+                       if (pid == map->pid) {
+                               if (map->machine == NULL)       /* no machine name */
+                                       break;                  /* show PID */
+
+                               return map->machine;
+                       }
+               }
+       }
+
+       /* PID not in list or machine name NULL? return pid as string */
+       snprintf (pidbuf, sizeof (pidbuf) - 1, "%d", pid);
+       return pidbuf;
+}
+
+static char *tstring(time_t t)
+{
+       static pstring buf;
+       pstrcpy(buf, asctime(LocalTime(&t)));
+       all_string_sub(buf," ","&nbsp;",sizeof(buf));
+       return buf;
+}
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+       d_printf("<tr><td>%s</td>",_(mapPid2Machine(e->pid)));
+       d_printf("<td>");
+       switch ((e->share_mode>>4)&0xF) {
+       case DENY_NONE: d_printf("DENY_NONE"); break;
+       case DENY_ALL:  d_printf("DENY_ALL   "); break;
+       case DENY_DOS:  d_printf("DENY_DOS   "); break;
+       case DENY_READ: d_printf("DENY_READ  "); break;
+       case DENY_WRITE:d_printf("DENY_WRITE "); break;
+       }
+       d_printf("</td>");
+
+       d_printf("<td>");
+       switch (e->share_mode&0xF) {
+       case 0: d_printf("RDONLY     "); break;
+       case 1: d_printf("WRONLY     "); break;
+       case 2: d_printf("RDWR       "); break;
+       }
+       d_printf("</td>");
+
+       d_printf("<td>");
+       if((e->op_type & 
+           (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) == 
+          (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+               d_printf("EXCLUSIVE+BATCH ");
+       else if (e->op_type & EXCLUSIVE_OPLOCK)
+               d_printf("EXCLUSIVE       ");
+       else if (e->op_type & BATCH_OPLOCK)
+               d_printf("BATCH           ");
+       else if (e->op_type & LEVEL_II_OPLOCK)
+               d_printf("LEVEL_II        ");
+       else
+               d_printf("NONE            ");
+       d_printf("</td>");
+
+       d_printf("<td>%s</td><td>%s</td></tr>\n",
+              fname,tstring(e->time.tv_sec));
+}
+
+
+/* kill off any connections chosen by the user */
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+       struct connections_data crec;
+
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+       if (crec.cnum == -1 && process_exists(crec.pid)) {
+               char buf[30];
+               slprintf(buf,sizeof(buf)-1,"kill_%d", (int)crec.pid);
+               if (cgi_variable(buf)) {
+                       kill_pid(crec.pid);
+               }
+       }
+       return 0;
+}
+
+/* traversal fn for showing machine connections */
+static int traverse_fn2(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+       struct connections_data crec;
+
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+       
+       if (crec.cnum != -1 || !process_exists(crec.pid) || (crec.pid == smbd_pid))
+               return 0;
+
+       addPid2Machine (crec.pid, crec.machine);
+
+       d_printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td>\n",
+              (int)crec.pid,
+              crec.machine,crec.addr,
+              tstring(crec.start));
+       if (geteuid() == 0) {
+               d_printf("<td><input type=submit value=\"X\" name=\"kill_%d\"></td>\n",
+                      (int)crec.pid);
+       }
+       d_printf("</tr>\n");
+
+       return 0;
+}
+
+/* traversal fn for showing share connections */
+static int traverse_fn3(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+       struct connections_data crec;
+       TALLOC_CTX *mem_ctx;
+
+       if (dbuf.dsize != sizeof(crec))
+               return 0;
+
+       memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+       if (crec.cnum == -1 || !process_exists(crec.pid))
+               return 0;
+
+       mem_ctx = talloc_init("smbgroupedit talloc");
+       if (!mem_ctx) return -1;
+       d_printf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td></tr>\n",
+              crec.name,uidtoname(crec.uid),
+              gidtoname(mem_ctx, crec.gid),(int)crec.pid,
+              crec.machine,
+              tstring(crec.start));
+       talloc_destroy(mem_ctx);
+       return 0;
+}
+
+
+/* show the current server status */
+void status_page(void)
+{
+       const char *v;
+       int autorefresh=0;
+       int refresh_interval=30;
+       TDB_CONTEXT *tdb;
+
+       smbd_pid = pidfile_pid("smbd");
+
+       if (cgi_variable("smbd_restart")) {
+               stop_smbd();
+               start_smbd();
+       }
+
+       if (cgi_variable("smbd_start")) {
+               start_smbd();
+       }
+
+       if (cgi_variable("smbd_stop")) {
+               stop_smbd();
+       }
+
+       if (cgi_variable("nmbd_restart")) {
+               stop_nmbd();
+               start_nmbd();
+       }
+       if (cgi_variable("nmbd_start")) {
+               start_nmbd();
+       }
+
+       if (cgi_variable("nmbd_stop")) {
+               stop_nmbd();
+       }
+
+#ifdef WITH_WINBIND
+       if (cgi_variable("winbindd_restart")) {
+               stop_winbindd();
+               start_winbindd();
+       }
+
+       if (cgi_variable("winbindd_start")) {
+               start_winbindd();
+       }
+
+       if (cgi_variable("winbindd_stop")) {
+               stop_winbindd();
+       }
+#endif
+       if (cgi_variable("autorefresh")) {
+               autorefresh = 1;
+       } else if (cgi_variable("norefresh")) {
+               autorefresh = 0;
+       } else if (cgi_variable("refresh")) {
+               autorefresh = 1;
+       }
+
+       if ((v=cgi_variable("refresh_interval"))) {
+               refresh_interval = atoi(v);
+       }
+
+       if (cgi_variable("show_client_in_col_1")) {
+               PID_or_Machine = 1;
+       }
+
+       tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+       if (tdb) tdb_traverse(tdb, traverse_fn1, NULL);
+       initPid2Machine ();
+
+       d_printf("<H2>%s</H2>\n", _("Server Status"));
+
+       d_printf("<FORM method=post>\n");
+
+       if (!autorefresh) {
+               d_printf("<input type=submit value=\"%s\" name=autorefresh>\n", _("Auto Refresh"));
+               d_printf("<br>%s", _("Refresh Interval: "));
+               d_printf("<input type=text size=2 name=\"refresh_interval\" value=%d>\n", 
+                      refresh_interval);
+       } else {
+               d_printf("<input type=submit value=\"%s\" name=norefresh>\n", _("Stop Refreshing"));
+               d_printf("<br>%s%d\n", _("Refresh Interval: "), refresh_interval);
+               d_printf("<input type=hidden name=refresh value=1>\n");
+       }
+
+       d_printf("<p>\n");
+
+       if (!tdb) {
+               /* open failure either means no connections have been
+                   made */
+       }
+
+
+       d_printf("<table>\n");
+
+       d_printf("<tr><td>%s</td><td>%s</td></tr>", _("version:"), VERSION);
+
+       fflush(stdout);
+       d_printf("<tr><td>%s</td><td>%s</td>\n", _("smbd:"), smbd_running()?_("running"):_("not running"));
+       if (geteuid() == 0) {
+           if (smbd_running()) {
+               d_printf("<td><input type=submit name=\"smbd_stop\" value=\"%s\"></td>\n", _("Stop smbd"));
+           } else {
+               d_printf("<td><input type=submit name=\"smbd_start\" value=\"%s\"></td>\n", _("Start smbd"));
+           }
+           d_printf("<td><input type=submit name=\"smbd_restart\" value=\"%s\"></td>\n", _("Restart smbd"));
+       }
+       d_printf("</tr>\n");
+
+       fflush(stdout);
+       d_printf("<tr><td>%s</td><td>%s</td>\n", _("nmbd:"), nmbd_running()?_("running"):_("not running"));
+       if (geteuid() == 0) {
+           if (nmbd_running()) {
+               d_printf("<td><input type=submit name=\"nmbd_stop\" value=\"%s\"></td>\n", _("Stop nmbd"));
+           } else {
+               d_printf("<td><input type=submit name=\"nmbd_start\" value=\"%s\"></td>\n", _("Start nmbd"));
+           }
+           d_printf("<td><input type=submit name=\"nmbd_restart\" value=\"%s\"></td>\n", _("Restart nmbd"));
+       }
+       d_printf("</tr>\n");
+
+#ifdef WITH_WINBIND
+       fflush(stdout);
+       d_printf("<tr><td>%s</td><td>%s</td>\n", _("winbindd:"), winbindd_running()?_("running"):_("not running"));
+       if (geteuid() == 0) {
+           if (winbindd_running()) {
+               d_printf("<td><input type=submit name=\"winbindd_stop\" value=\"%s\"></td>\n", _("Stop winbindd"));
+           } else {
+               d_printf("<td><input type=submit name=\"winbindd_start\" value=\"%s\"></td>\n", _("Start winbindd"));
+           }
+           d_printf("<td><input type=submit name=\"winbindd_restart\" value=\"%s\"></td>\n", _("Restart winbindd"));
+       }
+       d_printf("</tr>\n");
+#endif
+
+       d_printf("</table>\n");
+       fflush(stdout);
+
+       d_printf("<p><h3>%s</h3>\n", _("Active Connections"));
+       d_printf("<table border=1>\n");
+       d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th>\n", _("PID"), _("Client"), _("IP address"), _("Date"));
+       if (geteuid() == 0) {
+               d_printf("<th>%s</th>\n", _("Kill"));
+       }
+       d_printf("</tr>\n");
+
+       if (tdb) tdb_traverse(tdb, traverse_fn2, NULL);
+
+       d_printf("</table><p>\n");
+
+       d_printf("<p><h3>%s</h3>\n", _("Active Shares"));
+       d_printf("<table border=1>\n");
+       d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n\n",
+               _("Share"), _("User"), _("Group"), _("PID"), _("Client"), _("Date"));
+
+       if (tdb) tdb_traverse(tdb, traverse_fn3, NULL);
+
+       d_printf("</table><p>\n");
+
+       d_printf("<h3>%s</h3>\n", _("Open Files"));
+       d_printf("<table border=1>\n");
+       d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n", _("PID"), _("Sharing"), _("R/W"), _("Oplock"), _("File"), _("Date"));
+
+       locking_init(1);
+       share_mode_forall(print_share_mode);
+       locking_end();
+       d_printf("</table>\n");
+
+       if (tdb) tdb_close(tdb);
+
+       d_printf("<br><input type=submit name=\"show_client_in_col_1\" value=\"Show Client in col 1\">\n");
+       d_printf("<input type=submit name=\"show_pid_in_col_1\" value=\"Show PID in col 1\">\n");
+
+       d_printf("</FORM>\n");
+
+       if (autorefresh) {
+               /* this little JavaScript allows for automatic refresh
+                   of the page. There are other methods but this seems
+                   to be the best alternative */
+               d_printf("<script language=\"JavaScript\">\n");
+               d_printf("<!--\nsetTimeout('window.location.replace(\"%s/status?refresh_interval=%d&refresh=1\")', %d)\n", 
+                      cgi_baseurl(),
+                      refresh_interval,
+                      refresh_interval*1000);
+               d_printf("//-->\n</script>\n");
+       }
+}
diff --git a/source4/web/swat.c b/source4/web/swat.c
new file mode 100644 (file)
index 0000000..db48cbb
--- /dev/null
@@ -0,0 +1,1344 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba Web Administration Tool
+   Version 3.0.0
+   Copyright (C) Andrew Tridgell 1997-2002
+   Copyright (C) John H Terpstra 2002
+   
+   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.
+*/
+
+/**
+ * @defgroup swat SWAT - Samba Web Administration Tool
+ * @{ 
+ * @file swat.c
+ *
+ * @brief Samba Web Administration Tool.
+ **/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+#define GLOBALS_SNUM -1
+
+static BOOL demo_mode = False;
+static BOOL have_write_access = False;
+static BOOL have_read_access = False;
+static int iNumNonAutoPrintServices = 0;
+
+/*
+ * Password Management Globals
+ */
+#define SWAT_USER "username"
+#define OLD_PSWD "old_passwd"
+#define NEW_PSWD "new_passwd"
+#define NEW2_PSWD "new2_passwd"
+#define CHG_S_PASSWD_FLAG "chg_s_passwd_flag"
+#define CHG_R_PASSWD_FLAG "chg_r_passwd_flag"
+#define ADD_USER_FLAG "add_user_flag"
+#define DELETE_USER_FLAG "delete_user_flag"
+#define DISABLE_USER_FLAG "disable_user_flag"
+#define ENABLE_USER_FLAG "enable_user_flag"
+#define RHOST "remote_host"
+
+/* we need these because we link to locking*.o */
+ void become_root(void) {}
+ void unbecome_root(void) {}
+
+/****************************************************************************
+****************************************************************************/
+static int enum_index(int value, const struct enum_list *enumlist)
+{
+       int i;
+       for (i=0;enumlist[i].name;i++)
+               if (value == enumlist[i].value) break;
+       return(i);
+}
+
+static char *fix_backslash(const char *str)
+{
+       static char newstring[1024];
+       char *p = newstring;
+
+        while (*str) {
+                if (*str == '\\') {*p++ = '\\';*p++ = '\\';}
+                else *p++ = *str;
+                ++str;
+        }
+       *p = '\0';
+       return newstring;
+}
+
+static char *stripspaceupper(const char *str)
+{
+       static char newstring[1024];
+       char *p = newstring;
+
+       while (*str) {
+               if (*str != ' ') *p++ = toupper(*str);
+               ++str;
+       }
+       *p = '\0';
+       return newstring;
+}
+
+static char *make_parm_name(const char *label)
+{
+       static char parmname[1024];
+       char *p = parmname;
+
+       while (*label) {
+               if (*label == ' ') *p++ = '_';
+               else *p++ = *label;
+               ++label;
+       }
+       *p = '\0';
+       return parmname;
+}
+
+/****************************************************************************
+  include a lump of html in a page 
+****************************************************************************/
+static int include_html(const char *fname)
+{
+       int fd;
+       char buf[1024];
+       int ret;
+
+       fd = web_open(fname, O_RDONLY, 0);
+
+       if (fd == -1) {
+               d_printf("ERROR: Can't open %s\n", fname);
+               return 0;
+       }
+
+       while ((ret = read(fd, buf, sizeof(buf))) > 0) {
+               write(1, buf, ret);
+       }
+
+       close(fd);
+       return 1;
+}
+
+/****************************************************************************
+  start the page with standard stuff 
+****************************************************************************/
+static void print_header(void)
+{
+       if (!cgi_waspost()) {
+               d_printf("Expires: 0\r\n");
+       }
+       d_printf("Content-type: text/html\r\n\r\n");
+
+       if (!include_html("include/header.html")) {
+               d_printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n");
+               d_printf("<HTML>\n<HEAD>\n<TITLE>Samba Web Administration Tool</TITLE>\n</HEAD>\n<BODY background=\"/swat/images/background.jpg\">\n\n");
+       }
+}
+
+/* *******************************************************************
+   show parameter label with translated name in the following form
+   because showing original and translated label in one line looks
+   too long, and showing translated label only is unusable for
+   heavy users.
+   -------------------------------
+   HELP       security   [combo box][button]
+   SECURITY
+   -------------------------------
+   (capital words are translated by gettext.)
+   if no translation is available, then same form as original is
+   used.
+   "i18n_translated_parm" class is used to change the color of the
+   translated parameter with CSS.
+   **************************************************************** */
+static const char* get_parm_translated(
+       const char* pAnchor, const char* pHelp, const char* pLabel)
+{
+       const char* pTranslated = _(pLabel);
+       static pstring output;
+       if(strcmp(pLabel, pTranslated) != 0)
+       {
+               snprintf(output, sizeof(output),
+                 "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s <br><span class=\"i18n_translated_parm\">%s</span>",
+                  pAnchor, pHelp, pLabel, pTranslated);
+               return output;
+       }
+       snprintf(output, sizeof(output), 
+         "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s",
+         pAnchor, pHelp, pLabel);
+       return output;
+}
+/****************************************************************************
+ finish off the page 
+****************************************************************************/
+static void print_footer(void)
+{
+       if (!include_html("include/footer.html")) {
+               d_printf("\n</BODY>\n</HTML>\n");
+       }
+}
+
+/****************************************************************************
+  display one editable parameter in a form 
+****************************************************************************/
+static void show_parameter(int snum, struct parm_struct *parm)
+{
+       int i;
+       void *ptr = parm->ptr;
+
+       if (parm->class == P_LOCAL && snum >= 0) {
+               ptr = lp_local_ptr(snum, ptr);
+       }
+
+       printf("<tr><td>%s</td><td>", get_parm_translated(stripspaceupper(parm->label), _("Help"), parm->label));
+       switch (parm->type) {
+       case P_CHAR:
+               d_printf("<input type=text size=2 name=\"parm_%s\" value=\"%c\">",
+                      make_parm_name(parm->label), *(char *)ptr);
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%c\'\">",
+                       _("Set Default"), make_parm_name(parm->label),(char)(parm->def.cvalue));
+               break;
+
+       case P_LIST:
+               d_printf("<input type=text size=40 name=\"parm_%s\" value=\"",
+                       make_parm_name(parm->label));
+               if ((char ***)ptr && *(char ***)ptr && **(char ***)ptr) {
+                       char **list = *(char ***)ptr;
+                       for (;*list;list++) {
+                               d_printf("%s%s", *list, ((*(list+1))?" ":""));
+                       }
+               }
+               d_printf("\">");
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'",
+                       _("Set Default"), make_parm_name(parm->label));
+               if (parm->def.lvalue) {
+                       char **list = (char **)(parm->def.lvalue);
+                       for (; *list; list++) {
+                               d_printf("%s%s", *list, ((*(list+1))?" ":""));
+                       }
+               }
+               d_printf("\'\">");
+               break;
+
+       case P_STRING:
+       case P_USTRING:
+               d_printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
+                      make_parm_name(parm->label), *(char **)ptr);
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+                       _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
+               break;
+
+       case P_BOOL:
+               d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label)); 
+               d_printf("<option %s>Yes", (*(BOOL *)ptr)?"selected":"");
+               d_printf("<option %s>No", (*(BOOL *)ptr)?"":"selected");
+               d_printf("</select>");
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+                       _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?0:1);
+               break;
+
+       case P_BOOLREV:
+               d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label)); 
+               d_printf("<option %s>Yes", (*(BOOL *)ptr)?"":"selected");
+               d_printf("<option %s>No", (*(BOOL *)ptr)?"selected":"");
+               d_printf("</select>");
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+                       _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?1:0);
+               break;
+
+       case P_INTEGER:
+               d_printf("<input type=text size=8 name=\"parm_%s\" value=%d>", make_parm_name(parm->label), *(int *)ptr);
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%d\'\">",
+                       _("Set Default"), make_parm_name(parm->label),(int)(parm->def.ivalue));
+               break;
+
+       case P_OCTAL:
+               d_printf("<input type=text size=8 name=\"parm_%s\" value=%s>", make_parm_name(parm->label), octal_string(*(int *)ptr));
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+                      _("Set Default"), make_parm_name(parm->label),
+                      octal_string((int)(parm->def.ivalue)));
+               break;
+
+       case P_ENUM:
+               d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label)); 
+               for (i=0;parm->enum_list[i].name;i++) {
+                       if (i == 0 || parm->enum_list[i].value != parm->enum_list[i-1].value) {
+                               d_printf("<option %s>%s",(*(int *)ptr)==parm->enum_list[i].value?"selected":"",parm->enum_list[i].name);
+                       }
+               }
+               d_printf("</select>");
+               d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+                       _("Set Default"), make_parm_name(parm->label),enum_index((int)(parm->def.ivalue),parm->enum_list));
+               break;
+       case P_SEP:
+               break;
+       }
+       d_printf("</td></tr>\n");
+}
+
+/****************************************************************************
+  display a set of parameters for a service 
+****************************************************************************/
+static void show_parameters(int snum, int allparameters, unsigned int parm_filter, int printers)
+{
+       int i = 0;
+       struct parm_struct *parm;
+       const char *heading = NULL;
+       const char *last_heading = NULL;
+
+       while ((parm = lp_next_parameter(snum, &i, allparameters))) {
+               if (snum < 0 && parm->class == P_LOCAL && !(parm->flags & FLAG_GLOBAL))
+                       continue;
+               if (parm->class == P_SEPARATOR) {
+                       heading = parm->label;
+                       continue;
+               }
+               if (parm->flags & FLAG_HIDE) continue;
+               if (snum >= 0) {
+                       if (printers & !(parm->flags & FLAG_PRINT)) continue;
+                       if (!printers & !(parm->flags & FLAG_SHARE)) continue;
+               }
+               if (parm_filter == FLAG_BASIC) {
+                       if (!(parm->flags & FLAG_BASIC)) {
+                               void *ptr = parm->ptr;
+
+                               if (parm->class == P_LOCAL && snum >= 0) {
+                                       ptr = lp_local_ptr(snum, ptr);
+                               }
+
+                               switch (parm->type) {
+                               case P_CHAR:
+                                       if (*(char *)ptr == (char)(parm->def.cvalue)) continue;
+                                       break;
+
+                               case P_LIST:
+                                       if (!str_list_compare(*(char ***)ptr, (char **)(parm->def.lvalue))) continue;
+                                       break;
+
+                               case P_STRING:
+                               case P_USTRING:
+                                       if (!strcmp(*(char **)ptr,(char *)(parm->def.svalue))) continue;
+                                       break;
+
+                               case P_BOOL:
+                               case P_BOOLREV:
+                                       if (*(BOOL *)ptr == (BOOL)(parm->def.bvalue)) continue;
+                                       break;
+
+                               case P_INTEGER:
+                               case P_OCTAL:
+                                       if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+                                       break;
+
+
+                               case P_ENUM:
+                                       if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+                                       break;
+                               case P_SEP:
+                                       continue;
+                               }
+                       }
+                       if (printers && !(parm->flags & FLAG_PRINT)) continue;
+               }
+               if (parm_filter == FLAG_WIZARD) {
+                       if (!((parm->flags & FLAG_WIZARD))) continue;
+               }
+               if (parm_filter == FLAG_ADVANCED) {
+                       if (!((parm->flags & FLAG_ADVANCED))) continue;
+               }
+               if (heading && heading != last_heading) {
+                       d_printf("<tr><td></td></tr><tr><td><b><u>%s</u></b></td></tr>\n", _(heading));
+                       last_heading = heading;
+               }
+               show_parameter(snum, parm);
+       }
+}
+
+/****************************************************************************
+  load the smb.conf file into loadparm.
+****************************************************************************/
+static BOOL load_config(BOOL save_def)
+{
+       lp_resetnumservices();
+       return lp_load(dyn_CONFIGFILE,False,save_def,False);
+}
+
+/****************************************************************************
+  write a config file 
+****************************************************************************/
+static void write_config(FILE *f, BOOL show_defaults)
+{
+       fprintf(f, "# Samba config file created using SWAT\n");
+       fprintf(f, "# from %s (%s)\n", cgi_remote_host(), cgi_remote_addr());
+       fprintf(f, "# Date: %s\n\n", timestring(False));
+       
+       lp_dump(f, show_defaults, iNumNonAutoPrintServices);
+}
+
+/****************************************************************************
+  save and reload the smb.conf config file 
+****************************************************************************/
+static int save_reload(int snum)
+{
+       FILE *f;
+       struct stat st;
+
+       f = sys_fopen(dyn_CONFIGFILE,"w");
+       if (!f) {
+               d_printf("failed to open %s for writing\n", dyn_CONFIGFILE);
+               return 0;
+       }
+
+       /* just in case they have used the buggy xinetd to create the file */
+       if (fstat(fileno(f), &st) == 0 &&
+           (st.st_mode & S_IWOTH)) {
+               fchmod(fileno(f), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+       }
+
+       write_config(f, False);
+       if (snum)
+               lp_dump_one(f, False, snum);
+       fclose(f);
+
+       lp_killunused(NULL);
+
+       if (!load_config(False)) {
+                d_printf("Can't reload %s\n", dyn_CONFIGFILE);
+                return 0;
+        }
+       iNumNonAutoPrintServices = lp_numservices();
+       load_printers();
+
+       return 1;
+}
+
+/****************************************************************************
+  commit one parameter 
+****************************************************************************/
+static void commit_parameter(int snum, struct parm_struct *parm, const char *v)
+{
+       int i;
+       char *s;
+
+       if (snum < 0 && parm->class == P_LOCAL) {
+               /* this handles the case where we are changing a local
+                  variable globally. We need to change the parameter in 
+                  all shares where it is currently set to the default */
+               for (i=0;i<lp_numservices();i++) {
+                       s = lp_servicename(i);
+                       if (s && (*s) && lp_is_default(i, parm)) {
+                               lp_do_parameter(i, parm->label, v);
+                       }
+               }
+       }
+
+       lp_do_parameter(snum, parm->label, v);
+}
+
+/****************************************************************************
+  commit a set of parameters for a service 
+****************************************************************************/
+static void commit_parameters(int snum)
+{
+       int i = 0;
+       struct parm_struct *parm;
+       pstring label;
+       const char *v;
+
+       while ((parm = lp_next_parameter(snum, &i, 1))) {
+               slprintf(label, sizeof(label)-1, "parm_%s", make_parm_name(parm->label));
+               if ((v = cgi_variable(label))) {
+                       if (parm->flags & FLAG_HIDE) continue;
+                       commit_parameter(snum, parm, v); 
+               }
+       }
+}
+
+/****************************************************************************
+  spit out the html for a link with an image 
+****************************************************************************/
+static void image_link(const char *name, const char *hlink, const char *src)
+{
+       d_printf("<A HREF=\"%s/%s\"><img border=\"0\" src=\"/swat/%s\" alt=\"%s\"></A>\n", 
+              cgi_baseurl(), hlink, src, name);
+}
+
+/****************************************************************************
+  display the main navigation controls at the top of each page along
+  with a title 
+****************************************************************************/
+static void show_main_buttons(void)
+{
+       char *p;
+       
+       if ((p = cgi_user_name()) && strcmp(p, "root")) {
+               d_printf(_("Logged in as <b>%s</b><p>\n"), p);
+       }
+
+       image_link(_("Home"), "", "images/home.gif");
+       if (have_write_access) {
+               image_link(_("Globals"), "globals", "images/globals.gif");
+               image_link(_("Shares"), "shares", "images/shares.gif");
+               image_link(_("Printers"), "printers", "images/printers.gif");
+               image_link(_("Wizard"), "wizard", "images/wizard.gif");
+       }
+       if (have_read_access) {
+               image_link(_("Status"), "status", "images/status.gif");
+               image_link(_("View Config"), "viewconfig", "images/viewconfig.gif");
+       }
+       image_link(_("Password Management"), "passwd", "images/passwd.gif");
+
+       d_printf("<HR>\n");
+}
+
+/****************************************************************************
+ * Handle Display/Edit Mode CGI
+ ****************************************************************************/
+static void ViewModeBoxes(int mode)
+{
+       d_printf("<p>%s\n", _("Configuration View:&nbsp"));
+       d_printf("<input type=radio name=\"ViewMode\" value=0 %s>Basic\n", (mode == 0) ? "checked" : "");
+       d_printf("<input type=radio name=\"ViewMode\" value=1 %s>Advanced\n", (mode == 1) ? "checked" : "");
+       d_printf("<input type=radio name=\"ViewMode\" value=2 %s>Developer\n", (mode == 2) ? "checked" : "");
+       d_printf("</p><br>\n");
+}
+
+/****************************************************************************
+  display a welcome page  
+****************************************************************************/
+static void welcome_page(void)
+{
+       include_html("help/welcome.html");
+}
+
+/****************************************************************************
+  display the current smb.conf  
+****************************************************************************/
+static void viewconfig_page(void)
+{
+       int full_view=0;
+
+       if (cgi_variable("full_view")) {
+               full_view = 1;
+       }
+
+       d_printf("<H2>%s</H2>\n", _("Current Config"));
+       d_printf("<form method=post>\n");
+
+       if (full_view) {
+               d_printf("<input type=submit name=\"normal_view\" value=\"%s\">\n", _("Normal View"));
+       } else {
+               d_printf("<input type=submit name=\"full_view\" value=\"%s\">\n", _("Full View"));
+       }
+
+       d_printf("<p><pre>");
+       write_config(stdout, full_view);
+       d_printf("</pre>");
+       d_printf("</form>\n");
+}
+
+/****************************************************************************
+  second screen of the wizard ... Fetch Configuration Parameters
+****************************************************************************/
+static void wizard_params_page(void)
+{
+       unsigned int parm_filter = FLAG_WIZARD;
+
+       /* Here we first set and commit all the parameters that were selected
+          in the previous screen. */
+
+       d_printf("<H2>Wizard Parameter Edit Page</H2>\n");
+
+       if (cgi_variable("Commit")) {
+               commit_parameters(GLOBALS_SNUM);
+               save_reload(0);
+       }
+
+       d_printf("<form name=\"swatform\" method=post action=wizard_params>\n");
+
+       if (have_write_access) {
+               d_printf("<input type=submit name=\"Commit\" value=\"Commit Changes\">\n");
+       }
+
+       d_printf("<input type=reset name=\"Reset Values\" value=\"Reset\">\n");
+       d_printf("<p>\n");
+       
+       d_printf("<table>\n");
+       show_parameters(GLOBALS_SNUM, 1, parm_filter, 0);
+       d_printf("</table>\n");
+       d_printf("</form>\n");
+}
+
+/****************************************************************************
+  Utility to just rewrite the smb.conf file - effectively just cleans it up
+****************************************************************************/
+static void rewritecfg_file(void)
+{
+       commit_parameters(GLOBALS_SNUM);
+       save_reload(0);
+       d_printf("<H2>Note: smb.conf %s</H2>\n", _("file has been read and rewritten"));
+}
+
+/****************************************************************************
+  wizard to create/modify the smb.conf file
+****************************************************************************/
+static void wizard_page(void)
+{
+       /* Set some variables to collect data from smb.conf */
+       int role = 0;
+       int winstype = 0;
+       int have_home = -1;
+       int HomeExpo = 0;
+       int SerType = 0;
+
+       if (cgi_variable("Rewrite")) {
+               (void) rewritecfg_file();
+               return;
+       }
+
+       if (cgi_variable("GetWizardParams")){
+               (void) wizard_params_page();
+               return;
+       }
+
+       if (cgi_variable("Commit")){
+               SerType = atoi(cgi_variable("ServerType"));
+               winstype = atoi(cgi_variable("WINSType"));
+               have_home = lp_servicenumber(HOMES_NAME);
+               HomeExpo = atoi(cgi_variable("HomeExpo"));
+
+               /* Plain text passwords are too badly broken - use encrypted passwords only */
+               lp_do_parameter( GLOBALS_SNUM, "encrypt passwords", "Yes");
+               
+               switch ( SerType ){
+                       case 0:
+                               /* Stand-alone Server */
+                               lp_do_parameter( GLOBALS_SNUM, "security", "USER" );
+                               lp_do_parameter( GLOBALS_SNUM, "domain logons", "No" );
+                               break;
+                       case 1:
+                               /* Domain Member */
+                               lp_do_parameter( GLOBALS_SNUM, "security", "DOMAIN" );
+                               lp_do_parameter( GLOBALS_SNUM, "domain logons", "No" );
+                               break;
+                       case 2:
+                               /* Domain Controller */
+                               lp_do_parameter( GLOBALS_SNUM, "security", "USER" );
+                               lp_do_parameter( GLOBALS_SNUM, "domain logons", "Yes" );
+                               break;
+               }
+               switch ( winstype ) {
+                       case 0:
+                               lp_do_parameter( GLOBALS_SNUM, "wins support", "No" );
+                               lp_do_parameter( GLOBALS_SNUM, "wins server", "" );
+                               break;
+                       case 1:
+                               lp_do_parameter( GLOBALS_SNUM, "wins support", "Yes" );
+                               lp_do_parameter( GLOBALS_SNUM, "wins server", "" );
+                               break;
+                       case 2:
+                               lp_do_parameter( GLOBALS_SNUM, "wins support", "No" );
+                               lp_do_parameter( GLOBALS_SNUM, "wins server", cgi_variable("WINSAddr"));
+                               break;
+               }
+
+               /* Have to create Homes share? */
+               if ((HomeExpo == 1) && (have_home == -1)) {
+                       pstring unix_share;
+                       
+                       pstrcpy(unix_share,HOMES_NAME);
+                       load_config(False);
+                       lp_copy_service(GLOBALS_SNUM, unix_share);
+                       iNumNonAutoPrintServices = lp_numservices();
+                       have_home = lp_servicenumber(HOMES_NAME);
+                       lp_do_parameter( have_home, "read only", "No");
+                       lp_do_parameter( have_home, "valid users", "%S");
+                       lp_do_parameter( have_home, "browseable", "No");
+                       commit_parameters(have_home);
+               }
+
+               /* Need to Delete Homes share? */
+               if ((HomeExpo == 0) && (have_home != -1)) {
+                       lp_remove_service(have_home);
+                       have_home = -1;
+               }
+
+               commit_parameters(GLOBALS_SNUM);
+               save_reload(0);
+       }
+       else
+       {
+               /* Now determine smb.conf WINS settings */
+               if (lp_wins_support())
+                       winstype = 1;
+               if (lp_wins_server_list() && strlen(*lp_wins_server_list()))
+                       winstype = 2;
+               
+
+               /* Do we have a homes share? */
+               have_home = lp_servicenumber(HOMES_NAME);
+       }
+       if ((winstype == 2) && lp_wins_support())
+               winstype = 3;
+
+       role = lp_server_role();
+       
+       /* Here we go ... */
+       d_printf("<H2>Samba Configuration Wizard</H2>\n");
+       d_printf("<form method=post action=wizard>\n");
+
+       if (have_write_access) {
+               d_printf(_("The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments.\n"));
+               d_printf(_("The same will happen if you press the commit button."));
+               d_printf("<br><br>");
+               d_printf("<center>");
+               d_printf("<input type=submit name=\"Rewrite\" value=%s> &nbsp;&nbsp;",_("Rewrite smb.conf file"));
+               d_printf("<input type=submit name=\"Commit\" value=%s> &nbsp;&nbsp;",_("Commit"));
+               d_printf("<input type=submit name=\"GetWizardParams\" value=%s>", _("Edit Parameter Values"));
+               d_printf("</center>");
+       }
+
+       d_printf("<hr>");
+       d_printf("<center><table border=0>");
+       d_printf("<tr><td><b>%s</b></td>\n", "Server Type:&nbsp;");
+       d_printf("<td><input type=radio name=\"ServerType\" value=0 %s> Stand Alone&nbsp;</td>", (role == ROLE_STANDALONE) ? "checked" : "");
+       d_printf("<td><input type=radio name=\"ServerType\" value=1 %s> Domain Member&nbsp;</td>", (role == ROLE_DOMAIN_MEMBER) ? "checked" : ""); 
+       d_printf("<td><input type=radio name=\"ServerType\" value=2 %s> Domain Controller&nbsp;</td>", (role == ROLE_DOMAIN_PDC) ? "checked" : "");
+       d_printf("</tr>");
+       if (role == ROLE_DOMAIN_BDC) {
+               d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">Unusual Type in smb.conf - Please Select New Mode</font></td></tr>");
+       }
+       d_printf("<tr><td><b>%s</b></td>\n", "Configure WINS As:&nbsp;");
+       d_printf("<td><input type=radio name=\"WINSType\" value=0 %s> Not Used&nbsp;</td>", (winstype == 0) ? "checked" : "");
+       d_printf("<td><input type=radio name=\"WINSType\" value=1 %s> Server for client use&nbsp;</td>", (winstype == 1) ? "checked" : "");
+       d_printf("<td><input type=radio name=\"WINSType\" value=2 %s> Client of another WINS server&nbsp;</td>", (winstype == 2) ? "checked" : "");
+       d_printf("<tr><td></td><td></td><td></td><td>Remote WINS Server&nbsp;<input type=text size=\"16\" name=\"WINSAddr\" value=\"%s\"></td></tr>",lp_wins_server_list());
+       if (winstype == 3) {
+               d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">Error: WINS Server Mode and WINS Support both set in smb.conf</font></td></tr>");
+               d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">Please Select desired WINS mode above.</font></td></tr>");
+       }
+       d_printf("</tr>");
+       d_printf("<tr><td><b>%s</b></td>\n","Expose Home Directories:&nbsp;");
+       d_printf("<td><input type=radio name=\"HomeExpo\" value=1 %s> Yes</td>", (have_home == -1) ? "" : "checked ");
+       d_printf("<td><input type=radio name=\"HomeExpo\" value=0 %s> No</td>", (have_home == -1 ) ? "checked" : "");
+       d_printf("<td></td></tr>");
+       
+       /* Enable this when we are ready ....
+        * d_printf("<tr><td><b>%s</b></td>\n","Is Print Server:&nbsp;");
+        * d_printf("<td><input type=radio name=\"PtrSvr\" value=1 %s> Yes</td>");
+        * d_printf("<td><input type=radio name=\"PtrSvr\" value=0 %s> No</td>");
+        * d_printf("<td></td></tr>");
+        */
+       
+       d_printf("</table></center>");
+       d_printf("<hr>");
+
+       d_printf(_("The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment.\n"));
+       d_printf("</form>\n");
+}
+
+
+/****************************************************************************
+  display a globals editing page  
+****************************************************************************/
+static void globals_page(void)
+{
+       unsigned int parm_filter = FLAG_BASIC;
+       int mode = 0;
+
+       d_printf("<H2>%s</H2>\n", _("Global Variables"));
+
+       if (cgi_variable("Commit")) {
+               commit_parameters(GLOBALS_SNUM);
+               save_reload(0);
+       }
+
+       if ( cgi_variable("ViewMode") )
+               mode = atoi(cgi_variable("ViewMode"));
+
+       d_printf("<form name=\"swatform\" method=post action=globals>\n");
+
+       ViewModeBoxes( mode );
+       switch ( mode ) {
+               case 0:
+                       parm_filter = FLAG_BASIC;
+                       break;
+               case 1:
+                       parm_filter = FLAG_ADVANCED;
+                       break;
+               case 2:
+                       parm_filter = FLAG_DEVELOPER;
+                       break;
+       }
+       d_printf("<br>\n");
+       if (have_write_access) {
+               d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n",
+                       _("Commit Changes"));
+       }
+
+       d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", 
+                _("Reset Values"));
+
+       d_printf("<p>\n");
+       d_printf("<table>\n");
+       show_parameters(GLOBALS_SNUM, 1, parm_filter, 0);
+       d_printf("</table>\n");
+       d_printf("</form>\n");
+}
+
+/****************************************************************************
+  display a shares editing page. share is in unix codepage, and must be in
+  dos codepage. FIXME !!! JRA.
+****************************************************************************/
+static void shares_page(void)
+{
+       const char *share = cgi_variable("share");
+       char *s;
+       int snum = -1;
+       int i;
+       int mode = 0;
+       unsigned int parm_filter = FLAG_BASIC;
+
+       if (share)
+               snum = lp_servicenumber(share);
+
+       d_printf("<H2>%s</H2>\n", _("Share Parameters"));
+
+       if (cgi_variable("Commit") && snum >= 0) {
+               commit_parameters(snum);
+               save_reload(0);
+       }
+
+       if (cgi_variable("Delete") && snum >= 0) {
+               lp_remove_service(snum);
+               save_reload(0);
+               share = NULL;
+               snum = -1;
+       }
+
+       if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+               load_config(False);
+               lp_copy_service(GLOBALS_SNUM, share);
+               iNumNonAutoPrintServices = lp_numservices();
+               save_reload(0);
+               snum = lp_servicenumber(share);
+       }
+
+       d_printf("<FORM name=\"swatform\" method=post>\n");
+
+       d_printf("<table>\n");
+       if ( cgi_variable("ViewMode") )
+               mode = atoi(cgi_variable("ViewMode"));
+       ViewModeBoxes( mode );
+       switch ( mode ) {
+               case 0:
+                       parm_filter = FLAG_BASIC;
+                       break;
+               case 1:
+                       parm_filter = FLAG_ADVANCED;
+                       break;
+               case 2:
+                       parm_filter = FLAG_DEVELOPER;
+                       break;
+       }
+       d_printf("<br><tr>\n");
+       d_printf("<td><input type=submit name=selectshare value=\"%s\"></td>\n", _("Choose Share"));
+       d_printf("<td><select name=share>\n");
+       if (snum < 0)
+               d_printf("<option value=\" \"> \n");
+       for (i=0;i<lp_numservices();i++) {
+               s = lp_servicename(i);
+               if (s && (*s) && strcmp(s,"IPC$") && !lp_print_ok(i)) {
+                       d_printf("<option %s value=\"%s\">%s\n", 
+                              (share && strcmp(share,s)==0)?"SELECTED":"",
+                              s, s);
+               }
+       }
+       d_printf("</select></td>\n");
+       if (have_write_access) {
+               d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Share"));
+       }
+       d_printf("</tr>\n");
+       d_printf("</table>");
+       d_printf("<table>");
+       if (have_write_access) {
+               d_printf("<tr>\n");
+               d_printf("<td><input type=submit name=createshare value=\"%s\"></td>\n", _("Create Share"));
+               d_printf("<td><input type=text size=30 name=newshare></td></tr>\n");
+       }
+       d_printf("</table>");
+
+
+       if (snum >= 0) {
+               if (have_write_access) {
+                       d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+               }
+
+               d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+               d_printf("<p>\n");
+       }
+
+       if (snum >= 0) {
+               d_printf("<table>\n");
+               show_parameters(snum, 1, parm_filter, 0);
+               d_printf("</table>\n");
+       }
+
+       d_printf("</FORM>\n");
+}
+
+/*************************************************************
+change a password either locally or remotely
+*************************************************************/
+static BOOL change_password(const char *remote_machine, const char *user_name, 
+                           const char *old_passwd, const char *new_passwd, 
+                               int local_flags)
+{
+       BOOL ret = False;
+       pstring err_str;
+       pstring msg_str;
+
+       if (demo_mode) {
+               d_printf("%s<p>", _("password change in demo mode rejected\n"));
+               return False;
+       }
+       
+       if (remote_machine != NULL) {
+               ret = remote_password_change(remote_machine, user_name, old_passwd, 
+                                                                        new_passwd, err_str, sizeof(err_str));
+               if(*err_str)
+                       d_printf("%s\n<p>", err_str);
+               return ret;
+       }
+
+       if(!initialize_password_db(True)) {
+               d_printf("Can't setup password database vectors.\n<p>");
+               return False;
+       }
+       
+       ret = local_password_change(user_name, local_flags, new_passwd, err_str, sizeof(err_str),
+                                        msg_str, sizeof(msg_str));
+
+       if(*msg_str)
+               d_printf("%s\n<p>", msg_str);
+       if(*err_str)
+               d_printf("%s\n<p>", err_str);
+
+       return ret;
+}
+
+/****************************************************************************
+  do the stuff required to add or change a password 
+****************************************************************************/
+static void chg_passwd(void)
+{
+       const char *host;
+       BOOL rslt;
+       int local_flags = 0;
+
+       /* Make sure users name has been specified */
+       if (strlen(cgi_variable(SWAT_USER)) == 0) {
+               d_printf("<p>%s", _(" Must specify \"User Name\" \n"));
+               return;
+       }
+
+       /*
+        * smbpasswd doesn't require anything but the users name to delete, disable or enable the user,
+        * so if that's what we're doing, skip the rest of the checks
+        */
+       if (!cgi_variable(DISABLE_USER_FLAG) && !cgi_variable(ENABLE_USER_FLAG) && !cgi_variable(DELETE_USER_FLAG)) {
+
+               /*
+                * If current user is not root, make sure old password has been specified 
+                * If REMOTE change, even root must provide old password 
+                */
+               if (((!am_root()) && (strlen( cgi_variable(OLD_PSWD)) <= 0)) ||
+                   ((cgi_variable(CHG_R_PASSWD_FLAG)) &&  (strlen( cgi_variable(OLD_PSWD)) <= 0))) {
+                       d_printf("<p>%s", _(" Must specify \"Old Password\" \n"));
+                       return;
+               }
+
+               /* If changing a users password on a remote hosts we have to know what host */
+               if ((cgi_variable(CHG_R_PASSWD_FLAG)) && (strlen( cgi_variable(RHOST)) <= 0)) {
+                       d_printf("<p>%s", _(" Must specify \"Remote Machine\" \n"));
+                       return;
+               }
+
+               /* Make sure new passwords have been specified */
+               if ((strlen( cgi_variable(NEW_PSWD)) <= 0) ||
+                   (strlen( cgi_variable(NEW2_PSWD)) <= 0)) {
+                       d_printf("<p>%s", _(" Must specify \"New, and Re-typed Passwords\" \n"));
+                       return;
+               }
+
+               /* Make sure new passwords was typed correctly twice */
+               if (strcmp(cgi_variable(NEW_PSWD), cgi_variable(NEW2_PSWD)) != 0) {
+                       d_printf("<p>%s", _(" Re-typed password didn't match new password\n"));
+                       return;
+               }
+       }
+
+       if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+               host = cgi_variable(RHOST);
+       } else if (am_root()) {
+               host = NULL;
+       } else {
+               host = "127.0.0.1";
+       }
+
+       /*
+        * Set up the local flags.
+        */
+
+       local_flags |= (cgi_variable(ADD_USER_FLAG) ? LOCAL_ADD_USER : 0);
+       local_flags |= (cgi_variable(DELETE_USER_FLAG) ? LOCAL_DELETE_USER : 0);
+       local_flags |= (cgi_variable(ENABLE_USER_FLAG) ? LOCAL_ENABLE_USER : 0);
+       local_flags |= (cgi_variable(DISABLE_USER_FLAG) ? LOCAL_DISABLE_USER : 0);
+
+       rslt = change_password(host,
+                              cgi_variable(SWAT_USER),
+                              cgi_variable(OLD_PSWD), cgi_variable(NEW_PSWD),
+                                  local_flags);
+
+       if(local_flags == 0) {
+               d_printf("<p>");
+               if (rslt == True) {
+                       d_printf(_(" The passwd for '%s' has been changed. \n"), cgi_variable(SWAT_USER));
+               } else {
+                       d_printf(_(" The passwd for '%s' has NOT been changed. \n"), cgi_variable(SWAT_USER));
+               }
+       }
+       
+       return;
+}
+
+/****************************************************************************
+  display a password editing page  
+****************************************************************************/
+static void passwd_page(void)
+{
+       const char *new_name = cgi_user_name();
+
+       /* 
+        * After the first time through here be nice. If the user
+        * changed the User box text to another users name, remember it.
+        */
+       if (cgi_variable(SWAT_USER)) {
+               new_name = cgi_variable(SWAT_USER);
+       } 
+
+       if (!new_name) new_name = "";
+
+       d_printf("<H2>%s</H2>\n", _("Server Password Management"));
+
+       d_printf("<FORM name=\"swatform\" method=post>\n");
+
+       d_printf("<table>\n");
+
+       /* 
+        * Create all the dialog boxes for data collection
+        */
+       d_printf("<tr><td>%s</td>\n", _(" User Name : "));
+       d_printf("<td><input type=text size=30 name=%s value=%s></td></tr> \n", SWAT_USER, new_name);
+       if (!am_root()) {
+               d_printf("<tr><td>%s</td>\n", _(" Old Password : "));
+               d_printf("<td><input type=password size=30 name=%s></td></tr> \n",OLD_PSWD);
+       }
+       d_printf("<tr><td>%s</td>\n", _(" New Password : "));
+       d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+       d_printf("<tr><td>%s</td>\n", _(" Re-type New Password : "));
+       d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+       d_printf("</table>\n");
+
+       /*
+        * Create all the control buttons for requesting action
+        */
+       d_printf("<input type=submit name=%s value=\"%s\">\n", 
+              CHG_S_PASSWD_FLAG, _("Change Password"));
+       if (demo_mode || am_root()) {
+               d_printf("<input type=submit name=%s value=\"%s\">\n",
+                      ADD_USER_FLAG, _("Add New User"));
+               d_printf("<input type=submit name=%s value=\"%s\">\n",
+                      DELETE_USER_FLAG, _("Delete User"));
+               d_printf("<input type=submit name=%s value=\"%s\">\n", 
+                      DISABLE_USER_FLAG, _("Disable User"));
+               d_printf("<input type=submit name=%s value=\"%s\">\n", 
+                      ENABLE_USER_FLAG, _("Enable User"));
+       }
+       d_printf("<p></FORM>\n");
+
+       /*
+        * Do some work if change, add, disable or enable was
+        * requested. It could be this is the first time through this
+        * code, so there isn't anything to do.  */
+       if ((cgi_variable(CHG_S_PASSWD_FLAG)) || (cgi_variable(ADD_USER_FLAG)) || (cgi_variable(DELETE_USER_FLAG)) ||
+           (cgi_variable(DISABLE_USER_FLAG)) || (cgi_variable(ENABLE_USER_FLAG))) {
+               chg_passwd();           
+       }
+
+       d_printf("<H2>%s</H2>\n", _("Client/Server Password Management"));
+
+       d_printf("<FORM name=\"swatform\" method=post>\n");
+
+       d_printf("<table>\n");
+
+       /* 
+        * Create all the dialog boxes for data collection
+        */
+       d_printf("<tr><td>%s</td>\n", _(" User Name : "));
+       d_printf("<td><input type=text size=30 name=%s value=%s></td></tr>\n",SWAT_USER, new_name);
+       d_printf("<tr><td>%s</td>\n", _(" Old Password : "));
+       d_printf("<td><input type=password size=30 name=%s></td></tr>\n",OLD_PSWD);
+       d_printf("<tr><td>%s</td>\n", _(" New Password : "));
+       d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+       d_printf("<tr><td>%s</td>\n", _(" Re-type New Password : "));
+       d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+       d_printf("<tr><td>%s</td>\n", _(" Remote Machine : "));
+       d_printf("<td><input type=text size=30 name=%s></td></tr>\n",RHOST);
+
+       d_printf("</table>");
+
+       /*
+        * Create all the control buttons for requesting action
+        */
+       d_printf("<input type=submit name=%s value=\"%s\">", 
+              CHG_R_PASSWD_FLAG, _("Change Password"));
+
+       d_printf("<p></FORM>\n");
+
+       /*
+        * Do some work if a request has been made to change the
+        * password somewhere other than the server. It could be this
+        * is the first time through this code, so there isn't
+        * anything to do.  */
+       if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+               chg_passwd();           
+       }
+
+}
+
+/****************************************************************************
+  display a printers editing page  
+****************************************************************************/
+static void printers_page(void)
+{
+       const char *share = cgi_variable("share");
+       char *s;
+       int snum=-1;
+       int i;
+       int mode = 0;
+       unsigned int parm_filter = FLAG_BASIC;
+
+       if (share)
+               snum = lp_servicenumber(share);
+
+        d_printf("<H2>%s</H2>\n", _("Printer Parameters"));
+        d_printf("<H3>%s</H3>\n", _("Important Note:"));
+        d_printf(_("Printer names marked with [*] in the Choose Printer drop-down box "));
+        d_printf(_("are autoloaded printers from "));
+        d_printf("<A HREF=\"/swat/help/smb.conf.5.html#printcapname\" target=\"docs\">%s</A>\n", _("Printcap Name"));
+        d_printf(_("Attempting to delete these printers from SWAT will have no effect.\n"));
+
+       if (cgi_variable("Commit") && snum >= 0) {
+               commit_parameters(snum);
+               if (snum >= iNumNonAutoPrintServices)
+                   save_reload(snum);
+               else
+                   save_reload(0);
+       }
+
+       if (cgi_variable("Delete") && snum >= 0) {
+               lp_remove_service(snum);
+               save_reload(0);
+               share = NULL;
+               snum = -1;
+       }
+
+       if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+               load_config(False);
+               lp_copy_service(GLOBALS_SNUM, share);
+               iNumNonAutoPrintServices = lp_numservices();
+               snum = lp_servicenumber(share);
+               lp_do_parameter(snum, "print ok", "Yes");
+               save_reload(0);
+               snum = lp_servicenumber(share);
+       }
+
+       d_printf("<FORM name=\"swatform\" method=post>\n");
+
+       if ( cgi_variable("ViewMode") )
+               mode = atoi(cgi_variable("ViewMode"));
+       ViewModeBoxes( mode );
+       switch ( mode ) {
+               case 0:
+                       parm_filter = FLAG_BASIC;
+                       break;
+               case 1:
+                       parm_filter = FLAG_ADVANCED;
+                       break;
+               case 2:
+                       parm_filter = FLAG_DEVELOPER;
+                       break;
+       }
+       d_printf("<table>\n");
+       d_printf("<tr><td><input type=submit name=selectshare value=\"%s\"></td>\n", _("Choose Printer"));
+       d_printf("<td><select name=share>\n");
+       if (snum < 0 || !lp_print_ok(snum))
+               d_printf("<option value=\" \"> \n");
+       for (i=0;i<lp_numservices();i++) {
+               s = lp_servicename(i);
+               if (s && (*s) && strcmp(s,"IPC$") && lp_print_ok(i)) {
+                    if (i >= iNumNonAutoPrintServices)
+                        d_printf("<option %s value=\"%s\">[*]%s\n",
+                               (share && strcmp(share,s)==0)?"SELECTED":"",
+                               s, s);
+                    else
+                       d_printf("<option %s value=\"%s\">%s\n", 
+                              (share && strcmp(share,s)==0)?"SELECTED":"",
+                              s, s);
+               }
+       }
+       d_printf("</select></td>");
+       if (have_write_access) {
+               d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Printer"));
+       }
+       d_printf("</tr>");
+       d_printf("</table>\n");
+
+       if (have_write_access) {
+               d_printf("<table>\n");
+               d_printf("<tr><td><input type=submit name=createshare value=\"%s\"></td>\n", _("Create Printer"));
+               d_printf("<td><input type=text size=30 name=newshare></td></tr>\n");
+               d_printf("</table>");
+       }
+
+
+       if (snum >= 0) {
+               if (have_write_access) {
+                       d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+               }
+               d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+               d_printf("<p>\n");
+       }
+
+       if (snum >= 0) {
+               d_printf("<table>\n");
+               show_parameters(snum, 1, parm_filter, 1);
+               d_printf("</table>\n");
+       }
+       d_printf("</FORM>\n");
+}
+
+
+/**
+ * main function for SWAT.
+ **/
+ int main(int argc, char *argv[])
+{
+       extern char *optarg;
+       extern int optind;
+       int opt;
+       char *page;
+
+       fault_setup(NULL);
+       umask(S_IWGRP | S_IWOTH);
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+       set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+       /* just in case it goes wild ... */
+       alarm(300);
+
+       setlinebuf(stdout);
+
+       /* we don't want any SIGPIPE messages */
+       BlockSignals(True,SIGPIPE);
+
+       dbf = x_fopen("/dev/null", O_WRONLY, 0);
+       if (!dbf) dbf = x_stderr;
+
+       /* we don't want stderr screwing us up */
+       close(2);
+       open("/dev/null", O_WRONLY);
+
+       while ((opt = getopt(argc, argv,"s:a")) != EOF) {
+               switch (opt) {
+               case 's':
+                       pstrcpy(dyn_CONFIGFILE,optarg);
+                       break;    
+               case 'a':
+                       demo_mode = True;
+                       break;    
+               }
+       }
+
+       setup_logging(argv[0],False);
+       load_config(True);
+       iNumNonAutoPrintServices = lp_numservices();
+       load_printers();
+
+       cgi_setup(dyn_SWATDIR, !demo_mode);
+
+       print_header();
+
+       cgi_load_variables();
+
+       if (!file_exist(dyn_CONFIGFILE, NULL)) {
+               have_read_access = True;
+               have_write_access = True;
+       } else {
+               /* check if the authenticated user has write access - if not then
+                  don't show write options */
+               have_write_access = (access(dyn_CONFIGFILE,W_OK) == 0);
+
+               /* if the user doesn't have read access to smb.conf then
+                  don't let them view it */
+               have_read_access = (access(dyn_CONFIGFILE,R_OK) == 0);
+       }
+
+       show_main_buttons();
+
+       page = cgi_pathinfo();
+
+       /* Root gets full functionality */
+       if (have_read_access && strcmp(page, "globals")==0) {
+               globals_page();
+       } else if (have_read_access && strcmp(page,"shares")==0) {
+               shares_page();
+       } else if (have_read_access && strcmp(page,"printers")==0) {
+               printers_page();
+       } else if (have_read_access && strcmp(page,"status")==0) {
+               status_page();
+       } else if (have_read_access && strcmp(page,"viewconfig")==0) {
+               viewconfig_page();
+       } else if (strcmp(page,"passwd")==0) {
+               passwd_page();
+       } else if (have_read_access && strcmp(page,"wizard")==0) {
+               wizard_page();
+       } else if (have_read_access && strcmp(page,"wizard_params")==0) {
+               wizard_params_page();
+       } else if (have_read_access && strcmp(page,"rewritecfg")==0) {
+               rewritecfg_file();
+       } else {
+               welcome_page();
+       }
+
+       print_footer();
+       return 0;
+}
+
+/** @} **/
diff --git a/source4/wrepld/parser.c b/source4/wrepld/parser.c
new file mode 100644 (file)
index 0000000..b619cb0
--- /dev/null
@@ -0,0 +1,759 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Jean François Micouleau      1998-2002.
+ *  
+ *  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 "wins_repl.h"
+
+extern TALLOC_CTX *mem_ctx;
+
+/****************************************************************************
+grow the send buffer if necessary
+****************************************************************************/
+BOOL grow_buffer(struct BUFFER *buffer, int more)
+{
+       char *temp;
+
+       DEBUG(10,("grow_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+       
+       /* grow by at least 256 bytes */
+       if (more<256)
+               more=256;
+
+       if (buffer->offset+more >= buffer->length) {
+               temp=(char *)talloc_realloc(mem_ctx, buffer->buffer, sizeof(char)* (buffer->length+more) );
+               if (temp==NULL) {
+                       DEBUG(0,("grow_buffer: can't grow buffer\n"));
+                       return False;
+               }
+               buffer->length+=more;
+               buffer->buffer=temp;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+check if the buffer has that much data
+****************************************************************************/
+static BOOL check_buffer(struct BUFFER *buffer, int more)
+{
+       DEBUG(10,("check_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+       
+       if (buffer->offset+more > buffer->length) {
+               DEBUG(10,("check_buffer: buffer smaller than requested, size is: %d needed: %d\n", buffer->length, buffer->offset+more));
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+decode a WINS_OWNER struct
+****************************************************************************/
+static void decode_wins_owner(struct BUFFER *inbuf, WINS_OWNER *wins_owner)
+{
+       if(!check_buffer(inbuf, 24))
+               return;
+
+       wins_owner->address.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+       wins_owner->max_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+4))<<32;
+       wins_owner->max_version|=RIVAL(inbuf->buffer, inbuf->offset+8);
+       wins_owner->min_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+12))<<32;
+       wins_owner->min_version|=RIVAL(inbuf->buffer, inbuf->offset+16);
+       wins_owner->type=RIVAL(inbuf->buffer, inbuf->offset+20);
+       inbuf->offset+=24;
+
+}
+
+/****************************************************************************
+decode a WINS_NAME struct
+****************************************************************************/
+static void decode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{      
+       char *p;
+       int i;
+
+       if(!check_buffer(outbuf, 40))
+               return;
+
+       wins_name->name_len=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+       memcpy(wins_name->name,outbuf->buffer+outbuf->offset, 15);
+       wins_name->name[15]='\0';
+       if((p = strchr(wins_name->name,' ')) != NULL)
+               *p = 0;
+
+       outbuf->offset+=15;
+
+       wins_name->type=(int)outbuf->buffer[outbuf->offset++];
+       
+       /*
+        * fix to bug in WINS replication,
+        * present in all versions including W2K SP2 !
+        */
+       if (wins_name->name[0]==0x1B) {
+               wins_name->name[0]=(char)wins_name->type;
+               wins_name->type=0x1B;
+       }
+       
+       wins_name->empty=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+       
+       wins_name->name_flag=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+       wins_name->group_flag=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+       wins_name->id=((SMB_BIG_UINT)RIVAL(outbuf->buffer, outbuf->offset))<<32;
+       outbuf->offset+=4;
+       wins_name->id|=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+       
+       /* special groups have multiple address */
+       if (wins_name->name_flag & 2) {
+               if(!check_buffer(outbuf, 4))
+                       return;
+               wins_name->num_ip=IVAL(outbuf->buffer, outbuf->offset);
+               outbuf->offset+=4;
+       }
+       else
+               wins_name->num_ip=1;
+
+       if(!check_buffer(outbuf, 4))
+               return;
+       wins_name->owner.s_addr=IVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+
+       if (wins_name->name_flag & 2) {
+               wins_name->others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*wins_name->num_ip);
+               if (wins_name->others==NULL)
+                       return;
+
+               if(!check_buffer(outbuf, 4*wins_name->num_ip))
+                       return;
+               for (i=0; i<wins_name->num_ip; i++) {
+                       wins_name->others[i].s_addr=IVAL(outbuf->buffer, outbuf->offset);
+                       outbuf->offset+=4;
+               }
+       }
+
+       if(!check_buffer(outbuf, 4))
+               return;
+       wins_name->foo=RIVAL(outbuf->buffer, outbuf->offset);
+       outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a update notification request
+****************************************************************************/
+static void decode_update_notify_request(struct BUFFER *inbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+       int i;
+
+       if(!check_buffer(inbuf, 4))
+               return;
+       un_rq->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+       inbuf->offset+=4;
+
+       un_rq->wins_owner=(WINS_OWNER *)talloc(mem_ctx, un_rq->partner_count*sizeof(WINS_OWNER));
+       if (un_rq->wins_owner==NULL)
+               return;
+
+       for (i=0; i<un_rq->partner_count; i++)
+               decode_wins_owner(inbuf, &un_rq->wins_owner[i]);
+
+       if(!check_buffer(inbuf, 4))
+               return;
+       un_rq->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+       inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void decode_send_entries_request(struct BUFFER *inbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+       decode_wins_owner(inbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void decode_send_entries_reply(struct BUFFER *inbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+       int i;
+
+       if(!check_buffer(inbuf, 4))
+               return;
+       se_rp->max_names = RIVAL(inbuf->buffer, inbuf->offset);
+       inbuf->offset+=4;
+
+       se_rp->wins_name=(WINS_NAME *)talloc(mem_ctx, se_rp->max_names*sizeof(WINS_NAME));
+       if (se_rp->wins_name==NULL)
+               return;
+
+       for (i=0; i<se_rp->max_names; i++)
+               decode_wins_name(inbuf, &se_rp->wins_name[i]);
+}
+
+/****************************************************************************
+decode a add version number map table reply
+****************************************************************************/
+static void decode_add_version_number_map_table_reply(struct BUFFER *inbuf, AVMT_REP *avmt_rep)
+{
+       int i;
+
+       if(!check_buffer(inbuf, 4))
+               return;
+
+       avmt_rep->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+       inbuf->offset+=4;
+
+       avmt_rep->wins_owner=(WINS_OWNER *)talloc(mem_ctx, avmt_rep->partner_count*sizeof(WINS_OWNER));
+       if (avmt_rep->wins_owner==NULL)
+               return;
+
+       for (i=0; i<avmt_rep->partner_count; i++)
+               decode_wins_owner(inbuf, &avmt_rep->wins_owner[i]);
+
+       if(!check_buffer(inbuf, 4))
+               return;
+       avmt_rep->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+       inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void decode_replicate(struct BUFFER *inbuf, REPLICATE *rep)
+{
+       if(!check_buffer(inbuf, 4))
+               return;
+
+       rep->msg_type = RIVAL(inbuf->buffer, inbuf->offset);
+
+       inbuf->offset+=4;
+
+       switch (rep->msg_type) {
+               case 0:
+                       break;
+               case 1:
+                       /* add version number map table reply */
+                       decode_add_version_number_map_table_reply(inbuf, &rep->avmt_rep);
+                       break;
+               case 2:
+                       /* send entry request */
+                       decode_send_entries_request(inbuf, &rep->se_rq);
+                       break;
+               case 3:
+                       /* send entry request */
+                       decode_send_entries_reply(inbuf, &rep->se_rp);
+                       break;
+               case 4:
+                       /* update notification request */
+                       decode_update_notify_request(inbuf, &rep->un_rq);
+                       break;
+               default:
+                       DEBUG(0,("decode_replicate: unknown message type:%d\n", rep->msg_type));
+                       break;
+       }
+}
+
+/****************************************************************************
+read the generic header and fill the struct.
+****************************************************************************/
+static void read_generic_header(struct BUFFER *inbuf, generic_header *q)
+{
+       if(!check_buffer(inbuf, 16))
+               return;
+
+       q->data_size = RIVAL(inbuf->buffer, inbuf->offset+0);
+       q->opcode    = RIVAL(inbuf->buffer, inbuf->offset+4);
+       q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+8);
+       q->mess_type = RIVAL(inbuf->buffer, inbuf->offset+12);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void decode_start_assoc_request(struct BUFFER *inbuf, START_ASSOC_REQUEST *q)
+{
+       if(!check_buffer(inbuf, 8))
+               return;
+
+       q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+0);
+       q->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+       q->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_start_assoc_reply(struct BUFFER *inbuf, START_ASSOC_REPLY *r)
+{
+       if(!check_buffer(inbuf, 8))
+               return;
+
+       r->assoc_ctx=RIVAL(inbuf->buffer, inbuf->offset+0);
+       r->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+       r->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_stop_assoc(struct BUFFER *inbuf, STOP_ASSOC *r)
+{
+       if(!check_buffer(inbuf, 4))
+               return;
+
+       r->reason=RIVAL(inbuf->buffer, inbuf->offset);
+}
+
+/****************************************************************************
+decode a packet and fill a generic structure
+****************************************************************************/
+void decode_generic_packet(struct BUFFER *inbuf, GENERIC_PACKET *q)
+{
+       read_generic_header(inbuf, &q->header);
+
+       inbuf->offset+=16;
+
+       switch (q->header.mess_type) {
+               case 0:
+                       decode_start_assoc_request(inbuf, &q->sa_rq);
+                       break;
+               case 1:
+                       decode_start_assoc_reply(inbuf, &q->sa_rp);
+                       break;
+               case 2:
+                       decode_stop_assoc(inbuf, &q->so);
+                       break;
+               case 3:
+                       decode_replicate(inbuf, &q->rep);
+                       break;
+               default:
+                       DEBUG(0,("decode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+                       break;
+       }
+}
+
+/****************************************************************************
+encode a WINS_OWNER struct
+****************************************************************************/
+static void encode_wins_owner(struct BUFFER *outbuf, WINS_OWNER *wins_owner)
+{
+       if (!grow_buffer(outbuf, 24))
+               return;
+
+       SIVAL(outbuf->buffer, outbuf->offset, wins_owner->address.s_addr);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version>>32));
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version&0xffffffff));
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version>>32);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version&0xffffffff);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->type);
+       outbuf->offset+=4;
+       
+}
+
+/****************************************************************************
+encode a WINS_NAME struct
+****************************************************************************/
+static void encode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{      
+       int i;
+
+       if (!grow_buffer(outbuf, 48+(4*wins_name->num_ip)))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_len);
+       outbuf->offset+=4;
+       
+       memset(outbuf->buffer+outbuf->offset, ' ', 15);
+
+       /* to prevent copying the leading \0 */
+       memcpy(outbuf->buffer+outbuf->offset, wins_name->name, strlen(wins_name->name));
+       outbuf->offset+=15;             
+
+       outbuf->buffer[outbuf->offset++]=(char)wins_name->type;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->empty);
+       outbuf->offset+=4;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_flag);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->group_flag);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id>>32);
+       outbuf->offset+=4;
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id);
+       outbuf->offset+=4;
+
+       if (wins_name->name_flag & 2) {
+               SIVAL(outbuf->buffer, outbuf->offset, wins_name->num_ip);
+               outbuf->offset+=4;
+       }       
+
+       SIVAL(outbuf->buffer, outbuf->offset, wins_name->owner.s_addr);
+       outbuf->offset+=4;
+
+       if (wins_name->name_flag & 2) {
+               for (i=0;i<wins_name->num_ip;i++) {
+                       SIVAL(outbuf->buffer, outbuf->offset, wins_name->others[i].s_addr);
+                       outbuf->offset+=4;
+               }
+       }       
+
+       RSIVAL(outbuf->buffer, outbuf->offset, wins_name->foo);
+       outbuf->offset+=4;
+}
+
+/****************************************************************************
+encode a update notification request
+****************************************************************************/
+static void encode_update_notify_request(struct BUFFER *outbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+       int i;
+
+       if (!grow_buffer(outbuf, 8))
+               return;
+               
+       RSIVAL(outbuf->buffer, outbuf->offset, un_rq->partner_count);
+       outbuf->offset+=4;
+
+       for (i=0; i<un_rq->partner_count; i++)
+               encode_wins_owner(outbuf,  &un_rq->wins_owner[i]);
+
+       SIVAL(outbuf->buffer, outbuf->offset, un_rq->initiating_wins_server.s_addr);
+       outbuf->offset+=4;
+       
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void encode_send_entries_request(struct BUFFER *outbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+       encode_wins_owner(outbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void encode_send_entries_reply(struct BUFFER *outbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+       int i;
+
+       if (!grow_buffer(outbuf, 4))
+               return;
+               
+       RSIVAL(outbuf->buffer, outbuf->offset, se_rp->max_names);
+       outbuf->offset+=4;
+
+       for (i=0; i<se_rp->max_names; i++)
+               encode_wins_name(outbuf, &se_rp->wins_name[i]);
+
+}
+
+/****************************************************************************
+encode a add version number map table reply
+****************************************************************************/
+static void encode_add_version_number_map_table_reply(struct BUFFER *outbuf, AVMT_REP *avmt_rep)
+{
+       int i;
+
+       if (!grow_buffer(outbuf, 8))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, avmt_rep->partner_count);
+       outbuf->offset+=4;
+       
+       for (i=0; i<avmt_rep->partner_count; i++)
+               encode_wins_owner(outbuf, &avmt_rep->wins_owner[i]);
+
+       SIVAL(outbuf->buffer, outbuf->offset, avmt_rep->initiating_wins_server.s_addr);
+       outbuf->offset+=4;
+       
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void encode_replicate(struct BUFFER *outbuf, REPLICATE *rep)
+{
+       if (!grow_buffer(outbuf, 4))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, rep->msg_type);
+       outbuf->offset+=4;
+
+       switch (rep->msg_type) {
+               case 0:
+                       break;
+               case 1:
+                       /* add version number map table reply */
+                       encode_add_version_number_map_table_reply(outbuf, &rep->avmt_rep);
+                       break;
+               case 2:
+                       /* send entry request */
+                       encode_send_entries_request(outbuf, &rep->se_rq);
+                       break;
+               case 3:
+                       /* send entry request */
+                       encode_send_entries_reply(outbuf, &rep->se_rp);
+                       break;
+               case 4:
+                       /* update notification request */
+                       encode_update_notify_request(outbuf, &rep->un_rq);
+                       break;
+               default:
+                       DEBUG(0,("encode_replicate: unknown message type:%d\n", rep->msg_type));
+                       break;
+       }
+}
+
+/****************************************************************************
+write the generic header.
+****************************************************************************/
+static void write_generic_header(struct BUFFER *outbuf, generic_header *r)
+{
+       RSIVAL(outbuf->buffer, 0, r->data_size);
+       RSIVAL(outbuf->buffer, 4, r->opcode);
+       RSIVAL(outbuf->buffer, 8, r->assoc_ctx);
+       RSIVAL(outbuf->buffer,12, r->mess_type);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void encode_start_assoc_request(struct BUFFER *outbuf, START_ASSOC_REQUEST *q)
+{
+       if (!grow_buffer(outbuf, 45))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, q->assoc_ctx);
+       RSSVAL(outbuf->buffer, outbuf->offset+4, q->min_ver);
+       RSSVAL(outbuf->buffer, outbuf->offset+6, q->maj_ver);
+       
+       outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_start_assoc_reply(struct BUFFER *outbuf, START_ASSOC_REPLY *r)
+{
+       if (!grow_buffer(outbuf, 45))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, r->assoc_ctx);
+       RSSVAL(outbuf->buffer, outbuf->offset+4, r->min_ver);
+       RSSVAL(outbuf->buffer, outbuf->offset+6, r->maj_ver);
+
+       outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_stop_assoc(struct BUFFER *outbuf, STOP_ASSOC *r)
+{
+       if (!grow_buffer(outbuf, 44))
+               return;
+
+       RSIVAL(outbuf->buffer, outbuf->offset, r->reason);
+       
+       outbuf->offset=44;
+}
+
+/****************************************************************************
+write the generic header size.
+****************************************************************************/
+static void write_generic_header_size(generic_header *r, int size)
+{
+       /* the buffer size is the total size minus the size field */
+       r->data_size=size-4;
+}
+
+/****************************************************************************
+encode a packet and read a generic structure
+****************************************************************************/
+void encode_generic_packet(struct BUFFER *outbuf, GENERIC_PACKET *q)
+{
+       if (!grow_buffer(outbuf, 16))
+               return;
+
+       outbuf->offset=16;
+
+       switch (q->header.mess_type) {
+               case 0:
+                       encode_start_assoc_request(outbuf, &q->sa_rq);
+                       break;
+               case 1:
+                       encode_start_assoc_reply(outbuf, &q->sa_rp);
+                       break;
+               case 2:
+                       encode_stop_assoc(outbuf, &q->so);
+                       break;
+               case 3:
+                       encode_replicate(outbuf, &q->rep);
+                       break;
+               default:
+                       DEBUG(0,("encode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+                       break;
+       }
+       
+       write_generic_header_size(&q->header, outbuf->offset);
+       write_generic_header(outbuf, &q->header);
+}
+
+
+/****************************************************************************
+dump a WINS_OWNER structure
+****************************************************************************/
+static void dump_wins_owner(WINS_OWNER *wins_owner)
+{
+       DEBUGADD(10,("\t\t\t\taddress         : %s\n", inet_ntoa(wins_owner->address)));
+       DEBUGADD(10,("\t\t\t\tmax version: %d\n", (int)wins_owner->max_version));
+       DEBUGADD(10,("\t\t\t\tmin version: %d\n", (int)wins_owner->min_version));
+       DEBUGADD(10,("\t\t\t\ttype            : %d\n", wins_owner->type));
+}
+
+/****************************************************************************
+dump a WINS_NAME structure
+****************************************************************************/
+static void dump_wins_name(WINS_NAME *wins_name)
+{
+       fstring name;
+       int i;
+
+       strncpy(name, wins_name->name, 15);
+
+       DEBUGADD(10,("name: %d, %s<%02x> %x,%x, %d %s %d ", wins_name->name_len, name, wins_name->type,
+                   wins_name->name_flag, wins_name->group_flag, (int)wins_name->id,
+                   inet_ntoa(wins_name->owner), wins_name->num_ip));
+
+       if (wins_name->num_ip!=1)
+               for (i=0; i<wins_name->num_ip; i++)
+                       DEBUGADD(10,("%s ", inet_ntoa(wins_name->others[i])));  
+
+       DEBUGADD(10,("\n"));
+}
+
+/****************************************************************************
+dump a replicate structure
+****************************************************************************/
+static void dump_replicate(REPLICATE *rep)
+{
+       int i;
+
+       DEBUGADD(5,("\t\tmsg_type: %d ", rep->msg_type));
+
+       switch (rep->msg_type) {
+               case 0:
+                       DEBUGADD(5,("(Add Version Map Table Request)\n"));
+                       break;
+               case 1:
+                       DEBUGADD(5,("(Add Version Map Table Reply)\n"));
+                       DEBUGADD(5,("\t\t\tpartner_count         : %d\n", rep->avmt_rep.partner_count));
+                       for (i=0; i<rep->avmt_rep.partner_count; i++)
+                               dump_wins_owner(&rep->avmt_rep.wins_owner[i]);
+                       DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->avmt_rep.initiating_wins_server)));
+                       break;
+               case 2:
+                       DEBUGADD(5,("(Send Entries Request)\n"));
+                       dump_wins_owner(&rep->se_rq.wins_owner);
+                       break;
+               case 3:
+                       DEBUGADD(5,("(Send Entries Reply)\n"));
+                       DEBUGADD(5,("\t\t\tmax_names         : %d\n", rep->se_rp.max_names));
+                       for (i=0; i<rep->se_rp.max_names; i++)
+                               dump_wins_name(&rep->se_rp.wins_name[i]);
+                       break;
+               case 4:
+                       DEBUGADD(5,("(Update Notify Request)\n"));
+                       DEBUGADD(5,("\t\t\tpartner_count         : %d\n", rep->un_rq.partner_count));
+                       for (i=0; i<rep->un_rq.partner_count; i++)
+                               dump_wins_owner(&rep->un_rq.wins_owner[i]);
+                       DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->un_rq.initiating_wins_server)));
+                       break;
+               default:
+                       DEBUG(5,("\n"));
+                       break;
+       }
+}
+
+/****************************************************************************
+dump a generic structure
+****************************************************************************/
+void dump_generic_packet(GENERIC_PACKET *q)
+{
+       DEBUG(5,("dump_generic_packet:\n"));
+       DEBUGADD(5,("\tdata_size: %08x\n", q->header.data_size));
+       DEBUGADD(5,("\topcode   : %08x\n", q->header.opcode));
+       DEBUGADD(5,("\tassoc_ctx: %08x\n", q->header.assoc_ctx));
+       DEBUGADD(5,("\tmess_type: %08x ", q->header.mess_type));
+
+       switch (q->header.mess_type) {
+               case 0:
+                       DEBUGADD(5,("(Start Association Request)\n"));
+                       DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rq.assoc_ctx));
+                       DEBUGADD(5,("\t\tmin_ver  : %04x\n", q->sa_rq.min_ver));
+                       DEBUGADD(5,("\t\tmaj_ver  : %04x\n", q->sa_rq.maj_ver));
+                       break;
+               case 1:
+                       DEBUGADD(5,("(Start Association Reply)\n"));
+                       DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rp.assoc_ctx));
+                       DEBUGADD(5,("\t\tmin_ver  : %04x\n", q->sa_rp.min_ver));
+                       DEBUGADD(5,("\t\tmaj_ver  : %04x\n", q->sa_rp.maj_ver));
+                       break;
+               case 2:
+                       DEBUGADD(5,("(Stop Association)\n"));
+                       DEBUGADD(5,("\t\treason: %08x\n", q->so.reason));
+                       break;
+               case 3:
+                       DEBUGADD(5,("(Replication Message)\n"));
+                       dump_replicate(&q->rep);
+                       break;
+               default:
+                       DEBUG(5,("\n"));
+                       break;
+       }
+
+}
+
+/****************************************************************************
+generate a stop packet
+****************************************************************************/
+void stop_packet(GENERIC_PACKET *q, GENERIC_PACKET *r, int reason)
+{
+       r->header.opcode=OPCODE_NON_NBT;
+       r->header.assoc_ctx=get_server_assoc(q->header.assoc_ctx);
+       r->header.mess_type=MESSAGE_TYPE_STOP_ASSOC;
+       r->so.reason=reason;
+       
+}
+
+
diff --git a/source4/wrepld/partners.c b/source4/wrepld/partners.c
new file mode 100644 (file)
index 0000000..2387f5b
--- /dev/null
@@ -0,0 +1,200 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process incoming packets - main loop
+   Copyright (C) Jean François Micouleau      1998-2002.
+   
+   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 "wins_repl.h"
+
+/* we can exchange info with 64 partners at any given time */
+WINS_PARTNER current_partners[64];
+int total_current_partners;
+
+/*******************************************************************
+verify if we know this partner
+********************************************************************/
+BOOL check_partner(int assoc)
+{
+       int i;
+
+       DEBUG(5,("check_partner: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==assoc)
+                       return True;
+
+       return False;
+}
+
+/*******************************************************************
+add a new entry to the list
+********************************************************************/
+BOOL add_partner(int client_assoc, int server_assoc, BOOL pull, BOOL push)
+{
+       DEBUG(5,("add_partner: total_current_partners: %d\n", total_current_partners));
+
+       if (total_current_partners==64)
+               return False;
+
+       current_partners[total_current_partners].client_assoc=client_assoc;
+       current_partners[total_current_partners].server_assoc=server_assoc;
+       current_partners[total_current_partners].pull_partner=pull;
+       current_partners[total_current_partners].push_partner=push;
+
+       total_current_partners++;
+
+       return True;
+}
+
+/*******************************************************************
+remove an entry to the list
+********************************************************************/
+BOOL remove_partner(int client_assoc)
+{
+       int i,j;
+
+       DEBUG(5,("remove_partner: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; current_partners[i].client_assoc!=client_assoc && i<total_current_partners; i++)
+               ;
+       
+       if (i==total_current_partners)
+               return False;
+
+       for (j=i+1; j<total_current_partners; j++) {
+               current_partners[j-1].client_assoc=current_partners[j].client_assoc;
+               current_partners[j-1].server_assoc=current_partners[j].server_assoc;
+               current_partners[j-1].pull_partner=current_partners[j].pull_partner;
+               current_partners[j-1].push_partner=current_partners[j].push_partner;
+               current_partners[j-1].partner_server.s_addr=current_partners[j].partner_server.s_addr;
+               current_partners[j-1].other_server.s_addr=current_partners[j].other_server.s_addr;
+       }
+
+       total_current_partners--;
+
+       return True;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL update_server_partner(int client_assoc, int server_assoc)
+{
+       int i;
+       
+       DEBUG(5,("update_server_partner: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==client_assoc) {
+                       current_partners[i].server_assoc=server_assoc;
+                       return True;
+               }
+
+       return False;
+}
+
+/*******************************************************************
+verify if it's a pull partner
+********************************************************************/
+BOOL check_pull_partner(int assoc)
+{
+       int i;
+       
+       DEBUG(5,("check_pull_partner: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==assoc &&
+                   current_partners[i].pull_partner==True)
+                       return True;
+
+       return False;
+}
+
+/*******************************************************************
+verify if it's a push partner
+********************************************************************/
+BOOL check_push_partner(int assoc)
+{
+       int i;
+       
+       DEBUG(5,("check_push_partner: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==assoc &&
+                   current_partners[i].push_partner==True)
+                       return True;
+
+       return False;
+}
+
+/*******************************************************************
+return the server ctx linked to the client ctx
+********************************************************************/
+int get_server_assoc(int assoc)
+{
+       int i;
+       
+       DEBUG(5,("get_server_assoc: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==assoc)
+                       return current_partners[i].server_assoc;
+
+       return 0;
+}
+
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL write_server_assoc_table(int client_assoc, struct in_addr partner, struct in_addr server)
+{
+       int i;
+       
+       DEBUG(5,("write_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==client_assoc) {
+                       current_partners[i].partner_server=partner;
+                       current_partners[i].other_server=server;
+                       return True;
+               }
+
+       return False;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL get_server_assoc_table(int client_assoc, struct in_addr *partner, struct in_addr *server)
+{
+       int i;
+       
+       DEBUG(5,("get_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+       for (i=0; i<total_current_partners; i++)
+               if (current_partners[i].client_assoc==client_assoc) {
+                       partner->s_addr=current_partners[i].partner_server.s_addr;
+                       server->s_addr=current_partners[i].other_server.s_addr;
+                       return True;
+               }
+
+       return False;
+}
+
+
diff --git a/source4/wrepld/process.c b/source4/wrepld/process.c
new file mode 100644 (file)
index 0000000..1f96dc9
--- /dev/null
@@ -0,0 +1,983 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process incoming packets - main loop
+   Copyright (C) Jean François Micouleau      1998-2002.
+   
+   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 "wins_repl.h"
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+WINS_OWNER global_wins_table[64][64];
+int partner_count;
+
+TALLOC_CTX *mem_ctx;
+
+#define WINS_LIST "wins.tdb"
+#define INFO_VERSION   "INFO/version"
+#define INFO_COUNT     "INFO/num_entries"
+#define INFO_ID_HIGH   "INFO/id_high"
+#define INFO_ID_LOW    "INFO/id_low"
+#define ENTRY_PREFIX   "ENTRY/"
+
+
+/*******************************************************************
+fill the header of a reply.
+********************************************************************/
+static void fill_header(GENERIC_PACKET *g, int opcode, int ctx, int mess)
+{
+       if (g==NULL)
+               return;
+
+       g->header.opcode=opcode;
+       g->header.assoc_ctx=ctx;
+       g->header.mess_type=mess;
+}
+
+/*******************************************************************
+dump the global table, that's a debug code.
+********************************************************************/
+static void dump_global_table(void)
+{
+       int i,j;
+       
+       for (i=0;i<partner_count;i++) {
+               DEBUG(10,("\n%d ", i));
+               for (j=0; global_wins_table[i][j].address.s_addr!=0; j++)
+                       DEBUG(10,("%s:%d \t", inet_ntoa(global_wins_table[i][j].address),
+                               (int)global_wins_table[i][j].max_version));
+       }
+       DEBUG(10,("\n"));
+}
+
+/*******************************************************************
+start association
+********************************************************************/
+static void start_assoc_process(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       /*
+        * add this request to our current wins partners list
+        * this list is used to know with who we are in contact
+        *
+        */
+       r->sa_rp.assoc_ctx=time(NULL);
+       fill_header(r, OPCODE_NON_NBT, q->sa_rq.assoc_ctx, MESSAGE_TYPE_START_ASSOC_REPLY);
+
+       /* reply we are a NT4 server */
+       
+       /* w2K is min=2, maj=5 */
+       
+       r->sa_rp.min_ver=1;
+       r->sa_rp.maj_ver=1;
+
+       add_partner(r->sa_rp.assoc_ctx, q->sa_rq.assoc_ctx, False, False);
+}
+
+/*******************************************************************
+start association reply
+********************************************************************/
+static void start_assoc_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       int i;
+
+       /* check if we have already registered this client */
+       if (!check_partner(q->header.assoc_ctx)) {
+               DEBUG(0,("start_assoc_reply: unknown client\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       if (!update_server_partner(q->header.assoc_ctx, q->sa_rp.assoc_ctx)) {
+               DEBUG(0,("start_assoc_reply: can't update server ctx\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       /* if pull, request map table */        
+       if (check_pull_partner(q->header.assoc_ctx)) {
+               fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+               r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+               DEBUG(5,("start_assoc_reply: requesting map table\n"));
+
+               return;
+       }
+
+       /* if push, send our table */
+       if (check_push_partner(q->header.assoc_ctx)) {
+               fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+               r->rep.msg_type=MESSAGE_REP_UPDATE_NOTIFY_REQUEST;
+               r->rep.un_rq.partner_count=partner_count;
+               
+               r->rep.un_rq.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+               if (r->rep.un_rq.wins_owner==NULL) {
+                       DEBUG(0,("start_assoc_reply: can't alloc memory\n"));
+                       stop_packet(q, r, STOP_REASON_USER_REASON);
+                       return;
+               }
+
+               for (i=0; i<partner_count; i++)
+                       r->rep.un_rq.wins_owner[i]=global_wins_table[0][i];
+               
+               DEBUG(5,("start_assoc_reply: sending update table\n"));
+               return;
+       }
+       
+       /* neither push/pull, stop */
+       /* we should not come here */
+       DEBUG(0,("we have a partner which is neither push nor pull !\n"));
+       stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+initialise and fill the in-memory partner table.
+****************************************************************************/
+int init_wins_partner_table(void)
+{
+       int i=1,j=0,k;
+       char **partner = str_list_make(lp_wins_partners(), NULL);
+
+       if (partner==NULL) {
+               DEBUG(0,("wrepld: no partner list in smb.conf, exiting\n"));
+               exit_server("normal exit");
+               return(0);
+       }
+
+       DEBUG(4, ("init_wins_partner_table: partners: %s\n", lp_wins_partners()));
+
+       global_wins_table[0][0].address=*iface_n_ip(0);
+       global_wins_table[0][0].max_version=0;
+       global_wins_table[0][0].min_version=0;
+       global_wins_table[0][0].type=0;
+
+       while (partner[j]!=NULL) {
+               DEBUG(3,("init_wins_partner_table, adding partner: %s\n", partner[j]));
+               
+               global_wins_table[0][i].address=*interpret_addr2(partner[j]);
+               global_wins_table[0][i].max_version=0;
+               global_wins_table[0][i].min_version=0;
+               global_wins_table[0][i].type=0;
+               global_wins_table[0][i].last_pull=0;
+               global_wins_table[0][i].last_push=0;
+
+               i++;
+               j++;
+       }
+       
+       for (k=1; k<i;k++)
+               for (j=0; j<i; j++)
+                       global_wins_table[k][j]=global_wins_table[0][j];
+       
+       str_list_free (&partner);
+       
+       return i;
+}
+
+/****************************************************************************
+read the last ID from the wins tdb file.
+****************************************************************************/
+static void get_our_last_id(WINS_OWNER *wins_owner)
+{
+       TDB_CONTEXT *tdb;
+
+       tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+       if (!tdb) {
+               DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+               return;
+       }
+       
+       wins_owner->max_version=((SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_HIGH))<<32 | 
+                                (SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_LOW);
+
+       tdb_close(tdb);
+}
+
+/****************************************************************************
+send the list of wins server we know.
+****************************************************************************/
+static void send_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       int i;
+       int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+       if (s_ctx==0) {
+               DEBUG(5, ("send_version_number_map_table: request for a partner not in our table\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       /*
+        * return an array of wins servers, we are partner with.
+        * each entry contains the IP address and the version info
+        * version: ID of the last entry we've got
+        */
+
+       /* the first wins server must be self */
+
+       /*
+        * get our last ID from the wins database
+        * it can have been updated since last read
+        * as nmbd got registration/release.
+        */ 
+       get_our_last_id(&global_wins_table[0][0]);
+
+       r->rep.avmt_rep.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+       if (r->rep.avmt_rep.wins_owner==NULL) {
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+       
+       DEBUG(5,("send_version_number_map_table: partner_count: %d\n", partner_count));
+
+       for (i=0; i<partner_count; i++) {
+               DEBUG(5,("send_version_number_map_table, partner: %d -> %s, \n", i, inet_ntoa(global_wins_table[0][i].address)));
+               r->rep.avmt_rep.wins_owner[i]=global_wins_table[0][i];
+       }
+       
+       r->rep.msg_type=1;
+       r->rep.avmt_rep.partner_count=partner_count;
+       r->rep.avmt_rep.initiating_wins_server.s_addr=0; /* blatant lie, NT4/w2K do the same ! */
+       fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+}
+
+/****************************************************************************
+for a given partner, ask it to send entries we don't have.
+****************************************************************************/
+static BOOL check_partners_and_send_entries(GENERIC_PACKET *q, GENERIC_PACKET *r, int partner)
+{
+       int server;
+       int other;
+       SMB_BIG_UINT temp;
+       SMB_BIG_UINT current;
+
+
+       /*
+        * we check if our partner has more records than us.
+        * we need to check more than our direct partners as
+        * we can have this case:
+        * us: A, partners: B,C, indirect partner: D
+        * A<->B, A<->C, B<->D, C<->D
+        *
+        * So if we're talking to B, we need to check if between
+        * B and C, which one have more records about D.
+        * and also check if we don't already have the records.
+        */
+
+
+        /* check all servers even indirect */
+        for (server=1; global_wins_table[0][server].address.s_addr!=0; server++) {
+               current = global_wins_table[partner][server].max_version;
+               
+               temp=0;
+               
+               for (other=1; other<partner_count; other++) {
+                       /* skip the partner itself */
+                       if (other==partner)
+                               continue;
+
+                       if (global_wins_table[other][server].max_version > temp)
+                               temp=global_wins_table[other][server].max_version;
+               }
+               
+               if (current >= temp && current > global_wins_table[0][server].max_version) {
+                       /* 
+                        * it has more records than every body else and more than us,
+                        * ask it the difference between what we have and what it has
+                        */
+                       fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+                       r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REQUEST;
+                       r->rep.se_rq.wins_owner.address=global_wins_table[partner][server].address;
+                       
+                       r->rep.se_rq.wins_owner.max_version=global_wins_table[partner][server].max_version;
+                       r->rep.se_rq.wins_owner.min_version=global_wins_table[0][server].max_version;
+                       r->rep.se_rq.wins_owner.type=0;
+                       
+                       write_server_assoc_table(q->header.assoc_ctx, global_wins_table[0][partner].address, global_wins_table[partner][server].address);
+                       
+                       /*
+                        * and we update our version for this server
+                        * as we can't use the IDs returned in the send_entries function
+                        * the max ID can be larger than the largest ID returned
+                        */
+                       
+                       global_wins_table[0][server].max_version=global_wins_table[partner][server].max_version;
+
+                       return True;
+               }
+       }
+       return False;
+}
+       
+/****************************************************************************
+receive the list of wins server we know.
+****************************************************************************/
+static void receive_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       fstring peer;
+       struct in_addr addr;
+       int i,j,k,l;
+       int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+       if (s_ctx==0) {
+               DEBUG(5, ("receive_version_number_map_table: request for a partner not in our table\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       fstrcpy(peer,get_socket_addr(q->fd));
+       addr=*interpret_addr2(peer);
+
+       get_our_last_id(&global_wins_table[0][0]);
+       
+       DEBUG(5,("receive_version_number_map_table: received a map of %d server from: %s\n", 
+                 q->rep.avmt_rep.partner_count ,inet_ntoa(q->rep.avmt_rep.initiating_wins_server)));
+       DEBUG(5,("real peer is: %s\n", peer));
+
+       for (i=0; global_wins_table[0][i].address.s_addr!=addr.s_addr && i<partner_count;i++)
+               ;
+
+       if (i==partner_count) {
+               DEBUG(5,("receive_version_number_map_table: unknown partner: %s\n", peer));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       for (j=0; j<q->rep.avmt_rep.partner_count;j++) {
+               /*
+                * search if we already have this entry or if it's a new one
+                * it can be a new one in case of propagation
+                */
+               for (k=0; global_wins_table[0][k].address.s_addr!=0 && 
+                         global_wins_table[0][k].address.s_addr!=q->rep.avmt_rep.wins_owner[j].address.s_addr; k++);
+
+               global_wins_table[i][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+               global_wins_table[i][k].max_version=q->rep.avmt_rep.wins_owner[j].max_version;
+               global_wins_table[i][k].min_version=q->rep.avmt_rep.wins_owner[j].min_version;
+               global_wins_table[i][k].type=q->rep.avmt_rep.wins_owner[j].type;
+               
+               /*
+                * in case it's a new one, rewrite the address for all the partner
+                * to reserve the slot.
+                */
+
+               for(l=0; l<partner_count; l++)
+                       global_wins_table[l][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+       }
+
+       dump_global_table();
+
+       /*
+        * if this server have newer records than what we have
+        * for several wins servers, we need to ask it.
+        * Alas a send entry request is only on one server.
+        * So in the send entry reply, we'll ask for the next server if required.
+        */
+
+       if (check_partners_and_send_entries(q, r, i))
+               return;
+
+       /* it doesn't have more entries than us */
+       stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+add an entry to the wins list we'll send.
+****************************************************************************/
+static BOOL add_record_to_winsname(WINS_NAME **wins_name, int *max_names, char *name, int type, int wins_flags, int id, struct in_addr *ip_list, int num_ips)
+{
+       WINS_NAME *temp_list;
+       int i;
+       int current=*max_names;
+
+       temp_list=talloc_realloc(mem_ctx, *wins_name, (current+1)*sizeof(WINS_NAME));
+       if (temp_list==NULL)
+               return False;
+
+       temp_list[current].name_len=0x11;
+       
+       safe_strcpy(temp_list[current].name, name, 15);
+
+       temp_list[current].type=type;
+       temp_list[current].empty=0;
+
+       temp_list[current].name_flag=wins_flags;
+
+       if ( (wins_flags&0x03) == 1 || (wins_flags&0x03)==2)
+               temp_list[current].group_flag=0x01000000;
+       else
+               temp_list[current].group_flag=0x00000000;
+       
+       temp_list[current].id=id;
+       
+       temp_list[current].owner.s_addr=ip_list[0].s_addr;
+
+       if (temp_list[current].name_flag & 2) {
+               temp_list[current].num_ip=num_ips;
+               temp_list[current].others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*num_ips);
+               if (temp_list[current].others==NULL)
+                       return False;
+       
+               for (i=0; i<num_ips; i++)
+                       temp_list[current].others[i].s_addr=ip_list[i].s_addr;
+
+       } else 
+               temp_list[current].num_ip=1;
+
+       temp_list[current].foo=0xffffffff;
+
+       *wins_name=temp_list;
+       
+       return True;
+}
+
+/****************************************************************************
+send the list of name we have.
+****************************************************************************/
+static void send_entry_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       int max_names=0;
+       int i;
+       time_t time_now = time(NULL);
+       WINS_OWNER *wins_owner;
+       TDB_CONTEXT *tdb;
+       TDB_DATA kbuf, dbuf, newkey;
+       int s_ctx=get_server_assoc(q->header.assoc_ctx);
+       int num_interfaces = iface_count();
+
+       if (s_ctx==0) {
+               DEBUG(1, ("send_entry_request: request for a partner not in our table\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+
+       wins_owner=&q->rep.se_rq.wins_owner;
+       r->rep.se_rp.wins_name=NULL;
+
+       DEBUG(3,("send_entry_request: we have been asked to send the list of wins records\n"));
+       DEBUGADD(3,("owned by: %s and between min: %d and max: %d\n", inet_ntoa(wins_owner->address),
+                   (int)wins_owner->min_version, (int)wins_owner->max_version));
+
+       /*
+        * if we are asked to send records owned by us
+        * we overwrite the wins ip with 0.0.0.0
+        * to make it easy in case of multihomed
+        */
+
+       for (i=0; i<num_interfaces; i++)
+               if (ip_equal(wins_owner->address, *iface_n_ip(i))) {
+                       wins_owner->address=*interpret_addr2("0.0.0.0");
+                       break;
+               }
+
+
+       tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+       if (!tdb) {
+               DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+               return;
+       }
+
+       for (kbuf = tdb_firstkey(tdb); 
+            kbuf.dptr; 
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+               fstring name_type;
+               pstring name, ip_str;
+               char *p;
+               int type = 0;
+               int nb_flags;
+               int ttl;
+               unsigned int num_ips;
+               int low, high;
+               SMB_BIG_UINT version;
+               struct in_addr wins_ip;
+               struct in_addr *ip_list;
+               int wins_flags;
+               int len;
+
+               if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
+                       continue;
+               
+               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr)
+                       continue;
+
+               fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
+               pstrcpy(name, name_type);
+
+               if((p = strchr(name,'#')) != NULL) {
+                       *p = 0;
+                       sscanf(p+1,"%x",&type);
+               }
+
+               len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
+                               &nb_flags,
+                               &high,
+                               &low,
+                               ip_str,
+                               &ttl, 
+                               &num_ips,
+                               &wins_flags);
+
+               wins_ip=*interpret_addr2(ip_str);
+
+               /* Allocate the space for the ip_list. */
+               if((ip_list = (struct in_addr *)talloc(mem_ctx,  num_ips * sizeof(struct in_addr))) == NULL) {
+                       SAFE_FREE(dbuf.dptr);
+                       DEBUG(0,("initialise_wins: talloc fail !\n"));
+                       return;
+               }
+
+               for (i = 0; i < num_ips; i++) {
+                       len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
+                       ip_list[i] = *interpret_addr2(ip_str);
+               }
+
+               SAFE_FREE(dbuf.dptr);
+
+               /* add all entries that have 60 seconds or more to live */
+               if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+                       if(ttl != PERMANENT_TTL)
+                               ttl -= time_now;
+    
+                       DEBUG( 4, ("send_entry_request: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+                           name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+                       /* add the record to the list to send */
+                       version=((SMB_BIG_UINT)high)<<32 | low;
+                       
+                       if (wins_owner->min_version<=version && wins_owner->max_version>=version &&
+                           wins_owner->address.s_addr==wins_ip.s_addr) {
+                               if(!add_record_to_winsname(&r->rep.se_rp.wins_name, &max_names, name, type, wins_flags, version, ip_list, num_ips))
+                                       return;
+                               max_names++;
+                       }
+
+               } else {
+                       DEBUG(4, ("send_entry_request: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+                                 name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+               }
+       }
+    
+       tdb_close(tdb);
+
+       DEBUG(4,("send_entry_request, sending %d records\n", max_names));
+       fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+       r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REPLY; /* reply */
+       r->rep.se_rp.max_names=max_names;
+}
+
+
+/****************************************************************************
+.
+****************************************************************************/
+static void update_notify_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       int i,j,k,l;
+       UPDATE_NOTIFY_REQUEST *u;
+       int s_ctx=get_server_assoc(q->header.assoc_ctx);
+       
+       if (s_ctx==0) {
+               DEBUG(4, ("update_notify_request: request for a partner not in our table\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       u=&q->rep.un_rq;
+
+       /* check if we already have the range of records */
+
+       DEBUG(5,("update_notify_request: wins server: %s offered this list of %d records:\n",
+               inet_ntoa(u->initiating_wins_server), u->partner_count));
+
+       get_our_last_id(&global_wins_table[0][0]);
+
+       for (i=0; i<partner_count; i++) {
+               if (global_wins_table[0][i].address.s_addr==u->initiating_wins_server.s_addr) {
+                       DEBUG(5,("update_notify_request: found initiator at index %d\n", i));
+                       break;
+               }
+       }
+
+       /*
+        * some explanation is required, before someone say it's crap.
+        *
+        * let's take an example, we have 2 wins partners, we already now
+        * that our max id is 10, partner 1 ID is 20 and partner 2 ID is 30
+        * the array looks like:
+        *
+        *      0       1       2
+        * 0    10      20      30  
+        * 1
+        * 2
+        *
+        * we receive an update from partner 2 saying he has: 1:15, 2:40, 3:50
+        * we must enlarge the array to add partner 3, it will look like:
+        *
+        *      0       1       2       3
+        * 0    10      20      30
+        * 1
+        * 2            15      40      50
+        *
+        * now we know, we should pull from partner 2, the records 30->40 of 2 and 0->50 of 3.
+        * once the pull will be over, our table will look like:
+        *
+        *      0       1       2       3
+        * 0    10      20      40      50
+        * 1
+        * 2            15      40      50
+        *
+        *
+        */
+
+       for (j=0; j<u->partner_count;j++) {
+               /*
+                * search if we already have this entry or if it's a new one
+                * it can be a new one in case of propagation
+                */
+
+               for (k=0; global_wins_table[0][k].address.s_addr!=0 && 
+                         global_wins_table[0][k].address.s_addr!=u->wins_owner[j].address.s_addr; k++);
+
+               global_wins_table[i][k].address.s_addr=u->wins_owner[j].address.s_addr;
+               global_wins_table[i][k].max_version=u->wins_owner[j].max_version;
+               global_wins_table[i][k].min_version=u->wins_owner[j].min_version;
+               global_wins_table[i][k].type=u->wins_owner[j].type;
+               
+               /*
+                * in case it's a new one, rewrite the address for all the partner
+                * to reserve the slot.
+                */
+
+               for(l=0; l<partner_count; l++)
+                       global_wins_table[l][k].address.s_addr=u->wins_owner[j].address.s_addr;
+       }
+
+       dump_global_table();
+
+       stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+.
+****************************************************************************/
+static void send_entry_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       int i,j,k;
+       struct in_addr partner, server;
+       pid_t pid;
+       int s_ctx=get_server_assoc(q->header.assoc_ctx);
+       WINS_RECORD record;
+       
+       if (s_ctx==0) {
+               DEBUG(1, ("send_entry_reply: request for a partner not in our table\n"));
+               stop_packet(q, r, STOP_REASON_USER_REASON);
+               return;
+       }
+
+       DEBUG(5,("send_entry_reply:got %d new records\n", q->rep.se_rp.max_names));
+
+       /* we got records from a wins partner but that can be from another wins server */
+       /* hopefully we track that */
+
+       /* and the only doc available from MS is wrong ! */
+
+       get_server_assoc_table(q->header.assoc_ctx, &partner, &server);
+
+       for (j=0; global_wins_table[0][j].address.s_addr!=0; j++) {
+               if (global_wins_table[0][j].address.s_addr==server.s_addr) {
+                       DEBUG(5,("send_entry_reply: found server at index %d\n", j));
+                       break;
+               }
+       }
+
+       pid = pidfile_pid("nmbd");
+       if (pid == 0) {
+               DEBUG(0,("send_entry_reply: Can't find pid for nmbd\n"));
+               return;
+       }
+
+       for (k=0; k<q->rep.se_rp.max_names; k++) {
+               DEBUG(5,("send_entry_reply: %s<%02x> %d\n", q->rep.se_rp.wins_name[k].name, q->rep.se_rp.wins_name[k].type,
+                        (int)q->rep.se_rp.wins_name[k].id));
+
+               safe_strcpy(record.name, q->rep.se_rp.wins_name[k].name, 16);
+               record.type=q->rep.se_rp.wins_name[k].type;
+               record.id=q->rep.se_rp.wins_name[k].id;
+               record.wins_flags=q->rep.se_rp.wins_name[k].name_flag&0x00ff;
+               record.num_ips=q->rep.se_rp.wins_name[k].num_ip;
+
+               record.wins_ip.s_addr=server.s_addr;
+
+               if (record.num_ips==1)
+                       record.ip[0]=q->rep.se_rp.wins_name[k].owner;
+               else
+                       for (i=0; i<record.num_ips; i++)
+                               record.ip[i]=q->rep.se_rp.wins_name[k].others[i];
+
+               record.nb_flags=0;
+
+               if (record.wins_flags&WINS_NGROUP || record.wins_flags&WINS_SGROUP)
+                       record.nb_flags|=NB_GROUP;
+               
+               if (record.wins_flags&WINS_ACTIVE)
+                       record.nb_flags|=NB_ACTIVE;
+               
+               record.nb_flags|=record.wins_flags&WINS_HNODE;
+               
+               message_send_pid(pid, MSG_WINS_NEW_ENTRY, &record, sizeof(record), False);
+
+       }
+
+       dump_global_table();
+
+       /*
+        * we got some entries, 
+        * ask the partner to send us the map table again
+        * to get the other servers entries.
+        *
+        * we're getting the map table 1 time more than really
+        * required. We could remove that call, but that
+        * would complexify the code. I prefer this trade-of. 
+        */
+       fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+
+       r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+}
+
+/****************************************************************************
+decode the replication message and reply.
+****************************************************************************/
+static void replicate(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       switch (q->rep.msg_type) {
+               case 0:
+                       /* add version number map table request */
+                       send_version_number_map_table(q, r);
+                       break;
+               case 1:
+                       receive_version_number_map_table(q, r);
+                       break;
+               case 2:
+                       /* send entry request */
+                       send_entry_request(q, r);
+                       break;
+               case 3:
+                       /* send entry reply */
+                       send_entry_reply(q, r);
+                       break;
+               case 4:
+                       /* update notification request */
+                       update_notify_request(q, r);
+                       break;
+       }
+}
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static BOOL switch_message(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+       switch (q->header.mess_type) {
+               case 0:
+                       /* Start association type */                    
+                       start_assoc_process(q, r);
+                       return True;
+                       break;
+               case 1:
+                       /* start association reply */
+                       start_assoc_reply(q, r);
+                       return True;
+                       break;
+               case 2:
+                       /* stop association message */
+                       return False;
+                       break;
+               case 3:
+                       /* replication message */
+                       replicate(q, r);
+                       return True;
+                       break;
+       }
+
+       return False;
+}
+
+
+/****************************************************************************
+  construct a reply to the incoming packet
+****************************************************************************/
+void construct_reply(struct wins_packet_struct *p)
+{
+       GENERIC_PACKET r;
+       struct BUFFER buffer;
+
+       buffer.buffer=NULL;
+       buffer.offset=0;
+       buffer.length=0;
+
+       DEBUG(5,("dump: received packet\n"));
+       dump_generic_packet(p->packet);
+
+       /* Verify if the request we got is from a listed partner */
+       if (!check_partner(p->packet->header.assoc_ctx)) {
+               fstring peer;
+               struct in_addr addr;
+               int i;
+               fstrcpy(peer,get_socket_addr(p->fd));
+               addr=*interpret_addr2(peer);
+
+               for (i=1; i<partner_count; i++)
+                       if (ip_equal(addr, global_wins_table[0][i].address))
+                               break;
+
+               if (i==partner_count) {
+                       DEBUG(1,("construct_reply: got a request from a non peer machine: %s\n", peer));
+                       stop_packet(p->packet, &r, STOP_REASON_AUTH_FAILED);
+                       p->stop_packet=True;
+                       encode_generic_packet(&buffer, &r);
+                       if (!send_smb(p->fd, buffer.buffer))
+                               exit_server("process_smb: send_smb failed.");
+                       return;
+               }
+       }
+
+       if (switch_message(p->packet, &r)) {
+               encode_generic_packet(&buffer, &r);
+               DEBUG(5,("dump: sending packet\n"));
+               dump_generic_packet(&r);
+
+               if(buffer.offset > 0) {
+                       if (!send_smb(p->fd, buffer.buffer))
+                               exit_server("process_smb: send_smb failed.");
+               }
+       }
+
+       /* if we got a stop assoc or if we send a stop assoc, close the fd after */
+       if (p->packet->header.mess_type==MESSAGE_TYPE_STOP_ASSOC || 
+           r.header.mess_type==MESSAGE_TYPE_STOP_ASSOC) {
+               remove_partner(p->packet->header.assoc_ctx);
+               p->stop_packet=True;
+       }
+}
+
+/****************************************************************************
+  contact periodically our wins partner to do a pull replication
+****************************************************************************/
+void run_pull_replication(time_t t)
+{
+       /* we pull every 30 minutes to query about new records*/
+       int i, s;
+       struct BUFFER buffer;
+       GENERIC_PACKET p;
+
+       buffer.buffer=NULL;
+       buffer.offset=0;
+       buffer.length=0;
+
+       for (i=1; i<partner_count; i++) {
+               if (global_wins_table[0][i].last_pull < t) {
+                       global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+                       
+                       /* contact the wins server */
+                       p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+                       p.header.opcode=OPCODE_NON_NBT;
+                       p.header.assoc_ctx=0;
+                       p.sa_rq.assoc_ctx=(int)t;
+                       p.sa_rq.min_ver=1;
+                       p.sa_rq.maj_ver=1;
+                       
+                       DEBUG(3,("run_pull_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+                       encode_generic_packet(&buffer, &p);
+                       dump_generic_packet(&p);
+
+                       /* send the packet to the server and add the descriptor to receive answers */
+                       s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+                       if (s==-1) {
+                               DEBUG(0,("run_pull_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+                               return;
+                       }
+                       
+                       if(buffer.offset > 0) {
+                               if (!send_smb(s, buffer.buffer))
+                                       exit_server("run_pull_replication: send_smb failed.");
+                       }
+                       
+                       add_fd_to_sock_array(s);
+                       FD_SET(s, listen_set);
+
+                       /* add ourself as a client */
+                       add_partner((int)t, 0, True, False);
+               }
+       }
+}
+
+/****************************************************************************
+  contact periodically our wins partner to do a push replication
+****************************************************************************/
+void run_push_replication(time_t t)
+{
+       /* we push every 30 minutes or 25 new entries */
+       int i, s;
+       struct BUFFER buffer;
+       GENERIC_PACKET p;
+
+       buffer.buffer=NULL;
+       buffer.offset=0;
+       buffer.length=0;
+
+       for (i=1; i<partner_count; i++) {
+               if (global_wins_table[0][i].last_pull < t) {
+                       global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+                       
+                       /* contact the wins server */
+                       p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+                       p.header.opcode=OPCODE_NON_NBT;
+                       p.header.assoc_ctx=0;
+                       p.sa_rq.assoc_ctx=(int)t;
+                       p.sa_rq.min_ver=1;
+                       p.sa_rq.maj_ver=1;
+                       
+                       DEBUG(3,("run_push_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+                       encode_generic_packet(&buffer, &p);
+                       dump_generic_packet(&p);
+
+                       /* send the packet to the server and add the descriptor to receive answers */
+                       s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+                       if (s==-1) {
+                               DEBUG(0,("run_push_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+                               return;
+                       }
+                       
+                       if(buffer.offset > 0) {
+                               if (!send_smb(s, buffer.buffer))
+                                       exit_server("run_push_replication: send_smb failed.");
+                       }
+                       
+                       add_fd_to_sock_array(s);
+                       FD_SET(s, listen_set);
+
+                       /* add ourself as a client */
+                       add_partner((int)t, 0, False, True);
+               }
+       }
+}
+
diff --git a/source4/wrepld/server.c b/source4/wrepld/server.c
new file mode 100644 (file)
index 0000000..afeec34
--- /dev/null
@@ -0,0 +1,737 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main SMB server routines
+   Copyright (C) Jean François Micouleau      1998-2002.
+   
+   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 "wins_repl.h"
+
+extern WINS_OWNER *global_wins_table;
+extern int partner_count;
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+extern TALLOC_CTX *mem_ctx;
+
+int wins_port = 42;
+
+/****************************************************************************
+  when exiting, take the whole family
+****************************************************************************/
+static void *dflt_sig(void)
+{
+       exit_server("caught signal");
+       return NULL;
+}
+
+/****************************************************************************
+  reload the services file
+  **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+       BOOL ret;
+       
+       if (lp_loaded()) {
+               pstring fname;
+               pstrcpy(fname,lp_configfile());
+               if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+                       pstrcpy(dyn_CONFIGFILE,fname);
+                       test = False;
+               }
+       }
+
+       reopen_logs();
+
+       if (test && !lp_file_list_changed())
+               return(True);
+
+       ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+
+       /* perhaps the config filename is now set */
+       if (!test)
+               reload_services(True);
+
+       reopen_logs();
+
+       load_interfaces();
+
+       return(ret);
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+VOLATILE sig_atomic_t reload_after_sighup = False;
+
+static void sig_hup(int sig)
+{
+       BlockSignals(True,SIGHUP);
+       DEBUG(0,("Got SIGHUP\n"));
+
+       sys_select_signal();
+       reload_after_sighup = True;
+       BlockSignals(False,SIGHUP);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+       char *p;
+       pstring dname;
+       pstrcpy(dname,lp_logfile());
+       if ((p=strrchr_m(dname,'/'))) *p=0;
+       pstrcat(dname,"/corefiles");
+       mkdir(dname,0700);
+       sys_chown(dname,getuid(),getgid());
+       chmod(dname,0700);
+       if (chdir(dname)) return(False);
+       umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+       {
+               struct rlimit rlp;
+               getrlimit(RLIMIT_CORE, &rlp);
+               rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+               setrlimit(RLIMIT_CORE, &rlp);
+               getrlimit(RLIMIT_CORE, &rlp);
+               DEBUG(3,("Core limits now %d %d\n",
+                        (int)rlp.rlim_cur,(int)rlp.rlim_max));
+       }
+#endif
+#endif
+
+
+       DEBUG(0,("Dumping core in %s\n",dname));
+       abort();
+       return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(const char *reason)
+{
+       static int firsttime=1;
+
+       if (!firsttime)
+               exit(0);
+       firsttime = 0;
+
+       DEBUG(2,("Closing connections\n"));
+
+       if (!reason) {   
+               int oldlevel = DEBUGLEVEL;
+               DEBUGLEVEL = 10;
+               DEBUGLEVEL = oldlevel;
+               DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+               if (dump_core()) return;
+#endif
+       }
+
+       DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+       exit(0);
+}
+
+/****************************************************************************
+ Usage of the program.
+****************************************************************************/
+
+static void usage(char *pname)
+{
+
+       d_printf("Usage: %s [-DFSaioPh?V] [-d debuglevel] [-l log basename] [-p port]\n", pname);
+       d_printf("       [-O socket options] [-s services file]\n");
+       d_printf("\t-D                    Become a daemon (default)\n");
+       d_printf("\t-F                    Run daemon in foreground (for daemontools, etc)\n");
+       d_printf("\t-S                    Log to stdout\n");
+       d_printf("\t-a                    Append to log file (default)\n");
+       d_printf("\t-i                    Run interactive (not a daemon)\n" );
+       d_printf("\t-o                    Overwrite log file, don't append\n");
+       d_printf("\t-h                    Print usage\n");
+       d_printf("\t-?                    Print usage\n");
+       d_printf("\t-V                    Print version\n");
+       d_printf("\t-d debuglevel         Set the debuglevel\n");
+       d_printf("\t-l log basename.      Basename for log/debug files\n");
+       d_printf("\t-p port               Listen on the specified port\n");
+       d_printf("\t-O socket options     Socket options\n");
+       d_printf("\t-s services file.     Filename of services file\n");
+       d_printf("\n");
+}
+
+/****************************************************************************
+  Create an fd_set containing all the sockets in the subnet structures,
+  plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(void)
+{
+       int i;
+       int num_interfaces = iface_count();
+       int s;
+
+       listen_set = (fd_set *)malloc(sizeof(fd_set));
+       if(listen_set == NULL) {
+               DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+               return True;
+       }
+
+#ifdef HAVE_ATEXIT
+       {
+               static int atexit_set;
+               if(atexit_set == 0) {
+                       atexit_set=1;
+               }
+       }
+#endif
+
+       FD_ZERO(listen_set);
+
+       if(lp_interfaces() && lp_bind_interfaces_only()) {
+               /* We have been given an interfaces line, and been 
+                  told to only bind to those interfaces. Create a
+                  socket per interface and bind to only these.
+               */
+               
+               if(num_interfaces > FD_SETSIZE) {
+                       DEBUG(0,("create_listen_fdset: Too many interfaces specified to bind to. Number was %d max can be %d\n", num_interfaces, FD_SETSIZE));
+                       return False;
+               }
+
+               /* Now open a listen socket for each of the interfaces. */
+               for(i = 0; i < num_interfaces; i++) {
+                       struct in_addr *ifip = iface_n_ip(i);
+                       
+                       if(ifip == NULL) {
+                               DEBUG(0,("create_listen_fdset: interface %d has NULL IP address !\n", i));
+                               continue;
+                       }
+                       s = open_socket_in(SOCK_STREAM, wins_port, 0, ifip->s_addr, True);
+                       if(s == -1)
+                               return False;
+
+                       /* ready to listen */
+                       set_socket_options(s,"SO_KEEPALIVE"); 
+                       set_socket_options(s,lp_socket_options());
+      
+                       if (listen(s, 5) == -1) {
+                               DEBUG(5,("listen: %s\n",strerror(errno)));
+                               close(s);
+                               return False;
+                       }
+                       add_fd_to_sock_array(s);
+                       FD_SET(s, listen_set);
+               }
+       } else {
+               /* Just bind to 0.0.0.0 - accept connections from anywhere. */
+               num_interfaces = 1;
+               
+               /* open an incoming socket */
+               s = open_socket_in(SOCK_STREAM, wins_port, 0, interpret_addr(lp_socket_address()),True);
+               if (s == -1)
+                       return(False);
+               
+               /* ready to listen */
+               set_socket_options(s,"SO_KEEPALIVE"); 
+               set_socket_options(s, lp_socket_options());
+
+               if (listen(s, 5) == -1) {
+                       DEBUG(0,("create_listen_fdset: listen: %s\n", strerror(errno)));
+                       close(s);
+                       return False;
+               }
+               
+               add_fd_to_sock_array(s);
+               FD_SET(s, listen_set);
+       } 
+
+       return True;
+}
+
+/*******************************************************************
+  read a packet from a socket and parse it, returning a packet ready
+  to be used or put on the queue. This assumes a UDP socket
+  ******************************************************************/
+static struct wins_packet_struct *read_wins_packet(int fd, int timeout)
+{
+       struct wins_packet_struct *p;
+       GENERIC_PACKET *q;
+       struct BUFFER inbuf;
+       ssize_t len=0;
+       size_t total=0;  
+       ssize_t ret;
+       BOOL ok = False;
+
+       inbuf.buffer=NULL;
+       inbuf.length=0;
+       inbuf.offset=0;
+
+       if(!grow_buffer(&inbuf, 4))
+               return NULL;
+
+       ok = (read(fd, inbuf.buffer,4) == 4);
+       if (!ok)
+               return NULL;
+       len = smb_len(inbuf.buffer);
+
+       if (len<=0)
+               return NULL;
+
+       if(!grow_buffer(&inbuf, len))
+               return NULL;
+               
+       while (total < len) {
+               ret = read(fd, inbuf.buffer + total + 4, len - total);
+               if (ret == 0) {
+                       DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(len - total), strerror(errno) ));
+                       return NULL;
+               }
+               if (ret == -1) {
+                       DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(len - total), strerror(errno) ));
+                       return NULL;
+               }
+               total += ret;
+       }
+
+       q = (GENERIC_PACKET *)talloc(mem_ctx, sizeof(GENERIC_PACKET));
+       p = (struct wins_packet_struct *)talloc(mem_ctx, sizeof(*p));
+       if (q==NULL || p==NULL)
+               return NULL;
+
+       decode_generic_packet(&inbuf, q);
+
+       q->fd=fd;
+       
+       p->next = NULL;
+       p->prev = NULL;
+       p->stop_packet = False;
+       p->timestamp = time(NULL);
+       p->fd = fd;
+       p->packet=q;
+       
+       return p;
+}
+
+static struct wins_packet_struct *packet_queue = NULL;
+
+/*******************************************************************
+  Queue a packet into a packet queue
+******************************************************************/
+static void queue_packet(struct wins_packet_struct *packet)
+{
+       struct wins_packet_struct *p;
+
+       if (!packet_queue) {
+               packet->prev = NULL;
+               packet->next = NULL;
+               packet_queue = packet;
+               return;
+       }
+  
+       /* find the bottom */
+       for (p=packet_queue;p->next;p=p->next) 
+               ;
+
+       p->next = packet;
+       packet->next = NULL;
+       packet->prev = p;
+}
+
+/****************************************************************************
+  Listens for NMB or DGRAM packets, and queues them.
+  return True if the socket is dead
+***************************************************************************/
+static BOOL listen_for_wins_packets(void)
+{
+       int num_interfaces = iface_count();
+       fd_set fds;
+       int i, num, s, new_s;
+       struct timeval timeout;
+
+       if(listen_set == NULL) {
+               if(!create_listen_fdset()) {
+                       DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+                       return True;
+               }
+       }
+
+       memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+       timeout.tv_sec = NMBD_SELECT_LOOP;
+       timeout.tv_usec = 0;
+
+       /* Prepare for the select - allow certain signals. */
+
+       BlockSignals(False, SIGTERM);
+
+       num = sys_select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+
+       /* We can only take signals when we are in the select - block them again here. */
+
+       BlockSignals(True, SIGTERM);
+
+       if(num == -1)
+               return False;
+
+       for (; num > 0; num--) {
+               s = -1;
+               /* check the sockets we are only listening on, waiting to accept */             
+               for (i=0; i<num_interfaces; i++) {
+                       struct sockaddr addr;
+                       socklen_t in_addrlen = sizeof(addr);
+               
+                       if(FD_ISSET(sock_array[i], &fds)) {
+                               s = sock_array[i];
+                               /* Clear this so we don't look at it again. */
+                               FD_CLR(sock_array[i], &fds);
+
+                               /* accept and add the new socket to the listen set */
+                               new_s=accept(s, &addr, &in_addrlen);
+
+                               if (new_s < 0)
+                                       continue;
+       
+                               DEBUG(5,("listen_for_wins_packets: new connection, old: %d, new : %d\n", s, new_s));
+                               
+                               set_socket_options(new_s, "SO_KEEPALIVE");
+                               set_socket_options(new_s, lp_socket_options());
+                               FD_SET(new_s, listen_set);
+                               add_fd_to_sock_array(new_s);
+                       }
+               }
+
+               /*
+                * check for the sockets we are waiting data from
+                * either client sending datas
+                * or reply to our requests
+                */
+               for (i=num_interfaces; i<listen_number; i++) {
+                       if(FD_ISSET(sock_array[i], &fds)) {
+                               struct wins_packet_struct *packet = read_wins_packet(sock_array[i], timeout.tv_sec);
+                               if (packet) {
+                                       packet->fd = sock_array[i];
+                                       queue_packet(packet);
+                               }
+                               DEBUG(2,("listen_for_wins_packets: some data on fd %d\n", sock_array[i]));
+                               FD_CLR(sock_array[i], &fds);
+                               break;
+                       }
+       
+               }
+
+       }
+
+       return False;
+}
+
+
+/*******************************************************************
+  Run elements off the packet queue till its empty
+******************************************************************/
+
+static void run_wins_packet_queue(void)
+{
+       struct wins_packet_struct *p;
+
+       while ((p = packet_queue)) {
+               packet_queue = p->next;
+               if (packet_queue)
+                       packet_queue->prev = NULL;
+               p->next = p->prev = NULL;
+
+               construct_reply(p);
+
+               /* if it was a stop assoc, close the connection */
+               if (p->stop_packet) {
+                       FD_CLR(p->fd, listen_set);
+                       remove_fd_from_sock_array(p->fd);
+                       close(p->fd);
+               }
+       }
+} 
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+static void process(void)
+{
+
+       while( True ) {
+               time_t t = time(NULL);
+
+               /* check for internal messages */
+               message_dispatch();
+
+               if(listen_for_wins_packets())
+                       return;
+
+               run_wins_packet_queue();
+
+               run_pull_replication(t);
+               
+               run_push_replication(t);
+               
+               /*
+                * Reload the services file if we got a sighup.
+                */
+
+               if(reload_after_sighup) {
+                       reload_services( True );
+                       reopen_logs();
+                       reload_after_sighup = False;
+               }
+
+               /* free temp memory */
+               talloc_destroy_pool(mem_ctx);
+
+               /* free up temp memory */
+               lp_talloc_free();
+       }
+} /* process */
+
+/****************************************************************************
+  main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+       extern char *optarg;
+       /* shall I run as a daemon */
+       BOOL is_daemon = False;
+       BOOL interactive = False;
+       BOOL specified_logfile = False;
+       BOOL Fork = True;
+       BOOL log_stdout = False;
+       int opt;
+       pstring logfile;
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+       set_auth_parameters(argc,argv);
+#endif
+
+       /* this is for people who can't start the program correctly */
+       while (argc > 1 && (*argv[1] != '-')) {
+               argv++;
+               argc--;
+       }
+
+       while ( EOF != (opt = getopt(argc, argv, "FSO:l:s:d:Dp:h?Vaiof:")) )
+               switch (opt)  {
+               case 'F':
+                       Fork = False;
+                       break;
+               case 'S':
+                       log_stdout = True;
+                       break;
+               case 'O':
+                       lp_set_cmdline("socket options", optarg);
+                       break;
+
+               case 's':
+                       pstrcpy(dyn_CONFIGFILE,optarg);
+                       break;
+
+               case 'l':
+                       specified_logfile = True;
+                       slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld", optarg);
+                       lp_set_logfile(logfile);
+                       break;
+
+               case 'i':
+                       interactive = True;
+                       Fork = False;
+                       log_stdout = True;
+                       break;
+
+               case 'D':
+                       is_daemon = True;
+                       break;
+
+               case 'd':
+                       if (*optarg == 'A')
+                               DEBUGLEVEL = 10000;
+                       else
+                               DEBUGLEVEL = atoi(optarg);
+                       break;
+
+               case 'p':
+                       wins_port = atoi(optarg);
+                       break;
+
+               case 'h':
+               case '?':
+                       usage(argv[0]);
+                       exit(0);
+                       break;
+
+               case 'V':
+                       d_printf("Version %s\n",VERSION);
+                       exit(0);
+                       break;
+               default:
+                       DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
+                       usage(argv[0]);
+                       exit(1);
+               }
+       if (log_stdout && Fork) {
+               d_printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n");
+               usage(argv[0]);
+               exit(1);
+       }
+
+#ifdef HAVE_SETLUID
+       /* needed for SecureWare on SCO */
+       setluid(0);
+#endif
+
+       sec_init();
+
+       load_case_tables();
+
+       if(!specified_logfile) {
+               slprintf(logfile, sizeof(logfile)-1, "%s/log.wrepld",
+                        dyn_LOGFILEBASE);
+               lp_set_logfile(logfile);
+       }
+
+       set_remote_machine_name("wrepld");
+
+       setup_logging(argv[0],log_stdout);
+
+       /* we want to re-seed early to prevent time delays causing
+           client problems at a later date. (tridge) */
+       generate_random_buffer(NULL, 0, False);
+
+       /* make absolutely sure we run as root - to handle cases where people
+          are crazy enough to have it setuid */
+
+       gain_root_privilege();
+       gain_root_group_privilege();
+
+       fault_setup((void (*)(void *))exit_server);
+       CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig);
+
+       /* we are never interested in SIGPIPE */
+       BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+       /* we are never interested in SIGFPE */
+       BlockSignals(True,SIGFPE);
+#endif
+
+#if defined(SIGUSR2)
+       /* We are no longer interested in USR2 */
+       BlockSignals(True,SIGUSR2);
+#endif
+
+       /* POSIX demands that signals are inherited. If the invoking process has
+        * these signals masked, we will have problems, as we won't recieve them. */
+       BlockSignals(False, SIGHUP);
+       BlockSignals(False, SIGUSR1);
+
+       /* we want total control over the permissions on created files,
+          so set our umask to 0 */
+       umask(0);
+
+       reopen_logs();
+
+       DEBUG(1,( "wrepld version %s started.\n", VERSION));
+       DEBUGADD(1,( "Copyright Andrew Tridgell and the Samba Team 1992-2002\n"));
+
+       DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+                (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+       if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+               DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+               exit(1);
+       }
+
+       /*
+        * Do this before reload_services.
+        */
+
+       if (!reload_services(False))
+               return(-1);     
+
+       if (!init_names())
+               return -1;
+       
+#ifdef WITH_PROFILE
+       if (!profile_setup(False)) {
+               DEBUG(0,("ERROR: failed to setup profiling\n"));
+               return -1;
+       }
+#endif
+
+       CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+       
+       DEBUG(3,( "loaded services\n"));
+
+       if (!is_daemon && !is_a_socket(0)) {
+               DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+               is_daemon = True;
+       }
+
+       if (is_daemon && !interactive) {
+               DEBUG( 3, ( "Becoming a daemon.\n" ) );
+               become_daemon(Fork);
+       }
+
+#if HAVE_SETPGID
+       /*
+        * If we're interactive we want to set our own process group for
+        * signal management.
+        */
+       if (interactive)
+               setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+       if (!directory_exist(lp_lockdir(), NULL)) {
+               mkdir(lp_lockdir(), 0755);
+       }
+
+       if (is_daemon) {
+               pidfile_create("wrepld");
+       }
+
+       if (!message_init()) {
+               exit(1);
+       }
+
+       /* Initialise the memory context */
+       mem_ctx=talloc_init("wins repl talloc ctx");
+
+       /* initialise the global partners table */
+       partner_count=init_wins_partner_table();
+
+       /* We can only take signals in the select. */
+       BlockSignals( True, SIGTERM );
+
+       process();
+
+       exit_server("normal exit");
+       return(0);
+}
diff --git a/source4/wrepld/socket.c b/source4/wrepld/socket.c
new file mode 100644 (file)
index 0000000..3d759f0
--- /dev/null
@@ -0,0 +1,69 @@
+/* 
+   Unix SMB/CIFS implementation.
+   process incoming packets - main loop
+   Copyright (C) Jean François Micouleau      1998-2002.
+   
+   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 "wins_repl.h"
+
+fd_set *listen_set = NULL;
+int listen_number = 0;
+int *sock_array = NULL;
+
+/*******************************************************************
+  Add an fd from the sock_array
+******************************************************************/
+void add_fd_to_sock_array(int fd)
+{
+       int *temp_sock=NULL;
+
+       temp_sock=(int *)Realloc(sock_array, (listen_number+1)*sizeof(int));
+       if (temp_sock==NULL)
+               return;
+
+       sock_array=temp_sock;
+       sock_array[listen_number]=fd;
+       listen_number++;
+}
+
+
+/*******************************************************************
+  Remove an fd from the sock_array
+******************************************************************/
+void remove_fd_from_sock_array(int fd)
+{
+       int i,j;
+
+       for (i=0; sock_array[i]!=fd && i<listen_number; i++)
+               ;
+       
+       if (i==listen_number) {
+               DEBUG(0,("remove_fd_from_sock_array: unknown fd: %d\n", fd));
+               return;
+       }
+       
+       if (i==listen_number-1) {
+               sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));
+               return;
+       }
+
+       for (j=i; j<listen_number-1; j++)
+               sock_array[j]=sock_array[j+1];
+       
+       sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));     
+}
diff --git a/source4/wrepld/wins_repl.h b/source4/wrepld/wins_repl.h
new file mode 100644 (file)
index 0000000..25b4442
--- /dev/null
@@ -0,0 +1,161 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  RPC Pipe client / server routines
+ *  Copyright (C) Jean François Micouleau      1998-2002.
+ *  
+ *  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.
+ */
+
+#define OPCODE_NON_NBT 0x00007800
+
+/* the messages */
+#define MESSAGE_TYPE_START_ASSOC_REQUEST       0
+#define MESSAGE_TYPE_START_ASSOC_REPLY         1
+#define MESSAGE_TYPE_STOP_ASSOC                        2
+#define MESSAGE_TYPE_REPLICATE                 3
+
+/* the replication sub-message */
+#define MESSAGE_REP_ADD_VERSION_REQUEST                0
+#define MESSAGE_REP_ADD_VERSION_REPLY          1
+#define MESSAGE_REP_SEND_ENTRIES_REQUEST       2
+#define MESSAGE_REP_SEND_ENTRIES_REPLY         3
+#define MESSAGE_REP_UPDATE_NOTIFY_REQUEST      4
+
+/* stop reasons */
+#define STOP_REASON_USER_REASON                        0
+#define STOP_REASON_AUTH_FAILED                        1
+#define STOP_REASON_INCOMPLETE_VERSION         2
+#define STOP_REASON_BUG_CHECK                  3
+#define STOP_REASON_MESSAGE_ERROR              4
+
+
+typedef struct _WINS_OWNER {
+       struct in_addr address;
+       SMB_BIG_UINT max_version;
+       SMB_BIG_UINT min_version;
+       int type;
+       time_t last_pull;
+       time_t last_push;
+} WINS_OWNER;
+
+typedef struct _WINS_NAME {
+       int name_len; /* always 0x11 */
+       char name[16];
+       char type;
+       int empty;
+       int name_flag;
+       int group_flag;
+       SMB_BIG_UINT id;
+       int num_ip;
+       struct in_addr owner;
+       struct in_addr *others;
+       int foo; /* 0xffffff */
+} WINS_NAME;
+       
+typedef struct _WINS_PARTNERS
+{
+       int client_assoc;
+       int server_assoc;
+       BOOL pull_partner;
+       BOOL push_partner;
+       struct in_addr partner_server;
+       struct in_addr other_server;
+} WINS_PARTNER;
+
+typedef struct _generic_header{
+       int data_size;
+       int opcode;
+       int assoc_ctx;
+       int mess_type;
+} generic_header;
+
+typedef struct _START_ASSOC_REQUEST {
+       int assoc_ctx;
+       int min_ver;
+       int maj_ver;
+} START_ASSOC_REQUEST;
+
+typedef struct _START_ASSOC_REPLY {
+       int assoc_ctx;
+       int min_ver;
+       int maj_ver;
+} START_ASSOC_REPLY;
+
+typedef struct _STOP_ASSOC {
+       int reason;
+} STOP_ASSOC;
+
+typedef struct _AVMT_REP {
+       int partner_count;
+       WINS_OWNER *wins_owner;
+       struct in_addr initiating_wins_server;
+} AVMT_REP;
+
+typedef struct _SEND_ENTRIES_REQUEST {
+       WINS_OWNER wins_owner;
+} SEND_ENTRIES_REQUEST;
+
+typedef struct _SEND_ENTRIES_REPLY {
+       int max_names;
+       WINS_NAME *wins_name;   
+} SEND_ENTRIES_REPLY;
+
+typedef struct  _UPDATE_NOTIFY_REQUEST {
+       int partner_count;
+       WINS_OWNER *wins_owner; 
+       struct in_addr initiating_wins_server;
+} UPDATE_NOTIFY_REQUEST;
+
+typedef struct _REPLICATE {
+       int msg_type;
+       
+       AVMT_REP avmt_rep;
+       SEND_ENTRIES_REQUEST se_rq;
+       SEND_ENTRIES_REPLY se_rp;
+       UPDATE_NOTIFY_REQUEST un_rq;
+} REPLICATE;
+
+
+typedef struct _GENERIC_PACKET {
+       int fd;
+
+       generic_header header;
+
+       START_ASSOC_REQUEST sa_rq;
+       START_ASSOC_REPLY sa_rp;
+       STOP_ASSOC so;
+       REPLICATE rep;
+} GENERIC_PACKET;
+
+struct wins_packet_struct
+{
+       struct wins_packet_struct *next;
+       struct wins_packet_struct *prev;
+       BOOL stop_packet;
+       int fd;
+       time_t timestamp;
+       GENERIC_PACKET *packet;
+};
+
+struct BUFFER {
+       char *buffer;
+       int offset;
+       int length;
+};
+
+
+
+#include "wrepld_proto.h"
+